/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    rxcemgmt.c

Abstract:

    This module implements the RXCE routines related to connection management.

Revision History:

    Balan Sethu Raman     [SethuR]    15-Feb-1995

Notes:

--*/

#include "precomp.h"
#pragma  hdrstop

#ifdef  ALLOC_PRAGMA
#pragma alloc_text(PAGE, RxCepInitializeVC)
#pragma alloc_text(PAGE, RxCeBuildVC)
#pragma alloc_text(PAGE, RxCeTearDownVC)
#pragma alloc_text(PAGE, RxCeInitiateVCDisconnect)
#pragma alloc_text(PAGE, DuplicateConnectionInformation)
#pragma alloc_text(PAGE, RxCepInitializeConnection)
#pragma alloc_text(PAGE, RxCeBuildConnection)
#pragma alloc_text(PAGE, RxCeCleanupConnectCallOutContext)
#pragma alloc_text(PAGE, RxCeBuildConnectionOverMultipleTransports)
#pragma alloc_text(PAGE, RxCeTearDownConnection)
#pragma alloc_text(PAGE, RxCeCancelConnectRequest)
#pragma alloc_text(PAGE, RxCeQueryInformation)
#endif

//
//  The debug trace level
//

#define Dbg                              (DEBUG_TRACE_RXCEMANAGEMENT)

NTSTATUS
RxCepInitializeVC(
    PRXCE_VC         pVc,
    PRXCE_CONNECTION pConnection)
/*++

Routine Description:

    This routine initializes a VCdata structure

Arguments:

    pVc            - the VC instance.

    pConnection    - the connection.

Return Value:

    STATUS_SUCCESS if successfull.

Notes:

--*/
{
    PAGED_CODE();

    ASSERT(RxCeIsConnectionValid(pConnection));

    RtlZeroMemory(
        pVc,
        sizeof(RXCE_VC));

    pVc->Signature   = RXCE_VC_SIGNATURE;
    pVc->pConnection = pConnection;
    pVc->hEndpoint   = INVALID_HANDLE_VALUE;
    pVc->State       = RXCE_VC_DISCONNECTED;

    return STATUS_SUCCESS;
}

NTSTATUS
RxCeBuildVC(
    IN OUT PRXCE_VC            pVc,
    IN     PRXCE_CONNECTION    pConnection)
/*++

Routine Description:

    This routine adds a virtual circuit to a specified connection

Arguments:

    pConnection  - the connection for which a VC is to be added

    pVcPointer    - the handle of the new virtual circuit

Return Value:

    STATUS_SUCCESS if successfull.

Notes:

--*/
{
    NTSTATUS         Status = STATUS_INVALID_PARAMETER;

    PRXCE_TRANSPORT  pTransport = NULL;
    PRXCE_ADDRESS    pAddress   = NULL;

    PAGED_CODE();

    // Update profiling info.
    RxProfile(RxCeManagement,RxCeBuildVc);

    try {
        pAddress = pConnection->pAddress;
        pTransport = pAddress->pTransport;

        if (RxCeIsConnectionValid(pConnection) &&
            RxCeIsAddressValid(pAddress) &&
            RxCeIsTransportValid(pTransport)) {

            Status = RxCepInitializeVC(
                         pVc,
                         pConnection);

            if (NT_SUCCESS(Status)) {
                Status = RxTdiConnect(
                             pTransport,    // the associated transport
                             pAddress,      // the RxCe address
                             pConnection,   // the RxCe connection
                             pVc);          // the RxCe virtual circuit associated with the connection

                if (Status == STATUS_SUCCESS) {
                    pVc->State       = RXCE_VC_ACTIVE;
                }
            }
        }
    } finally {
        if (AbnormalTermination()) {
            Status = STATUS_INVALID_PARAMETER;
            RxLog(("RxCeAddVC: VC: %lx Status %lx\n",pVc,Status));
            RxWmiLog(LOG,
                     RxCeBuildVC,
                     LOGPTR(pVc)
                     LOGULONG(Status));
        }
    }

    return Status;
}


NTSTATUS
RxCeInitiateVCDisconnect(
    IN PRXCE_VC pVc)
/*++

Routine Description:

    This routine initiates a disconnect on the VC.

Arguments:

    pVc - the VC instance to be disconnected

Return Value:

    STATUS_SUCCESS if successfull.

Notes:

--*/
{
    NTSTATUS         Status = STATUS_SUCCESS;

    PRXCE_TRANSPORT  pTransport  = NULL;
    PRXCE_ADDRESS    pAddress    = NULL;
    PRXCE_CONNECTION pConnection = NULL;

    PAGED_CODE();

    // Update profiling info.
    RxProfile(RxCeManagement,RxCeTearDownVc);

    try {
        if ((pVc->pEndpointFileObject != NULL)  &&
            (pVc->hEndpoint != INVALID_HANDLE_VALUE)) {
            pConnection = pVc->pConnection;
            pAddress = pConnection->pAddress;
            pTransport = pAddress->pTransport;

            if (RxCeIsVcValid(pVc) &&
                RxCeIsConnectionValid(pConnection) &&
                RxCeIsAddressValid(pAddress) &&
                RxCeIsTransportValid(pTransport)) {

                LONG VcState = InterlockedExchange(
                                   &pVc->State,
                                   RXCE_VC_TEARDOWN);

                if (VcState != RXCE_VC_TEARDOWN) {
                    Status = RxTdiDisconnect(
                                 pTransport,    // the associated transport
                                 pAddress,      // the RxCe address
                                 pConnection,   // the RxCe connection
                                 pVc,           // the RxCe virtual circuit associated with the connection
                                 RXCE_DISCONNECT_ABORT); // disconnect options

                    if (!NT_SUCCESS(Status)) {
                        RxDbgTrace(0, Dbg,("RxCeTearDownVC returned %lx\n",Status));
                    }
                } else {
                    Status = STATUS_SUCCESS;
                }
            } else {
                RxDbgTrace(0, Dbg,("RxCeTearDownVC -- Invalid VC %lx\n",pVc));
            }
        }
    } finally {
        if (AbnormalTermination()) {
            Status = STATUS_INVALID_PARAMETER;
            RxLog(("RxCeInitiateVCDisconnect: VC: %lx Status %lx\n",pVc,Status));
            RxWmiLog(LOG,
                     RxCeInitiateVCDisconnect,
                     LOGPTR(pVc)
                     LOGULONG(Status));
        }
    }

    return Status;
}

NTSTATUS
RxCeTearDownVC(
    IN PRXCE_VC pVc)
/*++

Routine Description:

    This routine tears down the VC instance.

Arguments:

    pVc - the VC instance to be torn down

Return Value:

    STATUS_SUCCESS if successfull.

Notes:

--*/
{
    NTSTATUS         Status = STATUS_SUCCESS;

    PRXCE_TRANSPORT  pTransport  = NULL;
    PRXCE_ADDRESS    pAddress    = NULL;
    PRXCE_CONNECTION pConnection = NULL;

    PAGED_CODE();

    // Update profiling info.
    RxProfile(RxCeManagement,RxCeTearDownVc);

    try {
        if (pVc->pCleanUpEvent != NULL) {
            // wait for the clean up of connections over other transports to be completed
            KeWaitForSingleObject(
                       pVc->pCleanUpEvent,
                       Executive,
                       KernelMode,
                       FALSE,
                       NULL);

            RxFreePool(pVc->pCleanUpEvent);
            pVc->pCleanUpEvent = NULL;
        }

        if ((pVc->pEndpointFileObject != NULL)  &&
            (pVc->hEndpoint != INVALID_HANDLE_VALUE)) {
            pConnection = pVc->pConnection;
            pAddress = pConnection->pAddress;
            pTransport = pAddress->pTransport;

            if (RxCeIsVcValid(pVc) &&
                RxCeIsConnectionValid(pConnection) &&
                RxCeIsAddressValid(pAddress) &&
                RxCeIsTransportValid(pTransport)) {

                LONG VcState = InterlockedExchange(
                                   &pVc->State,
                                   RXCE_VC_TEARDOWN);

                if (VcState != RXCE_VC_TEARDOWN) {
                    Status = RxTdiDisconnect(
                                 pTransport,    // the associated transport
                                 pAddress,      // the RxCe address
                                 pConnection,   // the RxCe connection
                                 pVc,           // the RxCe virtual circuit associated with the connection
                                 RXCE_DISCONNECT_ABORT); // disconnect options

                    if (!NT_SUCCESS(Status)) {
                        RxDbgTrace(0, Dbg,("RxCeTearDownVC returned %lx\n",Status));
                    }
                } else {
                    Status = STATUS_SUCCESS;
                }
            } else {
                RxDbgTrace(0, Dbg,("RxCeTearDownVC -- Invalid VC %lx\n",pVc));
            }

            // Dereference the endpoint file object.
            ObDereferenceObject(pVc->pEndpointFileObject);

            // Close the endpoint file object handle
            Status = ZwClose(pVc->hEndpoint);

            ASSERT(Status == STATUS_SUCCESS);

            pVc->hEndpoint = INVALID_HANDLE_VALUE;
            pVc->pEndpointFileObject = NULL;
        }

        RtlZeroMemory(pVc,sizeof(RXCE_VC));
    } finally {
        if (AbnormalTermination()) {
            Status = STATUS_INVALID_PARAMETER;
            RxLog(("RxCeTearDownVC: VC: %lx Status %lx\n",pVc,Status));
            RxWmiLog(LOG,
                     RxCeTearDownVC,
                     LOGPTR(pVc)
                     LOGULONG(Status));
        }
    }
    
    return Status;
}


NTSTATUS
DuplicateConnectionInformation(
    PRXCE_CONNECTION_INFORMATION *pCopy,
    PRXCE_CONNECTION_INFORMATION pOriginal,
    POOL_TYPE                    PoolType)
/*++

Routine Description:

    This routine duplicates a connection information addresses.

Arguments:

    pCopy  - the pointer to the new copy

    pOriginal - the original.

    PoolType - type of pool for memory allocation

Return Value:

    STATUS_SUCCESS if successful.

Notes:

--*/
{
    PVOID pUserData = NULL;
    PVOID pRemoteAddress = NULL;
    PVOID pOptions = NULL;
    PRXCE_CONNECTION_INFORMATION pConnectionInformation = NULL;
    BOOLEAN fFailed = FALSE;

    PAGED_CODE();

    pConnectionInformation = RxAllocatePoolWithTag(
                                 PoolType,
                                 sizeof(RXCE_CONNECTION_INFORMATION),
                                 RXCE_CONNECTION_POOLTAG);
    if (pConnectionInformation != NULL) {
        RtlCopyMemory(
            pConnectionInformation,
            pOriginal,
            sizeof(RXCE_CONNECTION_INFORMATION));
    } else
        fFailed = TRUE;

    if (!fFailed && pOriginal->UserDataLength > 0) {
        pUserData = RxAllocatePoolWithTag(
                        PoolType,
                        pOriginal->UserDataLength,
                        RXCE_CONNECTION_POOLTAG);
        if (pUserData != NULL) {
            RtlCopyMemory(
                pUserData,
                pOriginal->UserData,
                pOriginal->UserDataLength);
        } else
            fFailed = TRUE;
    }

    if (!fFailed && pOriginal->RemoteAddressLength > 0) {
        pRemoteAddress = RxAllocatePoolWithTag(
                             PoolType,
                             pOriginal->RemoteAddressLength,
                             RXCE_CONNECTION_POOLTAG);
        if (pRemoteAddress != NULL) {
            PTA_ADDRESS pTaAdress;
            PTRANSPORT_ADDRESS pTransportAddress = (PTRANSPORT_ADDRESS)pRemoteAddress;
            LONG NoOfAddress;

            RtlCopyMemory(
                pRemoteAddress,
                pOriginal->RemoteAddress,
                pOriginal->RemoteAddressLength);

            pTaAdress = &pTransportAddress->Address[0];

            for (NoOfAddress=0; NoOfAddress<pTransportAddress->TAAddressCount;NoOfAddress++) {
                if (pTaAdress->AddressType == TDI_ADDRESS_TYPE_NETBIOS_UNICODE_EX) {
                    PTDI_ADDRESS_NETBIOS_UNICODE_EX pTdiNetbiosUnicodeExAddress;

                    pTdiNetbiosUnicodeExAddress = (PTDI_ADDRESS_NETBIOS_UNICODE_EX)pTaAdress->Address;
                    pTdiNetbiosUnicodeExAddress->EndpointName.Buffer = (PWSTR)pTdiNetbiosUnicodeExAddress->EndpointBuffer;
                    pTdiNetbiosUnicodeExAddress->RemoteName.Buffer = (PWSTR)pTdiNetbiosUnicodeExAddress->RemoteNameBuffer;

                    //DbgPrint("Rdbss copy NETBIOS_UNICODE_EX on TA %lx UA %lx %wZ %wZ\n",
                    //         pTaAdress,
                    //         pTdiNetbiosUnicodeExAddress,
                    //         &pTdiNetbiosUnicodeExAddress->EndpointName,
                    //         &pTdiNetbiosUnicodeExAddress->RemoteName);
                    break;
                } else {
                    pTaAdress = (PTA_ADDRESS)((PCHAR)pTaAdress +
                                    FIELD_OFFSET(TA_ADDRESS,Address) +
                                    pTaAdress->AddressLength);
                }
            }
        } else
            fFailed = TRUE;
    }

    if (!fFailed && pOriginal->OptionsLength > 0) {
        pOptions = RxAllocatePoolWithTag(
                       PoolType,
                       pOriginal->OptionsLength,
                       RXCE_CONNECTION_POOLTAG);

        if (pOptions != NULL) {
            RtlCopyMemory(
                pOptions,
                pOriginal->Options,
                pOriginal->OptionsLength);
        } else
            fFailed = TRUE;
    }

    if (!fFailed) {
        pConnectionInformation->UserData = pUserData;
        pConnectionInformation->RemoteAddress = pRemoteAddress;
        pConnectionInformation->Options = pOptions;
        *pCopy = pConnectionInformation;
        return STATUS_SUCCESS;
    } else {
        if (pOptions != NULL) {
            RxFreePool(pOptions);
        }

        if (pRemoteAddress != NULL) {
            RxFreePool(pRemoteAddress);
        }

        if (pUserData != NULL) {
            RxFreePool(pUserData);
        }

        if (pConnectionInformation != NULL) {
            RxFreePool(pConnectionInformation);
        }

        *pCopy = NULL;
        return STATUS_INSUFFICIENT_RESOURCES;
    }
}

