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。
- 192.168.0.100 -> 192.168.0.1:
.
的 NS 记录是什么? - 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
- 192.168.0.100 -> 192.168.0.1:
- 192.168.0.100 -> 199.9.14.201:
.me
的 A 记录是什么? - 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
- 192.168.0.100 -> 192.168.0.1:
- 192.168.0.100 -> 199.253.60.1:
cjting.me
的 A 记录是什么? - 199.253.60.1 -> dig:
cjting.me
的 NS 记录是f1g1ns1.dnspod.net
和f1g1ns2.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
- 192.168.0.100 -> 192.168.0.1:
- 192.168.0.100 -> 180.163.19.15:
cjting.me
的 A 记录是什么? - 180.163.19.15 -> 192.168.0.100: A 记录是 192.30.252.154 和 192.30.252.153, NS 记录是 f1g1ns1.dnspod.net 和 f1g1ns2.dnspod.net
- 至此整个过程结束
可以看出,一共进行了 1 + 1 + 13 * 2 + 1 + 1 + 5 * 2 + 1 + 1 + 2 * 2 + 1 + 1 = 48
次通信(请求+响应),可见,如果不进行缓存的话,DNS 是多么地浪费资源。