/*++ 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