//
// 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: lspadd.cpp
//
// Description:
//
//    This file contains the routines used in installing an LSP into the Winsock
//    catalog. This file also contains routines for changing the Winsock provider
//    order since the catalog is typically reordered upon LSP installation.
//    See instlsp.cpp for more information on running this code.
//
#include "instlsp.h"

//
// Function: InstallLsp
//
// Description:
//    This function installs the LSP over each of the catalog IDs supplied in
//    the pdwCatalogIdArray into the given Winsock catalog (since there are
//    two catalogs on WIN64 platforms). This routine first installs the layered
//    protocol entry (the dummy hidden provider) followed by installing the
//    layered protocol chains.
//
int
InstallLsp(
    WINSOCK_CATALOG eCatalog,               // Which catalog to install LSP into
    __in_z char    *lpszLspName,            // String name of LSP
    __in_z char    *lpszLspPathAndFile,     // Location of LSP dll and dll name
    DWORD           dwCatalogIdArrayCount,  // Number of entries in pdwCatalogIdArray
    DWORD          *pdwCatalogIdArray,      // Array of IDs to install over
    BOOL            IfsProvider,
    BOOL            InstallOverAll
    )
{
    OSVERSIONINFOEX     osv = {0};
    WSAPROTOCOL_INFOW  *pProtocolInfo = NULL,
                       *pDummyEntry = NULL,
                       *pLayeredEntries = NULL;
    WCHAR               wszLspName[ WSAPROTOCOL_LEN ],
                        wszFullProviderPath[ MAX_PATH+1 ];
    GUID                ProviderBaseGuid;
    INT                 rc = SOCKET_ERROR;

    //
    // Install the LSP over the given entries. If selected to install over all
    // entries, then enumerate the catalog to obtain all the available providers.
    //

    if ( NULL == lpszLspName )
    {
        lpszLspName = DEFAULT_LSP_NAME;
    }

    // Convert the LSP name to UNICODE since the Winsock catalog is all UNICODE
    rc = MultiByteToWideChar(
            CP_ACP, 
            0, 
            lpszLspName, 
            (int) strlen( lpszLspName ) + 1,
            wszLspName, 
            WSAPROTOCOL_LEN 
            );
    if (rc == 0)
    {
        fprintf(stderr, "InstallLsp: MultiByteToWideChar failed to convert '%s'; Error = %d\n",
                lpszLspName, GetLastError());
        goto cleanup;
    }

    rc = MultiByteToWideChar(
            CP_ACP,
            0,
            lpszLspPathAndFile,
            (int) strlen( lpszLspPathAndFile ) + 1,
            wszFullProviderPath,
            MAX_PATH
            );
    if ( 0 == rc )
    {
        fprintf( stderr, "InstallLsp: MultiByteToWidechar failed to convert '%s': Error = %d\n",
                lpszLspPathAndFile, GetLastError() );
        goto cleanup;
    }

    // Verify there's at least one entry to layer over
    if ( 0 == dwCatalogIdArrayCount )
    {
        fprintf(stderr, "InstallLsp: Error! Must specify at least one provider to layer over!\n\n");
        goto cleanup;
    }

    printf("LSP name is '%S'\n", wszLspName);

    // Retrieve the GUID under which the LSP is to be installed
    RetrieveLspGuid( lpszLspPathAndFile, &ProviderBaseGuid );

    osv.dwOSVersionInfoSize = sizeof(osv);
    GetVersionEx( (LPOSVERSIONINFO) &osv );

    if ( osv.dwMajorVersion >= 6 ) 
    {
        // On Windows Vista, use the new LSP install API

        rc = InstallProviderVista(
                eCatalog,
                wszLspName,
                wszFullProviderPath,
               &ProviderBaseGuid,
                dwCatalogIdArrayCount,
                pdwCatalogIdArray,
                IfsProvider,
                InstallOverAll
                );
        if ( SOCKET_ERROR == rc )
        {
            goto cleanup;
        }

    }
    else
    {
        //
        // This isn't Vista so install the LSP the old way
        //

        // Create the 'dummy' protocol entry
        pDummyEntry = CreateDummyEntry( eCatalog, pdwCatalogIdArray[ 0 ], wszLspName, IfsProvider );
        if (pDummyEntry == NULL)
        {
            fprintf(stderr, "InstallLsp: CreateDummyEntry failed!\n");
            goto cleanup;
        }

        // Install the 'dummy' protocol entry for the LSP
        rc = InstallProvider(
                eCatalog, 
                &ProviderBaseGuid, 
                wszFullProviderPath, 
                pDummyEntry, 
                1
                );
        if ( NO_ERROR != rc )
        {
            fprintf(stderr, "InstallLsp: Unable to install the dummy LSP entry!\n");
            goto cleanup;
        }

        // Don't need this struture any more
        LspFree( pDummyEntry );
        pDummyEntry = NULL;

        if ( FALSE == IfsProvider )
        {
            rc = InstallNonIfsLspProtocolChains( eCatalog, &ProviderBaseGuid, wszLspName,
                    wszFullProviderPath, pdwCatalogIdArray, dwCatalogIdArrayCount );

        }
        else
        {
            rc = InstallIfsLspProtocolChains( eCatalog, &ProviderBaseGuid, wszLspName,
                    wszFullProviderPath, pdwCatalogIdArray, dwCatalogIdArrayCount );
        }

        if ( SOCKET_ERROR == rc )
        {
            // An error occured installing the chains so remove the dummy entry
            DeinstallProvider( eCatalog, &ProviderBaseGuid );
        }

    }

cleanup:

    if ( NULL != pProtocolInfo )
        FreeProviders( pProtocolInfo );

    if ( NULL != pDummyEntry )
        LspFree( pDummyEntry );

    if ( NULL != pLayeredEntries )
        LspFree( pLayeredEntries );

    return rc;
}

