← → 翻页 · B 静态 · ESC 索引
自建 DNS 分流记 · Field Note 001
26.06 · 01 / 20
DNS · COREDNS · GO PLUGIN · LOON · TAILSCALE

自建
DNS
分流记

从 Rosetta 过热到自建 Go 插件、打通 Loon、解开 GeoDNS 谜题,再到 Homebrew Tap 分发的完整记录。
bilxio · 2026.06
→ arrow keys / swipe
前传 · PRELUDE
02 / 20
HTOP · ROSETTA · ARM64

干活吃饭的家伙,
没有人不爱惜。

htop 里 CoreDNS 常年高居榜首——那是一个很久以前下载的 amd64 二进制,Rosetta 在幕后翻译了不知多少个月,CPU 不知疲倦地降频发热。

重新编译 arm64 只是个开始 →
问题起点 · THE BUG
03 / 20
同一个域名 · 两个浏览器 · 两条完全不同的路径
 CHROME + DOH
ERR_NAME
NOT RESOLVED
Chrome 自带 DoH,直连本机 CoreDNS :5353,拿到真实 AAAA 记录,本机无 IPv6 出口,直连失败。
  • DoH → CoreDNS → Cloudflare
  • 真实 AAAA 记录 (IPv6)
  • 直连 IPv6 → 无出口 → 失败
  • 绕过了 Loon FakeIP 劫持
 SAFARI + 系统 DNS
页面正常
加载
Safari 走系统 DNS,被 Loon FakeIP 劫持,同时合成假 A(198.x.x.x)和假 AAAA(fd27:712::/32),流量进 TUN 代理隧道出去。
  • 系统 DNS → Loon 劫持
  • FakeIP: 同时合成假 A + 假 AAAA
  • 双协议流量 → TUN → 代理隧道
  • 出口 IP 正确 → 成功
解决路径 · SOLUTION PATH
04 / 20

五步从发现到完整方案

STEP 01
arm64 重编译
htop 发现 Rosetta,重新交叉编译 CoreDNS native binary
STEP 02
AAAA 重写
CoreDNS rewrite rule 屏蔽 AAAA,强迫 Chrome DoH 降级 IPv4
STEP 03
splitdns 插件
Go 插件写 Hash Set,国内域名走国内 DNS,国外走 DoT
STEP 04
Loon 接入
CoreDNS 作为 Loon 内部 DNS,打通 FakeIP 完整链路
STEP 05
GeoDNS 揭秘
Playwright 审计 aliyun.com,发现 DNS 隧道→新加坡→国际站
30 MIN · FIELD NOTE 001 · BILXIO · 2026
FakeIP 机制 · FAKEIP
05 / 20
LOON · FAKEIP · TUN · IPV4-ONLY

FakeIP
的秘密

双 FakeIP 合成机制

Loon 劫持系统 DNS 后,无论原始域名是否有 AAAA,都同时合成假 A(198.0.8.x)和假 AAAA(fd27:712::/32),IPv4/IPv6 双栈流量全部导入 TUN。

DoH 绕过了劫持

Chrome 的 DoH 不走系统 DNS,直接访问 CoreDNS :5353,拿到真实 AAAA,FakeIP 机制彻底失效。

插件架构 · PLUGIN ARCH
06 / 20

splitdns · 三层结构

LAYER 01 · 数据层
Hash Set
内置 CN 域名列表,O(1) 查找。启动时加载到内存,无磁盘 I/O 开销,零正则匹配,纯哈希命中。
~300k domains · O(1) lookup · in-memory
LAYER 02 · 分流引擎
DNS 路由决策
命中 CN 域名 → match upstream(国内 DNS);未命中 → default upstream(加密 DoT)。逻辑极简,无配置文件热重载风险。
match · default · corefile plugin chain
LAYER 03 · 上游层
DoT 加密上游
海外查询走 Cloudflare DoT (1.1.1.1:853),TLS 加密传输,防止 ISP 监听。国内 DNS 走 AliDNS 直连,低延迟。
cloudflare dot · alidns · tls 1.3

四个关键能力

07 / 20 · CAPABILITIES
01 · 性能
arm64

原生编译,告别 Rosetta,CPU 使用率从持续高负载降到接近零。

02 · 兼容
AAAA
重写

CoreDNS rewrite,屏蔽 IPv6 返回,强制 FakeIP 通路。

03 · 路由
DNS
分流

Go 插件,Hash Set O(1),国内/国外域名自动分流到不同上游。

04 · 隐私
DoT
加密

DNS-over-TLS 1.3,海外查询全程加密,防 ISP 监听。

查询闭环 · DNS LOOP
08 / 20

DNS 查询完整闭环

