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

1285 lines
34 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 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