--- /dev/null
+/*
+ * 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 <pcap.h> // for libpcap/WinPcap calls
+#include <errno.h> // for the errno variable
+#include <stdlib.h> // for malloc(), free(), ...
+#include <string.h> // for strlen(), ...
+#include <pthread.h>
+#include "sockutils.h" // for socket calls
+#include "rpcap-protocol.h"
+#include "pcap-rpcap.h"
+#include "daemon.h"
+
+#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
+
+#define RPCAP_TIMEOUT_INIT 90 /* Initial timeout for RPCAP connections (default: 90 sec) */
+#define RPCAP_TIMEOUT_RUNTIME 180 /* Run-time timeout for RPCAP connections (default: 3 min) */
+#define RPCAP_SUSPEND_WRONGAUTH 1 /* If the authentication is wrong, stops 1 sec before accepting a new auth message */
+
+/*
+ * Data for a session managed by a thread.
+ */
+struct session {
+ SOCKET sockctrl;
+ SOCKET sockdata;
+ pcap_t *fp;
+ unsigned int TotCapt;
+};
+
+// Locally defined functions
+static int daemon_checkauth(SOCKET sockctrl, int nullAuthAllowed, char *errbuf);
+static int daemon_AuthUserPwd(char *username, char *password, char *errbuf);
+
+static int daemon_findalldevs(SOCKET sockctrl, char *errbuf);
+
+static int daemon_opensource(SOCKET sockctrl, char *source, int srclen, uint32 plen, char *errbuf);
+static struct session *daemon_startcapture(SOCKET sockctrl, pthread_t *threaddata, char *source, int active,
+ struct rpcap_sampling *samp_param, uint32 plen, char *errbuf);
+static int daemon_endcapture(struct session *session, pthread_t *threaddata, char *errbuf);
+
+static int daemon_updatefilter(struct session *session, uint32 plen);
+static int daemon_unpackapplyfilter(struct session *session, uint32 *totread, uint32 *plen, char *errbuf);
+
+static int daemon_getstats(struct session *session);
+static int daemon_getstatsnopcap(SOCKET sockctrl, unsigned int ifdrops, unsigned int ifrecv,
+ unsigned int krnldrop, unsigned int svrcapt, char *errbuf);
+
+static int daemon_setsampling(SOCKET sockctrl, struct rpcap_sampling *samp_param, int plen, char *errbuf);
+
+static void daemon_seraddr(struct sockaddr_storage *sockaddrin, struct sockaddr_storage *sockaddrout);
+static 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
+ struct session *session = NULL; // struct session 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)
+ {
+ 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 (session->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 session != NULL, but
+ // sockdata is 0
+ if ( (!pars->isactive) && ( (session == NULL) || ( (session != NULL) && (session->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:
+ {
+ session = daemon_startcapture(pars->sockctrl, &threaddata, source, pars->isactive, &samp_param, ntohl(header.plen), errbuf);
+
+ if (session == NULL)
+ SOCK_ASSERT(errbuf, 1);
+
+ break;
+ };
+
+ case RPCAP_MSG_UPDATEFILTER_REQ:
+ {
+ if (session)
+ {
+ if (daemon_updatefilter(session, ntohl(header.plen)) )
+ SOCK_ASSERT(pcap_geterr(session->fp), 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 (session && session->fp)
+ {
+ if (daemon_getstats(session) )
+ SOCK_ASSERT(pcap_geterr(session->fp), 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 (session && session->fp)
+ {
+ struct pcap_stat stats;
+
+ // Save statistics (we can need them in the future)
+ if (pcap_stats(session->fp, &stats) )
+ {
+ ifdrops= stats.ps_ifdrop;
+ ifrecv= stats.ps_recv;
+ krnldrop= stats.ps_drop;
+ svrcapt= session->TotCapt;
+ }
+ else
+ ifdrops= ifrecv= krnldrop= svrcapt= 0;
+
+ if ( daemon_endcapture(session, &threaddata, errbuf) )
+ SOCK_ASSERT(pcap_geterr(session->fp), 1);
+ free(session);
+ session = 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
+ if (session)
+ {
+ if (threaddata)
+ {
+ pthread_cancel(threaddata);
+ threaddata= 0;
+ }
+ if (session->sockdata)
+ {
+ sock_close(session->sockdata, NULL, 0);
+ session->sockdata= 0;
+ }
+ pcap_close(session->fp);
+ free(session);
+ session = NULL;
+ }
+
+ // Print message and exit
+ SOCK_ASSERT("I'm exiting from the child loop", 1);
+ SOCK_ASSERT(errbuf, 1);
+
+ if (!pars->isactive)
+ {
+ if (pars->sockctrl)
+ sock_close(pars->sockctrl, NULL, 0);
+
+ free(pars);
+#ifdef _WIN32
+ pthread_exit(0);
+#endif
+ }
+}
+
+/*!
+ \brief It checks if the authentication credentials supplied by the user are valid.
+
+ This function is called each time the rpcap daemon starts a new serving thread.
+ It reads the authentication message from the network and it checks that the
+ user information are valid.
+
+ \param sockctrl: the socket if of the control connection.
+
+ \param nullAuthAllowed: '1' if the NULL authentication is allowed.
+
+ \param errbuf: a user-allocated buffer in which the error message (if one) has to be written.
+
+ \return '0' if everything is fine, '-1' if an unrecoverable error occurred.
+ The error message is returned in the 'errbuf' variable.
+ '-2' is returned in case the authentication failed or in case of a recoverable error (like
+ wrong version). In that case, 'errbuf' keeps the reason of the failure. This provides
+ a way to know that the connection does not have to be closed.
+
+ In case the message is a 'CLOSE' or an 'ERROR', it returns -3. The error can be due to a
+ connection refusal in active mode, since this host cannot be allowed to connect to the remote
+ peer.
+*/
+int daemon_checkauth(SOCKET sockctrl, int nullAuthAllowed, char *errbuf)
+{
+ struct rpcap_header header; // RPCAP message general header
+ int retval; // generic return value
+ uint32 totread = 0; // number of bytes of the payload read from the socket
+ ssize_t nread;
+ struct rpcap_auth auth; // RPCAP authentication header
+ char *string1, *string2; // two strings exchanged by the authentication message
+ unsigned int plen; // length of the payload
+ int retcode; // the value we have to return to the caller
+
+ if (sock_recv(sockctrl, (char *) &header, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ return -1;
+
+ plen= ntohl(header.plen);
+
+ retval= rpcap_checkmsg(errbuf, sockctrl, &header,
+ RPCAP_MSG_AUTH_REQ,
+ RPCAP_MSG_CLOSE,
+ 0);
+
+ if (retval != RPCAP_MSG_AUTH_REQ)
+ {
+ switch (retval)
+ {
+ case -3: // Unrecoverable network error
+ return -1; // Do nothing; just exit; the error code is already into the errbuf
+
+ case -2: // The other endpoint send a message that is not allowed here
+ case -1: // The other endpoint has a version number that is not compatible with our
+ return -2;
+
+ case RPCAP_MSG_CLOSE:
+ {
+ // Check if all the data has been read; if not, discard the data in excess
+ if (ntohl(header.plen) )
+ {
+ if (sock_discard(sockctrl, ntohl(header.plen), NULL, 0) )
+ return -1;
+ }
+ return -3;
+ };
+
+ case RPCAP_MSG_ERROR:
+ return -3;
+
+ default:
+ {
+ SOCK_ASSERT("Internal error.", 1);
+ retcode= -2;
+ goto error;
+ };
+ }
+ }
+
+ // If it comes here, it means that we have an authentication request message
+ nread = sock_recv(sockctrl, (char *) &auth, sizeof(struct rpcap_auth),
+ SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE);
+ if (nread == -1)
+ {
+ retcode= -1;
+ goto error;
+ }
+ totread += nread;
+
+ switch (ntohs(auth.type) )
+ {
+ case RPCAP_RMTAUTH_NULL:
+ {
+ if (!nullAuthAllowed)
+ {
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed; NULL autentication not permitted.");
+ retcode= -2;
+ goto error;
+ }
+ break;
+ }
+
+ case RPCAP_RMTAUTH_PWD:
+ {
+ int len1, len2;
+
+ len1= ntohs(auth.slen1);
+ len2= ntohs(auth.slen2);
+
+ string1= (char *) malloc (len1 + 1);
+ string2= (char *) malloc (len2 + 1);
+
+ if ( (string1 == NULL) || (string2 == NULL) )
+ {
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno));
+ retcode= -1;
+ goto error;
+ }
+
+ nread = sock_recv(sockctrl, string1, len1,
+ SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE);
+ if (nread == -1)
+ {
+ retcode= -1;
+ goto error;
+ }
+ totread = nread;
+ nread = sock_recv(sockctrl, string2, len2,
+ SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE);
+ if (nread == -1)
+ {
+ retcode= -1;
+ goto error;
+ }
+ totread += nread;
+
+ string1[len1]= 0;
+ string2[len2]= 0;
+
+ if (daemon_AuthUserPwd(string1, string2, errbuf) )
+ {
+ retcode= -2;
+ goto error;
+ }
+
+ break;
+ }
+
+ default:
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication type not recognized.");
+ retcode= -2;
+ goto error;
+ }
+
+
+ // Check if all the data has been read; if not, discard the data in excess
+ if (totread != plen)
+ {
+ if (sock_discard(sockctrl, plen - totread, NULL, 0) )
+ {
+ retcode= -1;
+ goto error;
+ }
+ }
+
+ rpcap_createhdr(&header, RPCAP_MSG_AUTH_REPLY, 0, 0);
+
+ // Send the ok message back
+ if ( sock_send(sockctrl, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1)
+ {
+ retcode= -1;
+ goto error;
+ }
+
+ return 0;
+
+error:
+ // Check if all the data has been read; if not, discard the data in excess
+ if (totread != plen)
+ sock_discard(sockctrl, plen - totread, NULL, 0);
+
+ return retcode;
+}
+
+int daemon_AuthUserPwd(char *username, char *password, char *errbuf)
+{
+#ifdef _WIN32
+ /*
+ Warning: the user which launches the process must have the SE_TCB_NAME right.
+ This corresponds to have the "Act as part of the Operating System" turined on
+ (administrative tools, local security settings, local policies, user right assignment)
+ However, it seems to me that if you run it as a service, this right should be
+ provided by default.
+ */
+ HANDLE Token;
+ if (LogonUser(username, ".", password, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &Token) == 0)
+ {
+ int error;
+
+ error = GetLastError();
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, errbuf,
+ PCAP_ERRBUF_SIZE, NULL);
+
+ return -1;
+ }
+
+ // This call should change the current thread to the selected user.
+ // I didn't test it.
+ if (ImpersonateLoggedOnUser(Token) == 0)
+ {
+ int error;
+
+ error = GetLastError();
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, errbuf,
+ PCAP_ERRBUF_SIZE, NULL);
+
+ CloseHandle(Token);
+ return -1;
+ }
+
+ CloseHandle(Token);
+ return 0;
+
+#else
+/* Standard user authentication:
+ http://www.unixpapa.com/incnote/passwd.html
+ Problem: it is not able to merge the standard pwd file with the shadow one
+
+ Shadow user authentication:
+ http://www.tldp.org/HOWTO/Shadow-Password-HOWTO-8.html
+ Problem: the program must either (1) run as root, or (2) run as user, but it
+ must be owned by root and must be SUID root (chmod u+s rpcapd)
+*/
+
+ struct passwd *user;
+#ifdef linux
+ struct spwd *usersp;
+#endif
+
+ // This call is needed to get the uid
+ if ((user= getpwnam(username)) == NULL)
+ {
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed: no such user");
+ return -1;
+ }
+
+#ifdef linux
+ // This call is needed to get the password; otherwise 'x' is returned
+ if ((usersp= getspnam(username)) == NULL)
+ {
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed: no such user");
+ return -1;
+ }
+
+ if (strcmp(usersp->sp_pwdp, (char *) crypt(password, usersp->sp_pwdp) ) != 0)
+ {
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed: password incorrect");
+ return -1;
+ }
+#endif
+
+#ifdef bsd
+ if (strcmp(user->pw_passwd, (char *) crypt(password, user->pw_passwd) ) != 0)
+ {
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed: password incorrect");
+ return -1;
+ }
+#endif
+
+ if (setuid(user->pw_uid) )
+ {
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s", pcap_strerror(errno) );
+ return -1;
+ }
+
+/* if (setgid(user->pw_gid) )
+ {
+ SOCK_ASSERT("setgid failed", 1);
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s", pcap_strerror(errno) );
+ return -1;
+ }
+*/
+ return 0;
+
+#endif
+
+}
+
+// PORTING WARNING We assume u_int is a 32bit value
+int daemon_findalldevs(SOCKET sockctrl, char *errbuf)
+{
+ char sendbuf[RPCAP_NETBUF_SIZE]; // temporary buffer in which data to be sent is buffered
+ int sendbufidx= 0; // index which keeps the number of bytes currently buffered
+ pcap_if_t *alldevs; // pointer to the heade of the interface chain
+ pcap_if_t *d; // temp pointer neede to scan the interface chain
+ uint16 plen= 0; // length of the payload of this message
+ struct pcap_addr *address; // pcap structure that keeps a network address of an interface
+ struct rpcap_findalldevs_if *findalldevs_if;// rpcap structure that packet all the data of an interface together
+ uint16 nif= 0; // counts the number of interface listed
+
+ // Retrieve the device list
+ if (pcap_findalldevs(&alldevs, errbuf) == -1)
+ {
+ rpcap_senderror(sockctrl, errbuf, PCAP_ERR_FINDALLIF, NULL);
+ return -1;
+ }
+
+ if (alldevs == NULL)
+ {
+ rpcap_senderror(sockctrl,
+ "No interfaces found! Make sure libpcap/WinPcap is properly installed"
+ " and you have the right to access to the remote device.",
+ PCAP_ERR_NOREMOTEIF,
+ errbuf);
+ return -1;
+ }
+
+ // checks the number of interfaces and it computes the total length of the payload
+ for (d= alldevs; d != NULL; d= d->next)
+ {
+ nif++;
+
+ if (d->description)
+ plen+= strlen(d->description);
+ if (d->name)
+ plen+= strlen(d->name);
+
+ plen+= sizeof(struct rpcap_findalldevs_if);
+
+ for (address= d->addresses; address != NULL; address= address->next)
+ plen+= ( sizeof(struct sockaddr_storage) * 4);
+ }
+
+ // RPCAP findalldevs command
+ if ( sock_bufferize(NULL, sizeof(struct rpcap_header), NULL,
+ &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ return -1;
+
+ rpcap_createhdr( (struct rpcap_header *) sendbuf, RPCAP_MSG_FINDALLIF_REPLY, nif, plen);
+
+ // send the interface list
+ for (d= alldevs; d != NULL; d= d->next)
+ {
+ uint16 lname, ldescr;
+
+ findalldevs_if= (struct rpcap_findalldevs_if *) &sendbuf[sendbufidx];
+
+ if ( sock_bufferize(NULL, sizeof(struct rpcap_findalldevs_if), NULL,
+ &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ return -1;
+
+ memset(findalldevs_if, 0, sizeof(struct rpcap_findalldevs_if) );
+
+ if (d->description) ldescr= (short) strlen(d->description);
+ else ldescr= 0;
+ if (d->name) lname= (short) strlen(d->name);
+ else lname= 0;
+
+ findalldevs_if->desclen= htons(ldescr);
+ findalldevs_if->namelen= htons(lname);
+ findalldevs_if->flags= htonl(d->flags);
+
+ for (address= d->addresses; address != NULL; address= address->next)
+ findalldevs_if->naddr++;
+
+ findalldevs_if->naddr= htons(findalldevs_if->naddr);
+
+ if (sock_bufferize(d->name, lname, sendbuf, &sendbufidx,
+ RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ return -1;
+
+ if (sock_bufferize(d->description, ldescr, sendbuf, &sendbufidx,
+ RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ return -1;
+
+ // send all addresses
+ for (address= d->addresses; address != NULL; address= address->next)
+ {
+ struct sockaddr_storage *sockaddr;
+
+ sockaddr= (struct sockaddr_storage *) &sendbuf[sendbufidx];
+ if (sock_bufferize(NULL, sizeof(struct sockaddr_storage), NULL,
+ &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ return -1;
+ daemon_seraddr( (struct sockaddr_storage *) address->addr, sockaddr);
+
+ sockaddr= (struct sockaddr_storage *) &sendbuf[sendbufidx];
+ if (sock_bufferize(NULL, sizeof(struct sockaddr_storage), NULL,
+ &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ return -1;
+ daemon_seraddr( (struct sockaddr_storage *) address->netmask, sockaddr);
+
+ sockaddr= (struct sockaddr_storage *) &sendbuf[sendbufidx];
+ if (sock_bufferize(NULL, sizeof(struct sockaddr_storage), NULL,
+ &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ return -1;
+ daemon_seraddr( (struct sockaddr_storage *) address->broadaddr, sockaddr);
+
+ sockaddr= (struct sockaddr_storage *) &sendbuf[sendbufidx];
+ if (sock_bufferize(NULL, sizeof(struct sockaddr_storage), NULL,
+ &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ return -1;
+ daemon_seraddr( (struct sockaddr_storage *) address->dstaddr, sockaddr);
+ }
+ }
+
+ // Send a final command that says "now send it!"
+ if (sock_send(sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ return -1;
+
+ // We do no longer need the device list. Free it
+ pcap_freealldevs(alldevs);
+
+ // everything is fine
+ return 0;
+}
+
+/*
+ \param plen: the length of the current message (needed in order to be able
+ to discard excess data in the message, if present)
+*/
+static int daemon_opensource(SOCKET sockctrl, char *source, int srclen, uint32 plen, char *errbuf)
+{
+ pcap_t *fp= NULL; // pcap_t main variable
+ uint32 totread; // number of bytes of the payload read from the socket
+ ssize_t nread;
+ char sendbuf[RPCAP_NETBUF_SIZE]; // temporary buffer in which data to be sent is buffered
+ int sendbufidx= 0; // index which keeps the number of bytes currently buffered
+ struct rpcap_openreply *openreply; // open reply message
+
+ strcpy(source, PCAP_SRC_IF_STRING);
+
+ if (srclen <= (int) (strlen(PCAP_SRC_IF_STRING) + plen) )
+ {
+ rpcap_senderror(sockctrl, "Source string too long", PCAP_ERR_OPEN, NULL);
+ return -1;
+ }
+
+ nread = sock_recv(sockctrl, &source[strlen(PCAP_SRC_IF_STRING)], plen,
+ SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE);
+ if (nread == -1)
+ return -1;
+ totread = nread;
+
+ // Check if all the data has been read; if not, discard the data in excess
+ if (totread != plen)
+ sock_discard(sockctrl, plen - totread, NULL, 0);
+
+ // Puts a '0' to terminate the source string
+ source[strlen(PCAP_SRC_IF_STRING) + plen]= 0;
+
+ // Open the selected device
+ // This is a fake open, since we do that only to get the needed parameters, then we close the device again
+ if ( (fp= pcap_open_live(source,
+ 1500 /* fake snaplen */,
+ 0 /* no promis */,
+ 1000 /* fake timeout */,
+ errbuf)) == NULL)
+ {
+ rpcap_senderror(sockctrl, errbuf, PCAP_ERR_OPEN, NULL);
+ return -1;
+ }
+
+
+ // Now, I can send a RPCAP open reply message
+ if ( sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, &sendbufidx,
+ RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ goto error;
+
+ rpcap_createhdr( (struct rpcap_header *) sendbuf, RPCAP_MSG_OPEN_REPLY, 0, sizeof(struct rpcap_openreply) );
+
+ openreply= (struct rpcap_openreply *) &sendbuf[sendbufidx];
+
+ if ( sock_bufferize(NULL, sizeof(struct rpcap_openreply), NULL, &sendbufidx,
+ RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ goto error;
+
+ memset(openreply, 0, sizeof(struct rpcap_openreply) );
+ openreply->linktype = htonl(pcap_datalink(fp));
+ openreply->tzoff = 0; /* This is always 0 for live captures */
+
+ if ( sock_send(sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ goto error;
+
+ // I have to close the device again, since it has been opened with wrong parameters
+ pcap_close(fp);
+ fp= NULL;
+
+ return 0;
+
+error:
+ if (fp)
+ {
+ pcap_close(fp);
+ fp= NULL;
+ }
+
+ return -1;
+}
+
+/*
+ \param plen: the length of the current message (needed in order to be able
+ to discard excess data in the message, if present)
+*/
+static struct session *daemon_startcapture(SOCKET sockctrl, pthread_t *threaddata, char *source, int active, struct rpcap_sampling *samp_param, uint32 plen, char *errbuf)
+{
+ char portdata[PCAP_BUF_SIZE]; // temp variable needed to derive the data port
+ char peerhost[PCAP_BUF_SIZE]; // temp variable needed to derive the host name of our peer
+ struct session *session; // saves state of session
+ uint32 totread; // number of bytes of the payload read from the socket
+ ssize_t nread;
+ char sendbuf[RPCAP_NETBUF_SIZE]; // temporary buffer in which data to be sent is buffered
+ int sendbufidx= 0; // index which keeps the number of bytes currently buffered
+
+ // socket-related variables
+ SOCKET sockdata= 0; // socket descriptor of the data connection
+ struct addrinfo hints; // temp, needed to open a socket connection
+ struct addrinfo *addrinfo; // temp, needed to open a socket connection
+ struct sockaddr_storage saddr; // temp, needed to retrieve the network data port chosen on the local machine
+ socklen_t saddrlen; // temp, needed to retrieve the network data port chosen on the local machine
+
+ pthread_attr_t detachedAttribute; // temp, needed to set the created thread as detached
+
+ // RPCAP-related variables
+ struct rpcap_startcapreq startcapreq; // start capture request message
+ struct rpcap_startcapreply *startcapreply; // start capture reply message
+ int serveropen_dp; // keeps who is going to open the data connection
+
+ addrinfo= NULL;
+
+ nread = sock_recv(sockctrl, (char *) &startcapreq,
+ sizeof(struct rpcap_startcapreq), SOCK_RECEIVEALL_YES,
+ errbuf, PCAP_ERRBUF_SIZE);
+ if (nread == -1)
+ return NULL;
+ totread = nread;
+
+ startcapreq.flags= ntohs(startcapreq.flags);
+
+ // Create a session structure
+ session = malloc(sizeof(struct session));
+ if (session == NULL) {
+ rpcap_senderror(sockctrl, "Can't allocate session structure",
+ PCAP_ERR_OPEN, NULL);
+ return NULL;
+ }
+
+ // Open the selected device
+ if ( (session->fp= pcap_open(source,
+ ntohl(startcapreq.snaplen),
+ (startcapreq.flags & RPCAP_STARTCAPREQ_FLAG_PROMISC) ? PCAP_OPENFLAG_PROMISCUOUS : 0 /* local device, other flags not needed */,
+ ntohl(startcapreq.read_timeout),
+ NULL /* local device, so no auth */,
+ errbuf)) == NULL)
+ {
+ rpcap_senderror(sockctrl, errbuf, PCAP_ERR_OPEN, NULL);
+ return NULL;
+ }
+
+#if 0
+ // Apply sampling parameters
+ fp->rmt_samp.method= samp_param->method;
+ fp->rmt_samp.value= samp_param->value;
+#endif
+
+ /*
+ We're in active mode if:
+ - we're using TCP, and the user wants us to be in active mode
+ - we're using UDP
+ */
+ serveropen_dp= (startcapreq.flags & RPCAP_STARTCAPREQ_FLAG_SERVEROPEN) || (startcapreq.flags & RPCAP_STARTCAPREQ_FLAG_DGRAM) || active;
+
+ /*
+ Gets the sockaddr structure referred to the other peer in the ctrl connection
+
+ We need that because:
+ - if we're in passive mode, we need to know the address family we want to use
+ (the same used for the ctrl socket)
+ - if we're in active mode, we need to know the network address of the other host
+ we want to connect to
+ */
+ saddrlen = sizeof(struct sockaddr_storage);
+ if (getpeername(sockctrl, (struct sockaddr *) &saddr, &saddrlen) == -1)
+ {
+ sock_geterror("getpeername(): ", errbuf, PCAP_ERRBUF_SIZE);
+ goto error;
+ }
+
+ memset(&hints, 0, sizeof(struct addrinfo) );
+ hints.ai_socktype = (startcapreq.flags & RPCAP_STARTCAPREQ_FLAG_DGRAM) ? SOCK_DGRAM : SOCK_STREAM;
+ hints.ai_family = saddr.ss_family;
+
+ // Now we have to create a new socket to send packets
+ if (serveropen_dp) // Data connection is opened by the server toward the client
+ {
+ sprintf(portdata, "%d", ntohs(startcapreq.portdata) );
+
+ // Get the name of the other peer (needed to connect to that specific network address)
+ if (getnameinfo( (struct sockaddr *) &saddr, saddrlen, peerhost,
+ sizeof(peerhost), NULL, 0, NI_NUMERICHOST) )
+ {
+ sock_geterror("getnameinfo(): ", errbuf, PCAP_ERRBUF_SIZE);
+ goto error;
+ }
+
+ if (sock_initaddress(peerhost, portdata, &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ goto error;
+
+ if ( (sockdata= sock_open(addrinfo, SOCKOPEN_CLIENT, 0, errbuf, PCAP_ERRBUF_SIZE)) == -1)
+ goto error;
+ }
+ else // Data connection is opened by the client toward the server
+ {
+ hints.ai_flags = AI_PASSIVE;
+
+ // Let's the server socket pick up a free network port for us
+ if (sock_initaddress(NULL, "0", &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ goto error;
+
+ if ( (sockdata= sock_open(addrinfo, SOCKOPEN_SERVER, 1 /* max 1 connection in queue */, errbuf, PCAP_ERRBUF_SIZE)) == -1)
+ goto error;
+
+ // get the complete sockaddr structure used in the data connection
+ saddrlen = sizeof(struct sockaddr_storage);
+ if (getsockname(sockdata, (struct sockaddr *) &saddr, &saddrlen) == -1)
+ {
+ sock_geterror("getsockname(): ", errbuf, PCAP_ERRBUF_SIZE);
+ goto error;
+ }
+
+ // Get the local port the system picked up
+ if (getnameinfo( (struct sockaddr *) &saddr, saddrlen, NULL,
+ 0, portdata, sizeof(portdata), NI_NUMERICSERV) )
+ {
+ sock_geterror("getnameinfo(): ", errbuf, PCAP_ERRBUF_SIZE);
+ goto error;
+ }
+ }
+
+ // addrinfo is no longer used
+ freeaddrinfo(addrinfo);
+ addrinfo= NULL;
+
+ session->sockctrl = sockctrl; // Needed to send an error on the ctrl connection
+
+ // Now I can set the filter
+ if ( daemon_unpackapplyfilter(session, &totread, &plen, errbuf) )
+ goto error;
+
+
+ // Now, I can send a RPCAP start capture reply message
+ if ( sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, &sendbufidx,
+ RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ goto error;
+
+ rpcap_createhdr( (struct rpcap_header *) sendbuf, RPCAP_MSG_STARTCAP_REPLY, 0, sizeof(struct rpcap_startcapreply) );
+
+ startcapreply= (struct rpcap_startcapreply *) &sendbuf[sendbufidx];
+
+ if ( sock_bufferize(NULL, sizeof(struct rpcap_startcapreply), NULL,
+ &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ goto error;
+
+ memset(startcapreply, 0, sizeof(struct rpcap_startcapreply) );
+ startcapreply->bufsize= htonl(pcap_bufsize(session->fp));
+
+ if (!serveropen_dp)
+ {
+ unsigned short port = (unsigned short)strtoul(portdata,NULL,10);
+ startcapreply->portdata= htons(port);
+ }
+
+ if ( sock_send(sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ goto error;
+
+ if (!serveropen_dp)
+ {
+ SOCKET socktemp; // We need another socket, since we're going to accept() a connection
+
+ // Connection creation
+ saddrlen = sizeof(struct sockaddr_storage);
+
+ socktemp= accept(sockdata, (struct sockaddr *) &saddr, &saddrlen);
+
+ if (socktemp == -1)
+ {
+ sock_geterror("accept(): ", errbuf, PCAP_ERRBUF_SIZE);
+ goto error;
+ }
+
+ // Now that I accepted the connection, the server socket is no longer needed
+ sock_close(sockdata, errbuf, PCAP_ERRBUF_SIZE);
+ sockdata= socktemp;
+ }
+
+ session->sockdata = sockdata;
+
+ /* GV we need this to create the thread as detached. */
+ /* GV otherwise, the thread handle is not destroyed */
+ pthread_attr_init(&detachedAttribute);
+ pthread_attr_setdetachstate(&detachedAttribute, PTHREAD_CREATE_DETACHED);
+
+ // Now we have to create a new thread to receive packets
+ if ( pthread_create(threaddata, &detachedAttribute, (void *) daemon_thrdatamain, (void *) session) )
+ {
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error creating the data thread");
+ pthread_attr_destroy(&detachedAttribute);
+ goto error;
+ }
+
+ pthread_attr_destroy(&detachedAttribute);
+ // Check if all the data has been read; if not, discard the data in excess
+ if (totread != plen)
+ sock_discard(sockctrl, plen - totread, NULL, 0);
+
+ return session;
+
+error:
+ rpcap_senderror(sockctrl, errbuf, PCAP_ERR_STARTCAPTURE, NULL);
+
+ if (addrinfo)
+ freeaddrinfo(addrinfo);
+
+ if (threaddata)
+ pthread_cancel(*threaddata);
+
+ if (sockdata)
+ sock_close(sockdata, NULL, 0);
+
+ // Check if all the data has been read; if not, discard the data in excess
+ if (totread != plen)
+ sock_discard(sockctrl, plen - totread, NULL, 0);
+
+ if (session->fp)
+ {
+ pcap_close(session->fp);
+ }
+ free(session);
+
+ return NULL;
+}
+
+static int daemon_endcapture(struct session *session, pthread_t *threaddata, char *errbuf)
+{
+ struct rpcap_header header;
+
+ if (threaddata)
+ {
+ pthread_cancel(*threaddata);
+ threaddata= 0;
+ }
+ if (session->sockdata)
+ {
+ sock_close(session->sockdata, NULL, 0);
+ session->sockdata= 0;
+ }
+
+ pcap_close(session->fp);
+
+ rpcap_createhdr( &header, RPCAP_MSG_ENDCAP_REPLY, 0, 0);
+
+ if ( sock_send(session->sockctrl, (char *) &header, sizeof(struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1)
+ return -1;
+
+ return 0;
+}
+
+static int daemon_unpackapplyfilter(struct session *session, uint32 *totread, uint32 *plen, char *errbuf)
+{
+ ssize_t nread;
+ struct rpcap_filter filter;
+ struct rpcap_filterbpf_insn insn;
+ struct bpf_insn *bf_insn;
+ struct bpf_program bf_prog;
+ unsigned int i;
+
+ nread = sock_recv(session->sockctrl, (char *) &filter,
+ sizeof(struct rpcap_filter), SOCK_RECEIVEALL_YES,
+ errbuf, PCAP_ERRBUF_SIZE);
+ if (nread == -1)
+ {
+ // to avoid blocking on the sock_discard()
+ *plen = *totread;
+ return -1;
+ }
+ *totread += nread;
+
+ bf_prog.bf_len= ntohl(filter.nitems);
+
+ if (ntohs(filter.filtertype) != RPCAP_UPDATEFILTER_BPF)
+ {
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "Only BPF/NPF filters are currently supported");
+ return -1;
+ }
+
+ bf_insn= (struct bpf_insn *) malloc ( sizeof(struct bpf_insn) * bf_prog.bf_len);
+ if (bf_insn == NULL)
+ {
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno));
+ return -1;
+ }
+
+ bf_prog.bf_insns= bf_insn;
+
+ for (i= 0; i < bf_prog.bf_len; i++)
+ {
+ nread = sock_recv(session->sockctrl, (char *) &insn,
+ sizeof(struct rpcap_filterbpf_insn), SOCK_RECEIVEALL_YES,
+ errbuf, PCAP_ERRBUF_SIZE);
+ if (nread == -1)
+ return -1;
+ *totread += nread;
+
+ bf_insn->code= ntohs(insn.code);
+ bf_insn->jf= insn.jf;
+ bf_insn->jt= insn.jt;
+ bf_insn->k= ntohl(insn.k);
+
+ bf_insn++;
+ }
+
+ if (bpf_validate(bf_prog.bf_insns, bf_prog.bf_len) == 0)
+ {
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "The filter contains bogus instructions");
+ return -1;
+ }
+
+ if (pcap_setfilter(session->fp, &bf_prog) )
+ {
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "RPCAP error: %s", pcap_geterr(session->fp));
+ return -1;
+ }
+
+ return 0;
+}
+
+int daemon_updatefilter(struct session *session, uint32 plen)
+{
+ struct rpcap_header header; // keeps the answer to the updatefilter command
+ unsigned int nread;
+
+ nread= 0;
+
+ if ( daemon_unpackapplyfilter(session, &nread, &plen, pcap_geterr(session->fp)) )
+ goto error;
+
+ // Check if all the data has been read; if not, discard the data in excess
+ if (nread != plen)
+ {
+ if (sock_discard(session->sockctrl, plen - nread, NULL, 0) )
+ {
+ nread= plen; // just to avoid to call discard again in the 'error' section
+ goto error;
+ }
+ }
+
+ // A response is needed, otherwise the other host does not know that everything went well
+ rpcap_createhdr( &header, RPCAP_MSG_UPDATEFILTER_REPLY, 0, 0);
+
+ if ( sock_send(session->sockctrl, (char *) &header, sizeof (struct rpcap_header), pcap_geterr(session->fp), PCAP_ERRBUF_SIZE) )
+ goto error;
+
+ return 0;
+
+
+error:
+ if (nread != plen)
+ sock_discard(session->sockctrl, plen - nread, NULL, 0);
+
+ rpcap_senderror(session->sockctrl, pcap_geterr(session->fp), PCAP_ERR_UPDATEFILTER, NULL);
+
+ return -1;
+}
+
+/*!
+ \brief Received the sampling parameters from remote host and it stores in the pcap_t structure.
+*/
+int daemon_setsampling(SOCKET sockctrl, struct rpcap_sampling *samp_param, int plen, char *errbuf)
+{
+ struct rpcap_header header;
+ struct rpcap_sampling rpcap_samp;
+ int nread; // number of bytes of the payload read from the socket
+
+ if ( ( nread= sock_recv(sockctrl, (char *) &rpcap_samp, sizeof(struct rpcap_sampling),
+ SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE)) == -1)
+ goto error;
+
+
+ // Save these settings in the pcap_t
+ samp_param->method= rpcap_samp.method;
+ samp_param->value= ntohl(rpcap_samp.value);
+
+
+ // A response is needed, otherwise the other host does not know that everything went well
+ rpcap_createhdr( &header, RPCAP_MSG_SETSAMPLING_REPLY, 0, 0);
+
+ if ( sock_send(sockctrl, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) )
+ goto error;
+
+ if (nread != plen)
+ sock_discard(sockctrl, plen - nread, NULL, 0);
+
+ return 0;
+
+error:
+ if (nread != plen)
+ sock_discard(sockctrl, plen - nread, NULL, 0);
+
+ rpcap_senderror(sockctrl, errbuf, PCAP_ERR_SETSAMPLING, NULL);
+
+ return -1;
+}
+
+int daemon_getstats(struct session *session)
+{
+ char sendbuf[RPCAP_NETBUF_SIZE]; // temporary buffer in which data to be sent is buffered
+ int sendbufidx= 0; // index which keeps the number of bytes currently buffered
+ struct pcap_stat stats; // local statistics
+ struct rpcap_stats *netstats; // statistics sent on the network
+
+ if ( sock_bufferize(NULL, sizeof(struct rpcap_header), NULL,
+ &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, pcap_geterr(session->fp), PCAP_ERRBUF_SIZE) == -1)
+ goto error;
+
+ rpcap_createhdr( (struct rpcap_header *) sendbuf, RPCAP_MSG_STATS_REPLY, 0, (uint16) sizeof(struct rpcap_stats));
+
+ netstats= (struct rpcap_stats *) &sendbuf[sendbufidx];
+
+ if ( sock_bufferize(NULL, sizeof(struct rpcap_stats), NULL,
+ &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, pcap_geterr(session->fp), PCAP_ERRBUF_SIZE) == -1)
+ goto error;
+
+ if (pcap_stats(session->fp, &stats) )
+ goto error;
+
+ netstats->ifdrop= htonl(stats.ps_ifdrop);
+ netstats->ifrecv= htonl(stats.ps_recv);
+ netstats->krnldrop= htonl(stats.ps_drop);
+ netstats->svrcapt= htonl(session->TotCapt);
+
+ // Send the packet
+ if ( sock_send(session->sockctrl, sendbuf, sendbufidx, pcap_geterr(session->fp), PCAP_ERRBUF_SIZE) == -1)
+ goto error;
+
+ return 0;
+
+error:
+ rpcap_senderror(session->sockctrl, pcap_geterr(session->fp), PCAP_ERR_GETSTATS, NULL);
+ return -1;
+}
+
+int daemon_getstatsnopcap(SOCKET sockctrl, unsigned int ifdrops, unsigned int ifrecv,
+ unsigned int krnldrop, unsigned int svrcapt, char *errbuf)
+{
+ char sendbuf[RPCAP_NETBUF_SIZE]; // temporary buffer in which data to be sent is buffered
+ int sendbufidx= 0; // index which keeps the number of bytes currently buffered
+ struct rpcap_stats *netstats; // statistics sent on the network
+
+ if ( sock_bufferize(NULL, sizeof(struct rpcap_header), NULL,
+ &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ goto error;
+
+ rpcap_createhdr( (struct rpcap_header *) sendbuf, RPCAP_MSG_STATS_REPLY, 0, (uint16) sizeof(struct rpcap_stats));
+
+ netstats= (struct rpcap_stats *) &sendbuf[sendbufidx];
+
+ if ( sock_bufferize(NULL, sizeof(struct rpcap_stats), NULL,
+ &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ goto error;
+
+ netstats->ifdrop= htonl(ifdrops);
+ netstats->ifrecv= htonl(ifrecv);
+ netstats->krnldrop= htonl(krnldrop);
+ netstats->svrcapt= htonl(svrcapt);
+
+ // Send the packet
+ if ( sock_send(sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ goto error;
+
+ return 0;
+
+error:
+ rpcap_senderror(sockctrl, errbuf, PCAP_ERR_GETSTATS, NULL);
+ return -1;
+}
+
+void *daemon_thrdatamain(void *ptr)
+{
+ char errbuf[PCAP_ERRBUF_SIZE + 1]; // error buffer
+ struct session *session; // pointer to the struct session for this session
+ int retval; // general variable used to keep the return value of other functions
+ struct rpcap_pkthdr *net_pkt_header;// header of the packet
+ struct pcap_pkthdr *pkt_header; // pointer to the buffer that contains the header of the current packet
+ u_char *pkt_data; // pointer to the buffer that contains the current packet
+ char *sendbuf; // temporary buffer in which data to be sent is buffered
+ int sendbufidx; // index which keeps the number of bytes currently buffered
+
+ session = (struct session *) ptr;
+
+ session->TotCapt= 0; // counter which is incremented each time a packet is received
+
+ // Initialize errbuf
+ memset(errbuf, 0, sizeof(errbuf) );
+
+ // Some platforms (e.g. Win32) allow creating a static variable with this size
+ // However, others (e.g. BSD) do not, so we're forced to allocate this buffer dynamically
+ sendbuf= (char *) malloc (sizeof(char) * RPCAP_NETBUF_SIZE);
+ if (sendbuf == NULL)
+ {
+ snprintf(errbuf, sizeof(errbuf) - 1, "Unable to create the buffer for this child thread");
+ goto error;
+ }
+
+ // Modify thread params so that it can be killed at any time
+ if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) )
+ goto error;
+ if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) )
+ goto error;
+
+ // Retrieve the packets
+ while ((retval = pcap_next_ex(session->fp, &pkt_header, (const u_char **) &pkt_data)) >= 0) // cast to avoid a compiler warning
+ {
+ if (retval == 0) // Read timeout elapsed
+ continue;
+
+ sendbufidx= 0;
+
+ // Bufferize the general header
+ if ( sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, &sendbufidx,
+ RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ goto error;
+
+ rpcap_createhdr( (struct rpcap_header *) sendbuf, RPCAP_MSG_PACKET, 0,
+ (uint16) (sizeof(struct rpcap_pkthdr) + pkt_header->caplen) );
+
+ net_pkt_header= (struct rpcap_pkthdr *) &sendbuf[sendbufidx];
+
+ // Bufferize the pkt header
+ if ( sock_bufferize(NULL, sizeof(struct rpcap_pkthdr), NULL, &sendbufidx,
+ RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ goto error;
+
+ net_pkt_header->caplen= htonl(pkt_header->caplen);
+ net_pkt_header->len= htonl(pkt_header->len);
+ net_pkt_header->npkt= htonl( ++(session->TotCapt) );
+ net_pkt_header->timestamp_sec= htonl(pkt_header->ts.tv_sec);
+ net_pkt_header->timestamp_usec= htonl(pkt_header->ts.tv_usec);
+
+ // Bufferize the pkt data
+ if ( sock_bufferize((char *) pkt_data, pkt_header->caplen, sendbuf, &sendbufidx,
+ RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ goto error;
+
+ // Send the packet
+ if ( sock_send(session->sockdata, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ goto error;
+
+ }
+
+ if (retval == -1)
+ {
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error reading the packets: %s", pcap_geterr(session->fp) );
+ rpcap_senderror(session->sockctrl, errbuf, PCAP_ERR_READEX, NULL);
+ goto error;
+ }
+
+error:
+
+ SOCK_ASSERT(errbuf, 1);
+ closesocket(session->sockdata);
+ session->sockdata = 0;
+
+ free(sendbuf);
+
+ return NULL;
+}
+
+/*!
+ \brief It serializes a network address.
+
+ It accepts a 'sockaddr_storage' structure as input, and it converts it appropriately into a format
+ that can be used to be sent on the network. Basically, it applies all the hton()
+ conversion required to the input variable.
+
+ \param sockaddrin: a 'sockaddr_storage' pointer to the variable that has to be
+ serialized. This variable can be both a 'sockaddr_in' and 'sockaddr_in6'.
+
+ \param sockaddrout: a 'sockaddr_storage' pointer to the variable that will contain
+ the serialized data. This variable has to be allocated by the user.
+
+ \return None
+
+ \warning This function supports only AF_INET and AF_INET6 address families.
+*/
+void daemon_seraddr(struct sockaddr_storage *sockaddrin, struct sockaddr_storage *sockaddrout)
+{
+ memset(sockaddrout, 0, sizeof(struct sockaddr_storage) );
+
+ // There can be the case in which the sockaddrin is not available
+ if (sockaddrin == NULL) return;
+
+ // Warning: we support only AF_INET and AF_INET6
+ if (sockaddrin->ss_family == AF_INET)
+ {
+ struct sockaddr_in *sockaddr;
+
+ sockaddr= (struct sockaddr_in *) sockaddrin;
+ sockaddr->sin_family= htons(sockaddr->sin_family);
+ sockaddr->sin_port= htons(sockaddr->sin_port);
+ memcpy(sockaddrout, sockaddr, sizeof(struct sockaddr_in) );
+ }
+ else
+ {
+ struct sockaddr_in6 *sockaddr;
+
+ sockaddr= (struct sockaddr_in6 *) sockaddrin;
+ sockaddr->sin6_family= htons(sockaddr->sin6_family);
+ sockaddr->sin6_port= htons(sockaddr->sin6_port);
+ sockaddr->sin6_flowinfo= htonl(sockaddr->sin6_flowinfo);
+ sockaddr->sin6_scope_id= htonl(sockaddr->sin6_scope_id);
+ memcpy(sockaddrout, sockaddr, sizeof(struct sockaddr_in6) );
+ }
+}
+
+/*!
+ \brief Suspends a pthread for msec milliseconds.
+
+ This function is provided since pthreads do not have a suspend() call.
+*/
+void pthread_suspend(int msec)
+{
+#ifdef _WIN32
+ Sleep(msec);
+#else
+ struct timespec abstime;
+ struct timeval now;
+
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+ pthread_mutexattr_t attr;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutex_init(&mutex, &attr);
+ pthread_mutex_lock(&mutex);
+
+ pthread_cond_init(&cond, NULL);
+
+ gettimeofday(&now, NULL);
+
+ abstime.tv_sec = now.tv_sec + msec/1000;
+ abstime.tv_nsec = now.tv_usec * 1000 + (msec%1000) * 1000 * 1000;
+
+ pthread_cond_timedwait(&cond, &mutex, &abstime);
+
+ pthread_mutex_destroy(&mutex);
+ pthread_cond_destroy(&cond);
+#endif
+}
+
+
+
--- /dev/null
+/*
+ * 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 <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 "portability.h"
+#include "rpcapd.h"
+#include "fileconf.h" // for the configuration file management
+#include "sockutils.h" // for socket calls
+#include "rpcap-protocol.h"
+#include "pcap-rpcap.h"
+#include "daemon.h" // the true main() method of this daemon
+#include "utils.h" // Missing calls and such
+
+#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
+static 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
+
+#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(void)
+{
+ 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 are allowed\n"
+ " to connect to this server (if more than one, list them one per line).\n"
+ " We suggest to use literal names (instead of numeric ones) in order to\n"
+ " avoid 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"
+ " In case 'port' is omitted, the default port (" RPCAP_DEFAULT_NETPORT_ACTIVE ") is used\n"
+ " -v: run in active mode only (default: if '-a' is specified, it accepts\n"
+ " passive 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("%s", 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, PCAP_ERRBUF_SIZE) == -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;
+ 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);
+
+ strlcpy(activelist[i].address, tmpaddress, MAX_LINE);
+
+ if ( (tmpport == NULL) || (strcmp(tmpport, "DEFAULT") == 0) ) // the user choose a custom port
+ strlcpy(activelist[i].port, RPCAP_DEFAULT_NETPORT_ACTIVE, MAX_LINE);
+ else
+ strlcpy(activelist[i].port, tmpport, MAX_LINE);
+
+ tmpaddress = pcap_strtok_r(NULL, RPCAP_HOSTLIST_SEP, &lasts);
+
+ 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':
+ strlcpy(loadfile, optarg, MAX_LINE);
+ break;
+ case 's':
+ strlcpy(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', needed to reload the configuration
+ 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);
+
+#ifndef WIN32
+ // generated under unix with 'kill -HUP', needed to reload the configuration
+ // We do not have this kind of signal in Win32
+ signal(SIGHUP, fileconf_read);
+#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
+ pthread_t threadId; // Pthread variable that keeps the thread structures
+ pthread_attr_t detachedAttribute; // PThread attribute needed to create the thread as detached
+#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
+ /* GV we need this to create the thread as detached. */
+ /* GV otherwise, the thread handle is not destroyed */
+ pthread_attr_init(&detachedAttribute);
+ pthread_attr_setdetachstate(&detachedAttribute, PTHREAD_CREATE_DETACHED);
+
+ if ( pthread_create( &threadId, &detachedAttribute, (void *) &main_active, (void *) &activelist[i]) )
+ {
+ SOCK_ASSERT("Error creating the active child thread", 1);
+ pthread_attr_destroy(&detachedAttribute);
+ continue;
+ }
+ pthread_attr_destroy(&detachedAttribute);
+#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.
+ So, if the user do 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;
+
+ // Do the work
+ if (sock_initaddress((address[0]) ? address : NULL, port, &mainhints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ {
+ SOCK_ASSERT(errbuf, 1);
+ return;
+ }
+
+ tempaddrinfo= addrinfo;
+
+ while (tempaddrinfo)
+ {
+ SOCKET *socktemp;
+
+ if ( (sockmain= sock_open(tempaddrinfo, SOCKOPEN_SERVER, SOCKET_MAXCONN, errbuf, PCAP_ERRBUF_SIZE)) == -1)
+ {
+ SOCK_ASSERT(errbuf, 1);
+ tempaddrinfo= tempaddrinfo->ai_next;
+ continue;
+ }
+
+ // This trick is needed in order to allow the child thread to save the 'sockmain' variable
+ // withouth getting it overwritten by the sock_open, in case we want to open more than one waiting sockets
+ // For instance, the pthread_create() will accept the socktemp variable, and it will deallocate immediately that variable
+ socktemp= (SOCKET *) malloc (sizeof (SOCKET));
+ if (socktemp == NULL)
+ exit(0);
+
+ *socktemp= sockmain;
+
+#ifdef WIN32
+ /* GV we need this to create the thread as detached. */
+ /* GV otherwise, the thread handle is not destroyed */
+ pthread_attr_init(&detachedAttribute);
+ pthread_attr_setdetachstate(&detachedAttribute, PTHREAD_CREATE_DETACHED);
+
+ if ( pthread_create( &threadId, &detachedAttribute, (void *) &main_passive, (void *) socktemp ) )
+ {
+ SOCK_ASSERT("Error creating the passive child thread", 1);
+ pthread_attr_destroy(&detachedAttribute);
+ continue;
+ }
+
+ pthread_attr_destroy(&detachedAttribute);
+#else
+ if ( (pid= fork() ) == 0) // I am the child
+ {
+ main_passive( (void *) socktemp);
+ return;
+ }
+#endif
+ tempaddrinfo= tempaddrinfo->ai_next;
+ }
+
+ freeaddrinfo(addrinfo);
+ }
+
+ // All the previous calls are no blocking, so the main line of execution goes here
+ // and I have to avoid that the program terminates
+ while (1)
+ pthread_suspend(10*60*1000); // it wakes up every 10 minutes; it seems to me reasonable
+}
+
+
+/*
+ \brief Closes gracefully (more or less) the program.
+
+ This function is called:
+ - when we're running in console
+ - when we're running as a Win32 service (in case we press STOP)
+
+ It is not called when we are running as a daemon on UNIX, since
+ we do not define a signal in order to terminate gracefully the daemon.
+
+ This function makes a fast cleanup (it does not clean everything, as
+ you can see from the fact that it uses kill() on UNIX), closes
+ the main socket, free winsock resources (on Win32) and exits the
+ program.
+*/
+void main_cleanup(int sign)
+{
+#ifndef WIN32
+ // Sends a KILL signal to all the processes
+ // that share the same process group (i.e. kills all the childs)
+ kill(0, SIGKILL);
+#endif
+
+ SOCK_ASSERT(PROGRAM_NAME " is closing.\n", 1);
+
+ // FULVIO (bug)
+ // Here we close only the latest 'sockmain' created; if we opened more than one waiting sockets,
+ // only the latest one is closed correctly.
+ if (sockmain)
+ closesocket(sockmain);
+ sock_cleanup();
+
+ /*
+ This code is executed under the following conditions:
+ - SIGTERM: we're under UNIX, and the user kills us with 'kill -15'
+ (no matter is we're a daemon or in a console mode)
+ - SIGINT: we're in console mode and the user sends us a Ctrl+C
+ (SIGINT signal), no matter if we're UNIX or Win32
+
+ In all these cases, we have to terminate the program.
+ The case that still remains is if we're a Win32 service: in this case,
+ we're a child thread, and we want just to terminate ourself. This is because
+ the exit(0) will be invoked by the main thread, which is blocked waiting that
+ all childs terminates. We are forced to call exit from the main thread otherwise
+ the Win32 service control manager (SCM) does not work well.
+ */
+ if ( (sign == SIGTERM) || (sign == SIGINT) )
+ exit(0);
+ else
+ return;
+}
+
+
+
+#ifdef linux
+
+void main_cleanup_childs(int sign)
+{
+pid_t pid;
+int stat;
+
+ // For reference, Stevens, pg 128
+
+ while ( (pid= waitpid(-1, &stat, WNOHANG) ) > 0)
+ SOCK_ASSERT("Child terminated", 1);
+
+ return;
+}
+
+#endif
+
+
+
+
+
+/*!
+ \brief 'true' main of the program.
+
+ It must be in a separate function because:
+ - if we're in 'console' mode, we have to put the main thread waiting for a Ctrl+C
+ (in order to be able to stop everything)
+ - if we're in daemon mode, the main program must terminate and a new child must be
+ created in order to create the daemon
+
+ \param ptr: it keeps the main socket handler (what's called 'sockmain' in the main() ), that
+ represents the socket used in the main connection. It is a 'void *' just because pthreads
+ want this format.
+*/
+void main_passive(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 sockaddr_storage from; // generic sockaddr_storage variable
+socklen_t fromlen; // keeps the length of the sockaddr_storage variable
+SOCKET sockmain;
+
+#ifndef WIN32
+ pid_t pid;
+#endif
+
+ sockmain= *((SOCKET *) ptr);
+
+ // Delete the pointer (which has been allocated in the main)
+ free(ptr);
+
+ // Initialize errbuf
+ memset(errbuf, 0, sizeof(errbuf) );
+
+ // main thread loop
+ while (1)
+ {
+#ifdef WIN32
+ pthread_t threadId; // Pthread variable that keeps the thread structures
+ pthread_attr_t detachedAttribute;
+#endif
+ struct daemon_slpars *pars; // parameters needed by the daemon_serviceloop()
+
+ // Connection creation
+ fromlen = sizeof(struct sockaddr_storage);
+
+ sockctrl= accept(sockmain, (struct sockaddr *) &from, &fromlen);
+
+ if (sockctrl == -1)
+ {
+ // The accept() call can return this error when a signal is catched
+ // 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);
+ SOCK_ASSERT(errbuf, 1);
+ continue;
+ }
+
+ // checks if the connecting host is among the ones allowed
+ if (sock_check_hostlist(hostlist, RPCAP_HOSTLIST_SEP, &from, errbuf, PCAP_ERRBUF_SIZE) < 0 )
+ {
+ rpcap_senderror(sockctrl, errbuf, PCAP_ERR_HOSTNOAUTH, NULL);
+ sock_close(sockctrl, NULL, 0);
+ continue;
+ }
+
+
+#ifdef WIN32
+ // in case of passive mode, this variable is deallocated by the daemon_serviceloop()
+ pars= (struct daemon_slpars *) malloc ( sizeof(struct daemon_slpars) );
+ if (pars == NULL)
+ {
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno));
+ continue;
+ }
+
+ pars->sockctrl= sockctrl;
+ pars->activeclose= 0; // useless in passive mode
+ pars->isactive= 0;
+ pars->nullAuthAllowed= nullAuthAllowed;
+
+ /* GV we need this to create the thread as detached. */
+ /* GV otherwise, the thread handle is not destroyed */
+ pthread_attr_init(&detachedAttribute);
+ pthread_attr_setdetachstate(&detachedAttribute, PTHREAD_CREATE_DETACHED);
+ if ( pthread_create( &threadId, &detachedAttribute, (void *) &daemon_serviceloop, (void *) pars) )
+ {
+ SOCK_ASSERT("Error creating the child thread", 1);
+ pthread_attr_destroy(&detachedAttribute);
+ continue;
+ }
+ pthread_attr_destroy(&detachedAttribute);
+
+#else
+ if ( (pid= fork() ) == 0) // I am the child
+ {
+ // in case of passive mode, this variable is deallocated by the daemon_serviceloop()
+ pars= (struct daemon_slpars *) malloc ( sizeof(struct daemon_slpars) );
+ if (pars == NULL)
+ {
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno));
+ exit(0);
+ }
+
+ pars->sockctrl= sockctrl;
+ pars->activeclose= 0; // useless in passive mode
+ pars->isactive= 0;
+ pars->nullAuthAllowed= nullAuthAllowed;
+
+ // Close the main socket (must be open only in the parent)
+ closesocket(sockmain);
+
+ daemon_serviceloop( (void *) pars);
+ exit(0);
+ }
+
+ // I am the parent
+ // Close the childsocket (must be open only in the child)
+ closesocket(sockctrl);
+#endif
+
+ // loop forever, until interrupted
+ }
+}
+
+
+
+
+/*!
+ \brief 'true' main of the program in case the active mode is turned on.
+
+ It does not have any return value nor parameters.
+ 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 pthreads
+ want this format.
+*/
+void 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;
+struct daemon_slpars *pars; // parameters needed by the daemon_serviceloop()
+
+
+ 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;
+
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "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");
+ SOCK_ASSERT(errbuf, 1);
+
+ // Initialize errbuf
+ memset(errbuf, 0, sizeof(errbuf) );
+
+ // Do the work
+ if (sock_initaddress(activepars->address, activepars->port, &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ {
+ SOCK_ASSERT(errbuf, 1);
+ return;
+ }
+
+ while (1)
+ {
+ int activeclose;
+
+ if ( (sockctrl= sock_open(addrinfo, SOCKOPEN_CLIENT, 0, errbuf, PCAP_ERRBUF_SIZE)) == -1)
+ {
+ SOCK_ASSERT(errbuf, 1);
+
+ 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" );
+
+ SOCK_ASSERT(errbuf, 1);
+
+ pthread_suspend(RPCAP_ACTIVE_WAIT * 1000);
+
+ continue;
+ }
+
+ pars= (struct daemon_slpars *) malloc ( sizeof(struct daemon_slpars) );
+ if (pars == NULL)
+ {
+ snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno));
+ continue;
+ }
+
+ pars->sockctrl= sockctrl;
+ pars->activeclose= 0;
+ pars->isactive= 1;
+ pars->nullAuthAllowed= nullAuthAllowed;
+
+ daemon_serviceloop( (void *) pars);
+
+ activeclose= pars->activeclose;
+
+ free(pars);
+
+ // If the connection is closed by the user explicitely, don't try to connect to it again
+ // just exit the program
+ if (activeclose == 1)
+ break;
+ }
+}
+