在本DNS系列中,我们正在涵盖所有关于DNS的内容。 从客户端的第一个查询一直到后端的所有过程。 DNS如何被保护,TCP、UDP等等所有相关的东西。
在本例中,我们将探讨当一个请求到达递归服务器后,后端会发生什么。 递归服务器究竟是如何完整查找一个域名的?
为什么这很重要
记住这一点至关重要,因为 DNS 是分层的。 世界上没有任何一个单一的服务器拥有所有这些域名,或者知道所有从名称到IP的映射。
所以,我们不能只去一个服务器就获取到信息。 相反,递归服务器会查询其他几台不同的机器,来帮助它完全解析我们正在寻找的名称。
DNS递归查询的工作原理
为了向你展示这个系统是如何工作的,我们先看看这个流程。 我们的客户端会向它的递归名称服务器发出请求。
对于你我来说,很多时候,如果我们在家,这个服务器可能是我们的本地网关。 或者是在我们的实验环境中一个非常简单的系统。
那个设备并没有存储所有已知的查询结果。 相反,它必须为我们执行一些后端的查询工作。
所以,我们向它发送我们的请求,比如 example.com。
我们说:“嘿,如果你缓存里没有,就帮我查一下。”
“如果你缓存里有,就直接把IP地址给我。”
“但如果你没有,那就开始你的工作吧。”
第一步:查询根服务器
那个服务器做的第一件事是,它有一个内置的、它已经知道的根服务器列表。 它会说,我什么都不知道。 我准备去和那个根服务器谈谈。
我们假设这个递归名称服务器的缓存里什么都没有。 它刚从午睡中醒来,必须查找整个链条。
好的,所以我们把 example.com 的请求发送到一个根名称服务器。
这些根名称服务器,它知道的名字只有13个。
当然,这不一定是13台物理服务器。
根名称服务器有数百个,但只有到其中13个的命名连接。
我们进入数据包分析时会讨论这意味着什么。
根服务器收到请求“嘿,给我 example.com”,它会回应说:
“什么?我不认识什么 example。我从来没听说过这个域名。”
“我不是负责这个的。”
“但我知道的是,我知道 .com 域的顶级域(TLD)服务器是谁,或者说这些服务器的列表。”
“好了,去和这些服务器谈谈,它们会给你进一步的指引。”
graph TD;
A[客户端] --> B(递归名称服务器);
B --> C{根服务器};
C -- "我不知道 example.com, <br/>但我知道 .com 的 TLD 服务器" --> B;
第二步:查询顶级域 (TLD) 服务器
递归名称服务器说:“太好了,非常棒。”
然后我把我的请求发送给它。
我会说:“嘿,example.com 怎么样?”
顶级域服务器的工作也不是知道天下所有的域名。 所以,它回复我说:“什么?我是顶级域服务器,但我不是你正在寻找的域名的权威名称服务器。”
graph TD;
B(递归名称服务器) --> D{顶级域 (TLD) 服务器};
D -- "我不知道 example.com, <br/>但我知道它的权威服务器" --> B;
第三步:查询权威名称服务器
它会回复我说:“什么?这是权威名称服务器。去和它谈。” “它才是那个拥有你正在寻找的 A 记录的服务器,无论是什么记录。”
那个权威名称服务器把IP地址返回给我。 然后那个IP地址最终被转发给客户端。
graph TD;
B(递归名称服务器) --> E{权威名称服务器};
E -- "这是 example.com 的 IP 地址" --> B;
B --> A[客户端];
我们刚刚看过的这个图表演示,其实是相当简化的。 很多过程都发生在我们的视野之外,因为我们大多数人都在客户端这一端。
我们只看到我们和递归名称服务器之间发生了什么。 这就是为什么很多时候在Wireshark中捕获DNS解析时,你只看到一个请求发往你的递归服务器,然后它带着一个地址回来。 所以,你只看到那两个数据包。
很多时候你并不知道后端这里发生了什么,所有这些对话。 甚至你的递归名称服务器,很多时候在本地,你的本地设备,可能是你的网关,你的设备,无论是什么,它可能只是一个转发器。 它把请求发送到你ISP中的名称服务器,而那台服务器才是做这些后端工作的。
所以,除非你身处DNS的世界,否则你可能永远不会看到这些对话。 但理解它的工作原理真的非常重要。
深入分析数据包
好了,数据包爱好者们,我们开始吧。
在下面的描述中,我提供了pcap文件,你可以跟着我一起操作。
这个文件叫做 DNS full recursion。
我这样命名是为了让我们能看到后端发生了什么。
初始客户端请求
这里我们有第一个数据包。 顺便说一句,这里有太多东西要解开。 我可以在这个数据包捕获上花好几节课的时间,所以你会有问题,这很正常。
第一个问题,这是我们的终端客户端向其本地递归服务器发出的请求。 让我们看看。
我展开这个,进入查询部分。
我们正在寻找 infoblocks.com。
所以,b2b.info.com,这是我想要的查询。
类型是,我想要一个主机地址。
我想要一个 IPv4 地址。
让我们看看附加记录。 如果我们展开这个,UDP 负载大小。 这里我在说:“嘿,别给我发比这更大的东西。如果超过了,我就得转到TCP。”
递归服务器开始工作
我们发送了那个请求,下一个数据包下来了。 这是来自同一个服务器。 它用IPv4与客户端通信。 但当它进入下一跳时,它用的是 IPv6。
这里是它与 12D0D 通信的地方。它在和谁说话?
让我们看看数据包2。
如果你看一下数据包1,我喜欢Wireshark的这个功能。 你看到,对1的请求,你看到这些虚线,一直延伸到数据包31。 所以,数据包31中的响应是针对数据包1中发出的请求的。 所有这些额外的事情都发生在后端,以使DNS能够做它所做的事情。
[!TIP] 在Wireshark中,你可以通过
编辑 -> 首选项 -> 名称解析,并勾选“使用捕获的DNS数据包数据进行名称解析”来查看解析后的名称,而不是IP地址。这使得分析更容易。
我给解析器起了个名字叫 resolver。
现在我可以看到解析器和它通信的服务器,而不用看那些长长的地址。
与根服务器的对话
所以,这是我的第一个请求,从 resolver 发出。
我出去,只是在查找 infoblocks.com。
如果我进入数据包2,resolver -> g.root-servers.net。
这是我的查询。我把它发送到那个根服务器。
附加记录。现在看看我的UDP负载大小:512。
这是从解析器到那个根服务器的。 很有趣。 它确实接受DNS安全RR。
现在,当我和那个服务器通话时,我做的另一件有趣的事情是。
这有点无关紧要,但我们谈谈。
我正在做一个完整的根查询,权威名称服务器。
“嘿,根服务器,趁着我和你说话,我有没有你最新的信息?”
“我的 g.rootservers.net 信息正确吗?”
因为我的解析器是根据它本地已有的列表来工作的。 递归服务器会做的是,它们知道根服务器是谁以及那些IP。 这个系统正在做的是验证,就像,“嘿,给我最新的。你变了吗?G在别的地方吗?你现在在用不同的IP吗?我应该和谁谈?” 所以,它只是在验证自己的信息。
UDP 到 TCP 的切换
第一个回来的数据包是对第一个根服务的响应。 但看看这个。我转到数据包4。 我们发现了什么?我展开标志。Truncated(已截断)。
哇哦。哇哦。 根服务器回来说:“对不起,我要给你的响应,我要说的一切,远远超过了UDP能处理的范围。” “我得把这个转到 TCP。”
实际上,我甚至不打算开始给你发东西,因为我有太多话要说。
数据包5怎么样?同样的事情。
那个根服务器回来说:“嘿,你想要一些关于 info 的东西,我有很多话要说,而且我会突破我们设定的限制。”
“所以,我得把这个转到TCP。”
解析器领会了,说:“酷。没问题。我不想破坏这些大小规则。” 所以,让我们转到数据包6。 这里我们向那个DNS解析器发起了两个 TCP SYN。
通过 TCP 获取根服务器信息
这是一个很好的过滤时机。 我只想看TCP的对话。
tcp.port == 44653
如果我下到数据包15,这里是好东西。 这是响应。这不再是截断的了。 “嘿,你只是想知道最新的根服务器是什么情况等等。”
有趣的是,这个响应回来了,它给了你所有根服务器的名字。 这是历史性的DNS,但那些根服务器的名字已经很久没变了。 而且它们也很短。它们只是一个字母。
[!NOTE] 根服务器的名称(A、B、C…)之所以这么短,一个原因是为了确保即使包含所有13个根服务器的名称,DNS响应也能保持在 512字节 的限制之内。如果名字更长,就会超出数据报的大小。
好的,所以A、B、C、D等等。 我正在做的是,我只是在更新和刷新这个解析器。 “嘿,看看这个。这是M的IPv4。这是它的IPv4地址。” “这是这些根服务器的所有最新地址。” 不仅是IPv4,我们还要获取v6的。
查询顶级域 (TLD) 服务器
好的,这些信息很棒,但在我的pcap里有点乱。 所以,我要把这些TCP更新流量从视图中移除,专注于主要的查询流程。
我们来看数据包7。
这里是我们的 SYN-ACK。我们进行握手。
然后我们发出一个请求。
我们对根服务器说:“嘿,给我 infoblocks.com。这是我们想要得到的。”
在DNS信息中,这是我们的请求。
infoblocks.com。给我那个A记录。
我的UDP负载大小是1220。
服务器回来说:“好的,这是你的东西。没有截断。给你。”
这里的查询是 infoblocks。
这里我们得到了一些有趣的东西。
根服务器说:“很好。你在请求一个 .com 域名。”
“我不在乎那些 b2b.info 的废话。我只看第一部分。”
“所以是 .com。你想知道和谁谈?谁更了解 .com?”
“你得和这些顶级域服务器谈。”
- E.gtld-servers.net
- L.gtld-servers.net
- K.gtld-servers.net
- H.gtld-servers.net
“你不必和所有这些服务器谈。这里是你的所有选项。” 这里是实际的名字。
附加记录。 这里是所有的名字。但现在我要给你IP地址,让你去和它们谈。 这是你的V4地址。这是那些TLD服务器的V4地址。 如果我再往下展开一点,我还会给你 AAAA 记录(IPv6)。
这个客户端,或者说解析器,正在使用V6。 所以它会想用下面的一个。 它会随机选择一个。
我最终选择了 g.gtld-servers.net。
在数据包19中,这是来自根服务器的响应。
它告诉我:“嘿,g.gtld-servers.net,你下一步要去的那个顶级域服务器。这是地址:EA330。”
我的解析器说:“酷。让我去和那个谈谈。”
查询权威名称服务器
解析器确认了。它说谢谢。 但看数据包21。这里我进入了下一步。 现在我完成了TCP的部分。
数据包19是通过TCP的。它说我选择了顶级域G。
解析器说:“好的,顶级域,我接下来和你谈。”
这是通过 UDP 的,这是我同样的请求 infoblocks.com,给我一个A记录。
我被限制在512字节,所以要注意。
7毫秒后,服务器回来了。这是顶级域服务器。
它说:“我看到你的 info.com 了。”
“现在,我不是那个知道 infobox 的。我知道 .com,但我不知道 infobox。”
“所以,我要把你转到权威名称服务器,那个保持信息最新、保持更新的服务器。”
“那才是你想谈的那个。”
然后它给了我们下一步的记录。
你有 ns5.infoblocks.com,ns6.infoblocks.com,ns7,ns1。
如果我往下看一些附加记录,那里我可以看到那些不同服务器的地址。
我选择了 NS5。
看这里,NS5。这是那个知道最终答案的权威名称服务器的完整地址。
那个是不断更新的。
TLD就像是说:“看,我只知道 .com。我只能把你送到那家伙那里。他才是知道的。”
获得最终答案
下一个数据包24。我进入下一步。 现在我到了最后一步,权威名称服务器。 我联系它,说:“嘿,我被告知来找你。” “仍然限制在那个512字节。如果你愿意,可以给我发DNSSEC。”
数据包30回来了,看看我们。 这里我们有了我们实际的答案资源记录。 终于,我们不再是寻找那个知道答案的家伙了。我们终于找到他了。
现在,查询 infoblocks.com 的答案。
这是你的IP:8.39.143.138。
然后解析器转过身来说:“好的,终于,这是响应。”
“这是你请求的,终端客户端,infobox.com。”
“这是那个A记录。这是你请求的IP地址:8.39.143.138。”
“现在去吧。去发起那个TCP连接。去做那个ping。去做任何你想做的事。”
“这就是你寻找的地址。”
结论
后端有很多步骤,不是吗? DNS超级有趣。我喜欢看到所有这些实际操作。 我们去了根服务器,我们去了顶级域,我们去了权威名称服务器。
现在这个过程,正如我们提到的,对最终用户来说通常是不可见的。 他们看不到后端这么多事情。 但后端发生了这么多事,难道不有趣吗?
[!WARNING] 这也是为什么我们不应该在DNS环境中阻塞TCP的另一个原因。这会给我们带来麻烦,因为如果一个记录大于负载允许的大小,解析器必须使用TCP来传输它。所以要小心,对阻塞TCP要非常谨慎。实际上,这根本不是一个好做法。DNS不仅仅是通过UDP工作的。
好了,这就是你的完整递归查询。 如果需要,可以再看一遍这篇文章。
接下来我们去哪里? 下一步,安全。 不仅仅是保护数据本身,还要保护协议。 今天这是怎么做的?有什么后果? 我们应该真正理解DNSSEC、DNS over TLS,甚至DNS over HTTPS之间有什么区别? 那将是我们下一篇文章的内容。