2025-04-27 07:49:33 -04:00

706 lines
20 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:
transport.c
Abstract:
This module implements all transport related functions in the SMB connection engine
--*/
#include "precomp.h"
#pragma hdrstop
#include "ntddbrow.h"
SMBCE_TRANSPORTS MRxSmbTransports;
RXDT_DefineCategory(TRANSPRT);
#define Dbg (DEBUG_TRACE_TRANSPRT)
extern VOID
SmbCePnpBindBrowser(PUNICODE_STRING pTransportName);
VOID SmbCeAddTransport(PSMBCE_TRANSPORT pNewTransport)
/*++
Routine Description:
Adds a transport to the list of available transports and
increment the transport count. The list is ordered by the
transport priority.
Returns:
Nothing
Notes:
--*/
{
KIRQL SavedIrql;
PLIST_ENTRY pListEntry,pPreviousListEntry;
PSMBCE_TRANSPORT pTransport;
KeAcquireSpinLock(&MRxSmbTransports.Lock,&SavedIrql);
MRxSmbTransports.Count++;
pPreviousListEntry = &MRxSmbTransports.ListHead;;
pListEntry = MRxSmbTransports.ListHead.Flink;
while (pListEntry != &MRxSmbTransports.ListHead) {
pTransport = (PSMBCE_TRANSPORT)CONTAINING_RECORD(
pListEntry,
SMBCE_TRANSPORT,
TransportsList);
if (pTransport->Priority > pNewTransport->Priority) {
break;
} else {
pPreviousListEntry = pListEntry;
pListEntry = pListEntry->Flink;
}
}
InsertHeadList(pPreviousListEntry,
&pNewTransport->TransportsList);
KeReleaseSpinLock(&MRxSmbTransports.Lock,SavedIrql);
}
VOID SmbCeRemoveTransport(PSMBCE_TRANSPORT pTransport)
/*++
Routine Description:
Removes a transport from the list of available transports
Returns:
Nothing
Notes:
--*/
{
KIRQL SavedIrql;
KeAcquireSpinLock(&MRxSmbTransports.Lock,&SavedIrql);
MRxSmbTransports.Count--;
RemoveEntryList(&pTransport->TransportsList);
KeReleaseSpinLock(&MRxSmbTransports.Lock,SavedIrql);
}
NTSTATUS
MRxIfsInitializeTransport()
/*++
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);
InitializeListHead(&MRxSmbTransports.ListHead);
MRxSmbTransports.Count = 0;
return STATUS_SUCCESS;
}
NTSTATUS
MRxIfsUninitializeTransport()
/*++
Routine Description:
This routine uninitializes the transport related data structures
Notes:
--*/
{
PSMBCE_TRANSPORT pTransport;
KIRQL SavedIrql;
ULONG TransportCount = 0;
PLIST_ENTRY pTransportEntry;
KeAcquireSpinLock(&MRxSmbTransports.Lock,&SavedIrql);
TransportCount = MRxSmbTransports.Count;
pTransportEntry = MRxSmbTransports.ListHead.Flink;
InitializeListHead(&MRxSmbTransports.ListHead);
MRxSmbTransports.Count = 0;
KeReleaseSpinLock(&MRxSmbTransports.Lock,SavedIrql);
while (TransportCount > 0) {
pTransport = (PSMBCE_TRANSPORT)CONTAINING_RECORD(
pTransportEntry,
SMBCE_TRANSPORT,
TransportsList);
pTransportEntry = pTransportEntry->Flink;
TransportCount--;
ASSERT(pTransport->SwizzleCount == 1);
RxCeDeregisterClientAddress(pTransport->hAddress);
RxFreePool(pTransport);
}
return STATUS_SUCCESS;
}
PSMBCE_TRANSPORT
SmbCeGetNextTransport(PSMBCE_TRANSPORT pTransport)
/*++
Routine Description:
This routine is used to enumerate the transports used by a mini redirector
Arguments:
pTransport - the current transport instance ( can be NULL in which case the
first transport is returned )
Return Value:
a valid PSMBCE_TRANSPORT if one exists otherwise NULL
Notes:
The lock on the list of transports should be held for very small intervals of time.
Therefore the lock is acquired and released during every step of the enumeration.
This allows multiple threads to make progress. This behaviour is desirable since
the typical action is to initiate a connection engine operation on accquiring a
transport ( a long term operation )
This routine returns referenced transport instances. It is the callers responsibility
to dereference it.
--*/
{
KIRQL SavedIrql;
PLIST_ENTRY pEntry;
PSMBCE_TRANSPORT pNextTransport;
KeAcquireSpinLock(&MRxSmbTransports.Lock,&SavedIrql);
if (pTransport == NULL) {
pEntry = MRxSmbTransports.ListHead.Flink;
} else {
pEntry = pTransport->TransportsList.Flink;
}
if (pEntry != &MRxSmbTransports.ListHead) {
do {
pNextTransport = (PSMBCE_TRANSPORT)CONTAINING_RECORD(
pEntry,
SMBCE_TRANSPORT,
TransportsList);
pEntry = pEntry->Flink;
} while (pNextTransport != NULL &&
!pNextTransport->Active &&
(pEntry != &MRxSmbTransports.ListHead));
if ((pNextTransport != NULL) && pNextTransport->Active) {
SmbCeReferenceTransport(pNextTransport);
} else {
pNextTransport = NULL;
}
} else {
pNextTransport = NULL;
}
KeReleaseSpinLock(&MRxSmbTransports.Lock,SavedIrql);
return pNextTransport;
}
PSMBCE_TRANSPORT
SmbCeFindTransport(RXCE_TRANSPORT_HANDLE hRxCeTransport)
/*++
Routine Description:
This routine maps a RXCE_TRANSPORT_HANDLE to the appropriate
PSMBCE_TRANSPORT instance
Arguments:
hTransport - the RxCe transport handle
Return Value:
a valid PSMBCE_TRANSPORT if one exists otherwise NULL
Notes:
--*/
{
KIRQL SavedIrql;
PLIST_ENTRY pEntry;
PSMBCE_TRANSPORT pTransport = NULL;
KeAcquireSpinLock(&MRxSmbTransports.Lock,&SavedIrql);
pEntry = MRxSmbTransports.ListHead.Flink;
while (pEntry != &MRxSmbTransports.ListHead) {
pTransport = (PSMBCE_TRANSPORT)CONTAINING_RECORD(
pEntry,
SMBCE_TRANSPORT,
TransportsList);
if (pTransport->hTransport == hRxCeTransport) {
SmbCeReferenceTransport(pTransport);
break;
} else {
pEntry = pEntry->Flink;
}
}
if (pEntry == &MRxSmbTransports.ListHead) {
pTransport = NULL;
}
KeReleaseSpinLock(&MRxSmbTransports.Lock,SavedIrql);
return pTransport;
}
NTSTATUS
SmbCeInitializeServerTransport(
PSMBCEDB_SERVER_ENTRY pServerEntry)
/*++
Routine Description:
This routine initializes the transport information corresponding to a server
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:
Currently, only connection oriented transports are handled.
--*/
{
NTSTATUS Status;
SMBCEDB_SERVER_TYPE ServerType = SmbCeGetServerType(pServerEntry);
PSMBCE_SERVER_TRANSPORT pServerTransport = NULL;
if (pServerEntry->pTransport != NULL) {
SmbCeUninitializeServerTransport(pServerEntry);
}
Status = VctInstantiateServerTransport(pServerEntry,&pServerTransport);
if (Status == STATUS_SUCCESS) {
ASSERT(pServerTransport != NULL);
SmbCeAcquireSpinLock();
pServerTransport->SwizzleCount = 1;
pServerEntry->pTransport = pServerTransport;
SmbCeReleaseSpinLock();
}
return Status;
}
NTSTATUS
SmbCeUninitializeServerTransport(
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
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 = STATUS_SUCCESS;
SMBCEDB_SERVER_TYPE ServerType = SmbCeGetServerType(pServerEntry);
PSMBCE_SERVER_TRANSPORT pServerTransport = pServerEntry->pTransport;
if (pServerTransport != NULL) {
KEVENT RundownEvent;
KeInitializeEvent(&RundownEvent,NotificationEvent,FALSE);
SmbCeAcquireSpinLock();
pServerTransport->State = SMBCEDB_MARKED_FOR_DELETION;
pServerTransport->pRundownEvent = &RundownEvent;
SmbCeReleaseSpinLock();
SmbCeDereferenceServerTransport(pServerEntry);
KeWaitForSingleObject(
&RundownEvent,
Executive,
KernelMode,
FALSE,
NULL );
ASSERT(pServerEntry->pTransport == NULL);
}
return Status;
}
NTSTATUS
SmbCepReferenceServerTransport(
PSMBCEDB_SERVER_ENTRY pServerEntry)
/*++
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;
PSMBCE_SERVER_TRANSPORT pServerTransport;
SmbCeAcquireSpinLock();
pServerTransport = pServerEntry->pTransport;
if (pServerTransport->State == SMBCEDB_ACTIVE) {
pServerTransport->SwizzleCount++;
Status = STATUS_SUCCESS;
} else {
Status = STATUS_CONNECTION_DISCONNECTED;
}
SmbCeReleaseSpinLock();
return Status;
}
NTSTATUS
SmbCepDereferenceServerTransport(
PSMBCEDB_SERVER_ENTRY pServerEntry)
/*++
Routine Description:
This routine dereferences 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 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.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PSMBCE_SERVER_TRANSPORT pServerTransport;
BOOLEAN FinalizeServerTransport = FALSE;
SmbCeAcquireSpinLock();
pServerTransport = pServerEntry->pTransport;
pServerTransport->SwizzleCount--;
FinalizeServerTransport = (pServerTransport->SwizzleCount == 0);
SmbCeReleaseSpinLock();
if (FinalizeServerTransport) {
SmbCeAcquireSpinLock();
pServerEntry->pTransport = NULL;
SmbCeReleaseSpinLock();
if (pServerTransport->pRundownEvent != NULL) {
KeSetEvent( pServerTransport->pRundownEvent, 0, FALSE );
}
pServerTransport->pDispatchVector->TearDown(pServerTransport);
}
return Status;
}
NTSTATUS
MRxIfsTransportUpdateHandler(
PRXCE_TRANSPORT_NOTIFICATION pTransportNotification)
/*++
Routine Description:
This routine is the callback handler that is invoked by the RxCe when transports
are either enabled or disabled. It is further possible to extend this routine
to provide feedback regarding the transports which can aid transport selection
Arguments:
pTransportNotification - information pertaining to the transport
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. No feedback for
transport selection has been implemented as yet.
--*/
{
NTSTATUS Status;
RXCE_TRANSPORT_HANDLE hTransport;
RXCE_TRANSPORT_EVENT TransportEvent;
PRXCE_TRANSPORT_PROVIDER_INFO pProviderInfo;
PUNICODE_STRING pTransportName;
hTransport = pTransportNotification->hTransport;
pProviderInfo = pTransportNotification->pProviderInformation;
pTransportName = pTransportNotification->pTransportName;
ASSERT(IoGetCurrentProcess() == RxGetRDBSSProcess());
switch (pTransportNotification->TransportEvent) {
case TransportActivated:
{
ULONG Priority;
BOOLEAN fBindToTransport = FALSE;
ASSERT(pProviderInfo != NULL);
// 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) {
// No transports were specfied. There are two options -- either
// all the available transports can be used or none. Currently
// the later option is implemented.
Status = STATUS_SUCCESS;
} else {
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_DEBUG {
if (!fBindToTransport) {
DbgPrint("Ignoring Transport %ws\n",pTransportName->Buffer);
}
}
if (fBindToTransport &&
(pProviderInfo->ServiceFlags & TDI_SERVICE_CONNECTION_MODE) &&
(pProviderInfo->ServiceFlags & TDI_SERVICE_ERROR_FREE_DELIVERY)) {
// 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;
RXCE_ADDRESS_HANDLE hLocalAddress;
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_NAMESIZE;
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_NAMESIZE - OemServerName.Length);
OemServerName.Buffer[NETBIOS_NAMESIZE - 1] = '\0';
// Register the Transport address for this mini redirector with the connection
// engine.
Status = RxCeRegisterClientAddress(
hTransport,
pTransportAddress,
&MRxSmbVctAddressEventHandler,
&SmbCeContext,
&hLocalAddress);
if (NT_SUCCESS(Status)) {
PSMBCE_TRANSPORT pTransport;
pTransport = SmbCeFindTransport(hTransport);
if (pTransport == NULL) {
pTransport = RxAllocatePoolWithTag(
NonPagedPool,
sizeof(SMBCE_TRANSPORT),
MRXSMB_TRANSPORT_POOLTAG);
if (pTransport != NULL) {
RxDbgTrace( 0, Dbg, ("MRxSmbTransportUpdateHandler: Adding new transport\n"));
pTransport->hTransport = hTransport;
pTransport->hAddress = hLocalAddress;
pTransport->Active = TRUE;
pTransport->Priority = Priority;
pTransport->SwizzleCount = 1;
SmbCeAddTransport(pTransport);
//SmbCePnpBindBrowser(pTransportName); // egb
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
SmbCeDereferenceTransport(pTransport);
ASSERT(!"Duplicate Transport binding Notification");
RxDbgTrace( 0, Dbg, ("MRxSmbTransportUpdateHandler: Duplicate Transport indication, Error In RxCe\n"));
}
} else {
RxDbgTrace( 0, Dbg, ("MRxSmbTransportUpdateHandler: Address registration failed %lx\n",Status));
}
}
} else {
// The connection capabilities do not match the capabilities required.
// Disregard the transport in future considerations
RxDbgTrace( 0, Dbg, ("MRxSmbTransportUpdateHandler: Ignoring transport %lx because of insufficient capabilities\n",hTransport));
}
}
break;
case TransportDeactivated:
{
PSMBCE_TRANSPORT pTransport;
pTransport = SmbCeFindTransport(hTransport);
DbgPrint("****** TRANSPORT (%lx) being invalidated\n",pTransport);
if (pTransport != NULL) {
// 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);
// Deregister the address associated with the transport and uninitialize it
RxCeDeregisterClientAddress(pTransport->hAddress);
RxFreePool(pTransport);
}
DbgPrint("****** TRANSPORT (%lx) invalidated\n",pTransport);
}
break;
case TransportStatusUpdate:
break;
default:
break;
}
return Status;
}