00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <config.h>
00024
00025 #include "remoteconnection.h"
00026 #include "tcpclient.h"
00027 #include <xapian/error.h>
00028
00029 #include "safeerrno.h"
00030 #include "safefcntl.h"
00031 #include "safesysselect.h"
00032 #include "socket_utils.h"
00033
00034 #include <cmath>
00035 #include <cstring>
00036 #ifndef __WIN32__
00037 # include <netdb.h>
00038 # include <netinet/in.h>
00039 # include <netinet/tcp.h>
00040 # include <sys/socket.h>
00041 #endif
00042
00043 using namespace std;
00044
00045 int
00046 TcpClient::open_socket(const std::string & hostname, int port,
00047 double timeout_connect, bool tcp_nodelay)
00048 {
00049
00050 struct hostent *host = gethostbyname(hostname.c_str());
00051
00052 if (host == 0) {
00053 throw Xapian::NetworkError(std::string("Couldn't resolve host ") + hostname,
00054 #ifdef __WIN32__
00055 socket_errno()
00056 #else
00057
00058
00059
00060
00061
00062 (h_errno < 0 ? errno : -h_errno)
00063 #endif
00064 );
00065 }
00066
00067 int socketfd = socket(PF_INET, SOCK_STREAM, 0);
00068 if (socketfd < 0) {
00069 throw Xapian::NetworkError("Couldn't create socket", socket_errno());
00070 }
00071
00072 struct sockaddr_in remaddr;
00073 memset(&remaddr, 0, sizeof(remaddr));
00074 remaddr.sin_family = AF_INET;
00075 remaddr.sin_port = htons(port);
00076 memcpy(&remaddr.sin_addr, host->h_addr, host->h_length);
00077
00078 #ifdef __WIN32__
00079 ULONG enabled = 1;
00080 int rc = ioctlsocket(socketfd, FIONBIO, &enabled);
00081 #else
00082 int rc = fcntl(socketfd, F_SETFL, O_NDELAY);
00083 #endif
00084 if (rc < 0) {
00085 int saved_errno = socket_errno();
00086 close_fd_or_socket(socketfd);
00087 throw Xapian::NetworkError("Couldn't set O_NDELAY", saved_errno);
00088 }
00089
00090 if (tcp_nodelay) {
00091 int optval = 1;
00092
00093
00094 if (setsockopt(socketfd, IPPROTO_TCP, TCP_NODELAY,
00095 reinterpret_cast<char *>(&optval),
00096 sizeof(optval)) < 0) {
00097 int saved_errno = socket_errno();
00098 close_fd_or_socket(socketfd);
00099 throw Xapian::NetworkError("Couldn't set TCP_NODELAY", saved_errno);
00100 }
00101 }
00102
00103 int retval = connect(socketfd, reinterpret_cast<sockaddr *>(&remaddr),
00104 sizeof(remaddr));
00105
00106 if (retval < 0) {
00107 #ifdef __WIN32__
00108 if (WSAGetLastError() != WSAEWOULDBLOCK) {
00109 #else
00110 if (socket_errno() != EINPROGRESS) {
00111 #endif
00112 int saved_errno = socket_errno();
00113 close_fd_or_socket(socketfd);
00114 throw Xapian::NetworkError("Couldn't connect (1)", saved_errno);
00115 }
00116
00117
00118 fd_set fdset;
00119 FD_ZERO(&fdset);
00120 FD_SET(socketfd, &fdset);
00121
00122 do {
00123
00124 struct timeval tv;
00125 tv.tv_sec = long(timeout_connect);
00126 tv.tv_usec = long(std::fmod(timeout_connect, 1.0) * 1e6);
00127
00128 retval = select(socketfd + 1, 0, &fdset, &fdset, &tv);
00129 } while (retval < 0 && errno == EINTR);
00130
00131 if (retval < 0) {
00132 int saved_errno = errno;
00133 close_fd_or_socket(socketfd);
00134 throw Xapian::NetworkError("Couldn't connect (2)", saved_errno);
00135 }
00136
00137 if (retval <= 0) {
00138 close_fd_or_socket(socketfd);
00139 throw Xapian::NetworkTimeoutError("Timed out waiting to connect", ETIMEDOUT);
00140 }
00141
00142 int err = 0;
00143 SOCKLEN_T len = sizeof(err);
00144
00145
00146
00147 retval = getsockopt(socketfd, SOL_SOCKET, SO_ERROR,
00148 reinterpret_cast<char *>(&err), &len);
00149
00150 if (retval < 0) {
00151 int saved_errno = socket_errno();
00152 close_fd_or_socket(socketfd);
00153 throw Xapian::NetworkError("Couldn't get socket options", saved_errno);
00154 }
00155 if (err) {
00156 close_fd_or_socket(socketfd);
00157 throw Xapian::NetworkError("Couldn't connect (3)", err);
00158 }
00159 }
00160
00161 #ifdef __WIN32__
00162 enabled = 0;
00163 ioctlsocket(socketfd, FIONBIO, &enabled);
00164 #else
00165 fcntl(socketfd, F_SETFL, 0);
00166 #endif
00167 return socketfd;
00168 }