2026/9/23
透明分流下的 DNS
应用通过 Specola 的本地 HTTP/SOCKS5 端口访问网络时,会直接交出目标域名:
CONNECT api.github.com:443。域名规则很好实现。
透明接管不一样。程序调用 connect() 时早已完成解析,手里只剩一个 IP 地址。CDN 后面多个毫不
相关的域名可能共用同一个 IP,同一个域名也可能解析出很多 IP。如果不做额外处理,
DOMAIN-SUFFIX,github.com,work 根本无从匹配。
Specola 如何把域名和连接对应起来
使用 Linux eBPF 后端时,Specola 的 DNS 模块始终启用:
- 在内核中拦截 DNS 查询。 在任何放行判断之前,cgroup hook 就把发往 53 端口的 UDP 和 TCP 查询交给 Core 的 DNS 模块;
- Core 应答并记录。 每次应答时,Core 记录哪个域名得到了哪个地址,以及这个域名命中的规则;
- 内核获知候选地址。 属于带规则域名的地址会写入 BPF 候选 map,之后对这些地址的
connect()会被重定向给 Core; - Core 还原域名。 被重定向的连接到达后,Core 通过地址查到域名,再应用域名规则。
没有规则的域名正常解析,其连接按 FINAL 处理。使用 FINAL,DIRECT 时,它们始终走内核快速
路径。
Fake-IP
透明接管推荐使用 Fake-IP:
[dns.tun]
mode = "fake-ip"
fake-ip-range = "198.18.0.1/16"
fake-ip-filter-mode = "blacklist"
fake-ip-filter = ["+.lan", "+.local"]
对于规则要求走代理的域名,Core 不返回真实地址,而是从 198.18.0.0/15 地址池中分配一个。
这个地址相当于一个令牌:每个域名唯一,CDN 共享 IP 不再造成信息丢失;对它的连接总是进入
Core,由 Core 映射回域名。
两个额外的好处:
- 代理收到的是域名而不是本地解析出的 IP,域名在代理一侧解析,得到的是适合代理所在网络的地址;
- 走代理的域名不会在本地解析,本地解析器不会看到这些查询。
走 DIRECT 的域名拿到真实地址,直连流量的行为与不使用 Specola 时完全一致。命中
fake-ip-filter 的域名(如 *.lan 这类本地名称)总是返回真实地址。走 REJECT 的域名返回
REFUSED。
另一种模式 mode = "redir-host" 对所有域名返回真实地址,只适合坚持要求真实 IP 的软件,并且
会遇到上面说的 CDN 歧义问题。
需要避开的坑
自带加密 DNS 的应用。 开启“安全 DNS”的浏览器和部分命令行工具会自己通过 HTTPS 解析域名,
Specola 看不到这些查询,它们的连接也就不带域名。可以关闭应用内置的 DoH,或者用
PROCESS-NAME、IP-CIDR 规则匹配它。
已建立的连接。 连接保持建立时的决策。修改规则后,重启长连接客户端或等待它重新连接。
IPv6。 eBPF 后端只接管 IPv4。如果域名有 AAAA 记录且应用优先使用 IPv6,这条连接会绕过
Specola。保持 [dns].ipv6 = false(默认值),让被接管的应用拿到 IPv4 应答。
嗅探只是兜底,不能替代 DNS。 Specola 也能读取被重定向连接的 HTTP Host 头或 TLS SNI。这在
连接到达 Core 之后有用,但在 FINAL,DIRECT 下,没有命中的连接根本不会被重定向,也就无从嗅探。
完整的 DNS 选项见 DNS 手册。