//
// Function: InstallProvider
//
// Description:
//    This is a wrapper for the WSCInstallProvider function. Depending on
//    which catalog is specified, this routine calls the correct install
//    routine.
//
int 
InstallProvider(
    WINSOCK_CATALOG     Catalog,        // Which catalog are we operating on
    GUID               *Guid,           // GUID under which provider will be installed
    WCHAR              *lpwszLspPath,   // Path to LSP's DLL
    WSAPROTOCOL_INFOW  *pProvider,      // Array of provider structures to install
    INT                 iProviderCount  // Number of providers in array
    )
{
    WSAPROTOCOL_INFOW *pEnumProviders = NULL,
                      *pEntry = NULL;
    INT                iEnumProviderCount,
                       ErrorCode,
                       rc = SOCKET_ERROR;

#ifdef _WIN64
    if ( LspCatalog32Only == Catalog )
    {
        // Can't install only in 32-bit catalog from 64-bit
        fprintf( stderr, "InstallProvider: Error! It is not possible to install only "
                "in 32-bit catalog from 64-bit process!\n\n"
                );
        goto cleanup;
    }
    else if ( LspCatalog64Only == Catalog )
    {
        // Just need to call WSCInstallProvider
        rc = WSCInstallProvider( 
                Guid, 
                lpwszLspPath, 
                pProvider, 
                iProviderCount, 
               &ErrorCode 
                );
    }
    else
    {
        // To install in both we must call WSCInstallProviderPath64_32
        rc = WSCInstallProvider64_32(
                Guid, 
                lpwszLspPath, 
                pProvider, 
                iProviderCount, 
               &ErrorCode
                );
    }
#else
    if ( LspCatalog32Only == Catalog )
    {
        // From a 32-bit process we can only install into 32-bit catalog
        rc = WSCInstallProvider(
                Guid, 
                lpwszLspPath, 
                pProvider, 
                iProviderCount, 
               &ErrorCode
                );
    }
    else
    {
        // From a 32-bit process, we can't touch the 64-bit catalog at all
        fprintf( stderr, "InstallProvider: Error! It is not possible to install into "
                "the 64-bit catalog from a 32-bit process!\n\n"
                );
        goto cleanup;
    }
#endif
    if ( SOCKET_ERROR == rc )
    {
        fprintf( stderr, "InstallProvider: WSCInstallProvider* failed: %d\n", ErrorCode );
        goto cleanup;
    }

    // Go back and enumerate what we just installed
    pEnumProviders = EnumerateProviders( Catalog, &iEnumProviderCount );
    if ( NULL == pEnumProviders )
    {
        fprintf( stderr, "InstallProvider: EnumerateProviders failed!\n" );
        goto cleanup;
    }
    
    // Make sure our entry is in the catalog
    pEntry = FindProviderByGuid( Guid, pEnumProviders, iEnumProviderCount );
    if ( pEntry )
    {
        printf( "Installed: [%4d] %S\n", 
                pEntry->dwCatalogEntryId,
                pEntry->szProtocol
                );
    }

cleanup:

    if ( NULL != pEnumProviders )
        FreeProviders( pEnumProviders );

    return rc;
}

//
// Function: CreateDummyEntry
//
// Description:
//    This creates a single WSAPROTOCOL_INFOW structure which describes the
//    hidden "dummy" entry for an LSP. This is required since the layered
//    chain entries must reference the catalog ID of the LSP and a catalog ID
//    cannot be obtained until an entry is installed.
//
WSAPROTOCOL_INFOW *
CreateDummyEntry(
    WINSOCK_CATALOG Catalog, 
    INT CatalogId, 
    WCHAR *lpwszLspName,
    BOOL IfsProvider
    )
{
    WSAPROTOCOL_INFOW *pProtocolInfo = NULL,
                      *pDummyEntry = NULL,
                      *pEntry = NULL;
    INT                iProtocolCount = 0;
    int                err;

    // Enumerate the catalog
    pProtocolInfo = EnumerateProviders( Catalog, &iProtocolCount );
    if ( NULL == pProtocolInfo )
    {
        fprintf(stderr, "CreateDummyEntry: EnumerateProviders failed!\n");
        goto cleanup;
    }

    // Find one of the providers we are layering over
    pEntry = FindProviderById( CatalogId, pProtocolInfo, iProtocolCount );
    if ( pEntry )
    {
        // Allocate space and copy the provider structure
        pDummyEntry = (WSAPROTOCOL_INFOW *) LspAlloc(
                sizeof( WSAPROTOCOL_INFOW ),
               &err
                );
        if ( NULL == pDummyEntry )
        {
            fprintf( stderr, "CreateDummyEntry: LspAlloc failed: %d\n", err );
            goto cleanup;
        }

        // Copy the entry as a basis for the dummy entry
        memcpy( pDummyEntry, pEntry, sizeof( WSAPROTOCOL_INFOW ) );
    }
    else
    {
        fprintf(stderr, "CreateDummyEntry: Error! Unable to find provider with ID of %d\n\n",
                CatalogId 
                );
        goto cleanup;
    }

    // Remove the IFS provider flag if the LSP doesn't support it
    if ( FALSE == IfsProvider )
        pDummyEntry->dwServiceFlags1 &= (~XP1_IFS_HANDLES);

    // Set the flags indicating this is a hidden ("dummy") entry
    pDummyEntry->iSocketType = 0;
    pDummyEntry->iProtocol   = 0;
    pDummyEntry->dwProviderFlags |= PFL_HIDDEN;
    pDummyEntry->dwProviderFlags &= (~PFL_MATCHES_PROTOCOL_ZERO);
    pDummyEntry->ProtocolChain.ChainLen = LAYERED_PROTOCOL;

    // Copy the LSP name
    wcsncpy( pDummyEntry->szProtocol, lpwszLspName, WSAPROTOCOL_LEN );

cleanup:

    if ( NULL != pProtocolInfo )
        FreeProviders( pProtocolInfo );

    return pDummyEntry;
}

