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

📄 pcap-remote.c

📁 用来监视网络通信数据的源代码和应用程序,方便网络程序底层开发.
💻 C
📖 第 1 页 / 共 5 页
字号:
unsigned int nread= 0;			// number of bytes of the payload read from the socket
int retval;						// store the return value of the functions
int active= 0;					// '1' if we're in active mode
struct activehosts *temp;		// temp var needed to scan the host list chain, to detect if we're in active mode
char host[INET6_ADDRSTRLEN + 1];	// numeric name of the other host

// socket-related variables
struct addrinfo hints;			// temp, needed to open a socket connection
struct addrinfo *addrinfo;		// temp, needed to open a socket connection
SOCKET sockdata= 0;				// socket descriptor of the data 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
int ai_family;					// temp, keeps the address family used by the control connection

// RPCAP-related variables
struct rpcap_header header;					// header of the RPCAP packet
struct rpcap_startcapreq *startcapreq;		// start capture request message
struct rpcap_startcapreply startcapreply;	// start capture reply message


	// detect if we're in active mode
	temp= activeHosts;
	while (temp)
	{
		if (temp->sockctrl == fp->rmt_sockctrl)
		{
			active= 1;
			break;
		}
		temp= temp->next;
	}

	addrinfo= NULL;

	// Gets the complete sockaddr structure used in the ctrl connection
	// This is needed to get the address family of the control socket
	// Tip: I cannot save the ai_family of the ctrl sock in the pcap_t struct,
	// since the ctrl socket can already be open in case of active mode;
	// so I would have to call getpeername() anyway
	saddrlen = sizeof(struct sockaddr_storage);
	if (getpeername(fp->rmt_sockctrl, (struct sockaddr *) &saddr, &saddrlen) == -1)
	{
		sock_geterror("getsockname(): ", fp->errbuf, PCAP_ERRBUF_SIZE);
		goto error;
	}
	ai_family= ((struct sockaddr_storage *) &saddr)->ss_family;

	// Get the numeric address of the remote host we are connected to
	if (getnameinfo( (struct sockaddr *) &saddr, saddrlen, host, 
			sizeof(host), NULL, 0, NI_NUMERICHOST) )
	{
		sock_geterror("getnameinfo(): ", fp->errbuf, PCAP_ERRBUF_SIZE);
		goto error;
	}

	/*
		Data connection is opened by the server toward the client if:
		- we're using TCP, and the user wants us to be in active mode
		- we're using UDP
	*/
	if ( (active) || (fp->rmt_flags & PCAP_OPENFLAG_UDP_DP) )
	{
		// We have to create a new socket to receive packets
		// We have to do that immediately, since we have to tell the other 
		// end which network port we picked up
		memset(&hints, 0, sizeof(struct addrinfo) );
		// TEMP addrinfo is NULL in case of active
		hints.ai_family = ai_family;	// Use the same address family of the control socket
		hints.ai_socktype = (fp->rmt_flags & PCAP_OPENFLAG_UDP_DP) ? SOCK_DGRAM : SOCK_STREAM;
		hints.ai_flags = AI_PASSIVE;	// Data connection is opened by the server toward the client

		// Let's the server pick up a free network port for us
		if (sock_validaddr(NULL, "0", &hints, &addrinfo, fp->errbuf) == -1)
			goto error;

		if ( (sockdata= sock_open(addrinfo, SOCKOPEN_SERVER, 1 /* max 1 connection in queue */, fp->errbuf)) == -1)
			goto error;

		// addrinfo is no longer used
		freeaddrinfo(addrinfo);
		addrinfo= NULL;

		// 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(): ", fp->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(): ", fp->errbuf, PCAP_ERRBUF_SIZE);
			goto error;
		}
	}

	// Now it's time to start playing with the RPCAP protocol
	// RPCAP start ca[ture command: create the request message
	if ( sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, fp->errbuf) )
		goto error;

	rpcap_createhdr( (struct rpcap_header *) sendbuf, RPCAP_MSG_STARTCAP_REQ, 0,
		sizeof(struct rpcap_startcapreq) + sizeof(struct rpcap_filter) + fp->fcode.bf_len * sizeof(struct rpcap_filterbpf_insn) );

	// Fill the structure needed to open an adapter remotely
	startcapreq= (struct rpcap_startcapreq *) &sendbuf[sendbufidx];

	if ( sock_bufferize(NULL, sizeof(struct rpcap_startcapreq), NULL, &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, fp->errbuf) )
		goto error;

	memset(startcapreq, 0, sizeof(struct rpcap_startcapreq) );

	// By default, apply half the timeout on one side, half of the other
