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] = { \0 };
        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\n", ::GetLastError());
        return FALSE;
    }

    if ((m_sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
    {
        printf("socket(...) err : %d\n", ::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\n", ::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\n", ::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 "\x03www\x05baidu\x03com"
 * 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 "\x03www\x05baidu\x03com\x00" to "www.baidu.com"
 * 0x0000 03 77 77 77 05 62 61 69 64 75 03 63 6f 6d 00 ff
 * convert "\x03www\x05baidu\xc0\x13" 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] = \0;
    *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):\n", szDomainName);
    if (!bRet)
    {
        printf("timeout!\n");
        return -1;
    }

    for (int i = 0; i != veculIPList.size(); ++i)
    {
        printf("IP%d(ULONG) = %u\n", i + 1, veculIPList[i]);
    }
    for (int i = 0; i != vecstrIPList.size(); ++i)
    {
        printf("IP%d(string) = %s\n", i + 1, vecstrIPList[i].c_str());
    }
    for (int i = 0; i != vecCNameList.size(); ++i)
    {
        printf("CName%d = %s\n", i + 1, vecCNameList[i].c_str());
    }
    printf("time spent = %ums\n", 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\n", ::GetLastError());
        return FALSE;
    }

    if ((m_sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
    {
        printf("socket(...) err : %d\n", ::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\n", ::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\n", ::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 "\x03www\x05baidu\x03com"
 * 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 "\x03www\x05baidu\x03com\x00" to "www.baidu.com"
 * 0x0000 03 77 77 77 05 62 61 69 64 75 03 63 6f 6d 00 ff
 * convert "\x03www\x05baidu\xc0\x13" 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] = \0;
    *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\n", inet_ntoa(*pAddr));
    

    return 0;
}

 

3、

4、

5、

 

VC.DNS解析(winsock)

上一篇:什么是Maven? 使用Apache Maven构建和依赖项管理


下一篇:华人开创NTP网络授时服务器成功投运世界级超大工程港珠澳大桥