feat: forward-by-default, auto recursive mode, Linux install fixes #27

Merged
razvandimescu merged 7 commits from feat/auto-recursive-install-fixes into main 2026-04-01 13:49:16 +08:00
4 changed files with 36 additions and 17 deletions
Showing only changes of commit da7bb3fb69 - Show all commits

View File

@@ -24,7 +24,7 @@ sudo numa # port 53 requires root
Open the dashboard: **http://numa.numa** (or `http://localhost:5380`) Open the dashboard: **http://numa.numa** (or `http://localhost:5380`)
Set as system DNS: `sudo numa install && sudo numa service start` Set as system DNS: `sudo numa install`
## Local Services ## Local Services

View File

@@ -59,8 +59,10 @@ fn default_bind_addr() -> String {
"0.0.0.0:53".to_string() "0.0.0.0:53".to_string()
} }
pub const DEFAULT_API_PORT: u16 = 5380;
fn default_api_port() -> u16 { fn default_api_port() -> u16 {
5380 DEFAULT_API_PORT
} }
#[derive(Deserialize, Default, PartialEq, Eq, Clone, Copy)] #[derive(Deserialize, Default, PartialEq, Eq, Clone, Copy)]

View File

@@ -65,18 +65,19 @@ pub async fn probe_udp(root_hints: &[SocketAddr]) {
} }
} }
/// Probe whether recursive resolution works by querying a root server. /// Probe whether recursive resolution works by querying root servers.
/// Tries up to 3 hints before declaring failure.
pub async fn probe_recursive(root_hints: &[SocketAddr]) -> bool { pub async fn probe_recursive(root_hints: &[SocketAddr]) -> bool {
let hint = match root_hints.first() {
Some(h) => *h,
None => return false,
};
let mut probe = DnsPacket::query(next_id(), ".", QueryType::NS); let mut probe = DnsPacket::query(next_id(), ".", QueryType::NS);
probe.header.recursion_desired = false; probe.header.recursion_desired = false;
match forward_udp(&probe, hint, Duration::from_secs(3)).await { for hint in root_hints.iter().take(3) {
Ok(resp) => !resp.answers.is_empty() || !resp.authorities.is_empty(), if let Ok(resp) = forward_udp(&probe, *hint, Duration::from_secs(3)).await {
Err(_) => false, if !resp.answers.is_empty() || !resp.authorities.is_empty() {
return true;
}
}
} }
false
} }
pub async fn prime_tld_cache( pub async fn prime_tld_cache(

View File

@@ -255,7 +255,7 @@ fn resolvectl_dns_server() -> Option<String> {
if line.contains("DNS Servers") || line.contains("Current DNS Server") { if line.contains("DNS Servers") || line.contains("Current DNS Server") {
if let Some(ip) = line.split(':').next_back() { if let Some(ip) = line.split(':').next_back() {
let ip = ip.trim(); let ip = ip.trim();
if !is_loopback_or_stub(ip) { if ip.parse::<std::net::IpAddr>().is_ok() && !is_loopback_or_stub(ip) {
return Some(ip.to_string()); return Some(ip.to_string());
} }
} }
@@ -632,12 +632,7 @@ fn install_service_macos() -> Result<(), String> {
std::fs::write(PLIST_DEST, plist) std::fs::write(PLIST_DEST, plist)
.map_err(|e| format!("failed to write {}: {}", PLIST_DEST, e))?; .map_err(|e| format!("failed to write {}: {}", PLIST_DEST, e))?;
// Configure system DNS before starting service // Load the service first so numa is listening before DNS redirect
if let Err(e) = install_macos() {
eprintln!(" warning: failed to configure system DNS: {}", e);
}
// Load the service
let status = std::process::Command::new("launchctl") let status = std::process::Command::new("launchctl")
.args(["load", "-w", PLIST_DEST]) .args(["load", "-w", PLIST_DEST])
.status() .status()
@@ -647,6 +642,27 @@ fn install_service_macos() -> Result<(), String> {
return Err("launchctl load failed".to_string()); return Err("launchctl load failed".to_string());
} }
// Wait for numa to be ready before redirecting DNS
let api_up = (0..10).any(|i| {
if i > 0 {
std::thread::sleep(std::time::Duration::from_millis(500));
}
std::net::TcpStream::connect(("127.0.0.1", crate::config::DEFAULT_API_PORT)).is_ok()
});
if !api_up {
// Service failed to start — don't redirect DNS to a dead endpoint
let _ = std::process::Command::new("launchctl")
.args(["unload", PLIST_DEST])
.status();
return Err(
"numa service did not start (port 53 may be in use). Service unloaded.".to_string(),
);
}
if let Err(e) = install_macos() {
eprintln!(" warning: failed to configure system DNS: {}", e);
}
eprintln!(" Service installed and started."); eprintln!(" Service installed and started.");
eprintln!(" Numa will auto-start on boot and restart if killed."); eprintln!(" Numa will auto-start on boot and restart if killed.");
eprintln!(" Logs: /usr/local/var/log/numa.log"); eprintln!(" Logs: /usr/local/var/log/numa.log");