/*++ 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; NoOfAddressTAAddressCount;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; }