Home | History | Annotate | Download | only in flip_server
      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:
     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 //
     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.
     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 
     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.
     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 
    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