diff options
Diffstat (limited to 'private/ntos/tdi/loopback')
-rw-r--r-- | private/ntos/tdi/loopback/connect.c | 1593 | ||||
-rw-r--r-- | private/ntos/tdi/loopback/datagram.c | 848 | ||||
-rw-r--r-- | private/ntos/tdi/loopback/endpoint.c | 373 | ||||
-rw-r--r-- | private/ntos/tdi/loopback/info.c | 183 | ||||
-rw-r--r-- | private/ntos/tdi/loopback/loopback.c | 1284 | ||||
-rw-r--r-- | private/ntos/tdi/loopback/loopback.h | 433 | ||||
-rw-r--r-- | private/ntos/tdi/loopback/loopdbg.h | 58 | ||||
-rw-r--r-- | private/ntos/tdi/loopback/loopsub.c | 379 | ||||
-rw-r--r-- | private/ntos/tdi/loopback/makefile | 6 | ||||
-rw-r--r-- | private/ntos/tdi/loopback/sources | 46 | ||||
-rw-r--r-- | private/ntos/tdi/loopback/transfer.c | 859 |
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 + |