ip桌子!由于众所周知的原因,向国外递归服务器发起的请求常常会得到损坏的数据包,故而dnscrypt,DoH等技术常被使用以应对这一现状。但cdn们却会带些脏东西(geodns是什么垃圾....),故而得到的解析结果往往会造成不理想的访问速度。虽然edns0 client subnet或许会改变这一状况,但该标准仍不普及,因此dns分流实为必要。传统的dns分流手段往往是依靠域名列表,虽然效果绝佳,但列表的维护与匹配时带来的overhead却是不可忽略。
既然要不依赖列表,实时监测便是我给出的答案,udp劫持、tcp阻断、连接超时,依靠这三大主要指征我构建了Project Calorina,并得到了较为不错的结果....然后....干!内存漏了.... 排查了许久,发现问题似乎出在第三方库内,who cares? 恰好在推特上看到了某位不愿透露姓名的大佬的推文:两个损坏的数据包之一的IP Identification为0,而另一则设置了Don't fragment,而Google public dns则不会有这两个特征,可以用iptables过滤..... 这一结论很有理论价值,但现实价值却不高,毕竟有DoH存在,此等极度依赖网路环境的方案不会被作为首选。但这两个特征,又让我想到了损坏的数据包的另一个特征:在answser section中只有一个RR。这一特征则可用于对国内的递归服务器的查询,避免上游投毒造成的影响(虽然会有误杀,但实为少数)
sudo iptables -I INPUT -s 8.8.8.8 -p udp --sport 53 -m u32 --u32 "2&0xFFFF=0x0" -j DROP
sudo iptables -I INPUT -s 8.8.8.8 -p udp --sport 53 -m u32 --u32 "4&0xFFFF=0x4000" -j DROP
sudo iptables -I INPUT -s 223.5.5.5 -p udp --sport 53 -m u32 --u32 "32&0xFFFF=0x0001" -j DROP
这便是我最终得出的解答。但经过短暂测试,便发现了这一规则的漏洞:CNAME。对于域名其他记录的查询会导致响应中CNAME字段的出现,使得RR数不为一,而与之相伴的损坏的A/AAAA记录也会进而污染本地缓存。对此的应对措施我则采用了unbound,这也依赖于unbound的一个安全特性:
CNAMEs are chased by unbound itself, asking the remote server for every name in the indirection chain, to protect the local cache from illegal indirect referenced items.
配置文件如下
server:
interface: 127.0.0.1
interface: ::1
port: 53
do-daemonize: no
prefetch: yes
forward-zone:
name: "."
forward-addr: 223.5.5.5
forward-addr: 8.8.8.8
三行iptables规则,三行forward zone配置,于我而言确是一个比近千行的code base更优美的方案。ONE TABLE TO RULE THEM ALL