net/tcpclient.cc

Go to the documentation of this file.
00001 /* tcpclient.cc: implementation of NetClient which connects to a remote server.
00002  *
00003  * Copyright 1999,2000,2001 BrightStation PLC
00004  * Copyright 2002 Ananova Ltd
00005  * Copyright 2004,2005,2006,2007 Olly Betts
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License as
00009  * published by the Free Software Foundation; either version 2 of the
00010  * License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
00020  * USA
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     // Note: can't use RemoteDatabase::timeout because it won't yet have
00053     // been initialised.
00054 
00055     // FIXME: timeout on gethostbyname() ?
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                 // "socket_errno()" is just errno on UNIX which is
00065                 // inappropriate here - if gethostbyname() returns NULL an
00066                 // error code is available in h_errno (with values
00067                 // incompatible with errno).  On Linux at least, if h_errno
00068                 // is < 0, then the error code *IS* in errno!
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(); // note down in case close hits an error
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         // 4th argument might need to be void* or char* - cast it to char*
00100         // since C++ allows implicit conversion to void* but not from void*.
00101         if (setsockopt(socketfd, IPPROTO_TCP, TCP_NODELAY,
00102                        reinterpret_cast<char *>(&optval),
00103                        sizeof(optval)) < 0) {
00104             int saved_errno = socket_errno(); // note down in case close hits an error
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(); // note down in case close hits an error
00120             close(socketfd);
00121             throw Xapian::NetworkError("Couldn't connect", get_tcpcontext(hostname, port), saved_errno);
00122         }
00123 
00124         // wait for input to be available.
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         // 4th argument might need to be void* or char* - cast it to char*
00144         // since C++ allows implicit conversion to void* but not from void*.
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(); // note down in case close hits an error
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 }

Documentation for Xapian (version 1.0.10).
Generated on 24 Dec 2008 by Doxygen 1.5.2.