/*++

Module Name:

    device.c - Device handling events for example driver.

Abstract:

   This file contains the device entry points and callbacks.
    
Environment:

    Kernel-mode Driver Framework

--*/

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

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


NTSTATUS
dnsfirewallCreateDevice(
    _Inout_ PWDFDEVICE_INIT DeviceInit
    )
/*++

Routine Description:

    Worker routine called to create a device and its software resources.

Arguments:

    DeviceInit - Pointer to an opaque init structure. Memory for this
                    structure will be freed by the framework when the WdfDeviceCreate
                    succeeds. So don't access the structure after that point.

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS status;
    WDF_FILEOBJECT_CONFIG   fileConfig;
    PAGED_CODE();
    DECLARE_CONST_UNICODE_STRING(ntDeviceName, DNSFIREWALL_DEVICE_NAME);
    DECLARE_CONST_UNICODE_STRING(symbolicLinkName, DNSFIREWALL_SYMBOLIC_NAME);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DEVICE,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered);
    status = WdfDeviceInitAssignName(DeviceInit, &ntDeviceName);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DEVICE,
            "%!FUNC! line:%!LINE! %!LEVEL! WdfDeviceInitAssignName failed %!STATUS!",
            status
            );
        goto End;
    }
    WdfControlDeviceInitSetShutdownNotification(
        DeviceInit,
        dnsfirewallShutdown,
        WdfDeviceShutdown
        );
    //
    // Initialize WDF_FILEOBJECT_CONFIG_INIT struct to tell the
    // framework whether you are interested in handling Create, Close and
    // Cleanup requests that gets generated when an application or another
    // kernel component opens an handle to the device. If you don't register
    // the framework default behaviour would be to complete these requests
    // with STATUS_SUCCESS. A driver might be interested in registering these
    // events if it wants to do security validation and also wants to maintain
    // per handle (fileobject) context.
    //
    WDF_FILEOBJECT_CONFIG_INIT(
        &fileConfig,
        dnsfirewallEvtDeviceFileCreate,
        dnsfirewallEvtFileClose,
        WDF_NO_EVENT_CALLBACK // not interested in Cleanup
        );
    fileConfig.FileObjectClass =
        (WDF_FILEOBJECT_CLASS) (WdfFileObjectWdfCannotUseFsContexts | WdfFileObjectCanBeOptional);
    WdfDeviceInitSetFileObjectConfig(
        DeviceInit,
        &fileConfig,
        WDF_NO_OBJECT_ATTRIBUTES
        );
    status = WdfDeviceCreate(
        &DeviceInit,
        WDF_NO_OBJECT_ATTRIBUTES,
        &g_wdfControlDevice
        );
    if (!NT_SUCCESS(status))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DEVICE,
            "%!FUNC! line:%!LINE! %!LEVEL! WdfDeviceCreate failed %!STATUS!",
            status
            );
        goto End;
    }
    //
    // Create a symbolic link for the control object so that usermode can open
    // the device.
    //
    status = WdfDeviceCreateSymbolicLink(
        g_wdfControlDevice,
        &symbolicLinkName
        );
    if (!NT_SUCCESS(status))
    {
        //
        // Control device will be deleted automatically by the framework.
        //
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DEVICE,
            "%!FUNC! line:%!LINE! %!LEVEL! WdfDeviceCreateSymbolicLink failed %!STATUS!",
            status
            );
        goto End;
    }
    //
    // Initialize the I/O Package and any Queues
    //
    status = dnsfirewallQueueInitialize(g_wdfControlDevice);
    if (!NT_SUCCESS(status))
    {
        //
        // Control g_wdfControlDevice will be deleted automatically by the framework.
        //
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DEVICE,
            "%!FUNC! line:%!LINE! %!LEVEL! dnsfirewallQueueInitialize failed %!STATUS!",
            status
            );
        goto End;
    }
    //
    // Control devices must notify WDF when they are done initializing.   I/O is
    // rejected until this call is made.
    //
    WdfControlFinishInitializing(g_wdfControlDevice);
End: ;
    //
    // If the device is created successfully, framework would clear the
    // DeviceInit value. Otherwise device create must have failed so we
    // should free the memory ourself.
    //
    if (DeviceInit != NULL)
    {
        WdfDeviceInitFree(DeviceInit);
    }
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DEVICE,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
    return status;
}

VOID
dnsfirewallShutdown(
WDFDEVICE Device
)
/*++

Routine Description:
Callback invoked when the machine is shutting down.  If you register for
a last chance shutdown notification you cannot do the following:
o Call any pageable routines
o Access pageable memory
o Perform any file I/O operations

If you register for a normal shutdown notification, all of these are
available to you.

This function implementation does nothing, but if you had any outstanding
file handles open, this is where you would close them.

Arguments:
Device - The device which registered the notification during init

Return Value:
None

--*/

