xapian-core  1.4.21
tcpserver.cc
Go to the documentation of this file.
1 
4 /* Copyright 1999,2000,2001 BrightStation PLC
5  * Copyright 2002 Ananova Ltd
6  * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2015,2017,2018 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 "tcpserver.h"
27 
28 #include <xapian/error.h>
29 
30 #include "safefcntl.h"
31 #include "safenetdb.h"
32 #include "safesyssocket.h"
33 
34 #include "noreturn.h"
35 #include "remoteconnection.h"
36 #include "resolver.h"
37 #include "str.h"
38 
39 #ifdef __WIN32__
40 # include <process.h> /* _beginthreadex, _endthreadex */
41 #else
42 # include <netinet/in_systm.h>
43 # include <netinet/in.h>
44 # include <netinet/ip.h>
45 # include <netinet/tcp.h>
46 # include <arpa/inet.h>
47 # include <signal.h>
48 # include <sys/wait.h>
49 #endif
50 
51 #include <iostream>
52 
53 #include <cerrno>
54 #include <cstring>
55 #include <cstdlib>
56 #include <sys/types.h>
57 
58 using namespace std;
59 
60 // Handle older systems.
61 #if !defined SIGCHLD && defined SIGCLD
62 # define SIGCHLD SIGCLD
63 #endif
64 
65 #ifdef __WIN32__
66 // We must call closesocket() (instead of just close()) under __WIN32__ or
67 // else the socket remains in the CLOSE_WAIT state.
68 # define CLOSESOCKET(S) closesocket(S)
69 #else
70 # define CLOSESOCKET(S) close(S)
71 #endif
72 
74 TcpServer::TcpServer(const std::string & host, int port, bool tcp_nodelay,
75  bool verbose_)
76  : listen_socket(get_listening_socket(host, port, tcp_nodelay
77 #if defined __CYGWIN__ || defined __WIN32__
78  , mutex
79 #endif
80  )),
81  verbose(verbose_)
82 {
83 }
84 
85 int
86 TcpServer::get_listening_socket(const std::string & host, int port,
87  bool tcp_nodelay
88 #if defined __CYGWIN__ || defined __WIN32__
89  , HANDLE &mutex
90 #endif
91  )
92 {
93  int socketfd = -1;
94  int bind_errno = 0;
95  for (auto&& r : Resolver(host, port, AI_PASSIVE)) {
96  int socktype = r.ai_socktype | SOCK_CLOEXEC;
97  int fd = socket(r.ai_family, socktype, r.ai_protocol);
98  if (fd == -1)
99  continue;
100 
101 #if !defined __WIN32__ && defined F_SETFD && defined FD_CLOEXEC
102  // We can't use a preprocessor check on the *value* of SOCK_CLOEXEC as on
103  // Linux SOCK_CLOEXEC is an enum, with '#define SOCK_CLOEXEC SOCK_CLOEXEC'
104  // to allow '#ifdef SOCK_CLOEXEC' to work.
105  if (SOCK_CLOEXEC == 0)
106  (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
107 #endif
108 
109  int retval = 0;
110 
111  if (tcp_nodelay) {
112  int optval = 1;
113  // 4th argument might need to be void* or char* - cast it to char*
114  // since C++ allows implicit conversion to void* but not from
115  // void*.
116  retval = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
117  reinterpret_cast<char *>(&optval),
118  sizeof(optval));
119  }
120 
121  int optval = 1;
122 #if defined __CYGWIN__ || defined __WIN32__
123  // Windows has screwy semantics for SO_REUSEADDR - it allows the user
124  // to bind to a port which is already bound and listening! That's
125  // just not suitable as we don't want multiple processes listening on
126  // the same port, so we guard against that by using a named win32 mutex
127  // object (and we create it in the 'Global namespace' so that this
128  // still works in a Terminal Services environment).
129  string name = "Global\\xapian-tcpserver-listening-" + str(port);
130  if ((mutex = CreateMutex(NULL, TRUE, name.c_str())) == NULL) {
131  // We failed to create the mutex, probably the error is
132  // ERROR_ACCESS_DENIED, which simply means that TcpServer is
133  // already running on this port but as a different user.
134  } else if (GetLastError() == ERROR_ALREADY_EXISTS) {
135  // The mutex already existed, so TcpServer is already running
136  // on this port.
137  CloseHandle(mutex);
138  mutex = NULL;
139  }
140  if (mutex == NULL) {
141  cerr << "Server is already running on port " << port << endl;
142  // 69 is EX_UNAVAILABLE. Scripts can use this to detect if the
143  // server failed to bind to the requested port.
144  exit(69); // FIXME: calling exit() here isn't ideal...
145  }
146 #endif
147  if (retval >= 0) {
148  retval = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
149  reinterpret_cast<char *>(&optval),
150  sizeof(optval));
151  }
152 
153 #if defined SO_EXCLUSIVEADDRUSE
154  // NT4 sp4 and later offer SO_EXCLUSIVEADDRUSE which nullifies the
155  // security issues from SO_REUSEADDR (which affect *any* listening
156  // process, even if doesn't use SO_REUSEADDR itself). There's still no
157  // way of addressing the issue of not being able to listen on a port
158  // which has closed connections in TIME_WAIT state though.
159  //
160  // Note: SO_EXCLUSIVEADDRUSE requires admin privileges prior to XP SP2.
161  // Because of this and the lack support on older versions, we don't
162  // currently check the return value.
163  if (retval >= 0) {
164  (void)setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
165  reinterpret_cast<char *>(&optval),
166  sizeof(optval));
167  }
168 #endif
169 
170  if (retval < 0) {
171  int saved_errno = socket_errno(); // note down in case close hits an error
172  CLOSESOCKET(fd);
173  throw Xapian::NetworkError("setsockopt failed", saved_errno);
174  }
175 
176  if (::bind(fd, r.ai_addr, r.ai_addrlen) == 0) {
177  socketfd = fd;
178  break;
179  }
180 
181  // Note down the error code for the first address we try, which seems
182  // likely to be more helpful than the last in the case where they
183  // differ.
184  if (bind_errno == 0)
185  bind_errno = socket_errno();
186 
187  CLOSESOCKET(fd);
188  }
189 
190  if (socketfd == -1) {
191  if (bind_errno == EADDRINUSE) {
192  cerr << host << ':' << port << " already in use" << endl;
193  // 69 is EX_UNAVAILABLE. Scripts can use this to detect if the
194  // server failed to bind to the requested port.
195  exit(69); // FIXME: calling exit() here isn't ideal...
196  }
197  if (bind_errno == EACCES) {
198  cerr << "Can't bind to privileged port " << port << endl;
199  // 77 is EX_NOPERM. Scripts can use this to detect if
200  // xapian-tcpsrv failed to bind to the requested port.
201  exit(77); // FIXME: calling exit() here isn't ideal...
202  }
203  throw Xapian::NetworkError("bind failed", bind_errno);
204  }
205 
206  if (listen(socketfd, 5) < 0) {
207  int saved_errno = socket_errno(); // note down in case close hits an error
208  CLOSESOCKET(socketfd);
209  throw Xapian::NetworkError("listen failed", saved_errno);
210  }
211  return socketfd;
212 }
213 
214 int
215 TcpServer::accept_connection()
216 {
217  struct sockaddr_in remote_address;
218  SOCKLEN_T remote_address_size = sizeof(remote_address);
219  // accept connections
220  int con_socket = accept(listen_socket,
221  reinterpret_cast<sockaddr *>(&remote_address),
222  &remote_address_size);
223 
224  if (con_socket < 0) {
225 #ifdef __WIN32__
226  if (WSAGetLastError() == WSAEINTR) {
227  // Our CtrlHandler function closed the socket.
228  if (mutex) CloseHandle(mutex);
229  mutex = NULL;
230  return -1;
231  }
232 #endif
233  throw Xapian::NetworkError("accept failed", socket_errno());
234  }
235 
236  if (remote_address_size != sizeof(remote_address)) {
237  throw Xapian::NetworkError("accept: unexpected remote address size");
238  }
239 
240  if (verbose) {
241  char buf[INET_ADDRSTRLEN];
242 #ifndef __WIN32__
243  // Under __WIN32__, inet_ntop()'s second parameter isn't const for some
244  // reason. We don't currently use inet_ntop() there, but allow for a
245  // non-const second parameter in case it's more widespread.
246  void * src = &remote_address.sin_addr;
247  const char * r = inet_ntop(AF_INET, src, buf, sizeof(buf));
248  if (!r)
249  throw Xapian::NetworkError("inet_ntop failed", errno);
250 #else
251  // inet_ntop() isn't always available, at least with mingw.
252  // WSAAddressToString() supports both IPv4 and IPv6, so just use that.
253  DWORD size = sizeof(buf);
254  if (WSAAddressToString(reinterpret_cast<sockaddr*>(&remote_address),
255  sizeof(remote_address), NULL, buf, &size) != 0) {
256  throw Xapian::NetworkError("WSAAddressToString failed",
257  WSAGetLastError());
258  }
259  const char * r = buf;
260 #endif
261  int port = remote_address.sin_port;
262  cout << "Connection from " << r << ", port " << port << endl;
263  }
264 
265  return con_socket;
266 }
267 
268 TcpServer::~TcpServer()
269 {
270  CLOSESOCKET(listen_socket);
271 #if defined __CYGWIN__ || defined __WIN32__
272  if (mutex) CloseHandle(mutex);
273 #endif
274 }
275 
276 #ifdef HAVE_FORK
277 // A fork() based implementation.
278 
279 extern "C" {
280 
281 XAPIAN_NORETURN(static void on_SIGTERM(int /*sig*/));
282 
283 static void
284 on_SIGTERM(int /*sig*/)
285 {
286  signal(SIGTERM, SIG_DFL);
287  /* terminate all processes in my process group */
288 #ifdef HAVE_KILLPG
289  killpg(0, SIGTERM);
290 #else
291  kill(0, SIGTERM);
292 #endif
293  exit(0);
294 }
295 
296 #ifdef HAVE_WAITPID
297 static void
298 on_SIGCHLD(int /*sig*/)
299 {
300  int status;
301  while (waitpid(-1, &status, WNOHANG) > 0);
302 }
303 #endif
304 
305 }
306 
307 void
308 TcpServer::run()
309 {
310  // Handle connections until shutdown.
311 
312  // Set up signal handlers.
313 #ifdef HAVE_WAITPID
314  signal(SIGCHLD, on_SIGCHLD);
315 #else
316  signal(SIGCHLD, SIG_IGN);
317 #endif
318  signal(SIGTERM, on_SIGTERM);
319 
320  while (true) {
321  try {
322  int connected_socket = accept_connection();
323  pid_t pid = fork();
324  if (pid == 0) {
325  // Child process.
326  close(listen_socket);
327 
328  handle_one_connection(connected_socket);
329  close(connected_socket);
330 
331  if (verbose) cout << "Connection closed." << endl;
332  exit(0);
333  }
334 
335  // Parent process.
336 
337  if (pid < 0) {
338  // fork() failed.
339 
340  // Note down errno from fork() in case close() hits an error.
341  int saved_errno = socket_errno();
342  close(connected_socket);
343  throw Xapian::NetworkError("fork failed", saved_errno);
344  }
345 
346  close(connected_socket);
347  } catch (const Xapian::Error &e) {
348  // FIXME: better error handling.
349  cerr << "Caught " << e.get_description() << endl;
350  } catch (...) {
351  // FIXME: better error handling.
352  cerr << "Caught exception." << endl;
353  }
354  }
355 }
356 
357 #elif defined __WIN32__
358 
359 // A threaded, Windows specific, implementation.
360 
365 static const int *pShutdownSocket = NULL;
366 
368 static BOOL
369 CtrlHandler(DWORD fdwCtrlType)
370 {
371  switch (fdwCtrlType) {
372  case CTRL_C_EVENT:
373  case CTRL_CLOSE_EVENT:
374  // Console is about to die.
375  // CTRL_CLOSE_EVENT gives us 5 seconds before displaying a
376  // confirmation dialog asking if we really are sure.
377  case CTRL_LOGOFF_EVENT:
378  case CTRL_SHUTDOWN_EVENT:
379  // These 2 will probably need to change when we get service
380  // support - the service will prevent these being seen, so only
381  // apply interactively.
382  cout << "Shutting down..." << endl;
383  break; // default behaviour
384  case CTRL_BREAK_EVENT:
385  // This (probably) means the developer is struggling to get
386  // things to behave, and really wants to shutdown so let the OS
387  // handle Ctrl+Break in the default way.
388  cout << "Ctrl+Break: aborting process" << endl;
389  return FALSE;
390  default:
391  cerr << "unexpected CtrlHandler: " << fdwCtrlType << endl;
392  return FALSE;
393  }
394 
395  // Note: close() does not cause a blocking accept() call to terminate.
396  // However, it appears closesocket() does. This is much easier than trying
397  // to setup a non-blocking accept().
398  if (!pShutdownSocket || closesocket(*pShutdownSocket) == SOCKET_ERROR) {
399  // We failed to close the socket, so just let the OS handle the
400  // event in the default way.
401  return FALSE;
402  }
403 
404  pShutdownSocket = NULL;
405  return TRUE; // Tell the OS that we've handled the event.
406 }
407 
409 struct thread_param
410 {
411  thread_param(TcpServer *s, int c) : server(s), connected_socket(c) {}
412  TcpServer *server;
413  int connected_socket;
414 };
415 
417 static unsigned __stdcall
418 run_thread(void * param_)
419 {
420  thread_param * param(reinterpret_cast<thread_param *>(param_));
421  int socket = param->connected_socket;
422 
423  param->server->handle_one_connection(socket);
424  closesocket(socket);
425 
426  delete param;
427 
428  _endthreadex(0);
429  return 0;
430 }
431 
432 void
433 TcpServer::run()
434 {
435  // Handle connections until shutdown.
436 
437  // Set up the shutdown handler - this is a bit hacky, and sadly involves
438  // a global variable.
439  pShutdownSocket = &listen_socket;
440  if (!::SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE))
441  throw Xapian::NetworkError("Failed to install shutdown handler");
442 
443  while (true) {
444  try {
445  int connected_socket = accept_connection();
446  if (connected_socket == -1)
447  return; // Shutdown has happened
448 
449  // Spawn a new thread to handle the connection.
450  // (This seems like lots of hoops just to end up calling
451  // this->handle_one_connection() on a new thread. There might be a
452  // better way...)
453  thread_param *param = new thread_param(this, connected_socket);
454  HANDLE hthread = (HANDLE)_beginthreadex(NULL, 0, ::run_thread, param, 0, NULL);
455  if (hthread == 0) {
456  // errno holds the error code from _beginthreadex, and
457  // closesocket() doesn't set errno.
458  closesocket(connected_socket);
459  throw Xapian::NetworkError("_beginthreadex failed", errno);
460  }
461 
462  // FIXME: keep track of open thread handles so we can gracefully
463  // close each thread down. OTOH, when we want to kill them all its
464  // likely to mean the process is on its way down, so it doesn't
465  // really matter...
466  CloseHandle(hthread);
467  } catch (const Xapian::Error &e) {
468  // FIXME: better error handling.
469  cerr << "Caught " << e.get_description() << endl;
470  } catch (...) {
471  // FIXME: better error handling.
472  cerr << "Caught exception." << endl;
473  }
474  }
475 }
476 
477 #else
478 # error Neither HAVE_FORK nor __WIN32__ are defined.
479 #endif
480 
481 void
482 TcpServer::run_once()
483 {
484  // Run a single request in the current process/thread.
485  int fd = accept_connection();
486  handle_one_connection(fd);
487  CLOSESOCKET(fd);
488 }
int close(FD &fd)
Definition: fd.h:63
Define the XAPIAN_NORETURN macro.
RemoteConnection class used by the remote backend.
#define CLOSESOCKET(S)
Definition: tcpserver.cc:70
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:93
Hierarchy of classes which Xapian can throw as exceptions.
static int verbose
Definition: xapian-delve.cc:47
Resolve hostnames and ip addresses.
string str(int value)
Convert int to std::string.
Definition: str.cc:90
std::string get_description() const
Return a string describing this object.
Definition: error.cc:93
All exceptions thrown by Xapian are subclasses of Xapian::Error.
Definition: error.h:43
Indicates a problem communicating with a remote database.
Definition: error.h:803
Definition: header.h:151
Generic TCP/IP socket based server base class.
if(!(properties &BACKEND))
Definition: api_collated.h:3
int socket_errno()
include <fcntl.h>, but working around broken platforms.