Add LAN service discovery via UDP multicast #7
@@ -11,12 +11,12 @@ use crate::cache::DnsCache;
|
|||||||
use crate::config::ZoneMap;
|
use crate::config::ZoneMap;
|
||||||
use crate::forward::forward_query;
|
use crate::forward::forward_query;
|
||||||
use crate::header::ResultCode;
|
use crate::header::ResultCode;
|
||||||
|
use crate::lan::PeerStore;
|
||||||
use crate::override_store::OverrideStore;
|
use crate::override_store::OverrideStore;
|
||||||
use crate::packet::DnsPacket;
|
use crate::packet::DnsPacket;
|
||||||
use crate::query_log::{QueryLog, QueryLogEntry};
|
use crate::query_log::{QueryLog, QueryLogEntry};
|
||||||
use crate::question::QueryType;
|
use crate::question::QueryType;
|
||||||
use crate::record::DnsRecord;
|
use crate::record::DnsRecord;
|
||||||
use crate::lan::PeerStore;
|
|
||||||
use crate::service_store::ServiceStore;
|
use crate::service_store::ServiceStore;
|
||||||
use crate::stats::{QueryPath, ServerStats};
|
use crate::stats::{QueryPath, ServerStats};
|
||||||
use crate::system_dns::ForwardingRule;
|
use crate::system_dns::ForwardingRule;
|
||||||
@@ -70,9 +70,7 @@ pub async fn handle_query(
|
|||||||
&& (qname.ends_with(&ctx.proxy_tld_suffix) || qname == ctx.proxy_tld)
|
&& (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: local services → 127.0.0.1, LAN peers → peer IP
|
||||||
let service_name = qname
|
let service_name = qname.strip_suffix(&ctx.proxy_tld_suffix).unwrap_or(&qname);
|
||||||
.strip_suffix(&ctx.proxy_tld_suffix)
|
|
||||||
.unwrap_or(&qname);
|
|
||||||
let resolve_ip = {
|
let resolve_ip = {
|
||||||
let local = ctx.services.lock().unwrap();
|
let local = ctx.services.lock().unwrap();
|
||||||
if local.lookup(service_name).is_some() {
|
if local.lookup(service_name).is_some() {
|
||||||
|
|||||||
33
src/lan.rs
33
src/lan.rs
@@ -27,8 +27,7 @@ impl PeerStore {
|
|||||||
pub fn update(&mut self, host: IpAddr, services: &[(String, u16)]) {
|
pub fn update(&mut self, host: IpAddr, services: &[(String, u16)]) {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
for (name, port) in services {
|
for (name, port) in services {
|
||||||
self.peers
|
self.peers.insert(name.to_lowercase(), (host, *port, now));
|
||||||
.insert(name.to_lowercase(), (host, *port, now));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,11 +43,17 @@ impl PeerStore {
|
|||||||
|
|
||||||
pub fn list(&mut self) -> Vec<(String, IpAddr, u16, u64)> {
|
pub fn list(&mut self) -> Vec<(String, IpAddr, u16, u64)> {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
self.peers.retain(|_, (_, _, seen)| now.duration_since(*seen) < self.timeout);
|
self.peers
|
||||||
|
.retain(|_, (_, _, seen)| now.duration_since(*seen) < self.timeout);
|
||||||
self.peers
|
self.peers
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, (ip, port, seen))| {
|
.map(|(name, (ip, port, seen))| {
|
||||||
(name.clone(), *ip, *port, now.duration_since(*seen).as_secs())
|
(
|
||||||
|
name.clone(),
|
||||||
|
*ip,
|
||||||
|
*port,
|
||||||
|
now.duration_since(*seen).as_secs(),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@@ -81,7 +86,10 @@ pub async fn start_lan_discovery(ctx: Arc<ServerCtx>, config: &LanConfig) {
|
|||||||
let multicast_group: Ipv4Addr = match config.multicast_group.parse() {
|
let multicast_group: Ipv4Addr = match config.multicast_group.parse() {
|
||||||
Ok(g) => g,
|
Ok(g) => g,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("LAN: invalid multicast group {}: {}", config.multicast_group, e);
|
warn!(
|
||||||
|
"LAN: invalid multicast group {}: {}",
|
||||||
|
config.multicast_group, e
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -89,13 +97,19 @@ pub async fn start_lan_discovery(ctx: Arc<ServerCtx>, config: &LanConfig) {
|
|||||||
let interval = Duration::from_secs(config.broadcast_interval_secs);
|
let interval = Duration::from_secs(config.broadcast_interval_secs);
|
||||||
|
|
||||||
let local_ip = detect_lan_ip().unwrap_or(Ipv4Addr::LOCALHOST);
|
let local_ip = detect_lan_ip().unwrap_or(Ipv4Addr::LOCALHOST);
|
||||||
info!("LAN discovery on {}:{}, local IP {}", multicast_group, port, local_ip);
|
info!(
|
||||||
|
"LAN discovery on {}:{}, local IP {}",
|
||||||
|
multicast_group, port, local_ip
|
||||||
|
);
|
||||||
|
|
||||||
// Create socket with SO_REUSEADDR for multicast
|
// Create socket with SO_REUSEADDR for multicast
|
||||||
let std_socket = match create_multicast_socket(multicast_group, port) {
|
let std_socket = match create_multicast_socket(multicast_group, port) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("LAN: could not bind multicast socket: {} — LAN discovery disabled", e);
|
warn!(
|
||||||
|
"LAN: could not bind multicast socket: {} — LAN discovery disabled",
|
||||||
|
e
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -178,10 +192,7 @@ pub async fn start_lan_discovery(ctx: Arc<ServerCtx>, config: &LanConfig) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_multicast_socket(
|
fn create_multicast_socket(group: Ipv4Addr, port: u16) -> std::io::Result<std::net::UdpSocket> {
|
||||||
group: Ipv4Addr,
|
|
||||||
port: u16,
|
|
||||||
) -> std::io::Result<std::net::UdpSocket> {
|
|
||||||
use std::net::SocketAddrV4;
|
use std::net::SocketAddrV4;
|
||||||
|
|
||||||
let addr = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port);
|
let addr = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port);
|
||||||
|
|||||||
@@ -273,7 +273,8 @@ pre .str {{ color: #d48a5a }}
|
|||||||
.path_and_query()
|
.path_and_query()
|
||||||
.map(|pq| pq.as_str())
|
.map(|pq| pq.as_str())
|
||||||
.unwrap_or("/");
|
.unwrap_or("/");
|
||||||
let target_uri: hyper::Uri = format!("http://{}:{}{}", target_host, target_port, path_and_query)
|
let target_uri: hyper::Uri =
|
||||||
|
format!("http://{}:{}{}", target_host, target_port, path_and_query)
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user