#ifdef linux
	fp->md.timeout= fp->md.timeout/2;
	startcapreq->read_timeout= htonl(fp->md.timeout);
#endif
#ifdef WIN32
	fp->timeout= fp->timeout/2;
	startcapreq->read_timeout= htonl(fp->timeout);
#endif

	// portdata on the openreq is meaningful only if we're in active mode
	if ( (active) || (fp->rmt_flags & PCAP_OPENFLAG_UDP_DP) )
	{
		sscanf(portdata, "%d", &(startcapreq->portdata));
		startcapreq->portdata= htons(startcapreq->portdata);
	}

	startcapreq->snaplen= htonl(fp->snapshot);
	startcapreq->flags= 0;
	if (fp->rmt_flags & PCAP_OPENFLAG_PROMISCUOUS)
		startcapreq->flags|= RPCAP_STARTCAPREQ_FLAG_PROMISC;
	if (fp->rmt_flags & PCAP_OPENFLAG_UDP_DP)
		startcapreq->flags|= RPCAP_STARTCAPREQ_FLAG_DGRAM;
	if (active)
		startcapreq->flags|= RPCAP_STARTCAPREQ_FLAG_SERVEROPEN;

	startcapreq->flags= htons(startcapreq->flags);

	// Pack the capture filter
	if (pcap_pack_bpffilter(fp, &sendbuf[sendbufidx], &sendbufidx, &fp->fcode) )
		goto error;

	if ( sock_send(fp->rmt_sockctrl, sendbuf, sendbufidx, fp->errbuf) )
		goto error;


	// Receive the RPCAP start capture reply message
	if (sock_recv(fp->rmt_sockctrl, (char *) &header, sizeof(struct rpcap_header), fp->errbuf) == -1)
		goto error;

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

	if (retval != RPCAP_MSG_STARTCAP_REPLY)		// the message is not the one expected
	{
		switch (retval)
		{
			case -3:		// Unrecoverable network error
			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
				goto error;

			case RPCAP_MSG_ERROR:		// The other endpoint reported an error
				// Update nread, since the rpcap_checkmsg() already purged the buffer
				nread = ntohl(header.plen);
				// Do nothing; just exit; the error code is already into the errbuf
				goto error;

			default:
			{
				snprintf(fp->errbuf, PCAP_ERRBUF_SIZE, "Internal error");
				goto error;
			};
		}
	}


	if ( (nread+= sock_recv(fp->rmt_sockctrl, (char *) &startcapreply, sizeof(struct rpcap_startcapreply), fp->errbuf)) == -1)
		goto error;

	// Data connection is opened by the client toward the server
	// This happens because (a) we're not in active mode, and (b) we're not using UDP
	// So, let's start the connection
	if ( (!active) && !(fp->rmt_flags & PCAP_OPENFLAG_UDP_DP) )
	{
		memset(&hints, 0, sizeof(struct addrinfo) );
		hints.ai_family = ai_family;		// Use the same address family of the control socket
		hints.ai_socktype = (fp->rmt_flags & PCAP_OPENFLAG_UDP_DP) ? SOCK_DGRAM : SOCK_STREAM;

		sprintf(portdata, "%d", ntohs(startcapreply.portdata) );

		// Let's the server pick up a free network port for us
		if (sock_validaddr(host, portdata, &hints, &addrinfo, fp->errbuf) == -1)
			goto error;

		if ( (sockdata= sock_open(addrinfo, SOCKOPEN_CLIENT, 0, fp->errbuf)) == -1)
			goto error;

		// addrinfo is no longer used
		freeaddrinfo(addrinfo);
		addrinfo= NULL;
	}


	// Allocates WinPcap/libpcap user buffer
	// It has the same size of the one used on the other side of the connection
	fp->bufsize= ntohl(startcapreply.bufsize);

	/*
		Warning: on some kernels (e.g. linux), the size of the user buffer does not take
		into account the pcap_header and such, and it is set equal to the snaplen.
		In our view, this is wrong (the meaning of the bufsize becames a bit strange).
		So, in our view, bufsize is the whole size of the user buffer.
		In case the bufsize returned is too small, adjust it accordingly.
	*/
	if (fp->bufsize <= fp->snapshot)
		fp->bufsize+= sizeof (struct pcap_pkthdr);

	fp->buffer = (u_char *) malloc(fp->bufsize);
	if (fp->buffer == NULL)
	{
		snprintf(fp->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno));
		goto error;
	}

	// The server is trying to connect to me; so it have to block on an accept() call
	// However, if the data connection is UDP, I don't have to call accept()
	if ( (active) && !(fp->rmt_flags & PCAP_OPENFLAG_UDP_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(): ", fp->errbuf, PCAP_ERRBUF_SIZE);
			goto error;
		}

		// Now that I accepted the connection, the server socket is no longer needed
		sock_close(sockdata, fp->errbuf);
		sockdata= socktemp;
	}

	fp->rmt_sockdata= sockdata;

	// Now we have to create a new thread to receive packets
	if (fp->rmt_flags & PCAP_OPENFLAG_UDP_DP)
	{
		if ( pthread_create( &threaddata, NULL, (void *) &rpcap_thrdatamain_dgram, (void *) fp) )
		{
			snprintf(fp->errbuf, PCAP_ERRBUF_SIZE, "Error creating the data thread");
			goto error;
		}
	}
	else
	{
		if ( pthread_create( &threaddata, NULL, (void *) &rpcap_thrdatamain_stream, (void *) fp) )
		{
			snprintf(fp->errbuf, PCAP_ERRBUF_SIZE, "Error creating the data thread");
			goto error;
		}
	}

	fp->rmt_threaddata= threaddata;

	// Checks if all the data has been read; if not, discard the data in excess
	if (nread != ntohl(header.plen))
	{
		if (sock_discard(fp->rmt_sockctrl, ntohl(header.plen) - nread, fakeerrbuf) == 1)
			goto error;
	}
	return 0;

error:
// When the connection has been established, we have to close it. So, at the
// beginning of this function, if an error occur we return immediately with
// a return NULL; when the connection is established, we have to come here 
// ('goto error;') in order to close everything properly.

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

	if (threaddata)
		pthread_cancel(threaddata);

	if ((sockdata) && (sockdata != -1))		// we can be here because sockdata said 'error'
		sock_close(sockdata, fakeerrbuf);

	if (!active)
		sock_close(fp->rmt_sockctrl, fakeerrbuf);

	if (fp)
	{
		pcap_close(fp);
		fp= NULL;
	}

	return -1;
}





/*!
	\brief Takes a bpf program and sends it to the other host.

	This function can be called in two cases:
	- the pcap_startcapture() is called (we have to send the filter along with
	the 'start capture' command)
	- we want to udpate the filter during a capture (i.e. the pcap_setfilter()
	is called when the capture is still on)

	This function serializes the filter into the sending buffer ('sendbuf', passed
	as a parameter) and return back. It does not send anything on the network.

	\param fp: the pcap_t descriptor of the device currently opened.

	\param sendbuf: the buffer on which the serialized data has to copied.

	\param sendbufidx: it is used to return the abounf of bytes copied into the buffer.

	\param prog: the bpf prgoram we hve to copy.

	\return '0' if everything is fine, '-1' otherwise. The error message (if one)
	is returned into the 'errbuf' field of the pcap_t structure.
*/
int pcap_pack_bpffilter(pcap_t *fp, char *sendbuf, int *sendbufidx, struct bpf_program *prog)
{
struct rpcap_filter *filter;
struct rpcap_filterbpf_insn *insn;
struct bpf_insn *bf_insn;
unsigned int i;

	filter= (struct rpcap_filter *) sendbuf;

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

	filter->filtertype= htons(RPCAP_UPDATEFILTER_BPF);
	filter->nitems= htonl( (int32) prog->bf_len);

	if ( sock_bufferize(NULL, prog->bf_len * sizeof(struct rpcap_filterbpf_insn), NULL, sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, fp->errbuf) )
		return -1;
	
	insn= (struct rpcap_filterbpf_insn *) (filter + 1);
	bf_insn= prog->bf_insns;

	for (i= 0; i < prog->bf_len; i++)
	{
		insn->code= htons(bf_insn->code);
		insn->jf= bf_insn->jf;
		insn->jt= bf_insn->jt;
		insn->k= htonl(bf_insn->k);

		insn++;
		bf_insn++;
	}
	return 0;
}





/*!	\ingroup remote_pri_func

	\brief Update a filter on a remote host.

	This function is called when the user wants to update a filter.
	In case we're capturing from the network, it sends the filter to the other peer.
	This function is *not* called automatically when the user calls the pcap_setfilter().
	There will be two cases:

⌨️ 快捷键说明

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