/********************************************************************/
/**               Copyright(c) 1995 Microsoft Corporation.         **/
/********************************************************************/

//***
//
// Filename:    ifapi.c
//
// Description: Contains code to process interface admin api requests
//
// History:     May 11,1995     NarenG      Created original version.
//
#include "dimsvcp.h"
#include <ras.h>
#include <dimsvc.h>     // Generated by MIDL
#include "rpbk.h"
#include <mprapip.h>


#define MAX_GET_INFO_RETRIES    3

//
// DoStartRouter API interface
//

typedef struct _START_ROUTER_DATA
{
    DWORD   dwTransportId;
    DWORD   dwInterfaceInfoSize;
    LPBYTE  pInterfaceInfo;
    DWORD   dwGlobalInfoSize;
    LPBYTE  pGlobalInfo;

} START_ROUTER_DATA, *PSTART_ROUTER_DATA;


DWORD
RRouterDeviceEnum(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       dwLevel,
    IN OUT  PDIM_INFORMATION_CONTAINER  pInfoStruct,
    OUT     LPDWORD                     lpdwTotalEntries
    )
{
    DWORD                       dwAccessStatus;
    DWORD                       dwRetCode = NO_ERROR;
    LPRASDEVINFO                lpRasDevInfo = NULL;
    DWORD                       dwSize = 0, dwRetSize = 0;
    DWORD                       dwcDevices = 0;
    DWORD                       i = 0;
    MPR_DEVICE_0*               pDev0 = NULL;

    //
    // Check if caller has access
    //
    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwLevel != 0 ) 
    {
        return( ERROR_NOT_SUPPORTED );
    }

    do
    {
        *lpdwTotalEntries = 0;
        
        // Find out how much memory to allocate
        //
        dwRetCode = RasEnumDevices(
                        NULL,
                        &dwSize,
                        &dwcDevices);
        if ( ( dwRetCode != NO_ERROR ) &&
             ( dwRetCode != ERROR_BUFFER_TOO_SMALL )
           )
        {
            break;
        }

        if ( dwSize == 0 )
        {
            dwRetCode = NO_ERROR;
            break;
        }

        // Allocate the ras device info
        //
        lpRasDevInfo = LOCAL_ALLOC ( LPTR, dwSize );
        if ( lpRasDevInfo == NULL )
        {
            dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
            break;
        }
        ZeroMemory(lpRasDevInfo, dwSize );

        // Allocate the return value
        //
        dwRetSize = dwcDevices * sizeof(MPR_DEVICE_0);
        pInfoStruct->pBuffer = MIDL_user_allocate( dwRetSize );
        if ( pInfoStruct->pBuffer == NULL )
        {
            dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
            break;
        }
        ZeroMemory(pInfoStruct->pBuffer, dwRetSize );
        pDev0 = (MPR_DEVICE_0*) pInfoStruct->pBuffer;

        // Read in the device info
        lpRasDevInfo->dwSize = sizeof(RASDEVINFO);
        dwRetCode = RasEnumDevices(
                        lpRasDevInfo,
                        &dwSize,
                        &dwcDevices);
        if ( dwRetCode == ERROR_BUFFER_TOO_SMALL )
        {
            dwRetCode = NO_ERROR;
        }
        if ( dwRetCode != NO_ERROR )
        {
            break;
        }

        // Copy the device info over
        for ( i = 0; i < dwcDevices; i++ )
        {
            wcscpy(pDev0[i].szDeviceType, lpRasDevInfo[i].szDeviceType);
            wcscpy(pDev0[i].szDeviceName, lpRasDevInfo[i].szDeviceName);
        }

        // Everything's ok, go ahead and set the return values
        //
        *lpdwTotalEntries = dwcDevices;
        pInfoStruct->dwBufferSize = dwRetSize;
        
    } while( FALSE );

    // Cleanup
    {
        if ( lpRasDevInfo )
        {
            LOCAL_FREE( lpRasDevInfo );
        }

        if ( dwRetCode != NO_ERROR )
        {
            if ( pInfoStruct->pBuffer )
            {
                MIDL_user_free ( pInfoStruct->pBuffer );
                pInfoStruct->pBuffer = NULL;
            }
        }
    }

    TracePrintfExA( gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "DeviceEnum returned %d", dwRetCode );

    return( dwRetCode );
}

//**
//
// Call:        DoStartRouter
//
// Returns:     NO_ERROR         - Success
//              Non-zero returns - Failure
//
// Description:
//
VOID
DoStartRouter(
    IN PVOID pParameter
)
{
    DWORD   dwRetCode     = NO_ERROR;
    DWORD   dwIndex       = gblDIMConfigInfo.dwNumRouterManagers;
    ROUTER_INTERFACE_OBJECT * pIfObject;
    START_ROUTER_DATA *       pStartRouterData = (START_ROUTER_DATA *)pParameter;    
    DWORD       (*StartRouter)(
                        IN OUT DIM_ROUTER_INTERFACE * pDimRouterIf,
                        IN     BOOL                   fLANModeOnly,
                        IN     LPVOID                 pGlobalInfo );

    //
    // Wait for setup to finsh
    //

    Sleep( 15000 );

    //
    // Load the StartRouter
    //

    StartRouter = (PVOID)GetProcAddress( gblRouterManagers[dwIndex].hModule,
                                         "StartRouter" );

    if ( StartRouter == NULL )
    {
        if ( pStartRouterData->pInterfaceInfo != NULL )
        {
            LOCAL_FREE( pStartRouterData->pInterfaceInfo );
        }

        if ( pStartRouterData->pGlobalInfo != NULL )
        {
            LOCAL_FREE( pStartRouterData->pGlobalInfo );
        }

        LOCAL_FREE( pStartRouterData );

        return;
    }

    gblRouterManagers[dwIndex].DdmRouterIf.ConnectInterface     
                                                    = DIMConnectInterface;
    gblRouterManagers[dwIndex].DdmRouterIf.DisconnectInterface 
                                                    = DIMDisconnectInterface;
    gblRouterManagers[dwIndex].DdmRouterIf.SaveInterfaceInfo 
                                                    = DIMSaveInterfaceInfo;
    gblRouterManagers[dwIndex].DdmRouterIf.RestoreInterfaceInfo
                                                    = DIMRestoreInterfaceInfo;
    gblRouterManagers[dwIndex].DdmRouterIf.SaveGlobalInfo
                                                    = DIMSaveGlobalInfo;
    gblRouterManagers[dwIndex].DdmRouterIf.RouterStopped
                                                    = DIMRouterStopped;
    gblRouterManagers[dwIndex].DdmRouterIf.InterfaceEnabled
                                                    = DIMInterfaceEnabled;
    dwRetCode = (*StartRouter)(
                            &(gblRouterManagers[dwIndex].DdmRouterIf),
                            gblDIMConfigInfo.dwRouterRole == ROUTER_ROLE_LAN,
                            pStartRouterData->pGlobalInfo );

    if ( dwRetCode != NO_ERROR )
    {
        DIMTRACE1( "Start Router failed with %d", dwRetCode );

        if ( pStartRouterData->pInterfaceInfo != NULL )
        {
            LOCAL_FREE( pStartRouterData->pInterfaceInfo );
        }

        if ( pStartRouterData->pGlobalInfo != NULL )
        {
            LOCAL_FREE( pStartRouterData->pGlobalInfo );
        }
    
        LOCAL_FREE( pStartRouterData );

        return;
    }

    //
    // Save the global client info
    //

    if ( pStartRouterData->pInterfaceInfo == NULL )
    {
        gblRouterManagers[dwIndex].pDefaultClientInterface      = NULL;
        gblRouterManagers[dwIndex].dwDefaultClientInterfaceSize = 0;
    }
    else
    {
        LPBYTE  lpData = LOCAL_ALLOC(LPTR,pStartRouterData->dwInterfaceInfoSize);

        if ( lpData == NULL )
        {
            if ( pStartRouterData->pInterfaceInfo != NULL )
            {
                LOCAL_FREE( pStartRouterData->pInterfaceInfo );
            }

            if ( pStartRouterData->pGlobalInfo != NULL )
            {
                LOCAL_FREE( pStartRouterData->pGlobalInfo );
            }

            LOCAL_FREE( pStartRouterData );

            return;
        }

        CopyMemory( lpData,
                    pStartRouterData->pInterfaceInfo,
                    pStartRouterData->dwInterfaceInfoSize );

        gblRouterManagers[dwIndex].pDefaultClientInterface = lpData;

        gblRouterManagers[dwIndex].dwDefaultClientInterfaceSize
                                            = pStartRouterData->dwInterfaceInfoSize;
    }

    gblRouterManagers[dwIndex].fIsRunning = TRUE;

    gblDIMConfigInfo.dwNumRouterManagers++;

    //
    // Register all interfaces with the router manager
    //

    AddInterfacesToRouterManager( NULL, pStartRouterData->dwTransportId );

    //
    // Notify router manager that all interfaces have been added to the
    // router
    //

    gblRouterManagers[dwIndex].DdmRouterIf.RouterBootComplete();

    if ( gblDIMConfigInfo.dwRouterRole != ROUTER_ROLE_LAN )
    {
        DWORD (*DDMTransportCreate)( DWORD ) = 
                     (DWORD(*)( DWORD ))GetDDMEntryPoint("DDMTransportCreate");

        if(NULL == DDMTransportCreate)
        {
            return ;
        }

        DDMTransportCreate( pStartRouterData->dwTransportId );
    }

    //
    // We can only make this call while not holding the interface table
    // lock
    //

    DIMTRACE( "Setting router attributes in the identity object" );

    RouterIdentityObjectUpdateAttributes( FALSE, FALSE );

    if ( pStartRouterData->pInterfaceInfo != NULL )
    {
        LOCAL_FREE( pStartRouterData->pInterfaceInfo );
    }

    if ( pStartRouterData->pGlobalInfo != NULL )
    {
        LOCAL_FREE( pStartRouterData->pGlobalInfo );
    }

    LOCAL_FREE( pStartRouterData );
}