//
// Function: InstallIfsLspProtocolChains
//
// Description:
//      This routine installs the layered protocol chains for an IFS based LSP. It
//      assumes the LSP dummy entry is already installed. This function first enumerates
//      the catalog to find the ID of the dummy entry. It then builds the protocol
//      entries for the IFS layered protocols. Note that an IFS entry must be installed
//      such that no non-IFS providers are layered beneath it. This means if the user
//      chooses to install the IFS LSP over a provider which includes non-IFS layers, it
//      must insert itself into the chain such that it is below all non-IFS providers.
//      This means that existing entries need to be modified in order to reflect this
//      ordering. Also in the event that the IFS LSP is inserted into an existing chain
//      this installer still builds a series of standalone entries (i.e. entries that
//      would have existed of the LSPs layered over the IFS LSP were installed after
//      the IFS LSP was).
//
int
InstallIfsLspProtocolChains(
    WINSOCK_CATALOG eCatalog,
    GUID           *Guid,
    WCHAR          *lpszLspName,
    WCHAR          *lpszLspFullPathAndFile,
    DWORD          *pdwCatalogIdArray,
    DWORD           dwCatalogIdArrayCount
    )
{
    WSAPROTOCOL_INFOW  *pProvider = NULL,
                       *pProviderNew = NULL,
                       *pLayeredEntries = NULL,
                       *pEntry = NULL,
                        TempEntry = {0};
    DWORD              *pProviderOrder = NULL,
                        dwDummyLspId;
    WCHAR               wszLspDll[ MAX_PATH ];
    BOOL                bLayeredOverNonIfs = FALSE,
                        bContainsNonIfs = FALSE;
    HRESULT             hr;
    int                 ProviderPathLen = MAX_PATH-1,
                        iProviderCount,
                        iProviderCountNew,
                        LayerIdx,
                        retval = SOCKET_ERROR,
                        err,
                        idx,
                        rc,
                        i, j, k;

    // Enumerate the catalog
    pProvider = EnumerateProviders( eCatalog, &iProviderCount );
    if ( NULL == pProvider )
    {
        fprintf( stderr, "InstallIfsLspProtocolChains: Unable to enumerate catalog\n" );
        goto cleanup;
    }

    // Find the dummy, hidden entry of our new LSP
    dwDummyLspId = GetCatalogIdForProviderGuid( Guid, pProvider, iProviderCount );

    ASSERT( dwDummyLspId != 0 );

    // Allocate space for the protocol chains of the new LSP
    pLayeredEntries = (WSAPROTOCOL_INFOW *) LspAlloc( sizeof(WSAPROTOCOL_INFOW) *
            dwCatalogIdArrayCount, &err );
    if ( NULL == pLayeredEntries )
    {
        fprintf( stderr, "InstallIfsLspProtocolChains: LspAlloc failed: %d\n", err );
        goto cleanup;
    }

    LayerIdx = 0;

    // Build the layered protocol entries as well as a list of those providers which
    // require modification. Whenever an LSP is installed, a number of protocol entries
    // are installed where the first entry in the chain array is the LSP's dummy entry.
    // Addtionally, if we're installing an IFS LSP over an provider whose protocol chain
    // includes non-IFS LSPs, the IFS LSP must be placed in the chain such that no
    // non-IFS LSPs are positioned after it in the chain.

    // Loop through each ID we're layering over
    for(i=0; i < (int)dwCatalogIdArrayCount ;i++)
    {
        for(j=0; j < iProviderCount ;j++)
        {
            printf("Matching selected ID %d to catalog %d\n",
                    pdwCatalogIdArray[ i ], pProvider[ j ].dwCatalogEntryId );

            if ( pdwCatalogIdArray[ i ] == pProvider[ j ].dwCatalogEntryId )
            {
                // Verify the entry has room enough to be layered over
                if ( pProvider[ j ].ProtocolChain.ChainLen >= ( MAX_PROTOCOL_CHAIN - 1 ) )
                {
                    fprintf( stderr, "InstallIfsLspProtocolChain: Too many LSPs installed!\n");
                    goto cleanup;
                }

                // Save off the entry which we're layering over
                memcpy( &pLayeredEntries[ LayerIdx ], &pProvider[ j ],
                        sizeof( pLayeredEntries[ 0  ] ) );

                memcpy( &TempEntry, &pProvider[ j ], sizeof( TempEntry ) );        

                // Fill in the new LSP entry's name
                hr = StringCchPrintfW( pLayeredEntries[ LayerIdx ].szProtocol, WSAPROTOCOL_LEN,
                        L"%s over [%s]",
                        lpszLspName,
                        pProvider[ j ].szProtocol 
                        );
                if ( FAILED( hr ) )
                {
                    fprintf( stderr, "InstallIfsLspProtocolChains: StringCchPrintfW failed: 0x%x\n", hr );
                    goto cleanup;
                }

                // Check whether the selected entry contains non IFS LSPs in its chain
                if ( pProvider[ j ].ProtocolChain.ChainLen >= 2 )
                {
                    for(k=pProvider[ j ].ProtocolChain.ChainLen-2 ; k >= 0 ;k--)
                    {
                        bContainsNonIfs = IsNonIfsProvider( pProvider, iProviderCount, 
                                pProvider[ j ].ProtocolChain.ChainEntries[ k ] );

                        if ( TRUE == bContainsNonIfs )
                        {
                            // Need to modify the pProvider entry to reference the
                            // added LSP entry within its chain

                            // In the 'modified' array make a space at location after 'k'
                            InsertIdIntoProtocolChain( &pProvider[ j ], k+1, UPDATE_LSP_ENTRY );

                            // Save the index to the layer which corresponds to this entry
                            pProvider[ j ].dwProviderReserved = LayerIdx + 1;

                            // Need to fix the 'pLayeredEntry' as well
                            BuildSubsetLspChain( &pLayeredEntries[ LayerIdx ], k+1, dwDummyLspId );

                            pLayeredEntries[ LayerIdx ].dwServiceFlags1 |= XP1_IFS_HANDLES;

                            bLayeredOverNonIfs = TRUE;

                            // Need to insert the IFS provider in all LSPs that  are layered
                            // above the location where the IFS provider was just inserted
                            InsertIfsLspIntoAllChains( &TempEntry, pProvider, iProviderCount, 
                                    LayerIdx + 1, k );

                            break;
                        }
                    }
                }

                // Need to setup the protocol chain in the pLayeredEntry if we haven't
                // already done so above
                if ( TRUE != bContainsNonIfs )
                {
                    InsertIdIntoProtocolChain( &pLayeredEntries[ LayerIdx ], 0, dwDummyLspId );

                    // The second entry is always the ID of the current pProvider[i]
                    //     In case of multiple LSPs then if we didn't do this the [1] index
                    //     would contain the ID of that LSP's dummy entry and not the entry
                    //     itself.
                    pLayeredEntries[ LayerIdx ].ProtocolChain.ChainEntries[ 1 ] = 
                            TempEntry.dwCatalogEntryId;

                    pLayeredEntries[ LayerIdx ].dwServiceFlags1 |= XP1_IFS_HANDLES;
                }

                LayerIdx++;
            }
        }
    }

    ASSERT( LayerIdx == (int)dwCatalogIdArrayCount );

    // Create a unique GUID for each provider to install and install it
    for(i=0;i < (int)dwCatalogIdArrayCount ;i++)
    {
        if ( RPC_S_OK != UuidCreate( &pLayeredEntries[ i ].ProviderId ) )
        {
            fprintf(stderr, "InstallIfsLspProtocolChains: UuidCreate failed: %d\n", GetLastError());
            goto cleanup;
        }

        rc = InstallProvider( eCatalog, &pLayeredEntries[ i ].ProviderId,
                lpszLspFullPathAndFile, &pLayeredEntries[ i ], 1 );
        if ( NO_ERROR != rc )
        {
            fprintf(stderr, "InstallIfsLspProtocolChains: Unable to install the dummy LSP entry!\n");
            goto cleanup;
        }
    }

    if ( TRUE == bLayeredOverNonIfs )
    {
        // Enumerate the catalog again so we can find the catalog IDs

        pProviderNew = EnumerateProviders( eCatalog, &iProviderCountNew );
        if ( NULL == pProviderNew )
        {
            fprintf( stderr, "InstallIfsLspProtocolChains: Unable to enumerate catalog\n" );
            goto cleanup;
        }

        for(i=0; i < (int)dwCatalogIdArrayCount ;i++)
        {
            pLayeredEntries[ i ].dwCatalogEntryId = GetCatalogIdForProviderGuid(
                   &pLayeredEntries[ i ].ProviderId,
                    pProviderNew,
                    iProviderCountNew
                    );

            ASSERT( pLayeredEntries[ i ].dwCatalogEntryId != 0 );
        }

        // Update the protocol chains of the modified entries to point to the just
        //    installed providers
        for(i=0; i < iProviderCount ;i++)
        {
            if ( pProvider[ i ].dwProviderReserved == 0 )
                continue;

            for(j=0; j < pProvider[ i ].ProtocolChain.ChainLen ;j++)
            {
                if ( UPDATE_LSP_ENTRY == pProvider[ i ].ProtocolChain.ChainEntries[ j ] )
                {
                    pProvider[ i ].ProtocolChain.ChainEntries[ j ] = 
                        pLayeredEntries[ pProvider[ i ].dwProviderReserved - 1 ].dwCatalogEntryId;

                    pProvider[ i ].dwProviderReserved = 0;
                }
            }

            // Get the DLL path
            ProviderPathLen = MAX_PATH-1;
            rc = WSCGetProviderPath(
                    &pProvider[ i ].ProviderId,
                     wszLspDll,
                    &ProviderPathLen,
                    &err
                     );
            if ( SOCKET_ERROR == rc )
            {
                fprintf( stderr, "InstallIfsLspProtocolChains: WSCGetProviderPath failed: %d\n", err );
                goto cleanup;
            }

            // Update the providers which were modified
            rc = UpdateProvider( eCatalog, &pProvider[ i ].ProviderId,
                    wszLspDll, &pProvider[ i ], 1, &err );
            if ( SOCKET_ERROR == rc )
            {
                fprintf( stderr, "InstallIfsLspProtocolChains: UpdateProvider failed: %d\n", err );
                goto cleanup;
            }

            printf("Updated entry ID: %d: %S (chain len = %d)\n",
                    pProvider[ i ].dwCatalogEntryId,
                    pProvider[ i ].szProtocol,
                    pProvider[ i ].ProtocolChain.ChainLen
                    );
        }

        FreeProviders( pProvider );
        pProvider = NULL;

        FreeProviders( pProviderNew );
        pProviderNew = NULL;

        
        //WSCUpdateProvider doesn't update the process' copy of the winsock catalog. 
        //By calling cleanup and startup again, it forces a refresh. Otherwise, 
        //the rest of the installer code can't see the changes that were just made. 
        {
            WSADATA wsd;

            WSACleanup();

            WSAStartup( MAKEWORD(2,2), &wsd );
        }
        

        pProvider = EnumerateProviders( eCatalog, &iProviderCount );
        if ( NULL == pProvider )
        {
            fprintf( stderr, "InstallIfsLspProtocolChains: Unable to enumerate catalog\n" );
            goto cleanup;
        }

        // Allocate an array of DWORDs to contain the new catalog ordering
        pProviderOrder = (DWORD *)LspAlloc( iProviderCount * sizeof(DWORD), &err );
        if ( NULL == pProviderOrder )
        {
            fprintf( stderr, "InstallIfsLspProtocolChains: Unable to enumerate catalog\n" );
            goto cleanup;
        }

        // First add the entries we layered over first
        idx = 0;
        for(i=0; i < (int)dwCatalogIdArrayCount ;i++)
        {
            pEntry = FindProviderById( pdwCatalogIdArray[ i ], pProvider, iProviderCount );
            if ( NULL == pEntry )
            {
                fprintf(stderr, "InstallIfsLspProtocolChain: Unable to find entry to reorder catalog!\n");
                goto cleanup;
            }

            pEntry->dwProviderReserved = 1;

            pProviderOrder[ idx++ ] = pEntry->dwCatalogEntryId;
        }

        // Now go through the protocol chain of the entries we layered over and put those
        //    LSP entries next in the new order
        for(i=0; i < (int)dwCatalogIdArrayCount ;i++)
        {
            pEntry = FindProviderById( pdwCatalogIdArray[ i ], pProvider, iProviderCount );
            if ( NULL == pEntry )
            {
                fprintf(stderr, "InstallIfsLspProtocolChain: Unable to find entry to reorder catalog!\n");
                goto cleanup;
            }

            printf("Looping through: %d: %S (chain len = %d)\n", 
                    pEntry->dwCatalogEntryId,
                    pEntry->szProtocol,
                    pEntry->ProtocolChain.ChainLen );

            for(j=1; j < pEntry->ProtocolChain.ChainLen-1 ;j++)
            {
                dwDummyLspId = FindDummyIdFromProtocolChainId(
                        pEntry->ProtocolChain.ChainEntries[ j ],
                        pProvider,
                        iProviderCount
                        );

                printf("   Finding dummy ID for chain entry: %d is %d\n", 
                        pEntry->ProtocolChain.ChainEntries[ j ],
                        dwDummyLspId
                        );

                for(k=0; k < iProviderCount ;k++)
                {
                    if ( ( pProvider[ k ].ProtocolChain.ChainLen >= 2 ) &&
                         ( pProvider[ k ].ProtocolChain.ChainEntries[ 0 ] == dwDummyLspId ) &&
                         ( pProvider[ k ].dwProviderReserved == 0 )
                       )
                    {
                        pProviderOrder[ idx++ ] = pProvider[ k ].dwCatalogEntryId;
                        pProvider[ k ].dwProviderReserved = 1;

                        printf("      Adding: %d\n", pProvider[ k ].dwCatalogEntryId );
                    }
                }
            }
        }

        // Now any catalog entry that wasn't already copied, copy it
        for(i=0; i < iProviderCount ;i++)
        {
            if ( pProvider[ i ].dwProviderReserved == 0 )
                pProviderOrder[ idx++ ] = pProvider[ i ].dwCatalogEntryId;
        }

        ASSERT( idx == iProviderCount );

        // Write the new catalog order
        rc = WriteProviderOrder( eCatalog, pProviderOrder, iProviderCount, &err );
        if ( NO_ERROR != rc )
        {
            fprintf( stderr, "InstallIfsLspProtocolChains: WriteProviderOrder failed: %d\n",
                    err );
            goto cleanup;
        }
    }
    else
    {
        //
        // Reorder the winsock catalog so the layered chain entries appear first.
        // Since we didn't have to modify any existing entries, all we need to do is
        //    move the added entries to the head of the catalog
        // 
        rc = ReorderCatalog( eCatalog, dwDummyLspId );
        if ( NO_ERROR != rc )
        {
            fprintf(stderr, "InstallIfsLspProtocolChains: Unable to reorder Winsock catalog!\n");
            goto cleanup;
        }
    }

    retval = NO_ERROR;

cleanup:
    
    if ( NULL != pProvider )
    {
        FreeProviders( pProvider );
        pProvider = NULL;
    }

    if ( NULL != pProviderNew )
    {
        FreeProviders( pProviderNew );
        pProviderNew = NULL;
    }

    if ( NULL != pProviderOrder )
    {
        LspFree( pProviderOrder );
        pProviderOrder = NULL;
    }

    return retval;
}

//
// Function: InstallNonIfsLspProtocolChains
//
// Description:
//      This function builds and install the protocol chain entries associated with
//      a non-IFS LSP. The caling routine installs the dummy, hidden entry in the
//      catalog and this routine enumerates the catalog, finds the dummy entry,
//      builds the layered chain entries, and installs them into the catalog.
//
int
InstallNonIfsLspProtocolChains(
    WINSOCK_CATALOG eCatalog,
    GUID           *Guid,
    WCHAR          *lpszLspName,
    WCHAR          *lpszLspFullPathAndFile,
    DWORD          *pdwCatalogIdArray,
    DWORD           dwCatalogIdArrayCount
    )
{
    WSAPROTOCOL_INFOW   *pProvider = NULL,
                        *pLayeredEntries = NULL;
    DWORD                dwDummyLspId = 0;
    INT                  iProviderCount = 0,
                         retval = SOCKET_ERROR,
                         idx,
                         err,
                         rc,
                         i, j;
    HRESULT              hr;

    // Enumerate the catalog
    pProvider = EnumerateProviders( eCatalog, &iProviderCount );
    if ( NULL == pProvider )
    {
        fprintf( stderr, "InstallNonIfsLspProtocolChain: Unable to enumerate catalog\n" );
        goto cleanup;
    }

    pLayeredEntries = (WSAPROTOCOL_INFOW *) LspAlloc( sizeof(WSAPROTOCOL_INFOW) *
            dwCatalogIdArrayCount, &err );
    if ( NULL == pLayeredEntries )
    {
        fprintf( stderr, "InstallNonIfsLspProtocolChain: LspAlloc failed: %d\n", err );
        goto cleanup;
    }

    // Find the dummy entry so we can extract its catalog ID
    dwDummyLspId = GetCatalogIdForProviderGuid( Guid, pProvider, iProviderCount );

    ASSERT( dwDummyLspId != 0 );

    // Go through the catalog and build the layered entries
    idx = 0;
    for(i=0; i < iProviderCount ;i++)
    {
        for(j=0; j < (int) dwCatalogIdArrayCount ;j++)
        {
            if ( pProvider[ i ].dwCatalogEntryId == pdwCatalogIdArray[ j ] )
            {
                if ( pProvider[ i ].ProtocolChain.ChainLen >= ( MAX_PROTOCOL_CHAIN - 1 ) )
                {
                    fprintf( stderr, "InstallNonIfsLspProtocolchain: Too many LSPs installed!\n");
                    goto cleanup;
                }

                memcpy( &pLayeredEntries[ idx ], &pProvider[ i ], sizeof( WSAPROTOCOL_INFOW ) );

                // Put our LSP name in the protocol field
                hr = StringCchPrintfW( pLayeredEntries[ idx ].szProtocol, WSAPROTOCOL_LEN,
                        L"%s over [%s]",
                        lpszLspName,
                        pProvider[ i ].szProtocol
                        );
                if ( FAILED( hr ) )
                {
                    fprintf( stderr, "InstallNonIfsLspProtocolChain: StringCchPrintfW failed: 0x%x\n", hr );
                    goto cleanup;
                }

                // Move all the protocol chain entries down by 1 position and insert 
                // the dummy entry id at the head
                InsertIdIntoProtocolChain( &pLayeredEntries[ idx ], 0, dwDummyLspId );

                // The second entry is always the ID of the current pProvider[i]
                //     In case of multiple LSPs then if we didn't do this the [1] index
                //     would contain the ID of that LSP's dummy entry and not the entry
                //     itself.
                pLayeredEntries[ idx ].ProtocolChain.ChainEntries[ 1 ] = 
                        pProvider[ i ].dwCatalogEntryId;

                // Remove the IFS flag 
                pLayeredEntries[ idx ].dwServiceFlags1 &= (~XP1_IFS_HANDLES);

                idx++;
            }
        }
    }

    for(i=0; i < (int)dwCatalogIdArrayCount ;i++)
    {
        // Create a GUID for the protocol chain entries
        if ( UuidCreate( &pLayeredEntries[ i ].ProviderId ) != RPC_S_OK )
        {
            fprintf(stderr, "InstallNonIfsLspProtocolChains: UuidCreate failed: %d\n", GetLastError());
            goto cleanup;
        }

        // Install the layered chain providers
        rc = InstallProvider(
                eCatalog, 
               &pLayeredEntries[ i ].ProviderId, 
                lpszLspFullPathAndFile,
               &pLayeredEntries[ i ], 
                1
                );
        if ( NO_ERROR != rc )
        {
            fprintf(stderr, "InstallNonIfsLspProtocolChains: Unable to install layered chain entries!\n");
            goto cleanup;
        }
    }

    // Reorder the winsock catalog so the layered chain entries appear first
    rc = ReorderCatalog( eCatalog, dwDummyLspId );
    if ( NO_ERROR != rc )
    {
        fprintf(stderr, "InstallNonIfsLspProtocolChains: Unable to reorder Winsock catalog!\n");
        goto cleanup;
    }

    retval = NO_ERROR;

cleanup:

    if ( NULL != pProvider )
        FreeProviders( pProvider );

    if ( NULL != pLayeredEntries )
        LspFree( pLayeredEntries );

    return retval;
}

