Transport Service Providers

Two types of transport service providers are used in Winsock 2: base service providers and layered service providers. Base service providers implement the actual details of a network transport protocol—such as TCP/IP—including core network protocol functions such as sending and receiving data on a network. Layered service providers implement only higher-level custom communication functions and rely on an underlying base service provider for the actual exchange of data on a network. For example, you can implement a data security manager or a bandwidth manager on top of an existing base TCP/IP provider. Figure 14-2 shows how one or more layered providers can be installed between Ws2_32.dll and a base provider.

click to view at full size.

Figure 14-2. Layered provider architecture

This section focuses on the development aspects of a layered transport service provider. If you're developing a base service provider, the principles described here apply. However, we don't detail what is involved in implementing a particular SPI function from the ground up. For example, we don't supply the details of how the WSPSend SPI function writes data to a network adapter. Instead we show how the WSPSend function of a layered provider invokes the WSPSend function of a lower provider, which is a requirement of most layered service providers. Essentially, most of the work involved in developing a layered provider is relaying the SPI calls of your provider to the next provider below you. The tricky part is handling I/O calls from the Winsock I/O models described in Chapter 8, which we will discuss later in this chapter. On the companion CD-ROM, we provide an example named LSP that demonstrates how to implement a layered service provider that simply counts how many bytes are transmitted over a socket using the IP transport protocol. The Microsoft Platform SDK also features a more advanced layered service provider example named "layered" that can be found in the MSDN Platform SDK examples downloaded from ftp://ftp.microsoft.com/bussys/WinSock/winsock2/layered.zip.

NOTE
Throughout this section on transport service providers, we often use the terms "SPI client" and "lower provider." An SPI client can be either Winsock 2's Ws2_32.dll or another layered service provider placed above your service provider. An SPI client is never a Winsock application itself because Winsock applications must use the Winsock 2 API exported from Ws2_32.dll. The term "lower provider" is used only when we describe development aspects of a layered service provider. A lower provider can be either another layered service provider or a base server provider. As you will see, you can have multiple layered service providers installed on a machine; thus there is a chance that a layered provider might be installed below your provider.

WSPStartup

Winsock 2 transport service providers are implemented as standard Windows dynamic-link library modules in which you must export a DllMain function. Additionally, you must export a single function entry named WSPStartup. When a caller (the SPI client) invokes WSPStartup, it exposes 30 additional SPI functions that make up a transport service provider via a function dispatch table passed as a parameter. (See Table 14-1.) Your service provider must provide an implementation of WSPStartup plus all 30 functions.

Table 14-1. Transport provider support functions

API FunctionMaps to SPI Function
WSAAccept (accept also indirectly maps to WSPAccept)WSPAccept
WSAAddressToStringWSPAddressToString
WSAAsyncSelectWSPAsyncSelect
bindWSPBind
WSACancelBlockingCallWSPCancelBlockingCall
WSACleanupWSPCleanup
closesocketWSPCloseSocket
WSAConnect (connect also indirectly maps to WSPConnect)WSPConnect
WSADuplicateSocketWSPDuplicateSocket
WSAEnumNetworkEventsWSPEnumNetworkEvents
WSAEventSelectWSPEventSelect
WSAGetOverlappedResultWSPGetOverlappedResult
getpeernameWSPGetPeerName
getsocknameWSPGetSockName
getsockoptWSPGetSockOpt
WSAGetQOSByNameWSPGetQOSByName
WSAIoctlWSPIoctl
WSAJoinLeafWSPJoinLeaf
listenWSPListen
WSARecv (recv also indirectly maps to WSPRecv)WSPRecv
WSARecvDisconnectWSPRecvDisconnect
WSARecvFrom (recvfrom also indirectly maps to WSPRecvFrom)WSPRecvFrom
selectWSPSelect
WSASend (send also indirectly maps to WSPSend)WSPSend
WSASendDisconnectWSPSendDisconnect
WSASendTo (sendto also indirectly maps to WSPSendTo)WSPSendTo
setsockoptWSPSetSockOpt
shutdownWSPShutdown
WSASocket (socket also indirectly maps to WSPSocket)WSPSocket
WSAStringToAddressWSPStringToAddress

It is important to understand how and when WSPStartup is called. You might be compelled to think it is called when an application calls the WSAStartup API. This is not the case. Winsock does not know the type of service provider it needs to use during WSAStartup. Winsock determines which service provider it needs to load based on the address family, socket type, and protocol parameters of a WSASocket call. Therefore, Winsock invokes a service provider only when an application creates a socket through the socket or WSASocket API call. For example, if an application creates a socket using the address family AF_INET and the socket type SOCK_STREAM, Winsock searches and loads an appropriate transport provider that provides TCP/IP functionality. We will describe this loading process in more detail in this chapter's section on installing transport service providers.

Parameters

WSPStartup is the key function used to initialize the functionality of a transport service provider and is defined as

 int WSPStartup( WORD wVersionRequested, LPWSPDATAW lpWSPData, LPWSAPROTOCOL_INFOW lpProtocolInfo, WSPUPCALLTABLE UpcallTable, LPWSPPROC_TABLE lpProcTable ); 

The wVersionRequested parameter receives the latest version of Windows Sockets SPI support that the caller can use. Your service provider should check this value to see whether it can support the requested version. Your provider uses the lpWSPData parameter to return version information about itself through a WSPDATA structure, which is defined as

 typedef struct WSPData { WORD wVersion; WORD wHighVersion; WCHAR szDescription[WSPDESCRIPTION_LEN + 1]; } WSPDATA, FAR * LPWSPDATA; 

In the wVersion field, your provider must return which version of Winsock the caller is expected to use. The wHighVersion parameter must return the highest Winsock version supported by your provider. This is usually the same value as wVersion. (Winsock versioning information is described in detail in Chapter 7.) The szDescription field returns a null-terminated UNICODE string identifying your provider to an SPI client. This field can contain up to 256 characters.

The lpProtocolInfo parameter of WSPStartup is a pointer to a WSAPROTOCOL_ INFOW structure that contains characteristic information about your provider. (Protocol characteristics and the details of this structure are described in "Winsock 2 Protocol Information" in Chapter 5.) The information in WSAPROTOCOL_INFOW is retrieved by Ws2_32.dll from the Winsock 2 service provider catalog that contains property information about service providers. We will further describe Winsock 2 catalog entries in the section on installing transport service providers.

