// WS2InterceptDLL.cpp : implementation file
//

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


// CWS2InterceptAPP

CWS2InterceptAPP theApp;

IMPLEMENT_DYNCREATE(CWS2InterceptAPP, CWinApp)

CWS2InterceptAPP::CWS2InterceptAPP()
: m_pfSavedAcceptFunction(NULL)
, m_pfSavedBindFunction(NULL)
, m_pfSavedClosesocketFunction(NULL)
, m_pfSavedRecvFunction(NULL)
, m_pfSavedSendFunction(NULL)
, m_pfSavedSendtoFunction(NULL)
, m_pfSavedSocketFunction(NULL)
, m_pfSavedWSACleanupFunction(NULL)
, m_pfSavedWSARecvFromFunction(NULL)
, m_pfSavedWSASocketWFunction(NULL)
, m_pfSavedWSAStartupFunction(NULL)
, m_pfSavedGetQueuedCompletionStatusFunction(NULL)
, m_pfSavedCreateIoCompletionPortFunction(NULL)
, m_sDnsZoneName(_T(""))
, m_nPublicIpCount(0)
, m_pPublicIpArray(NULL)
, m_nPublicRangeCount(0)
, m_pPublicRangeArray(NULL)
, m_nPublicSubnetCount(0)
, m_pPublicSubnetArray(NULL)
, m_nPublicIPV6AddrCount(0)
, m_pPublicIPV6AddrArray(NULL)
, m_pPrivateIPV6AddrArray(NULL)
, m_nPrivateIpCount(0)
, m_pPrivateIpArray(NULL)
, m_nPrivateRangeCount(0)
, m_pPrivateRangeArray(NULL)
, m_nPrivateSubnetCount(0)
, m_pPrivateSubnetArray(NULL)
, m_nPrivateIPV6AddrCount(0)
, m_pDnsZoneRecord(NULL)
, m_sNamePrimaryServer(_T(""))
, m_sNameAdministrator(_T(""))
, m_pfSavedRegisterServiceCtrlHandlerWFunction(NULL)
, m_pfSavedSvcHandleFunction(NULL)
, m_oDnsSvcStopedEvent(FALSE, TRUE, _T("Global\\$DNS$SVC$STOPED$"))
, m_dwSerialNo(0)
, m_oPluginStateLock(FALSE, _T("Global\\$DNS$PLUGIN$STATE$LOCK$"))
, m_pDnsStateThread(NULL)
{
    WPP_INIT_TRACING(L"WS2InterceptDLL");
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! \"CWS2InterceptAPP\" object created"
        );
    RtlZeroMemory(
        &m_stIp4TestAddr,
        sizeof(m_stIp4TestAddr)
        );
}

CWS2InterceptAPP::~CWS2InterceptAPP()
{
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! \"CWS2InterceptAPP\" object deleted"
        );
    WPP_CLEANUP();
}

BOOL CWS2InterceptAPP::InitInstance()
{
    BOOL                        bRet;
    NTSTATUS                    nNtStatus = NOERROR;
    PLDR_DATA_TABLE_ENTRY       pModuleEntry;
    bool                        bResult;
    REAL_EXPORT_DESCRIPTOR      stExportDescriptor;
    CString                     sSectionName;
    CString                     sIPAddress;
    LONG                        nRet;
    LPCTSTR                     pTerminator;
    int                         i, j;
    IN_ADDR                     stIP4Address;
    IN6_ADDR                    stIP6Address;
    UINT32                      nPrefixLength;
    PDNS_SOA_DATA               pSoaDataDistanation;
    int                         nStringLength;
    PWSTR                       pDomainName = NULL;
    PBYTE                       pOldEntryPoint;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    _tsetlocale(LC_ALL, _T("Russian"));
    bRet = CWinApp::InitInstance();
    // Измените раздел реестра, в котором хранятся параметры
    // TODO: следует изменить эту строку на что-нибудь подходящее,
    // например на название организации
    SetRegistryKey(_T("Real Time Systems DD\\Public DNS WS System"));
    if (m_pszProfileName != NULL)
    {
        free((void*) m_pszProfileName);
    }
    m_pszProfileName = _tcsdup(_T("PublicDNSPluginConfig"));
    nRet = RtlIpv4StringToAddress(
        _T("192.168.0.17"),
        TRUE,
        &pTerminator,
        &m_stIp4TestAddr
        );
    nNtStatus = UpdateImports(
        //"API-MS-WIN-SERVICE-WINSVC-L1-2-0.DLL",
        "RegisterServiceCtrlHandlerW",
        PBYTE(sf_RegisterServiceCtrlHandlerW),
        pOldEntryPoint
        );
    if (!NT_SUCCESS(nNtStatus))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"UpdateImports\" failed with error %!STATUS!",
            nNtStatus
            );
        return FALSE;
    }
    if (NULL == pOldEntryPoint)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Entry point \"RegisterServiceCtrlHandlerW\" not found"
            );
        return FALSE;
    }
    m_pfSavedRegisterServiceCtrlHandlerWFunction = (tf_RegisterServiceCtrlHandlerW*) pOldEntryPoint;
    nNtStatus = UpdateImports(
        //"API-MS-WIN-CORE-IO-L1-1-1.DLL",
        "CreateIoCompletionPort",
        PBYTE(sf_CreateIoCompletionPort),
        pOldEntryPoint
        );
    if (!NT_SUCCESS(nNtStatus))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"UpdateImports\" failed with error %!STATUS!",
            nNtStatus
            );
        return FALSE;
    }
    if (NULL == pOldEntryPoint)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Entry point \"CreateIoCompletionPort\" not found"
            );
        return FALSE;
    }
    m_pfSavedCreateIoCompletionPortFunction = (tf_CreateIoCompletionPort*) pOldEntryPoint;
    nNtStatus = UpdateImports(
        //"API-MS-WIN-CORE-IO-L1-1-1.DLL",
        "GetQueuedCompletionStatus",
        PBYTE(sf_GetQueuedCompletionStatus),
        pOldEntryPoint
        );
    if (!NT_SUCCESS(nNtStatus))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"UpdateImports\" failed with error %!STATUS!",
            nNtStatus
            );
        return FALSE;
    }
    if (NULL == pOldEntryPoint)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Entry point \"GetQueuedCompletionStatus\" not found"
            );
        return FALSE;
    }
    m_pfSavedGetQueuedCompletionStatusFunction = (tf_GetQueuedCompletionStatus*) pOldEntryPoint;
    pModuleEntry = m_oModule32HLP.FindModule(L"WS2_32.DLL");
    if (NULL == pModuleEntry)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FindModule\" failed"
            );
        return FALSE;
    }
    bResult = m_oModule32HLP.GetExportByName(
        "accept",
        stExportDescriptor
        );
    if (!bResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"GetExportByName\" failed"
            );
        return FALSE;
    }
    m_pfSavedAcceptFunction = (tf_accept*) stExportDescriptor.pEntryPoint;
    nNtStatus = UpdateImports(
        PBYTE(m_pfSavedAcceptFunction),
        PBYTE(sf_accept)
        );
    if (!NT_SUCCESS(nNtStatus))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit with error %!STATUS!",
            nNtStatus
            );
        return FALSE;
    }
    pModuleEntry = m_oModule32HLP.FindModule(L"WS2_32.DLL");
    if (NULL == pModuleEntry)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FindModule\" failed"
            );
        return FALSE;
    }
    bResult = m_oModule32HLP.GetExportByName(
        "bind",
        stExportDescriptor
        );
    if (!bResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"GetExportByName\" failed"
            );
        return FALSE;
    }
    m_pfSavedBindFunction = (tf_bind*) stExportDescriptor.pEntryPoint;
    nNtStatus = UpdateImports(
        PBYTE(m_pfSavedBindFunction),
        PBYTE(sf_bind)
        );
    if (!NT_SUCCESS(nNtStatus))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit with error %!STATUS!",
            nNtStatus
            );
        return FALSE;
    }
    pModuleEntry = m_oModule32HLP.FindModule(L"WS2_32.DLL");
    if (NULL == pModuleEntry)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FindModule\" failed"
            );
        return FALSE;
    }
    bResult = m_oModule32HLP.GetExportByName(
        "closesocket",
        stExportDescriptor
        );
    if (!bResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"GetExportByName\" failed"
            );
        return FALSE;
    }
    m_pfSavedClosesocketFunction = (tf_closesocket*) stExportDescriptor.pEntryPoint;
    nNtStatus = UpdateImports(
        PBYTE(m_pfSavedClosesocketFunction),
        PBYTE(sf_closesocket)
        );
    if (!NT_SUCCESS(nNtStatus))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit with error %!STATUS!",
            nNtStatus
            );
        return FALSE;
    }
    pModuleEntry = m_oModule32HLP.FindModule(L"WS2_32.DLL");
    if (NULL == pModuleEntry)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FindModule\" failed"
            );
        return FALSE;
    }
    bResult = m_oModule32HLP.GetExportByName(
        "recv",
        stExportDescriptor
        );
    if (!bResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"GetExportByName\" failed"
            );
        return FALSE;
    }
    m_pfSavedRecvFunction = (tf_recv*) stExportDescriptor.pEntryPoint;
    nNtStatus = UpdateImports(
        PBYTE(m_pfSavedRecvFunction),
        PBYTE(sf_recv)
        );
    if (!NT_SUCCESS(nNtStatus))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit with error %!STATUS!",
            nNtStatus
            );
        return FALSE;
    }
    pModuleEntry = m_oModule32HLP.FindModule(L"WS2_32.DLL");
    if (NULL == pModuleEntry)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FindModule\" failed"
            );
        return FALSE;
    }
    bResult = m_oModule32HLP.GetExportByName(
        "send",
        stExportDescriptor
        );
    if (!bResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"GetExportByName\" failed"
            );
        return FALSE;
    }
    m_pfSavedSendFunction = (tf_send*) stExportDescriptor.pEntryPoint;
    nNtStatus = UpdateImports(
        PBYTE(m_pfSavedSendFunction),
        PBYTE(sf_send)
        );
    if (!NT_SUCCESS(nNtStatus))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit with error %!STATUS!",
            nNtStatus
            );
        return FALSE;
    }
    pModuleEntry = m_oModule32HLP.FindModule(L"WS2_32.DLL");
    if (NULL == pModuleEntry)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FindModule\" failed"
            );
        return FALSE;
    }
    bResult = m_oModule32HLP.GetExportByName(
        "sendto",
        stExportDescriptor
        );
    if (!bResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"GetExportByName\" failed"
            );
        return FALSE;
    }
    m_pfSavedSendtoFunction = (tf_sendto*) stExportDescriptor.pEntryPoint;
    nNtStatus = UpdateImports(
        PBYTE(m_pfSavedSendtoFunction),
        PBYTE(sf_sendto)
        );
    if (!NT_SUCCESS(nNtStatus))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit with error %!STATUS!",
            nNtStatus
            );
        return FALSE;
    }
    pModuleEntry = m_oModule32HLP.FindModule(L"WS2_32.DLL");
    if (NULL == pModuleEntry)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FindModule\" failed"
            );
        return FALSE;
    }
    bResult = m_oModule32HLP.GetExportByName(
        "socket",
        stExportDescriptor
        );
    if (!bResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"GetExportByName\" failed"
            );
        return FALSE;
    }
    m_pfSavedSocketFunction = (tf_socket*) stExportDescriptor.pEntryPoint;
    nNtStatus = UpdateImports(
        PBYTE(m_pfSavedSocketFunction),
        PBYTE(sf_socket)
        );
    if (!NT_SUCCESS(nNtStatus))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit with error %!STATUS!",
            nNtStatus
            );
        return FALSE;
    }
    pModuleEntry = m_oModule32HLP.FindModule(L"WS2_32.DLL");
    if (NULL == pModuleEntry)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FindModule\" failed"
            );
        return FALSE;
    }
    bResult = m_oModule32HLP.GetExportByName(
        "WSACleanup",
        stExportDescriptor
        );
    if (!bResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"GetExportByName\" failed"
            );
        return FALSE;
    }
    m_pfSavedWSACleanupFunction = (tf_WSACleanup*) stExportDescriptor.pEntryPoint;
    nNtStatus = UpdateImports(
        PBYTE(m_pfSavedWSACleanupFunction),
        PBYTE(sf_WSACleanup)
        );
    if (!NT_SUCCESS(nNtStatus))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit with error %!STATUS!",
            nNtStatus
            );
        return FALSE;
    }
    pModuleEntry = m_oModule32HLP.FindModule(L"WS2_32.DLL");
    if (NULL == pModuleEntry)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FindModule\" failed"
            );
        return FALSE;
    }
    bResult = m_oModule32HLP.GetExportByName(
        "WSARecvFrom",
        stExportDescriptor
        );
    if (!bResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"GetExportByName\" failed"
            );
        return FALSE;
    }
    m_pfSavedWSARecvFromFunction = (tf_WSARecvFrom*) stExportDescriptor.pEntryPoint;
    nNtStatus = UpdateImports(
        PBYTE(m_pfSavedWSARecvFromFunction),
        PBYTE(sf_WSARecvFrom)
        );
    if (!NT_SUCCESS(nNtStatus))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit with error %!STATUS!",
            nNtStatus
            );
        return FALSE;
    }
    pModuleEntry = m_oModule32HLP.FindModule(L"WS2_32.DLL");
    if (NULL == pModuleEntry)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FindModule\" failed"
            );
        return FALSE;
    }
    bResult = m_oModule32HLP.GetExportByName(
        "WSASocketW",
        stExportDescriptor
        );
    if (!bResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"GetExportByName\" failed"
            );
        return FALSE;
    }
    m_pfSavedWSASocketWFunction = (tf_WSASocketW*) stExportDescriptor.pEntryPoint;
    nNtStatus = UpdateImports(
        PBYTE(m_pfSavedWSASocketWFunction),
        PBYTE(sf_WSASocketW)
        );
    if (!NT_SUCCESS(nNtStatus))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit with error %!STATUS!",
            nNtStatus
            );
        return FALSE;
    }
    pModuleEntry = m_oModule32HLP.FindModule(L"WS2_32.DLL");
    if (NULL == pModuleEntry)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FindModule\" failed"
            );
        return FALSE;
    }
    bResult = m_oModule32HLP.GetExportByName(
        "WSAStartup",
        stExportDescriptor
        );
    if (!bResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"GetExportByName\" failed"
            );
        return FALSE;
    }
    m_pfSavedWSAStartupFunction = (tf_WSAStartup*) stExportDescriptor.pEntryPoint;
    nNtStatus = UpdateImports(
        PBYTE(m_pfSavedWSAStartupFunction),
        PBYTE(sf_WSAStartup)
        );
    if (!NT_SUCCESS(nNtStatus))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit with error %!STATUS!",
            nNtStatus
            );
        return FALSE;
    }
    m_sDnsZoneName = GetConfigString(
        _T("service_settings"),
        _T("dns_zone_name"),
        _T("")
        );
    m_pDnsZoneRecord = new CDnsResorceRecord;
    m_pDnsZoneRecord->m_sName = m_sDnsZoneName;
    m_pDnsZoneRecord->m_wType = DNS_TYPE_SOA;
    m_pDnsZoneRecord->m_wClass = DNS_CLASS_INTERNET;
    m_pDnsZoneRecord->m_unDnsRecordFlags.DW = GetConfigInt(
        _T("service_settings"),
        _T("dwFlags"),
        0
        );
    m_pDnsZoneRecord->m_unDnsRecordFlags.S.Section = DnsSectionAuthority;
    m_pDnsZoneRecord->m_dwTtl = GetConfigInt(
        _T("service_settings"),
        _T("dwTtl"),
        0
        );
    m_pDnsZoneRecord->m_wDataLength = WORD(sizeof(DNS_SOA_DATA));
    m_pDnsZoneRecord->m_pDnsRecordData = (PDNS_DATA) malloc(m_pDnsZoneRecord->m_wDataLength);
    pSoaDataDistanation = &m_pDnsZoneRecord->m_pDnsRecordData->SOA;
    m_sNamePrimaryServer = GetConfigString(
        _T("service_settings"),
        _T("pNamePrimaryServer"),
        _T("")
        );
    nStringLength = m_sNamePrimaryServer.GetLength();
    if (nStringLength > 0)
    {
        if (_T('.') != m_sNamePrimaryServer[nStringLength - 1])
        {
            m_sNamePrimaryServer += _T('.');
            nStringLength++;
        }
        pDomainName = _wcsdup((LPCWSTR) m_sNamePrimaryServer);
    }
    pSoaDataDistanation->pNamePrimaryServer = pDomainName;
    m_sNameAdministrator = GetConfigString(
        _T("service_settings"),
        _T("pNameAdministrator"),
        _T("")
        );
    nStringLength = m_sNameAdministrator.GetLength();
    pDomainName = NULL;
    if (nStringLength > 0)
    {
        if (_T('.') != m_sNameAdministrator[nStringLength - 1])
        {
            m_sNameAdministrator += _T('.');
            nStringLength++;
        }
        pDomainName = _wcsdup((LPCWSTR) m_sNameAdministrator);
    }
    pSoaDataDistanation->pNameAdministrator = pDomainName;
    m_dwSerialNo = GetConfigInt(
        _T("service_settings"),
        _T("dwSerialNo"),
        0
        );
    pSoaDataDistanation->dwSerialNo = m_dwSerialNo;
    pSoaDataDistanation->dwRefresh = GetConfigInt(
        _T("service_settings"),
        _T("dwRefresh"),
        0
        );
    pSoaDataDistanation->dwRetry = GetConfigInt(
        _T("service_settings"),
        _T("dwRetry"),
        0
        );
    pSoaDataDistanation->dwExpire = GetConfigInt(
        _T("service_settings"),
        _T("dwExpire"),
        0
        );
    pSoaDataDistanation->dwDefaultTtl = GetConfigInt(
        _T("service_settings"),
        _T("dwDefaultTtl"),
        0
        );
    m_nPublicIpCount = GetConfigInt(
        _T("service_settings"),
        _T("public_ip_count"),
        0
        );
    if (m_nPublicIpCount > 0)
    {
        m_pPublicIpArray = new UINT32[m_nPublicIpCount];
        for (i = 0; i<m_nPublicIpCount; i++)
        {
            sSectionName.Format(
                _T("single_ip%d"),
                i
                );
            sIPAddress = GetConfigString(
                _T("service_settings"),
                sSectionName,
                _T("")
                );
            nRet = RtlIpv4StringToAddress(
                sIPAddress,
                TRUE,
                &pTerminator,
                &stIP4Address
                );
            if (NO_ERROR != nRet)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"RtlIpv4StringToAddress\" failed with status: %!STATUS!",
                    nRet
                    );
                return FALSE;
            }
            INLINE_NTOHL(
                m_pPublicIpArray[i],
                stIP4Address.S_un.S_addr
                );
        }
    }
    m_nPublicRangeCount = GetConfigInt(
        _T("service_settings"),
        _T("public_range_count"),
        0
        );
    if (m_nPublicRangeCount > 0)
    {
        m_pPublicRangeArray = new FWP_RANGE0[m_nPublicRangeCount];
        for (i = 0; i<m_nPublicRangeCount; i++)
        {
            sSectionName.Format(
                _T("range_begin_ip%d"),
                i
                );
            sIPAddress = GetConfigString(
                _T("service_settings"),
                sSectionName,
                _T("")
                );
            nRet = RtlIpv4StringToAddress(
                sIPAddress,
                TRUE,
                &pTerminator,
                &stIP4Address
                );
            if (NO_ERROR != nRet)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"RtlIpv4StringToAddress\" failed with status: %!STATUS!",
                    nRet
                    );
                return FALSE;
            }
            m_pPublicRangeArray[i].valueLow.type = FWP_UINT32;
            INLINE_NTOHL(
                m_pPublicRangeArray[i].valueLow.uint32,
                stIP4Address.S_un.S_addr
                );
            sSectionName.Format(
                _T("range_end_ip%d"),
                i
                );
            sIPAddress = GetConfigString(
                _T("service_settings"),
                sSectionName,
                _T("")
                );
            nRet = RtlIpv4StringToAddress(
                sIPAddress,
                TRUE,
                &pTerminator,
                &stIP4Address
                );
            if (NO_ERROR != nRet)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"RtlIpv4StringToAddress\" failed with status: %!STATUS!",
                    nRet
                    );
                return FALSE;
            }
            m_pPublicRangeArray[i].valueHigh.type = FWP_UINT32;
            INLINE_NTOHL(
                m_pPublicRangeArray[i].valueHigh.uint32,
                stIP4Address.S_un.S_addr
                );
        }
    }
    m_nPublicSubnetCount = GetConfigInt(
        _T("service_settings"),
        _T("public_subnet_count"),
        0
        );
    if (m_nPublicSubnetCount > 0)
    {
        m_pPublicSubnetArray = new FWP_V4_ADDR_AND_MASK[m_nPublicSubnetCount];
        for (i = 0; i<m_nPublicSubnetCount; i++)
        {
            sSectionName.Format(
                _T("subnet_address_ip%d"),
                i
                );
            sIPAddress = GetConfigString(
                _T("service_settings"),
                sSectionName,
                _T("")
                );
            nRet = RtlIpv4StringToAddress(
                sIPAddress,
                TRUE,
                &pTerminator,
                &stIP4Address
                );
            if (NO_ERROR != nRet)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"RtlIpv4StringToAddress\" failed with status: %!STATUS!",
                    nRet
                    );
                return FALSE;
            }
            INLINE_NTOHL(
                m_pPublicSubnetArray[i].addr,
                stIP4Address.S_un.S_addr
                );
            sSectionName.Format(
                _T("subnet_mask_ip%d"),
                i
                );
            sIPAddress = GetConfigString(
                _T("service_settings"),
                sSectionName,
                _T("")
                );
            nRet = RtlIpv4StringToAddress(
                sIPAddress,
                TRUE,
                &pTerminator,
                &stIP4Address
                );
            if (NO_ERROR != nRet)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"RtlIpv4StringToAddress\" failed with status: %!STATUS!",
                    nRet
                    );
                return FALSE;
            }
            INLINE_NTOHL(
                m_pPublicSubnetArray[i].mask,
                stIP4Address.S_un.S_addr
                );
        }
        if (m_nPublicSubnetCount > 1)
        {
            FWP_V4_ADDR_AND_MASK    stAddrAndMask1;
            ULONG                   ulMask1, ulMask2;
            UINT8                   nMaskLength1, nMaskLength2;
            NETIO_STATUS            nioStaus;
            for (i = 0; i < m_nPublicSubnetCount - 1; i++)
            {
                INLINE_HTONL(
                    ulMask1,
                    m_pPublicSubnetArray[i].mask
                    );
                nioStaus = ConvertIpv4MaskToLength(
                    ulMask1,
                    &nMaskLength1
                    );
                for (j = i + 1; j < m_nPublicSubnetCount; j++)
                {
                    INLINE_HTONL(
                        ulMask2,
                        m_pPublicSubnetArray[j].mask
                        );
                    nioStaus = ConvertIpv4MaskToLength(
                        ulMask2,
                        &nMaskLength2
                        );
                    if (nMaskLength2 > nMaskLength1)
                    {
                        RtlCopyMemory(
                            &stAddrAndMask1,
                            &m_pPublicSubnetArray[i],
                            sizeof(FWP_V4_ADDR_AND_MASK)
                            );
                        RtlCopyMemory(
                            &m_pPublicSubnetArray[i],
                            &m_pPublicSubnetArray[j],
                            sizeof(FWP_V4_ADDR_AND_MASK)
                            );
                        RtlCopyMemory(
                            &m_pPublicSubnetArray[j],
                            &stAddrAndMask1,
                            sizeof(FWP_V4_ADDR_AND_MASK)
                            );
                        nMaskLength1 = nMaskLength2;
                    }
                }
            }
        }
    }
    m_nPublicIPV6AddrCount = GetConfigInt(
        _T("service_settings"),
        _T("public_ip6_count"),
        0
        );
    if (m_nPublicIPV6AddrCount > 0)
    {
        m_pPublicIPV6AddrArray = new IPV6_ADDRESS_STRUCT[m_nPublicIPV6AddrCount];
        for (i = 0; i<m_nPublicIPV6AddrCount; i++)
        {
            sSectionName.Format(
                _T("public_ip6_prefix%d"),
                i
                );
            sIPAddress = GetConfigString(
                _T("service_settings"),
                sSectionName,
                _T("")
                );
            nRet = RtlIpv6StringToAddress(
                sIPAddress,
                &pTerminator,
                &stIP6Address
                );
            if (NO_ERROR != nRet)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"RtlIpv6StringToAddress\" failed with status: %!STATUS!",
                    nRet
                    );
                return FALSE;
            }
            RtlCopyMemory(
                m_pPublicIPV6AddrArray[i]._IPv6Addr,
                &stIP6Address,
                sizeof(stIP6Address)
                );
            sSectionName.Format(
                _T("public_ip6_prefix_length%d"),
                i
                );
            nPrefixLength = GetConfigInt(
                _T("service_settings"),
                sSectionName,
                0
                );
            m_pPublicIPV6AddrArray[i]._PrefixLength = nPrefixLength;
        }
        if (m_nPublicIPV6AddrCount > 1)
        {
            IPV6_ADDRESS_STRUCT stIPv6Addres1;
            UINT32              nPrefixLength1, nPrefixLength2;
            for (i = 0; i < m_nPublicIPV6AddrCount - 1; i++)
            {
                nPrefixLength1 = m_pPublicIPV6AddrArray[i]._PrefixLength;
                for (j = i + 1; j < m_nPublicIPV6AddrCount; j++)
                {
                    nPrefixLength2 = m_pPublicIPV6AddrArray[j]._PrefixLength;
                    if (nPrefixLength2 > nPrefixLength1)
                    {
                        RtlCopyMemory(
                            &stIPv6Addres1,
                            &m_pPublicIPV6AddrArray[i],
                            sizeof(IPV6_ADDRESS_STRUCT)
                            );
                        RtlCopyMemory(
                            &m_pPublicIPV6AddrArray[i],
                            &m_pPublicIPV6AddrArray[j],
                            sizeof(IPV6_ADDRESS_STRUCT)
                            );
                        RtlCopyMemory(
                            &m_pPublicIPV6AddrArray[j],
                            &stIPv6Addres1,
                            sizeof(IPV6_ADDRESS_STRUCT)
                            );
                        nPrefixLength1 = nPrefixLength2;
                    }
                }
            }
        }
    }
    m_nPrivateIpCount = GetConfigInt(
        _T("service_settings"),
        _T("private_ip_count"),
        0
        );
    if (m_nPrivateIpCount > 0)
    {
        m_pPrivateIpArray = new UINT32[m_nPrivateIpCount];
        for (i = 0; i<m_nPrivateIpCount; i++)
        {
            sSectionName.Format(
                _T("private_ip%d"),
                i
                );
            sIPAddress = GetConfigString(
                _T("service_settings"),
                sSectionName,
                _T("")
                );
            nRet = RtlIpv4StringToAddress(
                sIPAddress,
                TRUE,
                &pTerminator,
                &stIP4Address
                );
            if (NO_ERROR != nRet)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"RtlIpv4StringToAddress\" failed with status: %!STATUS!",
                    nRet
                    );
                return FALSE;
            }
            INLINE_NTOHL(
                m_pPrivateIpArray[i],
                stIP4Address.S_un.S_addr
                );
        }
    }
    m_nPrivateRangeCount = GetConfigInt(
        _T("service_settings"),
        _T("private_range_count"),
        0
        );
    if (m_nPrivateRangeCount > 0)
    {
        m_pPrivateRangeArray = new FWP_RANGE0[m_nPrivateRangeCount];
        for (i = 0; i<m_nPrivateRangeCount; i++)
        {
            sSectionName.Format(
                _T("private_range_begin%d"),
                i
                );
            sIPAddress = GetConfigString(
                _T("service_settings"),
                sSectionName,
                _T("")
                );
            nRet = RtlIpv4StringToAddress(
                sIPAddress,
                TRUE,
                &pTerminator,
                &stIP4Address
                );
            if (NO_ERROR != nRet)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"RtlIpv4StringToAddress\" failed with status: %!STATUS!",
                    nRet
                    );
                return FALSE;
            }
            m_pPrivateRangeArray[i].valueLow.type = FWP_UINT32;
            INLINE_NTOHL(
                m_pPrivateRangeArray[i].valueLow.uint32,
                stIP4Address.S_un.S_addr
                );
            sSectionName.Format(
                _T("private_range_end%d"),
                i
                );
            sIPAddress = GetConfigString(
                _T("service_settings"),
                sSectionName,
                _T("")
                );
            nRet = RtlIpv4StringToAddress(
                sIPAddress,
                TRUE,
                &pTerminator,
                &stIP4Address
                );
            if (NO_ERROR != nRet)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"RtlIpv4StringToAddress\" failed with status: %!STATUS!",
                    nRet
                    );
                return FALSE;
            }
            m_pPrivateRangeArray[i].valueHigh.type = FWP_UINT32;
            INLINE_NTOHL(
                m_pPrivateRangeArray[i].valueHigh.uint32,
                stIP4Address.S_un.S_addr
                );
        }
    }
    m_nPrivateSubnetCount = GetConfigInt(
        _T("service_settings"),
        _T("private_subnet_count"),
        0
        );
    if (m_nPrivateSubnetCount > 0)
    {
        m_pPrivateSubnetArray = new FWP_V4_ADDR_AND_MASK[m_nPrivateSubnetCount];
        for (i = 0; i<m_nPrivateSubnetCount; i++)
        {
            sSectionName.Format(
                _T("private_subnet_address%d"),
                i
                );
            sIPAddress = GetConfigString(
                _T("service_settings"),
                sSectionName,
                _T("")
                );
            nRet = RtlIpv4StringToAddress(
                sIPAddress,
                TRUE,
                &pTerminator,
                &stIP4Address
                );
            if (NO_ERROR != nRet)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"RtlIpv4StringToAddress\" failed with status: %!STATUS!",
                    nRet
                    );
                return FALSE;
            }
            INLINE_NTOHL(
                m_pPrivateSubnetArray[i].addr,
                stIP4Address.S_un.S_addr
                );
            sSectionName.Format(
                _T("private_subnet_mask%d"),
                i
                );
            sIPAddress = GetConfigString(
                _T("service_settings"),
                sSectionName,
                _T("")
                );
            nRet = RtlIpv4StringToAddress(
                sIPAddress,
                TRUE,
                &pTerminator,
                &stIP4Address
                );
            if (NO_ERROR != nRet)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"RtlIpv4StringToAddress\" failed with status: %!STATUS!",
                    nRet
                    );
                return FALSE;
            }
            INLINE_NTOHL(
                m_pPrivateSubnetArray[i].mask,
                stIP4Address.S_un.S_addr
                );
        }
        if (m_nPrivateSubnetCount > 1)
        {
            FWP_V4_ADDR_AND_MASK    stAddrAndMask1;
            ULONG                   ulMask1, ulMask2;
            UINT8                   nMaskLength1, nMaskLength2;
            NETIO_STATUS            nioStaus;
            for (i = 0; i < m_nPrivateSubnetCount - 1; i++)
            {
                INLINE_HTONL(
                    ulMask1,
                    m_pPrivateSubnetArray[i].mask
                    );
                nioStaus = ConvertIpv4MaskToLength(
                    ulMask1,
                    &nMaskLength1
                    );
                for (j = i + 1; j < m_nPrivateSubnetCount; j++)
                {
                    INLINE_HTONL(
                        ulMask2,
                        m_pPrivateSubnetArray[j].mask
                        );
                    nioStaus = ConvertIpv4MaskToLength(
                        ulMask2,
                        &nMaskLength2
                        );
                    if (nMaskLength2 > nMaskLength1)
                    {
                        RtlCopyMemory(
                            &stAddrAndMask1,
                            &m_pPrivateSubnetArray[i],
                            sizeof(FWP_V4_ADDR_AND_MASK)
                            );
                        RtlCopyMemory(
                            &m_pPrivateSubnetArray[i],
                            &m_pPrivateSubnetArray[j],
                            sizeof(FWP_V4_ADDR_AND_MASK)
                            );
                        RtlCopyMemory(
                            &m_pPrivateSubnetArray[j],
                            &stAddrAndMask1,
                            sizeof(FWP_V4_ADDR_AND_MASK)
                            );
                        nMaskLength1 = nMaskLength2;
                    }
                }
            }
        }
    }
    m_nPrivateIPV6AddrCount = GetConfigInt(
        _T("service_settings"),
        _T("private_ip6_count"),
        0
        );
    if (m_nPrivateIPV6AddrCount > 0)
    {
        m_pPrivateIPV6AddrArray = new IPV6_ADDRESS_STRUCT[m_nPrivateIPV6AddrCount];
        for (i = 0; i<m_nPrivateIPV6AddrCount; i++)
        {
            sSectionName.Format(
                _T("private_ip6_prefix%d"),
                i
                );
            sIPAddress = GetConfigString(
                _T("service_settings"),
                sSectionName,
                _T("")
                );
            nRet = RtlIpv6StringToAddress(
                sIPAddress,
                &pTerminator,
                &stIP6Address
                );
            if (NO_ERROR != nRet)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"RtlIpv6StringToAddress\" failed with status: %!STATUS!",
                    nRet
                    );
                return FALSE;
            }
            RtlCopyMemory(
                m_pPrivateIPV6AddrArray[i]._IPv6Addr,
                &stIP6Address,
                sizeof(stIP6Address)
                );
            sSectionName.Format(
                _T("private_ip6_prefix_length%d"),
                i
                );
            nPrefixLength = GetConfigInt(
                _T("service_settings"),
                sSectionName,
                0
                );
            m_pPrivateIPV6AddrArray[i]._PrefixLength = nPrefixLength;
        }
        if (m_nPrivateIPV6AddrCount > 1)
        {
            IPV6_ADDRESS_STRUCT stIPv6Addres1;
            UINT32              nPrefixLength1, nPrefixLength2;
            for (i = 0; i < m_nPrivateIPV6AddrCount - 1; i++)
            {
                nPrefixLength1 = m_pPrivateIPV6AddrArray[i]._PrefixLength;
                for (j = i + 1; j < m_nPrivateIPV6AddrCount; j++)
                {
                    nPrefixLength2 = m_pPrivateIPV6AddrArray[j]._PrefixLength;
                    if (nPrefixLength2 > nPrefixLength1)
                    {
                        RtlCopyMemory(
                            &stIPv6Addres1,
                            &m_pPrivateIPV6AddrArray[i],
                            sizeof(IPV6_ADDRESS_STRUCT)
                            );
                        RtlCopyMemory(
                            &m_pPrivateIPV6AddrArray[i],
                            &m_pPrivateIPV6AddrArray[j],
                            sizeof(IPV6_ADDRESS_STRUCT)
                            );
                        RtlCopyMemory(
                            &m_pPrivateIPV6AddrArray[j],
                            &stIPv6Addres1,
                            sizeof(IPV6_ADDRESS_STRUCT)
                            );
                        nPrefixLength1 = nPrefixLength2;
                    }
                }
            }
        }
    }
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
    return TRUE;
}

