create_listener.cc revision c7f5f8508d98d5952d42ed7648c2a8f30a4da156
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 <sys/socket.h>  // for getaddrinfo and getnameinfo
11#include <sys/types.h>   // "
12#include <unistd.h>      // for exit()
13
14#include "net/tools/flip_server/create_listener.h"
15
16#include "base/logging.h"
17
18namespace net {
19
20// used to ensure we delete the addrinfo structure
21// alloc'd by getaddrinfo
22class AddrinfoGuard {
23 protected:
24  struct addrinfo * addrinfo_ptr_;
25 public:
26
27  explicit AddrinfoGuard(struct addrinfo* addrinfo_ptr) :
28    addrinfo_ptr_(addrinfo_ptr) {}
29
30  ~AddrinfoGuard() {
31    freeaddrinfo(addrinfo_ptr_);
32  }
33};
34
35////////////////////////////////////////////////////////////////////////////////
36////////////////////////////////////////////////////////////////////////////////
37
38// Summary:
39//   Closes a socket, with option to attempt it multiple times.
40//   Why do this? Well, if the system-call gets interrupted, close
41//   can fail with EINTR. In that case you should just retry.. Unfortunately,
42//   we can't be sure that errno is properly set since we're using a
43//   multithreaded approach in the filter proxy, so we should just retry.
44// Args:
45//   fd - the socket to close
46//   tries - the number of tries to close the socket.
47// Returns:
48//   true - if socket was closed
49//   false - if socket was NOT closed.
50// Side-effects:
51//   sets *fd to -1 if socket was closed.
52//
53bool CloseSocket(int *fd, int tries) {
54  for (int i = 0; i < tries; ++i) {
55    if (!close(*fd)) {
56      *fd = -1;
57      return true;
58    }
59  }
60  return false;
61}
62
63////////////////////////////////////////////////////////////////////////////////
64////////////////////////////////////////////////////////////////////////////////
65
66// see header for documentation of this function.
67void CreateListeningSocket(const std::string& host,
68                           const std::string& port,
69                           bool is_numeric_host_address,
70                           int backlog,
71                           int * listen_fd,
72                           bool reuseaddr,
73                           bool reuseport,
74                           std::ostream* error_stream) {
75  // start out by assuming things will fail.
76  *listen_fd = -1;
77
78  const char* node = NULL;
79  const char* service = NULL;
80
81  if (!host.empty()) node = host.c_str();
82  if (!port.empty()) service = port.c_str();
83
84  struct addrinfo *results = 0;
85  struct addrinfo hints;
86  memset(&hints, 0, sizeof(hints));
87
88  if (is_numeric_host_address) {
89    hints.ai_flags = AI_NUMERICHOST;  // iff you know the name is numeric.
90  }
91  hints.ai_flags |= AI_PASSIVE;
92
93  hints.ai_family = PF_INET;       // we know it'll be IPv4, but if we didn't
94  // hints.ai_family = PF_UNSPEC;  // know we'd use this. <---
95  hints.ai_socktype = SOCK_STREAM;
96
97  int err = 0;
98  if ((err=getaddrinfo(node, service, &hints, &results))) {
99    // gai_strerror -is- threadsafe, so we get to use it here.
100    *error_stream << "getaddrinfo " << " for (" << host << ":" << port
101                  << ") " << gai_strerror(err) << "\n";
102    return;
103  }
104  // this will delete the addrinfo memory when we return from this function.
105  AddrinfoGuard addrinfo_guard(results);
106
107  int sock = socket(results->ai_family,
108                    results->ai_socktype,
109                    results->ai_protocol);
110  if (sock == -1) {
111    *error_stream << "Unable to create socket for (" << host << ":"
112      << port << "): " << strerror(errno) << "\n";
113    return;
114  }
115
116  if (reuseaddr) {
117    // set SO_REUSEADDR on the listening socket.
118    int on = 1;
119    int rc;
120    rc = setsockopt(sock, SOL_SOCKET,  SO_REUSEADDR,
121                    reinterpret_cast<char *>(&on), sizeof(on));
122    if (rc < 0) {
123      close(sock);
124      LOG(FATAL) << "setsockopt() failed fd=" << listen_fd << "\n";
125    }
126  }
127#ifndef SO_REUSEPORT
128#define SO_REUSEPORT 15
129#endif
130  if (reuseport) {
131    // set SO_REUSEADDR on the listening socket.
132    int on = 1;
133    int rc;
134    rc = setsockopt(sock, SOL_SOCKET,  SO_REUSEPORT,
135                    reinterpret_cast<char *>(&on), sizeof(on));
136    if (rc < 0) {
137      close(sock);
138      LOG(FATAL) << "setsockopt() failed fd=" << listen_fd << "\n";
139    }
140  }
141
142  if (bind(sock, results->ai_addr, results->ai_addrlen)) {
143    *error_stream << "Bind was unsuccessful for (" << host << ":"
144      << port << "): " << strerror(errno) << "\n";
145    // if we knew that we were not multithreaded, we could do the following:
146    // " : " << strerror(errno) << "\n";
147    if (CloseSocket(&sock, 100)) {
148      return;
149    } else {
150      // couldn't even close the dang socket?!
151      *error_stream << "Unable to close the socket.. Considering this a fatal "
152                      "error, and exiting\n";
153      exit(EXIT_FAILURE);
154    }
155  }
156
157  if (listen(sock, backlog)) {
158    // listen was unsuccessful.
159    *error_stream << "Listen was unsuccessful for (" << host << ":"
160      << port << "): " << strerror(errno) << "\n";
161    // if we knew that we were not multithreaded, we could do the following:
162    // " : " << strerror(errno) << "\n";
163
164    if (CloseSocket(&sock, 100)) {
165      sock = -1;
166      return;
167    } else {
168      // couldn't even close the dang socket?!
169      *error_stream << "Unable to close the socket.. Considering this a fatal "
170                      "error, and exiting\n";
171      exit(EXIT_FAILURE);
172    }
173  }
174  // If we've gotten to here, Yeay! Success!
175  *listen_fd = sock;
176}
177
178}  // namespace net
179
180