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.
5 #include "net/tools/flip_server/create_listener.h"
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> // "
16 #include <unistd.h> // for exit()
19 #include "base/logging.h"
23 // used to ensure we delete the addrinfo structure
24 // alloc'd by getaddrinfo
27 struct addrinfo * addrinfo_ptr_;
30 explicit AddrinfoGuard(struct addrinfo* addrinfo_ptr) :
31 addrinfo_ptr_(addrinfo_ptr) {}
34 freeaddrinfo(addrinfo_ptr_);
38 ////////////////////////////////////////////////////////////////////////////////
39 ////////////////////////////////////////////////////////////////////////////////
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.
48 // fd - the socket to close
49 // tries - the number of tries to close the socket.
51 // true - if socket was closed
52 // false - if socket was NOT closed.
54 // sets *fd to -1 if socket was closed.
56 bool CloseSocket(int *fd, int tries) {
57 for (int i = 0; i < tries; ++i) {
66 ////////////////////////////////////////////////////////////////////////////////
67 ////////////////////////////////////////////////////////////////////////////////
69 // Sets an FD to be nonblocking.
70 void FlipSetNonBlocking(int fd) {
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;
78 if (fcntl_return & O_NONBLOCK)
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;
87 int SetDisableNagle(int fd) {
90 rc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
91 reinterpret_cast<char*>(&on), sizeof(on));
94 LOG(FATAL) << "setsockopt() TCP_NODELAY: failed on fd " << fd;
100 // see header for documentation of this function.
101 int CreateListeningSocket(const std::string& host,
102 const std::string& port,
103 bool is_numeric_host_address,
110 // start out by assuming things will fail.
113 const char* node = NULL;
114 const char* service = NULL;
116 if (!host.empty()) node = host.c_str();
117 if (!port.empty()) service = port.c_str();
119 struct addrinfo *results = 0;
120 struct addrinfo hints;
121 memset(&hints, 0, sizeof(hints));
123 if (is_numeric_host_address) {
124 hints.ai_flags = AI_NUMERICHOST; // iff you know the name is numeric.
126 hints.ai_flags |= AI_PASSIVE;
128 hints.ai_family = PF_INET;
129 hints.ai_socktype = SOCK_STREAM;
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";
138 // this will delete the addrinfo memory when we return from this function.
139 AddrinfoGuard addrinfo_guard(results);
141 int sock = socket(results->ai_family,
142 results->ai_socktype,
143 results->ai_protocol);
145 LOG(ERROR) << "Unable to create socket for (" << host << ":"
146 << port << "): " << strerror(errno) << "\n";
151 // set SO_REUSEADDR on the listening socket.
154 rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
155 reinterpret_cast<char *>(&on), sizeof(on));
158 LOG(FATAL) << "setsockopt() failed fd=" << listen_fd << "\n";
162 #define SO_REUSEPORT 15
165 // set SO_REUSEADDR on the listening socket.
168 rc = setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
169 reinterpret_cast<char *>(&on), sizeof(on));
172 LOG(FATAL) << "setsockopt() failed fd=" << listen_fd << "\n";
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";
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 ) {
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";
201 if (!SetDisableNagle(sock)) {
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";
213 if (CloseSocket(&sock, 100)) {
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";
223 // If we've gotten to here, Yeay! Success!
229 int 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;
241 service = port.c_str();
243 struct addrinfo *results = 0;
244 struct addrinfo hints;
245 memset(&hints, 0, sizeof(hints));
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;
251 hints.ai_family = PF_INET;
252 hints.ai_socktype = SOCK_STREAM;
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);
261 // this will delete the addrinfo memory when we return from this function.
262 AddrinfoGuard addrinfo_guard(results);
264 int sock = socket(results->ai_family,
265 results->ai_socktype,
266 results->ai_protocol);
268 LOG(ERROR) << "Unable to create socket for (" << node << ":" << service
269 << "): " << strerror( errno );
273 FlipSetNonBlocking( sock );
276 if (!SetDisableNagle(sock)) {
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);
293 // If we've gotten to here, Yeay! Success!