📄 rpcapd.c
字号:
/*
* 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.
*
*/
#include <errno.h> // for the errno variable
#include <string.h> // for strtok, etc
#include <stdlib.h> // for malloc(), free(), ...
#include <pcap.h> // for PCAP_ERRBUF_SIZE
#include <signal.h> // for signal()
#include <pthread.h>
#include "rpcapd.h"
#include "fileconf.h" // for the configuration file management
#include "pcap-remote.h"
#include "daemon.h" // the true main() method of this daemon
#include "utils.h" // Missing calls and such
#include "sockutils.h" // for socket calls
#ifndef WIN32
#include <unistd.h> // for exit()
#include <sys/wait.h> // waitpid()
#else
#include "win32-svc.h" // for Win32 service stuff
#endif
// 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
SOCKET sockmain; //!< keeps the main socket identifier
char loadfile[MAX_LINE + 1]; //!< Name of the file from which we have to load the configuration
int passivemode= 1; //!< '1' if we want to run in passive mode as well
struct addrinfo mainhints; //!< temporary struct to keep settings needed to open the new socket
char address[MAX_LINE + 1]; //!< keeps the network address (either numeric or literal) to bind to
char port[MAX_LINE + 1]; //!< keeps the network port to bind to
extern char *optarg; // for getopt()
// Function definition
void main_passive(void *ptr);
void main_active(void *ptr);
#ifndef WIN32
void main_cleanup_childs(int sign);
#endif
/*!
\brief Prints the usage screen if it is launched in console mode.
*/
void printusage()
{
char *usagetext =
"USAGE:\n"
" " PROGRAM_NAME " [-b <address>] [-p <port>] [-6] [-l <host_list>] [-a <host,port>]\n"
" [-n] [-v] [-d] [-s <file>] [-f <file>]\n"
" -b <address>: the address to bind to (either numeric or literal).\n"
" Default: it binds to all local IPv4 addresses\n"
" -p <port>: the port to bind to. Default: it binds to port " RPCAP_DEFAULT_NETPORT "\n"
" -4: use only IPv4 (default both IPv4 and IPv6 waiting sockets are used)\n"
" -l <host_list>: a file that keeps the list of the hosts which\n"
" are allowed to connect to this server (if more than one,\n"
" list them one per line). We suggest to use \n"
" literal names (instead of numeric ones) in order to avoid\n"
" problems with different address families\n"
" -n: permit NULL authentication (usually used with '-l')\n"
" -a <host, port>: run in active mode when connecting to 'host' on port 'port'\n"
" -v: run in active mode only (default: if '-a' is specified, it accepts passive\n"
" connections as well\n"
" -d: run in daemon mode (UNIX only) or as a service (Win32 only)\n"
" Warning (Win32): this switch is provided automatically when the service\n"
" is started from the control panel\n"
" -s <file>: save the current configuration to file\n"
" -f <file>: load the current configuration from file; all the switches\n"
" specified from the command line are ignored\n"
" -h: print this help screen\n\n";
printf(usagetext);
}
//! Program main
int main(int argc, char *argv[], char *envp[])
{
char savefile[MAX_LINE + 1]; // name of the file on which we have to save the configuration
int isdaemon= 0; // Not null if the user wants to run this program as a daemon
int retval; // keeps the returning value from several functions
char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed
savefile[0]= 0;
loadfile[0]= 0;
hostlist[0]= 0;
// Initialize errbuf
memset(errbuf, 0, sizeof(errbuf) );
if (sock_init(errbuf) == -1)
{
SOCK_ASSERT(errbuf, 1);
exit(-1);
}
strncpy(address, RPCAP_DEFAULT_NETADDR, MAX_LINE);
strncpy(port, RPCAP_DEFAULT_NETPORT, MAX_LINE);
// 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
while ((retval = getopt(argc, argv, "b:dhp:4l:na:s:f:v")) != -1)
{
switch (retval)
{
case 'b':
strncpy(address, optarg, MAX_LINE);
break;
case 'p':
strncpy(port, optarg, MAX_LINE);
break;
case '4':
mainhints.ai_family = PF_INET; // IPv4 server only
break;
case 'd':
isdaemon= 1;
break;
case 'n':
nullAuthAllowed= 1;
break;
case 'v':
passivemode= 0;
break;
case 'l':
{
strncpy(hostlist, optarg, sizeof(hostlist) );
break;
}
case 'a':
{
char *tmpaddress, *tmpport;
int i= 0;
tmpaddress= strtok(optarg, RPCAP_HOSTLIST_SEP);
while ( (tmpaddress != NULL) && (i < MAX_ACTIVE_LIST) )
{
tmpport= strtok(NULL, RPCAP_HOSTLIST_SEP);
snprintf(activelist[i].address, MAX_LINE, tmpaddress);
if ( (tmpport == NULL) || (strcmp(tmpport, "DEFAULT") == 0) ) // the user choose a custom port
snprintf(activelist[i].port, MAX_LINE, RPCAP_DEFAULT_NETPORT_ACTIVE);
else
snprintf(activelist[i].port, MAX_LINE, tmpport);
tmpaddress = strtok(NULL, RPCAP_HOSTLIST_SEP);
i++;
}
if (i > MAX_ACTIVE_LIST)
SOCK_ASSERT("Only MAX_ACTIVE_LIST active connections are currently supported.", 1);
// I don't initialize the remaining part of the structure, since
// it is already zeroed (it is a global var)
break;
}
case 'f':
strncpy(loadfile, optarg, MAX_LINE);
break;
case 's':
strncpy(savefile, optarg, MAX_LINE);
break;
case 'h':
printusage();
exit(0);
default:
break;
}
}
if (savefile[0])
{
if (fileconf_save(savefile) )
SOCK_ASSERT("Error when saving the configuration to file", 1);
}
// If the file does not exist, it keeps the settings provided by the command line
if (loadfile[0])
fileconf_read(0);
#ifdef linux
// SIGTERM (i.e. kill -15) is not generated in WIN32, although it is included for ANSI compatibility
signal(SIGTERM, main_cleanup);
signal(SIGCHLD, main_cleanup_childs);
#endif
// forking a daemon, if it is needed
if (isdaemon)
{
#ifndef WIN32
int pid;
// 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'
signal(SIGHUP, fileconf_read);
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
// We use the SIGABRT signal to kill the Win32 service
signal(SIGABRT, main_cleanup);
// If this call succeeds, it is blocking on Win32
if ( svc_start() != 1)
SOCK_ASSERT(1, "Unable to start the service");
// When the previous call returns, the entire application has to be stopped.
exit(0);
#endif
}
else // Console mode
{
// Enable the catching of Ctrl+C
signal(SIGINT, main_cleanup);
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
pthread_t threadId; // Pthread variable that keeps the thread structures
#else
pid_t pid;
#endif
i= 0;
addrinfo= NULL;
memset(errbuf, 0, sizeof(errbuf) );
// Starts all the active threads
while ( (activelist[i].address[0] != 0) && (i < MAX_ACTIVE_LIST) )
{
activelist[i].ai_family= mainhints.ai_family;
#ifdef WIN32
if ( pthread_create( &threadId, NULL, (void *) &main_active, (void *) &activelist[i]) )
{
SOCK_ASSERT("Error creating the active child thread", 1);
continue;
}
#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;
vice versa, the code that manages the passive connection is blocking.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -