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