Skip to content

Commit 5bb4464

Browse files
committed
fix(discovery): cache primary IP by service instance, not SRV target
The dialer's `primary_hints` lookup keys on the configured `hostname` ("JKMBP-M4-Max.local"), but the cache was being populated with the SRV target hostname returned by `ServiceInfo::get_hostname()`. macOS will sometimes appear in mDNS-SD with a suffixed system hostname ("JKMBP-M4-Max-2.local") for the SRV record while the service-instance label keeps the user-visible identifier ("JKMBP-M4-Max.local") — those two names are advertised together but mdns-sd resolves only one SRV target into the event, so the cache key drifted to a name the config never references and `preferred` came back None. Switch the cache key to the service-instance label, parsed off the fullname's `.<SERVICE_TYPE>` suffix. The label is what users put in their config (the announcer derives it from the same `local_hostname()` on registration) and it's stable across SRV-target variations. Log line now shows both fields so future hostname/target mismatches are visible without a packet capture: mdns: peer instance=jkmbp-m4-max.local (target=jkmbp-m4-max-2.local) ...
1 parent 0a636ed commit 5bb4464

1 file changed

Lines changed: 25 additions & 5 deletions

File tree

src/discovery.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,22 @@ fn strip_trailing_dot(s: &str) -> &str {
6565
s.strip_suffix('.').unwrap_or(s)
6666
}
6767

68+
/// Pull the service-instance label off a Bonjour fullname.
69+
///
70+
/// `mdns-sd` returns fullnames as `"<instance>.<service-type>"` where
71+
/// `<service-type>` is e.g. `"_lan-mouse._udp.local."`. The instance
72+
/// label is the user-visible identifier the announcer chose for itself
73+
/// — typically the system hostname, and the same string the user puts
74+
/// in their lan-mouse config's `hostname = "..."`. We key
75+
/// [`PrimaryCache`] on this instead of the SRV target so the dialer
76+
/// matches the config hostname even when the announcer's SRV target
77+
/// has macOS-style suffixes (`Foo.local` vs `Foo-2.local`) or other
78+
/// drift.
79+
fn instance_from_fullname<'a>(fullname: &'a str, service_type: &str) -> &'a str {
80+
let suffix = format!(".{service_type}");
81+
fullname.strip_suffix(&suffix).unwrap_or(fullname)
82+
}
83+
6884
/// Shared `peer_hostname -> primary_ipv4` map, populated by Discovery
6985
/// and read by the dialer (`connect_to_handle`). Owned by the dialer
7086
/// path so its references survive across discovery enable/disable
@@ -291,16 +307,20 @@ fn start_browse(
291307
let Ok(ip) = primary_str.parse::<IpAddr>() else {
292308
log::debug!(
293309
"mdns: peer {} advertised malformed primary={primary_str:?}",
294-
resolved.get_hostname()
310+
resolved.get_fullname()
295311
);
296312
continue;
297313
};
298-
let host = strip_trailing_dot(resolved.get_hostname()).to_ascii_lowercase();
314+
let instance =
315+
instance_from_fullname(resolved.get_fullname(), SERVICE_TYPE);
316+
let key = strip_trailing_dot(instance).to_ascii_lowercase();
317+
let target = strip_trailing_dot(resolved.get_hostname());
299318
log::info!(
300-
"mdns: peer {host} announces primary={ip} (port={})",
301-
resolved.get_port()
319+
"mdns: peer instance={key} (target={target}) announces primary={ip} \
320+
(port={port})",
321+
port = resolved.get_port(),
302322
);
303-
primary_cache.borrow_mut().insert(host, ip);
323+
primary_cache.borrow_mut().insert(key, ip);
304324
}
305325
ServiceEvent::ServiceRemoved(_, fullname) => {
306326
// Best-effort: the fullname is "<instance>._lan-

0 commit comments

Comments
 (0)