回家后发现家里的新拉的光纤是移动的,但是移动居然对53端口的udp实行完全拦截,也就是dns拦截。想起之前在宿舍使用的cloudflare的json格式的DoH还有DoT,所以决定搞一搞。
DoH
把RFC8484关于实现的那部分看了,并没有JSON API格式的,一查,原来还只是草案.
DoH(Dns over HTTPS),cloudflare(1.1.1.1)和Google(8.8.8.8)的dns都支持JSON格式的,虽说还是草案,但是国内好几个私人的DoH供应商都有提供,所以讲一下
JSON API
JSON格式的请求,草案里写了必须要有Qname(请求的查询的域名),Qtype(请求查询的类型,A,AAAA,CNAME等),Qclass(这个不知道是啥,CF和Google的实现都没有这个字段,所以无视就行了)
请求:
1 | https://1.0.0.1/dns-query?ct=application/dns-json&name=baidu.com&type=A |
响应:
1 | { |
好像很简单
Wireformat
格式按照RFC1035
,刚开始不知道,然后就开始按着文档手撸实现,越撸越不对劲,发现这就是一个普通的DNS的UDP包格式……
按照RFC8484,DoH需要支持GET和POST两种格式
GET请求,需要把DNS包进行base64转一下,去除结尾的=
后发送,如
1 | https://1.0.0.1/dns-query?dns=AAMBAAABAAAAAAAABWJhaWR1A2NvbQAAAQAB |
POST请求,DNS包不需要任何操作,当成body直接POST就可以了
返回数据也是一个标准的UDP的DNS报文,直接返回就可以了
所以按照RFC8484,用python的话,一个简单的本地DoH转UDP格式的本地服务器就可以搭建起来了:
1 | # -*- encoding: utf-8 -*- |
DoT
最近把之前缺的DoT的RFC也看了.根据RFC7858关于数据的部分,也是按照RFC1035的数据包.看来真的是dns over xxxx了,就是将之前UDP的dns通过https或tls进行传输.那就没啥好说了.
发送数据前,需要发送udp数据包的长度,编码为两字节大端格式
(All messages (requests and responses) in the established TLS session
MUST use the two-octet length field described in Section 4.2.2 of
[RFC1035]).
刚开始看漏了这几句,卡了好久.具体看代码吧.
PS:cloudflare的dns,是把长度+响应数据一起发送,阿里的dns是先发长度,然后再发相应包.RFC里面也没说是那种,所以发现只收到两个字节的,记得把后面那段也收了
1 | # -*- encoding: utf-8 -*- |