/*++ Copyright (c) 1993-1999 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 AfdRestartReceiveDatagramWithUserIrp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ); 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, AfdRestartReceiveDatagramWithUserIrp ) #pragma alloc_text( PAGEAFD, AfdCancelReceiveDatagram ) #pragma alloc_text( PAGEAFD, AfdCleanupReceiveDatagramIrp ) #endif // // Macros to make the receive datagram code more maintainable. // #define AfdRecvDatagramInfo Others #define AfdRecvAddressMdl Argument1 #define AfdRecvAddressLenMdl Argument2 #define AfdRecvControlLenMdl Argument3 #define AfdRecvFlagsMdl Argument4 #define AfdRecvMsgControlMdl Tail.Overlay.DriverContext[0] #define AfdRecvLength Tail.Overlay.DriverContext[1] #define AfdRecvDgIndStatus DeviceIoControl.OutputBufferLength NTSTATUS FASTCALL AfdReceiveDatagram ( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) { NTSTATUS status; AFD_LOCK_QUEUE_HANDLE lockHandle; PAFD_ENDPOINT endpoint; PLIST_ENTRY listEntry; BOOLEAN peek; PAFD_BUFFER_HEADER afdBuffer; ULONG recvFlags; ULONG afdFlags; ULONG recvLength; PVOID addressPointer; PMDL addressMdl; PULONG addressLengthPointer; ULONG addressLength; PMDL lengthMdl; PVOID controlPointer; PMDL controlMdl; ULONG controlLength; PULONG controlLengthPointer; PMDL controlLengthMdl; PULONG flagsPointer; PMDL flagsMdl; ULONG bufferCount; // // Set up some local variables. // endpoint = IrpSp->FileObject->FsContext; ASSERT( IS_DGRAM_ENDPOINT(endpoint) ); Irp->IoStatus.Information = 0; addressMdl = NULL; lengthMdl = NULL; controlMdl = NULL; flagsMdl = NULL; controlLengthMdl = NULL; if (!IS_DGRAM_ENDPOINT(endpoint)) { status = STATUS_INVALID_PARAMETER; goto complete; } // // 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; } // // Make sure that the endpoint is in the correct state. // if ( endpoint->State != AfdEndpointStateBound && endpoint->State != AfdEndpointStateConnected) { status = STATUS_INVALID_PARAMETER; goto complete; } // // Do some special processing based on whether this is a receive // datagram IRP, a receive IRP, or a read IRP. // if ( IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL ) { if ( IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_AFD_RECEIVE_MESSAGE) { #ifdef _WIN64 if (IoIs32bitProcess (Irp)) { PAFD_RECV_MESSAGE_INFO32 msgInfo32; if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*msgInfo32) ) { status = STATUS_INVALID_PARAMETER; goto complete; } AFD_W4_INIT status = STATUS_SUCCESS; try { msgInfo32 = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; if( Irp->RequestorMode != KernelMode ) { ProbeForReadSmallStructure( msgInfo32, sizeof(*msgInfo32), PROBE_ALIGNMENT32 (AFD_RECV_MESSAGE_INFO32) ); } controlPointer = UlongToPtr(msgInfo32->ControlBuffer); controlLengthPointer = UlongToPtr(msgInfo32->ControlLength); flagsPointer = UlongToPtr(msgInfo32->MsgFlags); } except (AFD_EXCEPTION_FILTER (status)) { ASSERT (NT_ERROR (status)); goto complete; } } else #endif _WIN64 { PAFD_RECV_MESSAGE_INFO msgInfo; if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*msgInfo) ) { status = STATUS_INVALID_PARAMETER; goto complete; } AFD_W4_INIT status = STATUS_SUCCESS; try { msgInfo = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; if( Irp->RequestorMode != KernelMode ) { ProbeForReadSmallStructure( msgInfo, sizeof(*msgInfo), PROBE_ALIGNMENT (AFD_RECV_MESSAGE_INFO) ); } controlPointer = msgInfo->ControlBuffer; controlLengthPointer = msgInfo->ControlLength; flagsPointer = msgInfo->MsgFlags; } except (AFD_EXCEPTION_FILTER (status)) { ASSERT (NT_ERROR (status)); goto complete; } } AFD_W4_INIT ASSERT (status == STATUS_SUCCESS); try { // // Create a MDL describing the length buffer, then probe it // for write access. // flagsMdl = IoAllocateMdl( flagsPointer, // VirtualAddress sizeof(*flagsPointer), // Length FALSE, // SecondaryBuffer TRUE, // ChargeQuota NULL // Irp ); if( flagsMdl == NULL ) { status = STATUS_INSUFFICIENT_RESOURCES; goto complete; } MmProbeAndLockPages( flagsMdl, // MemoryDescriptorList Irp->RequestorMode, // AccessMode IoWriteAccess // Operation ); controlLengthMdl = IoAllocateMdl( controlLengthPointer, // VirtualAddress sizeof(*controlLengthPointer),// Length FALSE, // SecondaryBuffer TRUE, // ChargeQuota NULL // Irp ); if( controlLengthMdl == NULL ) { status = STATUS_INSUFFICIENT_RESOURCES; goto complete; } MmProbeAndLockPages( controlLengthMdl, // MemoryDescriptorList Irp->RequestorMode, // AccessMode IoWriteAccess // Operation ); controlLength = *controlLengthPointer; if (controlLength!=0) { // // Create a MDL describing the control buffer, then probe // it for write access. // controlMdl = IoAllocateMdl( controlPointer, // VirtualAddress controlLength, // Length FALSE, // SecondaryBuffer TRUE, // ChargeQuota NULL // Irp ); if( controlMdl == NULL ) { status = STATUS_INSUFFICIENT_RESOURCES; goto complete; } MmProbeAndLockPages( controlMdl, // MemoryDescriptorList Irp->RequestorMode, // AccessMode IoWriteAccess // Operation ); } } except( AFD_EXCEPTION_FILTER (status) ) { ASSERT (NT_ERROR (status)); goto complete; } // // Change the control code to continue processing of the regular // RecvFrom parameters. // IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_AFD_RECEIVE_DATAGRAM; } if ( IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_AFD_RECEIVE_DATAGRAM) { #ifdef _WIN64 if (IoIs32bitProcess (Irp)) { PAFD_RECV_DATAGRAM_INFO32 recvInfo32; LPWSABUF32 bufferArray32; // // Grab the parameters from the input structure. // 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_DATAGRAM_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; addressPointer = UlongToPtr(recvInfo32->Address); addressLengthPointer = UlongToPtr(recvInfo32->AddressLength); // // Validate the WSABUF parameters. // if ( bufferArray32 != NULL && bufferCount > 0 ) { // // Create the MDL chain describing the WSABUF array. // This will also validate the buffer array and individual // buffers // status = AfdAllocateMdlChain32( Irp, // Requestor mode passed along bufferArray32, bufferCount, IoWriteAccess, &recvLength ); if (!NT_SUCCESS (status)) { goto complete; } } else { // // Zero-length input buffer. This is OK for datagrams. // ASSERT( Irp->MdlAddress == NULL ); status = STATUS_SUCCESS; recvLength = 0; } } 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_DATAGRAM_INFO recvInfo; LPWSABUF bufferArray; // // Grab the parameters from the input structure. // 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_DATAGRAM_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; addressPointer = recvInfo->Address; addressLengthPointer = recvInfo->AddressLength; // // Validate the WSABUF parameters. // if ( bufferArray != NULL && bufferCount > 0 ) { // // Create the MDL chain describing the WSABUF array. // This will also validate the buffer array and individual // buffers // status = AfdAllocateMdlChain( Irp, // Requestor mode passed along bufferArray, bufferCount, IoWriteAccess, &recvLength ); if (!NT_SUCCESS (status)) { goto complete; } } else { // // Zero-length input buffer. This is OK for datagrams. // ASSERT( Irp->MdlAddress == NULL ); recvLength = 0; status = STATUS_SUCCESS; } } 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; } } // // 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 ); // // If only one of addressPointer or addressLength are NULL, then // fail the request. // if( (addressPointer == NULL) ^ (addressLengthPointer == NULL) ) { status = STATUS_INVALID_PARAMETER; 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( addressLengthPointer != NULL ); AFD_W4_INIT ASSERT (status == STATUS_SUCCESS); try { // // Create a MDL describing the length buffer, then probe it // for write access. // lengthMdl = IoAllocateMdl( addressLengthPointer, // VirtualAddress sizeof(*addressLengthPointer),// Length FALSE, // SecondaryBuffer TRUE, // ChargeQuota NULL // Irp ); if( lengthMdl == NULL ) { status = STATUS_INSUFFICIENT_RESOURCES; goto complete; } MmProbeAndLockPages( lengthMdl, // MemoryDescriptorList Irp->RequestorMode, // AccessMode IoWriteAccess // Operation ); // // Save length to a local so that malicious app // cannot break us by modifying the value in the middle of // us processing it below here. Also, we can use this pointer now // since we probed it above. // addressLength = *addressLengthPointer; // // Bomb off if the user is trying to do something bad, like // specify a zero-length address, or one that's unreasonably // huge. Here, we define "unreasonably huge" as MAXUSHORT // or greater because TDI address length field is USHORT. // if( addressLength == 0 || addressLength >= MAXUSHORT ) { status = STATUS_INVALID_PARAMETER; goto complete; } // // 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 ) { status = STATUS_INSUFFICIENT_RESOURCES; goto complete; } MmProbeAndLockPages( addressMdl, // MemoryDescriptorList Irp->RequestorMode, // AccessMode IoWriteAccess // Operation ); } except( AFD_EXCEPTION_FILTER (status) ) { ASSERT (NT_ERROR (status)); goto complete; } ASSERT( addressMdl != NULL ); ASSERT( lengthMdl != NULL ); } else { ASSERT( addressMdl == NULL ); ASSERT( lengthMdl == NULL ); } } else { ASSERT( (Irp->Flags & IRP_INPUT_OPERATION) == 0 ); // // Grab the input parameters from the IRP. // ASSERT( IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_AFD_RECEIVE ); recvFlags = ((PAFD_RECV_INFO)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->TdiFlags; afdFlags = ((PAFD_RECV_INFO)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->AfdFlags; recvLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; // // 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; recvLength = IrpSp->Parameters.Read.Length; peek = FALSE; } // // 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. // 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; } ASSERT( !( ( addressMdl == NULL ) ^ ( lengthMdl == NULL ) ) ); IrpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressMdl = addressMdl; IrpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressLenMdl = lengthMdl; IrpSp->Parameters.AfdRecvDatagramInfo.AfdRecvControlLenMdl = controlLengthMdl; IrpSp->Parameters.AfdRecvDatagramInfo.AfdRecvFlagsMdl = flagsMdl; Irp->AfdRecvMsgControlMdl = controlMdl; Irp->AfdRecvLength = UlongToPtr (recvLength); // // 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 ( ARE_DATAGRAMS_ON_ENDPOINT(endpoint) ) { // // 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_HEADER, BufferListEntry ); // // Prepare the user's IRP for completion. // if (NT_SUCCESS(afdBuffer->Status)) { PAFD_BUFFER buf = CONTAINING_RECORD (afdBuffer, AFD_BUFFER, Header); ASSERT (afdBuffer->BufferLength!=AfdBufferTagSize); status = AfdSetupReceiveDatagramIrp ( Irp, buf->Buffer, buf->DataLength, (PUCHAR)buf->Buffer+afdBuffer->DataLength, buf->DataOffset, buf->TdiInfo.RemoteAddress, buf->TdiInfo.RemoteAddressLength, buf->DatagramFlags ); } else { // // This is error report from the transport // (ICMP_PORT_UNREACHEABLE) // Irp->IoStatus.Status = afdBuffer->Status; ASSERT (afdBuffer->DataLength==0); Irp->IoStatus.Information = 0; status = AfdSetupReceiveDatagramIrp ( Irp, NULL, 0, NULL, 0, afdBuffer->TdiInfo.RemoteAddress, afdBuffer->TdiInfo.RemoteAddressLength, 0 ); } // // 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->DgBufferredReceiveCount--; endpoint->DgBufferredReceiveBytes -= afdBuffer->DataLength; endpoint->EventsActive &= ~AFD_POLL_RECEIVE; IF_DEBUG(EVENT_SELECT) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdReceiveDatagram: Endp %p, Active %lx\n", endpoint, endpoint->EventsActive )); } if( ARE_DATAGRAMS_ON_ENDPOINT(endpoint)) { AfdIndicateEventSelectEvent( endpoint, AFD_POLL_RECEIVE, STATUS_SUCCESS ); } else { // // Disable fast IO path to avoid performance penalty // of unneccessarily going through it. // if (!endpoint->NonBlocking) endpoint->DisableFastIoRecv = TRUE; } } // // We've set up all return information. Clean up and complete // the IRP. // AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); if ( !peek ) { AfdReturnBuffer( afdBuffer, endpoint->OwningProcess ); } UPDATE_ENDPOINT2 (endpoint, "AfdReceiveDatagram, completing with error/bytes: 0x%lX", NT_SUCCESS (Irp->IoStatus.Status) ? (ULONG)Irp->IoStatus.Information : (ULONG)Irp->IoStatus.Status); 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 && !( afdFlags & AFD_OVERLAPPED ) ) { endpoint->EventsActive &= ~AFD_POLL_RECEIVE; IF_DEBUG(EVENT_SELECT) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdReceiveDatagram: Endp %p, Active %lx\n", endpoint, endpoint->EventsActive )); } AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); 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 ); } // // Set up the cancellation routine in the IRP. If the IRP has already // been cancelled, just call the cancellation routine here. // IoSetCancelRoutine( Irp, AfdCancelReceiveDatagram ); if ( Irp->Cancel ) { RemoveEntryList( &Irp->Tail.Overlay.ListEntry ); if (IoSetCancelRoutine( Irp, NULL ) != NULL) { AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); status = STATUS_CANCELLED; goto complete; } // // The cancel routine will run and complete the irp. // Set Flink to NULL so it knows that IRP is not on the list. // Irp->Tail.Overlay.ListEntry.Flink = NULL; } IoMarkIrpPending( Irp ); AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); 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 ); } if (controlMdl != NULL) { if( (controlMdl->MdlFlags & MDL_PAGES_LOCKED) != 0 ) { MmUnlockPages( controlMdl ); } IoFreeMdl( controlMdl ); } if (controlLengthMdl != NULL) { if( (controlLengthMdl->MdlFlags & MDL_PAGES_LOCKED) != 0 ) { MmUnlockPages( controlLengthMdl ); } IoFreeMdl( controlLengthMdl ); } if (flagsMdl != NULL) { if( (flagsMdl->MdlFlags & MDL_PAGES_LOCKED) != 0 ) { MmUnlockPages( flagsMdl ); } IoFreeMdl( flagsMdl ); } UPDATE_ENDPOINT2 (endpoint, "AfdReceiveDatagram, completing with error 0x%lX", (ULONG)Irp->IoStatus.Status); 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: --*/ { AFD_LOCK_QUEUE_HANDLE lockHandle; PAFD_ENDPOINT endpoint; PAFD_BUFFER afdBuffer; BOOLEAN result; // // Reference the endpoint so that it doesn't go away beneath us. // endpoint = TdiEventContext; ASSERT( endpoint != NULL ); CHECK_REFERENCE_ENDPOINT (endpoint, result); if (!result) return STATUS_INSUFFICIENT_RESOURCES; ASSERT( IS_DGRAM_ENDPOINT(endpoint) ); #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. // AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle ); if ( (endpoint->State == AfdEndpointStateConnected && !endpoint->Common.Datagram.HalfConnect && !AfdAreTransportAddressesEqual( endpoint->Common.Datagram.RemoteAddress, endpoint->Common.Datagram.RemoteAddressLength, SourceAddress, SourceAddressLength, TRUE ))) { endpoint->Common.Datagram.AddressDrop = TRUE; AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); *BytesTaken = BytesAvailable; DEREFERENCE_ENDPOINT (endpoint); 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. // while ( !IsListEmpty( &endpoint->ReceiveDatagramIrpListHead ) ) { PLIST_ENTRY listEntry; PIRP irp; ASSERT( *BytesTaken == 0 ); ASSERT( endpoint->DgBufferredReceiveCount == 0 ); ASSERT( endpoint->DgBufferredReceiveBytes == 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 ); if ( IoSetCancelRoutine( irp, NULL ) == NULL ) { // // This IRP is about to be canceled. Look for another in the // list. Set the Flink to NULL so the cancel routine knows // it is not on the list. // irp->Tail.Overlay.ListEntry.Flink = NULL; continue; } AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); // // 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? // 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; // // 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. // (VOID)AfdSetupReceiveDatagramIrp ( irp, Tsdu, BytesAvailable, Options, OptionsLength, SourceAddress, SourceAddressLength, ReceiveDatagramFlags ); DEREFERENCE_ENDPOINT2 (endpoint, "AfdReceiveDatagramEventHandler, completing with error/bytes: 0x%lX", NT_SUCCESS (irp->IoStatus.Status) ? (ULONG)irp->IoStatus.Information : (ULONG)irp->IoStatus.Status); // // 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; } else { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (irp); // // Otherwise, just copy the address and options. // and remember the error code if any. // irpSp->Parameters.AfdRecvDgIndStatus = AfdSetupReceiveDatagramIrp ( irp, NULL, BytesAvailable, Options, OptionsLength, SourceAddress, SourceAddressLength, ReceiveDatagramFlags ); TdiBuildReceiveDatagram( irp, endpoint->AddressDeviceObject, endpoint->AddressFileObject, AfdRestartReceiveDatagramWithUserIrp, endpoint, irp->MdlAddress, BytesAvailable, NULL, NULL, 0 ); #ifdef _AFD_VARIABLE_STACK_ if ((irp=AfdCheckStackSizeAndRecordOutstandingIrp ( endpoint, endpoint->AddressDeviceObject, irp))!=NULL) { #else // _AFD_VARIABLE_STACK_ if (AfdRecordOutstandingIrp (endpoint, endpoint->AddressDeviceObject, irp)) { #endif // _AFD_VARIABLE_STACK_ // // 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; } else { return STATUS_SUCCESS; } } } // // 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->DgBufferredReceiveBytes >= endpoint->Common.Datagram.MaxBufferredReceiveBytes) || (endpoint->DgBufferredReceiveBytes==0 && (endpoint->DgBufferredReceiveCount*sizeof (AFD_BUFFER_TAG)) >= endpoint->Common.Datagram.MaxBufferredReceiveBytes) ) ) { // // If circular queueing is not enabled, then just drop the // datagram on the floor. // endpoint->Common.Datagram.BufferDrop = TRUE; if( !endpoint->Common.Datagram.CircularQueueing ) { AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); *BytesTaken = BytesAvailable; DEREFERENCE_ENDPOINT (endpoint); 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->DgBufferredReceiveBytes >= endpoint->Common.Datagram.MaxBufferredReceiveBytes || (endpoint->DgBufferredReceiveBytes==0 && (endpoint->DgBufferredReceiveCount*sizeof (AFD_BUFFER_TAG)) >= endpoint->Common.Datagram.MaxBufferredReceiveBytes) ) { PLIST_ENTRY listEntry; PAFD_BUFFER_HEADER afdBufferHdr; endpoint->DgBufferredReceiveCount--; listEntry = RemoveHeadList( &endpoint->ReceiveDatagramBufferListHead ); afdBufferHdr = CONTAINING_RECORD( listEntry, AFD_BUFFER_HEADER, BufferListEntry ); endpoint->DgBufferredReceiveBytes -= afdBufferHdr->DataLength; AfdReturnBuffer( afdBufferHdr, endpoint->OwningProcess ); } // // Proceed to accept the incoming packet. // } // // We're able to buffer the datagram. Now acquire a buffer of // appropriate size. // afdBuffer = AfdGetBuffer ( endpoint, BytesAvailable + ((ReceiveDatagramFlags & TDI_RECEIVE_CONTROL_INFO) ? OptionsLength : 0), SourceAddressLength, endpoint->OwningProcess ); if (afdBuffer==NULL) { endpoint->Common.Datagram.ResourceDrop = TRUE; AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); *BytesTaken = BytesAvailable; DEREFERENCE_ENDPOINT (endpoint); return STATUS_SUCCESS; } // // Store the address of the sender of the datagram. // RtlCopyMemory( afdBuffer->TdiInfo.RemoteAddress, SourceAddress, SourceAddressLength ); afdBuffer->TdiInfo.RemoteAddressLength = SourceAddressLength; // // Store what transport is supposed to return to us. // afdBuffer->DataLength = BytesAvailable; // // Note the receive flags. afdBuffer->DatagramFlags = ReceiveDatagramFlags; // // Copy control info into the buffer after the data and // store the length as data offset // if (ReceiveDatagramFlags & TDI_RECEIVE_CONTROL_INFO) { RtlMoveMemory ( (PUCHAR)afdBuffer->Buffer+BytesAvailable, Options, OptionsLength); afdBuffer->DataOffset = OptionsLength; } else { afdBuffer->DataOffset = 0; } // // If the entire datagram is being indicated to us, just copy it // here. // if ( BytesIndicated == BytesAvailable ) { PIRP irp; // // 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. // irp = NULL; while ( !IsListEmpty( &endpoint->PeekDatagramIrpListHead ) ) { PLIST_ENTRY listEntry; // // 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. // if ( IoSetCancelRoutine( irp, NULL ) == NULL ) { // // This IRP is about to be canceled. Look for another in the // list. Set the Flink to NULL so the cancel routine knows // it is not on the list. // irp->Tail.Overlay.ListEntry.Flink = NULL; irp = NULL; continue; } break; } // // 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 results in the IRP as though it is completed // by the transport. // afdBuffer->Irp->IoStatus.Status = STATUS_SUCCESS; afdBuffer->Irp->IoStatus.Information = BytesAvailable; // // Store success status do distinguish this from // ICMP rejects reported by ErrorEventHandler(Ex). // afdBuffer->Status = STATUS_SUCCESS; // // 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->DgBufferredReceiveCount++; endpoint->DgBufferredReceiveBytes += BytesAvailable; // // Reenable FAST IO on the endpoint to allow quick // copying of buffered data. // endpoint->DisableFastIoRecv = FALSE; // // All done. Release the lock and tell the provider that we // took all the data. // AfdIndicateEventSelectEvent( endpoint, AFD_POLL_RECEIVE, STATUS_SUCCESS ); AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); // // Indicate that it is possible to receive on the endpoint now. // AfdIndicatePollEvent( endpoint, AFD_POLL_RECEIVE, STATUS_SUCCESS ); // // If there was a peek IRP on the endpoint, complete it now. // if ( irp != NULL ) { // // Copy the datagram and source address to the IRP. This // prepares the IRP to be completed. // (VOID)AfdSetupReceiveDatagramIrp ( irp, Tsdu, BytesAvailable, Options, OptionsLength, SourceAddress, SourceAddressLength, ReceiveDatagramFlags ); IoCompleteRequest( irp, AfdPriorityBoost ); } *BytesTaken = BytesAvailable; DEREFERENCE_ENDPOINT (endpoint); return STATUS_SUCCESS; } else { // // 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, &lockHandle ); // // We need to remember the endpoint in the AFD buffer because we'll // need to access it in the completion routine. // afdBuffer->Context = endpoint; ASSERT (afdBuffer->Irp->MdlAddress==afdBuffer->Mdl); TdiBuildReceiveDatagram( afdBuffer->Irp, endpoint->AddressDeviceObject, endpoint->AddressFileObject, AfdRestartBufferReceiveDatagram, afdBuffer, afdBuffer->Irp->MdlAddress, BytesAvailable, NULL, NULL, 0 ); // // Make the next stack location current. Normally IoCallDriver would // do this, but since we're bypassing that, we do it directly. // IoSetNextIrpStackLocation( afdBuffer->Irp ); *IoRequestPacket = afdBuffer->Irp; *BytesTaken = 0; return STATUS_MORE_PROCESSING_REQUIRED; } } // AfdReceiveDatagramEventHandler NTSTATUS AfdRestartReceiveDatagramWithUserIrp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description: Handles completion of datagram receives that were started in the datagram indication handler and application IRP was available for direct transfer. Arguments: DeviceObject - not used. Irp - the IRP that is completing. Context - referenced endpoint pointer. Return Value: STATUS_SUCCESS to indicate that IO completion should continue. --*/ { PAFD_ENDPOINT endpoint = Context; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp); NTSTATUS indStatus = irpSp->Parameters.AfdRecvDgIndStatus; UNREFERENCED_PARAMETER (DeviceObject); ASSERT( IS_DGRAM_ENDPOINT(endpoint) ); AfdCompleteOutstandingIrp (endpoint, Irp); // // Pick the worst status // if ((Irp->IoStatus.Status==STATUS_SUCCESS) || (!NT_ERROR (Irp->IoStatus.Status) && NT_ERROR(indStatus)) || (NT_SUCCESS (Irp->IoStatus.Status) && !NT_SUCCESS (indStatus)) ) { Irp->IoStatus.Status = indStatus; } // // If pending has be returned for this irp then mark the current // stack as pending. // if ( Irp->PendingReturned ) { IoMarkIrpPending(Irp); } DEREFERENCE_ENDPOINT2 (endpoint, "AfdRestartReceiveDatagramWithUserIrp, error/bytes 0x%lX", NT_SUCCESS (Irp->IoStatus.Status) ? (ULONG)Irp->IoStatus.Information : (ULONG)Irp->IoStatus.Status); return STATUS_SUCCESS; } 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 - AfdBuffer structure. Return Value: STATUS_MORE_PROCESSING_REQUIRED to indicate to the IO system that we own the IRP and the IO system should stop processing the it. --*/ { PAFD_ENDPOINT endpoint; AFD_LOCK_QUEUE_HANDLE lockHandle; PAFD_BUFFER afdBuffer; PIRP pendedIrp; UNREFERENCED_PARAMETER (DeviceObject); ASSERT( NT_SUCCESS(Irp->IoStatus.Status) ); afdBuffer = Context; ASSERT (IS_VALID_AFD_BUFFER (afdBuffer)); ASSERT (afdBuffer->DataOffset==0 || (afdBuffer->DatagramFlags & TDI_RECEIVE_CONTROL_INFO)); endpoint = afdBuffer->Context; ASSERT( IS_DGRAM_ENDPOINT(endpoint) ); // // If the IO failed, then just return the AFD buffer to our buffer // pool. // if ( !NT_SUCCESS(Irp->IoStatus.Status) ) { AfdReturnBuffer( &afdBuffer->Header, endpoint->OwningProcess ); AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle ); endpoint->Common.Datagram.ErrorDrop = TRUE; AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); DEREFERENCE_ENDPOINT2 (endpoint, "AfdRestartBufferReceiveDatagram, status: 0x%lX", Irp->IoStatus.Status); return STATUS_MORE_PROCESSING_REQUIRED; } // // Make sure transport did not lie to us in indication handler. // ASSERT (afdBuffer->DataLength == (ULONG)Irp->IoStatus.Information); // // If there are any pended IRPs on the endpoint, complete as // appropriate with the new information. // AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle ); while ( !IsListEmpty( &endpoint->ReceiveDatagramIrpListHead ) ) { PLIST_ENTRY listEntry; // // There was a pended receive datagram IRP. Remove it from the // head of the list. // listEntry = RemoveHeadList( &endpoint->ReceiveDatagramIrpListHead ); // // 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 ); // // Reset the cancel routine in the IRP. The IRP is no // longer cancellable, since we're about to complete it. // if ( IoSetCancelRoutine( pendedIrp, NULL ) == NULL ) { // // This IRP is about to be canceled. Look for another in the // list. Set the Flink to NULL so the cancel routine knows // it is not on the list. // pendedIrp->Tail.Overlay.ListEntry.Flink = NULL; pendedIrp = NULL; continue; } AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); // // Set up the user's IRP for completion. // (VOID)AfdSetupReceiveDatagramIrp ( pendedIrp, afdBuffer->Buffer, afdBuffer->DataLength, (PUCHAR)afdBuffer->Buffer+afdBuffer->DataLength, afdBuffer->DataOffset, afdBuffer->TdiInfo.RemoteAddress, afdBuffer->TdiInfo.RemoteAddressLength, afdBuffer->DatagramFlags ); // // 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. // AfdReturnBuffer( &afdBuffer->Header, endpoint->OwningProcess ); DEREFERENCE_ENDPOINT2 (endpoint, "AfdRestartBufferReceiveDatagram, completing IRP with 0x%lX bytes", (ULONG)pendedIrp->IoStatus.Information); IoCompleteRequest( pendedIrp, AfdPriorityBoost ); return STATUS_MORE_PROCESSING_REQUIRED; } // // If there are any pended peek IRPs on the endpoint, complete // one with this datagram. // pendedIrp = NULL; while ( !IsListEmpty( &endpoint->PeekDatagramIrpListHead ) ) { PLIST_ENTRY listEntry; // // 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 ); // // Reset the cancel routine in the IRP. The IRP is no // longer cancellable, since we're about to complete it. // if ( IoSetCancelRoutine( pendedIrp, NULL ) == NULL ) { // // This IRP is about to be canceled. Look for another in the // list. Set the Flink to NULL so the cancel routine knows // it is not on the list. // pendedIrp->Tail.Overlay.ListEntry.Flink = NULL; pendedIrp = NULL; continue; } // // Set up the user's IRP for completion. // (VOID)AfdSetupReceiveDatagramIrp ( pendedIrp, afdBuffer->Buffer, afdBuffer->DataLength, (PUCHAR)afdBuffer->Buffer+afdBuffer->DataLength, afdBuffer->DataOffset, afdBuffer->TdiInfo.RemoteAddress, afdBuffer->TdiInfo.RemoteAddressLength, afdBuffer->DatagramFlags ); // // Don't complete the pended peek IRP yet, since we still hold // locks. Wait until it is safe to release the locks. // break; } // // Store success status do distinguish this from // ICMP rejects reported by ErrorEventHandler(Ex). // afdBuffer->Status = STATUS_SUCCESS; // // 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->DgBufferredReceiveCount++; endpoint->DgBufferredReceiveBytes += afdBuffer->DataLength; // // Reenable FAST IO on the endpoint to allow quick // copying of buffered data. // endpoint->DisableFastIoRecv = FALSE; // // Release locks and indicate that there are bufferred datagrams // on the endpoint. // AfdIndicateEventSelectEvent( endpoint, AFD_POLL_RECEIVE, STATUS_SUCCESS ); AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); AfdIndicatePollEvent( endpoint, AFD_POLL_RECEIVE, 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. // DEREFERENCE_ENDPOINT (endpoint); 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; AFD_LOCK_QUEUE_HANDLE lockHandle; UNREFERENCED_PARAMETER (DeviceObject); // // Get the endpoint pointer from our IRP stack location. // irpSp = IoGetCurrentIrpStackLocation( Irp ); endpoint = irpSp->FileObject->FsContext; ASSERT( IS_DGRAM_ENDPOINT(endpoint) ); // // 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 or the Flink is NULL // if we are getting called here--anybody that removes the IRP from // the list must reset the cancel routine to NULL and set the // Flink to NULL before releasing the endpoint spin lock. // AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle ); if (Irp->Tail.Overlay.ListEntry.Flink != NULL) { RemoveEntryList( &Irp->Tail.Overlay.ListEntry ); } AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); // // 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 BOOLEAN AfdCleanupReceiveDatagramIrp( IN PIRP Irp ) /*++ Routine Description: Performs any cleanup specific to receive datagram IRPs. Arguments: Irp - the IRP to cleanup. Return Value: TRUE - complete IRP, FALSE - leave alone. 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.AfdRecvAddressMdl; if( mdl != NULL ) { irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressMdl = NULL; MmUnlockPages( mdl ); IoFreeMdl( mdl ); } mdl = (PMDL)irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressLenMdl; if( mdl != NULL ) { irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressLenMdl = NULL; MmUnlockPages( mdl ); IoFreeMdl( mdl ); } mdl = (PMDL)irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvControlLenMdl; if( mdl != NULL ) { irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvControlLenMdl = NULL; MmUnlockPages( mdl ); IoFreeMdl( mdl ); } mdl = (PMDL)irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvFlagsMdl; if( mdl != NULL ) { irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvFlagsMdl = NULL; MmUnlockPages( mdl ); IoFreeMdl( mdl ); } mdl = (PMDL)Irp->AfdRecvMsgControlMdl; if( mdl != NULL ) { Irp->AfdRecvMsgControlMdl = NULL; MmUnlockPages( mdl ); IoFreeMdl( mdl ); } return TRUE; } // AfdCleanupReceiveDatagramIrp NTSTATUS AfdSetupReceiveDatagramIrp ( IN PIRP Irp, IN PVOID DatagramBuffer OPTIONAL, IN ULONG DatagramLength, IN PVOID ControlBuffer OPTIONAL, IN ULONG ControlLength, IN PVOID SourceAddress OPTIONAL, IN ULONG SourceAddressLength, IN ULONG TdiReceiveFlags ) /*++ 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. 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; BOOLEAN dataOverflow = FALSE; BOOLEAN controlOverflow = FALSE; // // 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 ); // // 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 ) ) { ULONG bytesCopied = 0; if ( Irp->MdlAddress == NULL ) { if ( DatagramLength != 0 ) { status = STATUS_BUFFER_OVERFLOW; } else { status = STATUS_SUCCESS; } } else { status = AfdMapMdlChain (Irp->MdlAddress); if (NT_SUCCESS (status)) { status = TdiCopyBufferToMdl( DatagramBuffer, 0, DatagramLength, Irp->MdlAddress, 0, &bytesCopied ); } } Irp->IoStatus.Information = 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; if (DatagramLength>PtrToUlong (Irp->AfdRecvLength)) { status = STATUS_BUFFER_OVERFLOW; } } if (status==STATUS_BUFFER_OVERFLOW) { dataOverflow = TRUE; } if( irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressMdl != NULL ) { PMDL addressMdl = irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressMdl; PMDL addressLenMdl = irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressLenMdl; irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressMdl = NULL; irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressLenMdl = NULL; ASSERT( addressMdl->Next == NULL ); ASSERT( ( addressMdl->MdlFlags & MDL_PAGES_LOCKED ) != 0 ); ASSERT( MmGetMdlByteCount (addressMdl) > 0 ); ASSERT( addressLenMdl != NULL ); ASSERT( addressLenMdl->Next == NULL ); ASSERT( ( addressLenMdl->MdlFlags & MDL_PAGES_LOCKED ) != 0 ); ASSERT( MmGetMdlByteCount (addressLenMdl)==sizeof (ULONG) ); if ((NT_SUCCESS (status) || status==STATUS_BUFFER_OVERFLOW || status==STATUS_PORT_UNREACHABLE) && ARGUMENT_PRESENT (SourceAddress)) { PVOID dst; PTRANSPORT_ADDRESS tdiAddress; // // Extract the real SOCKADDR structure from the TDI address. // This duplicates MSAFD.DLL's SockBuildSockaddr() function. // C_ASSERT( sizeof(tdiAddress->Address[0].AddressType) == sizeof(u_short) ); C_ASSERT( FIELD_OFFSET( TA_ADDRESS, AddressLength ) == 0 ); C_ASSERT( FIELD_OFFSET( TA_ADDRESS, AddressType ) == sizeof(USHORT) ); C_ASSERT( FIELD_OFFSET( TRANSPORT_ADDRESS, Address ) == sizeof(int) ); tdiAddress = SourceAddress; 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. // if (SourceAddressLength>MmGetMdlByteCount (addressMdl)) { status = STATUS_BUFFER_TOO_SMALL; } else { dst = MmGetSystemAddressForMdlSafe (addressMdl, LowPagePriority); if (dst!=NULL) { PULONG dstU; RtlMoveMemory (dst, SourceAddress, SourceAddressLength); // // Copy succeeded, return the length as well. // dstU = MmGetSystemAddressForMdlSafe (addressLenMdl, LowPagePriority); if (dstU!=NULL) { *dstU = SourceAddressLength; } else { status = STATUS_INSUFFICIENT_RESOURCES; } } else { status = STATUS_INSUFFICIENT_RESOURCES; } } } MmUnlockPages( addressMdl ); IoFreeMdl( addressMdl ); MmUnlockPages( addressLenMdl ); IoFreeMdl( addressLenMdl ); } else { ASSERT( irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressLenMdl == NULL ); } if (irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvControlLenMdl!=NULL) { PMDL controlMdl = Irp->AfdRecvMsgControlMdl; PMDL controlLenMdl = irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvControlLenMdl; Irp->AfdRecvMsgControlMdl = NULL; irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvControlLenMdl = NULL; ASSERT( irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvFlagsMdl != NULL ); ASSERT( ( controlLenMdl->MdlFlags & MDL_PAGES_LOCKED ) != 0 ); ASSERT( MmGetMdlByteCount (controlLenMdl) == sizeof (ULONG) ); // // We still need to NULL the length even if no control data was delivered. // if (!NT_ERROR (status)) { PULONG dstU; dstU = MmGetSystemAddressForMdlSafe (controlLenMdl, LowPagePriority); if (dstU!=NULL) { if ((TdiReceiveFlags & TDI_RECEIVE_CONTROL_INFO)==0) { ControlLength = 0; } #ifdef _WIN64 else if (IoIs32bitProcess (Irp)) { ControlLength = AfdComputeCMSGLength32 ( ControlBuffer, ControlLength); } #endif //_WIN64 *dstU = ControlLength; } else { status = STATUS_INSUFFICIENT_RESOURCES; } } // // Ignore control data in case of error or if flag indicating // that data is in proper format is not set. // if (!NT_ERROR (status) && ControlLength!=0) { if (controlMdl==NULL) { controlOverflow = TRUE; status = STATUS_BUFFER_OVERFLOW; } else { PVOID dst; // // Copy control info if app needs them (WSARecvMsg). // if (ControlLength>MmGetMdlByteCount (controlMdl)) { ControlLength = MmGetMdlByteCount (controlMdl); controlOverflow = TRUE; status = STATUS_BUFFER_OVERFLOW; } dst = MmGetSystemAddressForMdlSafe (controlMdl, LowPagePriority); if (dst!=NULL) { #ifdef _WIN64 if (IoIs32bitProcess (Irp)) { AfdCopyCMSGBuffer32 (dst, ControlBuffer, ControlLength); } else #endif //_WIN64 { RtlMoveMemory (dst, ControlBuffer, ControlLength); } } else { status = STATUS_INSUFFICIENT_RESOURCES; } } } if (controlMdl!=NULL) { ASSERT( controlMdl->Next == NULL ); ASSERT( ( controlMdl->MdlFlags & MDL_PAGES_LOCKED ) != 0 ); ASSERT( MmGetMdlByteCount (controlMdl) > 0 ); MmUnlockPages (controlMdl); IoFreeMdl (controlMdl); } MmUnlockPages (controlLenMdl); IoFreeMdl (controlLenMdl); } else { ASSERT (Irp->AfdRecvMsgControlMdl==NULL); } if (irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvFlagsMdl!=NULL) { PMDL flagsMdl = irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvFlagsMdl; irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvFlagsMdl = NULL; ASSERT( flagsMdl->Next == NULL ); ASSERT( ( flagsMdl->MdlFlags & MDL_PAGES_LOCKED ) != 0 ); ASSERT( MmGetMdlByteCount (flagsMdl)==sizeof (ULONG) ); if (!NT_ERROR (status)) { PULONG dst; dst = MmGetSystemAddressForMdlSafe (flagsMdl, LowPagePriority); if (dst!=NULL) { ULONG flags = 0; if (TdiReceiveFlags & TDI_RECEIVE_BROADCAST) flags |= MSG_BCAST; if (TdiReceiveFlags & TDI_RECEIVE_MULTICAST) flags |= MSG_MCAST; if (dataOverflow) flags |= MSG_TRUNC; if (controlOverflow) flags |= MSG_CTRUNC; *dst = flags; } else { status = STATUS_INSUFFICIENT_RESOURCES; } } MmUnlockPages (flagsMdl); IoFreeMdl (flagsMdl); } // // Set up the IRP for completion. // Irp->IoStatus.Status = status; return status; } // AfdSetupReceiveDatagramIrp