You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1421 lines
37 KiB
1421 lines
37 KiB
/*
|
|
* Copyright (c) 2002 - 2003
|
|
* NetGroup, Politecnico di Torino (Italy)
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the Politecnico di Torino nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include "ftmacros.h"
|
|
|
|
#include <errno.h> // for the errno variable
|
|
#include <string.h> // for strtok, etc
|
|
#include <stdlib.h> // for malloc(), free(), ...
|
|
#include <stdio.h> // for fprintf(), stderr, FILE etc
|
|
#include <pcap.h> // for PCAP_ERRBUF_SIZE
|
|
#include <signal.h> // for signal()
|
|
|
|
#include "fmtutils.h"
|
|
#include "sockutils.h" // for socket calls
|
|
#include "varattrs.h" // for _U_
|
|
#include "portability.h"
|
|
#include "rpcapd.h"
|
|
#include "config_params.h" // configuration file parameters
|
|
#include "fileconf.h" // for the configuration file management
|
|
#include "rpcap-protocol.h"
|
|
#include "daemon.h" // the true main() method of this daemon
|
|
#include "log.h"
|
|
|
|
#ifdef HAVE_OPENSSL
|
|
#include "sslutils.h"
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
#include <process.h> // for thread stuff
|
|
#include "win32-svc.h" // for Win32 service stuff
|
|
#include "getopt.h" // for getopt()-for-Windows
|
|
#else
|
|
#include <fcntl.h> // for open()
|
|
#include <unistd.h> // for exit()
|
|
#include <sys/wait.h> // waitpid()
|
|
#endif
|
|
|
|
//
|
|
// Element in list of sockets on which we're listening for connections.
|
|
//
|
|
struct listen_sock {
|
|
struct listen_sock *next;
|
|
SOCKET sock;
|
|
};
|
|
|
|
// Global variables
|
|
char hostlist[MAX_HOST_LIST + 1]; //!< Keeps the list of the hosts that are allowed to connect to this server
|
|
struct active_pars activelist[MAX_ACTIVE_LIST]; //!< Keeps the list of the hosts (host, port) on which I want to connect to (active mode)
|
|
int nullAuthAllowed; //!< '1' if we permit NULL authentication, '0' otherwise
|
|
static struct listen_sock *listen_socks; //!< sockets on which we listen
|
|
char loadfile[MAX_LINE + 1]; //!< Name of the file from which we have to load the configuration
|
|
static int passivemode = 1; //!< '1' if we want to run in passive mode as well
|
|
static struct addrinfo mainhints; //!< temporary struct to keep settings needed to open the new socket
|
|
static char address[MAX_LINE + 1]; //!< keeps the network address (either numeric or literal) to bind to
|
|
static char port[MAX_LINE + 1]; //!< keeps the network port to bind to
|
|
#ifdef _WIN32
|
|
static HANDLE state_change_event; //!< event to signal that a state change should take place
|
|
#endif
|
|
static volatile sig_atomic_t shutdown_server; //!< '1' if the server is to shut down
|
|
static volatile sig_atomic_t reread_config; //!< '1' if the server is to re-read its configuration
|
|
static int uses_ssl; //!< '1' to use TLS over the data socket
|
|
|
|
extern char *optarg; // for getopt()
|
|
|
|
// Function definition
|
|
#ifdef _WIN32
|
|
static unsigned __stdcall main_active(void *ptr);
|
|
static BOOL WINAPI main_ctrl_event(DWORD);
|
|
#else
|
|
static void *main_active(void *ptr);
|
|
static void main_terminate(int sign);
|
|
static void main_reread_config(int sign);
|
|
#endif
|
|
static void accept_connections(void);
|
|
static void accept_connection(SOCKET listen_sock);
|
|
#ifndef _WIN32
|
|
static void main_reap_children(int sign);
|
|
#endif
|
|
#ifdef _WIN32
|
|
static unsigned __stdcall main_passive_serviceloop_thread(void *ptr);
|
|
#endif
|
|
|
|
#define RPCAP_ACTIVE_WAIT 30 /* Waiting time between two attempts to open a connection, in active mode (default: 30 sec) */
|
|
|
|
/*!
|
|
\brief Prints the usage screen if it is launched in console mode.
|
|
*/
|
|
static void printusage(FILE * f)
|
|
{
|
|
const char *usagetext =
|
|
"USAGE:"
|
|
" " PROGRAM_NAME " [-b <address>] [-p <port>] [-4] [-l <host_list>] [-a <host,port>]\n"
|
|
" [-n] [-v] [-d] "
|
|
#ifndef _WIN32
|
|
"[-i] "
|
|
#endif
|
|
"[-D] [-s <config_file>] [-f <config_file>]\n\n"
|
|
" -b <address> the address to bind to (either numeric or literal).\n"
|
|
" Default: binds to all local IPv4 and IPv6 addresses\n\n"
|
|
" -p <port> the port to bind to.\n"
|
|
" Default: binds to port " RPCAP_DEFAULT_NETPORT "\n\n"
|
|
" -4 use only IPv4.\n"
|
|
" Default: use both IPv4 and IPv6 waiting sockets\n\n"
|
|
" -l <host_list> a file that contains a list of hosts that are allowed\n"
|
|
" to connect to this server (if more than one, list them one\n"
|
|
" per line).\n"
|
|
" We suggest to use literal names (instead of numeric ones)\n"
|
|
" in order to avoid problems with different address families.\n\n"
|
|
" -n permit NULL authentication (usually used with '-l')\n\n"
|
|
" -a <host,port> run in active mode when connecting to 'host' on port 'port'\n"
|
|
" In case 'port' is omitted, the default port (" RPCAP_DEFAULT_NETPORT_ACTIVE ") is used\n\n"
|
|
" -v run in active mode only (default: if '-a' is specified, it\n"
|
|
" accepts passive connections as well)\n\n"
|
|
" -d run in daemon mode (UNIX only) or as a service (Win32 only)\n"
|
|
" Warning (Win32): this switch is provided automatically when\n"
|
|
" the service is started from the control panel\n\n"
|
|
#ifndef _WIN32
|
|
" -i run in inetd mode (UNIX only)\n\n"
|
|
#endif
|
|
" -D log debugging messages\n\n"
|
|
#ifdef HAVE_OPENSSL
|
|
" -S encrypt all communication with SSL (implements rpcaps://)\n"
|
|
" -C enable compression\n"
|
|
" -K <pem_file> uses the SSL private key in this file (default: key.pem)\n"
|
|
" -X <pem_file> uses the certificate from this file (default: cert.pem)\n"
|
|
#endif
|
|
" -s <config_file> save the current configuration to file\n\n"
|
|
" -f <config_file> load the current configuration from file; all switches\n"
|
|
" specified from the command line are ignored\n\n"
|
|
" -h print this help screen\n\n";
|
|
|
|
(void)fprintf(f, "RPCAPD, a remote packet capture daemon.\n"
|
|
"Compiled with %s\n", pcap_lib_version());
|
|
#if defined(HAVE_OPENSSL) && defined(SSLEAY_VERSION)
|
|
(void)fprintf(f, "Compiled with %s\n", SSLeay_version(SSLEAY_VERSION));
|
|
#endif
|
|
(void)fprintf(f, "\n%s", usagetext);
|
|
}
|
|
|
|
|
|
|
|
//! Program main
|
|
int main(int argc, char *argv[])
|
|
{
|
|
char savefile[MAX_LINE + 1]; // name of the file on which we have to save the configuration
|
|
int log_to_systemlog = 0; // Non-zero if we should log to the "system log" rather than the standard error
|
|
int isdaemon = 0; // Non-zero if the user wants to run this program as a daemon
|
|
#ifndef _WIN32
|
|
int isrunbyinetd = 0; // Non-zero if this is being run by inetd or something inetd-like
|
|
#endif
|
|
int log_debug_messages = 0; // Non-zero if the user wants debug messages logged
|
|
int retval; // keeps the returning value from several functions
|
|
char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed
|
|
#ifndef _WIN32
|
|
struct sigaction action;
|
|
#endif
|
|
#ifdef HAVE_OPENSSL
|
|
int enable_compression = 0;
|
|
#endif
|
|
|
|
savefile[0] = 0;
|
|
loadfile[0] = 0;
|
|
hostlist[0] = 0;
|
|
|
|
// Initialize errbuf
|
|
memset(errbuf, 0, sizeof(errbuf));
|
|
|
|
pcap_strlcpy(address, RPCAP_DEFAULT_NETADDR, sizeof (address));
|
|
pcap_strlcpy(port, RPCAP_DEFAULT_NETPORT, sizeof (port));
|
|
|
|
// Prepare to open a new server socket
|
|
memset(&mainhints, 0, sizeof(struct addrinfo));
|
|
|
|
mainhints.ai_family = PF_UNSPEC;
|
|
mainhints.ai_flags = AI_PASSIVE; // Ready to a bind() socket
|
|
mainhints.ai_socktype = SOCK_STREAM;
|
|
|
|
// Getting the proper command line options
|
|
# ifdef HAVE_OPENSSL
|
|
# define SSL_CLOPTS "SK:X:C"
|
|
# else
|
|
# define SSL_CLOPTS ""
|
|
# endif
|
|
|
|
# define CLOPTS "b:dDhip:4l:na:s:f:v" SSL_CLOPTS
|
|
|
|
while ((retval = getopt(argc, argv, CLOPTS)) != -1)
|
|
{
|
|
switch (retval)
|
|
{
|
|
case 'D':
|
|
log_debug_messages = 1;
|
|
rpcapd_log_set(log_to_systemlog, log_debug_messages);
|
|
break;
|
|
case 'b':
|
|
pcap_strlcpy(address, optarg, sizeof (address));
|
|
break;
|
|
case 'p':
|
|
pcap_strlcpy(port, optarg, sizeof (port));
|
|
break;
|
|
case '4':
|
|
mainhints.ai_family = PF_INET; // IPv4 server only
|
|
break;
|
|
case 'd':
|
|
isdaemon = 1;
|
|
log_to_systemlog = 1;
|
|
rpcapd_log_set(log_to_systemlog, log_debug_messages);
|
|
break;
|
|
case 'i':
|
|
#ifdef _WIN32
|
|
printusage(stderr);
|
|
exit(1);
|
|
#else
|
|
isrunbyinetd = 1;
|
|
log_to_systemlog = 1;
|
|
rpcapd_log_set(log_to_systemlog, log_debug_messages);
|
|
#endif
|
|
break;
|
|
case 'n':
|
|
nullAuthAllowed = 1;
|
|
break;
|
|
case 'v':
|
|
passivemode = 0;
|
|
break;
|
|
case 'l':
|
|
{
|
|
pcap_strlcpy(hostlist, optarg, sizeof(hostlist));
|
|
break;
|
|
}
|
|
case 'a':
|
|
{
|
|
char *tmpaddress, *tmpport;
|
|
char *lasts;
|
|
int i = 0;
|
|
|
|
tmpaddress = pcap_strtok_r(optarg, RPCAP_HOSTLIST_SEP, &lasts);
|
|
|
|
while ((tmpaddress != NULL) && (i < MAX_ACTIVE_LIST))
|
|
{
|
|
tmpport = pcap_strtok_r(NULL, RPCAP_HOSTLIST_SEP, &lasts);
|
|
|
|
pcap_strlcpy(activelist[i].address, tmpaddress, sizeof (activelist[i].address));
|
|
|
|
if ((tmpport == NULL) || (strcmp(tmpport, "DEFAULT") == 0)) // the user choose a custom port
|
|
pcap_strlcpy(activelist[i].port, RPCAP_DEFAULT_NETPORT_ACTIVE, sizeof (activelist[i].port));
|
|
else
|
|
pcap_strlcpy(activelist[i].port, tmpport, sizeof (activelist[i].port));
|
|
|
|
tmpaddress = pcap_strtok_r(NULL, RPCAP_HOSTLIST_SEP, &lasts);
|
|
|
|
i++;
|
|
}
|
|
|
|
if (i > MAX_ACTIVE_LIST)
|
|
rpcapd_log(LOGPRIO_ERROR, "Only MAX_ACTIVE_LIST active connections are currently supported.");
|
|
|
|
// I don't initialize the remaining part of the structure, since
|
|
// it is already zeroed (it is a global var)
|
|
break;
|
|
}
|
|
case 'f':
|
|
pcap_strlcpy(loadfile, optarg, sizeof (loadfile));
|
|
break;
|
|
case 's':
|
|
pcap_strlcpy(savefile, optarg, sizeof (savefile));
|
|
break;
|
|
#ifdef HAVE_OPENSSL
|
|
case 'S':
|
|
uses_ssl = 1;
|
|
break;
|
|
case 'C':
|
|
enable_compression = 1;
|
|
break;
|
|
case 'K':
|
|
ssl_set_keyfile(optarg);
|
|
break;
|
|
case 'X':
|
|
ssl_set_certfile(optarg);
|
|
break;
|
|
#endif
|
|
case 'h':
|
|
printusage(stdout);
|
|
exit(0);
|
|
/*NOTREACHED*/
|
|
default:
|
|
exit(1);
|
|
/*NOTREACHED*/
|
|
}
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
if (isdaemon && isrunbyinetd)
|
|
{
|
|
rpcapd_log(LOGPRIO_ERROR, "rpcapd: -d and -i can't be used together");
|
|
exit(1);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// We want UTF-8 error messages.
|
|
//
|
|
if (pcap_init(PCAP_CHAR_ENC_UTF_8, errbuf) == -1)
|
|
{
|
|
rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
|
|
exit(-1);
|
|
}
|
|
pcap_fmt_set_encoding(PCAP_CHAR_ENC_UTF_8);
|
|
|
|
if (sock_init(errbuf, PCAP_ERRBUF_SIZE) == -1)
|
|
{
|
|
rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
|
|
exit(-1);
|
|
}
|
|
|
|
if (savefile[0] && fileconf_save(savefile))
|
|
rpcapd_log(LOGPRIO_DEBUG, "Error when saving the configuration to file");
|
|
|
|
// If the file does not exist, it keeps the settings provided by the command line
|
|
if (loadfile[0])
|
|
fileconf_read();
|
|
|
|
#ifdef _WIN32
|
|
//
|
|
// Create a handle to signal the main loop to tell it to do
|
|
// something.
|
|
//
|
|
state_change_event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (state_change_event == NULL)
|
|
{
|
|
sock_geterror("Can't create state change event", errbuf,
|
|
PCAP_ERRBUF_SIZE);
|
|
rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
|
|
exit(2);
|
|
}
|
|
|
|
//
|
|
// Catch control signals.
|
|
//
|
|
if (!SetConsoleCtrlHandler(main_ctrl_event, TRUE))
|
|
{
|
|
sock_geterror("Can't set control handler", errbuf,
|
|
PCAP_ERRBUF_SIZE);
|
|
rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
|
|
exit(2);
|
|
}
|
|
#else
|
|
memset(&action, 0, sizeof (action));
|
|
action.sa_handler = main_terminate;
|
|
action.sa_flags = 0;
|
|
sigemptyset(&action.sa_mask);
|
|
sigaction(SIGTERM, &action, NULL);
|
|
memset(&action, 0, sizeof (action));
|
|
action.sa_handler = main_reap_children;
|
|
action.sa_flags = 0;
|
|
sigemptyset(&action.sa_mask);
|
|
sigaction(SIGCHLD, &action, NULL);
|
|
// Ignore SIGPIPE - we'll get EPIPE when trying to write to a closed
|
|
// connection, we don't want to get killed by a signal in that case
|
|
signal(SIGPIPE, SIG_IGN);
|
|
#endif
|
|
|
|
# ifdef HAVE_OPENSSL
|
|
if (uses_ssl) {
|
|
if (ssl_init_once(1, enable_compression, errbuf, PCAP_ERRBUF_SIZE) < 0)
|
|
{
|
|
rpcapd_log(LOGPRIO_ERROR, "Can't initialize SSL: %s",
|
|
errbuf);
|
|
exit(2);
|
|
}
|
|
}
|
|
# endif
|
|
|
|
#ifndef _WIN32
|
|
if (isrunbyinetd)
|
|
{
|
|
//
|
|
// -i was specified, indicating that this is being run
|
|
// by inetd or something that can run network daemons
|
|
// as if it were inetd (xinetd, launchd, systemd, etc.).
|
|
//
|
|
// We assume that the program that launched us just
|
|
// duplicated a single socket for the connection
|
|
// to our standard input, output, and error, so we
|
|
// can just use the standard input as our control
|
|
// socket.
|
|
//
|
|
int sockctrl;
|
|
int devnull_fd;
|
|
|
|
//
|
|
// Duplicate the standard input as the control socket.
|
|
//
|
|
sockctrl = dup(0);
|
|
if (sockctrl == -1)
|
|
{
|
|
sock_geterror("Can't dup standard input", errbuf,
|
|
PCAP_ERRBUF_SIZE);
|
|
rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
|
|
exit(2);
|
|
}
|
|
|
|
//
|
|
// Try to set the standard input, output, and error
|
|
// to /dev/null.
|
|
//
|
|
devnull_fd = open("/dev/null", O_RDWR);
|
|
if (devnull_fd != -1)
|
|
{
|
|
//
|
|
// If this fails, just drive on.
|
|
//
|
|
(void)dup2(devnull_fd, 0);
|
|
(void)dup2(devnull_fd, 1);
|
|
(void)dup2(devnull_fd, 2);
|
|
close(devnull_fd);
|
|
}
|
|
|
|
//
|
|
// Handle this client.
|
|
// This is passive mode, so we don't care whether we were
|
|
// told by the client to close.
|
|
//
|
|
char *hostlist_copy = strdup(hostlist);
|
|
if (hostlist_copy == NULL)
|
|
{
|
|
rpcapd_log(LOGPRIO_ERROR, "Out of memory copying the host/port list");
|
|
exit(0);
|
|
}
|
|
(void)daemon_serviceloop(sockctrl, 0, hostlist_copy,
|
|
nullAuthAllowed, uses_ssl);
|
|
|
|
//
|
|
// Nothing more to do.
|
|
//
|
|
exit(0);
|
|
}
|
|
#endif
|
|
|
|
if (isdaemon)
|
|
{
|
|
//
|
|
// This is being run as a daemon.
|
|
// On UN*X, it might be manually run, or run from an
|
|
// rc file.
|
|
//
|
|
#ifndef _WIN32
|
|
int pid;
|
|
|
|
//
|
|
// Daemonize ourselves.
|
|
//
|
|
// Unix Network Programming, pg 336
|
|
//
|
|
if ((pid = fork()) != 0)
|
|
exit(0); // Parent terminates
|
|
|
|
// First child continues
|
|
// Set daemon mode
|
|
setsid();
|
|
|
|
// generated under unix with 'kill -HUP', needed to reload the configuration
|
|
memset(&action, 0, sizeof (action));
|
|
action.sa_handler = main_reread_config;
|
|
action.sa_flags = 0;
|
|
sigemptyset(&action.sa_mask);
|
|
sigaction(SIGHUP, &action, NULL);
|
|
|
|
if ((pid = fork()) != 0)
|
|
exit(0); // First child terminates
|
|
|
|
// LINUX WARNING: the current linux implementation of pthreads requires a management thread
|
|
// to handle some hidden stuff. So, as soon as you create the first thread, two threads are
|
|
// created. Fom this point on, the number of threads active are always one more compared
|
|
// to the number you're expecting
|
|
|
|
// Second child continues
|
|
// umask(0);
|
|
// chdir("/");
|
|
#else
|
|
//
|
|
// This is being run as a service on Windows.
|
|
//
|
|
// If this call succeeds, it is blocking on Win32
|
|
//
|
|
if (!svc_start())
|
|
rpcapd_log(LOGPRIO_DEBUG, "Unable to start the service");
|
|
|
|
// When the previous call returns, the entire application has to be stopped.
|
|
exit(0);
|
|
#endif
|
|
}
|
|
else // Console mode
|
|
{
|
|
#ifndef _WIN32
|
|
// Enable the catching of Ctrl+C
|
|
memset(&action, 0, sizeof (action));
|
|
action.sa_handler = main_terminate;
|
|
action.sa_flags = 0;
|
|
sigemptyset(&action.sa_mask);
|
|
sigaction(SIGINT, &action, NULL);
|
|
|
|
// generated under unix with 'kill -HUP', needed to reload the configuration
|
|
// We do not have this kind of signal in Win32
|
|
memset(&action, 0, sizeof (action));
|
|
action.sa_handler = main_reread_config;
|
|
action.sa_flags = 0;
|
|
sigemptyset(&action.sa_mask);
|
|
sigaction(SIGHUP, &action, NULL);
|
|
#endif
|
|
|
|
printf("Press CTRL + C to stop the server...\n");
|
|
}
|
|
|
|
// If we're a Win32 service, we have already called this function in the service_main
|
|
main_startup();
|
|
|
|
// The code should never arrive here (since the main_startup is blocking)
|
|
// however this avoids a compiler warning
|
|
exit(0);
|
|
}
|
|
|
|
void main_startup(void)
|
|
{
|
|
char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed
|
|
struct addrinfo *addrinfo; // keeps the addrinfo chain; required to open a new socket
|
|
int i;
|
|
#ifdef _WIN32
|
|
HANDLE threadId; // handle for the subthread
|
|
#else
|
|
pid_t pid;
|
|
#endif
|
|
|
|
i = 0;
|
|
addrinfo = NULL;
|
|
memset(errbuf, 0, sizeof(errbuf));
|
|
|
|
// Starts all the active threads
|
|
while ((i < MAX_ACTIVE_LIST) && (activelist[i].address[0] != 0))
|
|
{
|
|
activelist[i].ai_family = mainhints.ai_family;
|
|
|
|
#ifdef _WIN32
|
|
threadId = (HANDLE)_beginthreadex(NULL, 0, main_active,
|
|
(void *)&activelist[i], 0, NULL);
|
|
if (threadId == 0)
|
|
{
|
|
rpcapd_log(LOGPRIO_DEBUG, "Error creating the active child threads");
|
|
continue;
|
|
}
|
|
CloseHandle(threadId);
|
|
#else
|
|
if ((pid = fork()) == 0) // I am the child
|
|
{
|
|
main_active((void *) &activelist[i]);
|
|
exit(0);
|
|
}
|
|
#endif
|
|
i++;
|
|
}
|
|
|
|
/*
|
|
* The code that manages the active connections is not blocking;
|
|
* the code that manages the passive connection is blocking.
|
|
* So, if the user does not want to run in passive mode, we have
|
|
* to block the main thread here, otherwise the program ends and
|
|
* all threads are stopped.
|
|
*
|
|
* WARNING: this means that in case we have only active mode,
|
|
* the program does not terminate even if all the child thread
|
|
* terminates. The user has always to press Ctrl+C (or send a
|
|
* SIGTERM) to terminate the program.
|
|
*/
|
|
if (passivemode)
|
|
{
|
|
struct addrinfo *tempaddrinfo;
|
|
|
|
//
|
|
// Get a list of sockets on which to listen.
|
|
//
|
|
if (sock_initaddress((address[0]) ? address : NULL, port, &mainhints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1)
|
|
{
|
|
rpcapd_log(LOGPRIO_DEBUG, "%s", errbuf);
|
|
return;
|
|
}
|
|
|
|
for (tempaddrinfo = addrinfo; tempaddrinfo;
|
|
tempaddrinfo = tempaddrinfo->ai_next)
|
|
{
|
|
SOCKET sock;
|
|
struct listen_sock *sock_info;
|
|
|
|
if ((sock = sock_open(tempaddrinfo, SOCKOPEN_SERVER, SOCKET_MAXCONN, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
|
|
{
|
|
switch (tempaddrinfo->ai_family)
|
|
{
|
|
case AF_INET:
|
|
{
|
|
struct sockaddr_in *in;
|
|
char addrbuf[INET_ADDRSTRLEN];
|
|
|
|
in = (struct sockaddr_in *)tempaddrinfo->ai_addr;
|
|
rpcapd_log(LOGPRIO_WARNING, "Can't listen on socket for %s:%u: %s",
|
|
inet_ntop(AF_INET, &in->sin_addr,
|
|
addrbuf, sizeof (addrbuf)),
|
|
ntohs(in->sin_port),
|
|
errbuf);
|
|
break;
|
|
}
|
|
|
|
case AF_INET6:
|
|
{
|
|
struct sockaddr_in6 *in6;
|
|
char addrbuf[INET6_ADDRSTRLEN];
|
|
|
|
in6 = (struct sockaddr_in6 *)tempaddrinfo->ai_addr;
|
|
rpcapd_log(LOGPRIO_WARNING, "Can't listen on socket for %s:%u: %s",
|
|
inet_ntop(AF_INET6, &in6->sin6_addr,
|
|
addrbuf, sizeof (addrbuf)),
|
|
ntohs(in6->sin6_port),
|
|
errbuf);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
rpcapd_log(LOGPRIO_WARNING, "Can't listen on socket for address family %u: %s",
|
|
tempaddrinfo->ai_family,
|
|
errbuf);
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
sock_info = (struct listen_sock *) malloc(sizeof (struct listen_sock));
|
|
if (sock_info == NULL)
|
|
{
|
|
rpcapd_log(LOGPRIO_ERROR, "Can't allocate structure for listen socket");
|
|
exit(2);
|
|
}
|
|
sock_info->sock = sock;
|
|
sock_info->next = listen_socks;
|
|
listen_socks = sock_info;
|
|
}
|
|
|
|
freeaddrinfo(addrinfo);
|
|
|
|
if (listen_socks == NULL)
|
|
{
|
|
rpcapd_log(LOGPRIO_ERROR, "Can't listen on any address");
|
|
exit(2);
|
|
}
|
|
|
|
//
|
|
// Now listen on all of them, waiting for connections.
|
|
//
|
|
accept_connections();
|
|
}
|
|
|
|
//
|
|
// We're done; exit.
|
|
//
|
|
rpcapd_log(LOGPRIO_DEBUG, PROGRAM_NAME " is closing.\n");
|
|
|
|
#ifndef _WIN32
|
|
//
|
|
// Sends a KILL signal to all the processes in this process's
|
|
// process group; i.e., it kills all the child processes
|
|
// we've created.
|
|
//
|
|
// XXX - that also includes us, so we will be killed as well;
|
|
// that may cause a message to be printed or logged.
|
|
//
|
|
kill(0, SIGKILL);
|
|
#endif
|
|
|
|
//
|
|
// Just leave. We shouldn't need to clean up sockets or
|
|
// anything else, and if we try to do so, we'll could end
|
|
// up closing sockets, or shutting Winsock down, out from
|
|
// under service loops, causing all sorts of noisy error
|
|
// messages.
|
|
//
|
|
// We shouldn't need to worry about cleaning up any resources
|
|
// such as handles, sockets, threads, etc. - exit() should
|
|
// terminate the process, causing all those resources to be
|
|
// cleaned up (including the threads; Microsoft claims in the
|
|
// ExitProcess() documentation that, if ExitProcess() is called,
|
|
// "If a thread is waiting on a kernel object, it will not be
|
|
// terminated until the wait has completed.", but claims in the
|
|
// _beginthread()/_beginthreadex() documentation that "All threads
|
|
// are terminated if any thread calls abort, exit, _exit, or
|
|
// ExitProcess." - the latter appears to be the case, even for
|
|
// threads waiting on the event for a pcap_t).
|
|
//
|
|
exit(0);
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
static void
|
|
send_state_change_event(void)
|
|
{
|
|
char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed
|
|
|
|
if (!SetEvent(state_change_event))
|
|
{
|
|
sock_geterror("SetEvent on shutdown event failed", errbuf,
|
|
PCAP_ERRBUF_SIZE);
|
|
rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
|
|
}
|
|
}
|
|
|
|
void
|
|
send_shutdown_notification(void)
|
|
{
|
|
//
|
|
// Indicate that the server should shut down.
|
|
//
|
|
shutdown_server = 1;
|
|
|
|
//
|
|
// Send a state change event, to wake up WSAWaitForMultipleEvents().
|
|
//
|
|
send_state_change_event();
|
|
}
|
|
|
|
void
|
|
send_reread_configuration_notification(void)
|
|
{
|
|
//
|
|
// Indicate that the server should re-read its configuration file.
|
|
//
|
|
reread_config = 1;
|
|
|
|
//
|
|
// Send a state change event, to wake up WSAWaitForMultipleEvents().
|
|
//
|
|
send_state_change_event();
|
|
}
|
|
|
|
static BOOL WINAPI main_ctrl_event(DWORD ctrltype)
|
|
{
|
|
//
|
|
// ctrltype is one of:
|
|
//
|
|
// CTRL_C_EVENT - we got a ^C; this is like SIGINT
|
|
// CTRL_BREAK_EVENT - we got Ctrl+Break
|
|
// CTRL_CLOSE_EVENT - the console was closed; this is like SIGHUP
|
|
// CTRL_LOGOFF_EVENT - a user is logging off; this is received
|
|
// only by services
|
|
// CTRL_SHUTDOWN_EVENT - the systemis shutting down; this is
|
|
// received only by services
|
|
//
|
|
// For now, we treat all but CTRL_LOGOFF_EVENT as indications
|
|
// that we should shut down.
|
|
//
|
|
switch (ctrltype)
|
|
{
|
|
case CTRL_C_EVENT:
|
|
case CTRL_BREAK_EVENT:
|
|
case CTRL_CLOSE_EVENT:
|
|
case CTRL_SHUTDOWN_EVENT:
|
|
//
|
|
// Set a shutdown notification.
|
|
//
|
|
send_shutdown_notification();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We handled this.
|
|
//
|
|
return TRUE;
|
|
}
|
|
#else
|
|
static void main_terminate(int sign _U_)
|
|
{
|
|
//
|
|
// Note that the server should shut down.
|
|
// select() should get an EINTR error when we return,
|
|
// so it will wake up and know it needs to check the flag.
|
|
//
|
|
shutdown_server = 1;
|
|
}
|
|
|
|
static void main_reread_config(int sign _U_)
|
|
{
|
|
//
|
|
// Note that the server should re-read its configuration file.
|
|
// select() should get an EINTR error when we return,
|
|
// so it will wake up and know it needs to check the flag.
|
|
//
|
|
reread_config = 1;
|
|
}
|
|
|
|
static void main_reap_children(int sign _U_)
|
|
{
|
|
pid_t pid;
|
|
int exitstat;
|
|
|
|
// Reap all child processes that have exited.
|
|
// For reference, Stevens, pg 128
|
|
|
|
while ((pid = waitpid(-1, &exitstat, WNOHANG)) > 0)
|
|
rpcapd_log(LOGPRIO_DEBUG, "Child terminated");
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Loop waiting for incoming connections and accepting them.
|
|
//
|
|
static void
|
|
accept_connections(void)
|
|
{
|
|
#ifdef _WIN32
|
|
struct listen_sock *sock_info;
|
|
DWORD num_events;
|
|
WSAEVENT *events;
|
|
int i;
|
|
char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed
|
|
|
|
//
|
|
// How big does the set of events need to be?
|
|
// One for the shutdown event, plus one for every socket on which
|
|
// we'll be listening.
|
|
//
|
|
num_events = 1; // shutdown event
|
|
for (sock_info = listen_socks; sock_info;
|
|
sock_info = sock_info->next)
|
|
{
|
|
if (num_events == WSA_MAXIMUM_WAIT_EVENTS)
|
|
{
|
|
//
|
|
// WSAWaitForMultipleEvents() doesn't support
|
|
// more than WSA_MAXIMUM_WAIT_EVENTS events
|
|
// on which to wait.
|
|
//
|
|
rpcapd_log(LOGPRIO_ERROR, "Too many sockets on which to listen");
|
|
exit(2);
|
|
}
|
|
num_events++;
|
|
}
|
|
|
|
//
|
|
// Allocate the array of events.
|
|
//
|
|
events = (WSAEVENT *) malloc(num_events * sizeof (WSAEVENT));
|
|
if (events == NULL)
|
|
{
|
|
rpcapd_log(LOGPRIO_ERROR, "Can't allocate array of events which to listen");
|
|
exit(2);
|
|
}
|
|
|
|
//
|
|
// Fill it in.
|
|
//
|
|
events[0] = state_change_event; // state change event first
|
|
for (sock_info = listen_socks, i = 1; sock_info;
|
|
sock_info = sock_info->next, i++)
|
|
{
|
|
WSAEVENT event;
|
|
|
|
//
|
|
// Create an event that is signaled if there's a connection
|
|
// to accept on the socket in question.
|
|
//
|
|
event = WSACreateEvent();
|
|
if (event == WSA_INVALID_EVENT)
|
|
{
|
|
sock_geterror("Can't create socket event", errbuf,
|
|
PCAP_ERRBUF_SIZE);
|
|
rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
|
|
exit(2);
|
|
}
|
|
if (WSAEventSelect(sock_info->sock, event, FD_ACCEPT) == SOCKET_ERROR)
|
|
{
|
|
sock_geterror("Can't setup socket event", errbuf,
|
|
PCAP_ERRBUF_SIZE);
|
|
rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
|
|
exit(2);
|
|
}
|
|
events[i] = event;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
//
|
|
// Wait for incoming connections.
|
|
//
|
|
DWORD ret;
|
|
|
|
ret = WSAWaitForMultipleEvents(num_events, events, FALSE,
|
|
WSA_INFINITE, FALSE);
|
|
if (ret == WSA_WAIT_FAILED)
|
|
{
|
|
sock_geterror("WSAWaitForMultipleEvents failed", errbuf,
|
|
PCAP_ERRBUF_SIZE);
|
|
rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
|
|
exit(2);
|
|
}
|
|
|
|
if (ret == WSA_WAIT_EVENT_0)
|
|
{
|
|
//
|
|
// The state change event was set.
|
|
//
|
|
if (shutdown_server)
|
|
{
|
|
//
|
|
// Time to quit. Exit the loop.
|
|
//
|
|
break;
|
|
}
|
|
if (reread_config)
|
|
{
|
|
//
|
|
// We should re-read the configuration
|
|
// file.
|
|
//
|
|
reread_config = 0; // clear the indicator
|
|
fileconf_read();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check each socket.
|
|
//
|
|
for (sock_info = listen_socks, i = 1; sock_info;
|
|
sock_info = sock_info->next, i++)
|
|
{
|
|
WSANETWORKEVENTS network_events;
|
|
|
|
if (WSAEnumNetworkEvents(sock_info->sock,
|
|
events[i], &network_events) == SOCKET_ERROR)
|
|
{
|
|
sock_geterror("WSAEnumNetworkEvents failed",
|
|
errbuf, PCAP_ERRBUF_SIZE);
|
|
rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
|
|
exit(2);
|
|
}
|
|
if (network_events.lNetworkEvents & FD_ACCEPT)
|
|
{
|
|
//
|
|
// Did an error occur?
|
|
//
|
|
if (network_events.iErrorCode[FD_ACCEPT_BIT] != 0)
|
|
{
|
|
//
|
|
// Yes - report it and keep going.
|
|
//
|
|
sock_fmterror("Socket error",
|
|
network_events.iErrorCode[FD_ACCEPT_BIT],
|
|
errbuf,
|
|
PCAP_ERRBUF_SIZE);
|
|
rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Accept the connection.
|
|
//
|
|
accept_connection(sock_info->sock);
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
struct listen_sock *sock_info;
|
|
int num_sock_fds;
|
|
|
|
//
|
|
// How big does the bitset of sockets on which to select() have
|
|
// to be?
|
|
//
|
|
num_sock_fds = 0;
|
|
for (sock_info = listen_socks; sock_info; sock_info = sock_info->next)
|
|
{
|
|
if (sock_info->sock + 1 > num_sock_fds)
|
|
{
|
|
if ((unsigned int)(sock_info->sock + 1) >
|
|
(unsigned int)FD_SETSIZE)
|
|
{
|
|
rpcapd_log(LOGPRIO_ERROR, "Socket FD is too bit for an fd_set");
|
|
exit(2);
|
|
}
|
|
num_sock_fds = sock_info->sock + 1;
|
|
}
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
fd_set sock_fds;
|
|
int ret;
|
|
|
|
//
|
|
// Set up an fd_set for all the sockets on which we're
|
|
// listening.
|
|
//
|
|
// This set is modified by select(), so we have to
|
|
// construct it anew each time.
|
|
//
|
|
FD_ZERO(&sock_fds);
|
|
for (sock_info = listen_socks; sock_info;
|
|
sock_info = sock_info->next)
|
|
{
|
|
FD_SET(sock_info->sock, &sock_fds);
|
|
}
|
|
|
|
//
|
|
// Wait for incoming connections.
|
|
//
|
|
ret = select(num_sock_fds, &sock_fds, NULL, NULL, NULL);
|
|
if (ret == -1)
|
|
{
|
|
if (errno == EINTR)
|
|
{
|
|
//
|
|
// If this is a "terminate the
|
|
// server" signal, exit the loop,
|
|
// otherwise just keep trying.
|
|
//
|
|
if (shutdown_server)
|
|
{
|
|
//
|
|
// Time to quit. Exit the loop.
|
|
//
|
|
break;
|
|
}
|
|
if (reread_config)
|
|
{
|
|
//
|
|
// We should re-read the configuration
|
|
// file.
|
|
//
|
|
reread_config = 0; // clear the indicator
|
|
fileconf_read();
|
|
}
|
|
|
|
//
|
|
// Go back and wait again.
|
|
//
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
rpcapd_log(LOGPRIO_ERROR, "select failed: %s",
|
|
strerror(errno));
|
|
exit(2);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check each socket.
|
|
//
|
|
for (sock_info = listen_socks; sock_info;
|
|
sock_info = sock_info->next)
|
|
{
|
|
if (FD_ISSET(sock_info->sock, &sock_fds))
|
|
{
|
|
//
|
|
// Accept the connection.
|
|
//
|
|
accept_connection(sock_info->sock);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Close all the listen sockets.
|
|
//
|
|
for (sock_info = listen_socks; sock_info; sock_info = sock_info->next)
|
|
{
|
|
closesocket(sock_info->sock);
|
|
}
|
|
sock_cleanup();
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
//
|
|
// A structure to hold the parameters to the daemon service loop
|
|
// thread on Windows.
|
|
//
|
|
// (On UN*X, there is no need for this explicit copy since the
|
|
// fork "inherits" the parent stack.)
|
|
//
|
|
struct params_copy {
|
|
SOCKET sockctrl;
|
|
char *hostlist;
|
|
};
|
|
#endif
|
|
|
|
//
|
|
// Accept a connection and start a worker thread, on Windows, or a
|
|
// worker process, on UN*X, to handle the connection.
|
|
//
|
|
static void
|
|
accept_connection(SOCKET listen_sock)
|
|
{
|
|
char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed
|
|
SOCKET sockctrl; // keeps the socket ID for this control connection
|
|
struct sockaddr_storage from; // generic sockaddr_storage variable
|
|
socklen_t fromlen; // keeps the length of the sockaddr_storage variable
|
|
|
|
#ifdef _WIN32
|
|
HANDLE threadId; // handle for the subthread
|
|
u_long off = 0;
|
|
struct params_copy *params_copy = NULL;
|
|
#else
|
|
pid_t pid;
|
|
#endif
|
|
|
|
// Initialize errbuf
|
|
memset(errbuf, 0, sizeof(errbuf));
|
|
|
|
for (;;)
|
|
{
|
|
// Accept the connection
|
|
fromlen = sizeof(struct sockaddr_storage);
|
|
|
|
sockctrl = accept(listen_sock, (struct sockaddr *) &from, &fromlen);
|
|
|
|
if (sockctrl != INVALID_SOCKET)
|
|
{
|
|
// Success.
|
|
break;
|
|
}
|
|
|
|
// The accept() call can return this error when a signal is caught
|
|
// In this case, we have simply to ignore this error code
|
|
// Stevens, pg 124
|
|
#ifdef _WIN32
|
|
if (WSAGetLastError() == WSAEINTR)
|
|
#else
|
|
if (errno == EINTR)
|
|
#endif
|
|
continue;
|
|
|
|
// Don't check for errors here, since the error can be due to the fact that the thread
|
|
// has been killed
|
|
sock_geterror("accept()", errbuf, PCAP_ERRBUF_SIZE);
|
|
rpcapd_log(LOGPRIO_ERROR, "Accept of control connection from client failed: %s",
|
|
errbuf);
|
|
return;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
//
|
|
// Put the socket back into blocking mode; doing WSAEventSelect()
|
|
// on the listen socket makes that socket non-blocking, and it
|
|
// appears that sockets returned from an accept() on that socket
|
|
// are also non-blocking.
|
|
//
|
|
// First, we have to un-WSAEventSelect() this socket, and then
|
|
// we can turn non-blocking mode off.
|
|
//
|
|
// If this fails, we aren't guaranteed that, for example, any
|
|
// of the error message will be sent - if it can't be put in
|
|
// the socket queue, the send will just fail.
|
|
//
|
|
// So we just log the message and close the connection.
|
|
//
|
|
if (WSAEventSelect(sockctrl, NULL, 0) == SOCKET_ERROR)
|
|
{
|
|
sock_geterror("WSAEventSelect()", errbuf, PCAP_ERRBUF_SIZE);
|
|
rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
|
|
sock_close(sockctrl, NULL, 0);
|
|
return;
|
|
}
|
|
if (ioctlsocket(sockctrl, FIONBIO, &off) == SOCKET_ERROR)
|
|
{
|
|
sock_geterror("ioctlsocket(FIONBIO)", errbuf, PCAP_ERRBUF_SIZE);
|
|
rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
|
|
sock_close(sockctrl, NULL, 0);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Make a copy of the host list to pass to the new thread, so that
|
|
// if we update it in the main thread, it won't catch us in the
|
|
// middle of updating it.
|
|
//
|
|
// daemon_serviceloop() will free it once it's done with it.
|
|
//
|
|
char *hostlist_copy = strdup(hostlist);
|
|
if (hostlist_copy == NULL)
|
|
{
|
|
rpcapd_log(LOGPRIO_ERROR, "Out of memory copying the host/port list");
|
|
sock_close(sockctrl, NULL, 0);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Allocate a location to hold the values of sockctrl.
|
|
// It will be freed in the newly-created thread once it's
|
|
// finished with it.
|
|
//
|
|
params_copy = malloc(sizeof(*params_copy));
|
|
if (params_copy == NULL)
|
|
{
|
|
rpcapd_log(LOGPRIO_ERROR, "Out of memory allocating the parameter copy structure");
|
|
free(hostlist_copy);
|
|
sock_close(sockctrl, NULL, 0);
|
|
return;
|
|
}
|
|
params_copy->sockctrl = sockctrl;
|
|
params_copy->hostlist = hostlist_copy;
|
|
|
|
threadId = (HANDLE)_beginthreadex(NULL, 0,
|
|
main_passive_serviceloop_thread, (void *) params_copy, 0, NULL);
|
|
if (threadId == 0)
|
|
{
|
|
rpcapd_log(LOGPRIO_ERROR, "Error creating the child thread");
|
|
free(params_copy);
|
|
free(hostlist_copy);
|
|
sock_close(sockctrl, NULL, 0);
|
|
return;
|
|
}
|
|
CloseHandle(threadId);
|
|
#else /* _WIN32 */
|
|
pid = fork();
|
|
if (pid == -1)
|
|
{
|
|
rpcapd_log(LOGPRIO_ERROR, "Error creating the child process: %s",
|
|
strerror(errno));
|
|
sock_close(sockctrl, NULL, 0);
|
|
return;
|
|
}
|
|
if (pid == 0)
|
|
{
|
|
//
|
|
// Child process.
|
|
//
|
|
// Close the socket on which we're listening (must
|
|
// be open only in the parent).
|
|
//
|
|
closesocket(listen_sock);
|
|
|
|
#if 0
|
|
//
|
|
// Modify thread params so that it can be killed at any time
|
|
// XXX - is this necessary? This is the main and, currently,
|
|
// only thread in the child process, and nobody tries to
|
|
// cancel us, although *we* may cancel the thread that's
|
|
// handling the capture loop.
|
|
//
|
|
if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL))
|
|
goto end;
|
|
if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL))
|
|
goto end;
|
|
#endif
|
|
|
|
//
|
|
// Run the service loop.
|
|
// This is passive mode, so we don't care whether we were
|
|
// told by the client to close.
|
|
//
|
|
char *hostlist_copy = strdup(hostlist);
|
|
if (hostlist_copy == NULL)
|
|
{
|
|
rpcapd_log(LOGPRIO_ERROR, "Out of memory copying the host/port list");
|
|
exit(0);
|
|
}
|
|
(void)daemon_serviceloop(sockctrl, 0, hostlist_copy,
|
|
nullAuthAllowed, uses_ssl);
|
|
|
|
exit(0);
|
|
}
|
|
|
|
// I am the parent
|
|
// Close the socket for this session (must be open only in the child)
|
|
closesocket(sockctrl);
|
|
#endif /* _WIN32 */
|
|
}
|
|
|
|
/*!
|
|
\brief 'true' main of the program in case the active mode is turned on.
|
|
|
|
This function loops forever trying to connect to the remote host, until the
|
|
daemon is turned down.
|
|
|
|
\param ptr: it keeps the 'activepars' parameters. It is a 'void *'
|
|
just because the thread APIs want this format.
|
|
*/
|
|
#ifdef _WIN32
|
|
static unsigned __stdcall
|
|
#else
|
|
static void *
|
|
#endif
|
|
main_active(void *ptr)
|
|
{
|
|
char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed
|
|
SOCKET sockctrl; // keeps the socket ID for this control connection
|
|
struct addrinfo hints; // temporary struct to keep settings needed to open the new socket
|
|
struct addrinfo *addrinfo; // keeps the addrinfo chain; required to open a new socket
|
|
struct active_pars *activepars;
|
|
|
|
activepars = (struct active_pars *) ptr;
|
|
|
|
// Prepare to open a new server socket
|
|
memset(&hints, 0, sizeof(struct addrinfo));
|
|
// WARNING Currently it supports only ONE socket family among IPv4 and IPv6
|
|
hints.ai_family = AF_INET; // PF_UNSPEC to have both IPv4 and IPv6 server
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_family = activepars->ai_family;
|
|
|
|
rpcapd_log(LOGPRIO_DEBUG, "Connecting to host %s, port %s, using protocol %s",
|
|
activepars->address, activepars->port, (hints.ai_family == AF_INET) ? "IPv4":
|
|
(hints.ai_family == AF_INET6) ? "IPv6" : "Unspecified");
|
|
|
|
// Initialize errbuf
|
|
memset(errbuf, 0, sizeof(errbuf));
|
|
|
|
// Do the work
|
|
if (sock_initaddress(activepars->address, activepars->port, &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1)
|
|
{
|
|
rpcapd_log(LOGPRIO_DEBUG, "%s", errbuf);
|
|
return 0;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
int activeclose;
|
|
|
|
if ((sockctrl = sock_open(addrinfo, SOCKOPEN_CLIENT, 0, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
|
|
{
|
|
rpcapd_log(LOGPRIO_DEBUG, "%s", errbuf);
|
|
|
|
snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error connecting to host %s, port %s, using protocol %s",
|
|
activepars->address, activepars->port, (hints.ai_family == AF_INET) ? "IPv4":
|
|
(hints.ai_family == AF_INET6) ? "IPv6" : "Unspecified");
|
|
|
|
rpcapd_log(LOGPRIO_DEBUG, "%s", errbuf);
|
|
|
|
sleep_secs(RPCAP_ACTIVE_WAIT);
|
|
|
|
continue;
|
|
}
|
|
|
|
char *hostlist_copy = strdup(hostlist);
|
|
if (hostlist_copy == NULL)
|
|
{
|
|
rpcapd_log(LOGPRIO_ERROR, "Out of memory copying the host/port list");
|
|
activeclose = 0;
|
|
sock_close(sockctrl, NULL, 0);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// daemon_serviceloop() will free the copy.
|
|
//
|
|
activeclose = daemon_serviceloop(sockctrl, 1,
|
|
hostlist_copy, nullAuthAllowed, uses_ssl);
|
|
}
|
|
|
|
// If the connection is closed by the user explicitly, don't try to connect to it again
|
|
// just exit the program
|
|
if (activeclose == 1)
|
|
break;
|
|
}
|
|
|
|
freeaddrinfo(addrinfo);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
//
|
|
// Main routine of a passive-mode service thread.
|
|
//
|
|
unsigned __stdcall main_passive_serviceloop_thread(void *ptr)
|
|
{
|
|
struct params_copy params = *(struct params_copy *)ptr;
|
|
free(ptr);
|
|
|
|
//
|
|
// Handle this client.
|
|
// This is passive mode, so we don't care whether we were
|
|
// told by the client to close.
|
|
//
|
|
(void)daemon_serviceloop(params.sockctrl, 0, params.hostlist,
|
|
nullAuthAllowed, uses_ssl);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|