NT4/private/ntos/rdr/tdi.c
2020-09-30 17:12:29 +02:00

5148 lines
139 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) 1990 Microsoft Corporation
Module Name:
tdi.c
Abstract:
This module implements all of the routines that interface with the TDI
transport for NT
Author:
Larry Osterman (LarryO) 21-Jun-1990
Revision History:
21-Jun-1990 LarryO
Created
--*/
#include "precomp.h"
#pragma hdrstop
#include <nbtioctl.h>
#ifdef RDR_PNP_POWER
#include "ntddbrow.h"
#endif
#define TDI_CONNECT_IN_PARALLEL 1
#define PRIMARY_TRANSPORT_CHECK 1
//
// Wait for 2 seconds before polling on thread deletion.
//
#define RDR_TDI_POLL_TIMEOUT (2 * 1000)
LARGE_INTEGER
RdrTdiPollTimeout = {0};
LARGE_INTEGER
RdrTdiConnectTimeout = {0};
LARGE_INTEGER
RdrTdiDisconnectTimeout = {0};
//
//
// Variables used for transport package
//
//
//
// Resource controling transport list. Claim this resource for shared
// access when walking the list, claim it for exclusive access when
// modifying the transport chain.
//
ERESOURCE
RdrTransportResource = {0};
#if MAGIC_BULLET
BOOLEAN RdrEnableMagic = FALSE;
PFILE_OBJECT
MagicBulletFileObject = NULL;
PDEVICE_OBJECT
MagicBulletDeviceObject = NULL;
#endif
typedef struct _RDR_TDI_CONNECT_CONTEXT {
LIST_ENTRY NextContext;
PLIST_ENTRY ListHead;
PIRP Irp;
KEVENT ConnectComplete;
PRDR_CONNECTION_CONTEXT ConnectionContext;
NTSTATUS ConnectionStatus;
ULONG QualityOfService; // QOS of transport
TDI_CONNECTION_INFORMATION RemoteConnectionInformation;
TDI_CONNECTION_INFORMATION ConnectedConnectionInformation;
CHAR TransportAddressBuffer[1]; // start of the transport address
} RDR_TDI_CONNECT_CONTEXT, *PRDR_TDI_CONNECT_CONTEXT;
//
//
// Forward definitions of local routines.
//
NTSTATUS
RdrQueryProviderInformation(
IN PFILE_OBJECT TransportObject,
OUT PTDI_PROVIDER_INFO ProviderInfo
);
NTSTATUS
RdrQueryAdapterStatus(
IN PFILE_OBJECT TransportObject,
OUT PADAPTER_STATUS AdapterStatus
);
NTSTATUS
RdrpTdiCreateAddress (
IN PTRANSPORT Transport
);
DBGSTATIC
NTSTATUS
RdrpTdiSetEventHandler (
IN PDEVICE_OBJECT DeviceObject,
IN PFILE_OBJECT FileObject,
IN ULONG EventType,
IN PVOID EventHandler
);
DBGSTATIC
VOID
RdrpTdiFreeTransport (
IN PTRANSPORT Transport
);
DBGSTATIC
NTSTATUS
SubmitTdiRequest (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
RdrTdiOpenConnection (
IN PTRANSPORT Transport,
IN PVOID ConnectionContext,
OUT PHANDLE ConnectionHandle
);
NTSTATUS
RdrTdiDisassociateAddress(
IN PIRP Irp OPTIONAL,
IN PFILE_OBJECT ConnectionObject
);
NTSTATUS
RdrTdiAssociateAddress(
IN PIRP Irp OPTIONAL,
IN PTRANSPORT Transport,
IN PFILE_OBJECT ConnectionObject
);
NTSTATUS
RdrDoTdiConnect(
IN PIRP Irp OPTIONAL,
IN PFILE_OBJECT ConnectionObject,
IN PTRANSPORT_ADDRESS RemoteTransport,
OUT PTRANSPORT_ADDRESS ConnectedTransport,
IN ULONG RemoteTransportAddresLength,
IN ULONG ConnectedTransportAddressLength
);
DBGSTATIC
NTSTATUS
CompleteTdiRequest (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
RdrRemoveConnectionsOnServerForTransport(
IN PSERVERLISTENTRY Server,
IN PVOID Ctx
);
#define RDR_COMPUTE_TRANSPORT_ADDRESS_LENGTH( _ta ) \
((PTRANSPORT_ADDRESS)(_ta))->Address[0].AddressLength + FIELD_OFFSET( TRANSPORT_ADDRESS, Address[0].Address )
// VOID
// RdrReferenceTransportForConnection(
// IN PTRANSPORT Transport
// );
#define RdrReferenceTransportForConnection( _Transport ) { \
InterlockedIncrement(&(_Transport)->ConnectionReferenceCount); \
dprintf(DPRT_TRANSPORT, ("Reference transport %lx (%wZ) for connection, now at %lx\n", \
(_Transport), &(_Transport)->TransportName, \
(_Transport)->ConnectionReferenceCount)); \
}
// VOID
// RdrDereferenceTransportForConnection(
// IN PTRANSPORT Transport
// );
#define RdrDereferenceTransportForConnection( _Transport ) { \
ASSERT ((_Transport)->NonPagedTransport->ReferenceCount > 0); \
ASSERT ((_Transport)->NonPagedTransport->ReferenceCount >= \
(_Transport)->ConnectionReferenceCount); \
InterlockedDecrement(&(_Transport)->ConnectionReferenceCount); \
dprintf(DPRT_TRANSPORT, ("Dereference transport %lx (%wZ) for connection, now at %lx\n", \
(_Transport), &(_Transport)->TransportName, \
(_Transport)->ConnectionReferenceCount)); \
}
VOID
RdrMarkTransportConnectionValid(
IN PSERVERLISTENTRY Server,
IN PRDR_CONNECTION_CONTEXT ConnectionContext
);
NTSTATUS
RdrDereferenceTransportConnectionNoRelease(
IN PSERVERLISTENTRY Server
);
VOID
RdrResetTransportConnectionValid(
IN PSERVERLISTENTRY Server
);
VOID
RdrFreeConnectionContext(
IN PRDR_TDI_CONNECT_CONTEXT ConnectionContext
);
NTSTATUS
CompleteTdiConnect (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Ctx
);
NTSTATUS
RdrSynchronousTdiConnectToServer (
IN PIRP Irp OPTIONAL,
IN PUNICODE_STRING ServerName,
IN PSERVERLISTENTRY Server
);
NTSTATUS
RdrDoTdiDisconnect(
IN PIRP Irp OPTIONAL,
IN PFILE_OBJECT ConnectionObject
);
#if TDI_CONNECT_IN_PARALLEL
NTSTATUS
RdrAllocateConnectionContext(
IN PIRP Irp OPTIONAL,
IN PTRANSPORT Transport,
IN PSERVERLISTENTRY Server,
IN PUNICODE_STRING ServerName,
OUT PRDR_TDI_CONNECT_CONTEXT *ConnectionContext
);
#endif
#if PRIMARY_TRANSPORT_CHECK
VOID
RdrCheckPrimaryTransport(
IN PUNICODE_STRING ServerName
);
#endif
extern BOOLEAN
RdrIsLoopBack(PUNICODE_STRING ServerName);
extern ULONG
RdrComputeTransportAddressSize(
PUNICODE_STRING ServerName);
extern NTSTATUS
RdrBuildNetbiosAddress(
IN PTRANSPORT_ADDRESS TransportAddress,
IN ULONG TransportAddressLength,
IN PUNICODE_STRING ServerName
);
extern NTSTATUS
RdrpTranslateNetbiosNameToIpAddress(
IN OEM_STRING *pName,
OUT ULONG *pRemoteIpAddress
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, RdrpTdiAllocateTransport)
#pragma alloc_text(PAGE, RdrRemoveConnectionsTransport)
#pragma alloc_text(PAGE, RdrRemoveConnectionsOnServerForTransport)
#pragma alloc_text(PAGE, RdrUnbindFromAllTransports)
#pragma alloc_text(PAGE, RdrDereferenceTransportByName)
#pragma alloc_text(PAGE, RdrFindTransport)
#pragma alloc_text(PAGE, RdrEnumerateTransports)
#pragma alloc_text(PAGE, RdrTdiSendDatagramOnAllTransports)
#pragma alloc_text(PAGE, RdrInitializeTransportConnection)
#pragma alloc_text(PAGE, RdrpTdiFreeTransport)
#pragma alloc_text(PAGE, RdrTdiConnectToServer)
#pragma alloc_text(PAGE, RdrSynchronousTdiConnectToServer)
#pragma alloc_text(PAGE, RdrTdiConnectOnTransport)
#pragma alloc_text(PAGE, RdrTdiDisconnect)
#pragma alloc_text(PAGE, SubmitTdiRequest)
#pragma alloc_text(PAGE, RdrTdiAssociateAddress)
#pragma alloc_text(PAGE, RdrTdiDisassociateAddress)
#pragma alloc_text(PAGE, RdrTdiOpenConnection)
#pragma alloc_text(PAGE, RdrDoTdiConnect)
#pragma alloc_text(PAGE, RdrQueryProviderInformation)
#pragma alloc_text(PAGE, RdrQueryAdapterStatus)
#pragma alloc_text(PAGE, RdrpTdiCreateAddress)
#pragma alloc_text(PAGE, RdrpTdiSetEventHandler)
#pragma alloc_text(PAGE, RdrBuildNetbiosAddress)
#pragma alloc_text(PAGE, RdrComputeTransportAddressSize)
#pragma alloc_text(PAGE, RdrpTranslateNetbiosNameToIpAddress)
#pragma alloc_text(PAGE, RdrBuildTransportAddress)
#pragma alloc_text(INIT, RdrpInitializeTdi)
#pragma alloc_text(PAGE, RdrFreeConnectionContext)
#pragma alloc_text(PAGE, RdrDoTdiDisconnect)
#if TDI_CONNECT_IN_PARALLEL
#pragma alloc_text(PAGE, RdrAllocateConnectionContext)
#endif
#if PRIMARY_TRANSPORT_CHECK
#pragma alloc_text(PAGE, RdrCheckPrimaryTransport)
#endif
#pragma alloc_text(PAGE2VC, CompleteTdiConnect)
#pragma alloc_text(PAGE2VC, RdrDereferenceTransport)
#pragma alloc_text(PAGE2VC, RdrReferenceTransportConnection)
#pragma alloc_text(PAGE2VC, RdrDereferenceTransportConnectionNoRelease)
#pragma alloc_text(PAGE2VC, RdrDereferenceTransportConnectionForThread)
#pragma alloc_text(PAGE2VC, RdrResetTransportConnectionValid)
#pragma alloc_text(PAGE2VC, RdrSetConnectionFlag)
#pragma alloc_text(PAGE2VC, RdrResetConnectionFlag)
#pragma alloc_text(PAGE2VC, RdrMarkTransportConnectionValid)
#pragma alloc_text(PAGE2VC, CompleteTdiRequest)
#pragma alloc_text(PAGE2VC, RdrQueryConnectionInformation)
#endif
#ifdef RDR_PNP_POWER
typedef struct {
WORK_QUEUE_ITEM WorkItem;
UNICODE_STRING DeviceName; // New device name
ULONG QualityOfService;
BOOLEAN DoSignal; // Should Event be signaled upon completion?
KEVENT Event; // Event signaled upon completion
NTSTATUS Status; // Status code of completion
} *PNP_BROWSER_REQUEST;
/*
* This routine is called in the system context
*/
VOID
PnpBindBrowser( PVOID Ctx )
{
PNP_BROWSER_REQUEST Context = Ctx;
HANDLE browserHandle;
PLMDR_REQUEST_PACKET drrp = NULL;
ULONG size = sizeof( *drrp ) + Context->DeviceName.Length;
IO_STATUS_BLOCK ioStatusBlock;
OBJECT_ATTRIBUTES objectAttributes;
UNICODE_STRING browserDeviceName;
NTSTATUS browserStatus;
Context->Status = RdrpTdiAllocateTransport( &Context->DeviceName, Context->QualityOfService );
//
// We might have failed the above call, but that does not mean that the
// browser is going to fail it as well. So, we bravely continue on.
//
//
// Open up a handle to the browser
//
RtlInitUnicodeString( &browserDeviceName, DD_BROWSER_DEVICE_NAME_U);
InitializeObjectAttributes(
&objectAttributes,
&browserDeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
browserStatus = ZwCreateFile(
&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
);
if( NT_SUCCESS( browserStatus ) ) {
browserStatus = ioStatusBlock.Status;
}
if( !NT_SUCCESS( browserStatus ) ) {
browserHandle = NULL;
goto Cleanup;
}
drrp = ALLOCATE_POOL( NonPagedPool, size, POOL_PNP_DATA );
if( drrp == NULL ) {
browserStatus = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
//
// Tell the browser to bind to this new transport
//
RtlZeroMemory( drrp, size );
drrp->Version = LMDR_REQUEST_PACKET_VERSION;
drrp->Parameters.Bind.TransportNameLength = Context->DeviceName.Length;
RtlCopyMemory( drrp->Parameters.Bind.TransportName, Context->DeviceName.Buffer, Context->DeviceName.Length );
browserStatus = ZwDeviceIoControlFile(
browserHandle, // FileHandle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&ioStatusBlock, // IoStatusBlock
IOCTL_LMDR_BIND_TO_TRANSPORT, // IoControlCode
drrp, // InputBuffer
size, // InputBufferLength
NULL, // OutputBuffer
0 // OutputBufferLength
);
if( NT_SUCCESS( browserStatus ) ) {
browserStatus = ioStatusBlock.Status;
}
Cleanup:
if (browserHandle != NULL)
ZwClose( browserHandle );
if (drrp != NULL)
FREE_POOL( drrp );
//
// If we successfully bound to the transport, then we'll return the
// browser status. Otherwise, our status is more important
//
if (NT_SUCCESS(Context->Status)) {
Context->Status = browserStatus;
}
if( Context->DoSignal ) {
KeSetEvent( &Context->Event, 0, FALSE );
}
return;
}
VOID
RdrTdiBindCallback(
IN PUNICODE_STRING DeviceName
)
/*++
Routine Description:
TDI calls this routine whenever a transport creates a new device object. If
the DeviceName is a device in our binding list, a work item is queued over
to the DelayedWorkQueue to initiate a bind to the transport.
Arguments:
DeviceName - the name of the newly created device object
--*/
{
PWSTR p;
ULONG qualityOfService = 65536;
PNP_BROWSER_REQUEST Context;
if( RdrTransportBindingList == NULL ) {
return;
}
//
// Check to see if this is one of the devices we're interested in.
// If it is, bind to the transport
//
for( p = RdrTransportBindingList; *p; p += wcslen(p) + 1 ) {
--qualityOfService;
if( wcslen( p ) != DeviceName->Length / sizeof( WCHAR ) ) {
continue;
}
if( _wcsnicmp( p, DeviceName->Buffer, DeviceName->Length / sizeof( WCHAR ) ) != 0 ) {
continue;
}
//
// DeviceName is in our binding list! We need to bind the redirector
// as well as the browser to this device. Use a kernel worker thread
// to ensure that we're in the system context so the open of the browser
// will succeed.
//
Context = ALLOCATE_POOL( NonPagedPool,
sizeof( *Context ) + DeviceName->Length,
POOL_PNP_DATA
);
if( Context != NULL ) {
Context->DeviceName.Buffer = (PUSHORT)(Context + 1);
Context->DeviceName.Length = DeviceName->Length;
Context->DeviceName.MaximumLength = DeviceName->MaximumLength;
Context->QualityOfService = qualityOfService;
RtlCopyMemory( Context->DeviceName.Buffer, DeviceName->Buffer, DeviceName->Length );
//
// Since we will be opening handles, we must ensure we are in the system
// process. If not, queue an item over to a kernel worker thread. If we
// are in the system process, just go ahead and do the work now.
//
if (PsGetCurrentProcess() != RdrFspProcess) {
//
// Send the request over to a kernel worker thread, and wait for completion
//
KeInitializeEvent( &Context->Event, NotificationEvent, FALSE );
Context->DoSignal = TRUE;
ExInitializeWorkItem( &Context->WorkItem, PnpBindBrowser, Context );
RdrQueueWorkItem( &Context->WorkItem, DelayedWorkQueue );
KeWaitForSingleObject( &Context->Event,
Executive,
KernelMode,
FALSE,
NULL
);
} else {
//
// Go ahead and do the binding right now
//
Context->DoSignal = FALSE;
PnpBindBrowser( Context );
}
FREE_POOL( Context );
}
break;
}
}
VOID
RdrTdiUnbindCallback(
IN PUNICODE_STRING DeviceName
)
/*++
Routine Description:
TDI calls this routine whenever a transport deletes a device object
Arguments:
DeviceName = the name of the deleted device object
--*/
{
}
NTSTATUS
RdrRegisterForPnpNotifications()
{
NTSTATUS status = STATUS_SUCCESS;
if( RdrTdiNotificationHandle == NULL ) {
status = TdiRegisterNotificationHandler (
RdrTdiBindCallback,
RdrTdiUnbindCallback,
&RdrTdiNotificationHandle );
}
return status;
}
NTSTATUS
RdrDeRegisterForPnpNotifications()
{
NTSTATUS status = STATUS_SUCCESS;
if( RdrTdiNotificationHandle != NULL ) {
status = TdiDeregisterNotificationHandler( RdrTdiNotificationHandle );
if( NT_SUCCESS( status ) ) {
RdrTdiNotificationHandle = NULL;
}
}
return status;
}
#endif
NTSTATUS
RdrpTdiAllocateTransport (
PUNICODE_STRING TransportName,
ULONG QualityOfService
)
/*++
Routine Description:
This routine will allocate a transport descriptor and bind the redirector
to the transport.
.
Arguments:
PUNICODE_STRING TransportName - Supplies the name of the transport provider
ULONG QualityOfService - Supplies the quality of service of the tranport.
Return Value:
NTSTATUS - Status of operation.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PTRANSPORT NewTransport = NULL;
PNONPAGED_TRANSPORT NonPagedTransport = NULL;
BOOLEAN TransportAcquired = FALSE;
BOOLEAN TransportInitialized = FALSE;
PLIST_ENTRY PreviousEntry;
PLIST_ENTRY NextEntry;
PTRANSPORT NextTransport;
PAGED_CODE();
ExAcquireResourceExclusive(&RdrTransportResource, TRUE);
TransportAcquired = TRUE;
try {
dprintf(DPRT_TRANSPORT, ("RdrpTdiAllocateTransport: %wZ\n", TransportName));
NewTransport = RdrFindTransport(TransportName);
if (NewTransport == NULL) {
NewTransport = ALLOCATE_POOL(PagedPool, sizeof(TRANSPORT), POOL_TRANSPORT);
if (NewTransport == NULL) {
try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
}
NewTransport->Signature = STRUCTURE_SIGNATURE_TRANSPORT;
NewTransport->Size = sizeof(TRANSPORT);
NewTransport->TransportName.Buffer = NULL;
NewTransport->InitEvent = NULL;
NonPagedTransport = NewTransport->NonPagedTransport = ALLOCATE_POOL(NonPagedPool, sizeof(NONPAGED_TRANSPORT), POOL_NONPAGED_TRANSPORT);
if (NonPagedTransport == NULL) {
try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
}
NonPagedTransport->PagedTransport = NewTransport;
NonPagedTransport->Signature = STRUCTURE_SIGNATURE_NONPAGED_TRANSPORT;
NonPagedTransport->Size = sizeof(NONPAGED_TRANSPORT);
NewTransport->InitEvent = ALLOCATE_POOL(NonPagedPool, sizeof(KEVENT), POOL_TRANSPORT_EVENT);
if (NewTransport->InitEvent == NULL) {
try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
}
Status = RdrpDuplicateUnicodeStringWithString(&NewTransport->TransportName,
TransportName,
PagedPool,
FALSE);
if (!NT_SUCCESS(Status)) {
try_return(Status);
}
NewTransport->QualityOfService = QualityOfService;
NewTransport->ConnectionReferenceCount = 0;
NewTransport->FileObject = NULL;
NewTransport->Handle = NULL;
NewTransport->ResumeKey = ++RdrTransportIndex;
KeInitializeEvent(NewTransport->InitEvent, NotificationEvent, FALSE);
NewTransport->InitError = STATUS_SUCCESS;
NonPagedTransport->ReferenceCount = 1; // Initialize reference count.
NonPagedTransport->DeviceObject = NULL;
TransportInitialized = TRUE;
//
// Insert the new transport in the transport list in order based
// on its quality of service. A higher quality of service means
// the transport is more preferred and goes earlier in the list.
//
PreviousEntry = &RdrTransportHead;
NextEntry = PreviousEntry->Flink;
while ( NextEntry != &RdrTransportHead ) {
NextTransport = CONTAINING_RECORD( NextEntry, TRANSPORT, GlobalNext );
if ( QualityOfService > NextTransport->QualityOfService ) {
break;
}
PreviousEntry = NextEntry;
NextEntry = PreviousEntry->Flink;
}
InsertHeadList( PreviousEntry, &NewTransport->GlobalNext );
ExReleaseResource(&RdrTransportResource);
TransportAcquired = FALSE;
//
// Now create the endpoint.
//
Status = RdrpTdiCreateAddress(NewTransport);
#if MAGIC_BULLET
if (NT_SUCCESS(Status)) {
ULONG i, NC = 0;
//
// See if this is NBF, and if so, remember the magic
// bullet settings.
//
for (i = 0; i < (NewTransport->TransportName.Length / sizeof(WCHAR)); i += 1) {
if (NewTransport->TransportName.Buffer[i] == L'\\') {
NC += 1;
}
if (NC == 2) {
if (_wcsnicmp(&NewTransport->TransportName.Buffer[i], L"\\Nbf_", 5) == 0) {
//
// This is NBF. See if it is RAS.
//
if (_wcsnicmp(&NewTransport->TransportName.Buffer[i], L"\\Nbf_Ras", 8) != 0) {
MagicBulletDeviceObject = NewTransport->NonPagedTransport->DeviceObject;
MagicBulletFileObject = NewTransport->FileObject;
}
}
break;
}
}
}
#endif
}
try_exit:NOTHING;
} finally {
if (!NT_SUCCESS(Status)) {
if (TransportInitialized) {
KeSetEvent(NewTransport->InitEvent, 0, FALSE);
NewTransport->InitError = Status;
dprintf(DPRT_TRANSPORT, ("RdrpTdiAllocateTransport: Dereference transport %lx\n", NewTransport));
RdrReferenceDiscardableCode(RdrVCDiscardableSection);
RdrDereferenceTransport(NonPagedTransport);
RdrDereferenceDiscardableCode(RdrVCDiscardableSection);
} else {
if (NewTransport != NULL) {
RdrpTdiFreeTransport(NewTransport);
}
}
} else {
KeSetEvent(NewTransport->InitEvent, 0, FALSE);
NewTransport->InitError = STATUS_SUCCESS;
}
if (TransportAcquired) {
ExReleaseResource(&RdrTransportResource);
}
}
return Status;
}
typedef struct _REMOVE_CONNECTIONS_CONTEXT {
PIRP Irp;
PTRANSPORT Transport;
ULONG ForceLevel;
} REMOVE_CONNECTIONS_CONTEXT, *PREMOVE_CONNECTIONS_CONTEXT;
NTSTATUS
RdrRemoveConnectionsTransport(
IN PIRP Irp,
IN PTRANSPORT Transport,
IN ULONG ForceLevel
)
{
NTSTATUS Status;
REMOVE_CONNECTIONS_CONTEXT Context;
PAGED_CODE();
RdrReferenceDiscardableCode(RdrVCDiscardableSection);
Context.Transport = Transport;
Context.ForceLevel = ForceLevel;
Context.Irp = Irp;
Status = RdrForeachServer(RdrRemoveConnectionsOnServerForTransport, &Context);
//
// Dereference this transport provider. This should delete
// the transport connection. Please note that you cannot call
// this routine with any mutexes owned. This is because this routine
// calls ZwClose when the reference count goes to zero,
// and that will attempt to acquire a VERY low level mutex.
//
dprintf(DPRT_TRANSPORT, ("RdrRemoveConnectionsTransport: Dereference transport %lx\n", Transport));
Status = RdrDereferenceTransport(Transport->NonPagedTransport);
RdrDereferenceDiscardableCode(RdrVCDiscardableSection);
return Status;
}
NTSTATUS
RdrRemoveConnectionsOnServerForTransport(
IN PSERVERLISTENTRY Sle,
IN PVOID Ctx
)
{
PREMOVE_CONNECTIONS_CONTEXT Context = Ctx;
PCONNECTLISTENTRY PreviousCle = NULL;
NTSTATUS Status;
BOOLEAN ConnectMutexAcquired = FALSE;
PLIST_ENTRY ConnectEntry;
PAGED_CODE();
ASSERT(Sle->Signature == STRUCTURE_SIGNATURE_SERVERLISTENTRY);
if ((Sle->ConnectionContext == NULL) ||
(Sle->ConnectionContext->TransportProvider != NULL) &&
(Sle->ConnectionContext->TransportProvider->PagedTransport != Context->Transport)) {
return STATUS_SUCCESS;
}
try {
dprintf(DPRT_TDI, ("Found server %wZ (%lx)\n", &Sle->Text, Sle));
//
// Reacquire the database mutex in preparation for
// stepping to the next connection.
//
if (!NT_SUCCESS(KeWaitForMutexObject(&RdrDatabaseMutex, // Object to wait.
Executive, // Reason for waiting
KernelMode, // Processor mode
FALSE, // Alertable
NULL))) {
InternalError(("Unable to claim connection mutex in GetConnection"));
}
ConnectMutexAcquired = TRUE;
for (ConnectEntry = Sle->CLEHead.Flink ;
ConnectEntry != &Sle->CLEHead ;
ConnectEntry = ConnectEntry->Flink) {
PCONNECTLISTENTRY Cle = CONTAINING_RECORD(ConnectEntry, CONNECTLISTENTRY, SiblingNext);
ASSERT(Cle->Signature == STRUCTURE_SIGNATURE_CONNECTLISTENTRY);
dprintf(DPRT_TDI, ("Delete Cle \\%wZ\\%wZ (%lx)\n", &Sle->Text, &Cle->Text, Cle));
//
// Apply an artificial reference to the connection.
//
RdrReferenceConnection(Cle);
//
// Release the connection mutex while deleting the
// connection.
//
KeReleaseMutex(&RdrDatabaseMutex, FALSE);
ConnectMutexAcquired = FALSE;
//
// Release the reference to the previous connection,
// if any.
//
// Please note that you cannot do this with any
// mutexes owned. This is because this routine
// calls ZwClose when the reference count goes to
// zero, and that will attempt to acquire a VERY
// low level mutex.
//
if (PreviousCle != NULL) {
RdrDereferenceConnection(Context->Irp, PreviousCle, NULL, FALSE);
}
PreviousCle = Cle;
//
// Delete all the files on the connection.
//
Status = RdrDeleteConnection(Context->Irp, Cle, NULL, NULL, Context->ForceLevel);
if (!NT_SUCCESS(Status)) {
dprintf(DPRT_TDI, ("Unable to delete connection. %lx\n", Status));
try_return(Status);
}
//
// Reacquire the database mutex in preparation for
// stepping to the next connection.
//
if (!NT_SUCCESS(KeWaitForMutexObject(&RdrDatabaseMutex, // Object to wait.
Executive, // Reason for waiting
KernelMode, // Processor mode
FALSE, // Alertable
NULL))) {
InternalError(("Unable to claim connection mutex in GetConnection"));
}
ConnectMutexAcquired = TRUE;
}
try_return(Status = STATUS_SUCCESS);
try_exit:NOTHING;
} finally {
if (ConnectMutexAcquired) {
KeReleaseMutex(&RdrDatabaseMutex, FALSE);
}
//
// Release the reference to the previous connection, if any.
//
// Please note that you cannot do this with any mutexes owned.
// This is because this routine calls ZwClose when the
// reference count goes to zero, and that will attempt to
// acquire a VERY low level mutex.
//
if (PreviousCle != NULL) {
RdrDereferenceConnection(Context->Irp, PreviousCle, NULL, FALSE);
}
}
return Status;
}
NTSTATUS
RdrUnbindFromAllTransports(
IN PIRP Irp
)
{
NTSTATUS Status = STATUS_SUCCESS;
PLIST_ENTRY TransportEntry;
PLIST_ENTRY NextEntry;
PAGED_CODE();
RdrReferenceDiscardableCode(RdrVCDiscardableSection);
ExAcquireResourceExclusive(&RdrTransportResource, TRUE);
for (TransportEntry = RdrTransportHead.Flink;
TransportEntry != &RdrTransportHead;
TransportEntry = NextEntry ) {
PTRANSPORT Transport = CONTAINING_RECORD(TransportEntry, TRANSPORT, GlobalNext);
dprintf(DPRT_TRANSPORT, ("RdrUnbindFromAllTransports: Reference transport %lx\n", Transport));
RdrReferenceTransport(Transport->NonPagedTransport);
ExReleaseResource(&RdrTransportResource);
Status = RdrRemoveConnectionsTransport(Irp, Transport, USE_LOTS_OF_FORCE);
ExAcquireResourceExclusive(&RdrTransportResource, TRUE);
NextEntry = TransportEntry->Flink;
dprintf(DPRT_TRANSPORT, ("RdrUnbindFromAllTransports: Dereference transport %lx\n", Transport));
RdrDereferenceTransport(Transport->NonPagedTransport);
}
ExReleaseResource(&RdrTransportResource);
RdrDereferenceDiscardableCode(RdrVCDiscardableSection);
return Status;
}
NTSTATUS
RdrDereferenceTransportByName (
IN PUNICODE_STRING TransportName
)
/*++
Routine Description:
This routine will deallocate an allocated transport
Arguments:
IN PUNICODE_STRING TransportName - Supplies a pointer to the name of the transport
to dereference
Return Value:
Status of remove request.
--*/
{
PTRANSPORT Transport;
PAGED_CODE();
dprintf(DPRT_TRANSPORT, ("RdrDereferenceTransportByName: %wZ", TransportName));
ExAcquireResourceExclusive(&RdrTransportResource, TRUE);
Transport = RdrFindTransport(TransportName);
if (Transport == NULL) {
ExReleaseResource(&RdrTransportResource);
return STATUS_OBJECT_NAME_NOT_FOUND;
}
//
// Remove the reference applied in RdrFindTransport.
//
RdrDereferenceTransport(Transport->NonPagedTransport);
//
// Remove the reference we were asked to remove.
//
RdrDereferenceTransport(Transport->NonPagedTransport);
ExReleaseResource(&RdrTransportResource);
return STATUS_SUCCESS;
}
NTSTATUS
RdrDereferenceTransport(
IN PNONPAGED_TRANSPORT Transport
)
{
KIRQL OldIrql;
DISCARDABLE_CODE(RdrVCDiscardableSection);
dprintf(DPRT_TRANSPORT, ("RdrDereferenceTransport: %lx", Transport));
ACQUIRE_SPIN_LOCK(&RdrTransportReferenceSpinLock, &OldIrql);
ASSERT (Transport->ReferenceCount > 0);
//
// Remove the reference we were asked to remove. Early out if we
// aren't going to drop it to 0.
//
if (Transport->ReferenceCount > 1) {
Transport->ReferenceCount-= 1;
RELEASE_SPIN_LOCK(&RdrTransportReferenceSpinLock, OldIrql);
return STATUS_SUCCESS;
}
RELEASE_SPIN_LOCK(&RdrTransportReferenceSpinLock, OldIrql);
ASSERT (KeGetCurrentIrql() <= APC_LEVEL);
//
// It's really likely we'll dereference this transport to 0, so
// remove the reference.
//
ExAcquireResourceExclusive(&RdrTransportResource, TRUE);
ACQUIRE_SPIN_LOCK(&RdrTransportReferenceSpinLock, &OldIrql);
//
// Remove the reference we were asked to remove.
//
Transport->ReferenceCount -= 1;
dprintf(DPRT_TRANSPORT, ("Dereference transport %lx (%wZ), now at %lx\n", Transport, &Transport->PagedTransport->TransportName, Transport->ReferenceCount));
if (Transport->ReferenceCount == 0) {
RELEASE_SPIN_LOCK(&RdrTransportReferenceSpinLock, OldIrql);
RemoveEntryList(&Transport->PagedTransport->GlobalNext);
if (Transport->PagedTransport->FileObject != NULL) {
ObDereferenceObject (Transport->PagedTransport->FileObject);
ASSERT( PsGetCurrentProcess() == RdrFspProcess );
ZwClose (Transport->PagedTransport->Handle);
}
RdrpTdiFreeTransport(Transport->PagedTransport);
} else {
RELEASE_SPIN_LOCK(&RdrTransportReferenceSpinLock, OldIrql);
}
ExReleaseResource(&RdrTransportResource);
return STATUS_SUCCESS;
}
PTRANSPORT
RdrFindTransport (
PUNICODE_STRING TransportName
)
/*++
Routine Description:
This routine will locate a transport in the bowsers transport list.
Arguments:
PUNICODE_STRING TransportName - Supplies the name of the transport provider
Return Value:
PTRANSPORT - NULL if no transport was found, TRUE if transport was found.
--*/
{
PLIST_ENTRY TransportEntry;
PTRANSPORT Transport = NULL;
PAGED_CODE();
dprintf(DPRT_TRANSPORT, ("RdrFindTransport %wZ\n", TransportName));
ExAcquireResourceShared(&RdrTransportResource, TRUE);
for (TransportEntry = RdrTransportHead.Flink ;
TransportEntry != &RdrTransportHead ;
TransportEntry = TransportEntry->Flink) {
Transport = CONTAINING_RECORD(TransportEntry, TRANSPORT, GlobalNext);
if (RtlEqualUnicodeString(TransportName, &Transport->TransportName, TRUE)) {
dprintf(DPRT_TRANSPORT, ("RdrFindTransport: Reference transport %lx\n", Transport));
RdrReferenceTransport(Transport->NonPagedTransport);
ExReleaseResource(&RdrTransportResource);
//
// Wait for the transport to be initialized.
//
KeWaitForSingleObject(Transport->InitEvent, Executive, KernelMode, FALSE, NULL);
//
// If the transport failed to initialize,
// dereference it and return failure.
//
if (!NT_SUCCESS(Transport->InitError)) {
dprintf(DPRT_TRANSPORT, ("RdrFindTransport: Dereference transport %lx\n", Transport));
RdrDereferenceTransport(Transport->NonPagedTransport);
Transport = NULL;
}
return Transport;
}
}
ExReleaseResource(&RdrTransportResource);
return NULL;
}
NTSTATUS
RdrEnumerateTransports(
IN BOOLEAN Wait,
IN PLMR_REQUEST_PACKET InputBuffer,
IN OUT PULONG InputBufferLength,
OUT PVOID OBuffer,
IN ULONG OutputBufferLength,
IN ULONG OutputBufferDisplacement)
{
PWKSTA_TRANSPORT_INFO_0 OutputBuffer = OBuffer;
PLIST_ENTRY TransportEntry;
PCHAR OutputBufferEnd;
ULONG ResumeHandle;
ULONG UserBufferLength = OutputBufferLength;
PAGED_CODE();
try {
InputBuffer->Parameters.Get.EntriesRead = 0;
InputBuffer->Parameters.Get.TotalEntries = 0;
InputBuffer->Parameters.Get.TotalBytesNeeded = 0;
ResumeHandle = InputBuffer->Parameters.Get.ResumeHandle;
} except(EXCEPTION_EXECUTE_HANDLER) {
return GetExceptionCode();
}
if (!ExAcquireResourceShared(&RdrTransportResource, Wait)) {
return STATUS_PENDING;
}
*InputBufferLength = sizeof(LMR_REQUEST_PACKET);
OutputBufferEnd = ((PCHAR )OutputBuffer)+OutputBufferLength;
for (TransportEntry = RdrTransportHead.Flink ;
TransportEntry != &RdrTransportHead ;
TransportEntry = TransportEntry->Flink) {
PTRANSPORT Transport = CONTAINING_RECORD(TransportEntry, TRANSPORT, GlobalNext);
ULONG TransportSize;
ASSERT (Transport->Signature == STRUCTURE_SIGNATURE_TRANSPORT);
if ( ResumeHandle < Transport->ResumeKey ) {
try {
TransportSize =
Transport->TransportName.Length+sizeof(WCHAR) +
(STRLEN(Transport->AdapterAddress)+1)*sizeof(TCHAR);
InputBuffer->Parameters.Get.TotalEntries++;
InputBuffer->Parameters.Get.TotalBytesNeeded +=
(TransportSize + sizeof(WKSTA_TRANSPORT_INFO_0));
if (OutputBufferLength >= sizeof(WKSTA_TRANSPORT_INFO_0)) {
//
// Fill in the constant (numeric) fields in the transport
// structure.
//
OutputBuffer->wkti0_quality_of_service = Transport->QualityOfService;
OutputBuffer->wkti0_wan_ish = Transport->Wannish;
OutputBuffer->wkti0_number_of_vcs = Transport->ConnectionReferenceCount;
OutputBufferLength -= sizeof(WKSTA_TRANSPORT_INFO_0);
InputBuffer->Parameters.Get.ResumeHandle = Transport->ResumeKey;
InputBuffer->Parameters.Get.EntriesRead++;
//
// See if we can fit the variable data.
//
if (OutputBufferLength >= TransportSize) {
OutputBuffer->wkti0_transport_address = Transport->AdapterAddress;
RdrPackString((PCHAR*)&OutputBuffer->wkti0_transport_address,
STRLEN(Transport->AdapterAddress)*sizeof(TCHAR),
OutputBufferDisplacement,
(PCHAR )(OutputBuffer+1),
&OutputBufferEnd);
//
// Pack the transport name into the output buffer.
//
OutputBuffer->wkti0_transport_name = (LPTSTR)Transport->TransportName.Buffer;
RdrPackString((PCHAR*)&OutputBuffer->wkti0_transport_name,
Transport->TransportName.Length,
OutputBufferDisplacement,
(PCHAR )(OutputBuffer+1),
&OutputBufferEnd);
OutputBufferLength -= TransportSize;
} else {
OutputBuffer->wkti0_transport_address = NULL;
OutputBuffer->wkti0_transport_name = NULL;
}
}
//
// Bump OutputBuffer to the next WKSTA_TRANSPORT structure.
//
OutputBuffer++;
} except (EXCEPTION_EXECUTE_HANDLER) {
ExReleaseResource(&RdrTransportResource);
return GetExceptionCode();
}
}
}
ExReleaseResource(&RdrTransportResource);
//
// Return the correct error code.
//
if ( (InputBuffer->Parameters.Get.EntriesRead <
InputBuffer->Parameters.Get.TotalEntries) ||
(UserBufferLength <
InputBuffer->Parameters.Get.TotalBytesNeeded) ) {
return STATUS_MORE_ENTRIES;
}
return STATUS_SUCCESS;
}
NTSTATUS
RdrTdiSendDatagramOnAllTransports(
IN PUNICODE_STRING DestinationName,
IN CHAR SignatureByte,
IN PMDL MdlToSend
)
{
PLIST_ENTRY TransportEntry;
NTSTATUS Status = STATUS_BAD_NETWORK_PATH;
BOOLEAN AnySendSucceeded = FALSE;
PTRANSPORT_ADDRESS RemoteAddress;
ULONG size;
PAGED_CODE();
size = sizeof( TA_NETBIOS_ADDRESS );
if( DestinationName->Length > NETBIOS_NAME_LEN ) {
size += DestinationName->Length - NETBIOS_NAME_LEN;
}
RemoteAddress = ALLOCATE_POOL( NonPagedPool, size, POOL_TRANSPORT );
if( RemoteAddress == NULL ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
ExAcquireResourceShared(&RdrTransportResource, TRUE);
for (TransportEntry = RdrTransportHead.Flink ;
TransportEntry != &RdrTransportHead ;
TransportEntry = TransportEntry->Flink) {
PTRANSPORT Transport = CONTAINING_RECORD(TransportEntry, TRANSPORT, GlobalNext);
TDI_CONNECTION_INFORMATION ConnectionInformation;
PIRP Irp;
//
// Wait for the transport to be initialized.
//
KeWaitForSingleObject(Transport->InitEvent, Executive, KernelMode, FALSE, NULL);
//
// If the transport failed to initialize, skip to the next transport.
//
if (!NT_SUCCESS(Transport->InitError)) {
continue;
}
Status = RdrBuildNetbiosAddress( RemoteAddress, size, DestinationName );
if (!NT_SUCCESS(Status)) {
goto ReturnStatus;
}
//
// Stick the correct signature byte to the computer name.
//
if( RemoteAddress->Address[0].AddressType == TDI_ADDRESS_TYPE_NETBIOS &&
RemoteAddress->Address[0].AddressLength == sizeof( TDI_ADDRESS_NETBIOS ) ) {
((PTA_NETBIOS_ADDRESS)RemoteAddress)->Address[0].Address[0].NetbiosName[NETBIOS_NAME_LEN-1] = SignatureByte;
}
ConnectionInformation.UserDataLength = 0;
ConnectionInformation.OptionsLength = 0;
ConnectionInformation.RemoteAddress = RemoteAddress;
ConnectionInformation.RemoteAddressLength = RDR_COMPUTE_TRANSPORT_ADDRESS_LENGTH( RemoteAddress );
Irp = ALLOCATE_IRP(Transport->FileObject, Transport->NonPagedTransport->DeviceObject, 11, NULL);
if (Irp == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ReturnStatus;
}
TdiBuildSendDatagram(Irp,
Transport->NonPagedTransport->DeviceObject,
Transport->FileObject,
NULL,
NULL,
MdlToSend,
RdrMdlLength(MdlToSend),
&ConnectionInformation);
Status = SubmitTdiRequest(Transport->NonPagedTransport->DeviceObject, Irp);
FREE_IRP( Irp, 15, NULL );
//
// If the send succeeded, mark that it worked. Regardless, go try
// the next transport.
//
if (NT_SUCCESS(Status)) {
AnySendSucceeded = TRUE;
}
}
ReturnStatus:
ExReleaseResource(&RdrTransportResource);
//
// If any of the sends succeeded, succeed the call.
//
FREE_POOL( RemoteAddress );
if (AnySendSucceeded) {
Status = STATUS_SUCCESS;
}
return Status;
}
NTSTATUS
RdrReferenceTransportConnection (
IN PSERVERLISTENTRY Server
)
/*++
Routine Description:
This routine will reference a transport connection. It will make
certain that the connection is still valid, and if it is, it will reference
the connection object. If not, it will return an error.
Arguments:
Connection - Supplies the connection to reference.
Return Value:
NTSTATUS - The status of the operation.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
KIRQL OldIrql;
BOOLEAN ResourceAcquired;
#if 0
PVOID Caller;
PVOID CallersCaller;
RtlGetCallersAddress(&Caller, &CallersCaller);
KdPrint(("Reference %lx %x %x\n", Connection, Caller, CallersCaller));
#endif
RdrReferenceDiscardableCode(RdrVCDiscardableSection);
DISCARDABLE_CODE(RdrVCDiscardableSection);
//
// This code is not paged, but since it blocks, it can only be called
// from below dispatch level.
//
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
//
// Flag that this request is outstanding to allow us to wait until
// the request completes.
//
TryAgain:
ResourceAcquired = ACQUIRE_REQUEST_RESOURCE_SHARED( Server, FALSE, 11 );
//
// We fail to get the resource when one of the following is happening:
// 1) The connection is being disconnected.
// 2) The connection is being reconnected.
// 3) The scan dormant connections routine is looking at this
// server.
//
ACQUIRE_SPIN_LOCK(&RdrServerConnectionValidSpinLock, &OldIrql);
//
// If we are processing a disconnect for this server, we can't allow referencing the transport
// connection.
//
if (Server->DisconnectNeeded ||
(Server->ConnectionContext == NULL)) {
RELEASE_SPIN_LOCK(&RdrServerConnectionValidSpinLock, OldIrql);
if (ResourceAcquired) {
RELEASE_REQUEST_RESOURCE( Server, 12 );
}
#if 0
KdPrint(("Reference %lx failed\n", Server));
#endif
RdrDereferenceDiscardableCode(RdrVCDiscardableSection);
return STATUS_VIRTUAL_CIRCUIT_CLOSED;
}
//
// If the connection is still valid, reference it.
//
if (Server->ConnectionValid) {
if (!ResourceAcquired) {
LARGE_INTEGER DelayTime;
RELEASE_SPIN_LOCK(&RdrServerConnectionValidSpinLock, OldIrql);
// 100ms
DelayTime.QuadPart = -100 * 1000 * 10;
//
// We were unable to get the resource on the last pass through
// because the scavenger thread was examining this connection.
// Try again. The ExAcquireResourceShared OutstandingRequestResource
// must not specify WAIT=TRUE because the Worker routine that
// performs Ping uses this routine. The same thread performs
// Delete PART 2. To avoid the subsequent starve out we loop back
// after a small delay, try to get the resource and check that
// Delete part 1 has not been executed on this connection.
//
KeDelayExecutionThread ( KernelMode, FALSE, &DelayTime );
goto TryAgain;
}
Server->ConnectionReferenceCount += 1;
ObReferenceObject(Server->ConnectionContext->ConnectionObject);
RELEASE_SPIN_LOCK(&RdrServerConnectionValidSpinLock, OldIrql);
} else {
RELEASE_SPIN_LOCK(&RdrServerConnectionValidSpinLock, OldIrql);
if (ResourceAcquired) {
RELEASE_REQUEST_RESOURCE( Server, 13 );
}
#if 0
KdPrint(("Reference %lx failed\n", Server));
#endif
RdrDereferenceDiscardableCode(RdrVCDiscardableSection);
Status = STATUS_VIRTUAL_CIRCUIT_CLOSED;
}
return Status;
}
NTSTATUS
RdrDereferenceTransportConnectionNoRelease(
IN PSERVERLISTENTRY Server
)
/*++
Routine Description:
This routine will dereference a transport connection after it has
been referenced by RdrReferenceTransportConnection. It will dereference
the connection object WITHOUT releasing the OutstandingRequests resource.
Arguments:
Connection - Supplies the connection to reference.
Return Value:
NTSTATUS - The status of the operation (always STATUS_SUCCESS).
--*/
{
KIRQL OldIrql;
PFILE_OBJECT ConnectionObject = Server->ConnectionContext->ConnectionObject;
//#if 0
// PVOID Caller;
// PVOID CallersCaller;
//
// RtlGetCallersAddress(&Caller, &CallersCaller);
//
// KdPrint(("DereferenceNoRelease %lx %x %x\n", Connection, Caller, CallersCaller));
//#endif
DISCARDABLE_CODE(RdrVCDiscardableSection);
ACQUIRE_SPIN_LOCK(&RdrServerConnectionValidSpinLock, &OldIrql);
Server->ConnectionReferenceCount -= 1;
//
// When we dereference the connection to 0, remove the connection
// object pointer - it is no longer valid.
//
if (Server->ConnectionReferenceCount == 0) {
PRDR_CONNECTION_CONTEXT ConnectionContext = Server->ConnectionContext;
PNONPAGED_TRANSPORT Transport = ConnectionContext->TransportProvider;
Server->ConnectionContext = NULL;
ConnectionContext->ConnectionObject = NULL;
ConnectionContext->Server = NULL;
ConnectionContext->TransportProvider = NULL;
RELEASE_SPIN_LOCK(&RdrServerConnectionValidSpinLock, OldIrql);
RdrDereferenceTransport(Transport);
//
// Dereference the connection object, we don't need it any more.
//
// Note: This will delete the connection object.
//
//
// PLEASE NOTE: WE RELY ON THE FACT THAT THIS WILL ACTUALLY DELETE
// THE CONNECTION OBJECT AND THAT THE CONNECTION CONTEXT IS NO LONGER
// NEEDED. THIS DEREFERENCE CANNOT BE MOVED INSIDE THE SPINLOCK
// BECAUSE THAT WILL CAUSE OB TO POST THE DEREFERENCE TO A THREAD THUS
// INTRODUCING A NASTY NASTY WINDOW.
//
ObDereferenceObject(ConnectionObject);
//
// We can now get rid of the connection context.
//
FREE_POOL(ConnectionContext);
} else {
RELEASE_SPIN_LOCK(&RdrServerConnectionValidSpinLock, OldIrql);
//
// Dereference the connection object, we don't need the reference any
// more.
//
ObDereferenceObject(ConnectionObject);
}
return STATUS_SUCCESS;
}
NTSTATUS
RdrDereferenceTransportConnectionForThread(
IN PSERVERLISTENTRY Server,
IN ERESOURCE_THREAD Thread
)
/*++
Routine Description:
This routine will dereference a transport connection after it has
been referenced by RdrReferenceTransportConnection.
Arguments:
Connection - Supplies the connection to reference.
Return Value:
NTSTATUS - The status of the operation (always STATUS_SUCCESS).
--*/
{
#if 0
PVOID Caller;
PVOID CallersCaller;
RtlGetCallersAddress(&Caller, &CallersCaller);
KdPrint(("Dereference %lx %x %x for thread %lx\n", Connection, Caller, CallersCaller, Thread));
#endif
DISCARDABLE_CODE(RdrVCDiscardableSection);
RdrDereferenceTransportConnectionNoRelease(Server);
//
// Now release the resource acquired in ReferenceTransportConnection.
//
RELEASE_REQUEST_RESOURCE_FOR_THREAD( Server, Thread, 14 );
RdrDereferenceDiscardableCode(RdrVCDiscardableSection);
return STATUS_SUCCESS;
}
VOID
RdrSetConnectionFlag(
IN PSERVERLISTENTRY Server,
IN ULONG Flag
)
/*++
Routine Description:
This routine will turn on a flag in a transport connection
Arguments:
Connection - Supplies the connection to reference.
Return Value:
NTSTATUS - The status of the operation (always STATUS_SUCCESS).
--*/
{
KIRQL OldIrql;
DISCARDABLE_CODE(RdrVCDiscardableSection);
KeAcquireSpinLock(&RdrServerConnectionValidSpinLock, &OldIrql);
Server->Flags |= Flag;
KeReleaseSpinLock(&RdrServerConnectionValidSpinLock, OldIrql);
}
VOID
RdrResetConnectionFlag(
IN PSERVERLISTENTRY Server,
IN ULONG Flag
)
/*++
Routine Description:
This routine will turn off a flag in a transport connection
Arguments:
Connection - Supplies the connection to reference.
Return Value:
NTSTATUS - The status of the operation (always STATUS_SUCCESS).
--*/
{
KIRQL OldIrql;
DISCARDABLE_CODE(RdrVCDiscardableSection);
KeAcquireSpinLock(&RdrServerConnectionValidSpinLock, &OldIrql);
Server->Flags &= ~Flag;
KeReleaseSpinLock(&RdrServerConnectionValidSpinLock, OldIrql);
return;
}
NTSTATUS
RdrInitializeTransportConnection(
IN PSERVERLISTENTRY Server
)
{
NTSTATUS Status;
PAGED_CODE();
try {
Server->ConnectionReferenceCount = 0;
//
// Initialize the list of security structures associated with
// the connection.
//
InitializeListHead(&Server->PotentialSecurityList);
InitializeListHead(&Server->ActiveSecurityList);
ExInitializeResource(&Server->RawResource);
//
// We need to disable resource priority boosting for this resource,
// since it will be acquired in one thread and released in another,
// and the thread that acquired the resource could go away.
//
ExDisableResourceBoost(&Server->RawResource);
ExInitializeResource(&Server->OutstandingRequestResource);
//
// We need to disable resource priority boosting for this resource,
// since it will be acquired in one thread and released in another,
// and the thread that acquired the resource could go away.
//
ExDisableResourceBoost(&Server->OutstandingRequestResource);
Server->SecurityEntryCount = 0;
Server->ConnectionContext = NULL;
Server->ConnectionValid = FALSE;
Server->DisconnectNeeded = FALSE;
Server->BufferSize = 0;
//
// Use LAN defaults for timeout on initial negotiate and for transports that
// do not support query connection information.
//
Server->Delay = 0;
Server->Reliable = TRUE;
Server->ReadAhead = TRUE;
Server->Throughput = 0;
//
// Assume 10M of write behind (used when determining if we should
// let a write go longterm or not).
//
Server->ThirtySecondsOfData.HighPart = 0;
Server->ThirtySecondsOfData.LowPart = 10000000;
//
// Initialize the SMB exchange logic for this connection with a limit of
// one SMB/server
//
Server->MpxTable = NULL;
Server->OpLockMpxEntry = NULL;
Server->MaximumCommands = 0;
Server->NumberOfEntries = 0;
Server->NumberOfActiveEntries = 0;
Server->NumberOfLongTermEntries = 0;
Server->OpLockMpxEntry = ALLOCATE_POOL(NonPagedPool, sizeof(MPX_ENTRY), POOL_MPX_TABLE_ENTRY);
if (Server->OpLockMpxEntry == NULL) {
try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
}
RtlZeroMemory(Server->OpLockMpxEntry, sizeof(MPX_ENTRY));
Server->OpLockMpxEntry->Flags = (MPX_ENTRY_ALLOCATED | MPX_ENTRY_OPLOCK);
Server->OpLockMpxEntry->Signature = STRUCTURE_SIGNATURE_MPX_ENTRY;
Server->OpLockMpxEntry->Mid = 0xffff;
Server->OpLockMpxEntry->RequestContext = NULL;
Server->OpLockMpxEntry->Callback = RdrBreakOplockCallback;
Status = RdrUpdateSmbExchangeForConnection(Server, 1, 1);
try_exit:NOTHING;
} finally {
if (!NT_SUCCESS(Status)) {
ExDeleteResource(&Server->RawResource);
ExDeleteResource(&Server->OutstandingRequestResource);
if (Server->OpLockMpxEntry != NULL) {
FREE_POOL(Server->OpLockMpxEntry);
}
}
}
return Status;
}
DBGSTATIC
VOID
RdrpTdiFreeTransport (
IN PTRANSPORT Transport
)
/*++
Routine Description:
This routine will deallocate an allocated transport
.
Arguments:
IN PTRANSPORT Transport - Supplies a pointer to the transport to free
Return Value:
None.
--*/
{
PAGED_CODE();
ASSERT(Transport->Signature == STRUCTURE_SIGNATURE_TRANSPORT);
if (Transport->TransportName.Buffer != NULL) {
FREE_POOL(Transport->TransportName.Buffer);
}
if (Transport->NonPagedTransport != NULL) {
FREE_POOL(Transport->NonPagedTransport);
}
if (Transport->InitEvent != NULL) {
FREE_POOL(Transport->InitEvent);
}
FREE_POOL(Transport);
}
#if TDI_CONNECT_IN_PARALLEL
NTSTATUS
RdrAllocateConnectionContext(
IN PIRP Irp OPTIONAL,
IN PTRANSPORT Transport,
IN PSERVERLISTENTRY Server,
IN PUNICODE_STRING ServerName,
OUT PRDR_TDI_CONNECT_CONTEXT *ConnectionContext
)
{
NTSTATUS Status;
PTRANSPORT_ADDRESS RemoteTransportAddress;
PRDR_TDI_CONNECT_CONTEXT Context;
BOOLEAN ProcessAttached = FALSE;
PRDR_CONNECTION_CONTEXT RdrConnectionContext;
ULONG size,NetbiosAddressSize,TransportAddressLength;
PAGED_CODE();
try {
TransportAddressLength = RdrComputeTransportAddressSize(ServerName);
size = sizeof( RDR_TDI_CONNECT_CONTEXT ) + TransportAddressLength;
Context = ALLOCATE_POOL(NonPagedPool, size, POOL_CONNECT_CONTEXT);
if (Context == NULL) {
try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
}
RdrConnectionContext = Context->ConnectionContext = ALLOCATE_POOL(NonPagedPool, sizeof(RDR_CONNECTION_CONTEXT), POOL_CONNECT_CONTEXT);
if (RdrConnectionContext == NULL) {
try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
}
RdrConnectionContext->TransportProvider = NULL;
Context->Irp = NULL;
RdrConnectionContext->ConnectionObject = NULL;
RdrConnectionContext->ConnectionHandle = NULL;
RdrConnectionContext->Server = NULL;
dprintf(DPRT_TDI, ("Connect context %lx for transport %wZ\n", Context, &Transport->TransportName));
RemoteTransportAddress = (PTRANSPORT_ADDRESS)Context->TransportAddressBuffer;
Status = RdrBuildTransportAddress((PTRANSPORT_ADDRESS)RemoteTransportAddress,
size - sizeof( RDR_TDI_CONNECT_CONTEXT ),
ServerName);
if (!NT_SUCCESS(Status)) {
try_return(Status);
}
//
// If the server's name matches the redirector's name, up to but not
// including the signature byte, then this is a loopback connection.
//
Server->IsLoopback = RdrIsLoopBack(ServerName);
KeInitializeEvent(&Context->ConnectComplete, NotificationEvent, FALSE);
Context->QualityOfService = Transport->QualityOfService;
Context->ConnectionStatus = STATUS_SUCCESS;
RdrReferenceTransport(Transport->NonPagedTransport);
RdrConnectionContext->TransportProvider = Transport->NonPagedTransport;
if (PsGetCurrentProcess() != RdrFspProcess) {
KeAttachProcess(RdrFspProcess);
ProcessAttached = TRUE;
}
Status = RdrTdiOpenConnection(
Transport,
RdrConnectionContext,
&RdrConnectionContext->ConnectionHandle);
if (!NT_SUCCESS(Status)) {
try_return(Status);
}
dprintf(DPRT_TDI, ("Context %lx: Connection handle %lx created\n", Context, RdrConnectionContext->ConnectionHandle));
Status = ObReferenceObjectByHandle(RdrConnectionContext->ConnectionHandle, 0,
*IoFileObjectType,
KernelMode,
(PVOID *)&RdrConnectionContext->ConnectionObject,
NULL
);
if (!NT_SUCCESS(Status)) {
try_return(Status);
}
dprintf(DPRT_TDI, ("Context %lx: Connection object %lx\n", Context, RdrConnectionContext->ConnectionObject));
Status = RdrTdiAssociateAddress(Irp, Transport, RdrConnectionContext->ConnectionObject);
if (ProcessAttached) {
KeDetachProcess();
ProcessAttached = FALSE;
}
if (!NT_SUCCESS(Status)) {
try_return(Status);
}
dprintf(DPRT_TDI, ("Context %lx: Connection associated\n", Context));
Context->RemoteConnectionInformation.UserDataLength = 0;
Context->RemoteConnectionInformation.OptionsLength = 0;
Context->RemoteConnectionInformation.RemoteAddressLength = TransportAddressLength;
Context->RemoteConnectionInformation.RemoteAddress = RemoteTransportAddress;
Context->ConnectedConnectionInformation.UserDataLength = 0;
Context->ConnectedConnectionInformation.OptionsLength = 0;
Context->ConnectedConnectionInformation.RemoteAddressLength = TransportAddressLength;
Context->ConnectedConnectionInformation.RemoteAddress = &Context->TransportAddressBuffer;
Context->Irp = ALLOCATE_IRP(RdrConnectionContext->ConnectionObject, NULL, 12, Context);
if (Context->Irp == NULL) {
try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
}
TdiBuildConnect(Context->Irp, IoGetRelatedDeviceObject(RdrConnectionContext->ConnectionObject),
RdrConnectionContext->ConnectionObject,
NULL,
NULL,
&RdrTdiConnectTimeout,
&Context->RemoteConnectionInformation,
&Context->ConnectedConnectionInformation);
IoSetCompletionRoutine(Context->Irp, CompleteTdiConnect, Context, TRUE, TRUE, TRUE);
dprintf(DPRT_TDI, ("Context %lx: Connect IRP %lx\n", Context, Context->Irp));
try_exit:NOTHING;
} finally {
if (ProcessAttached) {
KeDetachProcess();
}
if (!NT_SUCCESS(Status)) {
if (Context != NULL) {
RdrFreeConnectionContext(Context);
}
} else {
*ConnectionContext = Context;
}
}
return Status;
}
#endif
BOOLEAN
RdrNoTransportBindings (
VOID
)
{
BOOLEAN bEmpty;
ExAcquireResourceShared (&RdrTransportResource, TRUE);
bEmpty = IsListEmpty(&RdrTransportHead);
ExReleaseResource(&RdrTransportResource);
return bEmpty;
}
NTSTATUS
RdrTdiConnectToServer (
IN PIRP Irp OPTIONAL,
IN PUNICODE_STRING ServerName,
IN PSERVERLISTENTRY Server
)
/*++
Routine Description:
This routine establishes a VC with a remote server.
Arguments:
IN PIRP Irp - Optional IRP to use connecting to server.
IN PUNICODE_STRING ServerName - Supplies the name of the server to connect to.
OUT PSERVERLISTENTRY Server - Serverlistentry to connect to - connection info filled in.
Return Value:
NTSTATUS - Status of connection operation
--*/
{
NTSTATUS Status = STATUS_BAD_NETWORK_PATH;
#if TDI_CONNECT_IN_PARALLEL
PLIST_ENTRY XPortList;
LIST_ENTRY ConnectContextList;
PLIST_ENTRY ConnectContextEntry;
ULONG ConnectionRetryCount = 1;
BOOLEAN TransportLocked = FALSE;
BOOLEAN PrimaryTransport = TRUE;
BOOLEAN SynchronousRetry = FALSE;
PAGED_CODE();
//
// Prepare to walk the transport list.
//
ExAcquireResourceShared (&RdrTransportResource, TRUE);
TransportLocked = TRUE;
if (IsListEmpty(&RdrTransportHead)) {
ExReleaseResource(&RdrTransportResource);
return STATUS_BAD_NETWORK_PATH;
}
InitializeListHead(&ConnectContextList);
RdrReferenceDiscardableCode(RdrVCDiscardableSection);
ASSERT (Server->ConnectionContext == NULL);
ASSERT (ExIsResourceAcquiredExclusive(&Server->OutstandingRequestResource));
try {
for (XPortList = RdrTransportHead.Flink;
XPortList != &RdrTransportHead ;
XPortList = XPortList->Flink
) {
PTRANSPORT Transport = CONTAINING_RECORD(XPortList, TRANSPORT, GlobalNext);
PRDR_TDI_CONNECT_CONTEXT ConnectionContext = NULL;
//
// Wait for the transport to be bound.
//
KeWaitForSingleObject(Transport->InitEvent, Executive, KernelMode, FALSE, NULL);
//
// Skip over unsuccessful transports.
//
if (!NT_SUCCESS(Transport->InitError)) {
continue;
}
Status = RdrAllocateConnectionContext(Irp, Transport, Server, ServerName, &ConnectionContext);
if (!NT_SUCCESS(Status)) {
ASSERT (ConnectionContext == NULL);
SynchronousRetry = TRUE;
try_return(Status);
}
//
// Ok, we should be ready for prime-time now - everything we need has
// been set up for the connect operation. Now insert this entry into
// the list and go back to the next transport.
//
ConnectionContext->ListHead = &ConnectContextList;
InsertTailList(&ConnectContextList, &ConnectionContext->NextContext);
}
//
// From this point on, we cannot return until we have waited on all
// of the connect requests completing.
//
//
// Submit all the connect requests in parallel.
//
for (ConnectContextEntry = ConnectContextList.Flink;
ConnectContextEntry != &ConnectContextList;
ConnectContextEntry = ConnectContextEntry->Flink ) {
PRDR_TDI_CONNECT_CONTEXT Context = CONTAINING_RECORD(ConnectContextEntry, RDR_TDI_CONNECT_CONTEXT, NextContext);
dprintf(DPRT_TDI, ("Context %lx: Submit IRP %lx\n", Context, Context->Irp));
Status = IoCallDriver(IoGetRelatedDeviceObject(Context->ConnectionContext->ConnectionObject),
Context->Irp);
}
//
// Wait for all of the connect requests to complete.
//
for (ConnectContextEntry = ConnectContextList.Flink;
ConnectContextEntry != &ConnectContextList;
ConnectContextEntry = ConnectContextEntry->Flink ) {
PRDR_TDI_CONNECT_CONTEXT Context = CONTAINING_RECORD(ConnectContextEntry, RDR_TDI_CONNECT_CONTEXT, NextContext);
dprintf(DPRT_TDI, ("Context %lx: Wait for context\n", Context, Context->Irp));
//
// Wait for the connect operation to either complete or be
// canceled.
//
do {
Status = KeWaitForSingleObject(&Context->ConnectComplete,
Executive,
KernelMode,
FALSE,
&RdrTdiPollTimeout);
//
// If we timed out the wait, and the thread is terminating,
// give up and cancel the IRP.
//
if ( (Status == STATUS_TIMEOUT)
&&
ARGUMENT_PRESENT( Irp )
&&
PsIsThreadTerminating( Irp->Tail.Overlay.Thread ) ) {
//
// Ask the I/O system to cancel this IRP. This will cause
// everything to unwind properly.
//
IoCancelIrp( Context->Irp );
}
} while ( Status == STATUS_TIMEOUT );
}
//
// Now see if any of them suceeded.
//
for (ConnectContextEntry = ConnectContextList.Flink;
ConnectContextEntry != &ConnectContextList;
ConnectContextEntry = ConnectContextEntry->Flink ) {
PRDR_TDI_CONNECT_CONTEXT Context = CONTAINING_RECORD(ConnectContextEntry, RDR_TDI_CONNECT_CONTEXT, NextContext);
//
// If this request completed successfully, exit, we've connected
// ok.
//
if (NT_SUCCESS(Status = Context->ConnectionStatus)) {
dprintf(DPRT_TDI, ("Context %lx: Connect completed successfully\n", Context, Context->Irp));
try_return(Status);
} else {
dprintf(DPRT_TDI, ("Context %lx: Connect failed: %lx\n", Context, Context->ConnectionStatus));
}
}
//
// Pick the status of the highest QOS transport to return, since any others might have been
// canceled.
//
Status = CONTAINING_RECORD(ConnectContextList.Flink, RDR_TDI_CONNECT_CONTEXT, NextContext)->ConnectionStatus;
//
// This had better have failed.
//
ASSERT (!NT_SUCCESS(Status));
try_return(Status);
try_exit:NOTHING;
} finally {
if (!NT_SUCCESS(Status)) {
while (!IsListEmpty(&ConnectContextList)) {
PRDR_TDI_CONNECT_CONTEXT Context;
PLIST_ENTRY Entry = RemoveHeadList(&ConnectContextList);
Context = CONTAINING_RECORD(Entry, RDR_TDI_CONNECT_CONTEXT, NextContext);
//
// If the connect failed, all of the connect requests have to have also failed.
// (Unless we didn't actually start the connect requests due to a failure
// in RdrAllocateConnectionContext. The assert is bogus in that case.)
//
//ASSERT (!NT_SUCCESS(Context->ConnectionStatus));
RdrFreeConnectionContext(Context);
}
if (SynchronousRetry) {
#endif
Status = RdrSynchronousTdiConnectToServer(Irp, ServerName, Server);
#if TDI_CONNECT_IN_PARALLEL
}
} else {
BOOLEAN ConnectionEstablished = FALSE;
#if PRIMARY_TRANSPORT_CHECK
//
// If we have something to check, and if the primary transport (the first in the connection
// list) failed, pop up an error if appropriate.
//
if ((RdrServersWithAllTransports != NULL) &&
!NT_SUCCESS(CONTAINING_RECORD(ConnectContextList.Flink, RDR_TDI_CONNECT_CONTEXT, NextContext)->ConnectionStatus)) {
RdrCheckPrimaryTransport(ServerName);
}
#endif
//
// The connection succeeded, reference the discardable code
// section to make sure it doesn't go away until the VC goes down.
//
RdrReferenceDiscardableCode(RdrVCDiscardableSection);
//
// Free up the connection contexts.
//
while (!IsListEmpty(&ConnectContextList)) {
PRDR_TDI_CONNECT_CONTEXT Context;
PLIST_ENTRY Entry = RemoveHeadList(&ConnectContextList);
Context = CONTAINING_RECORD(Entry, RDR_TDI_CONNECT_CONTEXT, NextContext);
if (NT_SUCCESS(Context->ConnectionStatus)) {
if (!ConnectionEstablished) {
ConnectionEstablished = TRUE;
RdrReferenceTransportForConnection(Context->ConnectionContext->TransportProvider->PagedTransport);
Server->ConnectionReferenceCount = 1;
//
// This server is now finally valid - we've disconnected all
// our other connections, we can now use this connection.
//
RdrMarkTransportConnectionValid(Server, Context->ConnectionContext);
//
// Remove the connection context from the context block
// so we won't free it - we're going to use this one.
//
Context->ConnectionContext = NULL;
//
// Now query initial information about the connection.
//
RdrQueryConnectionInformation(Server);
} else {
dprintf(DPRT_TDI, ("Context %lx: Disconnect\n", Context));
RdrDoTdiDisconnect(NULL, Context->ConnectionContext->ConnectionObject);
RdrTdiDisassociateAddress(NULL, Context->ConnectionContext->ConnectionObject);
}
}
RdrFreeConnectionContext(Context);
}
}
Server->LastConnectStatus = Status;
Server->LastConnectTime = RdrCurrentTime;
if (TransportLocked) {
ExReleaseResource(&RdrTransportResource);
}
}
RdrDereferenceDiscardableCode(RdrVCDiscardableSection);
#endif
dprintf(DPRT_TDI, ("Returning status: %X", Status));
return Status;
}
VOID
RdrFreeConnectionContext(
IN PRDR_TDI_CONNECT_CONTEXT ConnectionContext
)
{
PRDR_CONNECTION_CONTEXT context;
NTSTATUS status;
BOOLEAN processAttached = FALSE;
if (ConnectionContext->Irp != NULL) {
FREE_IRP( ConnectionContext->Irp, 16, NULL );
}
context = ConnectionContext->ConnectionContext;
if (context != NULL) {
if (context->ConnectionHandle != NULL) {
if (PsGetCurrentProcess() != RdrFspProcess) {
KeAttachProcess(RdrFspProcess);
processAttached = TRUE;
}
status = ZwClose(context->ConnectionHandle);
ASSERT (NT_SUCCESS(status));
if (processAttached) {
KeDetachProcess();
}
if (context->ConnectionObject != NULL) {
ObDereferenceObject(context->ConnectionObject);
}
}
if (context->TransportProvider != NULL) {
RdrDereferenceTransport(context->TransportProvider);
}
FREE_POOL(context);
}
FREE_POOL(ConnectionContext);
}
NTSTATUS
CompleteTdiConnect (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Ctx
)
{
PRDR_TDI_CONNECT_CONTEXT connectionContext = Ctx;
PLIST_ENTRY entry;
dprintf(DPRT_TDI, ("Context %lx: Connect completed: %lx\n", connectionContext, Irp->IoStatus.Status));
//
// Remember the status of the operation.
//
connectionContext->ConnectionStatus = Irp->IoStatus.Status;
entry = connectionContext->NextContext.Flink;
if (NT_SUCCESS(Irp->IoStatus.Status)) {
while (entry != connectionContext->ListHead) {
PRDR_TDI_CONNECT_CONTEXT context = CONTAINING_RECORD(entry, RDR_TDI_CONNECT_CONTEXT, NextContext);
ASSERT (context->QualityOfService < connectionContext->QualityOfService);
//
// Cancel the connect request for all lower QOS connect requests, since we don't care about
// those requests any more. Note that we also don't care if the request was already completed
// since we won't be freeing the IRP on completion, canceling an already completed IRP is a NOP.
//
IoCancelIrp(context->Irp);
entry = entry->Flink;
}
}
KeSetEvent(&connectionContext->ConnectComplete, 0, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
RdrSynchronousTdiConnectToServer (
IN PIRP Irp OPTIONAL,
IN PUNICODE_STRING ServerName,
IN PSERVERLISTENTRY Server
)
/*++
Routine Description:
This routine establishes a VC with a remote server.
Arguments:
IN PIRP Irp - Optional IRP to use connecting to server.
IN PUNICODE_STRING ServerName - Supplies the name of the server to connect to.
OUT PSERVERLISTENTRY Server - Serverlistentry to connect to - connection info filled in.
Return Value:
NTSTATUS - Status of connection operation
--*/
{
NTSTATUS Status = STATUS_BAD_NETWORK_PATH;
PLIST_ENTRY XportList;
PTRANSPORT Xport;
ULONG ConnectionRetryCount;
BOOLEAN TransportLocked = FALSE;
BOOLEAN PrimaryTransport = TRUE;
PTRANSPORT NewXport;
PAGED_CODE();
RdrReferenceDiscardableCode(RdrVCDiscardableSection);
//
// If there are no transports bound to the redirector, return
// without trying anything.
//
ExAcquireResourceShared (&RdrTransportResource, TRUE);
TransportLocked = TRUE;
if (IsListEmpty(&RdrTransportHead)) {
ExReleaseResource(&RdrTransportResource);
RdrDereferenceDiscardableCode(RdrVCDiscardableSection);
return STATUS_BAD_NETWORK_PATH;
}
XportList = RdrTransportHead.Flink;
Xport = CONTAINING_RECORD(XportList, TRANSPORT, GlobalNext);
ASSERT(Xport->Signature == STRUCTURE_SIGNATURE_TRANSPORT);
RdrReferenceTransport(Xport->NonPagedTransport);
ExReleaseResource (&RdrTransportResource);
TransportLocked = FALSE;
do {
ConnectionRetryCount = 1;
RetryConnection:
Status = RdrTdiConnectOnTransport(Irp,
Xport,
ServerName,
Server);
//
// If the connect succeeded, return immediately.
//
if (NT_SUCCESS(Status)) {
break;
//
// If we got INSUFFICIENT_RESOURCES from the server, purge a dormant
// connection and retry the operation.
//
} else if ((Status == STATUS_INSUFFICIENT_RESOURCES)
&&
ConnectionRetryCount--) {
RdrScanForDormantConnections(1, Xport);
goto RetryConnection;
} else if ( ARGUMENT_PRESENT(Irp) &&
PsIsThreadTerminating( Irp->Tail.Overlay.Thread ) ) {
break;
}
PrimaryTransport = FALSE;
ASSERT (!TransportLocked);
ExAcquireResourceShared (&RdrTransportResource, TRUE);
TransportLocked = TRUE;
XportList = XportList->Flink;
if (XportList != &RdrTransportHead) {
NewXport = CONTAINING_RECORD(XportList, TRANSPORT, GlobalNext);
RdrReferenceTransport (NewXport->NonPagedTransport);
} else {
NewXport = NULL;
}
ExReleaseResource(&RdrTransportResource);
TransportLocked = FALSE;
//
// Dereference the transport. Note that we do NOT hold the transport
// resource exclusively at this point.
//
RdrDereferenceTransport(Xport->NonPagedTransport);
//
// Step to the next transport in the list and retry the connect.
//
Xport = NewXport;
//
// We know we're done when NewXport (and thus Xport) is null.
//
} while ( Xport != NULL );
if (TransportLocked) {
ExReleaseResource (&RdrTransportResource);
}
if (Xport != NULL) {
RdrDereferenceTransport (Xport->NonPagedTransport);
}
//
// If we connected, but not on the primary transport, we may want to
// pop up a hard error.
//
#if PRIMARY_TRANSPORT_CHECK
if ((RdrServersWithAllTransports != NULL) &&
!PrimaryTransport &&
NT_SUCCESS(Status)) {
RdrCheckPrimaryTransport(ServerName);
}
#endif
RdrDereferenceDiscardableCode(RdrVCDiscardableSection);
dprintf(DPRT_TDI, ("Returning status: %X", Status));
return Status;
}
#if PRIMARY_TRANSPORT_CHECK
VOID
RdrCheckPrimaryTransport(
IN PUNICODE_STRING ServerName
)
{
PWSTR ServerToCheck;
UNICODE_STRING ServerNameString;
PAGED_CODE();
ServerToCheck = RdrServersWithAllTransports;
while (*ServerToCheck != UNICODE_NULL) {
RtlInitUnicodeString(&ServerNameString, ServerToCheck);
//
// If we're connecting to one of our "known good" servers,
// we want to pop up an error.
if (RtlEqualUnicodeString(&ServerNameString, ServerName, TRUE)) {
IoRaiseInformationalHardError(STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED,
ServerName,
NULL
);
break;
}
//
// Skip to next server.
ServerToCheck += wcslen(ServerToCheck) + 1;
}
}
#endif
VOID
RdrMarkTransportConnectionValid(
IN PSERVERLISTENTRY Server,
IN PRDR_CONNECTION_CONTEXT ConnectionContext
)
{
KIRQL OldIrql;
DISCARDABLE_CODE(RdrVCDiscardableSection);
ACQUIRE_SPIN_LOCK(&RdrServerConnectionValidSpinLock, &OldIrql);
Server->ConnectionContext = ConnectionContext;
ConnectionContext->Server = Server;
Server->ConnectionValid = TRUE;
Server->DisconnectNeeded = FALSE;
RELEASE_SPIN_LOCK(&RdrServerConnectionValidSpinLock, OldIrql);
}
VOID
RdrResetTransportConnectionValid(
IN PSERVERLISTENTRY Server
)
{
KIRQL OldIrql;
DISCARDABLE_CODE(RdrVCDiscardableSection);
ACQUIRE_SPIN_LOCK(&RdrServerConnectionValidSpinLock, &OldIrql);
Server->ConnectionValid = FALSE;
RELEASE_SPIN_LOCK(&RdrServerConnectionValidSpinLock, OldIrql);
}
NTSTATUS
RdrTdiConnectOnTransport(
IN PIRP Irp OPTIONAL,
IN PTRANSPORT Transport,
IN PUNICODE_STRING ServerName,
IN PSERVERLISTENTRY Server
)
/*++
Routine Description:
This routine establishes a VC with a remote server.
Arguments:
IN PIRP Irp - Optional IRP to use connecting to server.
IN PTRANSPORT - Specifies the transport to connect to.
IN PUNICODE_STRING RemoteTransportAddress - Specifies the name to connect to.
Return Value:
NTSTATUS - Status of connection operation
--*/
{
NTSTATUS S1, S2, S3, S4;
NTSTATUS Status = STATUS_BAD_NETWORK_PATH;
BOOLEAN AddressAssociated = FALSE;
BOOLEAN ProcessAttached = FALSE;
PTRANSPORT_ADDRESS TransportAddressBuffer;
PTRANSPORT_ADDRESS RemoteTransportAddress;
PTRANSPORT_ADDRESS ConnectedTransportAddress;
PRDR_CONNECTION_CONTEXT ConnectionContext = NULL;
ULONG TransportAddressSize;
PAGED_CODE();
//
// Build a TRANSPORT_ADDRESS structure to describe this remote computer
// name.
//
TransportAddressSize = RdrComputeTransportAddressSize(ServerName);
TransportAddressBuffer = ALLOCATE_POOL( NonPagedPool, TransportAddressSize, POOL_TRANSPORT );
if( TransportAddressBuffer == NULL ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = RdrBuildTransportAddress( TransportAddressBuffer, TransportAddressSize, ServerName );
if (!NT_SUCCESS(Status)) {
FREE_POOL( TransportAddressBuffer );
return Status;
}
RemoteTransportAddress =
ConnectedTransportAddress = (PTRANSPORT_ADDRESS)TransportAddressBuffer;
//
// If the server's name matches the redirector's name, up to but not
// including the signature byte, then this is a loopback connection.
//
Server->IsLoopback = RdrIsLoopBack(ServerName);
IFDEBUG(TDI) {
OEM_STRING String;
String.MaximumLength = RemoteTransportAddress->Address[0].AddressLength;
String.Length = NETBIOS_NAME_LEN;
//String.Buffer = RemoteTransportAddress->Address[0].Address[0].NetbiosName;
dprintf(DPRT_TDI, ("Connect to \"%Z\"\n",&String));
}
RdrReferenceDiscardableCode(RdrVCDiscardableSection);
try {
ConnectionContext = ALLOCATE_POOL(NonPagedPool, sizeof(RDR_CONNECTION_CONTEXT), POOL_CONNECT_CONTEXT);
if (ConnectionContext == NULL) {
try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
}
ConnectionContext->Server = NULL;
ConnectionContext->ConnectionObject = NULL;
ConnectionContext->ConnectionHandle = NULL;
Server->ConnectionContext = ConnectionContext;
//
// Link the transport into the server before the connect (in case we
// get a disconnect immediately after this)
//
ConnectionContext->TransportProvider = Transport->NonPagedTransport;
//
// Reference this transport to make sure it doesn't go away while the
// connection is active.
//
RdrReferenceTransport(Transport->NonPagedTransport);
//
// And indicate that there is a connection reference for
// RdrEnumerateTransports to return.
//
RdrReferenceTransportForConnection(Transport);
//
// Make sure that the connection object, handle, and reference count
// are all 0 before we attempt to connect to the server.
//
ASSERT (Server->ConnectionReferenceCount == 0);
ASSERT (ExIsResourceAcquiredExclusive(&Server->OutstandingRequestResource));
//
// Wait for the transport to be bound in.
//
KeWaitForSingleObject(Transport->InitEvent, Executive, KernelMode, FALSE, NULL);
//
// If the transport failed to initialize, fail the connect request.
//
if (!NT_SUCCESS(Transport->InitError)) {
try_return(Status = Transport->InitError);
}
//
// Attach to the redirector's FSP to allow the handle for the
// connection to hang around.
//
if (PsGetCurrentProcess() != RdrFspProcess) {
KeAttachProcess(RdrFspProcess);
ProcessAttached = TRUE;
}
//
// Open a connection object for this connection on this transport.
//
S1 = Status = RdrTdiOpenConnection(
Transport,
ConnectionContext,
&ConnectionContext->ConnectionHandle);
if (!NT_SUCCESS(Status)) {
try_return(Status);
}
ASSERT (ConnectionContext->ConnectionHandle != NULL);
//
// Reference the file object created to allow us to use
// it in other processes context.
//
S2 = Status = ObReferenceObjectByHandle(ConnectionContext->ConnectionHandle, 0,
*IoFileObjectType,
KernelMode,
(PVOID *)&ConnectionContext->ConnectionObject,
NULL
);
if (!NT_SUCCESS(Status)) {
try_return(Status);
}
Server->ConnectionReferenceCount = 1;
// KdPrint(("Initialize %lx\n", Connection));
ASSERT (ConnectionContext->ConnectionObject != NULL);
S3 = Status = RdrTdiAssociateAddress(Irp, Transport, ConnectionContext->ConnectionObject);
if (!NT_SUCCESS(Status)) {
try_return(Status);
}
AddressAssociated = TRUE;
//
// Now perform the actual connection operation.
//
S4 = Status = RdrDoTdiConnect(
Irp,
ConnectionContext->ConnectionObject,
RemoteTransportAddress,
ConnectedTransportAddress,
TransportAddressSize,
TransportAddressSize);
Server->LastConnectStatus = Status;
Server->LastConnectTime = RdrCurrentTime;
if (!NT_SUCCESS(Status)) {
try_return(Status);
}
// DbgBreakPoint();
//
// Mark that this transport connection is now valid.
//
RdrMarkTransportConnectionValid(Server, ConnectionContext);
//
// Ignore errors from RdrQueryConnectionInformation.
//
RdrQueryConnectionInformation(Server);
try_exit:NOTHING;
} finally {
//
// If the connection attempt failed, clean up.
//
if (!NT_SUCCESS(Status)) {
//
// The connect attempt failed - clean up after ourselves.
//
if (ConnectionContext != NULL) {
if (AddressAssociated) {
ASSERT(ConnectionContext->ConnectionObject != NULL);
RdrTdiDisassociateAddress(Irp, ConnectionContext->ConnectionObject);
}
if (ConnectionContext->ConnectionObject != NULL) {
ObDereferenceObject(ConnectionContext->ConnectionObject);
ConnectionContext->ConnectionObject = NULL;
//
// Since we own the transport connection exclusively, we
// can safely blast the reference count to 0.
//
Server->ConnectionReferenceCount = 0;
}
if (ConnectionContext->ConnectionHandle != NULL) {
NTSTATUS CloseStatus;
CloseStatus = ZwClose(ConnectionContext->ConnectionHandle);
ASSERT (NT_SUCCESS(CloseStatus));
ConnectionContext->ConnectionHandle = NULL;
}
FREE_POOL(ConnectionContext);
}
Server->ConnectionContext = NULL;
//
// This transport connection is no longer valid, reset it.
//
RdrResetTransportConnectionValid(Server);
#if 0 && MAGIC_BULLET
if ( RdrEnableMagic ) {
RdrSendMagicBullet(Transport);
}
#endif
//
// The connect failed - Dereference the transport for the
// connection and reset the TransportConnection's provider.
//
RdrDereferenceTransportForConnection(Transport);
RdrDereferenceTransport(Transport->NonPagedTransport);
RdrDereferenceDiscardableCode(RdrVCDiscardableSection);
dprintf(DPRT_TDI, ("RdrpTdiConnect: Could not connect to server \"%wZ\", Status = %X",ServerName, Status));
} else {
dprintf(DPRT_TDI, ("Connection established...\n"));
IFDEBUG(TDI) {
OEM_STRING String;
String.MaximumLength = RemoteTransportAddress->Address[0].AddressLength;
String.Length = NETBIOS_NAME_LEN;
// String.Buffer = RemoteTransportAddress->Address[0].Address[0].NetbiosName;
dprintf(DPRT_TDI, ("Connect to \"%Z\"\n",&String));
dprintf(DPRT_TDI, ("Connection Handle:%lx\n",Server->ConnectionContext->ConnectionHandle));
}
ConnectionContext->TransportProvider = Transport->NonPagedTransport;
}
if (ProcessAttached) {
//
// Now re-attach back to our original process
//
KeDetachProcess();
}
}
FREE_POOL( TransportAddressBuffer );
return Status;
}
NTSTATUS
RdrTdiDisconnect (
IN PIRP Irp OPTIONAL,
IN PSERVERLISTENTRY Server
)
/*++
Routine Description:
This routine disconnects a specified session from the specified remote
server.
Arguments:
IN ULONG ConnectionId - Supplies the connection identifier to disconnect
IN PTRANSPORT TransportProvider - Supplies the transport to disconnect
from
Return Value:
NTSTATUS - Final status of operation
--*/
{
NTSTATUS Status;
BOOLEAN ProcessAttached = FALSE;
BOOLEAN IrpAllocated = FALSE;
PAGED_CODE();
if (Server->ConnectionContext == NULL) {
return STATUS_SUCCESS;
}
ASSERT(Server->ConnectionContext->TransportProvider->Signature == STRUCTURE_SIGNATURE_NONPAGED_TRANSPORT);
Status = RdrDoTdiDisconnect(Irp, Server->ConnectionContext->ConnectionObject);
//
// Ignore the error from the disconnect.
//
Status = RdrTdiDisassociateAddress(Irp, Server->ConnectionContext->ConnectionObject);
//
// Attach to the redirector's FSP to get the correct handle table
// for the connection.
//
if (PsGetCurrentProcess() != RdrFspProcess) {
KeAttachProcess(RdrFspProcess);
ProcessAttached = TRUE;
}
Status = ZwClose(Server->ConnectionContext->ConnectionHandle);
ASSERT (NT_SUCCESS(Status));
Server->ConnectionContext->ConnectionHandle = NULL;
RdrDereferenceTransportForConnection(Server->ConnectionContext->TransportProvider->PagedTransport);
ASSERT (NT_SUCCESS(Status));
RdrDereferenceTransportConnectionNoRelease(Server);
if (ProcessAttached) {
KeDetachProcess();
}
RdrDereferenceDiscardableCode(RdrVCDiscardableSection);
dprintf(DPRT_TDI, ("Returning status: %X\n", Status));
return Status;
}
DBGSTATIC
NTSTATUS
SubmitTdiRequest (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine submits a request to TDI and waits for it to complete.
Arguments:
IN PFILE_OBJECT FileObject - Connection or Address handle for TDI request
IN PIRP Irp - TDI request to submit.
Return Value:
NTSTATUS - Final status of request.
--*/
{
NTSTATUS Status;
KEVENT Event;
PAGED_CODE();
RdrReferenceDiscardableCode(RdrVCDiscardableSection);
KeInitializeEvent (&Event, NotificationEvent, FALSE);
IoSetCompletionRoutine(Irp, CompleteTdiRequest, &Event, TRUE, TRUE, TRUE);
//
// Submit the disconnect request
//
Status = IoCallDriver(DeviceObject, Irp);
//
// If it failed immediately, return now, otherwise wait.
//
if (!NT_SUCCESS(Status)) {
dprintf(DPRT_TDI, ("SubmitTdiRequest: submit request. Status = %X", Status));
RdrDereferenceDiscardableCode(RdrVCDiscardableSection);
return Status;
}
if (Status == STATUS_PENDING) {
dprintf(DPRT_TDI, ("TDI request issued, waiting..."));
do {
//
// Wait for a couple of seconds for the request to complete
//
// If it times out, and the thread is terminating, cancel the
// request and unwind that way.
//
Status = KeWaitForSingleObject(&Event, // Object to wait on.
Executive, // Reason for waiting
KernelMode, // Processor mode
FALSE, // Alertable
&RdrTdiPollTimeout); // Timeout
//
// If we timed out the wait, and the thread is terminating,
// give up and cancel the IRP.
//
if ( (Status == STATUS_TIMEOUT)
&&
ARGUMENT_PRESENT(Irp)
&&
PsIsThreadTerminating( Irp->Tail.Overlay.Thread ) ) {
//
// Ask the I/O system to cancel this IRP. This will cause
// everything to unwind properly.
//
IoCancelIrp(Irp);
}
} while ( Status == STATUS_TIMEOUT );
if (!NT_SUCCESS(Status)) {
dprintf(DPRT_TDI, ("Could not wait for connection to complete"));
RdrDereferenceDiscardableCode(RdrVCDiscardableSection);
return Status;
}
Status = Irp->IoStatus.Status;
}
dprintf(DPRT_TDI, ("TDI request complete\n"));
RdrDereferenceDiscardableCode(RdrVCDiscardableSection);
return(Status);
}
NTSTATUS
RdrTdiAssociateAddress(
IN PIRP Irp OPTIONAL,
IN PTRANSPORT Transport,
IN PFILE_OBJECT ConnectionObject
)
/*++
Routine Description:
This routine submits a request to TDI and waits for it to complete.
Arguments:
IN PTRANSPORT Transport - Supplies the transport provider to disconnect
IN PFILE_OBJECT ConnectionObject - Supplies the connection to associate
with the transport.
Return Value:
NTSTATUS - Final status of request.
--*/
{
NTSTATUS Status;
BOOLEAN IrpAllocated = FALSE;
PDEVICE_OBJECT deviceObject = Transport->NonPagedTransport->DeviceObject;
PAGED_CODE();
if (!ARGUMENT_PRESENT(Irp)) {
Irp = ALLOCATE_IRP(ConnectionObject, deviceObject, 13, NULL);
if (Irp == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
return Status;
}
IrpAllocated = TRUE;
}
TdiBuildAssociateAddress(Irp, deviceObject,
ConnectionObject,
NULL, NULL,
Transport->Handle);
Status = SubmitTdiRequest(deviceObject, Irp);
if (IrpAllocated) {
FREE_IRP( Irp, 17, NULL );
}
return(Status);
}
NTSTATUS
RdrDoTdiDisconnect(
IN PIRP Irp OPTIONAL,
IN PFILE_OBJECT ConnectionObject
)
{
NTSTATUS Status;
BOOLEAN IrpAllocated = FALSE;
PAGED_CODE();
//
// Make sure that there are no active requests before we disconnect.
//
if (!ARGUMENT_PRESENT(Irp)) {
Irp = ALLOCATE_IRP(ConnectionObject, NULL, 14, NULL);
if (Irp == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
return Status;
}
IrpAllocated = TRUE;
}
TdiBuildDisconnect(Irp, IoGetRelatedDeviceObject(ConnectionObject),
ConnectionObject, NULL, NULL, &RdrTdiDisconnectTimeout,
TDI_DISCONNECT_RELEASE, NULL, NULL);
Status = SubmitTdiRequest(IoGetRelatedDeviceObject(ConnectionObject), Irp);
if (IrpAllocated) {
FREE_IRP( Irp, 18, NULL );
}
return Status;
}
NTSTATUS
RdrTdiDisassociateAddress(
IN PIRP Irp OPTIONAL,
IN PFILE_OBJECT ConnectionObject
)
/*++
Routine Description:
This routine submits a request to TDI and waits for it to complete.
Arguments:
IN PFILE_OBJECT ConnectionObject - Supplies the connection object to
disassociate
Return Value:
NTSTATUS - Final status of request.
--*/
{
NTSTATUS Status;
BOOLEAN IrpAllocated = FALSE;
PDEVICE_OBJECT DeviceObject;
PAGED_CODE();
if (!ARGUMENT_PRESENT(Irp)) {
Irp = ALLOCATE_IRP(ConnectionObject, NULL, 15, NULL);
if (Irp == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
return Status;
}
IrpAllocated = TRUE;
}
DeviceObject = IoGetRelatedDeviceObject(ConnectionObject);
TdiBuildDisassociateAddress(Irp, DeviceObject,
ConnectionObject, NULL, NULL);
Status = SubmitTdiRequest(DeviceObject, Irp);
if (IrpAllocated) {
FREE_IRP( Irp, 19, NULL );
}
return(Status);
}
NTSTATUS
RdrTdiOpenConnection (
IN PTRANSPORT Transport,
IN PVOID ConnectionContext,
OUT PHANDLE Handle
)
/*++
Routine Description:
This routine submits a request to TDI and waits for it to complete.
Arguments:
IN PTRANSPORT Transport - Supplies the transport provider to disconnect
IN ULONG Function - Supplies the function to submit to TDI.
IN PVOID Buffer1 - Supplies the primary buffer (Input Buffer)
IN ULONG Buffer1Size - Supplies the size of Buffer1
IN PVOID Buffer2 - Supplies the secondary buffer (Output Buffer)
IN ULONG Buffer2Size - Supplies the size of Buffer2
Return Value:
NTSTATUS - Final status of request.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
OBJECT_ATTRIBUTES AddressAttributes;
IO_STATUS_BLOCK IoStatusBlock;
PFILE_FULL_EA_INFORMATION EABuffer;
CONNECTION_CONTEXT UNALIGNED *ContextPointer;
PAGED_CODE();
ASSERT(Transport->Signature == STRUCTURE_SIGNATURE_TRANSPORT);
EABuffer = ALLOCATE_POOL(PagedPool, sizeof(FILE_FULL_EA_INFORMATION)-1 +
TDI_CONNECTION_CONTEXT_LENGTH+1 +
sizeof(CONNECTION_CONTEXT), POOL_CONNCTX);
if (EABuffer == NULL) {
return(STATUS_INSUFFICIENT_RESOURCES);
}
EABuffer->NextEntryOffset = 0;
EABuffer->Flags = 0;
EABuffer->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH;
EABuffer->EaValueLength = sizeof(CONNECTION_CONTEXT);
RtlCopyMemory(EABuffer->EaName, TdiConnectionContext, TDI_CONNECTION_CONTEXT_LENGTH+1);
ContextPointer =
(CONNECTION_CONTEXT UNALIGNED *)&EABuffer->EaName[TDI_CONNECTION_CONTEXT_LENGTH+1];
*ContextPointer = ConnectionContext;
dprintf(DPRT_TDI, ("Create connection object on transport \"%wZ\"", &Transport->TransportName));
InitializeObjectAttributes (&AddressAttributes,
&Transport->TransportName, // Name
OBJ_CASE_INSENSITIVE, // Attributes
NULL, // RootDirectory
NULL); // SecurityDescriptor
Status = ZwCreateFile(Handle, // Handle
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
&AddressAttributes, // Object Attributes
&IoStatusBlock, // Final I/O status block
NULL, // Allocation Size
FILE_ATTRIBUTE_NORMAL, // Normal attributes
FILE_SHARE_READ | FILE_SHARE_WRITE, // Sharing attributes
FILE_OPEN_IF, // Create disposition
0, // CreateOptions
EABuffer, // EA Buffer
sizeof(FILE_FULL_EA_INFORMATION) +
TDI_CONNECTION_CONTEXT_LENGTH + 1 +
sizeof(CONNECTION_CONTEXT));
FREE_POOL(EABuffer);
if (NT_SUCCESS(Status)) {
dprintf(DPRT_TDI, ("RdrTdiOpenConnection: Returning connection handle %lx\n", *Handle));
Status = IoStatusBlock.Status;
}
return(Status);
}
NTSTATUS
RdrDoTdiConnect (
PIRP Irp OPTIONAL,
IN PFILE_OBJECT ConnectionObject,
IN PTRANSPORT_ADDRESS RemoteAddress,
OUT PTRANSPORT_ADDRESS ConnectedAddress,
IN ULONG RemoteTransportAddressSize,
IN ULONG ConnectedTransportAddressSize
)
/*++
Routine Description:
This routine submits a TdiConnect request to TDI and waits for it to complete.
Arguments:
IN PFILE_OBJECT ConnectionObject - Supplies a connection object for this connection request.
The connection object should have already been associated
with an address.
IN PTRANSPORT_ADDRESS RemoteAddress - Supplies the remote computer name to connect to.
OUT PTRANSPORT_ADDRESS ConnectedAddress - Returns the actual computer name connected to.
Return Value:
NTSTATUS - Final status of request.
--*/
{
NTSTATUS Status;
TDI_CONNECTION_INFORMATION RemoteConnectionInformation;
TDI_CONNECTION_INFORMATION ConnectedConnectionInformation;
BOOLEAN IrpAllocated = FALSE;
PDEVICE_OBJECT DeviceObject;
PAGED_CODE();
RemoteConnectionInformation.UserDataLength = 0;
RemoteConnectionInformation.OptionsLength = 0;
RemoteConnectionInformation.RemoteAddressLength =
RemoteTransportAddressSize;
RemoteConnectionInformation.RemoteAddress = RemoteAddress;
ConnectedConnectionInformation.UserDataLength = 0;
ConnectedConnectionInformation.OptionsLength = 0;
ConnectedConnectionInformation.RemoteAddressLength = RemoteConnectionInformation.RemoteAddressLength;
ConnectedConnectionInformation.RemoteAddress = ConnectedAddress;
if (!ARGUMENT_PRESENT(Irp)) {
Irp = ALLOCATE_IRP(ConnectionObject, NULL, 16, NULL);
if (Irp == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
return Status;
}
IrpAllocated = TRUE;
}
DeviceObject = IoGetRelatedDeviceObject(ConnectionObject);
TdiBuildConnect(Irp, DeviceObject, ConnectionObject,
NULL, NULL, &RdrTdiConnectTimeout,
&RemoteConnectionInformation, &ConnectedConnectionInformation);
Status = SubmitTdiRequest(DeviceObject, Irp);
if (IrpAllocated) {
FREE_IRP( Irp, 20, NULL );
}
if (Status == STATUS_IO_TIMEOUT) {
Status = STATUS_BAD_NETWORK_PATH;
}
return(Status);
}
DBGSTATIC
NTSTATUS
CompleteTdiRequest (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Ctx
)
/*++
Routine Description:
Completion routine for SubmitTdiRequest operation.
Arguments:
IN PDEVICE_OBJECT DeviceObject, - Supplies a pointer to the device object
IN PIRP Irp, - Supplies the IRP submitted
IN PVOID Context - Supplies a pointer to the kernel event to release
Return Value:
NTSTATUS - Status of KeSetEvent
We return STATUS_MORE_PROCESSING_REQUIRED to prevent the IRP completion
code from processing this puppy any more.
--*/
{
UNREFERENCED_PARAMETER(Irp);
UNREFERENCED_PARAMETER(DeviceObject);
dprintf(DPRT_TDI, ("CompleteTdiRequest: %lx\n", Ctx));
DISCARDABLE_CODE(RdrVCDiscardableSection);
//
// Set the event to the Signalled state with 0 priority increment and
// indicate that we will not be blocking soon.
//
KeSetEvent((PKEVENT) Ctx, 0, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
RdrQueryProviderInformation(
IN PFILE_OBJECT TransportObject,
OUT PTDI_PROVIDER_INFO ProviderInfo
)
/*++
Routine Description:
This routine will determine provider information about a transport.
Arguments:
IN PFILE_OBJECT TransportName - Supplies the name of the transport provider
Return Value:
Status of operation.
--*/
{
PIRP Irp;
PDEVICE_OBJECT DeviceObject;
PMDL Mdl = NULL;
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
DeviceObject = IoGetRelatedDeviceObject(TransportObject);
Irp = ALLOCATE_IRP(TransportObject, DeviceObject, 17, NULL);
if (Irp == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ReturnStatus;
}
//
// Allocate an MDL to hold the provider info.
//
Mdl = IoAllocateMdl(ProviderInfo, sizeof(TDI_PROVIDER_INFO),
FALSE,
FALSE,
NULL);
if (Mdl == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
FREE_IRP( Irp, 21, NULL );
goto ReturnStatus;
}
MmBuildMdlForNonPagedPool(Mdl);
TdiBuildQueryInformation(Irp, DeviceObject, TransportObject,
NULL, NULL,
TDI_QUERY_PROVIDER_INFORMATION, Mdl);
Status = SubmitTdiRequest(DeviceObject, Irp);
FREE_IRP( Irp, 22, NULL );
ReturnStatus:
if (Mdl != NULL) {
IoFreeMdl(Mdl);
}
return(Status);
}
NTSTATUS
RdrQueryServerAddresses(
IN PSERVERLISTENTRY Server,
OUT PUNICODE_STRING NBName,
OUT PTDI_ADDRESS_IP IPAddress
)
/*++
Routine Description:
This routine will determine a server's NetBIOS and IP address. Useful
for filling in the NBAddress and IPAddress fields of an SLE.
Arguments:
IN PSERVERLISTENTRY Server - The server whose NB/IP address is desired
OUT PUNICODE_STRING NBName - Buffer to fill with the NetBIOS name,
without the trailing spaces. Must have a
buffer of atleast 16 WCHARs
OUT PTDI_ADDRESS_IP IPAddress - Struct to fill in with the IP address
Return Value:
Status of operation.
--*/
{
PIRP Irp;
PFILE_OBJECT FileObject;
PDEVICE_OBJECT DeviceObject;
PMDL Mdl = NULL;
PNBT_ADDRESS_PAIR_INFO AddressPairInfo = NULL;
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
AddressPairInfo =
ALLOCATE_POOL(
NonPagedPool,
sizeof(NBT_ADDRESS_PAIR_INFO),
POOL_TRANSPORT);
if (AddressPairInfo == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ReturnStatus;
}
FileObject = Server->ConnectionContext->ConnectionObject;
DeviceObject = IoGetRelatedDeviceObject(FileObject);
Irp = ALLOCATE_IRP(FileObject, DeviceObject, 17, NULL);
if (Irp == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ReturnStatus;
}
//
// Allocate an MDL to hold the provider info.
//
Mdl = IoAllocateMdl(AddressPairInfo, sizeof(NBT_ADDRESS_PAIR_INFO),
FALSE,
FALSE,
NULL);
if (Mdl == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
FREE_IRP( Irp, 21, NULL );
goto ReturnStatus;
}
MmBuildMdlForNonPagedPool(Mdl);
TdiBuildQueryInformation(Irp, DeviceObject, FileObject,
NULL, NULL,
TDI_QUERY_ADDRESS_INFO, Mdl);
Status = SubmitTdiRequest(DeviceObject, Irp);
if (NT_SUCCESS(Status)) {
USHORT i;
ANSI_STRING nbNameInAnsi;
PTDI_ADDRESS_NETBIOS nbAddr;
ASSERT(NBName->MaximumLength >= 16);
nbAddr = &AddressPairInfo->AddressPair.AddressNetBIOS.Address;
//
// Pick up the NetBIOS name without the trailing spaces
//
for (i = 15; (i > 0) && (nbAddr->NetbiosName[i-1] == ' '); i--) {
NOTHING;
}
nbNameInAnsi.Length = i;
nbNameInAnsi.MaximumLength = i;
nbNameInAnsi.Buffer = nbAddr->NetbiosName;
RtlAnsiStringToUnicodeString(NBName, &nbNameInAnsi, FALSE);
*IPAddress = AddressPairInfo->AddressPair.AddressIP.Address;
}
FREE_IRP( Irp, 22, NULL );
ReturnStatus:
if (Mdl != NULL) {
IoFreeMdl(Mdl);
}
if (AddressPairInfo != NULL) {
FREE_POOL(AddressPairInfo);
}
return(Status);
}
NTSTATUS
RdrQueryConnectionInformation(
IN PSERVERLISTENTRY Server
)
/*++
Routine Description:
This routine will determine connection information about a transport connection.
Arguments:
IN PTRANSPORT_CONNECTION Connection
Return Value:
Status of operation.
--*/
{
PIRP Irp;
PDEVICE_OBJECT DeviceObject;
PMDL Mdl = NULL;
NTSTATUS Status = STATUS_SUCCESS;
TDI_CONNECTION_INFO ConnectionInfo;
RdrReferenceDiscardableCode(RdrVCDiscardableSection);
DISCARDABLE_CODE(RdrVCDiscardableSection);
Status = RdrReferenceTransportConnection(Server);
if (!NT_SUCCESS(Status)) {
RdrDereferenceDiscardableCode(RdrVCDiscardableSection);
return Status;
}
DeviceObject = IoGetRelatedDeviceObject(Server->ConnectionContext->ConnectionObject);
Irp = ALLOCATE_IRP(Server->ConnectionContext->ConnectionObject, DeviceObject, 18, NULL);
if (Irp == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ReturnStatus;
}
//
// Allocate an MDL to hold the connection info.
//
Mdl = IoAllocateMdl(&ConnectionInfo, sizeof(TDI_CONNECTION_INFO),
FALSE,
FALSE,
NULL);
if (Mdl == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
FREE_IRP( Irp, 23, NULL );
goto ReturnStatus;
}
MmBuildMdlForNonPagedPool(Mdl);
TdiBuildQueryInformation(Irp, DeviceObject, Server->ConnectionContext->ConnectionObject,
NULL, NULL,
TDI_QUERY_CONNECTION_INFO, Mdl);
Status = SubmitTdiRequest(DeviceObject, Irp);
FREE_IRP( Irp, 24, NULL );
if (NT_SUCCESS(Status)) {
KIRQL OldIrql;
LARGE_INTEGER WriteBehindAmount;
LARGE_INTEGER ReadAheadThroughput;
ReadAheadThroughput.QuadPart = Int32x32To64(RdrData.ReadAheadThroughput, 1024); // Change to bytes per second
ACQUIRE_SPIN_LOCK(&RdrServerConnectionValidSpinLock, &OldIrql);
if ( ConnectionInfo.Unreliable == FALSE ) {
Server->Reliable = TRUE;
} else {
Server->Reliable = FALSE;
}
//
// If the throughput didn't change, bail out right now.
//
if (ConnectionInfo.Throughput.HighPart == 0 &&
ConnectionInfo.Throughput.LowPart == Server->Throughput) {
RELEASE_SPIN_LOCK(&RdrServerConnectionValidSpinLock, OldIrql);
goto ReturnStatus;
}
if (ConnectionInfo.Delay.QuadPart != 0) {
if (ConnectionInfo.Delay.QuadPart == -1) {
Server->Delay = 0;
} else if (ConnectionInfo.Delay.HighPart != 0xffffffff) {
Server->Delay = 0xffffffff;
} else {
Server->Delay = -1 * ConnectionInfo.Delay.LowPart;
}
} else {
Server->Delay = 0;
}
if (ConnectionInfo.Throughput.QuadPart == -1) {
Server->Throughput = 0;
} else if (ConnectionInfo.Throughput.HighPart != 0) {
Server->Throughput = 0xffffffff;
} else {
Server->Throughput = ConnectionInfo.Throughput.LowPart;
}
if( Server->SupportsRawRead ) {
Server->ReadAhead = (Server->RawReadMaximum >= 32*1024);
} else {
Server->ReadAhead = ConnectionInfo.Throughput.QuadPart > ReadAheadThroughput.QuadPart;
}
//
// Calculate the amount of data transfered in 30 seconds.
//
if( Server->SupportsRawWrite ) {
if( RdrRawTimeLimit != 0 && RdrRawTimeLimit < 30 ) {
Server->ThirtySecondsOfData.QuadPart = Server->RawWriteMaximum
* ((30 + RdrRawTimeLimit - 1) / RdrRawTimeLimit);
} else {
Server->ThirtySecondsOfData.QuadPart = ConnectionInfo.Throughput.QuadPart
* WRITE_BEHIND_AMOUNT_TIME;
}
} else if (Server->Throughput != 0) {
//
// Save away the # of bytes that can be written in 30 seconds.
//
Server->ThirtySecondsOfData.QuadPart = ConnectionInfo.Throughput.QuadPart
* WRITE_BEHIND_AMOUNT_TIME;
} else {
Server->ThirtySecondsOfData.QuadPart = 0;
}
WriteBehindAmount.QuadPart = Server->ThirtySecondsOfData.QuadPart + PAGE_SIZE - 1;
WriteBehindAmount.QuadPart = WriteBehindAmount.QuadPart / PAGE_SIZE;
if (WriteBehindAmount.HighPart == 0) {
Server->WriteBehindPages = WriteBehindAmount.LowPart;
}
RELEASE_SPIN_LOCK(&RdrServerConnectionValidSpinLock, OldIrql);
}
ReturnStatus:
if (Mdl != NULL) {
IoFreeMdl(Mdl);
}
RdrDereferenceTransportConnection(Server);
RdrDereferenceDiscardableCode(RdrVCDiscardableSection);
return(Status);
}
NTSTATUS
RdrQueryAdapterStatus (
IN PFILE_OBJECT TransportObject,
OUT PADAPTER_STATUS AdapterStatus
)
/*++
Routine Description:
This routine will determine address information about a transport.
Arguments:
IN PFILE_OBJECT TransportName - Supplies the name of the transport provider
Return Value:
Status of operation.
--*/
{
PIRP Irp;
PDEVICE_OBJECT DeviceObject;
PMDL Mdl = NULL;
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
DeviceObject = IoGetRelatedDeviceObject(TransportObject);
Irp = ALLOCATE_IRP(TransportObject, DeviceObject, 19, NULL);
if (Irp == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ReturnStatus;
}
//
// Allocate an MDL to hold the provider info.
//
Mdl = IoAllocateMdl(AdapterStatus, sizeof(ADAPTER_STATUS),
FALSE,
FALSE,
NULL);
if (Mdl == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
FREE_IRP( Irp, 25, NULL );
goto ReturnStatus;
}
MmBuildMdlForNonPagedPool(Mdl);
TdiBuildQueryInformation(Irp, DeviceObject, TransportObject,
NULL, NULL,
TDI_QUERY_ADAPTER_STATUS, Mdl);
Status = SubmitTdiRequest(DeviceObject, Irp);
FREE_IRP( Irp, 26, NULL );
ReturnStatus:
if (Mdl != NULL) {
IoFreeMdl(Mdl);
}
return(Status);
}
NTSTATUS
RdrpTdiCreateAddress (
IN PTRANSPORT Transport
)
/*++
Routine Description:
This routine creates a transport address object.
Arguments:
IN PTRANSPORT Transport - Supplies a transport structure describing the
transport address object to be created.
Return Value:
NTSTATUS - Status of resulting operation.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
OBJECT_ATTRIBUTES AddressAttributes;
IO_STATUS_BLOCK IoStatusBlock;
PFILE_FULL_EA_INFORMATION EABuffer;
TDI_PROVIDER_INFO ProviderInfo;
ADAPTER_STATUS AdapterStatus;
PDEVICE_OBJECT DeviceObject;
PAGED_CODE();
ASSERT(Transport->Signature == STRUCTURE_SIGNATURE_TRANSPORT);
//
// Access the redirector name resource for shared access
//
ExAcquireResourceShared(&RdrDataResource, TRUE);
EABuffer = ALLOCATE_POOL(PagedPool, sizeof(FILE_FULL_EA_INFORMATION)-1 +
TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
sizeof(TA_NETBIOS_ADDRESS), POOL_NETBADDR);
if (EABuffer == NULL) {
return(STATUS_INSUFFICIENT_RESOURCES);
}
EABuffer->NextEntryOffset = 0;
EABuffer->Flags = 0;
EABuffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH;
EABuffer->EaValueLength = sizeof(TA_NETBIOS_ADDRESS);
RtlCopyMemory(EABuffer->EaName, TdiTransportAddress, EABuffer->EaNameLength+1);
RtlCopyMemory(&EABuffer->EaName[TDI_TRANSPORT_ADDRESS_LENGTH+1], RdrData.ComputerName,
EABuffer->EaValueLength);
dprintf(DPRT_TDI, ("Create endpoint of \"%wZ\"", &Transport->TransportName));
InitializeObjectAttributes (&AddressAttributes,
&Transport->TransportName, // Name
OBJ_CASE_INSENSITIVE,// Attributes
NULL, // RootDirectory
NULL); // SecurityDescriptor
Status = ZwCreateFile(&Transport->Handle, // Handle
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
&AddressAttributes, // Object Attributes
&IoStatusBlock, // Final I/O status block
NULL, // Allocation Size
FILE_ATTRIBUTE_NORMAL, // Normal attributes
FILE_SHARE_READ,// Sharing attributes
FILE_OPEN_IF, // Create disposition
0, // CreateOptions
EABuffer, // EA Buffer
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) +
TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
sizeof(TA_NETBIOS_ADDRESS)); // EA length
FREE_POOL(EABuffer);
if (!NT_SUCCESS(Status)) {
goto error_cleanup;
}
if (!NT_SUCCESS(Status = IoStatusBlock.Status)) {
goto error_cleanup;
}
//
// Obtain a referenced pointer to the file object.
//
Status = ObReferenceObjectByHandle (
Transport->Handle,
0,
*IoFileObjectType,
KernelMode,
(PVOID *)&Transport->FileObject,
NULL
);
if (!NT_SUCCESS(Status)) {
goto error_cleanup;
}
//
// Get the address of the device object for the endpoint.
//
DeviceObject = Transport->NonPagedTransport->DeviceObject = IoGetRelatedDeviceObject(Transport->FileObject);
//
// Register the redirector's Receive event handler.
//
Status = RdrpTdiSetEventHandler(DeviceObject, Transport->FileObject, TDI_EVENT_RECEIVE,
(PVOID )RdrTdiReceiveHandler);
if (!NT_SUCCESS(Status)) {
goto error_cleanup;
}
//
// Register the redirector's Disconnection event handler.
//
Status = RdrpTdiSetEventHandler(DeviceObject, Transport->FileObject, TDI_EVENT_DISCONNECT,
(PVOID )RdrTdiDisconnectHandler);
if (!NT_SUCCESS(Status)) {
goto error_cleanup;
}
Status = RdrQueryProviderInformation(Transport->FileObject, &ProviderInfo);
if (NT_SUCCESS(Status)) {
Transport->MaximumDatagramSize = ProviderInfo.MaxDatagramSize;
Transport->Wannish = BooleanFlagOn(ProviderInfo.ServiceFlags, TDI_SERVICE_ROUTE_DIRECTED);
} else {
Transport->MaximumDatagramSize = 512;
Transport->Wannish = FALSE;
}
Status = RdrQueryAdapterStatus(Transport->FileObject, &AdapterStatus);
if (!NT_ERROR(Status)) {
ULONG i;
#define tohexdigit(a) ((CHAR)( (a) > 9 ? ((a) + 'A' - 0xA) : ((a) + '0') ))
for ( i = 0; i < 6; i++ ) {
Transport->AdapterAddress[2*i] = tohexdigit( (AdapterStatus.adapter_address[i] >> 4) & 0x0F );
Transport->AdapterAddress[2*i+1] = tohexdigit( AdapterStatus.adapter_address[i] & 0x0F );
}
//
// Null terminate the adapter address.
//
Transport->AdapterAddress[2*i] = '\0';
} else {
STRCPY(Transport->AdapterAddress, TEXT("000000000000"));
}
ExReleaseResource(&RdrDataResource);
return STATUS_SUCCESS;
error_cleanup:
if ( Transport->FileObject != NULL ) {
ObDereferenceObject( Transport->FileObject );
Transport->FileObject = NULL;
}
if ( Transport->Handle != NULL ) {
ZwClose( Transport->Handle );
Transport->Handle = NULL;
}
ExReleaseResource(&RdrDataResource);
return Status;
}
DBGSTATIC
NTSTATUS
RdrpTdiSetEventHandler (
IN PDEVICE_OBJECT DeviceObject,
IN PFILE_OBJECT FileObject,
IN ULONG EventType,
IN PVOID EventHandler
)
/*++
Routine Description:
This routine registers an event handler with a TDI transport provider.
Arguments:
IN PDEVICE_OBJECT DeviceObject - Supplies the device object of the transport provider.
IN PFILE_OBJECT FileObject - Supplies the address object's file object.
IN ULONG EventType, - Supplies the type of event.
IN PVOID EventHandler - Supplies the event handler.
Return Value:
NTSTATUS - Final status of the set event operation
--*/
{
NTSTATUS Status;
PIRP Irp;
PAGED_CODE();
Irp = ALLOCATE_IRP(FileObject, NULL, 20, NULL);
if (Irp == NULL) {
return(STATUS_INSUFFICIENT_RESOURCES);
}
TdiBuildSetEventHandler(Irp, DeviceObject, FileObject,
NULL, NULL,
EventType, EventHandler, FileObject);
Status = SubmitTdiRequest(DeviceObject, Irp);
FREE_IRP( Irp, 27, NULL );
return Status;
}
NTSTATUS
RdrBuildNetbiosAddress (
IN PTRANSPORT_ADDRESS TransportAddress,
IN ULONG TransportAddressLength,
IN PUNICODE_STRING Name
)
/*++
Routine Description:
This routine takes a computer name (PUNICODE_STRING) and converts it into an
acceptable form for passing in as transport address.
Arguments:
OUT TRANSPORT_ADDRESS RemoteAddress, - Supplies the structure to fill in
IN TransportAddressLength - Supplies the length of the buffer at TransportAddress
IN PUNICODE_STRING Name - Supplies the name to put into the transport
Return Value:
None.
--*/
{
OEM_STRING ComputerName;
NTSTATUS Status;
PTA_NETBIOS_ADDRESS pNetbiosAddress = (PTA_NETBIOS_ADDRESS)TransportAddress;
PAGED_CODE();
if( TransportAddressLength < sizeof( TA_NETBIOS_ADDRESS ) ) {
return STATUS_BUFFER_OVERFLOW;
}
pNetbiosAddress->TAAddressCount = 1;
pNetbiosAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS;
pNetbiosAddress->Address[0].Address[0].NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
ComputerName.Buffer = pNetbiosAddress->Address[0].Address[0].NetbiosName;
ComputerName.MaximumLength = NETBIOS_NAME_LEN + (USHORT)TransportAddressLength - sizeof( TA_NETBIOS_ADDRESS );
Status = RtlUpcaseUnicodeStringToOemString(&ComputerName, Name, FALSE);
if( !NT_SUCCESS( Status ) ) {
return STATUS_BAD_NETWORK_PATH;
}
TransportAddress->Address[0].AddressLength = TDI_ADDRESS_LENGTH_NETBIOS;
if( ComputerName.Length < NETBIOS_NAME_LEN ) {
//
// Ensure that the name is at least big enough to fit in the TA_NETBIOS_ADDRESS
// structure. If it is too small, then pad it on out with blanks.
//
RtlCopyMemory( &ComputerName.Buffer[ ComputerName.Length ],
" ",
NETBIOS_NAME_LEN - ComputerName.Length
);
} else {
//
// If the passed-in name is longer than a netbios name, then just reflect it
// into the structure. Transports will reject if if they can't handle it.
//
TransportAddress->Address[0].AddressLength += ComputerName.Length - NETBIOS_NAME_LEN;
}
return STATUS_SUCCESS;
}
BOOLEAN
RdrIsLoopBack(
PUNICODE_STRING pServerName)
/*++
Routine Description:
This routine takes a computer name (PUNICODE_STRING) and determines if it is the same
as the client machine, i.e. a loopback has occurred.
Arguments:
IN PUNICODE_STRING Name - Supplies the name to put into the transport
Return Value:
TRUE if loop back FALSE otherwise
Notes:
This routine needs to be changed so that we merely compare the names. This should be
done by modifying the redirector data initialization so that a UNICODE_STRING is
stored for the client name.
--*/
{
BOOLEAN LoopBack;
NTSTATUS Status;
OEM_STRING OemServerName;
CHAR OemStringBuffer[NETBIOS_NAME_LEN];
UNICODE_STRING ComputerName;
OemServerName.MaximumLength = NETBIOS_NAME_LEN;
OemServerName.Buffer = OemStringBuffer;
ComputerName.MaximumLength = (NETBIOS_NAME_LEN - 1) * sizeof(WCHAR);
if (pServerName->Length > (NETBIOS_NAME_LEN * sizeof(WCHAR))) {
ComputerName.Length = ComputerName.MaximumLength;
} else {
ComputerName.Length = pServerName->Length;
}
ComputerName.Buffer = pServerName->Buffer;
Status = RtlUpcaseUnicodeStringToOemString(&OemServerName, &ComputerName, FALSE);
ASSERT(Status == STATUS_SUCCESS);
LoopBack = (BOOLEAN)RtlEqualMemory(
OemServerName.Buffer,
RdrData.ComputerName->Address[0].Address[0].NetbiosName,
OemServerName.Length);
return LoopBack;
}
ULONG
RdrComputeTransportAddressSize(
IN PUNICODE_STRING pServerName)
/*++
Routine Description:
This routine takes a computer name (PUNICODE_STRING) and computes the size of the
TRANSPORT_ADDRESSS buffer required to connect to it.
Arguments:
IN PUNICODE_STRING Name - Supplies the name to put into the transport
Return Value:
size of the buffer.
Notes:
The compound transport address passed to the transports consists of two
TDI_NETBIOS_EX_ADDRESSes and a TDI_NETBIOS_ADDRESS. The two NETBIOS_EX addresses refer
to the two different endpoints registered by the server, i.e., *SMBSERVER and
the Server name padded upto NETBIOS_ANEM_LEN with blanks. The order in which
the two NETBIOS_EX addresses are constructed depend upon the length of the server
name. If it is greater than NETBIOS_NAME_LEN *SMBSERVER is the first enpoint
and vice versa
--*/
{
ULONG NetbiosAddressLength,NetbiosExAddressLength,TransportAddressSize;
ULONG OemServerNameLength;
OemServerNameLength = pServerName->Length / sizeof(WCHAR);
NetbiosAddressLength = sizeof(TDI_ADDRESS_NETBIOS);
if( OemServerNameLength > NETBIOS_NAME_LEN ) {
NetbiosAddressLength += OemServerNameLength - NETBIOS_NAME_LEN;
}
NetbiosExAddressLength = FIELD_OFFSET(TDI_ADDRESS_NETBIOS_EX,NetbiosAddress) +
NetbiosAddressLength;
TransportAddressSize = FIELD_OFFSET(TRANSPORT_ADDRESS,Address) +
3 * FIELD_OFFSET(TA_ADDRESS,Address) +
NetbiosAddressLength +
2 * NetbiosExAddressLength;
return TransportAddressSize;
}
NTSTATUS
RdrBuildTransportAddress (
IN PTRANSPORT_ADDRESS pTransportAddress,
IN ULONG TransportAddressLength,
IN PUNICODE_STRING pServerName
)
/*++
Routine Description:
This routine takes a computer name (PUNICODE_STRING) and converts it into an
acceptable form for passing in as transport address.
Arguments:
OUT TRANSPORT_ADDRESS RemoteAddress, - Supplies the structure to fill in
IN TransportAddressLength - Supplies the length of the buffer at TransportAddress
IN PUNICODE_STRING Name - Supplies the name to put into the transport
Return Value:
None.
Notes:
The compound transport address passed to the transports consists of two
TDI_NETBIOS_EX_ADDRESSes and a TDI_NETBIOS_ADDRESS. The two NETBIOS_EX addresses refer
to the two different endpoints registered by the server, i.e., *SMBSERVER and
the Server name padded upto NETBIOS_NAME_LEN with blanks. The order in which
the two NETBIOS_EX addresses are constructed depend upon the length of the server
name. If it is greater than NETBIOS_NAME_LEN *SMBSERVER is the first enpoint
and vice versa
The WINS database can be inconsistent for extended periods of time. In order to
account for this inconsistency on NETBIOS names and DNS names we will not
issue the address for *SMBSERVER. This will be revisited when we have a better
mechanism for identifying/authenticating the server and the client machine to each other.
--*/
{
OEM_STRING OemServerName;
NTSTATUS Status;
PTDI_ADDRESS_NETBIOS_EX pTdiNetbiosExAddress;
PTDI_ADDRESS_NETBIOS pTdiNetbiosAddress;
PTA_ADDRESS pFirstNetbiosExAddress,pSecondNetbiosExAddress,pNetbiosAddress;
PCHAR FirstEndpointName,SecondEndpointName;
CHAR EndpointNameBuffer[NETBIOS_NAME_LEN];
USHORT NetbiosAddressLength,NetbiosExAddressLength;
USHORT NetbiosAddressType = TDI_ADDRESS_TYPE_NETBIOS;
ULONG ComponentLength;
ULONG RemoteIpAddress;
BOOLEAN ServerNameIsInIpAddressForm;
PAGED_CODE();
if (TransportAddressLength < RdrComputeTransportAddressSize(pServerName)) {
return STATUS_BUFFER_OVERFLOW;
}
pFirstNetbiosExAddress = &pTransportAddress->Address[0];
pTdiNetbiosExAddress = (PTDI_ADDRESS_NETBIOS_EX)pFirstNetbiosExAddress->Address;
pTdiNetbiosExAddress->NetbiosAddress.NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_QUICK_UNIQUE;
OemServerName.Length = pServerName->Length;
OemServerName.MaximumLength = OemServerName.Length + 1;
OemServerName.Buffer = pTdiNetbiosExAddress->NetbiosAddress.NetbiosName;
Status = RtlUpcaseUnicodeStringToOemString(&OemServerName, pServerName, FALSE);
if( !NT_SUCCESS( Status ) ) {
return STATUS_BAD_NETWORK_PATH;
}
if (OemServerName.Length < NETBIOS_NAME_LEN) {
RtlCopyMemory( &OemServerName.Buffer[ OemServerName.Length ],
" ",
NETBIOS_NAME_LEN - OemServerName.Length
);
OemServerName.Length = NETBIOS_NAME_LEN;
}
Status = RdrpTranslateNetbiosNameToIpAddress(&OemServerName,&RemoteIpAddress);
if (Status == STATUS_SUCCESS) {
if ((RemoteIpAddress == 0) || (RemoteIpAddress == 0xffffffff)) {
// If the server name is a valid IP address and matches with one of the two
// broadcast addresses used by IP turn back the request.
return STATUS_INVALID_ADDRESS_COMPONENT;
}
ServerNameIsInIpAddressForm = TRUE;
} else {
ServerNameIsInIpAddressForm = FALSE;
}
NetbiosAddressLength = sizeof(TDI_ADDRESS_NETBIOS);
if( OemServerName.Length > NETBIOS_NAME_LEN ) {
NetbiosAddressLength += OemServerName.Length - NETBIOS_NAME_LEN;
}
NetbiosExAddressLength = FIELD_OFFSET(TDI_ADDRESS_NETBIOS_EX,NetbiosAddress) +
NetbiosAddressLength;
pFirstNetbiosExAddress->AddressLength = NetbiosExAddressLength;
pFirstNetbiosExAddress->AddressType = TDI_ADDRESS_TYPE_NETBIOS_EX;
#if 0
// This arm of the code will be activated and the other arm deactivated when we have
// mutual authenitication between server and client machines in NT5.0
if (ServerNameIsInIpAddressForm) {
pTransportAddress->TAAddressCount = 2;
pNetbiosAddress = (PTA_ADDRESS)((PCHAR)pFirstNetbiosExAddress +
FIELD_OFFSET(TA_ADDRESS,Address) +
NetbiosExAddressLength);
FirstEndpointName = SMBSERVER_LOCAL_ENDPOINT_NAME;
} else {
pTransportAddress->TAAddressCount = 3;
pSecondNetbiosExAddress = (PTA_ADDRESS)((PCHAR)pFirstNetbiosExAddress +
FIELD_OFFSET(TA_ADDRESS,Address) +
NetbiosExAddressLength);
pNetbiosAddress = (PTA_ADDRESS)((PCHAR)pSecondNetbiosExAddress +
FIELD_OFFSET(TA_ADDRESS,Address) +
NetbiosExAddressLength);
// Scan the server name till the first delimiter (DNS delimiter .) and form
// the endpoint name by padding the remaining name with blanks.
RtlCopyMemory(
EndpointNameBuffer,
OemServerName.Buffer,
NETBIOS_NAME_LEN);
ComponentLength = 0;
while (ComponentLength < NETBIOS_NAME_LEN) {
if (EndpointNameBuffer[ComponentLength] == '.') {
break;
}
ComponentLength++;
}
if (ComponentLength == NETBIOS_NAME_LEN) {
EndpointNameBuffer[NETBIOS_NAME_LEN - 1] = ' ';
} else {
RtlCopyMemory(&EndpointNameBuffer[ComponentLength],
" ",
NETBIOS_NAME_LEN - ComponentLength);
}
FirstEndpointName = EndpointNameBuffer;
SecondEndpointName = SMBSERVER_LOCAL_ENDPOINT_NAME;
}
#else
pTransportAddress->TAAddressCount = 2;
pNetbiosAddress = (PTA_ADDRESS)((PCHAR)pFirstNetbiosExAddress +
FIELD_OFFSET(TA_ADDRESS,Address) +
NetbiosExAddressLength);
if (ServerNameIsInIpAddressForm) {
FirstEndpointName = SMBSERVER_LOCAL_ENDPOINT_NAME;
} else {
// Scan the server name till the first delimiter (DNS delimiter .) and form
// the endpoint name by padding the remaining name with blanks.
RtlCopyMemory(
EndpointNameBuffer,
OemServerName.Buffer,
NETBIOS_NAME_LEN);
ComponentLength = 0;
while (ComponentLength < NETBIOS_NAME_LEN) {
if (EndpointNameBuffer[ComponentLength] == '.') {
break;
}
ComponentLength++;
}
if (ComponentLength == NETBIOS_NAME_LEN) {
EndpointNameBuffer[NETBIOS_NAME_LEN - 1] = ' ';
} else {
RtlCopyMemory(&EndpointNameBuffer[ComponentLength],
" ",
NETBIOS_NAME_LEN - ComponentLength);
}
FirstEndpointName = EndpointNameBuffer;
}
#endif
// Copy the first endpoint name
RtlCopyMemory(
pTdiNetbiosExAddress->EndpointName,
FirstEndpointName,
NETBIOS_NAME_LEN);
#if 0
// This will be activated alongwith the other code when mutual authentication is
// in place
if (!ServerNameIsInIpAddressForm) {
// The same NETBIOS_EX address needs to be duplicated with a different endpoint name
// for the second TA_ADDRESS.
RtlCopyMemory(
pSecondNetbiosExAddress,
pFirstNetbiosExAddress,
(FIELD_OFFSET(TA_ADDRESS,Address) + NetbiosExAddressLength));
RtlCopyMemory(
((PCHAR)pSecondNetbiosExAddress +
FIELD_OFFSET(TA_ADDRESS,Address) +
FIELD_OFFSET(TDI_ADDRESS_NETBIOS_EX,EndpointName)),
SecondEndpointName,
NETBIOS_NAME_LEN);
}
#else
ASSERT(pTransportAddress->TAAddressCount == 2);
#endif
// The Netbios address associated with the first NETBIOS_EX address is the last netbios
// address that is passed in.
RtlCopyMemory(
((PCHAR)pNetbiosAddress),
&NetbiosAddressLength,
sizeof(USHORT));
RtlCopyMemory(
((PCHAR)pNetbiosAddress + FIELD_OFFSET(TA_ADDRESS,AddressType)),
&NetbiosAddressType,
sizeof(USHORT));
RtlCopyMemory(
((PCHAR)pNetbiosAddress + FIELD_OFFSET(TA_ADDRESS,Address)),
&pTdiNetbiosExAddress->NetbiosAddress,
NetbiosAddressLength);
return STATUS_SUCCESS;
}
VOID
RdrpInitializeTdi (
VOID
)
/*++
Routine Description:
This routine initializes the global variables used in the transport
package.
Arguments:
None.
Return Value:
None.
--*/
{
PAGED_CODE();
RdrTdiPollTimeout.QuadPart = Int32x32To64(RDR_TDI_POLL_TIMEOUT, -10000);
RdrTdiConnectTimeout.QuadPart = Int32x32To64(RdrTdiConnectTimeoutSeconds, 1000 * -10000);
RdrTdiDisconnectTimeout.QuadPart = Int32x32To64(RdrTdiDisconnectTimeoutSeconds, 1000 * -10000);
//
// Allocate a spin lock to protect the transport chain
//
ExInitializeResource(&RdrTransportResource);
//
// Initialize the Transport list chain
//
InitializeListHead(&RdrTransportHead);
RdrTransportIndex = 0;
//
// Allocate a spin lock to protect the reference count in the Transport.
//
KeInitializeSpinLock(&RdrTransportReferenceSpinLock);
}
NTSTATUS
RdrpTranslateNetbiosNameToIpAddress(
IN OEM_STRING *pName,
OUT ULONG *pIpAddress
)
/*++
Routine Description:
This routine converts ascii ipaddr (11.101.4.25) into a ULONG. This is
based on the inet_addr code in winsock
Arguments:
pName - the string containing the ipaddress
Return Value:
the ipaddress as a ULONG if it's a valid ipaddress. Otherwise, 0.
Notes:
The body of this routine has been borrowed fron NetBt.
--*/
{
NTSTATUS Status;
PCHAR pStr;
int i;
int len, fieldLen;
int fieldsDone;
ULONG IpAddress;
BYTE ByteVal;
PCHAR pIpPtr;
BOOLEAN fDotFound;
BOOLEAN fieldOk;
Status = STATUS_INVALID_ADDRESS_COMPONENT;
if (pName->Length > NETBIOS_NAME_LEN) {
return Status;
}
pStr = pName->Buffer;
len = 0;
pIpPtr = (PCHAR)&IpAddress;
pIpPtr += 3; // so that we store in network order
fieldsDone=0;
//
// the 11.101.4.25 format can be atmost 15 chars, and pName is guaranteed
// to be at least 16 chars long (how convenient!!). Convert the string to
// a ULONG.
//
while(len < NETBIOS_NAME_LEN)
{
fieldLen=0;
fieldOk = FALSE;
ByteVal = 0;
fDotFound = FALSE;
//
// This loop traverses each of the four fields (max len of each
// field is 3, plus 1 for the '.'
//
while (fieldLen < 4)
{
if (*pStr >='0' && *pStr <='9')
{
ByteVal = (ByteVal*10) + (*pStr - '0');
fieldOk = TRUE;
}
else if (*pStr == '.' || *pStr == ' ' || *pStr == '\0')
{
*pIpPtr = ByteVal;
pIpPtr--;
fieldsDone++;
if (*pStr == '.')
fDotFound = TRUE;
// if we got a space or 0, assume it's the 4th field
if (*pStr == ' ' || *pStr == '\0')
{
break;
}
}
// unacceptable char: can't be ipaddr
else
{
return(Status);
}
pStr++;
len++;
fieldLen++;
// if we found the dot, we are done with this field: go to the next one
if (fDotFound)
break;
}
// this field wasn't ok (e.g. "11.101..4" or "11.101.4." etc.)
if (!fieldOk)
{
return(Status);
}
// if we are done with all 4 fields, we are done with the outer loop too
if ( fieldsDone == 4)
break;
if (!fDotFound)
{
return(Status);
}
}
//
// make sure the remaining chars are spaces or 0's (i.e. don't allow
// 11.101.4.25xyz to succeed)
//
for (i=len; i<NETBIOS_NAME_LEN; i++, pStr++)
{
if (*pStr != ' ' && *pStr != '\0')
{
return(Status);
}
}
*pIpAddress = IpAddress;
return( STATUS_SUCCESS );
}
#if MAGIC_BULLET
DBGSTATIC
NTSTATUS
CompleteMagicBullet (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
RdrSendMagicBullet (
IN PTRANSPORT Transport
)
/*++
Routine Description:
This routine sends the "magic bullet" that allows a network sniffer
to be stopped when an unexpected event happens.
Arguments:
IN PTRANSPORT Transport - Specifies the transport to send the bullet on.
Return Value:
NTSTATUS - This will always be STATUS_PENDING.
Note:
This routine is called at DPC_LEVEL, and thus must not block.
--*/
{
PIRP Irp = NULL;
PIO_STACK_LOCATION IrpSp;
if (MagicBulletFileObject == NULL ||
MagicBulletDeviceObject == NULL) {
return STATUS_SUCCESS;
}
Irp = ALLOCATE_IRP(MagicBulletFileObject, MagicBulletDeviceObject, 21, NULL);
if (Irp == NULL) {
return(STATUS_INSUFFICIENT_RESOURCES);
}
IrpSp = IoGetNextIrpStackLocation(Irp);
IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
IrpSp->MinorFunction = 0x7f;
IoSetCompletionRoutine(Irp, CompleteMagicBullet, NULL, TRUE, TRUE, TRUE);
return IoCallDriver(MagicBulletDeviceObject, Irp);
}
DBGSTATIC
NTSTATUS
CompleteMagicBullet (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
FREE_IRP( Irp, 28, NULL );
return(STATUS_MORE_PROCESSING_REQUIRED);
UNREFERENCED_PARAMETER(DeviceObject);
UNREFERENCED_PARAMETER(Context);
}
#endif // DBG