diff options
Diffstat (limited to 'chromium/net/tools/flip_server/create_listener.cc')
-rw-r--r-- | chromium/net/tools/flip_server/create_listener.cc | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/chromium/net/tools/flip_server/create_listener.cc b/chromium/net/tools/flip_server/create_listener.cc new file mode 100644 index 00000000000..4676912c117 --- /dev/null +++ b/chromium/net/tools/flip_server/create_listener.cc @@ -0,0 +1,300 @@ +// 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/create_listener.h" + +#include <arpa/inet.h> // for inet_ntop +#include <errno.h> // for strerror +#include <netdb.h> // for getaddrinfo and getnameinfo +#include <netinet/in.h> // for IPPROTO_*, etc. +#include <stdlib.h> // for EXIT_FAILURE +#include <netinet/tcp.h> // For TCP_NODELAY +#include <sys/socket.h> // for getaddrinfo and getnameinfo +#include <sys/types.h> // " +#include <fcntl.h> +#include <unistd.h> // for exit() +#include <ostream> + +#include "base/logging.h" + +namespace net { + +// used to ensure we delete the addrinfo structure +// alloc'd by getaddrinfo +class AddrinfoGuard { + protected: + struct addrinfo * addrinfo_ptr_; + public: + + explicit AddrinfoGuard(struct addrinfo* addrinfo_ptr) : + addrinfo_ptr_(addrinfo_ptr) {} + + ~AddrinfoGuard() { + freeaddrinfo(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; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// Sets an FD to be nonblocking. +void SetNonBlocking(int fd) { + DCHECK_GE(fd, 0); + + int fcntl_return = fcntl(fd, F_GETFL, 0); + CHECK_NE(fcntl_return, -1) + << "error doing fcntl(fd, F_GETFL, 0) fd: " << fd + << " errno=" << errno; + + if (fcntl_return & O_NONBLOCK) + return; + + fcntl_return = fcntl(fd, F_SETFL, fcntl_return | O_NONBLOCK); + CHECK_NE(fcntl_return, -1) + << "error doing fcntl(fd, F_SETFL, fcntl_return) fd: " << fd + << " errno=" << errno; +} + +int SetDisableNagle(int fd) { + int on = 1; + int rc; + rc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, + reinterpret_cast<char*>(&on), sizeof(on)); + if (rc < 0) { + close(fd); + LOG(FATAL) << "setsockopt() TCP_NODELAY: failed on fd " << fd; + return 0; + } + return 1; +} + +// see header for documentation of this function. +int CreateListeningSocket(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<char *>(&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_REUSEADDR on the listening socket. + int on = 1; + int rc; + rc = setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, + reinterpret_cast<char *>(&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 (!SetDisableNagle(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 CreateConnectedSocket( int *connect_fd, + const std::string& host, + const std::string& port, + bool is_numeric_host_address, + bool disable_nagle ) { + 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; + } + + SetNonBlocking( sock ); + + if (disable_nagle) { + if (!SetDisableNagle(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 + |