From ed1f7a4c83d7997d8aa084e9cde49ff335a6cb69 Mon Sep 17 00:00:00 2001 From: Marcus Hanestad Date: Fri, 19 Jun 2026 16:04:26 +0200 Subject: [PATCH 1/2] deduplicate interface kind matching function --- src/service_daemon.rs | 2 +- src/service_info.rs | 13 +------------ 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/service_daemon.rs b/src/service_daemon.rs index f6d148f..02770c7 100644 --- a/src/service_daemon.rs +++ b/src/service_daemon.rs @@ -909,7 +909,7 @@ pub enum IfKind { impl IfKind { /// Checks if `intf` matches with this interface kind. - fn matches(&self, intf: &Interface) -> bool { + pub(crate) fn matches(&self, intf: &Interface) -> bool { match self { Self::All => true, Self::IPv4 => intf.ip().is_ipv4(), diff --git a/src/service_info.rs b/src/service_info.rs index b905857..48a7cc0 100644 --- a/src/service_info.rs +++ b/src/service_info.rs @@ -506,19 +506,8 @@ impl ServiceInfo { } fn is_address_supported(&self, intf: &Interface) -> bool { + let interface_supported = self.supported_intfs.iter().any(|i| i.matches(intf)); let addr = intf.ip(); - let interface_supported = self.supported_intfs.iter().any(|i| match i { - IfKind::Name(name) => *name == intf.name, - IfKind::IPv4 => addr.is_ipv4(), - IfKind::IPv6 => addr.is_ipv6(), - IfKind::Addr(a) => *a == addr, - IfKind::LoopbackV4 => matches!(addr, IpAddr::V4(ipv4) if ipv4.is_loopback()), - IfKind::LoopbackV6 => matches!(addr, IpAddr::V6(ipv6) if ipv6.is_loopback()), - IfKind::IndexV4(idx) => intf.index == Some(*idx) && addr.is_ipv4(), - IfKind::IndexV6(idx) => intf.index == Some(*idx) && addr.is_ipv6(), - IfKind::All => true, - }); - let passes_link_local = !self.is_link_local_only || match &addr { IpAddr::V4(ipv4) => ipv4.is_link_local(), From 7523ce8c0603b6c302938cf32940d19bf096eb39 Mon Sep 17 00:00:00 2001 From: Marcus Hanestad Date: Fri, 19 Jun 2026 16:15:06 +0200 Subject: [PATCH 2/2] add `IfPredicate` for more flexible interface filtering --- src/lib.rs | 6 +++--- src/service_daemon.rs | 33 +++++++++++++++++++++++++++++++++ src/service_info.rs | 10 +++++++++- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b46b491..2ffb7e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -180,9 +180,9 @@ mod service_info; pub use dns_parser::{InterfaceId, RRType, ScopedIp, ScopedIpV4, ScopedIpV6}; pub use error::{Error, Result}; pub use service_daemon::{ - DaemonEvent, DaemonStatus, DnsNameChange, HostnameResolutionEvent, IfKind, Metrics, - ServiceDaemon, ServiceEvent, UnregisterStatus, IP_CHECK_INTERVAL_IN_SECS_DEFAULT, MDNS_PORT, - SERVICE_NAME_LEN_MAX_DEFAULT, VERIFY_TIMEOUT_DEFAULT, + DaemonEvent, DaemonStatus, DnsNameChange, HostnameResolutionEvent, IfKind, IfPredicate, + Metrics, ServiceDaemon, ServiceEvent, UnregisterStatus, IP_CHECK_INTERVAL_IN_SECS_DEFAULT, + MDNS_PORT, SERVICE_NAME_LEN_MAX_DEFAULT, VERIFY_TIMEOUT_DEFAULT, }; pub use service_info::{ AsIpAddrs, IntoTxtProperties, ResolvedService, ServiceInfo, TxtProperties, TxtProperty, diff --git a/src/service_daemon.rs b/src/service_daemon.rs index 02770c7..1182a66 100644 --- a/src/service_daemon.rs +++ b/src/service_daemon.rs @@ -905,6 +905,9 @@ pub enum IfKind { /// By interface index, IPv6 only. IndexV6(u32), + + /// By a custom rule + Predicate(IfPredicate), } impl IfKind { @@ -920,6 +923,7 @@ impl IfKind { Self::LoopbackV6 => intf.is_loopback() && intf.ip().is_ipv6(), Self::IndexV4(idx) => intf.index == Some(*idx) && intf.ip().is_ipv4(), Self::IndexV6(idx) => intf.index == Some(*idx) && intf.ip().is_ipv6(), + Self::Predicate(p) => p.matches(intf), } } } @@ -971,6 +975,35 @@ impl> IntoIfKindVec for Vec { } } +/// A predicate function for matching against interfaces. +#[derive(Clone)] +pub struct IfPredicate(std::sync::Arc bool + Send + Sync>); + +impl IfPredicate { + /// Wraps the given predicate function. + /// + /// # Example + /// + /// ```no_run + /// # use mdns_sd::IfPredicate; + /// // Match any interface that doesn't look like a virtual bridge + /// IfPredicate::new(|intf| !intf.name.starts_with("virbr")); + /// ``` + pub fn new(predicate: impl Fn(&Interface) -> bool + Send + Sync + 'static) -> Self { + Self(std::sync::Arc::new(predicate)) + } + + pub(crate) fn matches(&self, intf: &Interface) -> bool { + self.0(intf) + } +} + +impl std::fmt::Debug for IfPredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "IfPredicate(...)") + } +} + /// Selection of interfaces. struct IfSelection { /// The interfaces to be selected. diff --git a/src/service_info.rs b/src/service_info.rs index 48a7cc0..6693efe 100644 --- a/src/service_info.rs +++ b/src/service_info.rs @@ -1405,7 +1405,7 @@ impl ResolvedService { #[cfg(test)] mod tests { use super::{decode_txt, encode_txt, u8_slice_to_hex, ServiceInfo, TxtProperty}; - use crate::IfKind; + use crate::{IfKind, IfPredicate}; use if_addrs::{IfAddr, IfOperStatus, Ifv4Addr, Ifv6Addr, Interface}; use std::net::{Ipv4Addr, Ipv6Addr}; @@ -1757,6 +1757,14 @@ mod tests { service_info.set_interfaces(vec![IfKind::LoopbackV6]); assert!(!service_info.is_address_supported(&intf_loopback_v4)); assert!(service_info.is_address_supported(&intf_loopback_v6)); + + // supported interfaces: IPv4 and name = "foo" + service_info.set_interfaces(vec![IfKind::Predicate(IfPredicate::new(|intf| { + intf.ip().is_ipv4() && intf.name == "foo" + }))]); + assert!(service_info.is_address_supported(&intf_loopback_v4)); + assert!(!service_info.is_address_supported(&intf_v4)); + assert!(!service_info.is_address_supported(&intf_loopback_v6)); } #[test]