Example #1
0
/*!
	\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
	if (fp)
	{
		if (threaddata)
		{
			pthread_cancel(threaddata);
			threaddata= 0;
		}
		if (fp->rmt_sockdata)
		{
			sock_close(fp->rmt_sockdata, NULL, 0);
			fp->rmt_sockdata= 0;
		}
		pcap_close(fp);
		fp= 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
	}
}
Example #2
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;

        // use global preselected interface if not set in config file
		if ((activelist[i].ifname[0] == '\0') &&
            (rpcapd_opt.preselected_ifname[0] != '\0')) {
		    snprintf(activelist[i].ifname, sizeof(activelist[i].ifname),
		             "%s", rpcapd_opt.preselected_ifname);
		}
		
#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]) )
		{
			log_warn("Error creating the active child thread");
			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)
		{
			log_warn("%s", errbuf);
			return;
		}

		tempaddrinfo= addrinfo;

		while (tempaddrinfo)
		{
		SOCKET *socktemp;

			if ( (sockmain= sock_open(tempaddrinfo, SOCKOPEN_SERVER, SOCKET_MAXCONN, errbuf, PCAP_ERRBUF_SIZE)) == -1)
			{
				log_warn("%s", errbuf);
				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 ) )
			{
				log_warn("Error creating the passive child thread");
				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
}
Example #3
0
/*!
	\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;

	log_info("Connecting to host %s, port %s, using protocol %s",
			 activepars->address, activepars->port, (hints.ai_family == AF_INET) ? "IPv4":
			 (hints.ai_family == AF_INET6) ? "IPv6" : "Unspecified");

	// Initialize errbuf
	memset(errbuf, 0, sizeof(errbuf) );

	// Do the work
	if (sock_initaddress(activepars->address, activepars->port, &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1)
	{
		log_warn("%s", errbuf);
		return;
	}

	while (1)
	{
	int activeclose;

		if ( (sockctrl= sock_open(addrinfo, SOCKOPEN_CLIENT, 0, errbuf, PCAP_ERRBUF_SIZE)) == -1)
		{
			log_warn("%s", errbuf);

			snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error connecting to host %s, port %s, using protocol %s",
					activepars->address, activepars->port, (hints.ai_family == AF_INET) ? "IPv4": 
					(hints.ai_family == AF_INET6) ? "IPv6" : "Unspecified" );

			log_warn("%s", errbuf);

			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;
		pars->preselected_ifname =
		        (activepars->ifname[0] != '\0') ? activepars->ifname : NULL;

		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;
	}
}
Example #4
0
static inline void ccxx_suspend(cctid_t tid) { pthread_suspend(tid); }