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