Skip to content

fix(speedtest): 端点 DNS 规则弃用 legacy strategy,拆掉与 query_type 共存即起核 FATAL 的隐患 - #334

Merged
Sway-Chan merged 1 commit into
dododook:mainfrom
Sway-Chan:fix/speedtest-dns-rule-strategy
Jul 28, 2026
Merged

fix(speedtest): 端点 DNS 规则弃用 legacy strategy,拆掉与 query_type 共存即起核 FATAL 的隐患#334
Sway-Chan merged 1 commit into
dododook:mainfrom
Sway-Chan:fix/speedtest-dns-rule-strategy

Conversation

@Sway-Chan

Copy link
Copy Markdown
Collaborator

问题

测速临时 config(SpeedTestService.generateProxyTestConfig)里端点(WG/WARP 等 L3)目标域名解析用的 dns.rules[],把族别偏好写在 rule-action 的 strategy 上。sing-box 1.14.0 起这是 legacy 写法:

  1. sing-box run 输出 deprecation 警告,1.16.0 移除。sing-box check 静默放行(实测 exit=0)——现有的配置校验抓不到它。
  2. 更要命:与同一份 DNS 配置内任何带 query_type / ip_version 的规则(含引用带 query_type 的 rule-set)互斥,共存则在 initialize dns router 阶段被直接拒绝,checkrun 双双硬失败,代理起不来。

第 2 条此前只是恰好没踩上,不是设计:主配置(singbox-dns-builder.ts)大量用 query_type: ['A','AAAA'],但 strategy 只出现在顶层 dns.strategy(非 rule-action);测速配置有 rule-action strategy 却零 query_type任一侧加一个字段就炸。

改动

改用 query_type 规则项表达族别偏好,FlowZ 全仓不再有 rule-action strategy,上述组合结构性不可能出现——埋雷是拆除,不是缓解。

// 纯 v4 localAddress(旧 ipv4_only):抑制规则在前 + route catch-all 在后
{ "inbound": ["http-in-<id8>"], "query_type": ["AAAA"], "action": "predefined", "rcode": "NOERROR" },
{ "inbound": ["http-in-<id8>"], "action": "route", "server": "dns-exit-<id8>", "disable_cache": true }

// localAddress 含 v6(旧 prefer_ipv4):只剩 route 规则,不下发任何 strategy
{ "inbound": ["http-in-<id8>"], "action": "route", "server": "dns-exit-<id8>", "disable_cache": true }

等价性依据(核源码 + 实测双证):

  • prefer_ipv4 ≡ 什么都不写dns/client.gosortAddresses 只对 prefer_ipv6 走 v6-first 分支,AsIS 与 prefer_ipv4 落同一分支(v4 在前);测速 config 无顶层 dns.strategy → 内核并发 A/AAAA 后 v4 排前。
  • ipv4_only ≡ AAAA 就地返空predefined + rcode: NOERROR 让该 inbound 的 AAAA 在核内直接返空、不出网,结果集只剩 A。
  • 前提是 1.14.0 起 query_type / ip_version内部解析(WG/Tailscale endpoint 解自身目标地址、SOCKS4 等)上开始生效——1.14 之前这些字段只对客户端来的查询有效、内部解析静默忽略。正是这条变化让 rule-items 能替代 rule-action strategy。

§5 的行为一字未改:端点目标解析仍经 inbound 键控规则定向到穿本节点隧道的 223.5.5.5(AliDNS ECS 按出口地理返 IP),detour / disable_cache / 单入站 / 代理路径全部零改动。

实测(本机 loopback,零宿主网络;随包核 1.14.0-beta.2

装置:两台 loopback 假 DNS(A→198.51.100.<n>、AAAA→2001:db8::<n>,按 listen port 区分被查的是哪台、逐条记 qtype),http inbound → WireGuard endpoint(gVisor 用户态、peer 指 127.0.0.1 死端口,无 TUN/netns/iptables),curl -x 触发端点对 probe.test 的本地解析。观测三件:run 日志的 deprecation 行、假 DNS 收到的 qtype、核 lookup succeed for probe.test: <addrs>含顺序)。

# 形态 deprecation 出网 qtype 解析结果
v0 strategy: ipv4_only WARN A 198.51.100.2
v0b strategy: prefer_ipv4 WARN AAAA + A 198.51.100.2 2001:db8::2
v1 新:无 strategy AAAA + A 198.51.100.2 2001:db8::2与 v0b 逐项同序
v2 新:query_type:[AAAA] → predefined NOERROR + route 仅 A 198.51.100.2与 v0 一致
v3 旧 strategy + 任一 query_type 规则 check exit=1 且 run FATAL(埋雷坐实)
v4 新形态 + 额外 query_type 规则 仅 A 正常启动(新形态与 query_type 可共存)
v5(变异反证) 新形态但抑制规则排到 route 之后 A + AAAA 抑制静默失效 → 顺序不变量有牙
v6 双端点混合(节点1 纯 v4 / 节点2 含 v6) 节点1 仅 A、节点2 A+AAAA,各走自家 exit DNS 198.51.100.2 / 198.51.100.3 2001:db8::3per-inbound 隔离成立、无跨节点缓存污染

真实发射物端到端复核(不是手搓 config):把 generateProxyTestConfig 实际发射的双端点配置落盘,仅把 stub outbound 补成完整 WG endpoint、把 223.5.5.5 改指 loopback 假 DNS(确保零出网),其余逐字节原样过核:

形态 sing-box check sing-box run
本 PR 后 exit=0 启动 OK,deprecation 行数 = 0
本 PR 前 exit=0(静默) 启动 OK,WARN deprecation
本 PR 前 + 一条 query_type 规则 exit=1 FATAL,起不来

