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 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
25 #include "tcpserver.h"
27 #include <xapian/error.h>
29 #include "safeerrno.h"
30 #include "safefcntl.h"
31 #include "safenetdb.h"
32 #include "safesyssocket.h"
34 #include "remoteconnection.h"
39 # include <process.h> /* _beginthreadex, _endthreadex */
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>
47 # include <sys/wait.h>
53 #include <cstdio> // For sprintf() on __WIN32__ or cygwin.
55 #include <sys/types.h>
59 // Handle older systems.
60 #if !defined SIGCHLD && defined SIGCLD
61 # define SIGCHLD SIGCLD
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)
69 # define CLOSESOCKET(S) close(S)
72 /// The TcpServer constructor, taking a database and a listening port.
73 TcpServer::TcpServer(const std::string
& host
, int port
, bool tcp_nodelay
,
76 #if defined __CYGWIN__ || defined __WIN32__
79 listen_socket(get_listening_socket(host
, port
, tcp_nodelay
80 #if defined __CYGWIN__ || defined __WIN32__
89 TcpServer::get_listening_socket(const std::string
& host
, int port
,
91 #if defined __CYGWIN__ || defined __WIN32__
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
);
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
);
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
119 retval
= setsockopt(fd
, IPPROTO_TCP
, TCP_NODELAY
,
120 reinterpret_cast<char *>(&optval
),
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).
133 sprintf(name
, "Global\\xapian-tcpserver-listening-%d", port
);
134 if ((mutex
= CreateMutex(NULL
, TRUE
, name
)) == NULL
) {
135 // We failed to create the mutex, probably the error is
136 // ERROR_ACCESS_DENIED, which simply means that TcpServer is
137 // already running on this port but as a different user.
138 } else if (GetLastError() == ERROR_ALREADY_EXISTS
) {
139 // The mutex already existed, so TcpServer is already running
145 cerr
<< "Server is already running on port " << port
<< endl
;
146 // 69 is EX_UNAVAILABLE. Scripts can use this to detect if the
147 // server failed to bind to the requested port.
148 exit(69); // FIXME: calling exit() here isn't ideal...
152 retval
= setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
,
153 reinterpret_cast<char *>(&optval
),
157 #if defined SO_EXCLUSIVEADDRUSE
158 // NT4 sp4 and later offer SO_EXCLUSIVEADDRUSE which nullifies the
159 // security issues from SO_REUSEADDR (which affect *any* listening
160 // process, even if doesn't use SO_REUSEADDR itself). There's still no
161 // way of addressing the issue of not being able to listen on a port
162 // which has closed connections in TIME_WAIT state though.
164 // Note: SO_EXCLUSIVEADDRUSE requires admin privileges prior to XP SP2.
165 // Because of this and the lack support on older versions, we don't
166 // currently check the return value.
168 (void)setsockopt(fd
, SOL_SOCKET
, SO_EXCLUSIVEADDRUSE
,
169 reinterpret_cast<char *>(&optval
),
175 int saved_errno
= socket_errno(); // note down in case close hits an error
177 throw Xapian::NetworkError("setsockopt failed", saved_errno
);
180 if (::bind(fd
, r
.ai_addr
, r
.ai_addrlen
) == 0) {
185 // Note down the error code for the first address we try, which seems
186 // likely to be more helpful than the last in the case where they
189 bind_errno
= socket_errno();
194 if (socketfd
== -1) {
195 if (bind_errno
== EADDRINUSE
) {
196 cerr
<< host
<< ':' << port
<< " already in use" << endl
;
197 // 69 is EX_UNAVAILABLE. Scripts can use this to detect if the
198 // server failed to bind to the requested port.
199 exit(69); // FIXME: calling exit() here isn't ideal...
201 if (bind_errno
== EACCES
) {
202 cerr
<< "Can't bind to privileged port " << port
<< endl
;
203 // 77 is EX_NOPERM. Scripts can use this to detect if
204 // xapian-tcpsrv failed to bind to the requested port.
205 exit(77); // FIXME: calling exit() here isn't ideal...
207 CLOSESOCKET(socketfd
);
208 throw Xapian::NetworkError("bind failed", bind_errno
);
211 if (listen(socketfd
, 5) < 0) {
212 int saved_errno
= socket_errno(); // note down in case close hits an error
213 CLOSESOCKET(socketfd
);
214 throw Xapian::NetworkError("listen failed", saved_errno
);
220 TcpServer::accept_connection()
222 struct sockaddr_in remote_address
;
223 SOCKLEN_T remote_address_size
= sizeof(remote_address
);
224 // accept connections
225 int con_socket
= accept(listen_socket
,
226 reinterpret_cast<sockaddr
*>(&remote_address
),
227 &remote_address_size
);
229 if (con_socket
< 0) {
231 if (WSAGetLastError() == WSAEINTR
) {
232 // Our CtrlHandler function closed the socket.
233 if (mutex
) CloseHandle(mutex
);
238 throw Xapian::NetworkError("accept failed", socket_errno());
241 if (remote_address_size
!= sizeof(remote_address
)) {
242 throw Xapian::NetworkError("accept: unexpected remote address size");
246 char buf
[INET_ADDRSTRLEN
];
248 // Under __WIN32__, inet_ntop()'s second parameter isn't const for some
249 // reason. We don't currently use inet_ntop() there, but allow for a
250 // non-const second parameter in case it's more widespread.
251 void * src
= &remote_address
.sin_addr
;
252 const char * r
= inet_ntop(AF_INET
, src
, buf
, sizeof(buf
));
254 throw Xapian::NetworkError("inet_ntop failed", errno
);
256 // inet_ntop() isn't always available, at least with mingw.
257 // WSAAddressToString() supports both IPv4 and IPv6, so just use that.
258 DWORD size
= sizeof(buf
);
259 if (WSAAddressToString(reinterpret_cast<sockaddr
*>(&remote_address
),
260 sizeof(remote_address
), NULL
, buf
, &size
) != 0) {
261 throw Xapian::NetworkError("WSAAddressToString failed",
264 const char * r
= buf
;
266 int port
= remote_address
.sin_port
;
267 cout
<< "Connection from " << r
<< ", port " << port
<< endl
;
273 TcpServer::~TcpServer()
275 CLOSESOCKET(listen_socket
);
276 #if defined __CYGWIN__ || defined __WIN32__
277 if (mutex
) CloseHandle(mutex
);
282 // A fork() based implementation.
284 TcpServer::run_once()
286 int connected_socket
= accept_connection();
290 close(listen_socket
);
292 handle_one_connection(connected_socket
);
293 close(connected_socket
);
295 if (verbose
) cout
<< "Connection closed." << endl
;
303 int saved_errno
= socket_errno(); // note down in case close hits an error
304 close(connected_socket
);
305 throw Xapian::NetworkError("fork failed", saved_errno
);
308 close(connected_socket
);
315 on_SIGTERM(int /*sig*/)
317 signal(SIGTERM
, SIG_DFL
);
318 /* terminate all processes in my process group */
329 on_SIGCHLD(int /*sig*/)
332 while (waitpid(-1, &status
, WNOHANG
) > 0);
341 // Handle connections until shutdown.
343 // Set up signal handlers.
345 signal(SIGCHLD
, on_SIGCHLD
);
347 signal(SIGCHLD
, SIG_IGN
);
349 signal(SIGTERM
, on_SIGTERM
);
354 } catch (const Xapian::Error
&e
) {
355 // FIXME: better error handling.
356 cerr
<< "Caught " << e
.get_description() << endl
;
358 // FIXME: better error handling.
359 cerr
<< "Caught exception." << endl
;
364 #elif defined __WIN32__
366 // A threaded, Windows specific, implementation.
368 /** The socket which will be closed by CtrlHandler.
370 * FIXME - is there any way to avoid using a global variable here?
372 static const int *pShutdownSocket
= NULL
;
374 /// Console interrupt handler.
376 CtrlHandler(DWORD fdwCtrlType
)
378 switch (fdwCtrlType
) {
380 case CTRL_CLOSE_EVENT
:
381 // Console is about to die.
382 // CTRL_CLOSE_EVENT gives us 5 seconds before displaying a
383 // confirmation dialog asking if we really are sure.
384 case CTRL_LOGOFF_EVENT
:
385 case CTRL_SHUTDOWN_EVENT
:
386 // These 2 will probably need to change when we get service
387 // support - the service will prevent these being seen, so only
388 // apply interactively.
389 cout
<< "Shutting down..." << endl
;
390 break; // default behaviour
391 case CTRL_BREAK_EVENT
:
392 // This (probably) means the developer is struggling to get
393 // things to behave, and really wants to shutdown so let the OS
394 // handle Ctrl+Break in the default way.
395 cout
<< "Ctrl+Break: aborting process" << endl
;
398 cerr
<< "unexpected CtrlHandler: " << fdwCtrlType
<< endl
;
402 // Note: close() does not cause a blocking accept() call to terminate.
403 // However, it appears closesocket() does. This is much easier than trying
404 // to setup a non-blocking accept().
405 if (!pShutdownSocket
|| closesocket(*pShutdownSocket
) == SOCKET_ERROR
) {
406 // We failed to close the socket, so just let the OS handle the
407 // event in the default way.
411 pShutdownSocket
= NULL
;
412 return TRUE
; // Tell the OS that we've handled the event.
415 /// Structure which is used to pass parameters to the new threads.
418 thread_param(TcpServer
*s
, int c
) : server(s
), connected_socket(c
) {}
420 int connected_socket
;
423 /// The thread entry-point.
424 static unsigned __stdcall
425 run_thread(void * param_
)
427 thread_param
* param(reinterpret_cast<thread_param
*>(param_
));
428 int socket
= param
->connected_socket
;
430 param
->server
->handle_one_connection(socket
);
442 // Handle connections until shutdown.
444 // Set up the shutdown handler - this is a bit hacky, and sadly involves
445 // a global variable.
446 pShutdownSocket
= &listen_socket
;
447 if (!::SetConsoleCtrlHandler((PHANDLER_ROUTINE
) CtrlHandler
, TRUE
))
448 throw Xapian::NetworkError("Failed to install shutdown handler");
452 int connected_socket
= accept_connection();
453 if (connected_socket
== -1)
454 return; // Shutdown has happened
456 // Spawn a new thread to handle the connection.
457 // (This seems like lots of hoops just to end up calling
458 // this->handle_one_connection() on a new thread. There might be a
460 thread_param
*param
= new thread_param(this, connected_socket
);
461 HANDLE hthread
= (HANDLE
)_beginthreadex(NULL
, 0, ::run_thread
, param
, 0, NULL
);
463 // errno holds the error code from _beginthreadex, and
464 // closesocket() doesn't set errno.
465 closesocket(connected_socket
);
466 throw Xapian::NetworkError("_beginthreadex failed", errno
);
469 // FIXME: keep track of open thread handles so we can gracefully
470 // close each thread down. OTOH, when we want to kill them all its
471 // likely to mean the process is on its way down, so it doesn't
473 CloseHandle(hthread
);
474 } catch (const Xapian::Error
&e
) {
475 // FIXME: better error handling.
476 cerr
<< "Caught " << e
.get_description() << endl
;
478 // FIXME: better error handling.
479 cerr
<< "Caught exception." << endl
;
485 TcpServer::run_once()
487 // Run a single request on the current thread.
488 int fd
= accept_connection();
489 handle_one_connection(fd
);
494 # error Neither HAVE_FORK nor __WIN32__ are defined.
497 #ifdef DISABLE_GPL_LIBXAPIAN
498 # error GPL source we cannot relicense included in libxapian