DNS协议详解
什么是DNS协议
DNS是一种可以将域名和IP地址相互映射的以层次结构分布的数据库系统。DNS系统采用递归查询请求的方式来响应用户的查询,为互联网的运行提供关键性的基础服务。目前绝大多数的防火墙和网络都会开放DNS服务,DNS数据包不会被拦截,因此可以基于DNS协议建立隐蔽信道,从而顺利穿过防火墙,在客户端和服务器之间传输数据。
DNS服务器
域名对应的IP地址都保存在DNS服务器。我们输入域名,浏览器就会在后台自动向DNS服务器发出请求,获取对应的IP地址。这就是DNS查询。
举个栗子,当我在浏览器输入www.ixxzhi.cn
这个域名,浏览器就要像DNS服务器查询www.ixxzhi.cn
对应的IP地址是什么,然后向该IP发出访问请求。
网上有很多公用的DNS服务器,我们选择Cloudflare公司提供的1.1.1.1
进行演示。
dig命令
命令行工具dig可以跟DNS服务器互动,我们就用它演示DNS查询。
它的查询语法如下:
1 | dig @[DNS服务器] [域名] |
假设我们要向1.1.1.1
查询www.ixxzhi.cn
这个域名,就可以执行下面的命令:
1 | dig @1.1.1.1 www.ixxzhi.cn |
正常情况下它会输出一大堆内容:
其中 “ANSWER SECTION”部分,给出了查询到的A记录和CNAME记录等信息。
域名的树状结构
要说清楚DNS完整的查询过程,就必须了解**域名是一个树状结构**。
根
在最上面的是根,但是没有对应的名字。
顶级域名
根下面一级的节点就是最高一级的顶级域名(由于根没有名字,所以在根下面一级的域名就叫做顶级域名)。它分成两种:
- 通用顶级域名(gTLD,比如
.com
和.net
) - 国别顶级域名(ccTLD,比如
.cn
和.us
)
顶级域名又国际域名管理机构ICANN控制,它委托商业公司管理gTLD,委托各国管理自己的国别域名。
- 通用顶级域名(gTLD,比如
二级域名
二级域名就是你在某个顶级域名下面,自己注册的域名。比如,
ixxzhi.cn
就是我在顶级域名.cn
下面注册的。三级域名
三级域名是二级域名的子域名,是域名拥有者自行设置的,不用得到许可。比如,
alist
就是ixxzhi.cn
的三级域名
域名的逐级查询
这种树状结构的意义在于,**只有上级域名,才知道下一级域名的IP地址,需要逐级查询**。
每一级域名都有自己的DNS服务器,存放下一级域名的IP地址。
所以想要查询三级域名www.ixxzhi.cn
的IP地址,需要三个步骤:
- 查询根域名服务器,获得顶级域名服务器
.cn
(又称TLD服务器)的IP地址。 - 查询TLD服务器
.cn
,获得二级域名服务器ixxzhi.cn
的IP地址。 - 查询二级域名服务器
ixxzhi.cn
,获得三级域名www.ixxzhi.cn
的IP地址。
下面依次演示三个步骤:
做实验之前我们添加一个A记录把test.ixxzhi.cn指向 11.22.33.44
根域名服务器
- 根域名服务器是最高层次的域名服务器,所有的根域名服务器都知道所有的顶级域名服务器的IP地址。
- 根域名服务器也是最重要的域名服务器,不管是哪个本地域名服务器,若要对Internet上任何一个域名进行解析,只要自己无法解析,就首先请求根域名服务器。
- Internet上有13个根域名服务器,尽管我们将这13个根域名服务器中的每一个都视为单个服务器,但是每个”服务器“实际上是冗余服务器的集群,以提供安全性和可靠性。
1 | A 根:198.41.0.4 A.root-servers.net:美国加利福尼亚州洛杉矶 |
根域名服务器的IP地址是不变的,集成在操作系统里面。操作系统会选其中一台查询TLD服务器的IP地址。
1 | dig @198.41.0.4 test.ixxzhi.cn |
上面示例中,我们选择198.41.0.4
,向它发出请求,询问test.ixxzhi.cn
的TLD服务器的IP地址。
dig命令的输出结果如下:
因为它给不了 test.ixxzhi.cn
的IP地址,所以输出结果中没有ANSWER SECTION,只有一个AUTHRITY SECTION,给出了 cn.
的TLD服务器的域名。下面还有一个ADDITIONAL SECTION,给出了这几台服务器的IP地址(包含IPv4和IPv6两个地址)。
TLD服务器
有了TLD服务器的IP地址以后,我们再选一台接着查询:
1 | dig @203.119.27.1 test.ixxzhi.cn |
上面示例中,203.119.27.1
是随便选的一台 .cn
的TLD服务器,我们向它询问 test.ixxzhi.cn
的IP地址。
它依然没有ANSWER SECTION 的部分,只有AUTHORITY SECTION,给出了二级域名 ixxzhi.cn
的两台DNS服务器。
二级域名的DNS服务器
上面只给出了NS记录?没关系,我们可以通过下面的命令来查询该服务器的IP地址:
1 | dig zahir.ns.cloudflare.com |
我们再向二级域名的DNS服务器查询三级域名的IP地址。
1 | dig @172.64.35.145 test.ixxzhi.cn |
返回结果如下:
这次终于有了ANSWER SECTION,得到了三级域名test.ixxzhi.cn
的IP地址为 11.22.33.44
,至此三个步骤的DNS查询全部完成。
DNS递归和迭代查询
迭代查询
本地域名服务器向根域名服务器的查询通常是采用迭代查询。迭代查询的特点:当根域名服务器收到本地域名服务器发出的迭代查询请求报文时,要么给出所要查询的IP地址,要么告诉本地服务器:“你下一步应当向哪个域名服务器进行查询”。然后让本地服务器进行后续的查询。根域名服务器通常是把自己知道的顶级域名服务器的IP地址告诉本地域名服务器,让本地域名服务器再向顶级域名服务器查询。顶级域名服务器收到本地域名服务器的查询请求后,要么给出所要查询的IP地址,要么告诉本地域名服务器下一步应当向哪一个权威域名服务器进行查询。最后,知道了所要解析的IP地址或报错,然后把这个结果返回给发起查询的主机。
递归查询
主机向本地域名服务器的查询一般都是采用递归查询。所谓递归查询就是:如果所询问的本地域名服务器不知道被查询域名的IP地址,那么本地域名服务器就以DNS客户端的身份,向其它根域名服务器继续发出查询请求报文,而不是让该主机自己进行下一步查询。递归查询时返回的结果只有两种:查询成功或查询失败。
DNS缓存机制
一条域名的DNS记录会在本地有两种缓存:
- 浏览器缓存
- 操作系统(OS)缓存
在浏览器中访问的时候,会优先访问浏览器缓存,如果未命中则访问OS缓存,最后再访问DNS服务器(一般是ISP提供)。同时本地的etc/hosts
中也可以记录DNS解析记录。Windows系统在 C:\Windows\System32\drivers\etc\hosts
。
Windows中刷新dns缓存命令:
1 | ipconfig /flushdns |
而由于很多Linux发行版都没有内置DNS本地缓存,所以无需刷新。DNS记录会有一个TTL
值(time to live),单位是秒,意思是这个记录最大有效期是多少。经过实验,OS缓存会参考ttl值,但是不完全等于TTL值,而浏览器DNS缓存的时间跟TTL值无关,每种浏览器都使用一个固定值。
DNS隧道
设置域名服务器
登录你的域名服务商,比如:https://dash.cloudflare.com/
在自己的域名下面添加一条A记录,名称任意(如 test2.ixxzhi.cn),值为你的VPS服务器的IP地址。
再添加一条NS记录,名称任意(如ns1.ixxzhi.cn),值为上面A记录的名称:test2.ixxzhi.cn
在服务器上运行dns2tcpd
如果安装了dns2tcp,编辑/etc/dns2tcpd.conf
,改成如下配置:
1 | listen = 0.0.0.0 |
服务端启动如下命令启动dns2tcpd:
1 | dns2tcpd -f /etc/dns2tcpd.conf -F -d 2 |
客户端运行dns2tcpc
DNS2TCP 的客户端配置较为简单, 一条命令就行:
1 | dns2tcpc -r ssh -z ns1.ixxzhi.cn 1.2.3.4 -l 8888 -d 2 |
-r 后接服务名称, 这里我们用ssh
-z 后接NS记录的网址、ip, 注意IP地址最好写上, 可以不写
-l 后接本地端口
-d 开启 Debug
ssh连接测试
至此我们已经成功搭建了DNS隧道。现在我们尝试ssh连接到客户端主机的8888端口:
1 | ssh root@127.0.0.1 -p 8888 -i rsa.key |
自己搭建DNSLOG
设置域名服务器
登录你的域名服务商,比如:https://dash.cloudflare.com/
在自己的域名下面添加一条A记录,名称任意(如 test2.ixxzhi.cn),值为你的VPS服务器的IP地址。
再添加一条NS记录,名称任意(如ns1.ixxzhi.cn),值为上面A记录的名称:test2.ixxzhi.cn
监听53端口
1 | tcpdump -n -i eth0 udp dst port 53 |
发包测试
现在尝试dig 获取一下test1111.ns1.ixxzhi.cn
的IP:
1 | dig test111.ns1.ixxzhi.cn |
此时到VPS上查看请求日志:
在这里我们可以看到解析的记录了。
但是由于缓存的原因第一次请求test1111.ns1.ixxzhi.cn
之后短时间内请求同一域名不会请求到DNS服务器,所以为了避免重复我们可以第一次请求test1111.a.ns1.ixxzhi.cn
,第二次请求test1111.b.ns1.ixxzhi.cn
以此类推。。。