create_listener.cc revision 201ade2fbba22bfb27ae029f4d23fca6ded109a0
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 <arpa/inet.h>   // for inet_ntop
6#include <errno.h>       // for strerror
7#include <netdb.h>       // for getaddrinfo and getnameinfo
8#include <netinet/in.h>  // for IPPROTO_*, etc.
9#include <stdlib.h>      // for EXIT_FAILURE
10#include <netinet/tcp.h> // For TCP_NODELAY
11#include <sys/socket.h>  // for getaddrinfo and getnameinfo
12#include <sys/types.h>   // "
13#include <fcntl.h>
14#include <unistd.h>      // for exit()
15#include <ostream>
16
17#include "net/tools/flip_server/create_listener.h"
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