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