/*++

Module Name:

    queue.c

Abstract:

    This file contains the queue entry points and callbacks.

Environment:

    Kernel-mode Driver Framework

--*/

#include "precomp.h"
#include "driver.h"
#include "queue.tmh"

#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, dnsfirewallQueueInitialize)
#endif

NTSTATUS
dnsfirewallQueueInitialize(
    _In_ WDFDEVICE Device
    )
/*++

Routine Description:


     The I/O dispatch callbacks for the frameworks device object
     are configured in this function.

     A single default I/O Queue is configured for parallel request
     processing, and a driver context memory allocation is created
     to hold our structure QUEUE_CONTEXT.

Arguments:

    Device - Handle to a framework device object.

Return Value:

    VOID

--*/
{
    WDFQUEUE queue;
    NTSTATUS status;
    WDF_IO_QUEUE_CONFIG    queueConfig;
    PAGED_CODE();
    //
    // Configure a default queue so that requests that are not
    // configure-fowarded using WdfDeviceConfigureRequestDispatching to goto
    // other queues get dispatched here.
    //
    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(
         &queueConfig,
        WdfIoQueueDispatchParallel
        );

    queueConfig.EvtIoDeviceControl = dnsfirewallEvtIoDeviceControl;
    queueConfig.EvtIoStop = dnsfirewallEvtIoStop;
    status = WdfIoQueueCreate(
                 Device,
                 &queueConfig,
                 WDF_NO_OBJECT_ATTRIBUTES,
                 &queue
                 );

    if( !NT_SUCCESS(status) ) {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_QUEUE,
            "WdfIoQueueCreate failed %!STATUS!",
            status
            );
        return status;
    }
    return status;
}

VOID
dnsfirewallEvtIoDeviceControl(
    _In_ WDFQUEUE Queue,
    _In_ WDFREQUEST Request,
    _In_ size_t OutputBufferLength,
    _In_ size_t InputBufferLength,
    _In_ ULONG IoControlCode
    )
/*++

Routine Description:

    This event is invoked when the framework receives IRP_MJ_DEVICE_CONTROL request.

Arguments:

    Queue -  Handle to the framework queue object that is associated with the
             I/O request.

    Request - Handle to a framework request object.

    OutputBufferLength - Size of the output buffer in bytes

    InputBufferLength - Size of the input buffer in bytes

    IoControlCode - I/O control code.

Return Value:

    VOID

--*/
{
    NTSTATUS        status = STATUS_SUCCESS;
    ULONG_PTR       ulInformation = 0;
    WDFFILEOBJECT   wdfFileObject = NULL;
    PADDRESSES_DESCRIPTOR   pDeviceAddressesDescriptor = NULL;
    WDF_OBJECT_ATTRIBUTES   stDeviceContextAttributes;
    WDFMEMORY       wdfOutputMemory = NULL;
    WDFMEMORY       wdfInputMemory = NULL;
    Queue;
    wdfFileObject = WdfRequestGetFileObject(
        Request
        );
    if (NULL != wdfFileObject)
    {
        PFLOW_DATA  pFlowData;
        pFlowData = WdfObjectGetTypedContext(
            wdfFileObject,
            FLOW_DATA
            );
        if (NULL != pFlowData)
        {
            status = WdfRequestForwardToIoQueue(
                Request,
                pFlowData->wdfFlowQueue
                );
            if (!NT_SUCCESS(status))
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! WdfRequestForwardToIoQueue failed with status %!STATUS!",
                    status
                    );
                goto End;
            }
            return;
        }
    }
    switch (IoControlCode)
    {
    case DNSFIREWALL_IOCTL_INIT_FILTERS:
        {
            int     nRequestedSize;
            int     nDataArraySize;
            PBYTE   pInputPosition;
            PBYTE   pCurrentPosition;
            PADDRESSES_DESCRIPTOR   pRequestAddressesDescriptor;
            HANDLE  hThread;
            if (InputBufferLength < sizeof(ADDRESSES_DESCRIPTOR))
            {
                status = STATUS_BUFFER_TOO_SMALL;
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Input Buffer Length = %d too small",
                    (int) InputBufferLength
                    );
                break;
            }
            WdfObjectAcquireLock(g_wdfControlDevice);
            g_bDriverUnload = FALSE;
            KeClearEvent(&g_oWorkerEvent);
            KeClearEvent(&g_oWorkerDoneEvent);
            status = PsCreateSystemThread(
                &hThread,
                THREAD_ALL_ACCESS,
                NULL,
                NULL,
                NULL,
                dnsfirewallWorker,
                NULL
                );
            if (!NT_SUCCESS(status))
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! PsCreateSystemThread failed %!STATUS!",
                    status
                    );
                WdfObjectReleaseLock(g_wdfControlDevice);
                break;
            }
            status = ObReferenceObjectByHandle(
                hThread,
                0,
                NULL,
                KernelMode, // irp->RequestorMode
                &g_pWorkerThreadObj,
                NULL
                );
            ZwClose(hThread);
            if (!NT_SUCCESS(status))
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! ObReferenceObjectByHandle failed %!STATUS!",
                    status
                    );
                WdfObjectReleaseLock(g_wdfControlDevice);
                break;
            }
            pDeviceAddressesDescriptor = WdfObjectGetTypedContext(
                g_wdfControlDevice,
                ADDRESSES_DESCRIPTOR
                );
            if (NULL != pDeviceAddressesDescriptor)
            {
                FilterDataCleanup(pDeviceAddressesDescriptor);
            }
            else
            {
                WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(
                    &stDeviceContextAttributes,
                    ADDRESSES_DESCRIPTOR
                    );
                status = WdfObjectAllocateContext(
                    g_wdfControlDevice,
                    &stDeviceContextAttributes,
                    (PVOID*) &pDeviceAddressesDescriptor
                    );
                if (!NT_SUCCESS(status))
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! WdfObjectAllocateContext failed %!STATUS!",
                        status
                        );
                    WdfObjectReleaseLock(g_wdfControlDevice);
                    break;
                }
            }
            status = WdfRequestRetrieveInputBuffer(
                Request,
                InputBufferLength,
                (PVOID*) &pRequestAddressesDescriptor,
                NULL
                );
            if (!NT_SUCCESS(status))
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! WdfRequestRetrieveInputBuffer failed %!STATUS!",
                    status
                    );
                WdfObjectReleaseLock(g_wdfControlDevice);
                break;
            }
            nRequestedSize = sizeof(ADDRESSES_DESCRIPTOR);
            nDataArraySize = pRequestAddressesDescriptor->m_nPublicIpCount*sizeof(UINT32)
                + pRequestAddressesDescriptor->m_nPrivateIpCount*sizeof(UINT32)
                + pRequestAddressesDescriptor->m_nPublicRangeCount*sizeof(IP4ADDR_RANGE_STRUCT)
                + pRequestAddressesDescriptor->m_nPrivateRangeCount*sizeof(IP4ADDR_RANGE_STRUCT)
                + pRequestAddressesDescriptor->m_nPublicSubnetCount*sizeof(IP4SUBNET_STRUCT)
                + pRequestAddressesDescriptor->m_nPrivateSubnetCount*sizeof(IP4SUBNET_STRUCT)
                + pRequestAddressesDescriptor->m_nPublicIPV6AddrCount*sizeof(IPV6_ADDRESS_STRUCT)
                + pRequestAddressesDescriptor->m_nPrivateIPV6AddrCount*sizeof(IPV6_ADDRESS_STRUCT);
            nRequestedSize += nDataArraySize;
            if (nRequestedSize != InputBufferLength)
            {
                status = STATUS_INVALID_PARAMETER;
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Requested buffer size not equel to Input Buffer Length, parameter is invalid."
                    );
                WdfObjectReleaseLock(g_wdfControlDevice);
                break;
            }