//
// Function: InsertIfsLspIntoAllChains
//
// Description:
//    This routine takes a Winsock catalog entry where an IFS LSP entry is being
//    inserted in the middle of the chain and the same IFS LSP entry needs to be 
//    installed in the individual provider entries for every LSP referenced in the
//    chain above the IFS provider position. This function traverses the protocol
//    chain for all entries above the inserted IFS entry, looks up the provider
//    belonging to that entry, and inserts the IFS LSP into it's chain.
//
int
InsertIfsLspIntoAllChains( 
    WSAPROTOCOL_INFOW  *OriginalEntry,    // Original (unmodified) entry to follow chains
    WSAPROTOCOL_INFOW  *Catalog,          // Array of catalog entries
    int                 CatalogCount,     // Number of entries in Catalog array
    int                 IfsEntryIdx,      // Index into IFS standalone entry array
    int                 ChainIdx          // Chain index in OriginalEntry to start at
    )
{
    WSAPROTOCOL_INFOW   TempEntry = {0};
    int                 Idx, i, j, k;

    for(i=ChainIdx; i > 0 ;i--)
    {
        #ifdef DBG
        printf( "Looking for entry: %d\n", OriginalEntry->ProtocolChain.ChainEntries[ i ] );
        #endif

        for(j=0; j < CatalogCount ;j++)
        {
            if ( Catalog[ j ].dwCatalogEntryId == OriginalEntry->ProtocolChain.ChainEntries[ i ] ) 
            {
                printf( "Found match: %ws\n", Catalog[ j ].szProtocol );
                Idx = j;

                if ( Catalog[ j ].ProtocolChain.ChainLen == LAYERED_PROTOCOL )
                {
                    Idx = -1;

                    // Not good. The catalog ID in the chain points to the dummy
                    // entry. We'll need to do some other heuristic to find the
                    // "right" entry.
                    for(k=0; k < CatalogCount ;k++)
                    {
                        if ( ( OriginalEntry->iAddressFamily == Catalog[ k ].iAddressFamily ) &&
                             ( OriginalEntry->iSocketType == Catalog[ k ].iSocketType ) && 
                             ( OriginalEntry->iProtocol == Catalog[ k ].iProtocol ) &&
                             ( (i+1) == Catalog[ k ].ProtocolChain.ChainLen )
                           )
                        {
                            Idx = k;
                            break;
                        }
                    }
                }

                if ( Idx != -1 )
                {
                    // Found a match and need to insert the new IFS LSP into the chain
                    memcpy( &TempEntry, &Catalog[ Idx ], sizeof( TempEntry ) );

                    if ( Catalog[ Idx ].ProtocolChain.ChainLen >= 2 )
                    {
                        for(k=Catalog[ Idx ].ProtocolChain.ChainLen-2 ; k >= 0 ;k--)
                        {
                            if ( TRUE == IsNonIfsProvider( Catalog, CatalogCount, 
                                    Catalog[ Idx ].ProtocolChain.ChainEntries[ k ] ) )
                            {
                                // K points to first non-IFS provider - insert after
                                InsertIdIntoProtocolChain( &Catalog[ Idx ], k+1, UPDATE_LSP_ENTRY );

                                // Save the index to the layer which corresponds to this entry
                                Catalog[ Idx ].dwProviderReserved = IfsEntryIdx;
                            }
                        }
                    }
                }
                else
                {
                    printf( "????? Index not found ????\n" );
                }

                break;
            }
        }
    }

    return 0;
}

//
// Function: ReorderCatalog
//
// Description:
//    This routine reorders the Winsock catalog such that those entries
//    which reference the given catalog ID (dwLayerId) as the first entry
//    in the chain array occur at the head of the catalog. This routine
//    also operates on the specified Winsock catalog.
//
int 
ReorderCatalog(
    WINSOCK_CATALOG Catalog, 
    DWORD           dwLayeredId
    )
{
    DWORD     *pdwProtocolOrder = NULL;
    INT        iProviderCount,
               ErrorCode,
               rc = SOCKET_ERROR;

#ifdef _WIN64
    if ( ( LspCatalog32Only == Catalog ) || ( LspCatalogBoth == Catalog ) )
    {
        printf("Reordering 32-bit Winsock catalog...\n");
        pdwProtocolOrder = ReorderACatalog(
                LspCatalog32Only, 
                dwLayeredId, 
               &iProviderCount
                );
        if ( NULL == pdwProtocolOrder )
        {
            fprintf( stderr, "ReorderCatalog: ReorderACatalog failed!\n" );
            goto cleanup;
        }
        
        rc = WriteProviderOrder( LspCatalog32Only, pdwProtocolOrder, iProviderCount, &ErrorCode );
        if ( SOCKET_ERROR == rc )
        {
            fprintf( stderr, "ReorderCatalog: Reorder of 32-bit catalog failed: %d\n", rc );
        }
    }
    if ( ( LspCatalog64Only == Catalog ) || ( LspCatalogBoth == Catalog ) )
    {
        printf("Reordering 64-bit Winsock catalog...\n");
        pdwProtocolOrder = ReorderACatalog(
                LspCatalog64Only, 
                dwLayeredId, 
               &iProviderCount
                );
        if ( NULL == pdwProtocolOrder )
        {
            fprintf(stderr, "ReorderCatalog: ReorderACatalog failed!\n");
            goto cleanup;
        }
       
        rc = WriteProviderOrder( LspCatalog64Only, pdwProtocolOrder, iProviderCount, &ErrorCode );
        if ( SOCKET_ERROR == rc )
        {
            fprintf(stderr, "ReorderCatalog: Reorder of 64-bit catalog failed: %d\n", rc);
        }
    }
#else
    if ( ( LspCatalog32Only == Catalog ) || ( LspCatalogBoth == Catalog ) )
    {
        printf("Reordering 32-bit Winsock catalog...\n");
        pdwProtocolOrder = ReorderACatalog(
                LspCatalog32Only, 
                dwLayeredId, 
               &iProviderCount
                );
        if ( NULL == pdwProtocolOrder )
        {
            fprintf( stderr, "ReorderCatalog: ReorderACatalog failed!\n" );
            goto cleanup;
        }
        
        rc = WriteProviderOrder( LspCatalog32Only, pdwProtocolOrder, iProviderCount, &ErrorCode );
        if ( SOCKET_ERROR == rc )
        {
            fprintf(stderr, "ReorderCatalog: Reorder of 32-bit catalog failed: %d\n", rc);
        }
    }
#endif

cleanup:

    if ( NULL != pdwProtocolOrder )
        LspFree( pdwProtocolOrder );

    return rc;
}

