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>   // for inet_ntop
      8 #include <errno.h>       // for strerror
      9 #include <netdb.h>       // for getaddrinfo and getnameinfo
     10 #include <netinet/in.h>  // for IPPROTO_*, etc.
     11 #include <stdlib.h>      // for EXIT_FAILURE
     12 #include <netinet/tcp.h> // For TCP_NODELAY
     13 #include <sys/socket.h>  // for getaddrinfo and getnameinfo
     14 #include <sys/types.h>   // "
     15 #include <fcntl.h>
     16 #include <unistd.h>      // for exit()
     17 #include <ostream>
     18 
     19 #include "base/logging.h"
     20 
     21 namespace net {
     22 
     23 // used to ensure we delete the addrinfo structure
     24 // alloc'd by getaddrinfo
     25 class 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 //
     56 bool 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.
     70 void 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 
     87 int 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.
    101 int 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 
    229 int 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