mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1284 lines
34 KiB
1284 lines
34 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
loopback.c
|
|
|
|
Abstract:
|
|
|
|
This module implements a loopback Transport Provider driver for NT
|
|
LAN Manager.
|
|
|
|
Author:
|
|
|
|
Chuck Lenzmeier (chuckl) 8-Oct-1989
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "loopback.h"
|
|
|
|
extern POBJECT_TYPE *IoDeviceObjectType;
|
|
|
|
//
|
|
// Global variables
|
|
//
|
|
|
|
ULONG LoopDebug = 0;
|
|
|
|
//
|
|
// The address of the loopback device object (there's only one) is kept
|
|
// in global storage to avoid having to pass it from routine to routine.
|
|
//
|
|
|
|
PLOOP_DEVICE_OBJECT LoopDeviceObject;
|
|
|
|
//
|
|
// LoopProviderInfo is a structure containing information that may be
|
|
// obtained using TdiQueryInformation.
|
|
//
|
|
|
|
TDI_PROVIDER_INFO LoopProviderInfo;
|
|
|
|
//
|
|
// I/O system forward declarations
|
|
//
|
|
|
|
STATIC
|
|
NTSTATUS
|
|
LoopDispatchCleanup (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
STATIC
|
|
NTSTATUS
|
|
LoopDispatchClose (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
STATIC
|
|
NTSTATUS
|
|
LoopDispatchCreate (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
STATIC
|
|
NTSTATUS
|
|
LoopDispatchDeviceControl (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
STATIC
|
|
NTSTATUS
|
|
LoopDispatchInternalDeviceControl (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
STATIC
|
|
VOID
|
|
LoopUnload (
|
|
IN PDRIVER_OBJECT DriverObject
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
DriverEntry (
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the initialization routine for the LAN Manager loopback
|
|
driver. This routine creates the device object for the loopback
|
|
device and performs all other driver initialization.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to driver object created by the system.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status from the initialization operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
STRING deviceName;
|
|
UNICODE_STRING unicodeString;
|
|
|
|
#ifdef MEMPRINT
|
|
MemPrintInitialize( );
|
|
#endif
|
|
|
|
IF_DEBUG(LOOP1) DbgPrint( "LoopInitialize entered\n" );
|
|
|
|
//
|
|
// Create the device object. (IoCreateDevice zeroes the memory
|
|
// occupied by the object.)
|
|
//
|
|
|
|
RtlInitString( &deviceName, LOOPBACK_DEVICE_NAME );
|
|
|
|
status = RtlAnsiStringToUnicodeString(
|
|
&unicodeString,
|
|
&deviceName,
|
|
TRUE
|
|
);
|
|
|
|
ASSERT( NT_SUCCESS(status) );
|
|
|
|
status = IoCreateDevice(
|
|
DriverObject, // DriverObject
|
|
LOOP_DEVICE_EXTENSION_LENGTH, // DeviceExtension
|
|
&unicodeString, // DeviceName
|
|
FILE_DEVICE_NETWORK, // DeviceType
|
|
0, // DeviceCharacteristics
|
|
FALSE, // Exclusive
|
|
(PDEVICE_OBJECT *) &LoopDeviceObject // DeviceObject
|
|
);
|
|
|
|
RtlFreeUnicodeString( &unicodeString );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
return status;
|
|
}
|
|
|
|
IF_DEBUG(LOOP1) DbgPrint( " Loop device object: %lx\n", LoopDeviceObject );
|
|
|
|
//
|
|
// Initialize the driver object for this driver's entry points.
|
|
//
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = LoopDispatchCreate;
|
|
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = LoopDispatchCleanup;
|
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] = LoopDispatchClose;
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
|
|
LoopDispatchDeviceControl;
|
|
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] =
|
|
LoopDispatchInternalDeviceControl;
|
|
DriverObject->DriverUnload = LoopUnload;
|
|
|
|
//
|
|
// Allocate the spin lock.
|
|
//
|
|
|
|
KeInitializeSpinLock( &LoopDeviceObject->SpinLock );
|
|
|
|
DEBUG LoopDeviceObject->SavedIrql = (KIRQL)-1;
|
|
|
|
//
|
|
// Initialize the address and connection endpoint list heads.
|
|
//
|
|
|
|
InitializeListHead( &LoopDeviceObject->EndpointList );
|
|
InitializeListHead( &LoopDeviceObject->ConnectionList );
|
|
|
|
//
|
|
// Initialize the provider information structure.
|
|
//
|
|
|
|
RtlZeroMemory( &LoopProviderInfo, sizeof(LoopProviderInfo) );
|
|
LoopProviderInfo.Version = 2; // !!! Need to get this into tdi2.h
|
|
LoopProviderInfo.MaxTsduSize = MAXULONG;
|
|
LoopProviderInfo.MaxDatagramSize = MAXULONG;
|
|
LoopProviderInfo.ServiceFlags = TDI_SERVICE_CONNECTION_MODE |
|
|
TDI_SERVICE_CONNECTIONLESS_MODE |
|
|
TDI_SERVICE_ERROR_FREE_DELIVERY |
|
|
TDI_SERVICE_BROADCAST_SUPPORTED |
|
|
TDI_SERVICE_MULTICAST_SUPPORTED;
|
|
LoopProviderInfo.MinimumLookaheadData = 256;
|
|
LoopProviderInfo.MaximumLookaheadData = 256;
|
|
|
|
IF_DEBUG(LOOP1) DbgPrint( "LoopInitialize complete\n" );
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // LoopInitialize
|
|
|
|
|
|
NTSTATUS
|
|
LoopDispatchCleanup(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the dispatch routine for Cleanup functions for the LAN
|
|
Manager loopback driver.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to device object for target device
|
|
|
|
Irp - Pointer to I/O request packet
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates whether the request was successfully queued.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
PBLOCK_HEADER blockHeader;
|
|
PLOOP_ENDPOINT endpoint;
|
|
PLIST_ENTRY listEntry;
|
|
PIRP pendingIrp;
|
|
PLOOP_CONNECTION connection;
|
|
BLOCK_STATE oldState;
|
|
PLOOP_CONNECTION previousConnection;
|
|
|
|
DeviceObject; // not otherwise referenced if !DBG
|
|
|
|
ASSERT( DeviceObject == (PDEVICE_OBJECT)LoopDeviceObject );
|
|
// only one loopback device
|
|
|
|
IF_DEBUG(LOOP1) {
|
|
DbgPrint( "LoopDispatchCleanup entered for IRP %lx\n", Irp );
|
|
}
|
|
|
|
//
|
|
// Initialize the I/O status block.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_PENDING;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Get a pointer to the current stack location in the IRP.
|
|
//
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
ASSERT( irpSp->MajorFunction == IRP_MJ_CLEANUP );
|
|
|
|
ACQUIRE_LOOP_LOCK( "DispatchCleanup initial" );
|
|
|
|
blockHeader = (PBLOCK_HEADER)irpSp->FileObject->FsContext;
|
|
|
|
if ( blockHeader == NULL ) {
|
|
|
|
//
|
|
// A control channel is being cleaned up. We need do nothing.
|
|
//
|
|
|
|
IF_DEBUG(LOOP2) DbgPrint( " cleaning up control channel\n" );
|
|
|
|
} else if ( GET_BLOCK_TYPE(blockHeader) == BlockTypeLoopConnection ) {
|
|
|
|
//
|
|
// A connection file object is being cleaned up. Reference the
|
|
// connection to keep it around while we perform the cleanup.
|
|
//
|
|
|
|
connection = (PLOOP_CONNECTION)blockHeader;
|
|
IF_DEBUG(LOOP2) DbgPrint( " Connection address: %lx\n", connection );
|
|
|
|
connection->BlockHeader.ReferenceCount++;
|
|
IF_DEBUG(LOOP3) {
|
|
DbgPrint( " New refcnt on connection %lx is %lx\n",
|
|
connection, connection->BlockHeader.ReferenceCount );
|
|
}
|
|
|
|
//
|
|
// Acquire the spin lock. Set the connection state to Closing.
|
|
// This will prevent other requests from being initiated.
|
|
//
|
|
// *** Note the assumption that this routine is only entered
|
|
// once. (That's the way the I/O system is supposed to
|
|
// work.) We don't check to see if the cleanup has already
|
|
// been initiated.
|
|
//
|
|
// *** In the rundown code, we release the lock, then reacquire
|
|
// it temporarily to remove things from lists and
|
|
// dereference the connection. We do this because we don't
|
|
// want to hold a spin lock for a long time.
|
|
//
|
|
|
|
oldState = GET_BLOCK_STATE( connection );
|
|
SET_BLOCK_STATE( connection, BlockStateClosing );
|
|
|
|
//
|
|
// If a Connect or Listen is active, abort it now.
|
|
//
|
|
|
|
if ( oldState == BlockStateConnecting ) {
|
|
|
|
pendingIrp = connection->ConnectOrListenIrp;
|
|
connection->ConnectOrListenIrp = NULL;
|
|
|
|
ASSERT( pendingIrp != NULL );
|
|
|
|
RemoveEntryList( &pendingIrp->Tail.Overlay.ListEntry );
|
|
|
|
RELEASE_LOOP_LOCK( "DispatchCleanup complete conn/listen" );
|
|
|
|
pendingIrp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
IoCompleteRequest( pendingIrp, 2 );
|
|
|
|
ACQUIRE_LOOP_LOCK( "DispatchCleanup conn/listen completed" );
|
|
|
|
}
|
|
|
|
//
|
|
// Disconnect the connection, if necessary.
|
|
//
|
|
|
|
if ( oldState == BlockStateActive ) {
|
|
LoopDoDisconnect( connection, TRUE );
|
|
}
|
|
|
|
//
|
|
// If the connection was bound, unbind it now.
|
|
//
|
|
|
|
if ( oldState == BlockStateBound ) {
|
|
endpoint = connection->Endpoint;
|
|
ASSERT( endpoint != NULL );
|
|
connection->Endpoint = NULL;
|
|
RemoveEntryList( &connection->EndpointListEntry );
|
|
LoopDereferenceEndpoint( endpoint );
|
|
}
|
|
|
|
//
|
|
// Dereference the connection.
|
|
//
|
|
|
|
LoopDereferenceConnection( connection );
|
|
|
|
RELEASE_LOOP_LOCK( "DispatchCleanup(conn) done" );
|
|
|
|
} else {
|
|
|
|
//
|
|
// An endpoint file object is being cleaned up.
|
|
//
|
|
|
|
endpoint = (PLOOP_ENDPOINT)blockHeader;
|
|
IF_DEBUG(LOOP2) DbgPrint( " Endpoint address: %lx\n", endpoint );
|
|
|
|
//
|
|
// Acquire the spin lock. Set the endpoint state to Closing.
|
|
// This will prevent other requests (Listens and Connects) from
|
|
// being initiated.
|
|
//
|
|
// *** Note the assumption that this routine is only entered
|
|
// once. (That's the way the I/O system is supposed to
|
|
// work.) We don't check to see if the cleanup has already
|
|
// been initiated.
|
|
//
|
|
// *** In the rundown code, we release the lock, then reacquire
|
|
// it temporarily to remove things from lists and
|
|
// dereference the endpoint. We do this because we don't
|
|
// want to hold a spin lock for a long time.
|
|
//
|
|
|
|
SET_BLOCK_STATE( endpoint, BlockStateClosing );
|
|
|
|
//
|
|
// Abort pending listens.
|
|
//
|
|
|
|
listEntry = RemoveHeadList( &endpoint->PendingListenList );
|
|
|
|
while ( listEntry != &endpoint->PendingListenList ) {
|
|
|
|
//
|
|
// A pending listen was found. Complete the listen with an
|
|
// error status. Get the next listen.
|
|
//
|
|
|
|
RELEASE_LOOP_LOCK( "DispatchCleanup complete Listen" );
|
|
|
|
pendingIrp = CONTAINING_RECORD(
|
|
listEntry,
|
|
IRP,
|
|
Tail.Overlay.ListEntry
|
|
);
|
|
pendingIrp->IoStatus.Status = STATUS_ENDPOINT_CLOSED;
|
|
IoCompleteRequest( pendingIrp, 2 );
|
|
|
|
ACQUIRE_LOOP_LOCK( "DispatchCleanup dequeue Listen" );
|
|
|
|
listEntry = RemoveHeadList( &endpoint->PendingListenList );
|
|
}
|
|
|
|
//
|
|
// Abort pending connects.
|
|
//
|
|
|
|
listEntry = RemoveHeadList( &endpoint->IncomingConnectList );
|
|
|
|
while ( listEntry != &endpoint->IncomingConnectList ) {
|
|
|
|
//
|
|
// A pending connect was found. Complete the connect with
|
|
// an error status. Get the next connect.
|
|
//
|
|
|
|
RELEASE_LOOP_LOCK( "DispatchCleanup complete Connect" );
|
|
|
|
pendingIrp = CONTAINING_RECORD(
|
|
listEntry,
|
|
IRP,
|
|
Tail.Overlay.ListEntry
|
|
);
|
|
pendingIrp->IoStatus.Status = STATUS_ENDPOINT_CLOSED;
|
|
IoCompleteRequest( pendingIrp, 2 );
|
|
|
|
ACQUIRE_LOOP_LOCK( "DispatchCleanup complete Connect" );
|
|
|
|
listEntry = RemoveHeadList( &endpoint->IncomingConnectList );
|
|
}
|
|
|
|
//
|
|
// Disconnect or unbind all bound connections.
|
|
//
|
|
// *** This loop is complicated by the fact that we can't remove
|
|
// connections from the list before disconnecting them, yet
|
|
// we want to keep the list consistent while we walk it. Be
|
|
// careful making changes to this loop!
|
|
//
|
|
|
|
previousConnection = NULL;
|
|
|
|
listEntry = endpoint->ConnectionList.Flink;
|
|
|
|
while ( listEntry != &endpoint->ConnectionList ) {
|
|
|
|
//
|
|
// A bound connection was found. If the connection's
|
|
// reference count is not already 0, reference it to keep it
|
|
// from going away. If the count is 0, skip to the next
|
|
// connection.
|
|
//
|
|
|
|
connection = CONTAINING_RECORD(
|
|
listEntry,
|
|
LOOP_CONNECTION,
|
|
EndpointListEntry
|
|
);
|
|
|
|
if ( connection->BlockHeader.ReferenceCount == 0 ) {
|
|
|
|
//
|
|
// Find the next connection in the list and loop.
|
|
//
|
|
|
|
listEntry = listEntry->Flink;
|
|
continue;
|
|
|
|
}
|
|
|
|
connection->BlockHeader.ReferenceCount++;
|
|
IF_DEBUG(LOOP3) {
|
|
DbgPrint( " New refcnt on connection %lx is %lx\n",
|
|
connection, connection->BlockHeader.ReferenceCount );
|
|
}
|
|
|
|
//
|
|
// Dereference the previous connection, if any.
|
|
//
|
|
|
|
if ( previousConnection != NULL ) {
|
|
LoopDereferenceConnection( previousConnection );
|
|
}
|
|
previousConnection = connection;
|
|
|
|
//
|
|
// Disconnect or unbind the current connection. It won't be
|
|
// deleted.
|
|
//
|
|
|
|
if ( GET_BLOCK_STATE(connection) == BlockStateActive ) {
|
|
|
|
//
|
|
// Disconnect the connection.
|
|
//
|
|
|
|
SET_BLOCK_STATE( connection, BlockStateDisconnecting );
|
|
LoopDoDisconnect( connection, TRUE );
|
|
|
|
//
|
|
// Find the next connection in the list.
|
|
//
|
|
|
|
listEntry = listEntry->Flink;
|
|
|
|
} else if ( GET_BLOCK_STATE(connection) == BlockStateBound ) {
|
|
|
|
//
|
|
// Find the next connection in the list.
|
|
//
|
|
|
|
listEntry = listEntry->Flink;
|
|
|
|
//
|
|
// Unbind the connection.
|
|
//
|
|
|
|
ASSERT( connection->Endpoint == endpoint );
|
|
connection->Endpoint = NULL;
|
|
RemoveEntryList( &connection->EndpointListEntry );
|
|
SET_BLOCK_STATE( connection, BlockStateUnbound );
|
|
LoopDereferenceEndpoint( connection->Endpoint );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Find the next connection in the list.
|
|
//
|
|
|
|
listEntry = listEntry->Flink;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Dereference the previous connection, if any.
|
|
//
|
|
|
|
if ( previousConnection != NULL ) {
|
|
LoopDereferenceConnection( previousConnection );
|
|
}
|
|
|
|
//
|
|
// The spin lock is still held here. Cancel the receive handler.
|
|
//
|
|
// *** Note that we do not dereference the endpoint here. That is
|
|
// done in the Close handler. We have already set the state
|
|
// of the endpoint to closing, which will prevent any further
|
|
// activity from occurring.
|
|
//
|
|
|
|
endpoint->FileObject = NULL;
|
|
endpoint->ReceiveHandler = NULL;
|
|
|
|
RELEASE_LOOP_LOCK( "DispatchCleanup final" );
|
|
|
|
} // connection vs. endpoint
|
|
|
|
//
|
|
// Successful completion. Complete the I/O request.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest( Irp, 2 );
|
|
|
|
IF_DEBUG(LOOP1) {
|
|
DbgPrint( "LoopDispatchCleanup complete for IRP %lx\n", Irp );
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // LoopDispatchCleanup
|
|
|
|
|
|
NTSTATUS
|
|
LoopDispatchClose(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the dispatch routine for Close functions for the LAN
|
|
Manager loopback driver.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to device object for target device
|
|
|
|
Irp - Pointer to I/O request packet
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates whether the request was successfully queued.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
PBLOCK_HEADER blockHeader;
|
|
PLOOP_ENDPOINT endpoint;
|
|
PLOOP_CONNECTION connection;
|
|
|
|
DeviceObject; // not otherwise referenced if !DBG
|
|
|
|
ASSERT( DeviceObject == (PDEVICE_OBJECT)LoopDeviceObject );
|
|
// only one loopback device
|
|
|
|
IF_DEBUG(LOOP1) {
|
|
DbgPrint( "LoopDispatchClose entered for IRP %lx\n", Irp );
|
|
}
|
|
|
|
//
|
|
// Initialize the I/O status block.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_PENDING;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Get a pointer to the current stack location in the IRP.
|
|
//
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
ASSERT( irpSp->MajorFunction == IRP_MJ_CLOSE );
|
|
|
|
ACQUIRE_LOOP_LOCK( "DispatchClose initial" );
|
|
|
|
blockHeader = (PBLOCK_HEADER)irpSp->FileObject->FsContext;
|
|
|
|
if ( blockHeader == NULL ) {
|
|
|
|
//
|
|
// A control channel is being closed. We need do nothing.
|
|
//
|
|
|
|
IF_DEBUG(LOOP2) DbgPrint( " closing control channel\n" );
|
|
|
|
} else if ( GET_BLOCK_TYPE(blockHeader) == BlockTypeLoopConnection ) {
|
|
|
|
//
|
|
// A connection file object is being closed.
|
|
//
|
|
|
|
connection = (PLOOP_CONNECTION)blockHeader;
|
|
IF_DEBUG(LOOP2) DbgPrint( " Connection address: %lx\n", connection );
|
|
|
|
//
|
|
// All external references to the file object (and thus the
|
|
// connection) are gone, but the connection block hasn't been
|
|
// deleted. This implies that a Disconnect is in progress,
|
|
// and when that operation completes, the connection block will
|
|
// be deleted. We need to set up for the Close IRP to be
|
|
// completed at that time. Reference and dereference the
|
|
// connection to allow it to be deleted.
|
|
//
|
|
|
|
connection->BlockHeader.ReferenceCount++;
|
|
IF_DEBUG(LOOP3) {
|
|
DbgPrint( " New refcnt on connection %lx is %lx\n",
|
|
connection, connection->BlockHeader.ReferenceCount );
|
|
}
|
|
|
|
SET_BLOCK_STATE( connection, BlockStateClosed );
|
|
|
|
connection->CloseIrp = Irp;
|
|
IoMarkIrpPending( Irp );
|
|
|
|
LoopDereferenceConnection( connection );
|
|
|
|
RELEASE_LOOP_LOCK( "DispatchClose(conn) final" );
|
|
|
|
} else {
|
|
|
|
ASSERT( GET_BLOCK_TYPE(blockHeader) == BlockTypeLoopEndpoint );
|
|
|
|
endpoint = (PLOOP_ENDPOINT)irpSp->FileObject->FsContext;
|
|
IF_DEBUG(LOOP2) DbgPrint( " Endpoint address: %lx\n", endpoint );
|
|
|
|
//
|
|
// All external references to the file object (and thus the
|
|
// endpoint) are gone. Normally, the only remaining internal
|
|
// reference to the endpoint is the one that keeps the endpoint
|
|
// "open". Eliminate that reference. The CloseIrp field in the
|
|
// endpoint is used to remember the IRP that must be completed
|
|
// when the reference count goes to 0.
|
|
//
|
|
// *** Because LoopDereferenceEndpoint may or may not complete
|
|
// the Close IRP, we return STATUS_PENDING from the service
|
|
// call. We must mark this fact in the IRP before calling
|
|
// LoopDereferenceEndpoint.
|
|
//
|
|
|
|
endpoint->CloseIrp = Irp;
|
|
IoMarkIrpPending( Irp );
|
|
|
|
LoopDereferenceEndpoint( endpoint );
|
|
|
|
RELEASE_LOOP_LOCK( "DispatchClose final" );
|
|
|
|
}
|
|
IF_DEBUG(LOOP1) {
|
|
DbgPrint( "LoopDispatchClose complete (pending) for IRP %lx\n",
|
|
Irp );
|
|
}
|
|
|
|
return STATUS_PENDING;
|
|
|
|
} // LoopDispatchClose
|
|
|
|
|
|
NTSTATUS
|
|
LoopDispatchCreate(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the dispatch routine for Create functions for the LAN
|
|
Manager loopback driver.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to device object for target device
|
|
|
|
Irp - Pointer to I/O request packet
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates whether the request was successfully queued.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
BLOCK_TYPE type;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PLOOP_ENDPOINT endpoint;
|
|
PLOOP_ENDPOINT existingEndpoint;
|
|
PLOOP_CONNECTION connection;
|
|
|
|
DeviceObject; // not otherwise referenced if !DBG
|
|
|
|
ASSERT( DeviceObject == (PDEVICE_OBJECT)LoopDeviceObject );
|
|
// only one loopback device
|
|
|
|
IF_DEBUG(LOOP1) {
|
|
DbgPrint( "LoopDispatchCreate entered for IRP %lx\n", Irp );
|
|
}
|
|
|
|
//
|
|
// Initialize the I/O status block.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_PENDING;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Get a pointer to the current stack location in the IRP.
|
|
//
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
ASSERT( irpSp->MajorFunction == IRP_MJ_CREATE );
|
|
|
|
//
|
|
// Determine whether an address endpoint or a connection endpoint is
|
|
// being created, or if a control channel is being opened.
|
|
//
|
|
|
|
if ( Irp->AssociatedIrp.SystemBuffer == NULL ) {
|
|
|
|
//
|
|
// A control channel is being opened. This channel is used
|
|
// only to get provider information and to determine the
|
|
// provider's broadcast address.
|
|
//
|
|
|
|
IF_DEBUG(LOOP2) DbgPrint( " opening control channel\n" );
|
|
|
|
irpSp->FileObject->FsContext = NULL;
|
|
|
|
} else {
|
|
|
|
status = LoopGetEndpointTypeFromEa(
|
|
(PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer,
|
|
&type
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest( Irp, 0 );
|
|
return status;
|
|
}
|
|
|
|
if ( type == BlockTypeLoopEndpoint ) {
|
|
|
|
//
|
|
// An address endpoint is being created.
|
|
//
|
|
// Allocate a LOOP_ENDPOINT block to describe the transport
|
|
// endpoint. Initialize it.
|
|
//
|
|
|
|
endpoint = ExAllocatePool( NonPagedPool, sizeof(LOOP_ENDPOINT) );
|
|
if ( endpoint == NULL ) {
|
|
IF_DEBUG(LOOP2) DbgPrint( " Unable to allocate pool\n" );
|
|
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
IoCompleteRequest( Irp, 0 );
|
|
IF_DEBUG(LOOP1) {
|
|
DbgPrint( "LoopDispatchCreate complete for IRP %lx\n",
|
|
Irp );
|
|
}
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
IF_DEBUG(LOOP2) {
|
|
DbgPrint( " Endpoint allocated: %lx\n", endpoint );
|
|
}
|
|
SET_BLOCK_TYPE( endpoint, BlockTypeLoopEndpoint );
|
|
SET_BLOCK_STATE( endpoint, BlockStateActive );
|
|
SET_BLOCK_SIZE( endpoint, sizeof(LOOP_ENDPOINT) );
|
|
endpoint->BlockHeader.ReferenceCount = 1; // for file object
|
|
IF_DEBUG(LOOP3) {
|
|
DbgPrint( " New refcnt on endpoint %lx is %lx\n",
|
|
endpoint, endpoint->BlockHeader.ReferenceCount );
|
|
}
|
|
|
|
InitializeListHead( &endpoint->ConnectionList );
|
|
InitializeListHead( &endpoint->PendingListenList );
|
|
InitializeListHead( &endpoint->IncomingConnectList );
|
|
endpoint->IndicatingConnectIrp = NULL;
|
|
|
|
endpoint->FileObject = irpSp->FileObject;
|
|
endpoint->ConnectHandler = NULL;
|
|
endpoint->ReceiveHandler = NULL;
|
|
endpoint->ReceiveDatagramHandler = NULL;
|
|
endpoint->ReceiveExpeditedHandler = NULL;
|
|
endpoint->DisconnectHandler = NULL;
|
|
endpoint->ErrorHandler = NULL;
|
|
endpoint->CloseIrp = NULL;
|
|
|
|
//
|
|
// Save a pointer to the endpoint block in the file object
|
|
// so that we can find it when file-based requests are
|
|
// issued.
|
|
//
|
|
|
|
irpSp->FileObject->FsContext = (PVOID)endpoint;
|
|
|
|
//
|
|
// Reference the loopback device object.
|
|
//
|
|
|
|
ObReferenceObject( LoopDeviceObject );
|
|
|
|
endpoint->DeviceObject = LoopDeviceObject;
|
|
|
|
//
|
|
// The EA contains the address to be bound to the endpoint.
|
|
// Verify that the address is not already bound.
|
|
//
|
|
// !!! This should really be a share-mode/SECURITY_DESCRIPTOR
|
|
// check.
|
|
//
|
|
|
|
LoopParseAddressFromEa(
|
|
(PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer,
|
|
endpoint->NetbiosName
|
|
);
|
|
endpoint->NetbiosName[NETBIOS_NAME_LENGTH] = 0;
|
|
|
|
IF_DEBUG(LOOP2) {
|
|
DbgPrint( " Address to bind: \"%s\"\n",
|
|
endpoint->NetbiosName );
|
|
}
|
|
|
|
ACQUIRE_LOOP_LOCK( "DispatchCreate(endp) initial" );
|
|
|
|
existingEndpoint = LoopFindBoundAddress( endpoint->NetbiosName );
|
|
|
|
if ( existingEndpoint != NULL ) {
|
|
|
|
IF_DEBUG(LOOP2) {
|
|
DbgPrint( " Duplicate address at endpoint %lx\n",
|
|
existingEndpoint );
|
|
}
|
|
|
|
RELEASE_LOOP_LOCK( "DispatchCreate duplicate address" );
|
|
|
|
ObDereferenceObject( LoopDeviceObject );
|
|
|
|
DEBUG SET_BLOCK_TYPE( endpoint, BlockTypeGarbage );
|
|
DEBUG SET_BLOCK_STATE( endpoint, BlockStateDead );
|
|
DEBUG SET_BLOCK_SIZE( endpoint, -1 );
|
|
DEBUG endpoint->BlockHeader.ReferenceCount = -1;
|
|
DEBUG endpoint->DeviceObject = NULL;
|
|
ExFreePool( endpoint );
|
|
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
IoCompleteRequest( Irp, 0 );
|
|
|
|
IF_DEBUG(LOOP1) {
|
|
DbgPrint( "LoopDispatchCreate complete for IRP %lx\n",
|
|
Irp );
|
|
}
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Link the new endpoint into the loopback device's endpoint
|
|
// list.
|
|
//
|
|
|
|
InsertTailList(
|
|
&LoopDeviceObject->EndpointList,
|
|
&endpoint->DeviceListEntry
|
|
);
|
|
|
|
RELEASE_LOOP_LOCK( "DispatchCreate(endp) final" );
|
|
|
|
} else {
|
|
|
|
//
|
|
// A connection endpoint is being created.
|
|
//
|
|
// Allocate a LOOP_CONNECTION block to describe the connection.
|
|
// Initialize it.
|
|
//
|
|
|
|
connection = ExAllocatePool(
|
|
NonPagedPool,
|
|
sizeof(LOOP_CONNECTION)
|
|
);
|
|
if ( connection == NULL ) {
|
|
IF_DEBUG(LOOP2) DbgPrint( " Unable to allocate pool\n" );
|
|
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
IoCompleteRequest( Irp, 0 );
|
|
IF_DEBUG(LOOP1) {
|
|
DbgPrint( "LoopDispatchCreate complete for IRP %lx\n",
|
|
Irp );
|
|
}
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
IF_DEBUG(LOOP2) {
|
|
DbgPrint( " Connection allocated: %lx\n", connection );
|
|
}
|
|
SET_BLOCK_TYPE( connection, BlockTypeLoopConnection );
|
|
SET_BLOCK_STATE( connection, BlockStateUnbound );
|
|
SET_BLOCK_SIZE( connection, sizeof(LOOP_CONNECTION) );
|
|
connection->BlockHeader.ReferenceCount = 0; // not connected
|
|
IF_DEBUG(LOOP3) {
|
|
DbgPrint( " New refcnt on connection %lx is %lx\n",
|
|
connection,
|
|
connection->BlockHeader.ReferenceCount );
|
|
}
|
|
|
|
connection->Endpoint = NULL;
|
|
connection->RemoteConnection = NULL;
|
|
connection->ConnectionContext = LoopGetConnectionContextFromEa(
|
|
Irp->AssociatedIrp.SystemBuffer
|
|
);
|
|
|
|
InitializeListHead( &connection->PendingReceiveList );
|
|
InitializeListHead( &connection->IncomingSendList );
|
|
|
|
connection->FileObject = irpSp->FileObject;
|
|
|
|
connection->IndicatingSendIrp = NULL;
|
|
connection->ConnectOrListenIrp = NULL;
|
|
connection->CloseIrp = NULL;
|
|
connection->DisconnectIrp = NULL;
|
|
|
|
//
|
|
// Save a pointer to the connection block in the file object
|
|
// so that we can find it when file-based requests are
|
|
// issued.
|
|
//
|
|
|
|
irpSp->FileObject->FsContext = (PVOID)connection;
|
|
|
|
//
|
|
// Reference the loopback device object.
|
|
//
|
|
|
|
ObReferenceObject( LoopDeviceObject );
|
|
|
|
connection->DeviceObject = LoopDeviceObject;
|
|
|
|
//
|
|
// Link the new connection into the loopback device's connection
|
|
// list.
|
|
//
|
|
|
|
ExInterlockedInsertTailList(
|
|
&LoopDeviceObject->ConnectionList,
|
|
&connection->DeviceListEntry,
|
|
&LoopDeviceObject->SpinLock
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Successful completion. Complete the I/O request.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest( Irp, 2 );
|
|
|
|
IF_DEBUG(LOOP1) {
|
|
DbgPrint( "LoopDispatchCreate complete for IRP %lx\n", Irp );
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // LoopDispatchCreate
|
|
|
|
|
|
NTSTATUS
|
|
LoopDispatchDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the dispatch routine for Device Control functions for the
|
|
LAN Manager loopback driver.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to device object for target device
|
|
|
|
Irp - Pointer to I/O request packet
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates whether the request was successfully queued.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
ASSERT( DeviceObject == (PDEVICE_OBJECT)LoopDeviceObject );
|
|
// only one loopback device
|
|
|
|
IF_DEBUG(LOOP1) {
|
|
DbgPrint( "LoopDispatchDeviceControl entered for IRP %lx\n", Irp );
|
|
}
|
|
|
|
//
|
|
// Initialize the I/O status block.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_PENDING;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Get a pointer to the current stack location in the IRP.
|
|
//
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
ASSERT( irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL );
|
|
|
|
//
|
|
// Convert the (external) device control into internal format, then
|
|
// treat it as if it had arrived that way.
|
|
//
|
|
|
|
status = TdiMapUserRequest( DeviceObject, Irp, irpSp );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest( Irp, 0 );
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
return LoopDispatchInternalDeviceControl( DeviceObject, Irp );
|
|
|
|
} // LoopDispatchDeviceControl
|
|
|
|
|
|
NTSTATUS
|
|
LoopDispatchInternalDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the dispatch routine for Internal Device Control functions
|
|
for the LAN Manager loopback driver.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to device object for target device
|
|
|
|
Irp - Pointer to I/O request packet
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates whether the request was successfully queued.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
DeviceObject; // not otherwise referenced if !DBG
|
|
|
|
ASSERT( DeviceObject == (PDEVICE_OBJECT)LoopDeviceObject );
|
|
// only one loopback device
|
|
|
|
IF_DEBUG(LOOP1) {
|
|
DbgPrint( "LoopDispatchInternalDeviceControl entered for IRP %lx\n",
|
|
Irp );
|
|
}
|
|
|
|
//
|
|
// Initialize the I/O status block.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_PENDING;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Get a pointer to the current stack location in the IRP.
|
|
//
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
ASSERT( irpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL );
|
|
|
|
//
|
|
// Case on the control code.
|
|
//
|
|
|
|
switch ( irpSp->MinorFunction ) {
|
|
|
|
case TDI_ACCEPT:
|
|
|
|
return LoopAccept( Irp, irpSp );
|
|
|
|
case TDI_ASSOCIATE_ADDRESS:
|
|
|
|
return LoopAssociateAddress( Irp, irpSp );
|
|
|
|
case TDI_CONNECT:
|
|
|
|
return LoopConnect( Irp, irpSp );
|
|
|
|
case TDI_DISASSOCIATE_ADDRESS:
|
|
|
|
return LoopDisassociateAddress( Irp, irpSp );
|
|
|
|
case TDI_DISCONNECT:
|
|
|
|
return LoopDisconnect( Irp, irpSp );
|
|
|
|
case TDI_LISTEN:
|
|
|
|
return LoopListen( Irp, irpSp );
|
|
|
|
case TDI_QUERY_INFORMATION:
|
|
|
|
return LoopQueryInformation( Irp, irpSp );
|
|
|
|
case TDI_RECEIVE:
|
|
|
|
return LoopReceive( Irp, irpSp );
|
|
|
|
case TDI_SEND:
|
|
|
|
return LoopSend( Irp, irpSp );
|
|
|
|
case TDI_SET_EVENT_HANDLER:
|
|
|
|
return LoopSetEventHandler( Irp, irpSp );
|
|
|
|
case TDI_SEND_DATAGRAM:
|
|
|
|
//
|
|
// !!! Need to implement this request.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest( Irp, 0 );
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
case TDI_RECEIVE_DATAGRAM:
|
|
case TDI_SET_INFORMATION:
|
|
|
|
//
|
|
// !!! Need to implement these requests.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
|
|
IoCompleteRequest( Irp, 0 );
|
|
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
|
|
default:
|
|
|
|
IF_DEBUG(LOOP2) {
|
|
DbgPrint( " Invalid device control function: %lx\n",
|
|
irpSp->Parameters.DeviceIoControl.IoControlCode );
|
|
}
|
|
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
IoCompleteRequest( Irp, 0 );
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
return STATUS_INVALID_PARAMETER; // can't get here
|
|
|
|
} // LoopDispatchInternalDeviceControl
|
|
|
|
|
|
VOID
|
|
LoopUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the unload routine for the LAN Manager loopback driver.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to driver object for this driver.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DriverObject; // prevent compiler warnings
|
|
|
|
return;
|
|
|
|
} // LoopUnload
|