目录

从 DNS 异常排查到使用 mosdns v5 对国外网站进行分流解析

背景:

最近3个月左右,只要打开任意国外网站,那么间接性会出现 DNS 解析异常的问题,表现为网页无法打开,无法解析出域名。这时使用ping 则表现为 请求找不到主机 xxx。请检查该名称,然后重试。,但只要使用nslookup 后则可以正常解析域名,且浏览器也可以正常打开。

但因为这种情况时有时无,所以也就一直没有去深究这个问题,直到最近实在是受不了了,决定彻底解决这个问题。

https://s3.aixfan.com/img/screenshot/2026/01/12/1768226989_86b.png


如果想直接查看如何搭建,点击跳转

问题排查:

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
  • 看上去像“给了结果”,但等价于:没解析出来

https://s3.aixfan.com/img/screenshot/2026/01/12/1768231014_88b.png

查看响应:

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 交互(例如先探测、再查询),同时“恰好”命中了一个正常工作/允许递归的解析路径或节点,于是后续这个域名就被缓存,浏览器就可以解析了。

但这里的“缓存”可以发生在:

  1. 119.29.29.29 对应的某个 Anycast 节点
  2. 或中间网络/路由对某类 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进行解析。

官方规则说明 已知的 DNS 提供商

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,以下是我想过的“个人”用一些方法,但都不怎么好,有大佬有更好的方案,欢迎交流~

  1. 动态IP,不好做白名单
  2. mTLS, 或许也可以(但每台设备装证书也麻烦),或者在局域网一台电脑安装证书,然后再搭建一个dns服务端?然后在路由器dhcp下发dns服务器为它?
  3. VPN / WireGuard + 内网 DNS,这个不好的地方也是设备需要随时挂着vpn。