前情提要
首先简述一下我的网络环境,所有设备通过 OpenWRT 接入网络,OpenWRT 上安装了 OpenClash 以实现透明代理和 DNS 解析服务,OpenClash 启用了 “本地 DNS 劫持”(划重点,后面要考)。
关于 LanCache
官方文档:Quickstart | LanCache.NET
LanCache 是一个被动缓存服务器,将需要缓存的域名解析到 LanCache 实例地址,LanCache 会检测本地是否存在缓存,不存在则将请求代理到真实地址并缓存响应结果,存在则直接从本地缓存。
例如若将 lancache.steamcontent.com 解析到 LanCache 地址,则 Steam 可支持缓存,第二次下载速度就能跑满局域网,且不需要从服务器重复下载。
起因
本来是,我在 OpenWRT 的 Dnsmasq 中设置好将 lancache.steamcontent.com 解析到 LanCache,也实验成功有效,Steam 成功命中缓存,第二次下载速度跑满局域网。
然后我查阅文档:Monolithic | LanCache.NET – Monolithic,他说支持 HTTP 范围请求,也就是说,只要请求使用的是 HTTP,他就能缓存。
而刚好我这两天在制作 Docker 镜像,测试的时候需要重复使用 apt-get 从 deb.debian.org 下载依赖包,我就想着把 deb.debian.org 也加入缓存。
于是乎参照 lancache.steamcontent.com 的设置方式,我在 OpenWRT 的 Dnsmasq 中设置将 deb.debian.org 也解析到 LanCache,然后 apt-get 直接报错 HTTP 508,然后就开始了排查。
排查过程
HTTP 508 代表请求发生了回环,由于我将 deb.debian.org 解析到了 LanCache,而 LanCache 没有命中缓存的情况下,需要将请求代理到真实地址,而真实地址需要解析 DNS,但 deb.debian.org 被解析到了 LanCache,于是乎请求又回到了 LanCache,这样就造成了回环。
于是我按照文档,添加了环境变量 UPSTREAM_DNS 为 8.8.8.8,意思是缓存未命中的时候,DNS 解析使用 8.8.8.8 而非当前默认 DNS 服务器也就是路由器,然而重启之后请求依旧 508。
那自然要用到 nslookup 来看 deb.debian.org 究竟解析到哪里了。
madray@nas:~$ nslookup
> server 8.8.8.8
Default server: 8.8.8.8
Address: 8.8.8.8#53
> deb.debian.org
Server: 8.8.8.8
Address: 8.8.8.8#53
Name: deb.debian.org
Address: 192.168.6.140
> server 114.114.114.114
Default server: 114.114.114.114
Address: 114.114.114.114#53
> deb.debian.org
Server: 114.114.114.114
Address: 114.114.114.114#53
Name: deb.debian.org
Address: 192.168.6.140
可以看到,就算我将 server 设置为外部地址,而非路由器地址,查询结果也始终返回我在 Dnsmasq 上设置的地址。
于是乎怀疑 OpenWRT 拦截了 DNS 请求,使用“openwrt 拦截dns请求”关键词搜索,找到这篇文章:openwrt 路由器的奇怪 DNS 劫持 (green-m.me),按照这篇文章的描述,OpenClash 如果启用了 “本地 DNS 劫持” 会劫持所有 DNS 请求转发到路由器,我原本以为这仅仅是让请求到路由器地址的 DNS 请求转发到 OpenClash,但实际上是劫持所有 DNS 请求,无论目标地址是谁。
关闭之后,deb.debian.org 的请求成功缓存。
结论
需要关闭 OpenClash 的 “本地 DNS 劫持”,但 OpenClash 就不会设置 Dnsmasq 转发 DNS 请求了,可以手动添加,也可以按照 openwrt 路由器的奇怪 DNS 劫持 (green-m.me) 的描述添加一个脚本,自动设置:
#!/bin/sh /etc/rc.common
# file: /etc/init.d/dnswatcher
START=10
STOP=15
watchdir=/var/etc/
LOGFILE=/tmp/openclash_mylogger.log
start() {
inotifywait -q -m "$watchdir" -e delete,create |
while read -r path action file; do
#echo "The file '$file' appeared in directory '$path' via '$action'" >> $LOGFILE
if printf "%s" "$file" |grep -q "openclash.include" ; then
sleep 5
enable=$(uci get openclash.config.enable)
# if enable ,modify dns
if [ "$enable" -eq 1 ]; then
#echo "enabled"
echo "$(date)-----openclash dns not set right, changing it!" >> $LOGFILE
uci del dhcp.@dnsmasq[-1].server >/dev/null 2>&1
uci add_list dhcp.@dnsmasq[0].server="127.0.0.1#7874"
uci delete dhcp.@dnsmasq[0].resolvfile
uci set dhcp.@dnsmasq[0].noresolv=1
uci set dhcp.@dnsmasq[0].cachesize=0
uci commit dhcp
/etc/init.d/dnsmasq restart
# else revert dns setting
else
#echo "disabled"
echo "$(date)-----Reverting dns!" >> $LOGFILE
uci del dhcp.@dnsmasq[-1].server >/dev/null 2>&1
uci add_list dhcp.@dnsmasq[0].server="223.5.5.5#53"
uci set dhcp.@dnsmasq[0].resolvfile=/tmp/resolv.conf.auto >/dev/null 2>&1
uci set dhcp.@dnsmasq[0].noresolv=0
uci set dhcp.@dnsmasq[0].cachesize=0
uci commit dhcp
/etc/init.d/dnsmasq restart
fi
fi
done &
return 0
}
stop() {
ps -ef | grep inotifywait | grep -v grep | awk {'print $1'} | xargs kill -9
return 0
}