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

来源:互联网 发布:信封制作软件 编辑:程序博客网 时间:2024/05/20 07:34

本文转自http://eslxf.blog.51cto.com/918801/198591

 

1.4.2.3     PacketAddAdapterNPF函数

函数PacketAddAdapterNPF()向适配器链表g_AdaptersInfoList中添加一个节点。参数AdName是要添加的链表节点的适配器名称。如果函数成功则返回非0值。
其主要代码如下所示:
static BOOLEAN PacketAddAdapterNPF(PCHAR AdName, UINT flags)
{
    LONG        Status;
    LPADAPTER   adapter = NULL;
    PPACKET_OID_DATA  OidData = NULL;
    PADAPTER_INFO   TmpAdInfo;
    PADAPTER_INFO TAdInfo; 
   
    /*
    *检查ADAPTER_INFO::Name成员是否有足够的空间存储适配器名称
*如果没有,函数返回失败
*/
    if (strlen(AdName) + 1 > sizeof(TmpAdInfo->Name))
    {
        return FALSE;
    }
 
/*获得g_AdaptersInfoMutex互斥信号*/
    WaitForSingleObject(g_AdaptersInfoMutex, INFINITE);
   
/*查看适配器的名称是否已在适配器链表中,如是,则函数返回*/
    for(TAdInfo = g_AdaptersInfoList; TAdInfo != NULL;
TAdInfo = TAdInfo->Next)
    {
        if(strcmp(AdName, TAdInfo->Name) == 0)
        {// AdName已存在链表中,函数返回
            ReleaseMutex(g_AdaptersInfoMutex);
            return TRUE;
        }
    }
   
    //此处不能释放g_AdaptersInfoMutex互斥信号
//如此时有两个线程试图添加同一个适配器,链表中将存在一个适配器的副本
   
    if(flags != INFO_FLAG_DONT_EXPORT)
    {   //试图打开NPF适配器,查看它是否可用
       
        //试图打开适配器
        adapter = PacketOpenAdapterNPF(AdName);
       
        if(adapter != NULL)
        {
            //分配一个缓冲区,从驱动程序获得厂商的描述  
            OidData = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_ZEROINIT,512);
            if (OidData == NULL)
            {   //分配失败     
                PacketCloseAdapter(adapter);
                ReleaseMutex(g_AdaptersInfoMutex);
                TRACE_EXIT("PacketAddAdapterNPF");
                return FALSE;
            }
        }
        else
        {
//NPF适配器不可用,不添加到链表中        
            ReleaseMutex(g_AdaptersInfoMutex);
            TRACE_EXIT("AddAdapter");
            return FALSE;
        }          
    }
 
    /*
*获得该适配器的PADAPTER_INFO结构体中各成员的值
*/
 
    // PacketOpenAdapter成功,认为这是一个可用的适配器,
//为它在适配器链表中分配一个条目。 
    TmpAdInfo = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(ADAPTER_INFO));
    if (TmpAdInfo == NULL)
    {//内存分配失败,函数返回
        
    }
   
    // 复制适配器名称AdName到TmpAdInfo->Name中
    strncpy(TmpAdInfo->Name, AdName,
sizeof(TmpAdInfo->Name)/ sizeof(TmpAdInfo->Name[0]) - 1);
    //无需给TmpAdInfo->Name添加结束符,
