📄 daemon.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 <pcap.h> // for libpcap/WinPcap calls
#include <pcap-int.h> // for the pcap_t definition
#include <errno.h> // for the errno variable
#include <stdlib.h> // for malloc(), free(), ...
#include <string.h> // for strlen(), ...
#include <pthread.h>
#include "pcap-remote.h"
#include "daemon.h"
#include "sockutils.h" // for socket calls
#ifndef WIN32 // for select() and such
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <pwd.h> // for password management
#endif
#ifdef linux
#include <shadow.h> // for password management
#endif
// Locally defined functions
int daemon_checkauth(SOCKET sockctrl, int nullAuthAllowed, char *errbuf);
int daemon_AuthUserPwd(char *username, char *password, char *errbuf);
int daemon_findalldevs(SOCKET sockctrl, char *errbuf);
int daemon_opensource(SOCKET sockctrl, char *source, int srclen, uint32 plen, char *errbuf);
pcap_t *daemon_startcapture(SOCKET sockctrl, pthread_t *threaddata, char *source, int active,
struct rpcap_sampling *samp_param, uint32 plen, char *errbuf);
int daemon_endcapture(pcap_t *fp, pthread_t *threaddata, char *errbuf);
int daemon_updatefilter(pcap_t *fp, uint32 plen);
int daemon_unpackapplyfilter(pcap_t *fp, unsigned int *nread, int *plen, char *errbuf);
int daemon_getstats(pcap_t *fp);
int daemon_getstatsnopcap(SOCKET sockctrl, unsigned int ifdrops, unsigned int ifrecv,
unsigned int krnldrop, unsigned int svrcapt, char *errbuf);
int daemon_setsampling(SOCKET sockctrl, struct rpcap_sampling *samp_param, int plen, char *errbuf);
void daemon_seraddr(struct sockaddr_storage *sockaddrin, struct sockaddr_storage *sockaddrout);
void *daemon_thrdatamain(void *ptr);
/*!
\brief Main serving funtion
This function is the one which does the job. It is the main() of the child
thread, which is created as soon as a new connection is accepted.
\param ptr: a void pointer that keeps the reference of the 'pthread_chain'
value corrisponding to this thread. This variable is casted into a 'pthread_chain'
value in order to retrieve the socket we're currently using, the therad ID, and
some pointers to the previous and next elements into this struct.
\return None.
*/
void daemon_serviceloop( void *ptr )
{
char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed
char source[PCAP_BUF_SIZE]; // keeps the string that contains the interface to open
struct rpcap_header header; // RPCAP message general header
pcap_t *fp= NULL; // pcap_t main variable
struct daemon_slpars *pars; // parameters related to the present daemon loop
pthread_t threaddata= 0; // handle to the 'read from daemon and send to client' thread
unsigned int ifdrops, ifrecv, krnldrop, svrcapt; // needed to save the values of the statistics
struct rpcap_sampling samp_param; // in case sampling has been requested
// Structures needed for the select() call
fd_set rfds; // set of socket descriptors we have to check
struct timeval tv; // maximum time the select() can block waiting for data
int retval; // select() return value
pars= (struct daemon_slpars *) ptr;
*errbuf= 0; // Initialize errbuf
// If we're in active mode, this is not a separate thread
if (! pars->isactive)
{
// Modify thread params so that it can be killed at any time
if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) )
goto end;
if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) )
goto end;
}
auth_again:
// If we're in active mode, we have to check for the initial timeout
if (!pars->isactive)
{
FD_ZERO(&rfds);
// We do not have to block here
tv.tv_sec = RPCAP_TIMEOUT_INIT;
tv.tv_usec = 0;
FD_SET(pars->sockctrl, &rfds);
retval = select(pars->sockctrl + 1, &rfds, NULL, NULL, &tv);
if (retval == -1)
{
sock_geterror("select(): ", errbuf, PCAP_ERRBUF_SIZE);
rpcap_senderror(pars->sockctrl, errbuf, PCAP_ERR_NETW, NULL);
goto end;
}
// The timeout has expired
// So, this was a fake connection. Drop it down
if (retval == 0)
{
rpcap_senderror(pars->sockctrl, "The RPCAP initial timeout has expired", PCAP_ERR_INITTIMEOUT, NULL);
goto end;
}
}
retval= daemon_checkauth(pars->sockctrl, pars->nullAuthAllowed, errbuf);
if (retval)
{
// the other user requested to close the connection
// It can be also the case of 'active mode', in which this host is not
// allowed to connect to the other peer; in that case, it drops down the connection
if (retval == -3)
goto end;
// It can be an authentication failure or an unrecoverable error
rpcap_senderror(pars->sockctrl, errbuf, PCAP_ERR_AUTH, NULL);
// authentication error
if (retval == -2)
{
// suspend for 1 sec
// WARNING: this day is inserted only in this point; if the user drops down the connection
// and it connects again, this suspension time does not have any effects.
pthread_suspend(RPCAP_SUSPEND_WRONGAUTH*1000);
goto auth_again;
}
// Unrecoverable error
if (retval == -1)
goto end;
}
while (1)
{
int retval;
errbuf[0]= 0; // clear errbuf
// Avoid zombies connections; check if the connection is opens but no commands are performed
// from more than RPCAP_TIMEOUT_RUNTIME
// Conditions:
// - I have to be in normal mode (no active mode)
// - if the device is open, I don't have to be in the middle of a capture (fp->rmt_sockdata)
// - if the device is closed, I have always to check if a new command arrives
//
// Be carefully: the capture can have been started, but an error occurred (so fp != NULL, but
// rmt_sockdata is 0
if ( (!pars->isactive) && ( (fp == NULL) || ( (fp != NULL) && (fp->rmt_sockdata == 0) ) ))
{
// Check for the initial timeout
FD_ZERO(&rfds);
// We do not have to block here
tv.tv_sec = RPCAP_TIMEOUT_RUNTIME;
tv.tv_usec = 0;
FD_SET(pars->sockctrl, &rfds);
retval = select(pars->sockctrl + 1, &rfds, NULL, NULL, &tv);
if (retval == -1)
{
sock_geterror("select(): ", errbuf, PCAP_ERRBUF_SIZE);
rpcap_senderror(pars->sockctrl, errbuf, PCAP_ERR_NETW, NULL);
goto end;
}
// The timeout has expired
// So, this was a fake connection. Drop it down
if (retval == 0)
{
SOCK_ASSERT("The RPCAP runtime timeout has expired", 1);
rpcap_senderror(pars->sockctrl, "The RPCAP runtime timeout has expired", PCAP_ERR_RUNTIMETIMEOUT, NULL);
goto end;
}
}
if (sock_recv(pars->sockctrl, (char *) &header, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE) == -1)
goto end;
// Checks if the message is correct
// In case it is wrong, it discard the data
retval= rpcap_checkmsg(errbuf, pars->sockctrl, &header,
RPCAP_MSG_FINDALLIF_REQ,
RPCAP_MSG_OPEN_REQ,
RPCAP_MSG_STARTCAP_REQ,
RPCAP_MSG_UPDATEFILTER_REQ,
RPCAP_MSG_STATS_REQ,
RPCAP_MSG_ENDCAP_REQ,
RPCAP_MSG_SETSAMPLING_REQ,
RPCAP_MSG_CLOSE,
RPCAP_MSG_ERROR,
0);
switch (retval)
{
case -3: // Unrecoverable network error
goto end; // Do nothing; just exit from findalldevs; the error code is already into the errbuf
case -2: // The other endpoint send a message that is not allowed here
{
rpcap_senderror(pars->sockctrl, "The RPCAP daemon received a message that is not valid", PCAP_ERR_WRONGMSG, errbuf);
}
case -1: // The other endpoint has a version number that is not compatible with our
{
rpcap_senderror(pars->sockctrl, "RPCAP version number mismatch", PCAP_ERR_WRONGVER, errbuf);
}
break;
case RPCAP_MSG_FINDALLIF_REQ:
{
// Checks that the header does not contain other data; if so, discard it
if (ntohl(header.plen))
sock_discard(pars->sockctrl, ntohl(header.plen), errbuf, PCAP_ERRBUF_SIZE);
if (daemon_findalldevs(pars->sockctrl, errbuf) )
SOCK_ASSERT(errbuf, 1);
break;
};
case RPCAP_MSG_OPEN_REQ:
{
retval= daemon_opensource(pars->sockctrl, source, sizeof(source), ntohl(header.plen), errbuf);
if (retval == -1)
SOCK_ASSERT(errbuf, 1);
break;
};
case RPCAP_MSG_SETSAMPLING_REQ:
{
retval= daemon_setsampling(pars->sockctrl, &samp_param, ntohl(header.plen), errbuf);
if (retval == -1)
SOCK_ASSERT(errbuf, 1);
break;
};
case RPCAP_MSG_STARTCAP_REQ:
{
fp= daemon_startcapture(pars->sockctrl, &threaddata, source, pars->isactive, &samp_param, ntohl(header.plen), errbuf);
if (fp == NULL)
SOCK_ASSERT(errbuf, 1);
break;
};
case RPCAP_MSG_UPDATEFILTER_REQ:
{
if (fp)
{
if (daemon_updatefilter(fp, ntohl(header.plen)) )
SOCK_ASSERT(fp->errbuf, 1);
}
else
{
rpcap_senderror(pars->sockctrl, "Device not opened. Cannot update filter", PCAP_ERR_UPDATEFILTER, errbuf);
}
break;
};
case RPCAP_MSG_STATS_REQ:
{
// Checks that the header does not contain other data; if so, discard it
if (ntohl(header.plen))
sock_discard(pars->sockctrl, ntohl(header.plen), errbuf, PCAP_ERRBUF_SIZE);
if (fp)
{
if (daemon_getstats(fp) )
SOCK_ASSERT(fp->errbuf, 1);
}
else
{
SOCK_ASSERT("GetStats: this call should't be allowed here", 1);
if (daemon_getstatsnopcap(pars->sockctrl, ifdrops, ifrecv, krnldrop, svrcapt, errbuf) )
SOCK_ASSERT(errbuf, 1);
// we have to keep compatibility with old applications, which ask for statistics
// also when the capture has already stopped
// rpcap_senderror(pars->sockctrl, "Device not opened. Cannot get statistics", PCAP_ERR_GETSTATS, errbuf);
}
break;
};
case RPCAP_MSG_ENDCAP_REQ: // The other endpoint close the current capture session
{
if (fp)
{
struct pcap_stat stats;
// Save statistics (we can need them in the future)
if (pcap_stats(fp, &stats) )
{
ifdrops= stats.ps_ifdrop;
ifrecv= stats.ps_recv;
krnldrop= stats.ps_drop;
svrcapt= fp->md.TotCapt;
}
else
ifdrops= ifrecv= krnldrop= svrcapt= 0;
if ( daemon_endcapture(fp, &threaddata, errbuf) )
SOCK_ASSERT(errbuf, 1);
fp= NULL;
}
else
{
rpcap_senderror(pars->sockctrl, "Device not opened. Cannot close the capture", PCAP_ERR_ENDCAPTURE, errbuf);
}
break;
};
case RPCAP_MSG_CLOSE: // The other endpoint close the pcap session
{
// signal to the main that the user closed the control connection
// This is used only in case of active mode
pars->activeclose= 1;
SOCK_ASSERT("The other end system asked to close the connection.", 1);
goto end;
break;
};
case RPCAP_MSG_ERROR: // The other endpoint reported an error
{
// Do nothing; just exit; the error code is already into the errbuf
SOCK_ASSERT(errbuf, 1);
break;
};
default:
{
SOCK_ASSERT("Internal error.", 1);
break;
};
}
}
end:
// The child thread is about to end
// perform pcap_t cleanup, in case it has not been done
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -