伯阳的网络笔记:DNS
伯阳的网络笔记(一):DNS
先出几个问题,带着问题去阅读更加有效。
- DNS 是什么?有什么用?
- DNS 的请求过程是怎么样的。递归查询和迭代查询是什么?有何区别。
- DNS 中用到了哪些协议?为什么要用?
- DNS 有什么缺点。
一. DNS 是什么?
DNS,全名 Domain Name System ,翻译过来就是域名系统,是一个分层的分布式数据库,也是一个使得主机能够查询到 IP 地址的应用层协议。因为有了它,使得我们可以方便的直接使用域名,而非可能随时更换的 IP 地址;可以说,DNS 就是一本因特网的电话簿。
说到域名系统,就不得不先说一下域名是什么。
举个最简单的例子, www.baidu.com
就是一个域名,用来做终端的定位标志;这个定位标志用 IP 地址也是可以的。话说到这里,你可以在终端中输入ping www.baidu.com
就可以获得一个对应的 IP 地址,直接使用 IP 地址放到浏览器里,会发现一样能访问到百度。
二. 什么要设计 DNS
一般来说有以下几个原因:
- IP 地址是四段数字,不是很容易让人记住,还容易记混。就如同,不用电话簿,普通人根本无法背下很多电话号码;
- 服务器自身的 IP 地址可能会发生改变;
- 假如你进行的长途的旅行,从北京跑到了西雅图,你总不会还要连接半个地球那边的服务器吧?DNS 可以让一个域名映射多个 IP 地址。
总结下来,就是 DNS 让我们使用者和提供服务者之间提供了一道枢纽,我们并不需要每时每刻的直接接触到 IP 地址,避免一些不必要的问题。当然,这并非天衣无缝。
三. 相关文档
在维基百科中,记录了所有有关的 RFC 文件,我只记录一些我认为比较重要或者有意义的文件(实际上我也没一一去阅读它们)。
- RFC882 : DOMAIN NAMES - CONCEPTS and FACILITIES
- RFC883 : DOMAIN NAMES - IMPLEMENTATION and SPECIFICATION
- 在1983年11月,发布了第一版的 DNS 设计。设计 DNS 的缘由,以及缓存、分布式数据库的内容都在这里提出。
- RFC1034 : DOMAIN NAMES - CONCEPTS AND FACILITIES
- RFC1035 : DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
- 修改和完善了之前的设计方案,添加了 TCP 协议作为 UDP 的补充;
- UDP 承载的消息限制为 512 字节,再长的话,会被截断并且设置 TC 位。
- RFC1123:Requirements for Internet Hosts – Application and Support
- 未来的 DNS 记录类型可能会超过 512 字节,要做好准备;
- 我们需要 TCP!
- RFC6891 : Extension Mechanisms for DNS (EDNS(0))
- EDNS 为 DNS 提供了扩展功能,让 DNS 通过 UDP 协议携带最多 4096 字节的数据;
- RFC7766 : DNS Transport over TCP - Implementation Requirements
- 所有的 DNS 服务器,都必须同时支持 TCP 和 UDP ;
- RFC1123 中的”未来“已经来到了;
- EDNS 并不可靠。
- RFC7858 : Specification for DNS over Transport Layer Security (TLS)
- 引入 TLS 来保障隐私。
- RFC8484 : DNS Queries over HTTPS (DoH)
- 引入 HTTPS。
注:TC–(TrunCation),截断:指的是消息由于长度大于传输通道上允许的长度而被截断
四. 工作原理和流程
域名层级
在上面,我们提到了,DNS 是是一个分层的分布式数据库。DNS服务器就像一棵树一样,从上到下,依次有着不同的能力。
与之对应的有四类DNS服务器:
- 根 DNS 服务器
- 顶级 DNS 服务器
- 权威 DNS 服务器
- 本地 DNS 服务器
与此对应,域名的也是有如同上图的层级系统。DNS 解析器需要从根 DNS 服务器查找到顶级域名服务器的 IP 地址,又从顶级 DNS 服务器查找到权威 DNS 服务器的 IP 地址,最终从权威 DNS 服务器查出了对应服务的 IP 地址(这是递归查询的流程,下面会讲还有另外一种方式)。
而且,在获得过 IP 地址之后,各级 DNS 服务器会将其缓存一段时间,以便下次获取的时候更快。
解析流程
借用之前看过的一张图来表示一下请求流程(这是迭代查询的过程,递归过程一想就明白)
- 客户端会发出一个 DNS 请求,问
www.baidu.com
的 IP 是啥啊,并发给本地域名服务器 (本地 DNS ); - 本地 DNS 收到来自客户端的请求。现在本机的缓存上查找。如果能找到,它直接就返回 IP 地址。如果没有,本地 DNS 会往上询问;
- 根 DNS 收到来自本地 DNS 的请求,发现后缀是
.com
,继续往顶级域名服务器询问; - 本地DNS转向问顶级域名服务器,顶级域名服务器就是大名鼎鼎的比如
.com
、.net
、.org
这些一级域名,它负责管理二级域名,比如baidu.com
,所以它能提供一条更清晰的方向。 - 顶级 DNS 服务器发出权威 DNS 服务器的地址;
- 本地 DNS 转向权威 DNS 服务器询问;
- 权限 DNS 服务器查询后将对应的 IP 地址 x.x.x.x 告诉本地 DNS ;
- 本地 DNS 再将 IP 地址返回客户端,客户端和目标建立连接。
递归查询与迭代查询
递归查询指的是在 DNS 查询过程中,一直是以本地 DNS 服务器为中心的,客户端只是发出原始的域名查询请求报文,然后就一直处于等待状态的,不断的向上一级 DNS 服务器请求完整的 IP 地址,直到本地 DNS 服务器发来了最终的查询结果。这个是默认选择。
而迭代查询的过程就是上节那张图一般。
当根域名服务器收到本地域名服务器发出的迭代查询请求报文时,要么给出所要查询的 IP 地址,要么告诉本地服务器:“你下一步应当向哪一个域名服务器进行查询”。
接着让本地 DNS 服务器进行后续的查询。根 DNS 服务器通常是把自己知道的顶级 DNS 服务器的 IP 地址告诉本地 DNS 服务器,让本地 DNS 服务器再向顶级 DNS 服务器查询。
顶级域名服务器在收到本地域名服务器的查询请求后,要么给出所要查询的 IP 地址,要么告诉本地服务器下一步应当向哪一个权限域名服务器进行查询。
即:
- 递归查询:客户端只发一次请求,要求对方给出最终结果。
- 迭代:客户端发出一次请求,对方如果没有授权回答,它就会返回一个能解答这个查询的其它名称服务器列表,客户端会再向返回的列表中发出请求,直到找到最终负责所查域名的名称服务器,从它得到最终结果。
迭代查询的要求有以下两点:
- 客户端的请求报文中没有申请使用递归查询,即在 DNS 请求报头部的 RD 字段(表示期望递归)没有置 1。
- 客户端在 DNS 请求报文中申请使用的是递归查询(也就是RD字段置1了),但在所配置的本地 DNS 服务器上是禁用递归查询(DNS 服务器一般默认支持递归查询的),即在应答 DNS 报文头部的 RA 字段(表示可用递归)置 0。
一般的情况来说,我们向本地 DNS 服务器查询的过程是一般是递归查询,本地 DNS 服务器再向其他 DNS 服务器获取 IP 地址的过程是是迭代的几率比较大。但是从理论上讲,任何 DNS 查询,都可能是递归的,也都可能是迭代的。
可以查看我的这篇文章来了解如何使用 dig 命令查看 DNS 信息。
五. DNS 其实不止用到 UDP
前段时间我看了大左的一篇文章为什么 DNS 使用 UDP 协议,初看标题给我两个感觉:
- 我靠,DNS 服务器会使用 UDP 不是很正常的事吗?直接原因不就是因为 UDP 不能保持连接么?
- 我去,DNS 服务器可不止用到 UDP 啊,30 年前就有 TCP 了;现在(2019 年)有的DNS服务连 HTTPS 都要用上了,灯塔也会范这个错误?
当然,随着我看了完了文章,才知道大左不过是搞了一个标题党罢了。
在上面的文档小节中,我们了解了 TCP、TLS、HTTPS 不断加入 DNS 的过程。作为不断成长的方案,最开始的 DNS 存在很多的问题,后来不断地去修正。在最开始 DNS 服务器互相传递消息的时候,因为 UDP 的不稳定,在紧急时刻需要 TCP 来当做备份工具。
在最开始设计的 DNS 方案中,每次 DNS 的记录是有着 512 字节限制,但是随着需求(IPv6、安全)的增多,数据包越来越大,让 DNS 不得不去寻找新的解决方案。
最开始,人们制定了一个 EDNS,是 DNS 的扩展,可以让 UDP 传输最多 4096 字节的数据。但是用过了一阵子之后,大家发现这个东西没有想象中的可靠,不是很稳定。DNS 传输的稳定性是不能太差的,而 TCP 能解决这个问题,于是 TCP 就被”扶正“了,不再是”备胎“。
随着时间的推移,人们对于隐私安全愈发的重视, TLS 和 HTTPS 也陆续的加入 DNS 协议中。
我画了张图,记录了加入其它协议的一些重要文件。
DNS 中 UDP 对比 TCP
这里就不多介绍这两位了,大家自然明白。我拿大左的文章来做个讲解。
DNS 请求的数据都会以二进制的形式封装成如下的所示的 UDP 数据包中,下面就是一个调用 DNS 服务器获取 www.baidu.com
域名 IP 地址的请求,从第四行的 05 字节开始到最后就是 DNS 请求的内容,整个数据包中除了 DNS 协议相关的内容之外,还包含以太网、IP 和 UDP 的协议头:
0000 b0 6e bf 6a 4c 40 38 f9 d3 ce 10 a6 08 00 45 00 .n.jL@8.......E.
0010 00 3b 97 ae 00 00 40 11 0b 0a c0 a8 32 6d 72 72 .;....@.....2mrr
0020 72 72 f3 27 00 35 00 27 6b ee 0c 5a 01 00 00 01 rr.'.5.'k..Z....
0030 00 00 00 00 00 00 03 77 77 77→05 62 61 69 64 75 .......www.baidu
0040 03 63 6f 6d 00 00 01 00 01 .com.....
虽然每一个 UDP 数据包中都包含了很多以太网、IP、UDP 以及 DNS 协议的相关内容,但是上面的 DNS 请求大小只有 73 个字节,上述 DNS 请求的响应也只有 132 个字节,这对于今天其他的常见请求来讲都是非常小的数据包:
0000 38 f9 d3 ce 10 a6 b0 6e bf 6a 4c 40 08 00 45 00 8......n.jL@..E.
0010 00 76 00 00 00 00 96 11 4c 7d 72 72 72 72 c0 a8 .v......L}rrrr..
0020 32 6d 00 35 f3 27 00 62 5b c2 0c 5a 81 80 00 01 2m.5.'.b[..Z....
0030 00 03 00 00 00 00 03 77 77 77 05 62 61 69 64 75 .......www.baidu
0040 03 63 6f 6d 00 00 01 00 01 c0 0c 00 05 00 01 00 .com............
0050 00 02 cb 00 0f 03 77 77 77 01 61 06 73 68 69 66 ......www.a.shif
0060 65 6e c0 16 c0 2b 00 01 00 01 00 00 01 18 00 04 en...+..........
0070 3d 87 a9 7d c0 2b 00 01 00 01 00 00 01 18 00 04 =..}.+..........
0080 3d 87 a9 79 =..y
UDP 和 TCP 的区别大家都清楚。一个 TCP 通信,是需要三次握手的,在不得已使用 TCP 的情况下,会比使用 UDP 多出来很多额外开销:
- TCP 建立连接需要进行三次网络通信;
- TCP 建立连接需要传输 ~130 字节的数据;
- TCP 销毁连接需要进行四次网络通信;
- TCP 销毁连接需要传输 ~160 字节的数据;
如果是 DNS 刚刚提出的那几年里,TCP 确实没有在 DNS 中使用的必要,或者说仅仅当做一个紧急时刻的备份就行(有的 DNS 服务器甚至不支持 TCP)。
然而,时代变了!
我们现在网络情况远比几十年前复杂,IPv4 就要被 IPv6 替代,人们对于数据安全也越来越重视。DNS 需要传递的数据越来越大,MTU 可能会直接截断数据,我们要想别的办法了。
MTU是啥
MTU (Maximum Transmit Unit),最大传输单元,即物理接口(数据链路层)提供给其上层最大一次传输数据的大小,比如 IP 层、MPLS 层等等,因为目前应用最多的接口是以太网,所以谈谈以太网口的 MTU 。
假定其上层协议是 IP ,缺省 MTU = 1500,意思是:整个 IP 包最大从这个接口发送出去的是 1500 个字节。可以通过配置修改成更大或更小的值,只要在系统的边界值以内即可,但是切记要在链路的两端都要修改,而且要大小一样,如果不一样,会造成大侧的数据被小侧丢弃!
较大的 MTU 带来更高的效率,因为每个网络数据包都承载更多的用户数据,而协议开销(例如报头或每个数据包的基本延迟)则保持不变。更高的效率意味着批量协议吞吐量的提高。较大的 MTU 还要求处理相同数量的数据的较少数据包。在某些系统中,按数据包处理可能是关键的性能限制。
但是,这种收益并非没有缺点。大数据包比小数据包占用慢速链接的时间更多,从而导致后续数据包的延迟更大,增加了网络延迟和延迟变化。例如,一个 1500 字节的数据包(以太网在网络层允许的最大数据包)占用了 14.4k ,需要调制解调器大约一秒钟的时间。
而且在存在通信错误的可能性的前提下,大数据包也会有额外的问题:如果不使用前向纠错功能,则数据包中单个位的损坏将要求重新传输整个数据包,这会增加成本。在给定的误码率下,较大的数据包更容易受到损坏。更大的有效负载使重发更大的数据包需要更长的时间。
更加详细的内容,可以查看这份文件。
TCP 扶正
为了防止 MTU 直接截断 DNS 文件,直接将支持 TCP 作为 DNS 服务器的强制要求。
TCP 作为可靠的传输协议,通过序列号、重传等机制能够保证消息的不重不漏,消息接受方的 TCP 栈会对分片的数据重新进行拼装,DNS 等应用层协议可以直接使用处理好的完整数据。同时,当数据包足够大的时候,TCP 三次握手带来的额外开销比例就会越来越小,与整个包的大小相比就会趋近于 0。
- 当 DNS 数据包大小为 500 字节时,TCP 协议的额外开销为 ~41.2%;
- 当 DNS 数据包大小为 1100 字节时,TCP 协议的额外开销为 ~20.7%;
- 当 DNS 数据包大小为 2300 字节时,TCP 协议的额外开销为 ~10.3%;
- 当 DNS 数据包大小为 4800 字节时,TCP 协议的额外开销为 ~5.0%;
所以,我们在 DNS 中存储较多的内容时,TCP 三次握手以及协议头带来的额外开销就显得不是那么关键了,不过我们 TCP 三次握手带来的三次网络传输耗时还是没有办法避免的,这也是我们在目前的场景下不得不接受的现实。
六. DNS 的存在的问题
当然,没有什么设计是完美的,尤其 DNS 协议是一个本身就刻意设计的简单的协议。
1. 域名缓存问题
它可以在本地做一个缓存,也就是说,不是每一个请求,它都会去访问权威 DNS 服务器,而是访问过一次就把结果缓存到自己本地,当其他服务来询问的时候,直接就返回这个缓存数据。
一般来说,iOS 设备是一天清除一次 DNS 缓存的,而 DNS 服务器是两天清除一次缓存。那么这就会产生一个问题,有时候本地 DNS 服务器发生了问题,会导致依赖本地 DNS 服务器的网络请求失效。
2. 域名转发
如果你是是 A 运营商的客户,会访问自己运营商的 DNS 服务器。如果 A 运营商去权威 DNS 服务器查询的话,权威 DNS 服务器知道你是A运营商的,就返回给一个部署在 A 运营商的网站地址,这样针对相同运营商的访问,速度就会快很多。
但是 A 运营商可能有种种原因,将解析的请求转发给 B 运营商,B 运营商去权威 DNS 服务器查询的话,权威服务器会误认为,你是 B 运营商的,那就返回给你一个在 B 运营商的网站地址吧,结果客户的每次访问都要跨运营商,速度就会很慢。
在国内,网络代理商非常多的情况下,这种问题其实非常普遍(如果你使用移动的宽带的话,你就会经常遇到这个问题)。
3. 域名更新问题
本地 DNS 服务器是由不同地区、不同运营商独立部署的。对域名解析缓存的处理上,实现策略也有区别,有的会偷懒,忽略域名解析结果的 TTL 时间限制,在权威 DNS 服务器解析变更的时候,解析结果在全网生效的周期非常漫长。
TTL:Time To Live,指的是域名解析信息在 DNS 中的存在时间,一般用于当地址解析程序取出资源记录后决定保存及使用缓存数据的时间,它同时也可以表明该资源记录的稳定程度,极为稳定的信息会被分配一个很大的值
4. 延迟问题
从上文的 DNS 查询过程来看,如果没有缓存,DNS 的查询过程需要递归(或者迭代)遍历多个 DNS 服务器,才能获得最终的解析结果,这会带来一定的时延,甚至会解析超时。
5. 域名劫持
这个就不多说了,相信大家有时候就会遇到,明明输入的是正经网址,但是弹出的确实某些不可明说的网站(性感荷官在线发牌)。
怎么办!?
面对传统 DNS 服务的种种困境,产生了一种新解决方案———— HttpDNS。直接 http 访问 HttpDNS 供应商的 DNS 服务器,最快的获取 IP 地址。这个之后有机会再讲。
七. 总结
-
DNS 是解决域名—— IP 地址转换的方案,它在最开始的时候只使用 UDP 作为传输协议。但是随着时间的推移,网络环境越来越复杂,DNS 也在不断的更新和优化,不断地将 TCP 、 TLS 、HTTPS 加入进来。
-
DNS 服务器一般是递归查询,但是也并不排除迭代查询的可能性。
-
DNS 可以说是网络的基石之一,但是也并非完美,HttpDNS 就是一种解决方案。
推荐
参考资料
Microsoft says yes to future encrypted DNS requests in Windows
Large MTUs and Internet Performance
《DNS 与 BIND》