DNS 的本质
DNS 本质上是一个分布式数据库,将人类可读的域名映射为机器可读的 IP 地址。它的设计目标是:去中心化、高可用、高性能。
完整解析链路
当你在浏览器输入 www.google.com 时,DNS 解析经历以下步骤:
浏览器缓存 → OS 缓存 → hosts 文件 → 本地 DNS 服务器 → 根DNS → 顶级域DNS → 权威DNS
第一步:本地缓存查询
1. 浏览器 DNS 缓存(Chrome: chrome://net-internals/#dns)
2. 操作系统缓存(Linux: /etc/nsswitch.conf 控制顺序)
3. /etc/hosts 文件
4. 本地 DNS 服务器缓存
第二步:递归查询
本地 DNS 服务器(如 223.5.5.5)代替客户端完成整个解析过程:
客户端 → 本地DNS: "www.google.com 的 IP 是什么?"
本地DNS → 根DNS (.): "请问 .com 在哪?"
根DNS → 本地DNS: "去找 .com 顶级域服务器"
本地DNS → .com TLD: "www.google.com 在哪?"
.com TLD → 本地DNS: "去找 google.com 权威DNS"
本地DNS → google.com 权威: "www.google.com 在哪?"
google.com 权威 → 本地DNS: "142.250.80.46"
本地DNS → 客户端: "142.250.80.46(缓存TTL=300秒)"
第三步:迭代查询(权威 DNS 之间)
根 DNS、TLD DNS、权威 DNS 之间使用迭代查询——每个 DNS 服务器只告诉下一步该找谁,不代替你完成全部查询。
DNS 记录类型
| 类型 | 用途 | 示例 |
|---|---|---|
| A | 域名 → IPv4 | example.com → 93.184.216.34 |
| AAAA | 域名 → IPv6 | example.com → 2606:2800:220:1::248 |
| CNAME | 域名别名 | www.example.com → example.com |
| MX | 邮件服务器 | example.com → mail.example.com |
| TXT | 文本记录 | SPF、DKIM、域名验证 |
| NS | 域名服务器 | example.com → ns1.example.com |
| SOA | 起始授权 | 包含序列号、刷新间隔等 |
| PTR | 反向解析 | 34.216.184.93 → example.com |
DNS 缓存与 TTL
TTL(Time To Live)
每条 DNS 记录都有 TTL 值(秒),表示缓存有效期:
# 查询 TTL
dig example.com +ttlunits
# 或
dig example.com | grep -A1 "ANSWER SECTION"
# 输出示例
# example.com. 300 IN A 93.184.216.34
# ^^^^ TTL=300秒
缓存层级
浏览器缓存(几分钟)
↓
OS 缓存(TTL 值)
↓
路由器缓存(几小时)
↓
ISP DNS 缓存(TTL 值)
↓
公共 DNS 缓存(TTL 值)
缓存清除
# Linux
sudo systemd-resolve --flush-caches
# 或
sudo resolvectl flush-caches
# macOS
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder
# Windows
ipconfig /flushdns
DNS 劫持与防御
常见劫持方式
- DNS 缓存投毒:向 DNS 服务器注入虚假记录
- 本地 hosts 篡改:修改 hosts 文件
- ISP 劫持:运营商透明代理 DNS 请求
- 中间人攻击:拦截 DNS 查询并返回虚假响应
检测方法
# 对比不同 DNS 服务器的结果
dig @8.8.8.8 example.com
dig @223.5.5.5 example.com
dig @1.1.1.1 example.com
# 如果结果不一致,可能存在劫持
防御手段
1. DoH(DNS over HTTPS)
# 使用 DoH 查询
curl -H 'accept: application/dns-json' \
'https://cloudflare-dns.com/dns-query?name=example.com&type=A'
DoH 的优势:
- 流量加密,无法被中间人窃听
- 隐藏在 HTTPS 流量中,难以被识别和阻断
- 使用标准 443 端口,兼容性好
2. DoT(DNS over TLS)
# 使用 DoT(端口 853)
kdig -d example.com @1.1.1.1 +tls
DoT 的特点:
- 专用端口 853,容易被识别和封锁
- 加密效果与 DoH 相同
- 适合内网环境
3. 本地 DNS 加密
# 使用 dnscrypt-proxy
apt install dnscrypt-proxy
# 配置 /etc/dnscrypt-proxy/dnscrypt-proxy.toml
DNS 性能优化
本地 DNS 缓存服务
# 使用 systemd-resolved
sudo systemctl enable systemd-resolved
sudo systemctl start systemd-resolved
# 配置 /etc/systemd/resolved.conf
[Resolve]
DNS=223.5.5.5 119.29.29.29
FallbackDNS=8.8.8.8
Cache=yes
DNSStubListener=yes
DNS 预解析
<!-- 在 HTML 中预解析常用域名 -->
<link rel="dns-prefetch" href="//www.google.com">
<link rel="preconnect" href="https://www.google.com">
缓存大小优化
# 查看 systemd-resolved 缓存状态
resolvectl statistics
# 查看缓存命中率
resolvectl query example.com
面试常见问题
DNS 使用 TCP 还是 UDP?
- 主要使用 UDP(响应快、开销小)
- 区域传送(AXFR)使用 TCP
- 响应超过 512 字节时切换到 TCP(EDNS0 可扩展到 4096)
DNS 为什么不用 TCP?
- TCP 需要三次握手,延迟高
- UDP 简单快速,适合短查询
- DNS 查询通常很小,不需要 TCP 的可靠性保证
什么是 DNS 放大攻击?
- 攻击者伪造源 IP 向开放 DNS 服务器发送查询
- DNS 服务器将大量响应发送到受害者 IP
- 利用 DNS 响应比请求大的特点进行放大
什么是 EDNS0?
- DNS 扩展机制,支持更大的 UDP 包(最大 4096 字节)
- 支持 DNSSEC、DNS COOKIE 等新特性
实战:搭建本地加密 DNS
# 安装 dnscrypt-proxy
apt install dnscrypt-proxy
# 配置 /etc/dnscrypt-proxy/dnscrypt-proxy.toml
listen_addresses = ['127.0.0.1:53']
server_names = ['alidns', '腾讯DNS']
max_clients = 250
ipv4_servers = true
dnssec = true