// 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: extension.cpp
//
// Description:
//
//    This sample illustrates an IFS based LSP which implements basic proxying
//    capabilities.
//
//    This file contains all of the Winsock extension functions that the LSP needs
//    to intercept in order to proxy connections. In terms of Winsock extension
//    functions this means only the ConnectEx function needs to be intercepted.
//

#include "lspdef.h"

//
// Function: LoadExtensionFunction
//
// Description:
//    This function dynamically loads the given extension function from the
//    underlying provider. Each extension function checks to see if the 
//    corresponding extension function for the lower provider is loaded
//    before calling. If not, it will load it as needed. This is necessary
//    if the app loads the extension function for say TCP and then calls
//    that extension function on a UDP socket. Normally this isn't the case
//    but we're being defensive here.
//
BOOL
LoadExtensionFunction(
        FARPROC   **func,
        GUID        ExtensionGuid,
        LPWSPIOCTL  fnIoctl,
        SOCKET      s
        )
{
    DWORD   dwBytes;
    int     rc, 
            error;

    // Use the lower provider's WSPIoctl to load the extension function
    rc = fnIoctl(
            s,
            SIO_GET_EXTENSION_FUNCTION_POINTER,
           &ExtensionGuid,
            sizeof(GUID),
            func,
            sizeof(FARPROC),
           &dwBytes,
            NULL,
            NULL,
            NULL,
           &error
            );
    if ( SOCKET_ERROR == rc )
    {
        dbgprint("LoadExtensionFunction: WSAIoctl (SIO_GET_EXTENSION_FUNCTION) failed: %d",
            error);
        return FALSE;
    }
    else
    {
        return TRUE;
    }
}

//
// Function: ExtConnectEx
//
// Description:
//    This is our provider's ConnectEx function. When an app calls WSAIoctl
//    to request the function pointer to ConnectEx, we intercept the call
//    and return a pointer to our extension function instead. This ConnectEx
//    implementation needs to perform the same proxying check that WSPConnect
//    does.
//
BOOL PASCAL FAR 
ExtConnectEx(
    IN SOCKET s,
    IN const struct sockaddr FAR *name,
    IN int namelen,
    IN PVOID lpSendBuffer OPTIONAL,
    IN DWORD dwSendDataLength,
    OUT LPDWORD lpdwBytesSent,
    IN LPOVERLAPPED lpOverlapped
    )
{
    SOCKET_CONTEXT *sockContext = NULL;
    SOCKADDR       *proxyAddr = NULL;
    int             Errno = NO_ERROR,
                    proxyLen = 0,
                    rc = FALSE;

    sockContext = FindSocketContext( s );
    if ( NULL == sockContext )
    {
        dbgprint("ExtConnectEx: FindSocketContext failed!");
        Errno = WSAENOTSOCK;
        goto cleanup;
    }

    // Make sure we already have the extension function
    if ( NULL == sockContext->Provider->NextProcTableExt.lpfnConnectEx )
    {
        GUID    guidConnectEx = WSAID_CONNECTEX;

        rc = LoadExtensionFunction(
                (FARPROC **)&sockContext->Provider->NextProcTableExt.lpfnConnectEx,
                guidConnectEx,
                sockContext->Provider->NextProcTable.lpWSPIoctl,
                s
                );
        if ( FALSE == rc )
        {
            dbgprint("Next proc table ConnectEx == NULL!");
            Errno = WSAEFAULT;
            goto cleanup;
        }
    }

    // See if the connect needs to be proxied
    FindDestinationAddress( sockContext, name, namelen, &proxyAddr, &proxyLen );

    rc = sockContext->Provider->NextProcTableExt.lpfnConnectEx(
            s,
            proxyAddr,
            proxyLen,
            lpSendBuffer,
            dwSendDataLength,
            lpdwBytesSent,
            lpOverlapped
            );

cleanup:

    return rc;
}