//
// Function: ReorderACatalog
//
// Description:
//    This routine enumerates a single catalog (32 or 64 bit) and reorders
//    the catalog according to the supplied catalog ID. That is, any provider
//    with that ID at the head of it's protocol chain is moved to the beginning
//    of the catalog.
//
DWORD *
ReorderACatalog(
    WINSOCK_CATALOG Catalog,
    DWORD           dwLayerId,
    INT            *dwEntryCount
    )
{
    WSAPROTOCOL_INFOW   *pProvider = NULL;
    DWORD               *pdwProtocolOrder = NULL;
    INT                  iProviderCount = 0,
                         idx,
                         err,
                         i;

    // Validate parameters
    if ( ( NULL == dwEntryCount ) || ( LspCatalogBoth == Catalog ) )
        return NULL;

    // Enumerate the catalog
    pProvider = EnumerateProviders( Catalog, &iProviderCount );
    if ( NULL == pProvider )
    {
        fprintf( stderr, "ReorderACatalog: Unable to enumerate Winsock catalog!\n" );
        goto cleanup;
    }

    // Allocate space for the array of catalog IDs (the catalog order)
    pdwProtocolOrder = (DWORD *) LspAlloc(
            sizeof( DWORD ) * iProviderCount,
           &err
            );
    if ( NULL == pdwProtocolOrder )
    {
        fprintf(stderr, "ReorderACatalog: LspAlloc failed: %d\n", GetLastError());
        goto cleanup;
    }

    idx = 0;

    // First put all the layered entries at the head of the catalog
    for(i=0; i < iProviderCount ;i++)
    {
        if ( TRUE == IsIdInChain( &pProvider[ i ], dwLayerId ) )
        {
            pdwProtocolOrder[ idx++ ] = pProvider[ i ].dwCatalogEntryId;
        }
    }

    // Put the remaining entries after the layered chain entries
    for(i=0; i < iProviderCount ;i++)
    {
        if ( FALSE == IsIdInChain( &pProvider[ i ], dwLayerId ) )
        {
            pdwProtocolOrder[ idx++ ] = pProvider[ i ].dwCatalogEntryId;
        }
    }

cleanup:

    if (pProvider)
        FreeProviders(pProvider);

    // Update the count
    *dwEntryCount = iProviderCount;


    return pdwProtocolOrder;
}

//
// Function: WriteProviderOrder
//
// Description:
//    This function wraps the WSCWriteProviderOrder function and calls the
//    appropriate function depending on the Winsock catalog being modified
//    (the 32-bit and/or 64-bit Winsock catalogs).
//
int
WriteProviderOrder(
    WINSOCK_CATALOG Catalog,
    DWORD          *pdwCatalogOrder,
    DWORD           dwNumberOfEntries,
    INT            *lpErrno
    )
{
    int     rc = NO_ERROR;

#ifdef _WIN64
    if ( LspCatalog32Only == Catalog )
    {
        rc = WSCWriteProviderOrder32( pdwCatalogOrder, dwNumberOfEntries );
    }
    else if ( LspCatalog64Only == Catalog )
    {
        rc = WSCWriteProviderOrder( pdwCatalogOrder, dwNumberOfEntries );
    }
#else
    if ( LspCatalog32Only == Catalog )
    {
        rc = WSCWriteProviderOrder(pdwCatalogOrder, dwNumberOfEntries );
    }
    else
    {
        fprintf( stderr, "WriteProviderOrder: Unable to manipulate 64-bit catalog from "
                "a 32-bit process\n" );
    }
#endif
    if ( 0 != rc )
    {
        *lpErrno = rc;
        fprintf( stderr, "WriteProviderOrder: WSCWriteProviderOrder failed: %d\n", *lpErrno );
        rc = SOCKET_ERROR;
    }

    return rc;
}


