diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/afd/recvdg.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/afd/recvdg.c')
-rw-r--r-- | private/ntos/afd/recvdg.c | 1653 |
1 files changed, 1653 insertions, 0 deletions
diff --git a/private/ntos/afd/recvdg.c b/private/ntos/afd/recvdg.c new file mode 100644 index 000000000..cc029652d --- /dev/null +++ b/private/ntos/afd/recvdg.c @@ -0,0 +1,1653 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + recvdg.c + +Abstract: + + This module contains routines for handling data receive for datagram + endpoints. + +Author: + + David Treadwell (davidtr) 7-Oct-1993 + +Revision History: + +--*/ + +#include "afdp.h" + +NTSTATUS +AfdSetupReceiveDatagramIrp ( + IN PIRP Irp, + IN PVOID DatagramBuffer OPTIONAL, + IN ULONG DatagramLength, + IN PVOID SourceAddress, + IN ULONG SourceAddressLength + ); + +NTSTATUS +AfdRestartBufferReceiveDatagram ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGEAFD, AfdReceiveDatagram ) +#pragma alloc_text( PAGEAFD, AfdReceiveDatagramEventHandler ) +#pragma alloc_text( PAGEAFD, AfdSetupReceiveDatagramIrp ) +#pragma alloc_text( PAGEAFD, AfdRestartBufferReceiveDatagram ) +#pragma alloc_text( PAGEAFD, AfdCancelReceiveDatagram ) +#pragma alloc_text( PAGEAFD, AfdCleanupReceiveDatagramIrp ) +#endif + +// +// Macros to make the receive datagram code more maintainable. +// + +#define AfdRecvDatagramInfo DeviceIoControl + +#define AfdRecvAddressLength InputBufferLength +#define AfdRecvAddressPointer Type3InputBuffer + + +NTSTATUS +AfdReceiveDatagram ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp, + IN ULONG RecvFlags, + IN ULONG AfdFlags + ) +{ + NTSTATUS status; + KIRQL oldIrql; + PAFD_ENDPOINT endpoint; + PLIST_ENTRY listEntry; + BOOLEAN peek; + PAFD_BUFFER afdBuffer; + ULONG recvFlags; + ULONG afdFlags; + ULONG recvLength; + PVOID addressPointer; + PULONG addressLength; + PMDL addressMdl; + PMDL lengthMdl; + + // + // Set up some local variables. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( endpoint->Type == AfdBlockTypeDatagram ); + + Irp->IoStatus.Information = 0; + + addressMdl = NULL; + lengthMdl = NULL; + + // + // If receive has been shut down or the endpoint aborted, fail. + // + // !!! Do we care if datagram endpoints get aborted? + // + + if ( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) ) { + status = STATUS_PIPE_DISCONNECTED; + goto complete; + } + +#if 0 + if ( (endpoint->DisconnectMode & AFD_ABORTIVE_DISCONNECT) ) { + status = STATUS_LOCAL_DISCONNECT; + goto complete; + } +#endif + + // + // Do some special processing based on whether this is a receive + // datagram IRP, a receive IRP, or a read IRP. + // + + if ( IrpSp->Parameters.DeviceIoControl.IoControlCode == + IOCTL_AFD_RECEIVE_DATAGRAM && + IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL ) { + + PAFD_RECV_DATAGRAM_INFO recvInfo; + + // + // Make sure that the endpoint is in the correct state. + // + + if ( endpoint->State != AfdEndpointStateBound ) { + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + // + // Grab the parameters from the input structure. + // + + if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + sizeof(*recvInfo) ) { + + try { + + // + // Probe the input structure. + // + + recvInfo = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; + + if( Irp->RequestorMode != KernelMode ) { + + ProbeForRead( + recvInfo, + sizeof(*recvInfo), + sizeof(ULONG) + ); + + } + + // + // Snag the receive flags. + // + + recvFlags = recvInfo->TdiFlags; + afdFlags = recvInfo->AfdFlags; + + // + // Setup the address fields so we can return the datagram + // address to the user. + // + + addressPointer = recvInfo->Address; + addressLength = recvInfo->AddressLength; + + // + // Validate the WSABUF parameters. + // + + if ( recvInfo->BufferArray != NULL && + recvInfo->BufferCount > 0 ) { + + // + // Create the MDL chain describing the WSABUF array. + // + + status = AfdAllocateMdlChain( + Irp, + recvInfo->BufferArray, + recvInfo->BufferCount, + IoWriteAccess, + &recvLength + ); + + } else { + + // + // Zero-length input buffer. This is OK for datagrams. + // + + ASSERT( Irp->MdlAddress == NULL ); + status = STATUS_SUCCESS; + + } + + } except ( EXCEPTION_EXECUTE_HANDLER ) { + + // + // Exception accessing input structure. + // + + status = GetExceptionCode(); + + } + + } else { + + // + // Invalid input buffer length. + // + + status = STATUS_INVALID_PARAMETER; + + } + + // + // If only one of addressPointer or addressLength are NULL, then + // fail the request. + // + + if( (addressPointer == NULL) ^ (addressLength == NULL) ) { + + status = STATUS_INVALID_PARAMETER; + goto complete; + + } + + if( !NT_SUCCESS(status) ) { + + goto complete; + + } + + // + // If the user wants the source address from the receive datagram, + // then create MDLs for the address & address length, then probe + // and lock the MDLs. + // + + if( addressPointer != NULL ) { + + ASSERT( addressLength != NULL ); + + // + // Setup so we know how to cleanup after the try/except block. + // + + status = STATUS_SUCCESS; + + try { + + // + // Bomb off if the user is trying to do something stupid, like + // specify a zero-length address, or one that's unreasonably + // huge. Here, we (arbitrarily) define "unreasonably huge" as + // anything 64K or greater. + // + + if( *addressLength == 0 || + *addressLength >= 65536 ) { + + ExRaiseStatus( STATUS_INVALID_PARAMETER ); + + } + + // + // Create a MDL describing the address buffer, then probe + // it for write access. + // + + addressMdl = IoAllocateMdl( + addressPointer, // VirtualAddress + *addressLength, // Length + FALSE, // SecondaryBuffer + TRUE, // ChargeQuota + NULL // Irp + ); + + if( addressMdl == NULL ) { + + ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); + + } + + MmProbeAndLockPages( + addressMdl, // MemoryDescriptorList + Irp->RequestorMode, // AccessMode + IoWriteAccess // Operation + ); + + // + // Create a MDL describing the length buffer, then probe it + // for write access. + // + + lengthMdl = IoAllocateMdl( + addressLength, // VirtualAddress + sizeof(*addressLength), // Length + FALSE, // SecondaryBuffer + TRUE, // ChargeQuota + NULL // Irp + ); + + if( lengthMdl == NULL ) { + + ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); + + } + + MmProbeAndLockPages( + lengthMdl, // MemoryDescriptorList + Irp->RequestorMode, // AccessMode + IoWriteAccess // Operation + ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + status = GetExceptionCode(); + + } + + if( !NT_SUCCESS(status) ) { + + goto complete; + + } + + ASSERT( addressMdl != NULL ); + ASSERT( lengthMdl != NULL ); + + } else { + + ASSERT( addressMdl == NULL ); + ASSERT( lengthMdl == NULL ); + + } + + // + // Validate the receive flags. + // + + if( ( recvFlags & TDI_RECEIVE_EITHER ) != TDI_RECEIVE_NORMAL ) { + status = STATUS_NOT_SUPPORTED; + goto complete; + } + + peek = (BOOLEAN)( (recvFlags & TDI_RECEIVE_PEEK) != 0 ); + + } else { + + ASSERT( (Irp->Flags & IRP_INPUT_OPERATION) == 0 ); + + if ( IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL ) { + + // + // Grab the input parameters from the IRP. + // + + ASSERT( IrpSp->Parameters.DeviceIoControl.IoControlCode == + IOCTL_AFD_RECEIVE ); + + recvFlags = RecvFlags; + afdFlags = AfdFlags; + + // + // It is illegal to attempt to receive expedited data on a + // datagram endpoint. + // + + if ( (recvFlags & TDI_RECEIVE_EXPEDITED) != 0 ) { + status = STATUS_NOT_SUPPORTED; + goto complete; + } + + ASSERT( ( recvFlags & TDI_RECEIVE_EITHER ) == TDI_RECEIVE_NORMAL ); + + peek = (BOOLEAN)( (recvFlags & TDI_RECEIVE_PEEK) != 0 ); + + } else { + + // + // This must be a read IRP. There are no special options + // for a read IRP. + // + + ASSERT( IrpSp->MajorFunction == IRP_MJ_READ ); + + recvFlags = TDI_RECEIVE_NORMAL; + afdFlags = AFD_OVERLAPPED; + peek = FALSE; + + } + + ASSERT( addressMdl == NULL ); + ASSERT( lengthMdl == NULL ); + + } + + // + // Save the address & length MDLs in the current IRP stack location. + // These will be used later in SetupReceiveDatagramIrp(). Note that + // they should either both be NULL or both be non-NULL. + // + + IoAcquireCancelSpinLock( &Irp->CancelIrql ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + ASSERT( !( ( addressMdl == NULL ) ^ ( lengthMdl == NULL ) ) ); + + IrpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressPointer = + (PVOID)addressMdl; + IrpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressLength = + (ULONG)lengthMdl; + + // + // Determine whether there are any datagrams already bufferred on + // this endpoint. If there is a bufferred datagram, we'll use it to + // complete the IRP. + // + + if ( endpoint->BufferredDatagramCount != 0 ) { + + KIRQL saveIrql; + + // + // There is at least one datagram bufferred on the endpoint. + // Use it for this receive. + // + + ASSERT( !IsListEmpty( &endpoint->ReceiveDatagramBufferListHead ) ); + + listEntry = endpoint->ReceiveDatagramBufferListHead.Flink; + afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry ); + + // + // Prepare the user's IRP for completion. + // + + status = AfdSetupReceiveDatagramIrp ( + Irp, + afdBuffer->Buffer, + afdBuffer->DataLength, + afdBuffer->SourceAddress, + afdBuffer->SourceAddressLength + ); + + // + // Release the cancel spin lock, since we don't need it. + // However, be careful about the IRQLs because we're releasing + // locks in a different order than we acquired them. + // + + saveIrql = Irp->CancelIrql; + IoReleaseCancelSpinLock( oldIrql ); + oldIrql = saveIrql; + + // + // If this wasn't a peek IRP, remove the buffer from the endpoint's + // list of bufferred datagrams. + // + + if ( !peek ) { + + RemoveHeadList( &endpoint->ReceiveDatagramBufferListHead ); + + // + // Update the counts of bytes and datagrams on the endpoint. + // + + endpoint->BufferredDatagramCount--; + endpoint->BufferredDatagramBytes -= afdBuffer->DataLength; + endpoint->EventsActive &= ~AFD_POLL_RECEIVE; + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdReceiveDatagram: Endp %08lX, Active %08lX\n", + endpoint, + endpoint->EventsActive + )); + } + + if( endpoint->BufferredDatagramCount > 0 ) { + + AfdIndicateEventSelectEvent( + endpoint, + AFD_POLL_RECEIVE_BIT, + STATUS_SUCCESS + ); + + } + } + + // + // We've set up all return information. Clean up and complete + // the IRP. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + if ( !peek ) { + AfdReturnBuffer( afdBuffer ); + } + + IoCompleteRequest( Irp, 0 ); + + return status; + } + + // + // There were no datagrams bufferred on the endpoint. If this is a + // nonblocking endpoint and the request was a normal receive (as + // opposed to a read IRP), fail the request. We don't fail reads + // under the asumption that if the application is doing reads they + // don't want nonblocking behavior. + // + + if ( endpoint->NonBlocking && !ARE_DATAGRAMS_ON_ENDPOINT( endpoint ) && + !( afdFlags & AFD_OVERLAPPED ) ) { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + status = STATUS_DEVICE_NOT_READY; + goto complete; + } + + // + // We'll have to pend the IRP. Place the IRP on the appropriate IRP + // list in the endpoint. + // + + if ( peek ) { + InsertTailList( + &endpoint->PeekDatagramIrpListHead, + &Irp->Tail.Overlay.ListEntry + ); + } else { + InsertTailList( + &endpoint->ReceiveDatagramIrpListHead, + &Irp->Tail.Overlay.ListEntry + ); + } + + IoMarkIrpPending( Irp ); + + // + // Set up the cancellation routine in the IRP. If the IRP has already + // been cancelled, just call the cancellation routine here. + // + + if ( Irp->Cancel ) { + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + AfdCancelReceiveDatagram( IrpSp->DeviceObject, Irp ); + status = STATUS_CANCELLED; + goto complete; + } + + IoSetCancelRoutine( Irp, AfdCancelReceiveDatagram ); + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + return STATUS_PENDING; + +complete: + + ASSERT( !NT_SUCCESS(status) ); + + if( addressMdl != NULL ) { + if( (addressMdl->MdlFlags & MDL_PAGES_LOCKED) != 0 ) { + MmUnlockPages( addressMdl ); + } + IoFreeMdl( addressMdl ); + } + + if( lengthMdl != NULL ) { + if( (lengthMdl->MdlFlags & MDL_PAGES_LOCKED) != 0 ) { + MmUnlockPages( lengthMdl ); + } + IoFreeMdl( lengthMdl ); + } + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, 0 ); + + return status; + +} // AfdReceiveDatagram + + +NTSTATUS +AfdReceiveDatagramEventHandler ( + IN PVOID TdiEventContext, + IN int SourceAddressLength, + IN PVOID SourceAddress, + IN int OptionsLength, + IN PVOID Options, + IN ULONG ReceiveDatagramFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *BytesTaken, + IN PVOID Tsdu, + OUT PIRP *IoRequestPacket + ) + +/*++ + +Routine Description: + + Handles receive datagram events for nonbufferring transports. + +Arguments: + + +Return Value: + + +--*/ + +{ + KIRQL oldIrql; + KIRQL cancelIrql; + PAFD_ENDPOINT endpoint; + PLIST_ENTRY listEntry; + PAFD_BUFFER afdBuffer; + PIRP irp; + ULONG requiredAfdBufferSize; + BOOLEAN userIrp; + + endpoint = TdiEventContext; + ASSERT( endpoint != NULL ); + ASSERT( endpoint->Type == AfdBlockTypeDatagram ); + +#if AFD_PERF_DBG + if ( BytesAvailable == BytesIndicated ) { + AfdFullReceiveDatagramIndications++; + } else { + AfdPartialReceiveDatagramIndications++; + } +#endif + + // + // If this endpoint is connected and the datagram is for a different + // address than the one the endpoint is connected to, drop the + // datagram. Also, if we're in the process of connecting the + // endpoint to a remote address, the MaximumDatagramCount field will + // be 0, in which case we shoul drop the datagram. + // + + IoAcquireCancelSpinLock( &cancelIrql ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + if ( (endpoint->State == AfdEndpointStateConnected && + !AfdAreTransportAddressesEqual( + endpoint->Common.Datagram.RemoteAddress, + endpoint->Common.Datagram.RemoteAddressLength, + SourceAddress, + SourceAddressLength, + TRUE )) || + (endpoint->Common.Datagram.MaxBufferredReceiveCount == 0) ) { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + *BytesTaken = BytesAvailable; + return STATUS_SUCCESS; + } + + // + // Check whether there are any IRPs waiting on the endpoint. If + // there is such an IRP, use it to receive the datagram. + // + + if ( !IsListEmpty( &endpoint->ReceiveDatagramIrpListHead ) ) { + + ASSERT( *BytesTaken == 0 ); + ASSERT( endpoint->BufferredDatagramCount == 0 ); + ASSERT( endpoint->BufferredDatagramBytes == 0 ); + + listEntry = RemoveHeadList( &endpoint->ReceiveDatagramIrpListHead ); + + // + // Get a pointer to the IRP and reset the cancel routine in + // the IRP. The IRP is no longer cancellable. + // + + irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); + IoSetCancelRoutine( irp, NULL ); + + // + // If the entire datagram is being indicated to us here, just + // copy the information to the MDL in the IRP and return. + // + // Note that we'll also take the entire datagram if the user + // has pended a zero-byte datagram receive (detectable as a + // NULL Irp->MdlAddress). We'll eat the datagram and fall + // through to AfdSetupReceiveDatagramIrp(), which will store + // an error status in the IRP since the user's buffer is + // insufficient to hold the datagram. + // + + if( BytesIndicated == BytesAvailable || + irp->MdlAddress == NULL ) { + + // + // Set BytesTaken to indicate that we've taken all the + // data. We do it here because we already have + // BytesAvailable in a register, which probably won't + // be true after making function calls. + // + + *BytesTaken = BytesAvailable; + + // + // Copy the datagram and source address to the IRP. This + // prepares the IRP to be completed. + // + // !!! do we need a special version of this routine to + // handle special RtlCopyMemory, like for + // TdiCopyLookaheadBuffer? + // + + (VOID)AfdSetupReceiveDatagramIrp ( + irp, + Tsdu, + BytesAvailable, + SourceAddress, + SourceAddressLength + ); + + // + // The IRP is off the endpoint's list and is no longer + // cancellable. We can release the locks we hold. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + // + // Complete the IRP. We've already set BytesTaken + // to tell the provider that we have taken all the data. + // + + IoCompleteRequest( irp, AfdPriorityBoost ); + + return STATUS_SUCCESS; + } + + // + // Some of the datagram was not indicated, so remember that we + // want to pass back this IRP to the TDI provider. Passing back + // this IRP directly is good because it avoids having to copy + // the data from one of our buffers into the user's buffer. + // + + userIrp = TRUE; + requiredAfdBufferSize = 0; + + } else { + + userIrp = FALSE; + requiredAfdBufferSize = BytesAvailable; + } + + // + // There were no IRPs available to take the datagram, so we'll have + // to buffer it. First make sure that we're not over the limit + // of bufferring that we can do. If we're over the limit, toss + // this datagram. + // + + if ( endpoint->BufferredDatagramCount >= + endpoint->Common.Datagram.MaxBufferredReceiveCount || + endpoint->BufferredDatagramBytes >= + endpoint->Common.Datagram.MaxBufferredReceiveBytes ) { + + // + // If circular queueing is not enabled, then just drop the + // datagram on the floor. + // + + + if( !endpoint->Common.Datagram.CircularQueueing ) { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + *BytesTaken = BytesAvailable; + return STATUS_SUCCESS; + + } + + // + // Circular queueing is enabled, so drop packets at the head of + // the receive queue until we're below the receive limit. + // + + while( endpoint->BufferredDatagramCount >= + endpoint->Common.Datagram.MaxBufferredReceiveCount || + endpoint->BufferredDatagramBytes >= + endpoint->Common.Datagram.MaxBufferredReceiveBytes ) { + + listEntry = RemoveHeadList( &endpoint->ReceiveDatagramBufferListHead ); + afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry ); + + endpoint->BufferredDatagramCount--; + endpoint->BufferredDatagramBytes -= afdBuffer->DataLength; + + AfdReturnBuffer( afdBuffer ); + + } + + // + // Proceed to accept the incoming packet. + // + + } + + // + // We're able to buffer the datagram. Now acquire a buffer of + // appropriate size. + // + + afdBuffer = AfdGetBuffer( requiredAfdBufferSize, SourceAddressLength ); + + if ( afdBuffer == NULL ) { + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + *BytesTaken = BytesAvailable; + return STATUS_SUCCESS; + } + + // + // If the entire datagram is being indicated to us, just copy it + // here. + // + + if ( BytesIndicated == BytesAvailable ) { + + ASSERT( !userIrp ); + + // + // If there is a peek IRP on the endpoint, remove it from the + // list and prepare to complete it. We can't complete it now + // because we hold a spin lock. + // + + if ( !IsListEmpty( &endpoint->PeekDatagramIrpListHead ) ) { + + // + // Remove the first peek IRP from the list and get a pointer + // to it. + // + + listEntry = RemoveHeadList( &endpoint->PeekDatagramIrpListHead ); + irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); + + // + // Reset the cancel routine in the IRP. The IRP is no + // longer cancellable, since we're about to complete it. + // + + IoSetCancelRoutine( irp, NULL ); + + // + // Copy the datagram and source address to the IRP. This + // prepares the IRP to be completed. + // + + (VOID)AfdSetupReceiveDatagramIrp ( + irp, + Tsdu, + BytesAvailable, + SourceAddress, + SourceAddressLength + ); + + } else { + + irp = NULL; + } + + // + // We don't need the cancel spin lock any more, so we can + // release it. However, since we acquired the cancel spin lock + // after the endpoint spin lock and we still need the endpoint + // spin lock, be careful to switch the IRQLs. + // + + IoReleaseCancelSpinLock( oldIrql ); + oldIrql = cancelIrql; + + // + // Use the special function to copy the data instead of + // RtlCopyMemory in case the data is coming from a special place + // (DMA, etc.) which cannot work with RtlCopyMemory. + // + + TdiCopyLookaheadData( + afdBuffer->Buffer, + Tsdu, + BytesAvailable, + ReceiveDatagramFlags + ); + + // + // Store the data length and set the offset to 0. + // + + afdBuffer->DataLength = BytesAvailable; + ASSERT( afdBuffer->DataOffset == 0 ); + + // + // Store the address of the sender of the datagram. + // + + RtlCopyMemory( + afdBuffer->SourceAddress, + SourceAddress, + SourceAddressLength + ); + + afdBuffer->SourceAddressLength = SourceAddressLength; + + // + // Place the buffer on this endpoint's list of bufferred datagrams + // and update the counts of datagrams and datagram bytes on the + // endpoint. + // + + InsertTailList( + &endpoint->ReceiveDatagramBufferListHead, + &afdBuffer->BufferListEntry + ); + + endpoint->BufferredDatagramCount++; + endpoint->BufferredDatagramBytes += BytesAvailable; + + // + // All done. Release the lock and tell the provider that we + // took all the data. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Indicate that it is possible to receive on the endpoint now. + // + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_RECEIVE_BIT, + STATUS_SUCCESS + ); + + // + // If there was a peek IRP on the endpoint, complete it now. + // + + if ( irp != NULL ) { + IoCompleteRequest( irp, AfdPriorityBoost ); + } + + *BytesTaken = BytesAvailable; + + return STATUS_SUCCESS; + } + + // + // We'll have to format up an IRP and give it to the provider to + // handle. We don't need any locks to do this--the restart routine + // will check whether new receive datagram IRPs were pended on the + // endpoint. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + // + // Use the IRP in the AFD buffer if appropriate. If userIrp is + // TRUE, then the local variable irp will already point to the + // user's IRP which we'll use for this IO. + // + + if ( !userIrp ) { + irp = afdBuffer->Irp; + ASSERT( afdBuffer->Mdl == irp->MdlAddress ); + } + + // + // Tell the TDI provider where to put the source address. + // + + afdBuffer->TdiOutputInfo.RemoteAddressLength = afdBuffer->AllocatedAddressLength; + afdBuffer->TdiOutputInfo.RemoteAddress = afdBuffer->SourceAddress; + + // + // We need to remember the endpoint in the AFD buffer because we'll + // need to access it in the completion routine. + // + + afdBuffer->Context = endpoint; + + // + // Finish building the receive datagram request. + // + + TdiBuildReceiveDatagram( + irp, + endpoint->AddressDeviceObject, + endpoint->AddressFileObject, + AfdRestartBufferReceiveDatagram, + afdBuffer, + irp->MdlAddress, + BytesAvailable, + &afdBuffer->TdiInputInfo, + &afdBuffer->TdiOutputInfo, + 0 + ); + + // + // Make the next stack location current. Normally IoCallDriver would + // do this, but since we're bypassing that, we do it directly. + // + + IoSetNextIrpStackLocation( irp ); + + *IoRequestPacket = irp; + *BytesTaken = 0; + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // AfdReceiveDatagramEventHandler + + +NTSTATUS +AfdRestartBufferReceiveDatagram ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + Handles completion of bufferred datagram receives that were started + in the datagram indication handler. + +Arguments: + + DeviceObject - not used. + + Irp - the IRP that is completing. + + Context - the endpoint which received the datagram. + +Return Value: + + NTSTATUS - if this is our IRP, then always + STATUS_MORE_PROCESSING_REQUIRED to indicate to the IO system that we + own the IRP and the IO system should stop processing the it. + + If this is a user's IRP, then STATUS_SUCCESS to indicate that + IO completion should continue. + +--*/ + +{ + PAFD_ENDPOINT endpoint; + KIRQL oldIrql; + KIRQL cancelIrql; + PAFD_BUFFER afdBuffer; + PIRP pendedIrp; + PLIST_ENTRY listEntry; + + ASSERT( NT_SUCCESS(Irp->IoStatus.Status) ); + + afdBuffer = Context; + + endpoint = afdBuffer->Context; + ASSERT( endpoint->Type == AfdBlockTypeDatagram ); + + // + // Remember the length of the received datagram and the length + // of the source address. + // + + afdBuffer->DataLength = Irp->IoStatus.Information; + afdBuffer->SourceAddressLength = afdBuffer->TdiOutputInfo.RemoteAddressLength; + + // + // Zero the fields of the TDI info structures in the AFD buffer + // that we used. They must be zero when we return the buffer. + // + + afdBuffer->TdiOutputInfo.RemoteAddressLength = 0; + afdBuffer->TdiOutputInfo.RemoteAddress = NULL; + + // + // If the IRP being completed is actually a user's IRP, set it up + // for completion and allow IO completion to finish. + // + + if ( Irp != afdBuffer->Irp ) { + + // + // Set up the IRP for completion. + // + + IoAcquireCancelSpinLock( &cancelIrql ); + + (VOID)AfdSetupReceiveDatagramIrp ( + Irp, + NULL, + Irp->IoStatus.Information, + afdBuffer->SourceAddress, + afdBuffer->SourceAddressLength + ); + + IoReleaseCancelSpinLock( cancelIrql ); + + // + // Free the AFD buffer we've been using to track this request. + // + + AfdReturnBuffer( afdBuffer ); + + // + // If pending has be returned for this irp then mark the current + // stack as pending. + // + + if ( Irp->PendingReturned ) { + IoMarkIrpPending(Irp); + } + + // + // Tell the IO system that it is OK to continue with IO + // completion. + // + + return STATUS_SUCCESS; + } + + // + // If the IO failed, then just return the AFD buffer to our buffer + // pool. + // + + if ( !NT_SUCCESS(Irp->IoStatus.Status) ) { + AfdReturnBuffer( afdBuffer ); + return STATUS_MORE_PROCESSING_REQUIRED; + } + + // + // If there are any pended IRPs on the endpoint, complete as + // appropriate with the new information. + // + + IoAcquireCancelSpinLock( &cancelIrql ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + if ( !IsListEmpty( &endpoint->ReceiveDatagramIrpListHead ) ) { + + // + // There was a pended receive datagram IRP. Remove it from the + // head of the list. + // + + listEntry = RemoveHeadList( &endpoint->ReceiveDatagramIrpListHead ); + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Get a pointer to the IRP and reset the cancel routine in + // the IRP. The IRP is no longer cancellable. + // + + pendedIrp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); + + IoSetCancelRoutine( pendedIrp, NULL ); + + // + // Set up the user's IRP for completion. + // + + (VOID)AfdSetupReceiveDatagramIrp ( + pendedIrp, + afdBuffer->Buffer, + afdBuffer->DataLength, + afdBuffer->SourceAddress, + afdBuffer->SourceAddressLength + ); + + IoReleaseCancelSpinLock( cancelIrql ); + + // + // Complete the user's IRP, free the AFD buffer we used for + // the request, and tell the IO system that we're done + // processing this request. + // + + IoCompleteRequest( pendedIrp, AfdPriorityBoost ); + + AfdReturnBuffer( afdBuffer ); + + return STATUS_MORE_PROCESSING_REQUIRED; + } + + // + // If there are any pended peek IRPs on the endpoint, complete + // one with this datagram. + // + + if ( !IsListEmpty( &endpoint->PeekDatagramIrpListHead ) ) { + + // + // There was a pended peek receive datagram IRP. Remove it from + // the head of the list. + // + + listEntry = RemoveHeadList( &endpoint->PeekDatagramIrpListHead ); + + // + // Get a pointer to the IRP and reset the cancel routine in + // the IRP. The IRP is no longer cancellable. + // + + pendedIrp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); + + IoSetCancelRoutine( pendedIrp, NULL ); + + // + // Set up the user's IRP for completion. + // + + (VOID)AfdSetupReceiveDatagramIrp ( + pendedIrp, + afdBuffer->Buffer, + afdBuffer->DataLength, + afdBuffer->SourceAddress, + afdBuffer->SourceAddressLength + ); + + // + // Don't complete the pended peek IRP yet, since we still hold + // locks. Wait until it is safe to release the locks. + // + + } else { + + pendedIrp = NULL; + } + + // + // Place the datagram at the end of the endpoint's list of bufferred + // datagrams, and update counts of datagrams on the endpoint. + // + + InsertTailList( + &endpoint->ReceiveDatagramBufferListHead, + &afdBuffer->BufferListEntry + ); + + endpoint->BufferredDatagramCount++; + endpoint->BufferredDatagramBytes += afdBuffer->DataLength; + + // + // Release locks and indicate that there are bufferred datagrams + // on the endpoint. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_RECEIVE_BIT, + STATUS_SUCCESS + ); + + // + // If there was a pended peek IRP to complete, complete it now. + // + + if ( pendedIrp != NULL ) { + IoCompleteRequest( pendedIrp, 2 ); + } + + // + // Tell the IO system to stop processing this IRP, since we now own + // it as part of the AFD buffer. + // + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // AfdRestartBufferReceiveDatagram + + +VOID +AfdCancelReceiveDatagram ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + Cancels a receive datagram IRP that is pended in AFD. + +Arguments: + + DeviceObject - not used. + + Irp - the IRP to cancel. + +Return Value: + + None. + +--*/ + +{ + PIO_STACK_LOCATION irpSp; + PAFD_ENDPOINT endpoint; + KIRQL oldIrql; + PMDL mdl; + + // + // Get the endpoint pointer from our IRP stack location. + // + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + endpoint = irpSp->FileObject->FsContext; + + ASSERT( endpoint->Type == AfdBlockTypeDatagram ); + + // + // Remove the IRP from the endpoint's IRP list, synchronizing with + // the endpoint lock which protects the lists. Note that the + // IRP *must* be on one of the endpoint's lists if we are getting + // called here--anybody that removes the IRP from the list must + // do so while holding the cancel spin lock and reset the cancel + // routine to NULL before releasing the cancel spin lock. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + RemoveEntryList( &Irp->Tail.Overlay.ListEntry ); + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Reset the cancel routine in the IRP. + // + + IoSetCancelRoutine( Irp, NULL ); + + // + // Free any MDL chains attached to the IRP stack location. + // + + AfdCleanupReceiveDatagramIrp( Irp ); + + // + // Release the cancel spin lock and complete the IRP with a + // cancellation status code. + // + + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_CANCELLED; + + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return; + +} // AfdCancelReceiveDatagram + + +VOID +AfdCleanupReceiveDatagramIrp( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + Performs any cleanup specific to receive datagram IRPs. + +Arguments: + + Irp - the IRP to cleanup. + +Return Value: + + None. + +Notes: + + This routine may be called at raised IRQL from AfdCompleteIrpList(). + +--*/ + +{ + PIO_STACK_LOCATION irpSp; + PMDL mdl; + + // + // Get the endpoint pointer from our IRP stack location. + // + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Free any MDL chains attached to the IRP stack location. + // + + mdl = (PMDL)irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressPointer; + + if( mdl != NULL ) { + irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressPointer = NULL; + MmUnlockPages( mdl ); + IoFreeMdl( mdl ); + } + + mdl = (PMDL)irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressLength; + + if( mdl != NULL ) { + irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressLength = 0; + MmUnlockPages( mdl ); + IoFreeMdl( mdl ); + } + +} // AfdCleanupReceiveDatagramIrp + + +NTSTATUS +AfdSetupReceiveDatagramIrp ( + IN PIRP Irp, + IN PVOID DatagramBuffer OPTIONAL, + IN ULONG DatagramLength, + IN PVOID SourceAddress, + IN ULONG SourceAddressLength + ) + +/*++ + +Routine Description: + + Copies the datagram to the MDL in the IRP and the datagram sender's + address to the appropriate place in the system buffer. + + NOTE: This function MUST be called with the I/O cancel spinlock held! + +Arguments: + + Irp - the IRP to prepare for completion. + + DatagramBuffer - datagram to copy into the IRP. If NULL, then + there is no need to copy the datagram to the IRP's MDL, the + datagram has already been copied there. + + DatagramLength - the length of the datagram to copy. + + SourceAddress - address of the sender of the datagram. + + SourceAddressLength - length of the source address. + +Return Value: + + NTSTATUS - The status code placed into the IRP. + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION irpSp; + ULONG bytesCopied; + PMDL addressPointer; + PMDL addressLength; + PTRANSPORT_ADDRESS tdiAddress; + ULONG addressBytesCopied; + NTSTATUS status2; + KIRQL cancelIrql; + + ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL ); + + // + // If necessary, copy the datagram in the buffer to the MDL in the + // user's IRP. If there is no MDL in the buffer, then fail if the + // datagram is larger than 0 bytes. + // + + if ( ARGUMENT_PRESENT( DatagramBuffer ) ) { + + if ( Irp->MdlAddress == NULL ) { + + if ( DatagramLength != 0 ) { + status = STATUS_BUFFER_OVERFLOW; + } else { + status = STATUS_SUCCESS; + } + + bytesCopied = 0; + + } else { + + status = TdiCopyBufferToMdl( + DatagramBuffer, + 0, + DatagramLength, + Irp->MdlAddress, + 0, + &bytesCopied + ); + } + + } else { + + // + // The information was already copied to the MDL chain in the + // IRP. Just remember the IO status block so we can do the + // right thing with it later. + // + + status = Irp->IoStatus.Status; + bytesCopied = Irp->IoStatus.Information; + } + + // + // To determine how to complete setting up the IRP for completion, + // figure out whether this IRP was for regular datagram information, + // in which case we need to return an address, or for data only, in + // which case we will not return the source address. NtReadFile() + // and recv() on connected datagram sockets will result in the + // latter type of IRP. + // + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + addressPointer = + (PMDL)irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressPointer; + addressLength = + (PMDL)irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressLength; + + irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressPointer = NULL; + irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressLength = 0; + + if( addressPointer != NULL ) { + + ASSERT( irpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_AFD_RECEIVE_DATAGRAM ); + + ASSERT( addressPointer->Next == NULL ); + ASSERT( ( addressPointer->MdlFlags & MDL_PAGES_LOCKED ) != 0 ); + ASSERT( addressPointer->Size > 0 ); + + ASSERT( addressLength != NULL ); + ASSERT( addressLength->Next == NULL ); + ASSERT( ( addressLength->MdlFlags & MDL_PAGES_LOCKED ) != 0 ); + ASSERT( addressLength->Size > 0 ); + + // + // Extract the real SOCKADDR structure from the TDI address. + // This duplicates MSAFD.DLL's SockBuildSockaddr() function. + // + + tdiAddress = SourceAddress; + + ASSERT( sizeof(tdiAddress->Address[0].AddressType) == sizeof(u_short) ); + ASSERT( FIELD_OFFSET( TA_ADDRESS, AddressLength ) == 0 ); + ASSERT( FIELD_OFFSET( TA_ADDRESS, AddressType ) == sizeof(USHORT) ); + ASSERT( FIELD_OFFSET( TRANSPORT_ADDRESS, Address[0] ) == sizeof(int) ); + ASSERT( SourceAddressLength >= + (tdiAddress->Address[0].AddressLength + sizeof(u_short)) ); + + SourceAddressLength = tdiAddress->Address[0].AddressLength + + sizeof(u_short); // sa_family + SourceAddress = &tdiAddress->Address[0].AddressType; + + // + // Copy the address to the user's buffer, then unlock and + // free the MDL describing the user's buffer. + // + + status2 = TdiCopyBufferToMdl( + SourceAddress, + 0, + SourceAddressLength, + addressPointer, + 0, + &addressBytesCopied + ); + + MmUnlockPages( addressPointer ); + IoFreeMdl( addressPointer ); + + // + // If the above TdiCopyBufferToMdl was successful, then + // copy the address length to the user's buffer, then unlock + // and free the MDL describing the user's buffer. + // + + if( NT_SUCCESS(status2) ) { + + status2 = TdiCopyBufferToMdl( + &SourceAddressLength, + 0, + sizeof(SourceAddressLength), + addressLength, + 0, + &addressBytesCopied + ); + + } + + MmUnlockPages( addressLength ); + IoFreeMdl( addressLength ); + + // + // If either of the above TdiCopyBufferToMdl calls failed, + // then use its status code as the completion code. + // + + if( !NT_SUCCESS(status2) ) { + + status = status2; + + } + + } else { + + ASSERT( addressLength == NULL ); + + } + + // + // Set up the IRP for completion. + // + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = bytesCopied; + + return status; + +} // AfdSetupReceiveDatagramIrp + |