Skip to content

[Bug] DNS disable 不还原系统 DNS(DhcpEmpty 路径下网络被卡在 127.0.0.1) #152

Description

@flyhigher139

现象

用户操作:Settings → Enable DNS Mode(成功,TCC 弹窗确认)→ Disable DNS Mode(UI 立即显示 "DNS Mode: Off")→ 浏览器/系统任何域名都解析失败,必须手动到 System Settings → Network → Wi-Fi → DNS 把 127.0.0.1 删掉才能恢复上网。

复现路径:用户原始系统 DNS 是 DHCP-empty(即 networksetup -getdnsservers Wi-Fi 输出 There aren't any DNS Servers set on Wi-Fi)。这是绝大多数家用 Wi-Fi 用户的默认状态。

期望

Disable DNS Mode 后系统 DNS 应该回到 Disable 之前的状态。DhcpEmpty 的情况下应该回到 networksetup -getdnsservers Wi-Fi 输出为空(DHCP 自动获取)。

实际

Disable 返回 Ok(()) 后,networksetup -getdnsservers Wi-Fi 仍然输出 127.0.0.1。系统级 DNS 解析全部失败,浏览器无法访问任何域名。

根因分析(基于 fix/dns-cancel-149 当前代码 + 现场日志)

PR #148 (fix(#148), commit 5a0ebd3) + PR #149 (feat(#149), commit d06822c) 的 disable 路径在 DhcpEmpty 场景下漏了兜底。

platform.rs::disable_dns_mode() (~line 863-1006) 的分支

match is_expected_proxy_alive() {
    true => {
        // 等 proxy 自我清理(最多 5s),NO fallback
        for _ in 0..50 { ... } // wait loop
        return Ok(());
    }
    false => {
        if interactive {
            osascript_restore(original).await?;  // 只有这条会调 networksetup
        }
    }
}

proxy 自我清理走的是 proxy.rs::restore_dns_and_exit()(~line 188-263),它会调 networksetup -setdnsservers Wi-Fi Empty(line 240-241)来还原 DhcpEmpty 状态。

但用户的现场日志显示:

  • proxy 收到 shutdown signal(server.rs:230 打了 "DNS server received shutdown signal")
  • restore_dns_and_exit 没执行到 networksetup 那行
  • platform.rs::disable_dns_mode 返回 Ok(()) 之前也没走 osascript_restore 分支

也就是说 proxy 在收到 shutdown signal 后没能完成 restore_dns_and_exit,但 is_expected_proxy_alive() 在 5s 等待后返回了 false(proxy 确实死了),可 disable 还是返回 Ok(()) —— 因为 false 分支要么走 interactive=true 的 osascript 兜底(现场没打 osascript log),要么什么都不做(interactive=false)。两条分支都没成功还原 DNS

链路 1: Proxy 还原失败 + interactive=false → 系统 DNS 泄漏

即使 is_expected_proxy_alive() 检测到 proxy 死掉,如果 interactive=false(disable IPC 默认值,从前端调用过来时),根本不会调 osascript 兜底。proxy 死了,restore 没完成,系统 DNS 卡在 127.0.0.1。

链路 2: Proxy 还原 "成功" 但 on-disk original 被污染

更隐蔽的是,即使 proxy 的 networksetup 调用成功了,下次 enable 也会失败:

1. 第一次 enable:DhcpEmpty,enable 不写 original.txt
2. disable:proxy 死了,restore 没跑
3. 系统 DNS = 127.0.0.1(脏)
4. 第二次 enable:capture_dns_state() 读到 Manual(["127.0.0.1"]) ← BUG
5. enable 把 127.0.0.1 当作 original 写到 manifest + original.txt
6. 下次 disable 时 proxy 调 networksetup -setdnsservers Wi-Fi 127.0.0.1
7. 等于 "还原" = "不变",但实际真正的原始 DHCP 状态已经永久丢失

capture_dns_state 必须过滤掉 127.0.0.1(这是 mHost 自己注入的,不是用户的 original)。

复现步骤

# 0. 准备:网络 DNS 是 DHCP-empty(绝大多数家用 Wi-Fi 默认状态)
networksetup -getdnsservers Wi-Fi
# 期望:There aren't any DNS Servers set on Wi-Fi

# 1. 启动
MHOST_AUTO_DNS=1 pnpm tauri dev
# (我自己加了 temp trigger:enable → wait → disable → wait → enable,
#  方便在没有 UI 自动化下复现。TRIGGER 在 src-tauri/src/lib.rs setup() 里,
#  提交前必须移除。)

# 2. 等 ~10s 让 3 个阶段都跑完

# 3. 验证(应该看到 Empty,但实际看到 127.0.0.1)
networksetup -getdnsservers Wi-Fi
# 实际:127.0.0.1
# 期望:There aren't any DNS Servers set on Wi-Fi

# 4. 验证 on-disk original 也被污染
cat "$HOME/Library/Application Support/mHost/.runtime/mhost-dns-original.txt"
# 实际:127.0.0.1
# 期望:空文件

提议修复(TBD — 等 issue 跟踪后决定方向)

最小修复(blocker):disable_dns_modeis_expected_proxy_alive() == false永远走 osascript 兜底(不再受 interactive 限制),保证系统 DNS 一定被还原。

推荐修复:

  1. disable_dns_mode 总在 proxy 死后做 networksetup -setdnsservers <iface> Empty 自检(不需要 sudo,因为 networksetup 不需要 root)。
  2. capture_dns_state 过滤掉 127.0.0.1(这是 mHost 自身注入的 sentinel,永远不应该被当成用户 original)。
  3. 加 eprintln 到 proxy.rs::restore_dns_and_exit 的所有分支,确认它到底执行到哪一步(exit_reason)—— 这是定位「为什么 proxy 收到 signal 但 restore 没跑」的关键。

相关 issue / PR

环境

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdns-modeDNS mode (本地 DNS server) 相关问题

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions