⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pcap-remote.c

📁 用来监视网络通信数据的源代码和应用程序,方便网络程序底层开发.
💻 C
📖 第 1 页 / 共 5 页
字号:
	- the capture is already on: in this case, pcap_setfilter() calls pcap_updatefilter_remote()
	- the capture has not started yet: in this case, pcap_setfilter() stores the filter into
	the pcap_t structure, and then the filter is sent with the pcap_startcap().

	Parameters and return values are exactly the same of the pcap_setfilter().

	\warning This function *does not* clear the packet currently into the buffers. Therefore,
	the user has to expect to receive some packets that are related to the previous filter.
	If you want to discard all the packets before applying a new filter, you have to close 
	the current capture session and start a new one.
*/
int pcap_updatefilter_remote(pcap_t *fp, struct bpf_program *prog)
{
int retval;						// general variable used to keep the return value of other functions
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_header header;		// To keep the reply message


	if ( sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, fp->errbuf) )
		return -1;

	rpcap_createhdr( (struct rpcap_header *) sendbuf, RPCAP_MSG_UPDATEFILTER_REQ, 0,
		sizeof(struct rpcap_filter) + prog->bf_len * sizeof(struct rpcap_filterbpf_insn));

	if (pcap_pack_bpffilter(fp, &sendbuf[sendbufidx], &sendbufidx, prog) )
		return -1;

	if ( sock_send(fp->rmt_sockctrl, sendbuf, sendbufidx, fp->errbuf) )
		return -1;

	// Waits for the answer
	if (sock_recv(fp->rmt_sockctrl, (char *) &header, sizeof(struct rpcap_header), fp->errbuf) == -1)
		return -1;

	// Checks if the message is correct
	retval= rpcap_checkmsg(fp->errbuf, fp->rmt_sockctrl, &header, RPCAP_MSG_UPDATEFILTER_REPLY, 0);

	if (retval != RPCAP_MSG_UPDATEFILTER_REPLY)		// the message is not the one expected
	{
		switch (retval)
		{
			case -3:		// Unrecoverable network error
			case -2:		// The other endpoint sent a message that is not allowed here
			case -1:	// The other endpoint has a version number that is not compatible with our
				// Do nothing; just exit from here; the error code is already into the errbuf
				return -1;

			default:
			{
				SOCK_ASSERT("Internal error", 0);
				return -1;
			};
		}
	}

	if (ntohl(header.plen) != 0)	// the message has an unexpected size
	{
		if (sock_discard(fp->rmt_sockctrl, ntohl(header.plen), fp->errbuf) == -1)
			return -1;
	}

	return 0;
}




/*!	\ingroup remote_pri_func

	\brief Send a filter to a remote host.

	This function is called when the user wants to set a filter.
	In case we're capturing from the network, it sends the filter to the other peer.
	This function is called automatically when the user calls the pcap_setfilter().

	Parameters and return values are exactly the same of the pcap_setfilter().
*/
int pcap_setfilter_remote(pcap_t *fp, struct bpf_program *prog)
{
	if (!fp->rmt_capstarted)
	{
		// copy filter into the pcap_t structure
		if (install_bpf_program(fp, prog) == -1)
			return -1;
		return 0;
	}

	// we have to update a filter during run-time
	if (pcap_updatefilter_remote(fp, prog) )
		return -1;

	return 0;
}