#pragma warning( suppress : 28197 )
            pRequestAddressesDescriptor->m_pDataArray = (PBYTE) ExAllocatePoolWithTag(
                NonPagedPool,
                nRequestedSize,
                POOL_TAG
                );
            if (NULL == pRequestAddressesDescriptor->m_pDataArray)
            {
                status = STATUS_NO_MEMORY;
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Allocate Filter Data failed"
                    );
                WdfObjectReleaseLock(g_wdfControlDevice);
                break;
            }
            RtlCopyMemory(
                pDeviceAddressesDescriptor,
                pRequestAddressesDescriptor,
                sizeof(ADDRESSES_DESCRIPTOR)
                );
            pInputPosition = (PBYTE) pRequestAddressesDescriptor;
            pInputPosition += sizeof(ADDRESSES_DESCRIPTOR);
            pCurrentPosition = pDeviceAddressesDescriptor->m_pDataArray;
            RtlCopyMemory(
                pCurrentPosition,
                pInputPosition,
                nDataArraySize
                );
            if (0 == pDeviceAddressesDescriptor->m_nPublicIpCount)
            {
                pDeviceAddressesDescriptor->m_pPublicIpArray = NULL;
            }
            else
            {
                pDeviceAddressesDescriptor->m_pPublicIpArray = (PUINT32) pCurrentPosition;
                pCurrentPosition += pDeviceAddressesDescriptor->m_nPublicIpCount*sizeof(UINT32);
            }
            if (0 == pDeviceAddressesDescriptor->m_nPrivateIpCount)
            {
                pDeviceAddressesDescriptor->m_pPrivateIpArray = NULL;
            }
            else
            {
                pDeviceAddressesDescriptor->m_pPrivateIpArray = (PUINT32) pCurrentPosition;
                pCurrentPosition += pDeviceAddressesDescriptor->m_nPrivateIpCount*sizeof(UINT32);
            }
            if (0 == pDeviceAddressesDescriptor->m_nPublicRangeCount)
            {
                pDeviceAddressesDescriptor->m_pPublicRangeArray = NULL;
            }
            else
            {
                pDeviceAddressesDescriptor->m_pPublicRangeArray = (PIP4ADDR_RANGE_STRUCT) pCurrentPosition;
                pCurrentPosition += pDeviceAddressesDescriptor->m_nPublicRangeCount*sizeof(IP4ADDR_RANGE_STRUCT);
            }
            if (0 == pDeviceAddressesDescriptor->m_nPrivateRangeCount)
            {
                pDeviceAddressesDescriptor->m_pPrivateRangeArray = NULL;
            }
            else
            {
                pDeviceAddressesDescriptor->m_pPrivateRangeArray = (PIP4ADDR_RANGE_STRUCT) pCurrentPosition;
                pCurrentPosition += pDeviceAddressesDescriptor->m_nPrivateRangeCount*sizeof(IP4ADDR_RANGE_STRUCT);
            }
            if (0 == pDeviceAddressesDescriptor->m_nPublicSubnetCount)
            {
                pDeviceAddressesDescriptor->m_pPublicSubnetArray = NULL;
            }
            else
            {
                pDeviceAddressesDescriptor->m_pPublicSubnetArray = (PIP4SUBNET_STRUCT) pCurrentPosition;
                pCurrentPosition += pDeviceAddressesDescriptor->m_nPublicSubnetCount*sizeof(IP4SUBNET_STRUCT);
            }
            if (0 == pDeviceAddressesDescriptor->m_nPrivateSubnetCount)
            {
                pDeviceAddressesDescriptor->m_pPrivateSubnetArray = NULL;
            }
            else
            {
                pDeviceAddressesDescriptor->m_pPrivateSubnetArray = (PIP4SUBNET_STRUCT) pCurrentPosition;
                pCurrentPosition += pDeviceAddressesDescriptor->m_nPrivateSubnetCount*sizeof(IP4SUBNET_STRUCT);
            }
            if (0 == pDeviceAddressesDescriptor->m_nPublicIPV6AddrCount)
            {
                pDeviceAddressesDescriptor->m_pPublicIPV6AddrArray = NULL;
            }
            else
            {
                pDeviceAddressesDescriptor->m_pPublicIPV6AddrArray = (PIPV6_ADDRESS_STRUCT) pCurrentPosition;
                pCurrentPosition += pDeviceAddressesDescriptor->m_nPublicIPV6AddrCount*sizeof(IPV6_ADDRESS_STRUCT);
            }
            if (0 == pDeviceAddressesDescriptor->m_nPrivateIPV6AddrCount)
            {
                pDeviceAddressesDescriptor->m_pPublicIPV6AddrArray = NULL;
            }
            else
            {
                pDeviceAddressesDescriptor->m_pPrivateIPV6AddrArray = (PIPV6_ADDRESS_STRUCT) pCurrentPosition;
                pCurrentPosition += pDeviceAddressesDescriptor->m_nPrivateIPV6AddrCount*sizeof(IPV6_ADDRESS_STRUCT);
            }
            WdfObjectReleaseLock(g_wdfControlDevice);
            ulInformation = InputBufferLength;
        }
        break;
    case DNSFIREWALL_IOCTL_WAIT_FOR_RESTART:
        {
            PIRP                    _pIrp;
            WDF_IO_QUEUE_CONFIG     stFlowContextQueueConfig;
            WdfObjectAcquireLock(g_wdfControlDevice);
            _pIrp = WdfRequestWdmGetIrp(Request);
            IoMarkIrpPending(_pIrp);
            WDF_IO_QUEUE_CONFIG_INIT(
                &stFlowContextQueueConfig,
                WdfIoQueueDispatchManual
                );
            status = WdfIoQueueCreate(
                g_wdfControlDevice,
                &stFlowContextQueueConfig,
                WDF_NO_OBJECT_ATTRIBUTES,
                &g_wdfWaitRestartQueue
                );
            if (!NT_SUCCESS(status))
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! WdfIoQueueCreate failed with status %!STATUS!",
                    status
                    );
                WdfObjectReleaseLock(g_wdfControlDevice);
                break;
            }
            status = WdfRequestForwardToIoQueue(
                Request,
                g_wdfWaitRestartQueue
                );
            if (!NT_SUCCESS(status))
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "WdfRequestForwardToIoQueue failed with status %!STATUS!",
                    status
                    );
                WdfObjectReleaseLock(g_wdfControlDevice);
                break;
            }
            WdfObjectReleaseLock(g_wdfControlDevice);
        }
        return;
    case DNSFIREWALL_IOCTL_RELEASE_RESORCE:
        WdfObjectAcquireLock(g_wdfControlDevice);
        g_bFiltersChangeInProgress = TRUE;
        pDeviceAddressesDescriptor = WdfObjectGetTypedContext(
            g_wdfControlDevice,
            ADDRESSES_DESCRIPTOR
            );
        if (NULL != pDeviceAddressesDescriptor)
        {
            FilterDataCleanup(pDeviceAddressesDescriptor);
        }
        WdfObjectReleaseLock(g_wdfControlDevice);
        break;
    case DNSFIREWALL_IOCTL_GET_FIREWALL_CONFIG:
        WdfObjectAcquireLock(g_wdfControlDevice);
        ASSERT(OutputBufferLength == sizeof(FIREWALL_CONFIG));
        g_bFiltersChangeInProgress = TRUE;
        status = WdfRequestRetrieveOutputMemory(
            Request,
            &wdfOutputMemory
            );
        ASSERT(NT_SUCCESS(status));
        status = WdfMemoryCopyFromBuffer(
            wdfOutputMemory,
            0,
            &g_stFirewallConfig,
            sizeof(g_stFirewallConfig)
            );
        ASSERT(NT_SUCCESS(status));
        ulInformation = sizeof(g_stFirewallConfig);
        WdfObjectReleaseLock(g_wdfControlDevice);
        break;
    case DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG:
        WdfObjectAcquireLock(g_wdfControlDevice);
        ASSERT(InputBufferLength == sizeof(FIREWALL_CONFIG));
        status = WdfRequestRetrieveInputMemory(
            Request,
            &wdfInputMemory
            );
        ASSERT(NT_SUCCESS(status));
        status = WdfMemoryCopyToBuffer(
            wdfInputMemory,
            0,
            &g_stFirewallConfig,
            sizeof(g_stFirewallConfig)
            );
        g_bFiltersChangeInProgress = FALSE;
        ulInformation = sizeof(g_stFirewallConfig);
        WdfObjectReleaseLock(g_wdfControlDevice);
        break;
    default:
        status = STATUS_INVALID_DEVICE_REQUEST;
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_QUEUE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid IO Control Code = 0x%08X",
            IoControlCode
            );
    }
End:
    WdfRequestCompleteWithInformation(
        Request,
        status,
        ulInformation
        );
    //TraceEvents(
    //    TRACE_LEVEL_INFORMATION,
    //    TRACE_QUEUE,
    //    "%!FUNC! line:%!LINE! %!LEVEL! Exit"
    //    );
    return;
}

VOID
dnsfirewallEvtIoStop(
    _In_ WDFQUEUE Queue,
    _In_ WDFREQUEST Request,
    _In_ ULONG ActionFlags
)
/*++

Routine Description:

    This event is invoked for a power-managed queue before the device leaves the working state (D0).

Arguments:

    Queue -  Handle to the framework queue object that is associated with the
             I/O request.

    Request - Handle to a framework request object.

    ActionFlags - A bitwise OR of one or more WDF_REQUEST_STOP_ACTION_FLAGS-typed flags
                  that identify the reason that the callback function is being called
                  and whether the request is cancelable.

Return Value:

    VOID

--*/
{
    TraceEvents(TRACE_LEVEL_INFORMATION, 
                TRACE_QUEUE, 
                "!FUNC! Queue 0x%p, Request 0x%p ActionFlags %d", 
                Queue, Request, ActionFlags);

    //
    // In most cases, the EvtIoStop callback function completes, cancels, or postpones
    // further processing of the I/O request.
    //
    // Typically, the driver uses the following rules:
    //
    // - If the driver owns the I/O request, it calls WdfRequestUnmarkCancelable
    //   (if the request is cancelable) and either calls WdfRequestStopAcknowledge
    //   with a Requeue value of TRUE, or it calls WdfRequestComplete with a
    //   completion status value of STATUS_SUCCESS or STATUS_CANCELLED.
    //
    //   Before it can call these methods safely, the driver must make sure that
    //   its implementation of EvtIoStop has exclusive access to the request.
    //
    //   In order to do that, the driver must synchronize access to the request
    //   to prevent other threads from manipulating the request concurrently.
    //   The synchronization method you choose will depend on your driver's design.
    //
    //   For example, if the request is held in a shared context, the EvtIoStop callback
    //   might acquire an internal driver lock, take the request from the shared context,
    //   and then release the lock. At this point, the EvtIoStop callback owns the request
    //   and can safely complete or requeue the request.
    //
    // - If the driver has forwarded the I/O request to an I/O target, it either calls
    //   WdfRequestCancelSentRequest to attempt to cancel the request, or it postpones
    //   further processing of the request and calls WdfRequestStopAcknowledge with
    //   a Requeue value of FALSE.
    //
    // A driver might choose to take no action in EvtIoStop for requests that are
    // guaranteed to complete in a small amount of time.
    //
    // In this case, the framework waits until the specified request is complete
    // before moving the device (or system) to a lower power state or removing the device.
    // Potentially, this inaction can prevent a system from entering its hibernation state
    // or another low system power state. In extreme cases, it can cause the system
    // to crash with bugcheck code 9F.
    //

    return;
}

