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