//
// Function: InstallProviderVista
//
// Description:
//    On Windows Vista and later there is a new LSP install function
//    (WSCInstallProviderAndChains) that performs all necessary installation
//    steps in a single call (instead of multiple WSCInstallProvider calls).
//    This function dynamically loads the function and invokes it to install
//    the LSP. The function either expects a NULL list of WSAPROTOCOL_INFOW
//    structures in which case it installs the LSP over each unique address
//    family, socket type, and protocol base provider on the system. Otherwise,
//    if the pdwCatalogIdArray specifies exact providers, this function builds
//    a list of those provider structures in which case the install API installs
//    the LSP over each instance of the address family, socket type and protocol
//    specified in the array.
//
int
InstallProviderVista(
        WINSOCK_CATALOG eCatalog,               // Which catalog to install LSP into
        __in_z WCHAR   *lpszLspName,            // String name of LSP
        __in_z WCHAR   *lpszLspPathAndFile,     // Location of LSP dll and dll name
        LPGUID          providerGuid,
        DWORD           dwCatalogIdArrayCount,  // Number of entries in pdwCatalogIdArray
        DWORD          *pdwCatalogIdArray,      // Array of IDs to install over
        BOOL            IfsProvider,
        BOOL            InstallOverAll
        )
{
    LPWSCINSTALLPROVIDERANDCHAINS lpInstallProviderAndChains;
    WSAPROTOCOL_INFOW *protocolList = NULL;
    WSAPROTOCOL_INFOW *pEnumProviders = NULL;
    HMODULE hMod = NULL;
    DWORD dwEntryCount;
    char *lpInstallFunction = NULL;
    INT iEnumProviderCount;
    int rc, i, j, error;
    

    rc = SOCKET_ERROR;

    //
    // Dynamically load the function in order for this installer to run properly
    // on downlevel OSes
    //
    hMod = LoadLibrary("ws2_32.dll");
    if ( NULL == hMod )
    {
        fprintf(stderr, "Unable to load ws2_32.dll!\n");
        goto cleanup;
    }

#ifdef _WIN64
    if ( ( eCatalog == LspCatalog32Only ) || ( eCatalog == LspCatalog64Only ) )
    {
        fprintf(stderr, "New install API always installs into both catalogs!\n");
        goto cleanup;
    }
    else 
    {
        lpInstallFunction = "WSCInstallProviderAndChains64_32";
    }
#else
    if ( ( eCatalog == LspCatalog64Only) || ( eCatalog == LspCatalogBoth ) )
    {
        fprintf(stderr, "Cannot install into 64-bit catalog from 32-bit process\n");
        goto cleanup;
    }
    else
    {
        lpInstallFunction = "WSCInstallProviderAndChains";
    }
#endif

    // Load the new install function
    lpInstallProviderAndChains = (LPWSCINSTALLPROVIDERANDCHAINS) GetProcAddress( 
            hMod,
            lpInstallFunction
            );
    if ( NULL == lpInstallProviderAndChains )
    {
        fprintf( stderr, "InstallLsp: Unable to load WSCInstallProviderAndChains function!\n");
        rc = SOCKET_ERROR;
        goto cleanup;
    }

    if ( InstallOverAll )
    {
        //
        // Install over all unique BSPs on the system so pass NULL for the provider list
        //

        rc = lpInstallProviderAndChains(
                providerGuid,
                lpszLspPathAndFile,
#ifdef _WIN64
                lpszLspPathAndFile,
#endif
                lpszLspName,
                ( IfsProvider ? XP1_IFS_HANDLES : 0 ),
                NULL,
                NULL,
                NULL,
               &error
                );
        if ( SOCKET_ERROR == rc )
        {
            fprintf(stderr, "InstallProviderVista: %s failed: %d\n", 
                    lpInstallFunction, error );
            goto cleanup;
        }
    }
    else
    {
        //
        // User specified a subset of providers to install over so build a list of
        //    the corresponding WSAPROTOCOL_INFOW structures to pass to install call
        //

        protocolList = (WSAPROTOCOL_INFOW *) LspAlloc( sizeof(WSAPROTOCOL_INFOW) *
                dwCatalogIdArrayCount, &error);
        if ( NULL == protocolList )
        {
            fprintf(stderr, "InstallProviderVista: Out of memory!\n");
            rc = SOCKET_ERROR;
            goto cleanup;
        }

        pEnumProviders = EnumerateProviders( eCatalog, &iEnumProviderCount );
        if ( NULL == pEnumProviders )
        {
            fprintf(stderr, "InstallProviderVista: Unable to enumerate catalog!\n");
            rc = SOCKET_ERROR;
            goto cleanup;
        }

        // Build a list of protocol structures to layer over
        dwEntryCount = 0;
        for(i=0; i < (int)dwCatalogIdArrayCount ;i++)
        {
            for(j=0; j < iEnumProviderCount ;j++)
            {
                if ( pdwCatalogIdArray[i] == pEnumProviders[j].dwCatalogEntryId )
                {
                    memcpy( &protocolList[dwEntryCount++], &pEnumProviders[j], sizeof(WSAPROTOCOL_INFOW) );
                }
            }
        }

        rc = lpInstallProviderAndChains(
                providerGuid,
                lpszLspPathAndFile,
#ifdef _WIN64
                lpszLspPathAndFile,
#endif
                lpszLspName,
                ( IfsProvider ? XP1_IFS_HANDLES : 0 ),
                protocolList,
                dwEntryCount,
                NULL,
               &error
                );
        if ( SOCKET_ERROR == rc )
        {
            fprintf(stderr, "InstallProviderVista: %s failed: %d\n", 
                    lpInstallFunction, error );
            goto cleanup;
        }
    }

    rc = NO_ERROR;

cleanup:

    if ( NULL != hMod )
        FreeLibrary( hMod );

    if ( NULL != pEnumProviders )
        FreeProviders( pEnumProviders );

    if ( NULL != protocolList )
        LspFree( protocolList );

    return rc;
}
