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
20namespace net {
21
22// used to ensure we delete the addrinfo structure
23// alloc'd by getaddrinfo
24class AddrinfoGuard {
25 protected:
26  struct addrinfo* addrinfo_ptr_;
27
28 public:
29  explicit AddrinfoGuard(struct addrinfo* addrinfo_ptr)
30      : addrinfo_ptr_(addrinfo_ptr) {}
31
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//
50bool 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.
61void 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
77int 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.
91int 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
227int 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