• 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>
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:
AddrinfoGuard(struct addrinfo * addrinfo_ptr)29   explicit AddrinfoGuard(struct addrinfo* addrinfo_ptr)
30       : addrinfo_ptr_(addrinfo_ptr) {}
31 
~AddrinfoGuard()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 //
CloseSocket(int * fd,int tries)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.
FlipSetNonBlocking(int fd)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 
SetDisableNagle(int fd)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.
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)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 
CreateConnectedSocket(int * connect_fd,const std::string & host,const std::string & port,bool is_numeric_host_address,bool disable_nagle)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