Skip to main content

hopr_transport_p2p/
utils.rs

1use std::net::{Ipv4Addr, ToSocketAddrs};
2
3use libp2p::Multiaddr;
4use multiaddr::Protocol;
5
6use crate::errors::{P2PError, Result};
7
8/// Replaces the IPv4 and IPv6 from the network layer with a unspecified interface in any multiaddress.
9pub fn replace_transport_with_unspecified(ma: &Multiaddr) -> Result<Multiaddr> {
10    let mut out = Multiaddr::empty();
11
12    for proto in ma.iter() {
13        match proto {
14            Protocol::Ip4(_) => out.push(std::net::IpAddr::V4(Ipv4Addr::UNSPECIFIED).into()),
15            Protocol::Ip6(_) => out.push(std::net::IpAddr::V6(std::net::Ipv6Addr::UNSPECIFIED).into()),
16            _ => out.push(proto),
17        }
18    }
19
20    Ok(out)
21}
22
23/// Resolves the DNS parts of a multiaddress and replaces it with the resolved IP address.
24pub fn resolve_dns_if_any(ma: &Multiaddr) -> Result<Multiaddr> {
25    let mut out = Multiaddr::empty();
26
27    for proto in ma.iter() {
28        match proto {
29            Protocol::Dns4(domain) => {
30                let ip = format!("{domain}:443") // dummy port, irrevelant at this point
31                    .to_socket_addrs()
32                    .map_err(|e| P2PError::Multiaddress(e.to_string()))?
33                    .filter(|sa| sa.is_ipv4())
34                    .collect::<Vec<_>>()
35                    .first()
36                    .ok_or(P2PError::Multiaddress(format!(
37                        "Failed to resolve {domain} to an IPv4 address. Does the DNS entry has an A record?"
38                    )))?
39                    .ip();
40
41                out.push(ip.into())
42            }
43            Protocol::Dns6(domain) => {
44                let ip = format!("{domain}:443") // dummy port, irrevelant at this point
45                    .to_socket_addrs()
46                    .map_err(|e| P2PError::Multiaddress(e.to_string()))?
47                    .filter(|sa| sa.is_ipv6())
48                    .collect::<Vec<_>>()
49                    .first()
50                    .ok_or(P2PError::Multiaddress(format!(
51                        "Failed to resolve {domain} to an IPv6 address. Does the DNS entry has an AAAA record?"
52                    )))?
53                    .ip();
54
55                out.push(ip.into())
56            }
57            _ => out.push(proto),
58        }
59    }
60
61    Ok(out)
62}
63
64#[cfg(test)]
65mod tests {
66    use std::str::FromStr;
67
68    use super::*;
69
70    #[test]
71    fn multiaddrs_modification_specific_ipv4_transport_should_be_replacable_with_unspecified() -> anyhow::Result<()> {
72        assert_eq!(
73            replace_transport_with_unspecified(&Multiaddr::from_str("/ip4/33.42.112.22/udp/9090/quic")?),
74            Ok(Multiaddr::from_str("/ip4/0.0.0.0/udp/9090/quic")?)
75        );
76
77        Ok(())
78    }
79
80    #[test]
81    fn multiaddrs_modification_specific_ipv6_transport_should_be_replacable_with_unspecified() -> anyhow::Result<()> {
82        assert_eq!(
83            replace_transport_with_unspecified(&Multiaddr::from_str(
84                "/ip6/82b0:a523:d8c0:1cba:365f:85f6:af3b:e369/udp/9090/quic"
85            )?),
86            Ok(Multiaddr::from_str("/ip6/0:0:0:0:0:0:0:0/udp/9090/quic")?)
87        );
88
89        Ok(())
90    }
91}