抛弃 UDP, 用 TCP 查询 DNS

Posted by River Yang on 2017-05-17

源起 —— UDP 查询不可靠

我的 ChinaDNS 一直是使用 UDP 方式通过 Shadowsocks 的 ss-tunnel 来作代理查询的,SS 开启 udp relay 即支持 UDP 协议。见我在前两篇文章的介绍:

长期以来,国外 DNS 查询的问题一直困扰着我,很多域名经常需要很长时间解析,通过 ChinaDNS 查询就是这样 —— 国内的域名正常查询,国外的走代理,我一直以为是我的 SS 代理不稳定造成的,所以我在几台服务器上都建了 SS,不停地切换UDP代理,TCP 却从来没有出过问题。

GFW —— 罪恶的源头

直到我在 ChinaDNSWIKI 上看到这句话:

目前部分 ISP 下使用 ss-tunnel 不稳定, 导致 ChinaDNS 无法正常工作, 根本原因是这类 ISP 的 UDP 不稳定.

才猛然醒悟,这不是代理的问题!这是 ISP 的线路造成的,也可能是 GFW 的故意为之!GFW 已经不仅仅是一个国际出口网关的防火墙,而是一个分布式、无处不在的庞然大物,它存在于每一个机房里,每一个路由器上,甚至每一个人心里……

上面的链接全是分析我们国家的 ISP 对 UDP 的封锁有关的文章,看完才发现我真是图样图深破。UDP 查询 DNS 太不可靠了,那怎么办呢?
解决办法有很多,ShadowsocksR 提供了 UDP over TCP 的方案,pdnsd 也提供了 TCP 查询的路子,网上可以搜索到很多文章可以参考,而我用的是 ChinaDNS for OpenWrt 建议的 DNS-Forwarder

TCP —— 使用你情非得已

如果你在使用 ChinaDNS 查询 DNS 也遇到了不稳定的情况,同样建议使用 DNS-Forwarder 将 DNS 查询转换为 TCP 协议。
DNS-Forwarder 是一个很简单的开源软件,就是将 DNS 查询转化成 TCP 协议,这样可以通过透明代理走 SS 的TCP通道。
为什么不建议用 pdnsd 呢?

因为 pdnsd 的缓存无法完全禁用, 会导致 ChinaDNS 的线路优化功能失效. 而 DNS-Forwarder 是不带缓存的, 可以避免此问题.

ChinaDNS 建议上游服务不要使用任何缓存功能,否则它的线路优化机制可能失效。

下面就是使用 DNS-Forwarder 的简单教程。注意,本教程教你安装的是 DNS-Forwarder for openwrt 版本,编译机器是 Arch Linux, 运行环境是 Netgear 的某款智能路由器。

安装 DNS-Forwarder

DNS-Forwarder 是一款开源软件,在 openwrt 的官方仓库里没有,需要自己编译。openwrt 的软件编译一般是在 Linux 或 Windows 主机上交叉编译成可执行软件包。我的编译环境是 Arch Linux。

  1. 首先下载安装 openwrt 的 SDK,openwrt 的官方教程: https://wiki.openwrt.org/doc/howto/obtain.firmware.sdk
    假设下载的是 OpenWrt-SDK-15.05.1-ar71xx-nand
    1
    2
    3
    4
    cd OpenWrt-SDK-15.05.1-ar71xx-nand
    ./script/feeds update -a
    ./script/feeds install -a
    make menuconfig # 仅用于测试 SDK 能够正常运行

注意很多 Linux 平台可能会见到这个错误:glob failed: No files found "package/utils/busybox/config/libbb/Config.in" make: *** [menuconfig] Error 1。解决方法:ln -s ../feeds/base/package/utils package/utils ,参考:https://dev.openwrt.org/ticket/18552

  1. 编译 DNS-Forwarder
    • 获取 Makefile
      git clone https://github.com/aa65535/openwrt-chinadns.git package/chinadns
    • 选择要编译的包 Network -> ChinaDNS
      make menuconfig
    • 开始编译
      make package/chinadns/compile V=99
      编译 DNS-Forwarder 可能依赖 ccache, 在你的系统上安装即可。不出意外,编译出的可执行软件躺在 bin/ar71xx/packages/base/dns-forwarder_1.2.0-1_ar71xx.ipk 下, 通过 scp 传到 openwrt 里即可。
  2. 设置 DNS-Forwarder
    DNS-Forwarder 通过文件设置,配置好记得将 ChinaDNS 的上游服务器配置为 DNS-Forwarder 的地址。

    1
    2
    3
    4
    5
    6
    7
    # /etc/config/dns-forwarder
    config dns-forwarder
    option listen_addr '0.0.0.0'
    option listen_port '5300'
    option dns_servers '8.8.8.8'
    option enable '1'
  3. 启动

    1
    /etc/init.d/dns-forwarder start

总结

现在,我的 DNS 查询的流程就是: dnsmasq -> ChinaDNS -> DNS-Forwarder -> SS (TCP) -> 国外DNS服务器(e.g: 8.8.8.8)
本文作为 DNS 系列文章中的第三篇,终于我可以正常的用上 DNS 了。前两篇文章为:

其实我的方案并不一定是最佳方案,文章里还提到了 ShadowsocksR, pdnsd, DNSDecrypt 等方案,我还没有尝试,应该都是可行的,大家如果试过,欢迎分享出来。本文还有什么值得讨论的地方,大家也可以提出来。