* feat: SRTT-based nameserver selection for recursive resolver BIND-style Smoothed RTT (EWMA) tracking per NS IP address. The resolver learns which nameservers respond fastest and prefers them, eliminating cascading timeouts from slow/unreachable IPv6 servers. - New src/srtt.rs: SrttCache with record_rtt, record_failure, sort_by_rtt - EWMA formula: new = (old * 7 + sample) / 8, 5s failure penalty, 5min decay - TCP penalty (+100ms) lets SRTT naturally deprioritize IPv6-over-TCP - Enabled flag embedded in SrttCache (no-op when disabled) - Batch eviction (64 entries) for O(1) amortized writes at capacity - Configurable via [upstream] srtt = true/false (default: true) - Benchmark script: scripts/benchmark.sh (full, cold, warm, compare-all) - Benchmarks show 12x avg improvement, 0% queries >1s (was 58%) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: show DNSSEC and SRTT status in dashboard + API Add dnssec and srtt boolean fields to /stats API response. Display on/off indicators in the dashboard footer. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: apply SRTT decay before EWMA so recovered servers rehabilitate Without decay-before-EWMA, a server penalized at 5000ms stayed near that value even after recovery — the stale raw penalty was used as the EWMA base instead of the decayed estimate. Extract decayed_srtt() helper and call it in record_rtt() before the smoothing step. Also restores removed "why" comments in send_query / resolve_recursive. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add install/upgrade instructions, smarter benchmark priming README: document `numa install`, `numa service`, Homebrew upgrade, and `make deploy` workflows. Benchmark: replace fixed `sleep 4` with `wait_for_priming` that polls cache entry count for stability. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
84 lines
2.1 KiB
Rust
84 lines
2.1 KiB
Rust
pub mod api;
|
|
pub mod blocklist;
|
|
pub mod buffer;
|
|
pub mod cache;
|
|
pub mod config;
|
|
pub mod ctx;
|
|
pub mod dnssec;
|
|
pub mod forward;
|
|
pub mod header;
|
|
pub mod lan;
|
|
pub mod override_store;
|
|
pub mod packet;
|
|
pub mod proxy;
|
|
pub mod query_log;
|
|
pub mod question;
|
|
pub mod record;
|
|
pub mod recursive;
|
|
pub mod service_store;
|
|
pub mod srtt;
|
|
pub mod stats;
|
|
pub mod system_dns;
|
|
pub mod tls;
|
|
|
|
pub type Error = Box<dyn std::error::Error + Send + Sync>;
|
|
pub type Result<T> = std::result::Result<T, Error>;
|
|
|
|
/// Shared config directory for persistent data (services.json, etc).
|
|
/// Unix: ~/.config/numa/ (or /usr/local/var/numa/ when running as root daemon)
|
|
/// Windows: %APPDATA%\numa
|
|
pub fn config_dir() -> std::path::PathBuf {
|
|
#[cfg(windows)]
|
|
{
|
|
std::path::PathBuf::from(
|
|
std::env::var("APPDATA").unwrap_or_else(|_| "C:\\ProgramData".into()),
|
|
)
|
|
.join("numa")
|
|
}
|
|
#[cfg(not(windows))]
|
|
{
|
|
config_dir_unix()
|
|
}
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
fn config_dir_unix() -> std::path::PathBuf {
|
|
// When run via sudo, SUDO_USER has the real user
|
|
if let Ok(user) = std::env::var("SUDO_USER") {
|
|
let home = if cfg!(target_os = "macos") {
|
|
format!("/Users/{}", user)
|
|
} else {
|
|
format!("/home/{}", user)
|
|
};
|
|
return std::path::PathBuf::from(home).join(".config").join("numa");
|
|
}
|
|
|
|
// Normal user (not root)
|
|
if let Ok(home) = std::env::var("HOME") {
|
|
let path = std::path::PathBuf::from(&home);
|
|
if !home.starts_with("/var/root") && !home.starts_with("/root") {
|
|
return path.join(".config").join("numa");
|
|
}
|
|
}
|
|
|
|
// Running as root daemon (launchd/systemd) — use system-wide path
|
|
std::path::PathBuf::from("/usr/local/var/numa")
|
|
}
|
|
|
|
/// System-wide data directory for TLS certs.
|
|
/// Unix: /usr/local/var/numa
|
|
/// Windows: %PROGRAMDATA%\numa
|
|
pub fn data_dir() -> std::path::PathBuf {
|
|
#[cfg(windows)]
|
|
{
|
|
std::path::PathBuf::from(
|
|
std::env::var("PROGRAMDATA").unwrap_or_else(|_| "C:\\ProgramData".into()),
|
|
)
|
|
.join("numa")
|
|
}
|
|
#[cfg(not(windows))]
|
|
{
|
|
std::path::PathBuf::from("/usr/local/var/numa")
|
|
}
|
|
}
|