Browsed by
标签:dns

NAME、CNAME 格式

NAME、CNAME 格式

总体而言, 早期 DNS 协议是个比较简单的协议。受限于 UDP 大小和数据量, 基于 UDP 的 DNS 协议限制了包大小,并且采用二进制编码方式力求减少数据量占用。

二进制编码方式相比文本方式(例如HTTP、SIP、SMTP等)最明显的缺点就是不直观,再用一些技巧减少数据量,DNS 协议在定义、实现方面总显得有些弯弯绕绕。

比如 NAME、CNAME 等结构的编码。

本文记录了这些古怪的编码方式,方便以后查找,并帮助后续的开发者不会被代码给绕晕。作为我个人来说,还是尽快忘记这些吧。


方式一:纯压缩编码方式

这种方式应用很普遍,以“0xc0”标记开始,加上一个字节的 offset,进行消息包内的寻址即可。如下图所示:

DNS offset 编码
offset 方式编码

其中, 0x0c 就是偏移量。


方式二:直接编码方式

这种方式比较直接,占用数据量也相对大一些,将域名完整信息存在数据包中。如下图所示:

直接编码
直接编码方式

需要说明的是:并不是直接将所有字符存进去就 OK 了。对于域名数据,以“.”符号分割成几个单元,每个单元数据采用 “length + value” 方式进行编码,最后以0x00结束编码。以上述数据为例:

08 6e 73 69 6e 61 69 6d 67 04 67 73 6c 62 08 73
69 6e 61 65 64 67 65 03 63 6f 6d 00

比如,第一个 0x08,说明后面的“nsinaimg”(即 6e 73 69 6e 61 69 6d 67)长度;然后是 0x04,说明后面的“gslb”(即67 73 6c 62)长度……其他单元类推。


方式三: 混合编码方式

这种方式其实就是综合了上述两种方式。以直接编码方式开始,然后以压缩(偏移)编码方式结尾。如下图所示:

混合编码方式
混合编码方式

对图中的数据码流描述如下:

03 6b 6c 6e 04 67 72 69 64 c0 38

0x03 描述了“kln”(即 6b 6c 6e)的长度,0x04 描述了“grid”(即 67 72 69 64)的长度,而域名的后续部分,通过“c0 38”编码可以知道:需要偏移0x38字节获取。

Cloudflare 返回的DNS结果

Cloudflare 返回的DNS结果

最近查一个客户遇到的 SIP 呼叫问题,发现在检查 DNS 记录时有问题,没有正确获取 DNS 记录。切换了几个 DNS 服务器测试,只有 Cloudflare DNS 服务器(1.1.1.x 系列) 返回的DNS记录会触发问题。

这并不是说 Cloudflare 的DNS记录有错误,而是我们自己实现的 DNS 库没有考虑到该记录的不同寻常之处。Cloudflare 的返回结果确实有点与众不同。

下图是Google DNS 以及 Ali DNS 返回的DNS结果,请注意其中的 Name 字段的内容(0xC0 0x0C)

Google DNS 结果
Google / Ali DNS 返回结果

很明显,这种DNS结果采用了 compress 方式,通过 offset 来获取真正的Name。图中的 0x0C 实际就是偏移量,指向了包中的另一处地址。Compress 方式最直接的好处就是节省了包大小。

而 Cloudflare 的 DNS 结果如下图所示,直接将域名包含在 Name 中,以 0x03 标注开始,以 0x00 表示字符串结束。不再需要偏移指向其他地址。

Cloudflare 的 DNS 记录
Cloudflare DNS 返回的结果

这种方法的好处是够直接,不再需要跳转获取真实的域名信息。当然坏处是增加了数据包的大小。

我们测试了网络上大部分的 DNS 服务器,目前仅发现 Cloudflare 采取了这种方式,其他服务器都是采用 compress 方式。

站在“人”的体验角度,似乎 Cloudflare 更合理一些。但是要注意,compress 方式更符合计算机的处理,毕竟前面的 Queries 部分已经解析过 Name 了,没必要再解析一次。另一方面,直接 offset 寻址,也远远快于再一次通过判断0x00来解析Name 字符串。

因此我个人认为 Cloudflare 的处理方式,即拖慢了速度,又增加了数据包消耗。当然,以目前计算机的处理能力以及网络速度,这些都是浮云。我们的解决方式也是淡定地接受这个结果,并修改 DNS 库进行适配即可。

在本次查问题中,测试了网上公布的香港 DNS 服务器,全部拒绝了来自大陆 IP 地址的 DNS 请求,真有意思。

设置SPF记录

设置SPF记录

最近发现的一个问题,如果邮件没有 SPF 记录,某些邮件服务器可能会过滤为垃圾邮件甚至直接拉入黑名单,比如 outlook 的服务器。相对而言,gmail 服务器要聪明很多,似乎在没有 SPF 记录的情况下,会反查MX记录及IP地址并进行匹配检验。

SPF的全称是:Sender Policy Framework, 由RFC7208文档定义详细规格。具体细节就不介绍了,基本意思就是让接收方的邮件服务器对发送者的域名和IP地址等进行检查。

在Linode的DNS管理中,仅仅添加MX记录是不够的,需要单独添加一条TXT记录,如图所示:

添加SPF的DNS记录
添加SPF的DNS记录

根据RFC7208的定义,可以进行很细致的控制。如果想省事,就如上图一样,设置一条”-all”的记录即可:

v=spf1 mx -all
网站启用IPv6应注意的事项

网站启用IPv6应注意的事项