顺序不变量

抑制规则必须排在同 inbound 的 route 规则之前。route 规则是该 inbound 的 catch-all,DNS 规则先匹配先命中——顺序反了则 AAAA 先被 route 吃掉、抑制静默失效,而配置照样 check 过、照样启动(上表 v5 实跑坐实)。单测显式锁 indexOf(predefined) < indexOf(route)

语义差异(如实声明)

唯一不等价点:legacy rule-action strategy覆盖顶层 dns.strategy,新写法不能——省略即回落顶层默认。实测量化:顶层 dns.strategy: prefer_ipv6 下,旧 strategy: prefer_ipv4198.51.100.2 2001:db8::2(v4 先),新写法返 2001:db8::2 198.51.100.2(v6 先)。

对 FlowZ 无影响:测速配置从不下发顶层 dns.strategydns 只有 servers + 可选 rules)。该前提已由单测 expect('strategy' in cfg.dns).toBe(false) 锁死 + 源码处显式注释;将来若要给测速配置加顶层 strategy,必须同时重新推导端点规则,不能只加一半。

另一处需知(无实际差别):新写法下内核仍会发起 AAAA 查询,只是被 predefined 就地返空、不出网;legacy ipv4_only 压根不发。对出网流量、解析结果、耗时均无差别,仅核内多一次规则匹配。

测试

speed-test-endpoint-dns.test.tstoEqual精确形状(非属性点检),覆盖两档规则组 / 规则顺序 / 多端点互不串扰 / 禁 rule-action strategy 复活(配「确有 query_type」前置断言防空集平凡通过)/ 禁顶层 dns.strategy

六个变异逐个实跑转红:删抑制规则、条件反转(v6 档发 v4 档不发)、strategy 复活、键名 typo(rcodercodsing-box check 对 rule 内未知键无牙)、偷加顶层 dns.strategy、抑制规则与 route 顺序颠倒。

gate 全绿:gen-build-info + tsc -p tsconfig.main.json + tsc -p tsconfig.renderer.json 双 0 错;jest 3515 passed / 6 skipped / 37 snapshots(235 suites);eslint 0 errors / 63 warnings(与现有基线一致,全部落在本 PR 未触碰的行)。

补充:上游文档尚未发布

deprecation 消息指向的 anchor #migrate-dns-rule-action-strategy-to-rule-items 在上游 testing 分支 docs/migration.md不存在(1.14 仍 beta,该节未写)。本 PR 的新写法系用随包核实测定型 + 核源码(dns/router.go resolveLegacyDNSMode / lookupWithRulesdns/client.go sortAddresses)交叉验证,非照抄文档。

…L 的隐患

测速临时 config 的端点目标解析规则用的是 sing-box rule-action 上的 legacy
`strategy`。1.14.0 起该写法:

1. `sing-box run` 输出 deprecation 警告,1.16.0 移除(`check` 静默放行,
   现有校验抓不到);
2. 与**同一份 DNS 配置内**任何带 `query_type` / `ip_version` 的规则(含引用
   带 `query_type` 的 rule-set)**互斥**,共存即在 `initialize dns router`
   阶段被拒——`check` 与 `run` 双双硬失败,代理直接起不来。

第 2 条此前只是恰好没踩上:主配置大量用 `query_type` 但 `strategy` 只出现在
顶层 `dns.strategy`(非 rule-action),测速配置有 rule-action `strategy` 却零
`query_type`。任一侧加一个字段就炸。

改用 `query_type` 规则项表达族别偏好,全仓不再有 rule-action `strategy`,该
组合结构性不可能出现:

- 旧 `prefer_ipv4`(localAddress 含 v6)→ 不下发任何 strategy。测速 config 无
  顶层 `dns.strategy`,内核默认并发 A/AAAA 且 v4 排前(`sortAddresses` 对
  AsIS 与 prefer_ipv4 同一分支)。
- 旧 `ipv4_only`(纯 v4 localAddress)→ 该 inbound 的 AAAA 前置一条
  `predefined` 空 NOERROR,AAAA 就地返空不出网,结果集只剩 A。

行为在随包核 1.14.0-beta.2 上逐观测实测等价(loopback 假 DNS + WireGuard
endpoint,比对 deprecation 行、出网 qtype、解析结果与顺序):新旧两形态出网
查询类型一致、解析地址列表逐项同序;把 `generateProxyTestConfig` 的真实发射物
过核,`check` exit=0、`run` 零 deprecation,而改回旧形态即复现警告、旧形态再
加一条 `query_type` 规则即 FATAL 起不来。

抑制规则必须排在同 inbound 的 route(catch-all)规则之前,否则 AAAA 先被
route 吃掉、抑制静默失效且配置照样通过校验——已用变异实跑坐实,单测显式锁顺序。

唯一不等价点:legacy rule-action `strategy` 能覆盖顶层 `dns.strategy`,新写法
不能。测速 config 从不下发顶层 `dns.strategy`,故无影响;该前提已由单测和源码
注释锁死。

单测用 `toEqual` 锁精确形状,并覆盖:两档规则组、规则顺序、多端点互不串扰、
禁止 rule-action `strategy` 复活(配 query_type 存在性前置断言防空集平凡通过)、
禁止顶层 `dns.strategy`。六个变异(删抑制规则 / 条件反转 / strategy 复活 /
键名 typo / 偷加顶层 strategy / 顺序颠倒)逐个实跑转红。
@Sway-Chan
Sway-Chan merged commit 0875f66 into dododook:main Jul 28, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant