hopr_transport_identity/
multiaddrs.rs1use std::net::{Ipv4Addr, ToSocketAddrs};
2
3pub use multiaddr::{Multiaddr, Protocol};
4
5use crate::errors::{Result, TransportIdentityError};
6
7pub fn strip_p2p_protocol(ma: &Multiaddr) -> Multiaddr {
9 Multiaddr::from_iter(ma.iter().filter(|v| !matches!(v, multiaddr::Protocol::P2p(_))))
10}
11
12pub fn is_dns(ma: &Multiaddr) -> bool {
14 ma.iter()
15 .next()
16 .map(|proto| matches!(proto, multiaddr::Protocol::Dns(_)))
17 .unwrap_or(false)
18}
19
20pub fn is_private(ma: &Multiaddr) -> bool {
22 ma.iter()
23 .next()
24 .map(|proto| match proto {
25 multiaddr::Protocol::Ip4(ip) => ip.is_private(),
26 multiaddr::Protocol::Dns(domain) => domain.to_ascii_lowercase() == "localhost",
27 _ => false,
28 })
29 .unwrap_or(false)
30}
31
32pub fn is_supported(ma: &Multiaddr) -> bool {
34 ma.iter()
35 .next()
36 .map(|proto| {
37 matches!(
38 proto,
39 multiaddr::Protocol::Ip4(_)
40 | multiaddr::Protocol::Ip6(_)
41 | multiaddr::Protocol::Dns(_)
42 | multiaddr::Protocol::Dns4(_)
43 | multiaddr::Protocol::Dns6(_)
44 )
45 })
46 .unwrap_or(false)
47}
48
49pub fn replace_transport_with_unspecified(ma: &Multiaddr) -> Result<Multiaddr> {
51 let mut out = Multiaddr::empty();
52
53 for proto in ma.iter() {
54 match proto {
55 Protocol::Ip4(_) => out.push(std::net::IpAddr::V4(Ipv4Addr::UNSPECIFIED).into()),
56 Protocol::Ip6(_) => out.push(std::net::IpAddr::V6(std::net::Ipv6Addr::UNSPECIFIED).into()),
57 _ => out.push(proto),
58 }
59 }
60
61 Ok(out)
62}
63
64pub fn resolve_dns_if_any(ma: &Multiaddr) -> Result<Multiaddr> {
66 let mut out = Multiaddr::empty();
67
68 for proto in ma.iter() {
69 match proto {
70 Protocol::Dns4(domain) => {
71 let ip = format!("{domain}:443") .to_socket_addrs()
73 .map_err(|e| TransportIdentityError::Multiaddress(e.to_string()))?
74 .filter(|sa| sa.is_ipv4())
75 .collect::<Vec<_>>()
76 .first()
77 .ok_or(TransportIdentityError::Multiaddress(format!(
78 "Failed to resolve {domain} to an IPv4 address. Does the DNS entry has an A record?"
79 )))?
80 .ip();
81
82 out.push(ip.into())
83 }
84 Protocol::Dns6(domain) => {
85 let ip = format!("{domain}:443") .to_socket_addrs()
87 .map_err(|e| TransportIdentityError::Multiaddress(e.to_string()))?
88 .filter(|sa| sa.is_ipv6())
89 .collect::<Vec<_>>()
90 .first()
91 .ok_or(TransportIdentityError::Multiaddress(format!(
92 "Failed to resolve {domain} to an IPv6 address. Does the DNS entry has an AAAA record?"
93 )))?
94 .ip();
95
96 out.push(ip.into())
97 }
98 _ => out.push(proto),
99 }
100 }
101
102 Ok(out)
103}
104
105#[cfg(test)]
106mod tests {
107 use std::str::FromStr;
108
109 use super::*;
110
111 #[test]
112 fn test_private_multiaddresses_are_shown_as_private() -> anyhow::Result<()> {
113 assert!(!is_private(&Multiaddr::from_str("/ip4/33.42.112.22/udp/9090/quic")?));
114
115 assert!(is_private(&Multiaddr::from_str("/ip4/192.168.1.23/udp/9090/quic")?));
116
117 Ok(())
118 }
119
120 #[test]
121 fn test_domain_dns4_multiaddresses_should_be_supported() -> anyhow::Result<()> {
122 assert!(is_supported(&Multiaddr::from_str("/dns4/localhost/tcp/5543")?));
123
124 Ok(())
125 }
126
127 #[test]
128 fn multiaddrs_modification_specific_ipv4_transport_should_be_replacable_with_unspecified() -> anyhow::Result<()> {
129 assert_eq!(
130 replace_transport_with_unspecified(&Multiaddr::from_str("/ip4/33.42.112.22/udp/9090/quic")?),
131 Ok(Multiaddr::from_str("/ip4/0.0.0.0/udp/9090/quic")?)
132 );
133
134 Ok(())
135 }
136
137 #[test]
138 fn multiaddrs_modification_specific_ipv6_transport_should_be_replacable_with_unspecified() -> anyhow::Result<()> {
139 assert_eq!(
140 replace_transport_with_unspecified(&Multiaddr::from_str(
141 "/ip6/82b0:a523:d8c0:1cba:365f:85f6:af3b:e369/udp/9090/quic"
142 )?),
143 Ok(Multiaddr::from_str("/ip6/0:0:0:0:0:0:0:0/udp/9090/quic")?)
144 );
145
146 Ok(())
147 }
148}