DNS 手册域名系统参考

DNS 是怎样工作的

DNS 是一个分布式的数据库:没有哪台服务器知道全部答案,但每台服务器都知道下一步该去问谁。理解了“委派”和“缓存”这两件事,DNS 的大部分行为就都能解释了。

最后修订 · 约 6 分钟读完

名字空间是一棵树#

DNS 的名字从右往左读。www.example.com. 由四个标签组成:www、example、com,以及最右边那个空标签——根。末尾的点代表根,写出来的叫完全限定域名(FQDN)。日常输入时省略这个点,解析器会替你补上;但在区文件里,带不带点意义完全不同(见区文件一节)。

每个节点可以挂数据,也可以有子节点。比较名字时不区分大小写(RFC 4343),所以 WWW.Example.COM 与 www.example.com 是同一个名字,但应答里会原样带回查询时的大小写,这一点后来被用作防伪手段(见缓存投毒)。

区与委派#

“域”(domain)是树上的一棵子树;“区”(zone)是由同一组服务器管理的那一部分。example.com 这个域可能包含 dev.example.com,但如果 dev 被单独委派出去,它就不属于 example.com 区了。委派发生的位置叫区切割(zone cut)。

委派靠父区里的 NS 记录完成:父区声明“这个子域由这些服务器负责”。如果子区的服务器名本身就落在子区里面(比如 ns1.example.com 负责 example.com),父区还要附上它们的地址,否则解析器陷入“要先解析 ns1.example.com 才能解析 example.com”的死循环。这些附带的地址叫胶水记录(glue)。

父子两边各有一份 NS

父区的 NS 是委派用的,不是权威数据;子区顶点上的 NS 才是权威的。两份内容应当一致,不一致时不同解析器可能走向不同的服务器,表现为“时好时坏”。

谁在做什么#

stub 解析器
操作系统或应用里的小组件。它把问题整个交给递归解析器,自己只缓存少量结果。查询时置 RD=1,意思是“请替我查到底”。
递归解析器
也叫全功能解析器或缓存解析器。它从根开始一层层地问,把结果缓存下来,可能还会做 DNSSEC 验证。家用路由器、运营商、公共解析器(1.1.1.1、8.8.8.8、9.9.9.9 等)都属于这一类,或者是它的前端。
转发器
自己不迭代,把查询转给上游递归解析器,只做缓存。家用路由器多半是这种。
权威服务器
持有某个区的数据,只回答自己负责的部分,不替别人递归。应答里的 AA 位表示“这是权威答案”。主服务器(primary)保存原始数据,辅服务器(secondary)通过区传送同步。

一次完整的解析#

下面的演示假设递归解析器刚启动,除了根服务器地址什么都没缓存。可以点按钮,也可以在聚焦按钮后用 ← → 切换。

脚本不可用时,过程可以概括为:设备问递归解析器 → 解析器问根,得到 .com 的转介 → 问 .com,得到 example.com 的转介 → 问 example.com 的权威服务器,得到答案 → 交回设备并缓存。

注意两类应答的区别:转介(referral)的 AA=0、回答节为空,授权节里是下一层的 NS;最终答案的 AA=1。解析器把转介也缓存下来,所以真实网络里的绝大多数查询根本不会到达根服务器——.com 的 NS 记录 TTL 是两天,热门域名的记录几乎总在缓存里。

缓存与 TTL#

每条资源记录都带一个 TTL(生存时间,单位秒),告诉缓存这条数据最多可以保存多久。解析器交出缓存里的答案时,会把 TTL 换成剩余时间,所以连续两次查询看到的 TTL 会递减。

  • TTL 是上限,不是承诺。解析器可以提前丢弃(内存紧张、被清理),很多实现还会把过大的 TTL 截短、把过小的 TTL 抬高。
  • 同一 RRset 共用一个 TTL。RFC 2181 要求同名同类型的一组记录 TTL 相同,不同时解析器按最小值处理。
  • 预取。一些解析器在热门记录快过期时提前刷新,用户就不会撞上缓存失效那一下的延迟。
  • 过期后继续服务(serve-stale)。RFC 8767 允许在权威服务器全部无法访问时,暂时用过期数据回答(建议把过期数据的 TTL 设为 30 秒),换取可用性。它不改变正常情况下的行为。

改记录之前先调低 TTL,是运维里最朴素也最有效的做法:如果原 TTL 是一天,至少要提前一天把它改成几分钟,变更才能很快生效。详见TTL 策略。

否定应答#

“没有”也是答案,而且分两种:

NXDOMAIN(RCODE 3)
这个名字根本不存在。RFC 8020 进一步明确:一个名字不存在,它下面的所有名字也都不存在,解析器可以据此直接回答更深层的查询。
NODATA
名字存在,但没有所问类型的记录。RCODE 是 NOERROR,回答节为空。它没有专门的代码,只能从“NOERROR 且无回答”看出来。

两种情况下,权威服务器都会在授权节里附上区的 SOA 记录。按 RFC 2308,否定结果的缓存时间取 SOA 记录自身的 TTL 与 SOA 中 MINIMUM 字段二者的较小值。把 MINIMUM 设得很大,意味着新增一条记录后,之前查过“不存在”的用户要等很久才能看到它。

CNAME 与别名#

CNAME 表示“这个名字是另一个名字的别名”。解析器遇到 CNAME,会接着查目标名字,直到拿到所需类型的记录。几条规则:

  • 有 CNAME 的名字上不能再有其他数据(DNSSEC 的 RRSIG、NSEC 除外)。这直接导致区顶点(example.com 本身)不能用 CNAME,因为顶点上必须有 SOA 和 NS。
  • CNAME 链越长,解析越慢,出错点也越多。解析器都会给链长设上限,防止环路和滥用,具体数值各实现不同。
  • 一些托管商提供的 ALIAS、ANAME、“CNAME 拉平”是在权威服务器端替你解析好目标、直接返回地址,不是标准记录类型,各家行为不完全一样。标准化的替代方案是 HTTPS 记录的别名模式(AliasMode),见SVCB 与 HTTPS。

什么时候用 TCP#

DNS 默认走 UDP:一问一答,不用建连接,开销最小。但下列情况要用 TCP:

  • 应答比客户端声明的 UDP 缓冲区大,服务器发回截断应答(TC=1),客户端用 TCP 重问;
  • 区传送(AXFR、IXFR);
  • 客户端出于防伪或隐私考虑主动选择 TCP,或者使用建立在 TCP 之上的 DoT。

RFC 7766 把 TCP 支持定为所有 DNS 实现的必选项,不再只是“可选的回退”。防火墙只放行 UDP/53 而拦截 TCP/53 的配置至今仍然常见,它会让签名较大的 DNSSEC 应答随机失败,排错时值得先查。报文层面的细节见报文格式。

DNS 手册 · 内容依据 IETF RFC 与各运营方公开资料整理,出现分歧时以原文为准。回到顶部