//**
//
// Call:        RRouterInterfaceTransportCreate
//
// Returns:     NO_ERROR         - Success
//              Non-zero returns - Failure
//
// Description:
//
DWORD
RRouterInterfaceTransportCreate(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       dwTransportId,
    IN      LPWSTR                      lpwsTransportName,
    IN      PDIM_INTERFACE_CONTAINER    pInfoStruct,
    IN      LPWSTR                      lpwsDLLPath 
)
{
    DWORD               dwAccessStatus      = 0;
    DWORD               dwRetCode           = NO_ERROR;
    DWORD               dwIndex             = gblDIMConfigInfo.dwNumRouterManagers;
    START_ROUTER_DATA * pStartRouterData    = NULL;

    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( GetTransportIndex( dwTransportId ) != (DWORD) -1 )
    {
        return( ERROR_PROTOCOL_ALREADY_INSTALLED );
    }

    if ( ( dwTransportId == PID_IP ) || ( dwTransportId == PID_IPX ) )
    {
        DWORD       cbSize           = 0;
        WCHAR *     pDllExpandedPath = NULL;

        //
        // Replace the %SystemRoot% with the actual path.
        //

        cbSize = ExpandEnvironmentStrings( lpwsDLLPath, NULL, 0 );

        if ( cbSize == 0 )
        {
            return( GetLastError() );
        }
        else
        {
            cbSize *= sizeof( WCHAR );
        }

        pDllExpandedPath = (LPWSTR)LOCAL_ALLOC( LPTR, cbSize*sizeof(WCHAR) );

        if ( pDllExpandedPath == (LPWSTR)NULL )
        {
            return( GetLastError() );
        }

        cbSize = ExpandEnvironmentStrings(  lpwsDLLPath,
                                            pDllExpandedPath,
                                            cbSize );
        if ( cbSize == 0 )
        {
            LOCAL_FREE( pDllExpandedPath );

            return( GetLastError() );
        }

        //
        // Load the DLL
        //

        gblRouterManagers[dwIndex].hModule = LoadLibrary( pDllExpandedPath );

        LOCAL_FREE( pDllExpandedPath );

        if ( gblRouterManagers[dwIndex].hModule == NULL )
        {
            return( GetLastError() );
        }

        pStartRouterData = LOCAL_ALLOC( LPTR, sizeof( START_ROUTER_DATA ) );

        if ( pStartRouterData == NULL )
        {
            return( GetLastError() );
        } 

        pStartRouterData->dwTransportId         = dwTransportId;
        pStartRouterData->dwInterfaceInfoSize   = pInfoStruct->dwInterfaceInfoSize;

        if ( pStartRouterData->dwInterfaceInfoSize != 0 )
        {
            pStartRouterData->pInterfaceInfo = LOCAL_ALLOC( LPTR,
                                                  pStartRouterData->dwInterfaceInfoSize );
    
            if ( pStartRouterData->pInterfaceInfo == NULL )
            {
                LOCAL_FREE( pStartRouterData );

                return( GetLastError() );
            }
            else
            {
                CopyMemory( pStartRouterData->pInterfaceInfo,
                            pInfoStruct->pInterfaceInfo,
                            pInfoStruct->dwInterfaceInfoSize );
            }
        }

        pStartRouterData->dwGlobalInfoSize = pInfoStruct->dwGlobalInfoSize;

        if ( pStartRouterData->dwGlobalInfoSize != 0 )
        {
            pStartRouterData->pGlobalInfo = LOCAL_ALLOC( LPTR,
                                                  pStartRouterData->dwGlobalInfoSize );

            if ( pStartRouterData->pGlobalInfo == NULL )
            {
                if ( pStartRouterData->pInterfaceInfo != NULL )
                {
                    LOCAL_FREE( pStartRouterData->pInterfaceInfo );
                }

                LOCAL_FREE( pStartRouterData );

                return( GetLastError() );
            }
            else
            {
                CopyMemory( pStartRouterData->pGlobalInfo,
                            pInfoStruct->pGlobalInfo,
                            pInfoStruct->dwGlobalInfoSize );
            }
        }

        //
        // Schedule this for 15 seconds after returning from this call since setup
        // may have a ways to go to complete doing all its installation.
        // This is a low risk fix to
        //

        RtlQueueWorkItem( DoStartRouter, pStartRouterData, WT_EXECUTEDEFAULT );
    }

    return( NO_ERROR );
}

//**
//
// Call:        RRouterInterfaceTransportSetGlobalInfo
//
// Returns:     NO_ERROR - Success
//              ERROR_ACCESS_DENIED
//              ERROR_UNKNOWN_PROTOCOL_ID
//              non-zero returns from SetGlobalInfo
//
// Description: Simply called the appropriate router manager to do the real
//              work.
//
DWORD
RRouterInterfaceTransportSetGlobalInfo(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       dwTransportId,
    IN      PDIM_INTERFACE_CONTAINER    pInfoStruct
)
{
    LPBYTE  lpData;
    DWORD   dwAccessStatus      = 0;
    DWORD   dwRetCode           = NO_ERROR;
    DWORD   dwTransportIndex    = GetTransportIndex( dwTransportId );
    BOOL    fGlobalDataUpdated  = FALSE;

    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwTransportIndex == (DWORD)-1 )
    {
        return( ERROR_UNKNOWN_PROTOCOL_ID );
    }

    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    do
    {
        if ( pInfoStruct->pGlobalInfo != NULL )
        {
            dwRetCode =
                gblRouterManagers[dwTransportIndex].DdmRouterIf.SetGlobalInfo(
                                                    pInfoStruct->pGlobalInfo );

            if ( dwRetCode != NO_ERROR )
            {
                break;
            }

            //
            // Update router identity object since we may be adding or
            // removing a routing protocol
            //

            fGlobalDataUpdated = TRUE;
        }

        if ( pInfoStruct->pInterfaceInfo != NULL )
        {
            lpData=gblRouterManagers[dwTransportIndex].pDefaultClientInterface;

            if ( lpData != NULL )
            {
                LOCAL_FREE( lpData );
            }

            lpData = LOCAL_ALLOC( LPTR, pInfoStruct->dwInterfaceInfoSize );

            if ( lpData == NULL )
            {
                dwRetCode = GetLastError();

                break;
            }

            CopyMemory( lpData,
                        pInfoStruct->pInterfaceInfo,
                        pInfoStruct->dwInterfaceInfoSize );

            gblRouterManagers[dwTransportIndex].pDefaultClientInterface=lpData;

            gblRouterManagers[dwTransportIndex].dwDefaultClientInterfaceSize
                                            = pInfoStruct->dwInterfaceInfoSize;
        }

    }while( FALSE );

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

    if ( ( dwRetCode == NO_ERROR ) && ( fGlobalDataUpdated ) )
    {
        //
        // We can only make this call while not holding the interface table
        // lock
        //

        RouterIdentityObjectUpdateAttributes( FALSE, FALSE );
    }

    TracePrintfExA( gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "SetGlobalInfo returned %d", dwRetCode );

    return( dwRetCode );
}

//**
//
// Call:        RRouterInterfaceTransportGetGlobalInfo
//
// Returns:     NO_ERROR - Success
//              ERROR_ACCESS_DENIED
//              ERROR_UNKNOWN_PROTOCOL_ID
//              non-zero returns from GetGlobalInfo
//
// Description: Simply called the appropriate router manager to do the real
//              work.
//
DWORD
RRouterInterfaceTransportGetGlobalInfo(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       dwTransportId,
    IN      PDIM_INTERFACE_CONTAINER    pInfoStruct
)
{
    DWORD dwAccessStatus                = 0;
    DWORD dwRetCode                     = NO_ERROR;
    DWORD dwTransportIndex              = GetTransportIndex( dwTransportId );
    ULONG ulNumAttempts;

    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwTransportIndex == (DWORD)-1 )
    {
        return( ERROR_UNKNOWN_PROTOCOL_ID );
    }

    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    do
    {
        if ( pInfoStruct->fGetGlobalInfo )
        {
            ulNumAttempts = 0;
            pInfoStruct->pGlobalInfo = NULL;
            
            do
            {
                //
                // The first iteration should get the size
                // The second iteration should get the info.
                // iteration 3 is only required if the
                // size of the info. changes between first and
                // second iteration.
                // of course the size can change between iteration
                // 2 and 3 and so on.  So we try MAX_GET_INFO_RETRIES
                // many times and quit if we still have not 
                // successfully retrieved the info.
                //
                
                dwRetCode =
                    gblRouterManagers[dwTransportIndex].DdmRouterIf.GetGlobalInfo(
                                                pInfoStruct->pGlobalInfo,
                                                &(pInfoStruct->dwGlobalInfoSize) );

                TracePrintfExA( 
                    gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "GetGlobalInfo: Transport %d requires size %d, result %d", 
                    dwTransportIndex, pInfoStruct->dwGlobalInfoSize,
                    dwRetCode
                    );
                    
                if ( dwRetCode != ERROR_INSUFFICIENT_BUFFER )
                {
                    break;
                }

                //
                // release previous allocation
                //

                if ( ulNumAttempts )
                {
                    MIDL_user_free( pInfoStruct->pGlobalInfo );
                    pInfoStruct-> pGlobalInfo = NULL;
                }
                
                if ( pInfoStruct->dwGlobalInfoSize > 0 )
                {
                    pInfoStruct->pGlobalInfo =
                                MIDL_user_allocate( pInfoStruct->dwGlobalInfoSize );

                    if ( pInfoStruct->pGlobalInfo == NULL )
                    {
                        dwRetCode = ERROR_NOT_ENOUGH_MEMORY;

                        break;
                    }
                }

                else
                {
                    break;
                }
        
                ulNumAttempts++;
                
            } while ( (dwRetCode == ERROR_INSUFFICIENT_BUFFER) &&
                      (ulNumAttempts < MAX_GET_INFO_RETRIES) );
        }

        if ( dwRetCode != NO_ERROR )
        {
            break;
        }
        
        if ((gblRouterManagers[dwTransportIndex].dwDefaultClientInterfaceSize>0)
            && ( pInfoStruct->fGetInterfaceInfo ) )
        {

            pInfoStruct->dwInterfaceInfoSize =
              gblRouterManagers[dwTransportIndex].dwDefaultClientInterfaceSize;

            pInfoStruct->pInterfaceInfo =
                        MIDL_user_allocate(pInfoStruct->dwInterfaceInfoSize);

            if ( pInfoStruct->pInterfaceInfo == NULL )
            {
                dwRetCode = ERROR_NOT_ENOUGH_MEMORY;

                break;
            }

            CopyMemory(
                pInfoStruct->pInterfaceInfo,
                gblRouterManagers[dwTransportIndex].pDefaultClientInterface,
                pInfoStruct->dwInterfaceInfoSize );

        }

    }while( FALSE );

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

    TracePrintfExA( gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "GetGlobalInfo returned %d", dwRetCode );

    return( dwRetCode );
}



//**
//
// Call:        InterfaceAjustVLSPointers
//
// Returns:     none
//
// Description: Adjusts pointers in variable length structures
//              dealing with interface info
//
DWORD
InterfaceAjustVLSPointers ( 
    DWORD  dwLevel, 
    LPBYTE lpbBuffer)
{
    if ( dwLevel == 1 )
    {
        MPRI_INTERFACE_1 * pIf1 = (MPRI_INTERFACE_1*)lpbBuffer;

        if ( pIf1->dwDialoutHoursRestrictionOffset )
        {
            pIf1->dwDialoutHoursRestrictionOffset = sizeof(MPRI_INTERFACE_1);
        }
    }

    else if ( dwLevel == 2 )
    {
        MPRI_INTERFACE_2 * pIf2 = (MPRI_INTERFACE_2*)lpbBuffer;
        DWORD dwOffset = 0;

        // Adjust the custom auth data pointer
        //
        dwOffset += sizeof(MPRI_INTERFACE_2);
        if ( pIf2->dwCustomAuthDataSize )
        {
            pIf2->dwCustomAuthDataOffset = dwOffset;
        }

        // Adjust the alternates list pointer
        //
        dwOffset += pIf2->dwCustomAuthDataSize;
        if ( pIf2->dwAlternatesOffset )
        {
            pIf2->dwAlternatesOffset = dwOffset;
        }
    }

    return NO_ERROR;
}
    
