// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (C) 2004 Microsoft Corporation. All Rights Reserved. // // Module Name: spi.cpp // // Description: // // This sample illustrates how to develop an IFS Layered Service Provider (LSP) which // functions as a TCP proxy client. Because this LSP only needs to intercept a handful // of functions like WSPSocket and WSPConnect as well as the fact it does not need to // intercept operation completion (e.g. intercept a WSPRecv after it completes but // before the application receives the indication), the LSP can be implemented as an // IFS provider. This means that the socket handle returned from this LSP is simply // the provider's handle below this LSP. However, this does mean this LSP must be // installed over other IFS providers only -- it will not function if it is installed // over a non-IFS provider. // // This file contains the Winsock Service Provider Interface (SPI) functions that // the LSP is overriding. The LSP only overrides a few functions (those related to // performing as a TCP proxy client) and the remaining functions are the lower // provider's. That is, when this LSP is loaded, it loads the lower provider's // function table, copies it as its own, and overrides only a few functions. This // proc table is then returned to the caller. // #include "lspdef.h" #include #include #include #pragma warning(disable:4127) // Disable "conditional expression is constant" warning #define DEFAULT_PRINT_BUFFER 512 //////////////////////////////////////////////////////////////////////////////// // // Globals used across files // //////////////////////////////////////////////////////////////////////////////// CRITICAL_SECTION gCriticalSection; // Critical section to protect startup/cleanup WSPUPCALLTABLE gMainUpCallTable; // Winsock upcall table LPPROVIDER gLayerInfo = NULL; // Provider information for each layer under us int gLayerCount = 0; // Number of providers layered over //////////////////////////////////////////////////////////////////////////////// // // Globals local to this file // //////////////////////////////////////////////////////////////////////////////// static BOOL gDetached = FALSE; // Indicates if process is detaching from DLL static int gStartupCount = 0; // Global startup count (for every WSPStartup call) // Parses a buffer looking for an HTTP GET request and returns the requested URL int FindUrl( __in_ecount(buflen) char *buf, int buflen, __out char **start ); //////////////////////////////////////////////////////////////////////////////// // // SPI Function Implementation // //////////////////////////////////////////////////////////////////////////////// // // Function: DllMain // // Description: // Provides initialization when the LSP DLL is loaded. In our case we simply, // initialize some critical sections used throughout the DLL. // BOOL WINAPI DllMain( IN HINSTANCE hinstDll, IN DWORD dwReason, LPVOID lpvReserved ) { UNREFERENCED_PARAMETER( hinstDll ); UNREFERENCED_PARAMETER( lpvReserved ); switch (dwReason) { case DLL_PROCESS_ATTACH: // // Initialize some critical section objects // __try { InitializeCriticalSection( &gCriticalSection ); InitializeCriticalSection( &gDebugCritSec ); } __except( EXCEPTION_EXECUTE_HANDLER ) { goto cleanup; } break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: gDetached = TRUE; EnterCriticalSection( &gCriticalSection ); if ( NULL != gLayerInfo ) { int Error; // Free LSP structures if still present as well as call WSPCleanup // for all providers this LSP loaded FreeLspProviders( gLayerInfo, gLayerCount, &Error ); gLayerInfo = NULL; gLayerCount = 0; } LeaveCriticalSection( &gCriticalSection ); DeleteCriticalSection( &gCriticalSection ); DeleteCriticalSection( &gDebugCritSec ); break; } return TRUE; cleanup: return FALSE; } // // Function: WSPCleanup // // Description: // Decrement the entry count. If equal to zero then we can prepare to have us // unloaded so all resources should be freed // int WSPAPI WSPCleanup( LPINT lpErrno ) { int rc = SOCKET_ERROR; if ( gDetached ) { rc = NO_ERROR; goto cleanup; } // // Grab the DLL global critical section // EnterCriticalSection( &gCriticalSection ); // Verify WSPStartup has been called if ( 0 == gStartupCount ) { *lpErrno = WSANOTINITIALISED; goto cleanup; } // Decrement the global entry count gStartupCount--; if ( 0 == gStartupCount ) { // Free LSP structures if still present as well as call WSPCleanup // for all providers this LSP loaded FreeLspProviders( gLayerInfo, gLayerCount, lpErrno ); gLayerInfo = NULL; gLayerCount = 0; } rc = NO_ERROR; cleanup: LeaveCriticalSection( &gCriticalSection ); return rc; } // // Function: WSPCloseSocket // // Description: // The LSP captures the WSPCloseSocket call to know when it can free the // socket context structure created for every socket. // int WSPAPI WSPCloseSocket( SOCKET s, LPINT lpErrno ) { SOCKET_CONTEXT *sockContext = NULL; int rc = SOCKET_ERROR; // Find the socket context and remove it from the provider's list of sockets sockContext = FindSocketContext( s, TRUE ); if ( NULL == sockContext ) { *lpErrno = WSAENOTSOCK; goto cleanup; } ASSERT( sockContext->Provider->NextProcTable.lpWSPCloseSocket ); // Pass the socket down to close it rc = sockContext->Provider->NextProcTable.lpWSPCloseSocket( s, lpErrno ); // Just free the structure as its alreayd removed from the provider's list LspFree( sockContext ); cleanup: return rc; } // // Function: WSPConnect // // Description: // This routine establishes a connection on a socket. For the LSP it first // determines if the destination is to be proxied to a different address. // Once the "correct" destination is determined, the connect call is passed // to the lower provider. // int WSPAPI WSPConnect( SOCKET s, const struct sockaddr FAR * name, int namelen, LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS, LPINT lpErrno ) { SOCKET_CONTEXT *sockContext = NULL; SOCKADDR *proxyAddr = NULL; int proxyLen = 0, rc = SOCKET_ERROR; // Find the socket context sockContext = FindSocketContext( s ); if ( NULL == sockContext ) { *lpErrno = WSAENOTSOCK; goto cleanup; } ASSERT( sockContext->Provider->NextProcTable.lpWSPConnect ); FindDestinationAddress( sockContext, name, namelen, &proxyAddr, &proxyLen ); rc = sockContext->Provider->NextProcTable.lpWSPConnect( s, proxyAddr, proxyLen, lpCallerData, lpCalleeData, lpSQOS, lpGQOS, lpErrno ); cleanup: return rc; } // // Function: WSPGetPeerName // // Description: // Since the LSP is proxying the remote address, this function needs to be // intercepted in order to return the address that the application thinks its // connected to. // int WSPAPI WSPGetPeerName( SOCKET s, struct sockaddr FAR * name, LPINT namelen, LPINT lpErrno ) { SOCKET_CONTEXT *sockContext = NULL; int rc = SOCKET_ERROR; // // Find our provider socket corresponding to this one // sockContext = FindSocketContext( s ); if ( NULL == sockContext ) { *lpErrno = WSAENOTSOCK; goto cleanup; } // If the connection has been proxied, return the address the application // originally tried to connect to. if ( TRUE == sockContext->Proxied ) { __try { // Verify buffer is large enough for underlying address structure if ( *namelen < sockContext->AddressLength ) { *namelen = sockContext->AddressLength; *lpErrno = WSAEFAULT; goto cleanup; } memcpy( name, &sockContext->OriginalAddress, *namelen ); *namelen = sockContext->AddressLength; } __except( EXCEPTION_EXECUTE_HANDLER ) { *lpErrno = WSAEFAULT; goto cleanup; } return NO_ERROR; } ASSERT( sockContext->Provider->NextProcTable.lpWSPGetPeerName ); rc = sockContext->Provider->NextProcTable.lpWSPGetPeerName( s, name, namelen, lpErrno ); cleanup: return rc; } // // Function: WSPGetSockOpt // // Description: // // int WSPAPI WSPGetSockOpt( SOCKET s, int level, int optname, __out_bcount(*optlen) char FAR * optval, __inout LPINT optlen, LPINT lpErrno ) { SOCKET_CONTEXT *sockContext = NULL; int rc = NO_ERROR; // Retrieve the socket context sockContext = FindSocketContext( s ); if ( NULL == sockContext ) { *lpErrno = WSAENOTSOCK; rc = SOCKET_ERROR; goto cleanup; } ASSERT( sockContext->Provider->NextProcTable.lpWSPGetSockOpt ); if ( ( SOL_SOCKET == level ) && ( ( SO_PROTOCOL_INFOA == optname ) || ( SO_PROTOCOL_INFOW == optname ) ) ) { __try { switch ( optname ) { case SO_PROTOCOL_INFOA: if ( *optlen <= sizeof( WSAPROTOCOL_INFOA ) ) { *optlen = sizeof( WSAPROTOCOL_INFOA ); *lpErrno = WSAEFAULT; rc = SOCKET_ERROR; goto cleanup; } *optlen = sizeof( WSAPROTOCOL_INFOA ); memcpy( optval, &sockContext->Provider->LayerProvider, sizeof( WSAPROTOCOL_INFOA ) ); break; case SO_PROTOCOL_INFOW: if ( *optlen <= sizeof( WSAPROTOCOL_INFOW ) ) { *optlen = sizeof( WSAPROTOCOL_INFOW ); *lpErrno = WSAEFAULT; rc = SOCKET_ERROR; goto cleanup; } *optlen = sizeof( WSAPROTOCOL_INFOW ); memcpy( optval, &sockContext->Provider->LayerProvider, sizeof( WSAPROTOCOL_INFOW ) ); break; } } __except( EXCEPTION_EXECUTE_HANDLER ) { *lpErrno = WSAEFAULT; rc = SOCKET_ERROR; goto cleanup; } if ( SO_PROTOCOL_INFOA == optname ) { WideCharToMultiByte( CP_ACP, 0, sockContext->Provider->LayerProvider.szProtocol, -1, ( (WSAPROTOCOL_INFOA *)optval )->szProtocol, WSAPROTOCOL_LEN+1, NULL, NULL ); } } else { rc = sockContext->Provider->NextProcTable.lpWSPGetSockOpt( s, level, optname, optval, optlen, lpErrno ); } cleanup: return rc; } // // Function: WSPIoctl // // Description: // Since the LSP proxies connection requests, the LSP needs to intercept the // ConnectEx function. This routine returns the LSPs ConnectEx function when // the application requests it. // int WSPAPI 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 ) { SOCKET_CONTEXT *sockContext = NULL; GUID ConnectExGuid = WSAID_CONNECTEX; int rc = NO_ERROR; // If loading an extension function, check for ConnectEx if ( SIO_GET_EXTENSION_FUNCTION_POINTER == dwIoControlCode ) { if ( 0 == memcmp( lpvInBuffer, &ConnectExGuid, sizeof( GUID ) ) ) { // Return a pointer to our intermediate extension function __try { if ( cbOutBuffer < sizeof( LPFN_CONNECTEX ) ) { *lpcbBytesReturned = sizeof( LPFN_CONNECTEX ); *lpErrno = WSAEFAULT; rc = SOCKET_ERROR; goto cleanup; } *lpcbBytesReturned = sizeof( LPFN_CONNECTEX ); *((DWORD_PTR *)lpvOutBuffer) = (DWORD_PTR) ExtConnectEx; } __except( EXCEPTION_EXECUTE_HANDLER ) { *lpErrno = WSAEFAULT; rc = SOCKET_ERROR; } return rc; } } // Retrieve the socket context sockContext = FindSocketContext( s ); if ( NULL == sockContext ) { *lpErrno = WSAENOTSOCK; rc = SOCKET_ERROR; goto cleanup; } ASSERT( sockContext->Provider->NextProcTable.lpWSPIoctl ); // Pass the call to the lower layer rc = sockContext->Provider->NextProcTable.lpWSPIoctl( s, dwIoControlCode, lpvInBuffer, cbInBuffer, lpvOutBuffer, cbOutBuffer, lpcbBytesReturned, lpOverlapped, lpCompletionRoutine, lpThreadId, lpErrno ); cleanup: return rc; } // // Function: WSPSend // // Description: // This function implements the WSPSend function for the IFS LSP. This routine // simply parses the send buffer for an HTTP GET request. If one is found it // is simply displayed to the debugger. This illustrates how to parse data // buffers. Note that this samply only intercepts the WSPSend routine since // we're interested in only HTTP TCP traffic. If we wanted to parse datagram // oriented protocols we should then also intercept WSPSendTo, WSASendMsg, // and TransmitPackets. Note that we don't intercept TransmitFile or // TransmitPackets for this HTTP parsing since the client HTTP sides do not // use those APIs for sending requests (i.e. these extension functions are // typically used in the server response). // int WSPAPI WSPSend( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, LPWSATHREADID lpThreadId, LPINT lpErrno ) { SOCKET_CONTEXT *sockContext = NULL; DWORD i; char *start = NULL, urlstr[ DEFAULT_PRINT_BUFFER ]; int rc = SOCKET_ERROR, len; // // Find our provider socket corresponding to this one // sockContext = FindSocketContext( s ); if ( NULL == sockContext ) { *lpErrno = WSAENOTSOCK; goto cleanup; } // Parse the send buffer and look for HTTP GET requests __try { for(i=0; i < dwBufferCount ;i++) { len = FindUrl( lpBuffers[ i ].buf, lpBuffers[ i ].len, &start ); if ( ( len > 0 ) && ( len+1 < DEFAULT_PRINT_BUFFER ) ) { if ( FAILED (StringCchCopyN( urlstr, DEFAULT_PRINT_BUFFER, start, len+1 ) ) ) { *lpErrno = WSAEFAULT; goto cleanup; } if ( len >= DEFAULT_PRINT_BUFFER ) urlstr[ DEFAULT_PRINT_BUFFER-1 ] = '\0'; else urlstr[len] = '\0'; // URL can be logged but this just displays it to the debugger dbgprint("Found URL: '%s'", urlstr ); } } } __except( EXCEPTION_EXECUTE_HANDLER ) { dbgprint("WSPSend: ***access violation ***"); *lpErrno = WSAEFAULT; goto cleanup; } ASSERT( sockContext->Provider->NextProcTable.lpWSPSend ); // Just pass the request along to the lower provider. NOTE: If we choose to // modify the data things get a bit trickier if we substitute our own send // buffer since we would need to be in the data notification path in order // to know when we are able to free that memory (i.e. the lower layer has // processed it and is done). In this case a non-IFS LSP is more appropriate // since it intercepts all IO completion notifications. rc = sockContext->Provider->NextProcTable.lpWSPSend( s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags, lpOverlapped, lpCompletionRoutine, lpThreadId, lpErrno ); cleanup: return rc; } // // Function: WSPSocket // // Description: // This routine creates a socket. For an IFS LSP the lower provider's socket // handle is returned to the uppler layer. When a socket is created, a socket // context structure is created for the socket returned from the lower provider. // This context is used if the socket is later connected to a proxied address. // SOCKET WSPAPI WSPSocket( int af, int type, int protocol, LPWSAPROTOCOL_INFOW lpProtocolInfo, GROUP g, DWORD dwFlags, LPINT lpErrno ) { WSAPROTOCOL_INFOW InfoCopy = {0}; SOCKET_CONTEXT *sockContext = NULL; PROVIDER *lowerProvider = NULL; SOCKET nextProviderSocket = INVALID_SOCKET, sret = INVALID_SOCKET; int rc; // Find the LSP entry which matches the given provider info lowerProvider = FindMatchingLspEntryForProtocolInfo( lpProtocolInfo, gLayerInfo, gLayerCount ); if ( NULL == lowerProvider ) { dbgprint("WSPSocket: FindMatchingLspEntryForProtocolInfo failed!" ); goto cleanup; } if ( 0 == lowerProvider->StartupCount ) { rc = InitializeProvider( lowerProvider, MAKEWORD(2,2), lpProtocolInfo, gMainUpCallTable, lpErrno ); if ( SOCKET_ERROR == rc ) { dbgprint("WSPSocket: InitializeProvider failed: %d", *lpErrno); goto cleanup; } } // If the next layer is a base, substitute the provider structure with the // base provider's if ( BASE_PROTOCOL == lowerProvider->NextProvider.ProtocolChain.ChainLen ) { memcpy( &InfoCopy, &lowerProvider->NextProvider, sizeof( InfoCopy ) ); InfoCopy.dwProviderReserved = lpProtocolInfo->dwProviderReserved; lpProtocolInfo = &InfoCopy; } ASSERT( lowerProvider->NextProcTable.lpWSPSocket ); // // Create the socket from the lower layer // nextProviderSocket = lowerProvider->NextProcTable.lpWSPSocket( af, type, protocol, lpProtocolInfo, g, dwFlags, lpErrno ); if ( INVALID_SOCKET == nextProviderSocket ) { dbgprint("WSPSocket: NextProcTable.WSPSocket failed: %d", *lpErrno); goto cleanup; } // // Create the context information to be associated with this socket // sockContext = CreateSocketContext( lowerProvider, nextProviderSocket, lpErrno ); if ( NULL == sockContext ) { dbgprint( "WSPSocket: CreateSocketContext failed: %d", *lpErrno ); goto cleanup; } // // Associate ownership of this handle with our LSP // sret = gMainUpCallTable.lpWPUModifyIFSHandle( lowerProvider->LayerProvider.dwCatalogEntryId, nextProviderSocket, lpErrno ); if ( INVALID_SOCKET == sret ) { dbgprint( "WSPSocket: WPUModifyIFSHandle failed: %d", *lpErrno ); goto cleanup; } ASSERT( sret == nextProviderSocket ); return nextProviderSocket; cleanup: // If an error occured close the socket if it was already created if ( ( NULL != sockContext ) && ( NULL != lowerProvider ) && ( INVALID_SOCKET != nextProviderSocket ) ) { rc = lowerProvider->NextProcTable.lpWSPCloseSocket( nextProviderSocket, lpErrno ); if ( SOCKET_ERROR == rc ) { dbgprint( "WSPSocket: WSPCloseSocket failed: %d", *lpErrno ); } } if ( ( NULL != sockContext ) && ( NULL != lowerProvider ) ) FreeSocketContext( lowerProvider, sockContext ); return INVALID_SOCKET; } // // Function: WSPAccept // // Description: // This routine must be intercepted for connection oriented providers. This is // necessary since if an accepted socket is used by one of the other functions // the IFS LSP is intercepting then state *must* be maintained; otherwise, the // search for the context would fail and an error be returned. // SOCKET WSPAPI WSPAccept( SOCKET s, struct sockaddr FAR *addr, LPINT addrlen, LPCONDITIONPROC lpfnCondition, DWORD_PTR dwCallbackData, LPINT lpErrno ) { SOCKET_CONTEXT *sockContext = NULL, *acceptContext = NULL; SOCKET acceptProviderSocket = INVALID_SOCKET, sret; int rc; sockContext = FindSocketContext( s ); if ( NULL == sockContext ) { *lpErrno = WSAENOTSOCK; goto cleanup; } ASSERT( sockContext->Provider->NextProcTable.lpWSPAccept ); // // Create the socket from the lower layer // acceptProviderSocket = sockContext->Provider->NextProcTable.lpWSPAccept( s, addr, addrlen, lpfnCondition, dwCallbackData, lpErrno ); if ( INVALID_SOCKET == acceptProviderSocket ) { dbgprint("WSPAccept: NextProcTable.WSPAccept failed: %d", *lpErrno); goto cleanup; } // // Create the context information to be associated with this socket // acceptContext = CreateSocketContext( sockContext->Provider, acceptProviderSocket, lpErrno ); if ( NULL == sockContext ) { dbgprint( "WSPAccept: CreateSocketContext failed: %d", *lpErrno ); goto cleanup; } // // Associate ownership of this handle with our LSP // sret = gMainUpCallTable.lpWPUModifyIFSHandle( sockContext->Provider->LayerProvider.dwCatalogEntryId, acceptProviderSocket, lpErrno ); if ( INVALID_SOCKET == sret ) { dbgprint( "WSPAccept: WPUModifyIFSHandle failed: %d", *lpErrno ); goto cleanup; } ASSERT( sret == acceptProviderSocket ); return acceptProviderSocket; cleanup: // If an error occured close the socket if it was already created if ( ( NULL != sockContext ) && ( NULL != acceptContext ) && ( INVALID_SOCKET != acceptProviderSocket ) ) { rc = sockContext->Provider->NextProcTable.lpWSPCloseSocket( acceptProviderSocket, lpErrno ); if ( SOCKET_ERROR == rc ) { dbgprint( "WSPAccept: WSPCloseSocket failed: %d", *lpErrno ); } } if ( ( NULL != sockContext ) && ( NULL != acceptContext ) ) FreeSocketContext( sockContext->Provider, acceptContext ); return INVALID_SOCKET; } // // Function: WSPStartup // // Description: // This function initializes the LSP. If this is the first time WSPStartup // is called, the LSP builds an array of its providers. The PROVIDER structure // contains an LSP protocol chain and the Winsock provider it is layered over. // Once this structure is created, the LSP determines which LSP protocol chain // matches the lpProtocolInfo passed into it. Note that the IFS LSP *requires* // that WSPStartup is invoked for *each* of its layered protocol chains. This // is required since the IFS LSP intercepts a subset of all the SPI functions. // For those functions the LSP isn't interested in intercepting, the LSP passes // the lower provider's functions pointers. Consider the following example: // // 1. Install this IFS LSP (LSP2 in the diagram) over a BASE TCP/IPv4 // provider as well as another IFS LSP UDP/IPv4 (LSP1 in the diagram). // ______________________ _______________________ // | IFS LSP2 TCP/IPv4 | | IFS LSP2 UDP/IPv4 | // |____________________| |_____________________| // | BASE TCP/IPv4 | | IFS LSP1 UDP/IPv4 | // |____________________| |_____________________| // // 2. If LSP2 WSPStartup is invoked with TCP/IPv4 then LSP2 loads the // BASE TCP/IPv4 provider and returns some of its function pointers to // the caller (i.e. the functions that LSP2 doesn't intercept) // // 3. Now, what if the process creates a UDP/IPv4 socket? If both of LSP2's // layered protocol entries were installed in a single WSCInstallProvider // call (under a single GUID), LSP2's WSPStartup is *NOT* invoked and this // UDP socket will have a proc table that points into the BASE TCP/IPv4 // provider. // // However, if the two LSP2 entries were installed in two calls to // WSCInstallProvider with two GUIDs, then when a UDP/IPv4 socket is created // LSP2's WSPStartup is invoked again at which point the correct proc // table is built using the LSP1's function pointers which LSP2 doesn't wish // to override. // int WSPAPI WSPStartup( WORD wVersion, LPWSPDATA lpWSPData, LPWSAPROTOCOL_INFOW lpProtocolInfo, WSPUPCALLTABLE UpCallTable, LPWSPPROC_TABLE lpProcTable ) { PROVIDER *loadProvider = NULL; int Error = WSAEPROVIDERFAILEDINIT, rc; EnterCriticalSection( &gCriticalSection ); // The first time the startup is called, create our heap and allocate some // data structures for tracking the LSP providers if ( 0 == gStartupCount ) { // Create the heap for all LSP allocations rc = LspCreateHeap( &Error ); if ( SOCKET_ERROR == rc ) { dbgprint("WSPStartup: LspCreateHeap failed: %d", Error ); goto cleanup; } // Find this LSP's entries in the Winsock catalog and build a map of them rc = FindLspEntries( &gLayerInfo, &gLayerCount, &Error ); if ( FALSE == rc ) { dbgprint("WSPStartup: FindLspEntries failed: %d", Error ); goto cleanup; } // Save off upcall table - this should be the same across all WSPStartup calls memcpy( &gMainUpCallTable, &UpCallTable, sizeof( gMainUpCallTable ) ); } // Find the matching LSP provider for the requested protocol info passed in. // This can either be an LSP layered over use or an entry belonging to this // LSP. Note that the LSP startup gets called for each LSP layered protocol // entry with a unique GUID. Because of this each layered protocol entry for // the IFS LSP should be installed with its own unique GUID. loadProvider = FindMatchingLspEntryForProtocolInfo( lpProtocolInfo, gLayerInfo, gLayerCount, TRUE ); if ( NULL == loadProvider ) { dbgprint("WSPStartup: FindMatchingLspEntryForProtocolInfo failed!"); ASSERT( 0 ); goto cleanup; } // If this is the first time to "load" this particular provider, initialize // the lower layer, etc. if ( 0 == loadProvider->StartupCount ) { rc = InitializeProvider( loadProvider, wVersion, lpProtocolInfo, UpCallTable, &Error ); if ( SOCKET_ERROR == rc ) { dbgprint("WSPStartup: InitializeProvider failed: %d", Error ); goto cleanup; } } gStartupCount++; // Build the proc table to return to the caller memcpy( lpProcTable, &loadProvider->NextProcTable, sizeof( *lpProcTable ) ); // Override only those functions the LSP wants to intercept lpProcTable->lpWSPAccept = WSPAccept; lpProcTable->lpWSPCleanup = WSPCleanup; lpProcTable->lpWSPCloseSocket = WSPCloseSocket; lpProcTable->lpWSPConnect = WSPConnect; lpProcTable->lpWSPGetPeerName = WSPGetPeerName; lpProcTable->lpWSPGetSockOpt = WSPGetSockOpt; lpProcTable->lpWSPIoctl = WSPIoctl; lpProcTable->lpWSPSend = WSPSend; lpProcTable->lpWSPSocket = WSPSocket; memcpy( lpWSPData, &loadProvider->WinsockVersion, sizeof( *lpWSPData ) ); Error = NO_ERROR; cleanup: LeaveCriticalSection( &gCriticalSection ); return Error; } //////////////////////////////////////////////////////////////////////////////// // // Helper Function Implementation // //////////////////////////////////////////////////////////////////////////////// // // Function: FindDestinationAddress // // Description: // This function is invoked whenver a connection request is made by the upper // layer which can occur at the WSPConnect and ConnectEx functions. This method // determines whether the application's destination address should be // redirected to another destination. Currently, this function just looks for // a single IPv4 destination address and substitutes it with another. // void FindDestinationAddress( SOCKET_CONTEXT *context, const SOCKADDR *destAddr, int destLen, SOCKADDR **proxyAddr, int *proxyLen ) { UNREFERENCED_PARAMETER( context ); UNREFERENCED_PARAMETER( destLen ); UNREFERENCED_PARAMETER( proxyAddr ); UNREFERENCED_PARAMETER( proxyLen ); context->AddressLength = destLen; // Save destination address memcpy( &context->OriginalAddress, destAddr, context->AddressLength ); *proxyAddr = (SOCKADDR *) &context->OriginalAddress; *proxyLen = context->AddressLength; if ( destAddr->sa_family == AF_INET ) { // Redirect one destination to another if ( ( (SOCKADDR_IN *)destAddr )->sin_addr.s_addr == inet_addr("157.56.236.201") ) { memcpy( &context->ProxiedAddress, destAddr, context->AddressLength ); ( (SOCKADDR_IN *)&context->ProxiedAddress )->sin_addr.s_addr = inet_addr( "157.56.237.9" ); *proxyAddr = (SOCKADDR *) &context->ProxiedAddress; context->Proxied = TRUE; } } else if ( destAddr->sa_family == AF_INET6 ) { // Perform redirection here } } // // Function: FreeLspProviders // // Description: // // void FreeLspProviders( PROVIDER *lspProvider, int lspProviderCount, int *lpErrno ) { int i; if ( NULL == lspProvider ) return; // Need to iterate through the LSP providers and call WSPCleanup accordingly for(i=0; i < lspProviderCount ;i++) { while( 0 != lspProvider[ i ].StartupCount ) { lspProvider[ i ].StartupCount--; lspProvider[ i ].NextProcTable.lpWSPCleanup( lpErrno ); } if ( NULL != lspProvider[ i ].Module ) { FreeLibrary( lspProvider[ i ].Module ); lspProvider[ i ].Module = NULL; } } for(i=0; i < lspProviderCount ;i++) { FreeSocketContextList( &lspProvider[ i ] ); DeleteCriticalSection( &lspProvider[ i ].ProviderCritSec ); } LspFree( lspProvider ); } // // Function: FindUrl // // Description: // This routine searches each send buffer for the presence of an HTPT GET // request. It parses the buffer and returns the URL which is being requested. // int FindUrl( __in_ecount(buflen) char *buf, int buflen, __out char **start ) { char *subptr = NULL, *substart = NULL; int subidx, idx; *start = NULL; // Perform a substring search starting at the beginning of 'buf' idx = 0; while ( idx < buflen ) { substart = buf; subidx = idx; subptr = "GET"; // Once a character is matched proceed to check for a complete match // while ensuring we don't go past the end of the buffer (since it may not // be NULL terminated -- at least not the portion we're currently looking // at) while ( ( subidx < buflen ) && ( *subptr != '\0' ) && ( *subptr == *substart ) ) { subptr++; substart++; subidx++; } // If 'subptr' is pointing to NULL then a match to "GET" was found if ( *subptr == '\0' ) { // A GET request was found so skip over the subsequent space idx = subidx + 1; // Validate we're still within the buffer if ( idx >= buflen ) { // Went past the buffer so return no match return 0; } *start = substart + 1; // Advance the character pointer past the space substart++; subidx = idx; // Advance the search pointer until we hit the end space while ( ( subidx < buflen ) && ( *substart != ' ' ) ) { substart++; subidx++; } // If we're sitting on the trailing space, we found a URL so return // how many bytes are in the URL if ( *substart == ' ' ) { return subidx - idx; } else { // Error occured so just return since we're not where we expected return 0; } } // Advance the main pointers and index and start the search with the next // string location buf++; idx++; } return 0; }