I want to find a job and then I have a Under the hood Chinese version, but I have to go to Moscow University to read mathematics, I have to work hard, wait for me to learn Russian, to translate Russian materials for everyone, in my opinion, I seem to have a very strong computer. of. I heard that Yuan Ge also wants to make mathers people?
Translatant: unraveling the mysteries of Writing a Winsock 2 Layered Service Provider
Wei hua, Jim Ohlund, Barry Butterklee
Dong Yan translation
Greatdong_2001@163.com
http://greatdong.blog.edu.cn
It is very powerful to use a layered Transport Service Provider to extend the basic transmission. The hierarchical Service Provider only implements the high-level custom communication function and performs data exchange with the remote side to rely on the existing lower base provider.
This article assumes that the reader is familiar with C , Winsock, Overlapped I / O.
Code of this article: Layered.exe (86KB)
Wei HUA and BARRY Butterklee are develope Engineers, which is specialized in network programming. Jim Ohlund is Software Test Engineer for Microsoft Proxy Server Team.
One of the most interesting but the most interesting characteristics in Winsock 2 is Service Provider Interface (SPI). Unlike many books, documents, the Winsock 2 API, which is known, Winsock 2 SPI is still waiting to be explored. Winsock 2 SPI is implemented by Network Transport Service Providers and NameSpace Resolution Service Provider. Winsock 2 SPI can extend existing Transport Service Provider by implementing a Layered Service Provider (LSP). For example, the Quality of Service (QoS) on Windows 98 and Windows 2000 is implemented in the form of LSP on the TCP / IP protocol stack. Other places where the LSP has special URL filtering software, which prevents web browsers from accessing a site, regardless of which browser is installed on the desktop.
Winsock 2 is launched at Windows NT 4.0; there is also a Windows 95 (which is an add-on package), Windows 98 and Windows 2000. Winsock 2 SPI specification can range from Platform SDK or
FTP: //ftp.microsoft.com/bussys/winsock/winsock2 is obtained. The only sample code is the "layered" sample in Microsoft Platform SDK. Below we will uncover the mystery of Winsock 2 SPI by in-depth study. Let us first introduce the desired background and then study this layered sample and how to extend this sample.
Background Information
Winsock 2 and Windows Open Service Architecture (WOSA) models are unsuarable (see Figure 1). The WOSA system allows the third-party service providers to be inserted without the need to rewrite the code without replacing the DLL of Winsock 2, WS2_32.dll.
----------- ------------- | Winsock 2 || Winsock 2 |
| Application || Application |
------------- -------------
Winsock 2 API |
--------------------------
| Winsock 2 DLL |
| TRANSPORT NAMESPACE |
| Function function |
--------------------------
| TRANSPORT NAMESPACE |
| API API |
--------------------------
| TRANSPORT NAMESPACE |
| Service Service |
| Provider provider |
--------------------------
Figure 1 Winsock 2 Architecture
Winsock 2 SPI enables developers to develop two different types of Service Providers - Transport and Namespace. Transport Providers (commonly known as protocol stacks) are all services that provide functions such as establishment of connectivity, transmission data, flow control, and error control. Namespace Providers' service in the addressing attribute of the network protocol is related to one or more user-friendly names, and can be parsed in the name of the protocol. SPI also allows developers to develop two types of Transport Service Providers - Base Service Providers and Layered Service Providers.
Figure 2 protocols
Base Service Providers implements the actual details of the transfer protocol: establish a connection, transfer data, stream control, and error control. Layered Service Provider only implements high-level custom communication features, and relying on the existing base provider to actually perform actual data exchange with the remote side (see Figure 2). For example, developers can implement a security manager or bandwidth manager at the top of the Base TCP / IP stack. These provoders can be linked to the Winsock 2 SPI as long as these Providers in the upper and lower layers are supported. The layered sample in the MSDN Platform SDK is a generalized Layered Service Provider, which is installed on all Base Providers.
Winsock 2 does not support the hierarch of NameSpace Provider, so you can use Winsock 2 SPI to implement a new Namespace Provider, but you cannot change or extend naming, registration, and query behavior of Namespace Provider.
We only discuss the SPI function for developing the Layered Transport Service Provider because Base Transport Providers and Namespace Provider can generally get from the operating system vendor and transport protocol stack manufacturers. It is very powerful to use a hierarchical Transport Service Provider to extend the basic transfer. However, it should be noted that only applications that use the Winsock interface can taste the sweets brought by the added features, while programs that use other network interfaces will not be affected. When we use the term "service provider", we actually refer to Transport Service Provider, which can be the Layered Transport Service Provider or Base Transport Service Provider. We use "LSP" to specifically refer to Layered Transport Service Provider. Figure 3 shows the naming rules for the function prefix used by Winsock 2 SPI.
When writing Service Provider, export only Wspstartup and NspStartup in the entry point. Wspstartup and NspStartup actually provide information about other functions, which consists of a Service Provider by using a special Function Dispatch Table for parameters.
Winsock 2 LSPs are implemented in standard Windows DLLs, which only have an exported entry function, Wspstartup, WSPSTARTUP. All other Transport SPIs in the WS2_32.dll and Upper Chain Layered Provider are accessed through the Dispatch Table of the LSP, which is the parameter LPPROCTABLE in the Wspstartup function. We give the UpCall Dispatch Table for WS2_32.dll to the LSP through Wspstartup parameter upcalltable. Structural wspproc_table (see Figure 4) Defines the function of the LSP must implement and the entry point of those functions to fill in the Wspstartup's LPPROCTABLE parameter.
INT Wspstartup (Word WVersionRequested,
LPWSPDATAW LPWSPDATA,
LPWSAPROTOCOL_INFOW LPPROTOCOLINFO,
WspupCallTable UpCallTable,
LPWSPPROC_TABLE LPPROCTABLE);
Although it seems that the LSP seems to achieve a lot of functions, it can directly call the next corresponding Function Service Provider in the protocol chain. The structural WspUpCallTable (see Figure 5) defines the UpCall function of WS2_32.dll for the LSP call. These UpCall functions are obtained from the UpCallTable parameters of Wspstartup. WspupCallTable is a fixed-length structure. If you want to add a new UpCall function to WS2_32.dll, you need to call GetProcAddress to get getProcAddress in LSP's Wspstartup to get pointers to the new UpCall function. There is only one such function that exists - WpucompleteOverlappedResult.
LSP chaining
Both LSPS and BASE Providers have been stringed to form a protocol chain. The WSAPROTOCOL_INFOW structure refers to the entire protocol chain. It describes the connection order of Layered Providers. Behind we will explain the details of this sector. The DLL for implementing the LSP is either loaded by another LSP or loaded directly from WS2_32.dll, which is to see the location of this LSP in the protocol chain. If the LSP is not at the top of the protocol chain, the LSP is loaded by the LSP of the previous layer, otherwise it is loaded by WS2_32.dll (in the layered sample, the low-level Chain Provider is loaded in the first WSPSocket call. of). After loading the LSP, the Wspstartup of the LSP must be called. The UpCallTable ended from WS2_32.dll must be passed down to the LSP, and the LSP's LPPROCTABLE must be obtained.
When the LSP calls the low-level Provider WspstartUp in the protocol chain, if the low-level Provider is a Layered Provider, this chain's WSAPROTOCOL_INFOW structure must be passed to Wspstartup calls. When the lower layer is a base protocol (end of the chain), the WSAPROTOCOL_INFOW structure of this chain is no longer transmitted down, and this must obtain the Base Provider's WSAPROTOCOL_ INFOW structure and passed it to the Base Provider Wspstartup call. . Therefore, this signs indicate that Base Provider is not in the protocol chain. The situation is the same when a special function (such as WspstringToadDress and WspaddressToString) passes the WSAPROTOCOL_INFOW structure.
Special pay must be taken to ensure that the dwProviderReServed domain of the WSAPROTOCOL_ INFOW structure is always transferred down, although the low layer is a base provider. This is because dwProviderReServed is a context information that generates a WspduPlicatesocket to save a copy of the package, including the Wspsocket to rebate the copy handle for reconstruction of the copied Socket handle.
It should also be noted that the call mode of the SPI function of the LSP is the same, and is independent of the location where the LSP is in the protocol chain. Thus, in order to see, we assume that our LSP client is WS2_32.dll, a low-level provider is a base provider.
Moreover, a Service Provider's Wspstartup can be called multiple times. Further, WS2_32.dll calls the WspCleanup each Wspstartup call. Therefore, every Service Provider should implement a reference counter every process, this counter is incremented by Wspstartup, which is reduced in WspCleanup. When this counter is zero, Service Provider must do prepared from memory from memory.
Mapping Between Winsock 2 API and SPI Functions
When the developer calls a WINSOCK 2 API, WS2_32.dll finally calls a corresponding WINSOCK 2 SPI function to use the established Service Provider to implement the requested features (note the exceptions mentioned later). For example, the SELECT API is mapped to Wspselect SPI, Connect, and WSACONNECT APIs are mapped to WSPConnect SPI, Accept, and WSAACcept APIS are mapped to WSPAccept SPI. But not all Winsock APIs have corresponding SPIs. The tool function like HTONL, HTONS, NTOHL, and NTOHS is only implemented in WS2_32.dll, and does not process to Service Providers. This also applies to WSA versions of these functions.
The conversion function of INET_ADDR and INT_NTOA is only implemented in WS2_32.dll.
All TCP / IP-related name transformations and parses related to Winsock 1.1 are implemented in WS2_32.DLL, such as GetXByy, WSaAsyncGetxByy, and WSAcanceLasyncRequest, and gethostname.
Winsock Service Provider enumeration and blocking hook related functions are implemented in WS2_32.dll. So WSAenumProtocols, WSAisblocking, WSASetBlockingHook, and WSAUNHOKBLOCKINGHOOK are not SPI functions. Because Error Codes returned with the SPI function, the SPI does not need to be equivalent to the function of WsageTlasTERROR and WSASTLASTERROR.
The operation and waiting functions of the time object, including WSACReateEvent, WSACloseEvent, WSASTEVENT, WSARESEVENT, and WSAWAITFORMULTIPEVENTS are directly mapped to Native Windows Operating System Services, not in the SPI.
LSP Socket Creation and IFS Handles
The Socket handle has three categories: The Socket handle returns to the LSP by base providers, returned by the LSP to the Socket handle of WS2_32.dll and the handle returned by WS2_32.dll in the user program.
WS2_32.dll maintains an associated list, which is associated with the Socket handle acquired from the LSP and the Socket handle that returns to the user program. The LSP should also use similar practices and maintain a associated list of Socket handles from Base Provider and return to the Socket handle of WS2_32.dll. This makes the Socket handle of a given layer, and the LSP can find the corresponding low-level socket, and when the LSP is uninstalled, all Base Socket handles can be properly turned off.
Which function should be used to study the LSP to generate a handle that returns to WS2_32.dll, we also need to take a look at the Install File System (IFS) handle. This handle can be used to complete the Winsock's RECV and Send calls in the file I / O function when there is an IFS handle. Under Windows NT, the IFS handle can be applied to I / O Comset Ports (IOCP) to achieve scalability. This is indicated by the Providers with the IFS handle through the XP1_IFS_Handles property bits in the WSAPROTOCOL_INFOW structure. All Microsoft's Base Providers implements sockets as an IFS handle. The LSP cannot create a Socket handle of the true IFS handle because IFS cannot be implemented in the LSP. However, the Socket handle that is returned to WS2_32.DLL by calling WPUCREATESOCKETHANDLE or WPUCREATESOCKETHANDLE or WPUMODIFSHANDLE can be used in file I / O calls. LSP acquires the base provider's Socket handle by calling the Base Provider's Wspsocket. If the base provider is used by the IFS handle, the LSP can call WPUMODIFSHANDLE to generate a modified handle to return to WS2_32.dll. Just consider the operating system, this modified handle is nothing different from IFS. In fact, the LSP can select only the modified handle in all internal processing. The 蹊跷 is that the LSP cannot use Wspsend (WriteFile), Wspsendto, Wsprecv (ReadFile), WspRecvfrom or WSpiOctL to proceed with postLapped I / O. In order to make an overlapped I / O in front of any of the Overlapped I / O, additional processing is performed in the LSP, or if the Base Provider's handle is not an IFS handle, WPUCREATESOCKETHANDLE must generate a handle that returns to WS2_32.dll
In order to provide the LSP, one of the input parameters provided by the LSP in WPUCREATESOCKETHANDLE is a DWORD context value. The DLL of Winsock 2 will associate this context value with the assigned Socket handle and make the LSP to get this context value at any time through WPUQuerySocketHandlexText. This context value is typically used to save a data structure that pointing the Socket handle that is maintained by the LSP. There is no difference between the Socket handle created with WPucreatesocketHandle and the real file system handle. However, any Layered Service Provider that uses this technology should still identify yourself as Non-IFS Provider, which is implemented by the XP1_IFS_Handles flag in the Provider Information structure. The application can use this flag to indicate if it uses file system calls.
If you use ReadFilers on these Providers, WS2_32.dll must make additional parameter arrangements and the user / kernel mode conversion. Accurately, even in the case of Wsasend and WSARECV, such provoders cannot avoid additional mode conversion, although readfile and writefile are more converted. Layered Sample allows all Winsock I / O calls pre- and post-processing. Therefore, it uses WPUCREATESOCKETHANDLE to create a handle that returns to WS2_32.dll. Until Windows NT 4.0 SP3, people can use WPUCREATESOCKETHANDLE functions in the context of Administrator privileges. This problem makes a LSP on Windows NT to be unprofitable. Windows 95 and Windows 98 do not have this limit because they do not use the security model of Windows NT. Windows NT 4.0 SP4 is working on this issue. For the developers of LSP, this is really good news.
LSP Socket I / O
There are three basic I / O models in Winsock 2: Blocking, Nonblocking and Overlapped. The I / O operation that occurs on IOCPS uses Overlapped I / O. We will discuss each kind of I / O model using WSPRECV. Details Similar to Wspsend.
Blocking I / O is the simplest form in Winsock 2. Any I / O operation on the Blocking Socket is until the operation is completed. Therefore, only one I / O operation can be performed at a time. When the WSPRECV of the LSP is called by the WS2_32.dll, the LPOVERLAPPED parameter will be NULL. The LSP only needs to pass the call to the Base Provider WSPRECV call. The LSP's WSPRECV is only returned when the base of the BASP is complete.
Even if Blocking I / O is easy to implement, we must also consider backward compatibility with Winsock 1.1 Blocking Hooks. WsaseTBlockingCall and WSAcancelBlockingCall calls are removed from the Winsock 2 API specification. If WSPCANCELBLOCKINGHOK and WSACANCELBLOCKINGCALL functions are called, WSPCanceLBlockingHook can still be called by WS2_32.dll. In the LSP, simply pass the WSPCanceLockingHook call to the base providers call. If you are implementing the base provider and develop a Blocking Call, you must implement a mechanism for cycle calling the WPUQueryBlockingCallback function.
Nonblocking I / O
If the socket is Nonblocking mode, any I / O operation must be either immediately complete either returning ERROR CODE WSAEWOULDBLOCK to indicate that this operation cannot be completed immediately. The latter case requires a mechanism to find the timing of executing the operation again. To this end, a group of network events are defined, and Wspselect can be used to wait for these events, or the asynchronous transfer can be registered by calling Wspasyncselect or WSPEventSelect.
If you use Wspselect, three fd_sets (readfds, writefds, and exceptfds) are passed from WS2_32.dll. LSP's Wspselect needs to find the base provider's sockets by calling WPUQuerySocketHandleContext and creates your own read fd_set, write fd_set, and except fd_set. Then it calls the Base Provider's Wspselect. When Base's Wspselect returns, it must convert the base socket's fd_set to the originally accepted FD_SETS. The system of Winsock 2 Service Provider allows SELECT in the user program to handle Sockets with the same service provider in a call. LSP's WSPEVENTSELECT implementation is actually very simple. When the user program is passed downward by WS2_32.dll, the LSP simply converts the WS2_32.dll's socket handle to the corresponding base socket handle and passes the call to the Base Provider WSPEventSelectElect. When the required I / O can be called, the base provider directly informs the event object, and the user program knows that there is a network event. LSP's WspasyncselectElect is a bit tricky. Because the LSP cannot rely on the user program's Message Pump to Pump Socket's notification message, the general LSP must create another window and have a dedicated WORKER THREAD to Pump Messages. In Layered Sample, when Wspasyncselect is called, a hidden window and a Worker Thread are also created, and the user program's window handle and notification messages are saved. After converting the Socket handle of WS2_32.dll to the Base Provider handle, this new hidden window and a new message are used to call the base provider's WSPasyncSelect. When accepting the new message indicating the required I / O available, the Window Procedure of the Hide Window uses WPUPOStMessage to send the user program's notification message to the window of the user program.
The LSP should use another reason for another practice. When running a 16-bit Winsock 1.1 application on Windows 9x, due to the implementation of WSOCK2.VXD and Winsock.dll, if the user's window is passed to Base Wspasyncselect, the LSP will not get a notification message. .
Overlapped I / O
All Winsock 2 Transport Providers must support Overlapped I / O. Sockets in LSP can perform Overlapped I / O is created by the WSPSocket function set by the WSA_FLAG_ Overlapped flag, and all follow the Overlapped I / O model established in WIN32. Overlapped I / O is the most complex one in all three Winsock 2 I / O models.
WS2_32.dll records all Overlapped I / O in the user program, including on IOCP. When the user program uses Overlapped WSARECV from the LSP perspective, its WSPRECV is called by WS2_32.dll, and the lpoverlapped parameter is not null. After the Socket handle is converted, you need to call the base provider WspRecv, and the base provider's WSPRECV must also be notified. Conversely, you also need to send a Completion Notification to WS2_32.dll. In other words, the LSP must handle two levels of COMPLETITIONNICs: one is from Base Provider to LSP, another from LSP to WS2_32.dll. All operations in this cannot block the calling thread. Let's take a look at the two levels of Completion Notifications. The first level is to issue an overlapped I / O on the base provider and detect if it is completed. When LSP launches Overlapped I / O on the base provider, there are two ways to manage the completion of the I / O request: notify an LSP-related event object or call an LSP-related Completion Routine. In both cases, a data structure, WSAOVERLAPPED, associated with Overlapped operations. The WSAOVERLAPPED structure can be used by the LSP to save the result of the Overlapped operation (such as the transmitted byte number, updated flag, and error code) "handle". To get these results, the LSP must call the Base Provider's WspGetoverLappedResult, which delivers a pointer to the WSAOVERLAPPED structure.
If you select an event-based COMPLETION, the Base Provider's WspGetoverLappedResult function can be used by the LSP to query or wait for the Base Provider's Overlapped operation. The LSP can also be waited using other methods (such as WSAWAITFORMULTIPLEVENTS) until the corresponding event object is notified. If you specify a Completion Routine for the Overlapped I / O, only the query item is available in WspGetoverlappedResult options. Because Winsock 2's Completion Routine is called by an Asynchronous Procedure Call (APC) mechanism, the LSP needs to be allowed to send I / O lines as a Alertable waiting state to make the base provider's overlapped I / O complete After the Completion Routine can be implemented. Therefore, the LSP needs to call a waiting function (such as WSAWAITFORMULTIPEEVENTS or SLEEPEX) and specify the falreable parameter in the waiting call to True. Once the Completion is indicated, the LSP can call the Base Provider's WspGetoverLappedResult, and the call can be done immediately. Figure 6 summarizes the semantics of the COMPLETion of Overlapped Socket and demonstrating the various combinations of parameters in WSPRECV. In Windows NT, there is actually more than one mechanism for LSP to get notifications for Base Provider's Overlapped I / O Completion. The LSP can add BASE Provider's Socket handle to IOCP, issue Overlapped I / O on the Base Provider, then call GetQueuedCompletionStatus in a WORKER THREAD to get the COMPLETION NOTIFICATION for Base Provider's Overlapped I / O.
Let's take a look at the second level of Completion Notification, where the LSP will notify the Overlapped I / O to WS2_32.dll. The LSP can use WPUCompleteOverlappedRequest or WPUQueueAPC by WSPRECV.
WPUCompleteOverlappedRequest is a new function, Winsock 2 SPI Revision 2.2.2 adds this function to support the IFS handle. This function is only used when the user program does not use the Completion Routine function for its Overlapped I / O. WPUCompleteOverlappedRequest just makes the LSP notification WS2_32.dll A Overlapped I / O has been completed. WS2_32.dll uses one of the above COMPLETITION mechanism to notify the user program. WINSOCK 2 implementations in Windows NT 4.0 SP3 and previous versions do not support WPUCompleteOverlappedRequest. With the latest WINSOCK 2 Add-ON, Windows 95, Windows 98, Windows NT 4.0 SP4 and Windows 2000 support this function in WS2_32.dll. For earlier versions of Windows NT, the LSP can call setEvent to notify the event handle in the Overlapped structure specified by the user program. When the LSP calls WPUCompleteOverlappedRequest to support the IFS handle, a heavier load occurs. If the support for the IFS handle is not required, setting setEvent in the LSP to notify the user program to notify Overlapped I / O completion more efficient. If the user program specifies the Completion Routine for Overlapped I / O, WS2_32.dll calls the LSP's WSPRECV SPI and delivers the user-specified completion routine with the LPCompletionRoutine parameter. When the I / O of the lower layer is completed, the LSP is responsible for arranging the call of this Completion Routine. Because the Completion Routine must be executed in the context of the same thread of the Overlapped operation, it is not possible to call directly from the LSP. The LSP needs to call WPUQueueAPC to make COMpletion Routine execute in the correct thread. This function can be called in any process and thread context, or even everlapped operations can be issued in a context that differ from this process and threads.
The parameter of WPUQueueAPC is a pointer to the WSATHREADID structure, a pointer to the APC function to be called and a 32-bit context value, which will then pass this value to the APC function. LSPS always provides a correct WSATHREADID structure pointer, which is passed by the LPTHREADID parameter of the Overlapped function. The LSP should store the WSathReadID structure locally and provide a pointer to this WSathreadID structure copy, which is used as input parameters for WPUQueueAPC. Once the WPUQueueAPC function returns, Provider can handle it WSATHREADID.
Function WPUQueueAPC just queues enough information to call the APC function with the give parameters, but direct calls are not. When the target thread has entered the Alertable waiting state, this information is out of the queue and the APC function is called in the context of the target thread and the process. In some environments, the LSP may need to issue and complete the overlapped operation within the inside of Worker Thread. At this time, there will be wsathreadID in the function call. The SPI provides a UpCall, WPUOpenCurrentThread, to get a WSATHREADID from the current thread. When this WSATHREADID is no longer required, its resources should return by calling WPUCloSethread. As mentioned, the WSAOVERLAPPED structure provides the media between the Overlapped I / O operation and the completion of the completion. The WSAOVERLAPPED structure is compatible from the design of Win32's Overlapped structure (see Figure 7).
Layered Sample uses a WSAOVERLAPPED structure to save the status information of the Overlapped I / O occurring. When the base overlapped I / O is called, the INTERNAL member is set to WSS_OPERATION_IN_PROGRESS. When the Overlapped I / O is done, the INTERNAL member is set to the value in the OFFSET member. Offset members are used to report ERROR CODE, Base Overlapped I / O. OffsetHigh members are used to save the flag of Winsock I / O (WspRecv's lpflags parameters). The role of INTERNALHIGH is to report the number of bytes transmitted. When the user's COMPLETION function is called, all other all the same in addition to the INTERNAL member is set to the user completion function, and it is submitted by the context as a context as WPUQueueAPC.
Winsock 2 supports a Debug / Trace mechanism that allows developers to come to TRACE WINSOCK 2 function call, function returns, parameter values, and return values. The parameter value and return value can be modified when the function call or function returns. Debug / TRACE DT_DLL.DLL Exposes has two functions, WsapReapInotify and WSAPostapinotify. The basic idea of implementing Winsock 2 in the LSP is that when entering a WSP function, the WSAPReapInotify function of DT_dll.dll is first called, and when the WSP function exits, Wsapostapinotify is last called. About Winsock 2 Debug and Trace Detailed Description You can find in dbgspec.doc in the PLATFORM SDK's DT_DLL sample.
Installing a Layered Transport Service Provider
Now we have talked about the implementation of the LSP, let's take a look at how to write an LSP installer. The installer is just the LSP in the Winsock 2 System Configuration Database (Winsock 2 System Configuration Database), which is a directory for all installed service providers. Winsock 2 can know the existence of Service Provider through the database and define the type of service provided. When the Winsock application creates a socket, Winsock 2 uses the database to determine the TRANSPORT Service Providers you want to load. WS2_32.dll Search Database to find the first Provider that matches the input parameters (such as the address family, socket type, and protocol) called with the Socket or WSASASOCKET API. Once found, WS2_32.dll is loaded with the corresponding Service Provider DLL specified in the directory. To successfully install and manage the Service Provider Entry in Database requires four functions. Each function starts with a WSC prefix:
WSCENUMPROTOCOLS
WSCINSTALLPROVIDER
WSCWRITEPROVIDERORDER
WSCDeinstallProvider
These functions queries and operates the database with the WSAPROTOCOL_INFOW structure (see Figure 8). For the installation of the LSP, the main concern is the ProviderID, DWCATALOGENTRYID and Protocolchain domain. The ProviderID domain is a globally unique identifier that can be used to define and install the provider on any system. The DWCatalogEntryID domain is only identified by each WSAPROTOCOL_INFOW directory item structure in the database. Protocolchain determines the WSAPROTOCOL_INFOW structure is a Base Provider's Catalog Entry, Layered Provider or Provider Protocol Chain. The protocolchain domain is a WSAPROTOCOLCHAIN structure:
Typedef struct {
Int chainlen; / * the length of the chain,
/ * Length = 0 means layered provider,
/ * Length = 1 Means Base Provider,
/ * Length> 1 means protocol chain * /
DWORD chainentries [max_protocol_chain];
/ * a List of dwcatalogentryids * /
Wsaprotocolchain, FAR * LPWSAPROTOCOLCHAIN
Chainlen determines that the directory item represents Base Provider, Layered Provider, or defines a protocol chain. The protocol chain is also a directory entry, which defines what positions that will be placed as the Service Provider between Winsock and other Service Providers (see Figure 2). The CHAINLEN field is 0 indicates a Layered Provider, which represents the Base Service Provider or other Service Provider, if a large value is a protocol chain. For Layered Provider and Base Provider, there is only one directory entry in the database.
The last ChainenTries domain is an array of directory IDs that is used to describe the loading order of Service Providers in the protocol chain directory. During creating a Socket, you only need to find the protocol chain and the base provider catalog directory when you look for the appropriate Service Provider in the directory. WS2_32.dll ignores the Layered Provider directory entry and the Layered Provider directory entry is only in the protocol chain link to the protocol chain directory. When installing an LSP on the Service Provider, you need to create two WSAPROTOCOL_INFOW directory item structures, a protocol chain that represents the Layered Provider to link the Layered Provider to the base provider. These two structures are generally initialized with existing Service Provider's WSAPROTOCOL_INFOW directory item structure, which can be obtained by calling WSCENUMPROTOCOLS. After initialization, you need to use WSCINSTALLPROVIDER to install the Layered Provider's directory item.
These two structures are generally initialized with the existing Service Provider's WSAPROTOCOL_INFOW directory item structure, which can be obtained by calling WSCENUMPROTOCOLS. After initialization, you need to use WSCINSTALLPROVIDER to install the Layered Provider directory entry, and then given the directory ID to which the structure is assigned. The directory item can be used to establish a protocol chain entry that will link the Layered Provider to another. The installation link provides WSCINSTALLPROVIDER (see Figure 9).
Note that the PFL_HIDEN flag is specified in the WSAPROTOCOL_INFOW structure of the Layered Provider in Figure 9. This logo guarantees that WSAenumProtocols will contain the directory of the Layered Provider in the returned buffer.
LSP is now installed on the system. How do Winsock 2 looks up for Service Providers in the database? Most Winsock applications determine the protocol of the intended use through the call parameters of Socket or WSASocket. For example, if you create a Socket using the AF_INET address family and the Sock_Stream type, Winsock 2 will first find the available TCP / IP protocol chain directory or the base provider directory item in the database that provides this feature. When using WSCINSTALLPROVIDER to the Layered Provider installation protocol chain, the directory item will automatically become the last item of the database. In order to make new chains a default TCP / IP Provider, you must also reorder in the database and call WSCWRITEPROVIDERORDER to place the protocol chain directory in front of other TCP / IP Providers. You can check the installation and sorting of the provoders through the Sporder.exe tool in Platform SDK. SPORDER.DLL must be in the same directory, otherwise sporder.exe will fail. Figure 10 shows the WINSOCK 2 configuration after installing a layered sample on a normal computer. The layered_provider item here represents the Layered Provider directory entry, the Layered MSAFD TCPIP [TCP / IP] represents the protocol chain that Layered Provider links to the Base Provider MSAFD TCPIP [TCP / IP]. Figure 10 Winsock 2 Configuration
As the number of LSPs added, a installer can install a LSP on a system that has previously installed LSPS. The installer needs to choose whether to insert its LSP into the existing protocol chain or create a new chain on the top of the base providers. We have told how to install LSP on Base Providers. To insert an existing protocol chain into the LSP, the installer needs to use the WSC function to do the following:
Install the Layered Provider to get its Catalog ID.
Modify CHAINFOWER's WSAPROTOCOL_INFOW by increasing Protocolchain.Chainlen and inserts the Catalog ID into the destination location in Protocolchain.Chanentries.
Remove existing chains and install the modified chain.
Managing Protocol Chain Order
LSPS has a huge potential for Value-Added network services. However, the current Winsock 2 specification does not give an answer to an important issue, this problem is: If the protocol chain has been installed, the new protocol chain should be inserted. For example, if you want to install a data encrypted LSP on a system that already has a URL filter LSP, it is clear that the data encrypted LSP needs to be inserted under the filter LSP of the existing protocol chain. But the problem is that the LSP installer cannot know what type of service provided by the existing LSP is also the correct insertion position it in the protocol chain. This is not a problem in what LSP and what kind of controlled network environment installed in the Administrators is installed. However, the widespread use of LSP has been suppressed because only the LSP is installed on the base provider and makes the new chain a default provocation. Such methods ensure new LSP services, but the LSP of the existing default Provider is removed.
Another issue not mentioned in the Winsock 2 specification is how the existing LSPS can be notified when it protects yourself in the link activity. This problem is as tricky as the first question. In practice, if the LSP protocol chain is not modified, the LSP developer can install the LSP in the order in the LSP, and install the LSP as Base Provider in the installer, which requires the LSP's WSAPROTOCOL_INFOW structure protocolchain.Chainlenlen Members are specified as 1. A Walk THROUGH THE LAYRED SAMPLE
Now we put all these together and to explore the layered sample in the Platform SDK. Although this sample may look a bit big, it achieves a complete set of Winsock 2 LSP, and developers can immediately start their extension.
To build this Layered Sample, you can run the NMAKE tool for Makefile. The generated Lsp.dll is the Layered LSP you want, INST_LSP.EXE is the installed executable. Copy lsp.dll to the Windows System (32) directory and run inst_lsp.exe. Running INST_LSP.EX again will remove the Layered Sample from the Directory of Winsock 2 Provider and therefore uninstall Layered. Layered Sample has several different versions. What is discussed here is the latest version of the Platform SDK to move into the official version of Windows 2000. The program can run on Windows 95, Windows 98, and Windows NT 4.0 SP4 with the latest Winsock 2, cannot run on Windows NT 4.0 SP3 and earlier Windows NT, because these versions do not implement WPUCompleteOverlappedRequest functions. To use on Windows NT 4.0 before SP4, you need to use the layered sample in the Platform SDK in March 97. The Layered Sample uses setEvent to notify the user program handle in the Overlapped Structure specified by the user. Figure 11 lists the files in Layered Sample.
List_entry and single_list_entry in layered, one is a two-way lin list defined in LList.h, one is a single-link list defined in NTDEF.H, and two headers are in Platform SDK:
Typedef struct _list_entry {
Struct _List_ENTRY * FLINK;
Struct _List_ENTRY * BLINK;
} List_entry, * PLIST_ENTRY,
* Restricted_pointer prlist_entry;
TYPEDEF STRUCT _SINGLE_LIST_ENTRY {
Struct _single_list_entry * next;
} SINGLE_LIST_ENTRY, * psingle_list_entry;
List_entry is used to save all protocol directory items, all Outstanding I / O on a socket, and all Outstanding Sockets. SINGLE_LIST_ENTRY is used to save a pre-allocated internaloverlappedstruct structure chain list. In order to let the structural body can be linked with List_entry, the structure must have the following form:
// typedef struct _foo
// {
// list_entry foolistentry ;?
?
?
//
//} foo, * pfoo;
A pointer to the member Foolistentry is given, and a containing_record macro returns a pointer to the host foo structure.
#define containing_record (address, type, field)
((Type Far *) (/
(Pchar) (Address) - /
(PCHAR) (& ((Type *) 0) -> Field))))))
Just understand the containing_record macro, then understand the rest of Llist.h should have no problem.
DT_DLL SPI Function Tracing
After setting the Winsock 2 Debug / TRACE DLL, the layered sample can complete all SPI functions of Debug Tracing. The sample DT_ DLL of the MSDN Platform SDK can be used with the Layered Sample. You also need to rename the file DT_DLL.DLL to myDt_dll.dll. Every WSP function of the Layered Sample is called two special macro Preapinotify and Postapinotify to hook WSapReapinotify and Wsapostapinotify in MyDT_DLL.DLL. A debug_tracing flag in makefile is used to make the debug function. If debug_tracing is set, the PreapInotify macro maps to WSAPReapInotify, postapinotify maps to WSAPostapinotify. Otherwise, these two macros are mapped to NO-OP.
DTHOKInitialize functions in dt_hook.cpp load myDt_dll.dll and put it WsapReapInotify and WSAPOSTAPINOTIFY functions. All SPI functions on this layer (implementation in spi.cpp) and all SPI functions on the Base Provider layer (DPROVIDE.H) are called. The DTHOOKSHUTDOWN function is used to uninstall myDt_dll.dll. Dllmain functions in dllmain.cpp call DLL_Process_attach, call DTHOOKSHUTDOWN in DLL_PROCESS_DETACH.
Proto_catalog_Item and DCATALOG
The first SPI.CPP in a WSPStartup call creates a global DCATALOG objects, gProviderCatalog, and calls DCATALOG.CPP in the Initialize, Initialize this using WSCEnumProtocols an all installed providers target their list of PROTO_CATALOG_ITEM fill gProviderCatalog- > m_protocol_list. gprovidercatalog-> m_local_item points to a Layered its own protocol directory entry. The gprovidercatalog-> FindNextProviderinChain function is responsible for the next Provider in the chain. If the next provider is a base provider, FindNextProviderinchain also returns the PPROTO_CATALOG_ITEM of Base Provider. Each time the Wspstartup is called, the variable gstartupcount must be increased; WspCleanup in spi.cpp is called each time, GstartupCount should be reduced. When gstartupCount changes to 0, WspCleanup deletes the GProviderCatalog object. DSOCKET
The DSocket object in dsocket.cpp is responsible for saving the Socket operating mode and establishs an association between the base provider's Socket handle with the handle submitted to WS2_32.dll. The static member m_socket_list in the DSocket class contains a global linked list for all DSocket objects. It is called in a static function dsocket :: DSocketClassInitialize, which is called when the Wspstartup is called for the first time. The m_provider_socket in the DSocket object is the Socket handle given by Base Provider. m_socket_handle members are the Socket handle submitted to WS2_32.dll, which is created by WPUCREATESOCKETHANDLE. The DSocket object The rest of the remaining members saves the context of different I / O models. For Overlapped I / O on Windows NT, m_completion_context is the completion key of IOCP. For Wspasyncselect, m_ async_events is a bit mask for network events used by a user program. m_async_window is the window handle of the hidden window of Lsp.dll for receiving the network notification window message.
There is no thing reflecting WSPEventSelect, because the call is only passed to the Base Provider's WSPEventSelect.
When the SPI function in the Layered Sample is called by the WS2_32.dll and the function is passed to the function, the handle is the handle of the previous WPUCREATESOCKETHANDLE to WS2_32.dll. When the WPUCREATESOCKETHANDLE function is called, the function saves the corresponding DSocket object pointer as context. Therefore, when getting a Socket handle in a WS2_32.dll, the Layered Sample calls WPUQuerySocketHandleContext to get the original DSocket object. The handle of the Base Provider obtained from the DSocket object is used to call the corresponding Base SPI function.
DPROVIDER
The DPROVIDER object saves all SPI function portals of a Provider. Layered Sample creates a DPROVIDER object for the base provider implemented by DPROVIDE.H and DPROVIDE.CPP. The IndnexTProviderinChain and LoadProvider functions in DCATALOG can load a DPROVIDER object for Base Provider. The Base Provider object is loaded by the following functions: WspstringToAdDress in Wspsocket, WspaddressToString, or spi.cpp. The DPROVIDE.CPP file also implements a mechanism that supports Microsoft to expand Winsock 2. When WSPIOCTL in spi.cpp is called with a SIO_GET_EXTENSION_ FUNCTION_POINTER flag, DPROVIDER's InterceptExtensions function is called. The returned WSPTransmitFile and WSPACCEPTEX function pointers in spi.cpp are not Base TransmitFile and AcceptEx function pointers. WspTransmitFile and WSPACCEPTEX in spi.cpp convert the Socket handle and call the Base Provider's TransmitFile and AcceptEx functions.
The implementation of the expansion function getacceptexsockAddrs is a bit different. Because getacceptexsockAddrs do not need a Socket handle, there is no use of the Socket handle to convert this call. The getAcceptExSockAddDRs function pointer of Base Provider in IntercePtextensions is not modified and passed directly upward in WSPIOCTL (SiO_ Get_extension_Function_ Pointer). The last Microsoft Winsock 2 extension function, WSARECVEX, is mapped by Mswsock.dll to WSARECV, so it is not processed by IntercePtextensions.
Only Windows NT implements these Microsoft extensions, while Windows 9x is not. To add your own extension function, you only need to modify the WSPiOctL function in spi.cpp to return the extension function pointer to SiO_GET_EXTENSION_FUNCTION_POINTER.
DBUFFERMANAGER
When Wspstartup in the spi.cpp file is called, a global variable called DBufferManager is created. GstartupCount will increase each time you call Wspstartup. When WSPCLEANUP is called (GstartupCount variable is 0), the GBufferManager object will be deleted. When an I / O function is called, GBufferManager-> AllocBuffer creates an internal buffer based on the user buffer. The Layered Sample then uses this internal buffer to call the corresponding base provider's I / O function. When completed, GBufferManager-> CopyBuffer copies the data in the internal buffer back into the original user buffer. GBufferManager-> FreeBuffer is called to release the internal buffer. The AllocateBuffer and CopyBuffers in the layered sample are just use the same user buffer pointer as the internal buffer pointer, and FreeBuffer is a NO-OP. Your own LSP can also cover these functions to easily intercept and modify data streams in the SPI I / O call. DasyncWindow
When WS2_32.dll calls the Wspasyncselect function in spi.cpp, the getasyncWindow function in spi.cpp is called to get a global DasyncWindow object called GasyncWindow. If this is the first time to call getasyncWindow, GasyncWindow will create GasyncWindow's Initialize function to create a WORKER THREAD in m_async_thread. A hidden window is created in its Thread Procedure, and ASYNCTHREADPROC will call a message pump. Returns the cached GasyncWindow if the getasyncWindow is called again. When entering AsyncThreadProc, the LOADLIBRAR is called again to add a LSP.dll system loading count, and FreElibraryandexitthread is called to load the Lsp.dll's system loading count. Layered load count GstartupCount is not affected by two calls. This is because when WS2_32.dll calls Wspcleanup last time, it will try to uninstall the provider. If there is an additional Lsp.dll load count, you can avoid the premature uninstall of the DLL before the thread exits.
After getaSyncWindow returns, you will call the socket-> RegisterasyncOperation function to save the Socket object's asynchronous window, async how to notify messages (async NOTINCTION Message), and a bit mask of the network event. Then call the GasyncWindow-> Registersocket function with hidden windows, a bit mask named WM_SELECT_MESSAGE to call the Base Provider's Wspasyncselect function. The Socket-> RegisterasyncOperation function completes the following:
Socket-> m_async_window = hwnd; // user app's async
// WINDOW
Socket-> m_async_message = wmsg; // user app's async // message
Socket-> m_async_events = levent; // user apps' async
// Event
GasyncWindow-> Registersocket function calls Base Provider's Wspasyncselect, parameter is GasyncWindow-> m_ async_window, wm_select_message, and level.
Note that the m_async_window member variable in the Socket object is the window of the user program, and the M_ async_message member variable in the socket object is an asynchronous Winsock notification message for the user program. M_async_window in GasyncWindow is an Async Message hidden window using WM_SELECT_MESSAGE as an Asynchronock notification message for Base Provider.
When gAsyncWindow-> m_ async_window WM_SELECT_MESSAGE window procedure of the received message, Socket-> SignalAsyncEvents function is called, the turn calls the original user program WPUPostMessage to post Socket-> m_async_message submitted to the original user program Socket-> m_async_window.
DoverlappedstructMgr and interfacelappedstruct
The extended Overlapped InterNalOverlappedStruct structure contains all context information of Overlapped I / O, including I / O types, buffers, Completion Routine, Socket handles, and more. When you first call Overlapped I / O operation, a global DWORKERTHREAD object called GWORKERTHREAD is created. GWORKERTHREAD's initialize function creates a global DoverlappedStructMgr object called GoverlappedManager. The initialize function of GoverlappedManager pre-allocates a predefined number (Outstandingoverlappedstructs) in GBufferManager-> m_overlapped_ struct_block. OutstandingoverlappedStrunts in DoverLap.h is defined as 1000. In other words, if the selected LSP wants to have more than 1000 Overlapped I / O operations any time, you need to increase the OutStandingoverlappedStructs defined in DoverLap.h. GWORKERTHREAD's destructor deletes GBufferManager.
DWORKERTHREAD
When the first Overlapped I / O is called, a global DWORKERTHREAD object is created, GWORKERTHREAD. In its initialize call, if the IOCP is created, a WORKER THREAD will be created. This suggests that the platform used is Windows NT. If IOCP creates fails, hints that the platform used is Windows 9x, and a semaphore is created, the number of threads created is the number of CPUs on the system.
On Windows NT, when WS2_32.dll calls the Layered Sample SAMPE SPI I / O function in an Overlapped mode, the Provider's Socket handle is added to IOCP. Then the base provider's overlapped call and Worker Thread are waiting to be done with the getQueuedCompletionStatus function. When getQueuedCompletionStatus returns, the OverlappedCompletionProc function is called to notify the WS2_32.dll operation. If the customer does not provide a completion function, OverlappedCompletionProc calls WPUCompleteOverlappedRequest, otherwise use WPUQueueAPC. Note that when entering the Worker Thread Process WorkerthreadProc, the LSP.dll calls LoadLibrary to increase the LSP.DLL system load count, and FreeELibraryandexitthread at the exit of WorkerthreadProc is called to reduce the LSP.dll's system loading count. GstartupCount is not affected by these two calls. This is to ensure that lsp.dll has been loaded when the Worker Thread is cleaned up, even if the last WSPCLEANUP is called, and attempts to uninstall Lsp.dll.
Blocking hook
WsaseTBlockingCall and WSAcancelBlockingCall calls are removed from the Winsock 2 API specification. However, if the application uses the Winsock 1.1 interface, WS2_32.dll can still call the WSPCANCELBLOCKINGHOK function.
In the Dllmain (DLL_PROCESS_ ATTACH) of Layered Sample, the TLSalloc function assigns a thread local storage (TLS) index. This TLS index is used to save the Base Provider object so that Base's WSPCANCELBLOCKINGHOK can be assigned and can be called in this layer's WSPCANCELBLOCKINGHOOK.
Two macros in SPI.cpp, SetBlockingProvider, and GetBlockingProvider, a Base Provider object is set to TLS INDEX, a Base Provider object from TLS INDEX. Take the WSPRECV function as an example, spi.cpp sends the following calls for blocking WSPRECV:
SetBlockingProvider (provider);
Provider-> wsprecv (...);
SetBlockingProvider (NULL);
The following SPI function implements a blocking call as shown above: WSPACCEPT, WSPACCEX, WSPCONNECT, WspRecv, Wsprecvfrom, Wspsend, and Wspsendto.
Because blocking actually occurs on Base Provider, Layered Sample does not need to call WPUQueryBlockingCallback.
WSPCLEANUP
Wspcleanup in spi.cpp just minimizes GstartupCount variables, and if GstartupCount is greater than 0, there is nothing else. When gstartupcount is reduced to 0, if GasyncWindow or GWORKERTHREAD is not null, its Destroy function is called, which will eventually exit the corresponding WORKER THREAD. Remember When you enter a WORKER THREAD, you have to add LoadLibrary once, and when you exit the Worker Thread, you will call a matching FreElibraryAndexitthRead. The Freelibraryandexitthread is also called when WS2_32.dll completes the LSP last WSPCLEANUP. Finally call who is free of FreelyInderandexitthread. When Lsp.dll is still loaded, the Cleanup code guarantee of Worker Thread is executed. The last FreelibraryAndexitthread call will uninstall Lsp.dll. The last WSPCLEANUP also calls DSocket :: DSocketClasscleanup to clean up the Socket list, delete the directory list GProviderCatalog, and delete the Overlapped structure list GBufferManager. In the previous version of the Layered Sample, the program release the Overlapped structure but whether the underlapped I / O has been completed, so it may cause the system to crash. The sequence of the Cleanup of the current Layered Sample is as follows:
Close Worker Threads.
Close the Socket handle so that more I / O cannot be submitted again.
If the corresponding I / O is complete, remove the Overlapped structure (Windows NT uses the Hasoverlappediocompleted macro)
Putting it all together
I have learned all the code in the Layered Sample, and now I have a summary. A typical lifecycle of a typical lsp.dll:
Load Lsp.dll in Dllmain (DLL_PROCESS_ ATTACH)
Call Wspsocket.
Multiple SPI function calls for Socket.
Call WspclosSocket.
Call wspcleanup.
Uninstall Lsp.dll (DLL_PROCESS_ DETACH) in Dllmain.
Understand all the code of the Layered Sample, this example is not difficult to learn. The reader is likely to agree with the C object in the Layered Sample clearly and simple and simplely simply realize each particular SPI function required for the LSP. So you can easily extend those objects to implement your own LSP without rewriting. If the reader just expands this Layered Sample, it may find that a LSP is no longer a scary and time consuming job. As Windows NT 4.0 SP4 fixes bugs of WPUCREATESOCKETHANDLE functions, we can expect many commercial LSPs in the market. Innovative thinking when developing Service Providers for Winsock 2, readers can start exploring these business opportunities!