一个强大的基于 Go 的 DNS 库
Contents
概要
dns 是一个基于 Go 的 DNS 库,支持 DNS 的所有特性,严格遵循 Less is more 的原则,被广泛用在很多著名的开源项目中,比如 CoreDNS(作者同时也是 CoreDNS 的核心开发者)。
这个库不仅仅可以用于客户端的 DNS 查询,而且也可用于服务端 nameserver 的编程(比如 CoreDNS)。Go 标准库客户端的 DNS 查询功能比较单一(比如 net.Lookup*
系列),缺少更细粒度的控制,如有特殊的需要,我们也可以用这个库构建符合自身服务特点的 DNS 查询。
这个库非常容易上手,在开玩之前我们先来复习一下 DNS 的基础知识。
DNS 基础知识复习
备注:大部分内容来源于《TCP/IP 详解》
DNS 报文格式
DNS 的查询和响应都遵循如下格式:
这个报文由 12 bytes 的首部和 4 个长度可变的字段组成。
-
标识:由 client 设置并由 server 返回结果,用以确定响应与查询是否匹配;
-
标志:16 位的标志字段被划分为若干子字段,如下图所示:
-
QR
:1 bit,0 表示查询报文,1 表示响应报文; -
opcode
:4 bit,通常值为 0(标准查询),其他值为 1(反向查询)和 2(服务器状态请求); -
AA
:1 bit,表示授权回答(authoritative answer),该名字服务器是授权于该域; -
TC
:1 bit,表示可截断(truncated)。使用 UDP 时,表示当应答总长度超过 512 bytes 时,只返回 512 bytes; -
RD
:1 bit,表示期望递归(recursion desired)。该比特能在一个查询中设置,并在响应中返回。该标志告诉名字服务器必须处理这个查询,即这是一个递归查询(说白了就是,我把请求交给你,不管你最终找谁处理,最后必须将响应回复给我)。如果设置为 0,说明这是一个迭代查询(我把请求给你,如果当前名字服务器无法处理,返回谁可以处理的名单),当请求的名字服务器没有一个授权回答,它就返回一个能解答该类型的其他名字服务器的列表; -
RA
:1 bit,表示可用递归。如果名字服务器支持递归查询,则在响应中将该位设置为 1; -
随后的 3 bit 必须为 0;
-
rcode
:4 bit 的返回码,通常为 0(没有差错)和 3(名字差错)。名字差错只有从一个授权名字服务器上返回,表示查询中指定的域名不存在。
-
-
问题数、资源记录数、授权资源记录数和额外资源记录数分别对应最后 4 个可变长字段中包含的条目数,对于查询报文,问题数通常是 1,而其他 3 项均为 0;对于应答报文,回答数至少为 1,剩下的两项可以是 0 或非 0;
-
查询问题
问题部分中每个问题的格式如下所示(通常只有 1 个问题):
-
查询名:即要查找的 DNS 名字,本质上一个分段带长度的字符序列,每个标识符以首字节的计数值来说明随后标识符的字节长度,每个名字以字节 0 结束。长度为 0 的标识符是根标识符。
计数字节的值必须是 0~63 的数,因此标识符的最大长度仅为 63。
该字段无须对齐和填充,如下是域名
gemini.tuc.noao.edu.
的表示: -
查询类型:每个问题都有一个查询类型,而每个响应(即每个资源记录)也有一个类型。比如
A
记录的话,该字段为 1; -
查询类:通常是 1,指互联网地址(IP);
-
-
DNS 报文中最后三个字段均采用资源记录 RR(Resource Record)的相同格式,如下所示:
-
域名:记录中数据对应的名字,格式与前文查询报文中对应字段一致;
-
类型:与前文中的查询报文中的查询类型一致;
-
类:通常是 1,指互联网数据;
-
生存时间:客户端程序保留该资源记录的秒数,通常为 2 天;
-
资源记录长度:说明资源记录的数量,该数据的格式依赖于类型字段的值。对于类型 1(A 记录)的资源数据是 4 字节的 IP 地址;
-
标签压缩
每一个查询请求和 RR 都是由名称开始,名称由一系列标签(label)组成。标签有两种类型:数据标签(data label)和压缩标签(compression label)。数据标签如上文所述,压缩标签则相当于一个指针。
在许多情况下,DNS 响应消息中几个字段中会有重复字符串,为避免这种冗余,采用如下压缩方式:原本计数字节高两位设置为 1,剩余位与随后的一个 byte 组成一个 14 位的指针,即偏移量,给出距离距离 DNS 消息开始出的字节数,在那可找到一个用于替代压缩标签的数据标签,即压缩标签能够指向一个距离开始处多达 16384(2^14
)个字节的位置。如下所示:
这表示了两个域名:usc.edu
和 ucla.edu
,其中 edu
字符串只需要出现一次,并与其他域名共享。
常见资源类型
-
A 记录:一个 A 记录定义了一个 IPv4 地址,存储 32 bit 的二进制数;
-
AAAA 记录:查询 IPv6 地址;
-
CNAME 记录:另一个域名的别名。即解析出来是另外一个域名;
-
SRV 记录:用来标识某台服务器使用了某个服务;
一个查询的例子
如果我们查询 gemini.tuc.noao.edu.
,得到的响应如下所示:
查询返回两个 A 记录(即这个域名对应两个 IP)。
注意以下几点:
-
返回结果中包含查询问题;
-
在返回的结果中会有许多重复的域名,因此使用压缩方式。比如上面的例子域名出现了 3 次,使用标签压缩的方式,则 RR 中原本域名的字段变成指针字段;
实际例子
我们来看一个实际的例子,根据这个例子稍加修改而来:
|
|
具体的含义可以参考具体注释。
让我们运行一下这个例子:
|
|
除了这个非常简单的例子之外,作者还在 exdns 列举了更多的例子,大家可以进一步阅读。
祝大家玩得愉快 !