//既然在尾部预留了一个字节,同时分配TmpAdInfo内存时已设为0。
 
    if(flags != INFO_FLAG_DONT_EXPORT)
    {
        PNPF_IF_ADDRESS_ITEM pAddressesFromRegistry;
 
        //对NIC驱动的查询,获得适配器的描述
OidData->Oid = OID_GEN_VENDOR_DESCRIPTION;
        OidData->Length = 256;
        ZeroMemory(OidData->Data, 256);
       
        Status = PacketRequest(adapter, FALSE, OidData);
       
        if(Status==0 || ((char*)OidData->Data)[0]==0)
        {
//不能从NIC驱动程序获得适配器的描述信息        
        }
               
        //复制设备的描述
        strncpy(TmpAdInfo->Description, (PCHAR)OidData->Data,
 sizeof(TmpAdInfo->Description)/ sizeof(TmpAdInfo->Description[0]) - 1);
       //无需给TmpAdInfo->Name添加结束符,既然在尾部预留了一个
//字节,同时分配TmpAdInfo内存时已设为0。
 
//从注册表获得一个适配器的NetType结构体,
//该结构体包含适配器的链接类型与速度
        Status = PacketGetLinkLayerFromRegistry(adapter, &(TmpAdInfo->LinkLayer));
        if (Status == FALSE)
        {//失败,函数返回
            
        }
       
        //查询NIC驱动程序,获取适配器的MAC地址
// 现在只支持以太网,等待补丁程序支持其它的链路层.
        OidData->Oid = OID_802_3_CURRENT_ADDRESS;  
        OidData->Length = 256;
        ZeroMemory(OidData->Data, 256);
       
        Status = PacketRequest(adapter, FALSE, OidData);
        if(Status)
        {//成功获取MAC地址,地址长度设为6
            memcpy(TmpAdInfo->MacAddress, OidData->Data, 6);
            TmpAdInfo->MacAddressLen = 6;          
        }
        else
        {//获取MAC地址失败,设为00:00:00:00:00:00,长度设为0。 
            memset(TmpAdInfo->MacAddress, 0, 6);
            TmpAdInfo->MacAddressLen = 0;
        }      
       
       //获取网络地址项PNPF_IF_ADDRESS_ITEM
        TmpAdInfo->pNetworkAddresses = NULL;
        //从注册表中获取一个适配器的网络地址项
        if(!PacketGetAddressesFromRegistry(TmpAdInfo->Name,
            &pAddressesFromRegistry))
        {
        }
        else
        {
            PNPF_IF_ADDRESS_ITEM pCursor;
 
            //把pAddressesFromRegistry追加到链表
//TmpAdInfo->pNetworkAddresses的尾部
            if (TmpAdInfo->pNetworkAddresses == NULL)
            {
                TmpAdInfo->pNetworkAddresses =
                    pAddressesFromRegistry;
            }
            else
            {
                pCursor = TmpAdInfo->pNetworkAddresses;
                while(pCursor->Next != NULL)
pCursor = pCursor->Next;
 
                pCursor->Next = pAddressesFromRegistry;
            }
        }
       
        TmpAdInfo->Flags = INFO_FLAG_NDIS_ADAPTER; 
// NdisWan适配器不会被NPF驱动程序导出,
//因此此处不可能见到它们
       
        //释放内存
        PacketCloseAdapter(adapter);
        GlobalFreePtr(OidData);
    }
    else
    {
        //是火线适配器,设置该标识,使后续调用将阻止它
        TmpAdInfo->Flags = INFO_FLAG_DONT_EXPORT;
    }
   
    /*更新g_AdaptersInfoList 链表*/
    TmpAdInfo->Next = g_AdaptersInfoList;
    g_AdaptersInfoList = TmpAdInfo;
 
/*释放g_AdaptersInfoMutex互斥信号*/
    ReleaseMutex(g_AdaptersInfoMutex);
 
    return TRUE;
}
函数PacketAddAdapterNPF()首先检查ADAPTER_INFO::Name成员是否有足够的空间存储适配器名称。然后获取g_AdaptersInfoMutex互斥信号。
查看要添加的适配器名称是否已在适配器链表中存在,如是,则函数返回。否则试图打开该NPF适配器,查看它是否可用。如果可用将分配一个缓冲区,用来从驱动程序获得厂商的描述信息。
获得该适配器的PADAPTER_INFO结构体中各成员的值。依次复制适配器名称,设备的描述,从注册表获得适配器的NetType结构体,查询NIC驱动程序,获取适配器的MAC地址,获取网络地址项PNPF_IF_ADDRESS_ITEM,并把它追加到链表TmpAdInfo->pNetworkAddresses的尾部。
    最后更新g_AdaptersInfoList 链表,释放g_AdaptersInfoMutex互斥信号,函数成功返回。
      
