Skip to main content

rs_matter_stack/
mdns.rs

1use core::fmt::Debug;
2use core::future::Future;
3use core::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6};
4
5use edge_nal::{UdpBind, UdpSplit};
6
7use rs_matter::crypto::Crypto;
8use rs_matter::error::{Error, ErrorCode};
9use rs_matter::transport::network::mdns::builtin::BuiltinMdns;
10use rs_matter::Matter;
11
12use crate::udp;
13
14/// A trait for running an mDNS responder.
15pub trait Mdns {
16    /// Run the mDNS responder with the given UDP binding, MAC address, IPv4 and IPv6 addresses, and interface index.
17    ///
18    /// NOTE: This trait might change once `rs-matter` starts supporting mDNS resolvers
19    ///
20    /// # Arguments
21    /// - `matter`: A reference to the `Matter` instance.
22    /// - `crypto`: An object implementing the `Crypto` trait for cryptographic operations.
23    /// - `udp`: An object implementing the `UdpBind` trait for binding UDP sockets.
24    /// - `mac`: The MAC address of the host, used to generate the hostname.
25    /// - `ipv4`: The IPv4 address of the host.
26    /// - `ipv6`: The IPv6 address of the host.
27    /// - `interface`: The interface index for the host, used for IPv6 multicast.
28    #[allow(clippy::too_many_arguments)]
29    async fn run<C, U>(
30        &mut self,
31        matter: &Matter<'_>,
32        crypto: C,
33        udp: U,
34        mac: &[u8],
35        ipv4: Ipv4Addr,
36        ipv6: Ipv6Addr,
37        interface: u32,
38    ) -> Result<(), Error>
39    where
40        C: Crypto,
41        U: UdpBind;
42}
43
44impl<T> Mdns for &mut T
45where
46    T: Mdns,
47{
48    fn run<C, U>(
49        &mut self,
50        matter: &Matter<'_>,
51        crypto: C,
52        udp: U,
53        mac: &[u8],
54        ipv4: Ipv4Addr,
55        ipv6: Ipv6Addr,
56        interface: u32,
57    ) -> impl Future<Output = Result<(), Error>>
58    where
59        C: Crypto,
60        U: UdpBind,
61    {
62        (*self).run(matter, crypto, udp, mac, ipv4, ipv6, interface)
63    }
64}
65
66impl Mdns for BuiltinMdns {
67    async fn run<C, U>(
68        &mut self,
69        matter: &Matter<'_>,
70        crypto: C,
71        udp: U,
72        mac: &[u8],
73        ipv4: Ipv4Addr,
74        ipv6: Ipv6Addr,
75        interface: u32,
76    ) -> Result<(), Error>
77    where
78        C: Crypto,
79        U: UdpBind,
80    {
81        use core::fmt::Write as _;
82
83        use edge_nal::{MulticastV4, MulticastV6};
84
85        use rs_matter::transport::network::mdns::builtin::Host;
86        use rs_matter::transport::network::mdns::{
87            MDNS_IPV4_BROADCAST_ADDR, MDNS_IPV6_BROADCAST_ADDR, MDNS_PORT,
88        };
89
90        let mut socket = udp
91            .bind(SocketAddr::V6(SocketAddrV6::new(
92                Ipv6Addr::UNSPECIFIED,
93                MDNS_PORT,
94                0,
95                0,
96            )))
97            .await
98            .map_err(map_err)?;
99
100        socket
101            .join_v4(MDNS_IPV4_BROADCAST_ADDR, ipv4)
102            .await
103            .map_err(map_err)?;
104
105        socket
106            .join_v6(MDNS_IPV6_BROADCAST_ADDR, interface)
107            .await
108            .map_err(map_err)?;
109
110        let (recv, send) = socket.split();
111
112        let mut hostname = heapless::String::<16>::new();
113        if mac.len() == 6 {
114            write_unwrap!(
115                hostname,
116                "{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}",
117                mac[0],
118                mac[1],
119                mac[2],
120                mac[3],
121                mac[4],
122                mac[5]
123            );
124        } else if mac.len() == 8 {
125            write_unwrap!(
126                hostname,
127                "{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}",
128                mac[0],
129                mac[1],
130                mac[2],
131                mac[3],
132                mac[4],
133                mac[5],
134                mac[6],
135                mac[7]
136            );
137        } else {
138            panic!("Invalid MAC address length: should be 6 or 8 bytes");
139        }
140
141        BuiltinMdns::run(
142            self,
143            udp::Udp(send),
144            udp::Udp(recv),
145            &Host {
146                hostname: &hostname,
147                ip: ipv4,
148                ipv6,
149            },
150            Some(ipv4),
151            Some(interface),
152            matter,
153            crypto,
154        )
155        .await
156    }
157}
158
159#[cfg(feature = "zbus")]
160impl Mdns for rs_matter::transport::network::mdns::avahi::AvahiMdns {
161    fn run<C, U>(
162        &mut self,
163        matter: &Matter<'_>,
164        _crypto: C,
165        _udp: U,
166        _mac: &[u8],
167        _ipv4: Ipv4Addr,
168        _ipv6: Ipv6Addr,
169        _interface: u32,
170    ) -> impl Future<Output = Result<(), Error>>
171    where
172        C: Crypto,
173        U: UdpBind,
174    {
175        Self::run(self, matter)
176    }
177}
178
179#[cfg(feature = "zbus")]
180impl Mdns for rs_matter::transport::network::mdns::resolve::ResolveMdns {
181    fn run<C, U>(
182        &mut self,
183        matter: &Matter<'_>,
184        _crypto: C,
185        _udp: U,
186        _mac: &[u8],
187        _ipv4: Ipv4Addr,
188        _ipv6: Ipv6Addr,
189        _interface: u32,
190    ) -> impl Future<Output = Result<(), Error>>
191    where
192        C: Crypto,
193        U: UdpBind,
194    {
195        Self::run(self, matter)
196    }
197}
198
199#[cfg(feature = "zeroconf")]
200impl Mdns for rs_matter::transport::network::mdns::zeroconf::ZeroconfMdns {
201    fn run<C, U>(
202        &mut self,
203        matter: &Matter<'_>,
204        _crypto: C,
205        _udp: U,
206        _mac: &[u8],
207        _ipv4: Ipv4Addr,
208        _ipv6: Ipv6Addr,
209        _interface: u32,
210    ) -> impl Future<Output = Result<(), Error>>
211    where
212        C: Crypto,
213        U: UdpBind,
214    {
215        Self::run(self, matter)
216    }
217}
218
219/// An mDNS responder for Matter using the `astro-dnssd` crate.
220#[cfg(feature = "astro-dnssd")]
221impl Mdns for rs_matter::transport::network::mdns::astro::AstroMdns {
222    async fn run<C, U>(
223        &mut self,
224        matter: &Matter<'_>,
225        _crypto: C,
226        _udp: U,
227        _mac: &[u8],
228        _ipv4: Ipv4Addr,
229        _ipv6: Ipv6Addr,
230        _interface: u32,
231    ) -> Result<(), Error>
232    where
233        C: Crypto,
234        U: UdpBind,
235    {
236        Self::run(self, matter).await
237    }
238}
239
240// TODO: Need to fix edge-mdns's `Service` signature.
241// /// Utilities for using `edge-mdns` as an mDNS responder for `rs-matter`.
242// ///
243// /// `rs-matter` does have a built-in mDNS imlementation, and that implementation is
244// /// in fact primarily maintained by the same author who maintains `edge-mdns`.
245// ///
246// /// However, the key difference between the two is that the `rs-matter` built-in mDNS
247// /// implementation - _for now_ -_only_ responds to queries which concern the Matter
248// /// protocol itself and also does not expose a query interface. This makes it unsuitable
249// /// for use in cases where the same host that operates an `rs-matter` stack needs to -
250// /// for whatever reasons - to host additional service types different than the ones
251// /// concerning Matter, and/or issue ad-hoc mDNS queries outside the queries necessary
252// /// for operating the Matter stack.
253// ///
254// /// Using `edge-mdns` solves this problem by providing a general-purpose mDNS which can be
255// /// shared between the `rs-matter` stack and other - user-specific use cases.
256// #[cfg(feature = "edge-mdns")]
257// pub mod edge_mdns {
258//     use rs_matter::error::Error;
259//     use rs_matter::Matter;
260
261//     /// An adaptor from `rs-matter` buffers to `edge-mdns` buffers.
262//     pub struct MatterBuffer<B>(B);
263
264//     impl<B> MatterBuffer<B> {
265//         /// Create a new instance of `MatterBuffer`
266//         pub const fn new(buffer: B) -> Self {
267//             Self(buffer)
268//         }
269//     }
270
271//     impl<B, T> edge_mdns::buf::BufferAccess<T> for MatterBuffer<B>
272//     where
273//         B: rs_matter::utils::storage::pooled::BufferAccess<T>,
274//         T: ?Sized,
275//     {
276//         type Buffer<'a>
277//             = B::Buffer<'a>
278//         where
279//             Self: 'a;
280
281//         async fn get(&self) -> Option<Self::Buffer<'_>> {
282//             self.0.get().await
283//         }
284//     }
285
286//     /// Visit all mDNS services registered by `rs-matter` as `edge_mdns::host::Service` instances.
287//     pub fn emdns_services<T>(matter: &Matter<'_>, mut visitor: T) -> Result<(), Error>
288//     where
289//         T: FnMut(&edge_mdns::host::Service) -> Result<(), Error>,
290//     {
291//         matter.mdns_services(|matter_service| {
292//             rs_matter::transport::network::mdns::Service::call_with(
293//                 &matter_service,
294//                 matter.dev_det(),
295//                 matter.port(),
296//                 |service| {
297//                     let service = edge_mdns::host::Service {
298//                         name: service.name,
299//                         service: service.service,
300//                         protocol: service.protocol,
301//                         port: service.port,
302//                         service_subtypes: service.service_subtypes,
303//                         txt_kvs: service.txt_kvs,
304//                         priority: 0,
305//                         weight: 0,
306//                     };
307
308//                     visitor(&service)
309//                 },
310//             )
311//         })
312//     }
313// }
314
315fn map_err<E: Debug>(e: E) -> Error {
316    warn!("mDNS network error: {:?}", debug2format!(e));
317    ErrorCode::StdIoError.into() // TODO
318}