chinadns 解决 Openwrt 中 DNS 泄露与 IPV6 不能代理的问题

在 openwrt 上使用 ssrp 插件中,当你选择使用代理非大陆 IP 模式时,它会将你非大陆的 IP 自动进行代理;但由于它本身不支持代理 IPV6,当你解析出了海外站点 IPV6 地址时,你是直连到海外,速度非常慢,而且有可能会被通过 sni 屏蔽干扰等,体验很差。

经过了一番折腾,本文总结一下基于 ssrp 的基础上稍微进行改进解决这个问题。

首先梳理下 ssrp 的工作流程,它会预先下载 大陆 IP 段的数据集合以及 gfw_list 域名集合 到本地,用于域名解析时将结果 IP 加入到路由表中,以及用于在路由时判断这个 IP 是否绕过代理,如果是中国大陆的 IP,则会绕过代理。另外还有黑名单(强制走代理),白名单(强制不走代理)、局域网设备代理与否的配置,这不在主要内容中。

Dnsmasq

下载的 gfw_list 它会通过插件程序将域名喂给 dnsmasq 的配置,简单介绍下 dnsmasq,这是一个 openwrt 内置的智能 dns 基础程序,它具备基础的 DNS 解析、缓存、反 DNS 劫持等功能。由于它的配置比较简单,可以轻松实现根据不同域名实现使用不同 dns 服务器进行解析。

那么 ssrp 就将 gfw_list 的所有域名喂给了 dnsmasq,告诉它这些域名你将通过 127.0.0.1:5335 这个 dns 服务器进行解析,并将解析后的 IP 添加到 ipset 中。

它的配置格式如下:

server=/google.com.np/127.0.0.1#5335
ipset=/google.com.np/gfwlist
server=/apple.be/127.0.0.1#5335
ipset=/apple.be/gfwlist

不仅如此,ssrp 还为 dnsmasq 配置了更多的相关配置,存储在 /tmp/dnsmasq.d/dnsmasq-ssrplus.d/

root@OpenWrt:~# ls /tmp/dnsmasq.d/dnsmasq-ssrplus.d/
blacklist_forward.conf  denylist.conf           gfw_base.conf           gfw_list.conf           whitelist_forward.conf

像一些白名单的配置如下,他将直接默认通过本机 53 端口的 dns 进行解析:

server=/bilibili.com/127.0.0.1
ipset=/bilibili.com/whitelist

简单解释:127.0.0.1:53 默认由 dnsmasq 接管,它默认的上游是 WAN 口中运营商通告的 DNS 地址,但如果你开启了 Turbo ACC 网络加速设置 DNS 缓存加速功能,则这个优先级将更高,程序中自动覆盖 Dnsmasq 的值,即上游在 Turbo ACC 网络加速设置 中填写。

当配置完成后,dnsmasq 的下一步将根据这些配置进行分流解析。

  • 如果域名在 gfw_list 中的域名将会通过代理进行解析,能够得到正确无污染的结果;

  • 但如果域名不在其中且是海外的服务器时,如果它能够得到正确的 IPV4,也能够在后续的代理之中被代理成功。

但如果解析结果中混入了一个 IPV6,哪怕它包含 IPV4,现代的很多操作系统默认都会将 IPV6 的优先级放大,那么当你的系统采用 IPV6 访问时,则不会被 SSRP 代理出去,就会出现上述主题我们所讲的访问直连等干扰的情况。

重新修正下,这里无论你的域名在不在 gfw_list,由谁解析,只要结果包含 IPV6,那么就会复现此问题。

我们重新理下思路:

  • 只要当解析结果是国内的,无论是 IPV4 还是 IPV6 都让他直连,能够得到比较好的体验。

  • 只要当解析结果是国外的,一律不返回 IPV6,那么他都会通过 ssrp 代理上。

遵循上述,我们需要将 dns 程序的稍作改变。

废弃方案

本身我想不引入第三方程序,直接在 dnsmasq 上动手脚,下载一份国内的常用域名,并通过配置将将常用的域名使用 114.114.114.114 进行解析。然后直接将 dnsmasq 的上游 dns 改为 127.0.0.1:5335 (ssrp dns),只要你的代理服务器不支持 IPV6 或者你在代理程序中配置不解析 DNSv6 就解决问题了。

常用的国内域名:https://github.com/felixonmars/dnsmasq-china-list/blob/master/accelerated-domains.china.conf

wget https://github.com/felixonmars/dnsmasq-china-list/raw/master/accelerated-domains.china.conf -O /etc/dnsmasq.d/accelerated-domains.china.conf

并在 /etc/dnsmasq.conf 尾部追加配置

conf-dir=/etc/dnsmasq.d

那么重启就能够应用成功

/etc/init.d/dnsmasq restart

可实际遇到一些问题,dnsmasq 根本没启动成功,但也没有报具体的错误,就是无法提供服务。排查了许久没有解决,最终放弃;

猜测的原因可能是因为这个配置文件太大,它还在遍历启动,又考虑到性能问题,遂放弃。

