mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3236 lines
93 KiB
3236 lines
93 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
transport.c
|
|
|
|
Abstract:
|
|
|
|
This module implements all transport related functions in the SMB connection engine
|
|
|
|
Revision History:
|
|
|
|
Balan Sethu Raman [SethuR] 6-March-1995
|
|
Will Lees (wlees) 08-Sep-1997 Initialize MoTcp Device
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include <nbtioctl.h>
|
|
#pragma hdrstop
|
|
|
|
#include "ntddbrow.h"
|
|
#include "tdikrnl.h"
|
|
#include "dfsfsctl.h"
|
|
|
|
NTSTATUS
|
|
SmbCeIsServerAvailable(
|
|
PUNICODE_STRING Name
|
|
);
|
|
|
|
VOID
|
|
SmbCeServerIsUnavailable(
|
|
PUNICODE_STRING Name,
|
|
NTSTATUS Status
|
|
);
|
|
|
|
VOID
|
|
SmbCeDiscardUnavailableServerList( );
|
|
|
|
VOID
|
|
MRxSmbpOverrideBindingPriority(
|
|
PUNICODE_STRING pTransportName,
|
|
PULONG pPriority
|
|
);
|
|
|
|
VOID
|
|
MRxSmbPnPBindingHandler(
|
|
IN TDI_PNP_OPCODE PnPOpcode,
|
|
IN PUNICODE_STRING pTransportName,
|
|
IN PWSTR BindingList
|
|
);
|
|
|
|
NTSTATUS
|
|
MRxSmbPnPPowerHandler(
|
|
IN PUNICODE_STRING DeviceName,
|
|
IN PNET_PNP_EVENT PowerEvent,
|
|
IN PTDI_PNP_CONTEXT Context1,
|
|
IN PTDI_PNP_CONTEXT Context2
|
|
);
|
|
|
|
VOID
|
|
SmbMRxNotifyChangesToNetBt(
|
|
IN TDI_PNP_OPCODE PnPOpcode,
|
|
IN PUNICODE_STRING DeviceName,
|
|
IN PWSTR MultiSZBindList
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, SmbCeFindTransport)
|
|
#pragma alloc_text(PAGE, SmbCepInitializeServerTransport)
|
|
#pragma alloc_text(PAGE, SmbCeInitializeExchangeTransport)
|
|
#pragma alloc_text(PAGE, SmbCeUninitializeExchangeTransport)
|
|
#pragma alloc_text(PAGE, SmbCepDereferenceTransport)
|
|
#pragma alloc_text(PAGE, MRxSmbpBindTransportCallback)
|
|
#pragma alloc_text(PAGE, MRxSmbpBindTransportWorkerThreadRoutine)
|
|
#pragma alloc_text(PAGE, MRxSmbBindTransportCallback)
|
|
#pragma alloc_text(PAGE, MRxSmbUnbindTransportCallback)
|
|
#pragma alloc_text(PAGE, MRxSmbRegisterForPnpNotifications)
|
|
#pragma alloc_text(PAGE, MRxSmbDeregisterForPnpNotifications)
|
|
#pragma alloc_text(PAGE, MRxSmbpBindTransportCallback)
|
|
#pragma alloc_text(PAGE, MRxSmbpBindTransportWorkerThreadRoutine)
|
|
#pragma alloc_text(PAGE, MRxSmbpUnbindTransportCallback)
|
|
#pragma alloc_text(PAGE, MRxSmbpOverrideBindingPriority)
|
|
#pragma alloc_text(PAGE, MRxSmbPnPBindingHandler)
|
|
#pragma alloc_text(PAGE, MRxSmbRegisterForPnpNotifications)
|
|
#pragma alloc_text(PAGE, MRxSmbDeregisterForPnpNotifications)
|
|
#pragma alloc_text(PAGE, SmbCePnpBindBrowser)
|
|
#pragma alloc_text(PAGE, SmbCeDereferenceTransportArray)
|
|
#pragma alloc_text(PAGE, SmbCeIsServerAvailable)
|
|
#pragma alloc_text(PAGE, SmbCeServerIsUnavailable)
|
|
#pragma alloc_text(PAGE, SmbCeDiscardUnavailableServerList)
|
|
#endif
|
|
|
|
SMBCE_TRANSPORTS MRxSmbTransports;
|
|
|
|
//
|
|
// The head of the list of servers that are currently unavailable
|
|
//
|
|
LIST_ENTRY UnavailableServerList = { &UnavailableServerList, &UnavailableServerList };
|
|
|
|
//
|
|
// Each entry in the UnavailableServerList is one of these:
|
|
//
|
|
typedef struct {
|
|
LIST_ENTRY ListEntry;
|
|
UNICODE_STRING Name; // Name of server that is unavailable
|
|
NTSTATUS Status; // Status received when we tried to connect to it
|
|
LARGE_INTEGER Time; // Time when we last attempted to connect
|
|
} *PUNAVAILABLE_SERVER;
|
|
|
|
//
|
|
// Protects UnavailableServerList
|
|
//
|
|
ERESOURCE UnavailableServerListResource = {0};
|
|
|
|
//
|
|
// Time (seconds) that we keep an entry in the UnavailableServerList.
|
|
// We will not retry a connection attempt to a server
|
|
// for UNAVAILABLE_SERVER_TIME seconds
|
|
//
|
|
#define UNAVAILABLE_SERVER_TIME 10
|
|
|
|
RXDT_DefineCategory(TRANSPRT);
|
|
#define Dbg (DEBUG_TRACE_TRANSPRT)
|
|
|
|
extern NTSTATUS
|
|
SmbCePnpBindBrowser(
|
|
PUNICODE_STRING pTransportName,
|
|
BOOLEAN IsBind);
|
|
|
|
|
|
NTSTATUS
|
|
MRxSmbInitializeTransport()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the transport related data structures
|
|
|
|
Returns:
|
|
|
|
STATUS_SUCCESS if the transport data structures was successfully initialized
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
KeInitializeSpinLock(&MRxSmbTransports.Lock);
|
|
|
|
MRxSmbTransports.pTransportArray = NULL;
|
|
|
|
ExInitializeResource( &UnavailableServerListResource );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbUninitializeTransport()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine uninitializes the transport related data structures
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
PSMBCE_TRANSPORT pTransport;
|
|
KIRQL SavedIrql;
|
|
ULONG TransportCount = 0;
|
|
PSMBCE_TRANSPORT_ARRAY pTransportArray = NULL;
|
|
|
|
KeAcquireSpinLock(&MRxSmbTransports.Lock,&SavedIrql);
|
|
|
|
if (MRxSmbTransports.pTransportArray != NULL) {
|
|
pTransportArray = MRxSmbTransports.pTransportArray;
|
|
MRxSmbTransports.pTransportArray = NULL;
|
|
}
|
|
|
|
KeReleaseSpinLock(&MRxSmbTransports.Lock,SavedIrql);
|
|
|
|
if (pTransportArray != NULL) {
|
|
SmbCeDereferenceTransportArray(pTransportArray);
|
|
}
|
|
|
|
SmbCeDiscardUnavailableServerList();
|
|
|
|
ExDeleteResource( &UnavailableServerListResource );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SmbCeAddTransport(
|
|
PSMBCE_TRANSPORT pNewTransport)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine adds a new instance to the known list of transports
|
|
|
|
Parameters:
|
|
|
|
pNewTransport -- the transport instance to be added
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
KIRQL SavedIrql;
|
|
|
|
LONG Count;
|
|
PSMBCE_TRANSPORT_ARRAY pNewTransportArray = NULL;
|
|
PSMBCE_TRANSPORT_ARRAY pOldTransportArray;
|
|
PSMBCE_TRANSPORT *pTransports = NULL;
|
|
PRXCE_ADDRESS *LocalAddresses = NULL;
|
|
BOOLEAN SignalCscAgent = FALSE;
|
|
|
|
SmbCeAcquireResource();
|
|
|
|
pOldTransportArray = SmbCeReferenceTransportArray();
|
|
|
|
if (pOldTransportArray != NULL)
|
|
Count = pOldTransportArray->Count + 1;
|
|
else
|
|
Count = 1;
|
|
|
|
pNewTransportArray = (PSMBCE_TRANSPORT_ARRAY)RxAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof(SMBCE_TRANSPORT_ARRAY),
|
|
MRXSMB_TRANSPORT_POOLTAG);
|
|
if (pNewTransportArray == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
pTransports = (PSMBCE_TRANSPORT *)RxAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
Count * sizeof(PSMBCE_TRANSPORT),
|
|
MRXSMB_TRANSPORT_POOLTAG);
|
|
if (pTransports == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
LocalAddresses = (PRXCE_ADDRESS *)RxAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
Count * sizeof(PRXCE_ADDRESS),
|
|
MRXSMB_TRANSPORT_POOLTAG);
|
|
if (LocalAddresses == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
LONG i;
|
|
|
|
if (Count > 1) {
|
|
PSMBCE_TRANSPORT *pOldTransports;
|
|
|
|
pOldTransports = pOldTransportArray->SmbCeTransports;
|
|
|
|
for (i=0;i<Count-1;i++) {
|
|
if (pNewTransport->Priority < pOldTransports[i]->Priority) { // The lower number, the higher priority
|
|
break;
|
|
}
|
|
pTransports[i] = pOldTransports[i];
|
|
LocalAddresses[i] = &pOldTransports[i]->RxCeAddress;
|
|
}
|
|
pTransports[i] = pNewTransport;
|
|
LocalAddresses[i] = &pNewTransport->RxCeAddress;
|
|
for (;i<Count-1;i++) {
|
|
pTransports[i+1] = pOldTransports[i];
|
|
LocalAddresses[i+1] = &pOldTransports[i]->RxCeAddress;
|
|
}
|
|
|
|
} else {
|
|
pTransports[0] = pNewTransport;
|
|
LocalAddresses[0] = &pNewTransport->RxCeAddress;
|
|
}
|
|
|
|
for(i=0;i<Count;i++)
|
|
SmbCeReferenceTransport(pTransports[i]);
|
|
|
|
pNewTransportArray->ReferenceCount = 1;
|
|
pNewTransportArray->Count = Count;
|
|
pNewTransportArray->SmbCeTransports = &pTransports[0];
|
|
pNewTransportArray->LocalAddresses = &LocalAddresses[0];
|
|
|
|
// signal the CSC agent if this is the first transport
|
|
SignalCscAgent = (pNewTransportArray->Count == 1);
|
|
|
|
KeAcquireSpinLock(&MRxSmbTransports.Lock,&SavedIrql);
|
|
MRxSmbTransports.pTransportArray = pNewTransportArray;
|
|
KeReleaseSpinLock(&MRxSmbTransports.Lock,SavedIrql);
|
|
|
|
// Double dereferencing is necessary to ensure that
|
|
// the old transport array is destroyed.
|
|
|
|
SmbCeDereferenceTransportArray(pOldTransportArray);
|
|
}
|
|
|
|
SmbCeDereferenceTransportArray(pOldTransportArray);
|
|
|
|
SmbCeReleaseResource();
|
|
|
|
MRxSmbCscSignalNetStatus(TRUE, SignalCscAgent);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
if (pNewTransportArray != NULL) {
|
|
RxFreePool(pNewTransportArray);
|
|
}
|
|
if (pTransports != NULL) {
|
|
RxFreePool(pTransports);
|
|
}
|
|
if (LocalAddresses != NULL) {
|
|
RxFreePool(LocalAddresses);
|
|
}
|
|
}
|
|
|
|
SmbCeDiscardUnavailableServerList();
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbCeRemoveTransport(
|
|
PSMBCE_TRANSPORT pTransport)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine removes a transport from the list of known transports
|
|
|
|
Parameters:
|
|
|
|
pTransport - the transport instance to be removed.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
KIRQL SavedIrql;
|
|
|
|
LONG Count;
|
|
PSMBCE_TRANSPORT_ARRAY pTransportArray = NULL;
|
|
PSMBCE_TRANSPORT_ARRAY pOldTransportArray = NULL;
|
|
PSMBCE_TRANSPORT *pTransports = NULL;
|
|
PRXCE_ADDRESS *pLocalAddresses = NULL;
|
|
|
|
BOOLEAN SignalCscAgent = FALSE, fReportRemovalToCSC=FALSE;
|
|
|
|
SmbCeAcquireResource();
|
|
|
|
pOldTransportArray = SmbCeReferenceTransportArray();
|
|
|
|
if (pOldTransportArray != NULL) {
|
|
LONG Index;
|
|
BOOLEAN Found = FALSE;
|
|
PSMBCE_TRANSPORT *pOldTransports;
|
|
|
|
// Establish the fact that the given transport is part of the array.
|
|
// if it is not then no further action is necessary
|
|
|
|
pOldTransports = pOldTransportArray->SmbCeTransports;
|
|
|
|
for (Index = 0; Index < (LONG)pOldTransportArray->Count; Index++) {
|
|
if (pTransport == pOldTransports[Index]) {
|
|
Found = TRUE;
|
|
}
|
|
}
|
|
|
|
if (Found) {
|
|
Count = pOldTransportArray->Count - 1;
|
|
fReportRemovalToCSC = (pOldTransportArray->Count != 0);
|
|
|
|
if (Count > 0) {
|
|
|
|
|
|
pTransportArray = (PSMBCE_TRANSPORT_ARRAY)RxAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof(SMBCE_TRANSPORT_ARRAY),
|
|
MRXSMB_TRANSPORT_POOLTAG);
|
|
if (pTransportArray == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
pTransports = (PSMBCE_TRANSPORT *)RxAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
Count * sizeof(PSMBCE_TRANSPORT),
|
|
MRXSMB_TRANSPORT_POOLTAG);
|
|
if (pTransports == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
pLocalAddresses = (PRXCE_ADDRESS *)RxAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
Count * sizeof(PRXCE_ADDRESS),
|
|
MRXSMB_TRANSPORT_POOLTAG);
|
|
if (pLocalAddresses == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
LONG i, j;
|
|
|
|
for (i=0, j=0;i<Count+1;i++) {
|
|
if (pTransport != pOldTransports[i]) {
|
|
pTransports[j] = pOldTransports[i];
|
|
pLocalAddresses[j] = &pOldTransports[i]->RxCeAddress;
|
|
j++;
|
|
}
|
|
}
|
|
|
|
for(i=0;i<Count;i++)
|
|
SmbCeReferenceTransport(pTransports[i]);
|
|
|
|
pTransportArray->ReferenceCount = 1;
|
|
pTransportArray->Count = Count;
|
|
pTransportArray->SmbCeTransports = &pTransports[0];
|
|
pTransportArray->LocalAddresses = &pLocalAddresses[0];
|
|
}
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
// signal the CSC agent if this is the last transport
|
|
SignalCscAgent = (pTransportArray == NULL);
|
|
|
|
KeAcquireSpinLock(&MRxSmbTransports.Lock,&SavedIrql);
|
|
MRxSmbTransports.pTransportArray = pTransportArray;
|
|
KeReleaseSpinLock(&MRxSmbTransports.Lock,SavedIrql);
|
|
|
|
// Double dereferencing is necessary to ensure that
|
|
// the old transport array is destroyed.
|
|
|
|
SmbCeDereferenceTransportArray(pOldTransportArray);
|
|
} else {
|
|
if (pTransportArray != NULL) {
|
|
RxFreePool(pTransportArray);
|
|
}
|
|
|
|
if (pTransports != NULL) {
|
|
RxFreePool(pTransports);
|
|
}
|
|
|
|
if (pLocalAddresses != NULL) {
|
|
RxFreePool(pLocalAddresses);
|
|
}
|
|
}
|
|
}
|
|
|
|
SmbCeDereferenceTransportArray(pOldTransportArray);
|
|
}
|
|
|
|
SmbCeReleaseResource();
|
|
|
|
if (fReportRemovalToCSC)
|
|
{
|
|
MRxSmbCscSignalNetStatus(FALSE, SignalCscAgent);
|
|
}
|
|
|
|
SmbCeDiscardUnavailableServerList();
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
PSMBCE_TRANSPORT
|
|
SmbCeFindTransport(
|
|
PUNICODE_STRING pTransportName)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine maps a transport name to the appropriate
|
|
PSMBCE_TRANSPORT instance
|
|
|
|
Arguments:
|
|
|
|
pTransportName - the transport name
|
|
|
|
Return Value:
|
|
|
|
a valid PSMBCE_TRANSPORT if one exists otherwise NULL
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
KIRQL SavedIrql;
|
|
PLIST_ENTRY pEntry;
|
|
PSMBCE_TRANSPORT pTransport;
|
|
BOOLEAN Found = FALSE;
|
|
PSMBCE_TRANSPORT_ARRAY pTransportArray;
|
|
|
|
PAGED_CODE();
|
|
|
|
pTransportArray = SmbCeReferenceTransportArray();
|
|
|
|
if (pTransportArray == NULL) {
|
|
RxDbgTrace(0, Dbg, ("SmbCeFindTransport : Transport not available.\n"));
|
|
return NULL;
|
|
}
|
|
|
|
if (pTransportArray != NULL) {
|
|
ULONG i;
|
|
|
|
for (i=0;i<pTransportArray->Count;i++) {
|
|
pTransport = pTransportArray->SmbCeTransports[i];
|
|
|
|
if (RtlEqualUnicodeString(
|
|
&pTransport->RxCeTransport.Name,
|
|
pTransportName,
|
|
TRUE)) {
|
|
SmbCeReferenceTransport(pTransport);
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Found) {
|
|
pTransport = NULL;
|
|
}
|
|
|
|
SmbCeDereferenceTransportArray(pTransportArray);
|
|
|
|
return pTransport;
|
|
}
|
|
|
|
|
|
VOID
|
|
SmbCepTearDownServerTransport(
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine uninitializes the transport information corresponding to a server
|
|
|
|
Arguments:
|
|
|
|
pServerEntry - the server entry instance in the database
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
SMBCEDB_SERVER_TYPE ServerType = SmbCeGetServerType(pServerEntry);
|
|
|
|
BOOLEAN WaitForMailSlotTransportRundown = FALSE;
|
|
BOOLEAN WaitForTransportRundown = FALSE;
|
|
BOOLEAN TearDown = FALSE;
|
|
|
|
SmbCeAcquireSpinLock();
|
|
|
|
if (!pServerEntry->IsTransportDereferenced) {
|
|
|
|
// ServerEntry takes only one reference count of transport, which should only be
|
|
// dereferenced once when it comes to tear down transport. Multiple dereference called
|
|
// from construct server transport and PNP unbind transport needs to be prevented.
|
|
pServerEntry->IsTransportDereferenced = TRUE;
|
|
TearDown = TRUE;
|
|
|
|
KeInitializeEvent(&pServerEntry->MailSlotTransportRundownEvent,NotificationEvent,FALSE);
|
|
KeInitializeEvent(&pServerEntry->TransportRundownEvent,NotificationEvent,FALSE);
|
|
|
|
if (pServerEntry->pTransport != NULL) {
|
|
pServerEntry->pTransport->State = SMBCEDB_MARKED_FOR_DELETION;
|
|
pServerEntry->pTransport->pRundownEvent = &pServerEntry->TransportRundownEvent;
|
|
|
|
WaitForTransportRundown = TRUE;
|
|
}
|
|
|
|
if (pServerEntry->pMailSlotTransport != NULL) {
|
|
pServerEntry->pMailSlotTransport->State = SMBCEDB_MARKED_FOR_DELETION;
|
|
pServerEntry->pMailSlotTransport->pRundownEvent = &pServerEntry->MailSlotTransportRundownEvent;
|
|
|
|
WaitForMailSlotTransportRundown = TRUE;
|
|
}
|
|
} else {
|
|
if (pServerEntry->pTransport != NULL) {
|
|
WaitForTransportRundown = TRUE;
|
|
}
|
|
|
|
if (pServerEntry->pMailSlotTransport != NULL) {
|
|
WaitForMailSlotTransportRundown = TRUE;
|
|
}
|
|
}
|
|
|
|
SmbCeReleaseSpinLock();
|
|
|
|
if (TearDown) {
|
|
if (pServerEntry->pTransport != NULL) {
|
|
SmbCeDereferenceServerTransport(&pServerEntry->pTransport);
|
|
}
|
|
|
|
if (pServerEntry->pMailSlotTransport != NULL) {
|
|
SmbCeDereferenceServerTransport(&pServerEntry->pMailSlotTransport);
|
|
}
|
|
}
|
|
|
|
if (WaitForTransportRundown) {
|
|
KeWaitForSingleObject(
|
|
&pServerEntry->TransportRundownEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
}
|
|
|
|
if (WaitForMailSlotTransportRundown) {
|
|
KeWaitForSingleObject(
|
|
&pServerEntry->MailSlotTransportRundownEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
}
|
|
}
|
|
|
|
VOID
|
|
SmbCeTearDownServerTransport(
|
|
IN OUT PSMBCE_SERVER_TRANSPORT_CONSTRUCTION_CONTEXT pContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine tears down the server transport instance
|
|
|
|
Arguments:
|
|
|
|
pContext - the server transport construction context
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
SmbCepTearDownServerTransport(pContext->pServerEntry);
|
|
|
|
if (pContext->pCompletionEvent != NULL) {
|
|
ASSERT(pContext->pCallbackContext == NULL);
|
|
ASSERT(pContext->pCompletionRoutine == NULL);
|
|
KeSetEvent(
|
|
pContext->pCompletionEvent,
|
|
0,
|
|
FALSE );
|
|
} else if (pContext->pCallbackContext != NULL) {
|
|
ASSERT(pContext->pCompletionEvent == NULL);
|
|
(pContext->pCompletionRoutine)(pContext->pCallbackContext);
|
|
}
|
|
|
|
RxFreePool(pContext);
|
|
}
|
|
|
|
VOID
|
|
SmbCepUpdateTransportConstructionState(
|
|
IN OUT PSMBCE_SERVER_TRANSPORT_CONSTRUCTION_CONTEXT pContext)
|
|
{
|
|
SMBCE_SERVER_TRANSPORT_CONSTRUCTION_STATE State;
|
|
|
|
if (pContext->Status == STATUS_SUCCESS) {
|
|
if (pContext->TransportsToBeConstructed & SMBCE_STT_MAILSLOT) {
|
|
pContext->TransportsToBeConstructed &= ~SMBCE_STT_MAILSLOT;
|
|
State = SmbCeServerMailSlotTransportConstructionBegin;
|
|
} else if (pContext->TransportsToBeConstructed & SMBCE_STT_VC) {
|
|
pContext->TransportsToBeConstructed &= ~SMBCE_STT_VC;
|
|
State = SmbCeServerVcTransportConstructionBegin;
|
|
} else {
|
|
State = SmbCeServerTransportConstructionEnd;
|
|
}
|
|
} else {
|
|
State = SmbCeServerTransportConstructionEnd;
|
|
}
|
|
|
|
pContext->State = State;
|
|
}
|
|
|
|
VOID
|
|
SmbCeConstructServerTransport(
|
|
IN OUT PSMBCE_SERVER_TRANSPORT_CONSTRUCTION_CONTEXT pContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine constructs the server transport instance
|
|
|
|
Arguments:
|
|
|
|
pContext - the server transport construction context
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
SMBCEDB_SERVER_TYPE ServerType;
|
|
|
|
BOOLEAN ContinueConstruction = TRUE;
|
|
BOOLEAN UpdateUnavailableServerlist = TRUE;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(IoGetCurrentProcess() == RxGetRDBSSProcess());
|
|
|
|
pServerEntry = pContext->pServerEntry;
|
|
ServerType = SmbCeGetServerType(pServerEntry);
|
|
|
|
do {
|
|
switch (pContext->State) {
|
|
case SmbCeServerTransportConstructionBegin :
|
|
{
|
|
if ((pServerEntry->pTransport != NULL) ||
|
|
(pServerEntry->pMailSlotTransport != NULL)) {
|
|
SmbCepTearDownServerTransport(pServerEntry);
|
|
}
|
|
|
|
ASSERT((pServerEntry->pTransport == NULL) &&
|
|
(pServerEntry->pMailSlotTransport == NULL));
|
|
|
|
pContext->Status = STATUS_SUCCESS;
|
|
|
|
// See if we have any reason to believe this server is unavailable
|
|
pContext->Status = SmbCeIsServerAvailable( &pServerEntry->Name );
|
|
|
|
if (pContext->Status != STATUS_SUCCESS) {
|
|
UpdateUnavailableServerlist = FALSE;
|
|
}
|
|
|
|
SmbCepUpdateTransportConstructionState(pContext);
|
|
}
|
|
break;
|
|
|
|
case SmbCeServerMailSlotTransportConstructionBegin:
|
|
{
|
|
Status = MsInstantiateServerTransport(
|
|
pContext);
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
ContinueConstruction = FALSE;
|
|
break;
|
|
}
|
|
|
|
ASSERT(pContext->State == SmbCeServerMailSlotTransportConstructionEnd);
|
|
}
|
|
// lack of break intentional
|
|
|
|
case SmbCeServerMailSlotTransportConstructionEnd:
|
|
{
|
|
SmbCepUpdateTransportConstructionState(pContext);
|
|
}
|
|
break;
|
|
|
|
case SmbCeServerVcTransportConstructionBegin:
|
|
{
|
|
Status = VctInstantiateServerTransport(
|
|
pContext);
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
ContinueConstruction = FALSE;
|
|
break;
|
|
}
|
|
|
|
ASSERT(pContext->State == SmbCeServerVcTransportConstructionEnd);
|
|
}
|
|
// lack of break intentional
|
|
|
|
case SmbCeServerVcTransportConstructionEnd:
|
|
{
|
|
SmbCepUpdateTransportConstructionState(pContext);
|
|
}
|
|
break;
|
|
|
|
case SmbCeServerTransportConstructionEnd:
|
|
{
|
|
pServerEntry->ServerStatus = pContext->Status;
|
|
|
|
if (pServerEntry->ServerStatus == STATUS_SUCCESS) {
|
|
SmbCeAcquireSpinLock();
|
|
|
|
ASSERT(pContext->pMailSlotTransport != NULL);
|
|
pContext->pMailSlotTransport->SwizzleCount = 1;
|
|
|
|
if (pContext->pTransport != NULL) {
|
|
pContext->pTransport->SwizzleCount = 1;
|
|
}
|
|
|
|
pServerEntry->pTransport = pContext->pTransport;
|
|
pServerEntry->pMailSlotTransport = pContext->pMailSlotTransport;
|
|
|
|
pContext->pTransport = NULL;
|
|
pContext->pMailSlotTransport = NULL;
|
|
|
|
if (pContext->pCallbackContext != NULL) {
|
|
pContext->pCallbackContext->Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
pServerEntry->IsTransportDereferenced = FALSE;
|
|
pServerEntry->SecuritySignaturesActive = FALSE;
|
|
pServerEntry->SecuritySignaturesEnabled = FALSE;
|
|
|
|
SmbCeReleaseSpinLock();
|
|
} else {
|
|
PRX_CONTEXT pRxContext = NULL;
|
|
|
|
if (UpdateUnavailableServerlist &&
|
|
!pServerEntry->Server.IsRemoteBootServer &&
|
|
(pServerEntry->PreferredTransport == NULL)) {
|
|
// In remote boot or specific transport cases, we don't add it to
|
|
// the list so that no negative caching is introduced.
|
|
SmbCeServerIsUnavailable( &pServerEntry->Name, pServerEntry->ServerStatus );
|
|
}
|
|
|
|
if (pContext->pMailSlotTransport != NULL) {
|
|
pContext->pMailSlotTransport->pDispatchVector->TearDown(
|
|
pContext->pMailSlotTransport);
|
|
}
|
|
|
|
if (pContext->pTransport != NULL) {
|
|
pContext->pTransport->pDispatchVector->TearDown(
|
|
pContext->pTransport);
|
|
}
|
|
|
|
pContext->pTransport = NULL;
|
|
pContext->pMailSlotTransport = NULL;
|
|
|
|
pServerEntry->pTransport = NULL;
|
|
pServerEntry->pMailSlotTransport = NULL;
|
|
|
|
if ((pContext->pCallbackContext) &&
|
|
(pContext->pCallbackContext->SrvCalldownStructure)) {
|
|
pRxContext =
|
|
pContext->pCallbackContext->SrvCalldownStructure->RxContext;
|
|
}
|
|
|
|
Status = CscTransitionServerEntryForDisconnectedOperation(
|
|
pServerEntry,
|
|
pRxContext,
|
|
pServerEntry->ServerStatus,
|
|
TRUE // to autodial or not to autodial
|
|
);
|
|
|
|
if (pContext->pCallbackContext != NULL) {
|
|
pContext->pCallbackContext->Status = Status;
|
|
}
|
|
|
|
if (SmbCeIsServerInDisconnectedMode(pServerEntry)) {
|
|
pServerEntry->ServerStatus = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
pServerEntry->ServerStatus = Status;
|
|
}
|
|
}
|
|
|
|
if (pContext->pCompletionEvent != NULL) {
|
|
ASSERT(pContext->pCallbackContext == NULL);
|
|
ASSERT(pContext->pCompletionRoutine == NULL);
|
|
KeSetEvent(
|
|
pContext->pCompletionEvent,
|
|
0,
|
|
FALSE );
|
|
} else if (pContext->pCallbackContext != NULL) {
|
|
ASSERT(pContext->pCompletionEvent == NULL);
|
|
|
|
(pContext->pCompletionRoutine)(pContext->pCallbackContext);
|
|
} else {
|
|
ASSERT(!"ill formed transport initialization context");
|
|
}
|
|
|
|
if (pContext->WorkQueueItem.List.Flink != NULL) {
|
|
//DbgBreakPoint();
|
|
}
|
|
|
|
// pServerEntry->ConstructionContext = NULL;
|
|
RxFreePool(pContext);
|
|
|
|
ContinueConstruction = FALSE;
|
|
}
|
|
}
|
|
} while (ContinueConstruction);
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbCepInitializeServerTransport(
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry,
|
|
PSMBCE_SERVER_TRANSPORT_CONSTRUCTION_CALLBACK pCallbackRoutine,
|
|
PMRX_SRVCALL_CALLBACK_CONTEXT pCallbackContext,
|
|
ULONG TransportsToBeConstructed)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the transport information corresponding to a server
|
|
|
|
Arguments:
|
|
|
|
pServerEntry - the server entry instance in the database
|
|
|
|
pCallbackRoutine - the callback routine
|
|
|
|
pCallbackContext - the callback context
|
|
|
|
TransportsToBeConstructed -- the transports to be constructed
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - the server transport construction has been finalized.
|
|
|
|
Other Status codes correspond to error situations.
|
|
|
|
Notes:
|
|
|
|
Currently, only connection oriented transports are handled.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
BOOLEAN CompleteConstruction;
|
|
|
|
PAGED_CODE();
|
|
|
|
if ((pServerEntry->ServerStatus == STATUS_SUCCESS) &&
|
|
(pServerEntry->pTransport != NULL) &&
|
|
(pServerEntry->pMailSlotTransport != NULL)) {
|
|
Status = STATUS_SUCCESS;
|
|
CompleteConstruction = TRUE;
|
|
} else {
|
|
PSMBCE_SERVER_TRANSPORT_CONSTRUCTION_CONTEXT pContext;
|
|
|
|
pContext = RxAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof(SMBCE_SERVER_TRANSPORT_CONSTRUCTION_CONTEXT),
|
|
MRXSMB_TRANSPORT_POOLTAG);
|
|
|
|
CompleteConstruction = (pContext == NULL);
|
|
|
|
if (pContext != NULL) {
|
|
KEVENT CompletionEvent;
|
|
|
|
RtlZeroMemory(
|
|
pContext,
|
|
sizeof(SMBCE_SERVER_TRANSPORT_CONSTRUCTION_CONTEXT));
|
|
|
|
pContext->Status = STATUS_SUCCESS;
|
|
pContext->pServerEntry = pServerEntry;
|
|
pContext->State = SmbCeServerTransportConstructionBegin;
|
|
pContext->TransportsToBeConstructed = TransportsToBeConstructed;
|
|
|
|
if (pCallbackContext == NULL) {
|
|
KeInitializeEvent(
|
|
&CompletionEvent,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
pContext->pCompletionEvent = &CompletionEvent;
|
|
} else {
|
|
pContext->pCallbackContext = pCallbackContext;
|
|
pContext->pCompletionRoutine = pCallbackRoutine;
|
|
}
|
|
|
|
pServerEntry->ConstructionContext = (PVOID)pContext;
|
|
|
|
Status = STATUS_PENDING;
|
|
|
|
// always post to a worker thread. This is to avaoid the problem of
|
|
// a thread in system process that is impersonating for a non-admin user
|
|
// When this happens, the thread gets access denied while opening a transport
|
|
// handle
|
|
|
|
Status = RxPostToWorkerThread(
|
|
MRxSmbDeviceObject,
|
|
CriticalWorkQueue,
|
|
&pContext->WorkQueueItem,
|
|
SmbCeConstructServerTransport,
|
|
pContext);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
Status = STATUS_PENDING;
|
|
} else {
|
|
pServerEntry->ConstructionContext = NULL;
|
|
RxFreePool(pContext);
|
|
CompleteConstruction = TRUE;
|
|
}
|
|
|
|
if ((Status == STATUS_PENDING) && (pCallbackContext == NULL)) {
|
|
KeWaitForSingleObject(
|
|
&CompletionEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
|
|
Status = pServerEntry->ServerStatus;
|
|
}
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
if (CompleteConstruction) {
|
|
pServerEntry->ServerStatus = Status;
|
|
|
|
if (pCallbackRoutine != NULL) {
|
|
pCallbackContext->Status = Status;
|
|
|
|
(pCallbackRoutine)(pCallbackContext);
|
|
|
|
Status = STATUS_PENDING;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbCeUninitializeServerTransport(
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry,
|
|
PSMBCE_SERVER_TRANSPORT_DESTRUCTION_CALLBACK pCallbackRoutine,
|
|
PVOID pCallbackContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine uninitializes the transport information corresponding to a server
|
|
|
|
Arguments:
|
|
|
|
pServerEntry - the server entry instance in the database
|
|
|
|
Returns:
|
|
|
|
STATUS_SUCCESS if successful
|
|
|
|
Notes:
|
|
|
|
Currently, only connection oriented transports are handled.
|
|
|
|
In order to handle async. operations the uninitialization has to be coordinated
|
|
with the referencing mechanism. It is for this reason that this routine sets up
|
|
a rundown event and waits for it to be set.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (pCallbackRoutine == NULL &&
|
|
IoGetCurrentProcess() == RxGetRDBSSProcess()) {
|
|
SmbCepTearDownServerTransport(pServerEntry);
|
|
} else {
|
|
PSMBCE_SERVER_TRANSPORT_CONSTRUCTION_CONTEXT pContext;
|
|
|
|
pContext = RxAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof(SMBCE_SERVER_TRANSPORT_CONSTRUCTION_CONTEXT),
|
|
MRXSMB_TRANSPORT_POOLTAG);
|
|
|
|
if (pContext != NULL) {
|
|
KEVENT CompletionEvent;
|
|
|
|
RtlZeroMemory(
|
|
pContext,
|
|
sizeof(SMBCE_SERVER_TRANSPORT_CONSTRUCTION_CONTEXT));
|
|
|
|
pContext->Status = STATUS_SUCCESS;
|
|
pContext->pServerEntry = pServerEntry;
|
|
|
|
if (pCallbackRoutine == NULL) {
|
|
KeInitializeEvent(
|
|
&CompletionEvent,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
pContext->pCompletionEvent = &CompletionEvent;
|
|
} else {
|
|
pContext->pCallbackContext = pCallbackContext;
|
|
pContext->pCompletionRoutine = pCallbackRoutine;
|
|
}
|
|
|
|
if (IoGetCurrentProcess() == RxGetRDBSSProcess()) {
|
|
SmbCeTearDownServerTransport(pContext);
|
|
} else {
|
|
Status = RxPostToWorkerThread(
|
|
MRxSmbDeviceObject,
|
|
CriticalWorkQueue,
|
|
&pContext->WorkQueueItem,
|
|
SmbCeTearDownServerTransport,
|
|
pContext);
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
if (pCallbackRoutine == NULL) {
|
|
KeWaitForSingleObject(
|
|
&CompletionEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
} else {
|
|
Status = STATUS_PENDING;
|
|
}
|
|
} else {
|
|
RxFreePool(pContext);
|
|
}
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
SmbCeCompleteUninitializeServerTransport(
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry)
|
|
{
|
|
// in case of async uninitialize server transport, an additional reference count of
|
|
// server entry should be taken so that uninitialize server transport will not be
|
|
// called once again from tear down server entry if its reference count comes to 0
|
|
// before uninitialize server transport is done.
|
|
SmbCeDereferenceServerEntry(pServerEntry);
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbCeInitiateDisconnect(
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initiates the TDI disconnect
|
|
|
|
Arguments:
|
|
|
|
pServerEntry - the server entry instance in the database
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - the server transport construction has been finalized.
|
|
|
|
Other Status codes correspond to error situations.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PSMBCE_SERVER_TRANSPORT pTransport;
|
|
PSMBCE_SERVER_TRANSPORT pMailSlotTransport;
|
|
|
|
ASSERT(IoGetCurrentProcess() == RxGetRDBSSProcess());
|
|
|
|
Status = SmbCeReferenceServerTransport(&pServerEntry->pTransport);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
Status = (pServerEntry->pTransport->pDispatchVector->InitiateDisconnect)(
|
|
pServerEntry->pTransport);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
RxDbgTrace(0, Dbg, ("SmbCeInitiateDisconnect : Status %lx\n",Status));
|
|
}
|
|
|
|
SmbCeDereferenceServerTransport(&pServerEntry->pTransport);
|
|
}
|
|
|
|
Status = SmbCeReferenceServerTransport(&pServerEntry->pMailSlotTransport);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
Status = (pServerEntry->pMailSlotTransport->pDispatchVector->InitiateDisconnect)(
|
|
pServerEntry->pMailSlotTransport);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
RxDbgTrace(0, Dbg, ("SmbCeInitiateDisconnect MS : Status %lx\n",Status));
|
|
}
|
|
|
|
SmbCeDereferenceServerTransport(&pServerEntry->pMailSlotTransport);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
LONG Initializes[SENTINEL_EXCHANGE] = {0,0,0,0,0};
|
|
LONG Uninitializes[SENTINEL_EXCHANGE] = {0,0,0,0,0};
|
|
|
|
NTSTATUS
|
|
SmbCeInitializeExchangeTransport(
|
|
PSMB_EXCHANGE pExchange)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the transport associated with the exchange
|
|
|
|
Arguments:
|
|
|
|
pExchange - the exchange to be initialized
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - the exchange transport initialization has been finalized.
|
|
|
|
Other Status codes correspond to error situations.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
|
|
PAGED_CODE();
|
|
|
|
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
|
|
|
|
Status = pExchange->SmbStatus;
|
|
if (Status == STATUS_SUCCESS) {
|
|
PSMBCE_SERVER_TRANSPORT *pTransportPointer;
|
|
|
|
if (FlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_MAILSLOT_OPERATION)) {
|
|
pTransportPointer = &pServerEntry->pMailSlotTransport;
|
|
} else {
|
|
pTransportPointer = &pServerEntry->pTransport;
|
|
}
|
|
|
|
if (*pTransportPointer != NULL) {
|
|
Status = SmbCeReferenceServerTransport(pTransportPointer);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
Status = ((*pTransportPointer)->pDispatchVector->InitializeExchange)(
|
|
*pTransportPointer,
|
|
pExchange);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
ULONG TransportInitialized;
|
|
|
|
InterlockedIncrement(&Initializes[pExchange->Type]);
|
|
TransportInitialized = InterlockedExchange(&pExchange->ExchangeTransportInitialized,1);
|
|
ASSERT(TransportInitialized == 0);
|
|
} else {
|
|
SmbCeDereferenceServerTransport(pTransportPointer);
|
|
}
|
|
}
|
|
} else {
|
|
Status = STATUS_CONNECTION_DISCONNECTED;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbCeUninitializeExchangeTransport(
|
|
PSMB_EXCHANGE pExchange)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine uniinitializes the transport associated with the exchange
|
|
|
|
Arguments:
|
|
|
|
pExchange - the exchange to be initialized
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - the exchange transport initialization has been finalized.
|
|
|
|
Other Status codes correspond to error situations.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
|
|
PAGED_CODE();
|
|
|
|
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
|
|
|
|
if (InterlockedExchange(&pExchange->ExchangeTransportInitialized,0)==1) {
|
|
PSMBCE_SERVER_TRANSPORT *pTransportPointer;
|
|
|
|
if (FlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_MAILSLOT_OPERATION)) {
|
|
pTransportPointer = &pServerEntry->pMailSlotTransport;
|
|
} else {
|
|
pTransportPointer = &pServerEntry->pTransport;
|
|
}
|
|
|
|
if (*pTransportPointer != NULL) {
|
|
Status = ((*pTransportPointer)->pDispatchVector->UninitializeExchange)(
|
|
*pTransportPointer,
|
|
pExchange);
|
|
|
|
SmbCeDereferenceServerTransport(pTransportPointer);
|
|
InterlockedIncrement(&Uninitializes[pExchange->Type]);
|
|
|
|
return Status;
|
|
} else {
|
|
return STATUS_CONNECTION_DISCONNECTED;
|
|
}
|
|
} else {
|
|
return pExchange->SmbStatus;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbCepReferenceServerTransport(
|
|
PSMBCE_SERVER_TRANSPORT *pServerTransportPointer)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine references the transport associated with a server entry
|
|
|
|
Arguments:
|
|
|
|
pServerEntry - the server entry instance in the database
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - the server transport was successfully referenced
|
|
|
|
Other Status codes correspond to error situations.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
SmbCeAcquireSpinLock();
|
|
|
|
if (*pServerTransportPointer != NULL &&
|
|
(*pServerTransportPointer)->State == SMBCEDB_ACTIVE) {
|
|
InterlockedIncrement(&(*pServerTransportPointer)->SwizzleCount);
|
|
Status = STATUS_SUCCESS;
|
|
} else {
|
|
Status = STATUS_CONNECTION_DISCONNECTED;
|
|
}
|
|
|
|
SmbCeReleaseSpinLock();
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbCepDereferenceServerTransport(
|
|
PSMBCE_SERVER_TRANSPORT *pServerTransportPointer)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine dereferences the transport associated with a server entry
|
|
|
|
Arguments:
|
|
|
|
pServerTransportPointer - the server entry transport instance pointer
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - the server transport was successfully dereferenced
|
|
|
|
Other Status codes correspond to error situations.
|
|
|
|
Notes:
|
|
|
|
On finalization this routine sets the event to enable the process awaiting
|
|
tear down to restart. It also tears down the associated server transport
|
|
instance.
|
|
|
|
As a side effect the pointer value is set to NULL under the protection of a
|
|
spin lock.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
SmbCeAcquireSpinLock();
|
|
|
|
if (*pServerTransportPointer != NULL) {
|
|
LONG FinalRefCount;
|
|
PKEVENT pRundownEvent;
|
|
PSMBCE_SERVER_TRANSPORT pServerTransport;
|
|
|
|
pServerTransport = *pServerTransportPointer;
|
|
|
|
FinalRefCount = InterlockedDecrement(&pServerTransport->SwizzleCount);
|
|
|
|
if (FinalRefCount == 0) {
|
|
pServerTransport->State = SMBCEDB_INVALID;
|
|
|
|
// transport is set to NULL before the spinlock is release so that no
|
|
// exchange should reference it after it's been torn down
|
|
*pServerTransportPointer = NULL;
|
|
pRundownEvent = pServerTransport->pRundownEvent;
|
|
}
|
|
|
|
SmbCeReleaseSpinLock();
|
|
|
|
if (FinalRefCount == 0) {
|
|
if (IoGetCurrentProcess() == RxGetRDBSSProcess()) {
|
|
pServerTransport->pDispatchVector->TearDown(pServerTransport);
|
|
} else {
|
|
Status = RxDispatchToWorkerThread(
|
|
MRxSmbDeviceObject,
|
|
CriticalWorkQueue,
|
|
pServerTransport->pDispatchVector->TearDown,
|
|
pServerTransport);
|
|
}
|
|
}
|
|
} else {
|
|
SmbCeReleaseSpinLock();
|
|
Status = STATUS_CONNECTION_DISCONNECTED;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SmbCepReferenceTransport(
|
|
PSMBCE_TRANSPORT pTransport)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine references the transport instance
|
|
|
|
Arguments:
|
|
|
|
pTransport - the transport instance
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - the server transport was successfully referenced
|
|
|
|
Other Status codes correspond to error situations.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
if (pTransport != NULL) {
|
|
SmbCeAcquireSpinLock();
|
|
|
|
if (pTransport->Active) {
|
|
InterlockedIncrement(&pTransport->SwizzleCount);
|
|
Status = STATUS_SUCCESS;
|
|
} else {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
SmbCeReleaseSpinLock();
|
|
} else {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbCepDereferenceTransport(
|
|
PSMBCE_TRANSPORT pTransport)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine dereferences the transport
|
|
|
|
Arguments:
|
|
|
|
pTransport - the transport instance
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - the server transport was successfully dereferenced
|
|
|
|
Other Status codes correspond to error situations.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN AttachToSystemProcess = FALSE;
|
|
KAPC_STATE ApcState;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (pTransport != NULL) {
|
|
LONG FinalRefCount;
|
|
|
|
FinalRefCount = InterlockedDecrement(&pTransport->SwizzleCount);
|
|
|
|
if (FinalRefCount == 0) {
|
|
SmbCeRemoveTransport(pTransport);
|
|
|
|
if (IoGetCurrentProcess() != RxGetRDBSSProcess()) {
|
|
KeStackAttachProcess(RxGetRDBSSProcess(),&ApcState);
|
|
AttachToSystemProcess = TRUE;
|
|
}
|
|
|
|
RxCeTearDownAddress(&pTransport->RxCeAddress);
|
|
|
|
RxCeTearDownTransport(&pTransport->RxCeTransport);
|
|
|
|
if (AttachToSystemProcess) {
|
|
KeUnstackDetachProcess(&ApcState);
|
|
}
|
|
|
|
RxFreePool(pTransport);
|
|
}
|
|
} else {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
#ifndef MRXSMB_PNP_POWER5
|
|
|
|
HANDLE MRxSmbTdiNotificationHandle = NULL;
|
|
|
|
VOID
|
|
MRxSmbpBindTransportCallback(
|
|
IN PUNICODE_STRING pTransportName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
TDI calls this routine whenever a transport creates a new device object.
|
|
|
|
Arguments:
|
|
|
|
DeviceName - the name of the newly created device object
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PSMBCE_TRANSPORT pTransport;
|
|
|
|
PRXCE_TRANSPORT_PROVIDER_INFO pProviderInfo;
|
|
|
|
ULONG Priority;
|
|
BOOLEAN fBindToTransport = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(IoGetCurrentProcess() == RxGetRDBSSProcess());
|
|
|
|
// if this is one of the transports that is of interest to the SMB
|
|
// mini rdr then register the address with it, otherwise skip it.
|
|
|
|
if (SmbCeContext.Transports.Length != 0) {
|
|
PWSTR pSmbMRxTransports = (PWSTR)SmbCeContext.Transports.Buffer;
|
|
UNICODE_STRING SmbMRxTransport;
|
|
|
|
Priority = 1;
|
|
while (*pSmbMRxTransports) {
|
|
SmbMRxTransport.Length = wcslen(pSmbMRxTransports) * sizeof(WCHAR);
|
|
|
|
if (SmbMRxTransport.Length == pTransportName->Length) {
|
|
SmbMRxTransport.MaximumLength = SmbMRxTransport.Length;
|
|
SmbMRxTransport.Buffer = pSmbMRxTransports;
|
|
|
|
if (RtlCompareUnicodeString(
|
|
&SmbMRxTransport,
|
|
pTransportName,
|
|
TRUE) == 0) {
|
|
fBindToTransport = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pSmbMRxTransports += (SmbMRxTransport.Length / sizeof(WCHAR) + 1);
|
|
Priority++;
|
|
}
|
|
}
|
|
|
|
if (!fBindToTransport) {
|
|
return;
|
|
}
|
|
|
|
pTransport = RxAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof(SMBCE_TRANSPORT),
|
|
MRXSMB_TRANSPORT_POOLTAG);
|
|
|
|
if (pTransport != NULL) {
|
|
Status = RxCeBuildTransport(
|
|
&pTransport->RxCeTransport,
|
|
pTransportName,
|
|
0xffff);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
PRXCE_TRANSPORT_PROVIDER_INFO pProviderInfo;
|
|
|
|
pProviderInfo = pTransport->RxCeTransport.pProviderInfo;
|
|
|
|
if (!(pProviderInfo->ServiceFlags & TDI_SERVICE_CONNECTION_MODE) ||
|
|
!(pProviderInfo->ServiceFlags & TDI_SERVICE_ERROR_FREE_DELIVERY)) {
|
|
RxCeTearDownTransport(
|
|
&pTransport->RxCeTransport);
|
|
|
|
Status = STATUS_PROTOCOL_UNREACHABLE;
|
|
|
|
RxFreePool(pTransport);
|
|
}
|
|
}
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
// The connection capabilities match the capabilities required by the
|
|
// SMB mini redirector. Attempt to register the local address with the
|
|
// transport and if successful update the local transport list to include
|
|
// this transport for future connection considerations.
|
|
|
|
OEM_STRING OemServerName;
|
|
CHAR TransportAddressBuffer[TDI_TRANSPORT_ADDRESS_LENGTH +
|
|
TDI_ADDRESS_LENGTH_NETBIOS];
|
|
PTRANSPORT_ADDRESS pTransportAddress = (PTRANSPORT_ADDRESS)TransportAddressBuffer;
|
|
PTDI_ADDRESS_NETBIOS pNetbiosAddress = (PTDI_ADDRESS_NETBIOS)pTransportAddress->Address[0].Address;
|
|
|
|
pTransportAddress->TAAddressCount = 1;
|
|
pTransportAddress->Address[0].AddressLength = TDI_ADDRESS_LENGTH_NETBIOS;
|
|
pTransportAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS;
|
|
pNetbiosAddress->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
|
|
|
|
OemServerName.MaximumLength = NETBIOS_NAME_LEN;
|
|
OemServerName.Buffer = pNetbiosAddress->NetbiosName;
|
|
|
|
Status = RtlUpcaseUnicodeStringToOemString(
|
|
&OemServerName,
|
|
&SmbCeContext.ComputerName,
|
|
FALSE);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
// Ensure that the name is always of the desired length by padding
|
|
// white space to the end.
|
|
RtlCopyMemory(
|
|
&OemServerName.Buffer[OemServerName.Length],
|
|
" ",
|
|
NETBIOS_NAME_LEN - OemServerName.Length);
|
|
|
|
OemServerName.Buffer[NETBIOS_NAME_LEN - 1] = '\0';
|
|
|
|
// Register the Transport address for this mini redirector with the connection
|
|
// engine.
|
|
|
|
Status = RxCeBuildAddress(
|
|
&pTransport->RxCeAddress,
|
|
&pTransport->RxCeTransport,
|
|
pTransportAddress,
|
|
&MRxSmbVctAddressEventHandler,
|
|
&SmbCeContext);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbTransportUpdateHandler: Adding new transport\n"));
|
|
|
|
pTransport->Active = TRUE;
|
|
pTransport->Priority = Priority;
|
|
pTransport->SwizzleCount = 0;
|
|
|
|
pTransport->ObjectCategory = SMB_SERVER_TRANSPORT_CATEGORY;
|
|
pTransport->ObjectType = SMBCEDB_OT_TRANSPORT;
|
|
pTransport->State = 0;
|
|
pTransport->Flags = 0;
|
|
|
|
// notify the browser about the transport
|
|
Status = SmbCePnpBindBrowser(pTransportName, TRUE);
|
|
|
|
// Add the transport to the list of transports
|
|
if (Status == STATUS_SUCCESS) {
|
|
SmbCeAddTransport(pTransport);
|
|
} else {
|
|
RxCeTearDownAddress(&pTransport->RxCeAddress);
|
|
|
|
MRxSmbLogTransportError(pTransportName,
|
|
&SmbCeContext.DomainName,
|
|
Status,
|
|
EVENT_RDR_CANT_BIND_TRANSPORT);
|
|
SmbLogError(Status,
|
|
LOG,
|
|
MRxSmbpBindTransportCallback_1,
|
|
LOGULONG(Status)
|
|
LOGUSTR(*pTransportName));
|
|
|
|
}
|
|
} else {
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbTransportUpdateHandler: Address registration failed %lx\n",Status));
|
|
MRxSmbLogTransportError(pTransportName,
|
|
&SmbCeContext.DomainName,
|
|
Status,
|
|
EVENT_RDR_CANT_REGISTER_ADDRESS);
|
|
SmbLogError(Status,
|
|
LOG,
|
|
MRxSmbpBindTransportCallback_2,
|
|
LOGUSTR(*pTransportName));
|
|
}
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
RxCeTearDownTransport(
|
|
&pTransport->RxCeTransport);
|
|
|
|
Status = STATUS_PROTOCOL_UNREACHABLE;
|
|
RxFreePool(pTransport);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
MRxSmbpBindTransportWorkerThreadRoutine(
|
|
IN PUNICODE_STRING pTransportName)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
MRxSmbpBindTransportCallback(pTransportName);
|
|
|
|
RxFreePool(pTransportName);
|
|
}
|
|
|
|
VOID
|
|
MRxSmbBindTransportCallback(
|
|
IN PUNICODE_STRING pTransportName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
TDI calls this routine whenever a transport creates a device object
|
|
|
|
Arguments:
|
|
|
|
TransportName = the name of the deleted device object
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (IoGetCurrentProcess() == RxGetRDBSSProcess()) {
|
|
MRxSmbpBindTransportCallback(pTransportName);
|
|
} else {
|
|
PUNICODE_STRING pNewTransportName;
|
|
NTSTATUS Status;
|
|
|
|
pNewTransportName = RxAllocatePoolWithTag(
|
|
PagedPool,
|
|
sizeof(UNICODE_STRING) + pTransportName->Length,
|
|
MRXSMB_TRANSPORT_POOLTAG);
|
|
|
|
if (pNewTransportName != NULL) {
|
|
pNewTransportName->MaximumLength = pTransportName->MaximumLength;
|
|
pNewTransportName->Length = pTransportName->Length;
|
|
pNewTransportName->Buffer = (PWCHAR)((PBYTE)pNewTransportName +
|
|
sizeof(UNICODE_STRING));
|
|
|
|
RtlCopyMemory(
|
|
pNewTransportName->Buffer,
|
|
pTransportName->Buffer,
|
|
pNewTransportName->Length);
|
|
|
|
Status = RxDispatchToWorkerThread(
|
|
MRxSmbDeviceObject,
|
|
CriticalWorkQueue,
|
|
MRxSmbpBindTransportWorkerThreadRoutine,
|
|
pNewTransportName);
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (Status != RX_MAP_STATUS(SUCCESS)) {
|
|
RxLog(("SmbCe Tdi Bind .Error %lx\n", Status));
|
|
|
|
MRxSmbLogTransportError(pTransportName,
|
|
&SmbCeContext.DomainName,
|
|
Status,
|
|
EVENT_RDR_CANT_BIND_TRANSPORT);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
MRxSmbUnbindTransportCallback(
|
|
IN PUNICODE_STRING pTransportName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
TDI calls this routine whenever a transport deletes a device object
|
|
|
|
Arguments:
|
|
|
|
TransportName = the name of the deleted device object
|
|
|
|
--*/
|
|
{
|
|
PSMBCE_TRANSPORT pTransport;
|
|
|
|
PAGED_CODE();
|
|
|
|
pTransport = SmbCeFindTransport(pTransportName);
|
|
|
|
if (pTransport != NULL) {
|
|
// notify the browser about the transport
|
|
SmbCePnpBindBrowser(pTransportName, FALSE);
|
|
|
|
// Remove this transport from the list of transports under consideration
|
|
// in the mini redirector.
|
|
SmbCeRemoveTransport(pTransport);
|
|
|
|
// Enumerate the servers and mark those servers utilizing this transport
|
|
// as having an invalid transport.
|
|
SmbCeHandleTransportInvalidation(pTransport);
|
|
|
|
// dereference the transport
|
|
SmbCeDereferenceTransport(pTransport);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbRegisterForPnpNotifications()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine registers with TDI for receiving transport notifications
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
if(MRxSmbTdiNotificationHandle == NULL ) {
|
|
Status = TdiRegisterNotificationHandler (
|
|
MRxSmbBindTransportCallback,
|
|
MRxSmbUnbindTransportCallback,
|
|
&MRxSmbTdiNotificationHandle );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbDeregisterForPnpNotifications()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine deregisters the TDI notification mechanism
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
if( MRxSmbTdiNotificationHandle != NULL ) {
|
|
Status = TdiDeregisterNotificationHandler( MRxSmbTdiNotificationHandle );
|
|
|
|
if( NT_SUCCESS( Status ) ) {
|
|
MRxSmbTdiNotificationHandle = NULL;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
#else
|
|
|
|
HANDLE MRxSmbTdiNotificationHandle = NULL;
|
|
|
|
KEVENT TdiNetStartupCompletionEvent;
|
|
|
|
LONG TdiBindRequestsActive = 0;
|
|
|
|
BOOLEAN TdiPnpNetReadyEventReceived = FALSE;
|
|
|
|
// The TRANSPORT_BIND_CONTEXT contains the result of the priority determination
|
|
// as well as the name. The priority is used to order the transports in the order
|
|
// in which connection attempts will be made
|
|
|
|
typedef struct _TRANSPORT_BIND_CONTEXT_ {
|
|
ULONG Priority;
|
|
UNICODE_STRING TransportName;
|
|
} TRANSPORT_BIND_CONTEXT, *PTRANSPORT_BIND_CONTEXT;
|
|
|
|
VOID
|
|
SmbCeSignalNetReadyEvent()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine signals the net ready event if all the bind requests
|
|
have been completed and if the net ready event has been received from TDI
|
|
|
|
Arguments:
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN SignalNetReadyEvent = FALSE;
|
|
|
|
SmbCeAcquireSpinLock();
|
|
|
|
if (TdiPnpNetReadyEventReceived &&
|
|
TdiBindRequestsActive == 0) {
|
|
SignalNetReadyEvent = TRUE;
|
|
}
|
|
|
|
SmbCeReleaseSpinLock();
|
|
|
|
if (SignalNetReadyEvent) {
|
|
KeSetEvent(
|
|
&TdiNetStartupCompletionEvent,
|
|
IO_NETWORK_INCREMENT,
|
|
FALSE);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
MRxSmbpBindTransportCallback(
|
|
IN PTRANSPORT_BIND_CONTEXT pTransportContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
TDI calls this routine whenever a transport creates a new device object.
|
|
|
|
Arguments:
|
|
|
|
TransportName - the name of the newly created device object
|
|
|
|
TransportBindings - the transport bindings ( multi sz)
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PSMBCE_TRANSPORT pTransport;
|
|
|
|
PRXCE_TRANSPORT_PROVIDER_INFO pProviderInfo;
|
|
|
|
PUNICODE_STRING pTransportName;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(IoGetCurrentProcess() == RxGetRDBSSProcess());
|
|
|
|
pTransportName = &pTransportContext->TransportName;
|
|
|
|
RxDbgTrace( 0, Dbg, ("MrxSmbpBindTransportCallback, Transport Name = %wZ\n", pTransportName ));
|
|
|
|
pTransport = RxAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof(SMBCE_TRANSPORT),
|
|
MRXSMB_TRANSPORT_POOLTAG);
|
|
|
|
if (pTransport != NULL) {
|
|
Status = RxCeBuildTransport(
|
|
&pTransport->RxCeTransport,
|
|
pTransportName,
|
|
0xffff);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
PRXCE_TRANSPORT_PROVIDER_INFO pProviderInfo;
|
|
|
|
pProviderInfo = pTransport->RxCeTransport.pProviderInfo;
|
|
|
|
if (!(pProviderInfo->ServiceFlags & TDI_SERVICE_CONNECTION_MODE) ||
|
|
!(pProviderInfo->ServiceFlags & TDI_SERVICE_ERROR_FREE_DELIVERY)) {
|
|
RxCeTearDownTransport(
|
|
&pTransport->RxCeTransport);
|
|
|
|
Status = STATUS_PROTOCOL_UNREACHABLE;
|
|
|
|
RxFreePool(pTransport);
|
|
}
|
|
}
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
// The connection capabilities match the capabilities required by the
|
|
// SMB mini redirector. Attempt to register the local address with the
|
|
// transport and if successful update the local transport list to include
|
|
// this transport for future connection considerations.
|
|
|
|
OEM_STRING OemServerName;
|
|
CHAR TransportAddressBuffer[TDI_TRANSPORT_ADDRESS_LENGTH +
|
|
TDI_ADDRESS_LENGTH_NETBIOS];
|
|
PTRANSPORT_ADDRESS pTransportAddress = (PTRANSPORT_ADDRESS)TransportAddressBuffer;
|
|
PTDI_ADDRESS_NETBIOS pNetbiosAddress = (PTDI_ADDRESS_NETBIOS)pTransportAddress->Address[0].Address;
|
|
|
|
pTransportAddress->TAAddressCount = 1;
|
|
pTransportAddress->Address[0].AddressLength = TDI_ADDRESS_LENGTH_NETBIOS;
|
|
pTransportAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS;
|
|
pNetbiosAddress->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
|
|
|
|
OemServerName.MaximumLength = NETBIOS_NAME_LEN;
|
|
OemServerName.Buffer = pNetbiosAddress->NetbiosName;
|
|
|
|
Status = RtlUpcaseUnicodeStringToOemString(
|
|
&OemServerName,
|
|
&SmbCeContext.ComputerName,
|
|
FALSE);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
// Ensure that the name is always of the desired length by padding
|
|
// white space to the end.
|
|
RtlCopyMemory(
|
|
&OemServerName.Buffer[OemServerName.Length],
|
|
" ",
|
|
NETBIOS_NAME_LEN - OemServerName.Length);
|
|
|
|
OemServerName.Buffer[NETBIOS_NAME_LEN - 1] = '\0';
|
|
|
|
// Register the Transport address for this mini redirector with the connection
|
|
// engine.
|
|
|
|
Status = RxCeBuildAddress(
|
|
&pTransport->RxCeAddress,
|
|
&pTransport->RxCeTransport,
|
|
pTransportAddress,
|
|
&MRxSmbVctAddressEventHandler,
|
|
&SmbCeContext);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbTransportUpdateHandler: Adding new transport\n"));
|
|
|
|
pTransport->Active = TRUE;
|
|
pTransport->Priority = pTransportContext->Priority;
|
|
pTransport->SwizzleCount = 0;
|
|
|
|
pTransport->ObjectCategory = SMB_SERVER_TRANSPORT_CATEGORY;
|
|
pTransport->ObjectType = SMBCEDB_OT_TRANSPORT;
|
|
pTransport->State = 0;
|
|
pTransport->Flags = 0;
|
|
|
|
// notify the browser about the transport
|
|
Status = SmbCePnpBindBrowser(pTransportName, TRUE);
|
|
|
|
if (MRxSmbBootedRemotely && (Status == STATUS_REDIRECTOR_NOT_STARTED)) {
|
|
|
|
//
|
|
// Ignore failures here, because when starting during
|
|
// textmode setup in remote boot, the browser is not around.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
// Add the transport to the list of transports
|
|
if (Status == STATUS_SUCCESS) {
|
|
SmbCeAddTransport(pTransport);
|
|
RxDbgTrace( 0, Dbg, ("MrxSmbpBindTransportCallback, Transport %wZ added\n", pTransportName ));
|
|
} else {
|
|
RxCeTearDownAddress(&pTransport->RxCeAddress);
|
|
|
|
MRxSmbLogTransportError(pTransportName,
|
|
&SmbCeContext.DomainName,
|
|
Status,
|
|
EVENT_RDR_CANT_BIND_TRANSPORT);
|
|
SmbLogError(Status,
|
|
LOG,
|
|
MRxSmbpBindTransportCallback_1,
|
|
LOGULONG(Status)
|
|
LOGUSTR(*pTransportName));
|
|
}
|
|
} else {
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbTransportUpdateHandler: Address registration failed %lx\n",Status));
|
|
MRxSmbLogTransportError(pTransportName,
|
|
&SmbCeContext.DomainName,
|
|
Status,
|
|
EVENT_RDR_CANT_REGISTER_ADDRESS);
|
|
SmbLogError(Status,
|
|
LOG,
|
|
MRxSmbpBindTransportCallback_2,
|
|
LOGULONG(Status)
|
|
LOGUSTR(*pTransportName));
|
|
}
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
RxDbgTrace( 0, Dbg, ("MrxSmbpBindTransportCallback, Transport %wZ unreachable 0x%x\n",
|
|
pTransportName, Status ));
|
|
RxCeTearDownTransport(
|
|
&pTransport->RxCeTransport);
|
|
|
|
Status = STATUS_PROTOCOL_UNREACHABLE;
|
|
RxFreePool(pTransport);
|
|
}
|
|
}
|
|
|
|
InterlockedDecrement(&TdiBindRequestsActive);
|
|
SmbCeSignalNetReadyEvent();
|
|
}
|
|
|
|
VOID
|
|
MRxSmbpBindTransportWorkerThreadRoutine(
|
|
IN PTRANSPORT_BIND_CONTEXT pTransportContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The TDI callbacks always do not occur in the context of the FSP process.
|
|
Since there are a few TDi interfaces that accept handles we need to ensure
|
|
that such calls always gets funnelled back to the FSP.
|
|
|
|
Arguments:
|
|
|
|
pTransportContext - the transport binding context
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
MRxSmbpBindTransportCallback(pTransportContext);
|
|
|
|
RxFreePool(pTransportContext);
|
|
}
|
|
|
|
VOID
|
|
MRxSmbpUnbindTransportCallback(
|
|
PSMBCE_TRANSPORT pTransport)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The Unbind callback routine which is always executed in the context of the
|
|
RDR process so that handles can be closed correctly
|
|
|
|
Arguments:
|
|
|
|
pTransport - the transport for which the PNP_OP_DEL was received
|
|
|
|
Notes:
|
|
|
|
On entry to this routine the appropriate transport must have been referenced
|
|
This routine will dereference it and invalidate the existing exchanges using
|
|
this transport.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
// notify the browser about the transport
|
|
SmbCePnpBindBrowser(&pTransport->RxCeTransport.Name, FALSE);
|
|
|
|
// Remove this transport from the list of transports under consideration
|
|
// in the mini redirector.
|
|
|
|
SmbCeRemoveTransport(pTransport);
|
|
|
|
// Enumerate the servers and mark those servers utilizing this transport
|
|
// as having an invalid transport.
|
|
SmbCeHandleTransportInvalidation(pTransport);
|
|
|
|
// dereference the transport
|
|
SmbCeDereferenceTransport(pTransport);
|
|
}
|
|
|
|
|
|
VOID
|
|
MRxSmbpOverrideBindingPriority(
|
|
PUNICODE_STRING pTransportName,
|
|
PULONG pPriority
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function obtains a overriding priority value from the registry for a given
|
|
transport.
|
|
|
|
The priority of a transport controls the order in which connections are accepted. It is
|
|
sometimes useful for a customer to control which transport is used first in the redirector.
|
|
|
|
The priority is usually determined by the order of the transports in the binding list. With
|
|
the new Connections UI model for network setup, it will no longer be possible to adjust
|
|
the order of the bindings in the binding list. Thus, another mechanism is needed when the
|
|
user wants to override the priority assigned to a given binding.
|
|
|
|
Arguments:
|
|
|
|
pTransportName - pointer to UNICODE_STRING descriptor for transport string, for example
|
|
"\Device\Netbt_tcpip_{guid}"
|
|
pPriority - pointer to LONG to receive new priority on success, otherwise not touched
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR valueBuffer[128];
|
|
UNICODE_STRING path, value, key;
|
|
USHORT length,ulength;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
NTSTATUS status;
|
|
HANDLE parametersHandle;
|
|
ULONG temp;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Validate input
|
|
|
|
if (pTransportName->Length == 0) {
|
|
return;
|
|
}
|
|
|
|
// Open parameters key
|
|
|
|
RtlInitUnicodeString( &path, SMBMRX_MINIRDR_PARAMETERS );
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&path,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = ZwOpenKey (¶metersHandle, KEY_READ, &objectAttributes);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return;
|
|
}
|
|
|
|
// Construct value name = "BindingPriority" + transportname
|
|
// First, find the last slash. Then form the value from the prefix and
|
|
// the remainder of the transport name.
|
|
ulength = pTransportName->Length / sizeof(WCHAR);
|
|
for( length = ulength - 1; length != 0; length-- ) {
|
|
if (pTransportName->Buffer[length] == L'\\') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
length++;
|
|
key.Buffer = pTransportName->Buffer + length;
|
|
key.Length = (ulength - length) * sizeof(WCHAR);
|
|
|
|
value.Buffer = valueBuffer;
|
|
value.MaximumLength = 128 * sizeof(WCHAR);
|
|
value.Length = 0;
|
|
|
|
RtlAppendUnicodeToString( &value, L"BindingPriority" );
|
|
RtlAppendUnicodeStringToString( &value, &key );
|
|
|
|
// Check if the value is present. If so, replace priority
|
|
// A value of zero is valid and indicates do not bind this one
|
|
|
|
status = MRxSmbGetUlongRegistryParameter(
|
|
parametersHandle,
|
|
value.Buffer,
|
|
(PULONG)&temp,
|
|
FALSE );
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
*pPriority = temp;
|
|
}
|
|
|
|
ZwClose(parametersHandle);
|
|
}
|
|
|
|
VOID
|
|
MRxSmbPnPBindingHandler(
|
|
IN TDI_PNP_OPCODE PnPOpcode,
|
|
IN PUNICODE_STRING pTransportName,
|
|
IN PWSTR BindingList)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The TDI callbacks routine for binding changes
|
|
|
|
Arguments:
|
|
|
|
PnPOpcode - the PNP op code
|
|
|
|
pTransportName - the transport name
|
|
|
|
BindingList - the binding order
|
|
|
|
--*/
|
|
{
|
|
ULONG Priority;
|
|
|
|
PAGED_CODE();
|
|
|
|
switch (PnPOpcode) {
|
|
case TDI_PNP_OP_ADD:
|
|
{
|
|
BOOLEAN fBindToTransport = FALSE;
|
|
PWSTR pSmbMRxTransports;
|
|
UNICODE_STRING SmbMRxTransport;
|
|
NTSTATUS Status;
|
|
|
|
Status = SmbCeGetConfigurationInformation();
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
return;
|
|
}
|
|
|
|
pSmbMRxTransports = (PWSTR)SmbCeContext.Transports.Buffer;
|
|
Priority = 1;
|
|
while (*pSmbMRxTransports) {
|
|
SmbMRxTransport.Length = wcslen(pSmbMRxTransports) * sizeof(WCHAR);
|
|
|
|
if (SmbMRxTransport.Length == pTransportName->Length) {
|
|
SmbMRxTransport.MaximumLength = SmbMRxTransport.Length;
|
|
SmbMRxTransport.Buffer = pSmbMRxTransports;
|
|
|
|
if (RtlCompareUnicodeString(
|
|
&SmbMRxTransport,
|
|
pTransportName,
|
|
TRUE) == 0) {
|
|
fBindToTransport = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pSmbMRxTransports += (SmbMRxTransport.Length / sizeof(WCHAR) + 1);
|
|
Priority++;
|
|
}
|
|
|
|
// Provide a local registry means to alter binding priority
|
|
if (fBindToTransport) {
|
|
MRxSmbpOverrideBindingPriority( pTransportName, &Priority );
|
|
fBindToTransport = (Priority != 0);
|
|
}
|
|
|
|
if (fBindToTransport) {
|
|
InterlockedIncrement(&TdiBindRequestsActive);
|
|
|
|
if (IoGetCurrentProcess() == RxGetRDBSSProcess()) {
|
|
TRANSPORT_BIND_CONTEXT TransportContext;
|
|
|
|
TransportContext.Priority = Priority;
|
|
TransportContext.TransportName = *pTransportName;
|
|
MRxSmbpBindTransportCallback(&TransportContext);
|
|
} else {
|
|
PTRANSPORT_BIND_CONTEXT pNewTransportContext;
|
|
|
|
NTSTATUS Status;
|
|
|
|
pNewTransportContext = RxAllocatePoolWithTag(
|
|
PagedPool,
|
|
sizeof(TRANSPORT_BIND_CONTEXT) + pTransportName->Length,
|
|
MRXSMB_TRANSPORT_POOLTAG);
|
|
|
|
if (pNewTransportContext != NULL) {
|
|
pNewTransportContext->Priority = Priority;
|
|
pNewTransportContext->TransportName.MaximumLength = pTransportName->MaximumLength;
|
|
pNewTransportContext->TransportName.Length = pTransportName->Length;
|
|
pNewTransportContext->TransportName.Buffer = (PWCHAR)((PBYTE)pNewTransportContext +
|
|
sizeof(TRANSPORT_BIND_CONTEXT));
|
|
|
|
RtlCopyMemory(
|
|
pNewTransportContext->TransportName.Buffer,
|
|
pTransportName->Buffer,
|
|
pTransportName->Length);
|
|
|
|
Status = RxDispatchToWorkerThread(
|
|
MRxSmbDeviceObject,
|
|
CriticalWorkQueue,
|
|
MRxSmbpBindTransportWorkerThreadRoutine,
|
|
pNewTransportContext);
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
InterlockedDecrement(&TdiBindRequestsActive);
|
|
SmbCeSignalNetReadyEvent();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TDI_PNP_OP_DEL:
|
|
{
|
|
PSMBCE_TRANSPORT pTransport;
|
|
|
|
pTransport = SmbCeFindTransport(pTransportName);
|
|
|
|
if (pTransport != NULL) {
|
|
if (IoGetCurrentProcess() == RxGetRDBSSProcess()) {
|
|
MRxSmbpUnbindTransportCallback(pTransport);
|
|
} else {
|
|
NTSTATUS Status;
|
|
|
|
Status = RxDispatchToWorkerThread(
|
|
MRxSmbDeviceObject,
|
|
CriticalWorkQueue,
|
|
MRxSmbpUnbindTransportCallback,
|
|
pTransport);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TDI_PNP_OP_UPDATE:
|
|
{
|
|
}
|
|
break;
|
|
|
|
case TDI_PNP_OP_NETREADY:
|
|
{
|
|
TdiPnpNetReadyEventReceived = TRUE;
|
|
SmbCeSignalNetReadyEvent();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if( PnPOpcode != TDI_PNP_OP_NETREADY )
|
|
{
|
|
SmbMRxNotifyChangesToNetBt( PnPOpcode, pTransportName, BindingList );
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbPnPPowerHandler(
|
|
IN PUNICODE_STRING DeviceName,
|
|
IN PNET_PNP_EVENT PowerEvent,
|
|
IN PTDI_PNP_CONTEXT Context1,
|
|
IN PTDI_PNP_CONTEXT Context2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine deals with power changes
|
|
|
|
Notes:
|
|
|
|
The implementation needs to be completed
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
LONG NumberOfActiveOpens;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
RxPurgeAllFobxs(MRxSmbDeviceObject);
|
|
|
|
RxScavengeAllFobxs(MRxSmbDeviceObject);
|
|
NumberOfActiveOpens = MRxSmbNumberOfSrvOpens;
|
|
|
|
switch (PowerEvent->NetEvent) {
|
|
case NetEventQueryPower:
|
|
{
|
|
// If the redirector were to return an error on this request there
|
|
// is no underlying support to tell the user about the files that
|
|
// are open. There are two approaches to doing this.. either the RDR
|
|
// rolls its own UI or the PNP manager provides the infra structure.
|
|
// The problem with the former is that hibernation becomes a painstaking
|
|
// process wherein the user has to contend with a variety of UI.
|
|
// Till this is resolved the decision was to use the power mgmt. API
|
|
// to manage system initiated hibernate requests and succeed user
|
|
// initiated requests after appropriate purging/scavenging.
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case NetEventQueryRemoveDevice:
|
|
{
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
ULONG NumberOfFilesOpen = 0;
|
|
PSMBCE_TRANSPORT pTransport = NULL;
|
|
|
|
pTransport = SmbCeFindTransport(DeviceName);
|
|
|
|
if (pTransport != NULL) {
|
|
SmbCeAcquireSpinLock();
|
|
|
|
pServerEntry = SmbCeGetFirstServerEntry();
|
|
|
|
while (pServerEntry != NULL) {
|
|
if ((pServerEntry->pTransport != NULL) &&
|
|
(pTransport == pServerEntry->pTransport->pTransport)) {
|
|
NumberOfFilesOpen += pServerEntry->Server.NumberOfSrvOpens;
|
|
}
|
|
|
|
pServerEntry = SmbCeGetNextServerEntry(pServerEntry);
|
|
}
|
|
|
|
SmbCeReleaseSpinLock();
|
|
|
|
SmbCeDereferenceTransport(pTransport);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbRegisterForPnpNotifications()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine registers with TDI for receiving transport notifications
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
if(MRxSmbTdiNotificationHandle == NULL ) {
|
|
UNICODE_STRING ClientName;
|
|
|
|
TDI_CLIENT_INTERFACE_INFO ClientInterfaceInfo;
|
|
|
|
RtlInitUnicodeString(&ClientName,L"LanmanWorkStation");
|
|
|
|
ClientInterfaceInfo.MajorTdiVersion = 2;
|
|
ClientInterfaceInfo.MinorTdiVersion = 0;
|
|
|
|
ClientInterfaceInfo.Unused = 0;
|
|
ClientInterfaceInfo.ClientName = &ClientName;
|
|
|
|
ClientInterfaceInfo.BindingHandler = MRxSmbPnPBindingHandler;
|
|
ClientInterfaceInfo.AddAddressHandler = NULL;
|
|
ClientInterfaceInfo.DelAddressHandler = NULL;
|
|
ClientInterfaceInfo.PnPPowerHandler = MRxSmbPnPPowerHandler;
|
|
|
|
KeInitializeEvent(
|
|
&TdiNetStartupCompletionEvent,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
Status = TdiRegisterPnPHandlers (
|
|
&ClientInterfaceInfo,
|
|
sizeof(ClientInterfaceInfo),
|
|
&MRxSmbTdiNotificationHandle );
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
LARGE_INTEGER WaitInterval;
|
|
|
|
WaitInterval.QuadPart = -( 10000 * 2 * 60 * 1000 );
|
|
|
|
Status = KeWaitForSingleObject(
|
|
&TdiNetStartupCompletionEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
&WaitInterval);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
DbgPrint("MRxSmb Finishes waiting on TDI_PNP_OP_NETREADY %lx\n",Status);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbDeregisterForPnpNotifications()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine deregisters the TDI notification mechanism
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
if( MRxSmbTdiNotificationHandle != NULL ) {
|
|
Status = TdiDeregisterPnPHandlers( MRxSmbTdiNotificationHandle );
|
|
|
|
if( NT_SUCCESS( Status ) ) {
|
|
MRxSmbTdiNotificationHandle = NULL;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
#endif
|
|
|
|
NTSTATUS
|
|
SmbCePnpBindBrowser( PUNICODE_STRING pTransportName, BOOLEAN IsBind)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine binds the browser with the specified transport
|
|
|
|
Arguments:
|
|
|
|
pTransportName - the name of the transport
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE BrowserHandle;
|
|
PLMDR_REQUEST_PACKET pLmdrRequestPacket;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING BrowserDeviceName;
|
|
ULONG LmdrRequestPacketSize;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Open up a handle to the browser
|
|
//
|
|
RtlInitUnicodeString( &BrowserDeviceName, DD_BROWSER_DEVICE_NAME_U);
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&BrowserDeviceName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = IoCreateFile(
|
|
&BrowserHandle, // FileHandle
|
|
SYNCHRONIZE, // DesiredAccess
|
|
&ObjectAttributes, // ObjectAttributes
|
|
&IoStatusBlock, // IoStatusBlock
|
|
NULL, // AllocationSize
|
|
0L, // FileAttributes
|
|
FILE_SHARE_VALID_FLAGS, // ShareAccess
|
|
FILE_OPEN, // Disposition
|
|
FILE_SYNCHRONOUS_IO_NONALERT, // CreateOptions
|
|
NULL, // EaBuffer
|
|
0, // EaLength
|
|
CreateFileTypeNone, // CreateFileType
|
|
NULL, // ExtraCreateParameters
|
|
0 // Options
|
|
);
|
|
|
|
if( NT_SUCCESS( Status ) ) {
|
|
Status = IoStatusBlock.Status;
|
|
}
|
|
|
|
if( !NT_SUCCESS(Status ) ) {
|
|
return Status;
|
|
}
|
|
|
|
// The browser requires that the computer name and the domain name be
|
|
// concatenated to the transport name and passed on. Since no length
|
|
// fields are provided to supply the length of these two names the
|
|
// NULL delimiter needs to be attached. This accounts for the two
|
|
// additional characters in the calculation
|
|
|
|
LmdrRequestPacketSize = sizeof(*pLmdrRequestPacket) +
|
|
pTransportName->Length +
|
|
SmbCeContext.DomainName.Length + sizeof(WCHAR) +
|
|
SmbCeContext.ComputerName.Length + sizeof(WCHAR);
|
|
|
|
pLmdrRequestPacket = RxAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
LmdrRequestPacketSize,
|
|
MRXSMB_TRANSPORT_POOLTAG);
|
|
|
|
if (pLmdrRequestPacket != NULL) {
|
|
ULONG BufferOffset = 0;
|
|
PVOID pBuffer;
|
|
WCHAR NullChar = L'\0';
|
|
ULONG BindMode;
|
|
|
|
//
|
|
// Tell the browser to bind to this new transport
|
|
//
|
|
|
|
RtlZeroMemory( pLmdrRequestPacket, sizeof(LMDR_REQUEST_PACKET));
|
|
pLmdrRequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
|
|
pLmdrRequestPacket->Parameters.Bind.TransportNameLength = pTransportName->Length;
|
|
|
|
pBuffer = pLmdrRequestPacket->Parameters.Bind.TransportName;
|
|
|
|
RtlCopyMemory(
|
|
pBuffer,
|
|
pTransportName->Buffer,
|
|
pTransportName->Length);
|
|
BufferOffset = pTransportName->Length;
|
|
|
|
// Tell the browser our computer name.
|
|
pLmdrRequestPacket->Level = TRUE; // Emulated computer name follows transport name.
|
|
RtlCopyMemory(
|
|
((PBYTE)pBuffer + BufferOffset),
|
|
SmbCeContext.ComputerName.Buffer,
|
|
SmbCeContext.ComputerName.Length);
|
|
BufferOffset += SmbCeContext.ComputerName.Length;
|
|
|
|
RtlCopyMemory(
|
|
((PBYTE)pBuffer + BufferOffset),
|
|
&NullChar,
|
|
sizeof(WCHAR));
|
|
BufferOffset += sizeof(WCHAR);
|
|
|
|
|
|
// Tell the browser our domain name.
|
|
pLmdrRequestPacket->EmulatedDomainName.Buffer = (LPWSTR)
|
|
((PBYTE)pBuffer + BufferOffset);
|
|
pLmdrRequestPacket->EmulatedDomainName.MaximumLength =
|
|
pLmdrRequestPacket->EmulatedDomainName.Length =
|
|
SmbCeContext.DomainName.Length;
|
|
RtlCopyMemory(
|
|
((PBYTE)pBuffer + BufferOffset),
|
|
SmbCeContext.DomainName.Buffer,
|
|
SmbCeContext.DomainName.Length);
|
|
BufferOffset += SmbCeContext.DomainName.Length;
|
|
|
|
RtlCopyMemory(
|
|
((PBYTE)pBuffer + BufferOffset),
|
|
&NullChar,
|
|
sizeof(WCHAR));
|
|
BufferOffset += sizeof(WCHAR);
|
|
|
|
BindMode = IsBind?
|
|
IOCTL_LMDR_BIND_TO_TRANSPORT_DOM:
|
|
IOCTL_LMDR_UNBIND_FROM_TRANSPORT_DOM;
|
|
|
|
Status = NtDeviceIoControlFile(
|
|
BrowserHandle, // FileHandle
|
|
NULL, // Event
|
|
NULL, // ApcRoutine
|
|
NULL, // ApcContext
|
|
&IoStatusBlock, // IoStatusBlock
|
|
BindMode, // IoControlCode
|
|
pLmdrRequestPacket, // InputBuffer
|
|
LmdrRequestPacketSize, // InputBufferLength
|
|
NULL, // OutputBuffer
|
|
0 // OutputBufferLength
|
|
);
|
|
|
|
RxFreePool(pLmdrRequestPacket);
|
|
|
|
if( NT_SUCCESS(Status ) ) {
|
|
Status = IoStatusBlock.Status;
|
|
}
|
|
}
|
|
|
|
ZwClose( BrowserHandle );
|
|
|
|
return Status;
|
|
}
|
|
|
|
PSMBCE_TRANSPORT_ARRAY
|
|
SmbCeReferenceTransportArray(VOID)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine references and returns the current transport array instance
|
|
|
|
Return Value:
|
|
|
|
PSMBCE_TRANSPORT_ARRAY - the pointer of the current transport array instance
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
KIRQL SavedIrql;
|
|
PSMBCE_TRANSPORT_ARRAY pTransportArray;
|
|
|
|
KeAcquireSpinLock(&MRxSmbTransports.Lock,&SavedIrql);
|
|
|
|
pTransportArray = MRxSmbTransports.pTransportArray;
|
|
|
|
if (pTransportArray != NULL) {
|
|
InterlockedIncrement(&pTransportArray->ReferenceCount);
|
|
}
|
|
|
|
KeReleaseSpinLock(&MRxSmbTransports.Lock,SavedIrql);
|
|
|
|
return pTransportArray;
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbCeDereferenceTransportArray(
|
|
PSMBCE_TRANSPORT_ARRAY pTransportArray)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine dereferences the transport array instance
|
|
|
|
Arguments:
|
|
|
|
pTransportArray - the transport array instance
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - the server transport was successfully dereferenced
|
|
|
|
Other Status codes correspond to error situations.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
KIRQL SavedIrql;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (pTransportArray != NULL) {
|
|
ASSERT( pTransportArray->ReferenceCount > 0 );
|
|
|
|
if(InterlockedDecrement(&pTransportArray->ReferenceCount)==0) {
|
|
ULONG i;
|
|
|
|
for(i=0;i<pTransportArray->Count;i++) {
|
|
SmbCeDereferenceTransport(pTransportArray->SmbCeTransports[i]);
|
|
}
|
|
|
|
RxFreePool(pTransportArray->SmbCeTransports);
|
|
RxFreePool(pTransportArray->LocalAddresses);
|
|
RxFreePool(pTransportArray);
|
|
}
|
|
} else {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbCeIsServerAvailable(
|
|
PUNICODE_STRING Name
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine scans the list of "unreachable" servers and returns the status
|
|
of the last failed connection attempt.
|
|
|
|
Return:
|
|
STATUS_SUCCESS -> we have no reason to believe this server is unreachable
|
|
other -> server is unreachable for this reason
|
|
--*/
|
|
{
|
|
PUNAVAILABLE_SERVER server;
|
|
LARGE_INTEGER now;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
KeQueryTickCount( &now );
|
|
|
|
ExAcquireResourceExclusive( &UnavailableServerListResource, TRUE );
|
|
|
|
for( server = (PUNAVAILABLE_SERVER)UnavailableServerList.Flink;
|
|
server != (PUNAVAILABLE_SERVER)&UnavailableServerList;
|
|
server = (PUNAVAILABLE_SERVER)server->ListEntry.Flink ) {
|
|
|
|
//
|
|
// If this entry has timed out, remove it.
|
|
//
|
|
if( now.QuadPart > server->Time.QuadPart ) {
|
|
PUNAVAILABLE_SERVER tmp;
|
|
//
|
|
// Unlink this entry from the list and discard it
|
|
//
|
|
tmp = (PUNAVAILABLE_SERVER)(server->ListEntry.Blink);
|
|
RemoveEntryList( &server->ListEntry );
|
|
RxFreePool( server );
|
|
server = tmp;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// See if this entry is the one we want
|
|
//
|
|
if( RtlCompareUnicodeString( &server->Name, Name, TRUE ) == 0 ) {
|
|
|
|
status = server->Status;
|
|
|
|
RxDbgTrace(0, Dbg, ("SmbCeIsServerAvailable: Found %wZ %X\n",
|
|
&server->Name, status ));
|
|
}
|
|
}
|
|
|
|
ExReleaseResource( &UnavailableServerListResource );
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
SmbCeServerIsUnavailable(
|
|
PUNICODE_STRING Name,
|
|
NTSTATUS Status
|
|
)
|
|
{
|
|
PUNAVAILABLE_SERVER server;
|
|
|
|
LARGE_INTEGER CurrentTime;
|
|
LARGE_INTEGER ExpiryTimeInTicks;
|
|
|
|
PAGED_CODE();
|
|
|
|
server = (PUNAVAILABLE_SERVER)RxAllocatePoolWithTag(
|
|
PagedPool,
|
|
sizeof( *server ) + Name->Length,
|
|
MRXSMB_TRANSPORT_POOLTAG
|
|
);
|
|
|
|
if( server == NULL ) {
|
|
return;
|
|
}
|
|
|
|
RxDbgTrace(0, Dbg, ("SmbCeServerIsUnavailable: Add %wZ %X\n", Name, Status ));
|
|
|
|
server->Name.Buffer = (PUSHORT)(server + 1);
|
|
server->Name.MaximumLength = Name->Length;
|
|
RtlCopyUnicodeString( &server->Name, Name );
|
|
|
|
KeQueryTickCount( &CurrentTime );
|
|
|
|
ExpiryTimeInTicks.QuadPart = (1000 * 1000 * 10) / KeQueryTimeIncrement();
|
|
|
|
ExpiryTimeInTicks.QuadPart = UNAVAILABLE_SERVER_TIME * ExpiryTimeInTicks.QuadPart;
|
|
|
|
server->Time.QuadPart = CurrentTime.QuadPart + ExpiryTimeInTicks.QuadPart;
|
|
|
|
server->Status = Status;
|
|
|
|
ExAcquireResourceExclusive( &UnavailableServerListResource, TRUE );
|
|
InsertHeadList( &UnavailableServerList, &server->ListEntry );
|
|
ExReleaseResource( &UnavailableServerListResource );
|
|
}
|
|
|
|
VOID
|
|
SmbCeDiscardUnavailableServerList(
|
|
)
|
|
{
|
|
PUNAVAILABLE_SERVER server;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(0, Dbg, ("SmbCeDiscardUnavailableServerList\n" ));
|
|
|
|
ExAcquireResourceExclusive( &UnavailableServerListResource, TRUE );
|
|
|
|
while( UnavailableServerList.Flink != &UnavailableServerList ) {
|
|
server = (PUNAVAILABLE_SERVER)UnavailableServerList.Flink;
|
|
RemoveEntryList( &server->ListEntry );
|
|
RxFreePool( server );
|
|
}
|
|
|
|
ExReleaseResource( &UnavailableServerListResource );
|
|
}
|
|
|
|
extern BOOLEAN SetupInProgress;
|
|
|
|
VOID
|
|
MRxSmbLogTransportError(
|
|
PUNICODE_STRING pTransportName,
|
|
PUNICODE_STRING pDomainName,
|
|
NTSTATUS ErrorStatus,
|
|
IN ULONG Id)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reports the error that occurs at binding the browser with the specified transport
|
|
|
|
Arguments:
|
|
|
|
pTransportName - the name of the transport
|
|
|
|
Status - the NT status of the error occured
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
USHORT RemainingLength = ERROR_LOG_MAXIMUM_SIZE - sizeof(IO_ERROR_LOG_PACKET) - 3*sizeof(UNICODE_NULL);
|
|
UNICODE_STRING ErrorLog[3];
|
|
UNICODE_STRING UnicodeStatus;
|
|
UNICODE_STRING TempUnicode;
|
|
ULONG DosError;
|
|
|
|
// strip of the "\device\" at the beginning of transport name to reduce the message length
|
|
pTransportName->Length -= 8*sizeof(UNICODE_NULL);
|
|
|
|
// assume the dos error code won't be larger than 10 digits
|
|
UnicodeStatus.Length = 12 * sizeof(UNICODE_NULL);
|
|
UnicodeStatus.MaximumLength = UnicodeStatus.Length;
|
|
UnicodeStatus.Buffer = RxAllocatePoolWithTag(NonPagedPool,
|
|
UnicodeStatus.Length,
|
|
MRXSMB_TRANSPORT_POOLTAG);
|
|
if (UnicodeStatus.Buffer == NULL) {
|
|
goto FINALY;
|
|
}
|
|
|
|
// use the dos error code to display the status on the event message
|
|
UnicodeStatus.Buffer[0] = L'%';
|
|
UnicodeStatus.Buffer[1] = L'%';
|
|
|
|
DosError = RtlNtStatusToDosError(ErrorStatus);
|
|
|
|
TempUnicode.Length = UnicodeStatus.Length - 2*sizeof(UNICODE_NULL);
|
|
TempUnicode.MaximumLength = UnicodeStatus.MaximumLength - 2*sizeof(UNICODE_NULL);
|
|
TempUnicode.Buffer = &UnicodeStatus.Buffer[2];
|
|
|
|
Status = RtlIntegerToUnicodeString(
|
|
DosError,
|
|
0,
|
|
&TempUnicode);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
goto FINALY;
|
|
}
|
|
|
|
ErrorLog[2].Length = TempUnicode.Length + 2*sizeof(UNICODE_NULL);
|
|
ErrorLog[2].MaximumLength = ErrorLog[2].Length;
|
|
ErrorLog[2].Buffer = UnicodeStatus.Buffer;
|
|
|
|
RemainingLength -= ErrorLog[2].Length;
|
|
|
|
if (pDomainName->Length + pTransportName->Length > RemainingLength) {
|
|
// the length error log message is limited by the ERROR_LOG_MAXIMUM_SIZE. This restriction can be
|
|
// enfored by truncating the doamin and transport names so that both of them can get chance to be
|
|
// displayed on the EvenLog.
|
|
|
|
ErrorLog[0].Length = pDomainName->Length < RemainingLength / 2 ?
|
|
pDomainName->Length :
|
|
RemainingLength / 2;
|
|
|
|
RemainingLength -= ErrorLog[0].Length;
|
|
|
|
ErrorLog[1].Length = pTransportName->Length < RemainingLength ?
|
|
pTransportName->Length :
|
|
RemainingLength;
|
|
} else {
|
|
ErrorLog[0].Length = pDomainName->Length;
|
|
ErrorLog[1].Length = pTransportName->Length;
|
|
}
|
|
|
|
ErrorLog[0].MaximumLength = ErrorLog[0].Length;
|
|
ErrorLog[1].MaximumLength = ErrorLog[1].Length;
|
|
|
|
ErrorLog[0].Buffer = pDomainName->Buffer;
|
|
|
|
// strip of the "\device\" at the beginning of transport name
|
|
ErrorLog[1].Buffer = &pTransportName->Buffer[8];
|
|
|
|
RxLogEventWithAnnotation (
|
|
MRxSmbDeviceObject,
|
|
Id,
|
|
ErrorStatus,
|
|
NULL,
|
|
0,
|
|
ErrorLog,
|
|
3
|
|
);
|
|
|
|
FINALY:
|
|
|
|
// restore the length with "\device\" at the beginning of transport name
|
|
pTransportName->Length += 8*sizeof(UNICODE_NULL);
|
|
|
|
if (UnicodeStatus.Buffer != NULL) {
|
|
RxFreePool(UnicodeStatus.Buffer);
|
|
}
|
|
|
|
if (!SetupInProgress && ErrorStatus == STATUS_DUPLICATE_NAME) {
|
|
IoRaiseInformationalHardError(ErrorStatus, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
SmbMRxNotifyChangesToNetBt(
|
|
IN TDI_PNP_OPCODE PnPOpcode,
|
|
IN PUNICODE_STRING DeviceName,
|
|
IN PWSTR MultiSZBindList)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine should not be part of rdr. It has been introduced into this
|
|
component to overcome current limitations in NetBt. The NetBt transport
|
|
exposes two kinds of devices -- the traditional NetBt device and the
|
|
new non Netbios device which make use of the NetBt framing code without the
|
|
name resolution aspects of it. The current implementation in NetBt exposes
|
|
the former devices on a per adapter basis while the second category of device
|
|
is exposed on a global basis ( one for all the adapters ). This poses
|
|
problems in disabling/enabling srv on a given adapter.
|
|
|
|
The correct solution is to expose the second category of devices on a per
|
|
adapter basis. Till it is done this workaround is reqd. With this workaround
|
|
whenever the server is notified of any changes to the binding string it turns
|
|
around and notifies the NetBt transport about these changes.
|
|
|
|
This routine is based upon the following assumptions ...
|
|
|
|
1) The notification from TDI is not done at raised IRQL.
|
|
|
|
2) The thread on which this notification occurs has enough access rights.
|
|
|
|
3) The notification to NetBt is done asynchronously with srv's reaction
|
|
to the change. The srv handles the PNP notification by passing it off to
|
|
user mode and have it come through the server service.
|
|
|
|
Arguments:
|
|
|
|
PNPOpcode - the PNP opcode
|
|
|
|
DeviceName - the transport for which this opcode is intended
|
|
|
|
MultiSZBindList - the binding list
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE NetbioslessSmbHandle;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
UNICODE_STRING NetbioslessSmbName = {36,36, L"\\device\\NetbiosSmb"};
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&NetbioslessSmbName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL );
|
|
|
|
Status = ZwCreateFile (
|
|
&NetbioslessSmbHandle,
|
|
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, // desired access
|
|
&ObjectAttributes, // object attributes
|
|
&IoStatusBlock, // returned status information
|
|
NULL, // block size (unused)
|
|
0, // file attributes
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, // share access
|
|
FILE_CREATE, // create disposition
|
|
0, // create options
|
|
NULL, // EA buffer
|
|
0 // EA length
|
|
);
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
NETBT_SMB_BIND_REQUEST NetBtNotificationParameters;
|
|
|
|
NetBtNotificationParameters.RequestType = SMB_CLIENT;
|
|
NetBtNotificationParameters.PnPOpCode = PnPOpcode;
|
|
NetBtNotificationParameters.pDeviceName = DeviceName;
|
|
NetBtNotificationParameters.MultiSZBindList = MultiSZBindList;
|
|
|
|
Status = ZwDeviceIoControlFile(
|
|
NetbioslessSmbHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
IOCTL_NETBT_SET_SMBDEVICE_BIND_INFO,
|
|
&NetBtNotificationParameters,
|
|
sizeof(NetBtNotificationParameters),
|
|
NULL,
|
|
0);
|
|
|
|
Status = ZwClose(NetbioslessSmbHandle);
|
|
}
|
|
}
|
|
|
|
|