DNS 原理
2023-02-18 计算机网络
概述
DNS
是互联网核心协议之一。不管是上网浏览,还是编程开发,都需要了解一点它的知识。
本文详细介绍 DNS
的原理,以及如何运用工具软件观察它的运作。我的目标是,读完此文后,你就能完全理解 DNS
。
一、DNS 是什么?
DNS
(Domain Name System 的缩写)的作用非常简单,就是根据域名查出IP地址。你可以把它想象成一本巨大的电话本。
举例来说,如果你要访问域名 math.stackexchange.com,首先要通过 DNS
查出它的IP地址是 151.101.129.69。
如果你不清楚为什么一定要查出IP地址,才能进行网络通信,建议先阅读我写的 《互联网协议入门》。
二、查询过程
虽然只需要返回一个IP地址,但是 DNS
的查询过程非常复杂,分成多个步骤。
工具软件 dig
可以显示整个查询过程。
$ dig math.stackexchange.com
上面的命令会输出 6 段信息。
# 第 1 段是查询参数和统计
; <<>> DiG 9.10.6 <<>> math.stackexchange.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 44088
;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 4, ADDITIONAL: 6
# 第 2 段是查询内容
# 结果表示,查询域名 math.stackexchange.com 的 A 记录,A 是 address 的缩写
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;math.stackexchange.com. IN A
# 第 3 段是 DNS 服务器的答复
# 结果显示,math.stackexchange.com 有四个 A 记录,即四个 IP 地址。
# 54 是 TTL 值(Time to live 的缩写),表示缓存时间,即 54 秒之内不用重新查询。
;; ANSWER SECTION:
math.stackexchange.com. 54 IN A 151.101.65.69
math.stackexchange.com. 54 IN A 151.101.193.69
math.stackexchange.com. 54 IN A 151.101.129.69
math.stackexchange.com. 54 IN A 151.101.1.69
# 第 4 段显示 stackexchange.com 的 NS 记录(Name Server 的缩写)
# 即哪些服务器负责管理 stackexchange.com 的 DNS 记录。
# 结果显示 stackexchange.com 共有四条 NS 记录,即四个域名服务器,向其中任一台查询就能知道 math.stackexchange.com 的 IP 地址是什么
;; AUTHORITY SECTION:
stackexchange.com. 1866 IN NS ns-925.awsdns-51.net.
stackexchange.com. 1866 IN NS ns-1832.awsdns-37.co.uk.
stackexchange.com. 1866 IN NS ns-cloud-d1.googledomains.com.
stackexchange.com. 1866 IN NS ns-cloud-d2.googledomains.com.
# 第 5 段是上面四个域名服务器的 IP 地址,这是随着前一段一起返回的
;; ADDITIONAL SECTION:
ns-925.awsdns-51.net. 1626 IN A 205.251.195.157
ns-1832.awsdns-37.co.uk. 1592 IN A 205.251.199.40
ns-925.awsdns-51.net. 1626 IN AAAA 2600:9000:5303:9d00::1
ns-1832.awsdns-37.co.uk. 1593 IN AAAA 2600:9000:5307:2800::1
ns-cloud-d1.googledomains.com. 3204 IN AAAA 2001:4860:4802:32::6d
# 第 6 段是 DNS 服务器的一些传输信息
;; Query time: 26 msec
;; SERVER: 192.168.1.1#53(192.168.1.1)
;; WHEN: Fri Feb 17 22:31:26 CST 2023
;; MSG SIZE rcvd: 368
如果不想看到这么多内容,可以使用 +short 参数
$ dig +short math.stackexchange.com
# 输出如下
151.101.1.69
151.101.129.69
151.101.193.69
151.101.65.69
上面命令只返回 math.stackexchange.com 对应的 4 个 IP 地址(即A记录)。
三、DNS服务器
下面我们根据前面这个例子,一步步还原,本机到底怎么得到域名 math.stackexchange.com 的IP地址。
首先,本机一定要知道 DNS
服务器的IP地址,否则上不了网。通过DNS服务器,才能知道某个域名的IP地址到底是什么。
DNS
服务器的IP地址,有可能是动态的,每次上网时由网关分配,这叫做 DHCP 机制;也有可能是事先指定的固定地址。
Linux系统里面,DNS
服务器的 IP 地址保存在 /etc/resolv.conf
文件。
上例的 DNS
服务器是 192.168.1.253,这是一个内网地址。有一些公网的DNS服务器,也可以使用,其中最有名的就是 Google 的 8.8.8.8 和 Level 3 的 4.2.2.2。
本机只向自己的 DNS
服务器查询,dig 命令有一个@参数,显示向其他 DNS
服务器查询的结果。
$ dig @4.2.2.2 math.stackexchange.com
上面命令指定向 DNS 服务器 4.2.2.2 查询。
四、域名的层级
DNS
服务器怎么会知道每个域名的 IP 地址呢?答案是分级查询。
请仔细看前面的例子,每个域名的尾部都多了一个点。
;; QUESTION SECTION:
;math.stackexchange.com. IN A
比如,域名 math.stackexchange.com 显示为 math.stackexchange.com.。这不是疏忽,而是所有域名的尾部,实际上都有一个根域名。
举例来说,www.example.com
真正的域名是 www.example.com.root
,简写为 www.example.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
记录,NS
记录指向该级域名的域名服务器。这些服务器知道下一级域名的各种记录。
所谓 分级查询,就是从根域名开始,依次查询每一级域名的 NS
记录,直到查到最终的 IP 地址,过程大致如下。
- 从 “根域名服务器” 查到 “顶级域名服务器"的 NS 记录和 A 记录(IP 地址)
- 从 “顶级域名服务器” 查到 “次级域名服务器"的 NS 记录和 A 记录(IP 地址)
- 从 “次级域名服务器” 查到 “主机名"的 IP 地址
仔细看上面的过程,你可能发现了,没有提到 DNS
服务器怎么知道 “根域名服务器” 的 IP 地址。
回答是 “根域名服务器” 的 NS
记录和 IP 地址一般是不会变化的, 所以内置在 DNS
服务器里面。
下面是内置的根域名服务器 IP 地址的一个 例子
; formerly NS.INTERNIC.NET
;
. 3600000 IN NS A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET. 3600000 A 198.41.0.4
A.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:BA3E::2:30
;
; formerly NS1.ISI.EDU
;
. 3600000 NS B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET. 3600000 A 192.228.79.201
;
; formerly C.PSI.NET
;
. 3600000 NS C.ROOT-SERVERS.NET.
C.ROOT-SERVERS.NET. 3600000 A 192.33.4.12
上面列表中,列出了根域名(.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 小时才查询一次根域名服务器的列表。
目前,世界上一共有 13 组根域名服务器,从 A.ROOT-SERVERS.NET 一直到 M.ROOT-SERVERS.NET。
六、分级查询的实例
dig 命令的 +trace 参数可以显示 DNS 的整个分级查询过程。
$ dig +trace math.stackexchange.com
上面命令的第一段列出根域名.的所有 NS
记录,即所有根域名服务器。
; <<>> DiG 9.16.37-Debian <<>> +trace math.stackexchange.com
;; global options: +cmd
. 259200 IN NS g.root-servers.net.
. 259200 IN NS j.root-servers.net.
. 259200 IN NS e.root-servers.net.
. 259200 IN NS l.root-servers.net.
. 259200 IN NS d.root-servers.net.
. 259200 IN NS a.root-servers.net.
. 259200 IN NS b.root-servers.net.
. 259200 IN NS i.root-servers.net.
. 259200 IN NS m.root-servers.net.
. 259200 IN NS h.root-servers.net.
. 259200 IN NS c.root-servers.net.
. 259200 IN NS k.root-servers.net.
. 259200 IN NS f.root-servers.net.
根据内置的根域名服务器 IP 地址,DNS 服务器向所有这些 IP 地址发出查询请求,询问 math.stackexchange.com 的顶级域名服务器 com.的 NS 记录。 最先回复的根域名服务器将被缓存,以后只向这台服务器发请求。
接着是第二段。
com. 172800 IN NS a.gtld-servers.net.
com. 172800 IN NS b.gtld-servers.net.
com. 172800 IN NS c.gtld-servers.net.
com. 172800 IN NS d.gtld-servers.net.
com. 172800 IN NS e.gtld-servers.net.
com. 172800 IN NS f.gtld-servers.net.
com. 172800 IN NS g.gtld-servers.net.
com. 172800 IN NS h.gtld-servers.net.
com. 172800 IN NS i.gtld-servers.net.
com. 172800 IN NS j.gtld-servers.net.
com. 172800 IN NS k.gtld-servers.net.
com. 172800 IN NS l.gtld-servers.net.
com. 172800 IN NS m.gtld-servers.net.
上面结果显示.com 域名的 13 条 NS
记录,同时返回的还有每一条记录对应的 IP 地址。
然后,DNS
服务器向这些顶级域名服务器发出查询请求,询问 math.stackexchange.com 的次级域名 stackexchange.com 的 NS
记录。
stackexchange.com. 172800 IN NS ns-925.awsdns-51.net.
stackexchange.com. 172800 IN NS ns-1832.awsdns-37.co.uk.
stackexchange.com. 172800 IN NS ns-cloud-d1.googledomains.com.
stackexchange.com. 172800 IN NS ns-cloud-d2.googledomains.com.
上面结果显示 stackexchange.com 有四条 NS
记录,同时返回的还有每一条 NS 记录对应的 IP 地址。
然后,DNS 服务器向上面这四台 NS 服务器查询 math.stackexchange.com 的主机名。
math.stackexchange.com. 300 IN A 151.101.129.69
math.stackexchange.com. 300 IN A 151.101.1.69
math.stackexchange.com. 300 IN A 151.101.65.69
math.stackexchange.com. 300 IN A 151.101.193.69
;; Received 115 bytes from 216.239.34.109#53(ns-cloud-d2.googledomains.com) in 35 ms
上面结果显示,math.stackexchange.com 有 4 条 A 记录,即这四个 IP 地址都可以访问到网站。并且还显示, 最先返回结果的 NS 服务器是 ns-cloud-d2.googledomains.com,IP 地址为 216.239.34.109。
七、NS 记录的查询
dig 命令可以单独查看每一级域名的 NS
记录。
$ dig ns com
$ dig ns stackexchange.com
+short 参数可以显示简化的结果
$ dig +short ns com
$ dig +short ns stackexchange.com
八、DNS 的记录类型
域名与 IP 之间的对应关系,称为 “记录”(record)。根据使用场景,“记录"可以分成不同的类型(type),前面已经看到了有 A
记录和 NS
记录。
域名系统是分层次的,一些 DNS 服务器位于顶层,当查询(域名) IP 时,路由或 ISP 提供连接 DNS 服务器的信息。较底层的 DNS 服务器缓存映射, 它可能会因为 DNS 传播延时而失效。DNS 结果可以缓存在浏览器或操作系统中一段时间,时间长短取决于 存活时间 TTL。
常见的 DNS 记录类型如下。
- A:地址记录(Address),返回域名指向的 IP 地址。
- AAAA: 记录是域名到 IPV6 地址。
- NS:域名服务器记录(Name Server),返回保存下一级域名信息的服务器地址。该记录只能设置为域名,不能设置为 IP 地址。
- MX:邮件记录(Mail eXchange),返回接收电子邮件的服务器地址。
- CNAME:规范名称记录(Canonical Name),返回另一个域名,即当前查询的域名是另一个域名的跳转,详见下文。
- PTR:逆向查询记录(Pointer Record),只用于从 IP 地址查询域名,详见下文。
一般来说,为了服务的安全可靠,至少应该有两条 NS
记录,而 A
记录和 MX 记录也可以有多条,这样就提供了服务的冗余性,防止出现单点失败。
CNAME
记录主要用于域名的内部跳转,为服务器配置提供灵活性,用户感知不到。举例来说,facebook.github.io 这个域名就是一个 CNAME 记录。
$ dig facebook.github.io
...
;; ANSWER SECTION:
facebook.github.io. 3370 IN CNAME github.map.fastly.net.
github.map.fastly.net. 600 IN A 103.245.222.133
上面结果显示,facebook.github.io 的 CNAME 记录指向 github.map.fastly.net。也就是说,用户查询 facebook.github.io 的时候, 实际上返回的是 github.map.fastly.net 的 IP 地址。这样的好处是,变更服务器 IP 地址的时候,只要修改 github.map.fastly.net 这个域名就可以了, 用户的 facebook.github.io 域名不用修改。
由于 CNAME
记录就是一个替换,所以域名一旦设置 CNAME
记录以后,就不能再设置其他记录了(比如 A
记录和 MX 记录),这是为了防止产生冲突。
举例来说,foo.com 指向 bar.com,而两个域名各有自己的 MX 记录,如果两者不一致,就会产生问题。由于顶级域名通常要设置 MX 记录,
所以一般不允许用户对顶级域名设置 CNAME 记录。
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。
逆向查询的一个应用,是可以防止垃圾邮件,即验证发送邮件的 IP 地址,是否真的有它所声称的域名。
dig 命令可以查看指定的记录类型。
$ dig a github.com
$ dig ns github.com
$ dig mx github.com
九、其他 DNS 工具
除了 dig,还有一些其他小工具也可以使用。
(1)host 命令
host 命令可以看作 dig 命令的简化版本,返回当前请求域名的各种记录。
$ host github.com
github.com has address 192.30.252.121
github.com mail is handled by 5 ALT2.ASPMX.L.GOOGLE.COM.
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.
$ host facebook.github.com
facebook.github.com is an alias for github.map.fastly.net.
github.map.fastly.net has address 103.245.222.133
host 命令也可以用于逆向查询,即从 IP 地址查询域名,等同于 dig -x 。
host 192.30.252.153
153.252.30.192.in-addr.arpa domain name pointer pages.github.com.
(2)nslookup 命令
nslookup 命令用于互动式地查询域名记录。
$ nslookup
> facebook.github.io
Server: 192.168.1.253
Address: 192.168.1.253#53
Non-authoritative answer:
facebook.github.io canonical name = github.map.fastly.net.
Name: github.map.fastly.net
Address: 103.245.222.133
(3)whois 命令
whois 命令用来查看域名的注册情况。
$ whois github.com
十、参考链接
转载声明
本文转载自 DNS 原理入门,笔者在文字描述和图片排版上略作修改。
附录
DNS 流程
下面是笔者补充的部分,简述下 DNS
的流程。
- 浏览器输入域名,如
www.example.com
- 查询当前硬件的缓存(
host 文件
或浏览器缓存)中是否存在该域名对应的记录,如果存在直接使用,如果不存在则进入后续流程 - 向运营商的 DNS 服务器发起 DNS 解析的请求,一般称运营商的 DNS 服务器为
Local DNS
Local DNS
会查询缓存记录 (内部实现对请求端来说是透明的)Local DNS
如果没有缓存,会把域名从右往左扫描,依次请求对应的服务器- 对于域名
www.example.com
,先去请求根域名服务器,假设根域名服务器返回了管理.com
域的服务器,名字为 TLD Local DNS
请求管理 TLD 服务器- 一般来说,TLD 返回的记录是一条 CNAME 记录,这里假设域名的 CNAME 解析到了 Amazon
Local DNS
请求 Amazon 的 DNS 服务器 (一般称之为权威服务器,权威服务器是 Amazon 自己构建的)- Amazon 返回
www.example.com
对应的服务器 IP 地址 Local DNS
缓存这个 IP 地址,并且返回给浏览器- 浏览器和返回的 IP 地址建立 TCP 连接,发送 HTTP 报文
DNS 轮询
当访问服务器时,客户端需要先向 DNS 服务器查询服务器的 IP 地址,如果在 DNS 服务器中填写多个名称相同的记录,则每次查询时 DNS 服务器都会按顺序返回不同的 IP 地址。
在 Linux 中可以使用 nslookup 命令查看 DNS 轮询结果。
$ nslookup dbwu.tech
# 输出如下
Server: fe80::1%11
Address: fe80::1%11#53
Non-authoritative answer:
Name: dbwu.tech
Address: 104.21.71.166
Name: dbwu.tech
Address: 172.67.147.110
$ nslookup dbwu.tech
# 输出如下
Server: fe80::1%11
Address: fe80::1%11#53
Non-authoritative answer:
Name: dbwu.tech
Address: 172.67.147.110
Name: dbwu.tech
Address: 104.21.71.166
DNS 轮询最典型的应用就是网站的负载均衡,比如某个网站有 10 台 Web 服务器,就可以在 DNS 里创建 10 个同名记录指向这些服务器的 IP。由于不同客户端查到的结果顺序不同,而且一般会选用结果中的第一个 IP,所以大量客户端就会被均衡地分配到 10 台Web服务器上。
但这种方式是有缺点的。假如多台 Web 服务器中有一台出现了故障,这时我们希望在返回 IP 地址时能够跳过故障的 Web 服务器,然而普通的 DNS 服务器并不能确认 Web 服务器是否正常工作,因此即便 Web 服务器宕机了,它依然可能会返回这台服务器的 IP 地址。
此外,轮询分配还可能会引发一些问题。在通过 CGI 等方式动态生成网页的情况下,有些操作是要跨多个页面的,如果这期间访问的服务器发生了变化,这个操作就可能无法继续。例如在购物网站中,可能会在第一个页面中输入地址和姓名,在第二个页面中输入信用卡号,这就属于刚才说的那种情况。
DNS 预加载
DNS 查询需要个 RTT 时间,在浏览器级别、系统级别都有一层 DNS 缓存,之前解析过的可以直接从本机缓存获取,以减少延迟。
Web 标准提供了一种 DNS 预解析技术,因为服务器是知道页面即将会发生哪些请求的,那我们可以在页面顶部,插入 <link rel="dns-prefetch" href="//host/">
,让浏览器先解析一下这个域名。后续扫到同域的请求,就可以直接从 DNS 缓存获取了。
此外 Web 标准也提供 prefetch,prerender
的预加载技术。prefectch 会在浏览器空闲的时候,向所提供的链接发起请求,而 prerender 不仅会请求,还会帮你在后台渲染页面。
如果在一个页面中,你知道用户有很大概率去点某个链接,可以尝试把这个链接加到 prefetch 或 prerender,那么用户点击时就会秒开这个页面了。
DNS 根服务器
分配个根域服务器的 IP 地址只有 13 个,不代表根域服务器只有 13 个。
之所以是 13 个,是因为 DNS 服务器的请求和响应报文的发送,是基于 UDP 协议的(追求最大传输速率的不可靠连接),而早期的 UDP 数据报存在大小的限制,RFC 1035 规定了 UDP 协议的数据容量(除去 IP 头部和 UDP 头部)只能是 512 字节,而 512 字节只能容纳 13 个根服务器的 NS 记录。
详细解析请参考:为什么域名根服务器只能有13台呢? - 命运之轮的回答 - 知乎
迭代查询和递归查询
递归查询时,过程查询的递交者变化。 迭代查询时,过程查询的递交者不变,关键在于查询主体。
对于递归查询来说,只知道某个 DNS 服务器解析了 example.com,并不关系这中间的过程是如何实现的 (黑盒模式,甩手掌柜)。
对于迭代查询来说,客户端先查到根服务器的地址,再从根服务器查到权威服务器,然后从权威服务器一直向下查询 …, 直到返回最终的解析结果 (白盒模式,亲力亲为)。
使用 dig
命令时,加上 +trace
参数可以强制采用迭代查询:
如何自建 DNS 服务器
- 在自己的服务器上 (例如 10.0.0.1) 构建一个
DNS
服务 (可以理解为一个软件,支持DNS
协议,就像 Nginx 支持 HTTP 协议) - 在域名系统管理后台,新增一条 CNAME 记录,将域名的解析转交给 10.0.0.1
- 10.0.0.1 可以自定义返回各个域名对应的 IP 地址,实现自定义
DNS
服务器
DNS 安全问题
放大攻击
如图所示为执行 dig ANY isc.org
命令解析 isc.org 时抓的包,可见 6 号包发出去的请求只有 25 字节,而 11 号包收到的回复却能达到 3111 字节,回复数据被放大了 124 倍。
假如在 6 号包里伪造一个想要攻击的源地址,那该地址就会莫名收到 DNS 服务器 3111 字节的回复,利用这个放大效应,只要控制少量计算机 (肉鸡) 就能把一个大网站攻击瘫痪。
HTTPDNS
HTTPDNS 本质上是通过 HTTP 完成对 DNS 版本的 “服务发现”。
根据用户的地址来判断应该返回什么IP,从而跳过传统DNS的影响。
使用 HTTPDNS 取代常规的 DNS 解析,这是很多移动应用会选择的方法,使用 HTTP 协议绕过链路中的 DNS 服务器,可以避免域名劫持问题。
工作原理
- 应用请求: 移动设备或应用通过发送 HTTP 请求来获取特定域名的 IP 地址
- DNS 解析服务器: HTTPDNS 解析服务器接收到请求后,会根据请求中的域名信息进行解析
- 返回 IP 地址: 解析服务器将域名对应的 IP 地址通过 HTTP 返回给移动设备或应用
- 缓存机制: HTTPDNS 通常会在解析服务器中设置缓存机制,以减少对真实 DNS 服务器的请求次数 (提升性能)
优点和使用场景
- 避免 DNS 污染: HTTPDNS 通过使用 HTTP 绕过了 DNS 被污染返回错误的 IP 地址问题
- 负载均衡和性能优化: HTTPDNS 可以根据网络状况和用户位置,动态地返回最优的 IP 地址
- 灵活性: HTTPDNS 域名解析的结果由服务器端控制 (类似 SDN 的思路),可以灵活地配置和调整 IP 地址
- 移动网络优化: HTTPDNS 可以适应不同的移动网络环境,提供稳定的用户体验
GSLB
基于 DNS 的全局负载均衡 GSLB (Global Server Load Balancing), 不仅为服务提供了负载均衡和高可用的功能,还可以在多个地理位置或数据中心之间进行负载均衡,确保用户的请求被发送到最合适的服务器。
在 DNS 解析域名的同时使用负载均衡算法计算服务器 IP 地址。大型网站基本使用了 DNS 做为第一级负载均衡手段,然后在内部使用其它方式做第二级负载均衡。也就是说,域名解析的结果为内部的负载均衡服务器 IP 地址。
优点: DNS 能够根据地理位置进行域名解析,返回离用户最近的服务器 IP 地址。
缺点: 由于 DNS 具有多级结构,每一级的域名记录都可能被缓存,当下线一台服务器需要修改 DNS 记录时,需要过很长一段时间才能生效。