NTSTATUS
RxCepInitializeConnection(
    IN OUT PRXCE_CONNECTION             pConnection,
    IN     PRXCE_ADDRESS                pAddress,
    IN     PRXCE_CONNECTION_INFORMATION pConnectionInformation,
    IN  PRXCE_CONNECTION_EVENT_HANDLER  pHandler,
    IN  PVOID                           pEventContext)
/*++

Routine Description:

    This routine initializes a connection data structure

Arguments:

    pConnection    - the newly created connection.

    pAddress       - the local address

    pConnectionInformation - the connection information specifying the remote address.

    pHandler       - the handler for processing receive indications

    pEventContext  - the context to be used for indications

Return Value:

    STATUS_SUCCESS if successfull.

Notes:

--*/
{
    NTSTATUS Status;

    PAGED_CODE();

    // Initialize the new connection
    RtlZeroMemory(
        pConnection,
        sizeof(RXCE_CONNECTION));

    pConnection->Signature = RXCE_CONNECTION_SIGNATURE;
    pConnection->pAddress = pAddress;

    // Duplicate the connection information if successful
    if (pConnectionInformation != NULL) {
        Status = DuplicateConnectionInformation(
                     &pConnection->pConnectionInformation,
                     pConnectionInformation,
                     NonPagedPool);
    }

    if (NT_SUCCESS(Status) &&
        (pHandler != NULL)) {
        pConnection->pHandler = (PRXCE_CONNECTION_EVENT_HANDLER)
                                 RxAllocatePoolWithTag(
                                     NonPagedPool,
                                     sizeof(RXCE_CONNECTION_EVENT_HANDLER),
                                     RXCE_CONNECTION_POOLTAG);

        if (pConnection->pHandler != NULL) {
            RtlZeroMemory(
                pConnection->pHandler,
                sizeof(RXCE_CONNECTION_EVENT_HANDLER));

            *(pConnection->pHandler) = *pHandler;
            pConnection->pContext    = pEventContext;
        } else {
            Status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    return Status;
}

NTSTATUS
RxCeBuildConnection(
    IN  PRXCE_ADDRESS                  pAddress,
    IN  PRXCE_CONNECTION_INFORMATION   pConnectionInformation,
    IN  PRXCE_CONNECTION_EVENT_HANDLER pHandler,
    IN  PVOID                          pEventContext,
    IN OUT PRXCE_CONNECTION            pConnection,
    IN OUT PRXCE_VC                    pVc)
/*++

Routine Description:

    This routine establishes a connection between a local RxCe address and a given remote address

Arguments:

    pAddress       - the local address

    pConnectionInformation - the connection information specifying the remote address.

    pHandler       - the handler for processing receive indications

    pEventContext  - the context to be used for indications

    pConnection    - the newly created connection.

    pVc            - the VC associated with the connection.

Return Value:

    STATUS_SUCCESS if successfull.

Notes:

--*/
{
    NTSTATUS          Status;

    PRXCE_TRANSPORT   pTransport     = NULL;

    PAGED_CODE();

    // Update profiling info.
    RxProfile(RxCeManagement,RxCeBuildConnection);

    try {
        pTransport = pAddress->pTransport;

        if (RxCeIsAddressValid(pAddress) &&
            RxCeIsTransportValid(pTransport)) {

            Status = RxCepInitializeConnection(
                         pConnection,
                         pAddress,
                         pConnectionInformation,
                         pHandler,
                         pEventContext);

            if (NT_SUCCESS(Status)) {
                Status = RxCeBuildVC(pVc,pConnection);
            }

            if (!NT_SUCCESS(Status)) {
                RxCeTearDownVC(pVc);
                RxCeTearDownConnection(pConnection);
                RxDbgTrace(0, Dbg,("RxCeOpenConnection returned %lx\n",Status));
            } else {
                // NetBT may return the DNS name on Remote Address
                RtlCopyMemory(pConnectionInformation->RemoteAddress,
                              pConnection->pConnectionInformation->RemoteAddress,
                              pConnection->pConnectionInformation->RemoteAddressLength);
            }
        } else {
            Status = STATUS_INVALID_PARAMETER;
        }
    } finally {
        if (AbnormalTermination()) {
            Status = STATUS_INVALID_PARAMETER;
            RxLog(("RxCeCreateConnection %lx \n",pAddress));
            RxWmiLog(LOG,
                     RxCeBuildConnection,
                     LOGPTR(pAddress));
        }
    }

    return Status;
}

extern
NTSTATUS
RxCeCompleteConnectRequest(
    PRX_CALLOUT_PARAMETERS_BLOCK pParameterBlock);

NTSTATUS
RxCeInitiateConnectRequest(
    PRX_CALLOUT_PARAMETERS_BLOCK pParameterBlock)
/*++

Routine Description:

    This routine initiates a connection callout request to a particular transport

Arguments:

    pParameterBlock  - the parameter block for initaiting the connection.

Notes:

--*/
{
    NTSTATUS Status;

    KIRQL   OldIrql;

    BOOLEAN InitiateConnectionRequest;

    PRX_CREATE_CONNECTION_CALLOUT_CONTEXT   pCreateConnectionContext;

    pCreateConnectionContext =  (PRX_CREATE_CONNECTION_CALLOUT_CONTEXT)
                                pParameterBlock->pCallOutContext;

    KeAcquireSpinLock(&pCreateConnectionContext->SpinLock,&OldIrql);

    InitiateConnectionRequest = (!pCreateConnectionContext->WinnerFound);

    KeReleaseSpinLock(&pCreateConnectionContext->SpinLock,OldIrql);

    if (InitiateConnectionRequest) {
        Status = RxTdiInitiateAsynchronousConnect(
                     (PRX_CREATE_CONNECTION_PARAMETERS_BLOCK)pParameterBlock);
    } else {
        Status = STATUS_CANCELLED;
    }

    if (Status != STATUS_PENDING) {
        pParameterBlock->CallOutStatus = Status;

        RxCeCompleteConnectRequest(pParameterBlock);
    }
    return Status;
}

VOID
RxCeCleanupConnectCallOutContext(
    PRX_CREATE_CONNECTION_CALLOUT_CONTEXT pCreateConnectionContext)
/*++

Routine Description:

    This routine cleansup a connection callout request. This cannot be done in
    the context of any of the transport callback routines because of environmental
    constraints, i.e., Transports can callback at DPC level.

Arguments:

    pCreateConnectionContext - the connection context.

Notes:

--*/
{
    NTSTATUS Status;

    // Walk through the list of parameter blocks associated with this
    // callout context and initiate the appropriate tear down action.

    PRX_CREATE_CONNECTION_PARAMETERS_BLOCK pTempParameterBlock;
    PRDBSS_DEVICE_OBJECT pRxDeviceObject = NULL;

    
    PAGED_CODE();
    
    pRxDeviceObject = pCreateConnectionContext->pRxDeviceObject;

    pTempParameterBlock = (PRX_CREATE_CONNECTION_PARAMETERS_BLOCK)
                          pCreateConnectionContext->pCallOutParameterBlock;

    while (pTempParameterBlock != NULL) {
        if (pTempParameterBlock->CallOutId != pCreateConnectionContext->WinnerCallOutId) {
            RxTdiCleanupAsynchronousConnect(
                pTempParameterBlock);
        }

        RxCeTearDownVC(
            &pTempParameterBlock->Vc);

        RxCeTearDownConnection(
            &pTempParameterBlock->Connection);

        pTempParameterBlock = (PRX_CREATE_CONNECTION_PARAMETERS_BLOCK)
                              pTempParameterBlock->pNextCallOutParameterBlock;
    }

    if (pCreateConnectionContext->pCallOutParameterBlock != NULL) {
        RxLog(("Freeparamblock %x, %x\n",
                pCreateConnectionContext->pCallOutParameterBlock, KeGetCurrentThread()));
        RxWmiLog(LOG,
                 RxCeCleanupConnectCallOutContext,
                 LOGPTR(pCreateConnectionContext->pCallOutParameterBlock));
        RxFreePool(pCreateConnectionContext->pCallOutParameterBlock);
    }

    if (pCreateConnectionContext->pCleanUpEvent != NULL) {
        RxFreePool(pCreateConnectionContext->pCleanUpEvent);
    } else {
        PRXCE_VC pVc = pCreateConnectionContext->pConnectionContext;
        
        KeSetEvent(pVc->pCleanUpEvent, 0, FALSE);
    }

    RxFreePool(pCreateConnectionContext);

    if (pRxDeviceObject != NULL) {
        RxDeregisterAsynchronousRequest(pRxDeviceObject);
    }
}

NTSTATUS
RxCeCompleteConnectRequest(
    PRX_CALLOUT_PARAMETERS_BLOCK pParameterBlock)
/*++

Routine Description:

    This routine completes a connection callout request

Arguments:

    pParameterBlock - the parameter block instance.

Notes:

--*/
{
    BOOLEAN  AllCallOutsCompleted = FALSE;
    BOOLEAN  AllCallOutsInitiated = FALSE;
    BOOLEAN  InvokeCompletionRoutine = FALSE;
    BOOLEAN  WinnerFound          = FALSE;
    NTSTATUS    Status = STATUS_SUCCESS;

    KIRQL OldIrql;

    PRX_CREATE_CONNECTION_PARAMETERS_BLOCK pWinningParameterBlock;

    PRX_CREATE_CONNECTION_CALLOUT_CONTEXT   pCreateConnectionContext;
    PRXCE_CONNECTION_COMPLETION_CONTEXT     pCompletionContext;
    PRXCE_CONNECTION_COMPLETION_ROUTINE     pCompletionRoutine;

    pCreateConnectionContext =  (PRX_CREATE_CONNECTION_CALLOUT_CONTEXT)
                                pParameterBlock->pCallOutContext;

    // save the two values below as the pCreateConnectionContext may be freed

    pCompletionContext = pCreateConnectionContext->pCompletionContext;
    pCompletionRoutine = pCreateConnectionContext->pCompletionRoutine;

    pWinningParameterBlock = NULL;

    KeAcquireSpinLock(&pCreateConnectionContext->SpinLock,&OldIrql);

    if (!pCreateConnectionContext->WinnerFound) {
        if (pParameterBlock->CallOutStatus == STATUS_SUCCESS) {
            // This instance of the call out was successful. Determine if this
            // instance is the winner.

            // In those cases in which the option was to select the best possible transport
            // the callout id of this instance must be less than the previously recorded
            // winner for the expectations to be revised.

            switch (pCreateConnectionContext->CreateOptions) {
            case RxCeSelectBestSuccessfulTransport:
                if (pParameterBlock->CallOutId != pCreateConnectionContext->BestPossibleWinner) {
                    break;
                }
                // lack of break intentional. The processing for the winner in the best transport case
                // and the first transport case is identical and have been folded together
            case RxCeSelectFirstSuccessfulTransport:
                {
                    pWinningParameterBlock = (PRX_CREATE_CONNECTION_PARAMETERS_BLOCK)
                                             pParameterBlock;
                }
                break;

            case RxCeSelectAllSuccessfulTransports:
            default:
                ASSERT(!"RXCE connection create option not yet implemented");
                break;
            }
        } else {
            switch (pCreateConnectionContext->CreateOptions) {
            case RxCeSelectBestSuccessfulTransport:
                {
                    // This instance was not successful. This implies one of two things
                    // -- a previously completed transport can be the winner or we can
                    // adjust our expectations as regards the eventual winner.

                    if (pParameterBlock->CallOutId == pCreateConnectionContext->BestPossibleWinner) {
                        // The transport that was regarded as the best transport has reported
                        // failure. Revise our expectations as regards the best transport.

                        PRX_CREATE_CONNECTION_PARAMETERS_BLOCK pTempParameterBlock;

                        pTempParameterBlock = (PRX_CREATE_CONNECTION_PARAMETERS_BLOCK)
                                              pCreateConnectionContext->pCallOutParameterBlock;

                        while (pTempParameterBlock != NULL) {
                            PRX_CREATE_CONNECTION_PARAMETERS_BLOCK pNextParameterBlock;

                            pNextParameterBlock = (PRX_CREATE_CONNECTION_PARAMETERS_BLOCK)
                                                  pTempParameterBlock->pNextCallOutParameterBlock;

                            if (pTempParameterBlock->CallOutId < pCreateConnectionContext->BestPossibleWinner) {
                                ASSERT(pTempParameterBlock->CallOutStatus != STATUS_SUCCESS);
                            } else {
                                if (pNextParameterBlock != NULL) {
                                    if (pNextParameterBlock->CallOutStatus
                                         == STATUS_PENDING) {
                                        pCreateConnectionContext->BestPossibleWinner =
                                            pNextParameterBlock->CallOutId;
                                        break;
                                    } else if (pNextParameterBlock->CallOutStatus
                                         == STATUS_SUCCESS ) {
                                        pWinningParameterBlock = pNextParameterBlock;
                                        break;
                                    }
                                }
                            }

                            pTempParameterBlock = pNextParameterBlock;
                        }
                    }

                }
                break;

            case RxCeSelectAllSuccessfulTransports:
            case RxCeSelectFirstSuccessfulTransport:
            default:
                break;
            }
        }

        if (pWinningParameterBlock != NULL) {
            // Transfer the parameters associated with the winning parameter block
            // onto the original connection and prepare the call out parameter block
            // for cleanup.

            pCreateConnectionContext->WinnerFound = TRUE;
            pCreateConnectionContext->WinnerCallOutId = pWinningParameterBlock->CallOutId;

            pCompletionContext->Status = STATUS_SUCCESS;
            pCompletionContext->AddressIndex = pWinningParameterBlock->CallOutId;

            pCompletionContext->pConnection->pAddress =
                pWinningParameterBlock->Connection.pAddress;

            pCompletionContext->pVc->hEndpoint =
                pWinningParameterBlock->Vc.hEndpoint;

            pCompletionContext->pVc->pEndpointFileObject =
                pWinningParameterBlock->Vc.pEndpointFileObject;

            pCompletionContext->pVc->State = RXCE_VC_ACTIVE;

            pCompletionContext->pVc->pCleanUpEvent = pCreateConnectionContext->pCleanUpEvent;
            pCreateConnectionContext->pCleanUpEvent = NULL;

            pWinningParameterBlock->Vc.hEndpoint = INVALID_HANDLE_VALUE;
            pWinningParameterBlock->Vc.pEndpointFileObject = NULL;

            //DbgPrint("Remote address src %lx target %lx\n",
            //         pWinningParameterBlock->Connection.pConnectionInformation->RemoteAddress,
            //         pCompletionContext->pConnectionInformation->RemoteAddress);

            if (pCompletionContext->pConnectionInformation)
            {
                // Copy the buffer which may contain the DNS name returned back from TDI
                RtlCopyMemory(pCompletionContext->pConnectionInformation->RemoteAddress,
                              pWinningParameterBlock->Connection.pConnectionInformation->RemoteAddress,
                              pWinningParameterBlock->Connection.pConnectionInformation->RemoteAddressLength);
            }
                    
           //{
           //    PTRANSPORT_ADDRESS pTransportAddress = (PTRANSPORT_ADDRESS)pWinningParameterBlock->Connection.pConnectionInformation->RemoteAddress;
           //    DbgPrint("Number of TA returned %d %lx\n",pTransportAddress->TAAddressCount,pTransportAddress->Address); 
           //}
        }
    }

    AllCallOutsInitiated = (pCreateConnectionContext->NumberOfCallOutsInitiated
                            == pCreateConnectionContext->NumberOfCallOuts);

    ((PRX_CREATE_CONNECTION_PARAMETERS_BLOCK)pParameterBlock)->pConnectIrp = NULL;
    
    KeReleaseSpinLock(&pCreateConnectionContext->SpinLock,OldIrql);

    // The winning transport has been located. Cancel all the other requests.
    if (pWinningParameterBlock != NULL) {
        PRX_CREATE_CONNECTION_PARAMETERS_BLOCK pTempParameterBlock, pNextTempBlock;

        pTempParameterBlock = (PRX_CREATE_CONNECTION_PARAMETERS_BLOCK)
                              pCreateConnectionContext->pCallOutParameterBlock;

        RxLog(("Use paramblock %x %x\n", pTempParameterBlock, KeGetCurrentThread()));
        RxWmiLog(LOG,
                 RxCeCompleteConnectRequest,
                 LOGPTR(pTempParameterBlock));
        while (pTempParameterBlock != NULL) {

            pNextTempBlock = (PRX_CREATE_CONNECTION_PARAMETERS_BLOCK)
                                      pTempParameterBlock->pNextCallOutParameterBlock;

            if (pTempParameterBlock->CallOutStatus == STATUS_PENDING) {

                // get the next block becfore we do the cancel and set the
                // current guys status to cacncelled
                // Don't touch it after cancellation as he may have gone away
                // by then

                pTempParameterBlock->CallOutStatus = STATUS_CANCELLED;

                RxTdiCancelAsynchronousConnect(pTempParameterBlock);
            }

            pTempParameterBlock = pNextTempBlock;
        }
    }

    KeAcquireSpinLock(&pCreateConnectionContext->SpinLock,&OldIrql);
    
    AllCallOutsCompleted =
        (InterlockedIncrement(&pCreateConnectionContext->NumberOfCallOutsCompleted) ==
         pCreateConnectionContext->NumberOfCallOuts);

    if (AllCallOutsCompleted) {
        if (!pCreateConnectionContext->WinnerFound) {
            pCompletionContext->Status = pParameterBlock->CallOutStatus;
        }
    }

    if (AllCallOutsInitiated &&
        (AllCallOutsCompleted || pCreateConnectionContext->WinnerFound) &&
        !pCreateConnectionContext->CompletionRoutineInvoked) {
        InvokeCompletionRoutine = TRUE;
        pCreateConnectionContext->CompletionRoutineInvoked = TRUE;
    } 
    
    KeReleaseSpinLock(&pCreateConnectionContext->SpinLock,OldIrql);

    if ((Status == STATUS_SUCCESS) && AllCallOutsCompleted) {
        Status = RxPostToWorkerThread(
            RxFileSystemDeviceObject,
            HyperCriticalWorkQueue,
            &pCreateConnectionContext->WorkQueueItem,
            RxCeCleanupConnectCallOutContext,
            pCreateConnectionContext);
    }

    if (InvokeCompletionRoutine) {
        if ((IoGetCurrentProcess() == RxGetRDBSSProcess()) &&
            !RxShouldPostCompletion()) {
            (pCompletionRoutine)(pCompletionContext);
        } else {
            Status = RxPostToWorkerThread(
                RxFileSystemDeviceObject,
                CriticalWorkQueue,
                &pCompletionContext->WorkQueueItem,
                pCompletionRoutine,
                pCompletionContext);
        }
    }
    
    return Status;
}

NTSTATUS
RxCeBuildConnectionOverMultipleTransports(
    IN OUT PRDBSS_DEVICE_OBJECT         pMiniRedirectorDeviceObject,
    IN  RXCE_CONNECTION_CREATE_OPTIONS  CreateOptions,
    IN  ULONG                           NumberOfAddresses,
    IN  PRXCE_ADDRESS                   *pLocalAddressPointers,
    IN  PUNICODE_STRING                 pServerName,
    IN  PRXCE_CONNECTION_INFORMATION    pConnectionInformation,
    IN  PRXCE_CONNECTION_EVENT_HANDLER  pHandler,
    IN  PVOID                           pEventContext,
    IN  PRXCE_CONNECTION_COMPLETION_ROUTINE     pCompletionRoutine,
    IN OUT PRXCE_CONNECTION_COMPLETION_CONTEXT  pCompletionContext)
/*++

Routine Description:

    This routine establishes a connection between a local RxCe address and a given remote address

Arguments:

    pMiniRedirectorDeviceObject - the mini redriector device object

    CreateOptions          - the create options

    NumberOfAddresses      - the number of local addresses(transports)

    pLocalAddressPointers  - the local address handles

    pServerName            - the name of the server ( for connection enumeration )

    pConnectionInformation - the connection information specifying the remote address.

    pHandler               - the connection handler

    pEventContext          - the connection handler context

    pLocalAddressHandleIndex - the index of the successful address/transport

    pConnectionHandle      - the handle to the newly created connection.

    pVcHandle              - the handle to the VC associated with the connection.

Return Value:

    STATUS_SUCCESS if successfull.

Notes:

--*/
{
    PRXCE_CONNECTION    pConnection;
    PRXCE_VC            pVc;

    NTSTATUS Status;

    PRX_CREATE_CONNECTION_CALLOUT_CONTEXT  pCallOutContext=NULL;
    PRX_CREATE_CONNECTION_PARAMETERS_BLOCK pParameterBlocks=NULL;

    ULONG   NumberOfCallOuts,i;
    BOOLEAN InitiateCleanup = FALSE;
    BOOLEAN AsynchronousRequestRegistered = FALSE;

    KEVENT  CompletionEvent;
    BOOLEAN     fCompletionContextFreed = FALSE;

    PAGED_CODE();

    Status = STATUS_SUCCESS;

    pConnection = pCompletionContext->pConnection;
    pVc         = pCompletionContext->pVc;

    pCallOutContext = (PRX_CREATE_CONNECTION_CALLOUT_CONTEXT)
                      RxAllocatePoolWithTag(
                          NonPagedPool,
                          sizeof(RX_CREATE_CONNECTION_CALLOUT_CONTEXT),
                          RXCE_CONNECTION_POOLTAG);

    if (pCallOutContext != NULL) {
        // Allocate one more parameter block then the number of addresses.
        // This sentinel block is used in completing the connect request
        // after ensuring that all of them have been initiated. This
        // ensures that race conditions when a transport completes before
        // the requests have been initiated on some transports are avoided.

        pCallOutContext->pCleanUpEvent = (PKEVENT)RxAllocatePoolWithTag(
                                            NonPagedPool,
                                            sizeof(KEVENT),
                                            RXCE_CONNECTION_POOLTAG);

        pParameterBlocks = (PRX_CREATE_CONNECTION_PARAMETERS_BLOCK)
                           RxAllocatePoolWithTag(
                               NonPagedPool,
                               sizeof(RX_CREATE_CONNECTION_PARAMETERS_BLOCK) *
                               (NumberOfAddresses + 1),
                               RXCE_CONNECTION_POOLTAG);
    }


    if ((pParameterBlocks == NULL) ||
        (pCallOutContext ==  NULL) ||
        (pCallOutContext->pCleanUpEvent == NULL)) {
        if (pCallOutContext != NULL) {
            if (pCallOutContext->pCleanUpEvent != NULL) {
                RxFreePool(pCallOutContext->pCleanUpEvent);
            }

            RxFreePool(pCallOutContext);
            pCallOutContext = NULL;
        }
        if (pParameterBlocks)
        {
            RxFreePool(pParameterBlocks);
            pParameterBlocks = NULL;
        }
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto bailout;
    }

    // Before initiating the callouts ensure that the asynchronous
    // request is registered. this will ensure that the mini
    // redirector cannot be unloaded till the asynchronous request
    // has been completed.
    Status = RxRegisterAsynchronousRequest(pMiniRedirectorDeviceObject);
    
    if (Status == STATUS_SUCCESS) {
        AsynchronousRequestRegistered = TRUE;
    }

    KeInitializeEvent(
        pCallOutContext->pCleanUpEvent,
        SynchronizationEvent,
        FALSE);

    if (Status == STATUS_SUCCESS) {
        Status = RxCepInitializeConnection(
                     pConnection,
                     NULL,
                     pConnectionInformation,
                     pHandler,
                     pEventContext);

        if (Status == STATUS_SUCCESS) {
            Status = RxCepInitializeVC(
                         pVc,
                         pConnection);
        }
    }

    if (Status == STATUS_SUCCESS) {
        try {
            NumberOfCallOuts = 0;

            // Fill up each of the parameter blocks
            for (i = 0; i < NumberOfAddresses; i++) {
                PRXCE_TRANSPORT pTransport;
                PRXCE_ADDRESS pAddress;

                pAddress = pLocalAddressPointers[i];
                pTransport = pAddress->pTransport;

                if (RxCeIsAddressValid(pAddress) &&
                    RxCeIsTransportValid(pTransport)) {

                    Status = RxCepInitializeConnection(
                                 &pParameterBlocks[NumberOfCallOuts].Connection,
                                 pAddress,
                                 pConnectionInformation,
                                 NULL,
                                 NULL);

                    if (Status == STATUS_SUCCESS) {
                        Status = RxCepInitializeVC(
                                     &pParameterBlocks[NumberOfCallOuts].Vc,
                                     &pParameterBlocks[NumberOfCallOuts].Connection);

                        if (Status != STATUS_SUCCESS) {
                            RxCeTearDownConnection(
                                &pParameterBlocks[NumberOfCallOuts].Connection);
                        }
                    }

                    if (Status == STATUS_SUCCESS) {
                        pParameterBlocks[NumberOfCallOuts].pConnectIrp = NULL;
                        pParameterBlocks[NumberOfCallOuts].IrpRefCount = NULL;
                        pParameterBlocks[NumberOfCallOuts].CallOutId = i;
                        pParameterBlocks[NumberOfCallOuts].pCallOutContext =
                            (PRX_CALLOUT_CONTEXT)pCallOutContext;
                        pParameterBlocks[NumberOfCallOuts].CallOutStatus = STATUS_PENDING;
                        NumberOfCallOuts++;
                    }
                }
            }

            if (NumberOfCallOuts > 0) {
                NTSTATUS LocalStatus = STATUS_SUCCESS;

                // Increment the number of callouts for the sentinel callout to
                // ensure that all initiation is completed before we complete
                // the connect request. Notice that the sentinel is not the very
                // last one but the one after the number of callouts.
                NumberOfCallOuts++;

                // Also exclude the sentinel from the list
                for (i = 0;  i < NumberOfCallOuts - 1; i++) {
                    pParameterBlocks[i].pNextCallOutParameterBlock =
                        (PRX_CALLOUT_PARAMETERS_BLOCK)&pParameterBlocks[i + 1];
                }

                pParameterBlocks[NumberOfCallOuts - 2].pNextCallOutParameterBlock = NULL;
                pParameterBlocks[NumberOfCallOuts - 1].pNextCallOutParameterBlock = NULL;

                // Initialize the callout context.
                pCallOutContext->CreateOptions   = CreateOptions;
                pCallOutContext->WinnerCallOutId = NumberOfCallOuts + 1;
                pCallOutContext->BestPossibleWinner = 0;
                pCallOutContext->NumberOfCallOuts = NumberOfCallOuts;
                pCallOutContext->NumberOfCallOutsInitiated = 0;
                pCallOutContext->NumberOfCallOutsCompleted = 0;
                pCallOutContext->pRxCallOutInitiation = RxCeInitiateConnectRequest;
                pCallOutContext->pRxCallOutCompletion = RxCeCompleteConnectRequest;
                pCallOutContext->WinnerFound = FALSE;
                pCallOutContext->CompletionRoutineInvoked = FALSE;
                pCallOutContext->pCallOutParameterBlock =
                    (PRX_CALLOUT_PARAMETERS_BLOCK)pParameterBlocks;

                pCompletionContext->AddressIndex =  NumberOfCallOuts + 1;

                pCallOutContext->pCompletionContext = pCompletionContext;
                pCallOutContext->pCompletionRoutine = pCompletionRoutine;
                pCallOutContext->pConnectionContext = pCompletionContext->pVc;
                 
                pCallOutContext->pRxDeviceObject = pMiniRedirectorDeviceObject;

                KeInitializeSpinLock(
                    &pCallOutContext->SpinLock);

                // Exclude the sentinel from the chain of parameter blocks
                for (i = 0; i < NumberOfCallOuts - 1; i++) {
                    pCallOutContext->pRxCallOutInitiation(
                                 (PRX_CALLOUT_PARAMETERS_BLOCK)&pParameterBlocks[i]);
                }

                pParameterBlocks[NumberOfCallOuts - 1].pConnectIrp = NULL;
                pParameterBlocks[NumberOfCallOuts - 1].CallOutId = NumberOfCallOuts;
                pParameterBlocks[NumberOfCallOuts - 1].pCallOutContext =
                    (PRX_CALLOUT_CONTEXT)pCallOutContext;
                pParameterBlocks[NumberOfCallOuts - 1].CallOutStatus = STATUS_NETWORK_UNREACHABLE;

                pCallOutContext->NumberOfCallOutsInitiated = NumberOfCallOuts;


                if((LocalStatus = RxCeCompleteConnectRequest(
                    (PRX_CALLOUT_PARAMETERS_BLOCK)&pParameterBlocks[NumberOfCallOuts - 1])) != STATUS_SUCCESS)
                {
                    InitiateCleanup = TRUE;
                    Status = LocalStatus;
                    RxLog(("LocalStatus %x\n", LocalStatus));
                    RxWmiLog(LOG,
                             RxCeBuildConnectionOverMultipleTransports_1,
                             LOGULONG(LocalStatus));
                }
                else
                {
                    Status = STATUS_PENDING;
                }

                fCompletionContextFreed = TRUE;
            } else {
                InitiateCleanup = TRUE;
                Status = STATUS_INVALID_HANDLE;
            }
        } finally {
            if (AbnormalTermination()) {
                InitiateCleanup = TRUE;
                Status = STATUS_INVALID_PARAMETER;
            }
        }
    }

    if (InitiateCleanup) {
        RxFreePool(pParameterBlocks);
        RxFreePool(pCallOutContext);
    }

    if (Status != STATUS_PENDING) {
        NTSTATUS LocalStatus;

        ASSERT(Status != STATUS_SUCCESS);

        LocalStatus = RxCeTearDownVC(pVc);
        ASSERT(LocalStatus == STATUS_SUCCESS);

        LocalStatus = RxCeTearDownConnection(pConnection);
        ASSERT(LocalStatus == STATUS_SUCCESS);

        if (!fCompletionContextFreed)
        {
            pCompletionContext->Status = Status;

            if ((IoGetCurrentProcess() == RxGetRDBSSProcess()) &&
                !RxShouldPostCompletion()) {
                (pCompletionRoutine)(pCompletionContext);
            } else {
                LocalStatus = RxPostToWorkerThread(
                    RxFileSystemDeviceObject,
                    CriticalWorkQueue,
                    &pCompletionContext->WorkQueueItem,
                    pCompletionRoutine,
                    pCompletionContext);

            }
        }

        if (LocalStatus == STATUS_SUCCESS)
        {
            if (AsynchronousRequestRegistered) {
                RxDeregisterAsynchronousRequest(pMiniRedirectorDeviceObject);
            }

            Status = STATUS_PENDING;
        }
        else
        {
            Status = LocalStatus;
            RxLog(("RxCeBldOvrMult: Failed Status %lx\n", Status));
            RxWmiLog(LOG,
                     RxCeBuildConnectionOverMultipleTransports_2,
                     LOGULONG(Status));
        }
    }
bailout:

    return Status;
}

NTSTATUS
RxCeTearDownConnection(
    IN PRXCE_CONNECTION pConnection)
/*++

Routine Description:

    This routine tears down a given connection

Arguments:

    pConnection - the connection to be torn down

Return Value:

    STATUS_SUCCESS if successfull.

Notes:

--*/
{
    NTSTATUS         Status = STATUS_SUCCESS;

    PAGED_CODE();

    // Update profiling info.
    RxProfile(RxCeManagement,RxCeTearDownConnection);

    try {
        if (RxCeIsConnectionValid(pConnection)) {
            if (pConnection->pConnectionInformation != NULL) {
                if (pConnection->pConnectionInformation->UserDataLength > 0) {
                    RxFreePool(pConnection->pConnectionInformation->UserData);
                }

                if (pConnection->pConnectionInformation->RemoteAddressLength > 0) {
                    RxFreePool(pConnection->pConnectionInformation->RemoteAddress);
                }

                if (pConnection->pConnectionInformation->OptionsLength > 0) {
                    RxFreePool(pConnection->pConnectionInformation->Options);
                }

                RxFreePool(pConnection->pConnectionInformation);
            }

            // free the memory allocated for the handler
            if (pConnection->pHandler != NULL) {
                RxFreePool(pConnection->pHandler);
            }

            RtlZeroMemory(
                pConnection,
                sizeof(RXCE_CONNECTION));
        }

    } finally {
        if (AbnormalTermination()) {
            Status = STATUS_INVALID_PARAMETER;
            RxLog(("RxCeTearDownConnection: C: %lx\n",pConnection));
            RxWmiLog(LOG,
                     RxCeTearDownConnection,
                     LOGPTR(pConnection));
        }
    }

    return Status;
}

NTSTATUS
RxCeCancelConnectRequest(
    IN  PRXCE_ADDRESS                pLocalAddress,
    IN  PUNICODE_STRING              pServerName,
    IN  PRXCE_CONNECTION_INFORMATION pConnectionInformation)
/*++

Routine Description:

    This routine cancels a previously issued connection request.

Arguments:

    pConnectionInformation - the connection information pertaining to a previsouly issued
                             connection request

Return Value:

    STATUS_SUCCESS if successfull.

Notes:

--*/
{
    PAGED_CODE();

    return STATUS_NOT_IMPLEMENTED;
}


NTSTATUS
RxCeQueryInformation(
    IN PRXCE_VC                          pVc,
    IN RXCE_CONNECTION_INFORMATION_CLASS InformationClass,
    OUT PVOID                            pInformation,
    IN ULONG                             Length)
/*++

Routine Description:

    This routine queries information pertaining to a connection

Arguments:

    pConnection - the connection for which the information is desired

    InformationClass - the desired information class.

    pInformation - the buffer for returning the information

    Length       - the length of the buffer.

Return Value:

    STATUS_SUCCESS if successfull.

--*/
{
    NTSTATUS Status = STATUS_INVALID_PARAMETER;

    PRXCE_TRANSPORT  pTransport  = NULL;
    PRXCE_ADDRESS    pAddress    = NULL;
    PRXCE_CONNECTION pConnection = NULL;

    PAGED_CODE();

    // Update profiling info.
    RxProfile(RxCeManagement,RxCeQueryInformation);

    try {
        pConnection = pVc->pConnection;
        pAddress = pConnection->pAddress;
        pTransport = pAddress->pTransport;

        if (RxCeIsVcValid(pVc)                 &&
            RxCeIsConnectionValid(pConnection) &&
            RxCeIsAddressValid(pAddress)       &&
            RxCeIsTransportValid(pTransport)) {

            switch (InformationClass) {
            case RxCeTransportProviderInformation:
                if (sizeof(RXCE_TRANSPORT_PROVIDER_INFO) <= Length) {
                    // Copy the necessary provider information.
                    RtlCopyMemory(
                        pInformation,
                        pTransport->pProviderInfo,
                        sizeof(RXCE_TRANSPORT_PROVIDER_INFO));

                    Status = STATUS_SUCCESS;
                } else {
                    Status = STATUS_BUFFER_OVERFLOW;
                }
                break;

            case RxCeConnectionInformation:
                if (sizeof(RXCE_CONNECTION_INFORMATION) <= Length) {
                    RtlCopyMemory(
                        pInformation,
                        pConnection->pConnectionInformation,
                        sizeof(RXCE_CONNECTION_INFORMATION));

                    Status = STATUS_SUCCESS;
                } else {
                    Status = STATUS_BUFFER_OVERFLOW;
                }
                break;

            case RxCeConnectionEndpointInformation:
                if (sizeof(RXCE_CONNECTION_INFO) <= Length) {
                    Status = RxTdiQueryInformation(
                                 pTransport,
                                 pAddress,
                                 pConnection,
                                 pVc,
                                 RXCE_QUERY_CONNECTION_INFO,
                                 pInformation,
                                 Length);
                } else {
                    Status = STATUS_BUFFER_OVERFLOW;
                }
                break;

            case RxCeRemoteAddressInformation:
                {
                    Status = RxTdiQueryInformation(
                                 pTransport,
                                 pAddress,
                                 pConnection,
                                 pVc,
                                 RXCE_QUERY_ADDRESS_INFO,
                                 pInformation,
                                 Length);
                }
                break;

            default:
                Status = STATUS_INVALID_PARAMETER;
                break;
            }
        }
    } finally {
        if (AbnormalTermination()) {
            Status = STATUS_INVALID_PARAMETER;
        }
    }

    return Status;
}

