mod if_nametoindex;
mod protocol;
pub mod service;
use std::{fmt, io, net::IpAddr};
#[cfg(feature = "dquic-network")]
use std::{net::SocketAddr, sync::Arc};
#[cfg(feature = "dquic-network")]
use dquic::qresolve::RecordStream;
use dquic::{
qbase::net::Family,
qresolve::{Publish, PublishFuture, Resolve, ResolveFuture, Source},
};
use futures::{FutureExt, StreamExt, TryFutureExt, future, stream};
#[cfg(feature = "dquic-network")]
use futures::{Stream, stream::FuturesUnordered};
#[cfg(feature = "dquic-network")]
use self::protocol::MdnsProtocol;
#[cfg(feature = "dquic-network")]
use crate::core::parser::packet::Packet;
use crate::core::parser::record::RData;
pub type MdnsResolver = service::Mdns;
pub type MdnsPublisher = service::Mdns;
impl MdnsResolver {
pub fn source(&self) -> Source {
Source::Mdns {
nic: self.bound_nic().into(),
family: match self.bound_ip() {
IpAddr::V4(..) => Family::V4,
IpAddr::V6(..) => Family::V6,
},
}
}
}
impl fmt::Display for MdnsResolver {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.source(), f)
}
}
impl Publish for MdnsPublisher {
fn publish<'a>(&'a self, name: &'a str, packet: &'a [u8]) -> PublishFuture<'a> {
let endpoints = match endpoints_from_packet(packet) {
Ok(endpoints) => endpoints,
Err(error) => return future::ready(Err(error)).boxed(),
};
self.insert_host(name.to_string(), endpoints);
future::ready(Ok(())).boxed()
}
}
impl Resolve for MdnsResolver {
fn lookup<'l>(&'l self, name: &'l str) -> ResolveFuture<'l> {
let source = self.source();
self.query(name.to_owned())
.map_ok(move |list| {
let endpoints = crate::resolvers::endpoint_group::selected_endpoint_addrs(list);
stream::iter(endpoints.into_iter().map(move |ep| (source.clone(), ep))).boxed()
})
.boxed()
}
}
fn endpoints_from_packet(packet: &[u8]) -> io::Result<Vec<crate::core::MdnsEndpoint>> {
use crate::core::parser::packet::be_packet;
be_packet(packet)
.map(|(_, pkt)| {
pkt.answers
.iter()
.filter_map(|rr| match rr.data() {
RData::E(ep) => Some(ep.clone()),
_ => None,
})
.collect::<Vec<_>>()
})
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error.to_string()))
}
#[cfg(feature = "dquic-network")]
pub struct MdnsBindDriver {
iface_manager: Arc<h3x::dquic::net::InterfaceManager>,
null_io_factory: Arc<h3x::dquic::NullIoFactory>,
service_name: Arc<str>,
}
#[cfg(feature = "dquic-network")]
impl MdnsBindDriver {
pub fn new(service_name: impl Into<Arc<str>>) -> Self {
Self {
iface_manager: Arc::new(h3x::dquic::net::InterfaceManager::new()),
null_io_factory: Arc::new(h3x::dquic::NullIoFactory),
service_name: service_name.into(),
}
}
fn install_or_rebind_mdns(
&self,
network: &h3x::dquic::Network,
bind_iface: &h3x::dquic::net::BindInterface,
) {
let bind_uri = bind_iface.bind_uri();
let Some((family, device, _port)) = bind_uri.as_iface_bind_uri() else {
tracing::debug!(%bind_uri, "skipping mdns binding for non-interface bind uri");
return;
};
let Some(ip) = network.resolve_device_addr(device, family) else {
tracing::debug!(%bind_uri, "skipping mdns binding without local interface address");
return;
};
bind_iface.with_components_mut(|components, _iface| {
match components.try_init_with(|| service::Mdns::new(&self.service_name, ip, device)) {
Ok(mdns) => mdns.reinit_on(device, ip),
Err(error) => {
let report = snafu::Report::from_error(&error);
tracing::debug!(error = %report, %bind_uri, "failed to initialize mdns binding");
}
}
});
}
}
#[cfg(feature = "dquic-network")]
impl h3x::dquic::BindDriver for MdnsBindDriver {
fn bind<'a>(
&'a self,
network: &'a h3x::dquic::Network,
uri: h3x::dquic::net::BindUri,
) -> futures::future::BoxFuture<'a, h3x::dquic::net::BindInterface> {
async move {
let iface = self
.iface_manager
.bind(uri, self.null_io_factory.clone())
.await;
self.install_or_rebind_mdns(network, &iface);
iface
}
.boxed()
}
fn rebind<'a>(
&'a self,
network: &'a h3x::dquic::Network,
iface: &'a h3x::dquic::net::BindInterface,
) -> futures::future::BoxFuture<'a, ()> {
async move {
self.install_or_rebind_mdns(network, iface);
}
.boxed()
}
}
#[cfg(feature = "dquic-network")]
pub struct MdnsResolvers {
network: Arc<h3x::dquic::Network>,
driver: Arc<MdnsBindDriver>,
patterns: Arc<Vec<h3x::dquic::binds::BindPattern>>,
_handles: Vec<h3x::dquic::BindHandle>,
}
#[cfg(feature = "dquic-network")]
#[derive(Debug, Clone)]
pub struct BoundMdnsResolver {
pub device: String,
pub family: Family,
pub resolver: MdnsResolver,
}
#[cfg(feature = "dquic-network")]
impl fmt::Debug for MdnsResolvers {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MdnsResolvers")
.field("patterns", &self.patterns)
.finish_non_exhaustive()
}
}
#[cfg(feature = "dquic-network")]
impl fmt::Display for MdnsResolvers {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("mDNS resolvers")
}
}
#[cfg(feature = "dquic-network")]
impl MdnsResolvers {
pub async fn bind(
network: Arc<h3x::dquic::Network>,
patterns: Arc<Vec<h3x::dquic::binds::BindPattern>>,
service_name: impl Into<Arc<str>>,
) -> Self {
let driver = Arc::new(MdnsBindDriver::new(service_name));
let mut handles = Vec::with_capacity(patterns.len());
for pattern in patterns.iter() {
handles.push(network.bind_with(driver.clone(), pattern.clone()).await);
}
Self {
network,
driver,
patterns,
_handles: handles,
}
}
pub fn bound_interfaces(
&self,
pattern: &h3x::dquic::binds::BindPattern,
) -> Option<Vec<h3x::dquic::net::BindInterface>> {
self.network.get_interfaces_with(&self.driver, pattern)
}
fn for_each_resolver(&self, mut f: impl FnMut(&MdnsResolver)) {
for pattern in self.patterns.iter() {
let Some(ifaces) = self.bound_interfaces(pattern) else {
continue;
};
for iface in ifaces {
iface.with_components(|components, _| {
if let Some(mdns) = components.get::<MdnsResolver>() {
f(mdns);
}
});
}
}
}
pub fn bound_resolvers(&self) -> Vec<BoundMdnsResolver> {
let mut resolvers = Vec::new();
for pattern in self.patterns.iter() {
let Some(ifaces) = self.bound_interfaces(pattern) else {
continue;
};
for iface in ifaces {
let bind_uri = iface.bind_uri();
let Some((family, device, _port)) = bind_uri.as_iface_bind_uri() else {
continue;
};
iface.with_components(|components, _| {
if let Some(resolver) = components.get::<MdnsResolver>() {
resolvers.push(BoundMdnsResolver {
device: device.to_owned(),
family,
resolver: resolver.clone(),
});
}
});
}
}
resolvers
}
pub async fn query(&self, name: &str) -> io::Result<RecordStream> {
let mut lookup_futures = FuturesUnordered::new();
let mut has_resolver = false;
self.for_each_resolver(|resolver| {
has_resolver = true;
let source = resolver.source();
lookup_futures.push(
resolver
.query(name.to_owned())
.map_ok(move |eps| (source, eps)),
);
});
if !has_resolver {
return Err(io::Error::other("no mdns resolvers available"));
}
let mut last_error = None;
let mut has_success = false;
let mut records = Vec::new();
while let Some(result) = lookup_futures.next().await {
match result {
Ok((source, endpoints)) => {
has_success = true;
records.extend(
endpoints
.into_iter()
.map(|endpoint| (source.clone(), endpoint)),
);
}
Err(error) => last_error = Some(error),
}
}
if !has_success {
return Err(
last_error.unwrap_or_else(|| io::Error::other("no mdns resolvers available"))
);
}
let records = crate::resolvers::endpoint_group::selected_endpoint_records(records);
Ok(stream::iter(records).boxed())
}
pub fn discover(&self) -> impl Stream<Item = (SocketAddr, Packet)> + use<> {
let mut protos = Vec::new();
self.for_each_resolver(|resolver| {
protos.push(resolver.protocol());
});
async fn receive_one(
proto: Arc<MdnsProtocol>,
) -> Option<((SocketAddr, Packet), Arc<MdnsProtocol>)> {
let result = proto.receive_boardcast().await.ok()?;
Some((result, proto))
}
let mut pending = protos
.into_iter()
.map(receive_one)
.collect::<FuturesUnordered<_>>();
Box::pin(stream::poll_fn(move |cx| {
use std::task::Poll;
loop {
match pending.poll_next_unpin(cx) {
Poll::Ready(Some(Some((item, proto)))) => {
pending.push(receive_one(proto));
return Poll::Ready(Some(item));
}
Poll::Ready(Some(None)) => continue,
Poll::Ready(None) => return Poll::Ready(None),
Poll::Pending => return Poll::Pending,
}
}
}))
}
}
#[cfg(feature = "dquic-network")]
impl Publish for MdnsResolvers {
fn publish<'a>(&'a self, name: &'a str, packet: &'a [u8]) -> PublishFuture<'a> {
let endpoints = match endpoints_from_packet(packet) {
Ok(endpoints) => endpoints,
Err(error) => return future::ready(Err(error)).boxed(),
};
self.for_each_resolver(|resolver| {
resolver.insert_host(name.to_string(), endpoints.clone());
});
future::ready(Ok(())).boxed()
}
}
#[cfg(feature = "dquic-network")]
impl Resolve for MdnsResolvers {
fn lookup<'l>(&'l self, name: &'l str) -> ResolveFuture<'l> {
self.query(name).boxed()
}
}