DNS 一站到家之 DNS 消息协议

本篇文章让我们来分析下 DNS 消息协议的组成,当然我们也可以查看 RFC 1035

文章中所用的 byte 而不是 octet sequence,我认为更助于理解。好,首先我需要了解一些有关 DNS 相关的知识。

相关术语

相关的记录类型都已经收录在 DNS 一站到家之记录类型

  • RR(resource record),资源记录,用来描述该条资源。
  • TTL(time to live),生命周期,一般用来描述某条资源的有效缓存时间等。
  • TYPE,资源类型,如AAAAACNAME等。
  • QTYPE,查询类型,是 TYPE 的超集,增加的有:AXFRMAILBMAILA*
  • CLASS,查询类,如INHS等,查看如上。
  • QCLASS。是 CLASS 的超集,增加的有 *
  • ZONE,你可能需要阅读 DNS 一站到家之历史由来
  • LABEL,消息中的域名均以标签序列表示
    • 每个标签代表了一个 byte 的长度字段 + 多个其他 byte。
    • 每个表示长度的 byte,高的 2bit 必须为 0,剩余 6bit 表示该标签的长度,故最大为 63.
    • 由于每个域名都以 root 节点的 null 标签结尾,故一个域名总是以一个值为 0 的 byte 结尾。
    • 为了简化实现,域名的总长度(标签字节 + 标签长度字节)限制为 255 以内。

如:st.deepzz.com. 该域名被划分为三个标签,注意,这里表示为 length + name:

  • 2 + st
  • 6 + deepzz
  • 3 + com

拼起来为:2 + st + 6 + deepzz + 3 + com + .

基本规范

  • 域名的命名规范:
    • 域名现在是不区分大小的。
    • 域名只能包含 [0-9]、[a-z]、- 字符。(08 年之后允许使用其他语言作为域名)。
    • 每一级域名长度的限制是 63 个字符,总长度不能超过 253 个字符。
  • 数据传输顺序:大端序。
  • DNS 服务器使用 TCP 和 UDP 的 53 端口。
  • DNS 解析的域名必须以 . 结尾,不过通常程序会帮你自动加上。

消息格式

协议内部的所有通信采用的是单一格式,称作 message。消息的顶层格式分成5个部分(在某些情况,它们中一些是空的)。

    +---------------------+
    |        Header       |
    +---------------------+
    |       Question      | the question for the name server
    +---------------------+
    |        Answer       | RRs answering the question
    +---------------------+
    |      Authority      | RRs pointing toward an authority
    +---------------------+
    |      Additional     | RRs holding additional information
    +---------------------+

Header 总是存在,其中包括一些字段,字段规定其余部分中哪些部分存在,也规定消息是查询还是响应,是标准查询还是某个其他的操作码,等等。

下面我们一一介绍这五个部分如何组成。

Header

Header 的格式是这样的:

                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      ID                       |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    QDCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    ANCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    NSCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    ARCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

其中:

  • ID,16 bit,程序生成,查询请求的 ID 与响应的 ID 必须相同,这样我们就能知道哪个响应对应着哪个请求。
  • QR,1 bit,标示是查询请求(0)还是还是响应(1)。
  • OPCODE,4 bit,标示查询种类,需要复制到响应中:
    • 0,标准查询(QUERY)
    • 1,反响查询(IQUERY)
    • 2,服务器状态查询(STATUS)
    • 3-15,保留将来使用
  • AA,1 bit,权威回答(Authoritative Answer),只在响应中有效,规定响应的名称服务器是 Question 部分中域名的权威。注意,由于别名,回答部分的内容可以有多个所有者名称。AA 位对应匹配查询名称的名称,或者 Answer 部分中第一个所有者名称。
  • TC,1 bit,截断(Truncation),表示这条消息由于长度大于传送通道上准许的长度而被截断。
  • RD,1 bit,期望递归(Recursion Desired),在查询中这个位可以置 1,并复制进响应中。如果 1,它引导名称服务器递归跟踪查询。支持递归查询是可选项。
  • RA,1 bit,递归可用(Recursion Available),在响应中这个字段被置 1 或 0,表示名称服务器中是否支持递归查询。
  • Z,3 bit,保留将来使用。在所有查询和响应中此位必须置 0。
  • RCODE,4 bit,响应代码(Response code),响应的一部分。其取值:
    • 0,没有出错
    • 1,消息格式错误
    • 2,名称服务器故障
    • 3,名称错误,名称不存在
    • 4,不支持查询种类
    • 5,拒绝,拒绝该操作
    • 6-15,保留将来使用
  • QDCOUNT,无符号 16 bit,Question 部分中条目的数量
  • ANCOUNT,无符号 16 bit,Answer 部分中资源记录的数量
  • NSCOUNT,无符号 16 bit,权威记录部分中名称服务器资源记录的数量。
  • ARCOUNT,无符号 16 bit,附加记录部分中资源记录的数量。

message_header

Question

查询请求中携带该字段,大部分响应也会返回,此部分包括 QDCOUNT(通常取 1)条目:

                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                                               |
    /                     QNAME                     /
    /                                               /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     QTYPE                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     QCLASS                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  • QNAME,变长,用标签序列表示的域名,如:deepzz.com,其中 deepzz 长度为 6,故 QNAME 的值为(16 进制):06 64 65 65 70 7a 7a 03 63 6f 6d 00
  • QTYPE,16 bit,它规定查询的类型。这个字段的值包括所有适用于 TYPE 字段的代码,以及某些更一般的代码(这些代码可以匹配不止一个 RR 类型)
  • QCLASS,16 bit,它规定查询的类。例如,对于互联网,QCLASS 字段是 IN

message_query

Resource record

Answer、Authority 和 Additional 都共享相同的格式:可变数目资源记录,其中记录的数目在首部内相应计数字段中规定。

                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                                               |
    /                                               /
    /                      NAME                     /
    |                                               |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      TYPE                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     CLASS                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      TTL                      |
    |                                               |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                   RDLENGTH                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
    /                     RDATA                     /
    /                                               /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  • NAME,变长,该记录匹配的域名
  • CLASS,16 bit,资源类型,影响 RDATA 的数据含义
  • TTL,32 bit,缓存时间,单位 s,0 为不缓存
  • RELENGTH,16 bit,指定 RDATA 的长度
  • RDATA,变长,这条信息的格式根据资源记录的 TYPE 和 CALSS 改变。例如,如果 TYPE 是 A 和 CALSS 是 IN,此 RDATA 字段是 4 byte 的 ARPA 互联网地址,即 IPv4。

message_resource

消息压缩

为了减小消息大小,域系统使用去除消息中域名重复的压缩方案。在这个方案中,整个域名或在域名底部的标签列表被用一个指针取代,该指针指向同一名称的前一个具体值。

    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    | 1  1|                OFFSET                   |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

前两位是 1。方便与 label 进行区分,因为由于标签被限制在小于等于 63 个 byte,标签必须用两个 0 位开始(10 和 01 这两个组合保留将来使用)。OFFSET 字段规定从消息开始的偏移(即,域首部中 ID 字段的第一个 byte 开始)。0 偏移表示 ID 字段的第一个字节,等等。

此压缩方案使得消息中的域名能够表示为下述之一:

  • 以零八位位组结束的标签序列
  • 指针
  • 用指针结束的标签序列

message_compress

指针仅能用于这样的域名具体值,该域名中格式不是特定类。如果不是这种情况,将要求名称服务器或解析器知道它所处理的所有 RRs 的格式。到目前为止没有这类情况,但是在将来的 RDATA 格式中可能出现。

如果消息(该消息受长度字段支配)的一部分包括域名(诸如 RR 的 RDATA 部分),并且使用压缩,在长度计算中使用被压缩的名称的长度(而不是扩展名称的长度)。

在由程序生成的消息中是否避免使用指针由程序自主考虑,尽管这将降低数据报的能力,并且可能引起截断。然而,要求所有程序理解包括指针的到达消息。

例如,数据报或许需要使用域名 F.ISI.ARPA、FOO.F.ISI.ARPA、ARPA 和 root。忽略消息的其他字段,这些域名或许如下图所示。

       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    20 |           1           |           F           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    22 |           3           |           I           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    24 |           S           |           I           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    26 |           4           |           A           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    28 |           R           |           P           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    30 |           A           |           0           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    40 |           3           |           F           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    42 |           O           |           O           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    44 | 1  1|                20                       |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    64 | 1  1|                26                       |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    92 |           0           |                       |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

F.ISI.ARPA 的域名以偏移 20 显示。FOO.F.ISI.ARPA 的域名以偏移 40 显示;这个定义使用了将 FOO 的标签级联到先前定义的 F.ISI.ARPA 的指针。使用以偏移 20 指向名称 F.ISI.ARPA 的 ARPA 分量的指针,以偏移 64 定义域名 ARPA;注意,这个指针依赖于 ARPA,该 ARPA 是在偏移 20 的字符串中的最后一个标签。根域名称由在偏移 92 的值为 0 的 byte 定义;根域名没有标签。

本文链接:参与评论 »

--EOF--

提醒:本文最后更新于 2540 天前,文中所描述的信息可能已发生改变,请谨慎使用。

专题「DNS 一站到家」的其它文章 »

Comments