libp2p_webrtc_utils/
transport.rs

1use std::net::{IpAddr, SocketAddr};
2
3use libp2p_core::{multiaddr::Protocol, Multiaddr};
4
5use crate::fingerprint::Fingerprint;
6
7/// Parse the given [`Multiaddr`] into a [`SocketAddr`] and a [`Fingerprint`] for dialing.
8pub fn parse_webrtc_dial_addr(addr: &Multiaddr) -> Option<(SocketAddr, Fingerprint)> {
9    let mut iter = addr.iter();
10
11    let ip = match iter.next()? {
12        Protocol::Ip4(ip) => IpAddr::from(ip),
13        Protocol::Ip6(ip) => IpAddr::from(ip),
14        _ => return None,
15    };
16
17    let port = iter.next()?;
18    let webrtc = iter.next()?;
19    let certhash = iter.next()?;
20
21    let (port, fingerprint) = match (port, webrtc, certhash) {
22        (Protocol::Udp(port), Protocol::WebRTCDirect, Protocol::Certhash(cert_hash)) => {
23            let fingerprint = Fingerprint::try_from_multihash(cert_hash)?;
24
25            (port, fingerprint)
26        }
27        _ => return None,
28    };
29
30    match iter.next() {
31        Some(Protocol::P2p(_)) => {}
32        // peer ID is optional
33        None => {}
34        // unexpected protocol
35        Some(_) => return None,
36    }
37
38    Some((SocketAddr::new(ip, port), fingerprint))
39}
40
41#[cfg(test)]
42mod tests {
43    use std::net::{Ipv4Addr, Ipv6Addr};
44
45    use super::*;
46
47    #[test]
48    fn parse_valid_address_with_certhash_and_p2p() {
49        let addr = "/ip4/127.0.0.1/udp/39901/webrtc-direct/certhash/uEiDikp5KVUgkLta1EjUN-IKbHk-dUBg8VzKgf5nXxLK46w/p2p/12D3KooWNpDk9w6WrEEcdsEH1y47W71S36yFjw4sd3j7omzgCSMS"
50            .parse()
51            .unwrap();
52
53        let maybe_parsed = parse_webrtc_dial_addr(&addr);
54
55        assert_eq!(
56            maybe_parsed,
57            Some((
58                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 39901),
59                Fingerprint::raw(hex_literal::hex!(
60                    "e2929e4a5548242ed6b512350df8829b1e4f9d50183c5732a07f99d7c4b2b8eb"
61                ))
62            ))
63        );
64    }
65
66    #[test]
67    fn peer_id_is_not_required() {
68        let addr = "/ip4/127.0.0.1/udp/39901/webrtc-direct/certhash/uEiDikp5KVUgkLta1EjUN-IKbHk-dUBg8VzKgf5nXxLK46w"
69            .parse()
70            .unwrap();
71
72        let maybe_parsed = parse_webrtc_dial_addr(&addr);
73
74        assert_eq!(
75            maybe_parsed,
76            Some((
77                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 39901),
78                Fingerprint::raw(hex_literal::hex!(
79                    "e2929e4a5548242ed6b512350df8829b1e4f9d50183c5732a07f99d7c4b2b8eb"
80                ))
81            ))
82        );
83    }
84
85    #[test]
86    fn parse_ipv6() {
87        let addr =
88            "/ip6/::1/udp/12345/webrtc-direct/certhash/uEiDikp5KVUgkLta1EjUN-IKbHk-dUBg8VzKgf5nXxLK46w/p2p/12D3KooWNpDk9w6WrEEcdsEH1y47W71S36yFjw4sd3j7omzgCSMS"
89                .parse()
90                .unwrap();
91
92        let maybe_parsed = parse_webrtc_dial_addr(&addr);
93
94        assert_eq!(
95            maybe_parsed,
96            Some((
97                SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 12345),
98                Fingerprint::raw(hex_literal::hex!(
99                    "e2929e4a5548242ed6b512350df8829b1e4f9d50183c5732a07f99d7c4b2b8eb"
100                ))
101            ))
102        );
103    }
104}