{
    Device;
    BOOLEAN bSignalWorkerThread;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DEVICE,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    if (!g_bDriverUnload)
    {
        bSignalWorkerThread = IsListEmpty(&g_stPendedPacketsQueue);
        g_bDriverUnload = TRUE;
        if (bSignalWorkerThread)
        {
            KeSetEvent(
                &g_oWorkerEvent,
                0,
                FALSE
                );
        }
    }
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DEVICE,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
}

VOID
dnsfirewallEvtDeviceFileCreate(
    IN WDFDEVICE        Device,
    IN WDFREQUEST       Request,
    IN WDFFILEOBJECT    FileObject
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PUNICODE_STRING             fileName;
    WDFDEVICE                   wdfDeviceObject = Device;
    WDF_OBJECT_ATTRIBUTES       stFlowContextAttributes;
    PFLOW_DATA                  pFlowContext = NULL;
    USHORT                      length = 0;
    WDF_IO_QUEUE_CONFIG         stFlowContextQueueConfig;
    int                         nFileIndex, nThreadIndex, nSubstrIndex;
    wchar_t                     *pEndPtr, *pSubstr, sCopy[32];
    if (NULL == FileObject)
    {
        goto End;
    }
    //
    // Parsed filename has "\" in the begining. The object manager strips
    // of all "\", except one, after the device name.
    //
    fileName = WdfFileObjectGetFileName(FileObject);
    length = fileName->Length;
    if (length > 0)
    {
        pSubstr = wcschr(
            fileName->Buffer,
            L'@'
            );
#pragma warning(suppress: 28182)
        nFileIndex = wcstoul(
            &pSubstr[1],
            &pEndPtr,
            10
            );
        nSubstrIndex = (int)(pSubstr - fileName->Buffer);
#pragma warning(suppress: 28719)
        wcsncpy(
            sCopy,
            fileName->Buffer,
            nSubstrIndex
            );
        sCopy[nSubstrIndex] = L'\0';
        nThreadIndex = wcstoul(
            &sCopy[2],
            &pEndPtr,
            10
            );
    }
    else
    {
        goto End;
    }
    WdfObjectAcquireLock(wdfDeviceObject);
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&stFlowContextAttributes, FLOW_DATA);
    status = WdfObjectAllocateContext(
        FileObject,
        &stFlowContextAttributes,
        (PVOID*) &pFlowContext
        );
    if (!NT_SUCCESS(status))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DEVICE,
            "%!FUNC! line:%!LINE! %!LEVEL! WdfObjectAllocateContext failed with status %!STATUS!",
            status
            );
        WdfObjectReleaseLock(wdfDeviceObject);
        goto End;
    }
    pFlowContext->pThis = pFlowContext;
    pFlowContext->nFileIndex = nFileIndex;
    pFlowContext->nThreadIndex = nThreadIndex;
    pFlowContext->wdfFlowFileObject = FileObject;
    pFlowContext->enFlowDataOwner = foFreeFileList;
    push_fos(pFlowContext->arFlowDataOwnerStack, pFlowContext->enFlowDataOwner);
    InsertTailList(
        &g_stFreeFileList,
        &pFlowContext->listEntry
        );
    WDF_IO_QUEUE_CONFIG_INIT(
        &stFlowContextQueueConfig,
        WdfIoQueueDispatchSequential
        );
    stFlowContextQueueConfig.AllowZeroLengthRequests = TRUE;
    stFlowContextQueueConfig.EvtIoDeviceControl = flowControlEvtIoDeviceControl;
    status = WdfIoQueueCreate(
        Device,
        &stFlowContextQueueConfig,
        WDF_NO_OBJECT_ATTRIBUTES,
        &pFlowContext->wdfFlowQueue
        );
    if (!NT_SUCCESS(status))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DEVICE,
            "%!FUNC! line:%!LINE! %!LEVEL! WdfIoQueueCreate failed with status %!STATUS!",
            status
            );
        WdfObjectReleaseLock(wdfDeviceObject);
        goto End;
    }
    WdfObjectReleaseLock(wdfDeviceObject);
