zoukankan      html  css  js  c++  java
  • VC.DNS解析(winsock)

    1、尝试了 gethostbyname(...) 和 Qt598中的qhostinfo::fromname(...),都可以解析出来IP,但是有一个问题:域名指向的IP改变了,这两种方式需要等较长时间 才会解析到更新后的ip地址。而CMD中 nslookup却一下子就解析到了更新后的IP...

     1.1、查了一下资料,貌似原因如下(大概的说下原理,可能不严谨准确):

      (1)域名服务器在 域名的TTL时间段内 它不会去实时的查找域名对应的IP,而是要等到TTL超时 域名服务器才回去再次查询 然后更新新的地址。

      (2)又∵ gethostbyname 和 qhostinfo::fromname 都没有参数可以输入 DNS服务器地址,猜测可能使用的是 OS中设置的DNS服务器。

      ZC:综上2个原因 造成了这个现象

      ZC:改 OS的DNS设置的话,如果 这个DNS服务器不是 常用上网的DNS服务器的话,到时候还要改回来,比较麻烦,暂不考虑使用

     1.2、于是猜测,指定 DNS解析服务器的话,就能解决这个现象

    2、C++实现DNS域名解析 - Snser - 博客园.html(https://www.cnblogs.com/snser/p/4101729.html

      ZC:DNS解析的 数据包结构、原理、代码 都在这个文章中查看

      ZC:只解析IPv4地址的精简版代码在 2.2

     2.1、这是文章中的代码(少许改动,运行OK)(项目->属性->C/C++->预处理器->"预处理器定义"中添加 "_ITERATOR_DEBUG_LEVEL=0"

      (1)CDNSLookup.h

    #pragma once
    
    // C++实现DNS域名解析 - Snser - 博客园.html (https://www.cnblogs.com/snser/p/4101729.html)
    
    #define _CRT_SECURE_NO_WARNINGS
    
    //这里需要导入库 Ws2_32.lib,在不同的IDE下可能不太一样
    //#pragma comment(lib, "Ws2_32.lib")
    
    #include <winsock2.h>
    #pragma comment(lib, "Ws2_32.lib")
    
    //#include <windows.h>
    #include <string>
    #include <vector>
    
    
    
    
    #define MAX_DOMAINNAME_LEN  255
    #define DNS_PORT            53
    #define DNS_TYPE_SIZE       2
    #define DNS_CLASS_SIZE      2
    #define DNS_TTL_SIZE        4
    #define DNS_DATALEN_SIZE    2
    #define DNS_TYPE_A          0x0001 //1 a host address
    #define DNS_TYPE_CNAME      0x0005 //5 the canonical name for an alias
    #define DNS_PACKET_MAX_SIZE (sizeof(DNSHeader) + MAX_DOMAINNAME_LEN + DNS_TYPE_SIZE + DNS_CLASS_SIZE)
    
    struct DNSHeader
    {
        USHORT usTransID; //标识符
        USHORT usFlags; //各种标志位
        USHORT usQuestionCount; //Question字段个数 
        USHORT usAnswerCount; //Answer字段个数
        USHORT usAuthorityCount; //Authority字段个数
        USHORT usAdditionalCount; //Additional字段个数
    };
    
    class CDNSLookup
    {
    public:
        CDNSLookup();
        ~CDNSLookup();
    
        BOOL DNSLookup(ULONG ulDNSServerIP, char *szDomainName,
            std::vector<ULONG> *pveculIPList = NULL, std::vector<std::string> *pvecstrCNameList = NULL,
            ULONG ulTimeout = 1000, ULONG *pulTimeSpent = NULL);
        BOOL DNSLookup(ULONG ulDNSServerIP, char *szDomainName,
            std::vector<std::string> *pvecstrIPList = NULL, std::vector<std::string>
            *pvecstrCNameList = NULL, ULONG ulTimeout = 1000, ULONG *pulTimeSpent = NULL);
    
    private:
        BOOL Init();
        BOOL UnInit();
    
        BOOL DNSLookupCore(ULONG ulDNSServerIP, char *szDomainName, std::vector<ULONG> *pveculIPList, std::vector<std::string> *pvecstrCNameList, ULONG ulTimeout, ULONG *pulTimeSpent);
        BOOL SendDNSRequest(sockaddr_in sockAddrDNSServer, char *szDomainName);
        BOOL RecvDNSResponse(sockaddr_in sockAddrDNSServer, ULONG ulTimeout,
            std::vector<ULONG> *pveculIPList, std::vector<std::string> *pvecstrCNameList, ULONG *pulTimeSpent);
    
        BOOL EncodeDotStr(char *szDotStr, char *szEncodedStr, USHORT nEncodedStrSize);
        BOOL DecodeDotStr(char *szEncodedStr, USHORT *pusEncodedStrLen, char *szDotStr, USHORT nDotStrSize, char *szPacketStartPos = NULL);
    
    private:
        BOOL m_bIsInitOK;
        SOCKET m_sock;
    };

      (2)CDNSLookup.cpp

    #include "CDNSLookup.h"
    #include <stdio.h>
    #include <string.h>
    
    
    
    CDNSLookup::CDNSLookup() :
        m_bIsInitOK(FALSE),
        m_sock(INVALID_SOCKET)
    {
        m_bIsInitOK = Init();
    }
    
    CDNSLookup::~CDNSLookup()
    {
        UnInit();
    }
    
    BOOL CDNSLookup::DNSLookup(ULONG ulDNSServerIP, char *szDomainName,
        std::vector<ULONG> *pveculIPList, std::vector<std::string> *pvecstrCNameList,
        ULONG ulTimeout, ULONG *pulTimeSpent)
    {
        return DNSLookupCore(ulDNSServerIP, szDomainName, pveculIPList, pvecstrCNameList, ulTimeout, pulTimeSpent);
    }
    
    BOOL CDNSLookup::DNSLookup(ULONG ulDNSServerIP, char *szDomainName,
        std::vector<std::string> *pvecstrIPList, std::vector<std::string> *pvecstrCNameList,
        ULONG ulTimeout, ULONG *pulTimeSpent)
    {
        std::vector<ULONG> *pveculIPList = NULL;
        if (pvecstrIPList != NULL)
        {
            std::vector<ULONG> veculIPList;
            pveculIPList = &veculIPList;
        }
    
        BOOL bRet = DNSLookupCore(ulDNSServerIP, szDomainName, pveculIPList, pvecstrCNameList, ulTimeout, pulTimeSpent);
    
        if (bRet)
        {
            pvecstrIPList->clear();
            char szIP[16] = { '' };
            for (std::vector<ULONG>::iterator iter = pveculIPList->begin(); iter != pveculIPList->end(); ++iter)
            {
                ULONG ul = (*iter);
                BYTE *pbyIPSegment = (BYTE*)(&ul);
                //BYTE *pbyIPSegment = (BYTE*)(&(*iter));
                //sprintf_s(szIP, 16, "%d.%d.%d.%d", pbyIPSegment[0], pbyIPSegment[1], pbyIPSegment[2], pbyIPSegment[3]);
                sprintf(szIP, "%d.%d.%d.%d", pbyIPSegment[0], pbyIPSegment[1], pbyIPSegment[2], pbyIPSegment[3]);
                pvecstrIPList->push_back(szIP);
            }
        }
    
        return bRet;
    }
    
    
    BOOL CDNSLookup::Init()
    {
        WSADATA wsaData;
        if (WSAStartup(MAKEWORD(2, 2), &wsaData) == SOCKET_ERROR)
        {
            printf("WSAStartup(...) err : %d
    ", ::GetLastError());
            return FALSE;
        }
    
        if ((m_sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
        {
            printf("socket(...) err : %d
    ", ::GetLastError());
            return FALSE;
        }
    
        DWORD dwTimeOut = 1000 * 5;
        if (setsockopt(m_sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&dwTimeOut, sizeof(dwTimeOut)) == SOCKET_ERROR)
        {
            printf("setsockopt(...) err : %d
    ", ::GetLastError());
            return FALSE;
        }
    
        return TRUE;
    }
    
    BOOL CDNSLookup::UnInit()
    {
        WSACleanup();
    
        return TRUE;
    }
    
    BOOL CDNSLookup::DNSLookupCore(ULONG ulDNSServerIP, char *szDomainName,
        std::vector<ULONG> *pveculIPList, std::vector<std::string> *pvecstrCNameList, ULONG ulTimeout, ULONG *pulTimeSpent)
    {
        if (m_bIsInitOK == FALSE || szDomainName == NULL)
        {
            return FALSE;
        }
    
        //配置SOCKET
        sockaddr_in addrDnsServer;
        addrDnsServer.sin_family = AF_INET;
        addrDnsServer.sin_addr.s_addr = ulDNSServerIP;
        addrDnsServer.sin_port = htons(DNS_PORT);
    
        //DNS查询与解析
        if (!SendDNSRequest(addrDnsServer, szDomainName))
        {
            return FALSE;
        }
    
        if (!RecvDNSResponse(addrDnsServer, ulTimeout, pveculIPList, pvecstrCNameList, pulTimeSpent))
        {
            return FALSE;
        }
    
        return TRUE;
    }
    
    BOOL CDNSLookup::SendDNSRequest(sockaddr_in _addrDnsServer, char *_szDomainName)
    {
        char bufSend[DNS_PACKET_MAX_SIZE] = {0};
        char* pcSend = &bufSend[0];
    
        //填充DNS查询报文头部
        DNSHeader *pDnsHdr = (DNSHeader*)pcSend;
        pDnsHdr->usTransID = (USHORT)GetCurrentProcessId();
        pDnsHdr->usFlags = htons(0x0100);
        pDnsHdr->usQuestionCount = htons(0x0001);
        pDnsHdr->usAnswerCount = 0x0000;
        pDnsHdr->usAuthorityCount = 0x0000;
        pDnsHdr->usAdditionalCount = 0x0000;
    
        //设置DNS查询报文内容
        USHORT usQType = htons(0x0001);
        USHORT usQClass = htons(0x0001);
        USHORT nDomainNameLen = strlen(_szDomainName);
        char *szEncodedDomainName = (char *)malloc(nDomainNameLen + 2);
        if (szEncodedDomainName == NULL)
        {
            return FALSE;
        }
        if (!EncodeDotStr(_szDomainName, szEncodedDomainName, nDomainNameLen + 2))
        {
            return FALSE;
        }
    
        //填充DNS查询报文内容
        USHORT nEncodedDomainNameLen = strlen(szEncodedDomainName) + 1;
        memcpy(pcSend += sizeof(DNSHeader), szEncodedDomainName, nEncodedDomainNameLen);
        memcpy(pcSend += nEncodedDomainNameLen, (char*)(&usQType), DNS_TYPE_SIZE);
        memcpy(pcSend += DNS_TYPE_SIZE, (char*)(&usQClass), DNS_CLASS_SIZE);
        free(szEncodedDomainName);
    
        //发送DNS查询报文
        USHORT nDNSPacketSize = sizeof(DNSHeader) + nEncodedDomainNameLen + DNS_TYPE_SIZE + DNS_CLASS_SIZE;
        if (sendto(m_sock, bufSend, nDNSPacketSize, 0, (sockaddr*)&_addrDnsServer, sizeof(_addrDnsServer)) == SOCKET_ERROR)
        {
            return FALSE;
        }
    
        return TRUE;
    }
    
    BOOL CDNSLookup::RecvDNSResponse(sockaddr_in _addrDnsServer, ULONG ulTimeout,
        std::vector<ULONG> *pveculIPList, std::vector<std::string> *pvecstrCNameList, ULONG *pulTimeSpent)
    {
        if (pveculIPList != NULL)
            pveculIPList->clear();
        if (pvecstrCNameList != NULL)
            pvecstrCNameList->clear();
    
        char bufRecv[1024] = { 0 };
        char szDotName[128] = { 0 };
        USHORT nEncodedNameLen = 0;
        int iAddrDnsServer = sizeof(_addrDnsServer);
    
        DWORD dw1 = ::GetTickCount();
    
    
        //接收响应报文
        if (recvfrom(m_sock, bufRecv, sizeof(bufRecv), 0, (struct sockaddr*)&_addrDnsServer, &iAddrDnsServer) == SOCKET_ERROR)
        {
            //WSAETIMEDOUT
            printf("DNS recvfrom(...) failed : %d
    ", ::GetLastError());
            return FALSE;
        }
    
        DNSHeader *pDnsHdr = (DNSHeader*)bufRecv;
        USHORT usQuestionCount = 0;
        USHORT usAnswerCount = 0;
    
        if (pDnsHdr->usTransID == (USHORT)GetCurrentProcessId()
            && (ntohs(pDnsHdr->usFlags) & 0xfb7f) == 0x8100 //RFC1035 4.1.1(Header section format)
            && (usQuestionCount = ntohs(pDnsHdr->usQuestionCount)) >= 0
            && (usAnswerCount = ntohs(pDnsHdr->usAnswerCount)) > 0)
        {
            char *pDNSData = bufRecv + sizeof(DNSHeader);
    
            //解析Question字段
            for (int q = 0; q != usQuestionCount; ++q)
            {
                if (!DecodeDotStr(pDNSData, &nEncodedNameLen, szDotName, sizeof(szDotName)))
                {
                    return FALSE;
                }
                pDNSData += (nEncodedNameLen + DNS_TYPE_SIZE + DNS_CLASS_SIZE);
            }
    
            //解析Answer字段
            for (int a = 0; a != usAnswerCount; ++a)
            {
                if (!DecodeDotStr(pDNSData, &nEncodedNameLen, szDotName, sizeof(szDotName), bufRecv))
                {
                    return FALSE;
                }
                pDNSData += nEncodedNameLen;
    
                USHORT usAnswerType = ntohs(*(USHORT*)(pDNSData));
                USHORT usAnswerClass = ntohs(*(USHORT*)(pDNSData + DNS_TYPE_SIZE));
                ULONG usAnswerTTL = ntohl(*(ULONG*)(pDNSData + DNS_TYPE_SIZE + DNS_CLASS_SIZE));
                USHORT usAnswerDataLen = ntohs(*(USHORT*)(pDNSData + DNS_TYPE_SIZE + DNS_CLASS_SIZE + DNS_TTL_SIZE));
                pDNSData += (DNS_TYPE_SIZE + DNS_CLASS_SIZE + DNS_TTL_SIZE + DNS_DATALEN_SIZE);
    
                if (usAnswerType == DNS_TYPE_A && pveculIPList != NULL)
                {
                    ULONG ulIP = *(ULONG*)(pDNSData);
                    pveculIPList->push_back(ulIP);
                }
                else if (usAnswerType == DNS_TYPE_CNAME && pvecstrCNameList != NULL)
                {
                    if (!DecodeDotStr(pDNSData, &nEncodedNameLen, szDotName, sizeof(szDotName), bufRecv))
                    {
                        return FALSE;
                    }
                    pvecstrCNameList->push_back(szDotName);
                }
    
                pDNSData += (usAnswerDataLen);
            }
    
        }
    
        DWORD dw2 = ::GetTickCount();
        if (pulTimeSpent != NULL)
            *pulTimeSpent = dw2 - dw1;
    
        return TRUE;
    }
    
    /*
     * convert "www.baidu.com" to "x03wwwx05baidux03com"
     * 0x0000 03 77 77 77 05 62 61 69 64 75 03 63 6f 6d 00 ff
     */
    BOOL CDNSLookup::EncodeDotStr(char *szDotStr, char *szEncodedStr, USHORT nEncodedStrSize)
    {
        USHORT nDotStrLen = strlen(szDotStr);
    
        if (szDotStr == NULL || szEncodedStr == NULL || nEncodedStrSize < nDotStrLen + 2)
        {
            return FALSE;
        }
    
        char *szDotStrCopy = new char[nDotStrLen + 1];
        //strcpy_s(szDotStrCopy, nDotStrLen + 1, szDotStr);
        strcpy(szDotStrCopy, szDotStr);
    
        char *pNextToken = NULL;
        //char *pLabel = strtok_s(szDotStrCopy, ".", &pNextToken);
        char *pLabel = strtok(szDotStrCopy, ".");
        USHORT nLabelLen = 0;
        USHORT nEncodedStrLen = 0;
        while (pLabel != NULL)
        {
            if ((nLabelLen = strlen(pLabel)) != 0)
            {
                //sprintf_s(szEncodedStr + nEncodedStrLen, nEncodedStrSize - nEncodedStrLen, "%c%s", nLabelLen, pLabel);
                sprintf(szEncodedStr + nEncodedStrLen, "%c%s", nLabelLen, pLabel);
                nEncodedStrLen += (nLabelLen + 1);
            }
            //pLabel = strtok_s(NULL, ".", &pNextToken);
            pLabel = strtok(NULL, ".");
        }
    
        delete[] szDotStrCopy;
    
        return TRUE;
    }
    
    /*
     * convert "x03wwwx05baidux03comx00" to "www.baidu.com"
     * 0x0000 03 77 77 77 05 62 61 69 64 75 03 63 6f 6d 00 ff
     * convert "x03wwwx05baiduxc0x13" to "www.baidu.com"
     * 0x0000 03 77 77 77 05 62 61 69 64 75 c0 13 ff ff ff ff
     * 0x0010 ff ff ff 03 63 6f 6d 00 ff ff ff ff ff ff ff ff
     */
    BOOL CDNSLookup::DecodeDotStr(char *szEncodedStr, USHORT *pusEncodedStrLen, char *szDotStr, USHORT nDotStrSize, char *szPacketStartPos)
    {
        if (szEncodedStr == NULL || pusEncodedStrLen == NULL || szDotStr == NULL)
        {
            return FALSE;
        }
    
        char *pDecodePos = szEncodedStr;
        USHORT usPlainStrLen = 0;
        BYTE nLabelDataLen = 0;
        *pusEncodedStrLen = 0;
    
        while ((nLabelDataLen = *pDecodePos) != 0x00)
        {
            if ((nLabelDataLen & 0xc0) == 0) //普通格式,LabelDataLen + Label
            {
                if (usPlainStrLen + nLabelDataLen + 1 > nDotStrSize)
                {
                    return FALSE;
                }
                memcpy(szDotStr + usPlainStrLen, pDecodePos + 1, nLabelDataLen);
                memcpy(szDotStr + usPlainStrLen + nLabelDataLen, ".", 1);
                pDecodePos += (nLabelDataLen + 1);
                usPlainStrLen += (nLabelDataLen + 1);
                *pusEncodedStrLen += (nLabelDataLen + 1);
            }
            else //消息压缩格式,11000000 00000000,两个字节,前2位为跳转标志,后14位为跳转的偏移
            {
                if (szPacketStartPos == NULL)
                {
                    return FALSE;
                }
                USHORT usJumpPos = ntohs(*(USHORT*)(pDecodePos)) & 0x3fff;
                USHORT nEncodeStrLen = 0;
                if (!DecodeDotStr(szPacketStartPos + usJumpPos, &nEncodeStrLen, szDotStr + usPlainStrLen, nDotStrSize - usPlainStrLen, szPacketStartPos))
                {
                    return FALSE;
                }
                else
                {
                    *pusEncodedStrLen += 2;
                    return TRUE;
                }
            }
        }
    
        szDotStr[usPlainStrLen - 1] = '';
        *pusEncodedStrLen += 1;
    
        return TRUE;
    }

      (3)main.cpp

    #include <iostream>//#define _CRT_SECURE_NO_WARNINGS
    #define _WINSOCK_DEPRECATED_NO_WARNINGS
    
    
    #include <stdio.h>
    #include "CDNSLookup.h"
    
    int main(void)
    {
        //char szDomainName[] = "www.baidu.com";
        char szDomainName[] = "hopeex.com";
        std::vector<ULONG> veculIPList;
        std::vector<std::string> vecstrIPList;
        std::vector<std::string> vecCNameList;
        ULONG ulTimeSpent = 0;
        CDNSLookup dnslookup;
        BOOL bRet = dnslookup.DNSLookup(inet_addr("114.114.114.114"), szDomainName, &vecstrIPList, &vecCNameList, 1000, &ulTimeSpent);
        //BOOL bRet = dnslookup.DNSLookup(inet_addr("221.228.255.1"), szDomainName, &vecstrIPList, &vecCNameList, 1000, &ulTimeSpent);
    
        printf("DNSLookup result (%s):
    ", szDomainName);
        if (!bRet)
        {
            printf("timeout!
    ");
            return -1;
        }
    
        for (int i = 0; i != veculIPList.size(); ++i)
        {
            printf("IP%d(ULONG) = %u
    ", i + 1, veculIPList[i]);
        }
        for (int i = 0; i != vecstrIPList.size(); ++i)
        {
            printf("IP%d(string) = %s
    ", i + 1, vecstrIPList[i].c_str());
        }
        for (int i = 0; i != vecCNameList.size(); ++i)
        {
            printf("CName%d = %s
    ", i + 1, vecCNameList[i].c_str());
        }
        printf("time spent = %ums
    ", ulTimeSpent);
    
        return 0;
    }

     2.2、只解析IPv4地址的精简版代码

      (1)dms_z.h

    #pragma once
    
    // C++实现DNS域名解析 - Snser - 博客园.html (https://www.cnblogs.com/snser/p/4101729.html)
    
    #define _CRT_SECURE_NO_WARNINGS
    #define _WINSOCK_DEPRECATED_NO_WARNINGS
    
    //这里需要导入库 Ws2_32.lib,在不同的IDE下可能不太一样
    //#pragma comment(lib, "Ws2_32.lib")
    
    #include <winsock2.h>
    #pragma comment(lib, "Ws2_32.lib")
    
    //#include <windows.h>
    #include <string>
    #include <vector>
    
    
    #define MAX_DOMAINNAME_LEN  255
    #define DNS_PORT            53
    #define DNS_TYPE_SIZE       2
    #define DNS_CLASS_SIZE      2
    #define DNS_TTL_SIZE        4
    #define DNS_DATALEN_SIZE    2
    #define DNS_TYPE_A          0x0001 //1 a host address
    #define DNS_TYPE_CNAME      0x0005 //5 the canonical name for an alias
    #define DNS_PACKET_MAX_SIZE (sizeof(DnsHeader) + MAX_DOMAINNAME_LEN + DNS_TYPE_SIZE + DNS_CLASS_SIZE)
    
    struct DnsHeader
    {
        USHORT usTransID; //标识符
        USHORT usFlags; //各种标志位
        USHORT usQuestionCount; //Question字段个数 
        USHORT usAnswerCount; //Answer字段个数
        USHORT usAuthorityCount; //Authority字段个数
        USHORT usAdditionalCount; //Additional字段个数
    };
    
    class DnsLookup
    {
    public:
        DnsLookup();
        ~DnsLookup();
    
        ULONG LookupIPv4(const char* pcDnsServerIP, char *szDomainName,
            ULONG ulTimeout = 1000 * 5);
    
    private:
        BOOL Init();
        BOOL UnInit();
    
        BOOL LookupCore(ULONG ulDNSServerIP, char *szDomainName, 
            std::vector<ULONG> *pveculIP, ULONG ulTimeOut, ULONG *pulTimeSpent);
    
        BOOL SendDNSRequest(sockaddr_in sockAddrDNSServer, char *szDomainName);
        BOOL RecvDNSResponse(sockaddr_in sockAddrDNSServer,
            std::vector<ULONG> *pveculIP, ULONG ulTimeOut, ULONG *pulTimeSpent);
    
        BOOL EncodeDotStr(char *szDotStr, char *szEncodedStr, USHORT nEncodedStrSize);
        BOOL DecodeDotStr(char *szEncodedStr, USHORT *pusEncodedStrLen, char *szDotStr, USHORT nDotStrSize, char *szPacketStartPos = NULL);
    
    private:
        BOOL m_bIsInitOK;
        SOCKET m_sock;
    };

      (2)dns_z.cpp

    #include "dns_z.h"
    #include <stdio.h>
    #include <string.h>
    
    
    
    DnsLookup::DnsLookup() :
        m_bIsInitOK(FALSE),
        m_sock(INVALID_SOCKET)
    {
        m_bIsInitOK = Init();
    }
    
    DnsLookup::~DnsLookup()
    {
        UnInit();
    }
    
    ULONG DnsLookup::LookupIPv4(const char* pcDnsServerIP, char *szDomainName,
        ULONG ulTimeout)
    {
        std::vector<ULONG> veculIP;
        ULONG ulTimeSpent;
        if (LookupCore(inet_addr(pcDnsServerIP), szDomainName, &veculIP, ulTimeout, &ulTimeSpent))
        {
            if (veculIP.size() > 0)
                return veculIP.front();
        }
        return 0;
    }
    
    
    BOOL DnsLookup::Init()
    {
        WSADATA wsaData;
        if (WSAStartup(MAKEWORD(2, 2), &wsaData) == SOCKET_ERROR)
        {
            printf("WSAStartup(...) err : %d
    ", ::GetLastError());
            return FALSE;
        }
    
        if ((m_sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
        {
            printf("socket(...) err : %d
    ", ::GetLastError());
            return FALSE;
        }
    
        return TRUE;
    }
    
    BOOL DnsLookup::UnInit()
    {
        WSACleanup();
    
        return TRUE;
    }
    
    BOOL DnsLookup::LookupCore(ULONG ulDNSServerIP, char *szDomainName,
        std::vector<ULONG> *pveculIP, ULONG ulTimeOut, ULONG *pulTimeSpent)
    {
        if (m_bIsInitOK == FALSE || szDomainName == NULL)
            return FALSE;
    
        //配置SOCKET
        sockaddr_in addrDnsServer;
        addrDnsServer.sin_family = AF_INET;
        addrDnsServer.sin_addr.s_addr = ulDNSServerIP;
        addrDnsServer.sin_port = htons(DNS_PORT);
    
        //DNS查询与解析
        if (!SendDNSRequest(addrDnsServer, szDomainName))
            return FALSE;
        if (!RecvDNSResponse(addrDnsServer, pveculIP, ulTimeOut, pulTimeSpent))
            return FALSE;
    
        return TRUE;
    }
    
    BOOL DnsLookup::SendDNSRequest(sockaddr_in _addrDnsServer, char *_szDomainName)
    {
        char bufSend[DNS_PACKET_MAX_SIZE] = { 0 };
        char* pcSend = &bufSend[0];
    
        //填充DNS查询报文头部
        DnsHeader *pDnsHdr = (DnsHeader*)pcSend;
        pDnsHdr->usTransID = (USHORT)GetCurrentProcessId();
        pDnsHdr->usFlags = htons(0x0100);
        pDnsHdr->usQuestionCount = htons(0x0001);
        pDnsHdr->usAnswerCount = 0x0000;
        pDnsHdr->usAuthorityCount = 0x0000;
        pDnsHdr->usAdditionalCount = 0x0000;
    
        //设置DNS查询报文内容
        USHORT usQType = htons(0x0001);
        USHORT usQClass = htons(0x0001);
        USHORT nDomainNameLen = strlen(_szDomainName);
        char *szEncodedDomainName = (char *)malloc(nDomainNameLen + 2);
        if (szEncodedDomainName == NULL)
        {
            return FALSE;
        }
        if (!EncodeDotStr(_szDomainName, szEncodedDomainName, nDomainNameLen + 2))
        {
            return FALSE;
        }
    
        //填充DNS查询报文内容
        USHORT nEncodedDomainNameLen = strlen(szEncodedDomainName) + 1;
        memcpy(pcSend += sizeof(DnsHeader), szEncodedDomainName, nEncodedDomainNameLen);
        memcpy(pcSend += nEncodedDomainNameLen, (char*)(&usQType), DNS_TYPE_SIZE);
        memcpy(pcSend += DNS_TYPE_SIZE, (char*)(&usQClass), DNS_CLASS_SIZE);
        free(szEncodedDomainName);
    
        //发送DNS查询报文
        USHORT nDNSPacketSize = sizeof(DnsHeader) + nEncodedDomainNameLen + DNS_TYPE_SIZE + DNS_CLASS_SIZE;
        if (sendto(m_sock, bufSend, nDNSPacketSize, 0, (sockaddr*)&_addrDnsServer, sizeof(_addrDnsServer)) == SOCKET_ERROR)
        {
            return FALSE;
        }
    
        return TRUE;
    }
    
    BOOL DnsLookup::RecvDNSResponse(sockaddr_in _addrDnsServer,
        std::vector<ULONG> *_pveculIP, ULONG _ulTimeOut, ULONG *_pulTimeSpent)
    {
        if (_pveculIP != NULL)
            _pveculIP->clear();
    
        if (setsockopt(m_sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&_ulTimeOut, sizeof(_ulTimeOut)) == SOCKET_ERROR)
        {
            printf("setsockopt(...) err : %d
    ", ::GetLastError());
            return FALSE;
        }
    
        char bufRecv[1024] = { 0 };
        char szDotName[128] = { 0 };
        USHORT nEncodedNameLen = 0;
        int iAddrDnsServer = sizeof(_addrDnsServer);
    
        DWORD dw1 = ::GetTickCount();
    
        //接收响应报文
        if (recvfrom(m_sock, bufRecv, sizeof(bufRecv), 0, (struct sockaddr*)&_addrDnsServer, &iAddrDnsServer) == SOCKET_ERROR)
        {
            //WSAETIMEDOUT
            printf("DNS recvfrom(...) failed : %d
    ", ::GetLastError());
            return FALSE;
        }
    
        DnsHeader *pDnsHdr = (DnsHeader*)bufRecv;
        USHORT usQuestionCount = 0;
        USHORT usAnswerCount = 0;
    
        if (pDnsHdr->usTransID == (USHORT)GetCurrentProcessId()
            && (ntohs(pDnsHdr->usFlags) & 0xfb7f) == 0x8100 //RFC1035 4.1.1(Header section format)
            && (usQuestionCount = ntohs(pDnsHdr->usQuestionCount)) >= 0
            && (usAnswerCount = ntohs(pDnsHdr->usAnswerCount)) > 0)
        {
            char *pDnsData = bufRecv + sizeof(DnsHeader);
    
            //解析Question字段
            for (int q = 0; q != usQuestionCount; ++q)
            {
                if (!DecodeDotStr(pDnsData, &nEncodedNameLen, szDotName, sizeof(szDotName)))
                {
                    return FALSE;
                }
                pDnsData += (nEncodedNameLen + DNS_TYPE_SIZE + DNS_CLASS_SIZE);
            }
    
            //解析Answer字段
            for (int a = 0; a != usAnswerCount; ++a)
            {
                if (!DecodeDotStr(pDnsData, &nEncodedNameLen, szDotName, sizeof(szDotName), bufRecv))
                {
                    return FALSE;
                }
                pDnsData += nEncodedNameLen;
    
                USHORT usAnswerType = ntohs(*(USHORT*)(pDnsData));
                USHORT usAnswerClass = ntohs(*(USHORT*)(pDnsData + DNS_TYPE_SIZE));
                ULONG usAnswerTTL = ntohl(*(ULONG*)(pDnsData + DNS_TYPE_SIZE + DNS_CLASS_SIZE));
                USHORT usAnswerDataLen = ntohs(*(USHORT*)(pDnsData + DNS_TYPE_SIZE + DNS_CLASS_SIZE + DNS_TTL_SIZE));
                pDnsData += (DNS_TYPE_SIZE + DNS_CLASS_SIZE + DNS_TTL_SIZE + DNS_DATALEN_SIZE);
    
                if (usAnswerType == DNS_TYPE_A && _pveculIP != NULL)
                {
                    ULONG ulIP = *(ULONG*)(pDnsData);
                    _pveculIP->push_back(ulIP);
                }
    
                pDnsData += (usAnswerDataLen);
            }
    
        }
    
        DWORD dw2 = ::GetTickCount();
        if (_pulTimeSpent != NULL)
            *_pulTimeSpent = dw2 - dw1;
    
        return TRUE;
    }
    
    /*
     * convert "www.baidu.com" to "x03wwwx05baidux03com"
     * 0x0000 03 77 77 77 05 62 61 69 64 75 03 63 6f 6d 00 ff
     */
    BOOL DnsLookup::EncodeDotStr(char *szDotStr, char *szEncodedStr, USHORT nEncodedStrSize)
    {
        USHORT nDotStrLen = strlen(szDotStr);
    
        if (szDotStr == NULL || szEncodedStr == NULL || nEncodedStrSize < nDotStrLen + 2)
        {
            return FALSE;
        }
    
        char *szDotStrCopy = new char[nDotStrLen + 1];
        //strcpy_s(szDotStrCopy, nDotStrLen + 1, szDotStr);
        strcpy(szDotStrCopy, szDotStr);
    
        char *pNextToken = NULL;
        //char *pLabel = strtok_s(szDotStrCopy, ".", &pNextToken);
        char *pLabel = strtok(szDotStrCopy, ".");
        USHORT nLabelLen = 0;
        USHORT nEncodedStrLen = 0;
        while (pLabel != NULL)
        {
            if ((nLabelLen = strlen(pLabel)) != 0)
            {
                //sprintf_s(szEncodedStr + nEncodedStrLen, nEncodedStrSize - nEncodedStrLen, "%c%s", nLabelLen, pLabel);
                sprintf(szEncodedStr + nEncodedStrLen, "%c%s", nLabelLen, pLabel);
                nEncodedStrLen += (nLabelLen + 1);
            }
            //pLabel = strtok_s(NULL, ".", &pNextToken);
            pLabel = strtok(NULL, ".");
        }
    
        delete[] szDotStrCopy;
    
        return TRUE;
    }
    
    /*
     * convert "x03wwwx05baidux03comx00" to "www.baidu.com"
     * 0x0000 03 77 77 77 05 62 61 69 64 75 03 63 6f 6d 00 ff
     * convert "x03wwwx05baiduxc0x13" to "www.baidu.com"
     * 0x0000 03 77 77 77 05 62 61 69 64 75 c0 13 ff ff ff ff
     * 0x0010 ff ff ff 03 63 6f 6d 00 ff ff ff ff ff ff ff ff
     */
    BOOL DnsLookup::DecodeDotStr(char *szEncodedStr, USHORT *pusEncodedStrLen, char *szDotStr, USHORT nDotStrSize, char *szPacketStartPos)
    {
        if (szEncodedStr == NULL || pusEncodedStrLen == NULL || szDotStr == NULL)
        {
            return FALSE;
        }
    
        char *pDecodePos = szEncodedStr;
        USHORT usPlainStrLen = 0;
        BYTE nLabelDataLen = 0;
        *pusEncodedStrLen = 0;
    
        while ((nLabelDataLen = *pDecodePos) != 0x00)
        {
            if ((nLabelDataLen & 0xc0) == 0) //普通格式,LabelDataLen + Label
            {
                if (usPlainStrLen + nLabelDataLen + 1 > nDotStrSize)
                {
                    return FALSE;
                }
                memcpy(szDotStr + usPlainStrLen, pDecodePos + 1, nLabelDataLen);
                memcpy(szDotStr + usPlainStrLen + nLabelDataLen, ".", 1);
                pDecodePos += (nLabelDataLen + 1);
                usPlainStrLen += (nLabelDataLen + 1);
                *pusEncodedStrLen += (nLabelDataLen + 1);
            }
            else //消息压缩格式,11000000 00000000,两个字节,前2位为跳转标志,后14位为跳转的偏移
            {
                if (szPacketStartPos == NULL)
                {
                    return FALSE;
                }
                USHORT usJumpPos = ntohs(*(USHORT*)(pDecodePos)) & 0x3fff;
                USHORT nEncodeStrLen = 0;
                if (!DecodeDotStr(szPacketStartPos + usJumpPos, &nEncodeStrLen, szDotStr + usPlainStrLen, nDotStrSize - usPlainStrLen, szPacketStartPos))
                {
                    return FALSE;
                }
                else
                {
                    *pusEncodedStrLen += 2;
                    return TRUE;
                }
            }
        }
    
        szDotStr[usPlainStrLen - 1] = '';
        *pusEncodedStrLen += 1;
    
        return TRUE;
    }

      (3)main.cpp

    //#define _CRT_SECURE_NO_WARNINGS
    #define _WINSOCK_DEPRECATED_NO_WARNINGS
    
    #include <stdio.h>
    #include "dns_z.h"
    
    int main(void)
    {
        //char szDomainName[] = "www.baidu.com";
        char szDomainName[] = "hopeex.com";
        DnsLookup dnslookup;
        ULONG ul = dnslookup.LookupIPv4("114.114.114.114", szDomainName, 1000);
        //BOOL bRet = dnslookup.DNSLookup(inet_addr("221.228.255.1"), szDomainName, &vecstrIPList, &vecCNameList, 1000, &ulTimeSpent);
    
        in_addr* pAddr = (in_addr*)&ul;
        printf("%s
    ", inet_ntoa(*pAddr));
        
    
        return 0;
    }

    3、

    4、

    5、

  • 相关阅读:
    Mysql里的isnull(),ifnull(),nullif
    懒加载数据
    MyEclipse编辑xml文件没有提示
    java-五子棋游戏源码
    Java版打字练习游戏源码
    Wpf实现图片自动轮播自定义控件
    WP8.1开发:自定义控件
    简单的UIButton按钮动画效果ios源码下载
    自定义的一款选项卡ios源码
    Aisen微博应用源码完整版
  • 原文地址:https://www.cnblogs.com/cppskill/p/11905882.html
Copyright © 2011-2022 走看看