// DnsStateThread.cpp : implementation file
//

#include "stdafx.h"
#include "WS2InterceptDLL.h"
#include "DnsResorceRecord.h"
#include "DnsMessageWB.h"
#include "DnsStateThread.h"
#include "commrcfg.h"
#include "DnsFirewallThread.h"
#include "Trace.h"
#include "DnsStateThread.tmh"


// CDnsStateThread

IMPLEMENT_DYNCREATE(CDnsStateThread, CWinThread)

CDnsStateThread::CDnsStateThread()
: m_pDnsServerAppID(NULL)
, m_nRetCode(0)
, m_hWfpEngineHandle(INVALID_HANDLE_VALUE)
, m_nAleFlowEstablishedV4CalloutId(0)
, m_nAleFlowEstablishedV6CalloutId(0)
, m_nStreamV4CalloutId(0)
, m_nStreamV6CalloutId(0)
, m_nDatagramsV4CalloutId(0)
, m_nDatagramsV6CalloutId(0)
, m_nAleFlowEstablishedV4FilterID(0)
, m_nAleFlowEstablishedV6FilterID(0)
, m_nStreamV4FilterID(0)
, m_nStreamV6FilterID(0)
, m_nDatagramV4DataFilterID(0)
, m_nDatagramV6DataFilterID(0)
, m_hDnsFirewallDevice(INVALID_HANDLE_VALUE)
, m_bFireWallInitialized(false)
, m_nInboundTransportV4Id(0)
, m_nInboundTransportV6Id(0)
, m_nInboundTransportV4FilterId(0)
, m_nInboundTransportV6FilterId(0)
, m_nAleAuthRecvAcceptV4FilterID(0)
, m_nAleAuthRecvAcceptV6FilterID(0)
, m_nAleAuthRecvAcceptV4CalloutId(0)
, m_nAleAuthRecvAcceptV6CalloutId(0)
, m_hTimerQueue(NULL)
, m_bServiceRebootNeed(false)
, m_oDnsSvcRestartNeedEvent(FALSE, TRUE, _T("Global\\$DNS$RESTART$NEED$"))
, m_bDriverStarted(false)
, m_oRestartQueryEvent(FALSE,TRUE)
, m_dwRestartStatus(0)
{
    m_bAutoDelete = FALSE;
    RtlZeroMemory(
        m_aDnsFirewallThreadsArray,
        sizeof(m_aDnsFirewallThreadsArray)
        );
    RtlZeroMemory(
        &m_stWaitRestartOverlapped,
        sizeof(m_stWaitRestartOverlapped)
        );
}

CDnsStateThread::~CDnsStateThread()
{
}

