1285 lines
34 KiB
C
1285 lines
34 KiB
C
/*++
|
||
|
||
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
|