网站支持IPv6的步骤比较简单,基本上目前常用的软件,例如Apache、Postfix、dovecot等,都已经支持IPv4和IPv6双栈,只要服务商的设备提供IPv4和IPv6连接,这些软件本身无需为IPv6做特别配置。

需要注意的是服务商处的DNS配置。如果DNS配置不全面,某些服务器在鉴权时会拒绝接受来自IPv6地址的消息。例如gmail服务器在收到 email 消息时,会鉴定DNS和IPv6地址是否匹配,如果不匹配,则视为垃圾邮件直接拒绝,可以看到如下错误警示:

Our system has detected that this 550-5.7.1 message does not meet IPv6 sending guidelines regarding PTR records 550-5.7.1 and authentication.

下面以Linode中的配置做简单介绍(对DO而言也基本如此)。假设IPv4地址是“1.2.3.4”,IPv6地址是“1:2:3:4::5:6”,域名是“demo.com”。

Reverse DNS

Linode 为每个节点分配的DNS名默认是类似的成员名,例如“1234.members.linode.com”。在 Linux 系统 ping 域名”demo.com”时,首先显示的是”1234.members.linode.com”,然后才是跳转到”demo.com”。某些服务应用会认为是PTR错误。在linode中需要设置“ reverse DNS”记录,将节点的DNS名直接指定为“demo.com”。

具体措施请参考linode的在线文档

如果同时支持IPv4和IPv6,上述文档里指导的操作需要单独操作两次,分别指定IPv4地址和IPv6地址。

MX 记录

另外一个需要注意的是 MX 记录,即邮件服务器记录。如果我们将 MX 记录设置为“mail.demo.com”,那么在DNS的A和AAAA记录中,要分别对“mail.demo.com”设置IPv4和IPv6地址,例如以下DNS记录结果:

mail.demo.com. 300 IN AAAA 1:2:3:4::5:6
mail.demo.com. 300 IN A 1.2.3.4

仅仅指定一个地址,对端有可能以域名和地址不匹配为由拒绝消息请求。

TTL

Linode 默认的TTL是3600秒,建议修改为300秒。TTL 太长太短都不好,要根据自己的实际情况调整,以方便自己网络部署、同时又不增加太多网络负担为准则。

解决DNS污染

解决DNS污染

DNS污染可能是一个比较普遍的问题,主要表现在:(1)DNS查询返回的IP地址根本是错误的。“错误”意思是IP地址要么根本不存在,要么就是个和域名完全无关系的地址。(2)IP地址虽然可能是对的,但是其他参数被莫名修改,例如TTL参数等。

测试了各类DNS服务器,例如阿里DNS、DNSPod(鹅厂)、Google DNS等。无一例外,最终的结果都或多或少被一些看不见的手给修改、甚至屏蔽了。从技术角度上讲,DNS协议本身非常随意、非常粗糙,是典型的互联网蛮荒时代的产物,比如面向UDP、无加密、无鉴权等,因此网络上任何一只手都有可能修改DNS的查询结果。网络进化到IPv6阶段,当然能解决这个问题,不过既然目前绝大部分设备还只支持IPv4协议栈,因此我们还是不得不修修补补来解决这个污染问题。

最根本的解决方法就是加密DNS查询。目前有些DNS服务商提供了私有的加密DNS方式,不过不太通用,需要私有的客户端程序配合。实际上可以不用搞这么复杂,自己建立加密通道,传递DNS消息即可。例如,参考下图的拓扑逻辑:

实现简单DNS透传的逻辑单元
实现简单DNS透传的逻辑单元

在这个网络中,有两个关键程序:dnsProxy和dnsAgent。

dnsProxy顾名思义就是个Proxy,本身并不负责DNS协议的解析,也不保存DNS的查询结果等信息,只是单纯地将DNS消息传递给真正的DNS服务器,并返回相应的结果即可。dnsProxy另一个功能是对外提供加密的数据连接,例如TLS、SSL加密等,甚至可以只是简单地对数据包进行自定义的异或运算即可。另外就是对外提供非标准连接接口,这点非常重要。DNS采用标准UDP53接口作为DNS服务器接口,网络上那些看不见的手,往往就是扫描并篡改53接口的数据包。这个小程序跑在境外(大家都懂的)的一台VPS设备上,推荐采用DigitalOcean,专业的云计算服务商,采用SSD硬盘,价格公道,我们一直用TA,如果你有兴趣的话,请点击这里自行了解细节。

dnsAgent是另一个小程序,主要负责建立与dnsProxy的加密连接,接收普通设备的DNS请求并将其传递给dnsProxy,同时返回DNS结果给普通计算设备。对于网内设备而言,dnsAgent就是个伪装的DNS服务器。同样,dnsAgent其实也不需要关心、也不需要解析DNS协议细节。在我的网络中,dnsAgent跑在一台常年吃灰的树莓派上(还是第一代的)。

实现这些仅仅需要一点UDP、TCP的网络知识,甚至不需要了解DNS协议的细节,无需对DNS数据包做修改。完成后可以愉快地打开很多以前打不开的网站。当然,有些网站始终是打不开的,这是另一个与DNS无关的话题了。

访问onedrive

访问onedrive

一个好端端的网站,被莫名其妙地干掉了。经网友介绍,还好只是DNS污染。网友推荐使用DNSCrypt解决这个污染问题。不过有个更简单点的方法,直接将onedrive的IP地址写入hosts即可。

修改C:\Windows\System32\drivers\etc\hosts文件,增加以下两行记录:

134.170.108.26 onedrive.live.com
134.170.108.152 skyapi.onedrive.live.com

只有在天朝才会明目张胆出现这种不要脸的事吧?