Skip to content

fix(dns): enable_dns_mode must kill orphan proxy via sudo + add AppleScript trap #148

Description

@flyhigher139

Part of #147 (epic: DNS mode enable state desync)

范围

src-tauri/crates/mhost-dns/src/platform.rs::enable_dns_mode

Fix A — kill_orphan_dns_proxies 改 sudo 提权 kill

当前 (line 503):

let rc = unsafe { libc::kill(pid as libc::pid_t, SIGTERM) };

问题: proxy 是 with administrator privileges 起 root 进程,user 态 libc::kill 在 macOS 默认被 EACCES 静默丢弃,pgrep 找得到但 SIGTERM 杀不掉

改为:

  • run_with_privileges 跑一个简单脚本:for pid in $pids; do kill $pid 2>/dev/null || true; done(整段 sudo 提权)
  • 放在 kill_orphan_dns_proxies 之前:必须先用 sudo 杀,再进 enable_dns_mode 主流程
  • pids 来源继续用 pgrep -x

Fix B — AppleScript sh 脚本加 trap

当前 (line 523-550):

#!/bin/sh
set -e
"{proxy}" --listen 53 --target {dns_port} &
proxy_pid=$!
echo "$proxy_pid {proxy}" > {pid_file}
disown
# ... 等 ready 文件 ...
networksetup -setdnsservers {interface} 127.0.0.1

改为:

#!/bin/sh
set -e
cleanup() {
  if [ -n "$proxy_pid" ]; then
    kill "$proxy_pid" 2>/dev/null || true
    wait "$proxy_pid" 2>/dev/null || true
  fi
  rm -f {pid_file} {ready_file}
}
trap cleanup EXIT INT TERM

"{proxy}" --listen 53 --target {dns_port} &
proxy_pid=$!
echo "$proxy_pid {proxy}" > {pid_file}
disown

# 等 ready 文件(5s timeout)...
# 失败时 trap 自动 kill proxy_pid + cleanup 文件

networksetup -setdnsservers {interface} 127.0.0.1
# networksetup 成功后正常退出 → trap 也会跑(杀 proxy? 不行!)
# → 需要 trap 在成功路径跳过 kill:

要点:networksetup 成功之后必须主动 trap - EXIT(取消 EXIT trap),否则脚本正常退出也会把刚启动的 proxy 杀掉。

实际更稳的写法:

trap 'cleanup_on_failure' EXIT INT TERM  # 默认 = 失败路径
# 主流程
# ...
# 成功标记 + 取消 trap
trap - EXIT INT TERM  # 成功路径不清理 proxy,留给正常 disable 路径管
exit 0

cleanup_on_failure 里 kill proxy_pid + cleanup pid_file + ready_file。

regression test

platform.rs 加单元测试 + 集成测试:

  1. test_kill_orphan_via_sudo_helper_signaturerun_with_privileges 跑一段 kill <pid> 脚本的 wrapper 函数存在 + 接受 pids vec
  2. test_enable_script_has_proxy_trap_cleanup — 断言生成的 sh 脚本:
    • 包含 trap 子句 + cleanup 函数
    • 包含 trap - EXIT 在 setdnsservers 成功路径之后
    • cleanup 函数引用 $proxy_pid
  3. 集成测试 (macOS CI,加 ignore):真跑 enable → 模拟 networksetup 拒绝 → 验证 proxy 已死

Acceptance criteria

  • dev 模式 enable → 在 osascript 弹窗点 Cancel → ps aux | grep mhost-dns-proxy 无输出
  • dev 模式 enable → 故意让 networksetup 失败(simulate -setdnsservers 拒绝)→ proxy 必须被 kill
  • dev 模式连续 enable 两次(中间 osascript cancel)→ 第二次 enable 不撞 53 端口冲突
  • cargo clippy --workspace -- -D warnings clean
  • cargo test --workspace --all-features clean

不做

依赖

无前置依赖。可独立 ship。

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