Winpcap不同版本之间的一个小差异导致的错误。

原来机器中的Winpcap的版本是3.0,程序总是运行不下去,分析之后得知原来是自己的Winpcap版本出了错,其中的一个函数pcap_findalldevs_ex(),在3.0的版本和3.2的版本中的参数的个数不一样,导致程序出错。事例代码如下:
#include <pcap.h>
#include <remote-ext.h>

/* 4 bytes IP address */
typedef struct ip_address
{
 u_char byte1;
 u_char byte2;
 u_char byte3;
 u_char byte4;
}ip_address;

/* IPv4 header */
typedef struct ip_header
{
 u_char ver_ihl;  /* Version (4 bits)版本 + Internet header length 首部长度(4 bits)*/
 u_char tos;      /* Type of service 服务类型*/
 u_short tlen;    /* Total length 16位标示*/
 u_short identification; /* Identification */
 u_short flags_fo;       /* Flags (3 bits)三位标志 + Fragment offset十三位偏移 (13 bits)*/
 u_char ttl;      /* Time to live八位生存时间 */
 u_char proto;    /* Protocol八位协议 */
 u_short crc;     /* Header checksum 十六位首部校验和*/
 ip_address saddr;/* Source address 32位源IP地址*/
 ip_address daddr;/* Destination address 32位目的IP地址*/
 u_int op_pad;    /* Option + Padding选项和数据 */
}ip_header;

/* UDP header */
typedef struct udp_header
{
 u_short sport;   /* Source port源端口 */
 u_short dport;   /* Destination port 目的端口*/
 u_short len;     /* Datagram length 数据长度*/
 u_short crc;     /* Checksum 校验和*/
}udp_header;

//连接表元素的结构
typedef    struct  link_elem   
{
        ip_address saddr;  //IP地址Source ip address
        ip_address daddr;  //目标IP地址
        u_short    sport;  // Source port
        u_short    dport;  // Destinaion port
        char      *mailsaddr; //源Email地址
        char      *maildaddr; //目的Email地址
        bool       ifdata; //是否Data
        time_t     rtime; //到达时间
}link_elem;
   

    //邮件正文段
typedef    struct data_content
    {
        char *lcontent;  //正文段
        data_content *next_file; //下一个邮件正文段
    }data_content;

