summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/loopback
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/tdi/loopback')
-rw-r--r--private/ntos/tdi/loopback/connect.c1593
-rw-r--r--private/ntos/tdi/loopback/datagram.c848
-rw-r--r--private/ntos/tdi/loopback/endpoint.c373
-rw-r--r--private/ntos/tdi/loopback/info.c183
-rw-r--r--private/ntos/tdi/loopback/loopback.c1284
-rw-r--r--private/ntos/tdi/loopback/loopback.h433
-rw-r--r--private/ntos/tdi/loopback/loopdbg.h58
-rw-r--r--private/ntos/tdi/loopback/loopsub.c379
-rw-r--r--private/ntos/tdi/loopback/makefile6
-rw-r--r--private/ntos/tdi/loopback/sources46
-rw-r--r--private/ntos/tdi/loopback/transfer.c859
11 files changed, 6062 insertions, 0 deletions
diff --git a/private/ntos/tdi/loopback/connect.c b/private/ntos/tdi/loopback/connect.c
new file mode 100644
index 000000000..10376e3e8
--- /dev/null
+++ b/private/ntos/tdi/loopback/connect.c
@@ -0,0 +1,1593 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ connect.c
+
+Abstract:
+
+ This module implements connection logic for the loopback Transport
+ Provider driver for NT LAN Manager.
+
+Author:
+
+ Chuck Lenzmeier (chuckl) 15-Aug-1991
+
+Revision History:
+
+--*/
+
+#include "loopback.h"
+
+//
+// Local declarations
+//
+
+STATIC
+NTSTATUS
+CompleteConnection (
+ IN PIRP ListenIrp,
+ IN PIRP ConnectIrp
+ );
+
+STATIC
+VOID
+IndicateConnect (
+ IN PLOOP_ENDPOINT Endpoint
+ );
+
+
+NTSTATUS
+LoopAccept (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine processes an Accept request.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+
+ IrpSp - Pointer to current stack location in IRP
+
+Return Value:
+
+ NTSTATUS - Status of request
+
+--*/
+
+{
+ NTSTATUS status;
+ PTDI_REQUEST_KERNEL_ACCEPT acceptRequest;
+ PLOOP_CONNECTION connection;
+ PLOOP_ENDPOINT endpoint;
+
+ IF_DEBUG(LOOP1) DbgPrint( " Accept request\n" );
+
+ acceptRequest = (PTDI_REQUEST_KERNEL_ACCEPT)&IrpSp->Parameters;
+
+ ACQUIRE_LOOP_LOCK( "Accept initial" );
+
+ //
+ // Verify that the connection is in the proper state.
+ //
+
+ connection = (PLOOP_CONNECTION)IrpSp->FileObject->FsContext;
+
+ if ( connection == NULL ) {
+ RELEASE_LOOP_LOCK( "Accept control channel" );
+ IF_DEBUG(LOOP2) DbgPrint( " Can't Accept on control channel\n" );
+ status = STATUS_INVALID_PARAMETER;
+ goto complete;
+ }
+
+ if ( GET_BLOCK_STATE(connection) != BlockStateBound ) {
+ RELEASE_LOOP_LOCK( "Accept conn not bound" );
+ IF_DEBUG(LOOP2) DbgPrint( " Connection not bound\n" );
+ status = STATUS_INVALID_PARAMETER;
+ goto complete;
+ }
+
+ //
+ // There must be an indication in progress on the endpoint.
+ //
+ // *** The loopback driver does not support delayed accept on
+ // TdiListen, but does on connect indications.
+ //
+
+ endpoint = connection->Endpoint;
+
+ if ( endpoint->IndicatingConnectIrp == NULL ) {
+ RELEASE_LOOP_LOCK( "Endpoint not indicating connect" );
+ IF_DEBUG(LOOP2) DbgPrint( " Endpoint not indicating connect\n" );
+ status = STATUS_INVALID_PARAMETER;
+ goto complete;
+ }
+
+ //
+ // Link the connections together. Indicate that the connect
+ // indication is no longer in progress.
+ //
+
+ CompleteConnection( Irp, endpoint->IndicatingConnectIrp );
+
+ endpoint->IndicatingConnectIrp = NULL;
+
+ RELEASE_LOOP_LOCK( "Accept connect completed" );
+
+ IF_DEBUG(LOOP1) DbgPrint( " Accept request complete\n" );
+ return STATUS_SUCCESS;
+
+complete:
+
+ //
+ // Complete the Accept request.
+ //
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest( Irp, 0 );
+
+ IF_DEBUG(LOOP1) DbgPrint( " Accept request complete\n" );
+ return status;
+
+} // LoopAccept
+
+
+NTSTATUS
+LoopAssociateAddress (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine processes an Associate Address request.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+
+ IrpSp - Pointer to current stack location in IRP
+
+Return Value:
+
+ NTSTATUS - Status of request
+
+--*/
+
+{
+ NTSTATUS status;
+ PTDI_REQUEST_KERNEL_ASSOCIATE associateRequest;
+ PLOOP_CONNECTION connection;
+ PFILE_OBJECT endpointFileObject = NULL;
+ PLOOP_ENDPOINT endpoint;
+
+ IF_DEBUG(LOOP1) DbgPrint( " Associate request\n" );
+
+ associateRequest = (PTDI_REQUEST_KERNEL_ASSOCIATE)&IrpSp->Parameters;
+
+ //
+ // Translate the address handle to a pointer to the address endpoint
+ // file object. Get a pointer to the loopback endpoint block.
+ // Verify that it is really a loopback endpoint.
+ //
+
+ status = ObReferenceObjectByHandle (
+ associateRequest->AddressHandle,
+ 0,
+ 0,
+ KernelMode,
+ (PVOID *)&endpointFileObject,
+ NULL);
+
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(LOOP2) DbgPrint( " Invalid endpoint handle\n" );
+ goto complete;
+ }
+
+ ACQUIRE_LOOP_LOCK( "Associate initial" );
+
+ endpoint = (PLOOP_ENDPOINT)endpointFileObject->FsContext;
+ status = LoopVerifyEndpoint( endpoint );
+
+ if ( !NT_SUCCESS(status) ) {
+ RELEASE_LOOP_LOCK( "Associate bad endpoint pointer" );
+ IF_DEBUG(LOOP2) DbgPrint( " Invalid endpoint pointer\n" );
+ goto complete;
+ }
+
+ //
+ // Verify that the connection is in the proper state.
+ //
+
+ connection = (PLOOP_CONNECTION)IrpSp->FileObject->FsContext;
+
+ if ( connection == NULL ) {
+ RELEASE_LOOP_LOCK( "Associate control channel" );
+ IF_DEBUG(LOOP2) DbgPrint( " Can't Associate on control channel\n" );
+ status = STATUS_INVALID_PARAMETER;
+ goto complete;
+ }
+
+ if ( GET_BLOCK_STATE(connection) != BlockStateUnbound ) {
+ RELEASE_LOOP_LOCK( "Associate conn not unbound" );
+ IF_DEBUG(LOOP2) DbgPrint( " Connection not unbound\n" );
+ status = STATUS_INVALID_PARAMETER;
+ goto complete;
+ }
+
+ //
+ // Verify that the endpoint is in the proper state.
+ //
+
+ if ( GET_BLOCK_STATE(endpoint) != BlockStateActive ) {
+ RELEASE_LOOP_LOCK( "Associate endpoint not active" );
+ IF_DEBUG(LOOP2) DbgPrint( " Endpoint not active\n" );
+ status = STATUS_INVALID_PARAMETER;
+ goto complete;
+ }
+
+ //
+ // Link this connection into the endpoint's connection list.
+ // Reference the endpoint block. Indicate that the connection has
+ // been bound to an address. Dereference the endpoint file object.
+ //
+
+ InsertTailList(
+ &endpoint->ConnectionList,
+ &connection->EndpointListEntry
+ );
+
+ endpoint->BlockHeader.ReferenceCount++;
+ IF_DEBUG(LOOP3) {
+ DbgPrint( " New refcnt on endpoint %lx is %lx\n",
+ endpoint, endpoint->BlockHeader.ReferenceCount );
+ }
+
+ SET_BLOCK_STATE( connection, BlockStateBound );
+
+ connection->Endpoint = endpoint;
+
+ RELEASE_LOOP_LOCK( "Associate done" );
+
+ status = STATUS_SUCCESS;
+
+complete:
+
+ //
+ // Dereference the endpoint file object, if necessary.
+ //
+
+ if ( endpointFileObject != NULL ) {
+ ObDereferenceObject( endpointFileObject );
+ }
+
+ //
+ // Complete the Associate request.
+ //
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest( Irp, 0 );
+
+ IF_DEBUG(LOOP1) DbgPrint( " Associate request complete\n" );
+ return status;
+
+} // LoopAssociateAddress
+
+
+NTSTATUS
+LoopConnect (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine processes a Connect request.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+
+ IrpSp - Pointer to current stack location in IRP
+
+Return Value:
+
+ NTSTATUS - Status of request
+
+--*/
+
+{
+ NTSTATUS status;
+ PTDI_REQUEST_KERNEL_CONNECT connectRequest;
+ CHAR netbiosName[NETBIOS_NAME_LENGTH+1];
+ PLOOP_CONNECTION connection;
+ PLOOP_ENDPOINT remoteEndpoint;
+ BOOLEAN firstConnect;
+
+ IF_DEBUG(LOOP1) DbgPrint( " Connect request\n" );
+
+ //
+ // Parse the remote address.
+ //
+
+ connectRequest = (PTDI_REQUEST_KERNEL_CONNECT)&IrpSp->Parameters;
+
+ status = LoopParseAddress(
+ (PTA_NETBIOS_ADDRESS)connectRequest->
+ RequestConnectionInformation->RemoteAddress,
+ netbiosName
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(LOOP2) DbgPrint( " Invalid remote address\n" );
+ goto complete;
+ }
+
+ netbiosName[NETBIOS_NAME_LENGTH] = 0; // ensure null-termination
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Address to connect to: \"%s\"\n", netbiosName );
+ }
+
+ //
+ // Make sure that the connection is in the right state.
+ //
+
+ ACQUIRE_LOOP_LOCK( "Connect initial" );
+
+ connection = (PLOOP_CONNECTION)IrpSp->FileObject->FsContext;
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Connection address: %lx\n", connection );
+ }
+
+ if ( connection == NULL ) {
+ RELEASE_LOOP_LOCK( "Connect control channel" );
+ IF_DEBUG(LOOP2) DbgPrint( " Can't Connect on control channel\n" );
+ status = STATUS_INVALID_PARAMETER;
+ goto complete;
+ }
+
+ if ( GET_BLOCK_STATE(connection) != BlockStateBound ) {
+ RELEASE_LOOP_LOCK( "Connection not bound" );
+ IF_DEBUG(LOOP2) DbgPrint( " Local endpoint not bound\n" );
+ status = STATUS_INVALID_PARAMETER;
+ goto complete;
+ }
+
+ //
+ // Find an endpoint with the target address.
+ //
+
+ remoteEndpoint = LoopFindBoundAddress( netbiosName );
+
+ if ( remoteEndpoint == NULL ) {
+ RELEASE_LOOP_LOCK( "Connect no such address" );
+ IF_DEBUG(LOOP2) DbgPrint( " Address does not exist\n" );
+ status = STATUS_INVALID_PARAMETER;
+ goto complete;
+ }
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Target endpoint address: %lx\n", remoteEndpoint );
+ }
+
+ //
+ // Set the connection state to Connecting, to prevent further
+ // connects or listens.
+ //
+
+ SET_BLOCK_STATE( connection, BlockStateConnecting );
+
+ //
+ // Determine whether this is the first active incoming Connect for
+ // the remote endpoint.
+ //
+
+ firstConnect = (BOOLEAN)( remoteEndpoint->IncomingConnectList.Flink ==
+ &remoteEndpoint->IncomingConnectList );
+
+ //
+ // Queue the Connect to the remote endpoint's Incoming Connect list,
+ // in order to prevent another Connect from getting ahead of this
+ // one.
+ //
+
+ InsertTailList(
+ &remoteEndpoint->IncomingConnectList,
+ &Irp->Tail.Overlay.ListEntry
+ );
+
+ IoMarkIrpPending( Irp );
+
+ if ( !firstConnect || (remoteEndpoint->IndicatingConnectIrp != NULL) ) {
+
+ //
+ // A pending Connect already exists, or a Connect indication is
+ // in progress. This Connect remains behind the already pending
+ // Connect.
+ //
+
+ IF_DEBUG(LOOP2) DbgPrint( " Connect already pending\n" );
+
+ RELEASE_LOOP_LOCK( "Connect connects pending" );
+
+ } else {
+
+ //
+ // Indicate the incoming Connect.
+ //
+ // *** Note that IndicateConnect returns with the loopback
+ // driver spin lock released.
+ //
+
+ IF_DEBUG(LOOP2) DbgPrint( " LoopConnect indicating connect\n" );
+ IndicateConnect( remoteEndpoint );
+
+ }
+
+ IF_DEBUG(LOOP1) DbgPrint( " Connect request complete\n" );
+ return status;
+
+complete:
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest( Irp, 0 );
+
+ IF_DEBUG(LOOP1) DbgPrint( " Connect request complete\n" );
+ return status;
+
+} // LoopConnect
+
+
+VOID
+LoopDereferenceConnection (
+ IN PLOOP_CONNECTION Connection
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to dereference a connection block. If the
+ reference count on the block goes to one, and the connection is in
+ the process of disconnecting, the connection block is reset to the
+ Bound state. If the reference count on the block goes to zero, the
+ connection block is deleted.
+
+ The Loopback device object's spin lock must be held when this
+ routine is called.
+
+Arguments:
+
+ Connection - Supplies a pointer to a connection block
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PIRP disconnectIrp = NULL;
+ PIRP closeIrp = NULL;
+
+ IF_DEBUG(LOOP3) {
+ DbgPrint( " Dereferencing connection %lx; old refcnt %lx\n",
+ Connection, Connection->BlockHeader.ReferenceCount );
+ }
+
+ ASSERT( (LONG)Connection->BlockHeader.ReferenceCount > 0 );
+
+ if ( --Connection->BlockHeader.ReferenceCount != 0 ) {
+ return;
+ }
+
+ //
+ // The reference count has gone to zero. Save the address of the
+ // pending Disconnect IRP, if any -- we'll complete the IRP later.
+ // If the connection state is Disconnecting, reset it to Bound. If
+ // the connection state is Closed, delete it.
+ //
+
+ disconnectIrp = Connection->DisconnectIrp;
+ Connection->DisconnectIrp = NULL;
+
+ //
+ // If the connection state is Disconnecting, reset it to Bound or
+ // Unbound, depending on the state of the endpoint. If the
+ // connection state is Closed, delete the connection. Note that the
+ // connection state may also be Closing, which means that an
+ // inactive connection's handle has been closed. We do nothing in
+ // this case; instead we wait until the Close IRP arrives.
+ //
+
+ if ( GET_BLOCK_STATE(Connection) == BlockStateDisconnecting ) {
+
+ if ( GET_BLOCK_STATE(Connection->Endpoint) == BlockStateActive ) {
+
+ IF_DEBUG(LOOP3) {
+ DbgPrint( " Resetting connection %lx to Bound\n",
+ Connection );
+ }
+ SET_BLOCK_STATE( Connection, BlockStateBound );
+
+ } else {
+
+ PLOOP_ENDPOINT endpoint;
+
+ IF_DEBUG(LOOP3) {
+ DbgPrint( " Resetting connection %lx to Unbound\n",
+ Connection );
+ }
+ endpoint = Connection->Endpoint;
+ ASSERT( endpoint != NULL );
+ Connection->Endpoint = NULL;
+
+ SET_BLOCK_STATE( Connection, BlockStateUnbound );
+
+ RemoveEntryList( &Connection->EndpointListEntry );
+ LoopDereferenceEndpoint( endpoint );
+
+ }
+
+ RELEASE_LOOP_LOCK( "Reset connection done" );
+
+ } else if ( GET_BLOCK_STATE(Connection) == BlockStateClosed ) {
+
+ //
+ // The connection file object has been closed, so we can
+ // deallocate the connection block now.
+ //
+
+ IF_DEBUG(LOOP3) {
+ DbgPrint( " Deleting connection %lx\n", Connection );
+ }
+
+ //
+ // Save the address of the pending Close IRP, if any -- we'll
+ // complete the IRP later.
+ //
+
+ closeIrp = Connection->CloseIrp;
+ Connection->CloseIrp = NULL;
+
+ //
+ // Unlink the connection from the endpoint's connection list.
+ //
+
+ if ( Connection->Endpoint != NULL ) {
+ RemoveEntryList( &Connection->EndpointListEntry );
+ LoopDereferenceEndpoint( Connection->Endpoint );
+ }
+
+ //
+ // Unlink the connection from the device's connection list.
+ //
+
+ RemoveEntryList( &Connection->DeviceListEntry );
+
+ RELEASE_LOOP_LOCK( "DerefConn deleting" );
+
+ ObDereferenceObject( Connection->DeviceObject );
+
+ //
+ // Deallocate the connection block.
+ //
+
+ DEBUG SET_BLOCK_TYPE( Connection, BlockTypeGarbage );
+ DEBUG SET_BLOCK_STATE( Connection, BlockStateDead );
+ DEBUG SET_BLOCK_SIZE( Connection, -1 );
+ DEBUG Connection->BlockHeader.ReferenceCount = -1;
+ DEBUG Connection->Endpoint = NULL;
+ DEBUG Connection->RemoteConnection = NULL;
+
+ ExFreePool( Connection );
+
+ } else {
+
+ ASSERT( GET_BLOCK_STATE(Connection) == BlockStateClosing );
+
+ RELEASE_LOOP_LOCK( "DerefConn closing -- no action" );
+
+ }
+
+ //
+ // If a Disconnect IRP is pending, complete it now.
+ //
+
+ if ( disconnectIrp != NULL ) {
+
+ IF_DEBUG(LOOP3) {
+ DbgPrint( " Completing Disconnect IRP %lx\n",
+ disconnectIrp );
+ }
+ disconnectIrp->IoStatus.Status = STATUS_SUCCESS;
+ disconnectIrp->IoStatus.Information = 0;
+
+ IoCompleteRequest( disconnectIrp, 2 );
+
+ }
+
+ //
+ // If a Close IRP is pending, complete it now.
+ //
+
+ if ( closeIrp != NULL ) {
+
+ IF_DEBUG(LOOP3) {
+ DbgPrint( " Completing Close IRP %lx\n", closeIrp );
+ }
+ closeIrp->IoStatus.Status = STATUS_SUCCESS;
+ closeIrp->IoStatus.Information = 0;
+
+ IoCompleteRequest( closeIrp, 2 );
+
+ }
+
+ ACQUIRE_LOOP_LOCK( "DerefConn done" );
+
+ return;
+
+} // LoopDereferenceConnection
+
+
+NTSTATUS
+LoopDisassociateAddress (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine processes a Disassociate Address request.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+
+ IrpSp - Pointer to current stack location in IRP
+
+Return Value:
+
+ NTSTATUS - Status of request
+
+--*/
+
+{
+ NTSTATUS status;
+ PLOOP_CONNECTION connection;
+ PLOOP_ENDPOINT endpoint;
+
+ IF_DEBUG(LOOP1) DbgPrint( " Disassociate request\n" );
+
+ ACQUIRE_LOOP_LOCK( "Disassociate initial" );
+
+ //
+ // Verify that the connection is in the proper state.
+ //
+
+ connection = (PLOOP_CONNECTION)IrpSp->FileObject->FsContext;
+
+ if ( connection == NULL ) {
+ RELEASE_LOOP_LOCK( "Disassociate control channel" );
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Can't Disassociate on control channel\n" );
+ }
+ status = STATUS_INVALID_PARAMETER;
+ goto complete;
+ }
+
+ if ( GET_BLOCK_STATE(connection) != BlockStateBound ) {
+ RELEASE_LOOP_LOCK( "Associate conn not bound" );
+ IF_DEBUG(LOOP2) DbgPrint( " Connection not bound\n" );
+ status = STATUS_INVALID_PARAMETER;
+ goto complete;
+ }
+
+ //
+ // Remove this connection from the endpoint's connection list.
+ // Dereference the endpoint block. Indicate that the connection is
+ // no longer bound to an address.
+ //
+
+ endpoint = connection->Endpoint;
+ ASSERT( endpoint != NULL );
+ connection->Endpoint = NULL;
+
+ SET_BLOCK_STATE( connection, BlockStateUnbound );
+
+ RemoveEntryList( &connection->EndpointListEntry );
+ LoopDereferenceEndpoint( endpoint );
+
+ RELEASE_LOOP_LOCK( "Disassociate done" );
+
+ status = STATUS_SUCCESS;
+
+complete:
+
+ //
+ // Complete the Disassociate request.
+ //
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest( Irp, 0 );
+
+ IF_DEBUG(LOOP1) DbgPrint( " Disassociate request complete\n" );
+ return status;
+
+} // LoopDisassociate
+
+
+NTSTATUS
+LoopDisconnect (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine processes a Disconnect request.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+
+ IrpSp - Pointer to current stack location in IRP
+
+Return Value:
+
+ NTSTATUS - Status of request
+
+--*/
+
+{
+ NTSTATUS status;
+ PTDI_REQUEST_KERNEL_DISCONNECT disconnectRequest;
+ PLOOP_CONNECTION connection;
+
+ IF_DEBUG(LOOP1) DbgPrint( " Disconnect request\n" );
+
+ disconnectRequest = (PTDI_REQUEST_KERNEL_DISCONNECT)&IrpSp->Parameters;
+ connection = (PLOOP_CONNECTION)IrpSp->FileObject->FsContext;
+ IF_DEBUG(LOOP2) DbgPrint( " Connection: %lx\n", connection );
+
+ ACQUIRE_LOOP_LOCK( "Disconnect initial" );
+
+ if ( connection == NULL ) {
+ RELEASE_LOOP_LOCK( "Disconnect control channel" );
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Can't Disconnect on control channel\n" );
+ }
+ status = STATUS_INVALID_PARAMETER;
+ goto complete;
+ }
+
+ //
+ // If the connection state is Bound, then this Disconnect must have
+ // been issued from within a Connect indication.
+ //
+
+ if ( GET_BLOCK_STATE(connection) == BlockStateBound ) {
+
+ PIRP connectIrp = connection->Endpoint->IndicatingConnectIrp;
+
+ if ( connectIrp != NULL ) {
+
+ //
+ // Abort the indicating Connect.
+ //
+
+ connection->Endpoint->IndicatingConnectIrp = NULL;
+
+ RELEASE_LOOP_LOCK( "Disconnect abort indication" );
+
+ connectIrp->IoStatus.Status = STATUS_DISCONNECTED;
+ IoCompleteRequest( connectIrp, 2 );
+
+ } else {
+
+ RELEASE_LOOP_LOCK( "Disconnect bound, no indication" );
+
+ }
+
+ status = STATUS_SUCCESS;
+ goto complete;
+
+ }
+
+ if ( GET_BLOCK_STATE(connection) != BlockStateActive ) {
+
+ RELEASE_LOOP_LOCK( "Disconnect closing" );
+ IF_DEBUG(LOOP2) DbgPrint( " Connection already disconnected\n" );
+
+ status = STATUS_SUCCESS;
+ goto complete;
+
+ }
+
+ //
+ // Set the disconnect IRP pointer in the connection. The IRP will
+ // be completed when the connection is actually deleted.
+ //
+
+ IoMarkIrpPending( Irp );
+ connection->DisconnectIrp = Irp;
+
+ SET_BLOCK_STATE( connection, BlockStateDisconnecting );
+ LoopDoDisconnect( connection, TRUE );
+
+ RELEASE_LOOP_LOCK( "Disconnect done" );
+
+ return STATUS_PENDING;
+
+complete:
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest( Irp, 2 );
+
+ IF_DEBUG(LOOP1) DbgPrint( " Disconnect request complete\n" );
+ return status;
+
+} // LoopDisconnect
+
+
+VOID
+LoopDoDisconnect (
+ IN PLOOP_CONNECTION Connection,
+ IN BOOLEAN ClientInitiated
+ )
+
+/*++
+
+Routine Description:
+
+ This routine does the work in disconnecting a connection. It is
+ called by LoopDisconnect and LoopDispatchClose.
+
+ The loopback device object's spin lock must be held when this
+ function is called. The lock is still held when the function
+ returns.
+
+Arguments:
+
+ Connection - Pointer to connection block
+
+ ClientInitiated - Indicates whether the local client initiated the
+ disconnect, either by issuing a TdiDisconnect or by closing the
+ endpoint
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLOOP_CONNECTION remoteConnection;
+ PLOOP_ENDPOINT endpoint;
+ PLIST_ENTRY listEntry;
+ PIRP pendingIrp;
+ PTDI_IND_DISCONNECT disconnectHandler;
+ PVOID disconnectContext;
+
+ IF_DEBUG(LOOP3) {
+ DbgPrint( " DoDisconnect called for connection %lx\n",
+ Connection );
+ }
+
+ //
+ // If there is a remote connection, run it down first.
+ //
+
+ remoteConnection = Connection->RemoteConnection;
+ Connection->RemoteConnection = NULL;
+
+ if ( (remoteConnection != NULL) &&
+ (GET_BLOCK_STATE(remoteConnection) == BlockStateActive) ) {
+ SET_BLOCK_STATE( remoteConnection, BlockStateDisconnecting );
+ LoopDoDisconnect( remoteConnection, FALSE );
+ }
+
+ //
+ // Abort pending receives and sends.
+ //
+
+ listEntry = RemoveHeadList( &Connection->PendingReceiveList );
+
+ while ( listEntry != &Connection->PendingReceiveList ) {
+
+ LoopDereferenceConnection( Connection );
+
+ RELEASE_LOOP_LOCK( "DoDisc complete Receive" );
+
+ pendingIrp = CONTAINING_RECORD(
+ listEntry,
+ IRP,
+ Tail.Overlay.ListEntry
+ );
+ pendingIrp->IoStatus.Status = STATUS_DISCONNECTED;
+ IoCompleteRequest( pendingIrp, 2 );
+
+ ACQUIRE_LOOP_LOCK( "DoDisc complete Receive done" );
+
+ listEntry = RemoveHeadList( &Connection->PendingReceiveList );
+ }
+
+ listEntry = RemoveHeadList( &Connection->IncomingSendList );
+
+ while ( listEntry != &Connection->IncomingSendList ) {
+
+ LoopDereferenceConnection( remoteConnection );
+ LoopDereferenceConnection( Connection );
+
+ RELEASE_LOOP_LOCK( "DoDisc complete Send" );
+
+ pendingIrp = CONTAINING_RECORD(
+ listEntry,
+ IRP,
+ Tail.Overlay.ListEntry
+ );
+ pendingIrp->IoStatus.Status = STATUS_DISCONNECTED;
+ IoCompleteRequest( pendingIrp, 2 );
+
+ ACQUIRE_LOOP_LOCK( "DoDisc complete Send done" );
+
+ listEntry = RemoveHeadList( &Connection->IncomingSendList );
+ }
+
+ //
+ // If this is a remotely-initiated disconnect, and the local client
+ // has established a disconnect event handler, call that handler
+ // now.
+ //
+
+ endpoint = Connection->Endpoint;
+ disconnectHandler = endpoint->DisconnectHandler;
+ disconnectContext = endpoint->DisconnectContext;
+
+ if ( !ClientInitiated && (disconnectHandler != NULL) ) {
+ RELEASE_LOOP_LOCK( "DoDisc call handler" );
+ (VOID)disconnectHandler(
+ disconnectContext,
+ Connection->ConnectionContext,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ TDI_DISCONNECT_ABORT
+ );
+ ACQUIRE_LOOP_LOCK( "DoDisc call handler done" );
+ }
+
+ //
+ // Dereference the connection. This will result in the completion
+ // of the disconnect IRP, if the reference count goes to zero.
+ //
+
+ LoopDereferenceConnection( Connection );
+
+ return;
+
+} // LoopDoDisconnect
+
+
+NTSTATUS
+LoopListen (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine processes a Listen request.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+
+ IrpSp - Pointer to current stack location in IRP
+
+Return Value:
+
+ NTSTATUS - Status of request
+
+--*/
+
+{
+ NTSTATUS status;
+ PLOOP_CONNECTION connection;
+ PLOOP_ENDPOINT endpoint;
+
+ IrpSp; // prevent compiler warnings
+
+ IF_DEBUG(LOOP1) DbgPrint( " Listen request\n" );
+
+ connection = (PLOOP_CONNECTION)IrpSp->FileObject->FsContext;
+ IF_DEBUG(LOOP2) DbgPrint( " Connection address: %lx\n", connection );
+
+ //
+ // !!! When the DELAYED_ACCEPT bit is defined, check here to ensure
+ // that it isn't set by the client. The loopback driver doesn't
+ // support delayed accept on Listen requests.
+ //
+
+ //
+ // Make sure the connection is in the right state.
+ //
+
+ ACQUIRE_LOOP_LOCK( "Listen initial" );
+
+ if ( connection == NULL ) {
+ RELEASE_LOOP_LOCK( "Listen control channel" );
+ IF_DEBUG(LOOP2) DbgPrint( " Can't Listen on control channel\n" );
+ status = STATUS_INVALID_PARAMETER;
+ goto complete;
+ }
+
+ if ( GET_BLOCK_STATE(connection) != BlockStateBound ) {
+ RELEASE_LOOP_LOCK( "Listen not bound" );
+ IF_DEBUG(LOOP2) DbgPrint( " Connection not bound\n" );
+ status = STATUS_INVALID_PARAMETER;
+ goto complete;
+ }
+
+ //
+ // Set the connection state to Connecting, to prevent further
+ // connects or listens.
+ //
+
+ SET_BLOCK_STATE( connection, BlockStateConnecting );
+
+ endpoint = connection->Endpoint;
+ IF_DEBUG(LOOP2) DbgPrint( " Endpoint address: %lx\n", endpoint );
+
+ //
+ // Queue the Listen to the endpoint's Pending Listen list.
+ //
+
+ InsertTailList(
+ &endpoint->PendingListenList,
+ &Irp->Tail.Overlay.ListEntry
+ );
+
+ IoMarkIrpPending( Irp );
+
+ //
+ // Check for a pending connect.
+ //
+
+ if ( (endpoint->IndicatingConnectIrp != NULL) ||
+ (endpoint->IncomingConnectList.Flink ==
+ &endpoint->IncomingConnectList) ) {
+
+ //
+ // There is no pending Connect, or a Connect indication is
+ // already in progress.
+ //
+
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " No pending Connect; leaving IRP %lx queued \n",
+ Irp );
+ }
+
+ RELEASE_LOOP_LOCK( "Listen no Connect" );
+
+ } else {
+
+ //
+ // There is a pending Connect. Call IndicateConnect to
+ // satisfy the Listen.
+ //
+ // *** Note that IndicateConnect returns with the loopback
+ // driver spin lock released.
+ //
+
+ IF_DEBUG(LOOP2) DbgPrint( " LoopListen indicating connect\n" );
+ IndicateConnect( endpoint );
+
+ }
+
+ IF_DEBUG(LOOP1) DbgPrint( " Listen request complete\n" );
+ return status;
+
+complete:
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest( Irp, 2 );
+
+ IF_DEBUG(LOOP1) DbgPrint( " Listen request complete\n" );
+ return status;
+
+} // LoopListen
+
+
+NTSTATUS
+CompleteConnection (
+ IN PIRP ListenIrp,
+ IN PIRP ConnectIrp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine completes the process of creating a connection. It
+ creates a connection block for each end of the connection and links
+ them together. The connection blocks are also linked off of their
+ respective endpoint blocks.
+
+ This routine must be called with the loopback device spin lock held.
+ The lock remains held when the function returns, although it is
+ released and reacquired during the function's operation.
+
+Arguments:
+
+ ListenIrp - Pointer to IRP used for Listen request
+
+ ConnectIrp - Pointer to IRP used for Connect request
+
+Return Value:
+
+ NTSTATUS - Indicates whether the connection was successfully created
+
+--*/
+
+{
+ NTSTATUS status;
+ PLOOP_CONNECTION listenerConnection;
+ PLOOP_CONNECTION connectorConnection;
+ PLOOP_ENDPOINT listenerEndpoint;
+ PLOOP_ENDPOINT connectorEndpoint;
+ PIO_STACK_LOCATION irpSp;
+ PTDI_REQUEST_KERNEL parameters;
+ PTDI_CONNECTION_INFORMATION connInfo;
+ TA_NETBIOS_ADDRESS connectorAddress;
+ TA_NETBIOS_ADDRESS listenerAddress;
+ int length;
+
+ //
+ // Get pointers.
+ //
+
+ irpSp = IoGetCurrentIrpStackLocation( ListenIrp );
+ listenerConnection = (PLOOP_CONNECTION)irpSp->FileObject->FsContext;
+ listenerEndpoint = listenerConnection->Endpoint;
+ irpSp = IoGetCurrentIrpStackLocation( ConnectIrp );
+ connectorConnection = (PLOOP_CONNECTION)irpSp->FileObject->FsContext;
+ connectorEndpoint = connectorConnection->Endpoint;
+
+ //
+ // Update the connection blocks.
+ //
+
+ listenerConnection->RemoteConnection = connectorConnection;
+ SET_BLOCK_STATE( listenerConnection, BlockStateActive );
+
+ connectorConnection->RemoteConnection = listenerConnection;
+ SET_BLOCK_STATE( connectorConnection, BlockStateActive );
+
+ //
+ // Increment the reference counts on the connections to account for
+ // the active link.
+ //
+
+ listenerConnection->BlockHeader.ReferenceCount++;
+ connectorConnection->BlockHeader.ReferenceCount++;
+ IF_DEBUG(LOOP3) {
+ DbgPrint( " New refcnt on connection %lx is %lx\n",
+ listenerConnection,
+ listenerConnection->BlockHeader.ReferenceCount );
+ DbgPrint( " New refcnt on connection %lx is %lx\n",
+ connectorConnection,
+ connectorConnection->BlockHeader.ReferenceCount );
+ }
+
+ //
+ // Save the addresses of the connector and the listener.
+ //
+
+ connectorAddress.TAAddressCount = 1;
+ connectorAddress.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS;
+ connectorAddress.Address[0].AddressLength = sizeof(TDI_ADDRESS_NETBIOS);
+ connectorAddress.Address[0].Address[0].NetbiosNameType =
+ TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
+ RtlMoveMemory(
+ connectorAddress.Address[0].Address[0].NetbiosName,
+ connectorEndpoint->NetbiosName,
+ NETBIOS_NAME_LENGTH
+ );
+
+ listenerAddress.TAAddressCount = 1;
+ listenerAddress.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS;
+ listenerAddress.Address[0].AddressLength = sizeof(TDI_ADDRESS_NETBIOS);
+ listenerAddress.Address[0].Address[0].NetbiosNameType =
+ TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
+ RtlMoveMemory(
+ listenerAddress.Address[0].Address[0].NetbiosName,
+ listenerEndpoint->NetbiosName,
+ NETBIOS_NAME_LENGTH
+ );
+
+ //
+ // Complete the Listen (or Accept) and Connect I/O requests.
+ //
+
+ RELEASE_LOOP_LOCK( "CompleteConnection complete IRPs" );
+
+ irpSp = IoGetCurrentIrpStackLocation( ListenIrp );
+ parameters = (PTDI_REQUEST_KERNEL)&irpSp->Parameters;
+ connInfo = parameters->ReturnConnectionInformation;
+
+ status = STATUS_SUCCESS;
+
+ if ( connInfo != NULL ) {
+
+ length = connInfo->RemoteAddressLength;
+
+ if ( length != 0 ) {
+ length = MIN( sizeof(connectorAddress), length );
+ RtlMoveMemory(
+ connInfo->RemoteAddress,
+ &connectorAddress,
+ length
+ );
+ if ( length < sizeof(connectorAddress) ) {
+ status = STATUS_BUFFER_OVERFLOW;
+ }
+ }
+
+ connInfo->UserDataLength = 0;
+ connInfo->OptionsLength = 0;
+
+ }
+
+ ListenIrp->IoStatus.Status = status;
+
+ irpSp = IoGetCurrentIrpStackLocation( ConnectIrp );
+ parameters = (PTDI_REQUEST_KERNEL)&irpSp->Parameters;
+ connInfo = parameters->ReturnConnectionInformation;
+
+ status = STATUS_SUCCESS;
+
+ if ( connInfo != NULL ) {
+
+ length = connInfo->RemoteAddressLength;
+
+ if ( length != 0 ) {
+ length = MIN( sizeof(listenerAddress), length );
+ RtlMoveMemory(
+ connInfo->RemoteAddress,
+ &listenerAddress,
+ length
+ );
+ if ( length < sizeof(listenerAddress) ) {
+ status = STATUS_BUFFER_OVERFLOW;
+ }
+ }
+
+ connInfo->UserDataLength = 0;
+ connInfo->OptionsLength = 0;
+
+ }
+
+ ConnectIrp->IoStatus.Status = status;
+
+ IoCompleteRequest( ListenIrp, 2 );
+ IoCompleteRequest( ConnectIrp, 2 );
+
+ ACQUIRE_LOOP_LOCK( "CompleteConnection complete IRPs done" );
+
+ return STATUS_SUCCESS;
+
+} // CompleteConnection
+
+
+VOID
+IndicateConnect (
+ IN PLOOP_ENDPOINT Endpoint
+ )
+
+/*++
+
+Routine Description:
+
+ This routine does the work of indicating an incoming connect.
+
+ The loopback device object's spin lock must be held when this
+ function is called.
+
+ *** The lock is released when the function returns.
+
+Arguments:
+
+ Endpoint - Pointer to receiving (listening) endpoint block
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS status;
+ PLIST_ENTRY listEntry;
+ PIRP listenIrp;
+ PIRP connectIrp;
+ PTDI_IND_CONNECT connectHandler;
+ PVOID connectContext;
+ PVOID connectionContext;
+ PIO_STACK_LOCATION connectIrpSp;
+ PLOOP_ENDPOINT connectingEndpoint;
+ TA_NETBIOS_ADDRESS address;
+
+ //
+ // Reference the endpoint to prevent it from going away while this
+ // routine is running.
+ //
+
+ Endpoint->BlockHeader.ReferenceCount++;
+ IF_DEBUG(LOOP3) {
+ DbgPrint( " New refcnt on endpoint %lx is %lx\n",
+ Endpoint,
+ Endpoint->BlockHeader.ReferenceCount );
+ }
+
+ //
+ // If the endpoint has a pending Listen, satisfy it with this
+ // Connect. If there is no pending Listen, and a Connect handler
+ // has been enabled on the endpoint, call it.
+ //
+
+ while ( TRUE ) {
+
+ //
+ // We have a Connect pending. Is there a pending Listen?
+ //
+
+ listEntry = RemoveHeadList( &Endpoint->PendingListenList );
+
+ if ( listEntry != &Endpoint->PendingListenList ) {
+
+ //
+ // Found a pending Listen. Use it to satisfy the first
+ // incoming Connect.
+ //
+
+ listenIrp = CONTAINING_RECORD(
+ listEntry,
+ IRP,
+ Tail.Overlay.ListEntry
+ );
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Listen IRP pending: %lx\n", listenIrp );
+ }
+
+ listEntry = RemoveHeadList(
+ &Endpoint->IncomingConnectList
+ );
+ ASSERT( listEntry != &Endpoint->IncomingConnectList );
+ connectIrp = CONTAINING_RECORD(
+ listEntry,
+ IRP,
+ Tail.Overlay.ListEntry
+ );
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Connect IRP pending: %lx\n", connectIrp );
+ }
+
+ CompleteConnection( listenIrp, connectIrp );
+
+ //
+ // Fall to bottom of loop to handle more incoming Connects.
+ //
+
+ } else {
+
+ //
+ // No pending Listen. Is there a Connection handler?
+ //
+
+ connectHandler = Endpoint->ConnectHandler;
+ connectContext = Endpoint->ConnectContext;
+
+ if ( connectHandler == NULL ) {
+
+ //
+ // No Connect handler. The Connect must remain queued.
+ //
+
+ IF_DEBUG(LOOP2) DbgPrint( " No Connect handler\n" );
+
+ break;
+
+ }
+
+ //
+ // The endpoint has a Connect handler. Call it. If it
+ // returns STATUS_EVENT_DONE, it handled the event by
+ // issuing a TdiAccept or TdiDisconnect from within the
+ // handler. If it returns STATUS_EVENT_PENDING, it also
+ // returns a connection context value indicating which
+ // connection it will use to issue a TdiAccept or
+ // TdiDisconect at a later time. If it returns
+ // STATUS_INSUFFICIENT_RESOURCES, it can't accept a new
+ // connection at this time, so we need to return an error to
+ // the Connect.
+ //
+ // First, remove the first Connect from the Incoming Connect
+ // list, and make it the Indicating Connect. It must be
+ // removed from the list to ensure that it isn't completed
+ // by LoopCleanup while we're indicating it.
+ //
+
+ listEntry = RemoveHeadList(
+ &Endpoint->IncomingConnectList
+ );
+ ASSERT( listEntry != &Endpoint->IncomingConnectList );
+ connectIrp = CONTAINING_RECORD(
+ listEntry,
+ IRP,
+ Tail.Overlay.ListEntry
+ );
+ Endpoint->IndicatingConnectIrp = connectIrp;
+
+ RELEASE_LOOP_LOCK( "IndicateConnect calling handler" );
+
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Connect handler: %lx\n", connectHandler );
+ }
+
+ //
+ // Build a TRANSPORT_ADDRESS describing the connector.
+ //
+
+ connectIrpSp = IoGetCurrentIrpStackLocation( connectIrp );
+ connectingEndpoint =
+ ((PLOOP_CONNECTION)connectIrpSp->FileObject->FsContext)->
+ Endpoint;
+
+ address.TAAddressCount = 1;
+ address.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS;
+ address.Address[0].AddressLength = sizeof(TDI_ADDRESS_NETBIOS);
+ address.Address[0].Address[0].NetbiosNameType =
+ TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
+ RtlMoveMemory(
+ address.Address[0].Address[0].NetbiosName,
+ connectingEndpoint->NetbiosName,
+ NETBIOS_NAME_LENGTH
+ );
+
+ //
+ // Call the Connect handler.
+ //
+
+ status = connectHandler(
+ connectContext,
+ sizeof(address),
+ &address,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ &connectionContext
+ );
+
+ ACQUIRE_LOOP_LOCK( "IndicateConnect after calling handler" );
+
+ if ( status == STATUS_EVENT_DONE ) {
+
+ //
+ // The Connect handler issued a TdiAccept or a
+ // TdiDisconnect, so the indicating Connect has already
+ // been completed.
+ //
+ // *** Note that Endpoint->IndicatingConnectIrp is
+ // cleared within LoopAccept or LoopDisconnect.
+ //
+
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Connect handler handled event\n" );
+ }
+
+ //
+ // Fall to bottom of loop to handle more incoming
+ // Connects.
+ //
+
+ } else if ( status == STATUS_EVENT_PENDING ) {
+
+ //
+ // The Connect handler has delayed the issuance of the
+ // TdiAccept or TdiDisconnect. Leave the indication
+ // pending.
+ //
+ // *** Note that Endpoint->IndicatingConnectIrp is
+ // cleared within LoopAccept or LoopDisconnect.
+ //
+
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Connect handler pended event\n" );
+ }
+
+ break;
+
+ } else {
+
+ //
+ // The Connect handler couldn't accept the connection,
+ // because it was out of resources. Abort the Connect.
+ //
+
+ ASSERT( status == STATUS_INSUFFICIENT_RESOURCES );
+
+ IF_DEBUG(LOOP2) DbgPrint( " No resources\n" );
+
+ Endpoint->IndicatingConnectIrp = NULL;
+
+ RELEASE_LOOP_LOCK( "IndicateConnect aborting connect" );
+
+ connectIrp->IoStatus.Status = status;
+ IoCompleteRequest( connectIrp, 2 );
+
+ ACQUIRE_LOOP_LOCK( "IndicateConnect connect aborted" );
+
+ //
+ // Fall to bottom of loop to handle more incoming
+ // Connects.
+ //
+
+ }
+
+ } // pending listen?
+
+ //
+ // If we get here, we need to indicate the next incoming
+ // Connect, if there is one.
+ //
+
+ if ( (GET_BLOCK_STATE(Endpoint) != BlockStateActive) ||
+ (Endpoint->IncomingConnectList.Flink ==
+ &Endpoint->IncomingConnectList) ) {
+
+ //
+ // No more Connects, or endpoint no longer active. Leave.
+ //
+
+ break;
+
+ }
+
+ //
+ // Process the next Connect.
+ //
+
+ } // while ( TRUE )
+
+ //
+ // Remote the endpoint reference acquired at the start of this
+ // routine.
+ //
+
+ LoopDereferenceEndpoint( Endpoint );
+
+ RELEASE_LOOP_LOCK( "IndicateConnect done" );
+
+ return;
+
+} // IndicateConnect
diff --git a/private/ntos/tdi/loopback/datagram.c b/private/ntos/tdi/loopback/datagram.c
new file mode 100644
index 000000000..0c5ec7c02
--- /dev/null
+++ b/private/ntos/tdi/loopback/datagram.c
@@ -0,0 +1,848 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ datagram.c
+
+Abstract:
+
+ This module implements datagram logic for the loopback Transport
+ Provider driver for NT LAN Manager.
+
+Author:
+
+ Chuck Lenzmeier (chuckl) 15-Aug-1991
+
+Revision History:
+
+--*/
+
+#include "loopback.h"
+
+//
+// Local declarations
+//
+
+STATIC
+VOID
+CompleteReceiveDatagram (
+ IN PIRP ReceiveIrp,
+ IN PIRP SendIrp
+ );
+
+STATIC
+VOID
+IndicateReceiveDatagram (
+ IN PLOOP_CONNECTION ReceivingConnection,
+ IN PIRP InitialSendIrp
+ );
+
+
+NTSTATUS
+LoopReceiveDatagram (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine processes a Receive Datagram request.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+
+ IrpSp - Pointer to current stack location in IRP
+
+Return Value:
+
+ NTSTATUS - Status of request
+
+--*/
+
+{
+ PLOOP_ENDPOINT receivingEndpoint;
+ PLOOP_CONNECTION sendingConnection;
+
+ IF_DEBUG(LOOP1) DbgPrint( " Receive Datagram request\n" );
+
+ //
+ // Verify that the receiving endpoint is connected.
+ //
+
+ receivingEndpoint = (PLOOP_ENDPOINT)IrpSp->FileObject->FsContext;
+
+ if ( receivingConnection == NULL ) {
+ IF_DEBUG(LOOP2) DbgPrint( " Can't Receive on control channel\n" );
+ Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+ IoCompleteRequest( Irp, 0 );
+ IF_DEBUG(LOOP1) DbgPrint( " Receive request complete\n" );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ sendingConnection = receivingConnection->RemoteConnection;
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Receiving connection: %lx\n", receivingConnection );
+ DbgPrint( " Sending connection: %lx\n", sendingConnection );
+ }
+
+ ACQUIRE_LOOP_LOCK( "Receive initial" );
+
+ if ( (sendingConnection == NULL) ||
+ (GET_BLOCK_STATE(receivingConnection) != BlockStateActive) ) {
+ RELEASE_LOOP_LOCK( "Receive closing" );
+ IF_DEBUG(LOOP2) DbgPrint( " Connection not connected\n" );
+ Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+ IoCompleteRequest( Irp, 0 );
+ IF_DEBUG(LOOP1) DbgPrint( " Receive request complete\n" );
+ return STATUS_DISCONNECTED;
+ }
+
+ //
+ // Queue the Receive to the connection's Pending Receive list.
+ //
+
+ InsertTailList(
+ &receivingConnection->PendingReceiveList,
+ &Irp->Tail.Overlay.ListEntry
+ );
+
+ IoMarkIrpPending( Irp );
+
+ //
+ // Check for pending data.
+ //
+
+ if ( (receivingConnection->IndicatingSendIrp != NULL) ||
+ (receivingConnection->IncomingSendList.Flink ==
+ &receivingConnection->IncomingSendList) ) {
+
+ //
+ // There is no pending Send, or an indication is already in
+ // progress. Reference the connection to account for the
+ // Receive IRP.
+ //
+
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " No pending Send; leaving IRP %lx queued \n", Irp );
+ }
+ receivingConnection->BlockHeader.ReferenceCount++;
+ IF_DEBUG(LOOP3) {
+ DbgPrint( " New refcnt on connection %lx is %lx\n",
+ receivingConnection,
+ receivingConnection->BlockHeader.ReferenceCount );
+ }
+
+ RELEASE_LOOP_LOCK( "Receive no Send" );
+
+ } else {
+
+ //
+ // There is pending data. Call IndicateReceive to satisfy
+ // the Receive.
+ //
+ // *** Note that IndicateReceive returns with the loopback
+ // driver spin lock released.
+ //
+
+ IF_DEBUG(LOOP2) DbgPrint( " LoopReceive indicating receive\n" );
+ IndicateReceive( receivingConnection, NULL );
+
+ }
+
+ IF_DEBUG(LOOP1) DbgPrint( " Receive request %lx complete\n", Irp );
+ return STATUS_PENDING;
+
+} // LoopReceive
+
+
+NTSTATUS
+LoopSend (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine processes a Send request.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+
+ IrpSp - Pointer to current stack location in IRP
+
+Return Value:
+
+ NTSTATUS - Status of request
+
+--*/
+
+{
+ PLOOP_CONNECTION sendingConnection;
+ PLOOP_CONNECTION receivingConnection;
+ BOOLEAN firstSend;
+
+ IF_DEBUG(LOOP1) DbgPrint( " Send request\n" );
+
+ //
+ // Verify that the sending connection is connected.
+ //
+
+ sendingConnection = (PLOOP_CONNECTION)IrpSp->FileObject->FsContext;
+
+ if ( sendingConnection == NULL ) {
+ IF_DEBUG(LOOP2) DbgPrint( " Can't Send on control channel\n" );
+ Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+ IoCompleteRequest( Irp, 0 );
+ IF_DEBUG(LOOP1) DbgPrint( " Send request complete\n" );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ receivingConnection = sendingConnection->RemoteConnection;
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Sending connection: %lx\n", sendingConnection );
+ DbgPrint( " Receiving connection: %lx\n", receivingConnection );
+ }
+
+ ACQUIRE_LOOP_LOCK( "Send initial" );
+
+ if ( (receivingConnection == NULL) ||
+ (GET_BLOCK_STATE(sendingConnection) != BlockStateActive) ) {
+ RELEASE_LOOP_LOCK( "Send closing" );
+ IF_DEBUG(LOOP2) DbgPrint( " Connection not connected\n" );
+ Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+ IoCompleteRequest( Irp, 0 );
+ IF_DEBUG(LOOP1) DbgPrint( " Send request complete\n" );
+ return STATUS_DISCONNECTED;
+ }
+
+ //
+ // Reference both ends.
+ //
+
+ sendingConnection->BlockHeader.ReferenceCount++;
+ receivingConnection->BlockHeader.ReferenceCount++;
+ IF_DEBUG(LOOP3) {
+ DbgPrint( " New refcnt on connection %lx is %lx\n",
+ sendingConnection,
+ sendingConnection->BlockHeader.ReferenceCount );
+ DbgPrint( " New refcnt on connection %lx is %lx\n",
+ receivingConnection,
+ receivingConnection->BlockHeader.ReferenceCount );
+ }
+
+ //
+ // Determine whether this is the first active incoming Send for the
+ // receiving connection.
+ //
+
+ firstSend = (BOOLEAN)( receivingConnection->IncomingSendList.Flink ==
+ &receivingConnection->IncomingSendList );
+
+ //
+ // Queue the Send to the receiving connection's Incoming Send list,
+ // in order to prevent another Send from getting ahead of this one.
+ //
+
+ InsertTailList(
+ &receivingConnection->IncomingSendList,
+ &Irp->Tail.Overlay.ListEntry
+ );
+
+ IoMarkIrpPending( Irp );
+
+ if ( !firstSend || (receivingConnection->IndicatingSendIrp != NULL) ) {
+
+ //
+ // Pending data already exists, or an indication is already in
+ // progress. This Send remains behind the already pending data.
+ //
+
+ IF_DEBUG(LOOP2) DbgPrint( " Data already pending\n" );
+
+ RELEASE_LOOP_LOCK( "Send sends pending" );
+
+ } else {
+
+ //
+ // Indicate the incoming data.
+ //
+ // *** Note that IndicateReceive returns with the loopback
+ // driver spin lock released.
+ //
+
+ IF_DEBUG(LOOP2) DbgPrint( " LoopSend indicating receive\n" );
+ IndicateReceive( receivingConnection, Irp );
+
+ }
+
+ IF_DEBUG(LOOP1) DbgPrint( " Send request %lx complete\n", Irp );
+ return STATUS_PENDING;
+
+} // LoopSend
+
+
+VOID
+CompleteReceive (
+ IN PIRP ReceiveIrp,
+ IN PIRP SendIrp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine completes the process of sending a message. It copies
+ the message from the source buffer into the destination buffer.
+
+ The reference counts on the owning connections must have been
+ incremented to account for the requesting IRPs in order to prevent
+ their deletion.
+
+Arguments:
+
+ ReceiveIrp - Pointer to IRP used for Receive request
+
+ SendIrp - Pointer to IRP used for Send request
+
+Return Value:
+
+ NTSTATUS - Indicates whether the connection was successfully created
+
+--*/
+
+{
+ NTSTATUS status;
+ PIO_STACK_LOCATION sendIrpSp;
+ PIO_STACK_LOCATION receiveIrpSp;
+ ULONG copyLength;
+ PTDI_REQUEST_KERNEL_SEND sendRequest;
+ PTDI_REQUEST_KERNEL_RECEIVE receiveRequest;
+ PLOOP_CONNECTION sendingConnection;
+ PLOOP_CONNECTION receivingConnection;
+
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Send IRP %lx completes receive IRP %lx\n",
+ SendIrp, ReceiveIrp );
+ }
+
+ //
+ // Copy the data from the source buffer into the destination buffer.
+ //
+ // *** Note that we special-case zero-length copies. We don't
+ // bother to call LoopCopyData. Part of the reason for this
+ // is that a zero-length send or receive issued from user mode
+ // yields an IRP that has a NULL MDL address.
+ //
+
+ sendIrpSp = IoGetCurrentIrpStackLocation( SendIrp );
+ sendRequest = (PTDI_REQUEST_KERNEL_SEND)&sendIrpSp->Parameters;
+ copyLength = sendRequest->SendLength;
+ IF_DEBUG(LOOP4) {
+ DbgPrint( " Send request at %lx, send length %lx\n",
+ sendRequest, copyLength );
+ }
+ receiveIrpSp = IoGetCurrentIrpStackLocation( ReceiveIrp );
+ receiveRequest = (PTDI_REQUEST_KERNEL_RECEIVE)&receiveIrpSp->Parameters;
+ IF_DEBUG(LOOP4) {
+ DbgPrint( " Receive request at %lx, receive length %lx\n",
+ receiveRequest, receiveRequest->ReceiveLength );
+ }
+ status = STATUS_SUCCESS;
+ if ( copyLength > receiveRequest->ReceiveLength ) {
+ status = STATUS_BUFFER_OVERFLOW;
+ copyLength = receiveRequest->ReceiveLength;
+ }
+
+ if ( copyLength != 0 ) {
+
+ ASSERT( ReceiveIrp->MdlAddress != NULL );
+ ASSERT( SendIrp->MdlAddress != NULL );
+
+ LoopCopyData(
+ ReceiveIrp->MdlAddress,
+ SendIrp->MdlAddress,
+ copyLength
+ );
+
+ }
+
+ //
+ // Complete the Receive and Send requests.
+ //
+
+ receivingConnection =
+ (PLOOP_CONNECTION)receiveIrpSp->FileObject->FsContext;
+ sendingConnection =
+ (PLOOP_CONNECTION)sendIrpSp->FileObject->FsContext;
+
+ ReceiveIrp->IoStatus.Status = status;
+ ReceiveIrp->IoStatus.Information = copyLength;
+
+ SendIrp->IoStatus.Status = STATUS_SUCCESS;
+ SendIrp->IoStatus.Information = copyLength;
+
+ IoCompleteRequest( ReceiveIrp, 2 );
+ IoCompleteRequest( SendIrp, 2 );
+
+ //
+ // Dereference the connections.
+ //
+
+ ACQUIRE_LOOP_LOCK( "CompleteReceive dereference" );
+ LoopDereferenceConnection( receivingConnection );
+ LoopDereferenceConnection( sendingConnection );
+ RELEASE_LOOP_LOCK( "CompleteReceive dereference" );
+
+ return;
+
+} // CompleteReceive
+
+
+VOID
+IndicateReceive (
+ IN PLOOP_CONNECTION ReceivingConnection,
+ IN PIRP InitialSendIrp
+ )
+{
+ NTSTATUS status;
+ PLOOP_ENDPOINT receivingEndpoint;
+ PLOOP_CONNECTION sendingConnection;
+ PLIST_ENTRY listEntry;
+ PIRP receiveIrp;
+ PIRP sendIrp;
+ PTDI_IND_RECEIVE receiveHandler;
+ PVOID receiveContext;
+ PIO_STACK_LOCATION sendIrpSp;
+ PTDI_REQUEST_KERNEL_SEND sendRequest;
+ ULONG length;
+ PMDL mdl;
+ PVOID address;
+ ULONG bytesTaken;
+
+ receivingEndpoint = ReceivingConnection->Endpoint;
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Receiving endpoint: %lx\n", receivingEndpoint );
+ }
+
+ //
+ // Reference the receiving connection to prevent it from going away
+ // while this routine is running.
+ //
+
+ ReceivingConnection->BlockHeader.ReferenceCount++;
+ IF_DEBUG(LOOP3) {
+ DbgPrint( " New refcnt on connection %lx is %lx\n",
+ ReceivingConnection,
+ ReceivingConnection->BlockHeader.ReferenceCount );
+ }
+
+ //
+ // Capture the address of the sending connection, as the receiving
+ // connection's pointer can be zeroed if a Disconnect occurs.
+ //
+
+ sendingConnection = ReceivingConnection->RemoteConnection;
+ ASSERT( sendingConnection != NULL );
+
+ //
+ // If the receiving connection has a pending Receive, satisfy it
+ // with this Send. If there is no pending Receive, and a Receive
+ // handler has been enabled on the receiving connection, call it.
+ // If the Receive handler returns with a Receive IRP, use it to
+ // satisfy this Send. If the Receive handler doesn't return an IRP,
+ // leave this Send pending.
+ //
+ // !!! Note that the current implementation only works because
+ // partial sends are not supported.
+ //
+
+ while ( TRUE ) {
+
+ //
+ // We have a Send pending. Is there a pending Receive?
+ //
+
+ listEntry = RemoveHeadList( &ReceivingConnection->PendingReceiveList );
+
+ if ( listEntry != &ReceivingConnection->PendingReceiveList ) {
+
+ //
+ // Found a pending Receive. Use it to satisfy the first
+ // incoming Send.
+ //
+
+ receiveIrp = CONTAINING_RECORD(
+ listEntry,
+ IRP,
+ Tail.Overlay.ListEntry
+ );
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Receive IRP pending: %lx\n", receiveIrp );
+ }
+
+ listEntry = RemoveHeadList(
+ &ReceivingConnection->IncomingSendList
+ );
+ ASSERT( listEntry != &ReceivingConnection->IncomingSendList );
+ sendIrp = CONTAINING_RECORD(
+ listEntry,
+ IRP,
+ Tail.Overlay.ListEntry
+ );
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Send IRP pending: %lx\n", sendIrp );
+ }
+
+ //
+ // If this is the first time through the loop, and we were
+ // called to process a newly queued Send, dereference the
+ // receiving connection -- LoopSend referenced the
+ // connection an extra time in case the Send had to remain
+ // queued.
+ //
+
+ if ( InitialSendIrp != NULL ) {
+ LoopDereferenceConnection( ReceivingConnection );
+ }
+
+ RELEASE_LOOP_LOCK( "IndicateReceive pending Receive" );
+
+ CompleteReceive( receiveIrp, sendIrp );
+
+ ACQUIRE_LOOP_LOCK( "IndicateReceive pending Receive completed" );
+
+ //
+ // Fall to bottom of loop to handle more incoming Sends.
+ //
+
+ } else {
+
+ //
+ // No pending Receive. Is there a Receive handler?
+ //
+
+ receiveHandler = receivingEndpoint->ReceiveHandler;
+ receiveContext = receivingEndpoint->ReceiveContext;
+
+ if ( receiveHandler == NULL ) {
+
+ //
+ // No Receive handler. The Send must remain queued.
+ //
+
+ IF_DEBUG(LOOP2) DbgPrint( " No Receive handler\n" );
+
+ break;
+
+ }
+
+ //
+ // The receiving endpoint has a Receive handler. Call it.
+ // If it returns STATUS_SUCCESS, it completely handled the
+ // data. If it returns STATUS_MORE_PROCESSING_REQUIRED, it
+ // also returns a Receive IRP describing where to put the
+ // data. Any other return status means the receiver can't
+ // take the data just now, so we leave the Send queued and
+ // wait for the receiver to post a Receive IRP.
+ //
+ // !!! Note that we don't currently handle partial data
+ // acceptance.
+ //
+ // First, remove the first Send from the Incoming Send list,
+ // and make it the Indicating Send. It must be removed from
+ // the list to ensure that it isn't completed by
+ // LoopDoDisconnection while we're indicating it.
+ //
+
+ listEntry = RemoveHeadList(
+ &ReceivingConnection->IncomingSendList
+ );
+ ASSERT( listEntry != &ReceivingConnection->IncomingSendList );
+ sendIrp = CONTAINING_RECORD(
+ listEntry,
+ IRP,
+ Tail.Overlay.ListEntry
+ );
+ ReceivingConnection->IndicatingSendIrp = sendIrp;
+
+ RELEASE_LOOP_LOCK( "IndicateReceive calling Receive handler" );
+
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Receive handler: %lx\n", receiveHandler );
+ DbgPrint( " Send IRP: %lx\n", sendIrp );
+ }
+
+ sendIrpSp = IoGetCurrentIrpStackLocation( sendIrp );
+ sendRequest = (PTDI_REQUEST_KERNEL_SEND)&sendIrpSp->Parameters;
+
+ length = sendRequest->SendLength;
+ ASSERTMSG(
+ "Loopback driver doesn't handle partial or expedited sends",
+ sendRequest->SendFlags == 0
+ );
+
+ //
+ // Map the send buffer, if necessary.
+ //
+
+ mdl = sendIrp->MdlAddress;
+ if ( MmGetMdlByteCount(mdl) == 0 ) {
+ address = NULL;
+ } else {
+ address = MmGetSystemAddressForMdl( mdl );
+ }
+
+ //
+ // Call the Receive handler.
+ //
+
+ status = receiveHandler(
+ receiveContext,
+ ReceivingConnection->ConnectionContext,
+ 0,
+ MmGetMdlByteCount( mdl ),
+ length,
+ &bytesTaken,
+ address,
+ &receiveIrp
+ );
+
+ ACQUIRE_LOOP_LOCK( "IndicateReceive after calling handler" );
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Indication for send IRP %lx done\n", sendIrp );
+ }
+
+ ReceivingConnection->IndicatingSendIrp = NULL;
+
+ if ( status == STATUS_SUCCESS ) {
+
+ //
+ // The Receive handler completely handled the data.
+ // Complete the Send.
+ //
+
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Receive handler handled data\n" );
+ }
+
+ ASSERTMSG(
+ "Loopback driver doesn't handle partial acceptance "
+ "of indications",
+ bytesTaken == length
+ );
+
+ //
+ // Dereference the sending and receiving connections --
+ // LoopSend referenced the connections when it queued
+ // the Send.
+ //
+
+ LoopDereferenceConnection( ReceivingConnection );
+ LoopDereferenceConnection( sendingConnection );
+
+ RELEASE_LOOP_LOCK( "IndicateReceive completely handled" );
+
+ //
+ // Complete the Send IRP.
+ //
+
+ sendIrp->IoStatus.Status = STATUS_SUCCESS;
+ sendIrp->IoStatus.Information = sendRequest->SendLength;
+
+ IoCompleteRequest( sendIrp, 2 );
+
+ ACQUIRE_LOOP_LOCK( "IndicateReceive send completed" );
+
+ //
+ // Fall to bottom of loop to handle more incoming
+ // Sends.
+ //
+
+ } else if ( status == STATUS_MORE_PROCESSING_REQUIRED ) {
+
+ //
+ // The Receive handler returned a Receive IRP to be used
+ // to satisfy the Send.
+ //
+
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Receive handler returned IRP: %lx\n",
+ receiveIrp );
+ }
+
+ ASSERTMSG(
+ "Loopback driver doesn't handle partial acceptance "
+ "of indications",
+ bytesTaken == 0
+ );
+
+ //
+ // Complete the Receive using the current Send.
+ //
+ // *** Note that the pending Send references both the
+ // sending and receiving connections.
+
+ RELEASE_LOOP_LOCK( "IndicateReceive complete new Receive" );
+
+ CompleteReceive( receiveIrp, sendIrp );
+
+ ACQUIRE_LOOP_LOCK( "IndicateReceive new receive completed" );
+
+ //
+ // Fall to bottom of loop to handle more incoming
+ // Sends.
+ //
+
+ } else {
+
+ //
+ // The Receive handler couldn't take the data. This
+ // Send will have to wait until receiver can post a
+ // Receive IRP.
+ //
+ // *** Because we didn't hold the spin lock around the
+ // call to the Receive handler, it's possible that
+ // the receiver has already posted a Receive IRP.
+ // Because we were in the middle of an indication,
+ // that Receive would have been queued to the
+ // Pending Receive list, and we should go get it
+ // now. If the receiver hasn't posted a Receive
+ // yet, then this Send will be put back on the
+ // Incoming Send list before the Receive does come
+ // in (since we're now holding the lock).
+ //
+
+ ASSERT( status == STATUS_DATA_NOT_ACCEPTED );
+
+ IF_DEBUG(LOOP2) DbgPrint( " Data not accepted\n" );
+
+ if ( GET_BLOCK_STATE(ReceivingConnection) !=
+ BlockStateActive ) {
+
+ //
+ // The connection is closing. Abort the current
+ // Send and leave.
+ //
+
+ LoopDereferenceConnection( ReceivingConnection );
+ LoopDereferenceConnection( sendingConnection );
+
+ RELEASE_LOOP_LOCK( "IndicateReceive disconnecting" );
+
+ sendIrp->IoStatus.Status = STATUS_DISCONNECTED;
+ IoCompleteRequest( sendIrp, 2 );
+
+ ACQUIRE_LOOP_LOCK( "IndicateReceive send aborted" );
+
+ break;
+
+ }
+
+ listEntry = RemoveHeadList(
+ &ReceivingConnection->PendingReceiveList
+ );
+
+ if ( listEntry !=
+ &ReceivingConnection->PendingReceiveList ) {
+
+ //
+ // A Receive has been posted. Use it to satisfy
+ // this Send.
+ //
+
+ receiveIrp = CONTAINING_RECORD(
+ listEntry,
+ IRP,
+ Tail.Overlay.ListEntry
+ );
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Receive IRP pending: %lx\n",
+ receiveIrp );
+ }
+
+ //
+ // Complete the Receive using the current Send.
+ //
+
+ RELEASE_LOOP_LOCK(
+ "IndicateReceive complete posted Receive"
+ );
+
+ CompleteReceive( receiveIrp, sendIrp );
+
+ ACQUIRE_LOOP_LOCK(
+ "IndicateReceive posted receive completed"
+ );
+
+ //
+ // Fall to bottom of loop to handle more incoming
+ // Sends.
+ //
+
+ } else {
+
+ //
+ // The handler didn't take the data, and it didn't
+ // post a Receive IRP. Requeue the current send and
+ // get out.
+ //
+
+ InsertHeadList(
+ &ReceivingConnection->IncomingSendList,
+ &sendIrp->Tail.Overlay.ListEntry
+ );
+
+ break;
+
+ }
+
+ }
+
+ } // pending receive?
+
+ //
+ // If we get here, we need to indicate the next incoming Send,
+ // if there is one.
+ //
+
+ InitialSendIrp = NULL;
+
+ if ( (GET_BLOCK_STATE(ReceivingConnection) != BlockStateActive) ||
+ (ReceivingConnection->IncomingSendList.Flink ==
+ &ReceivingConnection->IncomingSendList) ) {
+
+ //
+ // No more Sends, or connection no longer active. Leave.
+ //
+
+ break;
+
+ }
+
+ //
+ // Process the next Send.
+ //
+
+ } // while ( TRUE )
+
+ //
+ // Remove the connection reference acquired at the start of this
+ // routine.
+ //
+
+ LoopDereferenceConnection( ReceivingConnection );
+
+ RELEASE_LOOP_LOCK( "IndicateReceive done" );
+
+ return;
+
+} // IndicateReceive
+
diff --git a/private/ntos/tdi/loopback/endpoint.c b/private/ntos/tdi/loopback/endpoint.c
new file mode 100644
index 000000000..fd252d291
--- /dev/null
+++ b/private/ntos/tdi/loopback/endpoint.c
@@ -0,0 +1,373 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ endpoint.c
+
+Abstract:
+
+ This module implements endpoint logic for the loopback Transport
+ Provider driver for NT LAN Manager.
+
+Author:
+
+ Chuck Lenzmeier (chuckl) 15-Aug-1991
+
+Revision History:
+
+--*/
+
+#include "loopback.h"
+
+
+VOID
+LoopDereferenceEndpoint (
+ IN PLOOP_ENDPOINT Endpoint
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to dereference an endpoint block. If the
+ reference count on the block goes to zero, the endpoint block is
+ deleted.
+
+ The Loopback device object's spin lock must be held when this
+ routine is called.
+
+Arguments:
+
+ Endpoint - Supplies a pointer to an endpoint block
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PIRP closeIrp;
+
+ IF_DEBUG(LOOP3) {
+ DbgPrint( " Dereferencing endpoint %lx; old refcnt %lx\n",
+ Endpoint, Endpoint->BlockHeader.ReferenceCount );
+ }
+
+ ASSERT( (LONG)Endpoint->BlockHeader.ReferenceCount > 0 );
+
+ if ( --Endpoint->BlockHeader.ReferenceCount != 0 ) {
+ return;
+ }
+
+ IF_DEBUG(LOOP3) DbgPrint( " Deleting endpoint %lx\n", Endpoint );
+
+ RemoveEntryList( &Endpoint->DeviceListEntry );
+
+ RELEASE_LOOP_LOCK( "DerefEndp deleting" );
+
+ ObDereferenceObject( Endpoint->DeviceObject );
+
+ //
+ // If a Close IRP is pending, complete it now.
+ //
+
+ closeIrp = Endpoint->CloseIrp;
+
+ if ( closeIrp != NULL ) {
+
+ IF_DEBUG(LOOP3) {
+ DbgPrint( " Completing Close IRP %lx\n", closeIrp );
+ }
+ closeIrp->IoStatus.Status = STATUS_SUCCESS;
+ closeIrp->IoStatus.Information = 0;
+
+ IoCompleteRequest( closeIrp, 2 );
+
+ }
+
+ 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 );
+
+ ACQUIRE_LOOP_LOCK( "DerefEndp done" );
+
+ return;
+
+} // LoopDereferenceEndpoint
+
+
+PLOOP_ENDPOINT
+LoopFindBoundAddress (
+ IN PCHAR NetbiosName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine searches the list of endpoints for the loopback device
+ for one that has the specified address bound to it.
+
+ The loopback device object's spin lock must be owned when this
+ function is called.
+
+Arguments:
+
+ NetbiosName - Pointer to NetBIOS name string, formatted according
+ to TDI specification.
+
+Return Value:
+
+ PLOOP_OBJECT - Pointer to LOOP_ENDPOINT block if matching address
+ found, else NULL
+
+--*/
+
+{
+ PLIST_ENTRY listEntry;
+ PLOOP_ENDPOINT endpoint;
+ LONG compareResult;
+ CLONG i;
+
+ listEntry = LoopDeviceObject->EndpointList.Flink;
+
+ while ( listEntry != &LoopDeviceObject->EndpointList ) {
+
+ endpoint = CONTAINING_RECORD(
+ listEntry,
+ LOOP_ENDPOINT,
+ DeviceListEntry
+ );
+
+ if ( GET_BLOCK_STATE(endpoint) == BlockStateActive ) {
+
+ compareResult = 0;
+ for ( i = 0; i < NETBIOS_NAME_LENGTH; i++ ) {
+ if ( endpoint->NetbiosName[i] != NetbiosName[i] ) {
+ compareResult = 1;
+ break;
+ }
+ }
+
+ if ( compareResult == 0 ) {
+ return endpoint;
+ }
+
+ }
+
+ listEntry = listEntry->Flink;
+
+ }
+
+ return NULL;
+
+} // LoopFindBoundAddress
+
+
+NTSTATUS
+LoopSetEventHandler (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine processes a Set Receive Handler request.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+
+ IrpSp - Pointer to current stack location in IRP
+
+Return Value:
+
+ NTSTATUS - Status of request
+
+--*/
+
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PTDI_REQUEST_KERNEL_SET_EVENT setEventRequest;
+ PLOOP_ENDPOINT endpoint;
+ PVOID handler;
+ PVOID context;
+ PSZ s;
+
+ IrpSp; // prevent compiler warnings
+
+ IF_DEBUG(LOOP1) DbgPrint( " SetEventHandler request\n" );
+
+ setEventRequest = (PTDI_REQUEST_KERNEL_SET_EVENT)&IrpSp->Parameters;
+
+ handler = setEventRequest->EventHandler;
+ context = setEventRequest->EventContext;
+
+ //
+ // Get the endpoint address and update the handler address.
+ //
+
+ ACQUIRE_LOOP_LOCK( "SetEventHandler initial" );
+
+ endpoint = (PLOOP_ENDPOINT)IrpSp->FileObject->FsContext;
+ IF_DEBUG(LOOP2) DbgPrint( " Endpoint address: %lx\n", endpoint );
+
+ if ( endpoint == NULL ) {
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Can't SetEventHandler on control channel\n" );
+ }
+ status = STATUS_INVALID_PARAMETER;
+ goto complete;
+ }
+
+ if ( GET_BLOCK_TYPE(endpoint) != BlockTypeLoopEndpoint ) {
+ status = STATUS_INVALID_PARAMETER;
+ goto complete;
+ }
+
+ switch ( setEventRequest->EventType ) {
+
+ case TDI_EVENT_CONNECT:
+
+ IF_DEBUG(LOOP2) s = "connect";
+
+ endpoint->ConnectHandler = (PTDI_IND_CONNECT)handler;
+ endpoint->ConnectContext = context;
+
+ break;
+
+ case TDI_EVENT_DISCONNECT:
+
+ IF_DEBUG(LOOP2) s = "disconnect";
+
+ endpoint->DisconnectHandler = (PTDI_IND_DISCONNECT)handler;
+ endpoint->DisconnectContext = context;
+
+ break;
+
+ case TDI_EVENT_ERROR:
+
+ IF_DEBUG(LOOP2) s = "error";
+
+ endpoint->ErrorHandler = (PTDI_IND_ERROR)handler;
+ endpoint->ErrorContext = context;
+
+ break;
+
+ case TDI_EVENT_RECEIVE:
+
+ IF_DEBUG(LOOP2) s = "receive";
+
+ endpoint->ReceiveHandler = (PTDI_IND_RECEIVE)handler;
+ endpoint->ReceiveContext = context;
+
+ break;
+
+ case TDI_EVENT_RECEIVE_DATAGRAM:
+
+ IF_DEBUG(LOOP2) s = "receive datagram";
+
+ endpoint->ReceiveDatagramHandler = (PTDI_IND_RECEIVE_DATAGRAM)handler;
+ endpoint->ReceiveDatagramContext = context;
+
+ break;
+
+ case TDI_EVENT_RECEIVE_EXPEDITED:
+
+ IF_DEBUG(LOOP2) s = "receive expedited";
+
+ endpoint->ReceiveExpeditedHandler =
+ (PTDI_IND_RECEIVE_EXPEDITED)handler;
+ endpoint->ReceiveExpeditedContext = context;
+
+ break;
+
+ default:
+
+ DEBUG {
+ DbgPrint( " Invalid event type: %lx\n",
+ setEventRequest->EventType );
+ }
+ ASSERT( FALSE );
+ status = STATUS_INVALID_PARAMETER;
+ goto complete;
+
+ }
+
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " New %s handler address: %lx, context: %lx\n",
+ s, handler, context );
+ }
+
+complete:
+
+ RELEASE_LOOP_LOCK( "SetEventHandler final" );
+
+ //
+ // Complete the I/O request.
+ //
+
+ Irp->IoStatus.Status = status;
+
+ IoCompleteRequest( Irp, 2 );
+
+ IF_DEBUG(LOOP1) DbgPrint( " SetReceiveHandler request complete\n" );
+ return status;
+
+} // LoopSetEventHandler
+
+
+NTSTATUS
+LoopVerifyEndpoint (
+ IN PLOOP_ENDPOINT Endpoint
+ )
+
+/*++
+
+Routine Description:
+
+ This routine verifies that the specified endpoint block is really
+ an endpoint created by the loopback driver.
+
+ The loopback device object's spin lock must be owned when this
+ function is called.
+
+Arguments:
+
+ Endpoint - Pointer to endpoint block
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS or STATUS_INVALID_PARAMETER
+
+--*/
+
+{
+ PLIST_ENTRY listEntry;
+
+ listEntry = LoopDeviceObject->EndpointList.Flink;
+
+ while ( listEntry != &LoopDeviceObject->EndpointList ) {
+
+ if ( CONTAINING_RECORD( listEntry, LOOP_ENDPOINT, DeviceListEntry ) ==
+ Endpoint ) {
+ return STATUS_SUCCESS;
+ }
+
+ listEntry = listEntry->Flink;
+
+ }
+
+ return STATUS_INVALID_PARAMETER;
+
+} // LoopVerifyEndpoint
+
diff --git a/private/ntos/tdi/loopback/info.c b/private/ntos/tdi/loopback/info.c
new file mode 100644
index 000000000..6d71c41f6
--- /dev/null
+++ b/private/ntos/tdi/loopback/info.c
@@ -0,0 +1,183 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ info.c
+
+Abstract:
+
+ This module implements query/set information logic for the loopback
+ Transport Provider driver for NT LAN Manager.
+
+Author:
+
+ Chuck Lenzmeier (chuckl) 6-Nov-1991
+
+Revision History:
+
+--*/
+
+#include "loopback.h"
+
+#include <windef.h>
+#include <nb30.h>
+
+
+NTSTATUS
+LoopQueryInformation (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ This function handles the TdiQueryInformation request.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+
+ IrpSp - Pointer to current stack location in IRP
+
+Return Value:
+
+ NTSTATUS - Status of request
+
+--*/
+
+{
+ NTSTATUS status;
+ PTDI_REQUEST_KERNEL_QUERY_INFORMATION queryRequest;
+
+ IF_DEBUG(LOOP1) DbgPrint( " Query Information request\n" );
+
+ queryRequest = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)&IrpSp->Parameters;
+
+ switch ( queryRequest->QueryType ) {
+
+ case TDI_QUERY_BROADCAST_ADDRESS:
+ {
+ PTA_NETBIOS_ADDRESS address;
+
+ if ( Irp->MdlAddress->ByteCount < sizeof(TA_NETBIOS_ADDRESS) ) {
+
+ status = STATUS_BUFFER_TOO_SMALL;
+
+ } else {
+
+ address = MmGetSystemAddressForMdl( Irp->MdlAddress );
+
+ address->TAAddressCount = 1;
+ address->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS;
+ address->Address[0].AddressLength = 0;
+
+ Irp->IoStatus.Information = sizeof(TA_NETBIOS_ADDRESS);
+
+ status = STATUS_SUCCESS;
+
+ }
+
+ break;
+ }
+
+ case TDI_QUERY_PROVIDER_INFORMATION:
+ {
+ PTDI_PROVIDER_INFO providerInfo;
+
+ if ( Irp->MdlAddress->ByteCount < sizeof(TDI_PROVIDER_INFO) ) {
+
+ status = STATUS_BUFFER_TOO_SMALL;
+
+ } else {
+
+ providerInfo = MmGetSystemAddressForMdl( Irp->MdlAddress );
+
+ ACQUIRE_LOOP_LOCK( "Query Information copy provider info" );
+ RtlMoveMemory(
+ providerInfo,
+ &LoopProviderInfo,
+ sizeof(TDI_PROVIDER_INFO)
+ );
+ RELEASE_LOOP_LOCK( "Query Information copy provider info done" );
+
+ Irp->IoStatus.Information = sizeof(TDI_PROVIDER_INFO);
+
+ status = STATUS_SUCCESS;
+
+ }
+
+ break;
+ }
+
+ case TDI_QUERY_ADAPTER_STATUS:
+ {
+ PADAPTER_STATUS adapterStatus;
+ PNAME_BUFFER name;
+
+ if ( Irp->MdlAddress->ByteCount <
+ (sizeof(ADAPTER_STATUS) + sizeof(NAME_BUFFER)) ) {
+
+ status = STATUS_BUFFER_TOO_SMALL;
+
+ } else {
+
+ adapterStatus = MmGetSystemAddressForMdl( Irp->MdlAddress );
+
+ RtlZeroMemory(
+ adapterStatus,
+ sizeof(ADAPTER_STATUS) + sizeof(NAME_BUFFER)
+ );
+
+ adapterStatus->rev_major = 3;
+ adapterStatus->rev_minor = 0x02;
+ adapterStatus->free_ncbs = 0xffff;
+ adapterStatus->max_cfg_ncbs = 0xffff;
+ adapterStatus->max_ncbs = 0xffff;
+ adapterStatus->max_dgram_size = 0xffff;
+ adapterStatus->max_cfg_sess = 0xffff;
+ adapterStatus->max_sess = 0xffff;
+ adapterStatus->max_sess_pkt_size = 0xffff;
+ adapterStatus->name_count = 1;
+
+ name = (PNAME_BUFFER)(adapterStatus + 1);
+ name->name_num = 1;
+ name->name_flags = REGISTERED | UNIQUE_NAME;
+
+ Irp->IoStatus.Information =
+ sizeof(ADAPTER_STATUS) + sizeof(NAME_BUFFER);
+
+ status = STATUS_SUCCESS;
+
+ }
+
+ break;
+ }
+
+ case TDI_QUERY_SESSION_STATUS:
+
+ status = STATUS_NOT_IMPLEMENTED;
+
+ break;
+
+ default:
+
+ status = STATUS_INVALID_PARAMETER;
+
+ }
+
+ //
+ // Complete the Query Information request.
+ //
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest( Irp, 0 );
+
+ IF_DEBUG(LOOP1) DbgPrint( " Query Information request complete\n" );
+ return status;
+
+} // LoopQueryInformation
+
diff --git a/private/ntos/tdi/loopback/loopback.c b/private/ntos/tdi/loopback/loopback.c
new file mode 100644
index 000000000..00880f56d
--- /dev/null
+++ b/private/ntos/tdi/loopback/loopback.c
@@ -0,0 +1,1284 @@
+/*++
+
+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
diff --git a/private/ntos/tdi/loopback/loopback.h b/private/ntos/tdi/loopback/loopback.h
new file mode 100644
index 000000000..150a26ffb
--- /dev/null
+++ b/private/ntos/tdi/loopback/loopback.h
@@ -0,0 +1,433 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ loopback.h
+
+Abstract:
+
+ This module is the main include file for the LAN Manager loopback
+ driver.
+
+Author:
+
+ Chuck Lenzmeier (chuckl) 27-Jun-1991
+
+Revision History:
+
+--*/
+
+#ifndef _LOOP_
+#define _LOOP_
+
+//
+// "System" include files
+//
+
+#include <ntos.h>
+
+#include <tdikrnl.h>
+
+//
+// Network include files.
+//
+
+#include "status.h"
+
+//
+// Local, independent include files
+//
+
+#include "loopdbg.h"
+
+#define LOOPBACK_DEVICE_NAME "\\Device\\Loop"
+
+//
+// The length of a NetBIOS name. Fixed by the protocol.
+//
+
+#define NETBIOS_NAME_LENGTH 16
+
+//
+// Simple MIN and MAX macros. Watch out for side effects!
+//
+
+#define MIN(a,b) ( ((a) < (b)) ? (a) : (b) )
+#define MAX(a,b) ( ((a) < (b)) ? (b) : (a) )
+
+//
+// Macros for accessing the block header structure.
+//
+// *** Note that the existing usage of these macros assumes that the block
+// header is the first element in the block!
+//
+
+#define GET_BLOCK_STATE(block) ( ((PBLOCK_HEADER)(block))->State )
+#define SET_BLOCK_STATE(block,state) ( ((PBLOCK_HEADER)(block))->State = state )
+
+#define GET_BLOCK_TYPE(block) ( ((PBLOCK_HEADER)(block))->Type )
+#define SET_BLOCK_TYPE(block,type) ( ((PBLOCK_HEADER)(block))->Type = type )
+
+#define GET_BLOCK_SIZE(block) ( ((PBLOCK_HEADER)(block))->Size )
+#define SET_BLOCK_SIZE(block,size) ( ((PBLOCK_HEADER)(block))->Size = size )
+
+//
+// Local macros
+//
+
+//
+// Macros for lock debugging.
+//
+// *** Note that the test for recursion only works on uniprocessors.
+//
+
+#if LOOPDBG && defined(LOOPLOCK)
+
+#define ACQUIRE_LOOP_LOCK(instance) { \
+ IF_DEBUG(LOOP5) \
+ DbgPrint( "Acquire loop lock, %s\n", \
+ (instance) ); \
+ if ( LoopDeviceObject->SavedIrql != (KIRQL)-1 ) { \
+ DbgPrint( "Recursive lock acquisition attempt\n" ); \
+ DbgBreakPoint( ); \
+ } \
+ KeAcquireSpinLock( \
+ &LoopDeviceObject->SpinLock, \
+ &LoopDeviceObject->SavedIrql \
+ ); \
+ }
+
+#define RELEASE_LOOP_LOCK(instance) { \
+ KIRQL oldIrql; \
+ IF_DEBUG(LOOP5) \
+ DbgPrint( "Release loop lock, %s\n", \
+ (instance) ); \
+ ASSERT( LoopDeviceObject->SavedIrql != (KIRQL)-1 ); \
+ oldIrql = LoopDeviceObject->SavedIrql; \
+ LoopDeviceObject->SavedIrql = (KIRQL)-1; \
+ KeReleaseSpinLock( \
+ &LoopDeviceObject->SpinLock, \
+ oldIrql \
+ ); \
+ }
+
+#else // LOOPDBG && defined(LOOPLOCK)
+
+#define ACQUIRE_LOOP_LOCK(instance) \
+ KeAcquireSpinLock( \
+ &LoopDeviceObject->SpinLock, \
+ &LoopDeviceObject->SavedIrql \
+ ) \
+
+#define RELEASE_LOOP_LOCK(instance) \
+ KeReleaseSpinLock( \
+ &LoopDeviceObject->SpinLock, \
+ LoopDeviceObject->SavedIrql \
+ ) \
+
+#endif // else LOOPDBG && defined(LOOPLOCK)
+
+//
+// Local types
+//
+
+//
+// The loopback driver's device object is a standard I/O system device
+// object followed by fields specific to the device.
+//
+
+typedef struct _LOOP_DEVICE_OBJECT {
+
+ DEVICE_OBJECT DeviceObject;
+
+ //
+ // List of active address endpoints.
+ //
+
+ LIST_ENTRY EndpointList;
+
+ //
+ // List of active connection endpoints.
+ //
+
+ LIST_ENTRY ConnectionList;
+
+ //
+ // Spin lock synchronizing access to fields in the device object
+ // and to structures maintained by the device driver.
+ //
+
+ KSPIN_LOCK SpinLock;
+
+ //
+ // SavedIrql is used so that one routine can call another with the
+ // lock held and the called routine can release (and possibly
+ // reacquire it). SavedIrql is set *after* the lock is acquired.
+ //
+
+ KIRQL SavedIrql;
+
+} LOOP_DEVICE_OBJECT, *PLOOP_DEVICE_OBJECT;
+
+#define LOOP_DEVICE_EXTENSION_LENGTH (sizeof(LOOP_DEVICE_OBJECT) - \
+ sizeof(DEVICE_OBJECT))
+
+//
+// BLOCK_TYPE is an enumerated type defining the various types of
+// data blocks used by the driver.
+//
+
+typedef enum _BLOCK_TYPE {
+ BlockTypeGarbage = 0,
+ BlockTypeLoopConnection = 0x29290001,
+ BlockTypeLoopEndpoint = 0x29290002
+} BLOCK_TYPE, *PBLOCK_TYPE;
+
+//
+// BLOCK_STATE is an enumerated type defining the various states that
+// blocks can be in. Initializing is used (relatively rarely) to
+// indicate that creation/initialization of a block is in progress.
+// Active is the state blocks are usually in. Closing is used to
+// indicate that a block is being prepared for deletion; when the
+// reference count on the block reaches 0, the block will be deleted.
+// Dead is used when debugging code is enabled to indicate that the
+// block has been deleted.
+//
+
+typedef enum _BLOCK_STATE {
+ BlockStateDead,
+ BlockStateUnbound,
+ BlockStateBound,
+ BlockStateConnecting,
+ BlockStateActive,
+ BlockStateDisconnecting,
+ BlockStateClosing,
+ BlockStateClosed,
+ // The following is defined just to know how many states there are
+ BlockStateMax
+} BLOCK_STATE, *PBLOCK_STATE;
+
+
+//
+// BLOCK_HEADER is the standard block header that appears at the
+// beginning of most driver-private data structures. This header is
+// used primarily for debugging and tracing. The Type and State fields
+// are described above. The Size field indicates how much space was
+// allocated for the block. ReferenceCount indicates the number of
+// reasons why the block should not be deallocated. The count is set to
+// 2 by the allocation routine, to account for 1) the fact that the
+// block is "open" and 2) the pointer returned to the caller. When the
+// block is closed, State is set to Closing, and the ReferenceCount is
+// decremented. When all references (pointers) to the block are
+// deleted, and the reference count reaches 0, the block is deleted.
+//
+
+typedef struct _BLOCK_HEADER {
+ BLOCK_TYPE Type;
+ BLOCK_STATE State;
+ ULONG ReferenceCount;
+ CLONG Size;
+} BLOCK_HEADER, *PBLOCK_HEADER;
+
+//
+// The file object obtained when the loopback Transport Provider is
+// opened points to a LOOP_ENDPOINT record, which has context consisting
+// of a pointer to the file object and a list of connections created
+// over the endpoint.
+//
+
+typedef struct _LOOP_ENDPOINT {
+ BLOCK_HEADER BlockHeader;
+ LIST_ENTRY DeviceListEntry;
+ LIST_ENTRY ConnectionList;
+ LIST_ENTRY PendingListenList;
+ LIST_ENTRY IncomingConnectList;
+ PIRP IndicatingConnectIrp;
+ PLOOP_DEVICE_OBJECT DeviceObject;
+ PFILE_OBJECT FileObject;
+ PTDI_IND_CONNECT ConnectHandler;
+ PVOID ConnectContext;
+ PTDI_IND_RECEIVE ReceiveHandler;
+ PVOID ReceiveContext;
+ PTDI_IND_RECEIVE_DATAGRAM ReceiveDatagramHandler;
+ PVOID ReceiveDatagramContext;
+ PTDI_IND_RECEIVE_EXPEDITED ReceiveExpeditedHandler;
+ PVOID ReceiveExpeditedContext;
+ PTDI_IND_DISCONNECT DisconnectHandler;
+ PVOID DisconnectContext;
+ PTDI_IND_ERROR ErrorHandler;
+ PVOID ErrorContext;
+ PIRP CloseIrp;
+ CHAR NetbiosName[NETBIOS_NAME_LENGTH+1];
+} LOOP_ENDPOINT, *PLOOP_ENDPOINT;
+
+//
+// Each connection is represented by two LOOP_CONNECTION structures, one
+// for each end of the connection.
+//
+
+typedef struct _LOOP_CONNECTION {
+ BLOCK_HEADER BlockHeader;
+ LIST_ENTRY DeviceListEntry;
+ LIST_ENTRY EndpointListEntry;
+ PLOOP_ENDPOINT Endpoint;
+ PLOOP_DEVICE_OBJECT DeviceObject;
+ PFILE_OBJECT FileObject;
+ struct _LOOP_CONNECTION *RemoteConnection;
+ PVOID ConnectionContext;
+ LIST_ENTRY PendingReceiveList;
+ LIST_ENTRY IncomingSendList;
+ PIRP IndicatingSendIrp;
+ PIRP ConnectOrListenIrp;
+ PIRP DisconnectIrp;
+ PIRP CloseIrp;
+} LOOP_CONNECTION, *PLOOP_CONNECTION;
+
+//
+// Global variables
+//
+
+//
+// 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.
+//
+
+extern PLOOP_DEVICE_OBJECT LoopDeviceObject;
+
+//
+// LoopProviderInfo is a structure containing information that may be
+// obtained using TdiQueryInformation.
+//
+
+extern TDI_PROVIDER_INFO LoopProviderInfo;
+
+
+//
+// Global declarations
+//
+
+NTSTATUS
+LoopAccept (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+LoopAssociateAddress (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+LoopConnect (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+VOID
+LoopCopyData (
+ IN PMDL Destination,
+ IN PMDL Source,
+ IN ULONG Length
+ );
+
+NTSTATUS
+LoopCreate (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+VOID
+LoopDereferenceConnection (
+ IN PLOOP_CONNECTION Connection
+ );
+
+VOID
+LoopDereferenceEndpoint (
+ IN PLOOP_ENDPOINT Endpoint
+ );
+
+NTSTATUS
+LoopDisassociateAddress (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+LoopDisconnect (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+VOID
+LoopDoDisconnect (
+ IN PLOOP_CONNECTION Conection,
+ IN BOOLEAN ClientInitiated
+ );
+
+PLOOP_ENDPOINT
+LoopFindBoundAddress (
+ IN PCHAR NetbiosName
+ );
+
+PVOID
+LoopGetConnectionContextFromEa (
+ PFILE_FULL_EA_INFORMATION Ea
+ );
+
+NTSTATUS
+LoopGetEndpointTypeFromEa (
+ PFILE_FULL_EA_INFORMATION Ea,
+ PBLOCK_TYPE Type
+ );
+
+NTSTATUS
+LoopListen (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+LoopParseAddress (
+ IN PTA_NETBIOS_ADDRESS Address,
+ OUT PCHAR NetbiosName
+ );
+
+NTSTATUS
+LoopParseAddressFromEa (
+ IN PFILE_FULL_EA_INFORMATION Ea,
+ OUT PCHAR NetbiosName
+ );
+
+NTSTATUS
+LoopQueryInformation (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+LoopReceive (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+LoopSend (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+LoopSetEventHandler (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+LoopVerifyEndpoint (
+ IN PLOOP_ENDPOINT Endpoint
+ );
+
+#endif // def _LOOP_
diff --git a/private/ntos/tdi/loopback/loopdbg.h b/private/ntos/tdi/loopback/loopdbg.h
new file mode 100644
index 000000000..4024f5e4e
--- /dev/null
+++ b/private/ntos/tdi/loopback/loopdbg.h
@@ -0,0 +1,58 @@
+#ifndef _LOOPDBG_
+#define _LOOPDBG_
+
+#ifdef MEMPRINT
+#include <memprint.h>
+#endif
+
+//
+// Debugging macros
+//
+
+#ifndef DBG
+#define DBG 0
+#endif
+
+#if !DBG
+
+#undef LOOPDBG
+#define LOOPDBG 0
+
+#else
+
+#ifndef LOOPDBG
+#define LOOPDBG 0
+#endif
+
+#endif
+
+#undef IF_DEBUG
+
+#if !DEVL
+#define STATIC static
+#else
+#define STATIC
+#endif
+
+#if !LOOPDBG
+
+#define DEBUG if (FALSE)
+#define IF_DEBUG(flag) if (FALSE)
+
+#else
+
+#define DEBUG if (TRUE)
+#define IF_DEBUG(flag) if (LoopDebug & (DEBUG_ ## flag))
+extern ULONG LoopDebug;
+
+#define PRINT_LITERAL(literal) DbgPrint( #literal" = %lx\n", (literal) )
+
+#define DEBUG_LOOP1 0x00000001
+#define DEBUG_LOOP2 0x00000002
+#define DEBUG_LOOP3 0x00000004
+#define DEBUG_LOOP4 0x00000008
+#define DEBUG_LOOP5 0x00000010
+
+#endif // else !LOOPDBG
+
+#endif // ndef _LOOPDBG_
diff --git a/private/ntos/tdi/loopback/loopsub.c b/private/ntos/tdi/loopback/loopsub.c
new file mode 100644
index 000000000..003161e5f
--- /dev/null
+++ b/private/ntos/tdi/loopback/loopsub.c
@@ -0,0 +1,379 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ loopsub.c
+
+Abstract:
+
+ This module implements common functions for the loopback Transport
+ Provider driver for NT LAN Manager.
+
+Author:
+
+ Chuck Lenzmeier (chuckl) 15-Aug-1991
+
+Revision History:
+
+--*/
+
+#include "loopback.h"
+
+
+VOID
+LoopCopyData (
+ IN PMDL Destination,
+ IN PMDL Source,
+ IN ULONG Length
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies data from the storage described by one MDL chain
+ into the storage described by another MDL chain.
+
+Arguments:
+
+ Destination - Pointer to first MDL in Destination chain
+
+ Source - Pointer to first MDL in Source chain
+
+ Length - Amount of data to copy. Caller must ensure that the Source
+ and Destination chains are at least this long.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PCHAR sourceAddress;
+ ULONG sourceLength;
+
+ PCHAR destinationAddress;
+ ULONG destinationLength;
+
+ ULONG copyLength;
+
+ //
+ // Get the virtual address of the first source buffer, mapping it
+ // if necessary. Also get the length of the buffer.
+ //
+
+ sourceAddress = MmGetSystemAddressForMdl( Source );
+ sourceLength = MmGetMdlByteCount( Source );
+
+ //
+ // Get the virtual address of the first destination buffer, mapping
+ // it if necessary. Also get the length of the buffer.
+ //
+
+ destinationAddress = MmGetSystemAddressForMdl( Destination );
+ destinationLength = MmGetMdlByteCount( Destination );
+
+ //
+ // Loop copying data.
+ //
+
+ do {
+
+ //
+ // The amount to copy in this pass is the minimum of 1) the
+ // amount remaining in the current source buffer, 2) the amount
+ // remaining in the current destination buffer, and 3) the
+ // amount remaining in the overall copy operation.
+ //
+
+ copyLength = sourceLength;
+ if ( copyLength > destinationLength ) copyLength = destinationLength;
+ if ( copyLength > Length ) copyLength = Length;
+
+ //
+ // Copy from the source buffer into the destination buffer.
+ //
+
+#ifndef TIMING
+ IF_DEBUG(LOOP4) {
+ DbgPrint( " copying %lx bytes from %lx to %lx\n",
+ copyLength, sourceAddress, destinationAddress );
+ DbgPrint( " source data: %lx, %lx\n",
+ *(PULONG)sourceAddress, *((PULONG)sourceAddress + 1) );
+ }
+ RtlMoveMemory( destinationAddress, sourceAddress, copyLength );
+#else
+ if ( (NtGlobalFlag & 0x20000000) == 0 ) {
+ RtlMoveMemory( destinationAddress, sourceAddress, copyLength );
+ } else {
+ RtlMoveMemory(
+ destinationAddress,
+ sourceAddress,
+ (copyLength > 200) ? 200 : copyLength
+ );
+ }
+#endif
+
+ //
+ // If all of the requested data has been copied, leave.
+ //
+
+ Length -= copyLength;
+
+ if ( Length == 0 ) {
+
+ return;
+
+ }
+
+ //
+ // If we have used up all of the current source buffer, move to
+ // the next buffer. Get the virtual address of the next buffer,
+ // mapping it if necessary. Also get the length of the buffer.
+ // If we haven't used up the current source buffer, simply
+ // update the source pointer and the remaining length.
+ //
+
+ if ( copyLength == sourceLength ) {
+
+ Source = Source->Next;
+
+ sourceAddress = MmGetSystemAddressForMdl( Source );
+ sourceLength = MmGetMdlByteCount( Source );
+
+ } else {
+
+ sourceAddress += copyLength;
+ sourceLength -= copyLength;
+
+ }
+
+ //
+ // If we have used up all of the current destination buffer,
+ // move to the next buffer. Get the virtual address of the next
+ // buffer, mapping it if necessary. Also get the length of the
+ // buffer. If we haven't used up the current destination
+ // buffer, simply update the destination pointer and the
+ // remaining length.
+ //
+
+ if ( copyLength == destinationLength ) {
+
+ Destination = Destination->Next;
+
+ destinationAddress = MmGetSystemAddressForMdl( Destination );
+ destinationLength = MmGetMdlByteCount( Destination );
+
+ } else {
+
+ destinationAddress += copyLength;
+ destinationLength -= copyLength;
+
+ }
+
+ } while ( TRUE );
+
+ //
+ // Can't get here.
+ //
+
+ ASSERTMSG( FALSE, "Can't get here!" );
+
+} // LoopCopyData
+
+
+PVOID
+LoopGetConnectionContextFromEa (
+ PFILE_FULL_EA_INFORMATION Ea
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the connection context specified in an EA.
+
+Arguments:
+
+ Ea - Pointer to EA buffer
+
+Return Value:
+
+ PVOID - Connection context
+
+--*/
+
+{
+ PVOID ctx;
+
+ RtlMoveMemory( &ctx, &Ea->EaName[Ea->EaNameLength + 1], sizeof(PVOID) );
+
+ return ctx;
+
+} // LoopGetConnectionContextFromEa
+
+
+NTSTATUS
+LoopGetEndpointTypeFromEa (
+ PFILE_FULL_EA_INFORMATION Ea,
+ PBLOCK_TYPE Type
+ )
+
+/*++
+
+Routine Description:
+
+ This routine determines whether an EA describes an address or a
+ connection.
+
+Arguments:
+
+ Ea - Pointer to EA buffer
+
+ Type - Returns block type
+
+Return Value:
+
+ NTSTATUS - STATUS_INVALID_PARAMETER if EA is not valid
+
+--*/
+
+{
+ //
+ // First check for address type.
+ //
+
+ if ( (Ea->EaNameLength == TDI_TRANSPORT_ADDRESS_LENGTH) &&
+ (strcmp( Ea->EaName, TdiTransportAddress ) == 0) ) {
+ *Type = BlockTypeLoopEndpoint;
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Next check for connection type.
+ //
+
+ if ( (Ea->EaNameLength == TDI_CONNECTION_CONTEXT_LENGTH) &&
+ (strcmp( Ea->EaName, TdiConnectionContext ) == 0) ) {
+ *Type = BlockTypeLoopConnection;
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Invalid type.
+ //
+
+ return STATUS_INVALID_PARAMETER;
+
+} // LoopGetEndpointTypeFromEa
+
+
+NTSTATUS
+LoopParseAddress (
+ IN PTA_NETBIOS_ADDRESS Address,
+ OUT PCHAR NetbiosName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine parses the input AddressString according to conventions
+ defined for transport address strings as defined in the TDI
+ specification. It converts the name from that form into a "standard"
+ NetBIOS name.
+
+Arguments:
+
+ Address - Pointer to a transport address in the TDI format.
+
+ NetbiosName - A 16-character space into which the NULL-terminated
+ NetBIOS name is written.
+
+Return Value:
+
+ NTSTATUS - Indicates whether the address string was valid.
+
+--*/
+
+{
+ //
+ // If the input address is not a single unique address in NetBIOS
+ // format, reject it.
+ //
+
+ if ( (Address->TAAddressCount != 1) ||
+ (Address->Address[0].AddressType != TDI_ADDRESS_TYPE_NETBIOS) ||
+ (Address->Address[0].AddressLength != sizeof(TDI_ADDRESS_NETBIOS)) ||
+ (Address->Address[0].Address[0].NetbiosNameType !=
+ TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) ) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Copy the name into the output buffer.
+ //
+
+ RtlMoveMemory(
+ NetbiosName,
+ Address->Address[0].Address[0].NetbiosName,
+ NETBIOS_NAME_LENGTH
+ );
+
+ return STATUS_SUCCESS;
+
+} // LoopParseAddress
+
+
+NTSTATUS
+LoopParseAddressFromEa (
+ IN PFILE_FULL_EA_INFORMATION Ea,
+ OUT PCHAR NetbiosName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine parses the input EA according to conventions defined
+ for transport address strings as defined in the TDI specification.
+ It converts the name from that form into a "standard" NetBIOS name.
+
+Arguments:
+
+ Ea - Pointer to an EA in the TDI format.
+
+ NetbiosName - A 16-character space into which the NULL-terminated
+ NetBIOS name is written.
+
+Return Value:
+
+ NTSTATUS - Indicates whether the address string was valid.
+
+--*/
+
+{
+ TA_NETBIOS_ADDRESS nbAddress;
+
+ if ( Ea->EaValueLength != sizeof(TA_NETBIOS_ADDRESS) ) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ RtlMoveMemory(
+ &nbAddress,
+ &Ea->EaName[Ea->EaNameLength + 1],
+ sizeof(TA_NETBIOS_ADDRESS)
+ );
+
+ //
+ // Pass the value portion of the EA, which is a TRANSPORT_ADDRESS,
+ // to LoopParseAddress.
+ //
+
+ return LoopParseAddress( &nbAddress, NetbiosName );
+
+} // LoopParseAddressFromEa
+
diff --git a/private/ntos/tdi/loopback/makefile b/private/ntos/tdi/loopback/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/tdi/loopback/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/ntos/tdi/loopback/sources b/private/ntos/tdi/loopback/sources
new file mode 100644
index 000000000..dcbcb8f31
--- /dev/null
+++ b/private/ntos/tdi/loopback/sources
@@ -0,0 +1,46 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=tdi
+MINORCOMP=loopback
+
+TARGETNAME=loopback
+TARGETPATH=\nt\public\sdk\lib
+TARGETTYPE=DRIVER
+
+TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib
+
+INCLUDES=..\inc;..\..\inc;..\..\..\inc
+
+SOURCES= \
+ connect.c \
+ endpoint.c \
+ info.c \
+ loopback.c \
+ loopsub.c \
+ transfer.c
+
+!IFNDEF 386_WARNING_LEVEL
+386_WARNING_LEVEL=/W3
+!ENDIF
diff --git a/private/ntos/tdi/loopback/transfer.c b/private/ntos/tdi/loopback/transfer.c
new file mode 100644
index 000000000..d80fb44fb
--- /dev/null
+++ b/private/ntos/tdi/loopback/transfer.c
@@ -0,0 +1,859 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ transfer.c
+
+Abstract:
+
+ This module implements data transfer logic for the loopback
+ Transport Provider driver for NT LAN Manager.
+
+Author:
+
+ Chuck Lenzmeier (chuckl) 15-Aug-1991
+
+Revision History:
+
+--*/
+
+#include "loopback.h"
+
+//
+// Local declarations
+//
+
+STATIC
+VOID
+CompleteReceive (
+ IN PIRP ReceiveIrp,
+ IN PIRP SendIrp
+ );
+
+STATIC
+VOID
+IndicateReceive (
+ IN PLOOP_CONNECTION ReceivingConnection,
+ IN PIRP InitialSendIrp
+ );
+
+
+NTSTATUS
+LoopReceive (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine processes a Receive request.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+
+ IrpSp - Pointer to current stack location in IRP
+
+Return Value:
+
+ NTSTATUS - Status of request
+
+--*/
+
+{
+ PLOOP_CONNECTION receivingConnection;
+ PLOOP_CONNECTION sendingConnection;
+
+ IF_DEBUG(LOOP1) DbgPrint( " Receive request\n" );
+
+ //
+ // Verify that the receiving connection is connected.
+ //
+
+ receivingConnection = (PLOOP_CONNECTION)IrpSp->FileObject->FsContext;
+
+ if ( receivingConnection == NULL ) {
+ IF_DEBUG(LOOP2) DbgPrint( " Can't Receive on control channel\n" );
+ Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+ IoCompleteRequest( Irp, 0 );
+ IF_DEBUG(LOOP1) DbgPrint( " Receive request complete\n" );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ sendingConnection = receivingConnection->RemoteConnection;
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Receiving connection: %lx\n", receivingConnection );
+ DbgPrint( " Sending connection: %lx\n", sendingConnection );
+ }
+
+ ACQUIRE_LOOP_LOCK( "Receive initial" );
+
+ if ( (sendingConnection == NULL) ||
+ (GET_BLOCK_STATE(receivingConnection) != BlockStateActive) ) {
+ RELEASE_LOOP_LOCK( "Receive closing" );
+ IF_DEBUG(LOOP2) DbgPrint( " Connection not connected\n" );
+ Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+ IoCompleteRequest( Irp, 0 );
+ IF_DEBUG(LOOP1) DbgPrint( " Receive request complete\n" );
+ return STATUS_DISCONNECTED;
+ }
+
+ //
+ // Queue the Receive to the connection's Pending Receive list.
+ //
+
+ InsertTailList(
+ &receivingConnection->PendingReceiveList,
+ &Irp->Tail.Overlay.ListEntry
+ );
+
+ IoMarkIrpPending( Irp );
+
+ //
+ // Check for pending data.
+ //
+
+ if ( (receivingConnection->IndicatingSendIrp != NULL) ||
+ (receivingConnection->IncomingSendList.Flink ==
+ &receivingConnection->IncomingSendList) ) {
+
+ //
+ // There is no pending Send, or an indication is already in
+ // progress. Reference the connection to account for the
+ // Receive IRP.
+ //
+
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " No pending Send; leaving IRP %lx queued \n", Irp );
+ }
+ receivingConnection->BlockHeader.ReferenceCount++;
+ IF_DEBUG(LOOP3) {
+ DbgPrint( " New refcnt on connection %lx is %lx\n",
+ receivingConnection,
+ receivingConnection->BlockHeader.ReferenceCount );
+ }
+
+ RELEASE_LOOP_LOCK( "Receive no Send" );
+
+ } else {
+
+ //
+ // There is pending data. Call IndicateReceive to satisfy
+ // the Receive.
+ //
+ // *** Note that IndicateReceive returns with the loopback
+ // driver spin lock released.
+ //
+
+ IF_DEBUG(LOOP2) DbgPrint( " LoopReceive indicating receive\n" );
+ IndicateReceive( receivingConnection, NULL );
+
+ }
+
+ IF_DEBUG(LOOP1) DbgPrint( " Receive request %lx complete\n", Irp );
+ return STATUS_PENDING;
+
+} // LoopReceive
+
+
+NTSTATUS
+LoopSend (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine processes a Send request.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+
+ IrpSp - Pointer to current stack location in IRP
+
+Return Value:
+
+ NTSTATUS - Status of request
+
+--*/
+
+{
+ PLOOP_CONNECTION sendingConnection;
+ PLOOP_CONNECTION receivingConnection;
+ BOOLEAN firstSend;
+
+ IF_DEBUG(LOOP1) DbgPrint( " Send request\n" );
+
+ //
+ // Verify that the sending connection is connected.
+ //
+
+ sendingConnection = (PLOOP_CONNECTION)IrpSp->FileObject->FsContext;
+
+ if ( sendingConnection == NULL ) {
+ IF_DEBUG(LOOP2) DbgPrint( " Can't Send on control channel\n" );
+ Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+ IoCompleteRequest( Irp, 0 );
+ IF_DEBUG(LOOP1) DbgPrint( " Send request complete\n" );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ receivingConnection = sendingConnection->RemoteConnection;
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Sending connection: %lx\n", sendingConnection );
+ DbgPrint( " Receiving connection: %lx\n", receivingConnection );
+ }
+
+ ACQUIRE_LOOP_LOCK( "Send initial" );
+
+ if ( (receivingConnection == NULL) ||
+ (GET_BLOCK_STATE(sendingConnection) != BlockStateActive) ) {
+ RELEASE_LOOP_LOCK( "Send closing" );
+ IF_DEBUG(LOOP2) DbgPrint( " Connection not connected\n" );
+ Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+ IoCompleteRequest( Irp, 0 );
+ IF_DEBUG(LOOP1) DbgPrint( " Send request complete\n" );
+ return STATUS_DISCONNECTED;
+ }
+
+ //
+ // Reference both ends.
+ //
+
+ sendingConnection->BlockHeader.ReferenceCount++;
+ receivingConnection->BlockHeader.ReferenceCount++;
+ IF_DEBUG(LOOP3) {
+ DbgPrint( " New refcnt on connection %lx is %lx\n",
+ sendingConnection,
+ sendingConnection->BlockHeader.ReferenceCount );
+ DbgPrint( " New refcnt on connection %lx is %lx\n",
+ receivingConnection,
+ receivingConnection->BlockHeader.ReferenceCount );
+ }
+
+ //
+ // Determine whether this is the first active incoming Send for the
+ // receiving connection.
+ //
+
+ firstSend = (BOOLEAN)( receivingConnection->IncomingSendList.Flink ==
+ &receivingConnection->IncomingSendList );
+
+ //
+ // Queue the Send to the receiving connection's Incoming Send list,
+ // in order to prevent another Send from getting ahead of this one.
+ //
+
+ InsertTailList(
+ &receivingConnection->IncomingSendList,
+ &Irp->Tail.Overlay.ListEntry
+ );
+
+ IoMarkIrpPending( Irp );
+
+ if ( !firstSend || (receivingConnection->IndicatingSendIrp != NULL) ) {
+
+ //
+ // Pending data already exists, or an indication is already in
+ // progress. This Send remains behind the already pending data.
+ //
+
+ IF_DEBUG(LOOP2) DbgPrint( " Data already pending\n" );
+
+ RELEASE_LOOP_LOCK( "Send sends pending" );
+
+ } else {
+
+ //
+ // Indicate the incoming data.
+ //
+ // *** Note that IndicateReceive returns with the loopback
+ // driver spin lock released.
+ //
+
+ IF_DEBUG(LOOP2) DbgPrint( " LoopSend indicating receive\n" );
+ IndicateReceive( receivingConnection, Irp );
+
+ }
+
+ IF_DEBUG(LOOP1) DbgPrint( " Send request %lx complete\n", Irp );
+ return STATUS_PENDING;
+
+} // LoopSend
+
+
+VOID
+CompleteReceive (
+ IN PIRP ReceiveIrp,
+ IN PIRP SendIrp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine completes the process of sending a message. It copies
+ the message from the source buffer into the destination buffer.
+
+ The reference counts on the owning connections must have been
+ incremented to account for the requesting IRPs in order to prevent
+ their deletion.
+
+Arguments:
+
+ ReceiveIrp - Pointer to IRP used for Receive request
+
+ SendIrp - Pointer to IRP used for Send request
+
+Return Value:
+
+ NTSTATUS - Indicates whether the connection was successfully created
+
+--*/
+
+{
+ NTSTATUS status;
+ PIO_STACK_LOCATION sendIrpSp;
+ PIO_STACK_LOCATION receiveIrpSp;
+ ULONG copyLength;
+ PTDI_REQUEST_KERNEL_SEND sendRequest;
+ PTDI_REQUEST_KERNEL_RECEIVE receiveRequest;
+ PLOOP_CONNECTION sendingConnection;
+ PLOOP_CONNECTION receivingConnection;
+
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Send IRP %lx completes receive IRP %lx\n",
+ SendIrp, ReceiveIrp );
+ }
+
+ //
+ // Copy the data from the source buffer into the destination buffer.
+ //
+ // *** Note that we special-case zero-length copies. We don't
+ // bother to call LoopCopyData. Part of the reason for this
+ // is that a zero-length send or receive issued from user mode
+ // yields an IRP that has a NULL MDL address.
+ //
+
+ sendIrpSp = IoGetCurrentIrpStackLocation( SendIrp );
+ sendRequest = (PTDI_REQUEST_KERNEL_SEND)&sendIrpSp->Parameters;
+ copyLength = sendRequest->SendLength;
+ IF_DEBUG(LOOP4) {
+ DbgPrint( " Send request at %lx, send length %lx\n",
+ sendRequest, copyLength );
+ }
+ receiveIrpSp = IoGetCurrentIrpStackLocation( ReceiveIrp );
+ receiveRequest = (PTDI_REQUEST_KERNEL_RECEIVE)&receiveIrpSp->Parameters;
+ IF_DEBUG(LOOP4) {
+ DbgPrint( " Receive request at %lx, receive length %lx\n",
+ receiveRequest, receiveRequest->ReceiveLength );
+ }
+ status = STATUS_SUCCESS;
+ if ( copyLength > receiveRequest->ReceiveLength ) {
+ status = STATUS_BUFFER_OVERFLOW;
+ copyLength = receiveRequest->ReceiveLength;
+ }
+
+ if ( copyLength != 0 ) {
+
+ ASSERT( ReceiveIrp->MdlAddress != NULL );
+ ASSERT( SendIrp->MdlAddress != NULL );
+
+ LoopCopyData(
+ ReceiveIrp->MdlAddress,
+ SendIrp->MdlAddress,
+ copyLength
+ );
+
+ }
+
+ //
+ // Complete the Receive and Send requests.
+ //
+
+ receivingConnection =
+ (PLOOP_CONNECTION)receiveIrpSp->FileObject->FsContext;
+ sendingConnection =
+ (PLOOP_CONNECTION)sendIrpSp->FileObject->FsContext;
+
+ ReceiveIrp->IoStatus.Status = status;
+ ReceiveIrp->IoStatus.Information = copyLength;
+
+ SendIrp->IoStatus.Status = STATUS_SUCCESS;
+ SendIrp->IoStatus.Information = copyLength;
+
+ IoCompleteRequest( ReceiveIrp, 2 );
+ IoCompleteRequest( SendIrp, 2 );
+
+ //
+ // Dereference the connections.
+ //
+
+ ACQUIRE_LOOP_LOCK( "CompleteReceive dereference" );
+ LoopDereferenceConnection( receivingConnection );
+ LoopDereferenceConnection( sendingConnection );
+ RELEASE_LOOP_LOCK( "CompleteReceive dereference" );
+
+ return;
+
+} // CompleteReceive
+
+
+VOID
+IndicateReceive (
+ IN PLOOP_CONNECTION ReceivingConnection,
+ IN PIRP InitialSendIrp
+ )
+{
+ NTSTATUS status;
+ PLOOP_ENDPOINT receivingEndpoint;
+ PLOOP_CONNECTION sendingConnection;
+ PLIST_ENTRY listEntry;
+ PIRP receiveIrp;
+ PIRP sendIrp;
+ PTDI_IND_RECEIVE receiveHandler;
+ PVOID receiveContext;
+ PIO_STACK_LOCATION sendIrpSp;
+ PTDI_REQUEST_KERNEL_SEND sendRequest;
+ ULONG length;
+ PMDL mdl;
+ PVOID address;
+ ULONG bytesTaken;
+
+ receivingEndpoint = ReceivingConnection->Endpoint;
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Receiving endpoint: %lx\n", receivingEndpoint );
+ }
+
+ //
+ // Reference the receiving connection to prevent it from going away
+ // while this routine is running.
+ //
+
+ ReceivingConnection->BlockHeader.ReferenceCount++;
+ IF_DEBUG(LOOP3) {
+ DbgPrint( " New refcnt on connection %lx is %lx\n",
+ ReceivingConnection,
+ ReceivingConnection->BlockHeader.ReferenceCount );
+ }
+
+ //
+ // Capture the address of the sending connection, as the receiving
+ // connection's pointer can be zeroed if a Disconnect occurs.
+ //
+
+ sendingConnection = ReceivingConnection->RemoteConnection;
+ ASSERT( sendingConnection != NULL );
+
+ //
+ // If the receiving connection has a pending Receive, satisfy it
+ // with this Send. If there is no pending Receive, and a Receive
+ // handler has been enabled on the receiving connection, call it.
+ // If the Receive handler returns with a Receive IRP, use it to
+ // satisfy this Send. If the Receive handler doesn't return an IRP,
+ // leave this Send pending.
+ //
+ // !!! Note that the current implementation only works because
+ // partial sends are not supported.
+ //
+
+ while ( TRUE ) {
+
+ //
+ // We have a Send pending. Is there a pending Receive?
+ //
+
+ listEntry = RemoveHeadList( &ReceivingConnection->PendingReceiveList );
+
+ if ( listEntry != &ReceivingConnection->PendingReceiveList ) {
+
+ //
+ // Found a pending Receive. Use it to satisfy the first
+ // incoming Send.
+ //
+
+ receiveIrp = CONTAINING_RECORD(
+ listEntry,
+ IRP,
+ Tail.Overlay.ListEntry
+ );
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Receive IRP pending: %lx\n", receiveIrp );
+ }
+
+ listEntry = RemoveHeadList(
+ &ReceivingConnection->IncomingSendList
+ );
+ ASSERT( listEntry != &ReceivingConnection->IncomingSendList );
+ sendIrp = CONTAINING_RECORD(
+ listEntry,
+ IRP,
+ Tail.Overlay.ListEntry
+ );
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Send IRP pending: %lx\n", sendIrp );
+ }
+
+ //
+ // If this is the first time through the loop, and we were
+ // called to process a newly queued Send, dereference the
+ // receiving connection -- LoopSend referenced the
+ // connection an extra time in case the Send had to remain
+ // queued.
+ //
+
+ if ( InitialSendIrp != NULL ) {
+ LoopDereferenceConnection( ReceivingConnection );
+ }
+
+ RELEASE_LOOP_LOCK( "IndicateReceive pending Receive" );
+
+ CompleteReceive( receiveIrp, sendIrp );
+
+ ACQUIRE_LOOP_LOCK( "IndicateReceive pending Receive completed" );
+
+ //
+ // Fall to bottom of loop to handle more incoming Sends.
+ //
+
+ } else {
+
+ //
+ // No pending Receive. Is there a Receive handler?
+ //
+
+ receiveHandler = receivingEndpoint->ReceiveHandler;
+ receiveContext = receivingEndpoint->ReceiveContext;
+
+ if ( receiveHandler == NULL ) {
+
+ //
+ // No Receive handler. The Send must remain queued.
+ //
+
+ IF_DEBUG(LOOP2) DbgPrint( " No Receive handler\n" );
+
+ break;
+
+ }
+
+ //
+ // The receiving endpoint has a Receive handler. Call it.
+ // If it returns STATUS_SUCCESS, it completely handled the
+ // data. If it returns STATUS_MORE_PROCESSING_REQUIRED, it
+ // also returns a Receive IRP describing where to put the
+ // data. Any other return status means the receiver can't
+ // take the data just now, so we leave the Send queued and
+ // wait for the receiver to post a Receive IRP.
+ //
+ // !!! Note that we don't currently handle partial data
+ // acceptance.
+ //
+ // First, remove the first Send from the Incoming Send list,
+ // and make it the Indicating Send. It must be removed from
+ // the list to ensure that it isn't completed by
+ // LoopDoDisconnection while we're indicating it.
+ //
+
+ listEntry = RemoveHeadList(
+ &ReceivingConnection->IncomingSendList
+ );
+ ASSERT( listEntry != &ReceivingConnection->IncomingSendList );
+ sendIrp = CONTAINING_RECORD(
+ listEntry,
+ IRP,
+ Tail.Overlay.ListEntry
+ );
+ ReceivingConnection->IndicatingSendIrp = sendIrp;
+
+ RELEASE_LOOP_LOCK( "IndicateReceive calling Receive handler" );
+
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Receive handler: %lx\n", receiveHandler );
+ DbgPrint( " Send IRP: %lx\n", sendIrp );
+ }
+
+ sendIrpSp = IoGetCurrentIrpStackLocation( sendIrp );
+ sendRequest = (PTDI_REQUEST_KERNEL_SEND)&sendIrpSp->Parameters;
+
+ length = sendRequest->SendLength;
+ ASSERTMSG(
+ "Loopback driver doesn't handle partial or expedited sends",
+ sendRequest->SendFlags == 0
+ );
+
+ //
+ // Map the send buffer, if necessary.
+ //
+
+ mdl = sendIrp->MdlAddress;
+ if ( MmGetMdlByteCount(mdl) == 0 ) {
+ address = NULL;
+ } else {
+ address = MmGetSystemAddressForMdl( mdl );
+ }
+
+ //
+ // Call the Receive handler.
+ //
+
+ status = receiveHandler(
+ receiveContext,
+ ReceivingConnection->ConnectionContext,
+ 0,
+ MmGetMdlByteCount( mdl ),
+ length,
+ &bytesTaken,
+ address,
+ &receiveIrp
+ );
+
+ ACQUIRE_LOOP_LOCK( "IndicateReceive after calling handler" );
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Indication for send IRP %lx done\n", sendIrp );
+ }
+
+ ReceivingConnection->IndicatingSendIrp = NULL;
+
+ if ( status == STATUS_SUCCESS ) {
+
+ //
+ // The Receive handler completely handled the data.
+ // Complete the Send.
+ //
+
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Receive handler handled data\n" );
+ }
+
+ ASSERTMSG(
+ "Loopback driver doesn't handle partial acceptance "
+ "of indications",
+ bytesTaken == length
+ );
+
+ //
+ // Dereference the sending and receiving connections --
+ // LoopSend referenced the connections when it queued
+ // the Send.
+ //
+
+ LoopDereferenceConnection( ReceivingConnection );
+ LoopDereferenceConnection( sendingConnection );
+
+ RELEASE_LOOP_LOCK( "IndicateReceive completely handled" );
+
+ //
+ // Complete the Send IRP.
+ //
+
+ sendIrp->IoStatus.Status = STATUS_SUCCESS;
+ sendIrp->IoStatus.Information = sendRequest->SendLength;
+
+ IoCompleteRequest( sendIrp, 2 );
+
+ ACQUIRE_LOOP_LOCK( "IndicateReceive send completed" );
+
+ //
+ // Fall to bottom of loop to handle more incoming
+ // Sends.
+ //
+
+ } else if ( status == STATUS_MORE_PROCESSING_REQUIRED ) {
+
+ //
+ // The Receive handler returned a Receive IRP to be used
+ // to satisfy the Send.
+ //
+
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Receive handler returned IRP: %lx\n",
+ receiveIrp );
+ }
+
+ ASSERTMSG(
+ "Loopback driver doesn't handle partial acceptance "
+ "of indications",
+ bytesTaken == 0
+ );
+
+ //
+ // Complete the Receive using the current Send.
+ //
+ // *** Note that the pending Send references both the
+ // sending and receiving connections.
+
+ RELEASE_LOOP_LOCK( "IndicateReceive complete new Receive" );
+
+ CompleteReceive( receiveIrp, sendIrp );
+
+ ACQUIRE_LOOP_LOCK( "IndicateReceive new receive completed" );
+
+ //
+ // Fall to bottom of loop to handle more incoming
+ // Sends.
+ //
+
+ } else {
+
+ //
+ // The Receive handler couldn't take the data. This
+ // Send will have to wait until receiver can post a
+ // Receive IRP.
+ //
+ // *** Because we didn't hold the spin lock around the
+ // call to the Receive handler, it's possible that
+ // the receiver has already posted a Receive IRP.
+ // Because we were in the middle of an indication,
+ // that Receive would have been queued to the
+ // Pending Receive list, and we should go get it
+ // now. If the receiver hasn't posted a Receive
+ // yet, then this Send will be put back on the
+ // Incoming Send list before the Receive does come
+ // in (since we're now holding the lock).
+ //
+
+ ASSERT( status == STATUS_DATA_NOT_ACCEPTED );
+
+ IF_DEBUG(LOOP2) DbgPrint( " Data not accepted\n" );
+
+ if ( GET_BLOCK_STATE(ReceivingConnection) !=
+ BlockStateActive ) {
+
+ //
+ // The connection is closing. Abort the current
+ // Send and leave.
+ //
+
+ LoopDereferenceConnection( ReceivingConnection );
+ LoopDereferenceConnection( sendingConnection );
+
+ RELEASE_LOOP_LOCK( "IndicateReceive disconnecting" );
+
+ sendIrp->IoStatus.Status = STATUS_DISCONNECTED;
+ IoCompleteRequest( sendIrp, 2 );
+
+ ACQUIRE_LOOP_LOCK( "IndicateReceive send aborted" );
+
+ break;
+
+ }
+
+ listEntry = RemoveHeadList(
+ &ReceivingConnection->PendingReceiveList
+ );
+
+ if ( listEntry !=
+ &ReceivingConnection->PendingReceiveList ) {
+
+ //
+ // A Receive has been posted. Use it to satisfy
+ // this Send.
+ //
+
+ receiveIrp = CONTAINING_RECORD(
+ listEntry,
+ IRP,
+ Tail.Overlay.ListEntry
+ );
+ IF_DEBUG(LOOP2) {
+ DbgPrint( " Receive IRP pending: %lx\n",
+ receiveIrp );
+ }
+
+ //
+ // Complete the Receive using the current Send.
+ //
+ //
+ // If this is the first time through the loop, and
+ // we were called to process a newly queued Send,
+ // dereference the receiving connection -- LoopSend
+ // referenced the connection an extra time in case
+ // the Send had to remain queued.
+ //
+
+ if ( InitialSendIrp != NULL ) {
+ LoopDereferenceConnection( ReceivingConnection );
+ }
+
+ RELEASE_LOOP_LOCK(
+ "IndicateReceive complete posted Receive"
+ );
+
+ CompleteReceive( receiveIrp, sendIrp );
+
+ ACQUIRE_LOOP_LOCK(
+ "IndicateReceive posted receive completed"
+ );
+
+ //
+ // Fall to bottom of loop to handle more incoming
+ // Sends.
+ //
+
+ } else {
+
+ //
+ // The handler didn't take the data, and it didn't
+ // post a Receive IRP. Requeue the current send and
+ // get out.
+ //
+
+ InsertHeadList(
+ &ReceivingConnection->IncomingSendList,
+ &sendIrp->Tail.Overlay.ListEntry
+ );
+
+ break;
+
+ }
+
+ }
+
+ } // pending receive?
+
+ //
+ // If we get here, we need to indicate the next incoming Send,
+ // if there is one.
+ //
+
+ InitialSendIrp = NULL;
+
+ if ( (GET_BLOCK_STATE(ReceivingConnection) != BlockStateActive) ||
+ (ReceivingConnection->IncomingSendList.Flink ==
+ &ReceivingConnection->IncomingSendList) ) {
+
+ //
+ // No more Sends, or connection no longer active. Leave.
+ //
+
+ break;
+
+ }
+
+ //
+ // Process the next Send.
+ //
+
+ } // while ( TRUE )
+
+ //
+ // Remove the connection reference acquired at the start of this
+ // routine.
+ //
+
+ LoopDereferenceConnection( ReceivingConnection );
+
+ RELEASE_LOOP_LOCK( "IndicateReceive done" );
+
+ return;
+
+} // IndicateReceive
+