/*!	\ingroup remote_pri_func
	\brief Suspends a pthread for msec milliseconds.

	This fucntion 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
}



/*!	\ingroup remote_pri_func
	\brief Main function of the thread which waits for data packets (i.e.
	packets which carry a captured packet) in case of a TCP data connection.

	This function does basically the job of the Operating System kernel in the local
	capture: it takes packets and it puts them into the user buffer.

	\param ptr: it is a void pointer that will be casted into a pcap_t structure.
	This parameter is needed to retrieve the the socket (i.e. sockdata) we are currently 
	using for the data connection, the control socket (in case we want to send error messages),
	and so on.

	\return Nothing.
*/
void rpcap_thrdatamain_stream(void *ptr)
{
char errbuf[PCAP_ERRBUF_SIZE + 1];	// error buffer
pcap_t *fp;							// pointer to a 'pcap' structure
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
struct rpcap_header header;			// general header according to the RPCAP format
int cc;								// number of bytes of data available into the user buffer
unsigned int nread;					// number of bytes (of payload) currently read from the network (referred to the current pkt)
int pending= 0;						// '1' if we've already read the pkt header, but there was not enough space to read the pkt data

// 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

	fp= (pcap_t *) ptr;

	*errbuf= 0;	// Initialize errbuf

	// 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;

	// Watch out sockdata to see if it has input
	FD_ZERO(&rfds);

	// We do not have to block on the select()
	tv.tv_sec = 0;
	tv.tv_usec = 0;

	/*
		The algorithm chosen to manage ths user buffer is quite simple, although
		not completely optimized. For instance, this is the same algorithm that
		is implemented when a packet is read from the kernel.

		The producer (i.e. this thread) reads data from the network only when the
		user has already consumed all the data into the buffer.
		When data is read, it is copied at the beginning of the buffer.
		So, this is not a circular buffer.
		This job ends when (1) there is no data coming from the network, (2)
		the user buffer is completely full.

		The consumer (i.e. the thread that calls pcap_next_ex) can read only when
		the producer finished its job. In other words, even if there is already 
		data into the user buffer, the consumer cannot read it because the producer
		updates the status variables (fp->cc, fp->bp) only when it ends its job.
		This is the reason we do not need spinlocks.

		This algorithm has two main drawbacks:
		- even if there is data into the user buffer, the application could not be 
		able to see it
		- the producer reads the data from the network all at once, so the resulting
		behaviour is that all of a sudden the socket buffer is emptied and the TCP
		starts sending several acknowledges at the same time. This could lead to
		bursty traffic patterns.
	*/
	while (1)
	{
		if (fp->cc == 0) 
		{
			u_char *bp= fp->buffer;
			cc= 0;

			/*
				We will exit from this cycle while when:
				- the socket does not have data in it
				- the buffer does not have space left

				In all cases, the pcap status variables will be updated
				before calling 'break'.
			*/
			while (1)
			{
again:
				// 'fp->rmt_sockdata' has always to be set before calling the select(),
				// since it is cleared by the select()
				FD_SET(fp->rmt_sockdata, &rfds);

				retval = select(fp->rmt_sockdata + 1, &rfds, NULL, NULL, &tv);
				if (retval == -1)
				{
					sock_geterror("select(): ", errbuf, PCAP_ERRBUF_SIZE);
						goto error;
				}

				// There is no data waiting
				if (retval == 0)
				{
					// Update the pcap_t pointers so that the user application can read from the buffer
					fp->bp= fp->buffer;
					fp->cc= cc;

					pthread_suspend(100);	// Suspend 100 ms
					break;
				}

				if (!pending)
				{
					// I'm going to read a new packet; so I reset the number of bytes (payload only) read
					nread= 0;

					if (sock_recv(fp->rmt_sockdata, (char *) &header, sizeof(struct rpcap_header), errbuf) == -1)
						goto error;

					// Checks if the message is correct
					retval= rpcap_checkmsg(errbuf, fp->rmt_sockdata, &header, RPCAP_MSG_PACKET, 0);

					if (retval != RPCAP_MSG_PACKET)		// the message is not the one expected
					{
						switch (retval)
						{
							case -3:		// Unrecoverable network error									
								goto error;	// Do nothing; just exit from here; the error code is already into the errbuf

							case -2:		// The other endpoint sent a message that is not allowed here
							case -1:		// The other endpoint has a version number that is not compatible with our
								goto again;

							default:
							{
								SOCK_ASSERT("Internal error", 1);
								goto error;
							};
						}
					}

					// Read the RPCAP packet header from the network
					if ( (nread= sock_recv(fp->rmt_sockdata, (char *) &net_pkt_header, sizeof(struct rpcap_pkthdr), errbuf)) == -1)
						goto error;
				}

				if ( (cc + ntohl(net_pkt_header.caplen) + sizeof(struct pcap_pkthdr) ) <= ( (unsigned) fp->bufsize) )
				{
					pending= 0;

					pkt_header= (struct pcap_pkthdr *) bp;
					pkt_header->caplen= ntohl(net_pkt_header.caplen);
					pkt_header->len= ntohl(net_pkt_header.len);
					pkt_header->ts.tv_sec= ntohl(net_pkt_header.timestamp_sec);
					pkt_header->ts.tv_usec= ntohl(net_pkt_header.timestamp_usec);

					// I don't update the counter of the packets dropped by the network since we're using TCP;
					// therefore no packets are dropped. Just update the number of packets received correctly
					fp->md.TotCapt++;

					// Copies the packet into the data buffer
					if ( (nread+= sock_recv(fp->rmt_sockdata, (char *) bp + sizeof(struct pcap_pkthdr), pkt_header->caplen, errbuf)) == -1)
						goto error;

					bp += BPF_WORDALIGN(pkt_header->caplen + sizeof(struct pcap_pkthdr));
					cc += BPF_WORDALIGN(pkt_header->caplen + sizeof(struct pcap_pkthdr));

					// Checks if all the data has been read; if not, discard the data in excess
					if (nread != ntohl(header.plen))
						sock_discard(fp->rmt_sockdata, ntohl(header.plen) - nread, fakeerrbuf);

				}
				else
				{
					pending= 1;

					// Update the pcap_t pointers so that the user application can read from the buffer
					fp->bp= fp->buffer;
					fp->cc= cc;

					break;
				}
			} // end while (1)
		}
		else
		{
			pthread_suspend(100);	// Suspend 100 ms
		}

	}	// end main while(1)

error:
	SOCK_ASSERT("Exiting from the child data thread", 1);
	SOCK_ASSERT(errbuf, 1);

	// Removes the descriptor s from set
	FD_CLR(fp->rmt_sockdata, &rfds);

 	closesocket(fp->rmt_sockdata);
	fp->rmt_sockdata= 0;
	fp->rmt_threaddata= 0;
}



