📄 pcap-remote.c
字号:
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 + -