windows-nt/Source/XPSP1/NT/base/fs/rdr2/rxce/rxcecnct.c
2020-09-26 16:20:57 +08:00

1595 lines
50 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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