DNS 101

DNS 全称 Domain Name System,是我们每天都在使用的基础互联网设施。

它被发明出来的原因很简单,计算机之间的通信用的是 IP 地址,是一串数字,人类记忆起来十分不方便,因此,我们给地址起个名字,然后将名字和 IP 之间的关系记录起来,这样,我们只用记住名字就行了。

从上面可以看出,DNS 系统类似我们日常使用的电话本,只不过里面存储的是域名和 IP 之间的关系。和人与电话之间的关系一样,一个域名可以有多个 IP,一个 IP 也可以有多个域名。

基本概念

现在我们来介绍几个 DNS 系统的概念。

DNS Server: DNS 服务器,这是我们获取 DNS 服务的入口。每台上网的计算机都需要配置 DNS 服务器的 IP 地址,之后所有的域名查询,就通过询问这个服务器完成。

Record: 记录,域名在 DNS 系统的一条配置,称为一条记录。最常见的记录就是域名对应的 IP 是什么。

Record Type: 记录类型,除了域名对应的 IP,域名还有别的信息,比如域名对应的邮件服务器是什么等等,也就是,记录有不同的类型。

常见的记录类型如下:

  • A: Address 记录,域名对应的 IPv4 地址
  • AAAA: 域名对应的 IPv6 地址
  • MX: 域名对应的电子邮件服务器地址
  • NS: 域名对应的域名服务器域名,有点绕,简单来说,你想知道 A 域名的信息,需要去问 A 域名的 NS 记录对应的域名
  • CNAME: 域名的 Canonical Name,类似于文件系统中的链接

这里是 完整的记录列表

当我们购买了域名以后,就可以在服务商提供的配置系统中设置域名的相关记录。一般来说,服务商会默认配置两条 NS 记录到他们的域名服务器,当然,我们也可以修改这个记录,使用第三方域名服务器,比如 DNSPod

分级结构

互联网的规模太大,域名数量更是数不胜数,一台 DNS Server 将这些数据都存储下来是不现实的,因此 DNS 在设计的时候,采用的是分级结构,每一部分存储下一级的相关信息。

举个例子,我们想知道 www.example.com 的 IP 地址是什么,将这个请求发送给了我们的 DNS Server。

DNS Server 需要先问根域名服务器,谁负责管理 .com?然后再问 .com 域名服务器,谁负责管理 example.com?最后,再问 example.com 域名服务器,www.example.com 的 IP 地址是什么,从而获得答案返回给我们。

这就是域名的分级结构,域名查询需要从根开始,一级一级的向下,直到获得答案。

当然,因为域名查询是一个高频词的动作,无时无刻都在发生,如果每次都是这样一层一层获取,效率将十分低下,因此,DNS 系统中大量使用缓存,每一个中间环节都会缓存相关结果来节省时间提高效率。

根域名服务器

上面的描述引入了一个问题。因为我们每次查询(不考虑缓存)都需要询问根域名服务器,那么 DNS Server 是如何得知根域名服务器的地址呢?

答案是 使用配置文件写死,就是这么简单。

目前全世界一共有 13 组根域名服务器,分别是 [a-m].root-servers.net

IANA 提供了根域名服务器配置文件,下载下来配置相关的 DNS 软件就行了。

常用工具

DNS 相关的常用工具有两个,一个是 dig,功能强大,十分灵活。

用法是 dig <记录类型> <查询名称>

例如,我想知道根域名服务器的相关信息。

$ dig ns . # 注意,`.`代表根域名,这条指令表示查询根域名的NS记录
; <<>> DiG 9.8.3-P1 <<>> ns .
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12846
;; flags: qr rd ra; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;.        IN  NS

;; ANSWER SECTION:
.     40368 IN  NS  h.root-servers.net.
.     40368 IN  NS  m.root-servers.net.
.     40368 IN  NS  f.root-servers.net.
.     40368 IN  NS  c.root-servers.net.
.     40368 IN  NS  e.root-servers.net.
.     40368 IN  NS  l.root-servers.net.
.     40368 IN  NS  k.root-servers.net.
.     40368 IN  NS  a.root-servers.net.
.     40368 IN  NS  i.root-servers.net.
.     40368 IN  NS  b.root-servers.net.
.     40368 IN  NS  d.root-servers.net.
.     40368 IN  NS  g.root-servers.net.
.     40368 IN  NS  j.root-servers.net.

;; Query time: 10 msec
;; SERVER: 116.228.111.118#53(116.228.111.118)
;; WHEN: Tue May  1 16:53:16 2018
;; MSG SIZE  rcvd: 228

从返回的结果可以看出,根域名有 13 条 NS 记录,对应 13 组域名服务器。

使用 dig 获取 cjting.me 的 IPv4 地址。

$ dig cjting.me # 记录类型默认为 `A`
; <<>> DiG 9.8.3-P1 <<>> cjting.me
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 25983
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;cjting.me.     IN  A

;; ANSWER SECTION:
cjting.me.    598 IN  A 192.30.252.153
cjting.me.    598 IN  A 192.30.252.154

;; Query time: 10 msec
;; SERVER: 116.228.111.118#53(116.228.111.118)
;; WHEN: Tue May  1 16:55:06 2018
;; MSG SIZE  rcvd: 59

可以看到,cjting.me 这个域名有两条 A 记录,即查询到了两条 IP 地址。

第二个指令是 host 指令,相比 dig 指令,功能比较简单,输出也比较简洁。

使用 host 获取 cjting.me 的相关信息。

$ host cjting.me
cjting.me has address 192.30.252.154
cjting.me has address 192.30.252.153

