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 "safeerrno.h"
00026 #include "safefcntl.h"
00027
00028 #include "tcpclient.h"
00029 #include <xapian/error.h>
00030
00031 #include <string.h>
00032 #ifndef __WIN32__
00033 # include <netdb.h>
00034 # include <netinet/in.h>
00035 # include <netinet/tcp.h>
00036 # include <sys/socket.h>
00037 # include "safesysselect.h"
00038 #endif
00039
00040 #include "utils.h"
00041
00042 std::string
00043 TcpClient::get_tcpcontext(const std::string & hostname, int port)
00044 {
00045 return "remote:tcp(" + hostname + ":" + om_tostring(port) + ")";
00046 }
00047
00048 int
00049 TcpClient::open_socket(const std::string & hostname, int port,
00050 int msecs_timeout_connect)
00051 {
00052
00053
00054
00055
00056 struct hostent *host = gethostbyname(hostname.c_str());
00057
00058 if (host == 0) {
00059 throw Xapian::NetworkError(std::string("Couldn't resolve host ") + hostname,
00060 get_tcpcontext(hostname, port),
00061 #ifdef __WIN32__
00062 socket_errno()
00063 #else
00064
00065
00066
00067
00068
00069 (h_errno < 0 ? errno : -h_errno)
00070 #endif
00071 );
00072 }
00073
00074 int socketfd = socket(PF_INET, SOCK_STREAM, 0);
00075
00076 if (socketfd < 0) {
00077 throw Xapian::NetworkError("Couldn't create socket", get_tcpcontext(hostname, port), socket_errno());
00078 }
00079
00080 struct sockaddr_in remaddr;
00081 remaddr.sin_family = AF_INET;
00082 remaddr.sin_port = htons(port);
00083 memcpy(&remaddr.sin_addr, host->h_addr, sizeof(remaddr.sin_addr));
00084
00085 #ifdef __WIN32__
00086 ULONG enabled = 1;
00087 int rc = ioctlsocket(socketfd, FIONBIO, &enabled);
00088 #else
00089 int rc = fcntl(socketfd, F_SETFL, O_NDELAY);
00090 #endif
00091 if (rc < 0) {
00092 int saved_errno = socket_errno();
00093 close(socketfd);
00094 throw Xapian::NetworkError("Couldn't set O_NDELAY", get_tcpcontext(hostname, port), saved_errno);
00095 }
00096
00097 {
00098 int optval = 1;
00099
00100
00101 if (setsockopt(socketfd, IPPROTO_TCP, TCP_NODELAY,
00102 reinterpret_cast<char *>(&optval),
00103 sizeof(optval)) < 0) {
00104 int saved_errno = socket_errno();
00105 close(socketfd);
00106 throw Xapian::NetworkError("Couldn't set TCP_NODELAY", get_tcpcontext(hostname, port), saved_errno);
00107 }
00108 }
00109
00110 int retval = connect(socketfd, reinterpret_cast<sockaddr *>(&remaddr),
00111 sizeof(remaddr));
00112
00113 if (retval < 0) {
00114 #ifdef __WIN32__
00115 if (WSAGetLastError() != WSAEWOULDBLOCK) {
00116 #else
00117 if (socket_errno() != EINPROGRESS) {
00118 #endif
00119 int saved_errno = socket_errno();
00120 close(socketfd);
00121 throw Xapian::NetworkError("Couldn't connect", get_tcpcontext(hostname, port), saved_errno);
00122 }
00123
00124
00125 fd_set fdset;
00126 FD_ZERO(&fdset);
00127 FD_SET(socketfd, &fdset);
00128
00129 struct timeval tv;
00130 tv.tv_sec = msecs_timeout_connect / 1000;
00131 tv.tv_usec = msecs_timeout_connect % 1000 * 1000;
00132
00133 retval = select(socketfd + 1, 0, &fdset, &fdset, &tv);
00134
00135 if (retval == 0) {
00136 close(socketfd);
00137 throw Xapian::NetworkTimeoutError("Couldn't connect", get_tcpcontext(hostname, port), ETIMEDOUT);
00138 }
00139
00140 int err = 0;
00141 SOCKLEN_T len = sizeof(err);
00142
00143
00144
00145 retval = getsockopt(socketfd, SOL_SOCKET, SO_ERROR,
00146 reinterpret_cast<char *>(&err), &len);
00147
00148 if (retval < 0) {
00149 int saved_errno = socket_errno();
00150 close(socketfd);
00151 throw Xapian::NetworkError("Couldn't get socket options", get_tcpcontext(hostname, port), saved_errno);
00152 }
00153 if (err) {
00154 close(socketfd);
00155 throw Xapian::NetworkError("Couldn't connect", get_tcpcontext(hostname, port), err);
00156 }
00157 }
00158
00159 #ifdef __WIN32__
00160 enabled = 0;
00161 ioctlsocket(socketfd, FIONBIO, &enabled);
00162 #else
00163 fcntl(socketfd, F_SETFL, 0);
00164 #endif
00165 return socketfd;
00166 }
00167
00168 TcpClient::~TcpClient()
00169 {
00170 do_close();
00171 }