When you develop a layered service provider, you need to treat the lpProtocolInfo parameter in a unique way because it contains information on how your provider is layered between Ws2_32.dll and a base service provider. This parameter is used to determine the next service provider below your provider. (It could be another layered provider or possibly a base provider.) At some point, your provider must load the next service provider by loading the next provider's DLL module and calling the provider's WSPStartup function. The WSAPROTOCOL_INFOW structure pointed to by lpProtocolInfo contains a field, ProtocolChain, that identifies how your service provider is ordered with others on a machine.

The ProtocolChain field is actually a WSAPROTOCOLCHAIN structure that is defined as

 typedef struct _WSAPROTOCOLCHAIN { int ChainLen; DWORD ChainEntries[MAX_PROTOCOL_CHAIN]; } WSAPROTOCOLCHAIN, FAR * LPWSAPROTOCOLCHAIN; 

The ChainLen field identifies how many layers are sandwiched between Ws2_32.dll and a base service provider. (This number also includes the base provider.) If you have a computer that has only one layered service provider above a protocol, such as TCP/IP, this value will be 2. The ChainEntries field is an array of service-provider catalog identification numbers that uniquely identify the layered service providers that are linked together for a particular protocol. We will describe the WSAPROTOCOLCHAIN structure in the section on installing transport service providers later in this chapter. One requirement of a layered service provider is to search the ProtocolChain field of the structure to determine its own location in the array of service providers (by searching for your layer's own catalog entry) and also to determine the next provider in the array. If the next provider is another layer, you must pass the lpProtocolInfo structure unmodified to the next layer's WSPStartup function. If the next layer is the last element in the array (indicating a base provider), your provider must use the base provider's WSAPROTOCOL_INFOW structure to perform a substitution on the lpProtocolInfo structure when calling the base provider's WSPStartup function. Figure 14-3 demonstrates how a layered provider should programmatically manage the lpProtocolInfo structure.

Figure 14-3. Finding the appropriate WSAPROTOCOL_INFOW structure for WSPStartup

 LPWSAPROTOCOL_INFOW ProtocolInfo; LPWSAPROTOCOL_INFOW ProtoInfo = lpProtocolInfo; DWORD ProtocolInfoSize = 0; // Find out how many entries we need to enumerate if (WSCEnumProtocols(NULL, ProtocolInfo, &ProtocolInfoSize, &ErrorCode) == SOCKET_ERROR) { if (ErrorCode != WSAENOBUFS) { return WSAEPROVIDERFAILEDINIT; } } if ((ProtocolInfo = (LPWSAPROTOCOL_INFOW) GlobalAlloc(GPTR, ProtocolInfoSize)) == NULL) { return WSAEPROVIDERFAILEDINIT; } if ((TotalProtocols = WSCEnumProtocols(NULL, ProtocolInfo, &ProtocolInfoSize, &ErrorCode)) == SOCKET_ERROR) { return WSAEPROVIDERFAILEDINIT; } // Find our layered provider's catalog ID entry for (i = 0; i < TotalProtocols; i++) if (memcmp (&ProtocolInfo[i].ProviderId, &ProviderGuid, sizeof (GUID))==0) { gLayerCatId = ProtocolInfo[i].dwCatalogEntryId; break; } // Save our provider's catalog ID entry gChainId = lpProtocolInfo->dwCatalogEntryId; // Find our catalog ID entry in the protocol chain for(j = 0; j < lpProtocolInfo->ProtocolChain.ChainLen; j++) { if (lpProtocolInfo->ProtocolChain.ChainEntries[j] == gLayerCatId) { NextProviderCatId = lpProtocolInfo->ProtocolChain.ChainEntries[j+1]; // Check whether next provider is the base provider if (lpProtocolInfo->ProtocolChain.ChainLen == (j + 2)) { for (i = 0; i < TotalProtocols; i++) if (NextProviderCatId == ProtocolInfo[i].dwCatalogEntryId) { ProtoInfo = &ProtocolInfo[i]; break; } } break; } } // At this point, ProtoInfo will contain the appropriate // WSAPROTOCOL_INFOW structure 

The UpcallTable parameter of WSPStartup receives Ws2_32.dll's SPI upcall dispatch table, which contains pointers to support functions that your provider can use to manage I/O operations between itself and Winsock 2. We will define most of these functions and describe how to use them in this chapter's section on Winsock I/O model support.

The final parameter of WSPStartup—lpProcTable—represents a table of 30 SPI function pointers that your service provider must implement. These functions are listed in Table 14-1. Each SPI function conforms to the parameter specification of its API counterpart with the following two adjustments:

  • Each function supplies a final parameter, lpErrno, which your provider must use to report specific Winsock error code information when your implementation fails. For example, if you're implementing WSPSend and you cannot allocate memory, you might want to return the Winsock error WSAENOBUFS.
  • The SPI functions WSPSend, WSPSendTo, WSPRecv, WSPRecvFrom, and WSPIoctl feature an additional parameter, lpThreadId, which identifies the application thread that invoked the SPI function. As we will see later in this chapter, this feature is useful for supporting completion routines.

A final item worth noting is that several Winsock 1.1 functions, such as send and recv, map directly to their corresponding Winsock 2 functions. We claim that these Winsock 1.1 functions indirectly map to the corresponding SPI function because they actually call a Winsock 2 function featuring similar functionality. For example, the send function actually calls the WSASend function, which maps to WSPSend. In Table 14-1, we noted the functions that indirectly map to SPI functions.

Instance count

In the Winsock specification, applications can call the WSAStartup and WSACleanup functions an unlimited number of times. Your service provider's WSPStartup and WSPCleanup functions will be called as many times as their API equivalents. As a result, your service provider should maintain an instance count for how many times the WSPStartup call is invoked, and you should decrement this count for each WSPCleanup. The purpose of maintaining an instance count is to allow you to streamline the initialization and cleanup procedures of your service provider. For example, if your provider allocates memory to manage internal structures, you can hold on to this memory as long as your instance count is greater than 0. In addition, when your instance count falls to 0, Ws2_32.dll will eventually unload your provider from memory.

Socket Handles

A service provider must return socket handles when an SPI client calls the WSPSocket, WSPAccept, and WSPJoinLeaf functions. Socket handles returned to SPI clients can be either Win32 installable file system (IFS) handles or non-IFS handles. If a service provider returns IFS handles, it is known as an IFS provider; otherwise, it's a non-IFS provider. All of the Microsoft base transport providers are IFS providers.

Winsock is designed to allow Winsock applications to use the Win32 API functions ReadFile and WriteFile to receive and send data on a socket handle. Therefore, you have to consider how socket handles are created in a service provider. If you develop a service provider with the intention of providing service to Winsock applications that call ReadFile and WriteFile on a socket handle, consider developing an IFS provider. However, be aware of I/O limitations if you take this approach.

IFS provider

As we mentioned earlier, transport service providers can be layered or base. If you're developing a base IFS provider, your provider will have a kernel-mode operating system component, and this component enables the Winsock provider to create handles that can be used like file-system handles in ReadFile and WriteFile. Kernel-mode software development is beyond the scope of this book. If you want to know more about how to develop kernel-mode operating system components for Windows, consult the MSDN Device Development Kit (DDK) for more information.

A layered service provider can also become an IFS provider, but only if the layered provider is layered on top of an existing base IFS provider. This involves passing a lower IFS provider's socket handles—retrieved in your layered provider—directly up to your SPI client. Passing socket handles directly up from a lower provider limits a layered provider's functionality in the following ways:

  • The layered provider's WSPSend and WSPRecv functions will not be called if the ReadFile and WriteFile functions are called on a socket. These functions will bypass a layered provider and directly invoke the base IFS provider's implementation.
  • A layered provider will not be able to postprocess overlapped I/O requests that are submitted to a completion port. Postprocessing of the completion port completely bypasses a layered provider.

If your layered provider intends to monitor all I/O passed through the provider, you will have to develop a non-IFS layered provider, which we will discuss later in this chapter.

Whenever an IFS provider (either layered or base) creates a new socket descriptor, the provider is required to call WPUModifyIFSHandle prior to supplying the new handle to an SPI client. This allows the Winsock Ws2_32.dll to greatly streamline the process of identifying the IFS service provider associated with a given socket when Win32 APIs such as ReadFile and WriteFile perform I/O on a socket. WPUModifyIFSHandle is defined as

 SOCKET WPUModifyIFSHandle( DWORD dwCatalogEntryId, SOCKET ProposedHandle, LPINT lpErrno ); 

The dwCatalogEntryId field identifies the catalog ID of your service provider. The ProposedHandle parameter represents an IFS handle allocated by your service provider (in the case of a base provider). If you're developing a layered IFS provider, this handle will be passed up from a lower provider. The lpErrno parameter receives specific Winsock error code information if this function fails with the return value INVALID_SOCKET.

Non-IFS provider

If you're developing a layered provider and intend to monitor every read and write operation that occurs on a socket, you will have to develop a non-IFS provider. Non-IFS providers create socket handles by using the upcall WPUCreateSocketHandle. Socket handles created by WPUCreateSocketHandle are similar to IFS provider handles in that they also allow Winsock applications to use ReadFile and WriteFile functions on a socket. However, there is a significant I/O performance penalty associated with these two functions because the Winsock 2 architecture has to perform I/O redirection to a service provider's WSPRecv and WSPSend functions, respectively. WPUCreateSocketHandle is defined as

 SOCKET WPUCreateSocketHandle( DWORD dwCatalogEntryId, DWORD dwContext, LPINT lpErrno ); 

The dwCatalogEntryId identifies the catalog ID of your service provider. The dwContext parameter allows you to associate provider data with a socket descriptor. Note that dwContext gives you a lot of freedom in terms of the information you can associate with a socket descriptor. In the LSP sample on the companion CD-ROM, we take advantage of this field by storing the byte count of send and receive data. Winsock provides an upcall WPUQuerySocketHandleContext that can be used to retrieve associated socket provider data saved in dwContext, and this upcall is defined as

 int WPUQuerySocketHandleContext( SOCKET s, LPDWORD lpContext, LPINT lpErrno ); 

The s parameter represents the socket handle passed down from an SPI client (originally created from WPUCreateSocketHandle) that you want to retrieve socket provider data. The lpContext parameter receives the provider data originally passed in WPUCreateSocketHandle. The lpErrno parameter in both functions receives a specific Winsock error code if these functions fail. If WPUCreateSocketHandle fails, it returns INVALID_SOCKET; and if WPUQuerySocketHandleContext fails, it returns SOCKET_ERROR.

Winsock I/O Model Support

As you learned in Chapter 8, Winsock features several I/O models that can be used to manage I/O on a socket. From a service provider's point of view, each model requires using some of the SPI upcall functions provided by Ws2_32.dll that are available from the UpcallTable parameter in WSPStartup, mentioned earlier. If you're developing a simple IFS layered provider, the principles of each I/O model described in the following sections don't apply—you simply rely on the lower provider to manage all of the I/O for each model. The principles outlined in these sections do apply to any other type of provider. We'll focus our discussion on development aspects of a non-IFS layered service provider.

Blocking and nonblocking

Blocking I/O is the simplest form of I/O in Winsock 2. Any I/O operation with a blocking socket won't return until the operation has completed. Therefore, any thread can execute only one I/O operation at a time. For example, when an SPI client calls the WSPRecv function in a blocking fashion, your provider only needs to relay the call directly to the next provider's WSPRecv call. Your provider's WSPRecv function will return only when the next provider's WSPRecv call completes.

Even though blocking I/O is easy to implement, you still have to consider backward compatibility with Winsock 1.1 blocking hooks. The WSASetBlockingCall and WSACancelBlocking API calls have been removed from the Winsock 2 API specification. However, WSPCancelBlockingHook can still be called by Ws2_32.dll if a Winsock 1.1 application calls the WSASetBlockingHook and WSACancelBlockingCall functions. In a layered service provider, you can simply relay the WSPCancelBlockingHook call to the base provider's call. If you're implementing a base provider and a blocking call is in progress, you must implement a mechanism to call the WPUQueryBlockingCallback function periodically. WPUQueryBlockingCallback is defined as

 int WPUQueryBlockingCallback( DWORD dwCatalogEntryId, LPBLOCKINGCALLBACK FAR *lplpfnCallback, LPDWORD lpdwContext, LPINT lpErrno ); 

The dwCatalogEntryId parameter receives the catalog entry ID of your provider as described in the WSPStartup function. The lplpfnCallback parameter is a function pointer to the application's blocking hook function that you must call periodically to prevent the application's blocking calls from truly blocking. The callback function has the following form:

 typedef BOOL (CALLBACK FAR * LPBLOCKINGCALLBACK)( DWORD dwContext ); 

When your provider periodically invokes lplpfnCallback, pass the value of the lpdwContext parameter to the callback function's dwContext parameter. The final parameter of WPUQueryBlockingCallback, lpErrno, returns Winsock error code information if this function returns SOCKET_ERROR.

select

The select I/O model requires that a provider manage the fd_set structures for the parameters readfds, writefds, and exceptfds in the WSPSelect function, which is defined as

 int WSPSelect( int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout, LPINT lpErrno ); 

Essentially, the fd_set data type represents a collection of sockets. When an SPI client calls your provider using WSPSelect, it passes socket handles to one or more of these sets. Your provider is responsible for determining when network activities have occurred on each of the sockets listed.

For a non-IFS layered provider, this requires creating three fd_set data fields and mapping the SPI client's socket handles to the lower provider's socket handles in each set. Once all the sets are set up, your provider calls WSPSelect on the lower provider. When the lower provider completes, your provider has to determine which sockets have events pending in each of the fd_set fields. There is a useful upcall WPUFDIsSet that determines which lower provider sockets are set. This upcall is similar to the FD_ISSET macro discussed in Chapter 8 and is defined as

 int WPUFDIsSet( SOCKET s, FD_SET FAR *set ); 

The s parameter represents the socket you're looking for in a set. The set parameter is an actual set of socket descriptors. Since your provider will check the contents of each set passed to the lower provider, your provider should save the socket mappings from the upper provider to the lower provider. When the lower provider completes the WSPSelect call, you can easily map back which sockets have I/O pending for the upper provider.

Once you have determined which lower provider sockets have network events pending, you have to update the originating fd_set sets that were passed in from the originating SPI client. The Ws2spi.h file provides the three macros—FD_CLR, FD_SET, and FD_ZERO—that can be used to manage the originating sets. These macros are described in Chapter 8. Figure 14-4 demonstrates one possibility for implementing WSPSelect.

Figure 14-4. WSPSelect implementation details

 int WSPAPI WSPSelect( int nfds, fd_set FAR * readfds, fd_set FAR * writefds, fd_set FAR * exceptfds, const struct timeval FAR * timeout, LPINT lpErrno) { SOCK_INFO *SocketContext; u_int i; u_int count; int Ret; int HandleCount; // Build an Upper and Lower provider socket mapping table struct { SOCKET ClientSocket; SOCKET ProvSocket; } Read[FD_SETSIZE], Write[FD_SETSIZE], Except[FD_SETSIZE]; fd_set ReadFds, WriteFds, ExceptFds; // Build the ReadFds set for the lower provider if (readfds) { FD_ZERO(&ReadFds); for (i = 0; i < readfds->fd_count; i++) { if (MainUpCallTable.lpWPUQuerySocketHandleContext( (Read[i].ClientSocket = readfds->fd_array[i]), (LPDWORD) &SocketContext, lpErrno) == SOCKET_ERROR) return SOCKET_ERROR; FD_SET((Read[i].ProvSocket = SocketContext->ProviderSocket), &ReadFds); } } // Build the WriteFds set for the lower provider. // This is just like the ReadFds set above. ... // Build the ExceptFds set for the lower provider. // This is also like the ReadFds set above. ... // Call the lower provider's WSPSelect Ret = NextProcTable.lpWSPSelect(nfds, (readfds ? &ReadFds : NULL), (writefds ? &WriteFds : NULL), (exceptfds ? &ExceptFds : NULL), timeout, lpErrno); if (Ret != SOCKET_ERROR) { HandleCount = Ret; // Set up calling provider's readfds set if (readfds) { count = readfds->fd_count; FD_ZERO(readfds); for(i = 0; (i < count) && HandleCount; i++) { if (MainUpCallTable.lpWPUFDIsSet( Read[i].ProvSocket, &ReadFds)) { FD_SET(Read[i].ClientSocket, readfds); HandleCount--; } } } // Set up calling provider's writefds set. // This is just like the readfds set above. ... // Set up calling provider's exceptfds set. // This is also like the readfds set above. ... } return Ret; } 

WSAAsyncSelect

The WSAAsyncSelect I/O model involves managing Windows message-based notification of network events on a socket. SPI clients use this model by calling the WSPAsyncSelect function, which is defined as

 int WSPAsyncSelect( SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent, LPINT lpErrno ); 

The s parameter represents the SPI client's socket that expects windows message notification of network events. The hWnd parameter identifies the window handle that should receive the message defined in the wMsg parameter when a network event (defined in the lEvent parameter) occurs on the socket s. The lpErrno parameter receives a Winsock error code if your implementation of this function returns SOCKET_ERROR. The network events your provider must support (identified in the lEvent parameter) are described in Table 8-3 in "The WSAAsyncSelect Method" section.

When an SPI client calls WSPAsyncSelect, your provider is responsible for notifying the SPI client when network events occur on a socket using the client's supplied window handle and the client's supplied window message that is passed through the WSPAsyncSelect call. Your provider can notify an SPI client of network events by calling WPUPostMessage, which is defined as

 BOOL WPUPostMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam ); 

Your provider uses the hWnd parameter to identify the SPI client's window handle to be signaled. The Msg parameter identifies the user-defined message that was originally passed into the wMsg parameter of WSPAsyncSelect. The wParam parameter accepts the socket handle where a network event has occurred. The final parameter, lParam, takes two pieces of information. The low word of lParam accepts the network event that has occurred. The high word of lParam accepts a Winsock error code if an error has occurred in your provider implementation with respect to the network event identified in the low word of lParam.

If you're developing a non-IFS layered service provider, your provider is a client of a lower provider's WSPAsyncSelect function. As a result, your provider is required to translate an SPI client's socket handles to your provider's socket handles using WPUQuerySocketHandleContext before calling the lower provider's WSPAsyncSelect function. Since a service provider is required to inform an SPI client's window of network events using WPUPostMessage, your provider must intercept these window messages from the lower provider. This is a requirement because WPUPostMessage passes the lower provider's socket handle back in the high word of lParam. As a result, your provider must translate the socket handle in the high word of lParam to the SPI client's socket handle that was used in the originating WSPAsyncSelect call.

The best way to manage the interception of window messages from a lower provider is to establish a worker thread that creates a hidden window to manage network event window messages. When your provider calls WSPAsyncSelect on the lower provider, you simply pass the worker window handle to the lower provider's WSPAsyncSelect call. From there, as your provider's worker window receives network event window messages from the lower provider, your provider can inform the SPI client of the network events using WPUPostMessage.

WSAEventSelect

The WSAEventSelect I/O model involves signaling event objects when network events occur on a socket. SPI clients use this model by calling the WSPEventSelect function, which is defined as

 int WSPEventSelect( SOCKET s, WSAEVENT hEventObject, long lNetworkEvents, LPINT lpErrno ); 

The s parameter represents the SPI client's socket that expects notification of network events. The hEventObject parameter is a WSAEVENT object handle that your provider signals when network events specified in lNetworkEvents occur on the socket s. The lpErrno parameter receives a Winsock error code if your implementation of this function returns SOCKET_ERROR. The network events that your provider must support (identified in the lNetworkEvents parameter) are the same as those specified in the WSAAsyncSelect I/O model.

Implementing WSPEventSelect in a non-IFS layered provider is actually trivial. As an SPI client passes down an event object, your provider only needs to translate the SPI client's socket handle to the lower provider's socket handle using WPUQuerySocketHandleContext. After socket handle translation, you can call the lower provider's WSPEventSelect function using the event object directly from the SPI client. When I/O operations occur on the SPI client's event object, the lower provider directly signals the event object, and the SPI client is notified of the arrival of the network events. When the lower provider signals the event object, no socket handle is returned to the SPI client upon event completion. Therefore, your provider does not have to perform socket handle translation for the SPI client. This is unlike the WSAAsyncSelect model described earlier, in which your provider has to perform socket handle translation on a completed network event because a socket handle is passed in the window message sent from the lower provider.

If you're developing a base provider, your provider is responsible for notifying the SPI client when network events specified in lNetworkEvents occur on a socket using the client's supplied event object that is passed through the WSPEventSelect call. Your provider can notify an SPI client of network events by using the upcall WPUSetEvent, which is defined as

 BOOL WPUSetEvent ( WSAEVENT hEvent, LPINT lpErrno ); 

The hEvent parameter represents the event object handle your provider must signal that is passed in from WSPEventSelect. The lpErrno parameter returns with a specific Winsock error code if this function returns FALSE.

Overlapped I/O

The overlapped I/O model requires that your provider implement an overlapped I/O manager to service both event object_based and completion routine_based overlapped I/O requests. The following SPI functions use Win32 overlapped I/O in Winsock:

  • WSPSend
  • WSPSendTo
  • WSPRecv
  • WSPRecvFrom
  • WSPIoctl

Each function features three common parameters: an optional pointer to a WSAOVERLAPPED structure, an optional pointer to a WSAOVERLAPPED_COMPLETION_ROUTINE function, and a pointer to a WSATHREADID structure identifying the application thread performing the call.

The WSAOVERLAPPED structure is key to how a service provider communicates with an SPI client during overlapped I/O operations and is defined as

 typedef struct _WSAOVERLAPPED { DWORD Internal; DWORD InternalHigh; DWORD Offset; DWORD OffsetHigh; WSAEVENT hEvent; } WSAOVERLAPPED, FAR * LPWSAOVERLAPPED; 

Your service provider's overlapped I/O manager is responsible for managing the Internal field of an SPI client's WSAOVERLAPPED structure. At the start of overlapped processing, your service provider must set the Internal field to WSS_OPERATION_IN_PROGRESS. This is important because if the SPI client calls WSPGetOverlappedResult while your provider is servicing a pending overlapped operation, the WSS_OPERATION_IN_PROGRESS value can be used in your provider's implementation of WSPGetOverlappedResult to determine whether an overlapped operation is still in progress.

When an I/O operation is complete, your provider sets the OffsetHigh and Offset fields. OffsetHigh is set to a Winsock error code resulting from the operation, and Offset should be set to the flags resulting from a WSPRecv and WSPRecvFrom I/O operation. After setting these fields, your provider notifies the SPI client of the completed overlapped request through either an event object or a completion routine, depending on how the above I/O functions used the optional WSAOVERLAPPED structure.

Events

In event-based overlapped I/O, an SPI client invokes one of the I/O functions mentioned earlier with a WSAOVERLAPPED structure containing an event object. WSAOVERLAPPED_COMPLETION_ROUTINE must also be set to the NULL value. Your service provider is responsible for managing the overlapped I/O request. When it completes, your provider must alert the caller's thread of the completed I/O request by calling the upcall WPUCompleteOverlappedRequest, which is defined as

 WSAEVENT WPUCompleteOverlappedRequest( SOCKET s, LPWSAOVERLAPPED lpOverlapped, DWORD dwError, DWORD cbTransferred, LPINT lpErrno ); 

The s parameter and the lpOverlapped parameter represent the originating socket and the WSAOVERLAPPED structure that were originally sent down from the client. Your provider should set the dwError parameter to the completion status of the overlapped I/O request and the cbTransferred parameter to the number of bytes that were transferred during the overlapped operation. The final parameter, lpErrno, reports a Winsock error code if this function call returns SOCKET_ERROR. When WPUCompleteOverlappedRequest completes, it sets two fields in the SPI client's WSAOVERLAPPED structure: InternalHigh is set to the cbTransferred byte count, and Internal is set to a value other than WSS_OPERATION_IN_PROGRESS.

In event-based overlapped I/O, an SPI client eventually calls WSPGetOverlappedResult to retrieve the result of a completed overlapped request, which is defined as

 BOOL WSPGetOverlappedResult ( SOCKET s, LPWSAOVERLAPPED lpOverlapped, LPDWORD lpcbTransfer, BOOL fWait, LPDWORD lpdwFlags, LPINT lpErrno ); 

When WSPGetOverlappedResult is called, your service provider has to report back the operating state of the original overlapped request. As we mentioned earlier, your provider is responsible for managing the Internal, InternalHigh, Offset, and OffsetHigh fields in the SPI client's WSAOVERLAPPED structure. When WSPGetOverlappedResult is called, your provider should first check the Internal field of the SPI client's WSAOVERLAPPED structure. If the Internal field is set to WSS_OPERATION_IN_PROGRESS, your provider is still processing an overlapped request. If the fWait parameter of WSPGetOverlappedResult is set to TRUE, your provider must wait for the overlapped operation to complete by waiting on the event handle passed in the SPI client's WSAOVERLAPPED structure before returning the results. If the fWait parameter is set to FALSE, your provider should return the Winsock error WSA_IO_INCOMPLETE. Once the overlapped operation is complete or completes after waiting for results, your provider should set the parameters of WSPGetOverlappedResult as follows:

  • lpcbTransfer set to the value of the InternalHigh field from the WSAOVERLAPPED structure, which reports the number of bytes transferred by a send or receive operation
  • lpdwFlags set to the value of the Offset field from the WSAOVERLAPPED structure, which reports any flags resulting from a WSPRecv or a WSPRecvFrom operation
  • lpErrno set to the value of the OffsetHigh field from the WSAOVERLAPPED structure, which reports a resulting error code

Figure 14-5 demonstrates one possible way to implement WSPGetOverlappedResult.

Figure 14-5. WSPGetOverlappedResult implementation details

 BOOL WSPAPI WSPGetOverlappedResult( SOCKET s, LPWSAOVERLAPPED lpOverlapped, LPDWORD lpcbTransfer, BOOL fWait, LPDWORD lpdwFlags, LPINT lpErrno) { DWORD Ret; if (lpOverlapped->Internal != WSS_OPERATION_IN_PROGRESS) { *lpcbTransfer = lpOverlapped->InternalHigh; *lpdwFlags = lpOverlapped->Offset; *lpErrno = lpOverlapped->OffsetHigh; return(lpOverlapped->OffsetHigh == 0 ? TRUE : FALSE); } else { if (fWait) { Ret = WaitForSingleObject(lpOverlapped->hEvent, INFINITE); if ((Ret == WAIT_OBJECT_0) && (lpOverlapped->Internal != WSS_OPERATION_IN_PROGRESS)) { *lpcbTransfer = lpOverlapped->InternalHigh; *lpdwFlags = lpOverlapped->Offset; *lpErrno = lpOverlapped->OffsetHigh; return(lpOverlapped->OffsetHigh == 0 ? TRUE : FALSE); } else *lpErrno = WSASYSCALLFAILURE; } else *lpErrno = WSA_IO_INCOMPLETE; } return FALSE; } 

Completion routines

In completion routine_based overlapped I/O, an SPI client invokes one of the I/O functions mentioned earlier with a WSAOVERLAPPED structure and a WSAOVERLAPPED_COMPLETION_ROUTINE pointer. Your service provider is responsible for managing the overlapped I/O request. When the request completes, your provider must alert the caller's thread of the completed I/O by using the Win32 asynchronous procedure call (APC) I/O mechanism. The APC mechanism requires the caller's thread to be in an alertable wait state (described in Chapter 8). When your service provider is finished servicing a completion routine_based overlapped request, it must alert the SPI client of the completion by calling the upcall WPUQueueApc, which is defined as

 int WPUQueueApc( LPWSATHREADID lpThreadId, LPWSAUSERAPC lpfnUserApc, DWORD dwContext, LPINT lpErrno ); 

The lpThreadId parameter represents the SPI client's WSATHREADID structure, which is passed from an originating I/O call that supplies a completion routine. The lpfnUserApc parameter represents a pointer to a WSAUSERAPC function that serves as an intermediate function your provider must supply for callback to the SPI client. As an intermediate function, it must call the SPI client's WSAOVERLAPPED_ COMPLETION_ROUTINE supplied in the original overlapped call. A WSAUSERAPC intermediate function prototype is defined as

 typedef void (CALLBACK FAR * LPWSAUSERAPC)(DWORD dwContext); 

Notice that this function definition contains only one parameter: dwContext. When the SPI client calls this callback function, the dwContext parameter contains the information that was originally passed in the dwContext parameter of WPUQueueApc. Essentially, dwContext allows you to pass a data structure containing any information elements that you will need when you call the SPI client's WSAOVERLAPPED_ COMPLETION_ROUTINE, which is described in Chapter 8 as

 void CALLBACK CompletionROUTINE( IN DWORD dwError, IN DWORD cbTransferred, IN LPWSAOVERLAPPED lpOverlapped, IN DWORD dwFlags ); 

Your provider should pass the following information through the dwContext parameter of WPUQueueApc:

  • The status of the overlapped operation as a Winsock error code
  • The number of bytes transferred through the overlapped operation
  • The caller's WSAOVERLAPPED structure
  • The caller's flags passed into the I/O call

With this information, your provider can successfully call the SPI client's WSAOVERLAPPED_COMPLETION_ROUTINE from your intermediate completion routine.

Completion ports

The implementation of the completion port I/O model in Winsock 2 resides in the Ws2_32.dll module. As we mentioned in Chapter 8, the completion port model relies on the overlapped I/O model using event-based overlapped I/O. Therefore, no special considerations are necessary for your service provider to manage the completion port model.

Managing overlapped I/O

When an SPI client calls your layered provider using any of the overlapped I/O models mentioned above, your provider's overlapped manager should call the lower provider using either the event-based or the completion port overlapped I/O method described in Chapter 8. If your provider is running on Windows NT or Windows 2000, we recommend using completion ports to perform overlapped I/O on the lower provider. On Windows 95 and Windows 98, your only choice is to use event-based overlapped I/O. The following three upcalls are available to your provider for working with overlapped events:

 WSAEVENT WPUCreateEvent(LPINT lpErrno); BOOL WPUResetEvent(WSAEVENT hEvent, LPINT lpErrno); BOOL WPUCloseEvent(WSAEVENT hEvent, LPINT lpErrno); 

The WPUCreateEvent function creates and returns an event object. This function is exactly like the WSACreateEvent function described in Chapter 8: it creates an event object that is in manual reset mode. If WPUCreateEvent fails to create an event object, NULL is returned and lpErrno will contain a specific Winsock error code. The WSPResetEvent function is just like the WSAResetEvent function: it resets an event object (parameter hEvent) from the signaled state to the nonsignaled state. The WPUCloseEvent function is just like WSACloseEvent and frees all operating resources associated with an event object handle.

On the companion CD, our LSP example uses event-based overlapped I/O to manage all overlapped I/O activities for an SPI client. This allows the LSP sample to function on Windows 2000, Windows NT, Windows 98, and Windows 95. However, it is important to realize this example has only one thread for servicing overlapped I/O operations, limiting our provider to servicing no more than WSA_MAXIMUM_WAIT_EVENTS (64) event objects at a time using event-based overlapped I/O (described in Chapter 8). If your provider expects to service more than 64 event objects, you can create more servicing threads to service more event objects. If you're developing your provider for Windows NT and Windows 2000, we recommend using completion ports instead of event-based overlapped I/O to avoid this limitation.

Extension Functions

The Winsock library Mswsock.lib provides applications with extension functions that enhance Winsock functionality. Table 14-2 defines the extension functions currently supported.

Table 14-2. Winsock extension functions

Extension FunctionGUID
AcceptExWSAID_ACCEPTEX
GetAcceptExSockaddrsWSAID_GETACCEPTEXSOCKADDRS
TransmitFileWSAID_TRANSMITFILE
WSARecvExDoes not have an associated GUID

When an application linked to Mswsock.lib uses AcceptEx, GetAcceptExSockaddrs, and TransmitFile, it implicitly calls your provider's WSPIoctl function using the SIO_GET_EXTENSION_FUNCTION_POINTER option. WSPIoctl is defined as

 int WSPIoctl( SOCKET s, DWORD dwIoControlCode, LPVOID lpvInBuffer, DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer, LPDWORD lpcbBytesReturned, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, LPWSATHREADID lpThreadId, LPINT lpErrno ); 

When the call occurs, the dwIoControlCode parameter is set to the SIO_GET_EXTENSION_FUNCTION_POINTER value. The lpvInBuffer parameter contains a pointer to a globally unique identifier (GUID) that identifies which extension function the Mswsock.lib is looking for. (The GUID values listed in Table 14-2 define the currently supported extension functions.) If this GUID value matches any of the defined values, your provider must return a function pointer of your provider's implementation of the extension function through lpvOutBuffer. The remaining parameters do not apply directly to managing extension functions in the SPI.

We noted that the WSARecvEx function does not have a GUID in Table 14-2. This is because WSARecvEx does not invoke WSPIoctl. Instead, it directly calls WSARecv. As a result, your provider cannot directly monitor the WSARecvEx extension function.

Installing Transport Service Providers

Installing transport service providers involves developing a simple application to insert a layered or a base provider into the Winsock 2 catalog of service providers. How a transport provider is installed determines whether it is a layered provider or a base provider. The installation program simply configures your transport provider in the Winsock 2 system configuration database, which is a catalog of all installed service providers. The configuration database lets Winsock 2 know that your service provider exists and defines the type of service you're providing. Winsock 2 uses the database to determine which transport service providers it needs to load when a Winsock application creates a socket. Ws2_32.dll searches the database for the first provider that matches socket input parameters of the socket and WSASocket API calls, such as address family, type of socket, and protocol. Once an appropriate matching entry is found, Ws2_32.dll loads the appropriate service provider DLL that is specified in the catalog.

Essentially four SPI configuration functions are needed to successfully install and manage a service provider entry into the Winsock 2 service provider database. Each function begins with the WSC prefix:

  • WSCEnumProtocols
  • WSCInstallProvider
  • WSCWriteProviderOrder
  • WSCDeInstallProvider

These functions query and manipulate the database using a WSAPROTOCOL_INFOW structure, which is described in Chapter 5 in "Winsock 2 Protocol Information." For the purpose of installing a transport service provider, we are primarily concerned with the ProviderId, dwCatalogEntryId, and ProtocolChain fields of this structure. The ProviderId field is a GUID that uniquely allows you to define and install a provider on any system. The dwCatalogEntryId field simply identifies each WSAPROTOCOL_INFOW catalog entry structure in the database with a unique numeric value. The ProtocolChain field determines whether a WSAPROTOCOL_INFOW structure is a catalog entry for a base provider, a layered provider, or a provider protocol chain. The ProtocolChain field is a WSAPROTOCOLCHAIN structure that is defined as

 typedef struct { int ChainLen; DWORD ChainEntries[MAX_PROTOCOL_CHAIN]; } WSAPROTOCOLCHAIN, FAR * LPWSAPROTOCOLCHAIN; 

The ChainLen field determines whether a catalog entry represents a layered provider (ChainLen = 0), a base provider (ChainLen = 1), or a protocol chain (ChainLen > 1). A protocol chain is a special catalog entry that defines how to position a layered service provider between Winsock and other service providers. (See Figure 14-2.) Layered and base providers have only a single catalog entry per provider in the database. The final field, ChainEntries, is an array of catalog IDs used to describe the order in which to load service providers in a protocol chain. When Ws2_32.dll searches the catalog for an appropriate service provider during socket creation, it looks only for protocol chain and base provider catalog entries. Layered provider catalog entries (where ChainLen is 0) are ignored by Ws2_32.dll and exist only to identify a layered provider to a protocol chain in protocol chain catalog entries.

Base provider installation

To install a base provider, you have to create a WSAPROTOCOL_INFOW catalog entry structure that represents the base provider. This requires populating most of the fields in this structure with protocol attribute information describing the base provider. Remember to set the ProtocolChain structure's ChainLen field to the value 1. Once the structure is defined you need to install it in the catalog using the WSCInstallProvider function, which is defined as

 int WSCInstallProvider( const LPGUID lpProviderId, const LPWSTR lpszProviderDllPath, const LPWSAPROTOCOL_INFOW lpProtocolInfoList, DWORD dwNumberOfEntries, LPINT lpErrno ); 

The lpProviderId parameter is a GUID that allows you to identify a provider to the Winsock catalog. The lpszProviderDllPath parameter is a string containing the load path to the provider's DLL. The string can include environment variables such as %SystemRoot%. The lpProtocolInfoList parameter represents an array of WSAPROTOCOL_INFOW data structures to install in the catalog. For the purposes of installing a base provider, you can simply assign the WSAPROTOCOL_INFOW structure to the first element of the array. The dwNumberOfEntries parameter contains the number of entries in the lpProtocolInfoList array. The lpErrno parameter contains specific error code information if this function fails with SOCKET_ERROR.

Layered provider installation

If you're installing a layered service provider, you need to create two WSAPROTOCOL_INFOW catalog entry structures. One will represent your layered provider (for example, the protocol chain length equals 0), and the other will represent a protocol chain (for example, the protocol chain length is greater than 1) linking your layered provider to a base provider. These two structures should be initialized with properties of an existing service provider's WSAPROTOCOL_INFOW catalog entry structure that can be retrieved by calling WSCEnumProtocols, which is defined as

 int WSCEnumProtocols( LPINT lpiProtocols, LPWSAPROTOCOL_INFOW lpProtocolBuffer, LPDWORD lpdwBufferLength, LPINT lpErrno ); 

The lpiProtocols parameter is an optional array of values. If lpiProtocols is NULL, information on all available protocols is returned; otherwise, information is retrieved only for those protocols listed in the array. The lpProtocolBuffer parameter is an application-supplied buffer that is filled in with WSAPROTOCOL_INFOW structures from the Winsock 2 catalog. The lpdwBufferLength parameter on input is the count of bytes in the lpProtocolBuffer buffer passed to WSCEnumProtocols. On output, it receives the minimum buffer size that can be passed to WSCEnumProtocols to retrieve all the requested information. The lpErrno parameter contains specific error information if this function fails with SOCKET_ERROR. Once you have the catalog entry of the provider that you're going to layer over, you can copy the properties of the provider's WSAPROTOCOL_INFOW into your newly created structures.

After initialization, you first need to install your layered provider catalog entry using WSCInstallProvider and then retrieve the catalog ID that gets assigned to this structure after installation by enumerating the catalog entries using WSCEnumProtocols. The catalog entry can then be used in setting up a protocol chain catalog entry linking your layered provider to another provider. Next WSCInstallProvider is called to install the chained provider. This process is illustrated in the following pseudo-code:

 ... WSAPROTOCOL_INFOW LayeredProtocolInfoBuff, ProtocolChainProtoInfo, BaseProtocolInfoBuff; ... // Retrieve BaseProtocolInfoBuff using WSCEnumProtocols() memcpy (&LayeredProtocolInfoBuff , &BaseProtocolInfoBuff, sizeof(WSAPROTOCOL_INFO)); LayeredProtocolInfoBuff.dwProviderFlags = PFL_HIDDEN; LayeredProtocolInfoBuff.ProviderId = LayeredProviderGuid; // This entry will be filled in by the system LayeredProtocolInfoBuff.dwCatalogEntryId = 0; LayeredProtocolInfoBuff.ProtocolChain.ChainLen = LAYERED_PROTOCOL; WSCInstallProvider(&LayeredProviderGuid, L"lsp.dll", &LayeredProtocolInfoBuff, 1, & install_error); // Determine the catalog ID of the layered provider // using the WSCEnumProtocols() function for (i = 0; i < TotalProtocols; i++) if (memcmp (&ProtocolInfo[i].ProviderId, &ProviderGuid, sizeof (GUID))==0) { LayeredCatalogId = ProtocolInfo[i].dwCatalogEntryId; break; } Memcpy(&ProtocolChainProtoInfo, &BaseProtocolInfoBuff, sizeof(WSAPROTOCOL_INFO)); ProtocolChainProtoInfo.ProtocolChain.ChainLen = 2; ProtocolChainProtoInfo.ProtocolChain.ChainEntries[0] = LayeredProvideProtocolInfo.dwCatalogEntryId; ProtocolChainProtoInfo.ProtocolChain.ChainEntries[1] = BaseProtocolInfoBuff.dwCatalogEntryId; WSCInstallProvider( &ChainedProviderGuid, L"lsp.dll", // lpszProviderDllPath &ProtocolChainProtoInfo, // lpProtocolInfoList 1, // dwNumberOfEntries &install_error // lpErrno ); 

Notice that the PFL_HIDDEN flag is specified in the WSAPROTOCOL_INFOW structure for the layered provider in the above pseudo-code. This flag ensures that the WSAEnumProtocols function (described in Chapter 5) does not include the catalog for the layered provider in its returned buffer.

Another important flag your installation program should manage is XP1_IFS_ HANDLES. Any non-IFS service provider that uses WPUCreateSocketHandle to create its socket handles should not set the XP1_IFS_HANDLES flag in its WSAPROTOCOL_ INFOW structure. Winsock applications should take the absence of the XP1_ IFS_HANDLES flag as a clue to avoid the use of ReadFile and WriteFile functions because of the performance penalty mentioned previously.

Ordering providers

Once a service provider is installed on a system, you must consider how Winsock 2 searches the database for service providers. Most Winsock applications specify which protocol they need through the parameters of a call to the socket and WSASocket functions. For example, if your application creates a socket using the address family AF_INET and the socket type SOCK_STREAM, Winsock 2 searches for the default TCP/IP protocol chain or base provider catalog entry in the database that provides this functionality. When you install a service provider using WSCInstallProvider, the catalog entry automatically becomes the last entry in the database. To make the service provider the default TCP/IP provider, you must reorder the providers in the database and place the protocol chain catalog entry ahead of other TCP/IP providers by calling WSCWriteProviderOrder, which is defined as

 int WSCWriteProviderOrder( LPDWORD lpwdCatalogEntryId, DWORD dwNumberOfEntries ); 

The lpwdCatalogEntryId parameter accepts an array of catalog IDs that identify how the catalog should be ordered. You can retrieve the catalog IDs in the catalog by calling WSCEnumProtocols, as described earlier. The dwNumberOfEntries parameter is a count of how many catalog entries are in your array. This function returns ERROR_SUCCESS (0) if it is successful; otherwise, it returns a Winsock error code.

The WSCWriteProviderOrder function is not part of the Ws2_32.dll library. To use this function, your application must link to Sporder.lib. Also, the associated Sporder.dll module is not part of the Windows operating system. The support DLL can be found in the Microsoft Developer Network (MSDN) library. If you plan to use it, you must distribute it with your installation application. The MSDN library also provides a convenient software utility named Sporder.exe that allows you to view and reorder catalog entries in the Winsock 2 database. Figure 14-6 offers a quick look (using Sporder.exe) at the Winsock 2 configuration after installing a layered provider on a Windows 2000 computer.

click to view at full size.

Figure 14-6. Sporder.exe

Removing a service provider

Removing a service provider from the Winsock 2 catalog is easy. The main task is calling the WSCDeinstallProvider function, which is defined as

 int WSCDeinstallProvider( LPGUID lpProviderId, LPINT lpErrno ); 

The lpProviderId parameter represents the GUID of the service provider that you're removing. The lpErrno parameter receives a specific Winsock error code if the function returns SOCKET_ERROR.

Troubles with Installing Layered Providers

Layered service providers have tremendous potential for value-added networking services. However, the current Winsock 2 specification does not answer an important question: how can a layered service provider know where to insert itself in a protocol chain if it finds another layered provider installed? For example, if you want to install a data encryption provider on a system that already has a URL filtering provider, obviously the data encryption provider needs to be inserted below the filtering provider in the existing protocol chain. But the problem is that the provider installation program has no way to find out what type of service an existing provider provides and therefore does not know where to insert itself into a protocol chain. This is not a big concern for a controlled networking environment in which administrators decide which providers to install and in what order to install them. But the widespread success of layered service providers is, for all intents and purposes, prohibited because the only safe installation is to install a layered service provider directly over a base provider and make the new chain the default provider for the protocol. Such an approach guarantees the service of the new provider but removes the existing layered provider as the default provider chain.

Besides the problem of layered service provider ordering in protocol chains, there's another related issue that's not addressed in the Winsock 2 specification: how existing layered providers can protect themselves from changes in the chaining or be notified when they occur. This concern is not as grave as the first. In practice, if a layered provider protocol chain is not to be modified, the layered provider developer can hardcode the chain order within the WSPStartup function and install the provider as a base provider by specifying 1 in the ProtocolChain.ChainLen member of the LSP's WSAPROTOCOL_INFOW structure.

You should consider one important point when removing a service provider. There is always the possibility that a layered service provider includes your service provider's catalog ID in its protocol chain. If this is the case, you should remove your catalog ID from any protocol chains that reference your provider.



Network Programming for Microsoft Windows
Linux Server Hacks, Volume Two: Tips & Tools for Connecting, Monitoring, and Troubleshooting
ISBN: 735615799
EAN: 2147483647
Year: 1998
Pages: 159

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net