(一)循序渐进学习使用WINPCAP这一部分展示了如何使用WINPCAP-API的不同的功能,它作为一个使用指南被划分为一系列的课时来带领读者循序渐进的体会PCAP的程序设计的魅力:从简单的基本功能(如获取网卡的列表,数据包的捕获等)到统计和收集网络流量等高级功能。在这里将提供一些简单但完整的代码作为参考:所有的这些原代码都有和它相关的详细信息的连接以便单击这些功能和数据结构时能够即使跳转到相关的文献。这些例子是用C语言写的,所以在学习之前首先要有一定的C语言的基础,当然PCAP作为一个网络底层的驱动,要想学好它也必须具备一定的网络方面的知识。(一)得到网络驱动列表用PCAP写应用程序的第一件事往往就是要获得本地的网卡列表。PCAP提供了pcap_findalldevs()这个函数来实现此功能,这个API返回一个pcap_if结构的链表,表的每项内容含有全面的网卡信息:字段名字、含有名字的描述、有关驱动器的易读信息。得到网络驱动列表的程序如下:#includepcap.hmain(){pcap_if_t*alldevs;pcap_if_t*d;inti=0;charerrbuf[PCAP_ERRBUF_SIZE];/*这个API用来获得网卡的列表*/if(pcap_findalldevs(&alldevs,errbuf)==-1){fprintf(stderr,Errorinpcap_findalldevs:%s\n,errbuf);exit(1);}/*显示列表的响应字段的内容*/for(d=alldevs;d;d=d-next){printf(%d.%s,++i,d-name);if(d-description)printf((%s)\n,d-description);elseprintf((Nodescriptionavailable)\n);}if(i==0){printf(\nNointerfacesfound!MakesureWinPcapisinstalled.\n);return;}/*Wedon'tneedanymorethedevicelist.Freeit*/pcap_freealldevs(alldevs);}有关这段程序的一些说明:首先pcap_findalldevs()同其他的libpcap函数一样有一个errbuf参数,当有异常情况发生时,这个参数会被PCAP填充为某个特定的错误字串。再次,UNIX也同样提供pcap_findalldevs()这个函数,但是请注意并非所有的系统都支持libpcap提供的网络程序接口。所以我门要想写出合适的程序就必须考虑到这些情况(系统不能够返回一些字段的描述信息),在这种情况下我门应该给出类似Nodescriptionavailable这样的提示。最后结束时别忘了用pcap_freealldevs()释放掉内存资源。1.{4E273621-5161-46C8-895A-48D0E52A0B83}(RealtekRTL8029(AS)EthernetAdapter)2.{5D24AE04-C486-4A96-83FB-8B5EC6C7F430}(3ComEtherLinkPCI)(二)循序渐进学习使用WINPCAP:获得已安装网络驱动器的高级信息在第一章中演示了如何获得已存在适配器的静态信息。实际上WinPcap同样也提供其他的高级信息,特别是pcap_findalldevs()这个函数返回的每个pcap_if结构体都同样包含一个pcap_addr结构的列表,它包含:一个地址列表,一个掩码列表,一个广播地址列表和一个目的地址列表。下面的例子通过一个ifprint()函数打印出了pcap_if结构的的所有字段信息,该程序对每一个pcap_findalldevs()所返回的pcap_if结构循环调用ifprint()来显示详细的字段信息。#includepcap.h#ifndefWIN32#includesys/socket.h#includenetinet/in.h#else#includewinsock.h#endifvoidifprint(pcap_if_t*d);char*iptos(u_longin);intmain(){pcap_if_t*alldevs;pcap_if_t*d;charerrbuf[PCAP_ERRBUF_SIZE+1];/*获得网卡的列表*/if(pcap_findalldevs(&alldevs,errbuf)==-1){fprintf(stderr,Errorinpcap_findalldevs:%s\n,errbuf);exit(1);}/*循环调用ifprint()来显示pcap_if结构的信息*/for(d=alldevs;d;d=d-next){ifprint(d);}return1;}/*Printalltheavailableinformationonthegiveninterface*/voidifprint(pcap_if_t*d){pcap_addr_t*a;/*Name*/printf(%s\n,d-name);/*Description*/if(d-description)printf(\tDescription:%s\n,d-description);/*LoopbackAddress*/printf(\tLoopback:%s\n,(d-flags&PCAP_IF_LOOPBACK)?yes:no);/*IPaddresses*/for(a=d-addresses;a;a=a-next){printf(\tAddressFamily:#%d\n,a-addr-sa_family);/*关于sockaddr_in结构请参考其他的网络编程书*/switch(a-addr-sa_family){caseAF_INET:printf(\tAddressFamilyName:AF_INET\n);//打印网络地址类型if(a-addr)//打印IP地址printf(\tAddress:%s\n,iptos(((structsockaddr_in*)a-addr)-sin_addr.s_addr));if(a-netmask)//打印掩码printf(\tNetmask:%s\n,iptos(((structsockaddr_in*)a-netmask)-sin_addr.s_addr));if(a-broadaddr)//打印广播地址printf(\tBroadcastAddress:%s\n,iptos(((structsockaddr_in*)a-broadaddr)-sin_addr.s_addr));if(a-dstaddr)//目的地址printf(\tDestinationAddress:%s\n,iptos(((structsockaddr_in*)a-dstaddr)-sin_addr.s_addr));break;default:printf(\tAddressFamilyName:Unknown\n);break;}}printf(\n);}/*将一个unsignedlong型的IP转换为字符串类型的IP*/#defineIPTOSBUFFERS12char*iptos(u_longin){staticcharoutput[IPTOSBUFFERS][3*4+3+1];staticshortwhich;u_char*p;p=(u_char*)∈which=(which+1==IPTOSBUFFERS?0:which+1);sprintf(output[which],%d.%d.%d.%d,p[0],p[1],p[2],p[3]);returnoutput[which];}循序渐进学习使用WINPCAP(三)----打开网卡捕获数据包现在已经知道了如何获得网卡的信息,现在开始真正的工作:打开网卡并捕获数据流。在这里将写一个打印流经网络的每个数据包信息的程序。打开网卡的功能是通过pcap_open_live()来实现的它有三个参数snaplen,promisc,to_ms。snaplen用于指定所捕获包的特定部分,在一些系统上(象xBSDandWin32等)驱动只给出所捕获数据包的一部分而不是全部,这样就减少了拷贝数据的数量从而提高了包捕获的效率。promisc指明网卡处于混杂模式,在正常情况下网卡只接受发往它的包,而发往其他主机的数据包则被忽略。相反当网卡处于混杂模式时,它将接收所有的流经它的数据包。这就意味着在共享介质的情况下可以捕获到发往其它主机的数据包。大部分的包捕获程序都将混杂模式设为默认,在下面的例子里也将网卡设为混杂模式。to_ms参数指定读数据的超时控制,超时以毫秒计算。当在超时时间内网卡上没有数据到来时,对网卡的读操作将返回(如pcap_dispatch()orpcap_next_ex()等函数)。还有,如果网卡处于统计模式下(请查看“统计和收集网络数据流一节”)to_ms还定义了统计的时间间隔。如果该参数为0那么意味着没有超时控制,对网卡的读操作在没有数据到来是将永远堵塞。如果为-1那么对网卡的读操作将立即返回,不管有没有数据可读。#includepcap.h/*prototypeofthepackethandler*/voidpacket_handler(u_char*param,conststructpcap_pkthdr*header,constu_char*pkt_data);main(){pcap_if_t*alldevs;pcap_if_t*d;intinum;inti=0;pcap_t*adhandle;charerrbuf[PCAP_ERRBUF_SIZE];/*获得网卡的列表*/if(pcap_findalldevs(&alldevs,errbuf)==-1){fprintf(stderr,Errorinpcap_findalldevs:%s\n,errbuf);exit(1);}/*打印网卡信息*/for(d=alldevs;d;d=d-next){printf(%d.%s,++i,d-name);if(d-description)printf((%s)\n,d-description);elseprintf((Nodescriptionavailable)\n);}if(i==0){printf(\nNointerfacesfound!MakesureWinPcapisinstalled.\n);return-1;}printf(Entertheinterfacenumber(1-%d):,i);scanf(%d,&inum);//输入要选择打开的网卡号if(inum1||inumi)//判断号的合法性{printf(\nInterfacenumberoutofrange.\n);/*Freethedevicelist*/pcap_freealldevs(alldevs);return-1;}/*找到要选择的网卡结构*/for(d=alldevs,i=0;iinum-1;d=d-next,i++);/*打开选择的网卡*/if((adhandle=pcap_open_live(d-name,//设备名称65536,//portionofthepackettocapture.//65536grantsthatthewholepacketwillbecapturedonalltheMACs.1,//混杂模式1000,//读超时为1秒errbuf//errorbuffer))==NULL){fprintf(stderr,\nUnabletoopentheadapter.%sisnotsupportedbyWinPcap\n);/*Freethedevicelist