本页目录

DNS 协议与解析

互联网的"电话簿"——从根服务器到递归解析器到权威服务器,一次 DNS 查询可能触发数十次委派追踪。edns0、anycast、负缓存,这些机制让 DNS 在日均万亿次查询下仍然可用。

概述

DNS 是互联网的"电话簿"——将人类可读的域名翻译为 IP 地址。设计于 1983 年(RFC 882/883),分布式和层次化的架构使其可以扩展到互联网规模:从 root 到 TLD 到 authoritative,每一层只负责自己的命名空间。递归解析器做"繁重工作"——替 stub client 完成从 root 到 answer 的全过程。DNS 最初是无安全设计的协议(UDP 明文),后来的 DNSSEC 和加密传输(DoT/DoH/DoQ)都在修补这个缺憾。

DNS 查询流程

DNS 查询流程:从 stub resolver 到 authoritative ① stub resolver(libc / systemd-resolved) 读 /etc/resolv.conf → nameserver(如 127.0.0.1:53, AdGuard) ② recursive resolver(如 AdGuard → upstream DoH/DoT) 查 local cache → miss → 从 Root hints 开始 ③ Root(a–m.root-servers.net) 问 "com. NS?" → + glue: a.gtld-servers.net A 192.5.6.30 ④ TLD(com) 问 "example.com NS?" → + glue: ns1.example.com A ... ⑤ Authoritative(ns1.example.com) 问 "www.example.com A?" → answer: 93.184.216.34(+ 可选 RRSIG/NSEC) ⑥ stub resolver → 返回给客户端 recursive resolver 对 stub 是一次代劳(recursive):自己逐跳发起 iterative 查询 (root → TLD → authoritative),拼出最终答案再返回。

三种查询类型:

  • Recursive (RD=1): resolver 替客户端全程递归 (stub → recursive)
  • Iterative: 一次返回 referral 或 answer (recursive → authoritative)
  • Inverse: PTR 查询 (IP → name, 极少用)

DNS Wire Format

DNS Message (TCP or UDP):

Header (12 bytes):
  Transaction ID (2B): 随机, 匹配 request ⇔ response
  Flags (2B):
    QR (bit 0): 0=Query, 1=Response
    OPCODE (bits 1-4): 0=Standard, 1=Inverse, 2=Status, 4=Notify, 5=Update
    AA (bit 5): Authoritative Answer
    TC (bit 6): Truncated (use TCP)
    RD (bit 7): Recursion Desired
    RA (bit 8): Recursion Available
    Z (bits 9-11): 0 (reserved)
    RCODE (bits 12-15): 0=NoError, 1=FormErr, 2=SrvFail, 3=NXDomain, 5=Refused
  QDCOUNT (2B): Questions count
  ANCOUNT (2B): Answers count
  NSCOUNT (2B): Authority records count
  ARCOUNT (2B): Additional records count

Question Section (variable):
  QNAME: label format (见下)
  QTYPE (2B): 1=A, 28=AAAA, 5=CNAME, 15=MX, 2=NS, 6=SOA, 33=SRV, ...
  QCLASS (2B): 1=IN (Internet)

Answer/Authority/Additional Sections (variable):
  每个 RR:
    NAME: label format (通常 compressed — pointer to previous name)
    TYPE (2B)
    CLASS (2B): 1=IN
    TTL (4B): cache 有效期 (秒)
    RDLENGTH (2B): RDATA 长度
    RDATA: type-specific

Label Format 与 Compression

Label encoding:
  [length 1B][label data ...] → 长度 + 数据, 每 label 独立
  最后一个 label: length=0 (root, 空 label)

  例: www.example.com → 0x03 'w' 'w' 'w' 0x07 'e' 'x' 'a' 'm' 'p' 'l' 'e' 0x03 'c' 'o' 'm' 0x00

DNS Compression (节省空间):
  高 2 bits of length byte = 11 → 这是 pointer (不是 label!)
  pointer = 低 14 bits of offset (from DNS message start)
  例: 0xC0 0x0C → 指向 offset 12 (第一个 question 的 QNAME)

  这允许在同一消息中重复引用之前出现的 domain name
  → "example.com" 出现在 QNAME, A record 的 NAME, NS record 的 RDATA → 三次引用 → 省 ~12+12+12 = 36 bytes

关键 Record Types

A (type 1):  RDATA = 4-byte IPv4 address
AAAA (type 28): RDATA = 16-byte IPv6 address
CNAME (type 5): RDATA = canonical name (label format) — 别名解析
  → 限制: CNAME 不能与其他类型共存 (除了 RRSIG/NSEC)
    即: 如果 www.example.com 是 CNAME, 它不能同时有 A record
MX (type 15): RDATA = preference (2B) + exchange (label format)
  → 优先级: 低 preference = 高优先级
SOA (type 6): RDATA = mname + rname + serial + refresh + retry + expire + minimum TTL
  → zone 的权威信息: serial 用于 zone transfer 版本比较
NS (type 2): RDATA = nameserver (label format) — 授权此 zone 的 nameserver
SRV (type 33): RDATA = priority + weight + port + target
  → _service._proto.name → 目标 host:port
  例: _sip._tcp.example.com SRV 10 60 5060 sipserver.example.com
PTR (type 12): RDATA = name (label format) — reverse lookup (IP → name)
  → in-addr.arpa for IPv4, ip6.arpa for IPv6
TXT (type 16): RDATA = one or more text strings (length-prefixed)
  → SPF, DKIM, DMARC, verification tokens
CAA (type 257): RDATA = flags + tag + value
  → 限制哪些 CA 可以为此 domain 签发证书
  → 例: 0 issue "letsencrypt.org" → 只有 Let's Encrypt 能签

EDNS0 (RFC 6891)

传统 DNS 限制: UDP payload 最大 512B → 大型应答 (DNSSEC RRSIG) 需要 TCP fallback

EDNS0: 在 Additional section 加 OPT pseudo-RR:
  NAME=0 (root), TYPE=41 (OPT), CLASS=UDP payload size, TTL=flags+RCODE

  UDP payload size: 通告接收方能处理的最大 UDP 响应 (通常 1232 or 4096)
  → 减少 TCP fallback 次数
  DNSSEC OK (DO bit): client 支持 DNSSEC → server 应该附带 RRSIG

  flags: DNSSEC OK (DO bit 15)

AFXR (Zone Transfer)

AXFR (full zone transfer):
  常用于: primary → secondary nameserver 同步

  TCP connection (zone transfer 可能 >512B):
    secondary: AXFR request (type 252, class IN) → primary
    primary: 返回 zone 中所有 RRs (SOA ... SOA, 头尾两个 SOA)

IXFR (incremental zone transfer, RFC 1995):
  只传输变化 (基于 SOA serial)

参考

  • RFC: 1034, 1035, 6891, 3596 (AAAA), 2782 (SRV), 6844 (CAA)
  • 工具⁠: dig +trace, drill, kdig, delv, tcpdump -nn port 53
  • 源码⁠: glibc resolv/, systemd-resolved

Keywords: DNS, A/AAAA/MX/NS/SOA/SRV/CNAME/TXT/CAA, recursive resolver, EDNS0, label compression, zone transfer