diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/tdi/loopback/datagram.c | 848 |
1 files changed, 848 insertions, 0 deletions
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 + |