Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
35 changes: 34 additions & 1 deletion src/service_daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -905,11 +905,14 @@ pub enum IfKind {

/// By interface index, IPv6 only.
IndexV6(u32),

/// By a custom rule
Predicate(IfPredicate),
}

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(),
Expand All @@ -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),
}
}
}
Expand Down Expand Up @@ -971,6 +975,35 @@ impl<T: Into<IfKind>> IntoIfKindVec for Vec<T> {
}
}

/// A predicate function for matching against interfaces.
#[derive(Clone)]
pub struct IfPredicate(std::sync::Arc<dyn Fn(&Interface) -> bool + Send + Sync>);
Comment thread
keepsimple1 marked this conversation as resolved.

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.
Expand Down
23 changes: 10 additions & 13 deletions src/service_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Comment thread
keepsimple1 marked this conversation as resolved.
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(),
Expand Down Expand Up @@ -1416,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};

Expand Down Expand Up @@ -1768,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]
Expand Down