Support IPv6 for remote backend and replication
[xapian.git] / xapian-core / net / tcpserver.cc
blobc71ee99e19f23aa7ab5bc4436c4b08a70398e06d
1 /* tcpserver.cc: class for TCP/IP-based server.
3 * Copyright 1999,2000,2001 BrightStation PLC
4 * Copyright 2002 Ananova Ltd
5 * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2015,2017,2018 Olly Betts
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.
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.
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
23 #include <config.h>
25 #include "tcpserver.h"
27 #include <xapian/error.h>
29 #include "safefcntl.h"
30 #include "safenetdb.h"
31 #include "safesyssocket.h"
33 #include "remoteconnection.h"
34 #include "resolver.h"
35 #include "socket_utils.h"
36 #include "str.h"
38 #ifdef __WIN32__
39 # include <process.h> /* _beginthreadex, _endthreadex */
40 #else
41 # include <netinet/in_systm.h>
42 # include <netinet/in.h>
43 # include <netinet/ip.h>
44 # include <netinet/tcp.h>
45 # include <arpa/inet.h>
46 # include <signal.h>
47 # include <sys/wait.h>
48 #endif
50 #include <iostream>
52 #include <cerrno>
53 #include <cstring>
54 #include <cstdlib>
55 #include <sys/types.h>
57 using namespace std;
59 // Handle older systems.
60 #if !defined SIGCHLD && defined SIGCLD
61 # define SIGCHLD SIGCLD
62 #endif
64 #ifdef __WIN32__
65 // We must call closesocket() (instead of just close()) under __WIN32__ or
66 // else the socket remains in the CLOSE_WAIT state.
67 # define CLOSESOCKET(S) closesocket(S)
68 #else
69 # define CLOSESOCKET(S) close(S)
70 #endif
72 /// The TcpServer constructor, taking a database and a listening port.
73 TcpServer::TcpServer(const std::string & host, int port, bool tcp_nodelay,
74 bool verbose_)
76 #if defined __CYGWIN__ || defined __WIN32__
77 mutex(NULL),
78 #endif
79 listen_socket(get_listening_socket(host, port, tcp_nodelay
80 #if defined __CYGWIN__ || defined __WIN32__
81 , mutex
82 #endif
83 )),
84 verbose(verbose_)
88 int
89 TcpServer::get_listening_socket(const std::string & host, int port,
90 bool tcp_nodelay
91 #if defined __CYGWIN__ || defined __WIN32__
92 , HANDLE &mutex
93 #endif
96 int socketfd = -1;
97 int bind_errno = 0;
98 for (auto&& r : Resolver(host, port, AI_PASSIVE)) {
99 int socktype = r.ai_socktype | SOCK_CLOEXEC;
100 int fd = socket(r.ai_family, socktype, r.ai_protocol);
101 if (fd == -1)
102 continue;
104 #if !defined __WIN32__ && defined F_SETFD && defined FD_CLOEXEC
105 // We can't use a preprocessor check on the *value* of SOCK_CLOEXEC as on
106 // Linux SOCK_CLOEXEC is an enum, with '#define SOCK_CLOEXEC SOCK_CLOEXEC'
107 // to allow '#ifdef SOCK_CLOEXEC' to work.
108 if (SOCK_CLOEXEC == 0)
109 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
110 #endif
112 int retval = 0;
114 if (tcp_nodelay) {
115 int optval = 1;
116 // 4th argument might need to be void* or char* - cast it to char*
117 // since C++ allows implicit conversion to void* but not from
118 // void*.
119 retval = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
120 reinterpret_cast<char *>(&optval),
121 sizeof(optval));
124 int optval = 1;
125 #if defined __CYGWIN__ || defined __WIN32__
126 // Windows has screwy semantics for SO_REUSEADDR - it allows the user
127 // to bind to a port which is already bound and listening! That's
128 // just not suitable as we don't want multiple processes listening on
129 // the same port, so we guard against that by using a named win32 mutex
130 // object (and we create it in the 'Global namespace' so that this
131 // still works in a Terminal Services environment).
132 string name = "Global\\xapian-tcpserver-listening-" + str(port);
133 if ((mutex = CreateMutex(NULL, TRUE, name.c_str())) == NULL) {
134 // We failed to create the mutex, probably the error is
135 // ERROR_ACCESS_DENIED, which simply means that TcpServer is
136 // already running on this port but as a different user.
137 } else if (GetLastError() == ERROR_ALREADY_EXISTS) {
138 // The mutex already existed, so TcpServer is already running
139 // on this port.
140 CloseHandle(mutex);
141 mutex = NULL;
143 if (mutex == NULL) {
144 cerr << "Server is already running on port " << port << endl;
145 // 69 is EX_UNAVAILABLE. Scripts can use this to detect if the
146 // server failed to bind to the requested port.
147 exit(69); // FIXME: calling exit() here isn't ideal...
149 #endif
150 if (retval >= 0) {
151 retval = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
152 reinterpret_cast<char *>(&optval),
153 sizeof(optval));
156 #if defined SO_EXCLUSIVEADDRUSE
157 // NT4 sp4 and later offer SO_EXCLUSIVEADDRUSE which nullifies the
158 // security issues from SO_REUSEADDR (which affect *any* listening
159 // process, even if doesn't use SO_REUSEADDR itself). There's still no
160 // way of addressing the issue of not being able to listen on a port
161 // which has closed connections in TIME_WAIT state though.
163 // Note: SO_EXCLUSIVEADDRUSE requires admin privileges prior to XP SP2.
164 // Because of this and the lack support on older versions, we don't
165 // currently check the return value.
166 if (retval >= 0) {
167 (void)setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
168 reinterpret_cast<char *>(&optval),
169 sizeof(optval));
171 #endif
173 if (retval < 0) {
174 int saved_errno = socket_errno(); // note down in case close hits an error
175 CLOSESOCKET(fd);
176 throw Xapian::NetworkError("setsockopt failed", saved_errno);
179 if (::bind(fd, r.ai_addr, r.ai_addrlen) == 0) {
180 socketfd = fd;
181 break;
184 // Note down the error code for the first address we try, which seems
185 // likely to be more helpful than the last in the case where they
186 // differ.
187 if (bind_errno == 0)
188 bind_errno = socket_errno();
190 CLOSESOCKET(fd);
193 if (socketfd == -1) {
194 if (bind_errno == EADDRINUSE) {
195 cerr << host << ':' << port << " already in use" << endl;
196 // 69 is EX_UNAVAILABLE. Scripts can use this to detect if the
197 // server failed to bind to the requested port.
198 exit(69); // FIXME: calling exit() here isn't ideal...
200 if (bind_errno == EACCES) {
201 cerr << "Can't bind to privileged port " << port << endl;
202 // 77 is EX_NOPERM. Scripts can use this to detect if
203 // xapian-tcpsrv failed to bind to the requested port.
204 exit(77); // FIXME: calling exit() here isn't ideal...
206 CLOSESOCKET(socketfd);
207 throw Xapian::NetworkError("bind failed", bind_errno);
210 if (listen(socketfd, 5) < 0) {
211 int saved_errno = socket_errno(); // note down in case close hits an error
212 CLOSESOCKET(socketfd);
213 throw Xapian::NetworkError("listen failed", saved_errno);
215 return socketfd;
219 TcpServer::accept_connection()
221 struct sockaddr_in6 remote_address;
222 SOCKLEN_T remote_address_size = sizeof(remote_address);
223 // accept connections
224 int con_socket = accept(listen_socket,
225 reinterpret_cast<sockaddr *>(&remote_address),
226 &remote_address_size);
228 if (con_socket < 0) {
229 #ifdef __WIN32__
230 if (WSAGetLastError() == WSAEINTR) {
231 // Our CtrlHandler function closed the socket.
232 if (mutex) CloseHandle(mutex);
233 mutex = NULL;
234 return -1;
236 #endif
237 throw Xapian::NetworkError("accept failed", socket_errno());
240 if (verbose) {
241 char host[PRETTY_IP6_LEN];
242 int port = pretty_ip6(&remote_address, host);
243 if (port >= 0) {
244 cout << "Connection from " << host << " port " << port << endl;
245 } else {
246 cout << "Connection from unknown host" << endl;
250 return con_socket;
253 TcpServer::~TcpServer()
255 CLOSESOCKET(listen_socket);
256 #if defined __CYGWIN__ || defined __WIN32__
257 if (mutex) CloseHandle(mutex);
258 #endif
261 #ifdef HAVE_FORK
262 // A fork() based implementation.
264 extern "C" {
266 [[noreturn]]
267 static void
268 on_SIGTERM(int /*sig*/)
270 signal(SIGTERM, SIG_DFL);
271 /* terminate all processes in my process group */
272 #ifdef HAVE_KILLPG
273 killpg(0, SIGTERM);
274 #else
275 kill(0, SIGTERM);
276 #endif
277 exit(0);
280 #ifdef HAVE_WAITPID
281 static void
282 on_SIGCHLD(int /*sig*/)
284 int status;
285 while (waitpid(-1, &status, WNOHANG) > 0);
287 #endif
291 void
292 TcpServer::run()
294 // Handle connections until shutdown.
296 // Set up signal handlers.
297 #ifdef HAVE_WAITPID
298 signal(SIGCHLD, on_SIGCHLD);
299 #else
300 signal(SIGCHLD, SIG_IGN);
301 #endif
302 signal(SIGTERM, on_SIGTERM);
304 while (true) {
305 try {
306 int connected_socket = accept_connection();
307 pid_t pid = fork();
308 if (pid == 0) {
309 // Child process.
310 close(listen_socket);
312 handle_one_connection(connected_socket);
313 close(connected_socket);
315 if (verbose) cout << "Connection closed." << endl;
316 exit(0);
319 // Parent process.
321 if (pid < 0) {
322 // fork() failed.
324 // Note down errno from fork() in case close() hits an error.
325 int saved_errno = socket_errno();
326 close(connected_socket);
327 throw Xapian::NetworkError("fork failed", saved_errno);
330 close(connected_socket);
331 } catch (const Xapian::Error &e) {
332 // FIXME: better error handling.
333 cerr << "Caught " << e.get_description() << endl;
334 } catch (...) {
335 // FIXME: better error handling.
336 cerr << "Caught exception." << endl;
341 #elif defined __WIN32__
343 // A threaded, Windows specific, implementation.
345 /** The socket which will be closed by CtrlHandler.
347 * FIXME - is there any way to avoid using a global variable here?
349 static const int *pShutdownSocket = NULL;
351 /// Console interrupt handler.
352 static BOOL
353 CtrlHandler(DWORD fdwCtrlType)
355 switch (fdwCtrlType) {
356 case CTRL_C_EVENT:
357 case CTRL_CLOSE_EVENT:
358 // Console is about to die.
359 // CTRL_CLOSE_EVENT gives us 5 seconds before displaying a
360 // confirmation dialog asking if we really are sure.
361 case CTRL_LOGOFF_EVENT:
362 case CTRL_SHUTDOWN_EVENT:
363 // These 2 will probably need to change when we get service
364 // support - the service will prevent these being seen, so only
365 // apply interactively.
366 cout << "Shutting down..." << endl;
367 break; // default behaviour
368 case CTRL_BREAK_EVENT:
369 // This (probably) means the developer is struggling to get
370 // things to behave, and really wants to shutdown so let the OS
371 // handle Ctrl+Break in the default way.
372 cout << "Ctrl+Break: aborting process" << endl;
373 return FALSE;
374 default:
375 cerr << "unexpected CtrlHandler: " << fdwCtrlType << endl;
376 return FALSE;
379 // Note: close() does not cause a blocking accept() call to terminate.
380 // However, it appears closesocket() does. This is much easier than trying
381 // to setup a non-blocking accept().
382 if (!pShutdownSocket || closesocket(*pShutdownSocket) == SOCKET_ERROR) {
383 // We failed to close the socket, so just let the OS handle the
384 // event in the default way.
385 return FALSE;
388 pShutdownSocket = NULL;
389 return TRUE; // Tell the OS that we've handled the event.
392 /// Structure which is used to pass parameters to the new threads.
393 struct thread_param
395 thread_param(TcpServer *s, int c) : server(s), connected_socket(c) {}
396 TcpServer *server;
397 int connected_socket;
400 /// The thread entry-point.
401 static unsigned __stdcall
402 run_thread(void * param_)
404 thread_param * param(reinterpret_cast<thread_param *>(param_));
405 int socket = param->connected_socket;
407 param->server->handle_one_connection(socket);
408 closesocket(socket);
410 delete param;
412 _endthreadex(0);
413 return 0;
416 void
417 TcpServer::run()
419 // Handle connections until shutdown.
421 // Set up the shutdown handler - this is a bit hacky, and sadly involves
422 // a global variable.
423 pShutdownSocket = &listen_socket;
424 if (!::SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE))
425 throw Xapian::NetworkError("Failed to install shutdown handler");
427 while (true) {
428 try {
429 int connected_socket = accept_connection();
430 if (connected_socket == -1)
431 return; // Shutdown has happened
433 // Spawn a new thread to handle the connection.
434 // (This seems like lots of hoops just to end up calling
435 // this->handle_one_connection() on a new thread. There might be a
436 // better way...)
437 thread_param *param = new thread_param(this, connected_socket);
438 HANDLE hthread = (HANDLE)_beginthreadex(NULL, 0, ::run_thread, param, 0, NULL);
439 if (hthread == 0) {
440 // errno holds the error code from _beginthreadex, and
441 // closesocket() doesn't set errno.
442 closesocket(connected_socket);
443 throw Xapian::NetworkError("_beginthreadex failed", errno);
446 // FIXME: keep track of open thread handles so we can gracefully
447 // close each thread down. OTOH, when we want to kill them all its
448 // likely to mean the process is on its way down, so it doesn't
449 // really matter...
450 CloseHandle(hthread);
451 } catch (const Xapian::Error &e) {
452 // FIXME: better error handling.
453 cerr << "Caught " << e.get_description() << endl;
454 } catch (...) {
455 // FIXME: better error handling.
456 cerr << "Caught exception." << endl;
461 #else
462 # error Neither HAVE_FORK nor __WIN32__ are defined.
463 #endif
465 void
466 TcpServer::run_once()
468 // Run a single request in the current process/thread.
469 int fd = accept_connection();
470 handle_one_connection(fd);
471 CLOSESOCKET(fd);
474 #ifdef DISABLE_GPL_LIBXAPIAN
475 # error GPL source we cannot relicense included in libxapian
476 #endif