From 68503289299a1a868d79cc30c27c714119dc8f47 Mon Sep 17 00:00:00 2001 From: Han Xu Date: Sun, 25 Jan 2026 16:31:50 -0800 Subject: [PATCH 1/4] fix for macOS: exclude IFF_POINTTOPOINT interfaces and exclude Apple P2P by default Support a new API `set_apple_p2p` for "awdl", "llw" interfaces. --- examples/register.rs | 8 ++++++++ src/service_daemon.rs | 48 ++++++++++++++++++++++++++++++++++++++----- tests/mdns_test.rs | 15 +++++++++++++- 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/examples/register.rs b/examples/register.rs index 30ac7711..13b11aa4 100644 --- a/examples/register.rs +++ b/examples/register.rs @@ -26,6 +26,7 @@ fn main() { let mut should_unreg = false; let mut disable_ipv6 = false; let mut use_logfile = false; + let mut include_apple_p2p = false; for arg in args.iter() { if arg.as_str() == "--unregister" { @@ -34,6 +35,8 @@ fn main() { disable_ipv6 = true; } else if arg.as_str() == "--logfile" { use_logfile = true; + } else if arg.as_str() == "--include-apple-p2p" { + include_apple_p2p = true; } } @@ -60,6 +63,10 @@ fn main() { mdns.disable_interface(IfKind::IPv6).unwrap(); } + if include_apple_p2p { + mdns.set_apple_p2p(true).unwrap(); + } + let service_type = match args.get(1) { Some(arg) => format!("{}.local.", arg), None => { @@ -140,6 +147,7 @@ fn print_usage() { println!("--unregister: automatically unregister after 2 seconds"); println!("--disable-ipv6: not to use IPv6 interfaces."); println!("--logfile: write debug log to a file instead of stderr. The logfile is named 'mdns-register-.log'."); + println!("--include-apple-p2p: include Apple p2p interfaces (e.g., awdl, llw) for mDNS."); println!(); println!("For example:"); println!("cargo run --example register _my-hello._udp instance1 host1"); diff --git a/src/service_daemon.rs b/src/service_daemon.rs index 05e05889..0d3be292 100644 --- a/src/service_daemon.rs +++ b/src/service_daemon.rs @@ -570,6 +570,11 @@ impl ServiceDaemon { self.send_cmd(Command::SetOption(DaemonOption::AcceptUnsolicited(accept))) } + /// Enable or disable Apple P2P interfaces, e.g. "awdl0", "llw0". + pub fn set_apple_p2p(&self, enable: bool) -> Result<()> { + self.send_cmd(Command::SetOption(DaemonOption::EnableAppleP2P(enable))) + } + #[cfg(test)] pub fn test_down_interface(&self, ifname: &str) -> Result<()> { self.send_cmd(Command::SetOption(DaemonOption::TestDownInterface( @@ -942,6 +947,8 @@ struct Zeroconf { accept_unsolicited: bool, + include_apple_p2p: bool, + cmd_sender: Sender, signal_addr: SocketAddr, @@ -1118,6 +1125,7 @@ impl Zeroconf { multicast_loop_v4: true, multicast_loop_v6: true, accept_unsolicited: false, + include_apple_p2p: false, cmd_sender, signal_addr, @@ -1335,6 +1343,7 @@ impl Zeroconf { DaemonOption::MulticastLoopV4(on) => self.set_multicast_loop_v4(on), DaemonOption::MulticastLoopV6(on) => self.set_multicast_loop_v6(on), DaemonOption::AcceptUnsolicited(accept) => self.set_accept_unsolicited(accept), + DaemonOption::EnableAppleP2P(enable) => self.set_apple_p2p(enable), #[cfg(test)] DaemonOption::TestDownInterface(ifname) => { self.test_down_interfaces.insert(ifname); @@ -1355,7 +1364,7 @@ impl Zeroconf { }); } - self.apply_intf_selections(my_ip_interfaces(true)); + self.apply_intf_selections(my_ip_interfaces_inner(true, self.include_apple_p2p)); } fn disable_interface(&mut self, kinds: Vec) { @@ -1367,7 +1376,7 @@ impl Zeroconf { }); } - self.apply_intf_selections(my_ip_interfaces(true)); + self.apply_intf_selections(my_ip_interfaces_inner(true, self.include_apple_p2p)); } fn set_multicast_loop_v4(&mut self, on: bool) { @@ -1396,6 +1405,13 @@ impl Zeroconf { self.accept_unsolicited = accept; } + fn set_apple_p2p(&mut self, include: bool) { + if self.include_apple_p2p != include { + self.include_apple_p2p = include; + self.apply_intf_selections(my_ip_interfaces_inner(true, self.include_apple_p2p)); + } + } + fn notify_monitors(&mut self, event: DaemonEvent) { // Only retain the monitors that are still connected. self.monitors.retain(|sender| { @@ -1504,7 +1520,7 @@ impl Zeroconf { /// Check for IP changes and update [my_intfs] as needed. fn check_ip_changes(&mut self) { // Get the current interfaces. - let my_ifaddrs = my_ip_interfaces(true); + let my_ifaddrs = my_ip_interfaces_inner(true, self.include_apple_p2p); #[cfg(test)] let my_ifaddrs: Vec<_> = my_ifaddrs @@ -1800,7 +1816,8 @@ impl Zeroconf { } if info.is_addr_auto() { - let selected_intfs = self.selected_intfs(my_ip_interfaces(true)); + let selected_intfs = + self.selected_intfs(my_ip_interfaces_inner(true, self.include_apple_p2p)); for intf in selected_intfs { info.insert_ipaddr(&intf); } @@ -3853,6 +3870,7 @@ enum DaemonOption { MulticastLoopV4(bool), MulticastLoopV6(bool), AcceptUnsolicited(bool), + EnableAppleP2P(bool), #[cfg(test)] TestDownInterface(String), #[cfg(test)] @@ -3975,13 +3993,33 @@ fn call_hostname_resolution_listener( /// Operational down interfaces are excluded. /// Loopback interfaces are excluded if `with_loopback` is false. fn my_ip_interfaces(with_loopback: bool) -> Vec { + my_ip_interfaces_inner(with_loopback, false) +} + +fn my_ip_interfaces_inner(with_loopback: bool, with_apple_p2p: bool) -> Vec { if_addrs::get_if_addrs() .unwrap_or_default() .into_iter() - .filter(|i| i.is_oper_up() && (!i.is_loopback() || with_loopback)) + .filter(|i| { + i.is_oper_up() + && !is_iff_point_to_point_by_name(&i.name) + && (!i.is_loopback() || with_loopback) + && (with_apple_p2p || !is_apple_p2p_by_name(&i.name)) + }) .collect() } +fn is_apple_p2p_by_name(name: &str) -> bool { + let p2p_prefixes = ["awdl", "llw"]; + p2p_prefixes.iter().any(|prefix| name.starts_with(prefix)) +} + +/// A poor man's check as `if_addrs` does not support IFF_POINTTOPOINT flag yet. +fn is_iff_point_to_point_by_name(name: &str) -> bool { + let p2p_prefixes = ["utun", "tun", "tap", "ppp", "gif", "stf"]; + p2p_prefixes.iter().any(|prefix| name.starts_with(prefix)) +} + /// Send an outgoing mDNS query or response, and returns the packet bytes. /// Returns empty vec if no valid interface address is found. fn send_dns_outgoing( diff --git a/tests/mdns_test.rs b/tests/mdns_test.rs index c75e141e..2f0e965d 100644 --- a/tests/mdns_test.rs +++ b/tests/mdns_test.rs @@ -1243,6 +1243,16 @@ fn instance_name_two_dots() { server_daemon.shutdown().unwrap(); } +fn is_apple_p2p_by_name(name: &str) -> bool { + let p2p_prefixes = ["awdl", "llw"]; + p2p_prefixes.iter().any(|prefix| name.starts_with(prefix)) +} + +fn is_iff_point_to_point_by_name(name: &str) -> bool { + let p2p_prefixes = ["utun", "tun", "tap", "ppp", "gif", "stf"]; + p2p_prefixes.iter().any(|prefix| name.starts_with(prefix)) +} + fn my_ip_interfaces() -> Vec { // Use a random port for binding test. let test_port = fastrand::u16(8000u16..9000u16); @@ -1251,7 +1261,10 @@ fn my_ip_interfaces() -> Vec { .unwrap_or_default() .into_iter() .filter_map(|i| { - if i.is_loopback() { + if i.is_loopback() + || is_iff_point_to_point_by_name(&i.name) + || is_apple_p2p_by_name(&i.name) + { None } else { match &i.addr { From 1b6d8ffbb7d366568fdd04a21fff23d6272f27b3 Mon Sep 17 00:00:00 2001 From: Han Xu Date: Sun, 25 Jan 2026 20:06:40 -0800 Subject: [PATCH 2/4] renaming only --- src/service_daemon.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/service_daemon.rs b/src/service_daemon.rs index 0d3be292..93a755c2 100644 --- a/src/service_daemon.rs +++ b/src/service_daemon.rs @@ -572,7 +572,7 @@ impl ServiceDaemon { /// Enable or disable Apple P2P interfaces, e.g. "awdl0", "llw0". pub fn set_apple_p2p(&self, enable: bool) -> Result<()> { - self.send_cmd(Command::SetOption(DaemonOption::EnableAppleP2P(enable))) + self.send_cmd(Command::SetOption(DaemonOption::IncludeAppleP2P(enable))) } #[cfg(test)] @@ -1343,7 +1343,7 @@ impl Zeroconf { DaemonOption::MulticastLoopV4(on) => self.set_multicast_loop_v4(on), DaemonOption::MulticastLoopV6(on) => self.set_multicast_loop_v6(on), DaemonOption::AcceptUnsolicited(accept) => self.set_accept_unsolicited(accept), - DaemonOption::EnableAppleP2P(enable) => self.set_apple_p2p(enable), + DaemonOption::IncludeAppleP2P(enable) => self.set_apple_p2p(enable), #[cfg(test)] DaemonOption::TestDownInterface(ifname) => { self.test_down_interfaces.insert(ifname); @@ -3870,7 +3870,7 @@ enum DaemonOption { MulticastLoopV4(bool), MulticastLoopV6(bool), AcceptUnsolicited(bool), - EnableAppleP2P(bool), + IncludeAppleP2P(bool), #[cfg(test)] TestDownInterface(String), #[cfg(test)] From 7df2e9c2fdcce5044131dfd4fea2e09e7d3ec1b9 Mon Sep 17 00:00:00 2001 From: Han Xu Date: Sun, 8 Feb 2026 20:07:44 -0800 Subject: [PATCH 3/4] use if-addrs v0.15 --- Cargo.toml | 2 +- examples/register.rs | 2 +- src/service_daemon.rs | 19 +++++++++---------- src/service_info.rs | 7 +++++++ 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 28476214..1f00ec4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ default = ["reuseport", "async", "logging"] [dependencies] fastrand = "2.3" flume = { version = "0.11", default-features = false } # channel between threads -if-addrs = { version = "0.14", features = ["link-local"] } # get local IP addresses +if-addrs = { version = "0.15", features = ["link-local"] } # get local IP addresses log = { version = "0.4", optional = true } # logging mio = { version = "1.1", features = ["os-poll", "net"] } # select/poll sockets socket2 = { version = "0.6", features = ["all"] } # socket APIs diff --git a/examples/register.rs b/examples/register.rs index 13b11aa4..612123e3 100644 --- a/examples/register.rs +++ b/examples/register.rs @@ -64,7 +64,7 @@ fn main() { } if include_apple_p2p { - mdns.set_apple_p2p(true).unwrap(); + mdns.include_apple_p2p(true).unwrap(); } let service_type = match args.get(1) { diff --git a/src/service_daemon.rs b/src/service_daemon.rs index 6d618871..129cfbe0 100644 --- a/src/service_daemon.rs +++ b/src/service_daemon.rs @@ -570,9 +570,10 @@ impl ServiceDaemon { self.send_cmd(Command::SetOption(DaemonOption::AcceptUnsolicited(accept))) } - /// Enable or disable Apple P2P interfaces, e.g. "awdl0", "llw0". - pub fn set_apple_p2p(&self, enable: bool) -> Result<()> { - self.send_cmd(Command::SetOption(DaemonOption::IncludeAppleP2P(enable))) + /// Include or exclude Apple P2P interfaces, e.g. "awdl0", "llw0". + /// By default, they are excluded. + pub fn include_apple_p2p(&self, include: bool) -> Result<()> { + self.send_cmd(Command::SetOption(DaemonOption::IncludeAppleP2P(include))) } #[cfg(test)] @@ -4071,24 +4072,20 @@ fn my_ip_interfaces_inner(with_loopback: bool, with_apple_p2p: bool) -> Vec bool { let p2p_prefixes = ["awdl", "llw"]; p2p_prefixes.iter().any(|prefix| name.starts_with(prefix)) } -/// A poor man's check as `if_addrs` does not support IFF_POINTTOPOINT flag yet. -fn is_iff_point_to_point_by_name(name: &str) -> bool { - let p2p_prefixes = ["utun", "tun", "tap", "ppp", "gif", "stf"]; - p2p_prefixes.iter().any(|prefix| name.starts_with(prefix)) -} - /// Send an outgoing mDNS query or response, and returns the packet bytes. /// Returns empty vec if no valid interface address is found. fn send_dns_outgoing( @@ -4154,6 +4151,7 @@ fn send_dns_outgoing_impl( addr: if_addr.clone(), index: Some(if_index), oper_status: if_addrs::IfOperStatus::Down, + is_p2p: false, #[cfg(windows)] adapter_name: String::new(), }; @@ -4175,6 +4173,7 @@ fn send_dns_outgoing_impl( addr: if_addr.clone(), index: Some(if_index), oper_status: if_addrs::IfOperStatus::Down, + is_p2p: false, #[cfg(windows)] adapter_name: String::new(), }; diff --git a/src/service_info.rs b/src/service_info.rs index 00f86dba..a2dbfb17 100644 --- a/src/service_info.rs +++ b/src/service_info.rs @@ -1547,6 +1547,7 @@ mod tests { prefixlen: 16, }), oper_status: IfOperStatus::Up, + is_p2p: false, #[cfg(windows)] adapter_name: String::new(), }; @@ -1561,6 +1562,7 @@ mod tests { prefixlen: 16, }), oper_status: IfOperStatus::Up, + is_p2p: false, #[cfg(windows)] adapter_name: String::new(), }; @@ -1575,6 +1577,7 @@ mod tests { prefixlen: 16, }), oper_status: IfOperStatus::Up, + is_p2p: false, #[cfg(windows)] adapter_name: String::new(), }; @@ -1589,6 +1592,7 @@ mod tests { prefixlen: 16, }), oper_status: IfOperStatus::Up, + is_p2p: false, #[cfg(windows)] adapter_name: String::new(), }; @@ -1605,6 +1609,7 @@ mod tests { prefixlen: 16, }), oper_status: IfOperStatus::Up, + is_p2p: false, #[cfg(windows)] adapter_name: String::new(), }; @@ -1619,6 +1624,7 @@ mod tests { prefixlen: 16, }), oper_status: IfOperStatus::Up, + is_p2p: false, #[cfg(windows)] adapter_name: String::new(), }; @@ -1633,6 +1639,7 @@ mod tests { prefixlen: 16, }), oper_status: IfOperStatus::Up, + is_p2p: false, #[cfg(windows)] adapter_name: String::new(), }; From 8ac78e025d9060abaf75fda01751d9ea41f397b6 Mon Sep 17 00:00:00 2001 From: Han Xu Date: Sun, 8 Feb 2026 20:10:42 -0800 Subject: [PATCH 4/4] update test --- tests/mdns_test.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/tests/mdns_test.rs b/tests/mdns_test.rs index 3f51eab5..d618f5f2 100644 --- a/tests/mdns_test.rs +++ b/tests/mdns_test.rs @@ -1178,11 +1178,6 @@ fn is_apple_p2p_by_name(name: &str) -> bool { p2p_prefixes.iter().any(|prefix| name.starts_with(prefix)) } -fn is_iff_point_to_point_by_name(name: &str) -> bool { - let p2p_prefixes = ["utun", "tun", "tap", "ppp", "gif", "stf"]; - p2p_prefixes.iter().any(|prefix| name.starts_with(prefix)) -} - fn my_ip_interfaces() -> Vec { // Use a random port for binding test. let test_port = fastrand::u16(8000u16..9000u16); @@ -1191,10 +1186,7 @@ fn my_ip_interfaces() -> Vec { .unwrap_or_default() .into_iter() .filter_map(|i| { - if i.is_loopback() - || is_iff_point_to_point_by_name(&i.name) - || is_apple_p2p_by_name(&i.name) - { + if i.is_loopback() || i.is_p2p() || is_apple_p2p_by_name(&i.name) { None } else { match &i.addr {