/* Prototype of the packet handler */
void packet_handler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data);

 int main() {
 pcap_if_t* alldevs;
 pcap_if_t* d;
 int inum;
 int i = 0;
 pcap_t* adhandle;
 char errbuf[PCAP_ERRBUF_SIZE];
 u_int netmask;
 char packet_filter[] = "ip and udp";
 struct bpf_program fcode;???????????????????????//

 /* Retrieve the device list */
 if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
 {
    /*
    在UNIX操作系统结构中,它把各种外部设备也看成是文件。
    所以 fprintf不仅能把errbuf的内容输出到磁盘文件里,
    也可以输出到像终端这样的特殊文件里。printf是fprintf的一个特例,
    它固定的只能把相应内容输出到终端文件上。
    */
  fprintf(stderr, "Error in pcap_findalldevs: %s/n", errbuf);
  exit(1);
 }

 /* Print the list*/
 for (d = alldevs; d; d = d->next)
 {
  printf("%d. %s", ++ i, d->name);
  if (d->description)
  {
   printf(" (%s)/n", d->description);
  }
  else
  {
   printf(" (No description available)/n");
  }
 }

 if (i == 0)
 {
  printf("/nNo interfaces found! Make sure Winpcap is installed./n");
  return -1;
 }

 printf("Enter the interface number (1 - %d):", i);
 scanf("%d", &inum);

 if (inum < 1 || inum > i)
 {
  printf("/nInterface number out of range./n");
  /* Free the device list */
  pcap_freealldevs(alldevs);
  return -1;
 }

 /* Jump to the selected adapter */
 for (d = alldevs, i = 0; i < inum - 1; d = d->next, ++ i);

 /* Open the device */
 if ((adhandle = pcap_open(d->name, /* 设备名 */
         65536, /* 所截包的大小 */
         /* 65536 guarantees that the whole packet will be captured on all the link layers */
         PCAP_OPENFLAG_PROMISCUOUS, /* 混杂模式 */
         1000,  /* read timeout */
         NULL,  /* authentication on the remote machine远程机器的权限 */
         errbuf /* error buffer */
 )) == NULL)
 {

  fprintf(stderr, "/nUnable to open the adapter. %s is not supported by Winpcap/n");
  /* Free the devices list */
  pcap_freealldevs(alldevs);
  return -1;
 }

 /* Check the link layer. We support only Ethernet for simplicity
 
 int pcap_datalink(pcap_t* p)

返回链路层上的一个适配器。返回链路层的类型,链路层的类型包括:

l         DLT_NULL: BSD回路封装;链路层协议头是一个4字节的域,
          以主机字节顺序(host byte order),包含一个从socket.h来的PF_value。
          主机字节顺序(host byte order)是捕获数据包的机器的字节顺序,
          而PF_value是捕获数据包的机器的OS。如果一个读取一个文件,
          字节顺序和PF_value不一定是抓取文件的那些机器。

l         DLT_EN10MB: 以太网(10Mb, 100Mb, 1000Mb, 或者更高)。

l         DLT_IEEE802: IEEE802.5令牌环网。

l         DLT_ARCNET:ARCNET。

l         DLT_SLIP:SLIP。

l         DLT_PPP:PPP;如果第一个字节是0xff或0x03,它是类HDLC帧上的PPP。

l         DLT_FDDI:FDDI

l         DLT_ATM_RFC1483:RFC1483LLC/SNAP ATM;数据包以IEEE802.2 LLC头开始。

l         DLT_RAW:原始IP(raw IP);数据包以IP头开始。

l         DLT_PPP_SERIAL:按照RFC1662,基于类HDLC帧的PPP,或者按照RFC1547的4.3.1,
          基于HDLC帧的Cisco PPP;前者的第一个字节是0xFF,后者的第一个字节是0x0F或0x8F。

l         DLT_PPP_ETHER:按照RFC2516,PPPoE;数据包以PPPoE头开始。

l         DLT_C_HDLC:按照RFC1547的4.3.1,基于HDLC帧的Cisco PPP。

l         DLT_IEEE802_11:IEEE 802.11无线局域网。

l         DLT_FRELAY:帧中继(Frame Relay)。

l         DLT_LOOP:OpenBSD回路封装。

l         DLT_LINUX_SLL:Linux抓包封装。

l         DLT_LTALK:苹果的LocalTalk,数据包以AppleTalk LLAP头开始。

l         DLT_PFLOG:OpenBSD pflog。

l         DLT_PRISM_HEADER:后接802.11头的棱镜监视器模式(Prism monitor mode)信息。

l         DLT_IP_OVER_FC:RFC2625 IP-over-Fiber 频道,以RFC2625中定义的Network_Header开始。

l         DLT_SUNATM:SunATM设备。

l         DLT_IEEE802_11_RADIO:后接802.11头的链路层信息。

l         DLT_ARCNET_LINUX:没有异常帧的ARCNET。

l         DLT_LINUX_IRDA:Linux-IrDA数据包,DLT_LINUX_SLL头后接IrLAP头。
 
*/

 if (pcap_datalink(adhandle) != DLT_EN10MB)
 {
  fprintf(stderr, "/nThis program works only on Ethernet networks./n");
  /* Free the devices list */
  pcap_freealldevs(alldevs);
  return -1;
 }


/******************************************************
就是将pcap_addr结构体中的netmask字段赋值给netmask变量。
if判断表示如果网卡d的addresses不为NULL,
那么就将其中的netmask转化成相应格式的数据并赋值给netmask;
否则,就将netmask变量赋值为ffffffff。
********************************************************/
 if (d->addresses != NULL)
 {
  /* Retrieve the mask of the first address of the interface */
  netmask = ((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;//????????????
 }
 else
 {
  /* If the interface is without addresses we suppose to be in a C class network */
  netmask = 0xffffffff;
 }

 /* complie the filter */
 if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) < 0)
 {
  fprintf(stderr, "/nUnable to compile the packet filter. Check the syntax./n");
  /* Free the devices list */
  pcap_freealldevs(alldevs);
  return -1;
 }


 /* set the filter */
 if (pcap_setfilter(adhandle, &fcode) < 0)
 {
  fprintf(stderr, "/nError setting the filter./n");
  /* Free the devices list */
  pcap_freealldevs(alldevs);
  return -1;
 }

 printf("/nlistening on %s .../n", d->description);

 /* At this point,we don't need any more the device list. Free it */
 pcap_freealldevs(alldevs);

 /* Start the capture */
 pcap_loop(adhandle, 0, packet_handler, NULL);

 return 1;
}

/* Callback function invoked by libpcap for every incoming packet */
void packet_handler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data){
 struct tm* ltime;
 char timestr[16];
 ip_header* ih;
 udp_header* uh;
 u_int ip_len;
 u_short sport, dport;

 /* convert the timestamp to readable format */
 ltime = localtime(&header->ts.tv_sec);
 strftime(timestr, sizeof(timestr), "%H:%M:%S", ltime);

 /* print timestamp and length of the packet */
 printf("%s.%.6d len: %d ", timestr, header->ts.tv_usec, header->len);

 /* retrieve the position of the ip header */
 ih = (ip_header*)(pkt_data + 14);  /* length of ethernet header MAC头是14字节*/

 /* retrieve the position of the udp header */

     /***********************************************
     ih->ver_ihl & 0xf就是ih->ver_ihl与00001111按位与,
     这样就能取得ih->ver_ihl的后四位的值,
     也就是ip报头中的“报头长度”字段,
     再将该按位与的结果乘以4(该字段以4个字节为基本单位),
     就得到ip报头有多少个字节。
     ************************************************/

 ip_len = (ih->ver_ihl & 0xf) * 4
/*IP包头的长度域来获得Ip包头的大小*/;
 uh = (udp_header*)((u_char*)ih + ip_len);

 /* convert from network byte order to host byte order */
 /*sport = ntohs(uh->sport);
 dport = ntohs(uh->dport);*/

 /* print ip addresses and udp ports */
 printf("%d.%d.%d.%d -> %d.%d.%d.%d/n",
  ih->saddr.byte1,
  ih->saddr.byte2,
  ih->saddr.byte3,
  ih->saddr.byte4,
  /*sport,*/
  ih->daddr.byte1,
  ih->daddr.byte2,
  ih->daddr.byte3,
  ih->daddr.byte4
  /*dport*/);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值