一次完整的查询

dig 有一个很高级的功能,叫做 trace,即可以追踪输出每一级查询的具体信息,而不是只给一个最终结果。

下面我们来看一个完整的例子,dig +trace cjting.me,看看到底发生了哪些请求,最终的IP地址是如何得到的。

$ dig +trace cjting.me
; <<>> DiG 9.8.3-P1 <<>> +trace cjting.me @8.8.8.8
;; global options: +cmd
.                       209918  IN      NS      a.root-servers.net.
.                       209918  IN      NS      m.root-servers.net.
.                       209918  IN      NS      b.root-servers.net.
.                       209918  IN      NS      l.root-servers.net.
.                       209918  IN      NS      f.root-servers.net.
.                       209918  IN      NS      d.root-servers.net.
.                       209918  IN      NS      j.root-servers.net.
.                       209918  IN      NS      i.root-servers.net.
.                       209918  IN      NS      g.root-servers.net.
.                       209918  IN      NS      k.root-servers.net.
.                       209918  IN      NS      e.root-servers.net.
.                       209918  IN      NS      h.root-servers.net.
.                       209918  IN      NS      c.root-servers.net.
;; Received 228 bytes from 8.8.8.8#53(8.8.8.8) in 117 ms

me.                     172800  IN      NS      a2.nic.me.
me.                     172800  IN      NS      a0.nic.me.
me.                     172800  IN      NS      c0.nic.me.
me.                     172800  IN      NS      b0.nic.me.
me.                     172800  IN      NS      b2.nic.me.
;; Received 336 bytes from 199.9.14.201#53(199.9.14.201) in 768 ms

cjting.me.              86400   IN      NS      f1g1ns2.dnspod.net.
cjting.me.              86400   IN      NS      f1g1ns1.dnspod.net.
;; Received 81 bytes from 199.253.60.1#53(199.253.60.1) in 361 ms

cjting.me.              600     IN      A       192.30.252.154
cjting.me.              600     IN      A       192.30.252.153
cjting.me.              86400   IN      NS      f1g1ns1.dnspod.net.
cjting.me.              86400   IN      NS      f1g1ns2.dnspod.net.
;; Received 123 bytes from 180.163.19.15#53(180.163.19.15) in 8 ms

dig 会输出每一步的关键信息,但并不完整。例如,第二段中,dig 询问 199.9.14.201 获取到了 .me 域名的相关信息,但是 199.9.14.201 这个 IP 是如何来的呢?

下面我列出这中间发生的完整步骤,假设我的本机 IP 是 192.168.0.100,DNS Server 的 IP 是 192.168.0.1。

  1. 192.168.0.100 -> 192.168.0.1: .的 NS 记录是什么?
  2. 192.168.0.1 -> 192.168.0.100: 是 a.root-servers.net, b.root-servers.net
    • 192.168.0.100 -> 192.168.0.1: a.root-servers.net 的 A 记录是什么?
    • 192.168.0.1 -> 192.168.0.100: 是 198.41.0.4
    • 192.168.0.100 -> 192.168.0.1: b.root-servers.net 的 A 记录是什么?
    • 192.168.0.1 -> 192.168.0.100: 是 199.9.14.201
    • dig 会选择一个 IP 进行下一步,同时该地址对应的 IP 会被缓存起来,此时选中的是 199.9.14.201
  3. 192.168.0.100 -> 199.9.14.201: .me 的 A 记录是什么?
  4. 199.9.14.201 -> 192.168.0.1: .me 的 NS 记录是 a2.nic.me, b0.nic.me
    • 192.168.0.100 -> 192.168.0.1: b0.nic.me 的 A 记录是什么?
    • 192.168.0.1 -> 192.168.0.100: 是 199.253.60.1
    • 192.168.0.100 -> 192.168.0.1: a2.nic.me 的 A 记录是什么?
    • 192.168.0.1 -> 192.168.0.100: 是 199.253.59.1
    • dig 会选择一个 IP 进行下一步,同时该地址对应的 IP 会被缓存,此时选中的是 199.253.60.1
  5. 192.168.0.100 -> 199.253.60.1: cjting.me 的 A 记录是什么?
  6. 199.253.60.1 -> dig: cjting.me 的 NS 记录是 f1g1ns1.dnspod.netf1g1ns2.dnspod.net
    • 192.168.0.100 -> 192.168.0.1: f1g1ns2.dnspod.net 的 A 记录是什么?
    • 192.168.0.1 -> 192.168.0.100: 是 182.140.167.188, 101.226.220.16, …
    • 192.168.0.100 -> 192.168.0.1: f1g1ns1.dnspod.net 的 A 记录是什么?
    • 192.168.0.1 -> 192.168.0.100: 是 61.151.180.44, 58.247.212.36, …
    • dig 会选择一个 IP 进行下一步,同时该地址对应的 IP 会被缓存,此时选中的是 180.163.19.15
  7. 192.168.0.100 -> 180.163.19.15: cjting.me 的 A 记录是什么?
  8. 180.163.19.15 -> 192.168.0.100: A 记录是 192.30.252.154 和 192.30.252.153, NS 记录是 f1g1ns1.dnspod.net 和 f1g1ns2.dnspod.net
  9. 至此整个过程结束

可以看出,一共进行了 1 + 1 + 13 * 2 + 1 + 1 + 5 * 2 + 1 + 1 + 2 * 2 + 1 + 1 = 48 次通信(请求+响应),可见,如果不进行缓存的话,DNS 是多么地浪费资源。