之前我们实现了一个简单的 DNS 服务器(007. 从零开始搭建 DNS 服务器——入门篇),今天我们就来试试进阶操作,搭建一个功能更强大的 DNS 服务器。
DNS 服务器的解析大致分为几类,A、AAAA、CNAME、MX、NS、TXT、SRV、SOA、PTR 等,大致为:
A记录: 将域名指向一个 IPV4 地址
【资料图】
AAAA记录:将域名指向一个 IPV6 地址
CNAME 记录:将该域名指向另外一个域名
MX 记录:建立电子邮箱服务,将指向邮件服务地址
NS 记录:域名解析记录,如果要将子域名指定某个域名服务器来解析,需要设置 NS 记录
TXT 记录:可任意填写,一般为某主机或域名设置使用,用于做一些备注
PTR 记录:记录 A 记录的逆向记录,又称做 IP 反查记录或指针记录,负责将 IP 反向解析为域名
SOA 记录:表示开始权限记录,记录用于在众多 NS 记录中那一台是主服务器
QTYPE 实际上还有其他类型,所有可能的 type 及其约定的数值表示如下:
在这十几种类型中,最常用的是 A 记录,负责将域名转换为 IPV4 地址。在进阶篇中,只对 A 记录作特殊处理,其余类型仍是直接转发数据。
DNS 请求与响应的格式是一致的,其整体分为 Header、Question、Answer、Authority、Additional 五部分,如下图所示:
Header 部分是一定有的,长度固定为 12 个字节;其余 4 部分可能有也可能没有,并且长度也不固定,这个在 Header 部分中有指明。Question 与 Answer 部分在 A 记录中也是一定有的。注意,Question 与 Answer 部分并不是一定成对出现的。在请求中有 Question 部分,在响应中不一定会有 对应的Answer 部分。
1. 导入模块
使用 socketserver 代替 socket 库,它提供了服务器中心类,可以简化 DNS 服务器的开发。
socketserver 内部使用 IO 多路复用以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的 socket 服务端。即:每个客户端请求连接到服务器时,socket 服务端都会创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求,从而大大提升了并发处理能力。
由于是 Python 标准库,直接引入:
使用 dnslib 解析 DNS 报文,拦截并重新生成新的响应报文。
引入 time 库处理时间,用于 DNS 缓存过期控制。
引入 logging 库记录服务器日志,并进行配置。
2. 启动服务
以上下文管理器方式启动多线程 UDP 服务器,将会持续运行,直到手动结束程序退出为止。
3. 缓存控制与自定义记录
新建一个 Hosts 类,负责缓存与自定义记录。
可以在 record 中添加自定义记录,并设定过期时间为 9e11(相当于永不过期)。设定初始缓存为自定义记录。
对于一般的 DNS 记录,设定缓存时间为 12 小时。在每次 DNS 请求完毕,可以检查一次缓存,并清除过期记录:
4. 请求处理
在启动服务中,我们可以看见在 ThreadingUDPServer中传入了一个 Threading UDP RequestHandler参数,这是一个请求处理类,继承自 DatagramRequestHandler,即 UDP请求处理类。
在这个类里,我们需要重写 handle 函数,进行 DNS 请求的处理。
首先,我们需要解析客户端发来的数据,解析出请求类型是否为 A 记录。不是 A 记录就直接转发数据:
A 记录也有两种情况:一是有缓存(包含自定义记录),直接重缓存中获取 IP 地址,封装后发给原客户端。另一种则是没有缓存(或缓存过期)的情况,需要重新请求获取 IP 地址。由于需要将新得到记录加入缓存,也需要对响应报文解包再封包发回。
处理逻辑如下:
在 get_answer_A 函数中,对两种情况区别处理:
最后,由于引入了缓存与日志,还需要重写 finish 方法,最后清理缓存并添加 DNS 解析记录:
这样,一个具有并发处理能力,支持自定义记录、缓存机制、日志查看的实用 DNS 服务器就完成了。
至于有没有高级篇,那就不好说啦。
5. 完整代码
DNS 消息格式:https://www.cnblogs.com/cobbliu/archive/2013/04/02/2996333.html
自己动手实现 DNS 协议:https://www.cnblogs.com/dongkuo/p/6714071.html
写一个 DNS 服务器:http://python.quanduan.com/post/dns-fu-wu-qi/ji-chu/xie-yi-ge-dns-fu-wu-qi
socketserver --- 用于网络服务器的框架:https://docs.python.org/zh-cn/3/library/socketserver.html