5148 lines
139 KiB
C
5148 lines
139 KiB
C
/*++
|
||
|
||
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
|
||
|