win2

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函數

暫無
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章