// Copyright (c) 2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/tools/flip_server/tcp_socket_util.h" #include #include #include #include #include #include #include #include #include #include #include "base/files/file_util.h" #include "base/logging.h" #include "net/socket/tcp_socket.h" namespace net { namespace { // Used to ensure we delete the addrinfo structure alloc'd by getaddrinfo(). class AddrinfoGuard { public: explicit AddrinfoGuard(struct addrinfo* addrinfo_ptr) : addrinfo_ptr_(addrinfo_ptr) {} ~AddrinfoGuard() { freeaddrinfo(addrinfo_ptr_); } private: struct addrinfo* addrinfo_ptr_; }; // Summary: // Closes a socket, with option to attempt it multiple times. // Why do this? Well, if the system-call gets interrupted, close // can fail with EINTR. In that case you should just retry.. Unfortunately, // we can't be sure that errno is properly set since we're using a // multithreaded approach in the filter proxy, so we should just retry. // Args: // fd - the socket to close // tries - the number of tries to close the socket. // Returns: // true - if socket was closed // false - if socket was NOT closed. // Side-effects: // sets *fd to -1 if socket was closed. // bool CloseSocket(int* fd, int tries) { for (int i = 0; i < tries; ++i) { if (!close(*fd)) { *fd = -1; return true; } } return false; } } // namespace int CreateTCPServerSocket(const std::string& host, const std::string& port, bool is_numeric_host_address, int backlog, bool reuseaddr, bool reuseport, bool wait_for_iface, bool disable_nagle, int* listen_fd) { // start out by assuming things will fail. *listen_fd = -1; const char* node = NULL; const char* service = NULL; if (!host.empty()) node = host.c_str(); if (!port.empty()) service = port.c_str(); struct addrinfo* results = 0; struct addrinfo hints; memset(&hints, 0, sizeof(hints)); if (is_numeric_host_address) { hints.ai_flags = AI_NUMERICHOST; // iff you know the name is numeric. } hints.ai_flags |= AI_PASSIVE; hints.ai_family = PF_INET; hints.ai_socktype = SOCK_STREAM; int err = 0; if ((err = getaddrinfo(node, service, &hints, &results))) { // gai_strerror -is- threadsafe, so we get to use it here. LOG(ERROR) << "getaddrinfo " << " for (" << host << ":" << port << ") " << gai_strerror(err) << "\n"; return -1; } // this will delete the addrinfo memory when we return from this function. AddrinfoGuard addrinfo_guard(results); int sock = socket(results->ai_family, results->ai_socktype, results->ai_protocol); if (sock == -1) { LOG(ERROR) << "Unable to create socket for (" << host << ":" << port << "): " << strerror(errno) << "\n"; return -1; } if (reuseaddr) { // set SO_REUSEADDR on the listening socket. int on = 1; int rc; rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&on), sizeof(on)); if (rc < 0) { close(sock); LOG(FATAL) << "setsockopt() failed fd=" << listen_fd << "\n"; } } #ifndef SO_REUSEPORT #define SO_REUSEPORT 15 #endif if (reuseport) { // set SO_REUSEPORT on the listening socket. int on = 1; int rc; rc = setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast(&on), sizeof(on)); if (rc < 0) { close(sock); LOG(FATAL) << "setsockopt() failed fd=" << listen_fd << "\n"; } } if (bind(sock, results->ai_addr, results->ai_addrlen)) { // If we are waiting for the interface to be raised, such as in an // HA environment, ignore reporting any errors. int saved_errno = errno; if (!wait_for_iface || errno != EADDRNOTAVAIL) { LOG(ERROR) << "Bind was unsuccessful for (" << host << ":" << port << "): " << strerror(errno) << "\n"; } // if we knew that we were not multithreaded, we could do the following: // " : " << strerror(errno) << "\n"; if (CloseSocket(&sock, 100)) { if (saved_errno == EADDRNOTAVAIL) { return -3; } return -2; } else { // couldn't even close the dang socket?! LOG(ERROR) << "Unable to close the socket.. Considering this a fatal " "error, and exiting\n"; exit(EXIT_FAILURE); return -1; } } if (disable_nagle) { if (!SetTCPNoDelay(sock, /*no_delay=*/true)) { close(sock); LOG(FATAL) << "SetTCPNoDelay() failed on fd: " << sock; return -1; } } if (listen(sock, backlog)) { // listen was unsuccessful. LOG(ERROR) << "Listen was unsuccessful for (" << host << ":" << port << "): " << strerror(errno) << "\n"; // if we knew that we were not multithreaded, we could do the following: // " : " << strerror(errno) << "\n"; if (CloseSocket(&sock, 100)) { sock = -1; return -1; } else { // couldn't even close the dang socket?! LOG(FATAL) << "Unable to close the socket.. Considering this a fatal " "error, and exiting\n"; } } // If we've gotten to here, Yeay! Success! *listen_fd = sock; return 0; } int CreateTCPClientSocket(const std::string& host, const std::string& port, bool is_numeric_host_address, bool disable_nagle, int* connect_fd) { const char* node = NULL; const char* service = NULL; *connect_fd = -1; if (!host.empty()) node = host.c_str(); if (!port.empty()) service = port.c_str(); struct addrinfo* results = 0; struct addrinfo hints; memset(&hints, 0, sizeof(hints)); if (is_numeric_host_address) hints.ai_flags = AI_NUMERICHOST; // iff you know the name is numeric. hints.ai_flags |= AI_PASSIVE; hints.ai_family = PF_INET; hints.ai_socktype = SOCK_STREAM; int err = 0; if ((err = getaddrinfo(node, service, &hints, &results))) { // gai_strerror -is- threadsafe, so we get to use it here. LOG(ERROR) << "getaddrinfo for (" << node << ":" << service << "): " << gai_strerror(err); return -1; } // this will delete the addrinfo memory when we return from this function. AddrinfoGuard addrinfo_guard(results); int sock = socket(results->ai_family, results->ai_socktype, results->ai_protocol); if (sock == -1) { LOG(ERROR) << "Unable to create socket for (" << node << ":" << service << "): " << strerror(errno); return -1; } if (!base::SetNonBlocking(sock)) { LOG(FATAL) << "base::SetNonBlocking failed: " << sock; } if (disable_nagle) { if (!SetTCPNoDelay(sock, /*no_delay=*/true)) { close(sock); LOG(FATAL) << "SetTCPNoDelay() failed on fd: " << sock; return -1; } } int ret_val = 0; if (connect(sock, results->ai_addr, results->ai_addrlen)) { if (errno != EINPROGRESS) { LOG(ERROR) << "Connect was unsuccessful for (" << node << ":" << service << "): " << strerror(errno); close(sock); return -1; } } else { ret_val = 1; } // If we've gotten to here, Yeay! Success! *connect_fd = sock; return ret_val; } } // namespace net