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