diff --git a/src/system_dns.rs b/src/system_dns.rs index c4279cd..35490ae 100644 --- a/src/system_dns.rs +++ b/src/system_dns.rs @@ -572,7 +572,7 @@ fn windows_backup_path() -> std::path::PathBuf { #[cfg(windows)] fn disable_dnscache() -> Result { - // Check if Dnscache is running (it holds port 53 at kernel level) + // Check if Dnscache is running (it can hold port 53) let output = std::process::Command::new("sc") .args(["query", "Dnscache"]) .output() @@ -603,8 +603,16 @@ fn disable_dnscache() -> Result { return Err("failed to disable Dnscache via registry (run as Administrator?)".into()); } - eprintln!(" Dnscache disabled. A reboot is required to free port 53."); - Ok(true) + // Dnscache is disabled for next boot. Check whether port 53 is + // actually blocked right now — on many Windows configurations + // Dnscache doesn't bind port 53 even while running. + let port_blocked = std::net::UdpSocket::bind("127.0.0.1:53").is_err(); + if port_blocked { + eprintln!(" Dnscache disabled. A reboot is required to free port 53."); + } else { + eprintln!(" Dnscache disabled. Port 53 is free."); + } + Ok(port_blocked) } #[cfg(windows)] @@ -671,31 +679,6 @@ fn install_windows() -> Result<(), String> { std::fs::write(&path, json).map_err(|e| format!("failed to write backup: {}", e))?; } - for name in interfaces.keys() { - let status = std::process::Command::new("netsh") - .args([ - "interface", - "ipv4", - "set", - "dnsservers", - name, - "static", - "127.0.0.1", - "primary", - ]) - .status() - .map_err(|e| format!("failed to set DNS for {}: {}", name, e))?; - - if status.success() { - eprintln!(" set DNS for \"{}\" -> 127.0.0.1", name); - } else { - eprintln!( - " warning: failed to set DNS for \"{}\" (run as Administrator?)", - name - ); - } - } - let needs_reboot = disable_dnscache()?; // On re-install, stop the running service first so the binary can be @@ -710,9 +693,14 @@ fn install_windows() -> Result<(), String> { let service_exe = install_service_binary()?; register_service_scm(&service_exe)?; - // If no reboot is pending (Dnscache wasn't running, port 53 free), - // start the service immediately. Otherwise it'll launch on next boot. - if !needs_reboot { + if needs_reboot { + // Dnscache still holds port 53 until reboot. Do NOT redirect DNS + // yet — nothing is listening on 127.0.0.1:53, so redirecting now + // would kill DNS. The service will call redirect_dns_to_localhost() + // on its first startup after reboot. + } else { + redirect_dns_with_interfaces(&interfaces)?; + match start_service_scm() { Ok(_) => eprintln!(" Service started."), Err(e) => eprintln!( @@ -756,6 +744,45 @@ fn run_sc(args: &[&str]) -> Result { Ok(out) } +/// Point all active network interfaces at 127.0.0.1 so Numa handles DNS. +/// Called from the service on first boot after a reboot that freed Dnscache. +#[cfg(windows)] +pub fn redirect_dns_to_localhost() -> Result<(), String> { + let interfaces = get_windows_interfaces()?; + redirect_dns_with_interfaces(&interfaces) +} + +#[cfg(windows)] +fn redirect_dns_with_interfaces( + interfaces: &std::collections::HashMap, +) -> Result<(), String> { + for name in interfaces.keys() { + let status = std::process::Command::new("netsh") + .args([ + "interface", + "ipv4", + "set", + "dnsservers", + name, + "static", + "127.0.0.1", + "primary", + ]) + .status() + .map_err(|e| format!("failed to set DNS for {}: {}", name, e))?; + + if status.success() { + eprintln!(" set DNS for \"{}\" -> 127.0.0.1", name); + } else { + eprintln!( + " warning: failed to set DNS for \"{}\" (run as Administrator?)", + name + ); + } + } + Ok(()) +} + /// Copy the currently-running binary to the service install location. SCM /// keeps a handle to this path, so it must be stable across user sessions. #[cfg(windows)] diff --git a/src/windows_service.rs b/src/windows_service.rs index a1403d7..a363359 100644 --- a/src/windows_service.rs +++ b/src/windows_service.rs @@ -83,6 +83,23 @@ fn run_service() -> windows_service::Result<()> { let _ = server_done_tx.send(()); }); + // Wait for the API to be ready, then ensure DNS points at localhost. + // On first boot after install (Dnscache was disabled, reboot freed + // port 53), the installer deferred the DNS redirect — do it now. + let api_up = (0..20).any(|i| { + if i > 0 { + std::thread::sleep(Duration::from_millis(500)); + } + std::net::TcpStream::connect(("127.0.0.1", crate::config::DEFAULT_API_PORT)).is_ok() + }); + if api_up { + if let Err(e) = crate::system_dns::redirect_dns_to_localhost() { + log::warn!("could not redirect DNS to localhost: {}", e); + } + } else { + log::error!("numa API did not start within 10s — DNS not redirected"); + } + // Wait for either SCM stop or server termination. loop { if shutdown_rx.recv_timeout(Duration::from_millis(500)).is_ok() {