第七章Winsock服务提供者接口(SPI)13:30:071内容提要SPI概述Winsock协议目录分层服务提供者(LSP)基于SPI的数据包过滤实例13:30:072Winsock2是围绕着Windows开放系统架构(WindowsOpenSystemArchitecture,WOSA)来设计的,WOSA在Winsock和Winsock应用程序之间有一个标准API;在Winsock和Winsock服务提供者(比如TCP/IP)之间有一个标准的SPI。概述13:30:073图7-1Winsock2的WOSA架构13:30:074传输服务提供者传输服务提供者(TransportServiceproviders,一般称作协议栈,比如TCP/IP)即能够提供建立通信、传输数据、日常数据流控制和错误控制等功能的服务。类型:分层的(Layered),基础的(Base)基础服务提供者负责实现传输协议的真正细节,导出Winsock接口,此接口直接实现协议。分层服务提供者将自己安装到Winsock目录中的基础提供者上面,截取来自应用程序的WinsockAPI调用。13:30:075分层服务提供者仅实现更高层的定制通信函数,它依靠现存的底层基础提供者来与远程终端作实际的数据交换,分层服务提供者位于基础服务提供者之上,依靠它来实现各种功能。Winsock2APIWS2_32.DLLSPILayeredProtocolSPILayeredProtocolSPIBaseProtocolTransportNamespace图8-2协议层次13:30:076名字空间提供者把一个网络协议的地址属性和一个或多个用户友好名关联到一起,以便启用与协议无关的名字解析方案。命名空间提供者在命名空间目录安装自己,当应用程序执行名字解析时将会被调用。13:30:077SPI命名规则:前缀标示WSP(Winsock提供者):用于传输服务提供者函数。NSP(名字空间提供者):用于名字空间提供者函数。WPU(Winsock提供者上调):供服务提供者调用的Ws2_32.dll支持函数(分层服务提供者使用的支持函数。)WSC(Winsock配置):供在Winsock2中安装服务提供者的函数使用。13:30:078Winsock协议目录SPI提供3种协议:分层协议、基础协议、协议链基础协议是能够独立、安全地和远程端点实现数据通信的协议。分层协议在基础协议的上层,依靠底层基础协议实现更高层的通信服务。协议链是将一系列的基础协议和分层协议按特定的顺序连接在一起的链状结构。13:30:079协议信息系统安装了哪些协议以及这些协议的特性,通常为协议信息。如果一个协议支持多种行为,每类行为在系统中都有各自的目录条目。例如,如果系统中安装了TCP/IP,系统中就会有两个IP条目:TCP和UDP。Winsock采用WSAPROTOCOL_INFO结构描述特定协议的完整信息。具体定义如下:13:30:0710WSAPROTOCOL_INFO结构定义如下:typedefstructWSAPROTOCOL_INFO{DWORDdwServiceFlags1;DWORDdwServiceFlags2;DWORDdwServiceFlags3;DWORDdwServiceFlags4;DWORDdwProviderFlags;GUIDProviderId;//服务提供者厂商安排的GUIDDWORDdwCatalogEntryId;//为该结构体安排的唯一标示符(目录入口)WSAPROTOCOLCHAINProtocolChain;//协议链结构体intiVersion;intiAddressFamily;//地址家族intiMaxSockAddr;intiMinSockAddr;intiSocketType;//套接字类型intiProtocol;//协议intiNetworkByteOrder;intiSecurityScheme;DWORDdwMessageSize;DWORDdwProviderReserved;CHARszProtocol[WSAPROTOCOL_LEN+1];}WSAPROTOCOL_INFO,*LPWSAPROTOCOL_INFO;13:30:0711获取协议信息使用WinsockAPI函数WSAEnumProtocols();使用WinsockSPI函数WSCEnumProtocols();13:30:0712intWSAEnumProtocols(LPINTlpiProtocols,//整型数组,指定要枚举的协议,可选。指定为NULL则返回所有的协议。LPWSAPROTOCOL_INFOlpProtocolBuffer,//存放协议信息的缓冲区LPDWORDlpdwBufferLength//缓冲区长度);枚举指定的网络协议信息,将具体的协议信息填充到WSAPROTOCOL_INFO结构体中。WSAEnumProtocols函数仅能枚举基础协议和协议链,不能枚举分层协议。返回值:成功为协议个数,失败为SOCKET_ERROR。注:创建套接字时使用WSAEnumProtocols函数枚举系统中安装的协议,根据传递的参数找到一个与之匹配的协议,然后调用此协议的提供者导出的函数来完成各种Winsock调用。13:30:0713WSAEnumProtocols函数的使用方法:首先以lpProtocolBuffer为NULL,lpdwBufferLength为0调用WSAEnumProtocols,该调用会以WSAENBUFS失败,此时lpdwBufferLength参数包含了所需的缓冲区长度。以lpdwBufferLength包含的缓冲区长度分配内存空间再次以分配的内存空间调用WSAEnumProtocols实例13:30:0714WSCEnumPtotocols(LPINTlpiProtocols,LPWSAPROTOCOL_INFOWlpProtocolBuffer,LPDWORDlpdwBufferLength,LPINTlpErrno//取得调用出错后的出错代码);该函数能够枚举各种协议,包括分层协议、基础协议和协议链。协议信息存放在WSAPROTOCOL_INFOW结构体中,支持UNICODE。使用方法同WSAEnumProtocols实例13:30:0715typedefstruct_WSAPROTOCOL_INFOW{DWORDdwServiceFlags1;DWORDdwServiceFlags2;DWORDdwServiceFlags3;DWORDdwServiceFlags4;DWORDdwProviderFlags;GUIDProviderId;DWORDdwCatalogEntryId;WSAPROTOCOLCHAINProtocolChain;intiVersion;intiAddressFamily;intiMaxSockAddr;intiMinSockAddr;intiSocketType;intiProtocol;intiProtocolMaxOffset;intiNetworkByteOrder;intiSecurityScheme;DWORDdwMessageSize;DWORDdwProviderReserved;WCHARszProtocol[WSAPROTOCOL_LEN+1];//UNICODE字符串}WSAPROTOCOL_INFOW,*LPWSAPROTOCOL_INFOW;13:30:0716分层服务提供者(LSP)分层提供者的体系结构图7-2分层提供者的体系结构13:30:0717WS2_32.dll加载下层服务提供者,利用下层服务提供者导出的函数实现其内部API函数。被WS2_32加载的下层服务提供者,由调用基础服务提供者(或者其下层服务提供者)提供的服务实现其内部SPI函数。用户创建套接字时,套接字创建函数(如socket)在Winsock目录寻找合适的协议;此协议的提供者导出的函数完成各种功能编写分层服务提供者并安装可以截获Winsock调用13:30:0718运行原理安装LSP安装LSP实质就是安装一个WSAPROTOCOL_INFOW结构,该结构定义了分层提供者的特性和LSP是如何填写链的。安装LSP后在Winsock目录中就有了一个WSAPROTOCOL_INFOW结构,让创建套接字的应用程序可以枚举到它。13:30:0719协议链协议链描述了分层提供者加入Winsock目录的顺序,也就是在协议链中的位置。协议链由嵌入在WSAPROTOCOL_INFOW结构中的WSAPROTOCOLCHAIN结构中的数据指定,结构定义如下:typedefstruct_WSAPROTOCOLCHAIN{intChainLen;//0表示分层协议,1表示基础协议,//1表示协议链DWORDChainEntries[MAX_PROTOCOL_CHAIN];//目录ID数组}WSAPROTOCOLCHAIN,FAR*LPWSAPROTOCOLCHAIN;13:30:0720当ChianLen是0或1时,包含在ChianEntries数组中的数据是无意义的。当ChianLen大于1时,形成协议链的各个服务提供者的目录ID都包含在ChianEntries数组中。LSP在协议链中位置的影响:顶层:被Ws2_32.dll加载非顶层:被链中位于它上层的LSP加载LSP被加载后的动作:1)首先调用LSP导出的函数WSPStartup()2)将包含协议链的WSAPROTOCOL_INFOW结构传递给这个函数3)LSP再找到协议链中位于自己下方的提供者,进而加载它13:30:0721安装LSP时,必须在Winsock目录中安装两种协议:分层协议、协议链安装分层协议是为了获取Winsock目录分配的目录ID,以便在协议链中标识自己。协议链是Winsock目录中LSP的真正入口,链中包含了自己分层协议的目录ID号和下层提供者的目录ID号,这些目录ID构成ChainEntries,进而构建一个WSAPROTOCOL_INFOW结构安装函数需要为该函数提供LSP的GUID、DLL位置、描述LSP支持协议的一个或多个WSAPROTOCOL_INFOW结构。13:30:0722函数定义intWSCInstallProvider(constLPGUIDlpProviderId,//要安装的提供者的GUIDconstLPWSTRlpszProviderDllPath,//指定提供者DLL路径constLPWSAPROTOCOL_INFOlpProtocolInfoList,//指向一个WSAPROTOCOL_INFOW结构数组DWORDdwNumberOfEntries,//lpProtocolInfoList数组中条目数量,即数组大小LPINTlpErrno//返回可能的失败代码);//只有UNICODE版本,失败则返回SOCKET_ERROR13:30:0723lpProviderId:GUID可以通过命令行工具UUIDGEN或者编程中使用UuidCreate函数生成。lpszProviderDllPath:UNICODE字符串,包含环境变量。如%SYSTEMROOT%lpProtocolInfoList:WSAPROTOCOL_INFOW结构的数组,每个数组成员是一个要安装的单独目录,即可一次安装多个服务提供者通常从它要分层的下层提供者拷贝,两种情况例外:第一,szProtocol域要修改,以包含新提供者的名称第二,如有XP1_IFS_HANDLES标志,从dwServiceFlags1域中移除XP1_IFS_HANDLES标志,该标志表示此提供者返回的句柄是真正地操作系统句柄,在该句柄上会引起user/kernel