feat: resolve .numa services to LAN IP for remote clients

Remote DNS clients (e.g. phones on same WiFi) received 127.0.0.1 for
local .numa services, which is unreachable from their perspective.
Now returns the host's LAN IP when the query originates from a
non-loopback address. Also auto-widens proxy bind to 0.0.0.0 when
DNS is already public, and adds a startup warning when the proxy
remains localhost-only.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Razvan Dimescu
2026-03-29 22:42:43 +03:00
parent 32cd8624b4
commit 1a6a2a5f31
2 changed files with 21 additions and 4 deletions

View File

@@ -108,12 +108,17 @@ pub async fn handle_query(
} else if !ctx.proxy_tld_suffix.is_empty()
&& (qname.ends_with(&ctx.proxy_tld_suffix) || qname == ctx.proxy_tld)
{
// Resolve .numa: local services → 127.0.0.1, LAN peers → peer IP
// Resolve .numa: remote clients get LAN IP (can't reach 127.0.0.1), local get loopback
let service_name = qname.strip_suffix(&ctx.proxy_tld_suffix).unwrap_or(&qname);
let is_remote = !src_addr.ip().is_loopback();
let resolve_ip = {
let local = ctx.services.lock().unwrap();
if local.lookup(service_name).is_some() {
if is_remote {
*ctx.lan_ip.lock().unwrap()
} else {
std::net::Ipv4Addr::LOCALHOST
}
} else {
let mut peers = ctx.lan_peers.lock().unwrap();
peers

View File

@@ -208,6 +208,7 @@ async fn main() -> numa::Result<()> {
});
let zone_count: usize = ctx.zone_map.values().map(|m| m.len()).sum();
let dns_is_public = config.server.bind_addr.starts_with("0.0.0.0");
// Build banner rows, then size the box to fit the longest value
let api_url = format!("http://localhost:{}", api_port);
@@ -308,6 +309,17 @@ async fn main() -> numa::Result<()> {
);
if let Some(ref label) = proxy_label {
row("Proxy", g, label);
if !config.lan.enabled && !dns_is_public && config.proxy.bind_addr == "127.0.0.1" {
let y = "\x1b[38;2;204;176;59m"; // yellow
row(
"",
y,
&format!(
"⚠ proxy on 127.0.0.1 — .{} not LAN reachable",
config.proxy.tld
),
);
}
}
if config.lan.enabled {
row("LAN", g, "mDNS (_numa._tcp.local)");
@@ -375,8 +387,8 @@ async fn main() -> numa::Result<()> {
axum::serve(listener, app).await.unwrap();
});
// Proxy binds 0.0.0.0 when LAN is enabled (cross-machine access), otherwise config value
let proxy_bind: std::net::Ipv4Addr = if config.lan.enabled {
// Proxy binds 0.0.0.0 when LAN is enabled or DNS is already on 0.0.0.0 (cross-machine access)
let proxy_bind: std::net::Ipv4Addr = if config.lan.enabled || dns_is_public {
std::net::Ipv4Addr::UNSPECIFIED
} else {
config