VOID flowControlEvtIoDeviceControl(
    __in  WDFQUEUE Queue,
    __in  WDFREQUEST Request,
    __in  size_t OutputBufferLength,
    __in  size_t InputBufferLength,
    __in  ULONG IoControlCode
    )
{
    NTSTATUS                status = STATUS_SUCCESS;
    ULONG_PTR               ulInformation = 0;
    WDFFILEOBJECT           wdfFlowFileObject = NULL;
    PFLOW_DATA              pFlowContext = NULL;
    PNET_BUFFER_LIST        pNetBufferList;
    PNET_BUFFER             pNetBuffer;
    PMDL                    _pMdl;
    WDFMEMORY               wdfOutputMemory = NULL;
    PIRP                    _pIrp;
    PBYTE                   pInboundData;
    PBYTE                   pOutputBuffer;
    size_t                  nByteLength;
    PBYTE                   pInputBuffer;
    PBYTE                   pDataCopy;
    SIZE_T                  nResponceLength;
    WDF_IO_QUEUE_CONFIG     stFlowContextQueueConfig;
    BOOLEAN                 bEmptyList;
    BOOLEAN                 bSignalWorkerThread;
    PUDP_QUERY_APPROVAL     pUdpQueryApproval;
    ULONG                   ulUdpQueryDataLength;
    PTCP_QUERY_APPROVAL     pTcpQueryApproval;
    PTCP_RESPONCE_APPROVAL  pTcpResponceApproval;
    WDFQUEUE                wdfRequestQueue = NULL;
    WDFREQUEST              wdfPendedRequest = NULL;
    Queue;
    WdfObjectAcquireLock(g_wdfControlDevice);
    wdfFlowFileObject = WdfRequestGetFileObject(
        Request
        );
    ASSERT(NULL != wdfFlowFileObject);
    pFlowContext = WdfObjectGetTypedContext(
        wdfFlowFileObject,
        FLOW_DATA
        );
    ASSERT(NULL != pFlowContext);
    switch (IoControlCode)
    {
    case DNSFIREWALL_IOCTL_WAIT_FLOW_DONE:
        if (sizeof(FLOW_DATA) != OutputBufferLength)
        {
            status = STATUS_INVALID_PARAMETER;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid length of output buffer"
                );
            break;
        }
        switch (pFlowContext->enContextState)
        {
        case csNotRun:
            break;
        case csWaitQueryStarted:
            wdfRequestQueue = pFlowContext->wdfWaitDnsQueryQueue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitDnsQueryQueue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_END_OF_FILE,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            wdfRequestQueue = pFlowContext->wdfWaitUdpSetFirewallFilterQueue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitUdpSetFirewallFilterQueue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_END_OF_FILE,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            wdfRequestQueue = pFlowContext->wdfWaitUdpQuery2Queue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitUdpQuery2Queue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_END_OF_FILE,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            wdfRequestQueue = pFlowContext->wdfWaitFlowDoneQueue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitFlowDoneQueue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_NO_MORE_ENTRIES,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            pFlowContext->enContextState = csNotRun;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            bEmptyList = RemoveEntryList(&pFlowContext->listEntry);
            pFlowContext->enFlowDataOwner = foFreeFileList;
            push_fos(pFlowContext->arFlowDataOwnerStack, pFlowContext->enFlowDataOwner);
            InsertTailList(
                &g_stFreeFileList,
                &pFlowContext->listEntry
                );
            status = STATUS_SUCCESS;
            break;
        case csUdpWaitSetFilterStarted:
            wdfRequestQueue = pFlowContext->wdfWaitUdpSetFirewallFilterQueue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitUdpSetFirewallFilterQueue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_END_OF_FILE,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            wdfRequestQueue = pFlowContext->wdfWaitUdpQuery2Queue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitUdpQuery2Queue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_END_OF_FILE,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            wdfRequestQueue = pFlowContext->wdfWaitFlowDoneQueue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitFlowDoneQueue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_NO_MORE_ENTRIES,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            pFlowContext->enContextState = csNotRun;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            bEmptyList = RemoveEntryList(&pFlowContext->listEntry);
            pFlowContext->enFlowDataOwner = foFreeFileList;
            push_fos(pFlowContext->arFlowDataOwnerStack, pFlowContext->enFlowDataOwner);
            InsertTailList(
                &g_stFreeFileList,
                &pFlowContext->listEntry
                );
            status = STATUS_SUCCESS;
            break;
        case csUdpWaitQuery2Started:
            wdfRequestQueue = pFlowContext->wdfWaitUdpQuery2Queue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitUdpQuery2Queue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_END_OF_FILE,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            wdfRequestQueue = pFlowContext->wdfWaitFlowDoneQueue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitFlowDoneQueue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_NO_MORE_ENTRIES,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            pFlowContext->enContextState = csNotRun;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            status = STATUS_SUCCESS;
            break;
        case csWaitFlowDoneStarted:
            wdfRequestQueue = pFlowContext->wdfWaitFlowDoneQueue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitFlowDoneQueue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_NO_MORE_ENTRIES,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            pFlowContext->enContextState = csNotRun;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            bEmptyList = RemoveEntryList(&pFlowContext->listEntry);
            pFlowContext->enFlowDataOwner = foFreeFileList;
            push_fos(pFlowContext->arFlowDataOwnerStack, pFlowContext->enFlowDataOwner);
            InsertTailList(
                &g_stFreeFileList,
                &pFlowContext->listEntry
                );
            status = STATUS_SUCCESS;
            break;
        default:
            status = STATUS_INVALID_DEVICE_STATE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid Context State = %u",
                pFlowContext->enContextState
                );
            break;
        }
        if (STATUS_SUCCESS != status)
        {
            break;
        }
        pFlowContext->enContextState = csWaitFlowDoneStarted;
        push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
        _pIrp = WdfRequestWdmGetIrp(Request);
        IoMarkIrpPending(_pIrp);
        WDF_IO_QUEUE_CONFIG_INIT(
            &stFlowContextQueueConfig,
            WdfIoQueueDispatchManual
            );
        status = WdfIoQueueCreate(
            g_wdfControlDevice,
            &stFlowContextQueueConfig,
            WDF_NO_OBJECT_ATTRIBUTES,
            &pFlowContext->wdfWaitFlowDoneQueue
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfIoQueueCreate failed with status %!STATUS!",
                status
                );
            break;
        }
        status = WdfRequestForwardToIoQueue(
            Request,
            pFlowContext->wdfWaitFlowDoneQueue
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "WdfRequestForwardToIoQueue failed with status %!STATUS!",
                status
                );
            break;
        }
        bEmptyList = RemoveEntryList(&pFlowContext->listEntry);
        pFlowContext->enFlowDataOwner = foWaitForDoneFileList;
        push_fos(pFlowContext->arFlowDataOwnerStack, pFlowContext->enFlowDataOwner);
        InsertTailList(
            &g_stWaitForDoneFileList,
            &pFlowContext->listEntry
            );
        WdfObjectReleaseLock(g_wdfControlDevice);
        return;
    case DNSFIREWALL_IOCTL_WAIT_UDP_QUERY2:
        if (sizeof(FLOW_DATA) != OutputBufferLength)
        {
            status = STATUS_INVALID_PARAMETER;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid length of output buffer"
                );
            break;
        }
        switch (pFlowContext->enContextState)
        {
        case csWaitFlowDoneStarted:
            break;
        case csWaitQueryStarted:
            wdfRequestQueue = pFlowContext->wdfWaitDnsQueryQueue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitDnsQueryQueue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_END_OF_FILE,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            wdfRequestQueue = pFlowContext->wdfWaitUdpSetFirewallFilterQueue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitUdpSetFirewallFilterQueue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_END_OF_FILE,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            wdfRequestQueue = pFlowContext->wdfWaitUdpQuery2Queue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitUdpQuery2Queue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_END_OF_FILE,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            pFlowContext->enContextState = csWaitFlowDoneStarted;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            bEmptyList = RemoveEntryList(&pFlowContext->listEntry);
            pFlowContext->enFlowDataOwner = foWaitForDoneFileList;
            push_fos(pFlowContext->arFlowDataOwnerStack, pFlowContext->enFlowDataOwner);
            InsertTailList(
                &g_stWaitForDoneFileList,
                &pFlowContext->listEntry
                );
            status = STATUS_SUCCESS;
            break;
        case csUdpWaitSetFilterStarted:
            wdfRequestQueue = pFlowContext->wdfWaitUdpSetFirewallFilterQueue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitUdpSetFirewallFilterQueue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_END_OF_FILE,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            wdfRequestQueue = pFlowContext->wdfWaitUdpQuery2Queue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitUdpQuery2Queue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_END_OF_FILE,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            pFlowContext->enContextState = csWaitFlowDoneStarted;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            bEmptyList = RemoveEntryList(&pFlowContext->listEntry);
            pFlowContext->enFlowDataOwner = foWaitForDoneFileList;
            push_fos(pFlowContext->arFlowDataOwnerStack, pFlowContext->enFlowDataOwner);
            InsertTailList(
                &g_stWaitForDoneFileList,
                &pFlowContext->listEntry
                );
            status = STATUS_SUCCESS;
            break;
        case csUdpWaitQuery2Started:
            wdfRequestQueue = pFlowContext->wdfWaitUdpQuery2Queue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitUdpQuery2Queue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_END_OF_FILE,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            pFlowContext->enContextState = csWaitFlowDoneStarted;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            bEmptyList = RemoveEntryList(&pFlowContext->listEntry);
            pFlowContext->enFlowDataOwner = foWaitForDoneFileList;
            push_fos(pFlowContext->arFlowDataOwnerStack, pFlowContext->enFlowDataOwner);
            InsertTailList(
                &g_stWaitForDoneFileList,
                &pFlowContext->listEntry
                );
            status = STATUS_SUCCESS;
            break;
        default:
            status = STATUS_INVALID_DEVICE_STATE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid Context State = %u",
                pFlowContext->enContextState
                );
            break;
        }
        if (STATUS_SUCCESS != status)
        {
            break;
        }
        pFlowContext->enContextState = csUdpWaitQuery2Started;
        push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
        _pIrp = WdfRequestWdmGetIrp(Request);
        IoMarkIrpPending(_pIrp);
        WDF_IO_QUEUE_CONFIG_INIT(
            &stFlowContextQueueConfig,
            WdfIoQueueDispatchManual
            );
        status = WdfIoQueueCreate(
            g_wdfControlDevice,
            &stFlowContextQueueConfig,
            WDF_NO_OBJECT_ATTRIBUTES,
            &pFlowContext->wdfWaitUdpQuery2Queue
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfIoQueueCreate failed with status %!STATUS!",
                status
                );
            break;
        }
        status = WdfRequestForwardToIoQueue(
            Request,
            pFlowContext->wdfWaitUdpQuery2Queue
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfRequestForwardToIoQueue failed with status %!STATUS!",
                status
                );
            break;
        }
        bEmptyList = RemoveEntryList(&pFlowContext->listEntry);
        pFlowContext->enFlowDataOwner = foWaitForQuery2FileList;
        push_fos(pFlowContext->arFlowDataOwnerStack, pFlowContext->enFlowDataOwner);
        InsertTailList(
            &g_stWaitForQuery2FileList,
            &pFlowContext->listEntry
            );
        WdfObjectReleaseLock(g_wdfControlDevice);
        return;
    case DNSFIREWALL_IOCTL_WAIT_UDP_SET_FILTER:
        if (sizeof(FLOW_DATA) != OutputBufferLength)
        {
            status = STATUS_INVALID_PARAMETER;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid length of output buffer"
                );
            break;
        }
        switch (pFlowContext->enContextState)
        {
        case csUdpWaitQuery2Started:
            break;
        case csWaitQueryStarted:
            wdfRequestQueue = pFlowContext->wdfWaitDnsQueryQueue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitDnsQueryQueue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_END_OF_FILE,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            wdfRequestQueue = pFlowContext->wdfWaitUdpSetFirewallFilterQueue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitUdpSetFirewallFilterQueue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_END_OF_FILE,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            pFlowContext->enContextState = csUdpWaitQuery2Started;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            bEmptyList = RemoveEntryList(&pFlowContext->listEntry);
            pFlowContext->enFlowDataOwner = foWaitForQuery2FileList;
            push_fos(pFlowContext->arFlowDataOwnerStack, pFlowContext->enFlowDataOwner);
            InsertTailList(
                &g_stWaitForQuery2FileList,
                &pFlowContext->listEntry
                );
            status = STATUS_SUCCESS;
            break;
        case csUdpWaitSetFilterStarted:
            wdfRequestQueue = pFlowContext->wdfWaitUdpSetFirewallFilterQueue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitUdpSetFirewallFilterQueue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_END_OF_FILE,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            pFlowContext->enContextState = csUdpWaitQuery2Started;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            bEmptyList = RemoveEntryList(&pFlowContext->listEntry);
            pFlowContext->enFlowDataOwner = foWaitForQuery2FileList;
            push_fos(pFlowContext->arFlowDataOwnerStack, pFlowContext->enFlowDataOwner);
            InsertTailList(
                &g_stWaitForQuery2FileList,
                &pFlowContext->listEntry
                );
            status = STATUS_SUCCESS;
            break;
        default:
            status = STATUS_INVALID_DEVICE_STATE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid Context State = %u",
                pFlowContext->enContextState
                );
            break;
        }
        if (STATUS_SUCCESS != status)
        {
            break;
        }
        pFlowContext->enContextState = csUdpWaitSetFilterStarted;
        push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
        _pIrp = WdfRequestWdmGetIrp(Request);
        IoMarkIrpPending(_pIrp);
        WDF_IO_QUEUE_CONFIG_INIT(
            &stFlowContextQueueConfig,
            WdfIoQueueDispatchManual
            );
        status = WdfIoQueueCreate(
            g_wdfControlDevice,
            &stFlowContextQueueConfig,
            WDF_NO_OBJECT_ATTRIBUTES,
            &pFlowContext->wdfWaitUdpSetFirewallFilterQueue
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfIoQueueCreate failed with status %!STATUS!",
                status
                );
            break;
        }
        status = WdfRequestForwardToIoQueue(
            Request,
            pFlowContext->wdfWaitUdpSetFirewallFilterQueue
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfRequestForwardToIoQueue failed with status %!STATUS!",
                status
                );
            break;
        }
        bEmptyList = RemoveEntryList(&pFlowContext->listEntry);
        pFlowContext->enFlowDataOwner = foWaitForSetFilterFileList;
        push_fos(pFlowContext->arFlowDataOwnerStack, pFlowContext->enFlowDataOwner);
        InsertTailList(
            &g_stWaitForSetFilterFileList,
            &pFlowContext->listEntry
            );
        WdfObjectReleaseLock(g_wdfControlDevice);
        return;
    case DNSFIREWALL_IOCTL_WAIT_DNS_QUERY:
        if (sizeof(FLOW_DATA) != OutputBufferLength)
        {
            status = STATUS_INVALID_PARAMETER;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid length of output buffer"
                );
            break;
        }
        switch (pFlowContext->enContextState)
        {
        case csUdpWaitSetFilterStarted:
            break;
        case csWaitQueryStarted:
            wdfRequestQueue = pFlowContext->wdfWaitDnsQueryQueue;
            if (NULL != wdfRequestQueue)
            {
                pFlowContext->wdfWaitDnsQueryQueue = NULL;
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        STATUS_END_OF_FILE,
                        0
                        );
                }
                else if (STATUS_NO_MORE_ENTRIES != status)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            pFlowContext->enContextState = csUdpWaitSetFilterStarted;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            bEmptyList = RemoveEntryList(&pFlowContext->listEntry);
            pFlowContext->enFlowDataOwner = foWaitForSetFilterFileList;
            push_fos(pFlowContext->arFlowDataOwnerStack, pFlowContext->enFlowDataOwner);
            InsertTailList(
                &g_stWaitForSetFilterFileList,
                &pFlowContext->listEntry
                );
            status = STATUS_SUCCESS;
            break;
        default:
            status = STATUS_INVALID_DEVICE_STATE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid Context State = %u",
                pFlowContext->enContextState
                );
            break;
        }
        if (STATUS_SUCCESS != status)
        {
            break;
        }
        pFlowContext->enContextState = csWaitQueryStarted;
        push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
        _pIrp = WdfRequestWdmGetIrp(Request);
        IoMarkIrpPending(_pIrp);
        WDF_IO_QUEUE_CONFIG_INIT(
            &stFlowContextQueueConfig,
            WdfIoQueueDispatchManual
            );
        status = WdfIoQueueCreate(
            g_wdfControlDevice,
            &stFlowContextQueueConfig,
            WDF_NO_OBJECT_ATTRIBUTES,
            &pFlowContext->wdfWaitDnsQueryQueue
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfIoQueueCreate failed with status %!STATUS!",
                status
                );
            break;
        }
        status = WdfRequestForwardToIoQueue(
            Request,
            pFlowContext->wdfWaitDnsQueryQueue
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfRequestForwardToIoQueue failed with status %!STATUS!",
                status
                );
            break;
        }
        bEmptyList = RemoveEntryList(&pFlowContext->listEntry);
        pFlowContext->enFlowDataOwner = foAcceptableList;
        push_fos(pFlowContext->arFlowDataOwnerStack, pFlowContext->enFlowDataOwner);
        InsertTailList(
            &g_stAcceptableList,
            &pFlowContext->listEntry
            );
        WdfObjectReleaseLock(g_wdfControlDevice);
        return;
    case DNSFIREWALL_IOCTL_READ_UDP_QUERY:
        switch (pFlowContext->enContextState)
        {
        case csNotRun:
        case csUdpFlowBreak:
        case csFlowDeleted:
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            if (!g_bFiltersChangeInProgress)
            {
                TraceEvents(
                    TRACE_LEVEL_WARNING,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! The flow-delete state has been reached."
                    );
            }
            break;
        case csDnsError:
            ulInformation = 0;
            status = STATUS_UNSUCCESSFUL;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-error state has been reached."
                );
            break;
        case csUdpWaitReadQuery:
            ulUdpQueryDataLength = pFlowContext->ulUdpQueryDataLength;
            if (OutputBufferLength != (size_t) ulUdpQueryDataLength)
            {
                status = STATUS_INVALID_PARAMETER;
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid length of output buffer"
                    );
                break;
            }
            pNetBufferList = pFlowContext->pUdpQueryBufferList;
            pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList);
            status = WdfRequestRetrieveOutputMemory(
                Request,
                &wdfOutputMemory
                );
            ASSERT(NT_SUCCESS(status));
            pOutputBuffer = (PBYTE) WdfMemoryGetBuffer(
                wdfOutputMemory,
                NULL
                );
            pInboundData = (PBYTE) NdisGetDataBuffer(
                pNetBuffer,
                ulUdpQueryDataLength,
                pOutputBuffer,
                1,
                0
                );
            ASSERT(NULL != pInboundData);
            if (pOutputBuffer != pInboundData)
            {
                RtlCopyMemory(
                    pOutputBuffer,
                    pInboundData,
                    ulUdpQueryDataLength
                    );
            }
            ulInformation = ulUdpQueryDataLength;
            pFlowContext->enContextState = csUdpWaitApprovalQuery;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            break;
        default:
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid Context State = %u",
                pFlowContext->enContextState
                );
            break;
        }
        break;
    case DNSFIREWALL_IOCTL_APPROVAL_UDP_QUERY:
        if (csNotRun == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-delete state has been reached."
                );
            break;
        }
        if (csFlowDeleted == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-delete state has been reached."
                );
            break;
        }
        if (csDnsError == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_UNSUCCESSFUL;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-error state has been reached."
                );
            break;
        }
        if (sizeof(UDP_QUERY_APPROVAL) != InputBufferLength)
        {
            status = STATUS_INVALID_PARAMETER;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid length of input buffer"
                );
            break;
        }
        pNetBufferList = pFlowContext->pUdpQueryBufferList;
        status = WdfRequestRetrieveInputBuffer(
            Request,
            InputBufferLength,
            (PVOID*) &pUdpQueryApproval,
            NULL
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfRequestRetrieveInputBuffer failed %!STATUS!",
                status
                );
            break;
        }
        switch (pUdpQueryApproval->_enApprovalAction)
        {
        case aaDiscard:
            FwpsDereferenceNetBufferList(
                pNetBufferList,
                FALSE
                );
            pFlowContext->pUdpQueryBufferList = NULL;
            pFlowContext->bUdpQuery1Discarded = TRUE;
            ulInformation = InputBufferLength;
            switch (pFlowContext->enContextState)
            {
            case csUdpWaitApprovalQuery:
                if (pFlowContext->bUdpQueryClassifyPended)
                {
                    bEmptyList = RemoveEntryList(&pFlowContext->listEntry);
                    if (NULL != pFlowContext->pUdpQueryBufferList)
                    {
                        FwpsDereferenceNetBufferList(
                            pFlowContext->pUdpQueryBufferList,
                            FALSE
                            );
                        pFlowContext->pUdpQueryBufferList = NULL;
                    }
                    pFlowContext->bIPv6 = FALSE;
                    pFlowContext->bUdpQueryClassifyPended = FALSE;
                    pFlowContext->enContextState = csWaitQueryStarted;
                    push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
                    pFlowContext->enFlowDataOwner = foAcceptableList;
                    push_fos(pFlowContext->arFlowDataOwnerStack, pFlowContext->enFlowDataOwner);
                    InsertTailList(
                        &g_stAcceptableList,
                        &pFlowContext->listEntry
                        );
                    break;
                }
                pFlowContext->enContextState = csUdpQueryDiscarded;
                push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
                WdfObjectReleaseLock(g_wdfControlDevice);
                if (pFlowContext->bIPv6)
                {
                    status = FwpsFlowRemoveContext(
                        pFlowContext->flowHandle,
                        FWPS_LAYER_DATAGRAM_DATA_V6,
                        g_nDatagramIdV6
                        );
                }
                else
                {
                    status = FwpsFlowRemoveContext(
                        pFlowContext->flowHandle,
                        FWPS_LAYER_DATAGRAM_DATA_V4,
                        g_nDatagramIdV4
                        );
                }
                if (!NT_SUCCESS(status))
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"FwpsFlowRemoveContext\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectAcquireLock(g_wdfControlDevice);
                if (STATUS_PENDING == status)
                {
                    status = STATUS_SUCCESS;
                }
                break;
            default:
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid Context State = %u",
                    pFlowContext->enContextState
                    );
                //ASSERT(FALSE);
                pFlowContext->enContextState = csUdpQueryDiscarded;
                push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
                WdfObjectReleaseLock(g_wdfControlDevice);
                if (pFlowContext->bIPv6)
                {
                    status = FwpsFlowRemoveContext(
                        pFlowContext->flowHandle,
                        FWPS_LAYER_DATAGRAM_DATA_V6,
                        g_nDatagramIdV6
                        );
                }
                else
                {
                    status = FwpsFlowRemoveContext(
                        pFlowContext->flowHandle,
                        FWPS_LAYER_DATAGRAM_DATA_V4,
                        g_nDatagramIdV4
                        );
                }
                if (!NT_SUCCESS(status))
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"FwpsFlowRemoveContext\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectAcquireLock(g_wdfControlDevice);
                if (STATUS_PENDING == status)
                {
                    status = STATUS_SUCCESS;
                }
            }
            break;
        case aaPermit:
            {
                UINT32              ulNblOffset;
                UINT32              ulDataOffset;
                UINT32              ulIpHeaderSize;
                UINT32              ulTransportHeaderSize;
                UINT32              ulIpHeaderRetreat;
                PNET_BUFFER_LIST    pCloneBufferList;
                NDIS_STATUS         nStatus;
                pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList);
                ulNblOffset = NET_BUFFER_DATA_OFFSET(pNetBuffer);
                ulDataOffset = pFlowContext->ulUdpQueryDataOffset;
                ulIpHeaderSize = pFlowContext->ulUdpQueryIpHeaderSize;
                ulTransportHeaderSize = pFlowContext->ulUdpQueryTransportHeaderSize;
                //
                // The TCP/IP stack could have retreated the net buffer list by the 
                // transportHeaderSize amount; detect the condition here to avoid
                // retreating twice.
                //
                if (ulNblOffset != ulDataOffset)
                {
                    ASSERT(ulDataOffset - ulNblOffset == ulTransportHeaderSize);
                    ulTransportHeaderSize = 0;
                }
                ulIpHeaderRetreat = ulIpHeaderSize + ulTransportHeaderSize;
                //
                // Adjust the net buffer list offset to the start of the IP header.
                //
                nStatus = NdisRetreatNetBufferDataStart(
                    pNetBuffer,
                    ulIpHeaderRetreat,
                    0,
                    NULL
                    );
                //
                // Note that the clone will inherit the original net buffer list's offset.
                //
                status = FwpsAllocateCloneNetBufferList(
                    pNetBufferList,
                    g_hNetBufferListPool,
                    NULL,
                    0,
                    &pCloneBufferList
                    );
                //
                // Undo the adjustment on the original net buffer list.
                //
                NdisAdvanceNetBufferDataStart(
                    pNetBuffer,
                    ulIpHeaderRetreat,
                    FALSE,
                    NULL
                    );
                FwpsDereferenceNetBufferList(
                    pNetBufferList,
                    FALSE
                    );
                pFlowContext->pUdpQueryBufferList = NULL;
                if (!NT_SUCCESS(status))
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! FwpsAllocateCloneNetBufferList failed %!STATUS!",
                        status
                        );
                    break;
                }
                switch (pFlowContext->enContextState)
                {
                case csUdpWaitApprovalQuery:
                    pFlowContext->enContextState = csUdpPermitQueryPended;
                    push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
                    break;
                default:
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! Invalid Context State = %u",
                        pFlowContext->enContextState
                        );
                    //ASSERT(FALSE);
                    pFlowContext->enContextState = csUdpPermitQueryPended;
                    push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
                }
                pFlowContext->pUdpQueryCloneBufferList = pCloneBufferList;
                _pIrp = WdfRequestWdmGetIrp(Request);
                IoMarkIrpPending(_pIrp);
                WDF_IO_QUEUE_CONFIG_INIT(
                    &stFlowContextQueueConfig,
                    WdfIoQueueDispatchManual
                    );
                status = WdfIoQueueCreate(
                    g_wdfControlDevice,
                    &stFlowContextQueueConfig,
                    WDF_NO_OBJECT_ATTRIBUTES,
                    &pFlowContext->wdfUdpQueryPermitQueue
                    );
                ASSERT(NT_SUCCESS(status));
                status = WdfRequestForwardToIoQueue(
                    Request,
                    pFlowContext->wdfUdpQueryPermitQueue
                    );
                ASSERT(NT_SUCCESS(status));
                if (IsListHeadBad(&g_stPendedPacketsQueue))
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! Bad \"PendedPacketsQueue\" Header!!"
                        );
                    InitializeListHead(&g_stPendedPacketsQueue);
                }
                bSignalWorkerThread = IsListEmpty(&g_stPendedPacketsQueue);
                InsertTailList(&g_stPendedPacketsQueue, &pFlowContext->threadListEntry);
                WdfObjectReleaseLock(g_wdfControlDevice);
                if (bSignalWorkerThread)
                {
                    KeSetEvent(
                        &g_oWorkerEvent,
                        0,
                        FALSE
                        );
                }
            }
            return;
        case aaModify:
            ulInformation = InputBufferLength;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! UDP Query Replace operation not Implemented"
                );
            pFlowContext->enContextState = csUdpWaitReplaceQuery;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            WdfObjectReleaseLock(g_wdfControlDevice);
            if (pFlowContext->bIPv6)
            {
                status = FwpsFlowRemoveContext(
                    pFlowContext->flowHandle,
                    FWPS_LAYER_DATAGRAM_DATA_V6,
                    g_nDatagramIdV6
                    );
            }
            else
            {
                status = FwpsFlowRemoveContext(
                    pFlowContext->flowHandle,
                    FWPS_LAYER_DATAGRAM_DATA_V4,
                    g_nDatagramIdV4
                    );
            }
            if (!NT_SUCCESS(status))
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! \"FwpsFlowRemoveContext\" return %!STATUS!",
                    status
                    );
            }
            WdfObjectAcquireLock(g_wdfControlDevice);
            status = STATUS_NOT_IMPLEMENTED;
            break;
        }
        break;
    case DNSFIREWALL_IOCTL_READ_UDP_QUERY2:
        {
            ULONG   ulUdpQueryDataLength;
            PBYTE   pOutputBuffer;
            switch (pFlowContext->enContextState)
            {
            case csNotRun:
            case csFlowDeleted:
            case csUdpFlowBreak:
                ulInformation = 0;
                status = STATUS_END_OF_FILE;
                TraceEvents(
                    TRACE_LEVEL_WARNING,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! The flow-delete state has been reached."
                    );
                goto _break_read_udp_query2;
            case csWaitQueryStarted:
                ulInformation = 0;
                status = STATUS_END_OF_FILE;
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! The flow-delete state has been reached on state = \"csWaitQueryStarted\"."
                    );
                //ASSERT(FALSE);
                goto _break_read_udp_query2;
            case csDnsError:
                ulInformation = 0;
                status = STATUS_UNSUCCESSFUL;
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! The flow-error state has been reached."
                    );
                goto _break_read_udp_query2;
            case csUdpWaitReadQuery2:
                break;
            default:
                ulInformation = 0;
                status = STATUS_END_OF_FILE;
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid Context State = %u",
                    pFlowContext->enContextState
                    );
                goto _break_read_udp_query2;
            }
            ulUdpQueryDataLength = pFlowContext->ulUdpQuery2DataLength;
            if (OutputBufferLength != (size_t) ulUdpQueryDataLength)
            {
                status = STATUS_INVALID_PARAMETER;
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid length of output buffer"
                    );
                break;
            }
            pNetBufferList = pFlowContext->pUdpQuery2BufferList;
            pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList);
            status = WdfRequestRetrieveOutputMemory(
                Request,
                &wdfOutputMemory
                );
            if (!NT_SUCCESS(status))
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"WdfRequestRetrieveOutputMemory\" failed with status = %!STATUS!",
                    status
                    );
                break;
            }
            pOutputBuffer = (PBYTE) WdfMemoryGetBuffer(
                wdfOutputMemory,
                NULL
                );
            pInboundData = (PBYTE) NdisGetDataBuffer(
                pNetBuffer,
                ulUdpQueryDataLength,
                pOutputBuffer,
                1,
                0
                );
            ASSERT(NULL != pInboundData);
            if (pOutputBuffer != pInboundData)
            {
                RtlCopyMemory(
                    pOutputBuffer,
                    pInboundData,
                    ulUdpQueryDataLength
                    );
            }
            ulInformation = ulUdpQueryDataLength;
            pFlowContext->enContextState = csUdpWaitApprovalQuery2;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
        }
    _break_read_udp_query2:
        break;
    case DNSFIREWALL_IOCTL_APPROVAL_UDP_QUERY2:
        if (csNotRun == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-delete state has been reached."
                );
            break;
        }
        if (csFlowDeleted == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-delete state has been reached."
                );
            break;
        }
        if (csDnsError == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_UNSUCCESSFUL;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-error state has been reached."
                );
            break;
        }
        if (sizeof(UDP_QUERY_APPROVAL) != InputBufferLength)
        {
            status = STATUS_INVALID_PARAMETER;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid length of input buffer"
                );
            break;
        }
        if (csUdpWaitApprovalQuery2 != pFlowContext->enContextState)
        {
            TraceEvents(
                TRACE_LEVEL_WARNING,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid Context State = %u",
                pFlowContext->enContextState
                );
        }
        pNetBufferList = pFlowContext->pUdpQuery2BufferList;
        status = WdfRequestRetrieveInputBuffer(
            Request,
            InputBufferLength,
            (PVOID*) &pUdpQueryApproval,
            NULL
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfRequestRetrieveInputBuffer failed %!STATUS!",
                status
                );
            break;
        }
        switch (pUdpQueryApproval->_enApprovalAction)
        {
        case aaDiscard:
            FwpsDereferenceNetBufferList(
                pNetBufferList,
                FALSE
                );
            pFlowContext->pUdpQuery2BufferList = NULL;
            ulInformation = InputBufferLength;
            if (csUdpWaitApprovalQuery2 != pFlowContext->enContextState)
            {
                TraceEvents(
                    TRACE_LEVEL_WARNING,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid Context State = %u",
                    pFlowContext->enContextState
                    );
            }
            pFlowContext->enContextState = csUdpQuery2Discarded;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            if (pFlowContext->bUdpQuery1Discarded || pFlowContext->bUdpFlow1Done)
            {
                WdfObjectReleaseLock(g_wdfControlDevice);
                if (pFlowContext->bIPv6)
                {
                    status = FwpsFlowRemoveContext(
                        pFlowContext->flowHandle,
                        FWPS_LAYER_DATAGRAM_DATA_V6,
                        g_nDatagramIdV6
                        );
                }
                else
                {
                    status = FwpsFlowRemoveContext(
                        pFlowContext->flowHandle,
                        FWPS_LAYER_DATAGRAM_DATA_V4,
                        g_nDatagramIdV4
                        );
                }
                if (!NT_SUCCESS(status))
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"FwpsFlowRemoveContext\" return %!STATUS!",
                        status
                        );
                }
                WdfObjectAcquireLock(g_wdfControlDevice);
                if (STATUS_PENDING == status)
                {
                    status = STATUS_SUCCESS;
                }
            }
            break;
        case aaPermit:
            {
                UINT32              ulNblOffset;
                UINT32              ulDataOffset;
                UINT32              ulIpHeaderSize;
                UINT32              ulTransportHeaderSize;
                UINT32              ulIpHeaderRetreat;
                PNET_BUFFER_LIST    pCloneBufferList;
                NDIS_STATUS         nStatus;
                pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList);
                ulNblOffset = NET_BUFFER_DATA_OFFSET(pNetBuffer);
                ulDataOffset = pFlowContext->ulUdpQuery2DataOffset;
                ulIpHeaderSize = pFlowContext->ulUdpQuery2IpHeaderSize;
                ulTransportHeaderSize = pFlowContext->ulUdpQuery2TransportHeaderSize;
                if (csUdpWaitApprovalQuery2 != pFlowContext->enContextState)
                {
                    TraceEvents(
                        TRACE_LEVEL_WARNING,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! Invalid Context State = %u",
                        pFlowContext->enContextState
                        );
                }
                //
                // The TCP/IP stack could have retreated the net buffer list by the 
                // transportHeaderSize amount; detect the condition here to avoid
                // retreating twice.
                //
                if (ulNblOffset != ulDataOffset)
                {
                    ASSERT(ulDataOffset - ulNblOffset == ulTransportHeaderSize);
                    ulTransportHeaderSize = 0;
                }
                ulIpHeaderRetreat = ulIpHeaderSize + ulTransportHeaderSize;
                //
                // Adjust the net buffer list offset to the start of the IP header.
                //
                nStatus = NdisRetreatNetBufferDataStart(
                    pNetBuffer,
                    ulIpHeaderRetreat,
                    0,
                    NULL
                    );
                //
                // Note that the clone will inherit the original net buffer list's offset.
                //
                status = FwpsAllocateCloneNetBufferList(
                    pNetBufferList,
                    g_hNetBufferListPool,
                    NULL,
                    0,
                    &pCloneBufferList
                    );
                //
                // Undo the adjustment on the original net buffer list.
                //
                NdisAdvanceNetBufferDataStart(
                    pNetBuffer,
                    ulIpHeaderRetreat,
                    FALSE,
                    NULL
                    );
                FwpsDereferenceNetBufferList(
                    pNetBufferList,
                    FALSE
                    );
                pFlowContext->pUdpQuery2BufferList = NULL;
                if (!NT_SUCCESS(status))
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! FwpsAllocateCloneNetBufferList failed %!STATUS!",
                        status
                        );
                    break;
                }
                pFlowContext->enContextState = csUdpPermitQuery2Pended;
                push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
                pFlowContext->pUdpQuery2CloneBufferList = pCloneBufferList;
                _pIrp = WdfRequestWdmGetIrp(Request);
                IoMarkIrpPending(_pIrp);
                WDF_IO_QUEUE_CONFIG_INIT(
                    &stFlowContextQueueConfig,
                    WdfIoQueueDispatchManual
                    );
                status = WdfIoQueueCreate(
                    g_wdfControlDevice,
                    &stFlowContextQueueConfig,
                    WDF_NO_OBJECT_ATTRIBUTES,
                    &pFlowContext->wdfUdpQuery2PermitQueue
                    );
                ASSERT(NT_SUCCESS(status));
                status = WdfRequestForwardToIoQueue(
                    Request,
                    pFlowContext->wdfUdpQuery2PermitQueue
                    );
                ASSERT(NT_SUCCESS(status));
                if (IsListHeadBad(&g_stPendedPacketsQueue))
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_QUEUE,
                        "%!FUNC! line:%!LINE! %!LEVEL! Bad \"PendedPacketsQueue\" Header!!"
                        );
                    InitializeListHead(&g_stPendedPacketsQueue);
                }
                bSignalWorkerThread = IsListEmpty(&g_stPendedPacketsQueue);
                InsertTailList(&g_stPendedPacketsQueue, &pFlowContext->threadListEntry);
                WdfObjectReleaseLock(g_wdfControlDevice);
                if (bSignalWorkerThread)
                {
                    KeSetEvent(
                        &g_oWorkerEvent,
                        0,
                        FALSE
                        );
                }
            }
            return;
        case aaModify:
            ulInformation = InputBufferLength;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! UDP Query Replace operation not Implemented"
                );
            pFlowContext->enContextState = csUdpWaitReplaceQuery2;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            WdfObjectReleaseLock(g_wdfControlDevice);
            if (pFlowContext->bIPv6)
            {
                status = FwpsFlowRemoveContext(
                    pFlowContext->flowHandle,
                    FWPS_LAYER_DATAGRAM_DATA_V6,
                    g_nDatagramIdV6
                    );
            }
            else
            {
                status = FwpsFlowRemoveContext(
                    pFlowContext->flowHandle,
                    FWPS_LAYER_DATAGRAM_DATA_V4,
                    g_nDatagramIdV4
                    );
            }
            if (!NT_SUCCESS(status))
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! \"FwpsFlowRemoveContext\" return %!STATUS!",
                    status
                    );
            }
            WdfObjectAcquireLock(g_wdfControlDevice);
            status = STATUS_NOT_IMPLEMENTED;
            break;
        }
        break;
    case DNSFIREWALL_IOCTL_GET_FLOW_STATUS:
        if (OutputBufferLength != sizeof(FLOW_DATA))
        {
            status = STATUS_INVALID_PARAMETER;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid length of output buffer"
                );
            break;
        }
        status = WdfRequestRetrieveOutputMemory(
            Request,
            &wdfOutputMemory
            );
        ASSERT(NT_SUCCESS(status));
        status = WdfMemoryCopyFromBuffer(
            wdfOutputMemory,
            0,
            pFlowContext,
            sizeof(FLOW_DATA)
            );
        ASSERT(NT_SUCCESS(status));
        ulInformation = sizeof(FLOW_DATA);
        break;
    case DNSFIREWALL_IOCTL_READ_TCP_QUERY:
        if (csNotRun == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-delete state has been reached."
                );
            break;
        }
        if (csFlowDeleted == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Flow context deleted"
                );
            break;
        }
        if (csDnsError == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_UNSUCCESSFUL;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-error state has been reached."
                );
            break;
        }
        if (OutputBufferLength != pFlowContext->nTcpQueryLength)
        {
            status = STATUS_INVALID_PARAMETER;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid length of output buffer"
                );
            break;
        }
        pNetBufferList = pFlowContext->pTcpQueryBufferList;
        ASSERT(NULL != pNetBufferList);
        status = WdfRequestRetrieveOutputBuffer(
            Request,
            OutputBufferLength,
            (PVOID*) &pOutputBuffer,
            &nByteLength
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfRequestRetrieveOutputBuffer failed %!STATUS!",
                status
                );
            break;
        }
        ASSERT(nByteLength == OutputBufferLength);
        GetStreamData(
            pNetBufferList,
            pOutputBuffer,
            (UINT32) OutputBufferLength,
            (UINT32*) &nByteLength
            );
        ASSERT(nByteLength == OutputBufferLength);
        ulInformation = OutputBufferLength;
        switch (pFlowContext->enContextState)
        {
        case csTcpWaitReadQuery:
        case csTcpWaitResponceInjectDone:
            break;
        default:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
                );
            break;
        }
        pFlowContext->enContextState = csTcpWaitApprovalQuery;
        push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
        break;
    case DNSFIREWALL_IOCTL_WAIT_TCP_RESPONCE:
        if (csNotRun == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-delete state has been reached."
                );
            break;
        }
        if (csFlowDeleted == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Flow context deleted"
                );
            break;
        }
        if (csDnsError == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_UNSUCCESSFUL;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-error state has been reached."
                );
            break;
        }
        if (sizeof(FLOW_DATA) != OutputBufferLength)
        {
            status = STATUS_INVALID_PARAMETER;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid length of output buffer"
                );
            break;
        }
        _pIrp = WdfRequestWdmGetIrp(Request);
        IoMarkIrpPending(_pIrp);
        switch (pFlowContext->enContextState)
        {
        case csTcpWaitApprovalQuery:
        case csTcpWaitReplaceQuery:
        case csTcpQueryDiscarded:
            break;
        default:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
                );
            break;
        }
        pFlowContext->enContextState = csTcpWaitResponceStarted;
        push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
        WDF_IO_QUEUE_CONFIG_INIT(
            &stFlowContextQueueConfig,
            WdfIoQueueDispatchManual
            );
        status = WdfIoQueueCreate(
            g_wdfControlDevice,
            &stFlowContextQueueConfig,
            WDF_NO_OBJECT_ATTRIBUTES,
            &pFlowContext->wdfWaitTcpResponceQueue
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfIoQueueCreate failed with status %!STATUS!",
                status
                );
            break;
        }
        status = WdfRequestForwardToIoQueue(
            Request,
            pFlowContext->wdfWaitTcpResponceQueue
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfRequestForwardToIoQueue failed with status %!STATUS!",
                status
                );
            break;
        }
        WdfObjectReleaseLock(g_wdfControlDevice);
        return;
    case DNSFIREWALL_IOCTL_APPROVAL_TCP_QUERY:
        if (csNotRun == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-delete state has been reached."
                );
            break;
        }
        if (csFlowDeleted == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Flow context deleted"
                );
            break;
        }
        if (csDnsError == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_UNSUCCESSFUL;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-error state has been reached."
                );
            break;
        }
        if (sizeof(TCP_QUERY_APPROVAL) != InputBufferLength)
        {
            status = STATUS_INVALID_PARAMETER;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid length of input buffer"
                );
            break;
        }
        pNetBufferList = pFlowContext->pTcpQueryBufferList;
        status = WdfRequestRetrieveInputBuffer(
            Request,
            InputBufferLength,
            (PVOID*) &pTcpQueryApproval,
            NULL
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfRequestRetrieveInputBuffer failed %!STATUS!",
                status
                );
            break;
        }
        switch (pTcpQueryApproval->_enApprovalAction)
        {
        case aaPermit:
            if (csTcpWaitResponceStarted != pFlowContext->enContextState)
            {
                TraceEvents(
                    TRACE_LEVEL_WARNING,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid Context State = %u",
                    pFlowContext->enContextState
                    );
            }
            pFlowContext->enContextState = csTcpPermitQueryPended;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            _pIrp = WdfRequestWdmGetIrp(Request);
            IoMarkIrpPending(_pIrp);
            WDF_IO_QUEUE_CONFIG_INIT(
                &stFlowContextQueueConfig,
                WdfIoQueueDispatchManual
                );
            status = WdfIoQueueCreate(
                g_wdfControlDevice,
                &stFlowContextQueueConfig,
                WDF_NO_OBJECT_ATTRIBUTES,
                &pFlowContext->wdfTcpQueryPermitQueue
                );
            ASSERT(NT_SUCCESS(status));
            status = WdfRequestForwardToIoQueue(
                Request,
                pFlowContext->wdfTcpQueryPermitQueue
                );
            ASSERT(NT_SUCCESS(status));
            if (IsListHeadBad(&g_stPendedPacketsQueue))
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Bad \"PendedPacketsQueue\" Header!!"
                    );
                InitializeListHead(&g_stPendedPacketsQueue);
            }
            bSignalWorkerThread = IsListEmpty(&g_stPendedPacketsQueue);
            InsertTailList(&g_stPendedPacketsQueue, &pFlowContext->threadListEntry);
            WdfObjectReleaseLock(g_wdfControlDevice);
            if (bSignalWorkerThread)
            {
                KeSetEvent(
                    &g_oWorkerEvent,
                    0,
                    FALSE
                    );
            }
            return;
        case aaModify:
            FwpsDiscardClonedStreamData(
                pNetBufferList,
                0,
                FALSE
                );
            pFlowContext->pTcpQueryBufferList = NULL;
            ulInformation = InputBufferLength;
            switch (pFlowContext->enContextState)
            {
            case csTcpWaitResponceStarted:
            //case csTcpWaitApprovalQuery:
                break;
            default:
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
                    );
                break;
            }
            pFlowContext->enContextState = csTcpWaitReplaceQuery;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            break;
        case aaDiscard:
            FwpsDiscardClonedStreamData(
                pNetBufferList,
                0,
                FALSE
                );
            pFlowContext->pTcpQueryBufferList = NULL;
            ulInformation = InputBufferLength;
            if (csTcpWaitApprovalQuery != pFlowContext->enContextState)
            {
                TraceEvents(
                    TRACE_LEVEL_WARNING,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid Context State = %u",
                    pFlowContext->enContextState
                    );
            }
            pFlowContext->enContextState = csTcpQueryDiscarded;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            WdfObjectReleaseLock(g_wdfControlDevice);
            if (pFlowContext->bIPv6)
            {
                status = FwpsFlowRemoveContext(
                    pFlowContext->flowHandle,
                    FWPS_LAYER_STREAM_V6,
                    g_nStreamIdV6
                    );
            }
            else
            {
                status = FwpsFlowRemoveContext(
                    pFlowContext->flowHandle,
                    FWPS_LAYER_STREAM_V4,
                    g_nStreamIdV4
                    );
            }
            WdfObjectAcquireLock(g_wdfControlDevice);
            if (!NT_SUCCESS(status))
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! \"FwpsFlowRemoveContext\" return %!STATUS!",
                    status
                    );
            }
            if (STATUS_PENDING == status)
            {
                status = STATUS_SUCCESS;
            }
            break;
        }
        break;
    case DNSFIREWALL_IOCTL_INJECT_TCP_QUERY:
        if (csNotRun == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-delete state has been reached."
                );
            break;
        }
        if (csFlowDeleted == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Flow context deleted"
                );
            break;
        }
        if (csDnsError == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_UNSUCCESSFUL;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-error state has been reached."
                );
            break;
        }
        if (0 == InputBufferLength)
        {
            status = STATUS_INVALID_PARAMETER;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid length of input buffer"
                );
            break;
        }
        if (csFlowDeleted == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Flow context deleted"
                );
            break;
        }
        if (csDnsError == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_UNSUCCESSFUL;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-error state has been reached."
                );
            break;
        }
        status = WdfRequestRetrieveInputBuffer(
            Request,
            InputBufferLength,
            (PVOID*) &pInputBuffer,
            &nByteLength
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfRequestRetrieveInputBuffer failed %!STATUS!",
                status
                );
            break;
        }
        ASSERT(nByteLength == InputBufferLength);
        {
            pDataCopy = (PBYTE) ExAllocatePoolWithTag(
                NonPagedPool,
                InputBufferLength,
                POOL_TAG
                );
            ASSERT(NULL != pDataCopy);
            RtlCopyMemory(
                pDataCopy,
                pInputBuffer,
                InputBufferLength
                );
            _pMdl = IoAllocateMdl(
                pDataCopy,
                (ULONG) InputBufferLength,
                FALSE,
                FALSE,
                NULL
                );
            ASSERT(NULL != _pMdl);
            MmBuildMdlForNonPagedPool(_pMdl);
            status = FwpsAllocateNetBufferAndNetBufferList(
                g_hNetBufferListPool,
                0,
                0,
                _pMdl,
                0,
                InputBufferLength,
                &pNetBufferList
                );
            if (!NT_SUCCESS(status))
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! FwpsAllocateNetBufferAndNetBufferList failed %!STATUS!",
                    status
                    );
                IoFreeMdl(_pMdl);
                ExFreePoolWithTag(
                    _pMdl->MappedSystemVa,
                    POOL_TAG
                    );
                break;
            }
            ASSERT(NULL != pNetBufferList);
            pFlowContext->pTcpQueryMdl = _pMdl;
            pFlowContext->pTcpQueryInjectBufferList = pNetBufferList;
            pFlowContext->nTcpQueryInjectDataSize = InputBufferLength;
            switch (pFlowContext->enContextState)
            {
            case csTcpWaitReplaceQuery:
                break;
            default:
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
                    );
                break;
            }
            pFlowContext->enContextState = csTcpReplaceQueryPended;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            _pIrp = WdfRequestWdmGetIrp(Request);
            IoMarkIrpPending(_pIrp);
            WDF_IO_QUEUE_CONFIG_INIT(
                &stFlowContextQueueConfig,
                WdfIoQueueDispatchManual
                );
            status = WdfIoQueueCreate(
                g_wdfControlDevice,
                &stFlowContextQueueConfig,
                WDF_NO_OBJECT_ATTRIBUTES,
                &pFlowContext->wdfTcpQueryInjectQueue
                );
            ASSERT(NT_SUCCESS(status));
            status = WdfRequestForwardToIoQueue(
                Request,
                pFlowContext->wdfTcpQueryInjectQueue
                );
            ASSERT(NT_SUCCESS(status));
            if (IsListHeadBad(&g_stPendedPacketsQueue))
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Bad \"PendedPacketsQueue\" Header!!"
                    );
                InitializeListHead(&g_stPendedPacketsQueue);
            }
            bSignalWorkerThread = IsListEmpty(&g_stPendedPacketsQueue);
            InsertTailList(&g_stPendedPacketsQueue, &pFlowContext->threadListEntry);
            WdfObjectReleaseLock(g_wdfControlDevice);
            if (bSignalWorkerThread)
            {
                KeSetEvent(
                    &g_oWorkerEvent,
                    0,
                    FALSE
                    );
            }
        }
        return;
    case DNSFIREWALL_IOCTL_READ_TCP_RESPONCE:
        if (csNotRun == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-delete state has been reached."
                );
            break;
        }
        if (csFlowDeleted == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-delete state has been reached."
                );
            break;
        }
        if (csDnsError == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_UNSUCCESSFUL;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-error state has been reached."
                );
            break;
        }
        nResponceLength = pFlowContext->nTcpResponceLength;
        if (OutputBufferLength != nResponceLength)
        {
            status = STATUS_INVALID_PARAMETER;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid length of output buffer"
                );
            break;
        }
        pNetBufferList = pFlowContext->pTcpResponceBufferList;
        ASSERT(NULL != pNetBufferList);
        pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList);
        status = WdfRequestRetrieveOutputBuffer(
            Request,
            OutputBufferLength,
            (PVOID*) &pOutputBuffer,
            &nByteLength
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfRequestRetrieveOutputBuffer failed %!STATUS!",
                status
                );
            break;
        }
        ASSERT(nByteLength == OutputBufferLength);
        GetStreamData(
            pNetBufferList,
            pOutputBuffer,
            (UINT32) OutputBufferLength,
            (UINT32*) &nByteLength
            );
        ASSERT(nByteLength == OutputBufferLength);
        ulInformation = OutputBufferLength;
        if (csTcpWaitReadResponce != pFlowContext->enContextState)
        {
            TraceEvents(
                TRACE_LEVEL_WARNING,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid Context State = %u",
                pFlowContext->enContextState
                );
        }
        pFlowContext->enContextState = csTcpWaitApprovalResponce;
        push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
        break;
    case DNSFIREWALL_IOCTL_WAIT_TCP_NEW_MESSAGE:
        if (csNotRun == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-delete state has been reached."
                );
            break;
        }
        if (csDnsError == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_UNSUCCESSFUL;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-error state has been reached."
                );
            break;
        }
        if (sizeof(FLOW_DATA) != OutputBufferLength)
        {
            status = STATUS_INVALID_PARAMETER;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid length of output buffer"
                );
            break;
        }
        if (csFlowDeleted == pFlowContext->enContextState)
        {
            status = WdfRequestRetrieveOutputMemory(
                Request,
                &wdfOutputMemory
                );
            ASSERT(NT_SUCCESS(status));
            status = WdfMemoryCopyFromBuffer(
                wdfOutputMemory,
                0,
                pFlowContext,
                sizeof(FLOW_DATA)
                );
            ASSERT(NT_SUCCESS(status));
            ulInformation = sizeof(FLOW_DATA);
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Flow context deleted"
                );
            break;
        }
        _pIrp = WdfRequestWdmGetIrp(Request);
        IoMarkIrpPending(_pIrp);
        switch (pFlowContext->enContextState)
        {
        case csTcpWaitApprovalResponce:
        case csTcpWaitReplaceResponce:
        case csTcpResponceDiscarded:
            break;
        default:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
                );
            break;
        }
        pFlowContext->enContextState = csTcpWaitNewMessageStarted;
        push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
        WDF_IO_QUEUE_CONFIG_INIT(
            &stFlowContextQueueConfig,
            WdfIoQueueDispatchManual
            );
        status = WdfIoQueueCreate(
            g_wdfControlDevice,
            &stFlowContextQueueConfig,
            WDF_NO_OBJECT_ATTRIBUTES,
            &pFlowContext->wdfWaitTcpNewMessageQueue
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfIoQueueCreate failed with status %!STATUS!",
                status
                );
            break;
        }
        status = WdfRequestForwardToIoQueue(
            Request,
            pFlowContext->wdfWaitTcpNewMessageQueue
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfRequestForwardToIoQueue failed with status %!STATUS!",
                status
                );
            break;
        }
        WdfObjectReleaseLock(g_wdfControlDevice);
        return;
    case DNSFIREWALL_IOCTL_APPROVAL_TCP_RESPONCE:
        if (csNotRun == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-delete state has been reached."
                );
            break;
        }
        if (csFlowDeleted == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Flow context deleted"
                );
            break;
        }
        if (csDnsError == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_UNSUCCESSFUL;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-error state has been reached."
                );
            break;
        }
        if (sizeof(TCP_RESPONCE_APPROVAL) != InputBufferLength)
        {
            status = STATUS_INVALID_PARAMETER;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid length of input buffer"
                );
            break;
        }
        pNetBufferList = pFlowContext->pTcpResponceBufferList;
        status = WdfRequestRetrieveInputBuffer(
            Request,
            InputBufferLength,
            (PVOID*) &pTcpResponceApproval,
            NULL
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfRequestRetrieveInputBuffer failed %!STATUS!",
                status
                );
            break;
        }
        switch (pTcpResponceApproval->_enApprovalAction)
        {
        case aaPermit:
            if (csTcpWaitNewMessageStarted != pFlowContext->enContextState)
            {
                TraceEvents(
                    TRACE_LEVEL_WARNING,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid Context State = %u",
                    pFlowContext->enContextState
                    );
            }
            pFlowContext->enContextState = csTcpPermitResponcePended;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            _pIrp = WdfRequestWdmGetIrp(Request);
            IoMarkIrpPending(_pIrp);
            WDF_IO_QUEUE_CONFIG_INIT(
                &stFlowContextQueueConfig,
                WdfIoQueueDispatchManual
                );
            status = WdfIoQueueCreate(
                g_wdfControlDevice,
                &stFlowContextQueueConfig,
                WDF_NO_OBJECT_ATTRIBUTES,
                &pFlowContext->wdfTcpResponcePermitQueue
                );
            ASSERT(NT_SUCCESS(status));
            status = WdfRequestForwardToIoQueue(
                Request,
                pFlowContext->wdfTcpResponcePermitQueue
                );
            ASSERT(NT_SUCCESS(status));
            if (IsListHeadBad(&g_stPendedPacketsQueue))
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Bad \"PendedPacketsQueue\" Header!!"
                    );
                InitializeListHead(&g_stPendedPacketsQueue);
            }
            bSignalWorkerThread = IsListEmpty(&g_stPendedPacketsQueue);
            InsertTailList(&g_stPendedPacketsQueue, &pFlowContext->threadListEntry);
            WdfObjectReleaseLock(g_wdfControlDevice);
            if (bSignalWorkerThread)
            {
                KeSetEvent(
                    &g_oWorkerEvent,
                    0,
                    FALSE
                    );
            }
            return;
        case aaModify:
            FwpsDiscardClonedStreamData(
                pNetBufferList,
                0,
                FALSE
                );
            pFlowContext->pTcpResponceBufferList = NULL;
            ulInformation = InputBufferLength;
            if (csTcpWaitApprovalResponce != pFlowContext->enContextState)
            {
                TraceEvents(
                    TRACE_LEVEL_WARNING,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid Context State = %u",
                    pFlowContext->enContextState
                    );
            }
            pFlowContext->enContextState = csTcpWaitReplaceResponce;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            break;
        case aaDiscard:
            FwpsDiscardClonedStreamData(
                pNetBufferList,
                0,
                FALSE
                );
            pFlowContext->pTcpResponceBufferList = NULL;
            ulInformation = InputBufferLength;
            if (csTcpWaitApprovalResponce != pFlowContext->enContextState)
            {
                TraceEvents(
                    TRACE_LEVEL_WARNING,
                    TRACE_QUEUE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid Context State = %u",
                    pFlowContext->enContextState
                    );
            }
            pFlowContext->enContextState = csTcpResponceDiscarded;
            push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
            break;
        }
        break;
    case DNSFIREWALL_IOCTL_INJECT_TCP_RESPONCE:
        if ((csFlowDeleted == pFlowContext->enContextState)
            || (csNotRun == pFlowContext->enContextState))
        {
            ulInformation = 0;
            status = STATUS_END_OF_FILE;
            //TraceEvents(
            //    TRACE_LEVEL_WARNING,
            //    TRACE_QUEUE,
            //    "%!FUNC! line:%!LINE! %!LEVEL! Flow context deleted"
            //    );
            break;
        }
        if (csDnsError == pFlowContext->enContextState)
        {
            ulInformation = 0;
            status = STATUS_UNSUCCESSFUL;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! The flow-error state has been reached."
                );
            break;
        }
        if (0 == InputBufferLength)
        {
            status = STATUS_INVALID_PARAMETER;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid length of input buffer"
                );
            break;
        }
        status = WdfRequestRetrieveInputBuffer(
            Request,
            InputBufferLength,
            (PVOID*) &pInputBuffer,
            &nByteLength
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! WdfRequestRetrieveInputBuffer failed %!STATUS!",
                status
                );
            break;
        }
        ASSERT(nByteLength == InputBufferLength);
        pDataCopy = (PBYTE) ExAllocatePoolWithTag(
            NonPagedPool,
            InputBufferLength,
            POOL_TAG
            );
        ASSERT(NULL != pDataCopy);
        RtlCopyMemory(
            pDataCopy,
            pInputBuffer,
            InputBufferLength
            );
        _pMdl = IoAllocateMdl(
            pDataCopy,
            (ULONG) InputBufferLength,
            FALSE,
            FALSE,
            NULL
            );
        ASSERT(NULL != _pMdl);
        MmBuildMdlForNonPagedPool(_pMdl);
        status = FwpsAllocateNetBufferAndNetBufferList(
            g_hNetBufferListPool,
            0,
            0,
            _pMdl,
            0,
            InputBufferLength,
            &pNetBufferList
            );
        if (!NT_SUCCESS(status))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! FwpsAllocateNetBufferAndNetBufferList failed %!STATUS!",
                status
                );
            IoFreeMdl(_pMdl);
            ExFreePoolWithTag(
                _pMdl->MappedSystemVa,
                POOL_TAG
                );
            break;
        }
        ASSERT(NULL != pNetBufferList);
        switch (pFlowContext->enContextState)
        {
        case csTcpWaitNewMessageStarted:
        case csTcpReplaceResponceDone:
        case csTcpWaitReplaceResponce:
        case csTcpResponceDiscarded:
            break;
        default:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine = %u",
                pFlowContext->enContextState
                );
            break;
        }
        pFlowContext->enContextState = csTcpReplaceResponcePended;
        push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
        pFlowContext->pTcpResponceMdl = _pMdl;
        pFlowContext->pTcpResponceInjectBufferList = pNetBufferList;
        pFlowContext->nTcpResponceInjectDataSize = InputBufferLength;
        _pIrp = WdfRequestWdmGetIrp(Request);
        IoMarkIrpPending(_pIrp);
        WDF_IO_QUEUE_CONFIG_INIT(
            &stFlowContextQueueConfig,
            WdfIoQueueDispatchManual
            );
        status = WdfIoQueueCreate(
            g_wdfControlDevice,
            &stFlowContextQueueConfig,
            WDF_NO_OBJECT_ATTRIBUTES,
            &pFlowContext->wdfTcpResponceInjectQueue
            );
        ASSERT(NT_SUCCESS(status));
        status = WdfRequestForwardToIoQueue(
            Request,
            pFlowContext->wdfTcpResponceInjectQueue
            );
        ASSERT(NT_SUCCESS(status));
        if (IsListHeadBad(&g_stPendedPacketsQueue))
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_QUEUE,
                "%!FUNC! line:%!LINE! %!LEVEL! Bad \"PendedPacketsQueue\" Header!!"
                );
            InitializeListHead(&g_stPendedPacketsQueue);
        }
        bSignalWorkerThread = IsListEmpty(&g_stPendedPacketsQueue);
        InsertTailList(&g_stPendedPacketsQueue, &pFlowContext->threadListEntry);
        WdfObjectReleaseLock(g_wdfControlDevice);
        if (bSignalWorkerThread)
        {
            KeSetEvent(
                &g_oWorkerEvent,
                0,
                FALSE
                );
        }
        return;
    default:
        status = STATUS_INVALID_DEVICE_REQUEST;
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_QUEUE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid IO Control Code = 0x%08X",
            IoControlCode
            );
        break;
    }
    //_IO_CtlDone:
    WdfRequestCompleteWithInformation(
        Request,
        status,
        ulInformation
        );
    WdfObjectReleaseLock(g_wdfControlDevice);
    return;
}
