从 DNS 异常排查到使用 mosdns v5 对国外网站进行分流解析
背景:
最近3个月左右,只要打开任意国外网站,那么间接性会出现 DNS 解析异常的问题,表现为网页无法打开,无法解析出域名。这时使用ping 则表现为 请求找不到主机 xxx。请检查该名称,然后重试。,但只要使用nslookup 后则可以正常解析域名,且浏览器也可以正常打开。
但因为这种情况时有时无,所以也就一直没有去深究这个问题,直到最近实在是受不了了,决定彻底解决这个问题。

如果想直接查看如何搭建,点击跳转。
问题排查:
1. 怀疑 DNS 服务器问题
开始的时候,我以为是DNS服务器问题(我用的阿里的 223.5.5.5),因为看到阿里说过会对公共DNS进行限流,然后期间不间断的更换了多个DNS服务器,如 114、119 等等,甚至更换过 8.8.8.8,想过会被“提前”返回数据,但只要能正常解析就行,但无果。
2. 怀疑IPv6 问题
毕竟现在IPv6路由还是不怎么样,我将IPv6在路由器关闭后,无果。
也尝试在控制面板关闭v6协议。
控制面板
→ 网络和 Internet
→ 网络连接
→ 当前网卡
→ 属性
→ 取消勾选「Internet 协议版本 6 (IPv6)」
3. 怀疑 Windows 的 DNS Client(dnscache)服务异常
Windows 的 DNS 解析路径大概是:
应用(Firefox / 其他软件)
↓
Windows DNS Client 服务(dnscache)
↓
系统缓存 / UDP 查询
↓
DNS 服务器
也就是 DNS Client 服务还在运行,但内部状态卡死 / 超时,也会照常无法解析,但同样的,国内网站没问题。
这里GPT的回答是:
nslookup 会强制唤醒 / 刷新 Windows 的 DNS Client(dnscache)服务状态 nslookup 不完全依赖 dnscache,会直接发起一次 DNS 查询,这个行为会 刷新 DNS socket、更新系统 DNS 缓存,让 dnscache 从“半死不活”状态恢复。
这里是最难排除的,因为涉及到 本机网卡、代理、TUN 等。直到现在,我都不能保证这部分是否有问题。
4.怀疑 本地的 DNS 缓存出现了问题
尝试清除 DNS 缓存,命令为 ipconfig /flushdns,无果,坚持不了多久。
5. 怀疑运营商
其实到这一步,我是没想到会是运营商,之前听说过移动的墙中墙,但也只是对违法网站做劫持、屏蔽等,但没想到会无脑对dns协议,国外网站dns解析做随机的屏蔽(此屏蔽也不是一直有,等个十几秒或者一直按浏览器F5刷新,有几率可以解析出来)。
抓包
本机环境:
DNS: 119.29.29.29
刚好异常的网站: www.ip2location.io
这时刚好www.ip2location.io无法解析了,通过对本机DNS抓包,发现 114 返回的包则是一堆的d.root-servers.net d.root-servers.net,这是DNS的根服务器,但为什么返回是这样,暂时不清楚是移动还是DNS服务商配置错了,还是别的原因,个人能力有限,无法证实。
- Answer 区是空的(没有 A 记录)
- Authority 区给了一堆根服务器 NS
- 看上去像“给了结果”,但等价于:没解析出来