/*!	\ingroup remote_pri_func
	\brief Main function of the thread which waits for data packets (i.e.
	packets which carry a captured packet) in case of a UDP data connection.

	This function does basically the job of the Operating System kernel in the local
	capture: it takes packet and it puts them into the user buffer.

	This function is different from the rpcap_thrdatamain_stream(), because UDP requires
	that a message coming from the network is received using a single recv() call.
	Vice versa, TCP sockets allows you reading only a few bytes each time, and the remaining part 
	of the message is retained.

	From this point of view, UDP sockets makes this thread more epensive, because the message is 
	first copied into a temporary buffer (allocated by the thread itself), then, if the 
	WinPcap/libpcap user buffer has enough space, the data is copied into that buffer.

	\param ptr: it is a void pointer that will be casted into a pcap_t structure.
	This parameter is needed to retrieve the the socket (i.e. sockdata) we are currently 
	using for the data connection, the control socket (in case we want to send error messages),
	and so on.

	\return Nothing.
*/
void rpcap_thrdatamain_dgram(void *ptr)
{
char errbuf[PCAP_ERRBUF_SIZE + 1];	// error buffer
char netbuf[RPCAP_NETBUF_SIZE];		// size of the network buffer in which the packet is copied
pcap_t *fp;							// pointer to a 'pcap' structure
int retval;							// general variable used to keep the return value of other functions
struct rpcap_header *header;		// general header according to the RPCAP format
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
int cc;								// number of bytes of data available into the user buffer
int pending= 0;						// '1' if we've already read the pkt header, but there was not enough space to read the pkt data

// 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


	// See the rpcap_thrdatamain_stream() for any comment about the code
	fp= (pcap_t *) ptr;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -