计算机网络03-域名系统:DNS

0

DNS服务

DNS服务内容

上一节介绍了应用层协议的基本内容,并指出互联网上的每一个端系统都使用 IP 地址标识它的位置。根据 IP 地址,可以连接到正确的主机,并通过套接字向目的主机发送数据。

然而,在平时使用浏览器时,如果要访问一个网页,通常都会在地址栏中输入一个比较长、带英文字符的网址,例如要访问谷歌搜索,通常会在地址栏中输入 google.com ,而不是 IP 地址 172.217.163.46 ,尽管后者也能正确进入谷歌搜索页面。

在互联网中,主机可以使用主机名(hostname)或 IP 地址标识,主机名方便用户认识并记忆,例如 github.com 这种名称就比 20.205.243.166 方便记忆与输入。但主机名可能会变动,因此路由器只能解析 IP 地址。域名系统(Domain Name System, DNS)负责将主机名解析为 IP 地址。

DNS 是一个使主机能查询运行在 DNS 服务器上的分布式数据库的应用层协议。它运行在 UDP 协议上,使用 53 号端口。

DNS 的解析结果可以被其余应用层协议(如 HTTP 和 SMTP 等)利用,这也就是上一节编写邮件发送程序时并没有在代码中显式声明邮件服务器的 IP 地址的原因。当它们需要将一个报文发送到对应名称的主机上时,主要执行以下步骤:

  1. 浏览器将得到的主机名传给主机上正在运行的 DNS 客户端
  2. DNS 客户端向 DNS 服务器发送一个包含主机名的请求
  3. DNS 服务器返回一个包含解析得到的目标 IP 地址的回答报文
  4. 利用得到的目标 IP 地址向目的主机发送报文

DNS 服务器还提供以下服务:

当一台主机的规范主机名(canonical hostname)比较难以记忆时,它可能还有几个主机别名(host aliasing)。此时 DNS 服务器可以根据主机别名来获取主机的规范主机名及对应的 IP 地址。

当一个大型站点被分配在多个主机上时,DNS 数据库会轮流将每个主机的 IP 地址提供给客户端,通过负载分配(load distribution)平均服务器的压力。

DNS 服务基于大量分布式、分层形式的服务器组织。它们主要被分为 3 层:根 DNS 服务器、顶级域(Top-Level Domain, TLD) DNS 服务器以及权威 DNS 服务器。根 DNS 服务器遍及全球,提供 TLD 服务器的 IP 地址;顶级域提供权威 DNS 服务器的 IP 地址;权威 DNS 服务器提供由它管理的服务器的 IP 地址。

DNS 服务结构

每一级域名都由不区分大小写的英文字母和数字组成。多级域名由点号 . 隔开,且越高级的域名越靠右。例如,域名 en.wikipedia.org 中,顶级域名 org 提供 wikipedia.org 的 IP 地址,而 wikipedia.org 则提供 en.wikipedia.org 的 IP 地址。

还有一类重要的本地 DNS 服务器(local DNS server),由本地 ISP 提供,离用户较近,负责将主机发送的 DNS 请求转发到 DNS 服务器层次结构中。

DNS 提供了两种域名解析的方式:递归查询和迭代查询。递归查询如果没有得到目的主机的 IP 地址,该服务器自身会继续向下一级 DNS 服务器查询,最后得到的 IP 地址将层层上报给本地 DNS 。而迭代查询时,DNS 服务器只会将下一级 DNS 服务器的地址交给本地 DNS ,由本地 DNS 向下一级 DNS 查询。

两种查询方式的比较

由于递归查询对于被查询的域名服务器负担太大,因此通常从请求主机到本地域名服务器的查询使用递归查询,而其余的查询则采用迭代查询。

DNS 服务器普遍使用了 DNS 缓存(DNS caching)技术,当某个 DNS 服务器接收到一个包含主机名到 IP 地址的映射的 DNS 回答时,不管它是否属于该主机名的上一级服务器,都会被该 DNS 服务器保存在本地存储器中,这样下一次接收到相同的查询请求,该 DNS 服务器可以直接使用该缓存的结果而无需额外查询。

由于主机名和对应 IP 的映射关系可能被改变,因此缓存将在一段时间(例如两天)后被清除。

DNS记录和报文

所有包含 DNS 分布式数据库的 DNS 服务器上都存储了资源记录(Resource Record, RR),它提供了主机名到 IP 地址的映射,其结构是 (Name, Value, Type, TTL) 形式的四元组。

TTL 是该记录保存在缓存中的时间。NameValue 的值主要取决于 TypeType 是记录类型,其值可以是:

  • 如果其值为 A ,则 Name 是主机名,Value 是对应的 IP 地址
  • 如果其值为 NS ,则 Name 为一个域名,Value 是一个知道如何获得该域名的主机的 IP 地址的权威 DNS 服务器的主机名。该记录可以沿着查询链得到 DNS 记录
  • 。即将子域名指定其它的 DNS 服务器解析
  • 如果其值为 CNAME ,则 Value 是别名为 Name 的主机对应的规范主机名
  • 。即将域名指向另外一个域名
  • 如果其值为 MX ,则 Value 是别名为 Name 的主机名对应的邮件服务器的规范主机名。这样一个相同的别名可以同时充当邮件服务器和其它服务器

DNS 只有查询和回答两种报文,并且它们的格式相同。DNS 报文中各字段的语义为:

前 12 字节为首部区域,第一个 2 字节的字段用于标识查询的 id ,以此区分一个 DNS 应答是哪一个 DNS 查询的回应。

DNS 报文

第二个字段含有若干标志位,各标志位的含义为:

  • QR :查询/应答标志,置 1 代表是应答报文,清 0 代表是查询报文
  • opcode :查询或应答的类型
  • AA :置 1 表示该报文是权威 DNS 服务器的应答
  • TC :截断标志,使用 UDP 发生报文时若报文过长,则将报文截断并置该位为 1
  • RD :递归查询标志,置 1 为递归查询,清 0 为迭代查询
  • RA :允许递归标志,应答报文置 1 代表 DNS 服务器允许递归查询
  • zero :未使用,必须全部清 0
  • rcode :4 位返回码,表示应答状态

其余四个字段指定了首部后四类数据区域出现的数量。

问题区域包含正在进行的查询信息,包含名字字段(查询的主机名字)和类型字段(查询的问题类型)。回答区域对最初请求的名字的资源记录,可以包含多个 RR 。权威区域包含其它权威服务器的记录,附加区域包含了其它有帮助的记录。

接下来,通过具体的实验来探究 DNS 查询的细节。

实验:DNS查询

使用操作系统命令

为了执行 DNS 查询,首先要给出 DNS 服务器的地址。在 Linux 系统中,使用文件 /etc/resolv.conf 来存放 DNS 服务器的 IP 地址。该文件的内容大致为:

# [network] nameserver 8.8.8.8 nameserver 4.2.2.2

其中的两个 IP 地址分别是首选 DNS 服务器地址和备选 DNS 服务器地址。

nslookup 是一个简单的程序,可以运行在大多数 Linux 和 Windows 系统上,也可以使用在线工具如 https://www.nslookup.io/ 替代。它可以向任何 DNS 服务器发送 DNS 查询,并返回本次查询的结果报告。例如,以下是使用 nslookup 一次简单的 DNS 查询结果:

$ nslookup stackoverflow.com Server: cache-fz.fj100110.com Address: 65.115.28.98 Non-authoritative answer: Server: stackoverflow.com Addresses: 151.101.1.69 151.101.129.69 151.101.65.69 151.101.193.69

从解析结果可以看出,域名 stackoverflow.com 对应多台主机,在访问量较大的时候可以均衡负载。同时,由于本次查询用的 DNS 服务器并不实际管理该被查询的域名,它需要到其余 DNS 服务器中查询该域名,因此本次查询是非权威应答。

可以使用第二个参数指定查询用的 DNS 服务器。例如,以下使用管理本域名的 DNS 服务器查询本域名的地址:

$ nslookup frozencandles.fun dns1.hichina.com Server: UnKnown Address: 120.76.107.43 Name: frozencandles.fun Address: 123.56.83.47

这时,该应答便为权威应答。

还可以通过 -q=type 参数指定查询的记录类型。例如,假设要解析一个域名对应的邮件服务器的地址,那么可以执行以下命令:

$ nslookup -qt=MX frozencandles.fun Server: cache-fz.fj100110.com Address: 65.115.28.98 Non-authoritative answer: frozencandles.fun MX preference = 10, mail exchanger = mxw.mxhichina.com frozencandles.fun MX preference = 5, mail exchanger = mxn.mxhichina.com

如果想要查看更详细的 DNS 查询信息,可以使用 dig 命令发起一次 DNS 查询,第一个参数是 Name ,第二个参数是 Type 。该命令得到的 DNS 查询结果已经非常接近原始的 DNS 报文了:

$ dig frozencandles.fun ; <<>> DiG 9.10.3-P4-Ubuntu <<>> frozencandles.fun ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37772 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;frozencandles.fun. IN A ;; ANSWER SECTION: frozencandles.fun. 300 IN A 123.56.83.47 ;; Query time: 6 msec ;; SERVER: 65.115.28.98#53(65.115.28.98) ;; WHEN: Mon May 02 14:17:47 CST 2022 ;; MSG SIZE rcvd: 62

从以上结果可以大致还原 DNS 报文的数据。接下来介绍使用 WireShark 获取 DNS 报文并解析的过程。实际上该过程非常简单。

使用WireShark分析DNS

在使用 WireShark 前,可以使用以下命令清除操作系统的 DNS 缓存,确保结果是由 DNS 服务器返回的:

$ ipconfig /flushdns

然后打开 WireShark ,并在本地通过任意方式访问一个域名以执行 DNS 查询,例如使用 nslookup 命令。

如果本地的网络服务比较繁忙,出现的网络数据包比较多,那么可以在下图浅绿色区域的窗口将所有 DNS 数据包过滤出来:

经过筛选的数据包

下半部分的文字列出了该数据包的概览。从第四行可以看到,DNS 是运行在 UDP(User Datagram Protocol) 协议之上的,并且使用 53 号端口。

第五行表示该数据包是一个 DNS 应用层协议的数据,可以展开它进一步检查包含的内容:

DNS 数据包详细结构

第一个字段表示本次 DNS 查询的标识 id 为 0x7680 ;第二个字段的值的已经按位表示的很清楚了,说明这是一个标准的递归 DNS 查询;问题数和问题都在结果中显示出来了。

除此之外,WireShark 还获取了 DNS 回答的数据包,其结构和查询用的数据包相差不大,因此不再展示。

参考资料/延伸阅读

https://www.cloudflare.com/learning/dns/what-is-dns/

cloudflare(一个业务包括 DNS 解析的公司)的 DNS 介绍文档

https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/nslookup

MSDN 上关于 nslookup 命令的帮助文档

有条件或有兴趣的话可以选择一家域名服务商购买一个域名,然后使用它们提供的 DNS 服务尝试添加一些 DNS 记录并在本地解析,这样可以快速加深对 DNS 的印象。

京ICP备2021034974号
contact me by hello@frozencandles.fun