查看响应:
Flags=0x8180
递归可用但返回了一个“空答案 + root NS 列表”的 NOERROR。这属于 “NODATA/空应答(NOERROR + Answer=0)” 形态,只是它给的 Authority 是 root-servers。
把 0x8180 拆开(Wireshark 里其实也能展开看到):
- 0x8000:QR=1(这是 response)
- 0x0100:RD=1(请求了递归)
- 0x0080:RA=1(服务器“声称”递归可用)
- 0x0000:RCODE=0(No error)
所以:“能递归,没错误”,但又不给 A 记录,而是给了一堆 root NS。
这意味着:递归链路在某一环节失败了,但 resolver 没按常规用 SERVFAIL 表达,而是返回了一个等价于“没拿到数据”的包(NOERROR/0 answers)。
注意: 此时我无法使用nslookup对别的dns进行验证,因为一旦执行了nslookup,那么就会“解析成功”,此时因该用另一台电脑进行解析排除,这也是不严谨的地方。
为什么跑 nslookup 一下就可以解析?
查看图里的标注2,我在此时执行了 nslookup 命令。
1、系统先做了一个 PTR 29.29.29.119.in-addr.arpa(反查 DNS 服务器名) 2、紧接着:同域名 A 就返回了正确 IP
这非常像nslookup 触发了额外的 DNS 交互(例如先探测、再查询),同时“恰好”命中了一个正常工作/允许递归的解析路径或节点,于是后续这个域名就被缓存,浏览器就可以解析了。
但这里的“缓存”可以发生在:
- 119.29.29.29 对应的某个 Anycast 节点
- 或中间网络/路由对某类 DNS 包的处理路径
我这里先怀疑 第二种 可能,也就是DNS被劫持了。看看有没有效果。
搭建 mosdns
其实解决劫持最简单的方法,就是设置 DOH/DoH/DoT/DoQ,但我不想给局域网每一台设备单独设置,故单独在局域网一台小主机单独搭建了mosdns。
ps: 搭建之前我试过 adguardhome、SmartDNS ,可能是我姿势不对,没找到如何分流的地方。但adguardhome可视化确实很爽~
我这里使用docker搭建。
1、建立对应目录
mkdir -p ~/mosdns/rules
2、在 ~/mosdns目录建立规则文件 config.yaml
如果有条件,以下规则国外的dns设置为海外dns更好,我这里只是将海外网站让阿里doh进行解析。
log:
level: info
plugins:
# ========= 规则数据 =========
- tag: geosite_cn
type: domain_set
args:
files:
- "/etc/mosdns/rules/geosite_cn.txt" # 国内域名列表/规则(domain/full/regexp...)
- tag: geoip_cn
type: ip_set
args:
files:
- "/etc/mosdns/rules/geoip_cn.txt" # 国内 IP/CIDR 列表
# ========= 上游 =========
- tag: local_dns
type: forward
args:
upstreams:
# 示例:AliDNS / DNSPod DoH(按需替换)
- addr: "223.5.5.5:53"
- addr: "https://223.5.5.5/dns-query"
- addr: "https://1.12.12.12/dns-query"
- tag: remote_dns
type: forward
args:
upstreams:
# 示例:Cloudflare / Google DoH(按需替换)
- addr: "tls://223.6.6.6:853"
# - addr: "https://8.8.8.8/dns-query"
# ========= 主处理流程 =========
- tag: main
type: sequence
args:
# 0) 可选:缓存(按需开关/调大)
- exec: cache 4096
# 1) 国内域名 → 国内上游
- matches:
- qname $geosite_cn
exec: $local_dns
# 2) 其他域名 → 先走海外上游
- exec: $remote_dns
# 3) 按“应答IP”二次分流:
# 如果海外上游返回的 A/AAAA 在 CN IP 段内,则丢掉应答并改用国内上游再查一次
# - matches:
# - resp_ip $geoip_cn
# exec: drop_resp
# 4) drop_resp 后,这里再走国内上游补一次
# - exec: $local_dns
# 5) 如果仍没有应答,就结束(也可以 accept/return 等)
- matches:
- has_resp
exec: accept
# ========= 监听 =========
- type: udp_server
args:
entry: main
listen: "0.0.0.0:53"
- type: tcp_server
args:
entry: main
listen: "0.0.0.0:53"
3. 创建 geosite、geoip 规则。
以上规则文件中
/etc/mosdns/rules/geosite_cn.txt
/etc/mosdns/rules/geoip_cn.txt,是需要自己下载的,mosdns不带但兼容geosite格式。
注意: 由于 mosdns 的v5版本不再支持 geosite、geoip 原生查询了,故使用v2dat 解析 v2ray 的geosite、geoip数据包。 geosite、geoip 可在此处下载.
v2dat 解析geoip
注意: 这里我提供的规则没有用到geoip(已被注释),此处只是做一个记录。
解析 geosite
.\v2dat.exe unpack geosite .\geosite.dat -f cn
# 2026-01-12T20:19:43.600+0800 INFO unpacking entry {"tag": "cn", "length": 118283, "file": "geosite_cn.txt"}
解析 geoip
.\v2dat.exe unpack geoip .\geoip.dat -f cn
# 2026-01-12T20:23:33.335+0800 INFO unpacking entry {"tag": "cn", "length": 19219, "file": "geoip_cn.txt"}
然后将v2dat运行目录下的 geosite_cn.txt、geoip_cn.txt 放到 ~/mosdns/rules目录(也就是你linux登录用户目录下的mosdns目录)。
运行docker
docker run -d --name mosdns \
-p 53:53/udp \
-p 53:53/tcp \
-v ~/mosdns:/etc/mosdns \
-v ~/mosdns/rules:/etc/mosdns/rules \
docker.1ms.run/irinesistiana/mosdns:latest
1、网络使用host模式性能会更好。
2、如果不是在路由器搭建的mosdns,请记得在路由器dhcp下发dns配置那一块,设置dns为搭建mosdns的ip。
其他想法
有想过将 mosdns 直接搭建在 cn2gia 回程的小鸡上(国内网站则解析的时候附带 ecs_ip),可以解决污染等问题,但毕竟算提供了 DNS 服务,说不定什么时候就被墙,就算啦。
其次是就这么公开放在互联网,感觉风险也挺大,谁都可以用你的dns,以下是我想过的“个人”用一些方法,但都不怎么好,有大佬有更好的方案,欢迎交流~
- 动态IP,不好做白名单
- mTLS, 或许也可以(但每台设备装证书也麻烦),或者在局域网一台电脑安装证书,然后再搭建一个dns服务端?然后在路由器dhcp下发dns服务器为它?
- VPN / WireGuard + 内网 DNS,这个不好的地方也是设备需要随时挂着vpn。