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

Merged
razvandimescu merged 2 commits from fix/port53-diagnostic into main 2026-04-09 20:03:58 +08:00
Showing only changes of commit 49f4d29800 - Show all commits

View File

@@ -56,58 +56,37 @@ pub fn is_port_53(bind_addr: &str) -> bool {
.unwrap_or(false) .unwrap_or(false)
} }
/// Human-readable diagnostic for port-53 bind conflicts. Explains the /// Human-readable diagnostic for port-53 bind conflicts. Offers two
/// likely cause on the current platform and offers two concrete fixes: /// concrete fixes: install Numa as the system resolver, or bind to a
/// install Numa as the system resolver, or test on a non-privileged port. /// non-privileged port.
pub fn port53_conflict_advisory(bind_addr: &str) -> String { pub fn port53_conflict_advisory(bind_addr: &str) -> String {
let o = "\x1b[1;38;2;192;98;58m"; // bold orange let o = "\x1b[1;38;2;192;98;58m"; // bold orange
let r = "\x1b[0m"; let r = "\x1b[0m";
let mut msg = format!( format!(
"\n{o}Numa{r} — cannot bind to {}: port 53 is already in use.\n\n", "
bind_addr {o}Numa{r} — cannot bind to {bind_addr}: port 53 is already in use.
);
#[cfg(target_os = "linux")] Another process is already bound to port 53. On Linux this is
{ typically systemd-resolved; on Windows, the DNS Client service.
if is_systemd_resolved_active() {
msg.push_str(
" systemd-resolved is holding port 53 via its stub listener\n \
(127.0.0.53:53), which blocks bind(0.0.0.0:53) on Linux.\n\n",
);
} else {
msg.push_str(
" Another process is holding port 53 on this host.\n \
Check with: sudo ss -lntu 'sport = :53'\n\n",
);
}
}
#[cfg(windows)] Fix — pick one:
{
msg.push_str(" Windows DNS Client (Dnscache) holds port 53 at the kernel level.\n\n");
}
#[cfg(not(any(target_os = "linux", windows)))] 1. Install Numa as the system resolver (frees port 53):
{
msg.push_str(" Another process on this host is already bound to port 53.\n\n");
}
msg.push_str(" Fix — pick one:\n\n"); sudo numa install (on Windows, run as Administrator)
msg.push_str(" 1. Install Numa as the system resolver (frees port 53):\n");
#[cfg(windows)]
msg.push_str(" numa install (run as Administrator)\n\n");
#[cfg(not(windows))]
msg.push_str(" sudo numa install\n\n");
msg.push_str(" 2. Test without privileges on a non-standard port.\n"); 2. Run on a non-privileged port for testing.
msg.push_str(" Create ~/.config/numa/numa.toml with:\n\n"); Create ~/.config/numa/numa.toml with:
msg.push_str(" [server]\n");
msg.push_str(" bind_addr = \"127.0.0.1:5354\"\n");
msg.push_str(" api_port = 5380\n\n");
msg.push_str(" Then run: numa\n");
msg.push_str(" Test with: dig @127.0.0.1 -p 5354 example.com\n\n");
msg [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")] #[cfg(target_os = "macos")]
@@ -1258,7 +1237,7 @@ fn backup_path_linux() -> std::path::PathBuf {
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub fn is_systemd_resolved_active() -> bool { fn is_systemd_resolved_active() -> bool {
std::process::Command::new("systemctl") std::process::Command::new("systemctl")
.args(["is-active", "--quiet", "systemd-resolved"]) .args(["is-active", "--quiet", "systemd-resolved"])
.status() .status()