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

1654 lines
43 KiB
C
Raw Blame History

This file contains invisible Unicode characters

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

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