// 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 <strsafe.h>

#include <stdio.h>
#include <stdlib.h>

#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;
}