int CWS2InterceptAPP::ExitInstance()
{
    NTSTATUS    nNtStatus = NOERROR;
    PBYTE       pOldEntryPoint;
    BOOL        bRet;
    DWORD       dwWaitCode;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    bRet = m_oMainThreadLock.Lock();
    if (NULL != m_pDnsStateThread)
    {
        bRet = m_pDnsStateThread->PostThreadMessage(
            WM_QUIT,
            0,
            0
            );
        dwWaitCode = WaitForSingleObject(
            *m_pDnsStateThread,
            INFINITE
            );
        delete m_pDnsStateThread;
        m_pDnsStateThread = NULL;
    }
    bRet = m_oMainThreadLock.Unlock();
    if (NULL != m_pPrivateSubnetArray)
    {
        delete [] m_pPrivateSubnetArray;
        m_pPrivateSubnetArray = NULL;
        m_nPrivateSubnetCount = 0;
    }
    if (NULL != m_pPublicIPV6AddrArray)
    {
        delete [] m_pPublicIPV6AddrArray;
        m_pPublicIPV6AddrArray = NULL;
        m_pPublicIPV6AddrArray = 0;
    }
    if (NULL != m_pPrivateIPV6AddrArray)
    {
        delete [] m_pPrivateIPV6AddrArray;
        m_pPrivateSubnetArray = NULL;
        m_pPrivateIPV6AddrArray = 0;
    }
    if (NULL != m_pPrivateRangeArray)
    {
        delete [] m_pPrivateRangeArray;
        m_pPrivateRangeArray = NULL;
        m_nPrivateRangeCount = 0;
    }
    if (NULL != m_pPrivateIpArray)
    {
        delete [] m_pPrivateIpArray;
        m_pPrivateIpArray = NULL;
        m_nPrivateIpCount = 0;
    }
    if (NULL != m_pPublicSubnetArray)
    {
        delete [] m_pPublicSubnetArray;
        m_pPublicSubnetArray = NULL;
        m_nPublicSubnetCount = 0;
    }
    if (NULL != m_pPublicRangeArray)
    {
        delete [] m_pPublicRangeArray;
        m_pPublicRangeArray = NULL;
        m_nPublicRangeCount = 0;
    }
    if (NULL != m_pPublicIpArray)
    {
        delete [] m_pPublicIpArray;
        m_pPublicIpArray = NULL;
        m_nPublicIpCount = 0;
    }
    if (NULL != m_pDnsZoneRecord)
    {
        BOOL            bRet;
        PDNS_SOA_DATA   pSoaDataDistanation = PDNS_SOA_DATA(m_pDnsZoneRecord->m_pDnsRecordData);
        bRet = WriteConfigString(
            _T("service_settings"),
            _T("pNamePrimaryServer"),
            pSoaDataDistanation->pNamePrimaryServer
            );
        bRet = WriteConfigString(
            _T("service_settings"),
            _T("pNameAdministrator"),
            pSoaDataDistanation->pNameAdministrator
            );
        bRet = WriteConfigInt(
            _T("service_settings"),
            _T("dwSerialNo"),
            pSoaDataDistanation->dwSerialNo
            );
        bRet = WriteConfigInt(
            _T("service_settings"),
            _T("dwRefresh"),
            pSoaDataDistanation->dwRefresh
            );
        bRet = WriteConfigInt(
            _T("service_settings"),
            _T("dwRetry"),
            pSoaDataDistanation->dwRetry
            );
        bRet = WriteConfigInt(
            _T("service_settings"),
            _T("dwExpire"),
            pSoaDataDistanation->dwExpire
            );
        bRet = WriteConfigInt(
            _T("service_settings"),
            _T("dwDefaultTtl"),
            pSoaDataDistanation->dwDefaultTtl
            );
        bRet = WriteConfigInt(
            _T("service_settings"),
            _T("dwFlags"),
            m_pDnsZoneRecord->m_unDnsRecordFlags.DW
            );
        bRet = WriteConfigInt(
            _T("service_settings"),
            _T("dwTtl"),
            m_pDnsZoneRecord->m_dwTtl
            );
        delete m_pDnsZoneRecord;
        m_pDnsZoneRecord = NULL;
    }
    m_oDnsSocketArray.clear();
    if (NULL != m_pfSavedRegisterServiceCtrlHandlerWFunction)
    {
        nNtStatus = UpdateImports(
            //"API-MS-WIN-SERVICE-WINSVC-L1-2-0.DLL",
            "RegisterServiceCtrlHandlerW",
            PBYTE(m_pfSavedRegisterServiceCtrlHandlerWFunction),
            pOldEntryPoint
            );
        m_pfSavedRegisterServiceCtrlHandlerWFunction = NULL;
    }
    if (NULL != m_pfSavedCreateIoCompletionPortFunction)
    {
        nNtStatus = UpdateImports(
            //"API-MS-WIN-CORE-IO-L1-1-1.DLL",
            "CreateIoCompletionPort",
            PBYTE(m_pfSavedCreateIoCompletionPortFunction),
            pOldEntryPoint
            );
        m_pfSavedCreateIoCompletionPortFunction = NULL;
    }
    if (NULL != m_pfSavedGetQueuedCompletionStatusFunction)
    {
        nNtStatus = UpdateImports(
            //"API-MS-WIN-CORE-IO-L1-1-1.DLL",
            "GetQueuedCompletionStatus",
            PBYTE(m_pfSavedGetQueuedCompletionStatusFunction),
            pOldEntryPoint
            );
        m_pfSavedGetQueuedCompletionStatusFunction = NULL;
    }
    if (NULL != m_pfSavedAcceptFunction)
    {
        nNtStatus = UpdateImports(
            PBYTE(sf_accept),
            PBYTE(m_pfSavedAcceptFunction)
            );
        m_pfSavedAcceptFunction = NULL;
    }
    if (NULL != m_pfSavedBindFunction)
    {
        nNtStatus = UpdateImports(
            PBYTE(sf_bind),
            PBYTE(m_pfSavedBindFunction)
            );
        m_pfSavedBindFunction = NULL;
    }
    if (NULL != m_pfSavedClosesocketFunction)
    {
        nNtStatus = UpdateImports(
            PBYTE(sf_closesocket),
            PBYTE(m_pfSavedClosesocketFunction)
            );
        m_pfSavedClosesocketFunction = NULL;
    }
    if (NULL != m_pfSavedRecvFunction)
    {
        nNtStatus = UpdateImports(
            PBYTE(sf_recv),
            PBYTE(m_pfSavedRecvFunction)
            );
        m_pfSavedRecvFunction = NULL;
    }
    if (NULL != m_pfSavedSendFunction)
    {
        nNtStatus = UpdateImports(
            PBYTE(sf_send),
            PBYTE(m_pfSavedSendFunction)
            );
        m_pfSavedSendFunction = NULL;
    }
    if (NULL != m_pfSavedSendtoFunction)
    {
        nNtStatus = UpdateImports(
            PBYTE(sf_sendto),
            PBYTE(m_pfSavedSendtoFunction)
            );
        m_pfSavedSendtoFunction = NULL;
    }
    if (NULL != m_pfSavedSocketFunction)
    {
        nNtStatus = UpdateImports(
            PBYTE(sf_socket),
            PBYTE(m_pfSavedSocketFunction)
            );
        m_pfSavedSocketFunction = NULL;
    }
    if (NULL != m_pfSavedWSACleanupFunction)
    {
        nNtStatus = UpdateImports(
            PBYTE(sf_WSACleanup),
            PBYTE(m_pfSavedWSACleanupFunction)
            );
        m_pfSavedWSACleanupFunction = NULL;
    }
    if (NULL != m_pfSavedWSARecvFromFunction)
    {
        nNtStatus = UpdateImports(
            PBYTE(sf_WSARecvFrom),
            PBYTE(m_pfSavedWSARecvFromFunction)
            );
        m_pfSavedWSARecvFromFunction = NULL;
    }
    if (NULL != m_pfSavedWSASocketWFunction)
    {
        nNtStatus = UpdateImports(
            PBYTE(sf_WSASocketW),
            PBYTE(m_pfSavedWSASocketWFunction)
            );
        m_pfSavedWSASocketWFunction = NULL;
    }
    if (NULL != m_pfSavedWSAStartupFunction)
    {
        nNtStatus = UpdateImports(
            PBYTE(sf_WSAStartup),
            PBYTE(m_pfSavedWSAStartupFunction)
            );
        m_pfSavedWSAStartupFunction = NULL;
    }
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
    return CWinApp::ExitInstance();
}

BEGIN_MESSAGE_MAP(CWS2InterceptAPP, CWinApp)
END_MESSAGE_MAP()


// CWS2InterceptAPP message handlers


SOCKET CWS2InterceptAPP::mf_accept(
    SOCKET s,
    struct sockaddr* addr,
    int* addrlen
    )
{
    SOCKET      hRet;
    int         nError;
    TDnsSocketArray::iterator socks_iterator;
    TDnsSocket  *pDnsSocketOld, stDnsSocketNew;
    BOOL        bRet;
    bRet = m_oDataLock.Lock();
    socks_iterator = m_oDnsSocketArray.find(s);
    if (socks_iterator == m_oDnsSocketArray.end())
    {
        hRet = m_pfSavedAcceptFunction(s, addr, addrlen);
        return hRet;
    }
    pDnsSocketOld = &socks_iterator->second;
    if (!pDnsSocketOld->m_bBindedToDnsPort)
    {
        hRet = m_pfSavedAcceptFunction(s, addr, addrlen);
        return hRet;
    }
    bRet = m_oDataLock.Unlock();
    hRet = m_pfSavedAcceptFunction(s, addr, addrlen);
    bRet = m_oDataLock.Lock();
    if (INVALID_SOCKET == hRet)
    {
        nError = WSAGetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSSOCKET,
            "%!FUNC! line:%!LINE! %!LEVEL! call \"accept\" failed with error %!WINERROR!",
            nError
            );
        pDnsSocketOld->m_enSocketState = enErrorOperation;
        pDnsSocketOld->m_nErrorCode = nError;
        bRet = m_oDataLock.Unlock();
        SleepEx(0, TRUE);
        return hRet;
    }
    if (AF_INET == pDnsSocketOld->m_stSocketBindAddress.ss_family)
    {
    }
    else if (AF_INET6 == pDnsSocketOld->m_stSocketBindAddress.ss_family)
    {
    }
    else
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSSOCKET,
            "%!FUNC! line:%!LINE! %!LEVEL! Unexpected binding address family = %d",
            pDnsSocketOld->m_stSocketBindAddress.ss_family
            );
        bRet = m_oDataLock.Unlock();
        SleepEx(0, TRUE);
        return hRet;
    }
    socks_iterator = m_oDnsSocketArray.find(hRet);
    if (socks_iterator != m_oDnsSocketArray.end())
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSSOCKET,
            "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p already exist in array",
            (void*) hRet
            );
        bRet = m_oDataLock.Unlock();
        SleepEx(0, TRUE);
        return hRet;
    }
    stDnsSocketNew.m_hSocket = hRet;
    stDnsSocketNew.m_nAddressFamily = pDnsSocketOld->m_nAddressFamily;
    stDnsSocketNew.m_nSocketType = pDnsSocketOld->m_nSocketType;
    stDnsSocketNew.m_nSocketProtocol = pDnsSocketOld->m_nSocketProtocol;
    stDnsSocketNew.m_bAcceptedSocket = true;
    stDnsSocketNew.m_hListenSocket = s;
    if (AF_INET == addr->sa_family)
    {
        SOCKADDR_IN*    pAddr = (SOCKADDR_IN*) addr;
        stDnsSocketNew.m_bAcceptLocal = IsRemoteIPv4Local(pAddr->sin_addr.S_un.S_addr);
        if (!stDnsSocketNew.m_bAcceptLocal)
        {
        }
        stDnsSocketNew.m_enSocketState = enDnsIp4Accepted;
        stDnsSocketNew.m_stSocketBindAddress = pDnsSocketOld->m_stSocketBindAddress;
        RtlCopyMemory(
            &stDnsSocketNew.m_stSocketClientAddress,
            addr,
            *addrlen
            );
        m_oDnsSocketArray.insert(TDnsSocketArray::value_type(hRet, stDnsSocketNew));
        bRet = m_oDataLock.Unlock();
        return hRet;
    }
    if (AF_INET6 == addr->sa_family)
    {
        SOCKADDR_IN6*   pAddr = (SOCKADDR_IN6*) addr;
        stDnsSocketNew.m_bAcceptLocal = IsRemoteIPv6Local(&pAddr->sin6_addr);
        if (!stDnsSocketNew.m_bAcceptLocal)
        {
        }
        stDnsSocketNew.m_enSocketState = enDnsIp6Accepted;
        stDnsSocketNew.m_stSocketBindAddress = pDnsSocketOld->m_stSocketBindAddress;
        RtlCopyMemory(
            &stDnsSocketNew.m_stSocketClientAddress,
            addr,
            *addrlen
            );
        m_oDnsSocketArray.insert(TDnsSocketArray::value_type(hRet, stDnsSocketNew));
        SleepEx(0, TRUE);
        bRet = m_oDataLock.Unlock();
        return hRet;
    }
    stDnsSocketNew.m_enSocketState = enUnknownState;
    TraceEvents(
        TRACE_LEVEL_ERROR,
        TRACE_DNSSOCKET,
        "%!FUNC! line:%!LINE! %!LEVEL! Unexpected address family = %d",
        addr->sa_family
        );
    m_oDnsSocketArray.insert(TDnsSocketArray::value_type(hRet, stDnsSocketNew));
    bRet = m_oDataLock.Unlock();
    SleepEx(0, TRUE);
    return hRet;
}


int CWS2InterceptAPP::mf_bind(
    SOCKET s,
    const struct sockaddr* name,
    int namelen
    )
{
    int         nRet;
    int         nError;
    TDnsSocketArray::iterator socks_iterator;
    TDnsSocket* pDnsSocket;
    BOOL        bRet;
    nRet = m_pfSavedBindFunction(s, name, namelen);
    bRet = m_oDataLock.Lock();
    if (SOCKET_ERROR == nRet)
    {
        nError = WSAGetLastError();
        socks_iterator = m_oDnsSocketArray.find(s);
        if (socks_iterator != m_oDnsSocketArray.end())
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSSOCKET,
                "%!FUNC! line:%!LINE! %!LEVEL! call \"bind\" failed with error %!WINERROR!",
                nError
                );
            pDnsSocket = &socks_iterator->second;
            pDnsSocket->m_enSocketState = enErrorOperation;
            pDnsSocket->m_nErrorCode = nError;
        }
    }
    else
    {
        socks_iterator = m_oDnsSocketArray.find(s);
        if (socks_iterator == m_oDnsSocketArray.end())
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSSOCKET,
                "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p not created!",
                (void*) s
                );
        }
        else
        {
            pDnsSocket = &socks_iterator->second;
            pDnsSocket->m_bSocketBind = true;
            RtlCopyMemory(
                &pDnsSocket->m_stSocketBindAddress,
                name,
                namelen
                );
            if (AF_INET == name->sa_family)
            {
                SOCKADDR_IN*    pAddr = (SOCKADDR_IN*) name;
                if (53 == ntohs(pAddr->sin_port))
                {
                    pDnsSocket->m_bBindedToDnsPort = true;
                    pDnsSocket->m_enSocketState = enDnsIp4Binded;
                    //TraceEvents(
                    //    TRACE_LEVEL_INFORMATION,
                    //    TRACE_DNSSOCKET,
                    //    "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p was binded to IPv4 Dns port sin_addr = %!IPADDR! & socket type = %d",
                    //    (void*) s,
                    //    pAddr->sin_addr.S_un.S_addr,
                    //    pDnsSocket->m_nSocketType
                    //    );
                }
                else
                {
                    pDnsSocket->m_enSocketState = enIp4Binded;
                }
            }
            else if (AF_INET6 == name->sa_family)
            {
                SOCKADDR_IN6*   pAddr = (SOCKADDR_IN6*) name;
                if (53 == ntohs(pAddr->sin6_port))
                {
                    //WCHAR   szIn6Address[128], *pAddressEnd;
                    pDnsSocket->m_bBindedToDnsPort = true;
                    pDnsSocket->m_enSocketState = enDnsIp6Binded;
                    //pAddressEnd = RtlIpv6AddressToStringW(
                    //    &pAddr->sin6_addr,
                    //    szIn6Address
                    //    );
                    //TraceEvents(
                    //    TRACE_LEVEL_INFORMATION,
                    //    TRACE_DNSSOCKET,
                    //    "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p was binded to IPv6 Dns port sin6_addr = %ws & socket type = %d",
                    //    (void*) s,
                    //    szIn6Address,
                    //    pDnsSocket->m_nSocketType
                    //    );
                }
                else
                {
                    pDnsSocket->m_enSocketState = enIp6Binded;
                }
            }
            else
            {
                pDnsSocket->m_enSocketState = enUnknownState;
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNSSOCKET,
                    "%!FUNC! line:%!LINE! %!LEVEL! Unexpected binding address family = %d",
                    name->sa_family
                    );
            }
        }
    }
    bRet = m_oDataLock.Unlock();
    SleepEx(0, TRUE);
    return nRet;
}


int CWS2InterceptAPP::mf_closesocket(SOCKET s)
{
    int         nRet;
    int         nError;
    TDnsSocketArray::iterator socks_iterator;
    TDnsSocket* pDnsSocket;
    BOOL        bRet;
    nRet = m_pfSavedClosesocketFunction(s);
    bRet = m_oDataLock.Lock();
    if (SOCKET_ERROR == nRet)
    {
        nError = WSAGetLastError();
        socks_iterator = m_oDnsSocketArray.find(s);
        if (socks_iterator != m_oDnsSocketArray.end())
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSSOCKET,
                "%!FUNC! line:%!LINE! %!LEVEL! call \"closesocket\" failed with error %!WINERROR!",
                nError
                );
            pDnsSocket = &socks_iterator->second;
            pDnsSocket->m_enSocketState = enErrorOperation;
            pDnsSocket->m_nErrorCode = nError;
        }
    }
    else
    {
        try
        {
            socks_iterator = m_oDnsSocketArray.find(s);
        }
        catch (...)
        {
            socks_iterator = m_oDnsSocketArray.end();
        }
        if (socks_iterator == m_oDnsSocketArray.end())
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSSOCKET,
                "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p not created!",
                (void*) s
                );
        }
        else
        {
            pDnsSocket = &socks_iterator->second;
            pDnsSocket->m_enSocketState = enClosed;
            m_oDnsSocketArray.erase(socks_iterator);
        }
    }
    bRet = m_oDataLock.Unlock();
    SleepEx(0, TRUE);
    return nRet;
}


int CWS2InterceptAPP::mf_recv(
    SOCKET s,
    char* buf,
    int len,
    int flags
    )
{
    int                         nRet;
    TDnsSocketArray::iterator   socks_iterator;
    TDnsSocket*                 pDnsSocket;
    BOOL                        bLockReturn;
    bLockReturn = m_oDataLock.Lock();
    socks_iterator = m_oDnsSocketArray.find(s);
    if (socks_iterator == m_oDnsSocketArray.end())
    {
        bLockReturn = m_oDataLock.Unlock();
        nRet = m_pfSavedRecvFunction(s, buf, len, flags);
        return nRet;
    }
    pDnsSocket = &socks_iterator->second;
    if (!pDnsSocket->m_bAcceptedSocket)
    {
        bLockReturn = m_oDataLock.Unlock();
        nRet = m_pfSavedRecvFunction(s, buf, len, flags);
        return nRet;
    }
    if (pDnsSocket->m_bAcceptLocal)
    {
        bLockReturn = m_oDataLock.Unlock();
        nRet = m_pfSavedRecvFunction(s, buf, len, flags);
        return nRet;
    }
    bLockReturn = m_oDataLock.Unlock();
    nRet = m_pfSavedRecvFunction(s, buf, len, flags);
    return nRet;
}


int CWS2InterceptAPP::mf_send(
    SOCKET s,
    const char* buf,
    int len,
    int flags
    )
{
    int         nRet;
    TDnsSocketArray::iterator   socks_iterator;
    TDnsSocket* pDnsSocket;
    BOOL        bLockReturn;
    bLockReturn = m_oDataLock.Lock();
    socks_iterator = m_oDnsSocketArray.find(s);
    if (socks_iterator == m_oDnsSocketArray.end())
    {
        bLockReturn = m_oDataLock.Unlock();
        nRet = m_pfSavedSendFunction(s, buf, len, flags);
        return nRet;
    }
    pDnsSocket = &socks_iterator->second;
    if (!pDnsSocket->m_bAcceptedSocket)
    {
        bLockReturn = m_oDataLock.Unlock();
        nRet = m_pfSavedSendFunction(s, buf, len, flags);
        return nRet;
    }
    if (pDnsSocket->m_bAcceptLocal)
    {
        bLockReturn = m_oDataLock.Unlock();
        nRet = m_pfSavedSendFunction(s, buf, len, flags);
        return nRet;
    }
    bLockReturn = m_oDataLock.Unlock();
    nRet = m_pfSavedSendFunction(s, buf, len, flags);
    return nRet;
}

#pragma warning(push)
#pragma warning(disable: 4701 4703)
int CWS2InterceptAPP::mf_sendto(
    SOCKET s,
    const char* buf,
    int len,
    int flags,
    const struct sockaddr* to,
    int tolen
    )
{
    int                                 nRet;
    int                                 nError;
    int                                 nOriginalLength = len;
    TDnsSocketArray::iterator           socks_iterator;
    TDnsSocket*                         pDnsSocket;
    TRecvQueuedEntry*                   pRecvQueuedEntry;
    PDNS_HEADER                         pDnsHeader;
    bool                                bMessageParsed;
    CDnsMessageWB*                      pDnsMessage;
    WORD                                wDnsLength = (WORD) len;
    BYTE                                baResponceData[8192];
    DNS_APPROVAL_CONFIG                 stApprovalConfig;
    bool                                bException = false;
    CTUdpRemoteClientArray::iterator    clients_iterator;
    TUdpRemoteClient*                   pUdpRemoteClient;
    BOOL                                bLockReturn;
    UINT32                              ulIp4NAddr;
    //TCHAR                               szIpAddress[128];
    //LPTSTR                              pAddressEnd;
    bLockReturn = m_oDataLock.Lock();
    socks_iterator = m_oDnsSocketArray.find(s);
    if (socks_iterator == m_oDnsSocketArray.end())
    {
        bLockReturn = m_oDataLock.Unlock();
        nRet = m_pfSavedSendtoFunction(s, buf, len, flags, to, tolen);
        return nRet;
    }
    pDnsSocket = &socks_iterator->second;
    if (!pDnsSocket->m_bBindedToDnsPort)
    {
        bLockReturn = m_oDataLock.Unlock();
        nRet = m_pfSavedSendtoFunction(s, buf, len, flags, to, tolen);
        return nRet;
    }
    pRecvQueuedEntry = pDnsSocket->m_pCurrRecvQueuedEntry;
    if (pRecvQueuedEntry->m_bRemoteIsLocal)
    {
        bLockReturn = m_oDataLock.Unlock();
        nRet = m_pfSavedSendtoFunction(s, buf, len, flags, to, tolen);
        return nRet;
    }
    pDnsMessage = new CDnsMessageWB;
    pDnsHeader = (PDNS_HEADER) buf;
    if ((!pDnsHeader->Truncation) && (pDnsHeader->ResponseCode == 0))
    {
        bMessageParsed = pDnsMessage->ParseDnsMessageBuffer(
            (PDNS_MESSAGE_BUFFER) buf,
            wDnsLength
            );
    }
    else if (pDnsHeader->Truncation)
    {
        bMessageParsed = false;
    }
    else
    {
        ULONG       nResponceCode;
        nResponceCode = (ULONG) pDnsHeader->ResponseCode;
        bMessageParsed = false;
    }
    if (!bMessageParsed)
    {
        // подтверждение отправки клиенту ответного сообщения без RR, или с кодом ответа отличном от нуля,
        // или обрезанного DNS-ответа
        delete pDnsMessage;
    }
    else
    {
        if (pDnsSocket->m_bAbortedQuery)
        {
            // отвержение необрабатываемого запроса
            UINT32              nMessageLength;
            CDnsResorceRecord*  pSoaRRecord = NULL;
            CDnsResorceRecord*  pCurrentRR;
            CDnsResorceRecord*  pNextRR;
            CDnsMessageWB*      pDnsQueryMessage = pDnsSocket->m_pDnsQueryMessage;
            if (DNS_TYPE_SOA == pDnsMessage->m_stDnsQuestionRecord.stDnsWireQuestion.QuestionType)
            {
                pCurrentRR = pDnsMessage->m_pFirstDnsRRecord;
                while (NULL != pCurrentRR)
                {
                    pNextRR = pCurrentRR->m_pNext;
                    if (DnsSectionQuestion != pDnsMessage->m_pLastDnsRRecord->m_unDnsRecordFlags.S.Section)
                    {
                        if (DNS_TYPE_SOA == pCurrentRR->m_wType)
                        {
                            pSoaRRecord = pCurrentRR;
                            pSoaRRecord->m_unDnsRecordFlags.S.Section = DnsSectionAuthority;
                            pDnsMessage->RemoveRRAt(pCurrentRR);
                        }
                        else
                        {
                            pDnsMessage->RemoveRRAt(pCurrentRR);
                            delete pCurrentRR;
                        }
                    }
                    pCurrentRR = pNextRR;
                }
            }
            else
            {
                pCurrentRR = pDnsMessage->m_pFirstDnsRRecord;
                while (NULL != pCurrentRR)
                {
                    pNextRR = pCurrentRR->m_pNext;
                    if (DnsSectionQuestion != pDnsMessage->m_pLastDnsRRecord->m_unDnsRecordFlags.S.Section)
                    {
                        pDnsMessage->RemoveRRAt(pCurrentRR);
                        delete pCurrentRR;
                    }
                    pCurrentRR = pNextRR;
                }
                pSoaRRecord = new CDnsResorceRecord(*m_pDnsZoneRecord);
                pSoaRRecord->m_unDnsRecordFlags.S.Section = DnsSectionAuthority;
            }
            if (DNS_OPCODE_QUERY == pDnsQueryMessage->m_stDnsHeader.Opcode)
            {
// (DNS_OPCODE_QUERY == pDnsQueryMessage->m_stDnsHeader.Opcode) && m_bAbortedQuery && bMessageParsed && UDP
                pDnsMessage->m_stDnsHeader.ResponseCode = DNS_RCODE_NOTIMPL;
                pDnsMessage->AddRRToTail(pSoaRRecord);
                nMessageLength = pDnsMessage->PerformDnsMessageBuffer(
                    baResponceData,
                    sizeof(baResponceData)
                    );
            }
            else
            {
// (DNS_OPCODE_QUERY != pDnsQueryMessage->m_stDnsHeader.Opcode) && m_bAbortedQuery && bMessageParsed && UDP
                pDnsMessage->m_stDnsHeader.ResponseCode = DNS_RCODE_NOTIMPL;
                pDnsMessage->m_stDnsHeader.Opcode = pDnsQueryMessage->m_stDnsHeader.Opcode;
                pDnsMessage->AddRRToTail(pSoaRRecord);
                nMessageLength = pDnsMessage->PerformDnsMessageBuffer(
                    baResponceData,
                    sizeof(baResponceData)
                    );
            }
            len = nMessageLength;
            buf = (const char*) baResponceData;
            delete pDnsQueryMessage;
            pDnsSocket->m_pDnsQueryMessage = NULL;
            pDnsSocket->m_bAbortedQuery = false;
        }
        else // !m_bAbortedQuery
        {
            // обработка обычного UDP-ответа
            // (!m_bAbortedQuery) && bMessageParsed && UDP
            DWORD   dwResult;
            if (nOriginalLength < 512)
            {
                dwResult = 512;
            }
            else
            {
                dwResult = nOriginalLength;
            }
            RtlZeroMemory(
                &stApprovalConfig,
                sizeof(stApprovalConfig)
                );
            pDnsMessage->DnsReParseResponceMessage(
                stApprovalConfig,
                baResponceData,
                (size_t) dwResult
                );
            if (bException)
            {
                stApprovalConfig.m_bDataModify = false;
            }
            if (stApprovalConfig.m_bDataModify)
            {
// m_stDnsApprovalOutput.m_stApprovalConfig.m_bDataModify && (!m_bAbortedQuery) && bMessageParsed && UDP
                len = stApprovalConfig.m_nDataSize;
                buf = (const char*) baResponceData;
            }
            else
            {
// (!m_stDnsApprovalOutput.m_stApprovalConfig.m_bDataModify} && (!m_bAbortedQuery) && bMessageParsed && UDP
            }
        }
        delete pDnsMessage;
    }
    bLockReturn = m_oDataLock.Unlock();
    nRet = m_pfSavedSendtoFunction(s, buf, len, flags, to, tolen);
    bLockReturn = m_oDataLock.Lock();
    if (SOCKET_ERROR == nRet)
    {
        nError = WSAGetLastError();
        if (WSAEWOULDBLOCK != nError)
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSSOCKET,
                "%!FUNC! line:%!LINE! %!LEVEL! call \"sendto\" failed with error %!WINERROR!",
                nError
                );
            pDnsSocket->m_enSocketState = enErrorOperation;
            pDnsSocket->m_nErrorCode = nError;
        }
    }
    else
    {
        if (AF_INET == pDnsSocket->m_stSocketBindAddress.ss_family)
        {
            //SOCKADDR_IN*    pAddr = (SOCKADDR_IN*) &pDnsSocket->m_stSocketBindAddress;
            //TraceEvents(
            //    TRACE_LEVEL_INFORMATION,
            //    TRACE_DNSSOCKET,
            //    "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p sended %d bytes on IPv4 Dns binded port; sin_addr = %!IPADDR!;",
            //    (void*) s,
            //    nRet,
            //    pAddr->sin_addr.S_un.S_addr
            //    );
        }
        else if (AF_INET6 == pDnsSocket->m_stSocketBindAddress.ss_family)
        {
            //SOCKADDR_IN6*   pAddr = (SOCKADDR_IN6*) &pDnsSocket->m_stSocketBindAddress;
            //WCHAR           szIn6Address[128], *pAddressEnd;
            //pAddressEnd = RtlIpv6AddressToStringW(
            //    &pAddr->sin6_addr,
            //    szIn6Address
            //    );
            //TraceEvents(
            //    TRACE_LEVEL_INFORMATION,
            //    TRACE_DNSSOCKET,
            //    "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p sended %d bytes on IPv6 Dns binded port; sin6_addr = %ws;",
            //    (void*) s,
            //    nRet,
            //    szIn6Address
            //    );
        }
        else
        {
            pDnsSocket->m_enSocketState = enUnknownState;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSSOCKET,
                "%!FUNC! line:%!LINE! %!LEVEL! Unexpected binding address family = %d",
                pDnsSocket->m_stSocketBindAddress.ss_family
                );
        }
        RtlCopyMemory(
            &pDnsSocket->m_stSocketClientAddress,
            to,
            tolen
            );
        if (AF_INET == pDnsSocket->m_stSocketClientAddress.ss_family)
        {
            SOCKADDR_IN*    pAddr = (SOCKADDR_IN*) &pDnsSocket->m_stSocketClientAddress;
            ulIp4NAddr = pAddr->sin_addr.S_un.S_addr;
            pDnsSocket->m_enSocketState = enDnsUdp4SendResponce;
            clients_iterator = m_oUdpRemoteClientArray.find(ulIp4NAddr);
            if (clients_iterator != m_oUdpRemoteClientArray.end())
            {
                pUdpRemoteClient = &clients_iterator->second;
                if (pUdpRemoteClient->m_nQueryCount > 0)
                {
                    pUdpRemoteClient->m_nQueryCount--;
                }
                if (0 == pUdpRemoteClient->m_nQueryCount)
                {
                    m_oUdpRemoteClientArray.erase(clients_iterator);
                }
            }
        }
        else if (AF_INET6 == pDnsSocket->m_stSocketClientAddress.ss_family)
        {
            //SOCKADDR_IN6*   pAddr = (SOCKADDR_IN6*) &pDnsSocket->m_stSocketClientAddress;
            //WCHAR           szIn6Address[128], *pAddressEnd;
            pDnsSocket->m_enSocketState = enDnsUdp6SendResponce;
            //pAddressEnd = RtlIpv6AddressToStringW(
            //    &pAddr->sin6_addr,
            //    szIn6Address
            //    );
            //TraceEvents(
            //    TRACE_LEVEL_INFORMATION,
            //    TRACE_DNSSOCKET,
            //    "%!FUNC! line:%!LINE! %!LEVEL! to IPv6 client.port = %!PORT!; %ws;",
            //    pAddr->sin6_port,
            //    szIn6Address
            //    );
        }
        else
        {
            pDnsSocket->m_enSocketState = enUnknownState;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSSOCKET,
                "%!FUNC! line:%!LINE! %!LEVEL! Unexpected send to address family = %d",
                pDnsSocket->m_stSocketClientAddress.ss_family
                );
        }
        nRet = nOriginalLength;
    }
    bLockReturn = m_oDataLock.Unlock();
    return nRet;
}
#pragma warning(pop)


SOCKET CWS2InterceptAPP::mf_socket(
    int af,
    int type,
    int protocol
    )
{
    SOCKET      hRet;
    int         nError;
    TDnsSocketArray::iterator socks_iterator;
    TDnsSocket  stDnsSocket;
    BOOL        bLockReturn;
    hRet = m_pfSavedSocketFunction(af, type, protocol);
    bLockReturn = m_oDataLock.Lock();
    if (INVALID_SOCKET == hRet)
    {
        nError = WSAGetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSSOCKET,
            "%!FUNC! line:%!LINE! %!LEVEL! call \"socket\" failed with error %!WINERROR!",
            nError
            );
    }
    else
    {
        socks_iterator = m_oDnsSocketArray.find(hRet);
        if (socks_iterator != m_oDnsSocketArray.end())
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSSOCKET,
                "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p already exist in array",
                (void*) hRet
                );
        }
        else
        {
            stDnsSocket.m_hSocket = hRet;
            stDnsSocket.m_nAddressFamily = af;
            stDnsSocket.m_nSocketType = type;
            stDnsSocket.m_nSocketProtocol = protocol;
            if (AF_INET == af)
            {
                stDnsSocket.m_enSocketState = enIp4Created;
            }
            else if (AF_INET6 == af)
            {
                stDnsSocket.m_enSocketState = enIp6Created;
            }
            else
            {
                stDnsSocket.m_enSocketState = enUnknownState;
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNSSOCKET,
                    "%!FUNC! line:%!LINE! %!LEVEL! Unexpected address family = %d",
                    af
                    );
            }
            m_oDnsSocketArray.insert(TDnsSocketArray::value_type(hRet, stDnsSocket));
        }
    }
    bLockReturn = m_oDataLock.Unlock();
    return hRet;
}


int CWS2InterceptAPP::mf_WSACleanup()
{
    int     nRet;
    int     nError;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    SleepEx(0, TRUE);
    nRet = m_pfSavedWSACleanupFunction();
    SleepEx(0, TRUE);
    if (SOCKET_ERROR == nRet)
    {
        nError = WSAGetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! call \"WSACleanup\" failed with error %!WINERROR!",
            nError
            );
    }
    else
    {
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! call \"WSACleanup\" successfully"
            );
    }
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
    SleepEx(0, TRUE);
    return nRet;
}


#pragma warning(push)
#pragma warning(disable: 4701 4703)
int CWS2InterceptAPP::mf_WSARecvFrom(
    SOCKET s,
    LPWSABUF lpBuffers,
    DWORD dwBufferCount,
    LPDWORD lpNumberOfBytesRecvd,
    LPDWORD lpFlags,
    struct sockaddr* lpFrom,
    LPINT lpFromlen,
    LPWSAOVERLAPPED lpOverlapped,
    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
    )
{
    int                                 nRet;
    int                                 nError;
    TDnsSocketArray::iterator           socks_iterator;
    TDnsSocket*                         pDnsSocket;
    CTRecvQueuedArray::iterator         entry_iterator;
    TRecvQueuedEntry                    stRecvQueuedEntry;
    TRecvQueuedEntry*                   pRecvQueuedEntry;
    TRecvQueuedInsertPair               oRecvQueuedInsertPair;
    DNS_APPROVAL_OUTPUT                 stDnsApprovalOutput;
    bool                                bQueryParsed;
    DWORD                               dwUdpDataSize;
    WORD                                wDnsLength;
    CTUdpRemoteClientArray::iterator    clients_iterator;
    TUdpRemoteClient*                   pUdpRemoteClient;
    TUdpRemoteClient                    stNewClientEntry;
    TUdpRemoteClientInsertPair          oRemoteClientInsertPair;
    BOOL                                bLockReturn;
    UINT32                              ulIp4NAddr, ulIp4HAddr;
    TCHAR                               szIpAddress[128];
    LPTSTR                              pAddressEnd;
    bLockReturn = m_oDataLock.Lock();
    socks_iterator = m_oDnsSocketArray.find(s);
    if (socks_iterator == m_oDnsSocketArray.end())
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSSOCKET,
            "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p not created!",
            (void*) s
            );
    }
    else
    {
        pDnsSocket = &socks_iterator->second;
        if (pDnsSocket->m_bBindedToDnsPort)
        {
            u_long  ulBuffersLen = 0;
            DWORD   dwI;
            for (dwI = 0; dwI < dwBufferCount; dwI++)
            {
                ulBuffersLen += lpBuffers[dwI].len;
            }
            entry_iterator = m_oRecvQueuedArray.find(lpOverlapped);
            if (entry_iterator == m_oRecvQueuedArray.end())
            {
                stRecvQueuedEntry.m_pOverlapped = lpOverlapped;
                stRecvQueuedEntry.m_hSocket = s;
                stRecvQueuedEntry.m_pRecvFromAddress = lpFrom;
                stRecvQueuedEntry.m_pRecvFromAddressLen = lpFromlen;
                stRecvQueuedEntry.m_pBuffers = lpBuffers;
                stRecvQueuedEntry.m_dwBufferCount = dwBufferCount;
                stRecvQueuedEntry.m_pNumberOfBytesRecvd = lpNumberOfBytesRecvd;
                oRecvQueuedInsertPair = m_oRecvQueuedArray.insert(
                    CTRecvQueuedArray::value_type(lpOverlapped,stRecvQueuedEntry)
                    );
                entry_iterator = oRecvQueuedInsertPair.first;
                pRecvQueuedEntry = &entry_iterator->second;
            }
            else
            {
                pRecvQueuedEntry = &entry_iterator->second;
                pRecvQueuedEntry->m_pRecvFromAddress = lpFrom;
                pRecvQueuedEntry->m_pRecvFromAddressLen = lpFromlen;
                pRecvQueuedEntry->m_pBuffers = lpBuffers;
                pRecvQueuedEntry->m_dwBufferCount = dwBufferCount;
                pRecvQueuedEntry->m_pNumberOfBytesRecvd = lpNumberOfBytesRecvd;
            }
            if (AF_INET == pDnsSocket->m_stSocketBindAddress.ss_family)
            {
                //SOCKADDR_IN*    pAddr = (SOCKADDR_IN*) &pDnsSocket->m_stSocketBindAddress;
                pDnsSocket->m_enSocketState = enDnsUdp4RecvQueryPended;
                //TraceEvents(
                //    TRACE_LEVEL_INFORMATION,
                //    TRACE_DNSSOCKET,
                //    "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p receive pended on IPv4 Dns port; %!IPADDR!;",
                //    (void*) s,
                //    pAddr->sin_addr.S_un.S_addr
                //    );
            }
            else if (AF_INET6 == pDnsSocket->m_stSocketBindAddress.ss_family)
            {
                //SOCKADDR_IN6*   pAddr = (SOCKADDR_IN6*) &pDnsSocket->m_stSocketBindAddress;
                //WCHAR           szIn6Address[128], *pAddressEnd;
                //pAddressEnd = RtlIpv6AddressToStringW(
                //    &pAddr->sin6_addr,
                //    szIn6Address
                //    );
                pDnsSocket->m_enSocketState = enDnsUdp6RecvQueryPended;
                //TraceEvents(
                //    TRACE_LEVEL_INFORMATION,
                //    TRACE_DNSSOCKET,
                //    "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p receive pended on IPv6 Dns port; %ws;",
                //    (void*) s,
                //    szIn6Address
                //    );
            }
            else
            {
                pDnsSocket->m_enSocketState = enUnknownState;
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNSSOCKET,
                    "%!FUNC! line:%!LINE! %!LEVEL! Unexpected binded address family = %d",
                    pDnsSocket->m_stSocketBindAddress.ss_family
                    );
                SleepEx(0, TRUE);
            }
            //TraceEvents(
            //    TRACE_LEVEL_INFORMATION,
            //    TRACE_DNSSOCKET,
            //    "%!FUNC! line:%!LINE! %!LEVEL! ulBuffersLen = %d; lpOverlapped = %p;",
            //    ulBuffersLen,
            //    lpOverlapped
            //    );
        }
        else
        {
            if (AF_INET == pDnsSocket->m_stSocketBindAddress.ss_family)
            {
                pDnsSocket->m_enSocketState = enDnsUdp4RecvDataPended;
            }
            else if (AF_INET6 == pDnsSocket->m_stSocketBindAddress.ss_family)
            {
                pDnsSocket->m_enSocketState = enDnsUdp6RecvDataPended;
            }
            else
            {
                pDnsSocket->m_enSocketState = enUnknownState;
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNSSOCKET,
                    "%!FUNC! line:%!LINE! %!LEVEL! Unexpected binded address family = %d",
                    pDnsSocket->m_stSocketBindAddress.ss_family
                    );
                SleepEx(0, TRUE);
            }
        }
    }
    bLockReturn = m_oDataLock.Unlock();
    nRet = m_pfSavedWSARecvFromFunction(
        s,
        lpBuffers,
        dwBufferCount,
        lpNumberOfBytesRecvd,
        lpFlags,
        lpFrom,
        lpFromlen,
        lpOverlapped,
        lpCompletionRoutine
        );
    bLockReturn = m_oDataLock.Lock();
    if ((socks_iterator != m_oDnsSocketArray.end()) && pDnsSocket->m_bBindedToDnsPort)
    {
        if (SOCKET_ERROR == nRet)
        {
            nError = WSAGetLastError();
            if (WSA_IO_PENDING == nError)
            {
                //TraceEvents(
                //    TRACE_LEVEL_INFORMATION,
                //    TRACE_DNSSOCKET,
                //    "%!FUNC! line:%!LINE! %!LEVEL! overlapped operation has been successfully initiated"
                //    );
                pDnsSocket->m_bOverlappedRecvStarted = true;
                pDnsSocket->m_enSocketState = enDnsOverlappedRecvStarted;
            }
            else
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNSSOCKET,
                    "%!FUNC! line:%!LINE! %!LEVEL! call \"WSARecvFrom\" failed with error %!WINERROR!",
                    nError
                    );
                pDnsSocket->m_enSocketState = enErrorOperation;
                pDnsSocket->m_nErrorCode = nError;
                SleepEx(0, TRUE);
            }
            bLockReturn = m_oDataLock.Unlock();
            return nRet;
        }
        pDnsSocket->m_pCurrRecvQueuedEntry = pRecvQueuedEntry;
        dwUdpDataSize = *lpNumberOfBytesRecvd;
        wDnsLength = (WORD) dwUdpDataSize;
        RtlCopyMemory(
            &pRecvQueuedEntry->m_stSocketClientAddress,
            pRecvQueuedEntry->m_pRecvFromAddress,
            *pRecvQueuedEntry->m_pRecvFromAddressLen
            );
        RtlCopyMemory(
            &pDnsSocket->m_stSocketClientAddress,
            lpFrom,
            *lpFromlen
            );
        if (AF_INET == lpFrom->sa_family)
        {
            SOCKADDR_IN*    pAddr = (SOCKADDR_IN*) lpFrom;
            ulIp4NAddr = pAddr->sin_addr.S_un.S_addr;
            INLINE_NTOHL(ulIp4HAddr, ulIp4NAddr);
            pRecvQueuedEntry->m_bRemoteIsLocal = IsRemoteIPv4Local(ulIp4NAddr);
            if (pRecvQueuedEntry->m_bRemoteIsLocal)
            {
                bLockReturn = m_oDataLock.Unlock();
                return nRet;
            }
            clients_iterator = m_oUdpRemoteClientArray.find(ulIp4NAddr);
            if (clients_iterator == m_oUdpRemoteClientArray.end())
            {
                stNewClientEntry.m_pDnsSocket = pDnsSocket;
                stNewClientEntry.m_nQueryCount = 1;
                oRemoteClientInsertPair = m_oUdpRemoteClientArray.insert(
                    CTUdpRemoteClientArray::value_type(ulIp4NAddr, stNewClientEntry)
                    );
                clients_iterator = oRemoteClientInsertPair.first;
                pUdpRemoteClient = &clients_iterator->second;
            }
            else
            {   // clients_iterator != m_oUdpRemoteClientArray.end()
                pUdpRemoteClient = &clients_iterator->second;
                pUdpRemoteClient->m_nQueryCount++;
                if (pUdpRemoteClient->m_nQueryCount > 3)
                {
                    pAddressEnd = RtlIpv4AddressToString(
                        &pAddr->sin_addr,
                        szIpAddress
                        );
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_DNSSOCKET,
                        "%!FUNC! line:%!LINE! %!LEVEL! DNS Client count > 3; IP-address = %ws;",
                        szIpAddress
                        );
                    //bLockReturn = m_pDnsStateThread->m_oTimersLock.Lock();
                    m_pDnsStateThread->ResetFirewallIpV4Config(ulIp4HAddr);
                    //bLockReturn = m_pDnsStateThread->m_oTimersLock.Unlock();
                    pUdpRemoteClient->m_nQueryCount--;
                    pDnsSocket->m_pDnsQueryMessage = NULL;
                    *lpNumberOfBytesRecvd = 0;
                    *pRecvQueuedEntry->m_pNumberOfBytesRecvd = 0;
                    bLockReturn = m_oDataLock.Unlock();
                    return nRet;
                }
            }
            pDnsSocket->m_enSocketState = enDnsUdp4Received;
            pDnsSocket->m_pDnsQueryMessage = new CDnsMessageWB();
            RtlZeroMemory(
                &stDnsApprovalOutput.m_stApprovalConfig,
                sizeof(stDnsApprovalOutput.m_stApprovalConfig)
                );
            try
            {
                bQueryParsed = pDnsSocket->m_pDnsQueryMessage->DnsQueryParse(
                    (PDNS_MESSAGE_BUFFER) pRecvQueuedEntry->m_pBuffers[0].buf,
                    wDnsLength,
                    stDnsApprovalOutput.m_stApprovalConfig,
                    stDnsApprovalOutput.m_pData,
                    sizeof(stDnsApprovalOutput.m_pData)
                    );
            }
            catch (int nExeption)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNSSOCKET,
                    "%!FUNC! line:%!LINE! %!LEVEL! Exception #%d occurred when invoking \"DnsQueryParse\"",
                    nExeption
                    );
                bQueryParsed = false;
                stDnsApprovalOutput.m_stApprovalConfig.m_bDiscard = true;
                stDnsApprovalOutput.m_stApprovalConfig.m_bDataModify = false;
            }
            if (!bQueryParsed)
            {
                TraceEvents(
                    TRACE_LEVEL_WARNING,
                    TRACE_DNSSOCKET,
                    "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p; Non Parsed Query length=%u from IPv4 clent.port = %!PORT!; %!IPADDR!;",
                    (void*) pDnsSocket->m_hSocket,
                    *lpNumberOfBytesRecvd,
                    pAddr->sin_port,
                    pAddr->sin_addr.S_un.S_addr
                    );
                SleepEx(0, TRUE);
            }
            if (stDnsApprovalOutput.m_stApprovalConfig.m_bDiscard)
            {
                pDnsSocket->m_bAbortedQuery = false;
                pDnsSocket->m_bPermitQuery = false;
                pDnsSocket->m_bModifyQuery = false;
                if (721721721 != pDnsSocket->m_pDnsQueryMessage->m_nCookiesValue)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_WS2INTERCEPTDLL,
                        "%!FUNC! line:%!LINE! %!LEVEL! (721721721 != m_nCookiesValue)"
                        );
                    SleepEx(0, TRUE);
                }
                else
                {
                    try
                    {
                        delete pDnsSocket->m_pDnsQueryMessage;
                    }
                    catch (int nException)
                    {
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            TRACE_WS2INTERCEPTDLL,
                            "%!FUNC! line:%!LINE! %!LEVEL! The exception #%d occurred.",
                            nException
                            );
                        SleepEx(0, TRUE);
                    }
                }
                pDnsSocket->m_pDnsQueryMessage = NULL;
                *lpNumberOfBytesRecvd = 0;
                *pRecvQueuedEntry->m_pNumberOfBytesRecvd = 0;
                bLockReturn = m_oDataLock.Unlock();
                return nRet;
            }
            if (!stDnsApprovalOutput.m_stApprovalConfig.m_bDataModify)
            {
                pDnsSocket->m_bAbortedQuery = false;
                pDnsSocket->m_bPermitQuery = true;
                pDnsSocket->m_bModifyQuery = false;
                if (721721721 != pDnsSocket->m_pDnsQueryMessage->m_nCookiesValue)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_WS2INTERCEPTDLL,
                        "%!FUNC! line:%!LINE! %!LEVEL! (721721721 != m_nCookiesValue)"
                        );
                    SleepEx(0, TRUE);
                }
                else
                {
                    try
                    {
                        delete pDnsSocket->m_pDnsQueryMessage;
                    }
                    catch (int nException)
                    {
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            TRACE_WS2INTERCEPTDLL,
                            "%!FUNC! line:%!LINE! %!LEVEL! The exception #%d occurred.",
                            nException
                            );
                        SleepEx(0, TRUE);
                    }
                }
                pDnsSocket->m_pDnsQueryMessage = NULL;
                bLockReturn = m_oDataLock.Unlock();
                return nRet;
            }
            // m_bDataModify
            pDnsSocket->m_bAbortedQuery = true;
            pDnsSocket->m_bPermitQuery = false;
            pDnsSocket->m_bModifyQuery = true;
            RtlCopyMemory(
                pRecvQueuedEntry->m_pBuffers[0].buf,
                stDnsApprovalOutput.m_pData,
                stDnsApprovalOutput.m_stApprovalConfig.m_nDataSize
                );
            *lpNumberOfBytesRecvd = stDnsApprovalOutput.m_stApprovalConfig.m_nDataSize;
            bLockReturn = m_oDataLock.Unlock();
            return nRet;
        }
        if (AF_INET6 == lpFrom->sa_family)
        {
            SOCKADDR_IN6*   pAddr = (SOCKADDR_IN6*) lpFrom;
            WCHAR           szIn6Address[128], *pAddressEnd;
            pRecvQueuedEntry->m_bRemoteIsLocal = IsRemoteIPv6Local(&pAddr->sin6_addr);
            if (pRecvQueuedEntry->m_bRemoteIsLocal)
            {
                bLockReturn = m_oDataLock.Unlock();
                return nRet;
            }
            pDnsSocket->m_enSocketState = enDnsUdp6Received;
            pDnsSocket->m_pDnsQueryMessage = new CDnsMessageWB();
            RtlZeroMemory(
                &stDnsApprovalOutput.m_stApprovalConfig,
                sizeof(stDnsApprovalOutput.m_stApprovalConfig)
                );
            try
            {
                bQueryParsed = pDnsSocket->m_pDnsQueryMessage->DnsQueryParse(
                    (PDNS_MESSAGE_BUFFER) pRecvQueuedEntry->m_pBuffers[0].buf,
                    wDnsLength,
                    stDnsApprovalOutput.m_stApprovalConfig,
                    stDnsApprovalOutput.m_pData,
                    sizeof(stDnsApprovalOutput.m_pData)
                    );
            }
            catch (int nExeption)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNSSOCKET,
                    "%!FUNC! line:%!LINE! %!LEVEL! Exception #%d occurred when invoking \"DnsQueryParse\"",
                    nExeption
                    );
                bQueryParsed = false;
                stDnsApprovalOutput.m_stApprovalConfig.m_bDiscard = true;
                stDnsApprovalOutput.m_stApprovalConfig.m_bDataModify = false;
            }
            if (!bQueryParsed)
            {
                pAddressEnd = RtlIpv6AddressToStringW(
                    &pAddr->sin6_addr,
                    szIn6Address
                    );
                TraceEvents(
                    TRACE_LEVEL_WARNING,
                    TRACE_DNSSOCKET,
                    "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p; Non Parsed Query length=%u from IPv6 clent.port = %!PORT!; %ws;",
                    (void*) pDnsSocket->m_hSocket,
                    *lpNumberOfBytesRecvd,
                    pAddr->sin6_port,
                    szIn6Address
                    );
                SleepEx(0, TRUE);
            }
            if (stDnsApprovalOutput.m_stApprovalConfig.m_bDiscard)
            {
                pDnsSocket->m_bAbortedQuery = false;
                pDnsSocket->m_bPermitQuery = false;
                pDnsSocket->m_bModifyQuery = false;
                if (721721721 != pDnsSocket->m_pDnsQueryMessage->m_nCookiesValue)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_WS2INTERCEPTDLL,
                        "%!FUNC! line:%!LINE! %!LEVEL! (721721721 != m_nCookiesValue)"
                        );
                    SleepEx(0, TRUE);
                }
                else
                {
                    try
                    {
                        delete pDnsSocket->m_pDnsQueryMessage;
                    }
                    catch (int nException)
                    {
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            TRACE_WS2INTERCEPTDLL,
                            "%!FUNC! line:%!LINE! %!LEVEL! The exception #%d occurred.",
                            nException
                            );
                        SleepEx(0, TRUE);
                    }
                }
                pDnsSocket->m_pDnsQueryMessage = NULL;
                *lpNumberOfBytesRecvd = 0;
                *pRecvQueuedEntry->m_pNumberOfBytesRecvd = 0;
                bLockReturn = m_oDataLock.Unlock();
                return nRet;
            }
            if (!stDnsApprovalOutput.m_stApprovalConfig.m_bDataModify)
            {
                pDnsSocket->m_bAbortedQuery = false;
                pDnsSocket->m_bPermitQuery = true;
                pDnsSocket->m_bModifyQuery = false;
                if (721721721 != pDnsSocket->m_pDnsQueryMessage->m_nCookiesValue)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_WS2INTERCEPTDLL,
                        "%!FUNC! line:%!LINE! %!LEVEL! (721721721 != m_nCookiesValue)"
                        );
                    SleepEx(0, TRUE);
                }
                else
                {
                    try
                    {
                        delete pDnsSocket->m_pDnsQueryMessage;
                    }
                    catch (int nException)
                    {
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            TRACE_WS2INTERCEPTDLL,
                            "%!FUNC! line:%!LINE! %!LEVEL! The exception #%d occurred.",
                            nException
                            );
                        SleepEx(0, TRUE);
                    }
                }
                pDnsSocket->m_pDnsQueryMessage = NULL;
                bLockReturn = m_oDataLock.Unlock();
                return nRet;
            }
            // m_bDataModify
            pDnsSocket->m_bAbortedQuery = true;
            pDnsSocket->m_bPermitQuery = false;
            pDnsSocket->m_bModifyQuery = true;
            RtlCopyMemory(
                pRecvQueuedEntry->m_pBuffers[0].buf,
                stDnsApprovalOutput.m_pData,
                stDnsApprovalOutput.m_stApprovalConfig.m_nDataSize
                );
            *lpNumberOfBytesRecvd = stDnsApprovalOutput.m_stApprovalConfig.m_nDataSize;
        }
    }
    bLockReturn = m_oDataLock.Unlock();
    return nRet;
}
#pragma warning(pop)


SOCKET CWS2InterceptAPP::mf_WSASocketW(
    int af,
    int type,
    int protocol,
    LPWSAPROTOCOL_INFOW lpProtocolInfo,
    GROUP g,
    DWORD dwFlags
    )
{
    SOCKET      hRet;
    int         nError;
    TDnsSocketArray::iterator socks_iterator;
    TDnsSocket  stDnsSocket;
    BOOL        bLockReturn;
    hRet = m_pfSavedWSASocketWFunction(af, type, protocol, lpProtocolInfo, g, dwFlags);
    bLockReturn = m_oDataLock.Lock();
    if (INVALID_SOCKET == hRet)
    {
        nError = WSAGetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSSOCKET,
            "%!FUNC! line:%!LINE! %!LEVEL! call \"WSASocketW\" failed with error %!WINERROR!",
            nError
            );
    }
    else
    {
        socks_iterator = m_oDnsSocketArray.find(hRet);
        if (socks_iterator != m_oDnsSocketArray.end())
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSSOCKET,
                "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p already exist in array",
                (void*) hRet
                );
        }
        else
        {
            stDnsSocket.m_bWSACreated = true;
            stDnsSocket.m_bOverlapedIO = (0 != (WSA_FLAG_OVERLAPPED & dwFlags));
            stDnsSocket.m_hSocket = hRet;
            stDnsSocket.m_nAddressFamily = af;
            stDnsSocket.m_nSocketType = type;
            stDnsSocket.m_nSocketProtocol = protocol;
            stDnsSocket.m_pProtocolInfo = lpProtocolInfo;
            if (AF_INET == af)
            {
                stDnsSocket.m_enSocketState = enIp4Created;
            }
            else if (AF_INET6 == af)
            {
                stDnsSocket.m_enSocketState = enIp6Created;
            }
            else
            {
                stDnsSocket.m_enSocketState = enUnknownState;
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNSSOCKET,
                    "%!FUNC! line:%!LINE! %!LEVEL! Unexpected address family = %d",
                    af
                    );
            }
            if (NULL != lpProtocolInfo)
            {
                //TraceEvents(
                //    TRACE_LEVEL_INFORMATION,
                //    TRACE_DNSSOCKET,
                //    "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p af = %d; type = %d; protocol = %d; ProviderId = %!GUID!; szProtocol = %ws;",
                //    (void*) hRet,
                //    af,
                //    type,
                //    protocol,
                //    &lpProtocolInfo->ProviderId,
                //    lpProtocolInfo->szProtocol
                //    );
            }
            m_oDnsSocketArray.insert(TDnsSocketArray::value_type(hRet, stDnsSocket));
        }
    }
    bLockReturn = m_oDataLock.Unlock();
    return hRet;
}


int CWS2InterceptAPP::mf_WSAStartup(
    WORD wVersionRequested,
    LPWSADATA lpWSAData
    )
{
    int     nRet;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    //TraceEvents(
    //    TRACE_LEVEL_INFORMATION,
    //    TRACE_WS2INTERCEPTDLL,
    //    "%!FUNC! line:%!LINE! %!LEVEL!    wVersionRequested = %04hX",
    //    wVersionRequested
    //    );
    //TraceEvents(
    //    TRACE_LEVEL_INFORMATION,
    //    TRACE_WS2INTERCEPTDLL,
    //    "%!FUNC! line:%!LINE! %!LEVEL!    lpWSAData = %p",
    //    lpWSAData
    //    );
    SleepEx(0, TRUE);
    nRet = m_pfSavedWSAStartupFunction(wVersionRequested, lpWSAData);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! call \"WSAStartup\" return %!WINERROR!",
        nRet
        );
    if (0 == nRet)
    {
        //TraceEvents(
        //    TRACE_LEVEL_INFORMATION,
        //    TRACE_WS2INTERCEPTDLL,
        //    "%!FUNC! line:%!LINE! %!LEVEL!    iMaxUdpDg = %hu",
        //    lpWSAData->iMaxUdpDg
        //    );
    }
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
    SleepEx(0, TRUE);
    return nRet;
}


NTSTATUS CWS2InterceptAPP::UpdateImports(
    PBYTE oldFunction,
    PBYTE newFunction
    )
{
    PLDR_DATA_TABLE_ENTRY   pResult, pFindEntry;
    REAL_IMPORT_DESCRIPTOR  stImportDescriptor;
    PWSTR       pwsCurrentModuleName;
    bool        bResult;
    NTSTATUS    nNtStatus = NOERROR;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    pResult = PLDR_DATA_TABLE_ENTRY(m_oModule32HLP.m_pModuleListTable->InLoadOrderModuleList.Flink);
    pFindEntry = m_oModule32HLP.FindModuleByAddress(DWORD_PTR(pResult->DllBase));
    if (pFindEntry != pResult)
    {
        nNtStatus = -1;
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (pFindEntry != pResult)"
            );
        return nNtStatus;
    }
    pwsCurrentModuleName = m_oModule32HLP.GetModuleNameW();
    bResult = m_oModule32HLP.EnumImportStart(pwsCurrentModuleName);
    if (!bResult)
    {
        return nNtStatus;
    }
    RtlZeroMemory(
        &stImportDescriptor,
        sizeof(stImportDescriptor)
        );
    while (m_oModule32HLP.EnumImportNext(stImportDescriptor))
    {
        if (stImportDescriptor.pFunctionAddress != oldFunction)
        {
            continue;
        }
        nNtStatus = NTWriteProcessMemory(
            NtCurrentProcess(),
            &m_oModule32HLP.m_pImportThunkArray[stImportDescriptor.dwThunkIndex],
            &newFunction,
            sizeof(newFunction),
            NULL
            );
        if (!NT_SUCCESS(nNtStatus))
        {
            return nNtStatus;
        }
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Exchanged addresses for function \"%s\"",
            stImportDescriptor.szFunctionName
            );
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL!     szCalledModuleName = \"%s\"",
            stImportDescriptor.szCalledModuleName
            );
    }
    m_oModule32HLP.EnumImportStop();
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
    return nNtStatus;
}


SOCKET WSAAPI CWS2InterceptAPP::sf_accept(
    SOCKET s,
    struct sockaddr* addr,
    int* addrlen
    )
{
    SOCKET  hResult;
    hResult = theApp.mf_accept(s, addr, addrlen);
    return hResult;
}


int WSAAPI CWS2InterceptAPP::sf_bind(
    SOCKET s,
    const struct sockaddr* name,
    int namelen
    )
{
    int     nResult;
    nResult = theApp.mf_bind(s, name, namelen);
    return nResult;
}


int WSAAPI CWS2InterceptAPP::sf_closesocket(SOCKET s)
{
    int     nResult;
    nResult = theApp.mf_closesocket(s);
    return nResult;
}


int WSAAPI CWS2InterceptAPP::sf_recv(
    SOCKET s,
    char* buf,
    int len,
    int flags
    )
{
    int     nResult;
    nResult = theApp.mf_recv(s, buf, len, flags);
    return nResult;
}


int WSAAPI CWS2InterceptAPP::sf_send(
    SOCKET s,
    const char* buf,
    int len,
    int flags
    )
{
    int     nResult;
    nResult = theApp.mf_send(s, buf, len, flags);
    return nResult;
}


int WSAAPI CWS2InterceptAPP::sf_sendto(
    SOCKET s,
    const char* buf,
    int len,
    int flags,
    const struct sockaddr* to,
    int tolen
    )
{
    int     nResult;
    nResult = theApp.mf_sendto(s, buf, len, flags, to, tolen);
    return nResult;
}


SOCKET WSAAPI CWS2InterceptAPP::sf_socket(
    int af,
    int type,
    int protocol
    )
{
    SOCKET  hResult;
    hResult = theApp.mf_socket(af, type, protocol);
    return hResult;
}


int WSAAPI CWS2InterceptAPP::sf_WSACleanup()
{
    int     nResult;
    nResult = theApp.mf_WSACleanup();
    return nResult;
}


int WSAAPI CWS2InterceptAPP::sf_WSARecvFrom(
    SOCKET s,
    LPWSABUF lpBuffers,
    DWORD dwBufferCount,
    LPDWORD lpNumberOfBytesRecvd,
    LPDWORD lpFlags,
    struct sockaddr* lpFrom,
    LPINT lpFromlen,
    LPWSAOVERLAPPED lpOverlapped,
    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
    )
{
    int     nResult;
    nResult = theApp.mf_WSARecvFrom(s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpFrom, lpFromlen, lpOverlapped, lpCompletionRoutine);
    return nResult;
}


SOCKET WSAAPI CWS2InterceptAPP::sf_WSASocketW(
    int af,
    int type,
    int protocol,
    LPWSAPROTOCOL_INFOW lpProtocolInfo,
    GROUP g,
    DWORD dwFlags
    )
{
    SOCKET  hResult;
    hResult = theApp.mf_WSASocketW(af, type, protocol, lpProtocolInfo, g, dwFlags);
    return hResult;
}


int WSAAPI CWS2InterceptAPP::sf_WSAStartup(
    WORD wVersionRequested,
    LPWSADATA lpWSAData
    )
{
    int     nResult;
    nResult = theApp.mf_WSAStartup(wVersionRequested, lpWSAData);
    return nResult;
}


NTSTATUS CWS2InterceptAPP::UpdateWS2_32Imports(
    PBYTE oldFunction,
    PBYTE newFunction
    )
{
    PLDR_DATA_TABLE_ENTRY   pResult;
    REAL_IMPORT_DESCRIPTOR  stImportDescriptor;
    NTSTATUS                nNtStatus = NOERROR;
    bool                    bResult;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    pResult = m_oModule32HLP.FindModule(L"WS2_32.DLL");
    if (NULL == pResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"FindModule\" failed"
            );
        return -1;
    }
    bResult = m_oModule32HLP.EnumImportStart(L"WS2_32.DLL");
    if (!bResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"EnumImportStart\" failed"
            );
        return -1;
    }
    RtlZeroMemory(
        &stImportDescriptor,
        sizeof(stImportDescriptor)
        );
    while (m_oModule32HLP.EnumImportNext(stImportDescriptor))
    {
        if (stImportDescriptor.pFunctionAddress != oldFunction)
        {
            continue;
        }
        nNtStatus = NTWriteProcessMemory(
            NtCurrentProcess(),
            &m_oModule32HLP.m_pImportThunkArray[stImportDescriptor.dwThunkIndex],
            &newFunction,
            sizeof(newFunction),
            NULL
            );
        if (!NT_SUCCESS(nNtStatus))
        {
            return nNtStatus;
        }
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Exchanged addresses for function \"%s\"",
            stImportDescriptor.szFunctionName
            );
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL!     szCalledModuleName = \"%s\"",
            stImportDescriptor.szCalledModuleName
            );
    }
    m_oModule32HLP.EnumImportStop();
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
    return nNtStatus;
}


HANDLE WINAPI CWS2InterceptAPP::sf_CreateIoCompletionPort(
    HANDLE FileHandle,
    HANDLE ExistingCompletionPort,
    ULONG_PTR CompletionKey,
    DWORD NumberOfConcurrentThreads
    )
{
    HANDLE  hRet;
    hRet = theApp.mf_CreateIoCompletionPort(
        FileHandle,
        ExistingCompletionPort,
        CompletionKey,
        NumberOfConcurrentThreads
        );
    return hRet;
}


HANDLE CWS2InterceptAPP::mf_CreateIoCompletionPort(
    HANDLE FileHandle,
    HANDLE ExistingCompletionPort,
    ULONG_PTR CompletionKey,
    DWORD NumberOfConcurrentThreads
    )
{
    HANDLE      hRet;
    DWORD       dwError;
    TDnsSocketArray::iterator socks_iterator;
    TDnsSocket* pDnsSocket;
    BOOL        bRet;
    hRet = m_pfSavedCreateIoCompletionPortFunction(
        FileHandle,
        ExistingCompletionPort,
        CompletionKey,
        NumberOfConcurrentThreads
        );
    bRet = m_oDataLock.Lock();
    socks_iterator = m_oDnsSocketArray.find(SOCKET(FileHandle));
    if (socks_iterator != m_oDnsSocketArray.end())
    {
        pDnsSocket = &socks_iterator->second;
        if (pDnsSocket->m_bBindedToDnsPort)
        {
            if (NULL == hRet)
            {
                dwError = GetLastError();
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNSSOCKET,
                    "%!FUNC! line:%!LINE! %!LEVEL! call \"CreateIoCompletionPort\" for SOCKET %p failed with error %!WINERROR!",
                    (void*) FileHandle,
                    dwError
                    );
                pDnsSocket->m_enSocketState = enErrorOperation;
                pDnsSocket->m_nErrorCode = dwError;
            }
            else
            {
                pDnsSocket->m_ulCompletionKey = CompletionKey;
                //TraceEvents(
                //    TRACE_LEVEL_INFORMATION,
                //    TRACE_DNSSOCKET,
                //    "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p associated with I/O completion port %p; CompletionKey = %p;",
                //    (void*) FileHandle,
                //    (void*) hRet,
                //    (void*) CompletionKey
                //    );
                pDnsSocket->m_enSocketState = enDnsCreateIoCompletionPort;
                pDnsSocket->m_hIoCompletionPort = hRet;
            }
        }
    }
    bRet = m_oDataLock.Unlock();
    SleepEx(0, TRUE);
    return hRet;
}


BOOL WINAPI CWS2InterceptAPP::sf_GetQueuedCompletionStatus(
    HANDLE CompletionPort,
    LPDWORD lpNumberOfBytes,
    PULONG_PTR lpCompletionKey,
    LPOVERLAPPED* lpOverlapped,
    DWORD dwMilliseconds
    )
{
    BOOL    bRet;
    bRet = theApp.mf_GetQueuedCompletionStatus(
        CompletionPort,
        lpNumberOfBytes,
        lpCompletionKey,
        lpOverlapped,
        dwMilliseconds
        );
    return bRet;
}


BOOL CWS2InterceptAPP::mf_GetQueuedCompletionStatus(
    HANDLE CompletionPort,
    LPDWORD lpNumberOfBytes,
    PULONG_PTR lpCompletionKey,
    LPOVERLAPPED* lpOverlapped,
    DWORD dwMilliseconds
    )
{
    BOOL                                bRet;
    DWORD                               dwError;
    TDnsSocketArray::iterator           socks_iterator;
    CTRecvQueuedArray::iterator         entry_iterator;
    CTUdpRemoteClientArray::iterator    clients_iterator;
    TDnsSocket*                         pDnsSocket;
    TRecvQueuedEntry*                   pRecvQueuedEntry;
    DWORD                               dwUdpDataSize;
    WORD                                wDnsLength;
    bool                                bQueryParsed;
    DNS_APPROVAL_OUTPUT                 stDnsApprovalOutput;
    TUdpRemoteClient*                   pUdpRemoteClient;
    TUdpRemoteClient                    stNewClientEntry;
    TUdpRemoteClientInsertPair          oRemoteClientInsertPair;
    BOOL                                bLockReturn;
    UINT32                              ulIp4NAddr, ulIp4HAddr;
    TCHAR                               szIpAddress[128];
    LPTSTR                              pAddressEnd;
    bRet = m_pfSavedGetQueuedCompletionStatusFunction(
        CompletionPort,
        lpNumberOfBytes,
        lpCompletionKey,
        lpOverlapped,
        dwMilliseconds
        );
    bLockReturn = m_oDataLock.Lock();
    if (!bRet)
    {
        dwError = GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSSOCKET,
            "%!FUNC! line:%!LINE! %!LEVEL! call \"GetQueuedCompletionStatus\" failed with error %!WINERROR!",
            dwError
            );
        bLockReturn = m_oDataLock.Unlock();
        SleepEx(0, TRUE);
        return bRet;
    }
    entry_iterator = m_oRecvQueuedArray.find(*lpOverlapped);
    if (entry_iterator == m_oRecvQueuedArray.end())
    {
        bLockReturn = m_oDataLock.Unlock();
        return bRet;
    }
    dwUdpDataSize = *lpNumberOfBytes;
    wDnsLength = (WORD) dwUdpDataSize;
    pRecvQueuedEntry = &entry_iterator->second;
    pRecvQueuedEntry->m_ulCompletionKey = *lpCompletionKey;
    socks_iterator = m_oDnsSocketArray.find(pRecvQueuedEntry->m_hSocket);
    if (socks_iterator == m_oDnsSocketArray.end())
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSSOCKET,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (socks_iterator == m_oDnsSocketArray.end())"
            );
        bLockReturn = m_oDataLock.Unlock();
        SleepEx(0, TRUE);
        return bRet;
    }
    pDnsSocket = &socks_iterator->second;
    if (!pDnsSocket->m_bBindedToDnsPort)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSSOCKET,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (!m_bBindedToDnsPort)"
            );
        bLockReturn = m_oDataLock.Unlock();
        SleepEx(0, TRUE);
        return bRet;
    }
    pDnsSocket->m_pCurrRecvQueuedEntry = pRecvQueuedEntry;
    RtlCopyMemory(
        &pRecvQueuedEntry->m_stSocketClientAddress,
        pRecvQueuedEntry->m_pRecvFromAddress,
        *pRecvQueuedEntry->m_pRecvFromAddressLen
        );
    if (AF_INET == pRecvQueuedEntry->m_stSocketClientAddress.ss_family)
    {
        SOCKADDR_IN*    pAddr = (SOCKADDR_IN*) &pRecvQueuedEntry->m_stSocketClientAddress;
        ulIp4NAddr = pAddr->sin_addr.S_un.S_addr;
        ulIp4HAddr = ntohl(ulIp4NAddr);
        pRecvQueuedEntry->m_bRemoteIsLocal = IsRemoteIPv4Local(ulIp4NAddr);
        if (pRecvQueuedEntry->m_bRemoteIsLocal)
        {
            bLockReturn = m_oDataLock.Unlock();
            return bRet;
        }
        if (DNS_OPCODE_NOTIFY == PDNS_MESSAGE_BUFFER(pRecvQueuedEntry->m_pBuffers[0].buf)->MessageHead.Opcode)
        {
            pRecvQueuedEntry->m_bRemoteIsLocal = true;
            //TraceEvents(
            //    TRACE_LEVEL_INFORMATION,
            //    TRACE_DNSSOCKET,
            //    "%!FUNC! line:%!LINE! %!LEVEL! This is NOTIFY Query; Host IP = %!IPADDR!; clent.port = %!PORT!;",
            //    pAddr->sin_addr.S_un.S_addr,
            //    pAddr->sin_port
            //    );
            //SleepEx(0, TRUE);
            bLockReturn = m_oDataLock.Unlock();
            return bRet;
        }
        clients_iterator = m_oUdpRemoteClientArray.find(ulIp4NAddr);
        if (clients_iterator == m_oUdpRemoteClientArray.end())
        {
            stNewClientEntry.m_pDnsSocket = pDnsSocket;
            stNewClientEntry.m_nQueryCount = 1;
            oRemoteClientInsertPair = m_oUdpRemoteClientArray.insert(
                CTUdpRemoteClientArray::value_type(ulIp4NAddr, stNewClientEntry)
                );
            clients_iterator = oRemoteClientInsertPair.first;
            pUdpRemoteClient = &clients_iterator->second;
        }
        else
        {   // clients_iterator != m_oUdpRemoteClientArray.end()
            pUdpRemoteClient = &clients_iterator->second;
            pUdpRemoteClient->m_nQueryCount++;
            if (pUdpRemoteClient->m_nQueryCount > 3)
            {
                pAddressEnd = RtlIpv4AddressToString(
                    &pAddr->sin_addr,
                    szIpAddress
                    );
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNSSOCKET,
                    "%!FUNC! line:%!LINE! %!LEVEL! DNS Client count > 3; IP-address = %ws;",
                    szIpAddress
                    );
                //bLockReturn = m_pDnsStateThread->m_oTimersLock.Lock();
                m_pDnsStateThread->ResetFirewallIpV4Config(ulIp4HAddr);
                //bLockReturn = m_pDnsStateThread->m_oTimersLock.Unlock();
                pUdpRemoteClient->m_nQueryCount--;
                pDnsSocket->m_pDnsQueryMessage = NULL;
                *lpNumberOfBytes = 0;
                bLockReturn = m_oDataLock.Unlock();
                SleepEx(0, TRUE);
                return bRet;
            }
        }
        pDnsSocket->m_enSocketState = enDnsUdp4RecvQueryComplete;
        pDnsSocket->m_pDnsQueryMessage = new CDnsMessageWB();
        RtlZeroMemory(
            &stDnsApprovalOutput.m_stApprovalConfig,
            sizeof(stDnsApprovalOutput.m_stApprovalConfig)
            );
        try
        {
            bQueryParsed = pDnsSocket->m_pDnsQueryMessage->DnsQueryParse(
                (PDNS_MESSAGE_BUFFER) pRecvQueuedEntry->m_pBuffers[0].buf,
                wDnsLength,
                stDnsApprovalOutput.m_stApprovalConfig,
                stDnsApprovalOutput.m_pData,
                sizeof(stDnsApprovalOutput.m_pData)
                );
        }
        catch (int nExeption)
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSSOCKET,
                "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p received %u bytes from IPv4 clent.port = %!PORT!; %!IPADDR!;",
                (void*) pDnsSocket->m_hSocket,
                *lpNumberOfBytes,
                pAddr->sin_port,
                pAddr->sin_addr.S_un.S_addr
                );
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSSOCKET,
                "%!FUNC! line:%!LINE! %!LEVEL! Exception #%d occurred when invoking \"DnsQueryParse\"",
                nExeption
                );
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSSOCKET,
                "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p received %u bytes from IPv4 clent.port = %!PORT!; %!IPADDR!;",
                (void*) pDnsSocket->m_hSocket,
                *lpNumberOfBytes,
                pAddr->sin_port,
                pAddr->sin_addr.S_un.S_addr
                );
            bQueryParsed = false;
            stDnsApprovalOutput.m_stApprovalConfig.m_bDiscard = true;
            stDnsApprovalOutput.m_stApprovalConfig.m_bDataModify = false;
            SleepEx(0, TRUE);
        }
        if (stDnsApprovalOutput.m_stApprovalConfig.m_bDiscard)
        {
            pDnsSocket->m_bAbortedQuery = false;
            pDnsSocket->m_bPermitQuery = false;
            pDnsSocket->m_bModifyQuery = false;
            if (721721721 != pDnsSocket->m_pDnsQueryMessage->m_nCookiesValue)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_WS2INTERCEPTDLL,
                    "%!FUNC! line:%!LINE! %!LEVEL! (721721721 != m_nCookiesValue)"
                    );
            }
            else
            {
                try
                {
                    delete pDnsSocket->m_pDnsQueryMessage;
                }
                catch (int nException)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_WS2INTERCEPTDLL,
                        "%!FUNC! line:%!LINE! %!LEVEL! The exception #%d occurred.",
                        nException
                        );
                }
            }
            pDnsSocket->m_pDnsQueryMessage = NULL;
            *lpNumberOfBytes = 0;
            bLockReturn = m_oDataLock.Unlock();
            SleepEx(0, TRUE);
            return bRet;
        }
        if (!bQueryParsed)
        {
            TraceEvents(
                TRACE_LEVEL_WARNING,
                TRACE_DNSSOCKET,
                "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p; Non Parsed Query length=%u from IPv4 clent.port = %!PORT!; %!IPADDR!;",
                (void*) pDnsSocket->m_hSocket,
                *lpNumberOfBytes,
                pAddr->sin_port,
                pAddr->sin_addr.S_un.S_addr
                );
            SleepEx(0, TRUE);
        }
        if (!stDnsApprovalOutput.m_stApprovalConfig.m_bDataModify)
        {
            pDnsSocket->m_bAbortedQuery = false;
            pDnsSocket->m_bPermitQuery = true;
            pDnsSocket->m_bModifyQuery = false;
            if (721721721 != pDnsSocket->m_pDnsQueryMessage->m_nCookiesValue)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_WS2INTERCEPTDLL,
                    "%!FUNC! line:%!LINE! %!LEVEL! (721721721 != m_nCookiesValue)"
                    );
                SleepEx(0, TRUE);
            }
            else
            {
                try
                {
                    delete pDnsSocket->m_pDnsQueryMessage;
                }
                catch (int nException)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_WS2INTERCEPTDLL,
                        "%!FUNC! line:%!LINE! %!LEVEL! The exception #%d occurred.",
                        nException
                        );
                    SleepEx(0, TRUE);
                }
            }
            pDnsSocket->m_pDnsQueryMessage = NULL;
            bLockReturn = m_oDataLock.Unlock();
            return bRet;
        }
        // m_bDataModify
        pDnsSocket->m_bAbortedQuery = true;
        pDnsSocket->m_bPermitQuery = false;
        pDnsSocket->m_bModifyQuery = true;
        RtlCopyMemory(
            pRecvQueuedEntry->m_pBuffers[0].buf,
            stDnsApprovalOutput.m_pData,
            stDnsApprovalOutput.m_stApprovalConfig.m_nDataSize
            );
        *lpNumberOfBytes = stDnsApprovalOutput.m_stApprovalConfig.m_nDataSize;
        bLockReturn = m_oDataLock.Unlock();
        return bRet;
    }
    if (AF_INET6 == pRecvQueuedEntry->m_stSocketClientAddress.ss_family)
    {
        SOCKADDR_IN6*   pAddr = (SOCKADDR_IN6*) &pRecvQueuedEntry->m_stSocketClientAddress;
        WCHAR           szIn6Address[128], *pAddressEnd;
        pRecvQueuedEntry->m_bRemoteIsLocal = IsRemoteIPv6Local(&pAddr->sin6_addr);
        if (pRecvQueuedEntry->m_bRemoteIsLocal)
        {
            bLockReturn = m_oDataLock.Unlock();
            return bRet;
        }
        if (DNS_OPCODE_NOTIFY == PDNS_MESSAGE_BUFFER(pRecvQueuedEntry->m_pBuffers[0].buf)->MessageHead.Opcode)
        {
            pRecvQueuedEntry->m_bRemoteIsLocal = true;
            pAddressEnd = RtlIpv6AddressToStringW(
                &pAddr->sin6_addr,
                szIpAddress
                );
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                TRACE_DNSSOCKET,
                "%!FUNC! line:%!LINE! %!LEVEL! This is NOTIFY Query; Host IP = %ws; clent.port = %!PORT!;",
                szIpAddress,
                pAddr->sin6_port
                );
            SleepEx(0, TRUE);
            bLockReturn = m_oDataLock.Unlock();
            return bRet;
        }
        pDnsSocket->m_enSocketState = enDnsUdp6RecvQueryComplete;
        pDnsSocket->m_pDnsQueryMessage = new CDnsMessageWB();
        try
        {
            bQueryParsed = pDnsSocket->m_pDnsQueryMessage->DnsQueryParse(
                (PDNS_MESSAGE_BUFFER) pRecvQueuedEntry->m_pBuffers[0].buf,
                wDnsLength,
                stDnsApprovalOutput.m_stApprovalConfig,
                stDnsApprovalOutput.m_pData,
                sizeof(stDnsApprovalOutput.m_pData)
                );
        }
        catch (int nExeption)
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSSOCKET,
                "%!FUNC! line:%!LINE! %!LEVEL! Exception #%d occurred when invoking \"DnsQueryParse\"",
                nExeption
                );
            bQueryParsed = false;
            stDnsApprovalOutput.m_stApprovalConfig.m_bDiscard = true;
            stDnsApprovalOutput.m_stApprovalConfig.m_bDataModify = false;
        }
        if (!bQueryParsed)
        {
            pAddressEnd = RtlIpv6AddressToStringW(
                &pAddr->sin6_addr,
                szIn6Address
                );
            TraceEvents(
                TRACE_LEVEL_WARNING,
                TRACE_DNSSOCKET,
                "%!FUNC! line:%!LINE! %!LEVEL! SOCKET %p; Non Parsed Query length=%u from IPv6 clent.port = %!PORT!; %ws;",
                (void*) pDnsSocket->m_hSocket,
                *lpNumberOfBytes,
                pAddr->sin6_port,
                szIn6Address
                );
            SleepEx(0, TRUE);
        }
        if (stDnsApprovalOutput.m_stApprovalConfig.m_bDiscard)
        {
            pDnsSocket->m_bAbortedQuery = false;
            pDnsSocket->m_bPermitQuery = false;
            pDnsSocket->m_bModifyQuery = false;
            if (721721721 != pDnsSocket->m_pDnsQueryMessage->m_nCookiesValue)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_WS2INTERCEPTDLL,
                    "%!FUNC! line:%!LINE! %!LEVEL! (721721721 != m_nCookiesValue)"
                    );
                SleepEx(0, TRUE);
            }
            else
            {
                try
                {
                    delete pDnsSocket->m_pDnsQueryMessage;
                }
                catch (int nException)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_WS2INTERCEPTDLL,
                        "%!FUNC! line:%!LINE! %!LEVEL! The exception #%d occurred.",
                        nException
                        );
                }
                SleepEx(0, TRUE);
            }
            pDnsSocket->m_pDnsQueryMessage = NULL;
            *lpNumberOfBytes = 0;
            *pRecvQueuedEntry->m_pNumberOfBytesRecvd = 0;
            //WSASetLastError(WSAENETRESET);
            //SetLastError(WSAENETRESET);
            bLockReturn = m_oDataLock.Unlock();
            return bRet;
        }
        if (!stDnsApprovalOutput.m_stApprovalConfig.m_bDataModify)
        {
            pDnsSocket->m_bAbortedQuery = false;
            pDnsSocket->m_bPermitQuery = true;
            pDnsSocket->m_bModifyQuery = false;
            if (721721721 != pDnsSocket->m_pDnsQueryMessage->m_nCookiesValue)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_WS2INTERCEPTDLL,
                    "%!FUNC! line:%!LINE! %!LEVEL! (721721721 != m_nCookiesValue)"
                    );
                SleepEx(0, TRUE);
            }
            else
            {
                try
                {
                    delete pDnsSocket->m_pDnsQueryMessage;
                }
                catch (int nException)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_WS2INTERCEPTDLL,
                        "%!FUNC! line:%!LINE! %!LEVEL! The exception #%d occurred.",
                        nException
                        );
                    SleepEx(0, TRUE);
                }
            }
            pDnsSocket->m_pDnsQueryMessage = NULL;
            bLockReturn = m_oDataLock.Unlock();
            return bRet;
        }
        // m_bDataModify
        pDnsSocket->m_bAbortedQuery = true;
        pDnsSocket->m_bPermitQuery = false;
        pDnsSocket->m_bModifyQuery = true;
        RtlCopyMemory(
            pRecvQueuedEntry->m_pBuffers[0].buf,
            stDnsApprovalOutput.m_pData,
            stDnsApprovalOutput.m_stApprovalConfig.m_nDataSize
            );
        *lpNumberOfBytes = stDnsApprovalOutput.m_stApprovalConfig.m_nDataSize;
        bLockReturn = m_oDataLock.Unlock();
        return bRet;
    }
    pDnsSocket->m_enSocketState = enUnknownState;
    TraceEvents(
        TRACE_LEVEL_ERROR,
        TRACE_DNSSOCKET,
        "%!FUNC! line:%!LINE! %!LEVEL! Unexpected client address family = %d",
        pRecvQueuedEntry->m_stSocketClientAddress.ss_family
        );
    bLockReturn = m_oDataLock.Unlock();
    SleepEx(0, TRUE);
    return bRet;
}


bool CWS2InterceptAPP::IsRemoteIPv4Local(ULONG ulIp4NAddr)
{
    bool    bRetVal;
    ULONG   ulIp4HAddr;
    int     i;
    INLINE_NTOHL(ulIp4HAddr, ulIp4NAddr);
    bRetVal = ((ulIp4HAddr & 255) == 127);
    if (!bRetVal)
    {
        for (i = 0; i < m_nPublicIpCount; i++)
        {
            if (ulIp4HAddr == m_pPublicIpArray[i])
            {
                bRetVal = true;
                break;
            }
        }
    }
    else
    {
        return true;
    }
    if (!bRetVal)
    {
        for (i = 0; i < m_nPrivateIpCount; i++)
        {
            if (ulIp4HAddr == m_pPrivateIpArray[i])
            {
                bRetVal = true;
                break;
            }
        }
    }
    else
    {
        return true;
    }
    if (!bRetVal)
    {
        for (i = 0; i<m_nPublicRangeCount; i++)
        {
            bRetVal = (ulIp4HAddr <= m_pPublicRangeArray[i].valueHigh.uint32)
                && (ulIp4HAddr >= m_pPublicRangeArray[i].valueLow.uint32);
            if (bRetVal)
            {
                break;
            }
        }
    }
    else
    {
        return true;
    }
    if (!bRetVal)
    {
        for (i = 0; i<m_nPrivateRangeCount; i++)
        {
            bRetVal = (ulIp4HAddr <= m_pPrivateRangeArray[i].valueHigh.uint32)
                && (ulIp4HAddr >= m_pPrivateRangeArray[i].valueLow.uint32);
            if (bRetVal)
            {
                break;
            }
        }
    }
    else
    {
        return true;
    }
    if (!bRetVal)
    {
        for (i = 0; i<m_nPublicSubnetCount; i++)
        {
            bRetVal = ((ulIp4HAddr & m_pPublicSubnetArray[i].mask)
                == m_pPublicSubnetArray[i].addr);
            if (bRetVal)
            {
                break;
            }
        }
    }
    else
    {
        return true;
    }
    if (!bRetVal)
    {
        for (i = 0; i<m_nPrivateSubnetCount; i++)
        {
            bRetVal = ((ulIp4HAddr & m_pPrivateSubnetArray[i].mask)
                == m_pPrivateSubnetArray[i].addr);
            if (bRetVal)
            {
                break;
            }
        }
    }
    return bRetVal;
}


bool CWS2InterceptAPP::IsRemoteIPv6Local(IN6_ADDR* pIPv6Address)
{
    bool    bLocal;
    int     i;
    bLocal = !bfIPv6AddressInIPv6Subnet(
        pIPv6Address,
        c_pGlobalIPv6Subnet
        );
    if (!bLocal)
    {
        for (i = 0; i<m_nPublicIPV6AddrCount; i++)
        {
            bLocal = bfIPv6AddressInIPv6Subnet(
                pIPv6Address,
                &m_pPublicIPV6AddrArray[i]
                );
            if (bLocal)
            {
                break;
            }
        }
    }
    else
    {
        return true;
    }
    if (!bLocal)
    {
        for (i = 0; i<m_nPrivateIPV6AddrCount; i++)
        {
            bLocal = bfIPv6AddressInIPv6Subnet(
                pIPv6Address,
                &m_pPrivateIPV6AddrArray[i]
                );
            if (bLocal)
            {
                break;
            }
        }
    }
    return bLocal;
}


TDnsSocket::~TDnsSocket()
{
    if (NULL != m_pZoneTransferMessage)
    {
        delete m_pZoneTransferMessage;
        m_pZoneTransferMessage = NULL;
    }
    if (NULL != m_pDnsQueryMessage)
    {
        delete m_pDnsQueryMessage;
        m_pDnsQueryMessage = NULL;
    }
    if (NULL != m_pDnsApprovalOutput)
    {
        delete m_pDnsApprovalOutput;
        m_pDnsApprovalOutput = NULL;
    }
}


SERVICE_STATUS_HANDLE WINAPI CWS2InterceptAPP::sf_RegisterServiceCtrlHandlerW(
    LPCWSTR lpServiceName,
    LPHANDLER_FUNCTION lpHandlerProc
    )
{
    SERVICE_STATUS_HANDLE   hRet;
    hRet = theApp.mf_RegisterServiceCtrlHandlerW(lpServiceName, lpHandlerProc);
    return hRet;
}


SERVICE_STATUS_HANDLE CWS2InterceptAPP::mf_RegisterServiceCtrlHandlerW(
    LPCWSTR lpServiceName,
    LPHANDLER_FUNCTION lpHandlerProc
    )
{
    SERVICE_STATUS_HANDLE   hRet;
    BOOL                    bRet;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    m_pfSavedSvcHandleFunction = lpHandlerProc;
    lpHandlerProc = sf_SvcHandler;
    SleepEx(0, TRUE);
    hRet = m_pfSavedRegisterServiceCtrlHandlerWFunction(
        lpServiceName,
        lpHandlerProc
        );
    if (NULL != hRet)
    {
        if (NULL == m_pDnsStateThread)
        {
            bRet = m_oStateThreadInitDoneEvent.ResetEvent();
            m_pDnsStateThread = new CDnsStateThread;
            bRet = m_pDnsStateThread->CreateThread();
            if (!bRet)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_WS2INTERCEPTDLL,
                    "%!FUNC! line:%!LINE! %!LEVEL! Exit (error on create State Thread)"
                    );
            }
            else
            {
                HANDLE  haWaitHandles [] = {
                    m_oStateThreadInitDoneEvent,
                    *m_pDnsStateThread
                };
                DWORD   dwWaitState;
                dwWaitState = WaitForMultipleObjects(
                    sizeof(haWaitHandles) / sizeof(HANDLE),
                    haWaitHandles,
                    FALSE,
                    INFINITE
                    );
                if (WAIT_OBJECT_0 + 1 == dwWaitState)
                {
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        TRACE_WS2INTERCEPTDLL,
                        "%!FUNC! line:%!LINE! %!LEVEL! State Thread terminated unexpected with error %!WINERROR!",
                        m_pDnsStateThread->m_nRetCode
                        );
                    delete m_pDnsStateThread;
                    m_pDnsStateThread = NULL;
                }
            }
        }
    }
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
    SleepEx(0, TRUE);
    return hRet;
}


void WINAPI CWS2InterceptAPP::sf_SvcHandler(DWORD fdwControl)
{
    theApp.mf_SvcHandler(fdwControl);
}


void CWS2InterceptAPP::mf_SvcHandler(DWORD fdwControl)
{
    BOOL    bRet;
    DWORD   dwWaitCode;
    if ((SERVICE_CONTROL_STOP == fdwControl) || (SERVICE_CONTROL_SHUTDOWN == fdwControl))
    {
        bRet = m_oMainThreadLock.Lock();
        if (NULL != m_pDnsStateThread)
        {
            HANDLE  hThreadHandle = *m_pDnsStateThread;
            bRet = m_pDnsStateThread->PostThreadMessageW(
                WM_QUIT,
                0,
                0
                );
            bRet = m_oMainThreadLock.Unlock();
            dwWaitCode = WaitForSingleObject(
                hThreadHandle,
                INFINITE
                );
            bRet = m_oDnsSvcStopedEvent.SetEvent();
            m_pfSavedSvcHandleFunction(fdwControl);
            return;
        }
        bRet = m_oMainThreadLock.Unlock();
    }
    m_pfSavedSvcHandleFunction(fdwControl);
}


NTSTATUS CWS2InterceptAPP::UpdateImports(
    PCSTR szCalledModuleName,
    PCSTR szFunctionName,
    PBYTE pNewEntryPoint,
    PBYTE& rOldEntryPoint
    )
{
    PLDR_DATA_TABLE_ENTRY   pResult, pFindEntry;
    REAL_IMPORT_DESCRIPTOR  stImportDescriptor;
    PWSTR       pwsCurrentModuleName;
    bool        bResult;
    NTSTATUS    nNtStatus = NOERROR;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    rOldEntryPoint = NULL;
    pResult = PLDR_DATA_TABLE_ENTRY(m_oModule32HLP.m_pModuleListTable->InLoadOrderModuleList.Flink);
    pFindEntry = m_oModule32HLP.FindModuleByAddress(DWORD_PTR(pResult->DllBase));
    if (pFindEntry != pResult)
    {
        nNtStatus = -1;
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (pFindEntry != pResult)"
            );
        return nNtStatus;
    }
    pwsCurrentModuleName = m_oModule32HLP.GetModuleNameW();
    bResult = m_oModule32HLP.EnumImportStart(pwsCurrentModuleName);
    if (!bResult)
    {
        return nNtStatus;
    }
    RtlZeroMemory(
        &stImportDescriptor,
        sizeof(stImportDescriptor)
        );
    while (m_oModule32HLP.EnumImportNext(stImportDescriptor))
    {
        if (0 != _stricmp(stImportDescriptor.szCalledModuleName, szCalledModuleName))
        {
            continue;
        }
        if (0 != strcmp(stImportDescriptor.szFunctionName, szFunctionName))
        {
            continue;
        }
        rOldEntryPoint = m_oModule32HLP.m_pImportThunkArray[stImportDescriptor.dwThunkIndex];
        nNtStatus = NTWriteProcessMemory(
            NtCurrentProcess(),
            &m_oModule32HLP.m_pImportThunkArray[stImportDescriptor.dwThunkIndex],
            &pNewEntryPoint,
            sizeof(pNewEntryPoint),
            NULL
            );
        if (!NT_SUCCESS(nNtStatus))
        {
            return nNtStatus;
        }
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Exchanged addresses for function \"%s\"",
            stImportDescriptor.szFunctionName
            );
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL!     szCalledModuleName = \"%s\"",
            stImportDescriptor.szCalledModuleName
            );
        break;
    }
    m_oModule32HLP.EnumImportStop();
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
    return nNtStatus;
}


NTSTATUS CWS2InterceptAPP::UpdateImports(PCSTR szFunctionName, PBYTE pNewEntryPoint, PBYTE& rOldEntryPoint)
{
    PLDR_DATA_TABLE_ENTRY   pResult, pFindEntry;
    REAL_IMPORT_DESCRIPTOR  stImportDescriptor;
    PWSTR       pwsCurrentModuleName;
    bool        bResult;
    NTSTATUS    nNtStatus = NOERROR;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    rOldEntryPoint = NULL;
    pResult = PLDR_DATA_TABLE_ENTRY(m_oModule32HLP.m_pModuleListTable->InLoadOrderModuleList.Flink);
    pFindEntry = m_oModule32HLP.FindModuleByAddress(DWORD_PTR(pResult->DllBase));
    if (pFindEntry != pResult)
    {
        nNtStatus = -1;
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (pFindEntry != pResult)"
            );
        return nNtStatus;
    }
    pwsCurrentModuleName = m_oModule32HLP.GetModuleNameW();
    bResult = m_oModule32HLP.EnumImportStart(pwsCurrentModuleName);
    if (!bResult)
    {
        return nNtStatus;
    }
    RtlZeroMemory(
        &stImportDescriptor,
        sizeof(stImportDescriptor)
        );
    while (m_oModule32HLP.EnumImportNext(stImportDescriptor))
    {
        if (NULL == stImportDescriptor.szFunctionName)
        {
            continue;
        }
        if (0 != strcmp(stImportDescriptor.szFunctionName, szFunctionName))
        {
            continue;
        }
        rOldEntryPoint = m_oModule32HLP.m_pImportThunkArray[stImportDescriptor.dwThunkIndex];
        nNtStatus = NTWriteProcessMemory(
            NtCurrentProcess(),
            &m_oModule32HLP.m_pImportThunkArray[stImportDescriptor.dwThunkIndex],
            &pNewEntryPoint,
            sizeof(pNewEntryPoint),
            NULL
            );
        if (!NT_SUCCESS(nNtStatus))
        {
            return nNtStatus;
        }
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL! Exchanged addresses for function \"%s\"",
            stImportDescriptor.szFunctionName
            );
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_WS2INTERCEPTDLL,
            "%!FUNC! line:%!LINE! %!LEVEL!     szCalledModuleName = \"%s\"",
            stImportDescriptor.szCalledModuleName
            );
        break;
    }
    m_oModule32HLP.EnumImportStop();
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_WS2INTERCEPTDLL,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
    return nNtStatus;
}
