• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
21 namespace net {
22 
23 // used to ensure we delete the addrinfo structure
24 // alloc'd by getaddrinfo
25 class AddrinfoGuard {
26  protected:
27   struct addrinfo * addrinfo_ptr_;
28  public:
29 
AddrinfoGuard(struct addrinfo * addrinfo_ptr)30   explicit AddrinfoGuard(struct addrinfo* addrinfo_ptr) :
31     addrinfo_ptr_(addrinfo_ptr) {}
32 
~AddrinfoGuard()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 //
CloseSocket(int * fd,int tries)56 bool 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.
SetNonBlocking(int fd)70 void 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 
SetDisableNagle(int fd)87 int 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.
CreateListeningSocket(const std::string & host,const std::string & port,bool is_numeric_host_address,int backlog,bool reuseaddr,bool reuseport,bool wait_for_iface,bool disable_nagle,int * listen_fd)101 int 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 
CreateConnectedSocket(int * connect_fd,const std::string & host,const std::string & port,bool is_numeric_host_address,bool disable_nagle)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;
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