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