/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    socket.c

Abstract:

    This module implements socket file object for ws2ifsl.sys driver.

Author:

    Vadim Eydelman (VadimE)    Dec-1996

Revision History:

    Vadim Eydelman (VadimE)    Oct-1997, rewrite to properly handle IRP
                                        cancellation
--*/

#include "precomp.h"



VOID
SetSocketContext (
    IN PFILE_OBJECT     SocketFile,
    IN KPROCESSOR_MODE  RequestorMode,
    IN PVOID            InputBuffer,
    IN ULONG            InputBufferLength,
    OUT PVOID           OutputBuffer,
    IN ULONG            OutputBufferLength,
    OUT PIO_STATUS_BLOCK IoStatus
    );


VOID
CompletePvdRequest (
    IN PFILE_OBJECT     SocketFile,
    IN KPROCESSOR_MODE  RequestorMode,
    IN PVOID            InputBuffer,
    IN ULONG            InputBufferLength,
    OUT PVOID           OutputBuffer,
    IN ULONG            OutputBufferLength,
    OUT PIO_STATUS_BLOCK IoStatus
    );

VOID
ProcessedCancelRoutine (
        IN PDEVICE_OBJECT       DeviceObject,
        IN PIRP                         Irp
    );

PIRP
GetProcessedRequest (
    PIFSL_SOCKET_CTX    SocketCtx,
    ULONG               UniqueId
    );

VOID
CleanupProcessedRequests (
    PIFSL_SOCKET_CTX    SocketCtx,
    PLIST_ENTRY         IrpList
    );


VOID
CancelSocketIo (
    PFILE_OBJECT    SocketFile
    );

PFILE_OBJECT
GetSocketProcessReference (
    IN  PIFSL_SOCKET_CTX    SocketCtx
    );

PFILE_OBJECT
SetSocketProcessReference (
    IN  PIFSL_SOCKET_CTX    SocketCtx,
    IN  PFILE_OBJECT        NewProcessFile,
    IN  PVOID               NewDllContext
    );

NTSTATUS
CompleteTargetQuery (
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, CreateSocketFile)
#pragma alloc_text(PAGE, CleanupSocketFile)
#pragma alloc_text(PAGE, CloseSocketFile)
#pragma alloc_text(PAGE, DoSocketReadWrite)
#pragma alloc_text(PAGE, DoSocketAfdIoctl)
#pragma alloc_text(PAGE, SetSocketContext)
#pragma alloc_text(PAGE, CompleteDrvRequest)
#pragma alloc_text(PAGE, CompletePvdRequest)
#pragma alloc_text(PAGE, SocketPnPTargetQuery)
//#pragma alloc_text (PAGE, CompleteTargetQuery) - should never be paged. 
#endif

ULONG                  SocketIoctlCodeMap[2] = {
#if WS2IFSL_IOCTL_FUNCTION(SOCKET,IOCTL_WS2IFSL_SET_SOCKET_CONTEXT)!=0
#error Mismatch between IOCTL function code and SocketIoControlMap
#endif
    IOCTL_WS2IFSL_SET_SOCKET_CONTEXT,
#if WS2IFSL_IOCTL_FUNCTION(SOCKET,IOCTL_WS2IFSL_COMPLETE_PVD_REQ)!=1
#error Mismatch between IOCTL function code and SocketIoControlMap
#endif
    IOCTL_WS2IFSL_COMPLETE_PVD_REQ
};

PSOCKET_DEVICE_CONTROL SocketIoControlMap[2] = {
    SetSocketContext,
    CompletePvdRequest
};


#define GenerateUniqueId(curId) \
    ((ULONG)InterlockedIncrement (&(curId)))


NTSTATUS
CreateSocketFile (
    IN PFILE_OBJECT                 SocketFile,
    IN KPROCESSOR_MODE              RequestorMode,
    IN PFILE_FULL_EA_INFORMATION    eaInfo
    )
/*++

Routine Description:

    Allocates and initializes socket file context structure.

Arguments:
    SocketFile - socket file object
    eaInfo     - EA for socket file

Return Value:

    STATUS_SUCCESS  - operation completed OK
    STATUS_INSUFFICIENT_RESOURCES - not enough memory to allocate context
--*/
{
    NTSTATUS            status = STATUS_SUCCESS;
    PIFSL_SOCKET_CTX    SocketCtx;
    HANDLE              hProcessFile;
    PFILE_OBJECT        ProcessFile;
    PVOID               DllContext;

    PAGED_CODE ();

    if (eaInfo->EaValueLength!=WS2IFSL_SOCKET_EA_VALUE_LENGTH) {
        WsPrint (DBG_SOCKET|DBG_FAILURES,
            ("WS2IFSL-%04lx CreateSocketFile: Invalid ea info size (%ld)"
             " for process file %p.\n",
             PsGetCurrentProcessId(),
             eaInfo->EaValueLength,
             SocketFile));
        return STATUS_INVALID_PARAMETER;
    }

    hProcessFile = GET_WS2IFSL_SOCKET_EA_VALUE(eaInfo)->ProcessFile;
    DllContext = GET_WS2IFSL_SOCKET_EA_VALUE(eaInfo)->DllContext;
    // Get reference to the process file with which this context is associated
    status = ObReferenceObjectByHandle(
                 hProcessFile,
                 FILE_ALL_ACCESS,
                 *IoFileObjectType,
                 RequestorMode,
                 (PVOID *)&ProcessFile,
                 NULL
                 );
    if (NT_SUCCESS (status)) {
        // Verify that the file pointer is really our driver's process file
        // and that it created for the current process
        if ((IoGetRelatedDeviceObject (ProcessFile)
                        ==DeviceObject)
                && ((*((PULONG)ProcessFile->FsContext))
                        ==PROCESS_FILE_EANAME_TAG)
                && (((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId
                        ==PsGetCurrentProcessId())) {
            // Allocate socket context and charge it to the process
            try {
                SocketCtx = (PIFSL_SOCKET_CTX) ExAllocatePoolWithQuotaTag (
                                                    NonPagedPool,
                                                    sizeof (IFSL_SOCKET_CTX),
                                                    SOCKET_FILE_CONTEXT_TAG);
            }
            except (EXCEPTION_EXECUTE_HANDLER) {
                SocketCtx = NULL;
                status = GetExceptionCode ();
            }

            if (SocketCtx!=NULL) {
                WsProcessPrint ((PIFSL_PROCESS_CTX)ProcessFile->FsContext, DBG_SOCKET,
                    ("WS2IFSL-%04lx CreateSocketFile: Created socket %p (ctx:%p)\n",
                        ((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId, SocketFile, SocketCtx));
                // Initialize socket context structure
                SocketCtx->EANameTag = SOCKET_FILE_EANAME_TAG;
                SocketCtx->DllContext = DllContext;
                SocketCtx->ProcessRef = ProcessFile;
                InitializeListHead (&SocketCtx->ProcessedIrps);
                KeInitializeSpinLock (&SocketCtx->SpinLock);
                SocketCtx->CancelCtx = NULL;
                SocketCtx->IrpId = 0;

                // Associate socket context with socket file
                SocketFile->FsContext = SocketCtx;

                return status;
            }
            else {
                WsProcessPrint ((PIFSL_PROCESS_CTX)ProcessFile->FsContext, DBG_FAILURES|DBG_SOCKET,
                    ("WS2IFSL-%04lx CreateSocketFile: Could not allocate socket context\n",
                        ((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId));
                if (NT_SUCCESS (status)) {
                    ASSERT (FALSE);
                    status = STATUS_INSUFFICIENT_RESOURCES;
                }
            }
        }
        else {
            // Handle refers to random file object
            WsPrint (DBG_SOCKET|DBG_FAILURES,
                ("WS2IFSL-%04lx CreateSocketFile: Procees file handle %p (File:%p)"
                 " is not valid\n",
                 PsGetCurrentProcessId(),
                 ProcessFile, hProcessFile));
            status = STATUS_INVALID_PARAMETER;
        }
        ObDereferenceObject (ProcessFile);
    }
    else {
        WsPrint (DBG_SOCKET|DBG_FAILURES,
            ("WS2IFSL-%04lx CreateSocketFile: Could not get process file from handle %p,"
             " status:%lx.\n",
             PsGetCurrentProcessId(),
             hProcessFile,
             status));
    }

    return status;
} // CreateSocketFile


NTSTATUS
CleanupSocketFile (
    IN PFILE_OBJECT SocketFile,
    IN PIRP         Irp
    )
/*++

Routine Description:

    Initiates socket file cleanup in context of current process.

Arguments:
    SocketFile  - socket file object
    Irp         - cleanup request

Return Value:

    STATUS_PENDING  - operation initiated OK
    STATUS_INVALID_HANDLE - socket has not been initialized
                        in current process
--*/
{
    NTSTATUS                status;
    PIFSL_SOCKET_CTX        SocketCtx;
    PFILE_OBJECT            ProcessFile;
    LIST_ENTRY              irpList;
    PIFSL_CANCEL_CTX        cancelCtx;

    PAGED_CODE ();
    SocketCtx = SocketFile->FsContext;
    ProcessFile = GetSocketProcessReference (SocketCtx);
    WsProcessPrint ((PIFSL_PROCESS_CTX)SocketCtx->ProcessRef->FsContext, DBG_SOCKET,
        ("WS2IFSL-%04lx CleanupSocketFile: Socket %p \n",
        GET_SOCKET_PROCESSID(SocketCtx), SocketFile));
    //
    // Build the list of IRPS still panding on this socket
    //
    InitializeListHead (&irpList);
    CleanupQueuedRequests (ProcessFile->FsContext,
                            SocketFile,
                            &irpList);
    CleanupProcessedRequests (SocketCtx, &irpList);

            //
            // Complete the cancelled IRPS
            //
    while (!IsListEmpty (&irpList)) {
        PLIST_ENTRY entry;
        PIRP        irp;
        entry = RemoveHeadList (&irpList);
        irp = CONTAINING_RECORD (entry, IRP, Tail.Overlay.ListEntry);
        irp->IoStatus.Status = STATUS_CANCELLED;
        irp->IoStatus.Information = 0;
        WsProcessPrint ((PIFSL_PROCESS_CTX)ProcessFile->FsContext, DBG_SOCKET,
            ("WS2IFSL-%04lx CleanupSocketFile: Cancelling Irp %p on socket %p \n",
            GET_SOCKET_PROCESSID(SocketCtx), irp, SocketFile));
        CompleteSocketIrp (irp);
    }

    //
    // Indicate that cleanup routine is going to take care of the
    // pending cancel request if any.
    //
    cancelCtx = InterlockedExchangePointer (
                                    (PVOID *)&SocketCtx->CancelCtx,
                                    NULL);
    if (cancelCtx!=NULL) {
        //
        // We are going to try to free this request if it is still in the queue
        //
        WsProcessPrint ((PIFSL_PROCESS_CTX)ProcessFile->FsContext, DBG_SOCKET,
            ("WS2IFSL-%04lx CleanupSocketFile: Removing cancel ctx %p on socket %p \n",
            GET_SOCKET_PROCESSID(SocketCtx), cancelCtx, SocketFile));
        if (RemoveQueuedCancel (ProcessFile->FsContext, cancelCtx)) {
            //
            // Request was in the queue, it is safe to call regular free routine
            // (no-one else will find it now, so it is safe to put the pointer
            // back in place so that FreeSocketCancel can free it)
            //
            SocketCtx->CancelCtx = cancelCtx;
            FreeSocketCancel (cancelCtx);
        }
        else {
            //
            // Someone else managed to remove the request from the queue before
            // we did, let them or close routine free it. We aren't going to
            // touch it after this.
            //
            SocketCtx->CancelCtx = cancelCtx;
        }
    }

    status = STATUS_SUCCESS;

    ObDereferenceObject (ProcessFile);
    return status;
} // CleanupSocketFile


VOID
CloseSocketFile (
    IN PFILE_OBJECT SocketFile
    )
/*++

Routine Description:

    Deallocates all resources associated with socket file

Arguments:
    SocketFile  - socket file object

Return Value:
    None
--*/
{
    PIFSL_SOCKET_CTX    SocketCtx = SocketFile->FsContext;

    PAGED_CODE ();
    WsProcessPrint ((PIFSL_PROCESS_CTX)SocketCtx->ProcessRef->FsContext, DBG_SOCKET,
        ("WS2IFSL-%04lx CloseSocketFile: Socket %p \n",
         GET_SOCKET_PROCESSID(SocketCtx), SocketFile));

    // First dereference process file
    ObDereferenceObject (SocketCtx->ProcessRef);

    if (SocketCtx->CancelCtx!=NULL) {
        ExFreePool (SocketCtx->CancelCtx);
    }

    // Free context
    ExFreePool (SocketCtx);

} // CloseSocketFile

NTSTATUS
DoSocketReadWrite (
    IN PFILE_OBJECT SocketFile,
    IN PIRP         Irp
    )
/*++

Routine Description:

    Initiates read and write request processing on socket file.

Arguments:
    SocketFile  - socket file object
    Irp         - read/write request

Return Value:

    STATUS_PENDING  - operation initiated OK
    STATUS_INVALID_HANDLE - socket has not been initialized
                        in current process
--*/
{
    NTSTATUS                status;
    PIFSL_SOCKET_CTX        SocketCtx;
    PFILE_OBJECT            ProcessFile;
    PIO_STACK_LOCATION      irpSp;

    PAGED_CODE ();

    irpSp = IoGetCurrentIrpStackLocation (Irp);
    SocketCtx = SocketFile->FsContext;
    ProcessFile = GetSocketProcessReference (SocketCtx);

    if (((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId==PsGetCurrentProcessId()) {
        WsProcessPrint ((PIFSL_PROCESS_CTX)ProcessFile->FsContext, DBG_READWRITE,
            ("WS2IFSL-%04lx DoSocketReadWrite: %s irp %p on socket %p, len %ld.\n",
            ((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId,
            irpSp->MajorFunction==IRP_MJ_READ ? "Read" : "Write",
                        Irp, SocketFile,
                        irpSp->MajorFunction==IRP_MJ_READ
                                        ? irpSp->Parameters.Read.Length
                                        : irpSp->Parameters.Write.Length));
        //
        // Allocate MDL to describe the user buffer.
        //
        Irp->MdlAddress = IoAllocateMdl(
                        Irp->UserBuffer,      // VirtualAddress
                        irpSp->Parameters.DeviceIoControl.OutputBufferLength,
                                            // Length
                        FALSE,              // SecondaryBuffer
                        TRUE,               // ChargeQuota
                        NULL                // Irp
                        );
        if (Irp->MdlAddress!=NULL) {

            // We are going to pend this request
            IoMarkIrpPending (Irp);

            // Prepare IRP for insertion into the queue
            Irp->Tail.Overlay.IfslRequestId = UlongToPtr(GenerateUniqueId (SocketCtx->IrpId));
            Irp->Tail.Overlay.IfslRequestFlags = (PVOID)0;
            Irp->Tail.Overlay.IfslAddressLenPtr = NULL;
            Irp->Tail.Overlay.IfslRequestQueue = NULL;
            if (!QueueRequest (ProcessFile->FsContext, Irp)) {
                Irp->IoStatus.Status = STATUS_CANCELLED;
                Irp->IoStatus.Information = 0;
                WsProcessPrint ((PIFSL_PROCESS_CTX)ProcessFile->FsContext, DBG_READWRITE,
                    ("WS2IFSL-%04lx DoSocketReadWrite: Cancelling Irp %p on socket %p.\n",
                    ((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId,
                    Irp, SocketFile));
                CompleteSocketIrp (Irp);
            }

            status = STATUS_PENDING;
        }
        else {
            WsPrint (DBG_SOCKET|DBG_READWRITE|DBG_FAILURES,
                ("WS2IFSL-%04lx DoSocketReadWrite: Failed to allocate Mdl for Irp %p"
                " on socket %p, status %lx.\n",
            PsGetCurrentProcessId(), Irp, SocketFile));;
            status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }
    else {
        status = STATUS_INVALID_HANDLE;
        WsPrint (DBG_SOCKET|DBG_READWRITE|DBG_FAILURES,
            ("WS2IFSL-%04lx DoSocketReadWrite: Socket %p has not"
                " been setup in the process.\n",
            PsGetCurrentProcessId(), SocketFile));
    }

    ObDereferenceObject (ProcessFile);

    return status;
} // DoSocketReadWrite

NTSTATUS
DoSocketAfdIoctl (
    IN PFILE_OBJECT SocketFile,
    IN PIRP         Irp
    )
/*++

Routine Description:

    Initiates read and write request processing on socket file.

Arguments:
    SocketFile  - socket file object
    Irp         - afd IOCTL request

Return Value:

    STATUS_PENDING  - operation initiated OK
    STATUS_INVALID_HANDLE - socket has not been initialized
                        in current process
--*/
{
    NTSTATUS                status;
    PIO_STACK_LOCATION      irpSp;
    PIFSL_SOCKET_CTX        SocketCtx;
    PFILE_OBJECT            ProcessFile;
    LPWSABUF                bufferArray = NULL;
    ULONG                   bufferCount = 0, length = 0, flags = 0;
    PVOID                   address = NULL;
    PULONG                  lengthPtr = NULL;

    PAGED_CODE ();

    irpSp = IoGetCurrentIrpStackLocation (Irp);
    Irp->IoStatus.Information = 0;
    SocketCtx = SocketFile->FsContext;
    ProcessFile = GetSocketProcessReference (SocketCtx);

    if (((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId==PsGetCurrentProcessId()) {

        try {
            if (Irp->RequestorMode!=KernelMode) {
                ProbeForRead (
                    irpSp->Parameters.DeviceIoControl.Type3InputBuffer,
                    irpSp->Parameters.DeviceIoControl.InputBufferLength,
                    sizeof (ULONG));
            }
            switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
            case IOCTL_AFD_RECEIVE_DATAGRAM: {
                PAFD_RECV_DATAGRAM_INFO info;

                if (irpSp->Parameters.DeviceIoControl.InputBufferLength
                            < sizeof (*info)) {
                    ExRaiseStatus( STATUS_INVALID_PARAMETER );
                }
                info = irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
                bufferArray = info->BufferArray;
                bufferCount = info->BufferCount;
                address = info->Address;
                lengthPtr = info->AddressLength;
                if ((address == NULL) ^ (lengthPtr == NULL)) {
                    ExRaiseStatus( STATUS_INVALID_PARAMETER );
                }

                if (Irp->RequestorMode!=KernelMode) {
                    ProbeForRead (
                        lengthPtr,
                        sizeof (*lengthPtr),
                        sizeof (ULONG));
                }

                length = *lengthPtr;

                if (address != NULL ) {
                    //
                    // 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 (arbitrarily) define "unreasonably huge" as
                    // anything 64K or greater.
                    //
                    if( length == 0 ||
                        length >= 65536 ) {

                        ExRaiseStatus( STATUS_INVALID_PARAMETER );
                    }

                }
                flags = info->TdiFlags;
                WsProcessPrint ((PIFSL_PROCESS_CTX)ProcessFile->FsContext, DBG_AFDIOCTL,
                        ("WS2IFSL-%04lx DoSocketAfdIoctl: RecvFrom irp %p, socket %p,"
                        " arr %p, cnt %ld, addr %p, lenp %p, len %ld, flags %lx.\n",
                        ((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId, Irp, SocketFile,
                        bufferArray, bufferCount, address, lengthPtr, length, flags));
                break;
            }
            case IOCTL_AFD_RECEIVE: {
                PAFD_RECV_INFO info;
                if (irpSp->Parameters.DeviceIoControl.InputBufferLength
                        < sizeof (*info)) {
                    ExRaiseStatus( STATUS_INVALID_PARAMETER );
                }
                info = irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
                bufferArray = info->BufferArray;
                bufferCount = info->BufferCount;
                flags = info->TdiFlags;
                                WsProcessPrint ((PIFSL_PROCESS_CTX)ProcessFile->FsContext, DBG_AFDIOCTL,
                                        ("WS2IFSL-%04lx DoSocketAfdIoctl: Recv irp %p, socket %p,"
                                        " arr %p, cnt %ld, flags %lx.\n",
                                        ((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId, Irp, SocketFile,
                                        bufferArray, bufferCount, flags));
                break;
            }

            case IOCTL_AFD_SEND_DATAGRAM: {
                PAFD_SEND_DATAGRAM_INFO info;

                if (irpSp->Parameters.DeviceIoControl.InputBufferLength
                        < sizeof (*info)) {
                    ExRaiseStatus( STATUS_INVALID_PARAMETER );
                }
                info = irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
                bufferArray = info->BufferArray;
                bufferCount = info->BufferCount;
                address = &(((PTRANSPORT_ADDRESS)
                    info->TdiConnInfo.RemoteAddress)->Address[0].AddressType);
                length = info->TdiConnInfo.RemoteAddressLength
                    - FIELD_OFFSET (TRANSPORT_ADDRESS, Address[0].AddressType);

                //
                // 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 (arbitrarily) define "unreasonably huge" as
                // anything 64K or greater.
                //

                if( length == 0 ||
                    length >= 65536 ) {
                    ExRaiseStatus( STATUS_INVALID_PARAMETER );
                }

                if( Irp->RequestorMode != KernelMode ) {
                    ProbeForRead (
                        address,
                        length,
                        sizeof (UCHAR));
                }

                flags = 0;
                WsProcessPrint ((PIFSL_PROCESS_CTX)ProcessFile->FsContext, DBG_AFDIOCTL,
                        ("WS2IFSL-%04lx DoSocketAfdIoctl: SendTo irp %p, socket %p,"
                        " arr %p, cnt %ld, addr %p, len %ld, flags %lx.\n",
                        ((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId, Irp, SocketFile,
                        bufferArray, bufferCount, address, length, flags));
                break;
            }
            default:
                ASSERTMSG ("Unknown IOCTL!!!", FALSE);
                ExRaiseStatus( STATUS_INVALID_PARAMETER );
            }

            AllocateMdlChain (Irp,
                    bufferArray,
                    bufferCount,
                    &irpSp->Parameters.DeviceIoControl.OutputBufferLength);

            WsProcessPrint ((PIFSL_PROCESS_CTX)ProcessFile->FsContext, DBG_AFDIOCTL,
                    ("WS2IFSL-%04lx DoSocketAfdIoctl: %s irp %p, socket %p,"
                    " arr %p, cnt %ld, addr %p, lenp %p, len %ld, flags %lx.\n",
                    ((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId,
                    irpSp->Parameters.DeviceIoControl.IoControlCode==IOCTL_AFD_RECEIVE_DATAGRAM
                            ? "RecvFrom"
                            : (irpSp->Parameters.DeviceIoControl.IoControlCode==IOCTL_AFD_RECEIVE
                                    ? "Recv"
                                    : "SendTo"
                                    ),
                    Irp, SocketFile,
                    bufferArray, bufferCount, address, lengthPtr, length, flags));
        }
        except (EXCEPTION_EXECUTE_HANDLER) {

            status = GetExceptionCode ();
            WsPrint (DBG_SOCKET|DBG_AFDIOCTL|DBG_FAILURES,
                ("WS2IFSL-%04lx DoSocketAfdIoctl: Failed to process Irp %p"
                " on socket %p, status %lx.\n",
            PsGetCurrentProcessId(), Irp, SocketFile, status));;
            goto Exit;
        }

        // We are going to pend this request
        IoMarkIrpPending (Irp);

        // Prepare IRP for insertion into the queue
        irpSp->Parameters.DeviceIoControl.IfslAddressBuffer = address;
        irpSp->Parameters.DeviceIoControl.IfslAddressLength = length;

        Irp->Tail.Overlay.IfslRequestId = UlongToPtr(GenerateUniqueId (SocketCtx->IrpId));
        Irp->Tail.Overlay.IfslAddressLenPtr = lengthPtr;
        Irp->Tail.Overlay.IfslRequestFlags = UlongToPtr(flags);
        Irp->Tail.Overlay.IfslRequestQueue = NULL;

        if (!QueueRequest (ProcessFile->FsContext, Irp)) {
            Irp->IoStatus.Status = STATUS_CANCELLED;
            Irp->IoStatus.Information = 0;
            WsProcessPrint ((PIFSL_PROCESS_CTX)ProcessFile->FsContext, DBG_AFDIOCTL,
                ("WS2IFSL-%04lx DoAfdIoctl: Cancelling Irp %p on socket %p.\n",
                ((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId,
                Irp, SocketFile));
            CompleteSocketIrp (Irp);
        }
        status = STATUS_PENDING;
    }
    else {
        status = STATUS_INVALID_HANDLE;
        WsPrint (DBG_SOCKET|DBG_AFDIOCTL|DBG_FAILURES,
            ("WS2IFSL-%04lx DoSocketAfdIoctl: Socket %p has not"
                " been setup in the process\n",
            PsGetCurrentProcessId(), SocketFile));
    }
Exit:
    ObDereferenceObject (ProcessFile);
    return status;
} // DoSocketAfdIoctl

VOID
SetSocketContext (
    IN PFILE_OBJECT     SocketFile,
    IN KPROCESSOR_MODE  RequestorMode,
    IN PVOID            InputBuffer,
    IN ULONG            InputBufferLength,
    OUT PVOID           OutputBuffer,
    IN ULONG            OutputBufferLength,
    OUT PIO_STATUS_BLOCK IoStatus
    )
/*++

Routine Description:

    Sets up socket file in context of a current process: associates it with
    process file and assigns context supplied by the caller

Arguments:
    SocketFile          - Socket file on which to operate
    InputBuffer         - input buffer pointer
    InputBufferLength   - size of the input buffer
    OutputBuffer        - output buffer pointer
    OutputBufferLength  - size of output buffer
    IoStatus            - IO status information block

Return Value:
    None (result returned via IoStatus block)
--*/
{
    PIFSL_SOCKET_CTX        SocketCtx;
    HANDLE                  hProcessFile;
    PFILE_OBJECT            ProcessFile;
    PVOID                   DllContext;

    PAGED_CODE ();

    IoStatus->Information = 0;

    SocketCtx = SocketFile->FsContext;

    // First check arguments
    if (InputBufferLength<sizeof (WS2IFSL_SOCKET_CTX)) {
        IoStatus->Status = STATUS_INVALID_PARAMETER;
        WsPrint (DBG_SOCKET|DBG_FAILURES,
            ("WS2IFSL-%04lx SetSocketContext: Invalid input buffer size (%ld)"
             " for socket file %p.\n",
             PsGetCurrentProcessId(),
             InputBufferLength,
             SocketFile));
        return;
    }

    try {
        if (RequestorMode!=KernelMode) {
            ProbeForRead (InputBuffer,
                            sizeof (WS2IFSL_SOCKET_CTX),
                            sizeof (ULONG));
        }
        hProcessFile = ((PWS2IFSL_SOCKET_CTX)InputBuffer)->ProcessFile;
        DllContext = ((PWS2IFSL_SOCKET_CTX)InputBuffer)->DllContext;
    }
    except(EXCEPTION_EXECUTE_HANDLER) {
        IoStatus->Status = GetExceptionCode ();
        WsPrint (DBG_SOCKET|DBG_FAILURES,
            ("WS2IFSL-%04lx SetSocketContext: Invalid input buffer (%p)"
             " for socket file %p.\n",
             PsGetCurrentProcessId(),
             InputBuffer,
             SocketFile));
        return;
    }

    // Get reference to the process file with which this context is associated
    IoStatus->Status = ObReferenceObjectByHandle(
                 hProcessFile,
                 FILE_ALL_ACCESS,
                 *IoFileObjectType,
                 RequestorMode,
                 (PVOID *)&ProcessFile,
                 NULL
                 );
    if (NT_SUCCESS (IoStatus->Status)) {
        // Verify that the file pointer is really our driver's process file
        // and that it created for the current process
        if ((IoGetRelatedDeviceObject (ProcessFile)
                        ==DeviceObject)
                && ((*((PULONG)ProcessFile->FsContext))
                        ==PROCESS_FILE_EANAME_TAG)
                && (((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId
                        ==PsGetCurrentProcessId())) {

            PFILE_OBJECT    oldProcessFile;

            oldProcessFile = SetSocketProcessReference (
                                            SocketCtx,
                                            ProcessFile,
                                            DllContext);

            if (oldProcessFile==ProcessFile) {
                // Old socket, just reset DLL context
                WsProcessPrint ((PIFSL_PROCESS_CTX)ProcessFile->FsContext, DBG_SOCKET,
                    ("WS2IFSL-%04lx ResetSocketContext:"
                    " Socket %p (h:%p->%p)\n",
                     ((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId, SocketFile,
                     SocketCtx->DllContext, DllContext));
            }
            else {
                LIST_ENTRY  irpList;
                // Socket moved to a different process
                WsProcessPrint ((PIFSL_PROCESS_CTX)ProcessFile->FsContext, DBG_SOCKET,
                    ("WS2IFSL-%04lx ResetSocketContext:"
                    " Socket %p (f:%p->%p(h:%p)\n",
                     ((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId, SocketFile,
                     oldProcessFile, ProcessFile, DllContext));

                InitializeListHead (&irpList);

                // Make sure we do not keep IRPs that are queued to
                // the old object as it may go away as soon as we
                // dereference it below.  Note that processed IRPs
                // do not reference process file object in any way.
                CleanupQueuedRequests (oldProcessFile->FsContext, SocketFile, &irpList);
                while (!IsListEmpty (&irpList)) {
                    PLIST_ENTRY entry;
                    PIRP        irp;
                    entry = RemoveHeadList (&irpList);
                    irp = CONTAINING_RECORD (entry, IRP, Tail.Overlay.ListEntry);
                    irp->IoStatus.Status = STATUS_CANCELLED;
                    irp->IoStatus.Information = 0;
                    WsProcessPrint ((PIFSL_PROCESS_CTX)ProcessFile->FsContext, DBG_SOCKET,
                        ("WS2IFSL-%04lx ResetSocketContext: Cancelling Irp %p on socket %p \n",
                        ((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId, irp, SocketFile));
                    CompleteSocketIrp (irp);
                }


                // Dereference the old object below
                ProcessFile = oldProcessFile;

            }
        }
        else {
            // Handle refers to random file object
            WsPrint (DBG_SOCKET|DBG_FAILURES,
                ("WS2IFSL-%04lx SetSocketContext: Procees file handle %p (File:%p)"
                 " is not valid in the process.\n",
                 PsGetCurrentProcessId(),
                 ProcessFile, hProcessFile));
            IoStatus->Status = STATUS_INVALID_PARAMETER;
        }

        ObDereferenceObject (ProcessFile);
    }
    else {
        WsPrint (DBG_SOCKET|DBG_FAILURES,
            ("WS2IFSL-%04lx SetSocketContext: Could not get process file from handle %p,"
             " status:%lx.\n",
             PsGetCurrentProcessId(),
             hProcessFile,
             IoStatus->Status));
    }

} //SetSocketContext



VOID
CompletePvdRequest (
    IN PFILE_OBJECT     SocketFile,
    IN KPROCESSOR_MODE  RequestorMode,
    IN PVOID            InputBuffer,
    IN ULONG            InputBufferLength,
    OUT PVOID           OutputBuffer,
    IN ULONG            OutputBufferLength,
    OUT PIO_STATUS_BLOCK IoStatus
    )
/*++

Routine Description:

    Completes this IOCTL to allow completion port usage by non-IFS providers
Arguments:
    SocketFile          - Socket file on which to operate
    InputBuffer         - input buffer pointer
                            contains IoStatus structure to be returned
                            as the result of this call
    InputBufferLength   - size of the input buffer
    OutputBuffer        - NULL
    OutputBufferLength  - 0
    IoStatus            - IO status information block

Return Value:
    None (result returned via IoStatus block)
--*/
{
    PIFSL_SOCKET_CTX    SocketCtx;
    PFILE_OBJECT        ProcessFile;
    PAGED_CODE();

    IoStatus->Information = 0;
    // First check arguments
    if (InputBufferLength<sizeof (IO_STATUS_BLOCK)) {
        IoStatus->Status = STATUS_INVALID_PARAMETER;
        WsPrint (DBG_PVD_COMPLETE|DBG_FAILURES,
            ("WS2IFSL-%04lx CompletePvdRequest: Invalid input buffer size (%ld)"
             " for socket file %p.\n",
             PsGetCurrentProcessId(),
             InputBufferLength,
             SocketFile));
        return;
    }

    SocketCtx = SocketFile->FsContext;
    ProcessFile = GetSocketProcessReference (SocketCtx);

    if (((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId==PsGetCurrentProcessId()) {
        WsProcessPrint ((PIFSL_PROCESS_CTX)ProcessFile->FsContext, DBG_PVD_COMPLETE,
            ("WS2IFSL-%04lx CompletePvdRequest: Socket %p (h:%p,cport:%p)\n",
                ((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId,
                SocketFile, SocketCtx->DllContext,
                SocketFile->CompletionContext));

        // Carefully write status info
        try {
            if (RequestorMode!=KernelMode)
                ProbeForRead (InputBuffer,
                                sizeof (IO_STATUS_BLOCK),
                                sizeof (ULONG));
            *IoStatus = *((PIO_STATUS_BLOCK)InputBuffer);
        }
        except(EXCEPTION_EXECUTE_HANDLER) {
            IoStatus->Status = GetExceptionCode ();
            WsPrint (DBG_SOCKET|DBG_FAILURES,
                ("WS2IFSL-%04lx CompletePvdRequest: Invalid input buffer (%p)"
                 " for socket file %p.\n",
                 PsGetCurrentProcessId(),
                 InputBuffer,
                 SocketFile));
        }
    }
    else {
        IoStatus->Status = STATUS_INVALID_HANDLE;
        WsPrint (DBG_SOCKET|DBG_PVD_COMPLETE|DBG_FAILURES,
            ("WS2IFSL-%04lx CompletePvdRequest: Socket %p has not"
                " been setup in the process\n",
            PsGetCurrentProcessId(), SocketFile));
    }

    ObDereferenceObject (ProcessFile);

} //CompletePvdRequest

VOID
CompleteDrvRequest (
    IN PFILE_OBJECT         SocketFile,
    IN PWS2IFSL_CMPL_PARAMS Params,
    IN PVOID                OutputBuffer,
    IN ULONG                OutputBufferLength,
    OUT PIO_STATUS_BLOCK    IoStatus
    )
/*++

Routine Description:

    Complete request that was prviously passed to user mode DLL
Arguments:
    SocketFile          - Socket file on which to operate
    Params              - description of the parameters
    OutputBuffer        - Request results (data and address)
    OutputBufferLength  - sizeof result buffer
    IoStatus            - IO status information block
        Status: STATUS_SUCCESS - request was completed OK
                STATUS_CANCELLED - request was already cancelled

Return Value:
    None (result returned via IoStatus block)
--*/
{
    PIFSL_SOCKET_CTX    SocketCtx;
    PIRP                irp = NULL;
    PIO_STACK_LOCATION  irpSp;

    PAGED_CODE();

    SocketCtx = SocketFile->FsContext;


    // Check and copy parameters
    try {

        //
        // Try to find matching IRP in the processed list.
        //
        irp = GetProcessedRequest (SocketCtx, Params->UniqueId);
        if (irp!=NULL) {
            NTSTATUS    status = Params->Status , status2 = 0;
            ULONG       bytesCopied;

            irpSp = IoGetCurrentIrpStackLocation (irp);

            //
            // Copy data based on the function we performed
            //

            switch (irpSp->MajorFunction) {
            case IRP_MJ_DEVICE_CONTROL:
                switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
                case IOCTL_AFD_RECEIVE_DATAGRAM:
                    //
                    // Copy address buffer and length
                    //
                    if (irpSp->Parameters.DeviceIoControl.IfslAddressBuffer!=NULL) {
                        ULONG   addrOffset = ADDR_ALIGN(irpSp->Parameters.DeviceIoControl.OutputBufferLength);
                        if (addrOffset+Params->AddrLen > OutputBufferLength) {
                            ExRaiseStatus (STATUS_INVALID_PARAMETER);
                        }
                        if (Params->AddrLen
                                <=irpSp->Parameters.DeviceIoControl.IfslAddressLength) {
                            RtlCopyMemory (
                                    irpSp->Parameters.DeviceIoControl.IfslAddressBuffer,
                                    (PUCHAR)OutputBuffer+addrOffset,
                                    Params->AddrLen);
                        }
                        else {
                            RtlCopyMemory (
                                    irpSp->Parameters.DeviceIoControl.IfslAddressBuffer,
                                    (PUCHAR)OutputBuffer+addrOffset,
                                    irpSp->Parameters.DeviceIoControl.IfslAddressLength);
                            status2 = STATUS_BUFFER_OVERFLOW;
                        }
                    }
                    if (NT_SUCCESS (status2) && irp->UserBuffer) {
                        *((PULONG)(irp->Tail.Overlay.IfslAddressLenPtr)) = Params->AddrLen;
                    }

                    //
                    // Drop through to copy data as well
                    //

                case IOCTL_AFD_RECEIVE:
                    break;
                case IOCTL_AFD_SEND_DATAGRAM:
                    goto NoCopy;
                    break;
                default:
                    ASSERTMSG ("Unsupported IOCTL!!!", FALSE);
                    ExRaiseStatus (STATUS_INVALID_PARAMETER);
                    break;
                }

                //
                // Drop through to copy data as well
                //

            case IRP_MJ_READ:
                if (irp->MdlAddress!=NULL) {
                    bytesCopied = CopyBufferToMdlChain (
                                    OutputBuffer,
                                    Params->DataLen,
                                    irp->MdlAddress);
                }
                else
                    bytesCopied = 0;

                if ((bytesCopied<Params->DataLen)
                        && NT_SUCCESS (status))
                    status = STATUS_BUFFER_OVERFLOW;
                break;
            case IRP_MJ_WRITE:
                bytesCopied = Params->DataLen;
                // goto NoCopy; // same as break;
                break;
            case IRP_MJ_PNP: 
                if (OutputBufferLength>=sizeof (HANDLE)) {
                    PDEVICE_OBJECT  targetDevice;
                    PIRP            targetIrp;
                    PIO_STACK_LOCATION targetSp;

                    status = ObReferenceObjectByHandle (
                                    *((PHANDLE)OutputBuffer),
                                    MAXIMUM_ALLOWED,
                                    *IoFileObjectType,
                                    irp->RequestorMode,
                                    (PVOID *)&irpSp->FileObject,
                                    NULL
                                    );
                    if (NT_SUCCESS (status)) {
                        targetDevice = IoGetRelatedDeviceObject (irpSp->FileObject);
                        targetIrp = IoBuildAsynchronousFsdRequest (
                                                    IRP_MJ_PNP,
                                                    targetDevice,
                                                    NULL,
                                                    0,
                                                    NULL,
                                                    NULL
                                                    );
                        if (targetIrp!=NULL) {
                            targetSp = IoGetNextIrpStackLocation (targetIrp);
                            *targetSp = *irpSp;
                            targetSp->FileObject = irpSp->FileObject;
                            IoSetCompletionRoutine( targetIrp, CompleteTargetQuery, irp, TRUE, TRUE, TRUE );
                            IoCallDriver (targetDevice, targetIrp);
                            goto NoCompletion;
                        }
                        else {
                            ObDereferenceObject (irpSp->FileObject);
                            status = STATUS_INSUFFICIENT_RESOURCES;
                        }
                    }
                }
                else {
                    ExRaiseStatus (STATUS_INVALID_PARAMETER);
                }
                    
                                
                break;
            default:
                ASSERTMSG ("Unsupported MJ code!!!", FALSE);
                ExRaiseStatus (STATUS_INVALID_PARAMETER);
                break;
            }

        NoCopy:
            irp->IoStatus.Information = bytesCopied;

            if (NT_SUCCESS (status)) {
                irp->IoStatus.Status = status2;
            }
            else {
                irp->IoStatus.Status = status;
            }

            WsProcessPrint (
                (PIFSL_PROCESS_CTX)SocketCtx->ProcessRef->FsContext,
                DBG_DRV_COMPLETE,
                ("WS2IFSL-%04lx CompleteDrvRequest: Irp %p, status %lx, info %ld,"
                 " on socket %p (h:%p).\n",
                    GET_SOCKET_PROCESSID(SocketCtx),
                    irp, irp->IoStatus.Status,
                    irp->IoStatus.Information,
                    SocketFile, SocketCtx->DllContext));
            CompleteSocketIrp (irp);
        NoCompletion:
            IoStatus->Status = STATUS_SUCCESS;
        }
        else {
            IoStatus->Status = STATUS_CANCELLED;
            WsProcessPrint (
                (PIFSL_PROCESS_CTX)SocketCtx->ProcessRef->FsContext,
                DBG_DRV_COMPLETE|DBG_FAILURES,
                ("WS2IFSL-%04lx CompleteDrvRequest:"
                 " Request id %ld is not in the list"
                 " for socket %p.\n",
                 GET_SOCKET_PROCESSID(SocketCtx),
                 Params->UniqueId,
                 SocketFile));
        }
    }
    except(EXCEPTION_EXECUTE_HANDLER) {
        IoStatus->Status = GetExceptionCode ();
        WsProcessPrint (
             (PIFSL_PROCESS_CTX)SocketCtx->ProcessRef->FsContext,
             DBG_DRV_COMPLETE|DBG_FAILURES,
            ("WS2IFSL-%04lx CompleteDrvRequest: Failed to process"
             " Irp %p (id %ld) for socket %p, status %lx.\n",
             GET_SOCKET_PROCESSID(SocketCtx),
             irp, Params->UniqueId,
             SocketFile, IoStatus->Status));
        if (irp!=NULL) {
            //
            // Cleanup and complete the irp
            //
            irp->IoStatus.Status = IoStatus->Status;
            irp->IoStatus.Information = 0;
            if (irpSp->MajorFunction==IRP_MJ_DEVICE_CONTROL) {
                irp->UserBuffer = NULL;
            }
            CompleteSocketIrp (irp);
        }
    }
} //CompleteDrvRequest

NTSTATUS
CompleteTargetQuery (
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    )
{
    PIRP    irp = Context;
    PIO_STACK_LOCATION  irpSp = IoGetCurrentIrpStackLocation (irp);

    //
    // If pending has be returned for this irp then mark the current
    // stack as pending.
    //

    if ( Irp->PendingReturned ) {
        IoMarkIrpPending(Irp);
    }

    ObDereferenceObject (irpSp->FileObject);
    //
    // Copy the status info returned by target device
    //
    irp->IoStatus = Irp->IoStatus;

    //
    // Free the target irp;
    // 
    IoFreeIrp (Irp);

    //
    // Complete the original IRP.
    //
    CompleteSocketIrp (irp);

    //
    // Make sure IO subsystem does not touch the IRP we freed
    //
    return STATUS_MORE_PROCESSING_REQUIRED;
}


NTSTATUS
SocketPnPTargetQuery (
    IN PFILE_OBJECT SocketFile,
    IN PIRP         Irp
    )
/*++

Routine Description:

    Passes target device relation query to the underlying
    socket if any.

Arguments:
    SocketFile  - socket file object
    Irp         - query target device relation request

Return Value:

    STATUS_PENDING  - operation initiated OK
--*/
{
    NTSTATUS                status;
    PIO_STACK_LOCATION      irpSp;
    PIFSL_SOCKET_CTX        SocketCtx;
    PFILE_OBJECT            ProcessFile;

    PAGED_CODE ();

    irpSp = IoGetCurrentIrpStackLocation (Irp);
    Irp->IoStatus.Information = 0;
    SocketCtx = SocketFile->FsContext;
    ProcessFile = GetSocketProcessReference (SocketCtx);


    // We are going to pend this request
    IoMarkIrpPending (Irp);

    // Prepare IRP for insertion into the queue
    irpSp->Parameters.DeviceIoControl.IfslAddressBuffer = NULL;
    irpSp->Parameters.DeviceIoControl.IfslAddressLength = 0;

    Irp->Tail.Overlay.IfslRequestId = UlongToPtr(GenerateUniqueId (SocketCtx->IrpId));
    Irp->Tail.Overlay.IfslAddressLenPtr = NULL;
    Irp->Tail.Overlay.IfslRequestFlags = (PVOID)0;
    Irp->Tail.Overlay.IfslRequestQueue = NULL;

    if (!QueueRequest (ProcessFile->FsContext, Irp)) {
        Irp->IoStatus.Status = STATUS_CANCELLED;
        Irp->IoStatus.Information = 0;
        WsProcessPrint ((PIFSL_PROCESS_CTX)ProcessFile->FsContext, DBG_AFDIOCTL,
            ("WS2IFSL-%04lx DoAfdIoctl: Cancelling Irp %p on socket %p.\n",
            ((PIFSL_PROCESS_CTX)ProcessFile->FsContext)->UniqueId,
            Irp, SocketFile));
        CompleteSocketIrp (Irp);
    }
    status = STATUS_PENDING;

    ObDereferenceObject (ProcessFile);
    return status;
}

BOOLEAN
InsertProcessedRequest (
    PIFSL_SOCKET_CTX    SocketCtx,
    PIRP                Irp
    )
/*++

Routine Description:

    Inserts request that was processed to be passed to user mode
    DLL into socket list.  Checks if request is cancelled
Arguments:
    SocketCtx   - contex of the socket into which insert the request
    Irp         - request to insert
Return Value:
    TRUE        - request was inserted
    FALSE       - request is being cancelled
--*/
{
    KIRQL       oldIRQL;
    IoSetCancelRoutine (Irp, ProcessedCancelRoutine);
    KeAcquireSpinLock (&SocketCtx->SpinLock, &oldIRQL);
    if (!Irp->Cancel) {
        InsertTailList (&SocketCtx->ProcessedIrps,
                        &Irp->Tail.Overlay.ListEntry);
        Irp->Tail.Overlay.IfslRequestQueue = &SocketCtx->ProcessedIrps;
        KeReleaseSpinLock (&SocketCtx->SpinLock, oldIRQL);
        return TRUE;
    }
    else {
        KeReleaseSpinLock (&SocketCtx->SpinLock, oldIRQL);
        return FALSE;
    }
}

VOID
ProcessedCancelRoutine (
    IN PDEVICE_OBJECT       DeviceObject,
    IN PIRP                 Irp
    )
/*++

Routine Description:

    Driver cancel routine for socket request waiting in the list
    (being processed by the user mode DLL).
Arguments:
    DeviceObject - WS2IFSL device object
    Irp          - Irp to be cancelled

Return Value:
    None
--*/
{
    PIO_STACK_LOCATION      irpSp;
    PIFSL_SOCKET_CTX        SocketCtx;

    irpSp = IoGetCurrentIrpStackLocation (Irp);
    SocketCtx = irpSp->FileObject->FsContext;
    WsProcessPrint ((PIFSL_PROCESS_CTX)SocketCtx->ProcessRef->FsContext,
              DBG_SOCKET,
              ("WS2IFSL-%04lx ProcessedCancel: Socket %p, Irp %p\n",
              GET_SOCKET_PROCESSID(SocketCtx),
              irpSp->FileObject, Irp));
    KeAcquireSpinLockAtDpcLevel (&SocketCtx->SpinLock);
    if (Irp->Tail.Overlay.IfslRequestQueue!=NULL) {
        ASSERT (Irp->Tail.Overlay.IfslRequestQueue==&SocketCtx->ProcessedIrps);
        RemoveEntryList (&Irp->Tail.Overlay.ListEntry);
        Irp->Tail.Overlay.IfslRequestQueue = NULL;
        KeReleaseSpinLockFromDpcLevel (&SocketCtx->SpinLock);
        IoReleaseCancelSpinLock (Irp->CancelIrql);

        CancelSocketIo (irpSp->FileObject);

        Irp->IoStatus.Information = 0;
        Irp->IoStatus.Status = STATUS_CANCELLED;
        CompleteSocketIrp (Irp);
    }
    else {
        KeReleaseSpinLockFromDpcLevel (&SocketCtx->SpinLock);
        IoReleaseCancelSpinLock (Irp->CancelIrql);
        //
        // Don't touch IRP after this as we do not own it anymore
        //
    }
}

VOID
CleanupProcessedRequests (
    IN  PIFSL_SOCKET_CTX    SocketCtx,
    OUT PLIST_ENTRY         IrpList
    )
/*++

Routine Description:

    Cleans up all requests on the socket which are being
    processed by the user mode DLL

Arguments:
    SocketCtx   -   context of the socket
    IrpList     -   list to insert cleaned up request (to be completed
                    by the caller)
Return Value:
    None
--*/
{
    PIRP            irp;
    PLIST_ENTRY     entry;
    KIRQL           oldIRQL;

    KeAcquireSpinLock (&SocketCtx->SpinLock, &oldIRQL);
    while (!IsListEmpty(&SocketCtx->ProcessedIrps)) {
        entry = RemoveHeadList (&SocketCtx->ProcessedIrps);
        irp = CONTAINING_RECORD (entry, IRP, Tail.Overlay.ListEntry);
        ASSERT (irp->Tail.Overlay.IfslRequestQueue==&SocketCtx->ProcessedIrps);
        irp->Tail.Overlay.IfslRequestQueue = NULL;
        InsertTailList (IrpList, &irp->Tail.Overlay.ListEntry);
    }
    KeReleaseSpinLock (&SocketCtx->SpinLock, oldIRQL);
}

VOID
CompleteSocketIrp (
    PIRP        Irp
    )
/*++

Routine Description:

    Completes IRP and properly synchronizes with cancel routine
    if necessary (it has already been called).
Arguments:
    Irp     - irp to complete
Return Value:
    None
--*/
{

    //
    // Reset cancel routine (it wont complete the IRP as it
    // won't be able to find it)
    //

    if (IoSetCancelRoutine (Irp, NULL)==NULL) {
        KIRQL   oldIRQL;
        //
        // Cancel routine has been called.
        // Synchronize with cancel routine (it won't touch the
        // IRP after it releases cancel spinlock)

        IoAcquireCancelSpinLock (&oldIRQL);
        IoReleaseCancelSpinLock (oldIRQL);
    }

    if (Irp->MdlAddress!=NULL) {
        ASSERT ((Irp->MdlAddress->MdlFlags & MDL_PAGES_LOCKED) == 0);
        IoFreeMdl (Irp->MdlAddress);
        Irp->MdlAddress = NULL;
    }

    IoCompleteRequest (Irp, IO_NO_INCREMENT);
}

PIRP
GetProcessedRequest (
    PIFSL_SOCKET_CTX    SocketCtx,
    ULONG               UniqueId
    )
/*++

Routine Description:

    Finds and returns matching IRP from the processed IRP list

Arguments:
    SocketCtx   - socket context to search for the IRP in
    UniqueId    - id assigned to the request to distinguish identify
                    it case it was cancelled and IRP was reused
Return Value:
    IRP
    NULL    - irp was not found
--*/
{
    PIRP        irp;
    PLIST_ENTRY entry;
    KIRQL       oldIRQL;

    //
    // We do not usually have many request sumulteneously pending
    // on a socket, so the linear search should suffice.
    //

    KeAcquireSpinLock (&SocketCtx->SpinLock, &oldIRQL);
    entry = SocketCtx->ProcessedIrps.Flink;
    while (entry!=&SocketCtx->ProcessedIrps) {
        irp = CONTAINING_RECORD (entry, IRP, Tail.Overlay.ListEntry);
        entry = entry->Flink;
        if (irp->Tail.Overlay.IfslRequestId==UlongToPtr(UniqueId)) {
            ASSERT (irp->Tail.Overlay.IfslRequestQueue==&SocketCtx->ProcessedIrps);
            RemoveEntryList (&irp->Tail.Overlay.ListEntry);
            irp->Tail.Overlay.IfslRequestQueue = NULL;
            KeReleaseSpinLock (&SocketCtx->SpinLock, oldIRQL);
            return irp;
        }
    }
    KeReleaseSpinLock (&SocketCtx->SpinLock, oldIRQL);
    return NULL;
}


VOID
CancelSocketIo (
    PFILE_OBJECT    SocketFile
    )
/*++

Routine Description:

    Queue a request to user mode DLL to cancel all io on the socket

Arguments:
    SocketCtx   - socket context on which IO is to be cancelled

Return Value:
        None
--*/
{
    PIFSL_SOCKET_CTX    SocketCtx = SocketFile->FsContext;
    PIFSL_PROCESS_CTX   ProcessCtx = SocketCtx->ProcessRef->FsContext;
    PIFSL_CANCEL_CTX    cancelCtx;

    try {
        cancelCtx = (PIFSL_CANCEL_CTX) ExAllocatePoolWithQuotaTag (
                                        NonPagedPool,
                                        sizeof (IFSL_CANCEL_CTX),
                                        CANCEL_CTX_TAG);
    }
    except (EXCEPTION_EXECUTE_HANDLER) {
        cancelCtx = NULL;
    }


    if (cancelCtx!=NULL) {
        //
        // Make sure socket does not go away while this request exists
        //
        ObReferenceObject (SocketFile);
        cancelCtx->SocketFile = SocketFile;
        cancelCtx->UniqueId = GenerateUniqueId (ProcessCtx->CancelId);

        //
        // We do not want to queue another cancel request if we have
        // one pending or being executed
        //
        if (InterlockedCompareExchangePointer ((PVOID *)&SocketCtx->CancelCtx,
                                cancelCtx,
                                NULL)==NULL) {
            WsProcessPrint (
                      ProcessCtx,
                      DBG_CANCEL,
                      ("WS2IFSL-%04lx CancelSocketIo: Context %p, socket %p\n",
                      ProcessCtx->UniqueId,
                      cancelCtx, SocketFile));
            QueueCancel (ProcessCtx, cancelCtx);
            return;
        }

        WsProcessPrint (
                  ProcessCtx,
                  DBG_CANCEL,
                  ("WS2IFSL-%04lx CancelSocketIo: Another cancel active"
                  " context %p, socket %p\n",
                  ProcessCtx->UniqueId,
                  SocketCtx->CancelCtx, SocketFile));
        ObDereferenceObject (SocketFile);
        ExFreePool (cancelCtx);
    }
    else {
        WsPrint (DBG_SOCKET|DBG_CANCEL|DBG_FAILURES,
                  ("WS2IFSL-%04lx CancelSocketIo: Could not allocate cancel"
                  " context for socket %p\n",
                  PsGetCurrentProcessId(),
                  SocketFile));
    }
}

VOID
FreeSocketCancel (
    PIFSL_CANCEL_CTX CancelCtx
    )
/*++

Routine Description:

    Frees resources associated with cancel request

Arguments:
    CancelCtx   - cancel request context

Return Value:
        None
--*/
{
    PFILE_OBJECT        SocketFile = CancelCtx->SocketFile;
    PIFSL_SOCKET_CTX    SocketCtx = SocketFile->FsContext;

    ASSERT (IoGetRelatedDeviceObject (SocketFile)==DeviceObject);
    ASSERT (SocketCtx->EANameTag==SOCKET_FILE_EANAME_TAG);
    ASSERT (CancelCtx->ListEntry.Flink==NULL);

    //
    // We are going to dereference the file object whether
    // free the structure or not
    //
    CancelCtx->SocketFile = NULL;
    ObDereferenceObject (SocketFile);

    //
    // During socket closure, the cleanup routine may be in
    // process of freeing this cancel context and will set
    // the pointer to it to NULL to indicate the fact
    //
    if (InterlockedCompareExchangePointer ((PVOID *)&SocketCtx->CancelCtx,
                                            NULL,
                                            CancelCtx)) {
        WsProcessPrint (
                  (PIFSL_PROCESS_CTX)SocketCtx->ProcessRef->FsContext,
                  DBG_CANCEL,
                  ("WS2IFSL-%04lx FreeSocketCancel: Freeing cancel"
                  " context %p, socket %p\n",
                  GET_SOCKET_PROCESSID(SocketCtx),
                  CancelCtx, SocketFile));
        ExFreePool (CancelCtx);
    }
    else {
        //
        // The close routine will take care of freeing the request
        //
        WsProcessPrint (
                  (PIFSL_PROCESS_CTX)SocketCtx->ProcessRef->FsContext,
                  DBG_CANCEL,
                  ("WS2IFSL-%04lx FreeSocketCancel: Cleanup owns cancel"
                  " context %p, socket %p\n",
                  GET_SOCKET_PROCESSID(SocketCtx),
                  CancelCtx, SocketFile));
    }
}


PFILE_OBJECT
GetSocketProcessReference (
    IN  PIFSL_SOCKET_CTX    SocketCtx
    )
/*++

Routine Description:

    Reads and references process file currently associated with
    the socket under the lock to protect in case socket is moved
    to a different process

Arguments:
    SocketCtx   - socket context to read process file from

Return Value:
    Referenced pointer to process file object currently associated
    with the socket.

--*/
{
    KIRQL               oldIRQL;
    PFILE_OBJECT        ProcessFile;

    KeAcquireSpinLock (&SocketCtx->SpinLock, &oldIRQL);
    ObReferenceObject (SocketCtx->ProcessRef);
    ProcessFile = SocketCtx->ProcessRef;
    KeReleaseSpinLock (&SocketCtx->SpinLock, oldIRQL);

    return ProcessFile;
}


PFILE_OBJECT
SetSocketProcessReference (
    IN  PIFSL_SOCKET_CTX    SocketCtx,
    IN  PFILE_OBJECT        NewProcessFile,
    IN  PVOID               NewDllContext
    )
/*++

Routine Description:

    Sets new process context for the socket object under the protection
    of a lock.

Arguments:
    SocketCtx       - socket context to set
    NewProcessFile  - process file reference
    NewDllContext   - context to be associated with the socket
                        in the process

Return Value:
    Previous process file reference
--*/
{
    KIRQL               oldIRQL;
    PFILE_OBJECT        ProcessFile;

    KeAcquireSpinLock (&SocketCtx->SpinLock, &oldIRQL);
    ProcessFile = SocketCtx->ProcessRef;
    SocketCtx->ProcessRef = NewProcessFile;
    SocketCtx->DllContext = NewDllContext;
    KeReleaseSpinLock (&SocketCtx->SpinLock, oldIRQL);
    return ProcessFile;
}