xapian-core  1.4.27
tcpclient.cc
Go to the documentation of this file.
1 
4 /* Copyright 1999,2000,2001 BrightStation PLC
5  * Copyright 2002 Ananova Ltd
6  * Copyright 2004,2005,2006,2007,2008,2010,2012,2013,2015,2017 Olly Betts
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of the
11  * License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21  * USA
22  */
23 
24 #include <config.h>
25 
26 #include "tcpclient.h"
27 
28 #include "remoteconnection.h"
29 #include "resolver.h"
30 #include "str.h"
31 #include <xapian/error.h>
32 
33 #include "realtime.h"
34 #include "safefcntl.h"
35 #include "safenetdb.h"
36 #include "safesyssocket.h"
37 #include "socket_utils.h"
38 
39 #ifdef HAVE_POLL_H
40 # include <poll.h>
41 #else
42 # include "safesysselect.h"
43 #endif
44 
45 #include <cerrno>
46 #include <cstring>
47 #ifndef __WIN32__
48 # include <netinet/in.h>
49 # include <netinet/tcp.h>
50 #endif
51 
52 using namespace std;
53 
54 int
55 TcpClient::open_socket(const std::string & hostname, int port,
56  double timeout_connect, bool tcp_nodelay)
57 {
58  int socketfd = -1;
59  int connect_errno = 0;
60  for (auto&& r : Resolver(hostname, port)) {
61  int socktype = r.ai_socktype | SOCK_CLOEXEC;
62 #ifdef SOCK_NONBLOCK
63  socktype |= SOCK_NONBLOCK;
64 #endif
65  int fd = socket(r.ai_family, socktype, r.ai_protocol);
66  if (fd == -1)
67  continue;
68 
69 #if !defined __WIN32__ && defined F_SETFD && defined FD_CLOEXEC
70  // We can't use a preprocessor check on the *value* of SOCK_CLOEXEC as
71  // on Linux SOCK_CLOEXEC is an enum, with '#define SOCK_CLOEXEC
72  // SOCK_CLOEXEC' to allow '#ifdef SOCK_CLOEXEC' to work.
73  if (SOCK_CLOEXEC == 0)
74  (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
75 #endif
76 
77 #ifndef SOCK_NONBLOCK
78 #ifdef __WIN32__
79  ULONG enabled = 1;
80  int rc = ioctlsocket(fd, FIONBIO, &enabled);
81 #define FLAG_NAME "FIONBIO"
82 #elif defined O_NONBLOCK
83  int rc = fcntl(fd, F_SETFL, O_NONBLOCK);
84 #define FLAG_NAME "O_NONBLOCK"
85 #else
86  int rc = fcntl(fd, F_SETFL, O_NDELAY);
87 #define FLAG_NAME "O_NDELAY"
88 #endif
89  if (rc < 0) {
90  int saved_errno = socket_errno(); // note down in case close hits an error
91  CLOSESOCKET(fd);
92  throw Xapian::NetworkError("Couldn't set " FLAG_NAME, saved_errno);
93 #undef FLAG_NAME
94  }
95 #endif
96 
97  if (tcp_nodelay) {
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
101  // void*.
102  if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
103  reinterpret_cast<char *>(&optval),
104  sizeof(optval)) < 0) {
105  int saved_errno = socket_errno(); // note down in case close hits an error
106  CLOSESOCKET(fd);
107  throw Xapian::NetworkError("Couldn't set TCP_NODELAY", saved_errno);
108  }
109  }
110 
111  int retval = connect(fd, r.ai_addr, r.ai_addrlen);
112  if (retval == 0) {
113  socketfd = fd;
114  break;
115  }
116 
117  int err = socket_errno();
118  if (
119 #ifdef __WIN32__
120  WSAGetLastError() == WSAEWOULDBLOCK
121 #else
122  err == EINPROGRESS
123 #endif
124  ) {
125  // Wait for the socket to be writable or give an error, with a
126  // timeout.
127 #ifdef HAVE_POLL
128  struct pollfd fds;
129  fds.fd = fd;
130  fds.events = POLLOUT;
131  do {
132  retval = poll(&fds, 1, int(timeout_connect * 1000));
133  } while (retval < 0 && (errno == EINTR || errno == EAGAIN));
134 #else
135  fd_set fdset;
136  FD_ZERO(&fdset);
137  do {
138  FD_SET(fd, &fdset);
139  // FIXME: Reduce the timeout if we retry on EINTR.
140  struct timeval tv;
141  RealTime::to_timeval(timeout_connect, &tv);
142  retval = select(fd + 1, 0, &fdset, 0, &tv);
143  } while (retval < 0 && (errno == EINTR || errno == EAGAIN));
144 #endif
145 
146  if (retval <= 0) {
147  int saved_errno = errno;
148  CLOSESOCKET(fd);
149  if (retval < 0)
150  throw Xapian::NetworkError("Couldn't connect (poll() or "
151  "select() on socket failed)",
152  saved_errno);
153  throw Xapian::NetworkTimeoutError("Timed out waiting to connect", ETIMEDOUT);
154  }
155 
156  err = 0;
157  SOCKLEN_T len = sizeof(err);
158 
159  // 4th argument might need to be void* or char* - cast it to char*
160  // since C++ allows implicit conversion to void* but not from void*.
161  retval = getsockopt(fd, SOL_SOCKET, SO_ERROR,
162  reinterpret_cast<char *>(&err), &len);
163 
164  if (retval < 0) {
165  int saved_errno = socket_errno(); // note down in case close hits an error
166  CLOSESOCKET(fd);
167  throw Xapian::NetworkError("Couldn't get socket options", saved_errno);
168  }
169  if (err == 0) {
170  // Connected successfully.
171  socketfd = fd;
172  break;
173  }
174  }
175 
176  // Note down the error code for the first address we try, which seems
177  // likely to be more helpful than the last in the case where they
178  // differ.
179  if (connect_errno == 0)
180  connect_errno = err;
181 
182  // Failed to connect.
183  CLOSESOCKET(fd);
184  }
185 
186  if (socketfd == -1) {
187  throw Xapian::NetworkError("Couldn't connect", connect_errno);
188  }
189 
190 #ifdef __WIN32__
191  ULONG enabled = 0;
192  ioctlsocket(socketfd, FIONBIO, &enabled);
193 #else
194  fcntl(socketfd, F_SETFL, 0);
195 #endif
196  return socketfd;
197 }
Open a TCP connection to a server.
RemoteConnection class used by the remote backend.
include <sys/select.h> with portability workarounds.
Indicates a timeout expired while communicating with a remote database.
Definition: error.h:845
include <sys/socket.h> with portability workarounds.
#define SOCKLEN_T
Definition: config.h:371
STL namespace.
Convert types to std::string.
include <netdb.h>, with portability workarounds.
#define SOCK_CLOEXEC
Definition: safesyssocket.h:83
Hierarchy of classes which Xapian can throw as exceptions.
Resolve hostnames and ip addresses.
Socket handling utilities.
int open_socket(const std::string &hostname, int port, double timeout_connect, bool tcp_nodelay)
Attempt to open a TCP/IP socket connection to a server.
Definition: tcpclient.cc:55
Indicates a problem communicating with a remote database.
Definition: error.h:803
void to_timeval(double t, struct timeval *tv)
Fill in struct timeval from number of seconds in a double.
Definition: realtime.h:110
#define CLOSESOCKET(S)
Functions for handling a time or time interval in a double.
int socket_errno()
include <fcntl.h>, but working around broken platforms.
#define FLAG_NAME