From 860a4b36b4e055379d6278cfd122d4b6bc402b65 Mon Sep 17 00:00:00 2001 From: Han Xu Date: Thu, 2 Apr 2026 22:18:46 -0700 Subject: [PATCH] fix: avoid known-answer suppression when querying on a new interface When a new interface is added, the browse re-query previously sent queries on all interfaces with known answers included. This caused responders to suppress their responses (RFC 6762 Section 7.1), so address records were never attributed to the new interface. The resolve event would be delayed until TTL refresh (~90s). Replace the browse re-execution with a targeted query on only the new interface without known answers. This ensures the responder replies promptly, allowing address records to be attributed to the new interface immediately. --- src/service_daemon.rs | 47 ++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/src/service_daemon.rs b/src/service_daemon.rs index 2994b2b..c3d912e 100644 --- a/src/service_daemon.rs +++ b/src/service_daemon.rs @@ -1886,21 +1886,16 @@ impl Zeroconf { } } - // As we added a new interface, we want to execute all active "Browse" reruns now. - let mut browse_reruns = Vec::new(); - let mut i = 0; - while i < self.retransmissions.len() { - if matches!(self.retransmissions[i].command, Command::Browse(..)) { - browse_reruns.push(self.retransmissions.remove(i)); - } else { - i += 1; + // Send browse queries on the new interface without known answers. + // This avoids known-answer suppression (RFC 6762 Section 7.1) that + // would cause the responder to suppress its response, preventing + // address records from being attributed to the new interface. + if let Some(my_intf) = self.my_intfs.get(&if_index) { + for ty in self.service_queriers.keys() { + self.send_query_on_intf(ty, RRType::PTR, my_intf); } } - for rerun in browse_reruns { - self.exec_command(rerun.command, true); - } - // Notify the monitors. self.notify_monitors(DaemonEvent::IpAdd(intf.ip())); } @@ -2263,6 +2258,34 @@ impl Zeroconf { self.send_query_vec(&[(name, qtype)]); } + /// Sends a query on a specific interface without known answers. + /// + /// Used when a new interface is added so the responder won't suppress + /// its response due to known-answer suppression (RFC 6762 Section 7.1). + fn send_query_on_intf(&self, name: &str, qtype: RRType, intf: &MyIntf) { + let mut out = DnsOutgoing::new(FLAGS_QR_QUERY); + out.add_question(name, qtype); + + let mut invalid_intf_addrs = HashSet::new(); + if let Some(sock) = self.ipv4_sock.as_ref() { + if let Err(InternalError::IntfAddrInvalid(intf_addr)) = + send_dns_outgoing(&out, intf, &sock.pktinfo, self.port) + { + invalid_intf_addrs.insert(intf_addr); + } + } + if let Some(sock) = self.ipv6_sock.as_ref() { + if let Err(InternalError::IntfAddrInvalid(intf_addr)) = + send_dns_outgoing(&out, intf, &sock.pktinfo, self.port) + { + invalid_intf_addrs.insert(intf_addr); + } + } + if !invalid_intf_addrs.is_empty() { + let _ = self.send_cmd_to_self(Command::InvalidIntfAddrs(invalid_intf_addrs)); + } + } + /// Sends out a list of `questions` (i.e. DNS questions) via multicast. fn send_query_vec(&self, questions: &[(&str, RRType)]) { let mut out = DnsOutgoing::new(FLAGS_QR_QUERY);