01 · 发起
Loon DNS 查询
所有 App → 系统 DNS → Loon 内部转发到 CoreDNS :53
02 · 接收
CoreDNS 分发
Corefile 插件链:rewrite → cache → splitdns → forward
03 · 判断
splitdns 分流
Hash Set 命中 → AliDNS;未命中 → Cloudflare DoT
04 · 返回
FakeIP 生效
A 记录返回 Loon,FakeIP 接管,流量走正确隧道
01 · LOON
02 · COREDNS
03 · SPLITDNS
04 · DOT
集成挑战 · INTEGRATION
09 / 20

打通 Loon 的三个坎

核心前提
Loon
集成

CoreDNS 必须成为 Loon 的唯一内部 DNS,让 FakeIP 劫持发生在 DNS 返回到 App 之前。

坎 01 · FakeIP 前提
所有 DNS 必须经 Loon
Chrome DoH 会绕过系统 DNS,直连 CoreDNS,导致 Loon 无法注入 FakeIP。解法:CoreDNS DoH 端口也返回重写后的 A 记录。
坎 02 · 启动顺序
鸡蛋问题
CoreDNS 启动需要网络;Loon 启动需要 CoreDNS。用 launchd WaitForNetworkConnectivity 解决冷启动死锁。
坎 03 · *.ts.net
Tailscale 域名冲突
*.ts.net 不应走 FakeIP 代理。splitdns 需要在 match 规则里排除 Tailscale 域名,让其走 MagicDNS 直连。
10 / 20 · MOMENT OF TRUTH
COREDNS · LOON · FAKEIP

接通了。

Chrome 和 Safari 打开同一个页面,都走 FakeIP 隧道,GeoDNS 落点正确,aliyun.com 不再跳国际站。

DNS 分流
国内 → AliDNS
国外 → Cloudflare DoT
FakeIP 通路
所有 App 流量
经 TUN 代理隧道
GeoDNS 正确
出口 IP → 正确区域
CDN 节点落点准确
全景架构 · ARCHITECTURE
11 / 20

完整网络拓扑

Loon 在上、CoreDNS 在中、三条上游在下——所有 DNS 路径在这一层汇聚、分叉。

LOON · 代理客户端
COREDNS · splitdns 插件
上游 DNS · 三条路径
客户端层

Safari、Chrome DoH、所有 App → 系统 DNS → Loon FakeIP 劫持 → CoreDNS

核心枢纽

CoreDNS 作为唯一 DNS 入口:rewrite AAAA → A,splitdns Hash Set 分流决策,cache prefetch 预热

上游三叉

国内域名 → AliDNS 直连;国外域名 → Cloudflare DoT 1.1.1.1:853;*.ts.net → Tailscale MagicDNS

陷阱清单 · PITFALLS
12 / 20

六个配置陷阱

01
Rosetta 陷阱
amd64 二进制在 arm64 Mac 上 Rosetta 翻译,CPU 高占用,需交叉编译原生 binary。
02
AAAA 绕过
Chrome DoH 直连 CoreDNS,拿到真实 AAAA,绕过 Loon FakeIP 双栈劫持,必须在 CoreDNS 侧用 rewrite 拦截 AAAA 查询。
03
启动鸡蛋
CoreDNS 需要网络,Loon 需要 CoreDNS,launchd WaitForNetworkConnectivity 打破死锁。
04
Cache 预取
prefetch 10 触发后台预热,避免冷启动时第一次查询等待上游 RTT。
05
*.ts.net 冲突
Tailscale 域名不应进 FakeIP 代理,splitdns 需在 match 规则里排除 ts.net。
06
bypass-tun 路由
100.100.100.100 被 bypass-tun 静态路由拦截,Tailscale MagicDNS 无法到达,需排除 CIDR。
谜题 · MYSTERY
13 / 20
GEODNS · ALIYUN · CDN · SINGAPORE

同一台机器,同一个出口 IP,两个截然不同的响应

修好 CoreDNS 之后,aliyun.com 突然不跳国际站了。这件事本来一笑而过,但「为什么」让我停下来——Loon 日志显示 DIRECT,凭什么同一出口 IP 给出两个截然不同的 CDN 响应?

GeoDNS 两条路 · GEODNS
14 / 20
DNS 解析器 IP → 决定 CDN 返回哪个节点
 DNS 走直连
国内
CDN 节点
Loon 内部 DNS 直连 AliDNS,查询源 IP 是本机直连 IP(国内),GeoDNS 返回国内 CNAME 链。
  • DNS 源 IP → 国内 ISP
  • CNAME → cn.aliyuncs.com
  • CDN 节点 → 国内
  • → 跳转国内站
 DNS 走代理隧道
