| /* | 
 |  * Licensed to the Apache Software Foundation (ASF) under one | 
 |  * or more contributor license agreements. See the NOTICE file | 
 |  * distributed with this work for additional information | 
 |  * regarding copyright ownership. The ASF licenses this file | 
 |  * to you under the Apache License, Version 2.0 (the | 
 |  * "License"); you may not use this file except in compliance | 
 |  * with the License. You may obtain a copy of the License at | 
 |  * | 
 |  *   http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, | 
 |  * software distributed under the License is distributed on an | 
 |  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
 |  * KIND, either express or implied. See the License for the | 
 |  * specific language governing permissions and limitations | 
 |  * under the License. | 
 |  */ | 
 |  | 
 | #ifdef HAVE_CONFIG_H | 
 | #include <config.h> | 
 | #endif | 
 | #include <cstring> | 
 | #include <sstream> | 
 | #ifdef HAVE_SYS_SOCKET_H | 
 | #include <sys/socket.h> | 
 | #endif | 
 | #ifdef HAVE_SYS_UN_H | 
 | #include <sys/un.h> | 
 | #endif | 
 | #ifdef HAVE_SYS_POLL_H | 
 | #include <sys/poll.h> | 
 | #endif | 
 | #include <sys/types.h> | 
 | #ifdef HAVE_ARPA_INET_H | 
 | #include <arpa/inet.h> | 
 | #endif | 
 | #ifdef HAVE_NETINET_IN_H | 
 | #include <netinet/in.h> | 
 | #include <netinet/tcp.h> | 
 | #endif | 
 | #ifdef HAVE_UNISTD_H | 
 | #include <unistd.h> | 
 | #endif | 
 | #include <errno.h> | 
 | #include <fcntl.h> | 
 |  | 
 | #include "concurrency/Monitor.h" | 
 | #include "TSocket.h" | 
 | #include "TTransportException.h" | 
 |  | 
 | #ifndef SOCKOPT_CAST_T | 
 | #   ifndef _WIN32 | 
 | #       define SOCKOPT_CAST_T void | 
 | #   else | 
 | #       define SOCKOPT_CAST_T char | 
 | #   endif // _WIN32 | 
 | #endif | 
 |  | 
 | template<class T> | 
 | inline const SOCKOPT_CAST_T* const_cast_sockopt(const T* v) { | 
 |     return reinterpret_cast<const SOCKOPT_CAST_T*>(v); | 
 | } | 
 |  | 
 | template<class T> | 
 | inline SOCKOPT_CAST_T* cast_sockopt(T* v) { | 
 |     return reinterpret_cast<SOCKOPT_CAST_T*>(v); | 
 | } | 
 |  | 
 | namespace apache { namespace thrift { namespace transport { | 
 |  | 
 | using namespace std; | 
 |  | 
 | // Global var to track total socket sys calls | 
 | uint32_t g_socket_syscalls = 0; | 
 |  | 
 | /** | 
 |  * TSocket implementation. | 
 |  * | 
 |  */ | 
 |  | 
 | TSocket::TSocket(string host, int port) : | 
 |   host_(host), | 
 |   port_(port), | 
 |   path_(""), | 
 |   socket_(-1), | 
 |   connTimeout_(0), | 
 |   sendTimeout_(0), | 
 |   recvTimeout_(0), | 
 |   lingerOn_(1), | 
 |   lingerVal_(0), | 
 |   noDelay_(1), | 
 |   maxRecvRetries_(5) { | 
 |   recvTimeval_.tv_sec = (int)(recvTimeout_/1000); | 
 |   recvTimeval_.tv_usec = (int)((recvTimeout_%1000)*1000); | 
 | } | 
 |  | 
 | TSocket::TSocket(string path) : | 
 |   host_(""), | 
 |   port_(0), | 
 |   path_(path), | 
 |   socket_(-1), | 
 |   connTimeout_(0), | 
 |   sendTimeout_(0), | 
 |   recvTimeout_(0), | 
 |   lingerOn_(1), | 
 |   lingerVal_(0), | 
 |   noDelay_(1), | 
 |   maxRecvRetries_(5) { | 
 |   recvTimeval_.tv_sec = (int)(recvTimeout_/1000); | 
 |   recvTimeval_.tv_usec = (int)((recvTimeout_%1000)*1000); | 
 |   cachedPeerAddr_.ipv4.sin_family = AF_UNSPEC; | 
 | } | 
 |  | 
 | TSocket::TSocket() : | 
 |   host_(""), | 
 |   port_(0), | 
 |   path_(""), | 
 |   socket_(-1), | 
 |   connTimeout_(0), | 
 |   sendTimeout_(0), | 
 |   recvTimeout_(0), | 
 |   lingerOn_(1), | 
 |   lingerVal_(0), | 
 |   noDelay_(1), | 
 |   maxRecvRetries_(5) { | 
 |   recvTimeval_.tv_sec = (int)(recvTimeout_/1000); | 
 |   recvTimeval_.tv_usec = (int)((recvTimeout_%1000)*1000); | 
 |   cachedPeerAddr_.ipv4.sin_family = AF_UNSPEC; | 
 | } | 
 |  | 
 | TSocket::TSocket(int socket) : | 
 |   host_(""), | 
 |   port_(0), | 
 |   path_(""), | 
 |   socket_(socket), | 
 |   connTimeout_(0), | 
 |   sendTimeout_(0), | 
 |   recvTimeout_(0), | 
 |   lingerOn_(1), | 
 |   lingerVal_(0), | 
 |   noDelay_(1), | 
 |   maxRecvRetries_(5) { | 
 |   recvTimeval_.tv_sec = (int)(recvTimeout_/1000); | 
 |   recvTimeval_.tv_usec = (int)((recvTimeout_%1000)*1000); | 
 |   cachedPeerAddr_.ipv4.sin_family = AF_UNSPEC; | 
 | } | 
 |  | 
 | TSocket::~TSocket() { | 
 |   close(); | 
 | } | 
 |  | 
 | bool TSocket::isOpen() { | 
 |   return (socket_ >= 0); | 
 | } | 
 |  | 
 | bool TSocket::peek() { | 
 |   if (!isOpen()) { | 
 |     return false; | 
 |   } | 
 |   uint8_t buf; | 
 |   int r = recv(socket_, cast_sockopt(&buf), 1, MSG_PEEK); | 
 |   if (r == -1) { | 
 |     int errno_copy = errno; | 
 |     #if defined __FreeBSD__ || defined __MACH__ | 
 |     /* shigin: | 
 |      * freebsd returns -1 and ECONNRESET if socket was closed by  | 
 |      * the other side | 
 |      */ | 
 |     if (errno_copy == ECONNRESET) | 
 |     { | 
 |       close(); | 
 |       return false; | 
 |     } | 
 |     #endif | 
 |     GlobalOutput.perror("TSocket::peek() recv() " + getSocketInfo(), errno_copy); | 
 |     throw TTransportException(TTransportException::UNKNOWN, "recv()", errno_copy); | 
 |   } | 
 |   return (r > 0); | 
 | } | 
 |  | 
 | void TSocket::openConnection(struct addrinfo *res) { | 
 |  | 
 |   if (isOpen()) { | 
 |     return; | 
 |   } | 
 |  | 
 |   if (! path_.empty()) { | 
 |     socket_ = socket(PF_UNIX, SOCK_STREAM, IPPROTO_IP); | 
 |   } else { | 
 |     socket_ = socket(res->ai_family, res->ai_socktype, res->ai_protocol); | 
 |   } | 
 |  | 
 |   if (socket_ == -1) { | 
 |     int errno_copy = errno; | 
 |     GlobalOutput.perror("TSocket::open() socket() " + getSocketInfo(), errno_copy); | 
 |     throw TTransportException(TTransportException::NOT_OPEN, "socket()", errno_copy); | 
 |   } | 
 |  | 
 |   // Send timeout | 
 |   if (sendTimeout_ > 0) { | 
 |     setSendTimeout(sendTimeout_); | 
 |   } | 
 |  | 
 |   // Recv timeout | 
 |   if (recvTimeout_ > 0) { | 
 |     setRecvTimeout(recvTimeout_); | 
 |   } | 
 |  | 
 |   // Linger | 
 |   setLinger(lingerOn_, lingerVal_); | 
 |  | 
 |   // No delay | 
 |   setNoDelay(noDelay_); | 
 |  | 
 |   // Uses a low min RTO if asked to. | 
 | #ifdef TCP_LOW_MIN_RTO | 
 |   if (getUseLowMinRto()) { | 
 |     int one = 1; | 
 |     setsockopt(socket_, IPPROTO_TCP, TCP_LOW_MIN_RTO, &one, sizeof(one)); | 
 |   } | 
 | #endif | 
 |  | 
 |  | 
 |   // Set the socket to be non blocking for connect if a timeout exists | 
 |   int flags = fcntl(socket_, F_GETFL, 0); | 
 |   if (connTimeout_ > 0) { | 
 |     if (-1 == fcntl(socket_, F_SETFL, flags | O_NONBLOCK)) { | 
 |       int errno_copy = errno; | 
 |       GlobalOutput.perror("TSocket::open() fcntl() " + getSocketInfo(), errno_copy); | 
 |       throw TTransportException(TTransportException::NOT_OPEN, "fcntl() failed", errno_copy); | 
 |     } | 
 |   } else { | 
 |     if (-1 == fcntl(socket_, F_SETFL, flags & ~O_NONBLOCK)) { | 
 |       int errno_copy = errno; | 
 |       GlobalOutput.perror("TSocket::open() fcntl " + getSocketInfo(), errno_copy); | 
 |       throw TTransportException(TTransportException::NOT_OPEN, "fcntl() failed", errno_copy); | 
 |     } | 
 |   } | 
 |  | 
 |   // Connect the socket | 
 |   int ret; | 
 |   if (! path_.empty()) { | 
 |  | 
 | #ifndef _WIN32 | 
 |  | 
 |     struct sockaddr_un address; | 
 |     socklen_t len; | 
 |  | 
 |     if (path_.length() > sizeof(address.sun_path)) { | 
 |       int errno_copy = errno; | 
 |       GlobalOutput.perror("TSocket::open() Unix Domain socket path too long", errno_copy); | 
 |       throw TTransportException(TTransportException::NOT_OPEN, " Unix Domain socket path too long"); | 
 |     } | 
 |  | 
 |     address.sun_family = AF_UNIX; | 
 |     snprintf(address.sun_path, sizeof(address.sun_path), "%s", path_.c_str()); | 
 |     len = sizeof(address); | 
 |     ret = connect(socket_, (struct sockaddr *) &address, len); | 
 |  | 
 | #else | 
 |       GlobalOutput.perror("TSocket::open() Unix Domain socket path not supported on windows", -99); | 
 |       throw TTransportException(TTransportException::NOT_OPEN, " Unix Domain socket path not supported"); | 
 | #endif | 
 |  | 
 |   } else { | 
 |     ret = connect(socket_, res->ai_addr, res->ai_addrlen); | 
 |   } | 
 |  | 
 |   // success case | 
 |   if (ret == 0) { | 
 |     goto done; | 
 |   } | 
 |  | 
 |   if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK)) { | 
 |     int errno_copy = errno; | 
 |     GlobalOutput.perror("TSocket::open() connect() " + getSocketInfo(), errno_copy); | 
 |     throw TTransportException(TTransportException::NOT_OPEN, "connect() failed", errno_copy); | 
 |   } | 
 |  | 
 |  | 
 |   struct pollfd fds[1]; | 
 |   std::memset(fds, 0 , sizeof(fds)); | 
 |   fds[0].fd = socket_; | 
 |   fds[0].events = POLLOUT; | 
 |   ret = poll(fds, 1, connTimeout_); | 
 |  | 
 |   if (ret > 0) { | 
 |     // Ensure the socket is connected and that there are no errors set | 
 |     int val; | 
 |     socklen_t lon; | 
 |     lon = sizeof(int); | 
 |     int ret2 = getsockopt(socket_, SOL_SOCKET, SO_ERROR, cast_sockopt(&val), &lon); | 
 |     if (ret2 == -1) { | 
 |       int errno_copy = errno; | 
 |       GlobalOutput.perror("TSocket::open() getsockopt() " + getSocketInfo(), errno_copy); | 
 |       throw TTransportException(TTransportException::NOT_OPEN, "getsockopt()", errno_copy); | 
 |     } | 
 |     // no errors on socket, go to town | 
 |     if (val == 0) { | 
 |       goto done; | 
 |     } | 
 |     GlobalOutput.perror("TSocket::open() error on socket (after poll) " + getSocketInfo(), val); | 
 |     throw TTransportException(TTransportException::NOT_OPEN, "socket open() error", val); | 
 |   } else if (ret == 0) { | 
 |     // socket timed out | 
 |     string errStr = "TSocket::open() timed out " + getSocketInfo(); | 
 |     GlobalOutput(errStr.c_str()); | 
 |     throw TTransportException(TTransportException::NOT_OPEN, "open() timed out"); | 
 |   } else { | 
 |     // error on poll() | 
 |     int errno_copy = errno; | 
 |     GlobalOutput.perror("TSocket::open() poll() " + getSocketInfo(), errno_copy); | 
 |     throw TTransportException(TTransportException::NOT_OPEN, "poll() failed", errno_copy); | 
 |   } | 
 |  | 
 |  done: | 
 |   // Set socket back to normal mode (blocking) | 
 |   fcntl(socket_, F_SETFL, flags); | 
 |  | 
 |   if (path_.empty()) { | 
 |     setCachedAddress(res->ai_addr, res->ai_addrlen); | 
 |   } | 
 | } | 
 |  | 
 | void TSocket::open() { | 
 |   if (isOpen()) { | 
 |     return; | 
 |   } | 
 |   if (! path_.empty()) { | 
 |     unix_open(); | 
 |   } else { | 
 |     local_open(); | 
 |   } | 
 | } | 
 |  | 
 | void TSocket::unix_open(){ | 
 |   if (! path_.empty()) { | 
 |     // Unix Domain SOcket does not need addrinfo struct, so we pass NULL | 
 |     openConnection(NULL); | 
 |   } | 
 | } | 
 |  | 
 | void TSocket::local_open(){ | 
 |  | 
 | #ifdef _WIN32 | 
 |     TWinsockSingleton::create(); | 
 | #endif // _WIN32 | 
 |  | 
 |   if (isOpen()) { | 
 |     return; | 
 |   } | 
 |  | 
 |   // Validate port number | 
 |   if (port_ < 0 || port_ > 0xFFFF) { | 
 |     throw TTransportException(TTransportException::NOT_OPEN, "Specified port is invalid"); | 
 |   } | 
 |  | 
 |   struct addrinfo hints, *res, *res0; | 
 |   res = NULL; | 
 |   res0 = NULL; | 
 |   int error; | 
 |   char port[sizeof("65535")]; | 
 |   std::memset(&hints, 0, sizeof(hints)); | 
 |   hints.ai_family = PF_UNSPEC; | 
 |   hints.ai_socktype = SOCK_STREAM; | 
 |   hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; | 
 |   sprintf(port, "%d", port_); | 
 |  | 
 |   error = getaddrinfo(host_.c_str(), port, &hints, &res0); | 
 |  | 
 |   if (error) { | 
 |     string errStr = "TSocket::open() getaddrinfo() " + getSocketInfo() + string(gai_strerror(error)); | 
 |     GlobalOutput(errStr.c_str()); | 
 |     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(); | 
 |         freeaddrinfo(res0); // cleanup on failure | 
 |         throw; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // Free address structure memory | 
 |   freeaddrinfo(res0); | 
 | } | 
 |  | 
 | void TSocket::close() { | 
 |   if (socket_ >= 0) { | 
 |  | 
 | #ifdef _WIN32 | 
 |       shutdown(socket_, SD_BOTH); | 
 |       ::closesocket(socket_); | 
 | #else | 
 |       shutdown(socket_, SHUT_RDWR); | 
 |       ::close(socket_); | 
 | #endif | 
 |  | 
 |   } | 
 |   socket_ = -1; | 
 | } | 
 |  | 
 | void TSocket::setSocketFD(int socket) { | 
 |   if (socket_ >= 0) { | 
 |     close(); | 
 |   } | 
 |   socket_ = socket; | 
 | } | 
 |  | 
 | uint32_t TSocket::read(uint8_t* buf, uint32_t len) { | 
 |   if (socket_ < 0) { | 
 |     throw TTransportException(TTransportException::NOT_OPEN, "Called read on non-open socket"); | 
 |   } | 
 |  | 
 |   int32_t retries = 0; | 
 |  | 
 |   // EAGAIN can be signalled both when a timeout has occurred and when | 
 |   // the system is out of resources (an awesome undocumented feature). | 
 |   // The following is an approximation of the time interval under which | 
 |   // EAGAIN is taken to indicate an out of resources error. | 
 |   uint32_t eagainThresholdMicros = 0; | 
 |   if (recvTimeout_) { | 
 |     // if a readTimeout is specified along with a max number of recv retries, then | 
 |     // the threshold will ensure that the read timeout is not exceeded even in the | 
 |     // case of resource errors | 
 |     eagainThresholdMicros = (recvTimeout_*1000)/ ((maxRecvRetries_>0) ? maxRecvRetries_ : 2); | 
 |   } | 
 |  | 
 |  try_again: | 
 |   // Read from the socket | 
 |   struct timeval begin; | 
 |   if (recvTimeout_ > 0) { | 
 |     gettimeofday(&begin, NULL); | 
 |   } else { | 
 |     // if there is no read timeout we don't need the TOD to determine whether | 
 |     // an EAGAIN is due to a timeout or an out-of-resource condition. | 
 |     begin.tv_sec = begin.tv_usec = 0; | 
 |   } | 
 |   int got = recv(socket_, cast_sockopt(buf), len, 0); | 
 |   int errno_copy = errno; //gettimeofday can change errno | 
 |   ++g_socket_syscalls; | 
 |  | 
 |   // Check for error on read | 
 |   if (got < 0) { | 
 |     if (errno_copy == EAGAIN) { | 
 |       // if no timeout we can assume that resource exhaustion has occurred. | 
 |       if (recvTimeout_ == 0) { | 
 |         throw TTransportException(TTransportException::TIMED_OUT, | 
 |                                     "EAGAIN (unavailable resources)"); | 
 |       } | 
 |       // check if this is the lack of resources or timeout case | 
 |       struct timeval end; | 
 |       gettimeofday(&end, NULL); | 
 |       uint32_t readElapsedMicros =  (((end.tv_sec - begin.tv_sec) * 1000 * 1000) | 
 |                                      + (((uint64_t)(end.tv_usec - begin.tv_usec)))); | 
 |  | 
 |       if (!eagainThresholdMicros || (readElapsedMicros < eagainThresholdMicros)) { | 
 |         if (retries++ < maxRecvRetries_) { | 
 |           usleep(50); | 
 |           goto try_again; | 
 |         } else { | 
 |           throw TTransportException(TTransportException::TIMED_OUT, | 
 |                                     "EAGAIN (unavailable resources)"); | 
 |         } | 
 |       } else { | 
 |         // infer that timeout has been hit | 
 |         throw TTransportException(TTransportException::TIMED_OUT, | 
 |                                   "EAGAIN (timed out)"); | 
 |       } | 
 |     } | 
 |  | 
 |     // If interrupted, try again | 
 |     if (errno_copy == EINTR && retries++ < maxRecvRetries_) { | 
 |       goto try_again; | 
 |     } | 
 |  | 
 |     #if defined __FreeBSD__ || defined __MACH__ | 
 |     if (errno_copy == ECONNRESET) { | 
 |       /* shigin: freebsd doesn't follow POSIX semantic of recv and fails with | 
 |        * ECONNRESET if peer performed shutdown  | 
 |        * edhall: eliminated close() since we do that in the destructor. | 
 |        */ | 
 |       return 0; | 
 |     } | 
 |     #endif | 
 |  | 
 | #ifdef _WIN32 | 
 |     if(errno_copy == WSAECONNRESET) { | 
 |       return 0; // EOF | 
 |     } | 
 | #endif | 
 |  | 
 |     // Now it's not a try again case, but a real probblez | 
 |     GlobalOutput.perror("TSocket::read() recv() " + getSocketInfo(), errno_copy); | 
 |  | 
 |     // If we disconnect with no linger time | 
 |     if (errno_copy == ECONNRESET) { | 
 |       throw TTransportException(TTransportException::NOT_OPEN, "ECONNRESET"); | 
 |     } | 
 |  | 
 |     // This ish isn't open | 
 |     if (errno_copy == ENOTCONN) { | 
 |       throw TTransportException(TTransportException::NOT_OPEN, "ENOTCONN"); | 
 |     } | 
 |  | 
 |     // Timed out! | 
 |     if (errno_copy == ETIMEDOUT) { | 
 |       throw TTransportException(TTransportException::TIMED_OUT, "ETIMEDOUT"); | 
 |     } | 
 |  | 
 |     // Some other error, whatevz | 
 |     throw TTransportException(TTransportException::UNKNOWN, "Unknown", errno_copy); | 
 |   } | 
 |  | 
 |   // The remote host has closed the socket | 
 |   if (got == 0) { | 
 |     // edhall: we used to call close() here, but our caller may want to deal | 
 |     // with the socket fd and we'll close() in our destructor in any case. | 
 |     return 0; | 
 |   } | 
 |  | 
 |   // Pack data into string | 
 |   return got; | 
 | } | 
 |  | 
 | void TSocket::write(const uint8_t* buf, uint32_t len) { | 
 |   uint32_t sent = 0; | 
 |  | 
 |   while (sent < len) { | 
 |     uint32_t b = write_partial(buf + sent, len - sent); | 
 |     if (b == 0) { | 
 |       // This should only happen if the timeout set with SO_SNDTIMEO expired. | 
 |       // Raise an exception. | 
 |       throw TTransportException(TTransportException::TIMED_OUT, | 
 |                                 "send timeout expired"); | 
 |     } | 
 |     sent += b; | 
 |   } | 
 | } | 
 |  | 
 | uint32_t TSocket::write_partial(const uint8_t* buf, uint32_t len) { | 
 |   if (socket_ < 0) { | 
 |     throw TTransportException(TTransportException::NOT_OPEN, "Called write on non-open socket"); | 
 |   } | 
 |  | 
 |   uint32_t sent = 0; | 
 |  | 
 |   int flags = 0; | 
 | #ifdef MSG_NOSIGNAL | 
 |   // Note the use of MSG_NOSIGNAL to suppress SIGPIPE errors, instead we | 
 |   // check for the EPIPE return condition and close the socket in that case | 
 |   flags |= MSG_NOSIGNAL; | 
 | #endif // ifdef MSG_NOSIGNAL | 
 |  | 
 |   int b = send(socket_, const_cast_sockopt(buf + sent), len - sent, flags); | 
 |   ++g_socket_syscalls; | 
 |  | 
 |   if (b < 0) { | 
 |     if (errno == EWOULDBLOCK || errno == EAGAIN) { | 
 |       return 0; | 
 |     } | 
 |     // Fail on a send error | 
 |     int errno_copy = errno; | 
 |     GlobalOutput.perror("TSocket::write_partial() send() " + getSocketInfo(), errno_copy); | 
 |  | 
 |     if (errno_copy == EPIPE || errno_copy == ECONNRESET || errno_copy == ENOTCONN) { | 
 |       close(); | 
 |       throw TTransportException(TTransportException::NOT_OPEN, "write() send()", errno_copy); | 
 |     } | 
 |  | 
 |     throw TTransportException(TTransportException::UNKNOWN, "write() send()", errno_copy); | 
 |   } | 
 |    | 
 |   // Fail on blocked send | 
 |   if (b == 0) { | 
 |     throw TTransportException(TTransportException::NOT_OPEN, "Socket send returned 0."); | 
 |   } | 
 |   return b; | 
 | } | 
 |  | 
 | std::string TSocket::getHost() { | 
 |   return host_; | 
 | } | 
 |  | 
 | int TSocket::getPort() { | 
 |   return port_; | 
 | } | 
 |  | 
 | void TSocket::setHost(string host) { | 
 |   host_ = host; | 
 | } | 
 |  | 
 | void TSocket::setPort(int port) { | 
 |   port_ = port; | 
 | } | 
 |  | 
 | void TSocket::setLinger(bool on, int linger) { | 
 |   lingerOn_ = on; | 
 |   lingerVal_ = linger; | 
 |   if (socket_ < 0) { | 
 |     return; | 
 |   } | 
 |  | 
 |   struct linger l = {(lingerOn_ ? 1 : 0), lingerVal_}; | 
 |   int ret = setsockopt(socket_, SOL_SOCKET, SO_LINGER, cast_sockopt(&l), sizeof(l)); | 
 |   if (ret == -1) { | 
 |     int errno_copy = errno;  // Copy errno because we're allocating memory. | 
 |     GlobalOutput.perror("TSocket::setLinger() setsockopt() " + getSocketInfo(), errno_copy); | 
 |   } | 
 | } | 
 |  | 
 | void TSocket::setNoDelay(bool noDelay) { | 
 |   noDelay_ = noDelay; | 
 |   if (socket_ < 0 || !path_.empty()) { | 
 |     return; | 
 |   } | 
 |  | 
 |   // Set socket to NODELAY | 
 |   int v = noDelay_ ? 1 : 0; | 
 |   int ret = setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, cast_sockopt(&v), sizeof(v)); | 
 |   if (ret == -1) { | 
 |     int errno_copy = errno;  // Copy errno because we're allocating memory. | 
 |     GlobalOutput.perror("TSocket::setNoDelay() setsockopt() " + getSocketInfo(), errno_copy); | 
 |   } | 
 | } | 
 |  | 
 | void TSocket::setConnTimeout(int ms) { | 
 |   connTimeout_ = ms; | 
 | } | 
 |  | 
 | void TSocket::setRecvTimeout(int ms) { | 
 |   if (ms < 0) { | 
 |     char errBuf[512]; | 
 |     sprintf(errBuf, "TSocket::setRecvTimeout with negative input: %d\n", ms); | 
 |     GlobalOutput(errBuf); | 
 |     return; | 
 |   } | 
 |   recvTimeout_ = ms; | 
 |  | 
 |   if (socket_ < 0) { | 
 |     return; | 
 |   } | 
 |  | 
 |   recvTimeval_.tv_sec = (int)(recvTimeout_/1000); | 
 |   recvTimeval_.tv_usec = (int)((recvTimeout_%1000)*1000); | 
 |  | 
 |   // Copy because poll may modify | 
 |   struct timeval r = recvTimeval_; | 
 |   int ret = setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, cast_sockopt(&r), sizeof(r)); | 
 |   if (ret == -1) { | 
 |     int errno_copy = errno;  // Copy errno because we're allocating memory. | 
 |     GlobalOutput.perror("TSocket::setRecvTimeout() setsockopt() " + getSocketInfo(), errno_copy); | 
 |   } | 
 | } | 
 |  | 
 | void TSocket::setSendTimeout(int ms) { | 
 |   if (ms < 0) { | 
 |     char errBuf[512]; | 
 |     sprintf(errBuf, "TSocket::setSendTimeout with negative input: %d\n", ms); | 
 |     GlobalOutput(errBuf); | 
 |     return; | 
 |   } | 
 |   sendTimeout_ = ms; | 
 |  | 
 |   if (socket_ < 0) { | 
 |     return; | 
 |   } | 
 |  | 
 |   struct timeval s = {(int)(sendTimeout_/1000), | 
 |                       (int)((sendTimeout_%1000)*1000)}; | 
 |   int ret = setsockopt(socket_, SOL_SOCKET, SO_SNDTIMEO, cast_sockopt(&s), sizeof(s)); | 
 |   if (ret == -1) { | 
 |     int errno_copy = errno;  // Copy errno because we're allocating memory. | 
 |     GlobalOutput.perror("TSocket::setSendTimeout() setsockopt() " + getSocketInfo(), errno_copy); | 
 |   } | 
 | } | 
 |  | 
 | void TSocket::setMaxRecvRetries(int maxRecvRetries) { | 
 |   maxRecvRetries_ = maxRecvRetries; | 
 | } | 
 |  | 
 | string TSocket::getSocketInfo() { | 
 |   std::ostringstream oss; | 
 |   if (host_.empty() || port_ == 0) { | 
 |     oss << "<Host: " << getPeerAddress(); | 
 |     oss << " Port: " << getPeerPort() << ">"; | 
 |   } else { | 
 |     oss << "<Host: " << host_ << " Port: " << port_ << ">"; | 
 |   } | 
 |   return oss.str(); | 
 | } | 
 |  | 
 | std::string TSocket::getPeerHost() { | 
 |   if (peerHost_.empty() && path_.empty()) { | 
 |     struct sockaddr_storage addr; | 
 |     struct sockaddr* addrPtr; | 
 |     socklen_t addrLen; | 
 |  | 
 |     if (socket_ < 0) { | 
 |       return host_; | 
 |     } | 
 |  | 
 |     addrPtr = getCachedAddress(&addrLen); | 
 |  | 
 |     if (addrPtr == NULL) { | 
 |       addrLen = sizeof(addr); | 
 |       if (getpeername(socket_, (sockaddr*) &addr, &addrLen) != 0) { | 
 |         return peerHost_; | 
 |       } | 
 |       addrPtr = (sockaddr*)&addr; | 
 |  | 
 |       setCachedAddress(addrPtr, addrLen); | 
 |     } | 
 |  | 
 |     char clienthost[NI_MAXHOST]; | 
 |     char clientservice[NI_MAXSERV]; | 
 |  | 
 |     getnameinfo((sockaddr*) addrPtr, addrLen, | 
 |                 clienthost, sizeof(clienthost), | 
 |                 clientservice, sizeof(clientservice), 0); | 
 |  | 
 |     peerHost_ = clienthost; | 
 |   } | 
 |   return peerHost_; | 
 | } | 
 |  | 
 | std::string TSocket::getPeerAddress() { | 
 |   if (peerAddress_.empty() && path_.empty()) { | 
 |     struct sockaddr_storage addr; | 
 |     struct sockaddr* addrPtr; | 
 |     socklen_t addrLen; | 
 |  | 
 |     if (socket_ < 0) { | 
 |       return peerAddress_; | 
 |     } | 
 |  | 
 |     addrPtr = getCachedAddress(&addrLen); | 
 |  | 
 |     if (addrPtr == NULL) { | 
 |       addrLen = sizeof(addr); | 
 |       if (getpeername(socket_, (sockaddr*) &addr, &addrLen) != 0) { | 
 |         return peerAddress_; | 
 |       } | 
 |       addrPtr = (sockaddr*)&addr; | 
 |  | 
 |       setCachedAddress(addrPtr, addrLen); | 
 |     } | 
 |  | 
 |     char clienthost[NI_MAXHOST]; | 
 |     char clientservice[NI_MAXSERV]; | 
 |  | 
 |     getnameinfo(addrPtr, addrLen, | 
 |                 clienthost, sizeof(clienthost), | 
 |                 clientservice, sizeof(clientservice), | 
 |                 NI_NUMERICHOST|NI_NUMERICSERV); | 
 |  | 
 |     peerAddress_ = clienthost; | 
 |     peerPort_ = std::atoi(clientservice); | 
 |   } | 
 |   return peerAddress_; | 
 | } | 
 |  | 
 | int TSocket::getPeerPort() { | 
 |   getPeerAddress(); | 
 |   return peerPort_; | 
 | } | 
 |  | 
 | void TSocket::setCachedAddress(const sockaddr* addr, socklen_t len) { | 
 |   if (!path_.empty()) { | 
 |     return; | 
 |   } | 
 |  | 
 |   switch (addr->sa_family) { | 
 |   case AF_INET: | 
 |     if (len == sizeof(sockaddr_in)) { | 
 |       memcpy((void*)&cachedPeerAddr_.ipv4, (void*)addr, len); | 
 |     } | 
 |     break; | 
 |  | 
 |   case AF_INET6: | 
 |     if (len == sizeof(sockaddr_in6)) { | 
 |       memcpy((void*)&cachedPeerAddr_.ipv6, (void*)addr, len); | 
 |     } | 
 |     break; | 
 |   } | 
 | } | 
 |  | 
 | sockaddr* TSocket::getCachedAddress(socklen_t* len) const { | 
 |   switch (cachedPeerAddr_.ipv4.sin_family) { | 
 |   case AF_INET: | 
 |     *len = sizeof(sockaddr_in); | 
 |     return (sockaddr*) &cachedPeerAddr_.ipv4; | 
 |  | 
 |   case AF_INET6: | 
 |     *len = sizeof(sockaddr_in6); | 
 |     return (sockaddr*) &cachedPeerAddr_.ipv6; | 
 |  | 
 |   default: | 
 |     return NULL; | 
 |   } | 
 | }  | 
 |  | 
 | bool TSocket::useLowMinRto_ = false; | 
 | void TSocket::setUseLowMinRto(bool useLowMinRto) { | 
 |   useLowMinRto_ = useLowMinRto; | 
 | } | 
 | bool TSocket::getUseLowMinRto() { | 
 |   return useLowMinRto_; | 
 | } | 
 |  | 
 | }}} // apache::thrift::transport |