//**
//
// Call:        GetMprInterface0Data
//
// Returns:     none
//
// Description: Given a pointer to an interface object will fill an
//              MPR_INTERFACE_0 structure appropriately.
//
VOID
GetMprInterface0Data(
    IN  ROUTER_INTERFACE_OBJECT *    pIfObject,
    OUT MPRI_INTERFACE_0 *            pMprIf0
)
{
    wcscpy( pMprIf0->wszInterfaceName, pIfObject->lpwsInterfaceName );

    pMprIf0->dwIfType       = pIfObject->IfType;
    pMprIf0->dwInterface    = PtrToUlong(pIfObject->hDIMInterface);
    pMprIf0->dwLastError    = pIfObject->dwLastError;
    pMprIf0->fEnabled       = ( pIfObject->fFlags & IFFLAG_ENABLED );

    pMprIf0->fUnReachabilityReasons =
                            ( pIfObject->fFlags & IFFLAG_OUT_OF_RESOURCES )
                            ? MPR_INTERFACE_OUT_OF_RESOURCES : 0;

    if ( !( pIfObject->fFlags & IFFLAG_ENABLED ) )
    {
        pMprIf0->fUnReachabilityReasons |= MPR_INTERFACE_ADMIN_DISABLED;
    }

    if ( gblDIMConfigInfo.ServiceStatus.dwCurrentState == SERVICE_PAUSED )
    {
        pMprIf0->fUnReachabilityReasons |= MPR_INTERFACE_SERVICE_PAUSED;
    }

    if ( pIfObject->fFlags & IFFLAG_CONNECTION_FAILURE )
    {
        pMprIf0->fUnReachabilityReasons |= MPR_INTERFACE_CONNECTION_FAILURE;
    }

    if ( pIfObject->fFlags & IFFLAG_DIALOUT_HOURS_RESTRICTION )
    {
        pMprIf0->fUnReachabilityReasons |=
                                    MPR_INTERFACE_DIALOUT_HOURS_RESTRICTION;
    }

    if ( pIfObject->fFlags & IFFLAG_NO_MEDIA_SENSE )
    {
        pMprIf0->fUnReachabilityReasons |= MPR_INTERFACE_NO_MEDIA_SENSE;
    }

    switch( pIfObject->State )
    {

    case RISTATE_CONNECTED:

        pMprIf0->dwConnectionState = ROUTER_IF_STATE_CONNECTED;

        break;

    case RISTATE_DISCONNECTED:

        if ( pMprIf0->fUnReachabilityReasons != 0 )
        {
            pMprIf0->dwConnectionState = ROUTER_IF_STATE_UNREACHABLE;
        }
        else
        {
            pMprIf0->dwConnectionState = ROUTER_IF_STATE_DISCONNECTED;
        }

        break;

    case RISTATE_CONNECTING:

        pMprIf0->dwConnectionState = ROUTER_IF_STATE_CONNECTING;

        break;
    }
}

//**
//
// Call:        GetMprInterfaceData
//
// Returns:     none
//
// Description: Given a pointer to an interface object will fill an
//              MPRI_INTERFACE_* structure appropriately.
//
LPBYTE
GetMprInterfaceData(
    IN  ROUTER_INTERFACE_OBJECT *    pIfObject,
    IN  DWORD                        dwLevel,
    OUT LPDWORD                      lpdwcbSizeOfData
)
{
    DWORD               cbDialoutHoursRestriction   = 0;
    DWORD               dwErr;
    MPRI_INTERFACE_0 *  pMprIf0                     = NULL;
    MPRI_INTERFACE_1 *  pIf1                        = NULL;
    LPBYTE              pInterfaceData              = NULL;
    HANDLE              hEntry                      = NULL;

    switch ( dwLevel )
    {
        // Basic
        //
        case 0:
            *lpdwcbSizeOfData = sizeof( MPRI_INTERFACE_0 );
            dwErr = NO_ERROR;
            break;

        // Basic plus dialout hours restriction
        //
        case 1:
            *lpdwcbSizeOfData = sizeof( MPRI_INTERFACE_1 );
            if ( pIfObject->lpwsDialoutHoursRestriction != NULL )
            {
                cbDialoutHoursRestriction =
                                GetSizeOfDialoutHoursRestriction(
                                            pIfObject->lpwsDialoutHoursRestriction);
                
                *lpdwcbSizeOfData += cbDialoutHoursRestriction;
            }
            dwErr = NO_ERROR;
            break;

        // Basic plus router phonebook entry info
        //
        case 2:
            dwErr = RpbkOpenEntry(pIfObject, &hEntry);
            if (dwErr == NO_ERROR)
            {
                dwErr = RpbkEntryToIfDataSize(
                            hEntry, 
                            dwLevel, 
                            lpdwcbSizeOfData);
            }
            break;
    }

    if (dwErr != NO_ERROR)
    {
        return( NULL );
    }
    
    do 
    {
        // Allocate the return value
        pInterfaceData = MIDL_user_allocate( *lpdwcbSizeOfData );
        if ( pInterfaceData == NULL )
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            break;
        }

        //
        // Add any appropriate information
        //
        switch ( dwLevel )
        {
            case 0:
                GetMprInterface0Data( pIfObject, (MPRI_INTERFACE_0 *)pInterfaceData );
                break;

            case 1:
                GetMprInterface0Data( pIfObject, (MPRI_INTERFACE_0 *)pInterfaceData );
                pIf1 = (MPRI_INTERFACE_1*)pInterfaceData;
                if ( pIfObject->lpwsDialoutHoursRestriction != NULL )
                {
                    CopyMemory( pIf1 + 1,
                                pIfObject->lpwsDialoutHoursRestriction,
                                cbDialoutHoursRestriction );

                    pIf1->dwDialoutHoursRestrictionOffset = TRUE;
                }
                else
                {
                    pIf1->dwDialoutHoursRestrictionOffset = 0;
                }
                break;

            case 2:
                GetMprInterface0Data( pIfObject, (MPRI_INTERFACE_0 *)pInterfaceData );
                dwErr = RpbkEntryToIfData( 
                            hEntry,
                            dwLevel,
                            pInterfaceData );
                break;
        }
        
    } while (FALSE);

    // Cleanup
    {
        if ( dwErr != NO_ERROR )
        {
            if ( pInterfaceData )
            {
                MIDL_user_free( pInterfaceData );   
            }
        }

        if ( hEntry )
        {
            RpbkCloseEntry( hEntry );
        }
    }

    return( pInterfaceData );
}

//**
//
// Call:        GetMprInterfaceData
//
// Returns:     none
//
// Description: Given a pointer to an interface object will fill an
//              MPR_INTERFACE_* structure appropriately.
//
DWORD
GetMprInterfaceDeviceData(
    IN  ROUTER_INTERFACE_OBJECT *    pIfObject,
    IN  DWORD                        dwIndex,
    IN  DWORD                        dwLevel,
    OUT LPDWORD                      lpdwcbSizeOfData,
    OUT LPBYTE*                      lplpbReturn
)
{
    DWORD               dwErr                 = NO_ERROR;
    LPBYTE              pDevData              = NULL;
    HANDLE              hSubEntry             = NULL;

    *lplpbReturn = NULL;

    switch ( dwLevel )
    {
        // Basic
        //
        case 0:
            *lpdwcbSizeOfData = sizeof( MPR_DEVICE_0 );
            dwErr = NO_ERROR;
            break;

        // Basic plus phone numbers
        //
        case 1:
            dwErr = RpbkOpenSubEntry(pIfObject, dwIndex, &hSubEntry);
            if (dwErr == NO_ERROR)
            {
                dwErr = RpbkSubEntryToDevDataSize(
                            hSubEntry, 
                            dwLevel, 
                            lpdwcbSizeOfData);
            }
            break;
    }

    if (dwErr != NO_ERROR)
    {
        return( dwErr );
    }
    
    do 
    {
        // Allocate the return value
        //
        pDevData = MIDL_user_allocate( *lpdwcbSizeOfData );
        if ( pDevData == NULL )
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            break;
        }

        //
        // Add any appropriate information
        //
        switch ( dwLevel )
        {
            case 0:
            case 1:
                dwErr = RpbkSubEntryToDevData( 
                            hSubEntry,
                            dwLevel,
                            pDevData );
                break;
        }
        
    } while (FALSE);

    // Cleanup
    {
        if ( hSubEntry )
        {
            RpbkCloseSubEntry( hSubEntry );
        }
        if ( dwErr != NO_ERROR )
        {
            if ( pDevData )
            {
                MIDL_user_free( pDevData );
            }
        }
        else
        {
            *lplpbReturn = pDevData;
        }
    }

    return( dwErr );
}

//**
//
// Call:        RRouterInterfaceCreate
//
// Returns:     NO_ERROR - Success
//              ERROR_ACCESS_DENIED
//              ERROR_NOT_SUPPORTED
//
// Description: Creates an interface with DIM.
//
DWORD
RRouterInterfaceCreate(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       dwLevel,
    IN      PDIM_INFORMATION_CONTAINER  pInfoStruct,
    IN      LPDWORD                     phInterface
)
{
    DWORD                       dwAccessStatus;
    ROUTER_INTERFACE_OBJECT *   pIfObject;
    DWORD                       IfState;
    DWORD                       dwRetCode = NO_ERROR;
    LPWSTR                      lpwsDialoutHoursRestriction = NULL;
    BOOL                        fLANInterfaceAdded = FALSE;
    MPRI_INTERFACE_0 *           pMprIf0 =
                                    (MPRI_INTERFACE_0*)pInfoStruct->pBuffer;
    MPRI_INTERFACE_1 *           pMprIf1 =
                                    (MPRI_INTERFACE_1*)pInfoStruct->pBuffer;
    MPRI_INTERFACE_2 *           pMprIf2 =
                                    (MPRI_INTERFACE_2*)pInfoStruct->pBuffer;

    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( ( (int) dwLevel < 0 ) || ( dwLevel > 2 ) )
    {
        return( ERROR_NOT_SUPPORTED );
    }

    if ( pMprIf0->dwIfType == ROUTER_IF_TYPE_TUNNEL1)
    {
        return( ERROR_NOT_SUPPORTED );
    }

    //
    // Adjust the given buffer for any pointers to 
    // variable length data.
    //
    
    InterfaceAjustVLSPointers ( dwLevel, pInfoStruct->pBuffer );
        
    if ( ( pMprIf0->dwIfType == ROUTER_IF_TYPE_DEDICATED ) ||
         ( pMprIf0->dwIfType == ROUTER_IF_TYPE_INTERNAL  ) ||
         ( pMprIf0->dwIfType == ROUTER_IF_TYPE_LOOPBACK  ) ||
         ( pMprIf0->dwIfType == ROUTER_IF_TYPE_TUNNEL1 ) )
    {
        IfState = RISTATE_CONNECTED;

        if ( !pMprIf0->fEnabled )
        {
            return( ERROR_INVALID_PARAMETER );
        }

        //
        // Update router identity object since we are adding a LAN interface
        //

        fLANInterfaceAdded = TRUE;
    }
    else if ( ( pMprIf0->dwIfType == ROUTER_IF_TYPE_CLIENT )      ||
              ( pMprIf0->dwIfType == ROUTER_IF_TYPE_HOME_ROUTER ) ||
              ( pMprIf0->dwIfType == ROUTER_IF_TYPE_FULL_ROUTER ) )
    {
        if ( gblDIMConfigInfo.dwRouterRole == ROUTER_ROLE_LAN )
        {
            return( ERROR_DDM_NOT_RUNNING );
        }
        else
        {
            IfState = RISTATE_DISCONNECTED;
        }
    }
    else
    {
        return( ERROR_INVALID_PARAMETER );
    }

    if ( dwLevel == 1 )
    {
        if ( pMprIf1->dwDialoutHoursRestrictionOffset != 0 )
        {
            DWORD  cbDialoutHoursRestriction;

            cbDialoutHoursRestriction = 
                GetSizeOfDialoutHoursRestriction((LPWSTR)(pMprIf1 + 1));

            DIMTRACE1(
                "Creating l1 interface with %d bytes of dohr", 
                cbDialoutHoursRestriction);

            lpwsDialoutHoursRestriction =
                        LOCAL_ALLOC( LPTR, cbDialoutHoursRestriction );

            if ( lpwsDialoutHoursRestriction == NULL )
            {
                return( GetLastError() );
            }
        }
    }

    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    do
    {
        if ( pMprIf0->dwIfType != ROUTER_IF_TYPE_CLIENT )
        {
            //
            // Check for duplicates if we are not adding a client interface
            //

            pIfObject = IfObjectGetPointerByName( pMprIf0->wszInterfaceName,
                                                  FALSE );

            if ( pIfObject != NULL )
            {
                dwRetCode = ERROR_INTERFACE_ALREADY_EXISTS;

                break;
            }
        }

        //
        // If phonebook information is being specified, set it 
        // before continuing
        //
        
        if (dwLevel == 2)
        {
            dwRetCode = RpbkSetEntry(dwLevel, (LPBYTE)pMprIf2);
            
            if (dwRetCode != NO_ERROR)
            {
                break;
            }
        }

        pIfObject = IfObjectAllocateAndInit(
                                pMprIf0->wszInterfaceName,
                                IfState,
                                pMprIf0->dwIfType,
                                (HCONN)0,
                                pMprIf0->fEnabled,
                                600,
                                21600,
                                lpwsDialoutHoursRestriction );

        if ( pIfObject == NULL )
        {
            dwRetCode = GetLastError();

            break;
        }

        if ( ( dwRetCode = IfObjectInsertInTable( pIfObject ) ) != NO_ERROR )
        {
            LOCAL_FREE( pIfObject );

            break;
        }

        *phInterface = PtrToUlong(pIfObject->hDIMInterface);

        if ( lpwsDialoutHoursRestriction != NULL )
        {
            VOID (*IfObjectSetDialoutHoursRestriction)(
                                    ROUTER_INTERFACE_OBJECT * ) =
                        (VOID(*)( ROUTER_INTERFACE_OBJECT *))
                        GetDDMEntryPoint("IfObjectSetDialoutHoursRestriction");

            //
            // Set dialout hours restriction if there was one
            //

            IfObjectSetDialoutHoursRestriction( pIfObject );
        }

    }while( FALSE );

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

    TracePrintfExA( gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "InterfaceCreate returned %d", dwRetCode );

    if ( dwRetCode != NO_ERROR )
    {
        if ( lpwsDialoutHoursRestriction != NULL )
        {
            //
            // If there was some error then free this memory
            //

            LOCAL_FREE( lpwsDialoutHoursRestriction );
        }
    }
    else
    {
        if ( fLANInterfaceAdded )
        {
            //
            // Can only call this API while not holding the interface lock
            //

            RouterIdentityObjectUpdateAttributes( FALSE, FALSE );
        }
    }


    return( dwRetCode );
}

//**
//
// Call:        RRouterInterfaceGetInfo
//
// Returns:     NO_ERROR - Success
//              ERROR_ACCESS_DENIED
//              ERROR_NOT_SUPPORTED
//
// Description: Gets information about a DIM interface.
//
DWORD
RRouterInterfaceGetInfo(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       dwLevel,
    IN      PDIM_INFORMATION_CONTAINER  pInfoStruct,
    IN      DWORD                       hInterface
)
{
    DWORD                       dwAccessStatus;
    ROUTER_INTERFACE_OBJECT *   pIfObject;
    DWORD                       dwRetCode = NO_ERROR;

    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( ( (int) dwLevel < 0 ) || ( dwLevel > 2 ) )
    {
        return( ERROR_NOT_SUPPORTED );
    }

    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    do
    {
        if ( ( pIfObject = IfObjectGetPointer(
                        DimIndexToHandle(hInterface)) ) == NULL )
        {
            dwRetCode = ERROR_INVALID_HANDLE;

            break;
        }

        // pmay: 274487
        //
        // Validate the type of the interface against the level
        //
        if ( ( dwLevel == 2 ) &&
             ( pIfObject->IfType != ROUTER_IF_TYPE_FULL_ROUTER ) )
        {
            dwRetCode = ERROR_INVALID_LEVEL;

            break;
        }

        pInfoStruct->pBuffer = GetMprInterfaceData(
                                        pIfObject,
                                        dwLevel,
                                        &(pInfoStruct->dwBufferSize) );

        if ( pInfoStruct->pBuffer == NULL )
        {
            dwRetCode = ERROR_NOT_ENOUGH_MEMORY;

            break;
        }

    }while( FALSE );

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

    TracePrintfExA( gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "InterfaceGetInfo returned %d", dwRetCode );

    return( dwRetCode );
}

//**
//
// Call:        RRouterInterfaceSetInfo
//
// Returns:     NO_ERROR - Success
//              ERROR_ACCESS_DENIED
//              ERROR_NOT_SUPPORTED
//
// Description: Sets interface configuration information
//
DWORD
RRouterInterfaceSetInfo(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       dwLevel,
    IN      PDIM_INFORMATION_CONTAINER  pInfoStruct,
    IN      DWORD                       hInterface
)
{
    DWORD                       dwAccessStatus;
    ROUTER_INTERFACE_OBJECT *   pIfObject;
    DWORD                       IfState;
    DWORD                       dwRetCode = NO_ERROR;
    BOOL                        fNotify = FALSE;
    DWORD                       dwIndex;
    MPRI_INTERFACE_0 *           pMprIf0 =
                                    (MPRI_INTERFACE_0*)pInfoStruct->pBuffer;
    MPRI_INTERFACE_1 *           pMprIf1 =
                                    (MPRI_INTERFACE_1*)pInfoStruct->pBuffer;
    MPRI_INTERFACE_2 *           pMprIf2 =
                                    (MPRI_INTERFACE_2*)pInfoStruct->pBuffer;
    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( ( (int) dwLevel < 0 ) || ( dwLevel > 2 ) )
    {
        return( ERROR_NOT_SUPPORTED );
    }

    //
    // Adjust the given buffer for any pointers to 
    // variable length data.
    //
    
    InterfaceAjustVLSPointers ( dwLevel, pInfoStruct->pBuffer );
        
    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    do
    {
        if ( ( pIfObject = IfObjectGetPointer(
                    DimIndexToHandle(hInterface)) ) == NULL )
        {
            dwRetCode = ERROR_INVALID_HANDLE;

            break;
        }

        if ( ( pIfObject->IfType == ROUTER_IF_TYPE_DEDICATED ) ||
             ( pIfObject->IfType == ROUTER_IF_TYPE_TUNNEL1 ) ||
             ( pIfObject->IfType == ROUTER_IF_TYPE_INTERNAL ) )
        {
            if ( !pMprIf0->fEnabled )
            {
                dwRetCode = ERROR_INVALID_PARAMETER;
            }

            //
            // Nothing can be set for these interfaces
            //

            break;
        }

        if ( pMprIf0->fEnabled )
        {
            if ( !( pIfObject->fFlags & IFFLAG_ENABLED ) )
            {
                pIfObject->fFlags |= IFFLAG_ENABLED;

                fNotify = TRUE;
            }
        }
        else
        {
            if ( pIfObject->fFlags & IFFLAG_ENABLED )
            {
                pIfObject->fFlags &= ~IFFLAG_ENABLED;

                fNotify = TRUE;
            }

            if ( pIfObject->State != RISTATE_DISCONNECTED )
            {
                fNotify = FALSE;
            }
        }

        if ( fNotify )
        {
            VOID (*IfObjectNotifyOfReachabilityChange)(
                                    ROUTER_INTERFACE_OBJECT *,
                                    BOOL,
                                    UNREACHABILITY_REASON ) =
                        (VOID(*)( ROUTER_INTERFACE_OBJECT *,
                                  BOOL,
                                  UNREACHABILITY_REASON ))
                        GetDDMEntryPoint("IfObjectNotifyOfReachabilityChange");

            if(NULL != IfObjectNotifyOfReachabilityChange)
            {
                IfObjectNotifyOfReachabilityChange( pIfObject,
                                                    pMprIf0->fEnabled,
                                                    INTERFACE_DISABLED );
            }                                                
        }

        //
        // Check level 1 values
        //

        if ( dwLevel == 1 )
        {
            VOID (*IfObjectSetDialoutHoursRestriction)(
                                    ROUTER_INTERFACE_OBJECT * ) =
                        (VOID(*)( ROUTER_INTERFACE_OBJECT *))
                        GetDDMEntryPoint("IfObjectSetDialoutHoursRestriction");

            if ( pMprIf1->dwDialoutHoursRestrictionOffset == 0 )
            {
                if ( pIfObject->lpwsDialoutHoursRestriction != NULL )
                {
                    LOCAL_FREE( pIfObject->lpwsDialoutHoursRestriction );

                    pIfObject->lpwsDialoutHoursRestriction = NULL;

                    IfObjectSetDialoutHoursRestriction( pIfObject );
                }
            }
            else
            {
                DWORD cbDialoutHoursRestriction =
                            GetSizeOfDialoutHoursRestriction(
                                    (LPWSTR)(pMprIf1 + 1) );

                if ( pIfObject->lpwsDialoutHoursRestriction != NULL )
                {
                    //
                    // Free currently allocated memory for dialout hours
                    //

                    LOCAL_FREE( pIfObject->lpwsDialoutHoursRestriction );

                    pIfObject->lpwsDialoutHoursRestriction = NULL;
                }

                DIMTRACE1(
                    "Setting info on l1 interface.  %d bytes dohr",
                    cbDialoutHoursRestriction);

                pIfObject->lpwsDialoutHoursRestriction =
                        LOCAL_ALLOC( LPTR, cbDialoutHoursRestriction );

                if ( pIfObject->lpwsDialoutHoursRestriction == NULL )
                {
                    dwRetCode = GetLastError();
                    break;
                }

                CopyMemory( pIfObject->lpwsDialoutHoursRestriction,
                            (LPBYTE)(pMprIf1 + 1),
                            cbDialoutHoursRestriction );

                IfObjectSetDialoutHoursRestriction( pIfObject );
            }
        }

        //
        // Check level 2 values
        //

        else if ( dwLevel == 2 )
        {
            if (pIfObject->IfType != ROUTER_IF_TYPE_FULL_ROUTER)
            {
                dwRetCode = ERROR_INVALID_PARAMETER;
            }
            else
            {
                dwRetCode = RpbkSetEntry(dwLevel, (LPBYTE)pMprIf2);
            }
        }
        
    }while( FALSE );

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

    TracePrintfExA( gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "InterfaceSetInfo returned %d", dwRetCode );

    return( dwRetCode );
}

