📄 pcap-remote.c
字号:
/*
* 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.
*
*/
#include <string.h> // for strlen(), ...
#include <stdlib.h> // for malloc(), free(), ...
#include <stdarg.h> // for functions with variable number of arguments
#include <errno.h> // for the errno variable
#include <pcap.h>
#include <pcap-int.h>
#include <pcap-remote.h>
#include <sockutils.h>
/*!
\file pcap-remote.c
This file keeps all the new funtions that are needed for the RPCAP protocol.
Almost all the pcap functions need to be modified in order to become compatible
with the RPCAP protocol. However, you can find here only the ones that are completely new.
This file keeps also the functions that are 'private', i.e. are needed by the RPCAP
protocol but are not exported to the user.
\warning All the RPCAP functions that are allowed to return a buffer containing
the error description can return max PCAP_ERRBUF_SIZE characters.
However there is no guarantees that the string will be zero-terminated.
Best practice is to define the errbuf variable as a char of size 'PCAP_ERRBUF_SIZE+1'
and to insert manually a NULL character at the end of the buffer. This will
guarantee that no buffer overflows occur even if we use the printf() to show
the error on the screen.
*/
#define PCAP_STATS_STANDARD 0 /*!< Used by pcap_stats_remote to see if we want standard or extended statistics */
#define PCAP_STATS_EX 1 /*!< Used by pcap_stats_remote to see if we want standard or extended statistics */
/*
\brief Global variable; needed to keep the message due to an error that we want to discard.
This can happen, for instance, because we already have an error message and we want to keep
the first one.
*/
char fakeerrbuf[PCAP_ERRBUF_SIZE + 1];
//! Keeps a list of all the opened connections in the active mode.
struct activehosts *activeHosts;
/****************************************************
* *
* Locally defined functions *
* *
****************************************************/
void rpcap_thrdatamain_stream(void *ptr);
void rpcap_thrdatamain_dgram(void *ptr);
int rpcap_checkver(SOCKET sock, struct rpcap_header *header, char *errbuf);
struct pcap_stat *rpcap_stats_remote(pcap_t *p, struct pcap_stat *ps, int mode);
int pcap_pack_bpffilter(pcap_t *fp, char *sendbuf, int *sendbufidx, struct bpf_program *prog);
/****************************************************
* *
* Function bodies *
* *
****************************************************/
/*! \ingroup remote_pri_func
\brief It traslates (i.e. de-serializes) a 'sockaddr_storage' structure from
the network byte order to the host byte order.
It accepts a 'sockaddr_storage' structure as it is received from the network and it
converts it into the host byte order (by means of a set of ntoh() ).
The function will allocate the 'sockaddrout' variable according to the address family
in use. In case the address does not belong to the AF_INET nor AF_INET6 families,
'sockaddrout' is not allocated and a NULL pointer is returned.
This usually happens because that address does not exist on the other host, so the
RPCAP daemon sent a 'sockaddr_storage' structure containing all 'zero' values.
\param sockaddrin: a 'sockaddr_storage' pointer to the variable that has to be
de-serialized.
\param sockaddrout: a 'sockaddr_storage' pointer to the variable that will contain
the de-serialized data. The structure returned can be either a 'sockaddr_in' or 'sockaddr_in6'.
This variable will be allocated automatically inside this function.
\param errbuf: a pointer to a user-allocated buffer (of size PCAP_ERRBUF_SIZE)
that will contain the error message (in case there is one).
\return '0' if everything is fine, '-1' if some errors occurred. Basically, the error
can be only the fact that the malloc() failed to allocate memory.
The error message is returned in the 'errbuf' variable, while the deserialized address
is returned into the 'sockaddrout' variable.
\warning This function supports only AF_INET and AF_INET6 address families.
\warning The sockaddrout (if not NULL) must be deallocated by the user.
*/
int rpcap_deseraddr(struct sockaddr_storage *sockaddrin, struct sockaddr_storage **sockaddrout, char *errbuf)
{
// Warning: we support only AF_INET and AF_INET6
if ( ntohs(sockaddrin->ss_family) == AF_INET)
{
struct sockaddr_in *sockaddr;
sockaddr= (struct sockaddr_in *) sockaddrin;
sockaddr->sin_family= ntohs(sockaddr->sin_family);
sockaddr->sin_port= ntohs(sockaddr->sin_port);
(*sockaddrout)= (struct sockaddr_storage *) malloc ( sizeof(struct sockaddr_in) );
if ( (*sockaddrout) == NULL)
{
snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno));
return -1;
}
memcpy( *sockaddrout, sockaddr, sizeof(struct sockaddr_in) );
return 0;
}
if ( ntohs(sockaddrin->ss_family) == AF_INET6)
{
struct sockaddr_in6 *sockaddr;
sockaddr= (struct sockaddr_in6 *) sockaddrin;
sockaddr->sin6_family= ntohs(sockaddr->sin6_family);
sockaddr->sin6_port= ntohs(sockaddr->sin6_port);
sockaddr->sin6_flowinfo= ntohl(sockaddr->sin6_flowinfo);
sockaddr->sin6_scope_id= ntohl(sockaddr->sin6_scope_id);
(*sockaddrout)= (struct sockaddr_storage *) malloc ( sizeof(struct sockaddr_in6) );
if ( (*sockaddrout) == NULL)
{
snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno));
return -1;
}
memcpy( *sockaddrout, sockaddr, sizeof(struct sockaddr_in6) );
return 0;
}
// It is neither AF_INET nor AF_INET6
*sockaddrout= NULL;
return 0;
}
/*! \ingroup remote_pri_func
\brief It reads a packet from the network socket. This does not make used of
callback (hence the "nocb" string into its name).
This function is called by the several pcap_next_ex() when they detect that
we have a remote capture and they are the client side. In that case, they need
to read packets from the socket.
Parameters and return values are exactly the same of the pcap_next_ex().
\warning By choice, this function does not make use of semaphores. A smarter
implementation should put a semaphore into the data thread, and a signal will
be raised as soon as there is data into the socket buffer.
However this is complicated and it does not bring any advantages when reading
from the network, in which network delays can be much more important than
these optimizations. Therefore, we chose the following approach:
- the 'timeout' chosen by the user is split in two (half on the server side,
with the usual meaning, and half on the client side)
- this function checks for packets; if there are no packets, it waits for
timeout/2 and then it checks again. If packets are still missing, it returns,
otherwise it reads packets.
*/
int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr **pkt_header, u_char **pkt_data)
{
int cc;
register u_char *ep, *bp;
cc= p->cc;
bp= p->bp;
ep = bp + cc;
if (bp < ep)
{
register int caplen, hdrlen;
again:
caplen = ((struct pcap_pkthdr *)bp)->caplen;
hdrlen= sizeof(struct pcap_pkthdr);
/*
* XXX A bpf_hdr matches a pcap_pkthdr.
*/
*pkt_header = (struct pcap_pkthdr *) bp;
*pkt_data = bp + hdrlen;
bp += BPF_WORDALIGN(caplen + hdrlen);
p->bp = bp;
p->cc = ep - bp;
return (1);
}
else
{
p->cc = 0;
// If there are no packets, the read thread must be suspended
// another horrible difference...
#ifdef linux
pthread_suspend(p->md.timeout);
#endif
#ifdef WIN32
pthread_suspend(p->timeout);
#endif
// check if now if have data; otherwise returns
cc= p->cc;
bp= p->bp;
ep = bp + cc;
if (bp < ep)
goto again;
return (0);
}
}
/*! \ingroup remote_pri_func
\brief It reads a packet from the network socket.
This function is called by the several pcap_read() when they detect that
we have a remote capture and they are the client side. In that case, they need
to read packets from the socket.
This function relies on the pcap_read_nocb_remote to deliver packets. The
difference, here, is that as soon as a packet is read, it is delivered
to the application by means of a callback function.
Parameters and return values are exactly the same of the pcap_read().
*/
int pcap_read_remote(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
{
struct pcap_pkthdr *pkt_header;
u_char *pkt_data;
int n = 0;
while ( (++n <= cnt) || (cnt < 0) )
{
if (pcap_read_nocb_remote(p, &pkt_header, &pkt_data) )
(*callback)(user, pkt_header, pkt_data);
else
return n;
}
return n;
}
/*! \ingroup remote_pri_func
\brief It sends a CLOSE command to the capture server.
This function is called when the user wants to close a pcap_t adapter.
In case we're capturing from the network, it sends a command to the other
peer that says 'ok, let's stop capturing'.
This function is called automatically when the user calls the pcap_close().
Parameters and return values are exactly the same of the pcap_close().
\warning Since we're closing the connection, we do not check for errors.
*/
void pcap_close_remote(pcap_t *fp)
{
struct rpcap_header header; // header of the RPCAP packet
struct activehosts *temp; // temp var needed to scan the host list chain, to detect if we're in active mode
int active= 0; // active mode or not?
// detect if we're in active mode
temp= activeHosts;
while (temp)
{
if (temp->sockctrl == fp->rmt_sockctrl)
{
active= 1;
break;
}
temp= temp->next;
}
if (!active)
{
rpcap_createhdr( &header, RPCAP_MSG_CLOSE, 0, 0);
// I don't check for errors, since I'm going to close everything
sock_send(fp->rmt_sockctrl, (char *) &header, sizeof (struct rpcap_header), fakeerrbuf);
}
else
{
rpcap_createhdr( &header, RPCAP_MSG_ENDCAP_REQ, 0, 0);
// I don't check for errors, since I'm going to close everything
sock_send(fp->rmt_sockctrl, (char *) &header, sizeof (struct rpcap_header), fakeerrbuf);
// wait for the answer
// Don't check what we got, since the present libpcap does not uses this pcap_t anymore
sock_recv(fp->rmt_sockctrl, (char *) &header, sizeof(struct rpcap_header), fakeerrbuf);
if ( ntohl(header.plen) != 0)
sock_discard(fp->rmt_sockctrl, ntohl(header.plen), fakeerrbuf);
}
if (fp->rmt_threaddata)
{
pthread_cancel(fp->rmt_threaddata);
fp->rmt_threaddata= 0;
}
if (fp->rmt_sockdata)
{
sock_close(fp->rmt_sockdata, fakeerrbuf);
fp->rmt_sockdata= 0;
}
if ( (!active) && (fp->rmt_sockctrl) )
sock_close(fp->rmt_sockctrl, fakeerrbuf);
fp->rmt_sockctrl= 0;
// To avoid inconsistencies in the number of sock_init()
sock_cleanup();
}
/*! \ingroup remote_pri_func
\brief It retrieves network statistics from the other peer.
This function is just a void cointainer, since the work is done by the rpcap_stats_remote().
See that funcion for more details.
Parameters and return values are exactly the same of the pcap_stats().
*/
int pcap_stats_remote(pcap_t *p, struct pcap_stat *ps)
{
struct pcap_stat *retval;
retval= rpcap_stats_remote(p, ps, PCAP_STATS_STANDARD);
if (retval)
return 0;
else
return -1;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -