IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> DNS解析 -> 正文阅读

[数据结构与算法]DNS解析

1. 背景

最近遇到DNS解析慢的问题,梳理了一下DNS消息结构,用C++写了一个DNS解析过程。

2. 相关数据结构

2.1. DNS消息基类

class dns {
public:
    int hostlen(const unsigned char *buffer)
    {
        int length = 0;

        for (; ; ) {
            if ('\0' == buffer[length]) {
                length++;
                break;
            } else if (0xc0 == (buffer[length] & 0xc0)) {
                length += 2;
                break;
            }

            length++;
        }

        return length;
    }

    const char *hostname(const unsigned char *raw, int len, char *buffer, size_t size)
    {
        for (int res2 = 1; res2 < len - 1; res2++) {
            if ((raw[res2] >= 'a' && raw[res2] <= 'z') || (raw[res2] >= 'A' && raw[res2] <= 'Z')
                || (raw[res2] >= '0' && raw[res2] <= '9') || ('-' == raw[res2])) {
                buffer[res2 - 1] = raw[res2];
            } else {
                buffer[res2 - 1] = '.';
            }
        }

        return buffer;
    }

    const char *ipv4host(const unsigned char *raw, int len, char *buffer, size_t size)
    {
        int length = 0;

        for (int res = 0; res < len; res++) {
            length += snprintf(buffer + length, size - length - 1, "%d.", raw[res]);
        }
        buffer[length - 1] = '\0';

        return buffer;
    }

    const char *ipv6host(const unsigned char *raw, int len, char *buffer, size_t size)
    {
        int length = 0;

        for (int res = 0; res < len; res++) {
            length += snprintf(buffer + length, size - length - 1, "%02x:", raw[res]);
        }
        buffer[length - 1] = '\0';

        return buffer;
    }
protected:
private:
};

2.2. DNS header处理数据结构

class header {
public:
    explicit header(unsigned char *buffer) : head(nullptr)
    {
        ::memcpy(data, buffer, sizeof(data));
        head = (header_msg_t *)data;
    }

    int display() const
    {
        fprintf(stdout, "DNS header information:\n");
        fprintf(stdout, "    binary(%d):\n        ", (int)sizeof(header_msg_t));
        for (int res = 0; res < sizeof(header_msg_t); res++) {
            printf("0x%02x ", data[res]);
        }
        printf("\n\n");
        fprintf(stdout, "    0x%x \t\t Transaction ID\n", head->id);
        fprintf(stdout, "    Flags:\n");
        fprintf(stdout, "        %d \t\t %s\n", head->qr, head->qr ? "Response: Message is a response" : "Request: Message is a query");
        fprintf(stdout, "        %d \t\t %s\n", head->opcode, opcode());
        fprintf(stdout, "        %d \t\t %s\n", head->aa, head->qr ? (head->aa ? "Authoritative server" : "Non authoritative server") : "meaningless for request");
        fprintf(stdout, "        %d \t\t %s\n", head->tc, head->tc ? "Message is not truncated" : "Message is truncated");
        fprintf(stdout, "        %d \t\t %s\n", head->rd, head->rd ? "Do query recursively" : "Not do query recursively");
        fprintf(stdout, "        %d \t\t %s\n", head->ra, head->ra ? "Server can recursive queries" : "Server can not recursive queries");
        fprintf(stdout, "        %d \t\t reserved(0)\n", head->z);
        fprintf(stdout, "        %d \t\t %s\n", head->rcode, rcode());
        fprintf(stdout, "    %3d \t\t Questions\n", ntohs(head->qdcount));
        fprintf(stdout, "    %3d \t\t Answer RRs\n", ntohs(head->ancount));
        fprintf(stdout, "    %3d \t\t Authority RRs\n", ntohs(head->nscount));
        fprintf(stdout, "    %3d \t\t Additional RRs\n\n", ntohs(head->arcount));

        return sizeof(header_msg_t);
    }

    int questions() const
    {
        return ntohs(head->qdcount);
    }

    int answers() const
    {
        return ntohs(head->ancount);
    }

    int authority() const
    {
        return ntohs(head->nscount);
    }

    int additional() const
    {
        return ntohs(head->arcount);
    }
protected:
    const char *opcode(void) const
    {
        switch (head->opcode) {
        case 0:
            return "query: standard query";
        case 1:
            return "iquery: inverse query";
        case 2:
            return "status: DNS request status";
        case 5:
            return "update: DNS request update";
        default:
            break;
        }

        return "reserved for future use";
    }

    const char *rcode(void) const
    {
        switch (head->rcode) {
        case 0:
            return "success";
        case 1:
            return "Format error";
        case 2:
            return "Server failure";
        case 3:
            return "Name Error";
        case 4:
            return "Not Implemented";
        case 5:
            return "Refused";
        default:
            break;
        }

        return "Reserved for future use";
    }
private:
    /*! RFC 1035. */
    typedef struct header_msg {
        short   id;
#if __BYTE_ORDER == __BIG_ENDIAN
        unsigned char    qr:1;
        unsigned char    opcode:4;
        unsigned char    aa:1;
        unsigned char    tc:1;
        unsigned char    rd:1;

        unsigned char    ra:1;
        unsigned char    z:3;
        unsigned char    rcode:4;
#else
        unsigned char    rd:1;
        unsigned char    tc:1;
        unsigned char    aa:1;
        unsigned char    opcode:4;
        unsigned char    qr:1;

        unsigned char    rcode:4;
        unsigned char    z:3;
        unsigned char    ra:1;
#endif
        unsigned short   qdcount;
        unsigned short   ancount;
        unsigned short   nscount;
        unsigned short   arcount;
    } __attribute__((__packed__)) header_msg_t;

    unsigned char data[sizeof(header_msg_t)];
    header_msg_t  *head;
};

2.3. 请求消息处理数据结构

class request : private dns {
public:
    explicit request(unsigned char *buffer, int offset, int num = 1)
        : buffer(buffer), rnum(num), offset(offset)
    {}

    int display()
    {
        unsigned char *raw = buffer + offset;
        int length = 0;
        char binary[1024] = {0};
        int binlen = 0;

        printf("Queries information:\n");
        for (int res = 0; res < rnum; res++) {
            int len = hostlen(raw);
            char name[128] = {0};

            length += len;
            query_t query = {0};
            memcpy(&query, raw + length, sizeof(query));
            binlen += snprintf(binary + binlen, sizeof(binary) - binlen, "    Name: %s\n", hostname(raw, len, name, sizeof(name)));
            binlen += snprintf(binary + binlen, sizeof(binary) - binlen, "    Type: %s (%d)\n", qtype(ntohs(query.qtype)), ntohs(query.qtype));
            binlen += snprintf(binary + binlen, sizeof(binary) - binlen, "    Class: %s (%d)\n", qclass(ntohs(query.qclass)), ntohs(query.qclass));
        }

        printf("    binary(%d):\n        ", (int)sizeof(query_t) + length);
        for (int res = 0, res2 = 1; res < sizeof(query_t) + length; res++, res2++) {
            printf("0x%02x ", raw[res]);
            if (0 == (res2 % 16)) {
                printf("\n        ");
            }
        }
        printf("\n\n%s\n", binary);

        return sizeof(query_t) + length;
    }
protected:
    const char *qtype(unsigned short type) const
    {
        static const char *reqtype[] = {
            "unknow type",
            "a host address",
            "an authoritative name server",
            "a mail destination (Obsolete - use MX)",
            "a mail forwarder (Obsolete - use MX)",
            "the canonical name for an alias",
            "marks the start of a zone of authority",
            "a mailbox domain name (EXPERIMENTAL)",
            "a mail group member (EXPERIMENTAL)",
            "a mail rename domain name (EXPERIMENTAL)",
            "a null RR (EXPERIMENTAL)",
            "a well known service description",
            "a domain name pointer",
            "host information",
            "mailbox or mail list information",
            "mail exchange",
            "text strings"
        };

        return reqtype[type];
    }

    const char *qclass(unsigned short cls) const
    {
        static const char *reqclass[] = {
            "unknow class",
            "the Internet",
            "the CSNET class",
            "the CHAOS class",
            "Hesiod"
        };

        return reqclass[cls];
    }
private:
    typedef struct query {
        unsigned short   qtype;
        unsigned short   qclass;
    } __attribute__((__packed__)) query_t;

    unsigned char   *buffer;
    int             rnum;
    int             offset;
};

2.4. DNS响应处理数据结构

class response  : private dns {
public:
    explicit response(unsigned char *buffer, int offset, int num = 1)
        : buffer(buffer), anum(num), offset(offset)
    {}

    int display()
    {
        unsigned char *raw = buffer + offset;
        char binary[1024] = {0};
        int binlen = 0;
        int length = 0;

        printf("Answers information:\n");
        for (int res = 0; res < anum; res++) {
            if (0xc0 == (raw[0] & 0xc0)) {
                int len = hostlen(buffer + raw[1]);
                answer_t answer;
                char name[128] = {0};

                memset(&answer, 0, sizeof(answer));
                memcpy(&answer, raw + 2, sizeof(answer));
                binlen += snprintf(binary + binlen, sizeof(binary) - binlen, "    Name: %s\n", hostname(buffer + raw[1], len, name, sizeof(name)));
                binlen += snprintf(binary + binlen, sizeof(binary) - binlen, "    Type: %s (%d)\n", rtype(ntohs(answer.rtype)), ntohs(answer.rtype));
                binlen += snprintf(binary + binlen, sizeof(binary) - binlen, "    Class: %s (%d)\n", rclass(ntohs(answer.rclass)), ntohs(answer.rclass));
                binlen += snprintf(binary + binlen, sizeof(binary) - binlen, "    Time to live: %d (%d minutes)\n", ntohl(answer.rttl), ntohl(answer.rttl) / 60);
                binlen += snprintf(binary + binlen, sizeof(binary) - binlen, "    Data length: %d\n", ntohs(answer.rdlen));

                char address[128] = {0};
                switch (ntohs(answer.rtype)) {
                case 1:
                    binlen += snprintf(binary + binlen, sizeof(binary) - binlen, "    %s: %s\n", rtype(ntohs(answer.rtype)), ipv4host(raw + 2 + sizeof(answer), ntohs(answer.rdlen), address, sizeof(address)));
                    break;
                case 28:
                    binlen += snprintf(binary + binlen, sizeof(binary) - binlen, "    %s: %s\n", rtype(ntohs(answer.rtype)), ipv6host(raw + 2 + sizeof(answer), ntohs(answer.rdlen), address, sizeof(address)));
                    break;
                default:
                    binlen += snprintf(binary + binlen, sizeof(binary) - binlen, "    %s: %s\n", rtype(ntohs(answer.rtype)), hostname(raw + 2 + sizeof(answer), ntohs(answer.rdlen) - 1, address, sizeof(address)));
                    break;
                }
                raw += 2 + sizeof(answer) + ntohs(answer.rdlen);
                length += 2 + sizeof(answer) + ntohs(answer.rdlen);
            }
            binlen += snprintf(binary + binlen, sizeof(binary) - binlen, "\n");
        }

        printf("    binary(%d):\n        ", length);
        for (int res = 0, res2 = 1; res < length; res++, res2++) {
            printf("0x%02x ", (buffer + offset)[res]);
            if (0 == (res2 % 16)) {
                printf("\n        ");
            }
        }
        printf("\n\n%s", binary);

        return length;
    }

    virtual ~response()
    {
    }
protected:
    const char *rtype(unsigned short type) const
    {
        switch (type) {
        case 1:
            return "A"; /*! 主机地址记录。在 DNS 域名与 IP 地址之间建立映射关系 */
        case 2:
            return "NS"; /*! Name?Server, 域名服务器记录, 用来指定该域名由哪个DNS服务器来进行解析. */
        case 5:
            return "CNAME"; /*! Canonical?Name, 别名记录, 允许您将多个名字映射到同一台计算机 */
        case 6:
            return "SOA"; /*! SOA记录表明了DNS服务器之间的关系, SOA记录表明了谁是这个区域的所有者 */
        case 11:
            return "WKS";
        case 12:
            return "PTR"; /*! 指针记录, 用来指向域名空间中的某个位置 */
        case 13:
            return "HINFO"; /*! 主机信息 */
        case 15:
            return "MX"; /*! Mail?Exchanger, 记录是邮件交换记录,它指向一个邮件服务器 */
        case 28:
            return "AAAA";
        case 33:
            return "SRV"; /*! 说明一个服务器能够提供什么样的服务 */
        case 252:
            return "AXFR";
        case 255:
            return "ANY";
        default:
            break;
        }

        return "unknow";
    }

    const char *rclass(unsigned short rclass) const
    {
        static const char *reqclass[] = {
            "unknow class",
            "the Internet",
            "the CSNET class",
            "the CHAOS class",
            "Hesiod"
        };

        return reqclass[rclass];
    }
private:
    typedef struct answer {
        unsigned short   rtype;
        unsigned short   rclass;
        unsigned int     rttl;
        unsigned short   rdlen;
    } __attribute__((__packed__)) answer_t;

    unsigned char   *buffer;
    int             anum;
    int             offset;
};

2.5. 主程序

int main(int argc, const char *argv[])
{
    struct          __res_state dns_state = {0};
    unsigned char   buffer[4096] = {0};
    int             offset = 0;

    if (argc < 2) {
        printf("Please input a address:\n");
        return -1;
    }

    res_ninit(&dns_state);
    int res = res_nsearch(&dns_state, argv[1], ns_t_a, ns_c_in, buffer, sizeof(buffer));
    if (res > 0) {
        printf("receive '%d' bytes data\n", res);

        header head(buffer);
        offset += head.display();

        request request(buffer, offset, head.questions());
        offset += request.display();

        response response(buffer, offset, head.answers());
        offset += response.display();

        res_nclose(&dns_state);
    } else {
        printf("Request failure!\n");
    }

    return 0;
}

3. 运行结果(以www.baidu.com为例)

receive '90' bytes data
DNS header information:
    binary(12):
        0xee 0xaf 0x81 0x80 0x00 0x01 0x00 0x03 0x00 0x00 0x00 0x00 

    0xffffafee 		 Transaction ID
    Flags:
        1 		 Response: Message is a response
        0 		 query: standard query
        0 		 Non authoritative server
        0 		 Message is truncated
        1 		 Do query recursively
        1 		 Server can recursive queries
        0 		 reserved(0)
        0 		 success
      1 		 Questions          // 表明发送了一个请求消息
      3 		 Answer RRs         // 表明响应消息有三个
      0 		 Authority RRs
      0 		 Additional RRs

Queries information:
    binary(19):
        0x03 0x77 0x77 0x77 0x05 0x62 0x61 0x69 0x64 0x75 0x03 0x63 0x6f 0x6d 0x00 0x00 
        0x01 0x00 0x01 

    Name: www.baidu.com
    Type: a host address (1)
    Class: the Internet (1)

Answers information:
    binary(59):
        0xc0 0x0c 0x00 0x05 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x0f 0x03 0x77 0x77 0x77 
        0x01 0x61 0x06 0x73 0x68 0x69 0x66 0x65 0x6e 0xc0 0x16 0xc0 0x2b 0x00 0x01 0x00 
        0x01 0x00 0x00 0x00 0x40 0x00 0x04 0x24 0x98 0x2c 0x5f 0xc0 0x2b 0x00 0x01 0x00 
        0x01 0x00 0x00 0x00 0x40 0x00 0x04 0x24 0x98 0x2c 0x60 

    Name: www.baidu.com
    Type: CNAME (5)
    Class: the Internet (1)
    Time to live: 0 (0 minutes)
    Data length: 15
    CNAME: www.a.shifen

    Name: www.a.shifen.
    Type: A (1)
    Class: the Internet (1)
    Time to live: 64 (1 minutes)
    Data length: 4
    A: 36.152.44.95

    Name: www.a.shifen.
    Type: A (1)
    Class: the Internet (1)
    Time to live: 64 (1 minutes)
    Data length: 4
    A: 36.152.44.96

3.1.?Queries information分析

Queries information:
    binary(19):
        0x03 0x77 0x77 0x77 0x05 0x62 0x61 0x69 0x64 0x75 0x03 0x63 0x6f 0x6d 0x00 0x00 
        0x01 0x00 0x01 

    Name: www.baidu.com
    Type: a host address (1)
    Class: the Internet (1)
  • 请求消息的QNAME是变长的,且以标签进行划分的,如上二进制中的:0x03,0x05,0x03表示name的长度
  • QNAME以‘\0’结尾,其后的二进制0x00表示结尾符
  • 0x00 0x01:表示QTYPE类型
  • 0x00 0x01:表示QCLASS类型

3.2.?Answers information分析

Answers information:
    binary(59):
        0xc0 0x0c 0x00 0x05 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x0f 0x03 0x77 0x77 0x77 
        0x01 0x61 0x06 0x73 0x68 0x69 0x66 0x65 0x6e 0xc0 0x16 0xc0 0x2b 0x00 0x01 0x00 
        0x01 0x00 0x00 0x00 0x40 0x00 0x04 0x24 0x98 0x2c 0x5f 0xc0 0x2b 0x00 0x01 0x00 
        0x01 0x00 0x00 0x00 0x40 0x00 0x04 0x24 0x98 0x2c 0x60 

    Name: www.baidu.com
    Type: CNAME (5)
    Class: the Internet (1)
    Time to live: 0 (0 minutes)
    Data length: 15
    CNAME: www.a.shifen

    Name: www.a.shifen.
    Type: A (1)
    Class: the Internet (1)
    Time to live: 64 (1 minutes)
    Data length: 4
    A: 36.152.44.95

    Name: www.a.shifen.
    Type: A (1)
    Class: the Internet (1)
    Time to live: 64 (1 minutes)
    Data length: 4
    A: 36.152.44.96
  • Name:变长,若以0xc0开头,则表示name使用相对位置存放。如:0xc0 0x0c:表示name值相对于头开始偏移0x0c的位置即为name值;否则就是Name值

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-03-03 16:39:43  更:2022-03-03 16:44:37 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 15:33:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码