Skip to content

[Enhancement] Add OS-probing IPC so UI can independently verify system DNS state #153

Description

@flyhigher139

Background

When investigating #152, the root causes for DNS stuck at 127.0.0.1 after disable traced to two backend bugs (state mutation order in set_dns_mode_disable, and error-swallowing in cleanup_dns_on_exit). These will be fixed by the current fix/dns-cancel-149 PR.

However, the investigation surfaced a third, structural weakness: the frontend has no independent way to verify what the system DNS is actually pointing at.

Current behavior

get_dns_mode (src-tauri/src/commands/dns.rs:745) returns the in-memory AtomicBool dns_enabled:

pub async fn get_dns_mode(state: tauri::State<'_, AppState>) -> Result<bool, MhostError> {
    Ok(state.dns_enabled.load(Ordering::Relaxed))
}

It does not probe the OS. The frontend's truth-fetch on error therefore trusts Rust's in-memory view, which can disagree with reality if:

  • The Rust state machine has a bug we haven't found yet
  • A future change introduces a new path where dns_enabled and networksetup -getdnsservers diverge
  • An external event (manual networksetup edit, another tool) modifies system DNS while mHost thinks it's the one in charge

Proposed change

Add a new IPC command that actually probes the OS:

// new IPC, bound in src-tauri/src/lib.rs
#[tauri::command]
pub async fn probe_system_dns(
    state: tauri::State<'_, AppState>,
) -> Result<SystemDnsSnapshot, MhostError> {
    // runs networksetup -getdnsservers <iface>, returns the actual server list
    // + a derived bool `points_at_loopback: bool` via is_local_resolver
}

#[derive(Serialize, Deserialize)]
pub struct SystemDnsSnapshot {
    pub interface: String,
    pub servers: Vec<String>,
    pub points_at_loopback: bool,
}

Frontend integration

toggleDnsModeAtom (src/stores/profiles/actions.ts:409) currently truth-fetches only on error:

const actual = await getDnsMode();
set(dnsEnabledAtom, actual);

Add a parallel probeSystemDns() call and compare:

dnsEnabledAtom probe_system_dns.points_at_loopback UI state
true true "Running" (consistent)
true false "Discrepancy" banner — show "system DNS not pointing at mHost proxy, click to force sync"
false true "Discrepancy" banner — show "system DNS still at 127.0.0.1, click to force restore"
false false "Stopped" (consistent)

The "force sync" / "force restore" buttons would call the existing set_dns_mode(false) / set_dns_mode(true) IPC — same code path as the normal toggle, but with the user now informed why they're clicking.

Acceptance criteria

  1. New IPC probe_system_dns runs networksetup -getdnsservers on the active interface and returns the snapshot.
  2. probe_system_dns is gated to macOS only (DNS mode is macOS-only; Linux/Windows can return a clear "not supported" error).
  3. Frontend calls probeSystemDns in parallel with getDnsMode in the truth-fetch path; surfaces the discrepancy banner only when they disagree.
  4. The discrepancy banner offers a one-click recovery action that calls the existing IPC.
  5. Unit tests for the new IPC's pure-logic part (parsing, loopback detection) — networksetup_get_dns wrapper is already covered by test_networksetup_get_dns_filter_pipeline.
  6. macOS E2E scenario added to doc/tech/dns-mode-e2e-recipe.md: manually set system DNS to a stale value via networksetup -setdnsservers Wi-Fi 8.8.8.8 while Rust thinks dns_enabled=false, verify the UI surfaces the discrepancy banner.

Out of scope

  • Not changing get_dns_mode to probe the OS (would change semantics for all callers and break the existing "trust in-memory" contract).
  • Not auto-recovering when discrepancy is detected — the UI banner is informational, the user decides whether to act.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    dns-modeDNS mode (本地 DNS server) 相关问题enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions