fix(mdns): retain origin and bound daemon state

Vendor the pinned mdns-sd 0.21.1 source so response records retain their observed source IP. Bound unauthenticated cache records to 1024 globally and 128 per source, deduplicate and cap timers at 4096, and poll at least once per second for expiry cleanup.

Expose packet provenance through lanspread-mdns, drop originless resolutions, and add a dedicated vendor test recipe while keeping third-party sources outside workspace formatting and Clippy.

Test Plan:
- just mdns-vendor-test (106 tests passed with socket access)
- just test
- just fmt
- just clippy
- cache/per-source, exact-refresh, timer-cap, and serde tests
- git diff --check
This commit is contained in:
ddidderr committed 2026-09-12 13:08:26 +02:00
1 parent 49f8eef7b5
commit 4d5881d6b0
23 files changed
+18879 -41

No files matched your search

+9
View File
@@ -149,6 +149,9 @@ pub struct MdnsBrowser {
#[derive(Debug, Clone)]
pub struct MdnsService {
pub addr: SocketAddr,
/// IP address observed on the response datagram. This is independent of
/// the attacker-controlled A/AAAA target in `addr`.
pub source_ip: std::net::IpAddr,
pub fullname: String,
pub hostname: String,
pub properties: HashMap<String, String>,
@@ -260,6 +263,11 @@ impl MdnsBrowser {
) -> Option<MdnsService> {
log::trace!("mdns ServiceResolved event: {info:?}");
let Some(source_ip) = info.get_observed_source() else {
log::debug!("Ignoring mDNS resolution without an observed response source");
return None;
};
if info.ty_domain != self.service_type {
log::trace!(
"Got mDNS with uninteresting service type: {} (expected: {})",
@@ -283,6 +291,7 @@ impl MdnsBrowser {
let properties = info.get_properties().clone().into_property_map_str();
return Some(MdnsService {
addr,
source_ip,
fullname: info.get_fullname().to_string(),
hostname: info.get_hostname().to_string(),
properties,