您的位置:首页 > 理论基础 > 计算机网络

深度剖析WinPcap之(七)——获得与释放网络适配器设备列表(2)

2010-06-22 23:10 260 查看
转自http://eslxf.blog.51cto.com/918801/198579



st1/:*{behavior:url(#ieooui) }

1.3 wpcap.dll中相应函数接口的实现

1.3.1 pcap_findalldevs函数

wpcap/libpcap/fad-win32.c(219): 218-327
参数alldevsp返回所找到的适配器列表,参数errbuf返回错误信息。


int pcap_findalldevs(pcap_if_t **alldevsp, char *errbuf)

{
pcap_if_t *devlist = NULL;
int ret = 0;
const char *desc;
char *AdaptersName;
ULONG NameLength;
char *name;
// PacketGetAdapterNames 是packet.dll中提供的函数,获取一个可用网络适配器的列表与它们的描述
//NameLength返回存储适配器列表所需的字节数
if (!PacketGetAdapterNames(NULL, &NameLength))
{
//获取字节数失败,处理错误,程序退出

}

if (NameLength > 0)
AdaptersName = (char*) malloc(NameLength);//分配存储适配器列表所需内存
else
{
*alldevsp = NULL;
return 0;
}
if (AdaptersName == NULL)
{
snprintf(errbuf, PCAP_ERRBUF_SIZE, "Cannot allocate enough memory to list the adapters.");
return (-1);
}

if (!PacketGetAdapterNames(AdaptersName, &NameLength)) {//获取可用网络适配器列表
snprintf(errbuf, PCAP_ERRBUF_SIZE,
"PacketGetAdapterNames: %s",
pcap_win32strerror());
free(AdaptersName);
return (-1);
}

/* "PacketGetAdapterNames()"返回一个以空字符结尾的ASCII字符串,该字符串存储接口名列表,以一个空字符结尾,其后为一个以空字符结尾的ASCII字符串,该字符串存储接口描述列表,以一个空字符结尾。 这意味着在第一个列表的末尾有两个ASCII空字符。*/

/*查找第一个列表的末尾,那是第二个列表的开始*/
desc = &AdaptersName[0];
while (*desc != '/0' || *(desc + 1) != '/0') //查找第一个两个紧连着的空字符(‘/0/0’)
desc++;

/* 找到了 - "desc"指向接口名列表末尾的两个空字符的第一个。因此描述列表的第一个字节在后面两个字节的位置。*/
desc += 2;

/*循环遍历第一个列表中的所有元素*/
name = &AdaptersName[0];
while (*name != '/0') {
/*为每个接口在devlist链表中添加一个节点*/
if (pcap_add_if_win32(&devlist, name, desc, errbuf) == -1) {
/*操作失败*/
ret = -1;
break;
}
name += strlen(name) + 1;
desc += strlen(desc) + 1;
}


if (ret != -1) {
/*至此,没有任何错误,做任何特定平台的操作添加设备*/

/* pcap_platform_finddevs是 pcap_findalldevs()的内部接口
pcap_platform_finddevs()是一个依赖平台的例程,添加没有被“标准”机制"(SIOCGIFCONF,"getifaddrs()",等)找到的设备。
*/
if (pcap_platform_finddevs(&devlist, errbuf) < 0)
ret = -1;
}

if (ret == -1) {
//发生错误,释放所构建的列表devlist
if (devlist != NULL) {
pcap_freealldevs(devlist);
devlist = NULL;
}
}

*alldevsp = devlist; //返回最终的适配器列表
free(AdaptersName); //释放存储适配器列表所的内存
return (ret);

}

函数pcap_findalldevs首先调用packet.dll中提供的PacketGetAdapterNames 函数,通过给第一个参数pStr传递NULL值,使第二个参数 BufferSize返回存储适配器列表所需的字节数。按该字节数分配空间AdaptersName以存储适配器列表。把第一个参数设为AdaptersName所指的内存空间,第二次调用PacketGetAdapterNames
函数,以获得适配器列表。
对所获的适配器列表进行解析,获得每个适配器的名称与描述,并调用pcap_add_if_win32函数把每个适配器的信息添加到适配器设备列表devlist中。为防止通过“标准”方式没有找到的设备被遗漏,调用依赖平台的函数 pcap_platform_finddevs查找余下的设备信息,并添加到适配器设备列表devlist中。
添加信息结束后,把devlist存放到alldevsp参数所指内存中,释放存储适配器列表的AdaptersName所指的内存空间,函数返回。

1.3.1.1 pcap_add_if_win32函数

函数pcap_add_if_win32()为一个适配器在devlist链表中添加一个节点。
函数原型如下:

static int pcap_add_if_win32(pcap_if_t **devlist,

char *name, const char *desc,char *errbuf)

参数devlist返回存储所有适配器详细信息的链表。参数name为适配器的名称,参数desc为该适配器的描述。参数errbuf返回错误信息。
函数成功返回0值,否则返回非0值。
函数的主要代码如下:

static int pcap_add_if_win32(pcap_if_t **devlist, char *name, const char *desc,
char *errbuf)
{
pcap_if_t *curdev;
npf_if_addr if_addrs[MAX_NETWORK_ADDRESSES];
LONG if_addr_size;
int res = 0;

if_addr_size = MAX_NETWORK_ADDRESSES;

/*给devlist添加该接口节点,不带网络地址信息,curdev 返回该接口的节点*/
if (add_or_find_if(&curdev, devlist, name, 0, desc, errbuf) == -1) {
//添加失败,函数返回
return (-1);
}

//调用packet.dll提供的函数,返回一个适配器的所有网络地址信息
if (!PacketGetNetInfoEx((void *)name, if_addrs, &if_addr_size)) {
//失败,此处并不返回失败,而是返回一个空的地址链表。
//对于NdisWan接口,这种情况可能发生,同时希望提供这些接口,
//即使不能提供接口的网络地址。
return (0);
}

/*现在向节点中添加网络地址信息链表*/
//"curdev" 是表示该接口的节点,对该节点添加它的网络地址链表
while (if_addr_size-- > 0) {
if(curdev == NULL)
break;
//把接口一个网络地址的各种地址信息添加到curdev节点中
res = add_addr_to_list(curdev,
(struct sockaddr *)&if_addrs[if_addr_size].IPAddress,
(struct sockaddr *)&if_addrs[if_addr_size].SubnetMask,
(struct sockaddr *)&if_addrs[if_addr_size].Broadcast,
NULL,
errbuf);
if (res == -1) {
//失败
break;
}
}

return (res);

}

函数首先调用add_or_find_if()函数给所有适配器详细信息的链表devlist添加该接口节点,但不带网络地址信息,curdev局部变量指向该节点。然后调用packet.dll提供的PacketGetNetInfoEx()函数,返回该节点的存储所有网络地址信息的链表。最后循环调用add_addr_to_list函数,向该节点添加网络地址信息链表的每个节点。
函数add_addr_to_list()给一个接口的pcap_if_t类型的节点添加一个网络地址信息,函数的原形如下:

static int add_addr_to_list(pcap_if_t *curdev,

struct sockaddr *addr,struct sockaddr *netmask,
struct sockaddr *broadaddr,struct sockaddr *dstaddr,

char *errbuf)

参数curdev表示描述一个适配器详细信息链表的节点,参数addr, netmask, broadaddr, dstaddr分别为该适配器一个接口的IP地址,网络掩码,广播地址,P2P目的地址。参数errbuf返回该函数的错误信息。
函数成功返回0,失败返回-1。

1.3.1.2 add_or_find_if函数

函数add_or_find_if在alldevs设备链表中中查找或添加一个节点,并返回该节点的信息。函数原型如下:

int add_or_find_if(pcap_if_t **curdev_ret, pcap_if_t **alldevs, const char *name,

u_int flags, const char *description, char *errbuf)

参数alldevs为描述所有适配器的链表,参数curdev_ret为所找到或添加的节点。参数name是接口的名字,参数flag表示接口的标识,比如是否支持回环功能。参数description是接口的描述。参数errbuf返回函数的错误信息。
函数成功返回0值,否则返回非0值。
函数的主要代码如下:

int add_or_find_if(pcap_if_t **curdev_ret, pcap_if_t **alldevs, const char *name,

u_int flags, const char *description, char *errbuf)
{
pcap_t *p;
pcap_if_t *curdev, *prevdev, *nextdev;
int this_instance;

/*在设备列表中存在该接口吗?*/
for (curdev = *alldevs; curdev != NULL; curdev = curdev->next) {
if (strcmp(name, curdev->name) == 0)
break; /* 找到了该接口,跳出for循环 */
}

if (curdev == NULL) {
/*没有找到该接口
*我们能打开该接口实现在线捕获吗?
*/

p = pcap_open_live(name, 68, 0, 0, errbuf);
if (p == NULL) {
/*
* No. Don't bother including it.
* Don't treat this as an error, though.
*/
*curdev_ret = NULL;
return (0);
}
pcap_close(p);

/*
*可以成功的打开该适配器,为它分配一个新的节点
*/
curdev = malloc(sizeof(pcap_if_t));
if (curdev == NULL) {
//分配失败,返回
return (-1);
}

/*
* 填充该节点,除了网络地址信息
*/
curdev->next = NULL;
//设置适配器的名字
curdev->name = strdup(name);
…//处理错误的代码

//设置适配器的描述
if (description != NULL) {
//有接口的描述
curdev->description = strdup(description);
…//处理错误的代码
} else {
//没有接口的描述
curdev->description = NULL;
}

//设置适配器的网络地址列表,设置为NULL
curdev->addresses = NULL;

//设置PCAP_IF_接口标志
curdev->flags = 0;
if (ISLOOPBACK(name, flags))
curdev->flags |= PCAP_IF_LOOPBACK;

/*把节点添加到合适的位置*/

//首先,获取接口的实例数
this_instance = get_instance(name);

/*现在寻找实例号小于等于该新接口的实例号的最后一个接口
*除了非回环接口,因此回环接口放置在链表的尾部。
*/
prevdev = NULL;
for (;;) {
/*
* 获得下一个接口。
*/
if (prevdev == NULL) {
/*下一个元素是第一个元素*/
nextdev = *alldevs;
} else
nextdev = prevdev->next;

/*我们在链表的尾部吗?*/
if (nextdev == NULL) {
/*是的,在"prevdev"之后添加一个新的条目*/
break;
}

/*
*新的接口是一个非回环接口?下一个接口是一个回环接口吗?
*/
if (!(curdev->flags & PCAP_IF_LOOPBACK) &&
(nextdev->flags & PCAP_IF_LOOPBACK)) {
/*
*是的,
*我们应该把新的接口放置在"nextdev"之前,也就是在"prevdev"之后
*/
break;
}

/*新接口的实例数小于下一个接口的实例数,
*并且新接口不是回环接口或下一个接口是一个回环接口?
*
*(这两个回环检测的目的是确保我们不会把一个回环接口放置在任何一个
*不是回环的接口之前,并且我们总是把一个非回环接口放置在所有回环接口之前)
*/
if (this_instance < get_instance(nextdev->name) &&
(!(curdev->flags & PCAP_IF_LOOPBACK) ||
(nextdev->flags & PCAP_IF_LOOPBACK))) {
/*
* 是的,我们应该新的条目放置在"nextdev"之前,
*也就是"prevdev"之后
*/
break;
}

prevdev = nextdev;
}

/* 插入到"nextdev"之前*/
curdev->next = nextdev;

/*
*插入到"prevdev"之后 ——除非"prevdev"为null,
* 这种情况下,这是第一个接口。
*/
if (prevdev == NULL) {
/*
*传递回一个指向它的指针,并把"curdev"放置在"nextdev"之前。
*/
*alldevs = curdev;
} else
prevdev->next = curdev;
}

*curdev_ret = curdev;
return (0);

}

函数调用get_instance()函数获得接口的实例号,get_instance()函数的原型如下。

static int get_instance(const char *name)

函数从参数name中分析接口的实例号,如果name为"any"就返回INT_MAX最大的整数值。

1.3.2 pcap_findalldevs_ex函数

暂无
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