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}