起因

之前在面试时,被问到了DNS底层采用了什么协议。“UDP。”我脱口而出。面试官追问:“还有么?”我又想了想,说DoT和DoH会用TCP协议。面试官对我的回答并不是特别满意,说普通的DNS查询也支持用TCP协议。
面试结束之后,查询了相关资料,发现理论上标准的DNS协议确实也支持TCP传输,但一般的DNS客户端会比较少遇到这种情况,因为:

  1. 只有在解析配置过于复杂,或者记录值过长,使得没有自带分包功能的UDP协议无法承载的情况下,才会用TCP查询。
  2. 很多DNS服务提供商不支持TCP查询。

既然已经知道了对应的原理,有必要做个小实验来验证一下。

实验

我采用的系统环境是Windows 11 23H2,运行着Wireshark 4.2.4。为了采集DNS协议的包,我们可以使用port 53或者dns过滤器。

UDP协议的普通DNS查询与响应

开始捕获后,我们可以打开终端,用nslookup命令查询任意常见域名的对应IP地址。我这里用了自己博客的域名。

PS C:\Users\tian> nslookup tian051011.me
服务器:  RT-AC1900P-B8B8.lan
Address:  192.168.50.1

非权威应答:
名称:    tian051011.me
Address:  185.186.147.19

如果没有出意外的话,Wireshark应该就捕获到我们的DNS查询的查询和响应了。如下图,可以看到此时的DNS查询与响应确实采用了UDP协议。
普通的DNS查询采用了UDP协议

普通的DNS响应也采用了UDP协议

TCP协议的DNS查询

首先我们需要寻找或构造一个够长的记录,让UDP塞不下这个记录,服务器才会给我们发TCP格式的响应。
这里我给tian051011.me这个域名增加了test主机记录,记录类型为TXT,记录内容是768个1,实测DNSPod是支持这么长的TXT记录的。
长长长长的记录
让我们来看看对这个域名进行DNS查询会发生什么吧。

PS C:\Users\tian> nslookup -type=TXT test.tian051011.me
服务器:  RT-AC1900P-B8B8.lan
Address:  192.168.50.1

非权威应答:
test.tian051011.me      text =

        "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
        "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
        "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
        "11"

nslookup成功查出来了我们设置的TXT记录。让我们看看Wireshark那边都捕获到了些什么。
Wireshark捕获到了TCP格式的DNS查询与响应
我们可以发现客户端首先发送了UDP格式的查询,并得到了UDP格式的响应,但这个响应很特殊,里面并没有Answers,Truncated标记位也告诉我们这个响应是被截断的。
响应被截断
接下来,客户端就用TCP发送了查询请求,并得到了TCP包中的完整响应。
TCP DNS查询请求

TCP DNS响应

不支持TCP协议的DNS服务器

根据我的实测,不同的DNS服务提供商对于这种响应超长的查询会有不同的响应,例如114DNS会直接响应“无可用记录”、阿里云公共DNS会出现“未定义错误”,这也反映出很多DNS服务器的实现可能不符合RFC标准。
我这里采用了DNSPod Public DNS,让我们来看看这种情况下对test.tian051011.me进行TXT查询会发生什么吧。

PS C:\Users\tian> nslookup -type=TXT test.tian051011.me 119.29.29.29
服务器:  pdns.dnspod.cn
Address:  119.29.29.29

tian051011.me   nameserver = f1g1ns1.dnspod.net
tian051011.me   nameserver = Invalid Name at offset 80!


*** Error: record size incorrect (-408812189280 != 10)

tian051011.me   nameserver = f1g1ns1.dnspod.net
tian051011.me   nameserver = Invalid Name at offset 80!


*** Error: record size incorrect (-408812189280 != 10)

*** pdns.dnspod.cn 找不到 test.tian051011.me: Unspecified error

nslookup报错了,从报错信息我们可以大致了解到出现的问题是记录大小不对。那么在这种情况下,Wireshark捕获到了什么呢?
首先,查询和响应都没有出现TCP格式。

其次,DNS响应的记录是损坏的,Truncated标记位也没有被设置。

另一种情况-区域传送

其实,还有一种情况,DNS记录基本是在TCP协议下被传送的,那就是“DNS区域传送(zone-transfer)”。在这种情况下,一台备用服务器会使用来自主服务器的数据来刷新自己的域(zone)数据库。由于这种传送要求可靠性高且数据量大,一般会采用TCP协议,不过一般用户是接触不到这种情况的。目前暂时没有条件做区域传送的实验,可以先了解一下相关概念。