BOOL CDnsStateThread::InitInstance()
{
    BOOL                    bRet;
    DWORD                   dwResult, dwStopCount, dwWaitCode, dwStartCount;
    FWPM_SESSION            stFwpmSession;
    FWPM_FILTER             stDnsFilter;
    FWPM_CALLOUT            stCallOut;
    FWPM_FILTER_CONDITION   staFilterConditions[16];
    int                     nConditionIndex, i;
    CString                 sDnsFirewallFileName;
    PBYTE                   pIOBuffer = NULL;
    size_t                  nIOBufferSize = sizeof(ADDRESSES_DESCRIPTOR);
    int                     nArrayPossition = sizeof(ADDRESSES_DESCRIPTOR);
    PADDRESSES_DESCRIPTOR   pAddressesDescriptor = NULL;
    SC_HANDLE               schSCManager;
    SC_HANDLE               schService;
    DWORD                   cbBytesNeeded;
    SERVICE_STATUS          stServiceStatus;
    SERVICE_STATUS_PROCESS  stServiceProcessStatus;
    DWORD                   dwWaitState;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNS_STATE_THREAD,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    _tsetlocale(LC_ALL, _T("Russian"));
    bRet = m_oDnsSvcRestartNeedEvent.ResetEvent();
    bRet = m_oRestartQueryEvent.ResetEvent();
    m_hTimerQueue = CreateTimerQueue();
    if (NULL == m_hTimerQueue)
    {
        dwResult = GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"CreateTimerQueue\" failed. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
        return FALSE;
    }
    schSCManager = OpenSCManager(
        NULL,                 // local machine
        NULL,                 // ServicesActive database
        SC_MANAGER_ALL_ACCESS // full access rights
        );
    if (NULL == schSCManager)
    {
        dwResult = GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"OpenSCManager\" failed. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
        return FALSE;
    }
    schService = OpenService(
        schSCManager,
        DRIVER_SERVICE_NAME,
        SERVICE_ALL_ACCESS
        );
    bRet = CloseServiceHandle(schSCManager);
    if (NULL == schService)
    {
        dwResult = GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"OpenService\" failed. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
        return FALSE;
    }
    RtlZeroMemory(
        &stServiceProcessStatus,
        sizeof(stServiceProcessStatus)
        );
    RtlZeroMemory(
        &stServiceStatus,
        sizeof(stServiceStatus)
        );
    bRet = QueryServiceStatusEx(
        schService,
        SC_STATUS_PROCESS_INFO,
        (LPBYTE) &stServiceProcessStatus,
        sizeof(stServiceProcessStatus),
        &cbBytesNeeded
        );
    assert(bRet);
    switch (stServiceProcessStatus.dwCurrentState)
    {
    case SERVICE_RUNNING:
        break;
    case SERVICE_STOPPED:
        bRet = StartService(
            schService,
            0,
            NULL
            );
        if (!bRet)
        {
            dwResult = GetLastError();
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Call \"StartService\" failed. Error: %!WINERROR!",
                dwResult
                );
            m_nRetCode = dwResult;
            bRet = CloseServiceHandle(schService);
            bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
            return FALSE;
        }
        break;
    case SERVICE_STOP_PENDING:
        for (dwStopCount = 0; dwStopCount < 120; dwStopCount++)
        {
            dwWaitCode = SleepEx(1000, TRUE);
            RtlZeroMemory(
                &stServiceProcessStatus,
                sizeof(stServiceProcessStatus)
                );
            RtlZeroMemory(
                &stServiceStatus,
                sizeof(stServiceStatus)
                );
            bRet = QueryServiceStatusEx(
                schService,
                SC_STATUS_PROCESS_INFO,
                (LPBYTE) &stServiceProcessStatus,
                sizeof(stServiceProcessStatus),
                &cbBytesNeeded
                );
            assert(bRet);
            if (SERVICE_STOPPED == stServiceProcessStatus.dwCurrentState)
            {
                break;
            }
        }
        if (dwStopCount >= 120)
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Driver`s service can`t stopped."
                );
            m_nRetCode = -1;
            bRet = CloseServiceHandle(schService);
            bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
            return FALSE;
        }
        bRet = StartService(
            schService,
            0,
            NULL
            );
        if (!bRet)
        {
            dwResult = GetLastError();
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Call \"StartService\" failed. Error: %!WINERROR!",
                dwResult
                );
            m_nRetCode = dwResult;
            bRet = CloseServiceHandle(schService);
            bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
            return FALSE;
        }
        break;
    case SERVICE_START_PENDING:
    case SERVICE_CONTINUE_PENDING:
        for (dwStartCount = 0; dwStartCount < 120; dwStartCount++)
        {
            dwWaitCode = SleepEx(1000, TRUE);
            RtlZeroMemory(
                &stServiceProcessStatus,
                sizeof(stServiceProcessStatus)
                );
            RtlZeroMemory(
                &stServiceStatus,
                sizeof(stServiceStatus)
                );
            bRet = QueryServiceStatusEx(
                schService,
                SC_STATUS_PROCESS_INFO,
                (LPBYTE) &stServiceProcessStatus,
                sizeof(stServiceProcessStatus),
                &cbBytesNeeded
                );
            assert(bRet);
            if (SERVICE_RUNNING == stServiceProcessStatus.dwCurrentState)
            {
                break;
            }
        }
        if (dwStartCount >= 120)
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Driver`s service can`t started."
                );
            m_nRetCode = -1;
            bRet = CloseServiceHandle(schService);
            bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
            return FALSE;
        }
        break;
    case SERVICE_PAUSED:
        RtlZeroMemory(
            &stServiceStatus,
            sizeof(stServiceStatus)
            );
        bRet = ControlService(
            schService,
            SERVICE_CONTROL_CONTINUE,
            &stServiceStatus
            );
        if (!bRet)
        {
            dwResult = GetLastError();
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Call \"ControlService\" failed. Error: %!WINERROR!",
                dwResult
                );
            m_nRetCode = dwResult;
            bRet = CloseServiceHandle(schService);
            bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
            return FALSE;
        }
        for (dwStartCount = 0; dwStartCount < 120; dwStartCount++)
        {
            dwWaitCode = SleepEx(1000, TRUE);
            RtlZeroMemory(
                &stServiceProcessStatus,
                sizeof(stServiceProcessStatus)
                );
            RtlZeroMemory(
                &stServiceStatus,
                sizeof(stServiceStatus)
                );
            bRet = QueryServiceStatusEx(
                schService,
                SC_STATUS_PROCESS_INFO,
                (LPBYTE) &stServiceProcessStatus,
                sizeof(stServiceProcessStatus),
                &cbBytesNeeded
                );
            assert(bRet);
            if (SERVICE_RUNNING == stServiceProcessStatus.dwCurrentState)
            {
                break;
            }
        }
        if (dwStartCount >= 120)
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Driver`s service can`t started."
                );
            m_nRetCode = -1;
            bRet = CloseServiceHandle(schService);
            bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
            return FALSE;
        }
        break;
    case SERVICE_PAUSE_PENDING:
        for (dwStopCount = 0; dwStopCount < 120; dwStopCount++)
        {
            dwWaitCode = SleepEx(1000, TRUE);
            RtlZeroMemory(
                &stServiceProcessStatus,
                sizeof(stServiceProcessStatus)
                );
            RtlZeroMemory(
                &stServiceStatus,
                sizeof(stServiceStatus)
                );
            bRet = QueryServiceStatusEx(
                schService,
                SC_STATUS_PROCESS_INFO,
                (LPBYTE) &stServiceProcessStatus,
                sizeof(stServiceProcessStatus),
                &cbBytesNeeded
                );
            assert(bRet);
            if (SERVICE_PAUSED == stServiceProcessStatus.dwCurrentState)
            {
                break;
            }
        }
        if (dwStopCount >= 120)
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Driver`s service can`t paused."
                );
            m_nRetCode = -1;
            bRet = CloseServiceHandle(schService);
            bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
            return FALSE;
        }
        RtlZeroMemory(
            &stServiceStatus,
            sizeof(stServiceStatus)
            );
        bRet = ControlService(
            schService,
            SERVICE_CONTROL_CONTINUE,
            &stServiceStatus
            );
        if (!bRet)
        {
            dwResult = GetLastError();
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Call \"ControlService\" failed. Error: %!WINERROR!",
                dwResult
                );
            m_nRetCode = dwResult;
            bRet = CloseServiceHandle(schService);
            bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
            return FALSE;
        }
        for (dwStartCount = 0; dwStartCount < 120; dwStartCount++)
        {
            dwWaitCode = SleepEx(1000, TRUE);
            RtlZeroMemory(
                &stServiceProcessStatus,
                sizeof(stServiceProcessStatus)
                );
            RtlZeroMemory(
                &stServiceStatus,
                sizeof(stServiceStatus)
                );
            bRet = QueryServiceStatusEx(
                schService,
                SC_STATUS_PROCESS_INFO,
                (LPBYTE) &stServiceProcessStatus,
                sizeof(stServiceProcessStatus),
                &cbBytesNeeded
                );
            assert(bRet);
            if (SERVICE_RUNNING == stServiceProcessStatus.dwCurrentState)
            {
                break;
            }
        }
        if (dwStartCount >= 120)
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Driver`s service can`t started."
                );
            m_nRetCode = -1;
            bRet = CloseServiceHandle(schService);
            bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
            return FALSE;
        }
        break;
    default:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Illegal driver`s service status = 0x%08X.",
            stServiceProcessStatus.dwCurrentState
            );
        m_nRetCode = -1;
        bRet = CloseServiceHandle(schService);
        bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
        return FALSE;
    }
    bRet = CloseServiceHandle(schService);
    dwResult = FwpmGetAppIdFromFileName(
        L"%systemroot%\\system32\\dns.exe",
        &m_pDnsServerAppID
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Failed to get the ID of the DNS server. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        m_pDnsServerAppID = NULL;
        bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
        return FALSE;
    }
    RtlZeroMemory(
        &stFwpmSession,
        sizeof(stFwpmSession)
        );
    stFwpmSession.displayData.name = L"DNS FireWall Session";
    stFwpmSession.displayData.description = L"Public DNS FireWall engine Session";
    stFwpmSession.flags = FWPM_SESSION_FLAG_DYNAMIC;
    dwResult = FwpmEngineOpen(
        NULL,
        RPC_C_AUTHN_WINNT,
        NULL,
        &stFwpmSession,
        &m_hWfpEngineHandle
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Error opening WFP session. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        m_hWfpEngineHandle = INVALID_HANDLE_VALUE;
        bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
        return FALSE;
    }
    dwResult = FwpmTransactionBegin(
        m_hWfpEngineHandle,
        0
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Transaction failed to start creating a set of WFP-filters. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
        return FALSE;
    }
    RtlZeroMemory(
        &stCallOut,
        sizeof(stCallOut)
        );
    stCallOut.calloutKey = DNS_FIREWALL_INBOUND_TRANSPORT_CALLOUT_V4;
    stCallOut.displayData.name = L"DNS firewall INBOUND_TRANSPORT_V4 CALLOUT";
    stCallOut.displayData.description = L"DNS firewall INBOUND_TRANSPORT_V4 CALLOUT";
    stCallOut.applicableLayer = FWPM_LAYER_INBOUND_TRANSPORT_V4;
    dwResult = FwpmCalloutAdd(
        m_hWfpEngineHandle,
        &stCallOut,
        NULL,
        &m_nInboundTransportV4Id
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmCalloutAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stCallOut,
        sizeof(stCallOut)
        );
    stCallOut.calloutKey = DNS_FIREWALL_INBOUND_TRANSPORT_CALLOUT_V6;
    stCallOut.displayData.name = L"DNS firewall INBOUND_TRANSPORT_V6 CALLOUT";
    stCallOut.displayData.description = L"DNS firewall INBOUND_TRANSPORT_V6 CALLOUT";
    stCallOut.applicableLayer = FWPM_LAYER_INBOUND_TRANSPORT_V6;
    dwResult = FwpmCalloutAdd(
        m_hWfpEngineHandle,
        &stCallOut,
        NULL,
        &m_nInboundTransportV6Id
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmCalloutAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stCallOut,
        sizeof(stCallOut)
        );
    stCallOut.calloutKey = DNS_FIREWALL_ALE_ACCEPT_RECV_CALLOUT_V4;
    stCallOut.displayData.name = L"DNS firewall ALE_AUTH_RECV_ACCEPT IpV4 CALLOUT";
    stCallOut.displayData.description = L"DNS firewall ALE_AUTH_RECV_ACCEPT IpV4 CALLOUT";
    stCallOut.applicableLayer = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4;
    dwResult = FwpmCalloutAdd(
        m_hWfpEngineHandle,
        &stCallOut,
        NULL,
        &m_nAleAuthRecvAcceptV4CalloutId
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmCalloutAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stCallOut,
        sizeof(stCallOut)
        );
    stCallOut.calloutKey = DNS_FIREWALL_ALE_ACCEPT_RECV_CALLOUT_V6;
    stCallOut.displayData.name = L"DNS firewall ALE_AUTH_RECV_ACCEPT IpV4 CALLOUT";
    stCallOut.displayData.description = L"DNS firewall ALE_AUTH_RECV_ACCEPT IpV4 CALLOUT";
    stCallOut.applicableLayer = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6;
    dwResult = FwpmCalloutAdd(
        m_hWfpEngineHandle,
        &stCallOut,
        NULL,
        &m_nAleAuthRecvAcceptV6CalloutId
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmCalloutAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stCallOut,
        sizeof(stCallOut)
        );
    stCallOut.calloutKey = DNS_FIREWALL_ALE_FLOW_ESTABLISHED_CALLOUT_V4;
    stCallOut.displayData.name = L"DNS firewall ALE_FLOW_ESTABLISHED CALLOUT";
    stCallOut.displayData.description = L"DNS firewall Flow Established with DNS-client CALLOUT";
    stCallOut.applicableLayer = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4;
    dwResult = FwpmCalloutAdd(
        m_hWfpEngineHandle,
        &stCallOut,
        NULL,
        &m_nAleFlowEstablishedV4CalloutId
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmCalloutAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stCallOut,
        sizeof(stCallOut)
        );
    stCallOut.calloutKey = DNS_FIREWALL_ALE_FLOW_ESTABLISHED_CALLOUT_V6;
    stCallOut.displayData.name = L"DNS firewall ALE_FLOW_ESTABLISHED IPv6 CALLOUT";
    stCallOut.displayData.description = L"DNS firewall Flow Established IPv6 with DNS-client CALLOUT";
    stCallOut.applicableLayer = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6;
    dwResult = FwpmCalloutAdd(
        m_hWfpEngineHandle,
        &stCallOut,
        NULL,
        &m_nAleFlowEstablishedV6CalloutId
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmCalloutAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stCallOut,
        sizeof(stCallOut)
        );
    stCallOut.calloutKey = DSN_FIREWALL_STREAM_CALLOUT_V4;
    stCallOut.displayData.name = L"DNS firewall TCPv4-stream Flow CALLOUT";
    stCallOut.displayData.description = L"DNS firewall TCPv4-stream Flow CALLOUT";
    stCallOut.applicableLayer = FWPM_LAYER_STREAM_V4;
    dwResult = FwpmCalloutAdd(
        m_hWfpEngineHandle,
        &stCallOut,
        NULL,
        &m_nStreamV4CalloutId
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmCalloutAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stCallOut,
        sizeof(stCallOut)
        );
    stCallOut.calloutKey = DSN_FIREWALL_STREAM_CALLOUT_V6;
    stCallOut.displayData.name = L"DNS firewall TCPv6-stream Flow CALLOUT";
    stCallOut.displayData.description = L"DNS firewall TCPv6-stream Flow CALLOUT";
    stCallOut.applicableLayer = FWPM_LAYER_STREAM_V6;
    dwResult = FwpmCalloutAdd(
        m_hWfpEngineHandle,
        &stCallOut,
        NULL,
        &m_nStreamV6CalloutId
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmCalloutAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    stCallOut.calloutKey = DSN_FIREWALL_DATAGRAM_DATA_CALLOUT_V4;
    stCallOut.displayData.name = L"DNS firewall UDPv4-datagrams Flow CALLOUT";
    stCallOut.displayData.description = L"DNS firewall UDPv4-datagrams Flow CALLOUT";
    stCallOut.applicableLayer = FWPM_LAYER_DATAGRAM_DATA_V4;
    dwResult = FwpmCalloutAdd(
        m_hWfpEngineHandle,
        &stCallOut,
        NULL,
        &m_nDatagramsV4CalloutId
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmCalloutAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stCallOut,
        sizeof(stCallOut)
        );
    stCallOut.calloutKey = DSN_FIREWALL_DATAGRAM_DATA_CALLOUT_V6;
    stCallOut.displayData.name = L"DNS firewall UDPv6-datagrams Flow CALLOUT";
    stCallOut.displayData.description = L"DNS firewall UDPv6-datagrams Flow CALLOUT";
    stCallOut.applicableLayer = FWPM_LAYER_DATAGRAM_DATA_V6;
    dwResult = FwpmCalloutAdd(
        m_hWfpEngineHandle,
        &stCallOut,
        NULL,
        &m_nDatagramsV6CalloutId
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmCalloutAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stDnsFilter,
        sizeof(stDnsFilter)
        );
    stDnsFilter.layerKey = FWPM_LAYER_INBOUND_TRANSPORT_V4;
    stDnsFilter.displayData.name = L"DNS FirewallInbound Transport IPv4 layer Filter";
    stDnsFilter.displayData.description = L"DNS FirewallInbound Transport IPv4 layer Filter";
    stDnsFilter.flags = FWPM_FILTER_FLAG_INDEXED;
    stDnsFilter.weight.type = FWP_UINT64;
    stDnsFilter.weight.uint64 = (UINT64*) &c_uiFilterWeight;
    stDnsFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
    stDnsFilter.action.calloutKey = DNS_FIREWALL_INBOUND_TRANSPORT_CALLOUT_V4;
    stDnsFilter.filterCondition = staFilterConditions;
    stDnsFilter.subLayerKey = FWPM_SUBLAYER_IPSEC_DOSP;
    RtlZeroMemory(
        staFilterConditions,
        sizeof(staFilterConditions)
        );
    nConditionIndex = 0;
    staFilterConditions[nConditionIndex].fieldKey = FWPM_CONDITION_IP_PROTOCOL;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type = FWP_UINT8;
    staFilterConditions[nConditionIndex].conditionValue.uint8 = IPPROTO_UDP;
    nConditionIndex++;
    staFilterConditions[nConditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type = FWP_UINT16;
    staFilterConditions[nConditionIndex].conditionValue.uint16 = DNS_PORT;
    nConditionIndex++;
    stDnsFilter.numFilterConditions = nConditionIndex;
    dwResult = FwpmFilterAdd(
        m_hWfpEngineHandle,
        &stDnsFilter,
        NULL,
        &m_nInboundTransportV4FilterId
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmFilterAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stDnsFilter,
        sizeof(stDnsFilter)
        );
    stDnsFilter.layerKey = FWPM_LAYER_INBOUND_TRANSPORT_V6;
    stDnsFilter.displayData.name = L"DNS FirewallInbound Transport IPv6 layer Filter";
    stDnsFilter.displayData.description = L"DNS FirewallInbound Transport IPv6 layer Filter";
    stDnsFilter.flags = FWPM_FILTER_FLAG_INDEXED;
    stDnsFilter.weight.type = FWP_UINT64;
    stDnsFilter.weight.uint64 = (UINT64*) &c_uiFilterWeight;
    stDnsFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
    stDnsFilter.action.calloutKey = DNS_FIREWALL_INBOUND_TRANSPORT_CALLOUT_V6;
    stDnsFilter.filterCondition = staFilterConditions;
    stDnsFilter.subLayerKey = FWPM_SUBLAYER_IPSEC_DOSP;
    RtlZeroMemory(
        staFilterConditions,
        sizeof(staFilterConditions)
        );
    nConditionIndex = 0;
    staFilterConditions[nConditionIndex].fieldKey = FWPM_CONDITION_IP_PROTOCOL;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type = FWP_UINT8;
    staFilterConditions[nConditionIndex].conditionValue.uint8 = IPPROTO_UDP;
    nConditionIndex++;
    staFilterConditions[nConditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type = FWP_UINT16;
    staFilterConditions[nConditionIndex].conditionValue.uint16 = DNS_PORT;
    nConditionIndex++;
    stDnsFilter.numFilterConditions = nConditionIndex;
    dwResult = FwpmFilterAdd(
        m_hWfpEngineHandle,
        &stDnsFilter,
        NULL,
        &m_nInboundTransportV6FilterId
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmFilterAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stDnsFilter,
        sizeof(stDnsFilter)
        );
    stDnsFilter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4;
    stDnsFilter.displayData.name = L"ALE Authorizing Accept UDP DNS-requests IPv4 layer Filter";
    stDnsFilter.displayData.description = L"ALE Authorizing Accept UDP DNS-requests IPv4 layer Filter";
    stDnsFilter.flags = FWPM_FILTER_FLAG_INDEXED;
    stDnsFilter.weight.type = FWP_UINT64;
    stDnsFilter.weight.uint64 = (UINT64*) &c_uiFilterWeight;
    stDnsFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
    stDnsFilter.action.calloutKey = DNS_FIREWALL_ALE_ACCEPT_RECV_CALLOUT_V4;
    stDnsFilter.filterCondition = staFilterConditions;
    stDnsFilter.subLayerKey = FWPM_SUBLAYER_IPSEC_DOSP;
    RtlZeroMemory(
        staFilterConditions,
        sizeof(staFilterConditions)
        );
    nConditionIndex = 0;
    staFilterConditions[nConditionIndex].fieldKey = FWPM_CONDITION_ALE_APP_ID;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type = FWP_BYTE_BLOB_TYPE;
    staFilterConditions[nConditionIndex].conditionValue.byteBlob = m_pDnsServerAppID;
    nConditionIndex++;
    staFilterConditions[nConditionIndex].fieldKey = FWPM_CONDITION_IP_PROTOCOL;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type = FWP_UINT8;
    staFilterConditions[nConditionIndex].conditionValue.uint8 = IPPROTO_UDP;
    nConditionIndex++;
    staFilterConditions[nConditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type = FWP_UINT16;
    staFilterConditions[nConditionIndex].conditionValue.uint16 = DNS_PORT;
    nConditionIndex++;
    stDnsFilter.numFilterConditions = nConditionIndex;
    dwResult = FwpmFilterAdd(
        m_hWfpEngineHandle,
        &stDnsFilter,
        NULL,
        &m_nAleAuthRecvAcceptV4FilterID
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmFilterAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stDnsFilter,
        sizeof(stDnsFilter)
        );
    stDnsFilter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6;
    stDnsFilter.displayData.name = L"ALE Authorizing Accept UDP DNS-requests IPv6 layer Filter";
    stDnsFilter.displayData.description = L"ALE Authorizing Accept UDP DNS-requests IPv6 layer Filter";
    stDnsFilter.flags = FWPM_FILTER_FLAG_INDEXED;
    stDnsFilter.weight.type = FWP_UINT64;
    stDnsFilter.weight.uint64 = (UINT64*) &c_uiFilterWeight;
    stDnsFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
    stDnsFilter.action.calloutKey = DNS_FIREWALL_ALE_ACCEPT_RECV_CALLOUT_V6;
    stDnsFilter.filterCondition = staFilterConditions;
    stDnsFilter.subLayerKey = FWPM_SUBLAYER_IPSEC_DOSP;
    RtlZeroMemory(
        staFilterConditions,
        sizeof(staFilterConditions)
        );
    nConditionIndex = 0;
    staFilterConditions[nConditionIndex].fieldKey = FWPM_CONDITION_ALE_APP_ID;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type = FWP_BYTE_BLOB_TYPE;
    staFilterConditions[nConditionIndex].conditionValue.byteBlob = m_pDnsServerAppID;
    nConditionIndex++;
    staFilterConditions[nConditionIndex].fieldKey = FWPM_CONDITION_IP_PROTOCOL;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type = FWP_UINT8;
    staFilterConditions[nConditionIndex].conditionValue.uint8 = IPPROTO_UDP;
    nConditionIndex++;
    staFilterConditions[nConditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type = FWP_UINT16;
    staFilterConditions[nConditionIndex].conditionValue.uint16 = DNS_PORT;
    nConditionIndex++;
    stDnsFilter.numFilterConditions = nConditionIndex;
    dwResult = FwpmFilterAdd(
        m_hWfpEngineHandle,
        &stDnsFilter,
        NULL,
        &m_nAleAuthRecvAcceptV6FilterID
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmFilterAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stDnsFilter,
        sizeof(stDnsFilter)
        );
    stDnsFilter.layerKey = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4;
    stDnsFilter.displayData.name = L"ALE Flow Established DNS-requests IPv4 layer Filter";
    stDnsFilter.displayData.description = L"ALE Flow Established DNS-requests IPv4 layer Filter";
    stDnsFilter.flags = FWPM_FILTER_FLAG_INDEXED;
    stDnsFilter.weight.type = FWP_UINT64;
    stDnsFilter.weight.uint64 = (UINT64*) &c_uiFilterWeight;
    stDnsFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
    stDnsFilter.action.calloutKey = DNS_FIREWALL_ALE_FLOW_ESTABLISHED_CALLOUT_V4;
    stDnsFilter.filterCondition = staFilterConditions;
    stDnsFilter.subLayerKey = FWPM_SUBLAYER_IPSEC_DOSP;
    RtlZeroMemory(
        staFilterConditions,
        sizeof(staFilterConditions)
        );
    nConditionIndex = 0;
    staFilterConditions[nConditionIndex].fieldKey = FWPM_CONDITION_ALE_APP_ID;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type = FWP_BYTE_BLOB_TYPE;
    staFilterConditions[nConditionIndex].conditionValue.byteBlob = m_pDnsServerAppID;
    nConditionIndex++;
    staFilterConditions[nConditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type = FWP_UINT16;
    staFilterConditions[nConditionIndex].conditionValue.uint16 = DNS_PORT;
    nConditionIndex++;
    stDnsFilter.numFilterConditions = nConditionIndex;
    dwResult = FwpmFilterAdd(
        m_hWfpEngineHandle,
        &stDnsFilter,
        NULL,
        &m_nAleFlowEstablishedV4FilterID
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmFilterAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stDnsFilter,
        sizeof(stDnsFilter)
        );
    stDnsFilter.layerKey = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6;
    stDnsFilter.displayData.name = L"ALE Flow Established DNS-requests IPv6 layer Filter";
    stDnsFilter.displayData.description = L"ALE Flow Established DNS-requests IPv6 layer Filter";
    stDnsFilter.flags = FWPM_FILTER_FLAG_INDEXED;
    stDnsFilter.weight.type = FWP_UINT64;
    stDnsFilter.weight.uint64 = (UINT64*) &c_uiFilterWeight;
    stDnsFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
    stDnsFilter.action.calloutKey = DNS_FIREWALL_ALE_FLOW_ESTABLISHED_CALLOUT_V6;
    stDnsFilter.filterCondition = staFilterConditions;
    stDnsFilter.subLayerKey = FWPM_SUBLAYER_IPSEC_DOSP;
    RtlZeroMemory(
        staFilterConditions,
        sizeof(staFilterConditions)
        );
    nConditionIndex = 0;
    staFilterConditions[nConditionIndex].fieldKey = FWPM_CONDITION_ALE_APP_ID;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type = FWP_BYTE_BLOB_TYPE;
    staFilterConditions[nConditionIndex].conditionValue.byteBlob = m_pDnsServerAppID;
    nConditionIndex++;
    staFilterConditions[nConditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type = FWP_UINT16;
    staFilterConditions[nConditionIndex].conditionValue.uint16 = DNS_PORT;
    nConditionIndex++;
    stDnsFilter.numFilterConditions = nConditionIndex;
    dwResult = FwpmFilterAdd(
        m_hWfpEngineHandle,
        &stDnsFilter,
        NULL,
        &m_nAleFlowEstablishedV6FilterID
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmFilterAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stDnsFilter,
        sizeof(stDnsFilter)
        );
    stDnsFilter.layerKey = FWPM_LAYER_STREAM_V4;
    stDnsFilter.displayData.name = L"DNS-packets Stream IPv4 flow Filter";
    stDnsFilter.displayData.description = L"DNS-packets Stream IPv4 flow Filter";
    stDnsFilter.flags = FWPM_FILTER_FLAG_INDEXED;
    stDnsFilter.weight.type = FWP_EMPTY;
    stDnsFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
    stDnsFilter.action.calloutKey = DSN_FIREWALL_STREAM_CALLOUT_V4;
    stDnsFilter.filterCondition = staFilterConditions;
    stDnsFilter.subLayerKey = FWPM_SUBLAYER_IPSEC_DOSP;
    RtlZeroMemory(
        staFilterConditions,
        sizeof(staFilterConditions)
        );
    nConditionIndex = 0;
    staFilterConditions[nConditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type = FWP_UINT16;
    staFilterConditions[nConditionIndex].conditionValue.uint16 = DNS_PORT;
    nConditionIndex++;
    stDnsFilter.numFilterConditions = nConditionIndex;
    dwResult = FwpmFilterAdd(
        m_hWfpEngineHandle,
        &stDnsFilter,
        NULL,
        &m_nStreamV4FilterID
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmFilterAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stDnsFilter,
        sizeof(stDnsFilter)
        );
    stDnsFilter.layerKey = FWPM_LAYER_STREAM_V6;
    stDnsFilter.displayData.name = L"DNS-packets Stream IPv6 flow Filter";
    stDnsFilter.displayData.description = L"DNS-packets Stream IPv6 flow Filter";
    stDnsFilter.flags = FWPM_FILTER_FLAG_INDEXED;
    stDnsFilter.weight.type = FWP_EMPTY;
    stDnsFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
    stDnsFilter.action.calloutKey = DSN_FIREWALL_STREAM_CALLOUT_V6;
    stDnsFilter.filterCondition = staFilterConditions;
    stDnsFilter.subLayerKey = FWPM_SUBLAYER_IPSEC_DOSP;
    RtlZeroMemory(
        staFilterConditions,
        sizeof(staFilterConditions)
        );
    nConditionIndex = 0;
    staFilterConditions[nConditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type = FWP_UINT16;
    staFilterConditions[nConditionIndex].conditionValue.uint16 = DNS_PORT;
    nConditionIndex++;
    stDnsFilter.numFilterConditions = nConditionIndex;
    dwResult = FwpmFilterAdd(
        m_hWfpEngineHandle,
        &stDnsFilter,
        NULL,
        &m_nStreamV6FilterID
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Error creating filter flow TCP IPv6 DNS packets. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stDnsFilter,
        sizeof(stDnsFilter)
        );
    stDnsFilter.layerKey = FWPM_LAYER_DATAGRAM_DATA_V4;
    stDnsFilter.displayData.name = L"DNS-packets Datagram IPv4 flow Filter";
    stDnsFilter.displayData.description = L"DNS-packets Datagram IPv4 flow Filter";
    stDnsFilter.flags = FWPM_FILTER_FLAG_INDEXED;
    stDnsFilter.weight.type = FWP_UINT64;
    stDnsFilter.weight.uint64 = (UINT64*) &c_uiFilterWeight;
    stDnsFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
    stDnsFilter.action.calloutKey = DSN_FIREWALL_DATAGRAM_DATA_CALLOUT_V4;
    stDnsFilter.filterCondition = staFilterConditions;
    stDnsFilter.subLayerKey = FWPM_SUBLAYER_IPSEC_DOSP;
    RtlZeroMemory(
        staFilterConditions,
        sizeof(staFilterConditions)
        );
    nConditionIndex = 0;
    staFilterConditions[nConditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type = FWP_UINT16;
    staFilterConditions[nConditionIndex].conditionValue.uint16 = DNS_PORT;
    nConditionIndex++;
    stDnsFilter.numFilterConditions = nConditionIndex;
    dwResult = FwpmFilterAdd(
        m_hWfpEngineHandle,
        &stDnsFilter,
        NULL,
        &m_nDatagramV4DataFilterID
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmFilterAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stDnsFilter,
        sizeof(stDnsFilter)
        );
    stDnsFilter.layerKey = FWPM_LAYER_DATAGRAM_DATA_V6;
    stDnsFilter.displayData.name = L"DNS-packets Datagram IPv6 flow Filter";
    stDnsFilter.displayData.description = L"DNS-packets Datagram IPv6 flow Filter";
    stDnsFilter.flags = FWPM_FILTER_FLAG_INDEXED;
    stDnsFilter.weight.type = FWP_UINT64;
    stDnsFilter.weight.uint64 = (UINT64*) &c_uiFilterWeight;
    stDnsFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
    stDnsFilter.action.calloutKey = DSN_FIREWALL_DATAGRAM_DATA_CALLOUT_V6;
    stDnsFilter.filterCondition = staFilterConditions;
    stDnsFilter.subLayerKey = FWPM_SUBLAYER_IPSEC_DOSP;
    RtlZeroMemory(
        staFilterConditions,
        sizeof(staFilterConditions)
        );
    nConditionIndex = 0;
    staFilterConditions[nConditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type = FWP_UINT16;
    staFilterConditions[nConditionIndex].conditionValue.uint16 = DNS_PORT;
    nConditionIndex++;
    stDnsFilter.numFilterConditions = nConditionIndex;
    dwResult = FwpmFilterAdd(
        m_hWfpEngineHandle,
        &stDnsFilter,
        NULL,
        &m_nDatagramV6DataFilterID
        );
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FwpmFilterAdd\" failed with error = %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    dwResult = FwpmTransactionCommit(m_hWfpEngineHandle);
    if (ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Transaction failed to commit creating a set of WFP-filters. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        goto abortWfpTransaction;
    }
    m_hDnsFirewallDevice = CreateFileW(
        DNSFIREWALL_DOS_NAME,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
        NULL
        );
    if (INVALID_HANDLE_VALUE == m_hDnsFirewallDevice)
    {
        dwResult = ::GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Failed to open device filter DNS packets. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
        return FALSE;
    }
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNS_STATE_THREAD,
        "%!FUNC! line:%!LINE! %!LEVEL! DNSFIREWALL root device file was created."
        );
    nIOBufferSize += theApp.m_nPublicIpCount*sizeof(UINT32);
    nIOBufferSize += theApp.m_nPublicRangeCount*sizeof(IP4ADDR_RANGE_STRUCT);
    nIOBufferSize += theApp.m_nPublicSubnetCount*sizeof(IP4SUBNET_STRUCT);
    nIOBufferSize += theApp.m_nPrivateIpCount*sizeof(UINT32);
    nIOBufferSize += theApp.m_nPrivateRangeCount*sizeof(IP4ADDR_RANGE_STRUCT);
    nIOBufferSize += theApp.m_nPrivateSubnetCount*sizeof(IP4SUBNET_STRUCT);
    nIOBufferSize += theApp.m_nPublicIPV6AddrCount*sizeof(IPV6_ADDRESS_STRUCT);
    nIOBufferSize += theApp.m_nPrivateIPV6AddrCount*sizeof(IPV6_ADDRESS_STRUCT);
    pIOBuffer = new BYTE[nIOBufferSize];
    assert(NULL != pIOBuffer);
    pAddressesDescriptor = (PADDRESSES_DESCRIPTOR)pIOBuffer;
    pAddressesDescriptor->m_nPublicIpCount = theApp.m_nPublicIpCount;
    pAddressesDescriptor->m_pPublicIpArray = (UINT32*)&pIOBuffer[nArrayPossition];
    for(i=0;i<theApp.m_nPublicIpCount;i++)
    {
        pAddressesDescriptor->m_pPublicIpArray[i] = theApp.m_pPublicIpArray[i];
    }
    nArrayPossition += theApp.m_nPublicIpCount*sizeof(UINT32);
    pAddressesDescriptor->m_nPrivateIpCount = theApp.m_nPrivateIpCount;
    pAddressesDescriptor->m_pPrivateIpArray = (UINT32*)&pIOBuffer[nArrayPossition];
    for(i=0;i<theApp.m_nPrivateIpCount;i++)
    {
        pAddressesDescriptor->m_pPrivateIpArray[i] = theApp.m_pPrivateIpArray[i];
    }
    nArrayPossition += theApp.m_nPrivateIpCount*sizeof(UINT32);
    pAddressesDescriptor->m_nPublicRangeCount = theApp.m_nPublicRangeCount;
    pAddressesDescriptor->m_pPublicRangeArray = (PIP4ADDR_RANGE_STRUCT)&pIOBuffer[nArrayPossition];
    for(i=0;i<theApp.m_nPublicRangeCount;i++)
    {
        pAddressesDescriptor->m_pPublicRangeArray[i]._ulLowValue  = theApp.m_pPublicRangeArray[i].valueLow.uint32;
        pAddressesDescriptor->m_pPublicRangeArray[i]._ulHighValue = theApp.m_pPublicRangeArray[i].valueHigh.uint32;
    }
    nArrayPossition += theApp.m_nPublicRangeCount*sizeof(IP4ADDR_RANGE_STRUCT);
    pAddressesDescriptor->m_nPrivateRangeCount = theApp.m_nPrivateRangeCount;
    pAddressesDescriptor->m_pPrivateRangeArray = (PIP4ADDR_RANGE_STRUCT)&pIOBuffer[nArrayPossition];
    for(i=0;i<theApp.m_nPrivateRangeCount;i++)
    {
        pAddressesDescriptor->m_pPrivateRangeArray[i]._ulLowValue  = theApp.m_pPrivateRangeArray[i].valueLow.uint32;
        pAddressesDescriptor->m_pPrivateRangeArray[i]._ulHighValue = theApp.m_pPrivateRangeArray[i].valueHigh.uint32;
    }
    nArrayPossition += theApp.m_nPrivateRangeCount*sizeof(IP4ADDR_RANGE_STRUCT);
    pAddressesDescriptor->m_nPublicSubnetCount = theApp.m_nPublicSubnetCount;
    pAddressesDescriptor->m_pPublicSubnetArray = (PIP4SUBNET_STRUCT)&pIOBuffer[nArrayPossition];
    for(i=0;i<theApp.m_nPublicSubnetCount;i++)
    {
        pAddressesDescriptor->m_pPublicSubnetArray[i]._ulIpAddr = theApp.m_pPublicSubnetArray[i].addr;
        pAddressesDescriptor->m_pPublicSubnetArray[i]._ulIpMask = theApp.m_pPublicSubnetArray[i].mask;
    }
    nArrayPossition += theApp.m_nPublicSubnetCount*sizeof(IP4SUBNET_STRUCT);
    pAddressesDescriptor->m_nPrivateSubnetCount = theApp.m_nPrivateSubnetCount;
    pAddressesDescriptor->m_pPrivateSubnetArray = (PIP4SUBNET_STRUCT)&pIOBuffer[nArrayPossition];
    for(i=0;i<theApp.m_nPrivateSubnetCount;i++)
    {
        pAddressesDescriptor->m_pPrivateSubnetArray[i]._ulIpAddr = theApp.m_pPrivateSubnetArray[i].addr;
        pAddressesDescriptor->m_pPrivateSubnetArray[i]._ulIpMask = theApp.m_pPrivateSubnetArray[i].mask;
    }
    nArrayPossition += theApp.m_nPrivateSubnetCount*sizeof(IP4SUBNET_STRUCT);
    pAddressesDescriptor->m_nPublicIPV6AddrCount = theApp.m_nPublicIPV6AddrCount;
    pAddressesDescriptor->m_pPublicIPV6AddrArray = (PIPV6_ADDRESS_STRUCT) &pIOBuffer[nArrayPossition];
    for (i = 0; i<theApp.m_nPublicIPV6AddrCount; i++)
    {
        RtlCopyMemory(
            &pAddressesDescriptor->m_pPublicIPV6AddrArray[i],
            &theApp.m_pPublicIPV6AddrArray[i],
            sizeof(pAddressesDescriptor->m_pPublicIPV6AddrArray[i])
            );
    }
    nArrayPossition += theApp.m_nPublicIPV6AddrCount*sizeof(IPV6_ADDRESS_STRUCT);
    pAddressesDescriptor->m_nPrivateIPV6AddrCount = theApp.m_nPrivateIPV6AddrCount;
    pAddressesDescriptor->m_pPrivateIPV6AddrArray = (PIPV6_ADDRESS_STRUCT) &pIOBuffer[nArrayPossition];
    for (i = 0; i<theApp.m_nPrivateIPV6AddrCount; i++)
    {
        RtlCopyMemory(
            &pAddressesDescriptor->m_pPrivateIPV6AddrArray[i],
            &theApp.m_pPrivateIPV6AddrArray[i],
            sizeof(pAddressesDescriptor->m_pPrivateIPV6AddrArray[i])
            );
    }
    nArrayPossition += theApp.m_nPrivateIPV6AddrCount*sizeof(IPV6_ADDRESS_STRUCT);
    bRet = DeviceIoControl(
        m_hDnsFirewallDevice,
        DNSFIREWALL_IOCTL_INIT_FILTERS,
        pIOBuffer,
        (DWORD)nIOBufferSize,
        NULL,
        0,
        &dwResult,
        NULL
        );
    if(!bRet)
    {
        dwResult = ::GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Failed to start the driver initialization operations. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        delete pIOBuffer;
        bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
        return FALSE;
    }
    delete pIOBuffer;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNS_STATE_THREAD,
        "%!FUNC! line:%!LINE! %!LEVEL! DNSFIREWALL root device was initializiled."
        );
    for (i = 0; i < DNSFIREWALL_THREAD_COUNT; i++)
    {
        m_aDnsFirewallThreadsArray[i] = new CDnsFirewallThread;
        m_aDnsFirewallThreadsArray[i]->m_nThreadIndex = i;
        m_aDnsFirewallThreadsArray[i]->m_pMainStateThread = this;
        bRet = m_aDnsFirewallThreadsArray[i]->CreateThread( );
        if (!bRet)
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Call member \"CreateThread\" for thread #%d failed!!",
                i
                );
            m_nRetCode = -1;
            delete m_aDnsFirewallThreadsArray[i];
            m_aDnsFirewallThreadsArray[i] = NULL;
            bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
            return FALSE;
        }
        HANDLE haWaitHandles [] = {
            m_aDnsFirewallThreadsArray[i]->m_oThreadInitEvent,
            *m_aDnsFirewallThreadsArray[i],
        };
        dwWaitState = WaitForMultipleObjects(
            sizeof(haWaitHandles) / sizeof(HANDLE),
            haWaitHandles,
            FALSE,
            INFINITE
            );
        if (WAIT_OBJECT_0 + 1 == dwWaitState)
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! State Thread terminated unexpected with error %!WINERROR!",
                m_aDnsFirewallThreadsArray[i]->m_nRetCode
                );
            m_nRetCode = m_aDnsFirewallThreadsArray[i]->m_nRetCode;
            delete m_aDnsFirewallThreadsArray[i];
            m_aDnsFirewallThreadsArray[i] = NULL;
            bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
            return FALSE;
        }
    }
    m_stWaitRestartOverlapped.hEvent = m_oRestartQueryEvent;
    bRet = DeviceIoControl(
        m_hDnsFirewallDevice,
        DNSFIREWALL_IOCTL_WAIT_FOR_RESTART,
        NULL,
        0,
        &m_dwRestartStatus,
        sizeof(m_dwRestartStatus),
        &dwResult,
        &m_stWaitRestartOverlapped
        );
    m_bFireWallInitialized = true;
    bRet = theApp.m_oStateThreadInitDoneEvent.SetEvent();
    m_bDriverStarted = true;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNS_STATE_THREAD,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
	return TRUE;
abortWfpTransaction:
    m_nAleFlowEstablishedV4CalloutId = 0;
    m_nAleFlowEstablishedV6CalloutId = 0;
    m_nStreamV4CalloutId = 0;
    m_nStreamV6CalloutId = 0;
    m_nDatagramsV4CalloutId = 0;
    m_nDatagramsV6CalloutId = 0;
    m_nDatagramsV4CalloutId = 0;
    m_nDatagramsV6CalloutId = 0;
    m_nStreamV4CalloutId = 0;
    m_nStreamV6CalloutId = 0;
    m_nDatagramsV4CalloutId = 0;
    m_nDatagramsV6CalloutId = 0;
    dwResult = FwpmTransactionAbort(m_hWfpEngineHandle);
    TraceEvents(
        TRACE_LEVEL_ERROR,
        TRACE_DNS_STATE_THREAD,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit with Abort WfpTransaction"
        );
    bRet = theApp.m_oDnsSvcStopedEvent.SetEvent();
    return FALSE;
}

int CDnsStateThread::ExitInstance()
{
    int                     nRetCode, i;
    BOOL                    bRet;
    DWORD                   dwResult;
    SC_HANDLE               schSCManager;
    SC_HANDLE               schService;
    SERVICE_STATUS          stServiceStatus;
    TTimerArray::iterator   oTimerArrayIterator;
    TTimerDescriptor*       pTimerDescriptor;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNS_STATE_THREAD,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    nRetCode = CWinThread::ExitInstance();
    for (i = 0; i < DNSFIREWALL_THREAD_COUNT; i++)
    {
        if (NULL != m_aDnsFirewallThreadsArray[i])
        {
            bRet = m_aDnsFirewallThreadsArray[i]->PostThreadMessage(
                WM_QUIT,
                0,
                0
                );
            dwResult = WaitForSingleObject(
                *m_aDnsFirewallThreadsArray[i],
                INFINITE
                );
            delete m_aDnsFirewallThreadsArray[i];
            m_aDnsFirewallThreadsArray[i] = NULL;
        }
    }
    if (INVALID_HANDLE_VALUE != m_hDnsFirewallDevice)
    {
        if (m_stWaitRestartOverlapped.hEvent != NULL)
        {
            bRet = CancelIoEx(
                m_hDnsFirewallDevice,
                &m_stWaitRestartOverlapped
                );
        }
        bRet = DeviceIoControl(
            m_hDnsFirewallDevice,
            DNSFIREWALL_IOCTL_RELEASE_RESORCE,
            NULL,
            0,
            NULL,
            0,
            &dwResult,
            NULL
            );
        bRet = CloseHandle(m_hDnsFirewallDevice);
        m_hDnsFirewallDevice = INVALID_HANDLE_VALUE;
    }
    if (INVALID_HANDLE_VALUE != m_hWfpEngineHandle)
    {
        dwResult = FwpmTransactionBegin(
            m_hWfpEngineHandle,
            0
            );
        if (ERROR_SUCCESS != dwResult)
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Transaction failed to start destroy the set of WFP-filters. Error: %!WINERROR!",
                dwResult
                );
        }
        else
        {
            if (0 != m_nDatagramV6DataFilterID)
            {
                dwResult = FwpmFilterDeleteById(
                    m_hWfpEngineHandle,
                    m_nDatagramV6DataFilterID
                    );
                m_nDatagramV6DataFilterID = 0;
            }
            if (0 != m_nDatagramV4DataFilterID)
            {
                dwResult = FwpmFilterDeleteById(
                    m_hWfpEngineHandle,
                    m_nDatagramV4DataFilterID
                    );
                m_nDatagramV4DataFilterID = 0;
            }
            if (0 != m_nStreamV6FilterID)
            {
                dwResult = FwpmFilterDeleteById(
                    m_hWfpEngineHandle,
                    m_nStreamV6FilterID
                    );
                m_nStreamV6FilterID = 0;
            }
            if (0 != m_nStreamV4FilterID)
            {
                dwResult = FwpmFilterDeleteById(
                    m_hWfpEngineHandle,
                    m_nStreamV4FilterID
                    );
                m_nStreamV4FilterID = 0;
            }
            if (0 != m_nAleFlowEstablishedV6FilterID)
            {
                dwResult = FwpmFilterDeleteById(
                    m_hWfpEngineHandle,
                    m_nAleFlowEstablishedV6FilterID
                    );
                m_nAleFlowEstablishedV6FilterID = 0;
            }
            if (0 != m_nAleFlowEstablishedV4FilterID)
            {
                dwResult = FwpmFilterDeleteById(
                    m_hWfpEngineHandle,
                    m_nAleFlowEstablishedV4FilterID
                    );
                m_nAleFlowEstablishedV4FilterID = 0;
            }
            if (0 != m_nAleAuthRecvAcceptV6FilterID)
            {
                dwResult = FwpmFilterDeleteById(
                    m_hWfpEngineHandle,
                    m_nAleAuthRecvAcceptV6FilterID
                    );
                m_nAleAuthRecvAcceptV6FilterID = 0;
            }
            if (0 != m_nAleAuthRecvAcceptV4FilterID)
            {
                dwResult = FwpmFilterDeleteById(
                    m_hWfpEngineHandle,
                    m_nAleAuthRecvAcceptV4FilterID
                    );
                m_nAleAuthRecvAcceptV4FilterID = 0;
            }
            if (0 != m_nInboundTransportV6FilterId)
            {
                dwResult = FwpmFilterDeleteById(
                    m_hWfpEngineHandle,
                    m_nInboundTransportV6FilterId
                    );
                m_nInboundTransportV6FilterId = 0;
            }
            if (0 != m_nInboundTransportV4FilterId)
            {
                dwResult = FwpmFilterDeleteById(
                    m_hWfpEngineHandle,
                    m_nInboundTransportV4FilterId
                    );
                m_nInboundTransportV4FilterId = 0;
            }
            if (0 != m_nDatagramsV6CalloutId)
            {
                dwResult = FwpmCalloutDeleteById(
                    m_hWfpEngineHandle,
                    m_nDatagramsV6CalloutId
                    );
                m_nDatagramsV6CalloutId = 0;
            }
            if (0 != m_nDatagramsV4CalloutId)
            {
                dwResult = FwpmCalloutDeleteById(
                    m_hWfpEngineHandle,
                    m_nDatagramsV4CalloutId
                    );
                m_nDatagramsV4CalloutId = 0;
            }
            if (0 != m_nStreamV6CalloutId)
            {
                dwResult = FwpmCalloutDeleteById(
                    m_hWfpEngineHandle,
                    m_nStreamV6CalloutId
                    );
                m_nStreamV6CalloutId = 0;
            }
            if (0 != m_nStreamV4CalloutId)
            {
                dwResult = FwpmCalloutDeleteById(
                    m_hWfpEngineHandle,
                    m_nStreamV4CalloutId
                    );
                m_nStreamV4CalloutId = 0;
            }
            if (0 != m_nAleFlowEstablishedV6CalloutId)
            {
                dwResult = FwpmCalloutDeleteById(
                    m_hWfpEngineHandle,
                    m_nAleFlowEstablishedV6CalloutId
                    );
                m_nAleFlowEstablishedV6CalloutId = 0;
            }
            if (0 != m_nAleFlowEstablishedV4CalloutId)
            {
                dwResult = FwpmCalloutDeleteById(
                    m_hWfpEngineHandle,
                    m_nAleFlowEstablishedV4CalloutId
                    );
                m_nAleFlowEstablishedV4CalloutId = 0;
            }
            if (0 != m_nAleAuthRecvAcceptV6CalloutId)
            {
                dwResult = FwpmCalloutDeleteById(
                    m_hWfpEngineHandle,
                    m_nAleAuthRecvAcceptV6CalloutId
                    );
                m_nAleAuthRecvAcceptV6CalloutId = 0;
            }
            if (0 != m_nAleAuthRecvAcceptV4CalloutId)
            {
                dwResult = FwpmCalloutDeleteById(
                    m_hWfpEngineHandle,
                    m_nAleAuthRecvAcceptV4CalloutId
                    );
                m_nAleAuthRecvAcceptV4CalloutId = 0;
            }
            if (0 != m_nInboundTransportV6Id)
            {
                dwResult = FwpmCalloutDeleteById(
                    m_hWfpEngineHandle,
                    m_nInboundTransportV6Id
                    );
                m_nInboundTransportV6Id = 0;
            }
            if (0 != m_nInboundTransportV4Id)
            {
                dwResult = FwpmCalloutDeleteById(
                    m_hWfpEngineHandle,
                    m_nInboundTransportV4Id
                    );
                m_nInboundTransportV4Id = 0;
            }
            dwResult = FwpmTransactionCommit(m_hWfpEngineHandle);
        }
        dwResult = FwpmEngineClose(
            m_hWfpEngineHandle
            );
        m_hWfpEngineHandle = INVALID_HANDLE_VALUE;
    }
    schSCManager = OpenSCManager(
        NULL,                 // local machine
        NULL,                 // ServicesActive database
        SC_MANAGER_ALL_ACCESS // full access rights
        );
    if (NULL != schSCManager)
    {
        schService = OpenService(
            schSCManager,
            DRIVER_SERVICE_NAME,
            SERVICE_ALL_ACCESS
            );
        bRet = CloseServiceHandle(schSCManager);
        if (NULL != schService)
        {
            RtlZeroMemory(
                &stServiceStatus,
                sizeof(stServiceStatus)
                );
            bRet = ControlService(
                schService,
                SERVICE_CONTROL_STOP,
                &stServiceStatus
                );
            bRet = CloseServiceHandle(schService);
        }
    }
    if (NULL != m_hTimerQueue)
    {
        bRet = DeleteTimerQueueEx(
            m_hTimerQueue,
            NULL
            );
        m_hTimerQueue = NULL;
    }
    while (!m_oTimerArray.empty())
    {
        oTimerArrayIterator = m_oTimerArray.begin();
        pTimerDescriptor = oTimerArrayIterator->second;
        delete pTimerDescriptor;
        m_oTimerArray.erase(oTimerArrayIterator);
    }
    nRetCode = m_nRetCode;
    if (m_bServiceRebootNeed)
    {
        bRet = m_oDnsSvcRestartNeedEvent.SetEvent();
    }
    bRet = theApp.m_oMainThreadLock.Lock();
    if (NULL != theApp.m_pDnsStateThread)
    {
        theApp.m_pDnsStateThread = NULL;
        delete this;
    }
    bRet = theApp.m_oMainThreadLock.Unlock();
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNS_STATE_THREAD,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
    return nRetCode;
}

BEGIN_MESSAGE_MAP(CDnsStateThread, CWinThread)
END_MESSAGE_MAP()


// CDnsStateThread message handlers


BOOL CDnsStateThread::OnIdle(LONG lCount)
{
    DWORD   dwWaitState, dwErrorCode, dwThreadIndex;
    BOOL    bRet;
    HANDLE  haWaitHandles [] = {
        m_oRestartQueryEvent,
        *m_aDnsFirewallThreadsArray[0],
        *m_aDnsFirewallThreadsArray[1],
        *m_aDnsFirewallThreadsArray[2],
        *m_aDnsFirewallThreadsArray[3],
        *m_aDnsFirewallThreadsArray[4],
        *m_aDnsFirewallThreadsArray[5],
        *m_aDnsFirewallThreadsArray[6],
        *m_aDnsFirewallThreadsArray[7],
    };
    lCount;
    dwWaitState = MsgWaitForMultipleObjects(
        sizeof(haWaitHandles) / sizeof(HANDLE),
        haWaitHandles,
        FALSE,
        INFINITE,
        QS_POSTMESSAGE
        );
    switch (dwWaitState)
    {
    case WAIT_FAILED:
        dwErrorCode = GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"MsgWaitForMultipleObjects\" failed with error %!WINERROR!",
            dwErrorCode
            );
        bRet = PostThreadMessage(
            WM_QUIT,
            dwErrorCode,
            0
            );
        return FALSE;
   case WAIT_OBJECT_0:
        TraceEvents(
            TRACE_LEVEL_WARNING,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! DnsFirewall driver send RESTART request."
            );
        m_bServiceRebootNeed = true;
        m_stWaitRestartOverlapped.hEvent = NULL;
        bRet = PostThreadMessage(
            WM_QUIT,
            0,
            0
            );
        return FALSE;
    case WAIT_OBJECT_0 + 1:
    case WAIT_OBJECT_0 + 2:
    case WAIT_OBJECT_0 + 3:
    case WAIT_OBJECT_0 + 4:
    case WAIT_OBJECT_0 + 5:
    case WAIT_OBJECT_0 + 6:
    case WAIT_OBJECT_0 + 7:
    case WAIT_OBJECT_0 + 8:
        dwThreadIndex = dwWaitState - WAIT_OBJECT_0;
        dwErrorCode = m_aDnsFirewallThreadsArray[dwThreadIndex]->m_nRetCode;
        delete m_aDnsFirewallThreadsArray[dwThreadIndex];
        m_aDnsFirewallThreadsArray[dwThreadIndex] = NULL;
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! DnsFirewall thread #%u unexpectedly terminated with error %!WINERROR!",
            dwThreadIndex,
            dwErrorCode
            );
        bRet = PostThreadMessage(
            WM_QUIT,
            dwErrorCode,
            0
            );
        return FALSE;
    case WAIT_OBJECT_0 + 9:
        break;
    }
    return TRUE;
}


#pragma warning(push)
#pragma warning(disable: 4018)
void CDnsStateThread::ResetFirewallTimer(
    PTTimerDescriptor pTimerDescriptor
    )
{
    BOOL                    bRet;
    DWORD                   dwResult;
    int                     nIpV6SingleAddrIndex, nIpV4SingleAddrIndex, nIpV4SubnetIndex;
    TTimerArray::iterator   oTimerArrayIterator;
    bool                    bValidTimer;
    int                     i;
    UINT32                  ulIpAddressN, ulIpAddress;
    IN_ADDR                 stIpAddress;
    IN6_ADDR                stIp6Address;
    WCHAR                   szIpAddress[128];
    LPWSTR                  pAddressEnd;
    HANDLE                  hQueueTimer;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNS_STATE_THREAD,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    bRet = DeviceIoControl(
        m_hDnsFirewallDevice,
        DNSFIREWALL_IOCTL_GET_FIREWALL_CONFIG,
        NULL,
        0,
        &m_stFirewallConfig,
        sizeof(m_stFirewallConfig),
        &dwResult,
        NULL
        );
    if (!bRet)
    {
        dwResult = ::GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
            dwResult
            );
        return;
    }
    switch (pTimerDescriptor->enTimerType)
    {
    case enIpV4SingleAddr:
        nIpV4SingleAddrIndex = pTimerDescriptor->nIpV4SingleAddrIndex;
        hQueueTimer = m_stFirewallConfig.aBlackListTimers[nIpV4SingleAddrIndex];
        ulIpAddress = pTimerDescriptor->ulIpV4SingleAddr;
        ulIpAddressN = RtlUlongByteSwap(ulIpAddress);
        stIpAddress.S_un.S_addr = ulIpAddressN;
        pAddressEnd = RtlIpv4AddressToStringW(
            &stIpAddress,
            szIpAddress
            );
        bRet = DeleteTimerQueueTimer(
            m_hTimerQueue,
            hQueueTimer,
            NULL
            );
        if (!bRet)
        {
            dwResult = ::GetLastError();
            if (ERROR_IO_PENDING != dwResult)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeleteTimerQueueTimer\" faled with error: %!WINERROR!",
                    dwResult
                    );
            }
        }
        m_oTimerArray.erase(hQueueTimer);
        delete pTimerDescriptor;
        m_stFirewallConfig.nBlackListLength--;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Host %ws was Removed from Black List; nIpV4SingleAddrIndex = %d; hQueueTimer = 0x%016I64X;",
            szIpAddress,
            nIpV4SingleAddrIndex,
            (UINT64) hQueueTimer
            );
        for (i = nIpV4SingleAddrIndex; i < m_stFirewallConfig.nBlackListLength; i++)
        {
            m_stFirewallConfig.aBlackListArray[i] = m_stFirewallConfig.aBlackListArray[i + 1];
            hQueueTimer = m_stFirewallConfig.aBlackListTimers[i + 1];
            m_stFirewallConfig.aBlackListTimers[i] = hQueueTimer;
            oTimerArrayIterator = m_oTimerArray.find(hQueueTimer);
            if (oTimerArrayIterator == m_oTimerArray.end())
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Black List V4 Timer not in Timers Array"
                    );
            }
            else
            {
                pTimerDescriptor = oTimerArrayIterator->second;
                bValidTimer = (enIpV4SingleAddr == pTimerDescriptor->enTimerType)
                    && (i + 1 == pTimerDescriptor->nIpV4SingleAddrIndex);
                if (bValidTimer)
                {
                    pTimerDescriptor->nIpV4SingleAddrIndex = i;
                }
                else
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Not valid Black List V4 Timer"
                        );
                }
            }
        }
        break;
    case enIpV4Subnet:
        nIpV4SubnetIndex = pTimerDescriptor->nIpV4SubnetIndex;
        hQueueTimer = m_stFirewallConfig.aBlackSubnetTimers[nIpV4SubnetIndex];
        bRet = DeleteTimerQueueTimer(
            m_hTimerQueue,
            hQueueTimer,
            NULL
            );
        if (!bRet)
        {
            dwResult = ::GetLastError();
            if (ERROR_IO_PENDING != dwResult)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeleteTimerQueueTimer\" faled with error: %!WINERROR!",
                    dwResult
                    );
            }
        }
        m_oTimerArray.erase(hQueueTimer);
        delete pTimerDescriptor;
        m_stFirewallConfig.nBlackSubnetListLength--;
        for (i = nIpV4SubnetIndex; i < m_stFirewallConfig.nBlackSubnetListLength; i++)
        {
            m_stFirewallConfig.aBlackSubnetList[i] = m_stFirewallConfig.aBlackSubnetList[i + 1];
            hQueueTimer = m_stFirewallConfig.aBlackSubnetTimers[i + 1];
            m_stFirewallConfig.aBlackSubnetTimers[i] = hQueueTimer;
            oTimerArrayIterator = m_oTimerArray.find(hQueueTimer);
            if (oTimerArrayIterator == m_oTimerArray.end())
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Black List V4 Subnet Timer not in Timers Array"
                    );
            }
            else
            {
                pTimerDescriptor = oTimerArrayIterator->second;
                bValidTimer = (enIpV4Subnet == pTimerDescriptor->enTimerType)
                    && (i + 1 == pTimerDescriptor->nIpV4SubnetIndex);
                if (bValidTimer)
                {
                    pTimerDescriptor->nIpV4SubnetIndex = i;
                }
                else
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Not valid Black List V4 Subnet Timer"
                        );
                }
            }
        }
        break;
    case enIpV6SingleAddr:
        nIpV6SingleAddrIndex = pTimerDescriptor->nIpV6SingleAddrIndex;
        RtlCopyMemory(
            &stIp6Address,
            &pTimerDescriptor->stIpV6SingleAddr,
            sizeof(stIp6Address)
            );
        pAddressEnd = RtlIpv6AddressToStringW(
            &stIp6Address,
            szIpAddress
            );
        hQueueTimer = m_stFirewallConfig.aBlackListV6Timers[nIpV6SingleAddrIndex];
        bRet = DeleteTimerQueueTimer(
            m_hTimerQueue,
            hQueueTimer,
            NULL
            );
        if (!bRet)
        {
            dwResult = ::GetLastError();
            if (ERROR_IO_PENDING != dwResult)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeleteTimerQueueTimer\" faled with error: %!WINERROR!",
                    dwResult
                    );
            }
        }
        m_oTimerArray.erase(hQueueTimer);
        delete pTimerDescriptor;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Host %ws was Removed from Black List; nIpV6SingleAddrIndex = %d; hQueueTimer = 0x%016I64X;",
            szIpAddress,
            nIpV6SingleAddrIndex,
            (UINT64) hQueueTimer
            );
        m_stFirewallConfig.nBlackListV6Length--;
        for (i = nIpV6SingleAddrIndex; i < m_stFirewallConfig.nBlackListV6Length; i++)
        {
            m_stFirewallConfig.aBlackListV6Array[i] = m_stFirewallConfig.aBlackListV6Array[i + 1];
            hQueueTimer = m_stFirewallConfig.aBlackListV6Timers[i + 1];
            m_stFirewallConfig.aBlackListV6Timers[i] = hQueueTimer;
            oTimerArrayIterator = m_oTimerArray.find(hQueueTimer);
            if (oTimerArrayIterator == m_oTimerArray.end())
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Black List V6 Timer not in Timers Array"
                    );
            }
            else
            {
                pTimerDescriptor = oTimerArrayIterator->second;
                bValidTimer = (enIpV6SingleAddr == pTimerDescriptor->enTimerType)
                    && (i + 1 == pTimerDescriptor->nIpV6SingleAddrIndex);
                if (bValidTimer)
                {
                    pTimerDescriptor->nIpV6SingleAddrIndex = i;
                }
                else
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Not valid Black List V6 Timer"
                        );
                }
            }
        }
        break;
    case enExternalClientBlocked:
        hQueueTimer = m_stFirewallConfig.hEternalClientBlockedTimer;
        bRet = DeleteTimerQueueTimer(
            m_hTimerQueue,
            hQueueTimer,
            NULL
            );
        if (!bRet)
        {
            dwResult = ::GetLastError();
            if (ERROR_IO_PENDING != dwResult)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeleteTimerQueueTimer\" faled with error: %!WINERROR!",
                    dwResult
                    );
            }
        }
        m_oTimerArray.erase(hQueueTimer);
        delete pTimerDescriptor;
        m_stFirewallConfig.bExternalClientBlocked = FALSE;
        break;
    default:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Illegal Timer Descriptor"
            );
        delete pTimerDescriptor;
        break;
    }
    bRet = DeviceIoControl(
        m_hDnsFirewallDevice,
        DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
        &m_stFirewallConfig,
        sizeof(m_stFirewallConfig),
        NULL,
        0,
        &dwResult,
        NULL
        );
    if (!bRet)
    {
        dwResult = ::GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
            dwResult
            );
        return;
    }
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNS_STATE_THREAD,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
}
#pragma warning(pop)


void CDnsStateThread::sfTimerProc(
    _In_ PVOID   lpParameter,
    _In_ BOOLEAN TimerOrWaitFired
    )
{
    UNREFERENCED_PARAMETER(TimerOrWaitFired);
    theApp.m_pDnsStateThread->mfTimerProc(
        (PTTimerDescriptor) lpParameter
        );
}


void CDnsStateThread::mfTimerProc(
    PTTimerDescriptor pTimerDescriptor
    )
{
    //BOOL    bRet;
    //bRet = m_oTimersLock.Lock();
    ResetFirewallTimer(pTimerDescriptor);
    //bRet = m_oTimersLock.Unlock();
}


#pragma warning(push)
#pragma warning(disable : 4018 )
void CDnsStateThread::ResetFirewallIpV4Config(UINT32 ulIpAddress)
{
    BOOL                    bRet;
    DWORD                   dwResult;
    int                     nIpV4SingleAddrIndex, nIpV4SubnetIndex;
    TTimerArray::iterator   oTimerArrayIterator;
    HANDLE                  hQueueTimer;
    TTimerDescriptor*       pTimerDescriptor;
    bool                    bValidTimer, bSubnetExist;
    ULONG32                 aBlockedAddressArray[128], ulBlockedAddrMin, ulBlockedAddrAux, ulSubnetPart;
    int                     aBlockedIndexArray[128];
    HANDLE                  aBlockedTimerArray[128], ulBlockedTimerMin, ulBlockedTimerAux;
    int                     i, j, k, l, m, nBlockedIndexMin, nBlockedIndexAux;
    UINT32                  ulIpAddressN = RtlUlongByteSwap(ulIpAddress);
    IN_ADDR                 stIpAddress;
    WCHAR                   szIpAddress[128];
    LPWSTR                  pAddressEnd;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNS_STATE_THREAD,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    stIpAddress.S_un.S_addr = ulIpAddressN;
    pAddressEnd = RtlIpv4AddressToStringW(
        &stIpAddress,
        szIpAddress
        );
    bRet = DeviceIoControl(
        m_hDnsFirewallDevice,
        DNSFIREWALL_IOCTL_GET_FIREWALL_CONFIG,
        NULL,
        0,
        &m_stFirewallConfig,
        sizeof(m_stFirewallConfig),
        &dwResult,
        NULL
        );
    if (!bRet)
    {
        dwResult = ::GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
            dwResult
            );
        return;
    }
    if (m_stFirewallConfig.bExternalClientBlocked)
    {
        hQueueTimer = m_stFirewallConfig.hEternalClientBlockedTimer;
        if (NULL != hQueueTimer)
        {
            bRet = DeleteTimerQueueTimer(
                m_hTimerQueue,
                hQueueTimer,
                NULL
                );
            m_stFirewallConfig.hEternalClientBlockedTimer = NULL;
            if (!bRet)
            {
                dwResult = ::GetLastError();
                if (ERROR_IO_PENDING != dwResult)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeleteTimerQueueTimer\" faled with error: %!WINERROR!",
                        dwResult
                        );
                    bRet = DeviceIoControl(
                        m_hDnsFirewallDevice,
                        DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
                        &m_stFirewallConfig,
                        sizeof(m_stFirewallConfig),
                        NULL,
                        0,
                        &dwResult,
                        NULL
                        );
                    if (!bRet)
                    {
                        dwResult = ::GetLastError();
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            TRACE_DNS_STATE_THREAD,
                            "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                            dwResult
                            );
                    }
                    return;
                }
            }
            oTimerArrayIterator = m_oTimerArray.find(hQueueTimer);
            if (oTimerArrayIterator == m_oTimerArray.end())
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Error on find element in Timer Array"
                    );
                bRet = DeviceIoControl(
                    m_hDnsFirewallDevice,
                    DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
                    &m_stFirewallConfig,
                    sizeof(m_stFirewallConfig),
                    NULL,
                    0,
                    &dwResult,
                    NULL
                    );
                if (!bRet)
                {
                    dwResult = ::GetLastError();
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                        dwResult
                        );
                }
                return;
            }
            pTimerDescriptor = oTimerArrayIterator->second;
            delete pTimerDescriptor;
            m_oTimerArray.erase(oTimerArrayIterator);
        }
        pTimerDescriptor = new TTimerDescriptor;
        bRet = CreateTimerQueueTimer(
            &hQueueTimer,
            m_hTimerQueue,
            sfTimerProc,
            pTimerDescriptor,
            1800000,
            0,
            WT_EXECUTEONLYONCE
            );
        if (!bRet)
        {
            dwResult = ::GetLastError();
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Call \"CreateTimerQueueTimer\" faled with error: %!WINERROR!",
                dwResult
                );
            delete pTimerDescriptor;
            bRet = DeviceIoControl(
                m_hDnsFirewallDevice,
                DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
                &m_stFirewallConfig,
                sizeof(m_stFirewallConfig),
                NULL,
                0,
                &dwResult,
                NULL
                );
            if (!bRet)
            {
                dwResult = ::GetLastError();
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                    dwResult
                    );
            }
            return;
        }
        RtlZeroMemory(
            pTimerDescriptor,
            sizeof(TTimerDescriptor)
            );
        pTimerDescriptor->enTimerType = enExternalClientBlocked;
        m_stFirewallConfig.hEternalClientBlockedTimer = hQueueTimer;
        m_oTimerArray[hQueueTimer] = pTimerDescriptor;
        bRet = DeviceIoControl(
            m_hDnsFirewallDevice,
            DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
            &m_stFirewallConfig,
            sizeof(m_stFirewallConfig),
            NULL,
            0,
            &dwResult,
            NULL
            );
        if (!bRet)
        {
            dwResult = ::GetLastError();
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                dwResult
                );
        }
        return;
    }
    nIpV4SubnetIndex = m_stFirewallConfig.nBlackSubnetListLength;
    for (i = 0; i < nIpV4SubnetIndex; i++)
    {
        ulSubnetPart = (ulIpAddress & 0xFFFFFF00);
        if (m_stFirewallConfig.aBlackSubnetList[i]._ulIpAddr == ulSubnetPart)
        {
            hQueueTimer = m_stFirewallConfig.aBlackSubnetTimers[i];
            if (NULL != hQueueTimer)
            {
                bRet = DeleteTimerQueueTimer(
                    m_hTimerQueue,
                    hQueueTimer,
                    NULL
                    );
                m_stFirewallConfig.aBlackSubnetTimers[i] = NULL;
                if (!bRet)
                {
                    dwResult = ::GetLastError();
                    if (ERROR_IO_PENDING != dwResult)
                    {
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            TRACE_DNS_STATE_THREAD,
                            "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeleteTimerQueueTimer\" faled with error: %!WINERROR!",
                            dwResult
                            );
                        bRet = DeviceIoControl(
                            m_hDnsFirewallDevice,
                            DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
                            &m_stFirewallConfig,
                            sizeof(m_stFirewallConfig),
                            NULL,
                            0,
                            &dwResult,
                            NULL
                            );
                        if (!bRet)
                        {
                            dwResult = ::GetLastError();
                            TraceEvents(
                                TRACE_LEVEL_ERROR,
                                TRACE_DNS_STATE_THREAD,
                                "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                                dwResult
                                );
                        }
                        return;
                    }
                }
                oTimerArrayIterator = m_oTimerArray.find(hQueueTimer);
                if (oTimerArrayIterator == m_oTimerArray.end())
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Error on find element in Timer Array"
                        );
                    bRet = DeviceIoControl(
                        m_hDnsFirewallDevice,
                        DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
                        &m_stFirewallConfig,
                        sizeof(m_stFirewallConfig),
                        NULL,
                        0,
                        &dwResult,
                        NULL
                        );
                    if (!bRet)
                    {
                        dwResult = ::GetLastError();
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            TRACE_DNS_STATE_THREAD,
                            "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                            dwResult
                            );
                    }
                    return;
                }
                pTimerDescriptor = oTimerArrayIterator->second;
                delete pTimerDescriptor;
                m_oTimerArray.erase(oTimerArrayIterator);
            }
            pTimerDescriptor = new TTimerDescriptor;
            bRet = CreateTimerQueueTimer(
                &hQueueTimer,
                m_hTimerQueue,
                sfTimerProc,
                pTimerDescriptor,
                1200000,
                0,
                WT_EXECUTEONLYONCE
                );
            if (!bRet)
            {
                dwResult = ::GetLastError();
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"CreateTimerQueueTimer\" faled with error: %!WINERROR!",
                    dwResult
                    );
                delete pTimerDescriptor;
                bRet = DeviceIoControl(
                    m_hDnsFirewallDevice,
                    DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
                    &m_stFirewallConfig,
                    sizeof(m_stFirewallConfig),
                    NULL,
                    0,
                    &dwResult,
                    NULL
                    );
                if (!bRet)
                {
                    dwResult = ::GetLastError();
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                        dwResult
                        );
                }
                return;
            }
            RtlZeroMemory(
                pTimerDescriptor,
                sizeof(TTimerDescriptor)
                );
            pTimerDescriptor->enTimerType = enIpV4Subnet;
            pTimerDescriptor->nIpV4SubnetIndex = i;
            RtlCopyMemory(
                &pTimerDescriptor->stIpV4Subnet,
                &m_stFirewallConfig.aBlackSubnetList[i],
                sizeof(IP4SUBNET_STRUCT)
                );
            m_stFirewallConfig.aBlackSubnetTimers[i] = hQueueTimer;
            m_oTimerArray[hQueueTimer] = pTimerDescriptor;
            bRet = DeviceIoControl(
                m_hDnsFirewallDevice,
                DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
                &m_stFirewallConfig,
                sizeof(m_stFirewallConfig),
                NULL,
                0,
                &dwResult,
                NULL
                );
            if (!bRet)
            {
                dwResult = ::GetLastError();
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                    dwResult
                    );
                m_oTimerArray.erase(hQueueTimer);
                delete pTimerDescriptor;
                bRet = DeleteTimerQueueTimer(
                    m_hTimerQueue,
                    hQueueTimer,
                    NULL
                    );
            }
            return;
        }
    }
    nIpV4SingleAddrIndex = m_stFirewallConfig.nBlackListLength;
    for (i = 0; i < nIpV4SingleAddrIndex; i++)
    {
        if (ulIpAddress == m_stFirewallConfig.aBlackListArray[i])
        {
            TraceEvents(
                TRACE_LEVEL_WARNING,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Host %ws already Exist in Black List!",
                szIpAddress
                );
            hQueueTimer = m_stFirewallConfig.aBlackListTimers[i];
            if (NULL != hQueueTimer)
            {
                bRet = DeleteTimerQueueTimer(
                    m_hTimerQueue,
                    hQueueTimer,
                    NULL
                    );
                m_stFirewallConfig.aBlackSubnetTimers[i] = NULL;
                if (!bRet)
                {
                    dwResult = ::GetLastError();
                    if (ERROR_IO_PENDING != dwResult)
                    {
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            TRACE_DNS_STATE_THREAD,
                            "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeleteTimerQueueTimer\" faled with error: %!WINERROR!",
                            dwResult
                            );
                        bRet = DeviceIoControl(
                            m_hDnsFirewallDevice,
                            DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
                            &m_stFirewallConfig,
                            sizeof(m_stFirewallConfig),
                            NULL,
                            0,
                            &dwResult,
                            NULL
                            );
                        if (!bRet)
                        {
                            dwResult = ::GetLastError();
                            TraceEvents(
                                TRACE_LEVEL_ERROR,
                                TRACE_DNS_STATE_THREAD,
                                "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                                dwResult
                                );
                        }
                        return;
                    }
                }
                oTimerArrayIterator = m_oTimerArray.find(hQueueTimer);
                if (oTimerArrayIterator == m_oTimerArray.end())
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Error on find element in Timer Array"
                        );
                    bRet = DeviceIoControl(
                        m_hDnsFirewallDevice,
                        DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
                        &m_stFirewallConfig,
                        sizeof(m_stFirewallConfig),
                        NULL,
                        0,
                        &dwResult,
                        NULL
                        );
                    if (!bRet)
                    {
                        dwResult = ::GetLastError();
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            TRACE_DNS_STATE_THREAD,
                            "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                            dwResult
                            );
                    }
                    return;
                }
                pTimerDescriptor = oTimerArrayIterator->second;
                delete pTimerDescriptor;
                m_oTimerArray.erase(oTimerArrayIterator);
            }
            pTimerDescriptor = new TTimerDescriptor;
            bRet = CreateTimerQueueTimer(
                &hQueueTimer,
                m_hTimerQueue,
                sfTimerProc,
                pTimerDescriptor,
                600000,
                0,
                WT_EXECUTEONLYONCE
                );
            if (!bRet)
            {
                dwResult = ::GetLastError();
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"CreateTimerQueueTimer\" faled with error: %!WINERROR!",
                    dwResult
                    );
                delete pTimerDescriptor;
                bRet = DeviceIoControl(
                    m_hDnsFirewallDevice,
                    DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
                    &m_stFirewallConfig,
                    sizeof(m_stFirewallConfig),
                    NULL,
                    0,
                    &dwResult,
                    NULL
                    );
                if (!bRet)
                {
                    dwResult = ::GetLastError();
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                        dwResult
                        );
                }
                return;
            }
            RtlZeroMemory(
                pTimerDescriptor,
                sizeof(TTimerDescriptor)
                );
            pTimerDescriptor->enTimerType = enIpV4SingleAddr;
            pTimerDescriptor->nIpV4SingleAddrIndex = i;
            pTimerDescriptor->ulIpV4SingleAddr = ulIpAddress;
            m_stFirewallConfig.aBlackListTimers[i] = hQueueTimer;
            m_oTimerArray[hQueueTimer] = pTimerDescriptor;
            bRet = DeviceIoControl(
                m_hDnsFirewallDevice,
                DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
                &m_stFirewallConfig,
                sizeof(m_stFirewallConfig),
                NULL,
                0,
                &dwResult,
                NULL
                );
            if (!bRet)
            {
                dwResult = ::GetLastError();
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                    dwResult
                    );
                m_oTimerArray.erase(hQueueTimer);
                delete pTimerDescriptor;
                bRet = DeleteTimerQueueTimer(
                    m_hTimerQueue,
                    hQueueTimer,
                    NULL
                    );
            }
            return;
        }
    }
    if (128 == m_stFirewallConfig.nBlackListLength)
    {
        // èíèöèèðîâàòü ðàáî÷èå ìàññèâû
        for (i = 0; i < 128; i++)
        {
            aBlockedIndexArray[i] = i;
            aBlockedAddressArray[i] = m_stFirewallConfig.aBlackListArray[i];
            aBlockedTimerArray[i] = m_stFirewallConfig.aBlackListTimers[i];
        }
        // óïîðÿäî÷èòü ðàáî÷èå ìàññèâû ïî âîçðàñòàíèþ
        for (i = 0; i < 127; i++)
        {
            ulBlockedAddrMin = aBlockedAddressArray[i];
            ulBlockedTimerMin = aBlockedTimerArray[i];
            nBlockedIndexMin = aBlockedIndexArray[i];
            for (j = i + 1; j < 128; j++)
            {
                ulBlockedAddrAux = aBlockedAddressArray[j];
                ulBlockedTimerAux = aBlockedTimerArray[j];
                nBlockedIndexAux = aBlockedIndexArray[j];
                if (ulBlockedAddrAux < ulBlockedAddrMin)
                {
                    aBlockedAddressArray[j] = ulBlockedAddrMin;
                    aBlockedTimerArray[j] = ulBlockedTimerMin;
                    aBlockedIndexArray[j] = nBlockedIndexMin;
                    aBlockedAddressArray[i] = ulBlockedAddrAux;
                    aBlockedTimerArray[i] = ulBlockedTimerAux;
                    aBlockedIndexArray[i] = nBlockedIndexAux;
                    ulBlockedAddrMin = ulBlockedAddrAux;
                    ulBlockedTimerMin = ulBlockedTimerAux;
                    nBlockedIndexMin = nBlockedIndexAux;
                }
            }
        }
        // ïîèñê 8-áèòîâûõ ïîäñåòåé
        j = 0;
        ulSubnetPart = (aBlockedAddressArray[0] & 0xFFFFFF00);
        bSubnetExist = false;
        for (i = 1; i < 128; i++)
        {
            ulBlockedAddrAux = (aBlockedAddressArray[i] & 0xFFFFFF00);
            if (ulBlockedAddrAux != ulSubnetPart)
            {
                if (i - j >= 16)
                {
                    bSubnetExist = true;
                    k = i;
                    l = i - j;
                    break;
                }
                j = i;
                ulSubnetPart = ulBlockedAddrAux;
            }
        }
        if (bSubnetExist && (m_stFirewallConfig.nBlackSubnetListLength < 32))
        {
            // áëîêèðîâêà 8-áèòíîé ïîäñåòè
            for (i = j; i < k; i++)
            {
                m = i + l;
                hQueueTimer = aBlockedTimerArray[i];
                oTimerArrayIterator = m_oTimerArray.find(hQueueTimer);
                if (oTimerArrayIterator == m_oTimerArray.end())
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Black List V4 Timer not in Timers Array"
                        );
                }
                else
                {
                    pTimerDescriptor = oTimerArrayIterator->second;
                    bValidTimer = (enIpV4SingleAddr == pTimerDescriptor->enTimerType)
                        && (aBlockedIndexArray[i] == pTimerDescriptor->nIpV4SingleAddrIndex);
                    if (bValidTimer)
                    {
                        bRet = DeleteTimerQueueTimer(
                            m_hTimerQueue,
                            hQueueTimer,
                            NULL
                            );
                        if (!bRet)
                        {
                            dwResult = ::GetLastError();
                            if (ERROR_IO_PENDING != dwResult)
                            {
                                TraceEvents(
                                    TRACE_LEVEL_ERROR,
                                    TRACE_DNS_STATE_THREAD,
                                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeleteTimerQueueTimer\" faled with error: %!WINERROR!",
                                    dwResult
                                    );
                            }
                        }
                        delete pTimerDescriptor;
                        m_oTimerArray.erase(oTimerArrayIterator);
                    }
                    else
                    {
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            TRACE_DNS_STATE_THREAD,
                            "%!FUNC! line:%!LINE! %!LEVEL! Not valid Black List V4 Timer"
                            );
                    }
                }
                aBlockedAddressArray[i] = aBlockedAddressArray[m];
                aBlockedTimerArray[i] = aBlockedTimerArray[m];
                aBlockedIndexArray[i] = aBlockedIndexArray[m];
            }
            m_stFirewallConfig.nBlackListLength -= l;
            for (i = 0; i < m_stFirewallConfig.nBlackListLength; i++)
            {
                j = aBlockedIndexArray[i];
                m_stFirewallConfig.aBlackListArray[i] = aBlockedAddressArray[i];
                hQueueTimer = aBlockedTimerArray[i];
                m_stFirewallConfig.aBlackListTimers[i] = hQueueTimer;
                oTimerArrayIterator = m_oTimerArray.find(hQueueTimer);
                if (oTimerArrayIterator == m_oTimerArray.end())
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Black List V4 Timer not in Timers Array"
                        );
                }
                else
                {
                    pTimerDescriptor = oTimerArrayIterator->second;
                    bValidTimer = (enIpV4SingleAddr == pTimerDescriptor->enTimerType)
                        && (j == pTimerDescriptor->nIpV4SingleAddrIndex);
                    if (bValidTimer)
                    {
                        pTimerDescriptor->nIpV4SingleAddrIndex = i;
                    }
                    else
                    {
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            TRACE_DNS_STATE_THREAD,
                            "%!FUNC! line:%!LINE! %!LEVEL! Not valid Black List V4 Timer"
                            );
                    }
                }
            }
            nIpV4SubnetIndex = m_stFirewallConfig.nBlackSubnetListLength;
            m_stFirewallConfig.nBlackSubnetListLength++;
            m_stFirewallConfig.aBlackSubnetList[nIpV4SubnetIndex]._ulIpAddr = ulSubnetPart;
            m_stFirewallConfig.aBlackSubnetList[nIpV4SubnetIndex]._ulIpMask = 0xFFFFFF00;
            pTimerDescriptor = new TTimerDescriptor;
            bRet = CreateTimerQueueTimer(
                &hQueueTimer,
                m_hTimerQueue,
                sfTimerProc,
                pTimerDescriptor,
                1200000,
                0,
                WT_EXECUTEONLYONCE
                );
            if (!bRet)
            {
                dwResult = ::GetLastError();
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"CreateTimerQueueTimer\" faled with error: %!WINERROR!",
                    dwResult
                    );
                delete pTimerDescriptor;
            }
            else
            {
                RtlZeroMemory(
                    pTimerDescriptor,
                    sizeof(TTimerDescriptor)
                    );
                pTimerDescriptor->enTimerType = enIpV4Subnet;
                pTimerDescriptor->nIpV4SubnetIndex = nIpV4SubnetIndex;
                RtlCopyMemory(
                    &pTimerDescriptor->stIpV4Subnet,
                    &m_stFirewallConfig.aBlackSubnetList[nIpV4SubnetIndex],
                    sizeof(IP4SUBNET_STRUCT)
                    );
                m_stFirewallConfig.aBlackSubnetTimers[nIpV4SubnetIndex] = hQueueTimer;
                m_oTimerArray[hQueueTimer] = pTimerDescriptor;
            }
            ulBlockedAddrAux = (ulIpAddress & 0xFFFFFF00);
            if (ulBlockedAddrAux != ulSubnetPart)
            {
                nIpV4SingleAddrIndex = m_stFirewallConfig.nBlackListLength;
                m_stFirewallConfig.nBlackListLength++;
                m_stFirewallConfig.aBlackListArray[nIpV4SingleAddrIndex] = ulIpAddress;
                pTimerDescriptor = new TTimerDescriptor;
                bRet = CreateTimerQueueTimer(
                    &hQueueTimer,
                    m_hTimerQueue,
                    sfTimerProc,
                    pTimerDescriptor,
                    600000,
                    0,
                    WT_EXECUTEONLYONCE
                    );
                if (!bRet)
                {
                    dwResult = ::GetLastError();
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Call \"CreateTimerQueueTimer\" faled with error: %!WINERROR!",
                        dwResult
                        );
                    delete pTimerDescriptor;
                }
                else
                {
                    RtlZeroMemory(
                        pTimerDescriptor,
                        sizeof(TTimerDescriptor)
                        );
                    pTimerDescriptor->enTimerType = enIpV4SingleAddr;
                    pTimerDescriptor->nIpV4SingleAddrIndex = nIpV4SingleAddrIndex;
                    pTimerDescriptor->ulIpV4SingleAddr = ulIpAddress;
                    m_stFirewallConfig.aBlackListTimers[nIpV4SingleAddrIndex] = hQueueTimer;
                    m_oTimerArray[hQueueTimer] = pTimerDescriptor;
                }
            }
        }
        else
        {   // !(bSubnetExist && (m_stFirewallConfig.nBlackSubnetListLength < 32))
            for (i = 0; i < m_stFirewallConfig.nBlackListLength; i++)
            {
                hQueueTimer = m_stFirewallConfig.aBlackListTimers[i];
                oTimerArrayIterator = m_oTimerArray.find(hQueueTimer);
                if (oTimerArrayIterator == m_oTimerArray.end())
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Black List V4 Timer not in Timers Array"
                        );
                }
                else
                {
                    pTimerDescriptor = oTimerArrayIterator->second;
                    bValidTimer = (enIpV4SingleAddr == pTimerDescriptor->enTimerType)
                        && (i == pTimerDescriptor->nIpV4SingleAddrIndex);
                    if (bValidTimer)
                    {
                        bRet = DeleteTimerQueueTimer(
                            m_hTimerQueue,
                            hQueueTimer,
                            NULL
                            );
                        if (!bRet)
                        {
                            dwResult = ::GetLastError();
                            if (ERROR_IO_PENDING != dwResult)
                            {
                                TraceEvents(
                                    TRACE_LEVEL_ERROR,
                                    TRACE_DNS_STATE_THREAD,
                                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeleteTimerQueueTimer\" faled with error: %!WINERROR!",
                                    dwResult
                                    );
                            }
                        }
                        delete pTimerDescriptor;
                        m_oTimerArray.erase(oTimerArrayIterator);
                    }
                    else
                    {
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            TRACE_DNS_STATE_THREAD,
                            "%!FUNC! line:%!LINE! %!LEVEL! Not valid Black List V4 Timer"
                            );
                    }
                }
            }
            m_stFirewallConfig.nBlackListLength = 0;
            for (i = 0; i < m_stFirewallConfig.nBlackSubnetListLength; i++)
            {
                hQueueTimer = m_stFirewallConfig.aBlackSubnetTimers[i];
                oTimerArrayIterator = m_oTimerArray.find(hQueueTimer);
                if (oTimerArrayIterator == m_oTimerArray.end())
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Black List V4 Subnet Timer not in Timers Array"
                        );
                }
                else
                {
                    pTimerDescriptor = oTimerArrayIterator->second;
                    bValidTimer = (enIpV4Subnet == pTimerDescriptor->enTimerType)
                        && (i == pTimerDescriptor->nIpV4SubnetIndex);
                    if (bValidTimer)
                    {
                        bRet = DeleteTimerQueueTimer(
                            m_hTimerQueue,
                            hQueueTimer,
                            NULL
                            );
                        if (!bRet)
                        {
                            dwResult = ::GetLastError();
                            if (ERROR_IO_PENDING != dwResult)
                            {
                                TraceEvents(
                                    TRACE_LEVEL_ERROR,
                                    TRACE_DNS_STATE_THREAD,
                                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeleteTimerQueueTimer\" faled with error: %!WINERROR!",
                                    dwResult
                                    );
                            }
                        }
                        delete pTimerDescriptor;
                        m_oTimerArray.erase(oTimerArrayIterator);
                    }
                    else
                    {
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            TRACE_DNS_STATE_THREAD,
                            "%!FUNC! line:%!LINE! %!LEVEL! Not valid Black List V4 Subnet Timer"
                            );
                    }
                }
            }
            m_stFirewallConfig.nBlackSubnetListLength = 0;
            for (i = 0; i < m_stFirewallConfig.nBlackListV6Length; i++)
            {
                hQueueTimer = m_stFirewallConfig.aBlackListV6Timers[i];
                oTimerArrayIterator = m_oTimerArray.find(hQueueTimer);
                if (oTimerArrayIterator == m_oTimerArray.end())
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Black List V6 Timer not in Timers Array"
                        );
                }
                else
                {
                    pTimerDescriptor = oTimerArrayIterator->second;
                    bValidTimer = (enIpV6SingleAddr == pTimerDescriptor->enTimerType)
                        && (i == pTimerDescriptor->nIpV6SingleAddrIndex);
                    if (bValidTimer)
                    {
                        bRet = DeleteTimerQueueTimer(
                            m_hTimerQueue,
                            hQueueTimer,
                            NULL
                            );
                        if (!bRet)
                        {
                            dwResult = ::GetLastError();
                            if (ERROR_IO_PENDING != dwResult)
                            {
                                TraceEvents(
                                    TRACE_LEVEL_ERROR,
                                    TRACE_DNS_STATE_THREAD,
                                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeleteTimerQueueTimer\" faled with error: %!WINERROR!",
                                    dwResult
                                    );
                            }
                        }
                        delete pTimerDescriptor;
                        m_oTimerArray.erase(oTimerArrayIterator);
                    }
                    else
                    {
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            TRACE_DNS_STATE_THREAD,
                            "%!FUNC! line:%!LINE! %!LEVEL! Not valid Black List V6 Timer"
                            );
                    }
                }
            }
            m_stFirewallConfig.nBlackListV6Length = 0;
            m_stFirewallConfig.bExternalClientBlocked = TRUE;
            pTimerDescriptor = new TTimerDescriptor;
            bRet = CreateTimerQueueTimer(
                &hQueueTimer,
                m_hTimerQueue,
                sfTimerProc,
                pTimerDescriptor,
                1800000,
                0,
                WT_EXECUTEONLYONCE
                );
            if (!bRet)
            {
                dwResult = ::GetLastError();
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"CreateTimerQueueTimer\" faled with error: %!WINERROR!",
                    dwResult
                    );
                delete pTimerDescriptor;
            }
            else
            {
                RtlZeroMemory(
                    pTimerDescriptor,
                    sizeof(TTimerDescriptor)
                    );
                pTimerDescriptor->enTimerType = enExternalClientBlocked;
                m_stFirewallConfig.hEternalClientBlockedTimer = hQueueTimer;
                m_oTimerArray[hQueueTimer] = pTimerDescriptor;
            }
        }
    }
    else
    {   // 128 != m_stFirewallConfig.nBlackListLength
        m_stFirewallConfig.nBlackListLength++;
        m_stFirewallConfig.aBlackListArray[nIpV4SingleAddrIndex] = ulIpAddress;
        pTimerDescriptor = new TTimerDescriptor;
        bRet = CreateTimerQueueTimer(
            &hQueueTimer,
            m_hTimerQueue,
            sfTimerProc,
            pTimerDescriptor,
            600000,
            0,
            WT_EXECUTEONLYONCE
            );
        if (!bRet)
        {
            dwResult = ::GetLastError();
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Call \"CreateTimerQueueTimer\" faled with error: %!WINERROR!",
                dwResult
                );
            delete pTimerDescriptor;
        }
        else
        {
            RtlZeroMemory(
                pTimerDescriptor,
                sizeof(TTimerDescriptor)
                );
            pTimerDescriptor->enTimerType = enIpV4SingleAddr;
            pTimerDescriptor->nIpV4SingleAddrIndex = nIpV4SingleAddrIndex;
            pTimerDescriptor->ulIpV4SingleAddr = ulIpAddress;
            m_stFirewallConfig.aBlackListTimers[nIpV4SingleAddrIndex] = hQueueTimer;
            m_oTimerArray[hQueueTimer] = pTimerDescriptor;
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Host %ws was Added to Black List; nIpV4SingleAddrIndex = %d; hQueueTimer = 0x%016I64X;",
                szIpAddress,
                nIpV4SingleAddrIndex,
                (UINT64) hQueueTimer
                );
        }
    }
    bRet = DeviceIoControl(
        m_hDnsFirewallDevice,
        DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
        &m_stFirewallConfig,
        sizeof(m_stFirewallConfig),
        NULL,
        0,
        &dwResult,
        NULL
        );
    if (!bRet)
    {
        dwResult = ::GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
            dwResult
            );
        return;
    }
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNS_STATE_THREAD,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
}


void CDnsStateThread::ResetFirewallIpV6Config(FWP_BYTE_ARRAY16& rRemoteAddressV6)
{
    BOOL                    bRet;
    DWORD                   dwResult;
    int                     nIpV6SingleAddrIndex;
    TTimerArray::iterator   oTimerArrayIterator;
    HANDLE                  hQueueTimer;
    TTimerDescriptor*       pTimerDescriptor;
    bool                    bValidTimer;
    int                     i;
    IN6_ADDR                stIp6Address;
    WCHAR                   szIpAddress[128];
    LPWSTR                  pAddressEnd;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNS_STATE_THREAD,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    RtlCopyMemory(
        &stIp6Address,
        &rRemoteAddressV6,
        sizeof(stIp6Address)
        );
    pAddressEnd = RtlIpv6AddressToStringW(
        &stIp6Address,
        szIpAddress
        );
    bRet = DeviceIoControl(
        m_hDnsFirewallDevice,
        DNSFIREWALL_IOCTL_GET_FIREWALL_CONFIG,
        NULL,
        0,
        &m_stFirewallConfig,
        sizeof(m_stFirewallConfig),
        &dwResult,
        NULL
        );
    if (!bRet)
    {
        dwResult = ::GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
            dwResult
            );
        return;
    }
    if (m_stFirewallConfig.bExternalClientBlocked)
    {
        hQueueTimer = m_stFirewallConfig.hEternalClientBlockedTimer;
        if (NULL != hQueueTimer)
        {
            bRet = DeleteTimerQueueTimer(
                m_hTimerQueue,
                hQueueTimer,
                NULL
                );
            m_stFirewallConfig.hEternalClientBlockedTimer = NULL;
            if (!bRet)
            {
                dwResult = ::GetLastError();
                if (ERROR_IO_PENDING != dwResult)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeleteTimerQueueTimer\" faled with error: %!WINERROR!",
                        dwResult
                        );
                    bRet = DeviceIoControl(
                        m_hDnsFirewallDevice,
                        DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
                        &m_stFirewallConfig,
                        sizeof(m_stFirewallConfig),
                        NULL,
                        0,
                        &dwResult,
                        NULL
                        );
                    if (!bRet)
                    {
                        dwResult = ::GetLastError();
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            TRACE_DNS_STATE_THREAD,
                            "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                            dwResult
                            );
                    }
                    return;
                }
            }
            oTimerArrayIterator = m_oTimerArray.find(hQueueTimer);
            if (oTimerArrayIterator == m_oTimerArray.end())
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Error on find element in Timer Array"
                    );
                bRet = DeviceIoControl(
                    m_hDnsFirewallDevice,
                    DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
                    &m_stFirewallConfig,
                    sizeof(m_stFirewallConfig),
                    NULL,
                    0,
                    &dwResult,
                    NULL
                    );
                if (!bRet)
                {
                    dwResult = ::GetLastError();
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                        dwResult
                        );
                }
                return;
            }
            pTimerDescriptor = oTimerArrayIterator->second;
            delete pTimerDescriptor;
            m_oTimerArray.erase(oTimerArrayIterator);
        }
        pTimerDescriptor = new TTimerDescriptor;
        bRet = CreateTimerQueueTimer(
            &hQueueTimer,
            m_hTimerQueue,
            sfTimerProc,
            pTimerDescriptor,
            1800000,
            0,
            WT_EXECUTEONLYONCE
            );
        if (!bRet)
        {
            dwResult = ::GetLastError();
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Call \"CreateTimerQueueTimer\" faled with error: %!WINERROR!",
                dwResult
                );
            delete pTimerDescriptor;
            bRet = DeviceIoControl(
                m_hDnsFirewallDevice,
                DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
                &m_stFirewallConfig,
                sizeof(m_stFirewallConfig),
                NULL,
                0,
                &dwResult,
                NULL
                );
            if (!bRet)
            {
                dwResult = ::GetLastError();
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                    dwResult
                    );
            }
            return;
        }
        RtlZeroMemory(
            pTimerDescriptor,
            sizeof(TTimerDescriptor)
            );
        pTimerDescriptor->enTimerType = enExternalClientBlocked;
        m_stFirewallConfig.hEternalClientBlockedTimer = hQueueTimer;
        m_oTimerArray[hQueueTimer] = pTimerDescriptor;
        bRet = DeviceIoControl(
            m_hDnsFirewallDevice,
            DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
            &m_stFirewallConfig,
            sizeof(m_stFirewallConfig),
            NULL,
            0,
            &dwResult,
            NULL
            );
        if (!bRet)
        {
            dwResult = ::GetLastError();
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                dwResult
                );
        }
        return;
    }
    nIpV6SingleAddrIndex = m_stFirewallConfig.nBlackListV6Length;
    for (i = 0; i < nIpV6SingleAddrIndex; i++)
    {
        if (bfIsIPv6AddressesEqual(
            &m_stFirewallConfig.aBlackListV6Array[i],
            &rRemoteAddressV6
            ))
        {
            hQueueTimer = m_stFirewallConfig.aBlackListV6Timers[i];
            TraceEvents(
                TRACE_LEVEL_WARNING,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Host %ws already Exist in Black List!",
                szIpAddress
                );
            if (NULL != hQueueTimer)
            {
                bRet = DeleteTimerQueueTimer(
                    m_hTimerQueue,
                    hQueueTimer,
                    NULL
                    );
                m_stFirewallConfig.aBlackListV6Timers[i] = NULL;
                if (!bRet)
                {
                    dwResult = ::GetLastError();
                    if (ERROR_IO_PENDING != dwResult)
                    {
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            TRACE_DNS_STATE_THREAD,
                            "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeleteTimerQueueTimer\" faled with error: %!WINERROR!",
                            dwResult
                            );
                        bRet = DeviceIoControl(
                            m_hDnsFirewallDevice,
                            DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
                            &m_stFirewallConfig,
                            sizeof(m_stFirewallConfig),
                            NULL,
                            0,
                            &dwResult,
                            NULL
                            );
                        if (!bRet)
                        {
                            dwResult = ::GetLastError();
                            TraceEvents(
                                TRACE_LEVEL_ERROR,
                                TRACE_DNS_STATE_THREAD,
                                "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                                dwResult
                                );
                        }
                        return;
                    }
                }
                oTimerArrayIterator = m_oTimerArray.find(hQueueTimer);
                if (oTimerArrayIterator == m_oTimerArray.end())
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Error on find element in Timer Array"
                        );
                    bRet = DeviceIoControl(
                        m_hDnsFirewallDevice,
                        DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
                        &m_stFirewallConfig,
                        sizeof(m_stFirewallConfig),
                        NULL,
                        0,
                        &dwResult,
                        NULL
                        );
                    if (!bRet)
                    {
                        dwResult = ::GetLastError();
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            TRACE_DNS_STATE_THREAD,
                            "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                            dwResult
                            );
                    }
                    return;
                }
                pTimerDescriptor = oTimerArrayIterator->second;
                delete pTimerDescriptor;
                m_oTimerArray.erase(oTimerArrayIterator);
            }
            pTimerDescriptor = new TTimerDescriptor;
            bRet = CreateTimerQueueTimer(
                &hQueueTimer,
                m_hTimerQueue,
                sfTimerProc,
                pTimerDescriptor,
                600000,
                0,
                WT_EXECUTEONLYONCE
                );
            if (!bRet)
            {
                dwResult = ::GetLastError();
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"CreateTimerQueueTimer\" faled with error: %!WINERROR!",
                    dwResult
                    );
                delete pTimerDescriptor;
                bRet = DeviceIoControl(
                    m_hDnsFirewallDevice,
                    DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
                    &m_stFirewallConfig,
                    sizeof(m_stFirewallConfig),
                    NULL,
                    0,
                    &dwResult,
                    NULL
                    );
                if (!bRet)
                {
                    dwResult = ::GetLastError();
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                        dwResult
                        );
                }
                return;
            }
            RtlZeroMemory(
                pTimerDescriptor,
                sizeof(TTimerDescriptor)
                );
            pTimerDescriptor->enTimerType = enIpV6SingleAddr;
            pTimerDescriptor->nIpV6SingleAddrIndex = i;
            IPv6CopyAddress(
                &pTimerDescriptor->stIpV6SingleAddr,
                &rRemoteAddressV6
                );
            m_stFirewallConfig.aBlackListV6Timers[i] = hQueueTimer;
            m_oTimerArray[hQueueTimer] = pTimerDescriptor;
            bRet = DeviceIoControl(
                m_hDnsFirewallDevice,
                DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
                &m_stFirewallConfig,
                sizeof(m_stFirewallConfig),
                NULL,
                0,
                &dwResult,
                NULL
                );
            if (!bRet)
            {
                dwResult = ::GetLastError();
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
                    dwResult
                    );
            }
            return;
        }
    }
    if (128 == m_stFirewallConfig.nBlackListV6Length)
    {
        for (i = 0; i < m_stFirewallConfig.nBlackListLength; i++)
        {
            hQueueTimer = m_stFirewallConfig.aBlackListTimers[i];
            oTimerArrayIterator = m_oTimerArray.find(hQueueTimer);
            if (oTimerArrayIterator == m_oTimerArray.end())
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Black List V4 Timer not in Timers Array"
                    );
            }
            else
            {
                pTimerDescriptor = oTimerArrayIterator->second;
                bValidTimer = (enIpV4SingleAddr == pTimerDescriptor->enTimerType)
                    && (i == pTimerDescriptor->nIpV4SingleAddrIndex);
                if (bValidTimer)
                {
                    bRet = DeleteTimerQueueTimer(
                        m_hTimerQueue,
                        hQueueTimer,
                        NULL
                        );
                    if (!bRet)
                    {
                        dwResult = ::GetLastError();
                        if (ERROR_IO_PENDING != dwResult)
                        {
                            TraceEvents(
                                TRACE_LEVEL_ERROR,
                                TRACE_DNS_STATE_THREAD,
                                "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeleteTimerQueueTimer\" faled with error: %!WINERROR!",
                                dwResult
                                );
                        }
                    }
                    delete pTimerDescriptor;
                    m_oTimerArray.erase(oTimerArrayIterator);
                }
                else
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Not valid Black List V4 Timer"
                        );
                }
            }
        }
        m_stFirewallConfig.nBlackListLength = 0;
        for (i = 0; i < m_stFirewallConfig.nBlackSubnetListLength; i++)
        {
            hQueueTimer = m_stFirewallConfig.aBlackSubnetTimers[i];
            oTimerArrayIterator = m_oTimerArray.find(hQueueTimer);
            if (oTimerArrayIterator == m_oTimerArray.end())
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Black List V4 Subnet Timer not in Timers Array"
                    );
            }
            else
            {
                pTimerDescriptor = oTimerArrayIterator->second;
                bValidTimer = (enIpV4Subnet == pTimerDescriptor->enTimerType)
                    && (i == pTimerDescriptor->nIpV4SubnetIndex);
                if (bValidTimer)
                {
                    bRet = DeleteTimerQueueTimer(
                        m_hTimerQueue,
                        hQueueTimer,
                        NULL
                        );
                    if (!bRet)
                    {
                        dwResult = ::GetLastError();
                        if (ERROR_IO_PENDING != dwResult)
                        {
                            TraceEvents(
                                TRACE_LEVEL_ERROR,
                                TRACE_DNS_STATE_THREAD,
                                "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeleteTimerQueueTimer\" faled with error: %!WINERROR!",
                                dwResult
                                );
                        }
                    }
                    delete pTimerDescriptor;
                    m_oTimerArray.erase(oTimerArrayIterator);
                }
                else
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Not valid Black List V4 Subnet Timer"
                        );
                }
            }
        }
        m_stFirewallConfig.nBlackSubnetListLength = 0;
        for (i = 0; i < m_stFirewallConfig.nBlackListV6Length; i++)
        {
            hQueueTimer = m_stFirewallConfig.aBlackListV6Timers[i];
            oTimerArrayIterator = m_oTimerArray.find(hQueueTimer);
            if (oTimerArrayIterator == m_oTimerArray.end())
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNS_STATE_THREAD,
                    "%!FUNC! line:%!LINE! %!LEVEL! Black List V6 Timer not in Timers Array"
                    );
            }
            else
            {
                pTimerDescriptor = oTimerArrayIterator->second;
                bValidTimer = (enIpV6SingleAddr == pTimerDescriptor->enTimerType)
                    && (i == pTimerDescriptor->nIpV6SingleAddrIndex);
                if (bValidTimer)
                {
                    bRet = DeleteTimerQueueTimer(
                        m_hTimerQueue,
                        hQueueTimer,
                        NULL
                        );
                    if (!bRet)
                    {
                        dwResult = ::GetLastError();
                        if (ERROR_IO_PENDING != dwResult)
                        {
                            TraceEvents(
                                TRACE_LEVEL_ERROR,
                                TRACE_DNS_STATE_THREAD,
                                "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeleteTimerQueueTimer\" faled with error: %!WINERROR!",
                                dwResult
                                );
                        }
                    }
                    delete pTimerDescriptor;
                    m_oTimerArray.erase(oTimerArrayIterator);
                }
                else
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNS_STATE_THREAD,
                        "%!FUNC! line:%!LINE! %!LEVEL! Not valid Black List V6 Timer"
                        );
                }
            }
        }
        m_stFirewallConfig.nBlackListV6Length = 0;
        m_stFirewallConfig.bExternalClientBlocked = TRUE;
        pTimerDescriptor = new TTimerDescriptor;
        bRet = CreateTimerQueueTimer(
            &hQueueTimer,
            m_hTimerQueue,
            sfTimerProc,
            pTimerDescriptor,
            1800000,
            0,
            WT_EXECUTEONLYONCE
            );
        if (!bRet)
        {
            dwResult = ::GetLastError();
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Call \"CreateTimerQueueTimer\" faled with error: %!WINERROR!",
                dwResult
                );
            delete pTimerDescriptor;
        }
        else
        {
            RtlZeroMemory(
                pTimerDescriptor,
                sizeof(TTimerDescriptor)
                );
            pTimerDescriptor->enTimerType = enExternalClientBlocked;
            m_stFirewallConfig.hEternalClientBlockedTimer = hQueueTimer;
            m_oTimerArray[hQueueTimer] = pTimerDescriptor;
        }
    }
    else
    {   // 128 != m_stFirewallConfig.nBlackListV6Length
        m_stFirewallConfig.nBlackListV6Length++;
        IPv6CopyAddress(
            &m_stFirewallConfig.aBlackListV6Array[nIpV6SingleAddrIndex],
            &rRemoteAddressV6
            );
        pTimerDescriptor = new TTimerDescriptor;
        bRet = CreateTimerQueueTimer(
            &hQueueTimer,
            m_hTimerQueue,
            sfTimerProc,
            pTimerDescriptor,
            600000,
            0,
            WT_EXECUTEONLYONCE
            );
        if (!bRet)
        {
            dwResult = ::GetLastError();
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Call \"CreateTimerQueueTimer\" faled with error: %!WINERROR!",
                dwResult
                );
            delete pTimerDescriptor;
        }
        else
        {
            RtlZeroMemory(
                pTimerDescriptor,
                sizeof(TTimerDescriptor)
                );
            pTimerDescriptor->enTimerType = enIpV6SingleAddr;
            pTimerDescriptor->nIpV6SingleAddrIndex = nIpV6SingleAddrIndex;
            IPv6CopyAddress(
                &pTimerDescriptor->stIpV6SingleAddr,
                &rRemoteAddressV6
                );
            m_stFirewallConfig.aBlackListV6Timers[nIpV6SingleAddrIndex] = hQueueTimer;
            m_oTimerArray[hQueueTimer] = pTimerDescriptor;
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                TRACE_DNS_STATE_THREAD,
                "%!FUNC! line:%!LINE! %!LEVEL! Host %ws was Added to Black List; nIpV6SingleAddrIndex = %d; hQueueTimer = 0x%016I64X;",
                szIpAddress,
                nIpV6SingleAddrIndex,
                (UINT64) hQueueTimer
                );
        }
    }
    bRet = DeviceIoControl(
        m_hDnsFirewallDevice,
        DNSFIREWALL_IOCTL_SET_FIREWALL_CONFIG,
        &m_stFirewallConfig,
        sizeof(m_stFirewallConfig),
        NULL,
        0,
        &dwResult,
        NULL
        );
    if (!bRet)
    {
        dwResult = ::GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNS_STATE_THREAD,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"DeviceIoControl\" faled with error: %!WINERROR!",
            dwResult
            );
        return;
    }
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNS_STATE_THREAD,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
}
#pragma warning(pop)