函数调用了PacketRequest()、PacketGetLinkLayerFromRegistry()与PacketGetAddressesFromRegistry()函数。各函数的原型与作用分别如下:
函数PacketRequest()在驱动程序上执行一个查询/设置操作。函数原型如下:
BOOLEAN PacketRequest(LPADAPTER  AdapterObject,
BOOLEAN Set,PPACKET_OID_DATA  OidData)
参数AdapterObject指向一个_ADAPTER结构体。参数Set决定是设置(Set=TRUE)还是查询(Set=FALSE)操作。参数OidData是一个指向一个_PACKET_OID_DATA结构体的指针,该结构体包含了所接收的数据。
如果函数成功返回非0值。
注意并不是所有的网络适配器实现了所有的查询/设置功能。
 
该函数主要依赖于DeviceIoControl系统函数实现,如下面的调用
Result=(BOOLEAN)DeviceIoControl(
AdapterObject->hFile,
(DWORD) Set ? (DWORD)BIOCSETOID : (DWORD)BIOCQUERYOID,                           OidData,
sizeof(PACKET_OID_DATA)-1+OidData->Length,OidData,                       sizeof(PACKET_OID_DATA)-1+OidData->Length,&BytesReturned,NULL
);
NPF中由NPF_IoControl函数中的如下代码处理该操作。
NTSTATUS NPF_IoControl(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
{
switch (FunctionCode){
case BIOCSETOID:
        case BIOCQUERYOID:
            
}
}
 
函数PacketGetLinkLayerFromRegistry()获得一个已打开适配器的NetType结构体,该结构体包含适配器的链路层的类型与速度(单位为bps)。函数原型如下:
static BOOLEAN PacketGetLinkLayerFromRegistry(
LPADAPTER AdapterObject, NetType *type
)
参数AdapterObject为一个已打开适配器的句柄,参数type是一个指向 NetType结构体的指针,它将被函数用链路层的类型与速度值填充。参数type的LinkType成员可以为下列的值之一:
ØNdisMedium802_3: Ethernet (802.3)
ØNdisMediumWan: WAN
ØNdisMedium802_5: Token Ring (802.5)
Ø NdisMediumFddi: FDDI
ØNdisMediumAtm: ATM
ØNdisMediumArcnet878_2: ARCNET (878.2)
 
如果函数成功,返回非0值,否则返回0值。
该函数主要依赖于PacketRequest()函数实现,主要代码如下所示:
static BOOLEAN PacketGetLinkLayerFromRegistry(
LPADAPTER AdapterObject, NetType *type)
{
    
    //获得链路层的类型
    OidData->Oid = OID_GEN_MEDIA_IN_USE;
    OidData->Length = sizeof (ULONG);
    Status = PacketRequest(AdapterObject,FALSE,OidData);
    type->LinkType=*((UINT*)OidData->Data);
 
    //获得链路层的速度
    OidData->Oid = OID_GEN_LINK_SPEED;
    OidData->Length = sizeof (ULONG);
    Status = PacketRequest(AdapterObject,FALSE,OidData);
    
}
 
函数PacketGetAddressesFromRegistry()从注册表中获取一个适配器的网络地址项。函数原型如下:
static BOOLEAN PacketGetAddressesFromRegistry(
LPCSTR AdapterNameA,
    PNPF_IF_ADDRESS_ITEM *ppItems
)
    参数 AdapterName是存储适配器名称的字符串。参数ppItems是调用者分配的指向一个地址项指针的指针。函数将把该指针设置为从注册表中获得的地址。
如果函数成功,返回非0值。
函数从注册表中获得一个接口的信息,如IP地址、网络掩码地址与广播地址。用户传递进来的缓冲区将用npf_if_addr结构体填充,每一个都包含一个单独地址的数据。如果缓冲区已满,余下的地址信息将丢弃,因此如果只需要第一个地址,把它的大小设为sizeof(npf_if_addr)即可。

原创粉丝点击