整理自DNS 原理入门
DNS(Domain Name System)的作用是根据域名查询对应的 IP 地址,DNS 协议是应用层协议。
查询过程
DNS 的查询过程非常复杂,分成多个步骤。可以用工具dig
显示整个查询过程。
$ dig github.com
; <<>> DiG 9.8.3-P1 <<>> github.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 23873
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;github.com. IN A
;; ANSWER SECTION:
github.com. 23 IN A 13.250.177.223
github.com. 23 IN A 52.74.223.119
github.com. 23 IN A 13.229.188.59
;; Query time: 22 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Mar 8 14:22:32 2019
;; MSG SIZE rcvd: 76
在Mac OS X EI Capitan 10.11.2
系统中只有如上信息返回,并以空行分隔为四个部分,在其他系统有待试验。
第一段是查询参数和统计;第二段是查询内容,表示查询域名 github.com 的A
记录,A 是 address 的缩写。
;; QUESTION SECTION:
;github.com. IN A
第三段是 DNS 服务器的答复,表示 github.com 有三个记录,即四个 IP 地址。23
是TTL 值
(Time to live 的缩写),表示缓存时间,即 23 秒内不
用重复查询。
;; ANSWER SECTION:
github.com. 23 IN A 13.250.177.223
github.com. 23 IN A 52.74.223.119
github.com. 23 IN A 13.229.188.59
第四段是 DNS 服务器的一些传输信息,本机的 DNS 服务器是8.8.8.8
,查询端口是53
(DNS 服务器的默认端口),以及回应长度是76
。
;; Query time: 22 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Mar 8 14:22:32 2019
;; MSG SIZE rcvd: 76
如果不想显示这么多信息,可以加上+short
,如下只返回 github.com 对应的 4 个 IP 地址(即 A 记录)。
$ dig github.com +short
13.229.188.59
52.74.223.119
13.250.177.223
DNS 服务器
本机要获取 github.com 的 IP 地址,首先要先知道 DNS 服务器的 IP 地址,否则上不了网。
;
DNS 服务器的 IP 地址有可能是动态的,由网关分配,这叫做DHCP 机制
;也有可能是事先指定的固定地址,如上图所示可自行设定。Mac OS 与 Linux 系统
一样,也可以在/etc/resolv.conf
文件中设置 DNS。
域名层级
DNS 服务器通过分级查询知道每个域名的 IP 地址。回看前述例子,域名尾部都有一个.
,这是由于所有域名的尾部,实际上都有一个根域名。
;; QUESTION SECTION:
;github.com. IN A
举例来说,github.com
真正的域名是github.com.root
,简写为github.com.
。因为,根域名.root
对于所有域名都是一样的,所以平时是省略的。
根域名的下一级,叫做”顶级域名”(top-level domain,缩写为 TLD),比如.com
、.net
;再下一级叫做”次级域名”(second-level domain,缩写
为 SLD),比如www.example.com
中的.example
,这一级域名是用户可以注册的;再下一级是主机名(host),比如www.example.com
中的www
,
又称为”三级域名”,这是用户在自己的域里面为服务器分配的名称,是用户可以任意分配的。域名的层级结构总结如下。
主机名.次级域名.顶级域名.根域名
即
host.sld.tld.root
根域名服务器
DNS 服务器根据域名的层级,进行分级查询。
需要明确的是,每一级域名都有自己的NS
记录(Name Server的缩写),NS 记录指向该级域名的域名服务器。这些服务器知道下一级域名的各种记录。所谓
“分级查询”,就是从根域名开始,依次查询每一级域名的 NS 记录,直到查到最终的 IP 地址,过程大致如下。
- 从”根域名服务器”查到”顶级域名服务器”的 NS 记录和 A 记录(IP 地址);
- 从”顶级域名服务器”查到”次级域名服务器”的 NS 记录和 A 记录(IP 地址);
- 从”次级域名服务器”查出”主机名”的 IP 地址;
仔细看上面的过程,没有提到 DNS 服务器怎么知道”根域名服务器”的 IP 地址,因为”根域名服务器”的 NS 记录和 IP 地址一般是不会变化的,所以内置在 DNS 服务器里面。下面是内置的根域名服务器 IP 地址的一个例子。
上表中,列出了根域名(.root
)的三条 NS 记录A.ROOT-SERVERS.NET
、B.ROOT-SERVERS.NET
和C.ROOT-SERVERS.NET
,以及它们的 IP 地址
(即 A 记录)198.41.0.4
、192.228.79.201
、192.33.4.12
。
另外,可以看到所有记录的 TTL 值是 3600000 秒,相当于 1000 小时,也就是每 1000 小时才查询一次根域名服务器列表。目前,世界上一共有十三组域名服务器,从A.ROOT-SERVERS.NET
一直到M.ROOT-SERVERS.NET
。
以下是在 CentOS7 中 dig 查到的根域名服务器信息。
$ dig
; <<>> DiG 9.11.4-P2-RedHat-9.11.4-9.P2.el7 <<>>
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 3426
;; flags: qr rd ra; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 27
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;. IN NS
;; ANSWER SECTION:
. 385126 IN NS f.root-servers.net.
. 385126 IN NS m.root-servers.net.
. 385126 IN NS c.root-servers.net.
. 385126 IN NS j.root-servers.net.
. 385126 IN NS g.root-servers.net.
. 385126 IN NS h.root-servers.net.
. 385126 IN NS b.root-servers.net.
. 385126 IN NS k.root-servers.net.
. 385126 IN NS i.root-servers.net.
. 385126 IN NS d.root-servers.net.
. 385126 IN NS l.root-servers.net.
. 385126 IN NS a.root-servers.net.
. 385126 IN NS e.root-servers.net.
;; ADDITIONAL SECTION:
a.root-servers.net. 502708 IN A 198.41.0.4
b.root-servers.net. 604697 IN A 199.9.14.201
c.root-servers.net. 356889 IN A 192.33.4.12
d.root-servers.net. 542615 IN A 199.7.91.13
e.root-servers.net. 603186 IN A 192.203.230.10
f.root-servers.net. 603283 IN A 192.5.5.241
g.root-servers.net. 95352 IN A 192.112.36.4
h.root-servers.net. 142370 IN A 198.97.190.53
i.root-servers.net. 336668 IN A 192.36.148.17
j.root-servers.net. 335018 IN A 192.58.128.30
k.root-servers.net. 331806 IN A 193.0.14.129
l.root-servers.net. 603283 IN A 199.7.83.42
m.root-servers.net. 603281 IN A 202.12.27.33
a.root-servers.net. 410800 IN AAAA 2001:503:ba3e::2:30
b.root-servers.net. 604668 IN AAAA 2001:500:200::b
c.root-servers.net. 327659 IN AAAA 2001:500:2::c
d.root-servers.net. 500152 IN AAAA 2001:500:2d::d
e.root-servers.net. 604668 IN AAAA 2001:500:a8::e
f.root-servers.net. 602243 IN AAAA 2001:500:2f::f
g.root-servers.net. 94833 IN AAAA 2001:500:12::d0d
h.root-servers.net. 191449 IN AAAA 2001:500:1::53
i.root-servers.net. 340956 IN AAAA 2001:7fe::53
j.root-servers.net. 335960 IN AAAA 2001:503:c27::2:30
k.root-servers.net. 328199 IN AAAA 2001:7fd::1
l.root-servers.net. 602243 IN AAAA 2001:500:9f::42
m.root-servers.net. 602923 IN AAAA 2001:dc3::35
;; Query time: 122 msec
;; SERVER: 192.168.244.1#53(192.168.244.1)
;; WHEN: Sat Mar 28 15:26:17 CST 2020
;; MSG SIZE rcvd: 811
ADDITIONAL 部分显示了完整的根域名 IP。
分级查询的实例
dig
命令的+trace
参数可以显示 DNS 的整个分级查询过程。
$ dig +trace github.com
; <<>> DiG 9.8.3-P1 <<>> +trace github.com
;; global options: +cmd
. 73568 IN NS a.root-servers.net.
. 73568 IN NS b.root-servers.net.
. 73568 IN NS c.root-servers.net.
. 73568 IN NS d.root-servers.net.
. 73568 IN NS e.root-servers.net.
. 73568 IN NS f.root-servers.net.
. 73568 IN NS g.root-servers.net.
. 73568 IN NS h.root-servers.net.
. 73568 IN NS i.root-servers.net.
. 73568 IN NS j.root-servers.net.
. 73568 IN NS k.root-servers.net.
. 73568 IN NS l.root-servers.net.
. 73568 IN NS m.root-servers.net.
;; Received 228 bytes from 8.8.8.8#53(8.8.8.8) in 453 ms
com. 172800 IN NS c.gtld-servers.net.
com. 172800 IN NS b.gtld-servers.net.
com. 172800 IN NS f.gtld-servers.net.
com. 172800 IN NS d.gtld-servers.net.
com. 172800 IN NS e.gtld-servers.net.
com. 172800 IN NS i.gtld-servers.net.
com. 172800 IN NS g.gtld-servers.net.
com. 172800 IN NS a.gtld-servers.net.
com. 172800 IN NS k.gtld-servers.net.
com. 172800 IN NS h.gtld-servers.net.
com. 172800 IN NS m.gtld-servers.net.
com. 172800 IN NS l.gtld-servers.net.
com. 172800 IN NS j.gtld-servers.net.
;; Received 488 bytes from 192.33.4.12#53(192.33.4.12) in 33142 ms
github.com. 172800 IN NS ns1.p16.dynect.net.
github.com. 172800 IN NS ns3.p16.dynect.net.
github.com. 172800 IN NS ns2.p16.dynect.net.
github.com. 172800 IN NS ns4.p16.dynect.net.
github.com. 172800 IN NS ns-520.awsdns-01.net.
github.com. 172800 IN NS ns-421.awsdns-52.com.
github.com. 172800 IN NS ns-1707.awsdns-21.co.uk.
github.com. 172800 IN NS ns-1283.awsdns-32.org.
;; Received 264 bytes from 192.12.94.30#53(192.12.94.30) in 1952 ms
github.com. 60 IN A 13.250.177.223
github.com. 60 IN A 13.229.188.59
github.com. 60 IN A 52.74.223.119
github.com. 900 IN NS ns-520.awsdns-01.net.
github.com. 900 IN NS ns1.p16.dynect.net.
github.com. 900 IN NS ns-1283.awsdns-32.org.
github.com. 900 IN NS ns-421.awsdns-52.com.
github.com. 900 IN NS ns2.p16.dynect.net.
github.com. 900 IN NS ns3.p16.dynect.net.
github.com. 900 IN NS ns-1707.awsdns-21.co.uk.
github.com. 900 IN NS ns4.p16.dynect.net.
;; Received 296 bytes from 208.78.70.16#53(208.78.70.16) in 176 ms
第一段列出根域名.
的所有 NS 记录,即所有根域名服务器;根据内置的根域名服务器 IP 地址,DNS 服务器向所有这些 IP 发出查询请求,询问github.com
的顶级域名服务器com.
的 NS 记录。最先回复的根域名服务器将被缓存,以后只向这台服务器发请求。
第二段显示了.com
域名的 13 条 NS 记录,同时返回的还有每一条记录对应的 IP 地址(TODO: 如果返回的 IP 是在终端上显示的话,目前只看到一个 IP 啊)。
第三段 DNS 服务器向这些顶级域名服务器发出查询请求,询问github.com
次级域名的NS 记录
。
第四段 DNS 服务器向第三段的 NS 服务器查询github.com
的主机名
。最后结果如下。
github.com. 60 IN A 13.250.177.223
github.com. 60 IN A 13.229.188.59
github.com. 60 IN A 52.74.223.119
github.com. 900 IN NS ns-520.awsdns-01.net.
github.com. 900 IN NS ns1.p16.dynect.net.
github.com. 900 IN NS ns-1283.awsdns-32.org.
github.com. 900 IN NS ns-421.awsdns-52.com.
github.com. 900 IN NS ns2.p16.dynect.net.
github.com. 900 IN NS ns3.p16.dynect.net.
github.com. 900 IN NS ns-1707.awsdns-21.co.uk.
github.com. 900 IN NS ns4.p16.dynect.net.
;; Received 296 bytes from 208.78.70.16#53(208.78.70.16) in 176 ms
github.com 有 3 条 A 记录,即这四个 IP 地址都可以访问到网站。并且还显示,最先返回结果的 NS 服务器是208.79.70.16
(试着 ping 了一下,是
ns1.p16.dynect.net
的 IP)。(TODO: 剩下几个 NS 记录应该是 github.com 自己的 DNS 服务器吧?)
NS 记录的查询
dig
命令可以单独查看每一级域名的 NS 记录。
$ dig +short ns .
k.root-servers.net.
b.root-servers.net.
c.root-servers.net.
j.root-servers.net.
g.root-servers.net.
a.root-servers.net.
l.root-servers.net.
d.root-servers.net.
e.root-servers.net.
f.root-servers.net.
m.root-servers.net.
h.root-servers.net.
i.root-servers.net.
$ dig +short ns com
c.gtld-servers.net.
a.gtld-servers.net.
h.gtld-servers.net.
k.gtld-servers.net.
m.gtld-servers.net.
e.gtld-servers.net.
g.gtld-servers.net.
j.gtld-servers.net.
d.gtld-servers.net.
b.gtld-servers.net.
f.gtld-servers.net.
l.gtld-servers.net.
i.gtld-servers.net.
$ dig +short ns github.com
ns-1283.awsdns-32.org.
ns3.p16.dynect.net.
ns-1707.awsdns-21.co.uk.
ns4.p16.dynect.net.
ns-520.awsdns-01.net.
ns2.p16.dynect.net.
ns1.p16.dynect.net.
ns-421.awsdns-52.com.
DNS 的记录类型
域名与 IP 之间的对应关系,称为”记录”(record)。根据使用场景,”记录”可以分成不同的类型(type),前面已经看到了A
记录和NS
记录。常见的 DNS
记录类型如下。
A
:地址记录(Address),返回域名指向的 IP 地址;NS
:域名服务器记录(Name Server),返回保存下一级域名信息的服务器地址。该记录只能设置为域名,不能设置为 IP 地址;MX
:邮件记录(Mail eXchange),返回接收电子邮件的服务器地址;CNAME
:规范名称记录(Canonical Name)`,返回另一个域名,即当前查询的域名的另一个域名的跳转,详见下文;PTR
:逆向查询记录(Pointer Record),只用于从 IP 地址查询域名,详见下文;
一般来说,为了服务的安全可靠,至少应该有两条 NS 记录,而 A 记录和 MX 记录也可以有多条,这样就提供了服务的冗余性,防止出现单点失败。
CNAME 记录主要用于域名的内部跳转,为服务器配置提供灵活性,用户感知不到,如下。
$ dig pages.github.com
; <<>> DiG 9.8.3-P1 <<>> pages.github.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5058
;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;pages.github.com. IN A
;; ANSWER SECTION:
pages.github.com. 37 IN CNAME github.github.io.
github.github.io. 1985 IN A 185.199.111.153
github.github.io. 1985 IN A 185.199.110.153
github.github.io. 1985 IN A 185.199.108.153
github.github.io. 1985 IN A 185.199.109.153
;; Query time: 35 msec
;; SERVER: 114.114.114.114#53(114.114.114.114)
;; WHEN: Mon Mar 11 10:03:08 2019
;; MSG SIZE rcvd: 128
上面结果显示,pages.github.com
的 CNAME 记录指向github.github.io
。也就是说,用户查询pages.github.com
的时候,实际上返回
的是github.github.io
的 IP 地址。这样的好处是,变更服务器 IP 地址的时候,只要修改github.github.io
这个域名就可以了,
用户的pages.github.com
域名不用修改。
CNAME
记录就是一个替换,所以域名一旦设置CNAME
记录以后,就不能再设置其他记录了(比如 A 记录和 MX 记录),这是为了防止产生冲突。
PTR
记录用于从 IP 地址反查域名。dig
命令的-x
参数用于查询 PTR 记录。
$ dig -x 192.30.252.153
...
;; ANSWER SECTION:
153.252.30.192.in-addr.arpa. 3600 IN PTR pages.github.com.
上面结果显示,192.30.252.153
这台服务器的域名是pages.github.com
(TODO:同样跟我查询出来的结果不一致)。
逆向查询的一个应用,是可以防止垃圾邮件,即验证发送邮件的 IP 地址,是否真的有它所声称的域名。
dig
命令可以查看指定的记录类型。
$ dig a github.com
$ dig ns github.com
$ dig mx github.com
其他 DNS 工具
host 命令
host 命令可以看作 dig 命令的简化版本,返回当前请求域名的各种记录。
$ host github.com
github.com has address 52.74.223.119
github.com has address 13.229.188.59
github.com has address 13.250.177.223
github.com mail is handled by 10 alt4.aspmx.l.google.com.
github.com mail is handled by 10 alt3.aspmx.l.google.com.
github.com mail is handled by 5 alt1.aspmx.l.google.com.
github.com mail is handled by 1 aspmx.l.google.com.
github.com mail is handled by 5 alt2.aspmx.l.google.com.
host 命令也可以用于逆向查询,等同于dig -x <ip>
。
$ host 13.229.188.59
59.188.229.13.in-addr.arpa domain name pointer ec2-13-229-188-59.ap-southeast-1.compute.amazonaws.com.
nslookup 命令
nslookup 命令用于互动式查询域名记录。
$ nslookup
> github.com
Server: 114.114.114.114
Address: 114.114.114.114#53
Non-authoritative answer:
Name: github.com
Address: 13.250.177.223
Name: github.com
Address: 13.229.188.59
Name: github.com
Address: 52.74.223.119
>
whois 命令
whois 命令用来查看域名的注册情况。
Domain Name: GITHUB.COM
Registry Domain ID: 1264983250_DOMAIN_COM-VRSN
Registrar WHOIS Server: whois.markmonitor.com
Registrar URL: http://www.markmonitor.com
Updated Date: 2017-06-26T16:02:39Z
Creation Date: 2007-10-09T18:20:50Z
...