Rocky Linux 9 从入门到精通007 — DNS管理

前述

根据国际惯例,在编写 Linux 基础教程或书籍时,DNS 章节通常会选择介绍 Bind。然而,为了使《Rocky Linux 9 从入门到精通》系列教程更贴近实际工作需求,木子将对某些章节进行适当调整,以参考当前主流技术进行讲解。对于前期的一些基础理论,在本章节中会用到的,我们仍会进行简要介绍,但不会过于详细,因为每个章节的基础理论内容都足以单独成书。此章节大概完成了 80%,所以会逐步发布更新。因为微信公众号的发布不可修改,所以对于可能存在的错误,我们会在 RockyLinux 中文社区官方第一时间修正。

为什么需要 DNS?

当前网络通信大部分采用的是 TCP/IP 协议,而该协议的基础是 IP 地址。因此,计算机在网络中进行通信时只能识别由纯数字构成的 IP 地址,包括:IPv4 和 IPv6,例如:202.98.12.122001:4860:4801:53::48。试想一下,如果让您通过 https://2001:4860:4801:53::48 访问网站会有多困难。首先,输入这样的 IP 地址非常麻烦,其次,记住这一串数字也很不容易。然而,如果使用 https://www.rockylinux.cn 这样的域名访问,是不是简单很多?这样您可以记住的域名也会更多。

这正是 DNS 存在的最基本意义,将域名转换成 IP 地址。我们可以将 DNS 服务器比作一个电话簿,里面存有对应的域名和 IP 地址。当我们访问某个域名时,DNS 服务器会查询对应的 IP 地址并返回给用户,然后用户根据获取到的 IP 地址访问对应的服务器。当然,DNS 还有很多其他用途,例如:通过 DNS 轮询实现服务高可用性等。

DNS 服务器类型

DNS(Domain Name System,域名系统)是互联网的重要组成部分,是用于实现域名和 IP 地址相互映射的一个分布式数据库,它将人类易读的域名(如: www.rockylinux.cn )翻译成计算机能够识别的 IP 地址(如:192.168.1.1),使用户可以更快速便捷地访问互联。互联网连通的是全球资源,单一的域名服务器不足以支撑全部的地址转换操作,因此全球有多套域名服务器相互配合使用。早在 1983 年互联网就开始采用层次树状结构的命名方法,并使用分布式的域名系统进行解析操作。这样既提升了域名解析的效率,同时也保障了域名解析的稳定性,如果系统中单个域名服务器出现故障,不会对整个 DNS 系统的正常运行造成太大影响。

在这个过程中,不同类型的 DNS 服务器各司其职,确保查询过程顺利进行。以下是 DNS 服务器的四种主要类型:

  1. 根域名服务器(Root Name Server)

    • 根域名服务器是 DNS 层次结构的最顶层。全球共有 13 组根服务器,由多个组织分布在不同地点进行管理。它们保存着顶级域(TLD)服务器的列表。
    • 当递归域名服务器无法直接回答查询请求时,它会首先向根域名服务器请求顶级域的信息。
  2. 顶级域名服务器(TLD Server)

    • 顶级域名服务器管理特定顶级域(如:.com.org.net 等)。这些服务器保存着次级域名(如:rockylinux.cn)的权威域名服务器的列表。
    • 当递归域名服务器从根域名服务器获取到 TLD 服务器的信息后,会向 TLD 服务器请求次级域名的信息。
  3. 权威域名服务器(Authoritative Name Server)

    • 权威域名服务器保存着特定域名及其对应的 IP 地址信息(如:192.168.1.1 rockylinux.cn),是最终提供域名解析结果的服务器。
    • 当递归域名服务器向权威域名服务器请求特定域名的信息时,权威域名服务器会返回相应的 IP 地址。
  4. 递归域名服务器(Recursive Resolver)

    • 递归域名服务器负责接受客户端的 DNS 查询请求,并按照层次结构逐步查询根域名服务器、TLD 服务器和权威域名服务器,直到获取最终的 IP 地址。
    • 它们通常由 ISP(互联网服务提供商)或公共 DNS 服务(如:Google DNS、Cloudflare DNS 等)运行,当然也可以自建(如:Bind、SmartDNS、CoreDNS 等)。

除了上述四种主要类型的 DNS 服务器,还有其它类型的 DNS 服务器和相关技术,例如:

  • 缓存 DNS 服务器(Caching DNS Server)

    • 递归域名服务器在查询过程中会缓存查询结果,以便在未来的查询中更快速地提供响应。这种缓存机制也可以单独实现,通过专门的缓存 DNS 服务器来减少查询延迟。
  • 转发 DNS 服务器(Forwarding DNS Server)

    • 转发 DNS 服务器并不直接进行递归查询,而是将查询请求转发给其它递归 DNS 服务器处理。这种配置可以用于优化网络流量或集中管理 DNS 查询。
  • 动态 DNS 服务器(Dynamic DNS Server)

    • 动态 DNS 服务器允许用户将动态变化的 IP 地址与固定的域名关联起来,常用于家庭网络或小型企业网络中。

这些服务机制共同协作,确保 DNS 查询过程高效、可靠地进行。

特别说明
权威服务器即可以作为权威服务器,也可以作为递归服务器,反之亦然。

全球 DNS 根服务器

全球域名的最高管理机构是 ICANN(Internet Corporation for Assigned Names and Numbers),即互联网名称与数字地址分配机构。ICANN 是一个总部位于美国加利福尼亚州的非营利组织,负责管理全球域名系统的运行。它的主要职责是规划和管理顶级域名(Top-Level Domain,缩写为 TLD)。ICANN 不会直接管理所有顶级域名,因为数量庞大且复杂。目前,全球顶级域名已超过 1000 个,每个顶级域名下还有许多服务提供商和零售注册商。为了简化管理,ICANN 将每个顶级域名的具体管理任务委托给专门的注册局(Registry),这些注册局负责各自顶级域名的所有事务。例如:.cn 域名由中国互联网络信息中心(CNNIC)负责管理,CNNIC 制定 .cn 域名的相关政策。而 .com 域名则由 VeriSign 威瑞信 公司管理。通过这种方式,ICANN 只需与各个注册局合作,有效地管理全球域名系统。

IANA(Internet Assigned Numbers Authority) ,即互联网数字分配机构。本质 IANA 是 ICANN 的一个部门,它负责实施和管理 ICANN 的一些技术职能,包括:IP 地址、系统端口号、以及顶级域名(TLD)和国家顶级域名(ccTLD)的管理。IANA 负责全球 IP 地址和 AS(自治系统)号的分配,以及 DNS 根区的管理。在域名系统中,IANA 维护了一个中央的数据库,记录了所有的顶级域名和其对应的权威 DNS 服务器。每次新的顶级域名被创建或者顶级域名的权威服务器发生变化时,IANA 都会更新这个数据库。

在全球只有 13 组 DNS 根服务器,其中 10 组在美国,欧洲 2 组位于英国和瑞典,亚洲 1 组位于日本。详见下表:

特别说明
全球 DNS 根服务器,并不是真正的只有 13 台,而是由 13 个公网 IPv4 和公网 IPv6 组成的 DNS 根服务器集群,每组集群由全球分布的多个物理服务器组成,通过任播(Anycast)技术提供服务。截止2024年07月16日全球总共有 1845 个节点组成 DNS 根服务器。另外中国并不是没有根服务器,通过任播技术,目前在我国北京、上海、杭州、武汉、长沙、贵州、广州、香港、台北、高雄等地分布着多个根节点集群。详见参考:https://root-servers.org
主机名 IP 地址 IPv4 / IPv6 组织
a.root-servers.net 198.41.0.4, 2001:503:ba3e::2:30 Verisign 公司(美国,弗吉尼亚州)
b.root-servers.net 170.247.170.2, 2801:1b8:10::b 南加州大学 (ISI)(美国,加利福尼亚州)
c.root-servers.net 192.33.4.12, 2001:500:2::c Cogent 通讯(美国,弗吉尼亚州)
d.root-servers.net 199.7.91.13, 2001:500:2d::d 马里兰大学(美国,马里兰州)
e.root-servers.net 192.203.230.10, 2001:500:a8::e 美国国家航空航天局(NASA)(美国,加利弗尼亚州)
f.root-servers.net 192.5.5.241, 2001:500:2f::f 互联网系统联盟(ISC)(美国,加利弗尼亚州)
g.root-servers.net 192.112.36.4, 2001:500:12::d0d 美国国防部 (NIC)(美国,弗吉尼亚州)
h.root-servers.net 198.97.190.53, 2001:500:1::53 美国陆军研究实验室(美国,马里兰州)
i.root-servers.net 192.36.148.17, 2001:7fe::53 Autonomica 公司(瑞典,斯德哥尔摩)
j.root-servers.net 192.58.128.30, 2001:503:c27::2:30 Verisign 公司(美国,弗吉尼亚州)
k.root-servers.net 193.0.14.129, 2001:7fd::1 RIPE NCC(英国,伦敦)
l.root-servers.net 199.7.83.42, 2001:500:9f::42 ICANN(美国,弗吉尼亚州)
m.root-servers.net 202.12.27.33, 2001:dc3::35 WIDE Project(日本,东京)

 

思考
怎样查看所有根服务器信息?

参考链接:Root Servers,或通过 dig +bufsize=1200 +norec NS . @a.root-servers.net 可以获取所有根服务器信息。

[root@coredns-001 ~]# dig +bufsize=1200 +norec NS . @a.root-servers.net 

; <<>> DiG 9.16.23-RH \<<>> +bufsize +norec NS . @a.root-servers.net
;; global options: +cmd
;; Got answer:
;; ->>HEADER <<- \opcode: QUERY, status: NOERROR, id: 6268
;; flags: qr aa; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 27

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1472
;; QUESTION SECTION:
;.                              IN      NS

# 所有根服务器信息
;; ANSWER SECTION:
.                       518400  IN      NS      a.root-servers.net.
.                       518400  IN      NS      b.root-servers.net.
.                       518400  IN      NS      c.root-servers.net.
.                       518400  IN      NS      d.root-servers.net.
.                       518400  IN      NS      e.root-servers.net.
.                       518400  IN      NS      f.root-servers.net.
.                       518400  IN      NS      g.root-servers.net.
.                       518400  IN      NS      h.root-servers.net.
.                       518400  IN      NS      i.root-servers.net.
.                       518400  IN      NS      j.root-servers.net.
.                       518400  IN      NS      k.root-servers.net.
.                       518400  IN      NS      l.root-servers.net.
.                       518400  IN      NS      m.root-servers.net.

# 对应根服务器 A 与 AAAA 记录信息
;; ADDITIONAL SECTION:
a.root-servers.net.     518400  IN      A       198.41.0.4
b.root-servers.net.     518400  IN      A       170.247.170.2
c.root-servers.net.     518400  IN      A       192.33.4.12
d.root-servers.net.     518400  IN      A       199.7.91.13
e.root-servers.net.     518400  IN      A       192.203.230.10
f.root-servers.net.     518400  IN      A       192.5.5.241
g.root-servers.net.     518400  IN      A       192.112.36.4
h.root-servers.net.     518400  IN      A       198.97.190.53
i.root-servers.net.     518400  IN      A       192.36.148.17
j.root-servers.net.     518400  IN      A       192.58.128.30
k.root-servers.net.     518400  IN      A       193.0.14.129
l.root-servers.net.     518400  IN      A       199.7.83.42
m.root-servers.net.     518400  IN      A       202.12.27.33
a.root-servers.net.     518400  IN      AAAA    2001:503:ba3e::2:30
b.root-servers.net.     518400  IN      AAAA    2801:1b8:10::b
c.root-servers.net.     518400  IN      AAAA    2001:500:2::c
d.root-servers.net.     518400  IN      AAAA    2001:500:2d::d
e.root-servers.net.     518400  IN      AAAA    2001:500:a8::e
f.root-servers.net.     518400  IN      AAAA    2001:500:2f::f
g.root-servers.net.     518400  IN      AAAA    2001:500:12::d0d
h.root-servers.net.     518400  IN      AAAA    2001:500:1::53
i.root-servers.Net.     518400  IN      AAAA    2001:7fe::53
j.root-servers.Net.     518400  IN      AAAA    2001:503:c27::2:30
k.root-servers.net.     518400  IN      AAAA    2001:7fd::1
l.root-servers.net.     518400  IN      AAAA    2001:500:9f::42
m.root-servers.net.     518400  IN      AAAA    2001:dc3::35

;; Query time: 4 msec
;; SERVER: 198.41.0.4#53(198.41.0.4)
;; WHEN: Tue Jul 02 11:47:59 CST 2024
;; MSG SIZE  rcvd: 811

# 单独查询某个根服务器对应 IP 信息
[root@coredns-001 ~]# dig +short a.root-servers.net.
198.41.0.4
[root@coredns-001 ~]# dig +short b.root-servers.net.
170.247.170.2
[root@coredns-001 ~]# dig +short AAAA a.root-servers.net.
2001:503:ba3e::2:30
[root@coredns-001 ~]# dig +short AAAA b.root-servers.net.
2801:1b8:10::b

一般来说根服务器很少发生变更,但也有例外,在 2023 年 05 月16 日 USC/ISI 发布公告,变更 b.root-servers.net IPv4 地址为:170.247.170.2, IPv6 地址将为:2801:1b8:10::b。详见:New addresses for b.root-servers.net,变更公告:LACNIC asigna recursos de numeración al servidor raíz de USC/ISI

为什么只有 13 组 DNS 根服务器?

为什么只有 13 组 DNS 根服务器集群?是否可以有更多的 DNS 根服务器集群?
要回答这个问题,首先需要对 DNS 报文进行详细分析。如果您对此部分不感兴趣,可以直接跳到【结论】部分。

Priming Queries

DNS 查询都从根服务器开始,那么客户端如何知道当前的根服务器列表呢?原则上,客户端的代码中会硬编码这些信息。然而,实际上根服务器并非永久不变(前面提到 b.root-servers.net 就变更过 IP),新加入的服务器如何被识别呢?这就涉及到了“Priming Queries”。简单来讲,每个 DNS 解析客户端都会附带一个包含所有当前根服务器信息(包括域名、IP 地址等)的列表文件,称为 Root Hints,可以从 IANA 官网下载。但鉴于根服务器列表可能发生变化,客户端需定期从已知的根服务器查询最新的服务器列表,这种请求被称为 Priming Queries。

客户端首先从 Root Hints 中选出一台根服务器,然后查询最新的根服务器列表,并在本机进行一段时间的缓存,直至缓存过期。Priming Queries 使用的是 DNS 协议,通过 UDP 传输。由于互联网早期 IP 网络的最大传输单元长度(MTU)只有五百多字节,因此 DNS 回复信息的最大长度也不能过长,即不得超过 512 字节。这就构成了添加根服务器的技术难题。理论上,可以要求客户端使用 TCP 连接传输 Priming 查询结果。但由于所有 DNS 查询都采用 UDP 协议,简单且统一。虽然 DNS 也支持使用 TCP,但如果让 Priming Queries 单独使用 TCP,将会使系统变得复杂。因此,社区最终决定采取域名压缩查询的方法,来解决 DNS 报文可能超过 512 字节的问题。

512 字节

前面提到 DNS 报文不能够超过 512 字节,下面我们来简单了解一下它的来龙去脉。

DNS 协议最初的定义可以追溯到 20 世纪 80 年代末,它使用了两种通讯协议:UDPTCP。通常情况下,UDP 用于查询和响应,而 TCP 用于主 DNS 服务器与辅助 DNS 服务器之间的区域传送。然而,UDP 有一个限制,它只能处理最大 512 字节的数据包。这个限制对于需要在每个包中包含数字签名的一些新的 DNS 特性(如 DNSSEC)来说,显得有些小。这个 512 字节的限制也影响了 DNS 根服务器的数量和名称。为了使所有的根服务器数据能够在一个 512 字节的 UDP 包中进行传输,根服务器的数量只能限制在 13 个,并且每个服务器都被单个字母命名。

在以太网中,数据的长度必须在 46-1500 字节之间,这是由以太网的物理特性决定的。实际上,这个 1500 字节就是网络层 IP 数据包的长度限制。理论上,IP 数据包的最大长度可以达到 65535 (2^16 - 1) 字节。但是,除去 20 字节的 IP 首部和 8 个字节的 UDP 首部,UDP 数据包中的数据最大长度为 65507 字节。在互联网数据传输中,UDP 数据的长度通常控制在 576 字节(互联网标准 MTU 值)。而在很多 UDP 应用程序设计中,数据包的大小被限制为 512 字节或更小,以防止数据包的丢失。

当解析器首次发送 UDP 查询时,如果收到的响应被截断,它会使用 TCP 重新发送该查询。虽然这个过程可以绕过 512 字节的限制,但效率并不高,TCP 连接的开销要大得多。一个 UDP 名称服务器交换只需要两个包:一个查询包和一个响应包。而一个 TCP 交换至少需要 7 个包:三次握手初始化 TCP 会话、一个查询包、一个响应包以及最后一次握手来关闭连接。

DNS 报文拆解

一个典型的DNS报文由以下几个部分组成,每个部分包含的字节数如下:

  1. 报文头部(Header):12 字节。
  2. 问题(Question):可变长度,保存查询问题。
  3. 回答(Answer):可变长度,保存查询结果。
  4. 权威(Authority):可变长度,保存权威信息。
  5. 附加(Additional):可变长度,保存附加信息。

报文头部(Header)

报文头部总共占用 12 字节(96 位),其具体结构如下:

  • 标识(ID):2 字节(16 位),用于标识查询和响应的匹配。
  • 标志字段(Flags):2 字节(16 位)
    • QR:1 位,查询报文(查询请求 0)或响应报文(查询应答 1
    • Opcode:4 位,标识查询类型(标准查询 0、反向查询 1 或服务器状态请求 2
    • AA:1 位,权威应答(Authoritative Answer),表示当前查询结果是由域名的权威服务器回应。
    • TC:1 位,截断标志(Truncated),使用 UDP 时,如果应答超过 512 字节,只返回前 512 个字节。
    • RD:1 位,期望递归(Recursion Desired),在请求中设置,并在应答中返回。该位为 1 时,服务器必须处理这个请求,如果服务器没有授权回答,它必须替客户端请求其他 DNS 服务器,这也是所谓的 递归查询 。该位为 0 时,如果服务器没有授权回答,它就返回一个能够处理该查询的服务器列表给客户端,由客户端自己进行 迭代查询 。
    • RA:1 位,递归可用(Recursion Available),如果服务器支持递归查询,就会在应答中设置该位,以告知客户端。
    • Z:3 位,保留字段,必须为 0,目前 3 位未用,留给未来扩展使用。
    • RCODE:4 位 - 返回码(Response Code),表示请求结果,常见的值包括:0 表示没有差错,3 表示域名差错,该差错由权威服务器返回,表示查询的域名不存在。
  • 问题数(QDCOUNT):2 字节(16 位),问题的条目数。
  • 回答数(ANCOUNT):2 字节(16 位),回答的资源记录数。
  • 权威记录数(NSCOUNT):2 字节(16 位),权威的资源记录数。
  • 附加记录数(ARCOUNT):2 字节(16 位),附加的资源记录数。

问题(Question)

问题部分包含一个或多个查询,每个查询的结构如下:

  • 查询名(QNAME):可变长度,查询的域名,以点分十进制格式表示,每个标签长度前有一个字节表示该标签的长度。
  • 查询类型(QTYPE):2 字节(16 位),指定查询的类型,例如 A 记录、MX 记录等。
  • 查询类(QCLASS):2 字节(16 位),指定查询的类(网络类型),通常为 IN(Internet 类)。

如下图所示,QNAME 字段的长度是可变的,用于储存查询的域名。以域名 A.ROOT-SERVERS.NET. 为例,它会被分解为三个部分:AROOT-SERVERSNET。这些部分被称为标签(Label),而在 QNAME 字段中,只保存标签,不保存 .。每个标签的第一个字节用于记录当前标签的长度,后面则跟着标签的内容,在末尾使用一个零长度的标签来表示结束,因此总长度为 20 个字节。特别需要注意的是,对于根域名 .,其编码为 0,但总长度为 1 个字节。

查询部分包含 a.root-servers.net

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+  
| 1| a|12| r| o| o| t| -| s| e| r| v| e| r| s| 3| n| e| t| 0|  
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+  

具体字节占用如下:

  • 1 a:表示标签 a,占 2 个字节。
  • 12 root-servers:表示标签 root-servers,占 13 个字节。
  • 3 net:表示标签 net,占 4 个字节。
  • 0:表示域名结束,占 1 个字节。

总共长度为 2 + 13 + 4 + 1 = 20 字节。

回答(Answer)

回答包含一个或多个资源记录(RR,全称:Resource Record),每个资源记录的结构如下:

  • 名称(NAME):可变长度,资源记录的域名。
  • 类型(TYPE):2 字节(16 位),资源记录的类型。
  • 类(CLASS):2 字节(16 位),资源记录的类,通常为 IN(Internet 类)。
  • 生存时间(TTL):4 字节(32 位),资源记录的生存时间(缓存时间)。
  • 数据长度(RDLENGTH):2 字节(16 位), 资源数据的长度。
  • 资源数据(RDATA):可变长度,资源记录的数据。

相比问题(Question),RR 多了 TTLRDLENGTHRDATA 三个字段。其中,TTL 代表有效时长,RDLENGTH 代表后续 RDATA 的长度,而 RDATA 则储存实际的响应数据。根据 TYPECLASS 的不同,RDATA 的内容也会有所变化。在 Priming 查询中,RDATA 用于储存各个根服务器的域名,其编码方式与 Question 中的 QNAME 相同。

根据上面的逻辑,当 DNS 服务器返回如下一条 RR 记录:

.       518400  IN      NS      a.root-servers.net.

那么对应的 RR 总长度是 1(Name) + 2(Type) + 2(Class) + 4(TLL) + 2(RDLength) + 20(RDATA) = 31 字节。

权威(Authority)和附加(Additional)

权威(Authority)和附加(Additional)这两个部分的资源记录结构与回答(Question)的资源记录结构相同,均由一条或多条资源记录组成,记录数目保存在头部中的对应字段,这里不再赘述。

结论

上述一系列的 DNS 报文拆解,最终 Priming 查询响应的返回值有多大呢?我们可以进行以下计算:

  • Header 固定为 12 字节。
  • 第一个域名 NS 记录占用 31 字节。
  • 其余 12 个压缩后的域名 NS 记录,占用 180 字节(12 * 15 字节)。15 = 1(Name) + 2(Type) + 2(Class) + 4(TLL) + 2(RDLength) + 2(指针) + 2(偏移量)
  • 13 个 A 记录,占用 208 字节(13 * 16 字节)。16 = 2(指针) + 2(Type) + 2(Class) + 4(TLL) + 2(RDLength) + 4(RDATA),RDATA 即返回的 IPv4 地址。
  • Question 中的 QTYPE 和 QCLASS 字段占用 4 字节。
  • Question 中的 QNAME 字段占用 1 字节。

这样总共占用的字节为 436 字节(12+31+180+208+4+1)。由于 DNS 消息的大小限制为 512 字节,因此还剩下 76 字节(512-436)的可用空间。一组新的根服务器需要额外占用 31 字节(15+16)。理论上,还可以添加两台根服务器,也就是最多 15 台。然而,早期的根服务器还充当 .com.net.org 区域权威服务器功能。客户端可以向根服务器发起针对特定 .com 域名的 Priming 查询。由于响应结果需要包含查询域名 QNAME,所以在上述的 76 字节中,至少需要保留 64 字节用于 QNAME。这样就只剩 12 字节(512-436-64=500),因此不能再添加新的根服务器。如果您有任何疑问,可以参考官方文档说明: RSSAC FAQ

域名压缩

域名压缩本质就是为了解决超 512 字节的问题,举一个简单例子:

查询域名 a.root-servers.net.b.root-servers.net.A 记录,没有进行域名压缩之前,需要将这两个字符串完整传给 DNS 服务器进行解析,但使用域名压缩后,只需要传 a.root-servers.net.b 就可以了,这就是域名压缩带来的好处。

而在域名压缩中有一个关键点,就是偏移量问题,那么这个偏移量怎么计算了?在计算偏移量之前,首先需要了解 ASCII 码,然后在后面的案例中才能够明白其中的意思。在 DNS 中,域名中的字母是不区分大小写的,并且存储时也是采用小写字母,这就意味着在解析过程中,aA 被视为相同。以下是域名常用字符集:

  • 小写字母 a-z
  • 数字 0-9
  • 连字符 -

对应 ASCII 码如下。

小写字母 a-z

字符 ASCII 码 十六进制
a 97 0x61
b 98 0x62
c 99 0x63
d 100 0x64
e 101 0x65
f 102 0x66
g 103 0x67
h 104 0x68
i 105 0x69
j 106 0x6A
k 107 0x6B
l 108 0x6C
m 109 0x6D
n 110 0x6E
o 111 0x6F
p 112 0x70
q 113 0x71
r 114 0x72
s 115 0x73
t 116 0x74
u 117 0x75
v 118 0x76
w 119 0x77
x 120 0x78
y 121 0x79
z 122 0x7A

数字 0-9

字符 ASCII 码 十六进制
0 48 0x30
1 49 0x31
2 50 0x32
3 51 0x33
4 52 0x34
5 53 0x35
6 54 0x36
7 55 0x37
8 56 0x38
9 57 0x39

连字符 -

字符 ASCII 码 十六进制
- 45 0x2D

域名压缩规则

  • 在 DNS 报文中,域名可以通过指针进行压缩,指针的格式是两个字节。 DNS 规定域名的长度不能超过 63 字节。NAME 字段中的第一个字节表示长度,最大值为 63(32+16+8+4+2+1=63) ,二进制表示为 00111111,可见高两位是零。因此,约定当高两位设为 11 (即 c0)时,后面的 14 位表示从报文头开始的偏移量。
  • 偏移量是从 DNS 报文的开始位置(即第一个字节)计算的。

案例分析

为了让大家更方便地了解域名压缩的逻辑,我们将采用抓包逐步拆解的方法进行讲解。这里以查询 a.root-servers.net 为例进行演示。

# 客户端 DNS 查询
❯ dig @192.168.1.1 a.root-servers.net.

; <<>> DiG 9.10.6 <<>> @192.168.1.1 a.root-servers.net.
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- \opcode: QUERY, status: NOERROR, id: 62875
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;a.root-servers.net.        IN  A

;; ANSWER SECTION:
a.root-servers.net. 2640    IN  A   198.41.0.4

;; Query time: 7 msec
;; SERVER: 192.168.1.1#53(192.168.1.1)
;; WHEN: Tue Jul 16 19:23:05 CST 2024
;; MSG SIZE  rcvd: 81

# DNS 服务器端抓包
[root@coredns-001 ~]# tcpdump -nnvvXS -i any port 53 -vvv
tcpdump: data link type LINUX_SLL2
dropped privs to tcpdump
tcpdump: listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
19:23:05.461306 enp6s0f0 In  IP (tos 0x0, ttl 63, id 18162, offset 0, flags [none], proto UDP (17), length 75)
    192.168.3.1.63893 > 192.168.1.1.53: [udp sum ok] 62875+ [1au] A? a.root-servers.net. ar: . OPT UDPsize=4096 (47)
        0x0000:  4500 004b 46f2 0000 3f11 c0f2 ac10 1082  E..KF...?.......
        0x0010:  ac10 0b1b f995 0035 0037 dfff f59b 0120  .......5.7......
        0x0020:  0001 0000 0000 0001 0161 0c72 6f6f 742d  .........a.root-
        0x0030:  7365 7276 6572 7303 6e65 7400 0001 0001  servers.net.....
        0x0040:  0000 2910 0000 0000 0000 00              ..)........
19:23:05.461693 enp6s0f0 Out IP (tos 0x0, ttl 64, id 35132, offset 0, flags [DF], proto UDP (17), length 75)
    192.168.1.1.57323 > 223.6.6.6.53: [bad udp cksum 0x9c80 -> 0x90e6!] 13797+ [1au] A? a.root-servers.net. ar: . OPT UDPsize=4096 (47)
        0x0000:  4500 004b 893c 4000 4011 152e ac10 0b1b  E..K.<@.@.......
        0x0010:  df06 0606 dfeb 0035 0037 9c80 35e5 0120  .......5.7..5...
        0x0020:  0001 0000 0000 0001 0161 0c72 6f6f 742d  .........a.root-
        0x0030:  7365 7276 6572 7303 6e65 7400 0001 0001  servers.net.....
        0x0040:  0000 2910 0000 0000 0000 00              ..)........
19:23:05.468441 enp6s0f0 In  IP (tos 0x0, ttl 116, id 35132, offset 0, flags [DF], proto UDP (17), length 91)
    223.6.6.6.53 > 192.168.1.1.57323: [no cksum] 13797 q: A? a.root-servers.net. 1/0/1 a.root-servers.net. [44m] A 198.41.0.4 ar: . OPT UDPsize=1408 (63)
        0x0000:  4500 005b 893c 4000 7411 e11d df06 0606  E..[.<@.t.......
        0x0010:  ac10 0b1b 0035 dfeb 0047 0000 35e5 8180  .....5...G..5...
        0x0020:  0001 0001 0000 0001 0161 0c72 6f6f 742d  .........a.root-
        0x0030:  7365 7276 6572 7303 6e65 7400 0001 0001  servers.net.....
        0x0040:  c00c 0001 0001 0000 0a50 0004 c629 0004  .........P...)..
        0x0050:  0000 2905 8000 0000 0000 00              ..)........
19:23:05.468652 enp6s0f0 Out IP (tos 0x0, ttl 64, id 43342, offset 0, flags [DF], proto UDP (17), length 109)
    192.168.1.1.53 > 192.168.3.1.63893: [bad udp cksum 0x7428 -> 0xfcae!] 62875 q: A? a.root-servers.net. 1/0/1 a.root-servers.net. [44m] A 198.41.0.4 ar: . OPT UDPsize=4096 (81)
        0x0000:  4500 006d a94e 4000 4011 1d74 ac10 0b1b  E..m.N@[email protected]....
        0x0010:  ac10 1082 0035 f995 0059 7428 f59b 8180  .....5...Yt(....
        0x0020:  0001 0001 0000 0001 0161 0c72 6f6f 742d  .........a.root-
        0x0030:  7365 7276 6572 7303 6e65 7400 0001 0001  servers.net.....
        0x0040:  0161 0c72 6f6f 742d 7365 7276 6572 7303  .a.root-servers.
        0x0050:  6e65 7400 0001 0001 0000 0a50 0004 c629  net........P...)
        0x0060:  0004 0000 2910 0000 0000 0000 00         ....)........

4 packets captured
6 packets received by filter
0 packets dropped by kernel

第一个请求包

19:23:05.461306 enp6s0f0 In  IP (tos 0x0, ttl 63, id 18162, offset 0, flags [none], proto UDP (17), length 75)  
    192.168.3.1.63893 > 192.168.1.1.53: [udp sum ok] 62875+ [1au] A? a.root-servers.net. ar: . OPT UDPsize=4096 (47)  
        0x0000:  4500 004b 46f2 0000 3f11 c0f2 ac10 1082  E..KF...?.......  
        0x0010:  ac10 0b1b f995 0035 0037 dfff f59b 0120  .......5.7......  
        0x0020:  0001 0000 0000 0001 0161 0c72 6f6f 742d  .........a.root-  
        0x0030:  7365 7276 6572 7303 6e65 7400 0001 0001  servers.net.....  
        0x0040:  0000 2910 0000 0000 0000 00              ..)........  

第一个响应包

19:23:05.468441 enp6s0f0 In  IP (tos 0x0, ttl 116, id 35132, offset 0, flags [DF], proto UDP (17), length 91)  
    223.6.6.6.53 > 192.168.1.1.57323: [no cksum] 13797 q: A? a.root-servers.net. 1/0/1 a.root-servers.net. [44m] A 198.41.0.4 ar: . OPT UDPsize=1408 (63)  
        0x0000:  4500 005b 893c 4000 7411 e11d df06 0606  E..[.<@.t.......  
        0x0010:  ac10 0b1b 0035 dfeb 0047 0000 35e5 8180  .....5...G..5...  
        0x0020:  0001 0001 0000 0001 0161 0c72 6f6f 742d  .........a.root-  
        0x0030:  7365 7276 6572 7303 6e65 7400 0001 0001  servers.net.....  
        0x0040:  c00c 0001 0001 0000 0a50 0004 c629 0004  .........P...)..  
        0x0050:  0000 2905 8000 0000 0000 00              ..)........  

请求包分析

在第一个请求包中,我们看到查询的是 a.root-servers.net,并且未使用域名压缩。这部分数据从 0x00200x0040 解析如下:

        0x0020:  0001 0000 0000 0001 0161 0c72 6f6f 742d  .........a.root-  
        0x0030:  7365 7276 6572 7303 6e65 7400 0001 0001  servers.net.....  
        0x0040:  0000 2910 0000 0000 0000 00              ..)........  

以下的拆包基于上面的 ASCII 码进行分析,并且每两个字符对一个 ASCII 码。

  • 0161: 01 表示标签长度为 161 表示内容为 a
  • 0c72 6f6f 742d 7365 7276 6572 73: 0c 表示标签长度为 12,内容为 root-servers
  • 03 6e65 74: 03 表示标签长度为 3,内容为 net
  • 00: 结束符,表示域名结束。

响应包分析

在第一个响应包中,我们可以看到从 0x0040 开始有 c00c。这表示使用了域名压缩。

        0x0040:  c00c 0001 0001 0000 0a50 0004 c629 0004  .........P...)..  
  • c0: 前两位 11 表示这是一个指针。
  • 0c: 偏移量 12(十进制的 12)。

偏移量 12 对应的查询部分

在请求包中,从第 12 个字节开始的部分是:

        0x0020:  0001 0000 0000 0001 0161 0c72 6f6f 742d  .........a.root-  
        0x0030:  7365 7276 6572 7303 6e65 7400 0001 0001  servers.net.....  

从第 12 字节开始的内容是:

        0x002c:  0161 0c72 6f6f 742d 7365 7276 6572 7303  .a.root-servers  
        0x0030:  6e65 7400                                .net.  

这段内容解析为:

  • 0161: 标签长度为 1,内容为 a
  • 0c72 6f6f 742d 7365 7276 6572 73: 标签长度为 12,内容为 root-servers
  • 03 6e65 74: 标签长度为 3,内容为 net
  • 00: 结束符,表示域名结束。

响应包的域名压缩解析

在响应包中,c00c 指向了请求包中的域名部分,具体解析如下:

        0x0040:  c00c 0001 0001 0000 0a50 0004 c629 0004  .........P...)..  
  • c00c: 指向偏移量12,即 a.root-servers.net 部分。
  • 0001: 类型 A 记录。
  • 0001: 类 IN(Internet)。
  • 0000 0a50: TTL 为 2640 秒。
  • 0004: 数据长度为 4 个字节。
  • c629 0004: 对应的 IPv4 地址为 198.41.0.4

从这里可能看到 QNAME 未压缩使用了 20 个字节,而 RNAME 压缩后使用了 2 个字节,在响应包中减少了 18 个字节。通过使用 DNS 域名压缩技术,在处理包含重复域名的 DNS 响应时尤其有效,可以显著减少报文的大小,提高网络传输效率。

Avatar photo

关于 木子

Founder of the Rocky Linux Chinese community, MVP、VMware vExpert、TVP, advocate for cloud native technologies, with over ten years of experience in site reliability engineering (SRE) and the DevOps field. Passionate about Cloud Computing、Microservices、CI&CD、DevOps、Kubernetes, currently dedicated to promoting and implementing Rocky Linux in Chinese-speaking regions.
用一杯咖啡支持我们,每一篇 [文档] 都经过我们实操,并非从网上一味的copy,期间花费了大量的心思,希望能够帮忙到您。
暂无评论

发送评论 编辑评论


|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