1.4 wpcap.dll中相应函数接口的实现
下面对各函数在wpcap.dll中的实现进行详细分析。
1.4.1 pcap_open函数
函数首先确认参数source的长度不超过PCAP_BUF_SIZE,然后调用pcap_parsesrcstr()函数分析源的种类,是文件,本地主机接口还是远程主机接口,并依据不同的源类型作不同的处理。
如果是文件类型,直接调用pcap_open_offline函数处理。
如果是远程主机接口类型,调用pcap_create()创建一个pcap_t结构体,然后调用函数pcap_opensource_remote()通过打开一个远程主机的适配器,最后给pcap_t结构体几个重要成员设置合适的值,包括捕获数据包长度、读取超时,与pcap_startcapture()需要使用的标识。
如果是本地主机接口类型,直接调用pcap_open_live()函数,然后给NPF驱动设置几个标识。根据需要,可设置禁止回环数据包捕获,还可设置一次读操作所需获得的最小数据长度(字节为单位)。
最后函数返回一个pcap_t结构体指针,供后面的调用使用。
函数主要代码如下:
pcap_t *pcap_open(const char *source,
int snaplen, int flags, int read_timeout,
struct pcap_rmtauth *auth, char *errbuf)
{
char host[PCAP_BUF_SIZE], port[PCAP_BUF_SIZE], name[PCAP_BUF_SIZE];
int type;
pcap_t *fp;
int result;
/*确认source的长度不超过PCAP_BUF_SIZE*/
if (strlen(source) > PCAP_BUF_SIZE)
{//错误,函数返回
…
}
/*分析源的种类,文件,本地主机接口还是远程主机接口)*/
if (pcap_parsesrcstr(source, &type, host, port, name, errbuf) == -1)
return NULL;
/*依据不同的源类型作不同的处理*/
switch (type)
{
//文件类型,直接调用pcap_open_offline函数处理
case PCAP_SRC_FILE:
fp = pcap_open_offline(name, errbuf);
break;
//远程主机接口类型,打开远程主机的适配器,设置几个重要标识的值。
case PCAP_SRC_IFREMOTE:
//创建pcap_t结构体
fp= pcap_create(source, errbuf);
if (fp == NULL)
{
return NULL;
}
//打开一个远程主机的适配器
result= pcap_opensource_remote(fp, auth);
if (result != 0)
{
pcap_close(fp);
return NULL;
}
//设置捕获数据包长度、读取超时,
//与pcap_startcapture()需要使用的标识。
fp->snapshot= snaplen;
fp->md.timeout= read_timeout;
fp->rmt_flags= flags;
break;
//本地主机接口类型,直接调用pcap_open_live()函数,
//然后给NPF驱动设置几个标识。
case PCAP_SRC_IFLOCAL:
fp = pcap_open_live(name, snaplen, (flags &
PCAP_OPENFLAG_PROMISCUOUS), read_timeout, errbuf);
#ifdef WIN32
//这些标识仅被Windows支持
if (fp != NULL && fp->adapter != NULL)
{
/* 如果需要,禁止回环数据包捕获*/
if(flags & PCAP_OPENFLAG_NOCAPTURE_LOCAL)
{
if(!PacketSetLoopbackBehavior(fp->adapter,
NPF_DISABLE_LOOPBACK))
{ //设置失败,函数返回
…
}
}
/*如果需要,设置一次读操作所需获得的最小数据长度(字节为单位)*/
if(flags & PCAP_OPENFLAG_MAX_RESPONSIVENESS)
{
if(!PacketSetMinToCopy(fp->adapter, 0))
{//设置失败,函数返回
…
}
}
}
#endif //WIN32
break;
default:
//不支持的源类型
strcpy(errbuf, "Source type not supported");
return NULL;
}
return fp;
}
其中函数pcap_parsesrcstr()通过解析一个源字符串,来分析源的种类,是文件,本地主机接口还是远程主机接口)并返回分离出来的内容(type,host,port,name)。函数原型如下:
int
pcap_parsesrcstr(const char *source, int *type,
char
*host, char *port, char *name, char *errbuf)
参数source为要分析的源名称。。参数host、port与name分别返回源的主机、端口与名称,针对不同的源类型,该三个参数可能为空值。参数errbuf
保存函数的错误信息。
其中参数type返回源的类型,WinPcap内部用来表示源的类型的各标识如下:
PCAP_SRC_FILE指明为一个文件,用户希望从一个本地文件获得数据包。
PCAP_SRC_IFLOCAL指明为一个本机接口,比如用户希望从一个本地主机接口获得数据包。不采用RPCAP协议。
PCAP_SRC_IFREMOTE指明为一个远程接口,比如用户希望从一个远程主机接口获得数据包。需要采用RPCAP协议。
其中函数pcap_opensource_remote()通过打开一个RPCAP连接等方式,打开一个远程主机的适配器。函数原型如下:
int pcap_opensource_remote(pcap_t *fp, struct pcap_rmtauth *auth)
参数fp是一个指向pcap_t结构体的指针,该结构体在前面由pcap_create()函数所创建。参数auth是认证权限。
如果函数成功,返回0值,fp指针能够被用来作为后续调用的参数(如pcap_compile()等等)。如果函数失败,返回-1值,fp->errbuf中存储错误信息。
该函数为一个远程接口完成与pcap_open_live()基本类似的功能。不同的是,此处,在调用pcap_startcapture_remote()之前,捕获线程并不启动。因为,在远程捕获时,我们不能在“open adapter”命令一发送出去就开始捕获数据。考虑远程适配器已经负载很重的时候,如果我们开始一个捕获(其默认情况下,具有一个NULL过滤器),新的网络流量将使网络饱和。
作为替代方案,我们想先"open"适配器,接着仅当在我们准备好开始捕获时发送一个"start capture"的命令。该函数作了这样的工作:它发送一个“open adapter”命令(根据RPCAP协议),但是它不开始捕获。
既然其它的libpcap函数并不一定共享该工作方式,我们不得不做一些并不优雅的事情,使得每件事情都能妥善的解决。
注意:万一在捕获还没有开始时,就调用了pcap_compile()函数,过滤器将被存储到pcap_t结构体中,稍后它将被发送到另一个主机(当pcap_startcapture_remote()被调用时
Wpcap的筛选器基于公开的句法,一个筛选器其实就是一个包含筛选选项的ASCII码的字符串,pcap_compile()得到这个字符串并且在程序中对他进行翻译来完成内核数据筛选。
这个表述将选择什么样的数据报被截获,如果没有设置这个筛选器,那么网络上的所有数据将被截获,否则只有将满足这个条件的数据被截获。
筛选器语句的组成部分:
这个表述有一个或者多个参数组成,由一个或者多个限定词的ID组成,以下是允许的三种限定:
type:
这个限定词是指与ID名字有关的时间类型,一般来说可能的类型有host、net和port.。例如:host foo,net 128.3,port 20如果没有被指定,那么我们视为FALSE.也就是相当于这个条件不存在;
dir:
这个限定词对上面提及的ID进行一定的修饰,使用or或者and,和我们在离散数学中学习过的与或关系差不多,有以下几种src, dst, src or dst and src and dst.;
例如:scr foo,dst net 128.3,scr and dst port ftp-data;
如果这个限定词为空,src or dst 被置为False,在链路层(也就是点对点协议:想SLIP:串行线路接口协议)inbound and outbound 可以来规定一个想得到的方向。
proto:
这个限定词主要是用来限定特殊的协议类型,具体的类型有:ether, fddi, tr, ip, ip6, arp, rarp, decnet, tcp and udp ,例如:ether src foo ,arp net 128.3 ,tcp port 21;
如果没有指定协议限定词,所有协议类型的数据都可以被截获或者记录,
例如:`src foo' means `(ip or arp or rarp) src foo' (except the latter is not legal syntax), `net bar' means `(ip or arp or rarp) net bar' and `port 53' means `(tcp or udp) port 53'.
这个表述将选择什么样的数据报被截获,如果没有设置这个筛选器,那么网络上的所有数据将被截获,否则只有将满足这个条件的数据被截获。
筛选器语句的组成部分:
这个表述有一个或者多个参数组成,由一个或者多个限定词的ID组成,以下是允许的三种限定:
type:
这个限定词是指与ID名字有关的时间类型,一般来说可能的类型有host、net和port.。例如:host foo,net 128.3,port 20如果没有被指定,那么我们视为FALSE.也就是相当于这个条件不存在;
dir:
这个限定词对上面提及的ID进行一定的修饰,使用or或者and,和我们在离散数学中学习过的与或关系差不多,有以下几种src, dst, src or dst and src and dst.;
例如:scr foo,dst net 128.3,scr and dst port ftp-data;
如果这个限定词为空,src or dst 被置为False,在链路层(也就是点对点协议:想SLIP:串行线路接口协议)inbound and outbound 可以来规定一个想得到的方向。
proto:
这个限定词主要是用来限定特殊的协议类型,具体的类型有:ether, fddi, tr, ip, ip6, arp, rarp, decnet, tcp and udp ,例如:ether src foo ,arp net 128.3 ,tcp port 21;
如果没有指定协议限定词,所有协议类型的数据都可以被截获或者记录,
例如:`src foo' means `(ip or arp or rarp) src foo' (except the latter is not legal syntax), `net bar' means `(ip or arp or rarp) net bar' and `port 53' means `(tcp or udp) port 53'.