DWORD 
RRouterInterfaceDeviceGetInfo(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       dwLevel,
    IN      PDIM_INFORMATION_CONTAINER  pInfoStruct,
    IN      DWORD                       dwIndex,
    IN      DWORD                       hInterface
    )
{
    DWORD                       dwAccessStatus;
    ROUTER_INTERFACE_OBJECT *   pIfObject;
    DWORD                       dwRetCode = NO_ERROR;

    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( ( (int) dwLevel < 0 ) || ( dwLevel > 1 ) )
    {
        return( ERROR_NOT_SUPPORTED );
    }

    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    do
    {
        if ( ( pIfObject = IfObjectGetPointer(
                    DimIndexToHandle(hInterface)) ) == NULL )
        {
            dwRetCode = ERROR_INVALID_HANDLE;

            break;
        }

        dwRetCode = GetMprInterfaceDeviceData(
                        pIfObject,
                        dwIndex,
                        dwLevel,
                        &(pInfoStruct->dwBufferSize),
                        &(pInfoStruct->pBuffer));

        if ( dwRetCode != NO_ERROR )
        {
            if ( dwRetCode == ERROR_CANNOT_FIND_PHONEBOOK_ENTRY )
            {
                dwRetCode = ERROR_DEV_NOT_EXIST;
            }
            
            break;
        }

    }while( FALSE );

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

    TracePrintfExA( gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "InterfaceDeviceGetInfo returned %d", dwRetCode );

    return( dwRetCode );
}
    
DWORD
RRouterInterfaceDeviceSetInfo(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       dwLevel,
    IN      PDIM_INFORMATION_CONTAINER  pInfoStruct,
    IN      DWORD                       dwIndex,
    IN      DWORD                       hInterface
    )
{
    DWORD                       dwAccessStatus;
    ROUTER_INTERFACE_OBJECT *   pIfObject;
    DWORD                       dwRetCode = NO_ERROR;
    MPR_DEVICE_0 *              pDev0 =
                                    (MPR_DEVICE_0*)pInfoStruct->pBuffer;
    MPR_DEVICE_1 *              pDev1 =
                                    (MPR_DEVICE_1*)pInfoStruct->pBuffer;

    //
    // Check if caller has access
    //
    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( ( (int) dwLevel < 0 ) || ( dwLevel > 1 ) )
    {
        return( ERROR_NOT_SUPPORTED );
    }

    // Adjust the variable-length structure pointers
    //
    if ( dwLevel == 1 )
    {
        if ( pDev1->szAlternates )
        {
            pDev1->szAlternates = (PWCHAR)(pDev1 + 1);
        }
    }

    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    do
    {
        if ( ( pIfObject = IfObjectGetPointer(
                    DimIndexToHandle(hInterface)) ) == NULL )
        {
            dwRetCode = ERROR_INVALID_HANDLE;

            break;
        }

        //
        // Set the subentry
        //
        if (pIfObject->IfType != ROUTER_IF_TYPE_FULL_ROUTER)
        {
            dwRetCode = ERROR_INVALID_PARAMETER;
        }
        else
        {
            dwRetCode = RpbkSetSubEntry(
                            pIfObject->lpwsInterfaceName,
                            dwIndex,
                            dwLevel, 
                            pInfoStruct->pBuffer);
        }
        
    }while( FALSE );

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

    TracePrintfExA( gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "InterfaceDeviceSetInfo returned %d", dwRetCode );

    return( dwRetCode );
}
    
//**
//
// Call:        RRouterInterfaceGetHandle
//
// Returns:     NO_ERROR - Success
//              ERROR_ACCESS_DENIED
//
// Description:
//
DWORD
RRouterInterfaceGetHandle(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      LPWSTR                      lpwsInterfaceName,
    IN      LPDWORD                     phInterface,
    IN      DWORD                       fIncludeClientInterfaces
)
{
    DWORD                       dwAccessStatus;
    ROUTER_INTERFACE_OBJECT *   pIfObject   = NULL;
    DWORD                       dwRetCode   = NO_ERROR;

    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    do
    {
        pIfObject = IfObjectGetPointerByName( lpwsInterfaceName,
                                              fIncludeClientInterfaces );

        if ( pIfObject == NULL )
        {
            dwRetCode = ERROR_NO_SUCH_INTERFACE;

            break;
        }

        *phInterface = PtrToUlong(pIfObject->hDIMInterface);

    }while( FALSE );

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

    return( dwRetCode );
}

//**
//
// Call:        RRouterInterfaceDelete
//
// Returns:     NO_ERROR - Success
//              ERROR_ACCESS_DENIED
//
// Description: Deletes and interface from the DIM database.
//              work.
//
DWORD
RRouterInterfaceDelete(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       hInterface
)
{
    DWORD                       dwAccessStatus;
    DWORD                       dwTransportIndex;
    ROUTER_INTERFACE_OBJECT *   pIfObject            = NULL;
    DWORD                       dwRetCode            = NO_ERROR;
    BOOL                        fLANInterfaceRemoved = FALSE;
    BOOL                        fDeletePbkEntry      = FALSE;
    
    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    do
    {
        if ( ( pIfObject = IfObjectGetPointer(
                    DimIndexToHandle(hInterface)) ) == NULL )
        {
            dwRetCode = ERROR_INVALID_HANDLE;

            break;
        }

        //
        // If this is a demand dial dynamic interface then we cannot delete
        // it if it is connected.
        //

        if ( ( pIfObject->IfType == ROUTER_IF_TYPE_CLIENT )      ||
             ( pIfObject->IfType == ROUTER_IF_TYPE_HOME_ROUTER ) ||
             ( pIfObject->IfType == ROUTER_IF_TYPE_FULL_ROUTER ) )
        {
            if ( pIfObject->State != RISTATE_DISCONNECTED )
            {
                dwRetCode = ERROR_INTERFACE_CONNECTED;

                break;
            }

            fDeletePbkEntry = TRUE;

            //
            // Remove credentials for this interface if they were set.
            //

            MprAdminInterfaceSetCredentials( NULL,
                                             pIfObject->lpwsInterfaceName,
                                             NULL,
                                             NULL,
                                             NULL );
        }
        else
        {
            //
            // Update router identity object since we are removing a LAN
            // interface
            //

            fLANInterfaceRemoved = TRUE;
        }

        //
        // Delete any transport interfaces that might still be around
        //

        for ( dwTransportIndex = 0;
              dwTransportIndex < gblDIMConfigInfo.dwNumRouterManagers;
              dwTransportIndex++ )
        {
            if ( pIfObject->Transport[dwTransportIndex].hInterface !=
                                                        INVALID_HANDLE_VALUE )
            {
                dwRetCode =
                gblRouterManagers[dwTransportIndex].DdmRouterIf.DeleteInterface(
                        pIfObject->Transport[dwTransportIndex].hInterface );

                if ( dwRetCode != NO_ERROR )
                {
                    break;
                }

                pIfObject->Transport[dwTransportIndex].hInterface
                                                        = INVALID_HANDLE_VALUE;

                pIfObject->Transport[dwTransportIndex].fState = 0;
            }
        }

        if ( fDeletePbkEntry )
        {
            RpbkDeleteEntry( pIfObject->lpwsInterfaceName );
        }

        IfObjectRemove( (HANDLE)UlongToPtr(hInterface) );

    } while ( FALSE );

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );


    if ( ( dwRetCode == NO_ERROR ) && ( fLANInterfaceRemoved ) )
    {
        //
        // Can only call this API while not holding the interface lock
        //

        RouterIdentityObjectUpdateAttributes( FALSE, FALSE );
    }

    TracePrintfExA( gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "DeleteInterface returned %d", dwRetCode );

    return( dwRetCode );
}

//**
//
// Call:        RRouterInterfaceTransportRemove
//
// Returns:     NO_ERROR - Success
//              ERROR_ACCESS_DENIED
//
// Description: Simply called the appropriate router manager to do the real
//              work.
//
DWORD
RRouterInterfaceTransportRemove(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       hInterface,
    IN      DWORD                       dwTransportId
)
{
    DWORD                       dwAccessStatus;
    ROUTER_INTERFACE_OBJECT *   pIfObject        = NULL;
    DWORD                       dwRetCode        = NO_ERROR;
    DWORD                       dwTransportIndex
                                        = GetTransportIndex( dwTransportId );

    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwTransportIndex == (DWORD)-1 )
    {
        return( ERROR_UNKNOWN_PROTOCOL_ID );
    }

    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    do
    {
        if ( ( pIfObject = IfObjectGetPointer(
                        DimIndexToHandle(hInterface)) ) == NULL )
        {
            dwRetCode = ERROR_INVALID_HANDLE;

            break;
        }

        //
        // If this is a demand dial dynamic interface then we cannot delete
        // it if it is connected.
        //

        if ( ( pIfObject->IfType == ROUTER_IF_TYPE_CLIENT )      ||
             ( pIfObject->IfType == ROUTER_IF_TYPE_HOME_ROUTER ) ||
             ( pIfObject->IfType == ROUTER_IF_TYPE_FULL_ROUTER ) )
        {
            if ( pIfObject->State == RISTATE_CONNECTED )
            {
                dwRetCode = ERROR_INTERFACE_CONNECTED;

                break;
            }
        }

        //
        // Remove the transport interface.
        //

        if ( pIfObject->Transport[dwTransportIndex].hInterface
                                                    != INVALID_HANDLE_VALUE )
        {
            dwRetCode =
                gblRouterManagers[dwTransportIndex].DdmRouterIf.DeleteInterface(
                        pIfObject->Transport[dwTransportIndex].hInterface );

            if ( dwRetCode == NO_ERROR )
            {
                pIfObject->Transport[dwTransportIndex].hInterface
                                                    = INVALID_HANDLE_VALUE;
            }
        }
        else
        {
            dwRetCode = ERROR_NO_SUCH_INTERFACE;
        }

    }while( FALSE );

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

    TracePrintfExA( gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "RemoveInterface returned %d", dwRetCode );

    return( dwRetCode );
}

//**
//
// Call:        RRouterInterfaceTransportGetInfo
//
// Returns:     NO_ERROR - Success
//              ERROR_ACCESS_DENIED
//              ERROR_UNKNOWN_PROTOCOL_ID
//              ERROR_INVALID_HANDLE
//              non-zero returns from GetInterfaceInfo
//
// Description: Simply called the appropriate router manager to do the real
//              work.
//
DWORD
RRouterInterfaceTransportGetInfo(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       hInterface,
    IN      DWORD                       dwTransportId,
    IN      DIM_INTERFACE_CONTAINER *   pInfoStruct
)
{
    DWORD dwAccessStatus                = 0;
    ROUTER_INTERFACE_OBJECT * pIfObject = NULL;
    DWORD dwRetCode                     = NO_ERROR;
    DWORD dwTransportIndex              = GetTransportIndex( dwTransportId );
    ULONG ulNumAttempts;

    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwTransportIndex == (DWORD)-1 )
    {
        return( ERROR_UNKNOWN_PROTOCOL_ID );
    }

    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    do
    {

        if ( ( pIfObject = IfObjectGetPointer(
            (HANDLE) UlongToPtr(hInterface ))) == NULL )
        {
            dwRetCode = ERROR_INVALID_HANDLE;

            break;
        }

        //
        // If the interface has not been added for this protocol
        //

        if ( pIfObject->Transport[dwTransportIndex].hInterface
                                                    == INVALID_HANDLE_VALUE )
        {
            dwRetCode = ERROR_NO_SUCH_INTERFACE;

            break;
        }

        if ( pInfoStruct->fGetInterfaceInfo )
        {
            ulNumAttempts = 0;
            pInfoStruct->pInterfaceInfo = NULL;

            do
            {
                dwRetCode =
                    gblRouterManagers[dwTransportIndex].DdmRouterIf.GetInterfaceInfo(
                                    pIfObject->Transport[dwTransportIndex].hInterface,
                                    pInfoStruct->pInterfaceInfo,
                                    &(pInfoStruct->dwInterfaceInfoSize) );

                TracePrintfExA( 
                    gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "GetInterfaceInfo: Transport %d requires size %d, result %d", 
                    dwTransportIndex, pInfoStruct->dwInterfaceInfoSize,
                    dwRetCode
                    );
                    
                if ( dwRetCode != ERROR_INSUFFICIENT_BUFFER )
                {
                    break;
                }

                //
                // Release previous allocation
                //
                
                if ( ulNumAttempts )
                {
                    MIDL_user_free( pInfoStruct->pInterfaceInfo );
                    pInfoStruct->pInterfaceInfo = NULL;
                }
                
                if ( pInfoStruct->dwInterfaceInfoSize > 0 )
                {
                    pInfoStruct->pInterfaceInfo = MIDL_user_allocate(
                                                pInfoStruct->dwInterfaceInfoSize);

                    if ( pInfoStruct->pInterfaceInfo == NULL )
                    {
                        dwRetCode = ERROR_NOT_ENOUGH_MEMORY;

                        break;
                    }
                }

                else
                {
                    break;
                }

                ulNumAttempts++;
                
            } while ( (dwRetCode == ERROR_INSUFFICIENT_BUFFER) &&
                      (ulNumAttempts < MAX_GET_INFO_RETRIES) );
        }
        
    } while( FALSE );

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

    TracePrintfExA( gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "InterfaceGetInfo returned %d", dwRetCode );

    if ( !pInfoStruct->fGetInterfaceInfo )
    {
        pInfoStruct->dwInterfaceInfoSize = 0;
    }

    if ( dwRetCode != NO_ERROR )
    {
        pInfoStruct->dwInterfaceInfoSize = 0;
    }

    return( dwRetCode );
}



//**
//
// Call:        RRouterInterfaceTransportAdd
//
// Returns:     NO_ERROR - Success
//              ERROR_ACCESS_DENIED
//              ERROR_UNKNOWN_PROTOCOL_ID
//              ERROR_INVALID_HANDLE
//              non-zero returns from AddInterface
//
// Description: Simply called the appropriate router manager to do the real
//              work.
//
DWORD
RRouterInterfaceTransportAdd(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       hInterface,
    IN      DWORD                       dwTransportId,
    IN      DIM_INTERFACE_CONTAINER *   pInfoStruct
)
{
    DWORD dwAccessStatus                = 0;
    ROUTER_INTERFACE_OBJECT * pIfObject = NULL;
    DWORD dwRetCode                     = NO_ERROR;
    DWORD dwIndex                       = GetTransportIndex( dwTransportId );

    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwIndex == (DWORD)-1 )
    {
        return( ERROR_UNKNOWN_PROTOCOL_ID );
    }

    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    do
    {
        if ( ( pIfObject = IfObjectGetPointer(
                    DimIndexToHandle(hInterface) )) == NULL )
        {
            dwRetCode = ERROR_INVALID_HANDLE;

            break;
        }

        //
        // If the handle to the interface is NULL, then we have to add the
        // interface
        //

        if ( pIfObject->Transport[dwIndex].hInterface == INVALID_HANDLE_VALUE )
        {
            if (IsInterfaceRoleAcceptable(pIfObject, dwTransportId))
            {
                dwRetCode = 
                    gblRouterManagers[dwIndex].DdmRouterIf.AddInterface(
                        pIfObject->lpwsInterfaceName,
                        pInfoStruct->pInterfaceInfo,
                        pIfObject->IfType,
                        pIfObject->hDIMInterface,
                        &pIfObject->Transport[dwIndex].hInterface);

                if ( dwRetCode != NO_ERROR )
                {
                    pIfObject->Transport[dwIndex].hInterface = 
                        INVALID_HANDLE_VALUE;
                }
                else
                {
                    if ( !( pIfObject->fFlags & IFFLAG_ENABLED )         ||
                         ( pIfObject->fFlags & IFFLAG_OUT_OF_RESOURCES ) ||
                         ( gblDIMConfigInfo.ServiceStatus.dwCurrentState ==
                                                        SERVICE_PAUSED))
                    {
                        TracePrintfExA( 
                            gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                            "InterfaceTransportAdd: Set if to unreachable");

                       gblRouterManagers[dwIndex].DdmRouterIf.InterfaceNotReachable(
                                        pIfObject->Transport[dwIndex].hInterface,
                                        INTERFACE_DISABLED );
                    }
                }
            }
            else
            {
                pIfObject->Transport[dwIndex].hInterface = INVALID_HANDLE_VALUE;
            }
        }
        else
        {
            dwRetCode = ERROR_INTERFACE_ALREADY_EXISTS;
        }

    }while( FALSE );

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

    TracePrintfExA( gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "InterfaceTransportAdd returned %d", dwRetCode );

    return( dwRetCode );
}

//**
//
// Call:        RRouterInterfaceTransportSetInfo
//
// Returns:     NO_ERROR - Success
//              ERROR_ACCESS_DENIED
//              ERROR_UNKNOWN_PROTOCOL_ID
//              ERROR_INVALID_HANDLE
//              non-zero returns from AddInterface or SetInterfaceInfo
//
// Description: Simply called the appropriate router manager to do the real
//              work.
//
DWORD
RRouterInterfaceTransportSetInfo(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       hInterface,
    IN      DWORD                       dwTransportId,
    IN      DIM_INTERFACE_CONTAINER *   pInfoStruct
)
{
    DWORD dwAccessStatus                = 0;
    ROUTER_INTERFACE_OBJECT * pIfObject = NULL;
    DWORD dwRetCode                     = NO_ERROR;
    DWORD dwTransportIndex              = GetTransportIndex( dwTransportId );

    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwTransportIndex == (DWORD)-1 )
    {
        return( ERROR_UNKNOWN_PROTOCOL_ID );
    }

    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    do
    {
        if ( ( pIfObject = IfObjectGetPointer(
                        DimIndexToHandle(hInterface))) == NULL )
        {
            dwRetCode = ERROR_INVALID_HANDLE;

            break;
        }

        //
        // If the handle to the interface is NULL, then we have to add the
        // interface
        //

        if ( pIfObject->Transport[dwTransportIndex].hInterface
                                                    == INVALID_HANDLE_VALUE )
        {
            dwRetCode = ERROR_NO_SUCH_INTERFACE;
        }
        else
        {
            dwRetCode =
            gblRouterManagers[dwTransportIndex].DdmRouterIf.SetInterfaceInfo(
                            pIfObject->Transport[dwTransportIndex].hInterface,
                            pInfoStruct->pInterfaceInfo );
        }

    }while( FALSE );

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

    TracePrintfExA( gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "InterfaceSetInfo returned %d", dwRetCode );

    return( dwRetCode );
}

//**
//
// Call:        RRouterInterfaceEnum
//
// Returns:     NO_ERROR - Success
//              ERROR_ACCESS_DENIED
//              ERROR_NOT_SUPPORTED
//
// Description: Simply called the appropriate router manager to do the real
//              work.
//
DWORD
RRouterInterfaceEnum(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       dwLevel,
    IN OUT  PDIM_INFORMATION_CONTAINER  pInfoStruct,
    IN      DWORD                       dwPreferedMaximumLength,
    OUT     LPDWORD                     lpdwEntriesRead,
    OUT     LPDWORD                     lpdwTotalEntries,
    IN OUT  LPDWORD                     lpdwResumeHandle OPTIONAL
)
{
    DWORD                       dwAccessStatus;
    PMPRI_INTERFACE_0           pInterface0  = NULL;
    DWORD                       dwIfIndex    = 0;
    DWORD                       dwBucketIndex= 0;
    PROUTER_INTERFACE_OBJECT    pIfObject    = NULL;
    DWORD                       dwStartIndex = ( lpdwResumeHandle == NULL )
                                               ? 0 : *lpdwResumeHandle;
    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwLevel != 0 )
    {
        return( ERROR_NOT_SUPPORTED );
    }

    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    if ( gblInterfaceTable.dwNumTotalInterfaces < dwStartIndex )
    {
        LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

        return( ERROR_NO_MORE_ITEMS );
    }

    *lpdwTotalEntries = gblInterfaceTable.dwNumTotalInterfaces - dwStartIndex;

    if ( dwPreferedMaximumLength != -1 )
    {
        *lpdwEntriesRead = dwPreferedMaximumLength /
                                     sizeof( MPR_INTERFACE_0 );

        if ( *lpdwEntriesRead > *lpdwTotalEntries )
        {
            *lpdwEntriesRead = *lpdwTotalEntries;
        }
    }
    else
    {
        *lpdwEntriesRead = *lpdwTotalEntries;
    }

    pInfoStruct->dwBufferSize = *lpdwEntriesRead * sizeof( MPR_INTERFACE_0 );

    pInfoStruct->pBuffer = MIDL_user_allocate( pInfoStruct->dwBufferSize );

    if ( pInfoStruct->pBuffer == NULL )
    {
        LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

        pInfoStruct->dwBufferSize = 0;

        return( ERROR_NOT_ENOUGH_MEMORY );
    }

    pInterface0 = (PMPRI_INTERFACE_0)pInfoStruct->pBuffer;

    for ( dwBucketIndex = 0; dwBucketIndex < NUM_IF_BUCKETS; dwBucketIndex++ )
    {
        for( pIfObject = gblInterfaceTable.IfBucket[dwBucketIndex];
             pIfObject != (ROUTER_INTERFACE_OBJECT *)NULL;
             pIfObject = pIfObject->pNext )
        {
            //
            // Check if this interface is within the range we need to copy
            // from.
            //

            if ( ( dwIfIndex >= dwStartIndex ) &&
                 ( dwIfIndex < ( dwStartIndex + *lpdwEntriesRead ) ) )
            {
                //
                // Copy the info
                //

                GetMprInterface0Data( pIfObject, pInterface0 );

                pInterface0++;
            }
            else if ( dwIfIndex >= (dwStartIndex+*lpdwEntriesRead) )
            {
                //
                // Beyond the range so exit
                //

                if ( lpdwResumeHandle != NULL )
                {
                    *lpdwResumeHandle = dwIfIndex;
                }

                LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

                return( ERROR_MORE_DATA );
            }

            dwIfIndex++;
        }
    }

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

    return( NO_ERROR );
}

//**
//
// Call:        RRouterInterfaceUpdateRoutes
//
// Returns:     NO_ERROR - Success
//              ERROR_ACCESS_DENIED
//
// Description: Simply called the appropriate router manager to do the real
//              work.
//
DWORD
RRouterInterfaceUpdateRoutes(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       hDimInterface,
    IN      DWORD                       dwPid,
    IN      ULONG_PTR                   hEvent,
    IN      DWORD                       dwCallersProcessId
)
{
    DWORD                       dwAccessStatus;
    PROUTER_INTERFACE_OBJECT    pIfObject    = NULL;
    HANDLE                      hEventDuplicated;
    HANDLE                      hEventToBeDuplicated;
    HANDLE                      hClientProcess;
    DWORD                       dwRetCode        = NO_ERROR;
    DWORD                       dwTransportIndex = GetTransportIndex( dwPid );

    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwTransportIndex == (DWORD)-1 )
    {
        return( ERROR_UNKNOWN_PROTOCOL_ID );
    }

    if ( hEvent == PtrToUlong(NULL) )
    {
        //
        // This call is to be synchrnonous, create an event and block on
        // it.
        //

        hEventToBeDuplicated = CreateEvent( NULL, FALSE, FALSE, NULL );

        if ( hEventToBeDuplicated == NULL )
        {
            return( GetLastError() );
        }

        dwCallersProcessId = GetCurrentProcessId();
    }
    else
    {
        hEventToBeDuplicated = (HANDLE)hEvent;
    }

    //
    // Get process handle of the caller of this API
    //

    hClientProcess = OpenProcess(
                            STANDARD_RIGHTS_REQUIRED | SPECIFIC_RIGHTS_ALL,
                            FALSE,
                            dwCallersProcessId);

    if ( hClientProcess == NULL )
    {
        return( GetLastError() );
    }

    //
    // Duplicate the handle to the event
    //

    if ( !DuplicateHandle(  hClientProcess,
                            (HANDLE)hEventToBeDuplicated,
                            GetCurrentProcess(),
                            &hEventDuplicated,
                            0,
                            FALSE,
                            DUPLICATE_SAME_ACCESS ) )
    {
        CloseHandle( hClientProcess );

        return( GetLastError() );
    }

    CloseHandle( hClientProcess );

    //
    // Validate the interface handle and check to see if it is connected
    //

    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    do
    {
        if ( ( pIfObject = IfObjectGetPointer(
                    DimIndexToHandle(hDimInterface))) == NULL )
        {
            dwRetCode = ERROR_INVALID_HANDLE;

            break;
        }

        if ( pIfObject->State != RISTATE_CONNECTED )
        {
            dwRetCode = ERROR_INTERFACE_NOT_CONNECTED;

            break;
        }

        //
        // Make sure the handle to the interface is NULL, then the interface
        // has not yet been added to the transport.
        //

        if ( pIfObject->Transport[dwTransportIndex].hInterface
                                                == INVALID_HANDLE_VALUE )
        {
            dwRetCode = ERROR_NO_SUCH_INTERFACE;

            break;
        }

        dwRetCode =
            gblRouterManagers[dwTransportIndex].DdmRouterIf.UpdateRoutes(
                        pIfObject->Transport[dwTransportIndex].hInterface,
                        (HANDLE)hEventDuplicated );

    } while( FALSE );

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

    if ( ( dwRetCode != NO_ERROR ) && ( dwRetCode != PENDING ) )
    {
        CloseHandle( hEventDuplicated );
    }

    if ( hEvent == PtrToUlong(NULL) )
    {
        if ( ( dwRetCode == NO_ERROR ) || ( dwRetCode == PENDING ) )
        {
            //
            // Wait for this event to be signalled
            //
            if ( WaitForSingleObject( hEventToBeDuplicated, INFINITE ) ==
                                                                 WAIT_FAILED )
            {
                dwRetCode = GetLastError();
            }
        }

        CloseHandle( hEventToBeDuplicated );

        if ( dwRetCode == PENDING )
        {
            dwRetCode = NO_ERROR;
        }
    }

    TracePrintfExA( gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "InterfaceUpdateRoutes returned %d", dwRetCode );

    return( dwRetCode );
}

//**
//
// Call:        RRouterInterfaceQueryUpdateResult
//
// Returns:     NO_ERROR                - Success
//              ERROR_ACCESS_DENIED     - FAILURE
//
//
// Description: Simply called the appropriate router manager to do the real
//              work.
//
DWORD
RRouterInterfaceQueryUpdateResult(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       hDimInterface,
    IN      DWORD                       dwPid,
    IN      LPDWORD                     pUpdateResult
)
{
    DWORD                       dwAccessStatus;
    DWORD                       dwRetCode        = NO_ERROR;
    PROUTER_INTERFACE_OBJECT    pIfObject        = NULL;
    DWORD                       dwTransportIndex = GetTransportIndex( dwPid );

    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwTransportIndex == (DWORD)-1 )
    {
        return( ERROR_UNKNOWN_PROTOCOL_ID );
    }

    //
    // Validate the interface handle and check to see if it is connected
    //

    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    do
    {
        if ( ( pIfObject = IfObjectGetPointer(
                        DimIndexToHandle(hDimInterface))) == NULL )
        {
            dwRetCode = ERROR_INVALID_HANDLE;

            break;
        }

        if ( pIfObject->State != RISTATE_CONNECTED )
        {
            dwRetCode = ERROR_INTERFACE_NOT_CONNECTED;

            break;
        }

        //
        // Make sure the handle to the interface is NULL, then the interface
        // has not yet been added to the transport.
        //

        if ( pIfObject->Transport[dwTransportIndex].hInterface
                                                    == INVALID_HANDLE_VALUE )
        {
            dwRetCode = ERROR_NO_SUCH_INTERFACE;

            break;
        }

        dwRetCode =
        gblRouterManagers[dwTransportIndex].DdmRouterIf.GetUpdateRoutesResult(
                        pIfObject->Transport[dwTransportIndex].hInterface,
                        pUpdateResult );

    }while( FALSE );

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

    TracePrintfExA( gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "QueryUpdateResult returned %d", dwRetCode );

    return( dwRetCode );
}

//**
//
// Call:        RRouterInterfaceConnect
//
// Returns:     ERROR_ACCESS_DENIED - The caller does not have sufficient priv.
//              ERROR_DDM_NOT_RUNNING - This call cannot be made because DDM is
//                                    not loaded
//              non-zero returns from DDMAdminInterfaceConnect
//
// Description: Simply calles into DDM to do the work.
//
DWORD
RRouterInterfaceConnect(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       hDimInterface,
    IN      ULONG_PTR                   hEvent,
    IN      DWORD                       fBlocking,
    IN      DWORD                       dwCallersProcessId
)
{
    DWORD dwAccessStatus;

    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( gblDIMConfigInfo.dwRouterRole == ROUTER_ROLE_LAN )
    {
        return( ERROR_DDM_NOT_RUNNING );
    }
    else
    {
        DWORD (*DDMAdminInterfaceConnect)( HANDLE, HANDLE, BOOL, DWORD ) =
            (DWORD(*)( HANDLE, HANDLE, BOOL, DWORD ) )
                        GetDDMEntryPoint("DDMAdminInterfaceConnect");

        if(NULL == DDMAdminInterfaceConnect)
        {   
            return ERROR_PROC_NOT_FOUND;
        }

        return( DDMAdminInterfaceConnect( DimIndexToHandle(hDimInterface),
                                          (HANDLE)hEvent,
                                          (BOOL)fBlocking,
                                          (DWORD)dwCallersProcessId ) );
    }
}

//**
//
// Call:        RRouterInterfaceDisconnect
//
// Returns:     ERROR_ACCESS_DENIED - The caller does not have sufficient priv.
//              ERROR_DDM_NOT_RUNNING - This call cannot be made because DDM is
//                                    not loaded
//              non-zero returns from DDMAdminInterfaceDisconnect
//
// Description: Simply calles into DDM to do the work.
//
DWORD
RRouterInterfaceDisconnect(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       hDimInterface
)
{
    DWORD dwAccessStatus;

    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( gblDIMConfigInfo.dwRouterRole == ROUTER_ROLE_LAN )
    {
        return( ERROR_DDM_NOT_RUNNING );
    }
    else
    {
        DWORD
        (*DDMAdminInterfaceDisconnect)( HANDLE ) =
           (DWORD(*)( HANDLE ) )GetDDMEntryPoint("DDMAdminInterfaceDisconnect");

        if(NULL == DDMAdminInterfaceDisconnect)
        {
            return ERROR_PROC_NOT_FOUND;
        }

        return( DDMAdminInterfaceDisconnect( DimIndexToHandle(hDimInterface) ) );
    }
}

//**
//
// Call:        RRouterInterfaceUpdatePhonebookInfo
//
// Returns:     NO_ERROR - Success
///             ERROR_ACCESS_DENIED - The caller does not have sufficient priv.
//              non-zero returns
//
// Description: Will update the phonebook information for a given interface
//
DWORD
RRouterInterfaceUpdatePhonebookInfo(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       hDimInterface
)
{
    DWORD                       dwAccessStatus;
    DWORD                       dwRetCode;
    PROUTER_INTERFACE_OBJECT    pIfObject        = NULL;

    //
    // Check if caller has access
    //

    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( gblDIMConfigInfo.dwRouterRole == ROUTER_ROLE_LAN )
    {
        return( ERROR_DDM_NOT_RUNNING );
    }

    //
    // Validate the interface handle and check to see if it is connected
    //

    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    do
    {
        DWORD (*IfObjectLoadPhonebookInfo)( ROUTER_INTERFACE_OBJECT * ) =
                (DWORD(*)( ROUTER_INTERFACE_OBJECT * ))
                            GetDDMEntryPoint("IfObjectLoadPhonebookInfo");

        if(NULL == IfObjectLoadPhonebookInfo)
        {
            dwRetCode = ERROR_PROC_NOT_FOUND;
            break;
        }

        if ( ( pIfObject = IfObjectGetPointer(
                        DimIndexToHandle(hDimInterface))) == NULL )
        {
            dwRetCode = ERROR_INVALID_HANDLE;

            break;
        }

        //
        // Load phonebook information for this interface
        //

        dwRetCode = IfObjectLoadPhonebookInfo( pIfObject );

    } while( FALSE );

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

    return( dwRetCode );
}

DWORD
RRouterInterfaceSetCredentialsEx(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       dwLevel,
    IN      PDIM_INFORMATION_CONTAINER  pInfoStruct,
    IN      DWORD                       hInterface
    )
{
    ROUTER_INTERFACE_OBJECT *   pIfObject = NULL;
    PWCHAR                      pszPath = NULL;
    DWORD                       dwAccessStatus;
    DWORD                       dwRetCode = NO_ERROR;
    MPR_CREDENTIALSEX_0 *       pCredsEx0 = NULL;

    MPR_CREDENTIALSEX_1 *       pCredsEx1 = NULL;

    MPR_CREDENTIALSEXI *        pCredsI = (MPR_CREDENTIALSEXI *)
                                    pInfoStruct->pBuffer;
                                    
    //
    // Check if caller has access
    //
    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( (dwLevel != 0 ) &&
         (dwLevel != 1 ) &&
         (dwLevel != 2 ))
    {
        return( ERROR_NOT_SUPPORTED );
    }

    //
    // Adjust the given buffer for any pointers to 
    // variable length data.
    //
    if ( dwLevel == 0 )
    {
        //
        // Thunk the credentials structure
        //
        pCredsEx0 = LOCAL_ALLOC(LPTR, 
                    pCredsI->dwSize + sizeof(MPR_CREDENTIALSEX_0));

        if(pCredsEx0 == NULL)
        {
            return GetLastError();
        }
        pCredsEx0->dwSize = pCredsI->dwSize;
        pCredsEx0->lpbCredentialsInfo = (PBYTE) (pCredsEx0 + 1);
        CopyMemory(pCredsEx0->lpbCredentialsInfo,
                   ((PBYTE) pCredsI) + pCredsI->dwOffset,
                   pCredsI->dwSize);

    }

    if(     ( dwLevel == 1 )
        ||  ( dwLevel == 2 ))
    {

        //
        // Thunk the credentials structure
        //
        pCredsEx1 = LOCAL_ALLOC(LPTR, 
                    pCredsI->dwSize + sizeof(MPR_CREDENTIALSEX_1));

        if(pCredsEx1 == NULL)
        {
            return GetLastError();
        }
        
        pCredsEx1->dwSize = pCredsI->dwSize;
        pCredsEx1->lpbCredentialsInfo = (PBYTE) (pCredsEx1 + 1);
        CopyMemory(pCredsEx1->lpbCredentialsInfo,
                   ((PBYTE) pCredsI) + pCredsI->dwOffset,
                   pCredsI->dwSize);
    }
        
    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    do
    {
        if (( 2 != dwLevel ) &&
            ( pIfObject = IfObjectGetPointer(DimIndexToHandle(hInterface))) == NULL )
        {
            dwRetCode = ERROR_INVALID_HANDLE;
            break;
        }

        //
        // Process Level 0
        //
        if ( dwLevel == 0 )
        {
            if (pIfObject->IfType != ROUTER_IF_TYPE_FULL_ROUTER)
            {
                dwRetCode = ERROR_INVALID_PARAMETER;
                break;
            }

            dwRetCode = RpbkGetPhonebookPath( &pszPath );
            if ( dwRetCode != NO_ERROR)
            {
                break;
            }
            
            dwRetCode = RasSetEapUserDataW(
                            NULL,
                            pszPath,
                            pIfObject->lpwsInterfaceName,
                            pCredsEx0->lpbCredentialsInfo,
                            pCredsEx0->dwSize);
        }

        //
        // Process Levels 1 and 2
        //
        if (( dwLevel == 1 ) ||
            ( dwLevel == 2 ))
        {   
            HANDLE hEntry = NULL;
            
            if( ((NULL != pIfObject) &&
                 (pIfObject->IfType != ROUTER_IF_TYPE_FULL_ROUTER)) ||
               (pCredsEx1->dwSize > (PWLEN+1) * sizeof(WCHAR)))
            {
                dwRetCode = ERROR_INVALID_PARAMETER;
                break;
            }

            dwRetCode = RpbkGetPhonebookPath (&pszPath);
            if( dwRetCode != NO_ERROR )
            {
                break;
            }
            {
                RASCREDENTIALS rasCredentials;

                ZeroMemory(&rasCredentials, sizeof(RASCREDENTIALS));

                rasCredentials.dwSize = sizeof(RASCREDENTIALS);

                if(dwLevel == 1)
                {
                    rasCredentials.dwMask = RASCM_DDMPreSharedKey;
                }
                else if(dwLevel == 2)
                {
                    rasCredentials.dwMask = RASCM_ServerPreSharedKey;
                }

                memcpy((PBYTE) &rasCredentials.szPassword,
                        pCredsEx1->lpbCredentialsInfo,
                        pCredsEx1->dwSize);
                //
                // Call Ras api to set the credentials.
                //
                dwRetCode = RasSetCredentials(
                                pszPath,
                                (NULL != pIfObject)
                                ? pIfObject->lpwsInterfaceName
                                : NULL,
                                &rasCredentials,
                                (pCredsEx1->dwSize == 0)
                                ? TRUE
                                : FALSE);

                //
                // If these are server credentials, the set might fail
                // because of no listens being posted on l2tp ports.
                // Attempt to post listens on l2tp ports now that we
                // have a preshared key.
                //
                if(     (ERROR_IPSEC_MM_AUTH_NOT_FOUND == dwRetCode)
                    &&  (pCredsEx1->dwSize > 0)
                    &&  (dwLevel == 2))
                {
                    VOID (*DDMServicePostListens)(VOID *);

                    DDMServicePostListens = (VOID(*)(VOID *))
                                    GetDDMEntryPoint("DDMServicePostListens");
                    if(DDMServicePostListens != NULL)
                    {
                        DWORD rdt = RDT_Tunnel_L2tp;

                        DDMServicePostListens((VOID *) &rdt);

                        dwRetCode = ERROR_SUCCESS;
                    }
                }
            }                                
        }
        
    } while( FALSE );

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

    // Cleanup
    {
        if ( pszPath )
        {
            RpbkFreePhonebookPath( pszPath );
        }

        if(pCredsEx0)
        {
            LOCAL_FREE(pCredsEx0);
        }

        if(pCredsEx1)
        {
            LOCAL_FREE(pCredsEx1);
        }
    }
    
    TracePrintfExA( gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "InterfaceSetCredEx returned %d", dwRetCode );

    return( dwRetCode );
}

DWORD
RRouterInterfaceGetCredentialsEx(
    IN      MPR_SERVER_HANDLE           hMprServer,
    IN      DWORD                       dwLevel,
    IN      PDIM_INFORMATION_CONTAINER  pInfoStruct,
    IN      DWORD                       hInterface
    )
{
    ROUTER_INTERFACE_OBJECT *   pIfObject = NULL;
    PWCHAR                      pszPath = NULL;
    DWORD                       dwAccessStatus;
    DWORD                       dwRetCode = NO_ERROR, dwSize = 0;
    MPR_CREDENTIALSEXI *        pCredsI = NULL;
    
    //
    // Check if caller has access
    //
    if ( DimSecObjAccessCheck( DIMSVC_ALL_ACCESS, &dwAccessStatus) != NO_ERROR)
    {
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        return( ERROR_ACCESS_DENIED );
    }

    if (    (dwLevel != 0 )
        &&  (dwLevel != 1 )
        &&  (dwLevel != 2 ))
    {
        return( ERROR_NOT_SUPPORTED );
    }

    EnterCriticalSection( &(gblInterfaceTable.CriticalSection) );

    do
    {
        if (    (dwLevel != 2)
            &&  (( pIfObject = IfObjectGetPointer(
                    DimIndexToHandle(hInterface))) == NULL ))
        {
            dwRetCode = ERROR_INVALID_HANDLE;
            break;
        }

        //
        // Process Level 0
        //
        if ( dwLevel == 0 )
        {
            if (pIfObject->IfType != ROUTER_IF_TYPE_FULL_ROUTER)
            {
                dwRetCode = ERROR_INVALID_PARAMETER;
                break;
            }

            dwRetCode = RpbkGetPhonebookPath( &pszPath );
            if ( dwRetCode != NO_ERROR)
            {
                break;
            }

            // Find out how big the data is
            //
            dwSize = 0;
            dwRetCode = RasGetEapUserDataW(
                            NULL,
                            pszPath,
                            pIfObject->lpwsInterfaceName,
                            NULL,
                            &dwSize);
            if ( (dwRetCode != NO_ERROR) && 
                 (dwRetCode != ERROR_BUFFER_TOO_SMALL)
               )
            {
                break;
            }

            // Allocate the return value
            //
            pCredsI = (MPR_CREDENTIALSEXI *) 
                MIDL_user_allocate(dwSize + sizeof(MPR_CREDENTIALSEXI));
            if ( pCredsI == NULL )
            {
                dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
                break;
            }

            // Initialize
            ZeroMemory(pCredsI, dwSize + sizeof(MPR_CREDENTIALSEXI));
            pCredsI->dwSize = dwSize;
            pCredsI->dwOffset = FIELD_OFFSET(MPR_CREDENTIALSEXI, bData);
            if ( pCredsI->dwSize == 0)
            {
                dwRetCode = NO_ERROR;
                break;
            }
            
            // Read in the credentials info
            //
            // pCredsEx0->lpbCredentialsInfo = (BYTE*) (pCredsEx0 + 1);
            dwRetCode = RasGetEapUserDataW(
                            NULL,
                            pszPath,
                            pIfObject->lpwsInterfaceName,
                            pCredsI->bData,
                            &dwSize);
            if ( dwRetCode != NO_ERROR )                                
            {
                break;
            }
        }
        else if( (dwLevel == 1 ) || (dwLevel == 2))
        {
            RASCREDENTIALS rasCredentials;
            
            if (    (dwLevel != 2)
                &&  (pIfObject->IfType != ROUTER_IF_TYPE_FULL_ROUTER))
            {
                dwRetCode = ERROR_INVALID_PARAMETER;
                break;
            }

            dwRetCode = RpbkGetPhonebookPath( &pszPath );
            if ( dwRetCode != NO_ERROR)
            {
                break;
            }

            ZeroMemory(&rasCredentials, sizeof(RASCREDENTIALS));

            rasCredentials.dwSize = sizeof(RASCREDENTIALS);

            if(dwLevel == 1)
            {
                rasCredentials.dwMask = RASCM_DDMPreSharedKey;
            }
            else if(dwLevel == 2)
            {
                rasCredentials.dwMask = RASCM_ServerPreSharedKey;
            }

            dwRetCode = RasGetCredentials(
                            pszPath,
                            (NULL != pIfObject) 
                            ? pIfObject->lpwsInterfaceName
                            : NULL,
                            &rasCredentials);

            if(dwRetCode != NO_ERROR)
            {
                break;
            }

            dwSize = (1 + wcslen(rasCredentials.szPassword)) * sizeof(WCHAR);

            //
            // allocate for pCredsEx1
            //
            pCredsI = (MPR_CREDENTIALSEXI *) 
                    MIDL_user_allocate(dwSize + sizeof(MPR_CREDENTIALSEXI));
                    
            if(NULL == pCredsI)
            {
                break;
            }

            ZeroMemory(pCredsI, dwSize + sizeof(MPR_CREDENTIALSEXI));
            pCredsI->dwSize = dwSize;
            pCredsI->dwOffset = FIELD_OFFSET(MPR_CREDENTIALSEXI, bData);
            
            CopyMemory((pCredsI->bData),
                       (PBYTE) rasCredentials.szPassword,
                       dwSize);
            
        }
        
    } while( FALSE );

    LeaveCriticalSection( &(gblInterfaceTable.CriticalSection) );

    // Assign the return value
    //
    if ( dwRetCode == NO_ERROR )
    {

        pInfoStruct->pBuffer = (BYTE*)pCredsI;
        pInfoStruct->dwBufferSize = 
            sizeof(MPR_CREDENTIALSEXI) + pCredsI->dwSize;
            
    }
    
    // Cleanup
    {
        if ( pszPath )
        {
            RpbkFreePhonebookPath( pszPath );
        }
    }
    
    TracePrintfExA( gblDIMConfigInfo.dwTraceId, DIM_TRACE_FLAGS,
                    "InterfaceSetCredEx returned %d", dwRetCode );

    return( dwRetCode );
}