/*++

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