libp2p_mdns/behaviour/iface/
query.rs1use std::{
22 fmt,
23 net::SocketAddr,
24 str,
25 time::{Duration, Instant},
26};
27
28use hickory_proto::{
29 op::Message,
30 rr::{Name, RData},
31};
32use libp2p_core::multiaddr::{Multiaddr, Protocol};
33use libp2p_identity::PeerId;
34use libp2p_swarm::_address_translation;
35
36use super::dns;
37use crate::{META_QUERY_SERVICE_FQDN, SERVICE_NAME_FQDN};
38
39#[derive(Debug)]
41pub(crate) enum MdnsPacket {
42 Query(MdnsQuery),
44 Response(MdnsResponse),
46 ServiceDiscovery(MdnsServiceDiscovery),
48}
49
50impl MdnsPacket {
51 pub(crate) fn new_from_bytes(
52 buf: &[u8],
53 from: SocketAddr,
54 ) -> Result<Option<MdnsPacket>, hickory_proto::ProtoError> {
55 let packet = Message::from_vec(buf)?;
56
57 if packet.query().is_none() {
58 return Ok(Some(MdnsPacket::Response(MdnsResponse::new(&packet, from))));
59 }
60
61 if packet
62 .queries()
63 .iter()
64 .any(|q| q.name().to_utf8() == SERVICE_NAME_FQDN)
65 {
66 return Ok(Some(MdnsPacket::Query(MdnsQuery {
67 from,
68 query_id: packet.header().id(),
69 })));
70 }
71
72 if packet
73 .queries()
74 .iter()
75 .any(|q| q.name().to_utf8() == META_QUERY_SERVICE_FQDN)
76 {
77 return Ok(Some(MdnsPacket::ServiceDiscovery(MdnsServiceDiscovery {
80 from,
81 query_id: packet.header().id(),
82 })));
83 }
84
85 Ok(None)
86 }
87}
88
89pub(crate) struct MdnsQuery {
91 from: SocketAddr,
93 query_id: u16,
95}
96
97impl MdnsQuery {
98 pub(crate) fn remote_addr(&self) -> &SocketAddr {
100 &self.from
101 }
102
103 pub(crate) fn query_id(&self) -> u16 {
105 self.query_id
106 }
107}
108
109impl fmt::Debug for MdnsQuery {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 f.debug_struct("MdnsQuery")
112 .field("from", self.remote_addr())
113 .field("query_id", &self.query_id)
114 .finish()
115 }
116}
117
118pub(crate) struct MdnsServiceDiscovery {
120 from: SocketAddr,
122 query_id: u16,
124}
125
126impl MdnsServiceDiscovery {
127 pub(crate) fn remote_addr(&self) -> &SocketAddr {
129 &self.from
130 }
131
132 pub(crate) fn query_id(&self) -> u16 {
134 self.query_id
135 }
136}
137
138impl fmt::Debug for MdnsServiceDiscovery {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 f.debug_struct("MdnsServiceDiscovery")
141 .field("from", self.remote_addr())
142 .field("query_id", &self.query_id)
143 .finish()
144 }
145}
146
147pub(crate) struct MdnsResponse {
149 peers: Vec<MdnsPeer>,
150 from: SocketAddr,
151}
152
153impl MdnsResponse {
154 pub(crate) fn new(packet: &Message, from: SocketAddr) -> MdnsResponse {
156 let peers = packet
157 .answers()
158 .iter()
159 .filter_map(|record| {
160 if record.name().to_string() != SERVICE_NAME_FQDN {
161 return None;
162 }
163
164 let RData::PTR(record_value) = record.data() else {
165 return None;
166 };
167
168 MdnsPeer::new(packet, record_value, record.ttl())
169 })
170 .collect();
171
172 MdnsResponse { peers, from }
173 }
174
175 pub(crate) fn extract_discovered(
176 &self,
177 now: Instant,
178 local_peer_id: PeerId,
179 ) -> impl Iterator<Item = (PeerId, Multiaddr, Instant)> + '_ {
180 self.discovered_peers()
181 .filter(move |peer| peer.id() != &local_peer_id)
182 .flat_map(move |peer| {
183 let observed = self.observed_address();
184 let new_expiration = now + peer.ttl();
185
186 peer.addresses().iter().filter_map(move |address| {
187 let new_addr = _address_translation(address, &observed)?;
188 let new_addr = new_addr.with_p2p(*peer.id()).ok()?;
189
190 Some((*peer.id(), new_addr, new_expiration))
191 })
192 })
193 }
194
195 pub(crate) fn remote_addr(&self) -> &SocketAddr {
197 &self.from
198 }
199
200 fn observed_address(&self) -> Multiaddr {
201 let obs_ip = Protocol::from(self.remote_addr().ip());
204 let obs_port = Protocol::Udp(self.remote_addr().port());
205
206 Multiaddr::empty().with(obs_ip).with(obs_port)
207 }
208
209 fn discovered_peers(&self) -> impl Iterator<Item = &MdnsPeer> {
213 self.peers.iter()
214 }
215}
216
217impl fmt::Debug for MdnsResponse {
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 f.debug_struct("MdnsResponse")
220 .field("from", self.remote_addr())
221 .finish()
222 }
223}
224
225pub(crate) struct MdnsPeer {
227 addrs: Vec<Multiaddr>,
228 peer_id: PeerId,
230 ttl: u32,
232}
233
234impl MdnsPeer {
235 pub(crate) fn new(packet: &Message, record_value: &Name, ttl: u32) -> Option<MdnsPeer> {
237 let mut my_peer_id: Option<PeerId> = None;
238 let addrs = packet
239 .additionals()
240 .iter()
241 .filter_map(|add_record| {
242 if add_record.name() != record_value {
243 return None;
244 }
245
246 if let RData::TXT(ref txt) = add_record.data() {
247 Some(txt)
248 } else {
249 None
250 }
251 })
252 .flat_map(|txt| txt.iter())
253 .filter_map(|txt| {
254 let addr = dns::decode_character_string(txt).ok()?;
256
257 if !addr.starts_with(b"dnsaddr=") {
258 return None;
259 }
260
261 let mut addr = str::from_utf8(&addr[8..]).ok()?.parse::<Multiaddr>().ok()?;
262
263 match addr.pop() {
264 Some(Protocol::P2p(peer_id)) => {
265 if let Some(pid) = &my_peer_id {
266 if peer_id != *pid {
267 return None;
268 }
269 } else {
270 my_peer_id.replace(peer_id);
271 }
272 }
273 _ => return None,
274 };
275 Some(addr)
276 })
277 .collect();
278
279 my_peer_id.map(|peer_id| MdnsPeer {
280 addrs,
281 peer_id,
282 ttl,
283 })
284 }
285
286 #[inline]
288 pub(crate) fn id(&self) -> &PeerId {
289 &self.peer_id
290 }
291
292 #[inline]
294 pub(crate) fn ttl(&self) -> Duration {
295 Duration::from_secs(u64::from(self.ttl))
296 }
297
298 pub(crate) fn addresses(&self) -> &Vec<Multiaddr> {
302 &self.addrs
303 }
304}
305
306impl fmt::Debug for MdnsPeer {
307 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308 f.debug_struct("MdnsPeer")
309 .field("peer_id", &self.peer_id)
310 .finish()
311 }
312}
313
314#[cfg(test)]
315mod tests {
316 use super::{super::dns::build_query_response, *};
317
318 #[test]
319 fn test_create_mdns_peer() {
320 let ttl = 300;
321 let peer_id = PeerId::random();
322
323 let mut addr1: Multiaddr = "/ip4/1.2.3.4/tcp/5000".parse().expect("bad multiaddress");
324 let mut addr2: Multiaddr = "/ip6/::1/udp/10000".parse().expect("bad multiaddress");
325 addr1.push(Protocol::P2p(peer_id));
326 addr2.push(Protocol::P2p(peer_id));
327
328 let packets = build_query_response(
329 0xf8f8,
330 peer_id,
331 vec![&addr1, &addr2].into_iter(),
332 Duration::from_secs(60),
333 );
334
335 for bytes in packets {
336 let packet = Message::from_vec(&bytes).expect("unable to parse packet");
337 let record_value = packet
338 .answers()
339 .iter()
340 .filter_map(|record| {
341 if record.name().to_utf8() != SERVICE_NAME_FQDN {
342 return None;
343 }
344 let RData::PTR(record_value) = record.data() else {
345 return None;
346 };
347 Some(record_value)
348 })
349 .next()
350 .expect("empty record value");
351
352 let peer = MdnsPeer::new(&packet, record_value, ttl).expect("fail to create peer");
353 assert_eq!(peer.peer_id, peer_id);
354 }
355 }
356}