| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "net/tools/flip_server/tcp_socket_util.h" | |
| 6 | |
| 7 #include <arpa/inet.h> | |
| 8 #include <errno.h> | |
| 9 #include <netdb.h> | |
| 10 #include <netinet/in.h> | |
| 11 #include <netinet/tcp.h> | |
| 12 #include <stdlib.h> | |
| 13 #include <string.h> | |
| 14 #include <sys/socket.h> | |
| 15 #include <sys/types.h> | |
| 16 #include <unistd.h> | |
| 17 | |
| 18 #include "base/files/file_util.h" | |
| 19 #include "base/logging.h" | |
| 20 #include "net/socket/tcp_socket.h" | |
| 21 | |
| 22 namespace net { | |
| 23 | |
| 24 namespace { | |
| 25 | |
| 26 // Used to ensure we delete the addrinfo structure alloc'd by getaddrinfo(). | |
| 27 class AddrinfoGuard { | |
| 28 public: | |
| 29 explicit AddrinfoGuard(struct addrinfo* addrinfo_ptr) | |
| 30 : addrinfo_ptr_(addrinfo_ptr) {} | |
| 31 | |
| 32 ~AddrinfoGuard() { freeaddrinfo(addrinfo_ptr_); } | |
| 33 | |
| 34 private: | |
| 35 struct addrinfo* addrinfo_ptr_; | |
| 36 }; | |
| 37 | |
| 38 // Summary: | |
| 39 // Closes a socket, with option to attempt it multiple times. | |
| 40 // Why do this? Well, if the system-call gets interrupted, close | |
| 41 // can fail with EINTR. In that case you should just retry.. Unfortunately, | |
| 42 // we can't be sure that errno is properly set since we're using a | |
| 43 // multithreaded approach in the filter proxy, so we should just retry. | |
| 44 // Args: | |
| 45 // fd - the socket to close | |
| 46 // tries - the number of tries to close the socket. | |
| 47 // Returns: | |
| 48 // true - if socket was closed | |
| 49 // false - if socket was NOT closed. | |
| 50 // Side-effects: | |
| 51 // sets *fd to -1 if socket was closed. | |
| 52 // | |
| 53 bool CloseSocket(int* fd, int tries) { | |
| 54 for (int i = 0; i < tries; ++i) { | |
| 55 if (!close(*fd)) { | |
| 56 *fd = -1; | |
| 57 return true; | |
| 58 } | |
| 59 } | |
| 60 return false; | |
| 61 } | |
| 62 | |
| 63 } // namespace | |
| 64 | |
| 65 int CreateTCPServerSocket(const std::string& host, | |
| 66 const std::string& port, | |
| 67 bool is_numeric_host_address, | |
| 68 int backlog, | |
| 69 bool reuseaddr, | |
| 70 bool reuseport, | |
| 71 bool wait_for_iface, | |
| 72 bool disable_nagle, | |
| 73 int* listen_fd) { | |
| 74 // start out by assuming things will fail. | |
| 75 *listen_fd = -1; | |
| 76 | |
| 77 const char* node = NULL; | |
| 78 const char* service = NULL; | |
| 79 | |
| 80 if (!host.empty()) | |
| 81 node = host.c_str(); | |
| 82 if (!port.empty()) | |
| 83 service = port.c_str(); | |
| 84 | |
| 85 struct addrinfo* results = 0; | |
| 86 struct addrinfo hints; | |
| 87 memset(&hints, 0, sizeof(hints)); | |
| 88 | |
| 89 if (is_numeric_host_address) { | |
| 90 hints.ai_flags = AI_NUMERICHOST; // iff you know the name is numeric. | |
| 91 } | |
| 92 hints.ai_flags |= AI_PASSIVE; | |
| 93 | |
| 94 hints.ai_family = PF_INET; | |
| 95 hints.ai_socktype = SOCK_STREAM; | |
| 96 | |
| 97 int err = 0; | |
| 98 if ((err = getaddrinfo(node, service, &hints, &results))) { | |
| 99 // gai_strerror -is- threadsafe, so we get to use it here. | |
| 100 LOG(ERROR) << "getaddrinfo " | |
| 101 << " for (" << host << ":" << port << ") " << gai_strerror(err) | |
| 102 << "\n"; | |
| 103 return -1; | |
| 104 } | |
| 105 // this will delete the addrinfo memory when we return from this function. | |
| 106 AddrinfoGuard addrinfo_guard(results); | |
| 107 | |
| 108 int sock = | |
| 109 socket(results->ai_family, results->ai_socktype, results->ai_protocol); | |
| 110 if (sock == -1) { | |
| 111 LOG(ERROR) << "Unable to create socket for (" << host << ":" << port | |
| 112 << "): " << strerror(errno) << "\n"; | |
| 113 return -1; | |
| 114 } | |
| 115 | |
| 116 if (reuseaddr) { | |
| 117 // set SO_REUSEADDR on the listening socket. | |
| 118 int on = 1; | |
| 119 int rc; | |
| 120 rc = setsockopt(sock, | |
| 121 SOL_SOCKET, | |
| 122 SO_REUSEADDR, | |
| 123 reinterpret_cast<char*>(&on), | |
| 124 sizeof(on)); | |
| 125 if (rc < 0) { | |
| 126 close(sock); | |
| 127 LOG(FATAL) << "setsockopt() failed fd=" << listen_fd << "\n"; | |
| 128 } | |
| 129 } | |
| 130 #ifndef SO_REUSEPORT | |
| 131 #define SO_REUSEPORT 15 | |
| 132 #endif | |
| 133 if (reuseport) { | |
| 134 // set SO_REUSEPORT on the listening socket. | |
| 135 int on = 1; | |
| 136 int rc; | |
| 137 rc = setsockopt(sock, | |
| 138 SOL_SOCKET, | |
| 139 SO_REUSEPORT, | |
| 140 reinterpret_cast<char*>(&on), | |
| 141 sizeof(on)); | |
| 142 if (rc < 0) { | |
| 143 close(sock); | |
| 144 LOG(FATAL) << "setsockopt() failed fd=" << listen_fd << "\n"; | |
| 145 } | |
| 146 } | |
| 147 | |
| 148 if (bind(sock, results->ai_addr, results->ai_addrlen)) { | |
| 149 // If we are waiting for the interface to be raised, such as in an | |
| 150 // HA environment, ignore reporting any errors. | |
| 151 int saved_errno = errno; | |
| 152 if (!wait_for_iface || errno != EADDRNOTAVAIL) { | |
| 153 LOG(ERROR) << "Bind was unsuccessful for (" << host << ":" << port | |
| 154 << "): " << strerror(errno) << "\n"; | |
| 155 } | |
| 156 // if we knew that we were not multithreaded, we could do the following: | |
| 157 // " : " << strerror(errno) << "\n"; | |
| 158 if (CloseSocket(&sock, 100)) { | |
| 159 if (saved_errno == EADDRNOTAVAIL) { | |
| 160 return -3; | |
| 161 } | |
| 162 return -2; | |
| 163 } else { | |
| 164 // couldn't even close the dang socket?! | |
| 165 LOG(ERROR) << "Unable to close the socket.. Considering this a fatal " | |
| 166 "error, and exiting\n"; | |
| 167 exit(EXIT_FAILURE); | |
| 168 return -1; | |
| 169 } | |
| 170 } | |
| 171 | |
| 172 if (disable_nagle) { | |
| 173 if (!SetTCPNoDelay(sock, /*no_delay=*/true)) { | |
| 174 close(sock); | |
| 175 LOG(FATAL) << "SetTCPNoDelay() failed on fd: " << sock; | |
| 176 return -1; | |
| 177 } | |
| 178 } | |
| 179 | |
| 180 if (listen(sock, backlog)) { | |
| 181 // listen was unsuccessful. | |
| 182 LOG(ERROR) << "Listen was unsuccessful for (" << host << ":" << port | |
| 183 << "): " << strerror(errno) << "\n"; | |
| 184 // if we knew that we were not multithreaded, we could do the following: | |
| 185 // " : " << strerror(errno) << "\n"; | |
| 186 | |
| 187 if (CloseSocket(&sock, 100)) { | |
| 188 sock = -1; | |
| 189 return -1; | |
| 190 } else { | |
| 191 // couldn't even close the dang socket?! | |
| 192 LOG(FATAL) << "Unable to close the socket.. Considering this a fatal " | |
| 193 "error, and exiting\n"; | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 // If we've gotten to here, Yeay! Success! | |
| 198 *listen_fd = sock; | |
| 199 | |
| 200 return 0; | |
| 201 } | |
| 202 | |
| 203 int CreateTCPClientSocket(const std::string& host, | |
| 204 const std::string& port, | |
| 205 bool is_numeric_host_address, | |
| 206 bool disable_nagle, | |
| 207 int* connect_fd) { | |
| 208 const char* node = NULL; | |
| 209 const char* service = NULL; | |
| 210 | |
| 211 *connect_fd = -1; | |
| 212 if (!host.empty()) | |
| 213 node = host.c_str(); | |
| 214 if (!port.empty()) | |
| 215 service = port.c_str(); | |
| 216 | |
| 217 struct addrinfo* results = 0; | |
| 218 struct addrinfo hints; | |
| 219 memset(&hints, 0, sizeof(hints)); | |
| 220 | |
| 221 if (is_numeric_host_address) | |
| 222 hints.ai_flags = AI_NUMERICHOST; // iff you know the name is numeric. | |
| 223 hints.ai_flags |= AI_PASSIVE; | |
| 224 | |
| 225 hints.ai_family = PF_INET; | |
| 226 hints.ai_socktype = SOCK_STREAM; | |
| 227 | |
| 228 int err = 0; | |
| 229 if ((err = getaddrinfo(node, service, &hints, &results))) { | |
| 230 // gai_strerror -is- threadsafe, so we get to use it here. | |
| 231 LOG(ERROR) << "getaddrinfo for (" << node << ":" << service | |
| 232 << "): " << gai_strerror(err); | |
| 233 return -1; | |
| 234 } | |
| 235 // this will delete the addrinfo memory when we return from this function. | |
| 236 AddrinfoGuard addrinfo_guard(results); | |
| 237 | |
| 238 int sock = | |
| 239 socket(results->ai_family, results->ai_socktype, results->ai_protocol); | |
| 240 if (sock == -1) { | |
| 241 LOG(ERROR) << "Unable to create socket for (" << node << ":" << service | |
| 242 << "): " << strerror(errno); | |
| 243 return -1; | |
| 244 } | |
| 245 | |
| 246 if (!base::SetNonBlocking(sock)) { | |
| 247 LOG(FATAL) << "base::SetNonBlocking failed: " << sock; | |
| 248 } | |
| 249 | |
| 250 if (disable_nagle) { | |
| 251 if (!SetTCPNoDelay(sock, /*no_delay=*/true)) { | |
| 252 close(sock); | |
| 253 LOG(FATAL) << "SetTCPNoDelay() failed on fd: " << sock; | |
| 254 return -1; | |
| 255 } | |
| 256 } | |
| 257 | |
| 258 int ret_val = 0; | |
| 259 if (connect(sock, results->ai_addr, results->ai_addrlen)) { | |
| 260 if (errno != EINPROGRESS) { | |
| 261 LOG(ERROR) << "Connect was unsuccessful for (" << node << ":" << service | |
| 262 << "): " << strerror(errno); | |
| 263 close(sock); | |
| 264 return -1; | |
| 265 } | |
| 266 } else { | |
| 267 ret_val = 1; | |
| 268 } | |
| 269 | |
| 270 // If we've gotten to here, Yeay! Success! | |
| 271 *connect_fd = sock; | |
| 272 | |
| 273 return ret_val; | |
| 274 } | |
| 275 | |
| 276 } // namespace net | |
| OLD | NEW |