root / tags / 1.0.8 / xapian-core / net / tcpclient.cc

Revision 8245, 5.0 kB (checked in by olly, 21 months ago)

net/tcpclient.cc: Call WSAGetLastError() instead of socket_errno()
when we want to compare the result against WSAEWOULDBLOCK.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* tcpclient.cc: implementation of NetClient which connects to a remote server.
2 *
3 * Copyright 1999,2000,2001 BrightStation PLC
4 * Copyright 2002 Ananova Ltd
5 * Copyright 2004,2005,2006,2007 Olly Betts
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
20 * USA
21 */
22
23#include <config.h>
24
25#include "safeerrno.h"
26#include "safefcntl.h"
27
28#include "tcpclient.h"
29#include <xapian/error.h>
30
31#include <string.h>
32#ifndef __WIN32__
33# include <netdb.h>
34# include <netinet/in.h>
35# include <netinet/tcp.h>
36# include <sys/socket.h>
37# include "safesysselect.h"
38#endif
39
40#include "utils.h"
41
42std::string
43TcpClient::get_tcpcontext(const std::string & hostname, int port)
44{
45    return "remote:tcp(" + hostname + ":" + om_tostring(port) + ")";
46}
47
48int
49TcpClient::open_socket(const std::string & hostname, int port,
50                       int msecs_timeout_connect)
51{
52    // Note: can't use RemoteDatabase::timeout because it won't yet have
53    // been initialised.
54
55    // FIXME: timeout on gethostbyname() ?
56    struct hostent *host = gethostbyname(hostname.c_str());
57
58    if (host == 0) {
59        throw Xapian::NetworkError(std::string("Couldn't resolve host ") + hostname,
60                get_tcpcontext(hostname, port),
61#ifdef __WIN32__
62                socket_errno()
63#else
64                // "socket_errno()" is just errno on UNIX which is
65                // inappropriate here - if gethostbyname() returns NULL an
66                // error code is available in h_errno (with values
67                // incompatible with errno).  On Linux at least, if h_errno
68                // is < 0, then the error code *IS* in errno!
69                (h_errno < 0 ? errno : -h_errno)
70#endif
71                );
72    }
73
74    int socketfd = socket(PF_INET, SOCK_STREAM, 0);
75
76    if (socketfd < 0) {
77        throw Xapian::NetworkError("Couldn't create socket", get_tcpcontext(hostname, port), socket_errno());
78    }
79
80    struct sockaddr_in remaddr;
81    remaddr.sin_family = AF_INET;
82    remaddr.sin_port = htons(port);
83    memcpy(&remaddr.sin_addr, host->h_addr, sizeof(remaddr.sin_addr));
84
85#ifdef __WIN32__
86    ULONG enabled = 1;
87    int rc = ioctlsocket(socketfd, FIONBIO, &enabled);
88#else
89    int rc = fcntl(socketfd, F_SETFL, O_NDELAY);
90#endif
91    if (rc < 0) {
92        int saved_errno = socket_errno(); // note down in case close hits an error
93        close(socketfd);
94        throw Xapian::NetworkError("Couldn't set O_NDELAY", get_tcpcontext(hostname,  port), saved_errno);
95    }
96
97    {
98        int optval = 1;
99        // 4th argument might need to be void* or char* - cast it to char*
100        // since C++ allows implicit conversion to void* but not from void*.
101        if (setsockopt(socketfd, IPPROTO_TCP, TCP_NODELAY,
102                       reinterpret_cast<char *>(&optval),
103                       sizeof(optval)) < 0) {
104            int saved_errno = socket_errno(); // note down in case close hits an error
105            close(socketfd);
106            throw Xapian::NetworkError("Couldn't set TCP_NODELAY", get_tcpcontext(hostname,  port), saved_errno);
107        }
108    }
109
110    int retval = connect(socketfd, reinterpret_cast<sockaddr *>(&remaddr),
111                         sizeof(remaddr));
112
113    if (retval < 0) {
114#ifdef __WIN32__
115        if (WSAGetLastError() != WSAEWOULDBLOCK) {
116#else
117        if (socket_errno() != EINPROGRESS) {
118#endif
119            int saved_errno = socket_errno(); // note down in case close hits an error
120            close(socketfd);
121            throw Xapian::NetworkError("Couldn't connect", get_tcpcontext(hostname, port), saved_errno);
122        }
123
124        // wait for input to be available.
125        fd_set fdset;
126        FD_ZERO(&fdset);
127        FD_SET(socketfd, &fdset);
128
129        struct timeval tv;
130        tv.tv_sec = msecs_timeout_connect / 1000;
131        tv.tv_usec = msecs_timeout_connect % 1000 * 1000;
132
133        retval = select(socketfd + 1, 0, &fdset, &fdset, &tv);
134
135        if (retval == 0) {
136            close(socketfd);
137            throw Xapian::NetworkTimeoutError("Couldn't connect", get_tcpcontext(hostname, port), ETIMEDOUT);
138        }
139
140        int err = 0;
141        SOCKLEN_T len = sizeof(err);
142
143        // 4th argument might need to be void* or char* - cast it to char*
144        // since C++ allows implicit conversion to void* but not from void*.
145        retval = getsockopt(socketfd, SOL_SOCKET, SO_ERROR,
146                            reinterpret_cast<char *>(&err), &len);
147
148        if (retval < 0) {
149            int saved_errno = socket_errno(); // note down in case close hits an error
150            close(socketfd);
151            throw Xapian::NetworkError("Couldn't get socket options", get_tcpcontext(hostname, port), saved_errno);
152        }
153        if (err) {
154            close(socketfd);
155            throw Xapian::NetworkError("Couldn't connect", get_tcpcontext(hostname, port), err);
156        }
157    }
158
159#ifdef __WIN32__
160    enabled = 0;
161    ioctlsocket(socketfd, FIONBIO, &enabled);
162#else
163    fcntl(socketfd, F_SETFL, 0);
164#endif
165    return socketfd;
166}
167
168TcpClient::~TcpClient()
169{
170    do_close();
171}
Note: See TracBrowser for help on using the browser.