📄 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 */
//! Keeps a list of all the opened connections in the active mode.
struct activehosts *activeHosts;
/****************************************************
* *
* Locally defined functions *
* *
****************************************************/
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);
int pcap_createfilter_norpcappkt(pcap_t *fp, 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 use 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)
{
struct rpcap_header *header; // general header according to the RPCAP format
struct rpcap_pkthdr *net_pkt_header; // header of the packet
char netbuf[RPCAP_NETBUF_SIZE]; // size of the network buffer in which the packet is copied, just for UDP
unsigned int nread; // number of bytes (of payload) currently read from the network (referred to the current pkt)
int retval; // generic return value
// 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
// Define the read timeout, to be used in the select()
// 'timeout', in pcap_t, is in milliseconds; we have to convert it into sec and microsec
#ifdef linux
tv.tv_sec = p->md.timeout/1000;
tv.tv_usec = (p->md.timeout - tv.tv_sec * 1000 )* 1000;
#else
tv.tv_sec = p->timeout/1000;
tv.tv_usec = (p->timeout - tv.tv_sec * 1000 )* 1000;
#endif
// Watch out sockdata to see if it has input
FD_ZERO(&rfds);
// 'fp->rmt_sockdata' has always to be set before calling the select(),
// since it is cleared by the select()
FD_SET(p->rmt_sockdata, &rfds);
retval = select(p->rmt_sockdata + 1, &rfds, NULL, NULL, &tv);
if (retval == -1)
{
sock_geterror("select(): ", p->errbuf, PCAP_ERRBUF_SIZE);
return -1;
}
// There is no data waiting, so return '0'
if (retval == 0)
return 0;
// data is here; so, let's copy it into the user buffer
// I'm going to read a new packet; so I reset the number of bytes (payload only) read
nread= 0;
// We have to define 'header' as a pointer to a larger buffer,
// because in case of UDP we have to read all the message within a single call
header= (struct rpcap_header *) netbuf;
net_pkt_header= (struct rpcap_pkthdr *) (netbuf + sizeof(struct rpcap_header) );
if (p->rmt_flags & PCAP_OPENFLAG_DATATX_UDP)
{
// Read the entire message from the network
if (sock_recv(p->rmt_sockdata, netbuf, RPCAP_NETBUF_SIZE, SOCK_RECEIVEALL_NO, p->errbuf, PCAP_ERRBUF_SIZE) == -1)
return -1;
}
else
{
if (sock_recv(p->rmt_sockdata, netbuf, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES, p->errbuf, PCAP_ERRBUF_SIZE) == -1)
return -1;
}
// Checks if the message is correct
retval= rpcap_checkmsg(p->errbuf, p->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
return -1; // 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
return 0; // Return 'no packets received'
default:
{
SOCK_ASSERT("Internal error", 1);
return 0; // Return 'no packets received
};
}
}
// In case of TCP, read the remaining of the packet from the socket
if ( !(p->rmt_flags & PCAP_OPENFLAG_DATATX_UDP))
{
// Read the RPCAP packet header from the network
if ( (nread= sock_recv(p->rmt_sockdata, (char *) net_pkt_header,
sizeof(struct rpcap_pkthdr), SOCK_RECEIVEALL_YES, p->errbuf, PCAP_ERRBUF_SIZE)) == -1)
return -1;
}
if ( (ntohl(net_pkt_header->caplen) + sizeof(struct pcap_pkthdr) ) <= ( (unsigned) p->bufsize) )
{
// Initialize returned structures
*pkt_header= (struct pcap_pkthdr *) p->buffer;
*pkt_data= p->buffer + sizeof(struct pcap_pkthdr);
(*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
p->md.TotCapt++;
// Copies the packet into the data buffer
if (p->rmt_flags & PCAP_OPENFLAG_DATATX_UDP)
{
unsigned int npkt;
// In case of UDP the packet has already been read; we have to copy it into 'buffer'
// Another option should bne to declare 'netbuf' as 'static'; however this prevents
// using several pcap instances within the same process (because the static buffer is shared among
// all processes)
memcpy(*pkt_data, netbuf + sizeof(struct rpcap_header) + sizeof(struct rpcap_pkthdr), (*pkt_header)->caplen);
// We're using UDP, so we need to update the counter of the packets dropped by the network
npkt= ntohl(net_pkt_header->npkt);
if (p->md.TotCapt != npkt)
{
p->md.TotNetDrops+= (npkt - p->md.TotCapt);
p->md.TotCapt= npkt;
}
}
else
{
// In case of TCP, read the remaining of the packet from the socket
if ( (nread+= sock_recv(p->rmt_sockdata, *pkt_data, (*pkt_header)->caplen, SOCK_RECEIVEALL_YES, p->errbuf, PCAP_ERRBUF_SIZE)) == -1)
return -1;
// Checks if all the data has been read; if not, discard the data in excess
// This check has to be done only on TCP connections
if (nread != ntohl(header->plen))
sock_discard(p->rmt_sockdata, ntohl(header->plen) - nread, NULL, 0);
}
// Packet read successfully
return 1;
}
else
{
snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Received a packet that is larger than the internal buffer size.");
return -1;
}
}
/*! \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) == 1)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -