DNS 是互联网的电话簿,但它的报文只有区区几十个字节:12 字节头、问题区、几条资源记录。这篇手工拼一个查询、逐字节解析 1.1.1.1 的响应,看标志位、RCODE、名字压缩指针,并对照递归解析器与权威服务器的行为。
0000 1a 2b 01 00 00 01 00 00 00 00 00 00 07 65 78 61 .+...........exa
0010 6d 70 6c 65 03 63 6f 6d 00 00 01 00 01 mple.com.....
共 29 字节 = 12 字节头 + 问题区。头部:ID=1a2b 标志=0100(0x0100:QR=0 查询,Opcode=0,RD=1 期望递归) QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0
问题区:07 "example" 03 "com" 00 = 13 字节的名字(每个标签前一个长度字节,以长度 0 结束),后面 QTYPE=1(A)、QCLASS=1(IN)各 2 字节
对照 dnspython 生成的报文:29 字节,与手工拼的逐字节相同12 字节头(ID、标志、QDCOUNT 等 4 个计数)加问题区;名字是“长度 + 标签”序列,以 0 结束。
0000 1a 2b 81 80 00 01 00 02 00 00 00 00 07 65 78 61 .+...........exa
0010 6d 70 6c 65 03 63 6f 6d 00 00 01 00 01 c0 0c 00 mple.com........
0020 01 00 01 00 00 01 10 00 04 68 14 17 9a c0 0c 00 .........h......
0030 01 00 01 00 00 01 10 00 04 ac 42 93 f3 ..........B..
响应 61 字节,往返 290.3 ms;ID 0x1a2b(与查询相同:True),标志 0x8180 = QR RD RA,RCODE = NOERROR;问题 1 / 回答 2 / 授权 0 / 附加 0
[an] example.com 类型 A 类 1 TTL 272 RDLENGTH 4 RDATA 104.20.23.154
[an] example.com 类型 A 类 1 TTL 272 RDLENGTH 4 RDATA 172.66.147.243
回答区里记录的名字写成 2 字节的指针 c00c:高两位 11 表示“这是指针”,低 14 位 = 12 是报文内的偏移(从 ID 开头算),指向第 12 字节——问题区里的 example.com
报文里共有 2 个记录名使用了压缩指针响应里的记录名写成 c0 0c:指针指向报文内偏移 12。TTL、RDLENGTH、RDATA 紧随其后。
递归解析器,RD=1,存在的名字 :标志 QR RD RA;RCODE NOERROR;回答 2 授权 0 附加 0;61 字节
递归解析器,RD=0(不要求递归) :标志 QR RA;RCODE NOERROR;回答 2 授权 0 附加 0;61 字节
递归解析器,example.com 下不存在的子名字 :标志 QR RD RA;RCODE NOERROR;回答 0 授权 1 附加 0;108 字节
递归解析器,不存在的顶级域 .invalid :标志 QR RD RA;RCODE NXDOMAIN;回答 0 授权 1 附加 0;117 字节
NXDOMAIN 的授权区带 SOA(1 条):其中的最小 TTL 字段决定这个否定答案能缓存多久(负缓存,下一篇展开)
example.com 的 NS 记录:['hera.ns.cloudflare.com', 'elliott.ns.cloudflare.com']
其中 hera.ns.cloudflare.com 的地址 108.162.192.162(公网地址)
权威服务器,RD=1(权威服务器不递归) :标志 QR AA RD;RCODE NOERROR;回答 2 授权 0 附加 0;61 字节
权威服务器,RD=0 :标志 QR AA;RCODE NOERROR;回答 2 授权 0 附加 0;61 字节
权威服务器,不是它托管的名字 www.kernel.org :标志 QR RD;RCODE REFUSED;回答 0 授权 0 附加 0;32 字节递归解析器 QR RD RA;权威服务器 QR AA,没有 RA;不托管的名字得 REFUSED;不存在的顶级域得 NXDOMAIN + SOA。
example.com NS:实际响应 84 字节(回答 2 / 授权 0 / 附加 0);全部展开的估算 126 字节,压缩省了 42 字节(33%) example.com MX:实际响应 44 字节(回答 1 / 授权 0 / 附加 0);全部展开的估算 55 字节,压缩省了 11 字节(20%) www.iana.org A:实际响应 107 字节(回答 3 / 授权 0 / 附加 0);全部展开的估算 181 字节,压缩省了 74 字节(41%) iana.org NS:实际响应 113 字节(回答 4 / 授权 0 / 附加 0);全部展开的估算 180 字节,压缩省了 67 字节(37%) iana.org SOA:实际响应 80 字节(回答 1 / 授权 0 / 附加 0);全部展开的估算 104 字节,压缩省了 24 字节(23%)
真实响应里压缩省了 20%~41% 的字节,多记录响应省得更多。
发 eXaMpLe.CoM → 响应问题区里写的是 eXaMpLe.CoM;回答里的记录名 ['eXaMpLe.CoM'] 发 EXAMPLE.COM → 响应问题区里写的是 EXAMPLE.COM;回答里的记录名 ['EXAMPLE.COM'] 发一个长度字节为 64 的名字 → 3 次都没有响应(服务器直接丢弃) 发一个长度字节为 0x80 的名字 → 3 次都没有响应(服务器直接丢弃)
服务器原样回显大小写;长度字节 64 和 0x80 的畸形名字被 1.1.1.1 丢弃,没有回 FORMERR。