fix(linux): consult resolvectl when resolv.conf only shows the stub #52

Merged
razvandimescu merged 1 commits from fix/systemd-resolved-discovery into main 2026-04-10 03:32:57 +08:00
razvandimescu commented 2026-04-10 02:01:46 +08:00 (Migrated from github.com)

Summary

On modern Linux desktops (Arch, Ubuntu 22.04+, Fedora) using NetworkManager + systemd-resolved, /etc/resolv.conf is a symlink to stub-resolv.conf containing only nameserver 127.0.0.53. The real upstream DNS servers live inside systemd-resolved's per-link state, exposed via resolvectl status.

discover_linux() was parsing /etc/resolv.conf, correctly filtering the stub address, and then falling through to detect_dhcp_dns() — which is cfg(target_os = "macos")-only and returns None on Linux. Net effect: on a large chunk of Linux installs, numa silently defaulted to the hardcoded Quad9 DoH fallback instead of the user's actual DNS.

How this surfaced

Visible in @CaseyLabs's AUR test banner during #33:

```
║ Upstream https://9.9.9.9/dns-query
```

His Arch machine had working router DNS the whole time — numa just couldn't see past the systemd-resolved stub.

Fix

resolvectl_dns_server() already exists in system_dns.rs — it was introduced for cloud VPC forwarding-rule discovery and shells out to `resolvectl status --no-pager` to find the active DNS server. This PR wires it into the default-upstream fallback chain, between the primary resolv.conf parse and the `~/.numa/original-resolv.conf` backup:

```rust
let default_upstream = if let Some(ns) = upstream {
info!("detected system upstream: {}", ns);
Some(ns)
} else if let Some(ns) = resolvectl_dns_server() {
info!("detected system upstream via resolvectl: {}", ns);
Some(ns)
} else {
// existing backup-file fallback
...
};
```

Three lines, no new code — just wiring an existing helper into a path it should have been in from day one.

Test plan

  • On an Arch / Ubuntu 22.04+ / Fedora box with systemd-resolved active, run `sudo numa` and confirm the banner shows the real router/ISP DNS in the Upstream row instead of https://9.9.9.9/dns-query.
  • On a non-systemd-resolved Linux box (raw resolv.conf with real nameservers), confirm the existing path still wins — log line should say detected system upstream: not detected system upstream via resolvectl:.
  • Confirm macOS and Windows discovery are unaffected (change is inside #[cfg(target_os = "linux")]).

🤖 Generated with Claude Code

## Summary On modern Linux desktops (Arch, Ubuntu 22.04+, Fedora) using NetworkManager + systemd-resolved, `/etc/resolv.conf` is a symlink to `stub-resolv.conf` containing only `nameserver 127.0.0.53`. The real upstream DNS servers live inside systemd-resolved's per-link state, exposed via `resolvectl status`. `discover_linux()` was parsing `/etc/resolv.conf`, correctly filtering the stub address, and then falling through to `detect_dhcp_dns()` — which is `cfg(target_os = "macos")`-only and returns `None` on Linux. Net effect: on a large chunk of Linux installs, numa silently defaulted to the hardcoded Quad9 DoH fallback instead of the user's actual DNS. ## How this surfaced Visible in @CaseyLabs's AUR test banner during #33: \`\`\` ║ Upstream https://9.9.9.9/dns-query ║ \`\`\` His Arch machine had working router DNS the whole time — numa just couldn't see past the systemd-resolved stub. ## Fix `resolvectl_dns_server()` already exists in `system_dns.rs` — it was introduced for cloud VPC forwarding-rule discovery and shells out to \`resolvectl status --no-pager\` to find the active DNS server. This PR wires it into the default-upstream fallback chain, between the primary resolv.conf parse and the \`~/.numa/original-resolv.conf\` backup: \`\`\`rust let default_upstream = if let Some(ns) = upstream { info!("detected system upstream: {}", ns); Some(ns) } else if let Some(ns) = resolvectl_dns_server() { info!("detected system upstream via resolvectl: {}", ns); Some(ns) } else { // existing backup-file fallback ... }; \`\`\` Three lines, no new code — just wiring an existing helper into a path it should have been in from day one. ## Test plan - [ ] On an Arch / Ubuntu 22.04+ / Fedora box with systemd-resolved active, run \`sudo numa\` and confirm the banner shows the real router/ISP DNS in the `Upstream` row instead of `https://9.9.9.9/dns-query`. - [ ] On a non-systemd-resolved Linux box (raw `resolv.conf` with real nameservers), confirm the existing path still wins — log line should say `detected system upstream:` not `detected system upstream via resolvectl:`. - [ ] Confirm macOS and Windows discovery are unaffected (change is inside `#[cfg(target_os = "linux")]`). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.