Chinadns

上述解决不了后,通过 chinadns 代替 ssrp 默认提供的几个 dns 程序就好。

在 SSRP 的 DNS 解析方式中更改为: 使用 本机端口为 5335 的 DNS 服务

在 dnsmasq 中配置上游为 127.0.0.1#5335

安装 chinadns-ng

这是 chinadns 的重构版本,单文件安装简单,使用 epoll 性能更强, 能够自动根据域名列表进行分流.

创建配置目录

mkdir /etc/chinadns

下载 chnlist.txt 和 gfwlist.txt,我第一配置时就遗漏了这个配置,导致国内好多域名被代理到海外,速度很慢,而且有些打不开。

wget -O /etc/chinadns/gfwlist.txt https://github.com/zfl9/chinadns-ng/raw/master/res/gfwlist.txt
wget -O /etc/chinadns/chnlist.txt https://github.com/zfl9/chinadns-ng/raw/master/res/chnlist.txt

下载程序本体, https://github.com/zfl9/chinadns-ng/releases , 他这里程序版本特别多,自行根据说明挑选

wget -O /usr/bin/chinadns https://github.com/zfl9/chinadns-ng/releases/download/2024.07.21/chinadns-ng+wolfssl@x86_64-linux-musl@x86_64@fast+lto
chmod +x /usr/bin/chinadns

编写配置文件 /etc/chinadns/config.conf

# 监听地址和端口
bind-addr 0.0.0.0
bind-port 5335

# 国内上游、可信上游
china-dns 119.29.29.29
trust-dns tcp://1.0.0.1

# 域名列表,用于分流
chnlist-file /etc/chinadns/chnlist.txt
gfwlist-file /etc/chinadns/gfwlist.txt
# chnlist-first

# 收集 tag:chn、tag:gfw 域名的 IP
add-tagchn-ip chnip,chnip6
add-taggfw-ip gfwip,gfwip6

# 用于测试 tag:none 域名的 IP (国内上游)
ipset-name4 chnroute
ipset-name6 chnroute6

# dns 缓存
cache 4096
cache-stale 86400
cache-refresh 20

# verdict 缓存 (用于 tag:none 域名)
verdict-cache 4096

# 详细日志
# verbose

# 非中国域名 IP,屏蔽 IPV6
no-ipv6 ip:non_china

最重要的是结尾的 no-ipv6 ip:non_china : 它能够将处理非大陆域名 IP 时,屏蔽解析结果中的 IPV6

配置中也有注释, 这里简单说明下 chinadns 在这里处理的逻辑和作用

  1. 首先 chinadns 预存了中国域名和 gfw 域名到内存中.

  2. 为国内域名指定了 119.29.29.29 进行解析, 为其他域名(非国内就是其他)制定了可信的 dns 服务器为: tcp://1.0.0.1 (通过 tcp 解析,防止 udp 污染)

  3. 当你访问 163.com 时, 由于他在 chnlist 中,它会通过 119.29.29.29 进行解析并最终 direct 直连访问,速度很优.

  4. 当你访问 google.com 时, 由于他在 gfwlist 中,它会通过 1.0.0.1 进行解析, 又因 no-ipv6:non_china, 得到海外的 IPV4,最终通过 ssrp 程序成功代理.

  5. 当你访问 eller.top 时,由于它不在 gfwlist 中,它会通过 1.0.0.1 和 119.29.29.29 同时解析,如果解析结果中包含中国 IP, 则采纳中国的,并最终 direct 直连.(假设海外域名在中国有 CDN)

  6. 当你访问 eller.top 时,由于它不在 gfwlist 中,它会通过 1.0.0.1 和 119.29.29.29 同时解析,如果解析结果中不包含中国 IP, 则采纳海外的,并最终 proxy 成功代理(假设海外域名在中国有污染)

创建服务脚本 /etc/init.d/chinadns

#!/bin/sh /etc/rc.common
START=01
STOP=90
USE_PROCD=1
PROG=/usr/bin/chinadns
start_service(){
         procd_open_instance [chinadns]
         procd_set_param command /usr/bin/chinadns # service executable that has to run in **foreground**.
         procd_append_param command --config /etc/chinadns/config.conf # append command parameters

         procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}

         procd_set_param limits core="unlimited"  # If you need to set ulimit for your process
         procd_set_param file /etc/chinadns/config.conf # /etc/init.d/your_service reload will restart the daemon if these files have changed
         procd_set_param stdout 1 # forward stdout of the command to logd
         procd_set_param stderr 1 # same for stderr
         procd_set_param user nobody # run service as user nobody
         procd_set_param pidfile /var/run/chinadns.pid # write a pid file on instance start and remove it on stop
         procd_set_param term_timeout 60 # wait before sending SIGKILL
         procd_close_instance
}

执行启动和设置自启

/etc/init.d/chinadns start
/etc/init.d/chinadns enable

重启 dnsmasq 就好

/etc/init.d/dnsmasq restart

Comments