1.5 packet.dll对应的函数接口
下面对各函数在
packet.dll
中的实现进行详细分析。
1.5.1 关键结构体
结构体_ADAPTER描述了一个打开的适配器,该结构体对packet.dll的函数非常重要,不过大部分成员对用户而言都可不用关心,因为该库原本就是为了避免用户处理底层的参数而设计的。该结构体的定义如下:
typedef
struct _ADAPTER {
HANDLE hFile; //
指向一个NPF驱动程序实例的句柄
CHAR SymbolicLink[MAX_LINK_NAME_LENGTH]; //
保存当前打开网络适配器的名称
int NumWrites; //
对每个数据包重复发送的次数
HANDLE ReadEvent; //
与适配器上读调用关联的通知事件
UINT ReadTimeOut; //
读超时时间
CHAR Name[ADAPTER_NAME_LENGTH];
PWAN_ADAPTER pWanAdapter;
UINT Flags; //
适配器标识
结构体_PACKET包含了从驱动程序来的一组数据包。该结构体定义了与每个递送给应用程序的数据包相关联的包头。结构体的定义如下:
PVOID Buffer; //
存储数据包的缓冲区。参见PacketReceivePacket()了解缓冲区中的数据组织形式。
UINT Length; //
缓冲区的长度
DWORD ulBytesReceived; //
缓冲区中的有效字节数,如最后调用PacketReceivePacket所接收的数据量
结构体_PACKET_OID_DATA包含一个OID请求。它被PacketRequest函数使用来发送一个OID请求给接口卡驱动程序。它也能被用来获取诸如适配器的错误计数、MAC地址、多播列表,等信息。结构体的定义如下:
struct
_PACKET_OID_DATA {
ULONG Oid; //OID
码。 参见Microsoft DDK文档或文件 ntddndis.h获得一个有效编码的完整列表。
ULONG Length; //Data
成员的长度。
UCHAR Data[1]; //
变长成员,包含传递到适配器或从适配器接收的信息
};
typedef
struct _PACKET_OID_DATA PACKET_OID_DATA, *PPACKET_OID_DATA;
1.5.2 PacketOpenAdapter函数
在库wpcap.dll中pcap_activate_win32()函数调用
PacketOpenAdapter()
函数打开适配器。函数PacketOpenAdapter()的作用就是打开一个适配器。函数原型如下:
LPADAPTER PacketOpenAdapter(PCHAR AdapterNameWA)
;
参数AdapterNameWA字符串包含待打开设备的名称。函数调用PacketOpenAdapterNPF()函数获取可用的设备列表。
函数如果成功,返回一个已经正确初始化的ADAPTER对象的指针。否则返回NULL。
函数的主要代码如下:
LPADAPTER PacketOpenAdapter(PCHAR AdapterNameWA)
{
…
/*
检查适配器的名称是否为ASCII码字符串,如果不是,执行转换*/
if(AdapterNameWA[1]!=0)
{ //
是ASCII码字符串
bFreeAdapterNameA = FALSE;
AdapterNameA = AdapterNameWA;
}
else
{ //
是Unicode码字符串,转换为ASCII码字符串
size_t bufferSize = wcslen((PWCHAR)AdapterNameWA) + 1;
AdapterNameA = GlobalAllocPtr(GPTR, bufferSize);
if (AdapterNameA == NULL)
{//
分配错误,函数返回
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
StringCchPrintfA(AdapterNameA, bufferSize, "%ws",
(PWCHAR)AdapterNameWA);
bFreeAdapterNameA = TRUE;
}
/*
获得g_AdaptersInfoMutex互斥信号*/
WaitForSingleObject(g_AdaptersInfoMutex, INFINITE);
/*
查找适配器,并打开它*/
do
{
//
第一次查找给定适配器的PADAPTER_INFO结构体
TAdInfo = PacketFindAdInfo(AdapterNameA);
if(TAdInfo == NULL)
{//
没有找到,更新链表,然后执行第二次查找
PacketUpdateAdInfo(AdapterNameA);
TAdInfo = PacketFindAdInfo(AdapterNameA);
}
if(TAdInfo == NULL)
{//
出错处理,跳出do…while循环
…
break;
}
//
找到适配器,检查适配器的类型,查看它是否被正确的支持,
//
此处我们只考虑正常的NPF适配器
#ifdef HAVE_WANPACKET_API
if(TAdInfo->Flags == INFO_FLAG_NDISWAN_ADAPTER)
{
…
break;
}
#endif //HAVE_WANPACKET_API
…
if(TAdInfo->Flags == INFO_FLAG_DONT_EXPORT)
{
//
该适配器不允许被导出,跳出循环
…
break
;
}
if (TAdInfo->Flags != INFO_FLAG_NDIS_ADAPTER)
{//
打开不明标识的适配器,跳出循环
…
break;
}
//
正常的NPF适配器,试图打开它
lpAdapter = PacketOpenAdapterNPF(AdapterNameA);
if (lpAdapter == NULL)
{//
打开失败
dwLastError = GetLastError();
}
}while(FALSE);
/*
释放g_AdaptersInfoMutex互斥信号*/
ReleaseMutex(g_AdaptersInfoMutex);
/*
如果执行了Unicode转换,释放所用的内存*/
if (bFreeAdapterNameA)
GlobalFree(AdapterNameA);
/*
函数返回*/
if (dwLastError != ERROR_SUCCESS)
{//
函数失败,返回NULL
SetLastError(dwLastError);
return NULL;
}
else
{//
函数成功,返回所打开的适配器关联的结构体指针
return lpAdapter;
}
函数首先检查适配器的名称是否为
ASCII
码字符串,如果不是,执行
Unicode
码到
ASCII
码的转换。
然后获得
g_AdaptersInfoMutex
互斥信号,调用
PacketFindAdInfo()
函数
开始在
g_AdaptersInfoList
链表中
查找适配器。如果第一次没有查到,调用
PacketUpdateAdInfo()
函数更新该链表,执行第二次查找。
如果找到适配器,检查适配器的类型,查看它是否被正确的支持,我们只考虑正常的
NPF
适配器。
如果是正常的
NPF
适配器,调用
PacketOpenAdapterNPF()
函数
打开它。
然后释放
g_AdaptersInfoMutex
互斥信号,如果执行了
Unicode
转换,释放所用的内存。
最后,如果函数成功,则返回与打开适配器关联的结构体指针
lpAdapter
,否则返回NULL。