Fix DNS failure on network change #9

Merged
razvandimescu merged 4 commits from fix/upstream-redetect into main 2026-03-22 17:23:36 +08:00
3 changed files with 39 additions and 27 deletions
Showing only changes of commit 55ea49b003 - Show all commits

View File

@@ -33,8 +33,9 @@ pub struct ServerCtx {
pub lan_peers: Mutex<PeerStore>, pub lan_peers: Mutex<PeerStore>,
pub forwarding_rules: Vec<ForwardingRule>, pub forwarding_rules: Vec<ForwardingRule>,
pub upstream: Mutex<SocketAddr>, pub upstream: Mutex<SocketAddr>,
pub upstream_auto: bool, // true = auto-detected, false = explicitly configured pub upstream_auto: bool,
pub upstream_port: u16, pub upstream_port: u16,
pub lan_ip: Mutex<std::net::Ipv4Addr>,
pub timeout: Duration, pub timeout: Duration,
pub proxy_tld: String, pub proxy_tld: String,
pub proxy_tld_suffix: String, // pre-computed ".{tld}" to avoid per-query allocation pub proxy_tld_suffix: String, // pre-computed ".{tld}" to avoid per-query allocation

View File

@@ -113,7 +113,7 @@ pub async fn start_lan_discovery(ctx: Arc<ServerCtx>, config: &LanConfig) {
.as_nanos() as u64; .as_nanos() as u64;
pid ^ ts pid ^ ts
}; };
let local_ip = detect_lan_ip().unwrap_or(Ipv4Addr::LOCALHOST); let local_ip = *ctx.lan_ip.lock().unwrap();
info!( info!(
"LAN discovery on {}:{}, local IP {}, instance {:016x}", "LAN discovery on {}:{}, local IP {}, instance {:016x}",
multicast_group, port, local_ip, instance_id multicast_group, port, local_ip, instance_id
@@ -142,7 +142,6 @@ pub async fn start_lan_discovery(ctx: Arc<ServerCtx>, config: &LanConfig) {
// Spawn sender // Spawn sender
let sender_ctx = Arc::clone(&ctx); let sender_ctx = Arc::clone(&ctx);
let sender_socket = Arc::clone(&socket); let sender_socket = Arc::clone(&socket);
let local_ip_str = local_ip.to_string();
let dest = SocketAddr::new(IpAddr::V4(multicast_group), port); let dest = SocketAddr::new(IpAddr::V4(multicast_group), port);
tokio::spawn(async move { tokio::spawn(async move {
let mut ticker = tokio::time::interval(interval); let mut ticker = tokio::time::interval(interval);
@@ -162,9 +161,10 @@ pub async fn start_lan_discovery(ctx: Arc<ServerCtx>, config: &LanConfig) {
if services.is_empty() { if services.is_empty() {
continue; continue;
} }
let current_ip = sender_ctx.lan_ip.lock().unwrap().to_string();
let announcement = Announcement { let announcement = Announcement {
instance_id, instance_id,
host: local_ip_str.clone(), host: current_ip,
services, services,
}; };
if let Ok(json) = serde_json::to_vec(&announcement) { if let Ok(json) = serde_json::to_vec(&announcement) {

View File

@@ -132,6 +132,7 @@ async fn main() -> numa::Result<()> {
upstream: Mutex::new(upstream), upstream: Mutex::new(upstream),
upstream_auto: config.upstream.address.is_empty(), upstream_auto: config.upstream.address.is_empty(),
upstream_port: config.upstream.port, upstream_port: config.upstream.port,
lan_ip: Mutex::new(numa::lan::detect_lan_ip().unwrap_or(std::net::Ipv4Addr::LOCALHOST)),
timeout: Duration::from_millis(config.upstream.timeout_ms), timeout: Duration::from_millis(config.upstream.timeout_ms),
proxy_tld_suffix: if config.proxy.tld.is_empty() { proxy_tld_suffix: if config.proxy.tld.is_empty() {
String::new() String::new()
@@ -242,11 +243,11 @@ async fn main() -> numa::Result<()> {
} }
} }
// Spawn upstream re-detection (only for auto-detected upstream) // Spawn network change watcher (upstream re-detection, LAN IP update, peer flush)
if ctx.upstream_auto { {
let redetect_ctx = Arc::clone(&ctx); let watch_ctx = Arc::clone(&ctx);
tokio::spawn(async move { tokio::spawn(async move {
upstream_redetect_loop(redetect_ctx).await; network_watch_loop(watch_ctx).await;
}); });
} }
@@ -274,33 +275,43 @@ async fn main() -> numa::Result<()> {
} }
} }
async fn upstream_redetect_loop(ctx: Arc<numa::ctx::ServerCtx>) { async fn network_watch_loop(ctx: Arc<numa::ctx::ServerCtx>) {
use numa::system_dns::discover_system_dns;
let mut interval = tokio::time::interval(Duration::from_secs(30)); let mut interval = tokio::time::interval(Duration::from_secs(30));
interval.tick().await; // skip immediate tick interval.tick().await; // skip immediate tick
loop { loop {
interval.tick().await; interval.tick().await;
let mut changed = false;
let dns_info = discover_system_dns(); // Check LAN IP change
let new_addr = match dns_info.default_upstream { if let Some(new_ip) = numa::lan::detect_lan_ip() {
Some(addr) => addr, let mut current_ip = ctx.lan_ip.lock().unwrap();
None => continue, if new_ip != *current_ip {
}; info!("LAN IP changed: {} → {}", current_ip, new_ip);
let new_upstream: SocketAddr = match format!("{}:{}", new_addr, ctx.upstream_port).parse() { *current_ip = new_ip;
Ok(addr) => addr, changed = true;
Err(_) => continue, }
}; }
// Check upstream change (only for auto-detected upstream)
if ctx.upstream_auto {
let dns_info = numa::system_dns::discover_system_dns();
if let Some(new_addr) = dns_info.default_upstream {
if let Ok(new_upstream) =
format!("{}:{}", new_addr, ctx.upstream_port).parse::<SocketAddr>()
{
let mut upstream = ctx.upstream.lock().unwrap(); let mut upstream = ctx.upstream.lock().unwrap();
let current = *upstream; if new_upstream != *upstream {
if new_upstream != current { info!("upstream changed: {} → {}", *upstream, new_upstream);
*upstream = new_upstream; *upstream = new_upstream;
drop(upstream); changed = true;
info!("upstream changed: {} → {}", current, new_upstream); }
}
}
}
// Flush stale LAN peers from old network // Flush stale LAN peers on any network change
if changed {
ctx.lan_peers.lock().unwrap().clear(); ctx.lan_peers.lock().unwrap().clear();
info!("flushed LAN peers after network change"); info!("flushed LAN peers after network change");
} }