From 6d56eb9d651a72a1fe10b7829e569a7ac12afb20 Mon Sep 17 00:00:00 2001 From: Mark Slee Date: Fri, 6 Jul 2007 22:28:15 +0000 Subject: [PATCH] TSocket IPv6 support for C++ Summary: From Paul Saab at Powerset Reviewed By: mcslee Test Plan: test/cpp/TestClient git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665155 13f79535-47bb-0310-9956-ffa450edef68 --- CONTRIBUTORS | 6 ++ lib/cpp/src/transport/TServerSocket.cpp | 46 ++++++++--- lib/cpp/src/transport/TSocket.cpp | 100 ++++++++++++++---------- lib/cpp/src/transport/TSocket.h | 3 + 4 files changed, 102 insertions(+), 53 deletions(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 9c5c96f6..bb3ee24d 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -2,6 +2,12 @@ Release 200705xx ---------------- +Paul Saab +-IPv6 support for TSocket in C++/Python + +Kevin Clark +-Significant overhaul of Ruby code generation and servers + Simon Forman -TProcessorFactory abstraction for Java servers diff --git a/lib/cpp/src/transport/TServerSocket.cpp b/lib/cpp/src/transport/TServerSocket.cpp index 2f6ce5d2..0a480293 100644 --- a/lib/cpp/src/transport/TServerSocket.cpp +++ b/lib/cpp/src/transport/TServerSocket.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -72,7 +73,31 @@ void TServerSocket::listen() { intSock2_ = sv[0]; } - serverSocket_ = socket(AF_INET, SOCK_STREAM, 0); + struct addrinfo hints, *res, *res0; + int error; + char port[sizeof("65536") + 1]; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + sprintf(port, "%d", port_); + + // Wildcard address + error = getaddrinfo(NULL, port, &hints, &res0); + if (error) { + fprintf(stderr, "getaddrinfo %d: %s\n", error, gai_strerror(error)); + close(); + throw TTransportException(TTransportException::NOT_OPEN, "Could not resolve host for server socket."); + } + + // Pick the ipv6 address first since ipv4 addresses can be mapped + // into ipv6 space. + for (res = res0; res; res = res->ai_next) { + if (res->ai_family == AF_INET6 || res->ai_next == NULL) + break; + } + + serverSocket_ = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (serverSocket_ == -1) { GlobalOutput("TServerSocket::listen() socket"); close(); @@ -126,23 +151,20 @@ void TServerSocket::listen() { } // prepare the port information - struct sockaddr_in addr; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port_); - addr.sin_addr.s_addr = INADDR_ANY; - // we may want to try to bind more than once, since SO_REUSEADDR doesn't // always seem to work. The client can configure the retry variables. int retries = 0; do { - if (0 == bind(serverSocket_, (struct sockaddr *)&addr, sizeof(addr))) { + if (0 == bind(serverSocket_, res->ai_addr, res->ai_addrlen)) { break; } // use short circuit evaluation here to only sleep if we need to } while ((retries++ < retryLimit_) && (sleep(retryDelay_) == 0)); + // free addrinfo + freeaddrinfo(res0); + // throw an error if we failed to bind properly if (retries > retryLimit_) { char errbuf[1024]; @@ -208,7 +230,7 @@ shared_ptr TServerSocket::acceptImpl() { } } - struct sockaddr_in clientAddress; + struct sockaddr_storage clientAddress; int size = sizeof(clientAddress); int clientSocket = ::accept(serverSocket_, (struct sockaddr *) &clientAddress, @@ -216,18 +238,18 @@ shared_ptr TServerSocket::acceptImpl() { if (clientSocket < 0) { GlobalOutput("TServerSocket::accept()"); - throw TTransportException(TTransportException::UNKNOWN, "ERROR:" + errno); + throw TTransportException(TTransportException::UNKNOWN, std::string("ERROR:") + sys_errlist[errno]); } // Make sure client socket is blocking int flags = fcntl(clientSocket, F_GETFL, 0); if (flags == -1) { GlobalOutput("TServerSocket::select() fcntl GETFL"); - throw TTransportException(TTransportException::UNKNOWN, "ERROR:" + errno); + throw TTransportException(TTransportException::UNKNOWN, std::string("ERROR:") + sys_errlist[errno]); } if (-1 == fcntl(clientSocket, F_SETFL, flags & ~O_NONBLOCK)) { GlobalOutput("TServerSocket::select() fcntl SETFL"); - throw TTransportException(TTransportException::UNKNOWN, "ERROR:" + errno); + throw TTransportException(TTransportException::UNKNOWN, std::string("ERROR:") + sys_errlist[errno]); } shared_ptr client(new TSocket(clientSocket)); diff --git a/lib/cpp/src/transport/TSocket.cpp b/lib/cpp/src/transport/TSocket.cpp index 42364fa7..711d05df 100644 --- a/lib/cpp/src/transport/TSocket.cpp +++ b/lib/cpp/src/transport/TSocket.cpp @@ -98,22 +98,20 @@ bool TSocket::peek() { if (r == -1) { GlobalOutput("TSocket::peek()"); close(); - throw TTransportException(TTransportException::UNKNOWN, "recv() ERROR:" + errno); + throw TTransportException(TTransportException::UNKNOWN, std::string("recv() ERROR:") + sys_errlist[errno]); } return (r > 0); } -void TSocket::open() { +void TSocket::openConnection(struct addrinfo *res) { if (isOpen()) { throw TTransportException(TTransportException::ALREADY_OPEN); } - - // Create socket - socket_ = socket(AF_INET, SOCK_STREAM, 0); + + socket_ = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (socket_ == -1) { GlobalOutput("TSocket::open() socket"); - close(); - throw TTransportException(TTransportException::NOT_OPEN, "socket() ERROR:" + errno); + throw TTransportException(TTransportException::NOT_OPEN, std::string("socket() ERROR:") + sys_errlist[errno]); } // Send timeout @@ -132,28 +130,6 @@ void TSocket::open() { // No delay setNoDelay(noDelay_); - // Lookup the hostname - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(port_); - - { - // Scope lock on host entry lookup - Synchronized s(s_netdb_monitor); - struct hostent *host_entry = gethostbyname(host_.c_str()); - - if (host_entry == NULL) { - GlobalOutput("TSocket: dns error: failed call to gethostbyname."); - close(); - throw TTransportException(TTransportException::NOT_OPEN, "gethostbyname() failed"); - } - - addr.sin_port = htons(port_); - memcpy(&addr.sin_addr.s_addr, - host_entry->h_addr_list[0], - host_entry->h_length); - } - // Set the socket to be non blocking for connect if a timeout exists int flags = fcntl(socket_, F_GETFL, 0); if (connTimeout_ > 0) { @@ -171,18 +147,17 @@ void TSocket::open() { (int)((connTimeout_%1000)*1000)}; // Connect the socket - int ret = connect(socket_, (struct sockaddr *)&addr, sizeof(addr)); + int ret = connect(socket_, res->ai_addr, res->ai_addrlen); if (ret == 0) { goto done; } if (errno != EINPROGRESS) { - close(); char buff[1024]; sprintf(buff, "TSocket::open() connect %s %d", host_.c_str(), port_); GlobalOutput(buff); - throw TTransportException(TTransportException::NOT_OPEN, "open() ERROR: " + errno); + throw TTransportException(TTransportException::NOT_OPEN, std::string("open() ERROR: ") + sys_errlist[errno]); } fd_set fds; @@ -197,24 +172,20 @@ void TSocket::open() { lon = sizeof(int); int ret2 = getsockopt(socket_, SOL_SOCKET, SO_ERROR, (void *)&val, &lon); if (ret2 == -1) { - close(); GlobalOutput("TSocket::open() getsockopt SO_ERROR"); - throw TTransportException(TTransportException::NOT_OPEN, "open() ERROR: " + errno); + throw TTransportException(TTransportException::NOT_OPEN, std::string("open() ERROR: ") + sys_errlist[errno]); } if (val == 0) { goto done; } - close(); GlobalOutput("TSocket::open() SO_ERROR was set"); - throw TTransportException(TTransportException::NOT_OPEN, "open() ERROR: " + errno); + throw TTransportException(TTransportException::NOT_OPEN, std::string("open() ERROR: ") + sys_errlist[errno]); } else if (ret == 0) { - close(); GlobalOutput("TSocket::open() timeed out"); - throw TTransportException(TTransportException::NOT_OPEN, "open() ERROR: " + errno); + throw TTransportException(TTransportException::NOT_OPEN, std::string("open() ERROR: ") + sys_errlist[errno]); } else { - close(); GlobalOutput("TSocket::open() select error"); - throw TTransportException(TTransportException::NOT_OPEN, "open() ERROR: " + errno); + throw TTransportException(TTransportException::NOT_OPEN, std::string("open() ERROR: ") + sys_errlist[errno]); } done: @@ -222,6 +193,53 @@ void TSocket::open() { fcntl(socket_, F_SETFL, flags); } +void TSocket::open() { + if (isOpen()) { + throw TTransportException(TTransportException::ALREADY_OPEN); + } + + // Validate port number + if (port_ < 0 || port_ > 65536) { + throw TTransportException(TTransportException::NOT_OPEN, "Specified port is invalid"); + } + + struct addrinfo hints, *res, *res0; + int error; + char port[sizeof("65536") + 1]; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + sprintf(port, "%d", port_); + + { + // Scope lock on host entry lookup + Synchronized s(s_netdb_monitor); + error = getaddrinfo(host_.c_str(), port, &hints, &res0); + } + if (error) { + fprintf(stderr, "getaddrinfo %d: %s\n", error, gai_strerror(error)); + close(); + throw TTransportException(TTransportException::NOT_OPEN, "Could not resolve host for client socket."); + } + + // Cycle through all the returned addresses until one + // connects or push the exception up. + for (res = res0; res; res = res->ai_next) { + try { + openConnection(res); + break; + } catch (TTransportException& ttx) { + if (res->ai_next) { + close(); + } else { + close(); + throw; + } + } + } +} + void TSocket::close() { if (socket_ >= 0) { shutdown(socket_, SHUT_RDWR); @@ -355,7 +373,7 @@ void TSocket::write(const uint8_t* buf, uint32_t len) { } GlobalOutput("TSocket::write() send < 0"); - throw TTransportException(TTransportException::UNKNOWN, "ERROR:" + errno); + throw TTransportException(TTransportException::UNKNOWN, std::string("ERROR:") + sys_errlist[errno]); } // Fail on blocked send diff --git a/lib/cpp/src/transport/TSocket.h b/lib/cpp/src/transport/TSocket.h index 515172d5..9d8f082f 100644 --- a/lib/cpp/src/transport/TSocket.h +++ b/lib/cpp/src/transport/TSocket.h @@ -144,6 +144,9 @@ class TSocket : public TTransport { */ TSocket(int socket); + /** connect, called by open */ + void openConnection(struct addrinfo *res); + /** Host to connect to */ std::string host_; -- 2.17.1