libp2p_mdns/behaviour/iface/
query.rs

1// Copyright 2018 Parity Technologies (UK) Ltd.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the "Software"),
5// to deal in the Software without restriction, including without limitation
6// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7// and/or sell copies of the Software, and to permit persons to whom the
8// Software is furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19// DEALINGS IN THE SOFTWARE.
20
21use super::dns;
22use crate::{META_QUERY_SERVICE_FQDN, SERVICE_NAME_FQDN};
23use libp2p_core::{
24    address_translation,
25    multiaddr::{Multiaddr, Protocol},
26};
27use libp2p_identity::PeerId;
28use std::time::Instant;
29use std::{fmt, net::SocketAddr, str, time::Duration};
30use trust_dns_proto::{
31    op::Message,
32    rr::{Name, RData},
33};
34
35/// A valid mDNS packet received by the service.
36#[derive(Debug)]
37pub(crate) enum MdnsPacket {
38    /// A query made by a remote.
39    Query(MdnsQuery),
40    /// A response sent by a remote in response to one of our queries.
41    Response(MdnsResponse),
42    /// A request for service discovery.
43    ServiceDiscovery(MdnsServiceDiscovery),
44}
45
46impl MdnsPacket {
47    pub(crate) fn new_from_bytes(
48        buf: &[u8],
49        from: SocketAddr,
50    ) -> Result<Option<MdnsPacket>, trust_dns_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            // TODO: what if multiple questions, one with SERVICE_NAME and one with META_QUERY_SERVICE?
74            return Ok(Some(MdnsPacket::ServiceDiscovery(MdnsServiceDiscovery {
75                from,
76                query_id: packet.header().id(),
77            })));
78        }
79
80        Ok(None)
81    }
82}
83
84/// A received mDNS query.
85pub(crate) struct MdnsQuery {
86    /// Sender of the address.
87    from: SocketAddr,
88    /// Id of the received DNS query. We need to pass this ID back in the results.
89    query_id: u16,
90}
91
92impl MdnsQuery {
93    /// Source address of the packet.
94    pub(crate) fn remote_addr(&self) -> &SocketAddr {
95        &self.from
96    }
97
98    /// Query id of the packet.
99    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
113/// A received mDNS service discovery query.
114pub(crate) struct MdnsServiceDiscovery {
115    /// Sender of the address.
116    from: SocketAddr,
117    /// Id of the received DNS query. We need to pass this ID back in the results.
118    query_id: u16,
119}
120
121impl MdnsServiceDiscovery {
122    /// Source address of the packet.
123    pub(crate) fn remote_addr(&self) -> &SocketAddr {
124        &self.from
125    }
126
127    /// Query id of the packet.
128    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
142/// A received mDNS response.
143pub(crate) struct MdnsResponse {
144    peers: Vec<MdnsPeer>,
145    from: SocketAddr,
146}
147
148impl MdnsResponse {
149    /// Creates a new `MdnsResponse` based on the provided `Packet`.
150    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 record_value = match record.data() {
160                    Some(RData::PTR(record)) => record,
161                    _ => return None,
162                };
163
164                MdnsPeer::new(packet, record_value, record.ttl())
165            })
166            .collect();
167
168        MdnsResponse { peers, from }
169    }
170
171    pub(crate) fn extract_discovered(
172        &self,
173        now: Instant,
174        local_peer_id: PeerId,
175    ) -> impl Iterator<Item = (PeerId, Multiaddr, Instant)> + '_ {
176        self.discovered_peers()
177            .filter(move |peer| peer.id() != &local_peer_id)
178            .flat_map(move |peer| {
179                let observed = self.observed_address();
180                let new_expiration = now + peer.ttl();
181
182                peer.addresses().iter().filter_map(move |address| {
183                    let new_addr = address_translation(address, &observed)?;
184
185                    Some((*peer.id(), new_addr, new_expiration))
186                })
187            })
188    }
189
190    /// Source address of the packet.
191    pub(crate) fn remote_addr(&self) -> &SocketAddr {
192        &self.from
193    }
194
195    fn observed_address(&self) -> Multiaddr {
196        // We replace the IP address with the address we observe the
197        // remote as and the address they listen on.
198        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    /// Returns the list of peers that have been reported in this packet.
205    ///
206    /// > **Note**: Keep in mind that this will also contain the responses we sent ourselves.
207    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
220/// A peer discovered by the service.
221pub(crate) struct MdnsPeer {
222    addrs: Vec<Multiaddr>,
223    /// Id of the peer.
224    peer_id: PeerId,
225    /// TTL of the record in seconds.
226    ttl: u32,
227}
228
229impl MdnsPeer {
230    /// Creates a new `MdnsPeer` based on the provided `Packet`.
231    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                // TODO: wrong, txt can be multiple character strings
250                let addr = match dns::decode_character_string(txt) {
251                    Ok(a) => a,
252                    Err(_) => return None,
253                };
254                if !addr.starts_with(b"dnsaddr=") {
255                    return None;
256                }
257                let addr = match str::from_utf8(&addr[8..]) {
258                    Ok(a) => a,
259                    Err(_) => return None,
260                };
261                let mut addr = match addr.parse::<Multiaddr>() {
262                    Ok(a) => a,
263                    Err(_) => return None,
264                };
265                match addr.pop() {
266                    Some(Protocol::P2p(peer_id)) => {
267                        if let Some(pid) = &my_peer_id {
268                            if peer_id != *pid {
269                                return None;
270                            }
271                        } else {
272                            my_peer_id.replace(peer_id);
273                        }
274                    }
275                    _ => return None,
276                };
277                Some(addr)
278            })
279            .collect();
280
281        my_peer_id.map(|peer_id| MdnsPeer {
282            addrs,
283            peer_id,
284            ttl,
285        })
286    }
287
288    /// Returns the id of the peer.
289    #[inline]
290    pub(crate) fn id(&self) -> &PeerId {
291        &self.peer_id
292    }
293
294    /// Returns the requested time-to-live for the record.
295    #[inline]
296    pub(crate) fn ttl(&self) -> Duration {
297        Duration::from_secs(u64::from(self.ttl))
298    }
299
300    /// Returns the list of addresses the peer says it is listening on.
301    ///
302    /// Filters out invalid addresses.
303    pub(crate) fn addresses(&self) -> &Vec<Multiaddr> {
304        &self.addrs
305    }
306}
307
308impl fmt::Debug for MdnsPeer {
309    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310        f.debug_struct("MdnsPeer")
311            .field("peer_id", &self.peer_id)
312            .finish()
313    }
314}
315
316#[cfg(test)]
317mod tests {
318    use super::super::dns::build_query_response;
319    use super::*;
320
321    #[test]
322    fn test_create_mdns_peer() {
323        let ttl = 300;
324        let peer_id = PeerId::random();
325
326        let mut addr1: Multiaddr = "/ip4/1.2.3.4/tcp/5000".parse().expect("bad multiaddress");
327        let mut addr2: Multiaddr = "/ip6/::1/udp/10000".parse().expect("bad multiaddress");
328        addr1.push(Protocol::P2p(peer_id));
329        addr2.push(Protocol::P2p(peer_id));
330
331        let packets = build_query_response(
332            0xf8f8,
333            peer_id,
334            vec![&addr1, &addr2].into_iter(),
335            Duration::from_secs(60),
336        );
337
338        for bytes in packets {
339            let packet = Message::from_vec(&bytes).expect("unable to parse packet");
340            let record_value = packet
341                .answers()
342                .iter()
343                .filter_map(|record| {
344                    if record.name().to_utf8() != SERVICE_NAME_FQDN {
345                        return None;
346                    }
347                    let record_value = match record.data() {
348                        Some(RData::PTR(record)) => record,
349                        _ => return None,
350                    };
351                    Some(record_value)
352                })
353                .next()
354                .expect("empty record value");
355
356            let peer = MdnsPeer::new(&packet, record_value, ttl).expect("fail to create peer");
357            assert_eq!(peer.peer_id, peer_id);
358        }
359    }
360}