|
|
/*++
Copyright (c) 1989-1999 Microsoft Corporation
Module Name:
receive.c
Abstract:
This module contains the code for passing on receive IRPs to TDI providers.
Author:
David Treadwell (davidtr) 13-Mar-1992
Revision History: Vadim Eydelman (vadime) 1998-1999 Minimal NT5.0 changes (keep in sync with rest)
--*/
#include "afdp.h"
NTSTATUS AfdRestartReceive ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context );
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGEAFD, AfdReceive )
#pragma alloc_text( PAGEAFD, AfdRestartReceive )
#pragma alloc_text( PAGEAFD, AfdReceiveEventHandler )
#pragma alloc_text( PAGEAFD, AfdReceiveExpeditedEventHandler )
#pragma alloc_text( PAGEAFD, AfdQueryReceiveInformation )
#endif
NTSTATUS FASTCALL AfdReceive ( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) { NTSTATUS status; AFD_LOCK_QUEUE_HANDLE lockHandle; PAFD_ENDPOINT endpoint; PAFD_CONNECTION connection; PTDI_REQUEST_RECEIVE receiveRequest = NULL; BOOLEAN peek; LARGE_INTEGER bytesExpected; BOOLEAN isDataOnConnection; BOOLEAN isExpeditedDataOnConnection; ULONG recvFlags; ULONG afdFlags; ULONG recvLength; ULONG bufferCount;
//
// Make sure that the endpoint is in the correct state.
//
endpoint = IrpSp->FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
//
// Datagram endpoints can be received on event if they are just bound
// Connection oriented endpoints must be connected.
//
if ( (endpoint->State != AfdEndpointStateConnected ) && (!IS_DGRAM_ENDPOINT(endpoint) || (endpoint->State!= AfdEndpointStateBound))) { if (IS_DGRAM_ENDPOINT(endpoint)) status = STATUS_INVALID_PARAMETER; else status = STATUS_INVALID_CONNECTION; goto complete; }
//
// If receive has been shut down or the endpoint aborted, fail.
//
if ( (endpoint->DisconnectMode & AFD_ABORTIVE_DISCONNECT) ) { status = STATUS_LOCAL_DISCONNECT; goto complete; }
if ( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) ) { status = STATUS_PIPE_DISCONNECTED; goto complete; }
//
// If this is an IOCTL_AFD_RECEIVE, then grab the parameters from the
// supplied AFD_RECV_INFO structure, build an MDL chain describing
// the WSABUF array, and attach the MDL chain to the IRP.
//
// If this is an IRP_MJ_READ IRP, just grab the length from the IRP
// and set the flags to zero.
//
if ( IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL ) {
#ifdef _WIN64
if (IoIs32bitProcess (Irp)) { PAFD_RECV_INFO32 recvInfo32; LPWSABUF32 bufferArray32;
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*recvInfo32) ) {
AFD_W4_INIT status = STATUS_SUCCESS; try {
//
// Validate the input structure if it comes from the user mode
// application
//
recvInfo32 = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; if( Irp->RequestorMode != KernelMode ) { ProbeForReadSmallStructure( recvInfo32, sizeof(*recvInfo32), PROBE_ALIGNMENT32(AFD_RECV_INFO32) );
}
//
// Make local copies of the embeded pointer and parameters
// that we will be using more than once in case malicios
// application attempts to change them while we are
// validating
//
recvFlags = recvInfo32->TdiFlags; afdFlags = recvInfo32->AfdFlags; bufferArray32 = UlongToPtr(recvInfo32->BufferArray); bufferCount = recvInfo32->BufferCount;
//
// Validate the receive flags & WSABUF parameters.
// Note that either TDI_RECEIVE_NORMAL or
// TDI_RECEIVE_EXPEDITED (but not both) must be set
// in the receive flags. And expedited can only
// be set if transport supports expedited data and
// endpoint is not set to inline mode.
//
if ( ( recvFlags & TDI_RECEIVE_EITHER ) == 0 || ( recvFlags & TDI_RECEIVE_EITHER ) == TDI_RECEIVE_EITHER ) {
//
// Invalid receive flags
//
status = STATUS_INVALID_PARAMETER; goto complete; } else if (( recvFlags & TDI_RECEIVE_EXPEDITED) != 0 && (!IS_TDI_EXPEDITED( endpoint ) || endpoint->InLine )) {
if (endpoint->InLine) { //
// Endpoint set inline, OOB data is reported as
// normal.
//
status = STATUS_INVALID_PARAMETER; } else { //
// Transport does not support expedited data
//
status = STATUS_NOT_SUPPORTED; } goto complete;
} else {
//
// Create the MDL chain describing the WSABUF array.
// This will also validate the buffer array and individual
// buffers
//
AFD_W4_INIT recvLength = 0; status = AfdAllocateMdlChain32( Irp, // Requestor mode passed along
bufferArray32, bufferCount, IoWriteAccess, &recvLength ); if (!NT_SUCCESS (status)) { goto complete; } }
} except ( AFD_EXCEPTION_FILTER (status) ) { ASSERT (NT_ERROR (status)); //
// Exception accessing input structure.
//
goto complete; }
} else { //
// Invalid input buffer length.
//
status = STATUS_INVALID_PARAMETER; goto complete; } } else #endif // _WIN64
{ PAFD_RECV_INFO recvInfo; LPWSABUF bufferArray;
//
// Sanity check.
//
ASSERT( IrpSp->Parameters.DeviceIoControl.IoControlCode==IOCTL_AFD_RECEIVE );
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*recvInfo) ) {
AFD_W4_INIT status = STATUS_SUCCESS; try {
//
// Validate the input structure if it comes from the user mode
// application
//
recvInfo = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; if( Irp->RequestorMode != KernelMode ) { ProbeForReadSmallStructure( recvInfo, sizeof(*recvInfo), PROBE_ALIGNMENT(AFD_RECV_INFO) );
}
//
// Make local copies of the embeded pointer and parameters
// that we will be using more than once in case malicios
// application attempts to change them while we are
// validating
//
recvFlags = recvInfo->TdiFlags; afdFlags = recvInfo->AfdFlags; bufferArray = recvInfo->BufferArray; bufferCount = recvInfo->BufferCount;
//
// Validate the receive flags & WSABUF parameters.
// Note that either TDI_RECEIVE_NORMAL or
// TDI_RECEIVE_EXPEDITED (but not both) must be set
// in the receive flags. And expedited can only
// be set if transport supports expedited data and
// endpoint is not set to inline mode.
//
if ( ( recvFlags & TDI_RECEIVE_EITHER ) == 0 || ( recvFlags & TDI_RECEIVE_EITHER ) == TDI_RECEIVE_EITHER ) {
//
// Invalid receive flags
//
status = STATUS_INVALID_PARAMETER; goto complete; } else if (( recvFlags & TDI_RECEIVE_EXPEDITED) != 0 && (!IS_TDI_EXPEDITED( endpoint ) || endpoint->InLine )) {
if (endpoint->InLine) { //
// Endpoint set inline, OOB data is reported as
// normal.
//
status = STATUS_INVALID_PARAMETER; } else { //
// Transport does not support expedited data
//
status = STATUS_NOT_SUPPORTED; } goto complete;
} else {
//
// Create the MDL chain describing the WSABUF array.
// This will also validate the buffer array and individual
// buffers
//
AFD_W4_INIT recvLength = 0; status = AfdAllocateMdlChain( Irp, // Requestor mode passed along
bufferArray, bufferCount, IoWriteAccess, &recvLength ); if( !NT_SUCCESS(status) ) { goto complete; }
}
} except ( AFD_EXCEPTION_FILTER (status) ) { ASSERT (NT_ERROR (status)); //
// Exception accessing input structure.
//
goto complete; }
} else { //
// Invalid input buffer length.
//
status = STATUS_INVALID_PARAMETER; goto complete; } }
if (IS_SAN_ENDPOINT(endpoint)) { IrpSp->MajorFunction = IRP_MJ_READ; IrpSp->Parameters.Read.Length = recvLength; return AfdSanRedirectRequest (Irp, IrpSp); }
} else {
ASSERT( IrpSp->MajorFunction == IRP_MJ_READ );
recvFlags = TDI_RECEIVE_NORMAL; afdFlags = AFD_OVERLAPPED; recvLength = IrpSp->Parameters.Read.Length;
ASSERT( FIELD_OFFSET( IO_STACK_LOCATION, Parameters.Read.Length ) == FIELD_OFFSET( IO_STACK_LOCATION, Parameters.DeviceIoControl.OutputBufferLength ) );
}
//
// If this is a datagram endpoint, format up a receive datagram request
// and pass it on to the TDI provider.
//
if ( IS_DGRAM_ENDPOINT(endpoint) ) { AFD_RECV_INFO recvInfo; recvInfo.TdiFlags = recvFlags; recvInfo.AfdFlags = afdFlags; IrpSp->Parameters.DeviceIoControl.InputBufferLength = recvLength; IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = &recvInfo; return AfdReceiveDatagram( Irp, IrpSp); }
//
// If this is an endpoint on a nonbufferring transport, use another
// routine to handle the request.
//
if ( !IS_TDI_BUFFERRING(endpoint) ) { return AfdBReceive( Irp, IrpSp, recvFlags, afdFlags, recvLength ); }
//
// Allocate a buffer for the receive request structure.
//
try { receiveRequest = AFD_ALLOCATE_POOL_WITH_QUOTA ( NonPagedPool, sizeof(TDI_REQUEST_RECEIVE), AFD_TDI_POOL_TAG ); // AFD_ALLOCATE_POOL_WITH_QUOTA macro sets POOL_RAISE_IF_ALLOCATION_FAILURE
} except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode (); goto complete; }
//
// Set up the receive request structure.
//
RtlZeroMemory( receiveRequest, sizeof(*receiveRequest) );
receiveRequest->ReceiveFlags = (USHORT)recvFlags;
//
// If this endpoint is set up for inline reception of expedited data,
// change the receive flags to use either normal or expedited data.
//
if ( endpoint->InLine ) { receiveRequest->ReceiveFlags |= TDI_RECEIVE_EITHER; }
//
// Determine whether this is a request to just peek at the data.
//
peek = (BOOLEAN)( (receiveRequest->ReceiveFlags & TDI_RECEIVE_PEEK) != 0 );
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
//
// Check if endpoint was cleaned-up and cancel the request.
//
if (endpoint->EndpointCleanedUp) { AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle); status = STATUS_CANCELLED; goto complete; }
connection = AFD_CONNECTION_FROM_ENDPOINT (endpoint); if (connection==NULL) { //
// connection might have been cleaned up by transmit file.
//
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle); status = STATUS_INVALID_CONNECTION; goto complete; }
ASSERT( connection->Type == AfdBlockTypeConnection );
isDataOnConnection = (BOOLEAN)IS_DATA_ON_CONNECTION( connection ); isExpeditedDataOnConnection = (BOOLEAN)IS_EXPEDITED_DATA_ON_CONNECTION( connection );
if ( endpoint->InLine ) {
//
// If the endpoint is nonblocking, check whether the receive can
// be performed immediately. Note that if the endpoint is set
// up for inline reception of expedited data we don't fail just
// yet--there may be expedited data available to be read.
//
if ( endpoint->NonBlocking && !( afdFlags & AFD_OVERLAPPED ) ) {
if ( !isDataOnConnection && !isExpeditedDataOnConnection && !connection->Aborted && !connection->DisconnectIndicated ) {
IF_DEBUG(RECEIVE) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdReceive: failing nonblocking IL receive, ind %ld, " "taken %ld, out %ld\n", connection->Common.Bufferring.ReceiveBytesIndicated.LowPart, connection->Common.Bufferring.ReceiveBytesTaken.LowPart, connection->Common.Bufferring.ReceiveBytesOutstanding.LowPart )); KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, " EXP ind %ld, taken %ld, out %ld\n", connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.LowPart, connection->Common.Bufferring.ReceiveExpeditedBytesTaken.LowPart, connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.LowPart )); }
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
status = STATUS_DEVICE_NOT_READY; goto complete; } }
//
// If this is a nonblocking endpoint for a message-oriented
// transport, limit the number of bytes that can be received to the
// amount that has been indicated. This prevents the receive
// from blocking in the case where only part of a message has been
// received.
//
if ( IS_MESSAGE_ENDPOINT(endpoint) && endpoint->NonBlocking ) {
LARGE_INTEGER expBytesExpected;
bytesExpected.QuadPart = connection->Common.Bufferring.ReceiveBytesIndicated.QuadPart - (connection->Common.Bufferring.ReceiveBytesTaken.QuadPart + connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart); ASSERT( bytesExpected.HighPart == 0 );
expBytesExpected.QuadPart = connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.QuadPart - (connection->Common.Bufferring.ReceiveExpeditedBytesTaken.QuadPart + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart); ASSERT( expBytesExpected.HighPart == 0 );
IF_DEBUG(RECEIVE) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdReceive: %lx normal bytes expected, %ld exp bytes expected", bytesExpected.LowPart, expBytesExpected.LowPart )); }
//
// If expedited data exists on the connection, use the lower
// count between the available expedited and normal receive
// data.
//
if ( (isExpeditedDataOnConnection && bytesExpected.LowPart > expBytesExpected.LowPart) || !isDataOnConnection ) { bytesExpected = expBytesExpected; }
//
// If the request is for more bytes than are available, cut back
// the number of bytes requested to what we know is actually
// available.
//
if ( recvLength > bytesExpected.LowPart ) { recvLength = bytesExpected.LowPart; } }
//
// Increment the count of posted receive bytes outstanding.
// This count is used for polling and nonblocking receives.
// Note that we do not increment this count if this is only
// a PEEK receive, since peeks do not actually take any data
// they should not affect whether data is available to be read
// on the endpoint.
//
IF_DEBUG(RECEIVE) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdReceive: conn %p for %ld bytes, ind %ld, " "taken %ld, out %ld %s\n", connection, recvLength, connection->Common.Bufferring.ReceiveBytesIndicated.LowPart, connection->Common.Bufferring.ReceiveBytesTaken.LowPart, connection->Common.Bufferring.ReceiveBytesOutstanding.LowPart, peek ? "PEEK" : "" )); KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, " EXP ind %ld, taken %ld, out %ld\n", connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.LowPart, connection->Common.Bufferring.ReceiveExpeditedBytesTaken.LowPart, connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.LowPart )); }
if ( !peek ) {
connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart = connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart + recvLength;
connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart = connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart + recvLength; } }
if ( !endpoint->InLine && (receiveRequest->ReceiveFlags & TDI_RECEIVE_NORMAL) != 0 ) {
//
// If the endpoint is nonblocking, check whether the receive can
// be performed immediately.
//
if ( endpoint->NonBlocking && !( afdFlags & AFD_OVERLAPPED ) ) {
if ( !isDataOnConnection && !connection->Aborted && !connection->DisconnectIndicated ) {
IF_DEBUG(RECEIVE) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdReceive: failing nonblocking receive, ind %ld, " "taken %ld, out %ld\n", connection->Common.Bufferring.ReceiveBytesIndicated.LowPart, connection->Common.Bufferring.ReceiveBytesTaken.LowPart, connection->Common.Bufferring.ReceiveBytesOutstanding.LowPart )); }
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
status = STATUS_DEVICE_NOT_READY; goto complete; } }
//
// If this is a nonblocking endpoint for a message-oriented
// transport, limit the number of bytes that can be received to the
// amount that has been indicated. This prevents the receive
// from blocking in the case where only part of a message has been
// received.
//
if ( IS_MESSAGE_ENDPOINT(endpoint) && endpoint->NonBlocking ) {
bytesExpected.QuadPart = connection->Common.Bufferring.ReceiveBytesIndicated.QuadPart - (connection->Common.Bufferring.ReceiveBytesTaken.QuadPart + connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart);
ASSERT( bytesExpected.HighPart == 0 );
//
// If the request is for more bytes than are available, cut back
// the number of bytes requested to what we know is actually
// available.
//
if ( recvLength > bytesExpected.LowPart ) { recvLength = bytesExpected.LowPart; } }
//
// Increment the count of posted receive bytes outstanding.
// This count is used for polling and nonblocking receives.
// Note that we do not increment this count if this is only
// a PEEK receive, since peeks do not actually take any data
// they should not affect whether data is available to be read
// on the endpoint.
//
IF_DEBUG(RECEIVE) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdReceive: conn %p for %ld bytes, ind %ld, " "taken %ld, out %ld %s\n", connection, recvLength, connection->Common.Bufferring.ReceiveBytesIndicated.LowPart, connection->Common.Bufferring.ReceiveBytesTaken.LowPart, connection->Common.Bufferring.ReceiveBytesOutstanding.LowPart, peek ? "PEEK" : "" )); }
if ( !peek ) {
connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart = connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart + recvLength; } }
if ( !endpoint->InLine && (receiveRequest->ReceiveFlags & TDI_RECEIVE_EXPEDITED) != 0 ) {
if ( endpoint->NonBlocking && !( afdFlags & AFD_OVERLAPPED ) && !isExpeditedDataOnConnection && !connection->Aborted && !connection->DisconnectIndicated ) {
IF_DEBUG(RECEIVE) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdReceive: failing nonblocking EXP receive, ind %ld, " "taken %ld, out %ld\n", connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.LowPart, connection->Common.Bufferring.ReceiveExpeditedBytesTaken.LowPart, connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.LowPart )); }
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
status = STATUS_DEVICE_NOT_READY; goto complete; }
//
// If this is a nonblocking endpoint for a message-oriented
// transport, limit the number of bytes that can be received to the
// amount that has been indicated. This prevents the receive
// from blocking in the case where only part of a message has been
// received.
//
if ( IS_MESSAGE_ENDPOINT(endpoint) && endpoint->NonBlocking && IS_EXPEDITED_DATA_ON_CONNECTION( connection ) ) {
bytesExpected.QuadPart = connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.QuadPart - (connection->Common.Bufferring.ReceiveExpeditedBytesTaken.QuadPart + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart);
ASSERT( bytesExpected.HighPart == 0 ); ASSERT( bytesExpected.LowPart != 0 );
//
// If the request is for more bytes than are available, cut back
// the number of bytes requested to what we know is actually
// available.
//
if ( recvLength > bytesExpected.LowPart ) { recvLength = bytesExpected.LowPart; } }
//
// Increment the count of posted expedited receive bytes
// outstanding. This count is used for polling and nonblocking
// receives. Note that we do not increment this count if this
// is only a PEEK receive.
//
IF_DEBUG(RECEIVE) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdReceive: conn %p for %ld bytes, ind %ld, " "taken %ld, out %ld EXP %s\n", connection, recvLength, connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.LowPart, connection->Common.Bufferring.ReceiveExpeditedBytesTaken.LowPart, connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.LowPart, peek ? "PEEK" : "" )); }
if ( !peek ) {
connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart = connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart + recvLength; } }
//
// Reference the connection so it can't go away
// even if transmit file tries to clean it up
//
REFERENCE_CONNECTION (connection);
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
//
// Build the TDI receive request.
//
TdiBuildReceive( Irp, connection->DeviceObject, connection->FileObject, AfdRestartReceive, connection, Irp->MdlAddress, receiveRequest->ReceiveFlags, recvLength );
//
// Save a pointer to the receive request structure so that we
// can free it in our restart routine.
//
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = receiveRequest; IrpSp->Parameters.DeviceIoControl.OutputBufferLength = recvLength;
//
// Call the transport to actually perform the connect operation.
//
return AfdIoCallDriver( endpoint, connection->DeviceObject, Irp );
complete:
if ( receiveRequest!=NULL ) { AFD_FREE_POOL( receiveRequest, AFD_TDI_POOL_TAG ); }
Irp->IoStatus.Information = 0; Irp->IoStatus.Status = status; IoCompleteRequest( Irp, AfdPriorityBoost );
return status;
} // AfdReceive
NTSTATUS AfdRestartReceive ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PAFD_ENDPOINT endpoint; PAFD_CONNECTION connection; PIO_STACK_LOCATION irpSp; LARGE_INTEGER actualBytes; LARGE_INTEGER requestedBytes; AFD_LOCK_QUEUE_HANDLE lockHandle; ULONG receiveFlags; ULONG eventMask; BOOLEAN expedited; PTDI_REQUEST_RECEIVE receiveRequest;
UNREFERENCED_PARAMETER (DeviceObject); connection = Context; ASSERT( connection->Type == AfdBlockTypeConnection );
endpoint = connection->Endpoint; ASSERT( endpoint->Type == AfdBlockTypeVcConnecting || endpoint->Type == AfdBlockTypeVcBoth ); ASSERT( IS_TDI_BUFFERRING(endpoint) );
irpSp = IoGetCurrentIrpStackLocation( Irp );
AfdCompleteOutstandingIrp( endpoint, Irp );
actualBytes.QuadPart = Irp->IoStatus.Information; requestedBytes.QuadPart = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
//
// Determine whether we received normal or expedited data.
//
receiveRequest = irpSp->Parameters.DeviceIoControl.Type3InputBuffer; receiveFlags = receiveRequest->ReceiveFlags;
if ( Irp->IoStatus.Status == STATUS_RECEIVE_EXPEDITED || Irp->IoStatus.Status == STATUS_RECEIVE_PARTIAL_EXPEDITED ) { expedited = TRUE; } else { expedited = FALSE; }
//
// Free the receive request structure.
//
AFD_FREE_POOL( receiveRequest, AFD_TDI_POOL_TAG );
//
// If this was a PEEK receive, don't update the counts of received
// data, just return.
//
if ( (receiveFlags & TDI_RECEIVE_PEEK) != 0 ) {
IF_DEBUG(RECEIVE) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdRestartReceive: IRP %p, endpoint %p, conn %p, " "status %X\n", Irp, endpoint, endpoint->Common.VcConnecting.Connection, Irp->IoStatus.Status )); KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, " %s data, PEEKed only.\n", expedited ? "expedited" : "normal" )); }
DEREFERENCE_CONNECTION (connection); return STATUS_SUCCESS; }
//
// Update the count of bytes actually received on the connection.
//
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
if( expedited ) { eventMask = endpoint->InLine ? (ULONG)~AFD_POLL_RECEIVE : (ULONG)~AFD_POLL_RECEIVE_EXPEDITED; } else { eventMask = (ULONG)~AFD_POLL_RECEIVE; }
endpoint->EventsActive &= eventMask;
IF_DEBUG(EVENT_SELECT) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdReceive: Endp %p, Active %lx\n", endpoint, endpoint->EventsActive )); }
if ( !expedited ) {
if ( actualBytes.LowPart == 0 ) { ASSERT( actualBytes.HighPart == 0 ); connection->VcZeroByteReceiveIndicated = FALSE; } else { connection->Common.Bufferring.ReceiveBytesTaken.QuadPart = actualBytes.QuadPart + connection->Common.Bufferring.ReceiveBytesTaken.QuadPart; }
//
// If the number taken exceeds the number indicated, then this
// receive got some unindicated bytes because the receive was
// posted when the indication arrived. If this is the case, set
// the amount indicated equal to the amount received.
//
if ( connection->Common.Bufferring.ReceiveBytesTaken.QuadPart > connection->Common.Bufferring.ReceiveBytesIndicated.QuadPart ) {
connection->Common.Bufferring.ReceiveBytesIndicated = connection->Common.Bufferring.ReceiveBytesTaken; }
//
// Decrement the count of outstanding receive bytes on this connection
// by the receive size that was requested.
//
connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart = connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart - requestedBytes.QuadPart;
//
// If the endpoint is inline, decrement the count of outstanding
// expedited bytes.
//
if ( endpoint->InLine ) { connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart = connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart - requestedBytes.QuadPart; }
if( connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart > 0 || ( endpoint->InLine && connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart > 0 ) ) {
AfdIndicateEventSelectEvent( endpoint, AFD_POLL_RECEIVE, STATUS_SUCCESS );
}
IF_DEBUG(RECEIVE) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdRestartReceive: IRP %p, endpoint %p, conn %p, " "status %lx\n", Irp, endpoint, connection, Irp->IoStatus.Status )); KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, " req. bytes %ld, actual %ld, ind %ld, " " taken %ld, out %ld\n", requestedBytes.LowPart, actualBytes.LowPart, connection->Common.Bufferring.ReceiveBytesIndicated.LowPart, connection->Common.Bufferring.ReceiveBytesTaken.LowPart, connection->Common.Bufferring.ReceiveBytesOutstanding.LowPart )); }
} else {
connection->Common.Bufferring.ReceiveExpeditedBytesTaken.QuadPart = actualBytes.QuadPart + connection->Common.Bufferring.ReceiveExpeditedBytesTaken.QuadPart;
//
// If the number taken exceeds the number indicated, then this
// receive got some unindicated bytes because the receive was
// posted when the indication arrived. If this is the case, set
// the amount indicated equal to the amount received.
//
if ( connection->Common.Bufferring.ReceiveExpeditedBytesTaken.QuadPart > connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.QuadPart ) {
connection->Common.Bufferring.ReceiveExpeditedBytesIndicated = connection->Common.Bufferring.ReceiveExpeditedBytesTaken; }
//
// Decrement the count of outstanding receive bytes on this connection
// by the receive size that was requested.
//
ASSERT( connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.LowPart > 0 || connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.HighPart > 0 || requestedBytes.LowPart == 0 );
connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart = connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart - requestedBytes.QuadPart;
//
// If the endpoint is inline, decrement the count of outstanding
// normal bytes.
//
if ( endpoint->InLine ) {
connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart = connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart - requestedBytes.QuadPart;
if( connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart > 0 || connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart > 0 ) {
AfdIndicateEventSelectEvent( endpoint, AFD_POLL_RECEIVE, STATUS_SUCCESS );
}
} else {
if( connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart > 0 ) {
AfdIndicateEventSelectEvent( endpoint, AFD_POLL_RECEIVE_EXPEDITED, STATUS_SUCCESS );
}
}
IF_DEBUG(RECEIVE) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdRestartReceive: (exp) IRP %p, endpoint %p, conn %p, " "status %X\n", Irp, endpoint, connection, Irp->IoStatus.Status )); KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, " req. bytes %ld, actual %ld, ind %ld, " " taken %ld, out %ld\n", requestedBytes.LowPart, actualBytes.LowPart, connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.LowPart, connection->Common.Bufferring.ReceiveExpeditedBytesTaken.LowPart, connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.LowPart )); }
}
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
//
// We are not signaling select here because no new data was added and
// unlike EventSelect we do NOT need to indicate again on receive
// completion if some data remains buffered.
//
DEREFERENCE_CONNECTION (connection);
//
// If pending has be returned for this irp then mark the current
// stack as pending.
//
if ( Irp->PendingReturned ) { IoMarkIrpPending(Irp); }
return STATUS_SUCCESS;
} // AfdRestartReceive
NTSTATUS AfdReceiveEventHandler ( IN PVOID TdiEventContext, IN CONNECTION_CONTEXT ConnectionContext, IN ULONG ReceiveFlags, IN ULONG BytesIndicated, IN ULONG BytesAvailable, OUT ULONG *BytesTaken, IN PVOID Tsdu, OUT PIRP *IoRequestPacket ) { PAFD_CONNECTION connection; PAFD_ENDPOINT endpoint; AFD_LOCK_QUEUE_HANDLE lockHandle; BOOLEAN result;
UNREFERENCED_PARAMETER (TdiEventContext); UNREFERENCED_PARAMETER (ReceiveFlags); UNREFERENCED_PARAMETER (BytesIndicated); UNREFERENCED_PARAMETER (Tsdu); UNREFERENCED_PARAMETER (IoRequestPacket);
connection = (PAFD_CONNECTION)ConnectionContext; ASSERT( connection != NULL );
CHECK_REFERENCE_CONNECTION (connection,result); if (!result) { return STATUS_DATA_NOT_ACCEPTED; } ASSERT( connection->Type == AfdBlockTypeConnection );
endpoint = connection->Endpoint; ASSERT( endpoint != NULL );
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting || endpoint->Type == AfdBlockTypeVcListening || endpoint->Type == AfdBlockTypeVcBoth); ASSERT( !connection->DisconnectIndicated ); ASSERT( !connection->Aborted ); ASSERT( IS_TDI_BUFFERRING(endpoint) );
//
// Bump the count of bytes indicated on the connection to account for
// the bytes indicated by this event.
//
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
//
// Check if connection was accepted and use accept endpoint instead
// of the listening. Note that accept cannot happen while we are
// holding listening endpoint spinlock, nor can endpoint change after
// the accept, so it is safe to release listening spinlock if
// we discover that endpoint was accepted.
//
if (((endpoint->Type & AfdBlockTypeVcListening) == AfdBlockTypeVcListening) && (connection->Endpoint != endpoint)) { AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
endpoint = connection->Endpoint; ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ); ASSERT( !IS_TDI_BUFFERRING(endpoint) ); ASSERT( !IS_VC_ENDPOINT (endpoint) );
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle); }
if ( BytesAvailable == 0 ) {
connection->VcZeroByteReceiveIndicated = TRUE;
} else {
connection->Common.Bufferring.ReceiveBytesIndicated.QuadPart = connection->Common.Bufferring.ReceiveBytesIndicated.QuadPart + BytesAvailable; }
IF_DEBUG(RECEIVE) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdReceiveEventHandler: conn %p, bytes %ld, " "ind %ld, taken %ld, out %ld\n", connection, BytesAvailable, connection->Common.Bufferring.ReceiveBytesIndicated.LowPart, connection->Common.Bufferring.ReceiveBytesTaken.LowPart, connection->Common.Bufferring.ReceiveBytesOutstanding.LowPart )); }
//
// If the receive side of the endpoint has been shut down, tell the
// provider that we took all the data and reset the connection.
// Also, account for these bytes in our count of bytes taken from
// the transport.
//
if ( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) != 0 ) {
#if DBG
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL, "AfdReceiveEventHandler: receive shutdown, " "%ld bytes, aborting endp %p\n", BytesAvailable, endpoint )); #endif
connection->Common.Bufferring.ReceiveBytesTaken.QuadPart = connection->Common.Bufferring.ReceiveBytesTaken.QuadPart + BytesAvailable;
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
*BytesTaken = BytesAvailable;
//
// Abort the connection. Note that if the abort attempt fails
// we can't do anything about it.
//
(VOID)AfdBeginAbort( connection );
DEREFERENCE_CONNECTION (connection); return STATUS_SUCCESS;
} else {
// Make sure connection was accepted/connected to prevent
// indication on listening endpoint
//
if (connection->State==AfdConnectionStateConnected) { ASSERT (endpoint->Type & AfdBlockTypeVcConnecting); AfdIndicateEventSelectEvent( endpoint, AFD_POLL_RECEIVE, STATUS_SUCCESS ); } AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
//
// Note to the TDI provider that we didn't take any of the data here.
//
// !!! needs bufferring for non-bufferring transports!
*BytesTaken = 0;
//
// If there are any outstanding poll IRPs for this endpoint/
// event, complete them.
//
// Make sure connection was accepted/connected to prevent
// indication on listening endpoint
//
if (connection->State==AfdConnectionStateConnected) { ASSERT (endpoint->Type & AfdBlockTypeVcConnecting); AfdIndicatePollEvent( endpoint, AFD_POLL_RECEIVE, STATUS_SUCCESS ); }
DEREFERENCE_CONNECTION (connection); return STATUS_DATA_NOT_ACCEPTED; }
} // AfdReceiveEventHandler
NTSTATUS AfdReceiveExpeditedEventHandler ( IN PVOID TdiEventContext, IN CONNECTION_CONTEXT ConnectionContext, IN ULONG ReceiveFlags, IN ULONG BytesIndicated, IN ULONG BytesAvailable, OUT ULONG *BytesTaken, IN PVOID Tsdu, OUT PIRP *IoRequestPacket ) { PAFD_CONNECTION connection; PAFD_ENDPOINT endpoint; AFD_LOCK_QUEUE_HANDLE lockHandle; BOOLEAN result;
UNREFERENCED_PARAMETER (TdiEventContext); UNREFERENCED_PARAMETER (ReceiveFlags); UNREFERENCED_PARAMETER (BytesIndicated); UNREFERENCED_PARAMETER (Tsdu); UNREFERENCED_PARAMETER (IoRequestPacket);
connection = (PAFD_CONNECTION)ConnectionContext; ASSERT( connection != NULL );
CHECK_REFERENCE_CONNECTION (connection, result); if (!result) { return STATUS_DATA_NOT_ACCEPTED; } ASSERT( connection->Type == AfdBlockTypeConnection );
endpoint = connection->Endpoint; ASSERT( endpoint != NULL );
//
// Bump the count of bytes indicated on the connection to account for
// the expedited bytes indicated by this event.
//
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
//
// Check if connection was accepted and use accept endpoint instead
// of the listening. Note that accept cannot happen while we are
// holding listening endpoint spinlock, nor can endpoint change after
// the accept, so it is safe to release listening spinlock if
// we discover that endpoint was accepted.
//
if (((endpoint->Type & AfdBlockTypeVcListening) == AfdBlockTypeVcListening) && (connection->Endpoint != endpoint)) { AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
endpoint = connection->Endpoint; ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ); ASSERT( !IS_TDI_BUFFERRING(endpoint) ); ASSERT( !IS_VC_ENDPOINT (endpoint) );
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle); }
connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.QuadPart = connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.QuadPart + BytesAvailable;
IF_DEBUG(RECEIVE) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdReceiveExpeditedEventHandler: conn %p, bytes %ld, " "ind %ld, taken %ld, out %ld, offset %ld\n", connection, BytesAvailable, connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.LowPart, connection->Common.Bufferring.ReceiveExpeditedBytesTaken.LowPart, connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.LowPart )); }
//
// If the receive side of the endpoint has been shut down, tell
// the provider that we took all the data. Also, account for these
// bytes in our count of bytes taken from the transport.
//
//
if ( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) != 0 ) {
IF_DEBUG(RECEIVE) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdReceiveExpeditedEventHandler: receive shutdown, " "%ld bytes dropped.\n", BytesAvailable )); }
connection->Common.Bufferring.ReceiveExpeditedBytesTaken.QuadPart = connection->Common.Bufferring.ReceiveExpeditedBytesTaken.QuadPart + BytesAvailable;
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
*BytesTaken = BytesAvailable;
//
// Abort the connection. Note that if the abort attempt fails
// we can't do anything about it.
//
(VOID)AfdBeginAbort( connection );
} else {
if (connection->State==AfdConnectionStateConnected) { ASSERT (endpoint->Type & AfdBlockTypeVcConnecting); AfdIndicateEventSelectEvent( endpoint, endpoint->InLine ? AFD_POLL_RECEIVE : AFD_POLL_RECEIVE_EXPEDITED, STATUS_SUCCESS ); } AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
//
// Note to the TDI provider that we didn't take any of the data here.
//
// !!! needs bufferring for non-bufferring transports!
*BytesTaken = 0;
//
// If there are any outstanding poll IRPs for this endpoint/
// event, complete them. Indicate this data as normal data if
// this endpoint is set up for inline reception of expedited
// data.
//
// Make sure connection was accepted/connected to prevent
// indication on listening endpoint
//
if (connection->State==AfdConnectionStateConnected) { ASSERT (endpoint->Type & AfdBlockTypeVcConnecting); AfdIndicatePollEvent( endpoint, endpoint->InLine ? AFD_POLL_RECEIVE : AFD_POLL_RECEIVE_EXPEDITED, STATUS_SUCCESS ); } }
DEREFERENCE_CONNECTION (connection); return STATUS_DATA_NOT_ACCEPTED;
} // AfdReceiveExpeditedEventHandler
NTSTATUS AfdQueryReceiveInformation ( IN PFILE_OBJECT FileObject, IN ULONG IoctlCode, IN KPROCESSOR_MODE RequestorMode, IN PVOID InputBuffer, IN ULONG InputBufferLength, IN PVOID OutputBuffer, IN ULONG OutputBufferLength, OUT PULONG_PTR Information ) { AFD_RECEIVE_INFORMATION receiveInformation; PAFD_ENDPOINT endpoint; AFD_LOCK_QUEUE_HANDLE lockHandle; LARGE_INTEGER result; PAFD_CONNECTION connection; NTSTATUS status;
UNREFERENCED_PARAMETER (IoctlCode); UNREFERENCED_PARAMETER (InputBuffer); UNREFERENCED_PARAMETER (InputBufferLength); *Information = 0;
//
// Make sure that the output buffer is large enough.
//
if ( OutputBufferLength < sizeof(receiveInformation) ) { return STATUS_BUFFER_TOO_SMALL; }
//
// If this endpoint has a connection block, use the connection block's
// information, else use the information from the endpoint itself.
//
endpoint = FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint );
if ( connection != NULL ) {
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting || endpoint->Type == AfdBlockTypeVcBoth ); ASSERT( connection->Type == AfdBlockTypeConnection );
if ( !IS_TDI_BUFFERRING(endpoint) ) {
receiveInformation.BytesAvailable = connection->VcBufferredReceiveBytes; receiveInformation.ExpeditedBytesAvailable = connection->VcBufferredExpeditedBytes;
} else {
//
// Determine the number of bytes available to be read.
//
result.QuadPart = connection->Common.Bufferring.ReceiveBytesIndicated.QuadPart - (connection->Common.Bufferring.ReceiveBytesTaken.QuadPart + connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart);
ASSERT( result.HighPart == 0 );
receiveInformation.BytesAvailable = result.LowPart;
//
// Determine the number of expedited bytes available to be read.
//
result.QuadPart = connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.QuadPart - (connection->Common.Bufferring.ReceiveExpeditedBytesTaken.QuadPart + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart);
ASSERT( result.HighPart == 0 );
receiveInformation.ExpeditedBytesAvailable = result.LowPart; }
} else {
//
// Determine the number of bytes available to be read.
//
if ( IS_DGRAM_ENDPOINT(endpoint) ) {
//
// Return the amount of bytes of datagrams that are
// bufferred on the endpoint.
//
if (endpoint->DgBufferredReceiveBytes>0) { receiveInformation.BytesAvailable = endpoint->DgBufferredReceiveBytes; } else { // Report one byte to the application to prompt it to
// read the data if zero-sized datagrams are available.
receiveInformation.BytesAvailable = endpoint->DgBufferredReceiveCount>0 ? 1 : 0; }
} else {
//
// This is an unconnected endpoint, hence no bytes are
// available to be read.
//
receiveInformation.BytesAvailable = 0; }
//
// Whether this is a datagram endpoint or just unconnected,
// there are no expedited bytes available.
//
receiveInformation.ExpeditedBytesAvailable = 0; }
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
AFD_W4_INIT status = STATUS_SUCCESS; try { //
// Validate the output structure if it comes from the user mode
// application
//
if (RequestorMode != KernelMode ) { ProbeAndWriteStructure (((PAFD_RECEIVE_INFORMATION)OutputBuffer), receiveInformation, AFD_RECEIVE_INFORMATION); } else { //
// Copy parameters back to application's memory
//
*((PAFD_RECEIVE_INFORMATION)OutputBuffer) = receiveInformation; }
} except( AFD_EXCEPTION_FILTER (status) ) { ASSERT (NT_ERROR (status)); return status; }
*Information = sizeof(AFD_RECEIVE_INFORMATION); return STATUS_SUCCESS;
} // AfdQueryReceiveInformation
|