新加坡
CDN 节点
Loon 内部 DNS 经 Tunnel → Cloudflare DoT,查询从新加坡代理节点出,GeoDNS 返回国际 CNAME。
  • DNS 源 IP → 新加坡代理
  • CNAME → intl.aliyuncs.com
  • CDN 节点 → 新加坡
  • → 跳转国际站
隐藏假设 · HIDDEN ASSUMPTIONS
15 / 20

三个系统各有隐藏假设,叠在一起就会互相打架

FakeIP 假设:所有 DNS 都经过 Loon

一旦有程序绕过系统 DNS(如 Chrome DoH),FakeIP 机制就会失效,流量逃逸到真实 IPv6 地址上。

01
GeoDNS 假设:查询 IP 代表客户端位置

DNS 查询从新加坡代理出口发出,GeoDNS 就认为"客户端在新加坡",CDN 节点选择据此改变。

02
代理客户端假设:DNS 和路由是同一回事

Loon 的路由决策基于 IP,DNS 解析和流量路由是两条平行的链路,必须同时控制才能行为一致。

03
审计发现 · AUDIT
16 / 20

Playwright 无头浏览器审计:六项发现

发现 01
双重测试脚本
两套 DNS 配置下各跑一次 aliyun.com 首页完整加载,对比请求链差异。
发现 02
完整请求链
抓取所有 XHR/Fetch 请求,记录请求头、响应头、状态码、耗时。
发现 03
重定向 diff
两次 3xx 重定向记录做 diff,找到第一个分叉点:Location header 不同。
发现 04 · 关键
Cookie 暴露 CDN 位置
一个 cdn-node cookie 值包含了 CDN 节点的地理位置编码:SG(新加坡)vs CN(国内)。
发现 05
dig 查询根因
一条 dig +trace aliyun.com 完整追踪:不同 DNS 上游返回不同 CNAME 链。
发现 06
CNAME 链解析
aliyun.com → intl.aliyuncs.com → sg-cdn-node,CNAME 链的第二跳决定了最终落点。
Tailscale 集成 · TAILSCALE
17 / 20
MAGICDNS · BYPASS-TUN · *.TS.NET
STEP 01
发现
冲突
100.100.100.100(MagicDNS)被 Loon bypass-tun 静态路由拦截,Tailscale 域名解析失败,设备间互访断开。
STEP 02
修复
路由
在 bypass-tun 规则中排除 Tailscale CIDR(100.64.0.0/10),让 MagicDNS 流量绕过 TUN 直连 Tailscale 网络。
STEP 03
排除
ts.net
splitdns 的 match 规则中添加 *.ts.net 例外,Tailscale 域名不走 FakeIP 代理,直接走 MagicDNS 返回真实 IP。
修改步骤
3
配置文件
1
停机时间
0 min
回滚风险
打包分发 · DISTRIBUTION
18 / 20

打包
分发

GoReleaser 一键编译 arm64 + amd64,自动发布 GitHub Release,Homebrew Tap 一行安装。

GORELEASER
v2
自动化 build、archive、publish 全流程
HOMEBREW TAP
tap
私有 Tap,brew install 一行搞定
安装命令
# 添加 Tap
brew tap bilxio/coredns
# 安装
brew install coredns-splitdns
# 作为服务启动
brew services start
coredns-splitdns
arm64 原生 · 自动更新 · launchd 守护进程
方案全貌 · FULL SOLUTION
19 / 20

12 个组件,一套完整的本地 DNS 分流方案

01
arm64
重编译
02
AAAA
重写规则
03
splitdns
Go 插件
04
Hash Set
O(1) 查找
05
DoT
加密上游
06
Loon
内部 DNS
07
FakeIP
通路打通
08
Cache
prefetch
09
Tailscale
MagicDNS
10
GeoDNS
Playwright
11
GoReleaser
自动发布
12
Homebrew
Tap 分发
12组件
从一个 amd64 二进制到完整的本地 DNS 分流基础设施,每一步都有具体原因。
20 / 20
CLOSING
CONCLUSION

专业的
工具做
专业的事。

DNS 分流不是玄学,是工程——每一个症状背后都有根因,每一个根因都可以用正确的工具修复。
bilxio · 2026.06
Field Note 001
TAKEAWAYS
03 RULES
01

每层系统都有隐藏假设

FakeIP、GeoDNS、代理路由——各自成立,叠在一起就会互相打架。找到假设,才能找到根因。

02

不接受玄学,只相信工具

dig、Playwright、Wireshark——症状散落各处,但每一个症状都有工具可以定位根因,没有例外。

03

干活吃饭的家伙,值得认真对待

从 Rosetta 陷阱到 Homebrew Tap 分发——每一步都是对工具链的尊重,也是对自己工作环境的投入。

→ 完 · END OF FIELD NOTE 001