xref: /freebsd/contrib/libpcap/rpcapd/rpcapd.c (revision cd0c3137f8a21968ce147251633b8dbdde8829e7)
1 /*
2  * Copyright (c) 2002 - 2003
3  * NetGroup, Politecnico di Torino (Italy)
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the Politecnico di Torino nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  */
32 
33 #ifdef HAVE_CONFIG_H
34 #include <config.h>
35 #endif
36 
37 #include "ftmacros.h"
38 
39 #include <errno.h>		// for the errno variable
40 #include <string.h>		// for strtok, etc
41 #include <stdlib.h>		// for malloc(), free(), ...
42 #include <pcap.h>		// for PCAP_ERRBUF_SIZE
43 #include <signal.h>		// for signal()
44 
45 #include "fmtutils.h"
46 #include "sockutils.h"		// for socket calls
47 #include "varattrs.h"		// for _U_
48 #include "portability.h"
49 #include "rpcapd.h"
50 #include "config_params.h"	// configuration file parameters
51 #include "fileconf.h"		// for the configuration file management
52 #include "rpcap-protocol.h"
53 #include "daemon.h"		// the true main() method of this daemon
54 #include "log.h"
55 
56 #ifdef _WIN32
57   #include <process.h>		// for thread stuff
58   #include "win32-svc.h"	// for Win32 service stuff
59   #include "getopt.h"		// for getopt()-for-Windows
60 #else
61   #include <fcntl.h>		// for open()
62   #include <unistd.h>		// for exit()
63   #include <sys/wait.h>		// waitpid()
64 #endif
65 
66 //
67 // Element in list of sockets on which we're listening for connections.
68 //
69 struct listen_sock {
70 	struct listen_sock *next;
71 	SOCKET sock;
72 };
73 
74 // Global variables
75 char hostlist[MAX_HOST_LIST + 1];		//!< Keeps the list of the hosts that are allowed to connect to this server
76 struct active_pars activelist[MAX_ACTIVE_LIST];	//!< Keeps the list of the hosts (host, port) on which I want to connect to (active mode)
77 int nullAuthAllowed;				//!< '1' if we permit NULL authentication, '0' otherwise
78 static struct listen_sock *listen_socks;	//!< sockets on which we listen
79 char loadfile[MAX_LINE + 1];			//!< Name of the file from which we have to load the configuration
80 static int passivemode = 1;			//!< '1' if we want to run in passive mode as well
81 static struct addrinfo mainhints;		//!< temporary struct to keep settings needed to open the new socket
82 static char address[MAX_LINE + 1];		//!< keeps the network address (either numeric or literal) to bind to
83 static char port[MAX_LINE + 1];			//!< keeps the network port to bind to
84 #ifdef _WIN32
85 static HANDLE state_change_event;		//!< event to signal that a state change should take place
86 #endif
87 static volatile sig_atomic_t shutdown_server;	//!< '1' if the server is to shut down
88 static volatile sig_atomic_t reread_config;	//!< '1' if the server is to re-read its configuration
89 
90 extern char *optarg;	// for getopt()
91 
92 // Function definition
93 #ifdef _WIN32
94 static unsigned __stdcall main_active(void *ptr);
95 static BOOL WINAPI main_ctrl_event(DWORD);
96 #else
97 static void *main_active(void *ptr);
98 static void main_terminate(int sign);
99 static void main_reread_config(int sign);
100 #endif
101 static void accept_connections(void);
102 static void accept_connection(SOCKET listen_sock);
103 #ifndef _WIN32
104 static void main_reap_children(int sign);
105 #endif
106 #ifdef _WIN32
107 static unsigned __stdcall main_passive_serviceloop_thread(void *ptr);
108 #endif
109 
110 #define RPCAP_ACTIVE_WAIT 30		/* Waiting time between two attempts to open a connection, in active mode (default: 30 sec) */
111 
112 /*!
113 	\brief Prints the usage screen if it is launched in console mode.
114 */
115 static void printusage(void)
116 {
117 	const char *usagetext =
118 	"USAGE:"
119 	" "  PROGRAM_NAME " [-b <address>] [-p <port>] [-4] [-l <host_list>] [-a <host,port>]\n"
120 	"              [-n] [-v] [-d] "
121 #ifndef _WIN32
122 	"[-i] "
123 #endif
124         "[-D] [-s <config_file>] [-f <config_file>]\n\n"
125 	"  -b <address>    the address to bind to (either numeric or literal).\n"
126 	"                  Default: binds to all local IPv4 and IPv6 addresses\n\n"
127 	"  -p <port>       the port to bind to.\n"
128 	"                  Default: binds to port " RPCAP_DEFAULT_NETPORT "\n\n"
129 	"  -4              use only IPv4.\n"
130 	"                  Default: use both IPv4 and IPv6 waiting sockets\n\n"
131 	"  -l <host_list>  a file that contains a list of hosts that are allowed\n"
132 	"                  to connect to this server (if more than one, list them one\n"
133 	"                  per line).\n"
134 	"                  We suggest to use literal names (instead of numeric ones)\n"
135 	"                  in order to avoid problems with different address families.\n\n"
136 	"  -n              permit NULL authentication (usually used with '-l')\n\n"
137 	"  -a <host,port>  run in active mode when connecting to 'host' on port 'port'\n"
138 	"                  In case 'port' is omitted, the default port (" RPCAP_DEFAULT_NETPORT_ACTIVE ") is used\n\n"
139 	"  -v              run in active mode only (default: if '-a' is specified, it\n"
140 	"                  accepts passive connections as well)\n\n"
141 	"  -d              run in daemon mode (UNIX only) or as a service (Win32 only)\n"
142 	"                  Warning (Win32): this switch is provided automatically when\n"
143 	"                  the service is started from the control panel\n\n"
144 #ifndef _WIN32
145 	"  -i              run in inetd mode (UNIX only)\n\n"
146 #endif
147 	"  -D              log debugging messages\n\n"
148 	"  -s <config_file> save the current configuration to file\n\n"
149 	"  -f <config_file> load the current configuration from file; all switches\n"
150 	"                  specified from the command line are ignored\n\n"
151 	"  -h              print this help screen\n\n";
152 
153 	(void)fprintf(stderr, "RPCAPD, a remote packet capture daemon.\n"
154 	"Compiled with %s\n\n", pcap_lib_version());
155 	printf("%s", usagetext);
156 }
157 
158 
159 
160 //! Program main
161 int main(int argc, char *argv[])
162 {
163 	char savefile[MAX_LINE + 1];		// name of the file on which we have to save the configuration
164 	int log_to_systemlog = 0;		// Non-zero if we should log to the "system log" rather than the standard error
165 	int isdaemon = 0;			// Non-zero if the user wants to run this program as a daemon
166 #ifndef _WIN32
167 	int isrunbyinetd = 0;			// Non-zero if this is being run by inetd or something inetd-like
168 #endif
169 	int log_debug_messages = 0;		// Non-zero if the user wants debug messages logged
170 	int retval;				// keeps the returning value from several functions
171 	char errbuf[PCAP_ERRBUF_SIZE + 1];	// keeps the error string, prior to be printed
172 #ifndef _WIN32
173 	struct sigaction action;
174 #endif
175 
176 	savefile[0] = 0;
177 	loadfile[0] = 0;
178 	hostlist[0] = 0;
179 
180 	// Initialize errbuf
181 	memset(errbuf, 0, sizeof(errbuf));
182 
183 	strncpy(address, RPCAP_DEFAULT_NETADDR, MAX_LINE);
184 	strncpy(port, RPCAP_DEFAULT_NETPORT, MAX_LINE);
185 
186 	// Prepare to open a new server socket
187 	memset(&mainhints, 0, sizeof(struct addrinfo));
188 
189 	mainhints.ai_family = PF_UNSPEC;
190 	mainhints.ai_flags = AI_PASSIVE;	// Ready to a bind() socket
191 	mainhints.ai_socktype = SOCK_STREAM;
192 
193 	// Getting the proper command line options
194 	while ((retval = getopt(argc, argv, "b:dDhip:4l:na:s:f:v")) != -1)
195 	{
196 		switch (retval)
197 		{
198 			case 'D':
199 				log_debug_messages = 1;
200 				rpcapd_log_set(log_to_systemlog, log_debug_messages);
201 				break;
202 			case 'b':
203 				strncpy(address, optarg, MAX_LINE);
204 				break;
205 			case 'p':
206 				strncpy(port, optarg, MAX_LINE);
207 				break;
208 			case '4':
209 				mainhints.ai_family = PF_INET;		// IPv4 server only
210 				break;
211 			case 'd':
212 				isdaemon = 1;
213 				log_to_systemlog = 1;
214 				rpcapd_log_set(log_to_systemlog, log_debug_messages);
215 				break;
216 			case 'i':
217 #ifdef _WIN32
218 				printusage();
219 				exit(1);
220 #else
221 				isrunbyinetd = 1;
222 				log_to_systemlog = 1;
223 				rpcapd_log_set(log_to_systemlog, log_debug_messages);
224 #endif
225 				break;
226 			case 'n':
227 				nullAuthAllowed = 1;
228 				break;
229 			case 'v':
230 				passivemode = 0;
231 				break;
232 			case 'l':
233 			{
234 				strncpy(hostlist, optarg, sizeof(hostlist));
235 				break;
236 			}
237 			case 'a':
238 			{
239 				char *tmpaddress, *tmpport;
240 				char *lasts;
241 				int i = 0;
242 
243 				tmpaddress = pcap_strtok_r(optarg, RPCAP_HOSTLIST_SEP, &lasts);
244 
245 				while ((tmpaddress != NULL) && (i < MAX_ACTIVE_LIST))
246 				{
247 					tmpport = pcap_strtok_r(NULL, RPCAP_HOSTLIST_SEP, &lasts);
248 
249 					pcap_strlcpy(activelist[i].address, tmpaddress, MAX_LINE);
250 
251 					if ((tmpport == NULL) || (strcmp(tmpport, "DEFAULT") == 0)) // the user choose a custom port
252 						pcap_strlcpy(activelist[i].port, RPCAP_DEFAULT_NETPORT_ACTIVE, MAX_LINE);
253 					else
254 						pcap_strlcpy(activelist[i].port, tmpport, MAX_LINE);
255 
256 					tmpaddress = pcap_strtok_r(NULL, RPCAP_HOSTLIST_SEP, &lasts);
257 
258 					i++;
259 				}
260 
261 				if (i > MAX_ACTIVE_LIST)
262 					rpcapd_log(LOGPRIO_ERROR, "Only MAX_ACTIVE_LIST active connections are currently supported.");
263 
264 				// I don't initialize the remaining part of the structure, since
265 				// it is already zeroed (it is a global var)
266 				break;
267 			}
268 			case 'f':
269 				pcap_strlcpy(loadfile, optarg, MAX_LINE);
270 				break;
271 			case 's':
272 				pcap_strlcpy(savefile, optarg, MAX_LINE);
273 				break;
274 			case 'h':
275 				printusage();
276 				exit(0);
277 				/*NOTREACHED*/
278 			default:
279 				exit(1);
280 				/*NOTREACHED*/
281 		}
282 	}
283 
284 #ifndef _WIN32
285 	if (isdaemon && isrunbyinetd)
286 	{
287 		rpcapd_log(LOGPRIO_ERROR, "rpcapd: -d and -i can't be used together");
288 		exit(1);
289 	}
290 #endif
291 
292 	if (sock_init(errbuf, PCAP_ERRBUF_SIZE) == -1)
293 	{
294 		rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
295 		exit(-1);
296 	}
297 
298 	if (savefile[0] && fileconf_save(savefile))
299 		rpcapd_log(LOGPRIO_DEBUG, "Error when saving the configuration to file");
300 
301 	// If the file does not exist, it keeps the settings provided by the command line
302 	if (loadfile[0])
303 		fileconf_read();
304 
305 #ifdef WIN32
306 	//
307 	// Create a handle to signal the main loop to tell it to do
308 	// something.
309 	//
310 	state_change_event = CreateEvent(NULL, FALSE, FALSE, NULL);
311 	if (state_change_event == NULL)
312 	{
313 		sock_geterror("Can't create state change event", errbuf,
314 		    PCAP_ERRBUF_SIZE);
315 		rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
316 		exit(2);
317 	}
318 
319 	//
320 	// Catch control signals.
321 	//
322 	if (!SetConsoleCtrlHandler(main_ctrl_event, TRUE))
323 	{
324 		sock_geterror("Can't set control handler", errbuf,
325 		    PCAP_ERRBUF_SIZE);
326 		rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
327 		exit(2);
328 	}
329 #else
330 	memset(&action, 0, sizeof (action));
331 	action.sa_handler = main_terminate;
332 	action.sa_flags = 0;
333 	sigemptyset(&action.sa_mask);
334 	sigaction(SIGTERM, &action, NULL);
335 	memset(&action, 0, sizeof (action));
336 	action.sa_handler = main_reap_children;
337 	action.sa_flags = 0;
338 	sigemptyset(&action.sa_mask);
339 	sigaction(SIGCHLD, &action, NULL);
340 	// Ignore SIGPIPE - we'll get EPIPE when trying to write to a closed
341 	// connection, we don't want to get killed by a signal in that case
342 	signal(SIGPIPE, SIG_IGN);
343 #endif
344 
345 #ifndef _WIN32
346 	if (isrunbyinetd)
347 	{
348 		//
349 		// -i was specified, indicating that this is being run
350 		// by inetd or something that can run network daemons
351 		// as if it were inetd (xinetd, launchd, systemd, etc.).
352 		//
353 		// We assume that the program that launched us just
354 		// duplicated a single socket for the connection
355 		// to our standard input, output, and error, so we
356 		// can just use the standard input as our control
357 		// socket.
358 		//
359 		int sockctrl;
360 		int devnull_fd;
361 
362 		//
363 		// Duplicate the standard input as the control socket.
364 		//
365 		sockctrl = dup(0);
366 		if (sockctrl == -1)
367 		{
368 			sock_geterror("Can't dup standard input", errbuf,
369 			    PCAP_ERRBUF_SIZE);
370 			rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
371 			exit(2);
372 		}
373 
374 		//
375 		// Try to set the standard input, output, and error
376 		// to /dev/null.
377 		//
378 		devnull_fd = open("/dev/null", O_RDWR);
379 		if (devnull_fd != -1)
380 		{
381 			//
382 			// If this fails, just drive on.
383 			//
384 			(void)dup2(devnull_fd, 0);
385 			(void)dup2(devnull_fd, 1);
386 			(void)dup2(devnull_fd, 2);
387 			close(devnull_fd);
388 		}
389 
390 		//
391 		// Handle this client.
392 		// This is passive mode, so we don't care whether we were
393 		// told by the client to close.
394 		//
395 		char *hostlist_copy = strdup(hostlist);
396 		if (hostlist_copy == NULL)
397 		{
398 			rpcapd_log(LOGPRIO_ERROR, "Out of memory copying the host/port list");
399 			exit(0);
400 		}
401 		(void)daemon_serviceloop(sockctrl, 0, hostlist_copy,
402 		    nullAuthAllowed);
403 
404 		//
405 		// Nothing more to do.
406 		//
407 		exit(0);
408 	}
409 #endif
410 
411 	if (isdaemon)
412 	{
413 		//
414 		// This is being run as a daemon.
415 		// On UN*X, it might be manually run, or run from an
416 		// rc file.
417 		//
418 #ifndef _WIN32
419 		int pid;
420 
421 		//
422 		// Daemonize ourselves.
423 		//
424 		// Unix Network Programming, pg 336
425 		//
426 		if ((pid = fork()) != 0)
427 			exit(0);		// Parent terminates
428 
429 		// First child continues
430 		// Set daemon mode
431 		setsid();
432 
433 		// generated under unix with 'kill -HUP', needed to reload the configuration
434 		memset(&action, 0, sizeof (action));
435 		action.sa_handler = main_reread_config;
436 		action.sa_flags = 0;
437 		sigemptyset(&action.sa_mask);
438 		sigaction(SIGHUP, &action, NULL);
439 
440 		if ((pid = fork()) != 0)
441 			exit(0);		// First child terminates
442 
443 		// LINUX WARNING: the current linux implementation of pthreads requires a management thread
444 		// to handle some hidden stuff. So, as soon as you create the first thread, two threads are
445 		// created. Fom this point on, the number of threads active are always one more compared
446 		// to the number you're expecting
447 
448 		// Second child continues
449 //		umask(0);
450 //		chdir("/");
451 #else
452 		//
453 		// This is being run as a service on Windows.
454 		//
455 		// If this call succeeds, it is blocking on Win32
456 		//
457 		if (svc_start() != 1)
458 			rpcapd_log(LOGPRIO_DEBUG, "Unable to start the service");
459 
460 		// When the previous call returns, the entire application has to be stopped.
461 		exit(0);
462 #endif
463 	}
464 	else	// Console mode
465 	{
466 #ifndef _WIN32
467 		// Enable the catching of Ctrl+C
468 		memset(&action, 0, sizeof (action));
469 		action.sa_handler = main_terminate;
470 		action.sa_flags = 0;
471 		sigemptyset(&action.sa_mask);
472 		sigaction(SIGINT, &action, NULL);
473 
474 		// generated under unix with 'kill -HUP', needed to reload the configuration
475 		// We do not have this kind of signal in Win32
476 		memset(&action, 0, sizeof (action));
477 		action.sa_handler = main_reread_config;
478 		action.sa_flags = 0;
479 		sigemptyset(&action.sa_mask);
480 		sigaction(SIGHUP, &action, NULL);
481 #endif
482 
483 		printf("Press CTRL + C to stop the server...\n");
484 	}
485 
486 	// If we're a Win32 service, we have already called this function in the service_main
487 	main_startup();
488 
489 	// The code should never arrive here (since the main_startup is blocking)
490 	//  however this avoids a compiler warning
491 	exit(0);
492 }
493 
494 void main_startup(void)
495 {
496 	char errbuf[PCAP_ERRBUF_SIZE + 1];	// keeps the error string, prior to be printed
497 	struct addrinfo *addrinfo;		// keeps the addrinfo chain; required to open a new socket
498 	int i;
499 #ifdef _WIN32
500 	HANDLE threadId;			// handle for the subthread
501 #else
502 	pid_t pid;
503 #endif
504 
505 	i = 0;
506 	addrinfo = NULL;
507 	memset(errbuf, 0, sizeof(errbuf));
508 
509 	// Starts all the active threads
510 	while ((i < MAX_ACTIVE_LIST) && (activelist[i].address[0] != 0))
511 	{
512 		activelist[i].ai_family = mainhints.ai_family;
513 
514 #ifdef _WIN32
515 		threadId = (HANDLE)_beginthreadex(NULL, 0, main_active,
516 		    (void *)&activelist[i], 0, NULL);
517 		if (threadId == 0)
518 		{
519 			rpcapd_log(LOGPRIO_DEBUG, "Error creating the active child threads");
520 			continue;
521 		}
522 		CloseHandle(threadId);
523 #else
524 		if ((pid = fork()) == 0)	// I am the child
525 		{
526 			main_active((void *) &activelist[i]);
527 			exit(0);
528 		}
529 #endif
530 		i++;
531 	}
532 
533 	/*
534 	 * The code that manages the active connections is not blocking;
535 	 * the code that manages the passive connection is blocking.
536 	 * So, if the user does not want to run in passive mode, we have
537 	 * to block the main thread here, otherwise the program ends and
538 	 * all threads are stopped.
539 	 *
540 	 * WARNING: this means that in case we have only active mode,
541 	 * the program does not terminate even if all the child thread
542 	 * terminates. The user has always to press Ctrl+C (or send a
543 	 * SIGTERM) to terminate the program.
544 	 */
545 	if (passivemode)
546 	{
547 		struct addrinfo *tempaddrinfo;
548 
549 		//
550 		// Get a list of sockets on which to listen.
551 		//
552 		if (sock_initaddress((address[0]) ? address : NULL, port, &mainhints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1)
553 		{
554 			rpcapd_log(LOGPRIO_DEBUG, "%s", errbuf);
555 			return;
556 		}
557 
558 		for (tempaddrinfo = addrinfo; tempaddrinfo;
559 		     tempaddrinfo = tempaddrinfo->ai_next)
560 		{
561 			SOCKET sock;
562 			struct listen_sock *sock_info;
563 
564 			if ((sock = sock_open(tempaddrinfo, SOCKOPEN_SERVER, SOCKET_MAXCONN, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
565 			{
566 				switch (tempaddrinfo->ai_family)
567 				{
568 				case AF_INET:
569 				{
570 					struct sockaddr_in *in;
571 					char addrbuf[INET_ADDRSTRLEN];
572 
573 					in = (struct sockaddr_in *)tempaddrinfo->ai_addr;
574 					rpcapd_log(LOGPRIO_WARNING, "Can't listen on socket for %s:%u: %s",
575 					    inet_ntop(AF_INET, &in->sin_addr,
576 						addrbuf, sizeof (addrbuf)),
577 					    ntohs(in->sin_port),
578 					    errbuf);
579 					break;
580 				}
581 
582 				case AF_INET6:
583 				{
584 					struct sockaddr_in6 *in6;
585 					char addrbuf[INET6_ADDRSTRLEN];
586 
587 					in6 = (struct sockaddr_in6 *)tempaddrinfo->ai_addr;
588 					rpcapd_log(LOGPRIO_WARNING, "Can't listen on socket for %s:%u: %s",
589 					    inet_ntop(AF_INET6, &in6->sin6_addr,
590 						addrbuf, sizeof (addrbuf)),
591 					    ntohs(in6->sin6_port),
592 					    errbuf);
593 					break;
594 				}
595 
596 				default:
597 					rpcapd_log(LOGPRIO_WARNING, "Can't listen on socket for address family %u: %s",
598 					    tempaddrinfo->ai_family,
599 					    errbuf);
600 					break;
601 				}
602 				continue;
603 			}
604 
605 			sock_info = (struct listen_sock *) malloc(sizeof (struct listen_sock));
606 			if (sock_info == NULL)
607 			{
608 				rpcapd_log(LOGPRIO_ERROR, "Can't allocate structure for listen socket");
609 				exit(2);
610 			}
611 			sock_info->sock = sock;
612 			sock_info->next = listen_socks;
613 			listen_socks = sock_info;
614 		}
615 
616 		freeaddrinfo(addrinfo);
617 
618 		if (listen_socks == NULL)
619 		{
620 			rpcapd_log(LOGPRIO_ERROR, "Can't listen on any address");
621 			exit(2);
622 		}
623 
624 		//
625 		// Now listen on all of them, waiting for connections.
626 		//
627 		accept_connections();
628 	}
629 
630 	//
631 	// We're done; exit.
632 	//
633 	rpcapd_log(LOGPRIO_DEBUG, PROGRAM_NAME " is closing.\n");
634 
635 #ifndef _WIN32
636 	//
637 	// Sends a KILL signal to all the processes in this process's
638 	// process group; i.e., it kills all the child processes
639 	// we've created.
640 	//
641 	// XXX - that also includes us, so we will be killed as well;
642 	// that may cause a message to be printed or logged.
643 	//
644 	kill(0, SIGKILL);
645 #endif
646 
647 	//
648 	// Just leave.  We shouldn't need to clean up sockets or
649 	// anything else, and if we try to do so, we'll could end
650 	// up closing sockets, or shutting Winsock down, out from
651 	// under service loops, causing all sorts of noisy error
652 	// messages.
653 	//
654 	// We shouldn't need to worry about cleaning up any resources
655 	// such as handles, sockets, threads, etc. - exit() should
656 	// terminate the process, causing all those resources to be
657 	// cleaned up (including the threads; Microsoft claims in the
658 	// ExitProcess() documentation that, if ExitProcess() is called,
659 	// "If a thread is waiting on a kernel object, it will not be
660 	// terminated until the wait has completed.", but claims in the
661 	// _beginthread()/_beginthreadex() documentation that "All threads
662 	// are terminated if any thread calls abort, exit, _exit, or
663 	// ExitProcess." - the latter appears to be the case, even for
664 	// threads waiting on the event for a pcap_t).
665 	//
666 	exit(0);
667 }
668 
669 #ifdef _WIN32
670 static void
671 send_state_change_event(void)
672 {
673 	char errbuf[PCAP_ERRBUF_SIZE + 1];	// keeps the error string, prior to be printed
674 
675 	if (!SetEvent(state_change_event))
676 	{
677 		sock_geterror("SetEvent on shutdown event failed", errbuf,
678 		    PCAP_ERRBUF_SIZE);
679 		rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
680 	}
681 }
682 
683 void
684 send_shutdown_notification(void)
685 {
686 	//
687 	// Indicate that the server should shut down.
688 	//
689 	shutdown_server = 1;
690 
691 	//
692 	// Send a state change event, to wake up WSAWaitForMultipleEvents().
693 	//
694 	send_state_change_event();
695 }
696 
697 void
698 send_reread_configuration_notification(void)
699 {
700 	//
701 	// Indicate that the server should re-read its configuration file.
702 	//
703 	reread_config = 1;
704 
705 	//
706 	// Send a state change event, to wake up WSAWaitForMultipleEvents().
707 	//
708 	send_state_change_event();
709 }
710 
711 static BOOL WINAPI main_ctrl_event(DWORD ctrltype)
712 {
713 	//
714 	// ctrltype is one of:
715 	//
716 	// CTRL_C_EVENT - we got a ^C; this is like SIGINT
717 	// CTRL_BREAK_EVENT - we got Ctrl+Break
718 	// CTRL_CLOSE_EVENT - the console was closed; this is like SIGHUP
719 	// CTRL_LOGOFF_EVENT - a user is logging off; this is received
720 	//   only by services
721 	// CTRL_SHUTDOWN_EVENT - the systemis shutting down; this is
722 	//   received only by services
723 	//
724 	// For now, we treat all but CTRL_LOGOFF_EVENT as indications
725 	// that we should shut down.
726 	//
727 	switch (ctrltype)
728 	{
729 		case CTRL_C_EVENT:
730 		case CTRL_BREAK_EVENT:
731 		case CTRL_CLOSE_EVENT:
732 		case CTRL_SHUTDOWN_EVENT:
733 			//
734 			// Set a shutdown notification.
735 			//
736 			send_shutdown_notification();
737 			break;
738 
739 		default:
740 			break;
741 	}
742 
743 	//
744 	// We handled this.
745 	//
746 	return TRUE;
747 }
748 #else
749 static void main_terminate(int sign _U_)
750 {
751 	//
752 	// Note that the server should shut down.
753 	// select() should get an EINTR error when we return,
754 	// so it will wake up and know it needs to check the flag.
755 	//
756 	shutdown_server = 1;
757 }
758 
759 static void main_reread_config(int sign _U_)
760 {
761 	//
762 	// Note that the server should re-read its configuration file.
763 	// select() should get an EINTR error when we return,
764 	// so it will wake up and know it needs to check the flag.
765 	//
766 	reread_config = 1;
767 }
768 
769 static void main_reap_children(int sign _U_)
770 {
771 	pid_t pid;
772 	int exitstat;
773 
774 	// Reap all child processes that have exited.
775 	// For reference, Stevens, pg 128
776 
777 	while ((pid = waitpid(-1, &exitstat, WNOHANG)) > 0)
778 		rpcapd_log(LOGPRIO_DEBUG, "Child terminated");
779 
780 	return;
781 }
782 #endif
783 
784 //
785 // Loop waiting for incoming connections and accepting them.
786 //
787 static void
788 accept_connections(void)
789 {
790 #ifdef _WIN32
791 	struct listen_sock *sock_info;
792 	DWORD num_events;
793 	WSAEVENT *events;
794 	int i;
795 	char errbuf[PCAP_ERRBUF_SIZE + 1];	// keeps the error string, prior to be printed
796 
797 	//
798 	// How big does the set of events need to be?
799 	// One for the shutdown event, plus one for every socket on which
800 	// we'll be listening.
801 	//
802 	num_events = 1;		// shutdown event
803 	for (sock_info = listen_socks; sock_info;
804 	    sock_info = sock_info->next)
805 	{
806 		if (num_events == WSA_MAXIMUM_WAIT_EVENTS)
807 		{
808 			//
809 			// WSAWaitForMultipleEvents() doesn't support
810 			// more than WSA_MAXIMUM_WAIT_EVENTS events
811 			// on which to wait.
812 			//
813 			rpcapd_log(LOGPRIO_ERROR, "Too many sockets on which to listen");
814 			exit(2);
815 		}
816 		num_events++;
817 	}
818 
819 	//
820 	// Allocate the array of events.
821 	//
822 	events = (WSAEVENT *) malloc(num_events * sizeof (WSAEVENT));
823 	if (events == NULL)
824 	{
825 		rpcapd_log(LOGPRIO_ERROR, "Can't allocate array of events which to listen");
826 		exit(2);
827 	}
828 
829 	//
830 	// Fill it in.
831 	//
832 	events[0] = state_change_event;	// state change event first
833 	for (sock_info = listen_socks, i = 1; sock_info;
834 	    sock_info = sock_info->next, i++)
835 	{
836 		WSAEVENT event;
837 
838 		//
839 		// Create an event that is signaled if there's a connection
840 		// to accept on the socket in question.
841 		//
842 		event = WSACreateEvent();
843 		if (event == WSA_INVALID_EVENT)
844 		{
845 			sock_geterror("Can't create socket event", errbuf,
846 			    PCAP_ERRBUF_SIZE);
847 			rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
848 			exit(2);
849 		}
850 		if (WSAEventSelect(sock_info->sock, event, FD_ACCEPT) == SOCKET_ERROR)
851 		{
852 			sock_geterror("Can't setup socket event", errbuf,
853 			    PCAP_ERRBUF_SIZE);
854 			rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
855 			exit(2);
856 		}
857 		events[i] = event;
858 	}
859 
860 	for (;;)
861 	{
862 		//
863 		// Wait for incoming connections.
864 		//
865 		DWORD ret;
866 
867 		ret = WSAWaitForMultipleEvents(num_events, events, FALSE,
868 		    WSA_INFINITE, FALSE);
869 		if (ret == WSA_WAIT_FAILED)
870 		{
871 			sock_geterror("WSAWaitForMultipleEvents failed", errbuf,
872 			    PCAP_ERRBUF_SIZE);
873 			rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
874 			exit(2);
875 		}
876 
877 		if (ret == WSA_WAIT_EVENT_0)
878 		{
879 			//
880 			// The state change event was set.
881 			//
882 			if (shutdown_server)
883 			{
884 				//
885 				// Time to quit. Exit the loop.
886 				//
887 				break;
888 			}
889 			if (reread_config)
890 			{
891 				//
892 				// We should re-read the configuration
893 				// file.
894 				//
895 				reread_config = 0;	// clear the indicator
896 				fileconf_read();
897 			}
898 		}
899 
900 		//
901 		// Check each socket.
902 		//
903 		for (sock_info = listen_socks, i = 1; sock_info;
904 		    sock_info = sock_info->next, i++)
905 		{
906 			WSANETWORKEVENTS network_events;
907 
908 			if (WSAEnumNetworkEvents(sock_info->sock,
909 			    events[i], &network_events) == SOCKET_ERROR)
910 			{
911 				sock_geterror("WSAEnumNetworkEvents failed",
912 				    errbuf, PCAP_ERRBUF_SIZE);
913 				rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
914 				exit(2);
915 			}
916 			if (network_events.lNetworkEvents & FD_ACCEPT)
917 			{
918 				//
919 				// Did an error occur?
920 				//
921 			 	if (network_events.iErrorCode[FD_ACCEPT_BIT] != 0)
922 			 	{
923 					//
924 					// Yes - report it and keep going.
925 					//
926 					sock_fmterror("Socket error",
927 					    network_events.iErrorCode[FD_ACCEPT_BIT],
928 					    errbuf,
929 					    PCAP_ERRBUF_SIZE);
930 					rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
931 					continue;
932 				}
933 
934 				//
935 				// Accept the connection.
936 				//
937 				accept_connection(sock_info->sock);
938 			}
939 		}
940 	}
941 #else
942 	struct listen_sock *sock_info;
943 	int num_sock_fds;
944 
945 	//
946 	// How big does the bitset of sockets on which to select() have
947 	// to be?
948 	//
949 	num_sock_fds = 0;
950 	for (sock_info = listen_socks; sock_info; sock_info = sock_info->next)
951 	{
952 		if (sock_info->sock + 1 > num_sock_fds)
953 		{
954 			if ((unsigned int)(sock_info->sock + 1) >
955 			    (unsigned int)FD_SETSIZE)
956 			{
957 				rpcapd_log(LOGPRIO_ERROR, "Socket FD is too bit for an fd_set");
958 				exit(2);
959 			}
960 			num_sock_fds = sock_info->sock + 1;
961 		}
962 	}
963 
964 	for (;;)
965 	{
966 		fd_set sock_fds;
967 		int ret;
968 
969 		//
970 		// Set up an fd_set for all the sockets on which we're
971 		// listening.
972 		//
973 		// This set is modified by select(), so we have to
974 		// construct it anew each time.
975 		//
976 		FD_ZERO(&sock_fds);
977 		for (sock_info = listen_socks; sock_info;
978 		    sock_info = sock_info->next)
979 		{
980 			FD_SET(sock_info->sock, &sock_fds);
981 		}
982 
983 		//
984 		// Wait for incoming connections.
985 		//
986 		ret = select(num_sock_fds, &sock_fds, NULL, NULL, NULL);
987 		if (ret == -1)
988 		{
989 			if (errno == EINTR)
990 			{
991 				//
992 				// If this is a "terminate the
993 				// server" signal, exit the loop,
994 				// otherwise just keep trying.
995 				//
996 				if (shutdown_server)
997 				{
998 					//
999 					// Time to quit.  Exit the loop.
1000 					//
1001 					break;
1002 				}
1003 				if (reread_config)
1004 				{
1005 					//
1006 					// We should re-read the configuration
1007 					// file.
1008 					//
1009 					reread_config = 0;	// clear the indicator
1010 					fileconf_read();
1011 				}
1012 
1013 				//
1014 				// Go back and wait again.
1015 				//
1016 				continue;
1017 			}
1018 			else
1019 			{
1020 				rpcapd_log(LOGPRIO_ERROR, "select failed: %s",
1021 				    strerror(errno));
1022 				exit(2);
1023 			}
1024 		}
1025 
1026 		//
1027 		// Check each socket.
1028 		//
1029 		for (sock_info = listen_socks; sock_info;
1030 		    sock_info = sock_info->next)
1031 		{
1032 			if (FD_ISSET(sock_info->sock, &sock_fds))
1033 			{
1034 				//
1035 				// Accept the connection.
1036 				//
1037 				accept_connection(sock_info->sock);
1038 			}
1039 		}
1040 	}
1041 #endif
1042 
1043 	//
1044 	// Close all the listen sockets.
1045 	//
1046 	for (sock_info = listen_socks; sock_info; sock_info = sock_info->next)
1047 	{
1048 		closesocket(sock_info->sock);
1049 	}
1050 	sock_cleanup();
1051 }
1052 
1053 #ifdef _WIN32
1054 //
1055 // A structure to hold the parameters to the daemon service loop
1056 // thread on Windows.
1057 //
1058 // (On UN*X, there is no need for this explicit copy since the
1059 // fork "inherits" the parent stack.)
1060 //
1061 struct params_copy {
1062 	SOCKET sockctrl;
1063 	char *hostlist;
1064 };
1065 #endif
1066 
1067 //
1068 // Accept a connection and start a worker thread, on Windows, or a
1069 // worker process, on UN*X, to handle the connection.
1070 //
1071 static void
1072 accept_connection(SOCKET listen_sock)
1073 {
1074 	char errbuf[PCAP_ERRBUF_SIZE + 1];	// keeps the error string, prior to be printed
1075 	SOCKET sockctrl;			// keeps the socket ID for this control connection
1076 	struct sockaddr_storage from;		// generic sockaddr_storage variable
1077 	socklen_t fromlen;			// keeps the length of the sockaddr_storage variable
1078 
1079 #ifdef _WIN32
1080 	HANDLE threadId;			// handle for the subthread
1081 	u_long off = 0;
1082 	struct params_copy *params_copy = NULL;
1083 #else
1084 	pid_t pid;
1085 #endif
1086 
1087 	// Initialize errbuf
1088 	memset(errbuf, 0, sizeof(errbuf));
1089 
1090 	for (;;)
1091 	{
1092 		// Accept the connection
1093 		fromlen = sizeof(struct sockaddr_storage);
1094 
1095 		sockctrl = accept(listen_sock, (struct sockaddr *) &from, &fromlen);
1096 
1097 		if (sockctrl != INVALID_SOCKET)
1098 		{
1099 			// Success.
1100 			break;
1101 		}
1102 
1103 		// The accept() call can return this error when a signal is catched
1104 		// In this case, we have simply to ignore this error code
1105 		// Stevens, pg 124
1106 #ifdef _WIN32
1107 		if (WSAGetLastError() == WSAEINTR)
1108 #else
1109 		if (errno == EINTR)
1110 #endif
1111 			continue;
1112 
1113 		// Don't check for errors here, since the error can be due to the fact that the thread
1114 		// has been killed
1115 		sock_geterror("accept()", errbuf, PCAP_ERRBUF_SIZE);
1116 		rpcapd_log(LOGPRIO_ERROR, "Accept of control connection from client failed: %s",
1117 		    errbuf);
1118 		return;
1119 	}
1120 
1121 #ifdef _WIN32
1122 	//
1123 	// Put the socket back into blocking mode; doing WSAEventSelect()
1124 	// on the listen socket makes that socket non-blocking, and it
1125 	// appears that sockets returned from an accept() on that socket
1126 	// are also non-blocking.
1127 	//
1128 	// First, we have to un-WSAEventSelect() this socket, and then
1129 	// we can turn non-blocking mode off.
1130 	//
1131 	// If this fails, we aren't guaranteed that, for example, any
1132 	// of the error message will be sent - if it can't be put in
1133 	// the socket queue, the send will just fail.
1134 	//
1135 	// So we just log the message and close the connection.
1136 	//
1137 	if (WSAEventSelect(sockctrl, NULL, 0) == SOCKET_ERROR)
1138 	{
1139 		sock_geterror("WSAEventSelect()", errbuf, PCAP_ERRBUF_SIZE);
1140 		rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
1141 		sock_close(sockctrl, NULL, 0);
1142 		return;
1143 	}
1144 	if (ioctlsocket(sockctrl, FIONBIO, &off) == SOCKET_ERROR)
1145 	{
1146 		sock_geterror("ioctlsocket(FIONBIO)", errbuf, PCAP_ERRBUF_SIZE);
1147 		rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
1148 		sock_close(sockctrl, NULL, 0);
1149 		return;
1150 	}
1151 
1152 	//
1153 	// Make a copy of the host list to pass to the new thread, so that
1154 	// if we update it in the main thread, it won't catch us in the
1155 	// middle of updating it.
1156 	//
1157 	// daemon_serviceloop() will free it once it's done with it.
1158 	//
1159 	char *hostlist_copy = strdup(hostlist);
1160 	if (hostlist_copy == NULL)
1161 	{
1162 		rpcapd_log(LOGPRIO_ERROR, "Out of memory copying the host/port list");
1163 		sock_close(sockctrl, NULL, 0);
1164 		return;
1165 	}
1166 
1167 	//
1168 	// Allocate a location to hold the values of sockctrl.
1169 	// It will be freed in the newly-created thread once it's
1170 	// finished with it.
1171 	//
1172 	params_copy = malloc(sizeof(*params_copy));
1173 	if (params_copy == NULL)
1174 	{
1175 		rpcapd_log(LOGPRIO_ERROR, "Out of memory allocating the parameter copy structure");
1176 		free(hostlist_copy);
1177 		sock_close(sockctrl, NULL, 0);
1178 		return;
1179 	}
1180 	params_copy->sockctrl = sockctrl;
1181 	params_copy->hostlist = hostlist_copy;
1182 
1183 	threadId = (HANDLE)_beginthreadex(NULL, 0,
1184 	    main_passive_serviceloop_thread, (void *) params_copy, 0, NULL);
1185 	if (threadId == 0)
1186 	{
1187 		rpcapd_log(LOGPRIO_ERROR, "Error creating the child thread");
1188 		free(params_copy);
1189 		free(hostlist_copy);
1190 		sock_close(sockctrl, NULL, 0);
1191 		return;
1192 	}
1193 	CloseHandle(threadId);
1194 #else /* _WIN32 */
1195 	pid = fork();
1196 	if (pid == -1)
1197 	{
1198 		rpcapd_log(LOGPRIO_ERROR, "Error creating the child process: %s",
1199 		    strerror(errno));
1200 		sock_close(sockctrl, NULL, 0);
1201 		return;
1202 	}
1203 	if (pid == 0)
1204 	{
1205 		//
1206 		// Child process.
1207 		//
1208 		// Close the socket on which we're listening (must
1209 		// be open only in the parent).
1210 		//
1211 		closesocket(listen_sock);
1212 
1213 #if 0
1214 		//
1215 		// Modify thread params so that it can be killed at any time
1216 		// XXX - is this necessary?  This is the main and, currently,
1217 		// only thread in the child process, and nobody tries to
1218 		// cancel us, although *we* may cancel the thread that's
1219 		// handling the capture loop.
1220 		//
1221 		if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL))
1222 			goto end;
1223 		if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL))
1224 			goto end;
1225 #endif
1226 
1227 		//
1228 		// Run the service loop.
1229 		// This is passive mode, so we don't care whether we were
1230 		// told by the client to close.
1231 		//
1232 		char *hostlist_copy = strdup(hostlist);
1233 		if (hostlist_copy == NULL)
1234 		{
1235 			rpcapd_log(LOGPRIO_ERROR, "Out of memory copying the host/port list");
1236 			exit(0);
1237 		}
1238 		(void)daemon_serviceloop(sockctrl, 0, hostlist_copy,
1239 		    nullAuthAllowed);
1240 
1241 		exit(0);
1242 	}
1243 
1244 	// I am the parent
1245 	// Close the socket for this session (must be open only in the child)
1246 	closesocket(sockctrl);
1247 #endif /* _WIN32 */
1248 }
1249 
1250 /*!
1251 	\brief 'true' main of the program in case the active mode is turned on.
1252 
1253 	This function loops forever trying to connect to the remote host, until the
1254 	daemon is turned down.
1255 
1256 	\param ptr: it keeps the 'activepars' parameters.  It is a 'void *'
1257 	just because the thread APIs want this format.
1258 */
1259 #ifdef _WIN32
1260 static unsigned __stdcall
1261 #else
1262 static void *
1263 #endif
1264 main_active(void *ptr)
1265 {
1266 	char errbuf[PCAP_ERRBUF_SIZE + 1];	// keeps the error string, prior to be printed
1267 	SOCKET sockctrl;			// keeps the socket ID for this control connection
1268 	struct addrinfo hints;			// temporary struct to keep settings needed to open the new socket
1269 	struct addrinfo *addrinfo;		// keeps the addrinfo chain; required to open a new socket
1270 	struct active_pars *activepars;
1271 
1272 	activepars = (struct active_pars *) ptr;
1273 
1274 	// Prepare to open a new server socket
1275 	memset(&hints, 0, sizeof(struct addrinfo));
1276 						// WARNING Currently it supports only ONE socket family among IPv4 and IPv6
1277 	hints.ai_family = AF_INET;		// PF_UNSPEC to have both IPv4 and IPv6 server
1278 	hints.ai_socktype = SOCK_STREAM;
1279 	hints.ai_family = activepars->ai_family;
1280 
1281 	rpcapd_log(LOGPRIO_DEBUG, "Connecting to host %s, port %s, using protocol %s",
1282 	    activepars->address, activepars->port, (hints.ai_family == AF_INET) ? "IPv4":
1283 	    (hints.ai_family == AF_INET6) ? "IPv6" : "Unspecified");
1284 
1285 	// Initialize errbuf
1286 	memset(errbuf, 0, sizeof(errbuf));
1287 
1288 	// Do the work
1289 	if (sock_initaddress(activepars->address, activepars->port, &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1)
1290 	{
1291 		rpcapd_log(LOGPRIO_DEBUG, "%s", errbuf);
1292 		return 0;
1293 	}
1294 
1295 	for (;;)
1296 	{
1297 		int activeclose;
1298 
1299 		if ((sockctrl = sock_open(addrinfo, SOCKOPEN_CLIENT, 0, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
1300 		{
1301 			rpcapd_log(LOGPRIO_DEBUG, "%s", errbuf);
1302 
1303 			pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error connecting to host %s, port %s, using protocol %s",
1304 					activepars->address, activepars->port, (hints.ai_family == AF_INET) ? "IPv4":
1305 					(hints.ai_family == AF_INET6) ? "IPv6" : "Unspecified");
1306 
1307 			rpcapd_log(LOGPRIO_DEBUG, "%s", errbuf);
1308 
1309 			sleep_secs(RPCAP_ACTIVE_WAIT);
1310 
1311 			continue;
1312 		}
1313 
1314 		char *hostlist_copy = strdup(hostlist);
1315 		if (hostlist_copy == NULL)
1316 		{
1317 			rpcapd_log(LOGPRIO_ERROR, "Out of memory copying the host/port list");
1318 			activeclose = 0;
1319 			sock_close(sockctrl, NULL, 0);
1320 		}
1321 		else
1322 		{
1323 			//
1324 			// daemon_serviceloop() will free the copy.
1325 			//
1326 			activeclose = daemon_serviceloop(sockctrl, 1,
1327 			    hostlist_copy, nullAuthAllowed);
1328 		}
1329 
1330 		// If the connection is closed by the user explicitely, don't try to connect to it again
1331 		// just exit the program
1332 		if (activeclose == 1)
1333 			break;
1334 	}
1335 
1336 	freeaddrinfo(addrinfo);
1337 	return 0;
1338 }
1339 
1340 #ifdef _WIN32
1341 //
1342 // Main routine of a passive-mode service thread.
1343 //
1344 unsigned __stdcall main_passive_serviceloop_thread(void *ptr)
1345 {
1346 	struct params_copy params = *(struct params_copy *)ptr;
1347 	free(ptr);
1348 
1349 	//
1350 	// Handle this client.
1351 	// This is passive mode, so we don't care whether we were
1352 	// told by the client to close.
1353 	//
1354 	(void)daemon_serviceloop(params.sockctrl, 0, params.hostlist,
1355 	    nullAuthAllowed);
1356 
1357 	return 0;
1358 }
1359 #endif
1360