DNS的历史和原理

By | 2019年8月4日

这篇文章是用Golang实现一个DNS服务器系列第二部分的上篇。

要想实现一个上篇内描绘的DNS Server,那我们肯定得对DNS这个协议有一个完整而详细的了解。

DNS这个词拥有许多含义,它最根本的含义是指整个域名系统(Domain Name System),同时,它也可以指代这个系统工作时发送数据包的格式,也就是DNS协议。

DNS的历史

作为一个古老而又常用的一个典型应用层协议,DNS在任何一本介绍计算机网络的书籍内都会被提及。

在互联网的远古时期(阿帕网时代),人们使用IP主机名进行互联访问。相比于IP地址,由一串字母和数字构成的主机名显然要更容易记忆。然而在进行互联网通信时并不能直接使用主机名,而是需要先将主机名转换为对应的IP地址,然后再进行通信。主机名实际上是一串字符到对应的IP地址的一个映射,它们之间的对应关系由存放在计算机上的一个文件给出,这个文件也就是传说中的hosts。

在互联网早期,整个网络中的主机数量比较少,使用hosts文件进行主机名—IP地址的转换是一个可行的方案。然而随着连入互联网的主机规模的增长,人们发现这种完全离线的hosts文件不太靠得住,一来它由人手工维护,再者在不同的主机上同步hosts非常麻烦,也容易出错。

在1973年,IETF(国际互联网工程任务组)发布了RFC 606RFC 608等几个文档,决定由斯坦福研究院网络信息中心(NIC)作为hosts文件的官方来源,互联网上的所有主机均从该中心下载hosts文件使用。这套方案从1973到1983运作了差不多十年时间。相比于70年代,十年后的主机数量已经庞大得让这套系统得缺陷也被暴露出来:由于主机的增加,hosts文件像滚雪球一般变得越来越大。更糟糕的是主机名到IP地址的映射关系不是固定的,换人话说,那就是一台主机的IP地址可能随时间发生变化。主机数量越多,文件的变化率也就越大。以至于到后期每天都要从NIC重新下载最新的hosts文件。

在1982年的一次电脑邮件的会议上,阿帕网的工程师提出了一个解决电子邮件路由的方案。在那个年代,发送电子邮件是一件麻烦事。由于不是所有的主机都直接接入互联网,一封邮件可能需要通过几个不同的主机中转,最终才能发送到目标用户的邮箱。而发送者需要手工按顺序地指定中间经过的系统名称。不同的系统之间使用!进行分割,一个真实的地址看起来像这样:

utzoo!decvax!harpo!eagle!mhtsa!ihnss!ihuxp!grg

中间的一连串主机需要人为给出,要是不知道中间需要经过那些主机,你的邮件就无法发送。为了让邮件发送不再如此繁琐,工程师们在会议上讨论出来了一种叫做“域名”的概念,讨论的内容以文档的形式公开发布了出来,这就是RFC 805,它也是互联网域名的雏形。

RFC 805中提出了许多现代域名的一些原则,比如需要有一个域名注册机构,需要有一系列顶级域名作为域名查询起始点。

这次会议之后,一系列的RFC文档在当年相继发布。RFC 830提出了一种分布式的域名服务,它为现今的域名系统提供了主体架构。而RFC 882RFC 883则对整个域名解析流程和数据包结构做出了定义。

在1987年,IETF发布了RFC 1034RFC 1035正式标准,这两份标准的出台标志着DNS的正式走上历史舞台。


域名系统的工作流程

DNS是一种应用层协议,它底层使用UDPTCP协议进行数据传输。

使用UDP的原因是它没有握手,不用建立连接,查询的速度更快。而TCP则能保证数据的可靠传输,也没有UDP数据包那么严苛的数据包尺寸限制,但是它会增加一些握手开销。

DNS也是一种基础网络服务,为了保证可靠性,它采用了一种分布式架构,这能避免产生单点故障影响整个网络的情况。

若要细说DNS,那就不得不从域名开始,毕竟DNS三个字母里面它就占了俩(域名:Domain Name)

域名是啥?相信你哪怕不是敢拍胸脯的说你特别了解,也八成听说过这么个东西。

没错,你现在访问本网站的时候,你浏览器地址栏上显示的地址https://yangwang.hk/xxx,其中的yangwang.hk就是本网站的域名。

域名具有一种层级关系。层与层之间使用“.”隔开,yangwang.hk就能被拆分为两层,一个是yangwang,另一个是hk,在英文中,点与点之间的部分被称作label

域名的层级关系是倒着看的,hk是一级域名,也被称为顶级域名(Top Level Domain,TLD),在顶级域名左边的是二级域名,也就是yangwang

其实yangwang.hk只是域名的缩略写法,它的完整形式是:yangwang.hk.

没错,就是最后末尾多了个“.”,很多国内教材里将这个点称为根域名(Root Domain),其实并不完全正确,或者说就是错误的。

从某种程度上说,根域名不是独立存在的,它只是一个逻辑概念。根域名实际上是一个不包含任何字符,长度为0的label。对于yangwang.hk.,可以分解为三个label,分别是“yangwang”,“hk”和不包含任何字符的根域名“”。

由于域名的label之间使用.分割,于是乎如果一个域名包含了根域名,那么它写出来的的样子便是最后多了一个点,毕竟啥都没有,写出来了你也看不见是不是?……

由于本身长度为0,而如果我们又想展示它,那也只能通过书写一个.的方法来怒刷它的存在感了,在这样的背景下我们才能说根域名是“.”。那么为什么要设计出一个如此奇怪的root domain呢?在下一篇文章里我们对DNS数据包的格式进行分析后,这个问题的答案就自然出来了,这里便先卖个关子。

根域名在书写时的唯一痕迹只是域名末尾的一个点,而按照域名的层次结构划分,所有域名都处于根域名之下,因此这个点也就毫无悬念的被省略了。

这种带根域名的写法被叫做FQDN(Full Qualified Domain Name,全称域名/完全限定域名),而日常使用的不带点的版本,则被称为PQDN(Partiallly Qualified Domain Name,部分限定域名)。

对于域名www.example.comapi.example.comgoogle.com,它的结构便如下图:

整个域名的分配是由ICANN管理的,这是一家位于美国的非盈利机构。ICANN直接决定了那些域名能作为顶级域名。

