fix: advisory + exit(1) when port 53 is already in use (#45) (#47)

* fix: advisory + exit(1) when port 53 is already in use (#45)

Detect AddrInUse on bind, print a human-readable diagnostic explaining
systemd-resolved / Dnscache as the likely cause and offer two concrete
fixes (sudo numa install, or bind_addr on a non-privileged port), then
exit(1) instead of surfacing a raw OS error.

Adds tests/docker/smoke-port53.sh: end-to-end Docker test that
pre-binds port 53 with a Python UDP socket and asserts the advisory +
exit code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: collapse port53 advisory to single flat path

The per-platform cause sentences were cosmetic — they didn't change
the user's actions (install, or bind_addr on a non-privileged port),
but they introduced duplicated "another process..." strings, a
dead-from-CI branch (is_systemd_resolved_active() == true is never
reached by any test), and a pub visibility bump on
is_systemd_resolved_active for a single caller.

Replace with one flat format! whose cause line mentions both
systemd-resolved and the Windows DNS Client inline. The existing
smoke test now exercises 100% of the function.

is_systemd_resolved_active reverts to private.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit was merged in pull request #47.
This commit is contained in:
Razvan Dimescu
2026-04-09 15:03:58 +03:00
committed by GitHub
parent 27dfaab360
commit a6f23a5ddb
3 changed files with 197 additions and 1 deletions

View File

@@ -46,6 +46,49 @@ pub fn discover_system_dns() -> SystemDnsInfo {
}
}
/// True if `bind_addr` targets DNS port 53. Used to scope the port-53
/// conflict advisory — we only want to print the systemd-resolved /
/// Dnscache hint when the user is actually trying to bind the DNS port.
pub fn is_port_53(bind_addr: &str) -> bool {
bind_addr
.parse::<SocketAddr>()
.map(|s| s.port() == 53)
.unwrap_or(false)
}
/// Human-readable diagnostic for port-53 bind conflicts. Offers two
/// concrete fixes: install Numa as the system resolver, or bind to a
/// non-privileged port.
pub fn port53_conflict_advisory(bind_addr: &str) -> String {
let o = "\x1b[1;38;2;192;98;58m"; // bold orange
let r = "\x1b[0m";
format!(
"
{o}Numa{r} — cannot bind to {bind_addr}: port 53 is already in use.
Another process is already bound to port 53. On Linux this is
typically systemd-resolved; on Windows, the DNS Client service.
Fix — pick one:
1. Install Numa as the system resolver (frees port 53):
sudo numa install (on Windows, run as Administrator)
2. Run on a non-privileged port for testing.
Create ~/.config/numa/numa.toml with:
[server]
bind_addr = \"127.0.0.1:5354\"
api_port = 5380
Then run: numa
Test with: dig @127.0.0.1 -p 5354 example.com
"
)
}
#[cfg(target_os = "macos")]
fn discover_macos() -> SystemDnsInfo {
use log::{debug, warn};