End:
    WdfRequestComplete(Request, status);
}

VOID
dnsfirewallEvtFileClose(
IN WDFFILEOBJECT    wdfFlowFileObject
)
{
    BOOLEAN                 bSignalWorkerThread;
    PLIST_ENTRY             pFileListEntry, pLastLE, pNextLE;
    PFLOW_DATA              pFlowData;
    PADDRESSES_DESCRIPTOR   pDeviceAddressesDescriptor = NULL;
    NTSTATUS                status = STATUS_SUCCESS;
    BOOLEAN                 bEmptyList, bFind, bEmpty;
    WDFQUEUE                wdfRequestQueue = NULL;
    WDFREQUEST              wdfPendedRequest = NULL;
    PNET_BUFFER_LIST        pNetBufferList;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DEVICE,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    WdfObjectAcquireLock(g_wdfControlDevice);
    if (NULL == wdfFlowFileObject)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DEVICE,
            "%!FUNC! line:%!LINE! %!LEVEL! g_bDriverUnload = %!bool!",
            g_bDriverUnload
            );
        if (!g_bDriverUnload)
        {
            bSignalWorkerThread = IsListEmpty(&g_stPendedPacketsQueue);
            g_bDriverUnload = TRUE;
            if (bSignalWorkerThread)
            {
                KeSetEvent(
                    &g_oWorkerEvent,
                    0,
                    FALSE
                    );
            }
            WdfObjectReleaseLock(g_wdfControlDevice);
            KeWaitForSingleObject(
                &g_oWorkerDoneEvent,
                Executive,
                KernelMode,
                FALSE,
                NULL
                );
            WdfObjectAcquireLock(g_wdfControlDevice);
        }
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DEVICE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (NULL == wdfFlowFileObject)"
            );
        WdfObjectReleaseLock(g_wdfControlDevice);
        return;
    }
    pFlowData = WdfObjectGetTypedContext(
        wdfFlowFileObject,
        FLOW_DATA
        );
    if (NULL == pFlowData)
    {
        if ((!IsListEmpty(&g_stFlowedFileList))
            || (!IsListEmpty(&g_stUdpALEFlowEstablishList))
            || (!IsListEmpty(&g_stUdpALEAcceptPendedList))
            || (!IsListEmpty(&g_stUdpAcceptNotifyList))
            || (!IsListEmpty(&g_stUdpALEAcceptRecvList))
            || (!IsListEmpty(&g_stUdpInboundList))
            || (!IsListEmpty(&g_stAcceptableList))
            || (!IsListEmpty(&g_stWaitForSetFilterFileList))
            || (!IsListEmpty(&g_stWaitForQuery2FileList))
            || (!IsListEmpty(&g_stWaitForDoneFileList)))
        {
            TraceEvents(
                TRACE_LEVEL_WARNING,
                TRACE_DEVICE,
                "%!FUNC! line:%!LINE! %!LEVEL! DNSFIREWALL Filters files not Cleared"
                );
            g_bFiltersChangeInProgress = TRUE;
            pDeviceAddressesDescriptor = WdfObjectGetTypedContext(
                g_wdfControlDevice,
                ADDRESSES_DESCRIPTOR
                );
            if (NULL != pDeviceAddressesDescriptor)
            {
                FilterDataCleanup(pDeviceAddressesDescriptor);
            }
            else
            {
                ASSERT(FALSE);
            }
        }
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DEVICE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (DNSFIREWALL Device was Closed)"
            );
        WdfObjectReleaseLock(g_wdfControlDevice);
        return;
    }
    // NULL != pFlowData
    switch (pFlowData->enContextState)
    {
    case csFlowDeleted:
    case csNotRun:
        break;
    case csWaitQueryStarted:
    case csUdpWaitSetFilterStarted:
    case csUdpWaitQuery2Started:
    case csWaitFlowDoneStarted:
        ASSERT(pFlowData == pFlowData->pThis);
        wdfRequestQueue = pFlowData->wdfWaitDnsQueryQueue;
        if (NULL != wdfRequestQueue)
        {
            pFlowData->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_DEVICE,
                    "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                    status
                    );
            }
            WdfObjectDelete(wdfRequestQueue);
        }
        wdfRequestQueue = pFlowData->wdfWaitUdpSetFirewallFilterQueue;
        if (NULL != wdfRequestQueue)
        {
            pFlowData->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_DEVICE,
                    "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                    status
                    );
            }
            WdfObjectDelete(wdfRequestQueue);
        }
        wdfRequestQueue = pFlowData->wdfWaitUdpQuery2Queue;
        if (NULL != wdfRequestQueue)
        {
            pFlowData->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_DEVICE,
                    "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                    status
                    );
            }
            WdfObjectDelete(wdfRequestQueue);
        }
        wdfRequestQueue = pFlowData->wdfWaitFlowDoneQueue;
        if (NULL != wdfRequestQueue)
        {
            pFlowData->wdfWaitFlowDoneQueue = 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_DEVICE,
                    "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                    status
                    );
            }
            WdfObjectDelete(wdfRequestQueue);
        }
        bEmptyList = RemoveEntryList(&pFlowData->listEntry);
        pFlowData->enContextState = csNotRun;
        push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
        break;
    default:
        if (!IsListEmpty(&g_stUdpALEFlowEstablishList))
        {
            pFileListEntry = &pFlowData->listEntry;
            pLastLE = g_stUdpALEFlowEstablishList.Blink;
            pNextLE = g_stUdpALEFlowEstablishList.Flink;
            bFind = FALSE;
            for (;;)
            {
                if (pNextLE == pFileListEntry)
                {
                    bFind = TRUE;
                    break;
                }
                if (pNextLE == pLastLE)
                {
                    break;
                }
                pNextLE = pNextLE->Flink;
            }
            if (bFind)
            {
                ASSERT(pFlowData == pFlowData->pThis);
                pFlowData->bUdpQueryClassifyPended = FALSE;
                pNetBufferList = pFlowData->pUdpQueryCloneBufferList;
                if (NULL != pNetBufferList)
                {
                    pFlowData->pUdpQueryCloneBufferList = NULL;
                    FwpsFreeCloneNetBufferList(
                        pNetBufferList,
                        0
                        );
                }
                pNetBufferList = pFlowData->pUdpQueryBufferList;
                if (NULL != pNetBufferList)
                {
                    pFlowData->pUdpQueryBufferList = NULL;
                    FwpsDereferenceNetBufferList(
                        pNetBufferList,
                        FALSE
                        );
                }
                if (csUdpPermitQueryPended == pFlowData->enContextState)
                {
                    if (IsListEntryInList(&pFlowData->threadListEntry, &g_stPendedPacketsQueue))
                    {
                        bEmpty = RemoveEntryList(&pFlowData->threadListEntry);
                    }
                }
                wdfRequestQueue = pFlowData->wdfWaitDnsQueryQueue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                wdfRequestQueue = pFlowData->wdfWaitUdpSetFirewallFilterQueue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                wdfRequestQueue = pFlowData->wdfWaitUdpQuery2Queue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                wdfRequestQueue = pFlowData->wdfWaitFlowDoneQueue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->wdfWaitFlowDoneQueue = 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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                bEmptyList = RemoveEntryList(&pFlowData->listEntry);
                pFlowData->enContextState = csNotRun;
                push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
                break;
            }
        }
        if (!IsListEmpty(&g_stUdpALEAcceptPendedList))
        {
            pFileListEntry = &pFlowData->listEntry;
            pLastLE = g_stUdpALEAcceptPendedList.Blink;
            pNextLE = g_stUdpALEAcceptPendedList.Flink;
            bFind = FALSE;
            for (;;)
            {
                if (pNextLE == pFileListEntry)
                {
                    bFind = TRUE;
                    break;
                }
                if (pNextLE == pLastLE)
                {
                    break;
                }
                pNextLE = pNextLE->Flink;
            }
            if (bFind)
            {
                ASSERT(pFlowData == pFlowData->pThis);
                pFlowData->bUdpQueryClassifyPended = FALSE;
                pNetBufferList = pFlowData->pUdpQueryCloneBufferList;
                if (NULL != pNetBufferList)
                {
                    pFlowData->pUdpQueryCloneBufferList = NULL;
                    FwpsFreeCloneNetBufferList(
                        pNetBufferList,
                        0
                        );
                }
                pNetBufferList = pFlowData->pUdpQueryBufferList;
                if (NULL != pNetBufferList)
                {
                    pFlowData->pUdpQueryBufferList = NULL;
                    FwpsDereferenceNetBufferList(
                        pNetBufferList,
                        FALSE
                        );
                }
                if (csUdpPermitQueryPended == pFlowData->enContextState)
                {
                    if (IsListEntryInList(&pFlowData->threadListEntry, &g_stPendedPacketsQueue))
                    {
                        bEmpty = RemoveEntryList(&pFlowData->threadListEntry);
                    }
                }
                wdfRequestQueue = pFlowData->wdfWaitDnsQueryQueue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                wdfRequestQueue = pFlowData->wdfWaitUdpSetFirewallFilterQueue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                wdfRequestQueue = pFlowData->wdfWaitUdpQuery2Queue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                wdfRequestQueue = pFlowData->wdfWaitFlowDoneQueue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->wdfWaitFlowDoneQueue = 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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                bEmptyList = RemoveEntryList(&pFlowData->listEntry);
                pFlowData->enContextState = csNotRun;
                push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
                break;
            }
        }
        if (!IsListEmpty(&g_stUdpAcceptNotifyList))
        {
            pFileListEntry = &pFlowData->listEntry;
            pLastLE = g_stUdpAcceptNotifyList.Blink;
            pNextLE = g_stUdpAcceptNotifyList.Flink;
            bFind = FALSE;
            for (;;)
            {
                if (pNextLE == pFileListEntry)
                {
                    bFind = TRUE;
                    break;
                }
                if (pNextLE == pLastLE)
                {
                    break;
                }
                pNextLE = pNextLE->Flink;
            }
            if (bFind)
            {
                ASSERT(pFlowData == pFlowData->pThis);
                wdfRequestQueue = pFlowData->wdfWaitDnsQueryQueue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                wdfRequestQueue = pFlowData->wdfWaitUdpSetFirewallFilterQueue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                wdfRequestQueue = pFlowData->wdfWaitUdpQuery2Queue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                wdfRequestQueue = pFlowData->wdfWaitFlowDoneQueue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->wdfWaitFlowDoneQueue = 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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                bEmptyList = RemoveEntryList(&pFlowData->listEntry);
                pFlowData->enContextState = csNotRun;
                push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
                break;
            }
        }
        if (!IsListEmpty(&g_stUdpALEAcceptRecvList))
        {
            pFileListEntry = &pFlowData->listEntry;
            pLastLE = g_stUdpALEAcceptRecvList.Blink;
            pNextLE = g_stUdpALEAcceptRecvList.Flink;
            bFind = FALSE;
            for (;;)
            {
                if (pNextLE == pFileListEntry)
                {
                    bFind = TRUE;
                    break;
                }
                if (pNextLE == pLastLE)
                {
                    break;
                }
                pNextLE = pNextLE->Flink;
            }
            if (bFind)
            {
                ASSERT(pFlowData == pFlowData->pThis);
                wdfRequestQueue = pFlowData->wdfWaitDnsQueryQueue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                wdfRequestQueue = pFlowData->wdfWaitUdpSetFirewallFilterQueue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                wdfRequestQueue = pFlowData->wdfWaitUdpQuery2Queue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                wdfRequestQueue = pFlowData->wdfWaitFlowDoneQueue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->wdfWaitFlowDoneQueue = 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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                bEmptyList = RemoveEntryList(&pFlowData->listEntry);
                pFlowData->enContextState = csNotRun;
                push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
                break;
            }
        }
        if (!IsListEmpty(&g_stUdpInboundList))
        {
            pFileListEntry = &pFlowData->listEntry;
            pLastLE = g_stUdpInboundList.Blink;
            pNextLE = g_stUdpInboundList.Flink;
            bFind = FALSE;
            for (;;)
            {
                if (pNextLE == pFileListEntry)
                {
                    bFind = TRUE;
                    break;
                }
                if (pNextLE == pLastLE)
                {
                    break;
                }
                pNextLE = pNextLE->Flink;
            }
            if (bFind)
            {
                ASSERT(pFlowData == pFlowData->pThis);
                pFlowData->bUdpQueryClassifyPended = FALSE;
                pNetBufferList = pFlowData->pUdpQuery2BufferList;
                if (NULL != pNetBufferList)
                {
                    pFlowData->pUdpQuery2BufferList = NULL;
                    FwpsDereferenceNetBufferList(
                        pNetBufferList,
                        FALSE
                        );
                }
                wdfRequestQueue = pFlowData->wdfWaitDnsQueryQueue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                wdfRequestQueue = pFlowData->wdfWaitUdpSetFirewallFilterQueue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                wdfRequestQueue = pFlowData->wdfWaitUdpQuery2Queue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                wdfRequestQueue = pFlowData->wdfWaitFlowDoneQueue;
                if (NULL != wdfRequestQueue)
                {
                    pFlowData->wdfWaitFlowDoneQueue = 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_DEVICE,
                            "%!FUNC! line:%!LINE! %!LEVEL! \"WdfIoQueueRetrieveNextRequest\" return %!STATUS!",
                            status
                            );
                    }
                    WdfObjectDelete(wdfRequestQueue);
                }
                bEmptyList = RemoveEntryList(&pFlowData->listEntry);
                pFlowData->enContextState = csNotRun;
                push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
                break;
            }
        }
        pFlowData->enContextState = csFlowDeleted;
        push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
        if (!IsListEmpty(&g_stFlowedFileList))
        {
            pFileListEntry = &pFlowData->listEntry;
            pLastLE = g_stFlowedFileList.Blink;
            pNextLE = g_stFlowedFileList.Flink;
            bFind = FALSE;
            for (;;)
            {
                if (pNextLE == pFileListEntry)
                {
                    bFind = TRUE;
                    break;
                }
                if (pNextLE == pLastLE)
                {
                    break;
                }
                pNextLE = pNextLE->Flink;
            }
            if (bFind)
            {
                WdfObjectReleaseLock(g_wdfControlDevice);
                if (IPPROTO_UDP == pFlowData->enIpProto)
                {
                    if (pFlowData->bIPv6)
                    {
                        status = FwpsFlowRemoveContext(
                            pFlowData->flowHandle,
                            FWPS_LAYER_DATAGRAM_DATA_V6,
                            g_nDatagramIdV6
                            );
                    }
                    else
                    {
                        status = FwpsFlowRemoveContext(
                            pFlowData->flowHandle,
                            FWPS_LAYER_DATAGRAM_DATA_V4,
                            g_nDatagramIdV4
                            );
                    }
                }
                else
                {
                    if (pFlowData->bIPv6)
                    {
                        status = FwpsFlowRemoveContext(
                            pFlowData->flowHandle,
                            FWPS_LAYER_STREAM_V6,
                            g_nStreamIdV6
                            );
                    }
                    else
                    {
                        status = FwpsFlowRemoveContext(
                            pFlowData->flowHandle,
                            FWPS_LAYER_STREAM_V4,
                            g_nStreamIdV4
                            );
                    }
                }
                if (!NT_SUCCESS(status))
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DEVICE,
                        "%!FUNC! line:%!LINE! %!LEVEL! \"FwpsFlowRemoveContext\" return %!STATUS!",
                        status
                        );
                }
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    TRACE_DEVICE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Exit (Remove Flow context)"
                    );
                return;
            }
            // !bFind
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DEVICE,
                "%!FUNC! line:%!LINE! %!LEVEL! DNSFIREWALL Wrong g_stFlowedFileList; nFileIndex = %d; nThreadIndex = %d; enContextState = %u;",
                pFlowData->nFileIndex,
                pFlowData->nThreadIndex,
                pFlowData->arContextStateStack[1]
                );
            break;
        }
        // IsListEmpty(&g_stFlowedFileList)
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DEVICE,
            "%!FUNC! line:%!LINE! %!LEVEL! DNSFIREWALL g_stFlowedFileList is Empty; nFileIndex = %d; nThreadIndex = %d; enContextState = %u;",
            pFlowData->nFileIndex,
            pFlowData->nThreadIndex,
            pFlowData->arContextStateStack[1]
            );
        break;
    }
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DEVICE,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
    WdfObjectReleaseLock(g_wdfControlDevice);
}