顶级域名显然不止图中列出的三个。实际上顶级域名可以分为以下三大类,分别是

  • 通用顶级域名(gTLD),包含赞助顶级域名(sTLD)和非赞助顶级域名
  • 国家/地区顶级域名(ccTLD
  • 基础设施顶级域名.arpa

通用顶级域名是最常见的域名,它又可以分为赞助非赞助两类。

赞助顶级域名背后有一个特定的赞助商,比如gov域名背后是General Services Administration,dev域名背后是Google。在2010年以前,ICANN 对新顶级域名的审核比较严格,但在2010之后,ICANN批准公司可以以自己的商标注册新的TLD,于是乎一大批新的TLD出现了。与之相对的非赞助域名则没有对应的“赞助商”,简单地说,就是它并不归属某个公司或组织所有,ICANN对非赞助域名有管理权。非赞助域名不多,老的有com,net,org域名,新的有info等域名。

国家和地区顶级域名ccTLD是根据ISO 3166 alpha-2标准定义的两字节国家地区码规定的。ICANN规定两个字母的顶级域名一定是ccTLD,而ccTLD也一定是两个字母。

基础设施域名是一类(个)非常特殊的TLD,它只有.arpa这一个顶级域名。arpa其实是ARPANET的缩写,也就是现代英特网的前身阿帕网。

arpa实际上是全世界第一个TLD,它最初的目的只是临时使用,用于将传统ARPANET的主机名转换到域名系统里来。不过在arpa被用于反向解析(通过IP解析出域名)之后,人们发现废弃这个TLD似乎不太合适了。在现代互联网里,arpa只被用于一些特殊的查询,比如in-addr.arpa用于反向IPv4查询,ip6.arpa用于反向IPv6查询。由于历史原因,有些时候也把它看作是一个通用TLD。

域名系统的架构

互联网上域名的数量是极其庞大的,截止到2019年6月,顶级域名就有多达1530个。而在每个顶级域名下面还有以百万计的二级域名。在2018年底,全球二级域名的数量就接近3.5亿个。而二级域名下面还有三级甚至四级域名。如此多的域名如果采用中心化的单点架构,一来存在严重的性能问题,二来可靠性也满足不了要求。

DNS系统采用了类似域名结构的树状结构,按照域名的层次关系将域名分为若干管理区域,每个区域只负责自己管辖范围内的域名解析。这个管理区便是Zone。

从前文www.example.com域名的结构图可以看出来,所有的xxx.com域名都属于.com这个顶级域名,而所有的顶级域名都属于.根域名。

在发起一次DNS查询时就以这个根域名作为出发点,对根域名的查询会告诉你,你查询的某个顶级域名归那几台服务器管理。换句话说,根域名服务器里存放了一张表,表的内容是顶级域名和对应的托管商(下一步要去哪个服务器查询),也就是所谓的根区(root zone)。根区文件可以在ICANN官网上查看,地址是https://www.internic.net/domain/root.zone,文件比较大,因为它包含了所有顶级域名的托管信息。

以www.example.com域名为例,要查询www.example.com,就必须先询问根域名服务器,根域名服务器不会存放这个域名的解析结果,因为根域名服务器只会负责解析root zone,好在root zone里有对应的.com域名的解析服务器,于是便会返回NS记录,内容是.com域名由foo服务器托管,你下一步应该找它。

接着,你需要向foo服务器再发送一次请求,一般来说顶级域名服务器foo也不会存放example.com域名的具体信息,它同样会以NS记录的形式返回example.com域名由bar服务器管理。(ps:这里的foo和bar显然不是真实的地址)

于是乎你又向bar服务器发送一次查询,大多数时候到这里你就能拿到最终的结果。不过www.example.com的三级域名www完全也可以由另一台服务器管理,在这种情况下bar服务器也会返回和前面查询类似的消息,告诉你下一步应该找谁去查询。整个流程如下图所示:

DNS迭代查询流程图

DNS根服务器(DNS root server)是一切DNS查询的起点,为了保证可靠性,整个DNS系统在全球一共设置了13台服务器,编号从A到M,他们分别是a.root-servers.netm.root-servers.net。13这个数字是由于互联网上标准的UDP数据包长度最大为512字节,而为了在一个数据包内存放所有的根服务器的信息,根服务器的数量就被限制到了13。

准确的说,13个根服务器是逻辑上的13,实际上是13组服务器。其中a.root-servers.net是主根服务器,其余的12个是辅根。而每个根服务器组还会有对应的许多镜像服务器。这些根服务器广泛的分布于全球各个区域。在2019年7月28日,共有1006个根服务器正在运行。我国有8台镜像根,其中北京5台,上海3台。

你可以在https://root-servers.org/查询到它们实时的具体状态和位置。

虽然根服务器有域名,但是显然必须先知道其中某个服务器的具体IP地址,我们才能开始查询。否则,那就是想左脚踩右脚上天了。

根服务器的地址一般是不会变化的(不是不变,H根就在2015年改变过IP地址),因此根服务器的地址一般在软件里写死,或者由一个配置文件直接提供。

使用dig命令,我们可以看到完整的域名解析流程,还是以www.example.com为例,使用命令:

dig @1.0.0.1 www.example.com a +trace +nodnssec

整个解析过程被分为了四段,首先:1.0.0.1被用作DNS服务器查询13个根服务器的地址,这个IP是我们在执行命令时手工指定的。

这次查询返回了13台根服务器的地址。

随后dig选择了IP地址为192.36.148.17的I根服务器查询到了.com,这次查询返回了下面的地址:

com. 172800 IN NS k.gtld-servers.net.
com. 172800 IN NS l.gtld-servers.net.
com. 172800 IN NS c.gtld-servers.net.
com. 172800 IN NS j.gtld-servers.net.
...
com. 172800 IN NS g.gtld-servers.net.
com. 172800 IN NS h.gtld-servers.net.
com. 172800 IN NS e.gtld-servers.net.
com. 172800 IN NS m.gtld-servers.net.

这13个服务器便是com这个顶级域名的DNS服务器,

然后,dig选择了IP为2001:503:39c1::30i.gtld-servers.net进行下一步查询,这次返回了两个地址:

example.com. 172800 IN NS a.iana-servers.net.
example.com. 172800 IN NS b.iana-servers.net.

这两个地址对应的便是管理example.com这个域名的DNS服务器,即example.com的权威服务器。因为下一次向IP为2001:500:8f::53a.iana-servers.net服务器发起请求后我们便直接得到了对应域名的最终查询结果,即

www.example.com. 86400 IN A 93.184.216.34

这种一步步的查询过程叫做迭代查询,不过在我们日常生活中更常用的是递归查询。递归查询的过程是:我们向递归服务器发送一个查询请求,随后递归服务器会为我们进行迭代查询,最终将结果直接返回给我们。

这种查询的特点便是我们不再需要从根服务器开始一层层的向下查询,只需要一次请求我们便能从递归服务器上获取到我们想要的结果。

递归查询的优势很明显:服务器可以将查询结果缓存下来。如果在一定时间内再次查询这个域名,直接返回缓存的结果即可。这不仅减轻了整套域名系统的查询压力,也能大大提高查询的速度,毕竟一次迭代查询是非常耗时间的。

在上面查询结果的域名后面都有一个数字,比如example.com后面的86400,这个数字便是TTL,它的意思是你最多可以把结果缓存TTL秒。当缓存时间超过TTL时,你在下次查询时便需要丢弃这个缓存的结果,从头开始一次新的迭代或者递归查询。

递归服务器虽然也能返回某个域名的查询结果,但从查询过程就可以看出,给我们提供查询结果的,不是对这个域名有真实管理权的服务器,此时,该服务器会被称为非权威服务器。而非权威服务器返回的响应数据里会包含一个标记:非权威应答,与之相对的则是权威应答。权威应答表示提供该结果的服务器对该条记录有管理权。

使用nslookup命令可以看到这个差异:

其中第一次查询选择的是路由器上的DNS服务器,由于它对example.com没有管辖权,因此返回的结果中有Non-authoritative的提示,而第二次查询则使用的是example.com的权威服务器,这时查询的结果不包含该标记。

DNS的缺陷与解决方法

在DNS出台几十年后的今天,互联网早已不像曾经的互联网那么简单与纯粹,早期DNS设计的一些问题也逐渐暴露出来了。主要有两大方面:

  • UDP DNS数据包长度为512字节,不太能支撑现代的查询需求。DNS在设计时对部分字段设计过于保守,比如RCODE等字段已经基本分配完毕。
  • DNS系统是明文传输的,协议内没有任何安全校验机制,存在安全隐患

DNS的格式问题

前面提到过,鉴于互联网标准UDP尺寸为512,使用UDP进行DNS查询时,返回的结果也受限于512字节,在设计DNS协议时,设计者直接将DNS数据包限制到了512B。在早期互联网中,这个空间是非常充足的,但现如今DNS的用途更为复杂,512B的长度限制就显得有些捉襟见肘了。而现在的互联网对于大于512字节的UDP数据包也几乎不存在传输问题,仍然使用512B的限制便显得有些不合理。同时,部分字段和FLAG标记位的考虑不充分,原始DNS协议几乎无法添加新的需求了。

这个问题的解决方案是在保持兼容的前提下,对现有DNS协议做升级扩展。在RFC2671中提出了一种对现有DNS协议进行扩展的方法,被称为EDNS0(Extension Mechanisms for DNS version 0)。

EDNS0在完全兼容RFC1035规定的标准DNS格式的基础上,大大扩展了DNS的功能,同时也修改了DNS数据包的长度限制,使其至少能达到4096 个字节。

EDNS将在介绍DNS协议部分详细介绍。

DNS的安全问题

DNS协议在设计之初并没有考虑到安全性问题,整个DNS协议在互联网上都是没有任何校验的明文传输的。

这意味着在域名解析流程中,从我们客户端到权威服务器的任何一环,都能轻松获取到我们所请求的域名和域名解析的结果。更糟糕的是,大多数情况下DNS所使用的UDP数据包本身是无连接的,这意味着我们根本无法确认我们所收到的解析结果,到底是从真实服务器发送过来的,还是攻击者伪造的。

利用DNS的这个缺陷,有两种常见的攻击手段,分别是DNS劫持DNS污染

DNS劫持是在客户端进行DNS查询时,通信链路中的某一个部分篡改响应结果的数据包,或者直接伪造出一个虚假的DNS响应包的攻击。

DNS劫持的后果是一个域名的解析结果可以被攻击者任意的操控。比如你想访问一个银行网站,但攻击者偷偷篡改了银行域名解析出的IP,将其指向了攻击者部署的钓鱼网站,那么虽然浏览器山上看到的域名并没有什么变化,但是你实际访问的网站早已和真实的银行网站没有半毛钱关系了。

DNS污染影响的范围往往会更大,它利用的是DNS结果会在递归服务器缓存的特点,若能篡改递归服务器缓存的结果,那么就能让所有使用该服务器查询的客户端,获得的查询结果是攻击者所指定的。由于递归DNS同样要进行迭代查询,如果我们能在迭代查询的时候进行DNS劫持,那么就可以实现DNS污染。

进行DNS污染的攻击者往往是ISP(网络运营商)和国家政府,因为他们掌握了互联网的骨干节点,甚至是DNS根域名的镜像服务器,能轻松的对DNS数据包进行伪造。

例如某国的防火墙正是使用该技术阻止民众访问到某些网站。

对抗DNS攻击的基本解决方案是对DNS解析的结果进行签名。使用数字签名技术对解析结果签名,我们便能得知返回的结果是真实的,还是攻击者伪造的。

为此,RFC2535提出了一种DNS安全扩展,它被称为DNSSEC(Domain Name System Security Extensions)。这个扩展提供了一种来源鉴定和数据完整性检查的手段。启用DNSSEC之后,当客户端发现解析的结果是错误的,便会直接丢弃该结果,以此来避免攻击者得手。显然,这个技术没有保证可用性,换句话说,要么解析出正确结果,要么就没结果。虽然简单粗暴,但总比上了假网站,被卖了还帮人数钱要好一些。

然而理想很丰满,现实很骨感。目前国内的DNS对这个上个世纪就提出的安全扩展支持基本为0,这其中到底是由于政治因素还是技术原因抑或是什么别的原因,我们也就不得而知了。好在,除了DNSSEC,我们还能使用HTTPS技术来保障我们访问的网站是真实的。

DNS的介绍部分大概就到这里了,下一篇文章开始,我们将会详细又完整的了解整个DNS协议。

参考资料:

https://tools.ietf.org/html/rfc1035

https://www.livinginternet.com/i/iw_dns_history.htm

https://en.wikipedia.org/wiki/Top-level_domain

http://www.omnisecu.com/tcpip/fully-qualified-domain-name-fqdn-and-partially-qualified-domain-name-pqdn.php

发表评论

电子邮件地址不会被公开。 必填项已用*标注