hopr_network_types/
addr.rs

1use std::net::IpAddr;
2
3use multiaddr::Multiaddr;
4
5/// Check if an IP address is a public/routable one.
6pub fn is_public(ip_addr: IpAddr) -> bool {
7    match ip_addr {
8        IpAddr::V4(ip) => !ip.is_unspecified() && !ip.is_private() && !ip.is_loopback() && !ip.is_link_local(),
9        IpAddr::V6(ip) => {
10            !ip.is_unspecified() && !ip.is_loopback() && !ip.is_unicast_link_local() && !ip.is_unique_local()
11        }
12    }
13}
14
15/// Check if a multiaddress contains a public/routable IP address.
16pub fn is_public_address(addr: &Multiaddr) -> bool {
17    addr.iter().all(|protocol| match protocol {
18        multiaddr::Protocol::Ip4(ip) => is_public(ip.into()),
19        multiaddr::Protocol::Ip6(ip) => is_public(ip.into()),
20        _ => true,
21    })
22}
23
24#[cfg(test)]
25mod tests {
26    use std::str::FromStr;
27
28    use super::*;
29
30    #[test]
31    fn test_is_public_address_ipv4() -> anyhow::Result<()> {
32        // IPv4 public addresses - should return true
33        assert!(is_public_address(&Multiaddr::from_str("/ip4/8.8.8.8")?));
34        assert!(is_public_address(&Multiaddr::from_str("/ip4/1.1.1.1")?));
35        assert!(is_public_address(&Multiaddr::from_str("/ip4/104.16.0.0")?));
36
37        // IPv4 private addresses - should return false
38        assert!(!is_public_address(&Multiaddr::from_str("/ip4/192.168.0.1")?));
39        assert!(!is_public_address(&Multiaddr::from_str("/ip4/192.168.1.254")?));
40        assert!(!is_public_address(&Multiaddr::from_str("/ip4/10.0.0.1")?));
41        assert!(!is_public_address(&Multiaddr::from_str("/ip4/10.1.0.1")?));
42        assert!(!is_public_address(&Multiaddr::from_str("/ip4/10.255.255.255")?));
43        assert!(!is_public_address(&Multiaddr::from_str("/ip4/172.16.0.0")?));
44        assert!(!is_public_address(&Multiaddr::from_str("/ip4/172.31.255.255")?));
45
46        // IPv4 loopback - should return false
47        assert!(!is_public_address(&Multiaddr::from_str("/ip4/127.0.0.1")?));
48        assert!(!is_public_address(&Multiaddr::from_str("/ip4/127.255.255.255")?));
49
50        // IPv4 link-local - should return false
51        assert!(!is_public_address(&Multiaddr::from_str("/ip4/169.254.1.1")?));
52        assert!(!is_public_address(&Multiaddr::from_str("/ip4/169.254.254.254")?));
53
54        // IPv4 unspecified - should return false
55        assert!(!is_public_address(&Multiaddr::from_str("/ip4/0.0.0.0")?));
56
57        Ok(())
58    }
59
60    #[test]
61    fn test_is_public_address_ipv6() -> anyhow::Result<()> {
62        // IPv6 public addresses - should return true
63        assert!(is_public_address(&Multiaddr::from_str("/ip6/2001:4860:4860::8888")?));
64        assert!(is_public_address(&Multiaddr::from_str("/ip6/2606:4700:4700::1111")?));
65        assert!(is_public_address(&Multiaddr::from_str(
66            "/ip6/2a00:1450:4001:830::200e"
67        )?));
68
69        // IPv6 loopback - should return false
70        assert!(!is_public_address(&Multiaddr::from_str("/ip6/::1")?));
71
72        // IPv6 unique-local - should return false
73        assert!(!is_public_address(&Multiaddr::from_str("/ip6/fc00::1")?));
74        assert!(!is_public_address(&Multiaddr::from_str("/ip6/fd00::1")?));
75        assert!(!is_public_address(&Multiaddr::from_str(
76            "/ip6/fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
77        )?));
78
79        // IPv6 link-local - should return false
80        assert!(!is_public_address(&Multiaddr::from_str("/ip6/fe80::1")?));
81        assert!(!is_public_address(&Multiaddr::from_str(
82            "/ip6/fe80::dead:beef:cafe:babe"
83        )?));
84        assert!(!is_public_address(&Multiaddr::from_str(
85            "/ip6/febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
86        )?));
87
88        // IPv6 unspecified - should return false
89        assert!(!is_public_address(&Multiaddr::from_str("/ip6/::")?));
90
91        Ok(())
92    }
93
94    #[test]
95    fn test_is_public_address_with_protocols() -> anyhow::Result<()> {
96        // Public addresses with additional protocols - should return true
97        assert!(is_public_address(&Multiaddr::from_str("/ip4/8.8.8.8/tcp/4001")?));
98        assert!(is_public_address(&Multiaddr::from_str("/ip4/1.1.1.1/udp/30303")?));
99        assert!(is_public_address(&Multiaddr::from_str(
100            "/ip4/8.8.8.8/tcp/4001/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp"
101        )?));
102        assert!(is_public_address(&Multiaddr::from_str(
103            "/ip6/2001:4860:4860::8888/tcp/4001"
104        )?));
105        assert!(is_public_address(&Multiaddr::from_str(
106            "/ip6/2001:4860:4860::8888/udp/30303"
107        )?));
108        assert!(is_public_address(&Multiaddr::from_str(
109            "/ip6/2606:4700:4700::1111/tcp/443/wss"
110        )?));
111
112        // Private/special addresses with additional protocols - should return false
113        assert!(!is_public_address(&Multiaddr::from_str("/ip4/192.168.0.1/tcp/4001")?));
114        assert!(!is_public_address(&Multiaddr::from_str("/ip4/127.0.0.1/tcp/8080")?));
115        assert!(!is_public_address(&Multiaddr::from_str("/ip4/10.0.0.1/tcp/8080")?));
116        assert!(!is_public_address(&Multiaddr::from_str("/ip4/10.1.0.1/tcp/8080")?));
117        assert!(!is_public_address(&Multiaddr::from_str("/ip4/169.254.1.1/udp/5060")?));
118        assert!(!is_public_address(&Multiaddr::from_str("/ip4/0.0.0.0/tcp/3000")?));
119        assert!(!is_public_address(&Multiaddr::from_str("/ip6/::1/tcp/4001")?));
120        assert!(!is_public_address(&Multiaddr::from_str("/ip6/fe80::1/udp/30303")?));
121        assert!(!is_public_address(&Multiaddr::from_str("/ip6/fc00::1/tcp/443")?));
122        assert!(!is_public_address(&Multiaddr::from_str("/ip6/::/tcp/8080")?));
123
124        Ok(())
125    }
126
127    #[test]
128    fn test_is_public_address_mixed_protocols() -> anyhow::Result<()> {
129        // Test with DNS and other protocols (no IP) - should return true (non-IP protocols default to true)
130        assert!(is_public_address(&Multiaddr::from_str("/dns/example.com")?));
131        assert!(is_public_address(&Multiaddr::from_str("/dns4/example.com/tcp/443")?));
132        assert!(is_public_address(&Multiaddr::from_str("/dns6/example.com/tcp/443")?));
133
134        Ok(())
135    }
136
137    #[test]
138    fn test_non_local_addresses() -> anyhow::Result<()> {
139        assert!(!is_public_address(&Multiaddr::from_str("/ip4/127.0.0.1")?));
140        assert!(!is_public_address(&Multiaddr::from_str("/ip4/192.168.1.1")?));
141        assert!(!is_public_address(&Multiaddr::from_str("/ip4/10.0.0.1")?));
142        assert!(!is_public_address(&Multiaddr::from_str("/ip4/172.16.0.1")?));
143        assert!(!is_public_address(&Multiaddr::from_str("/ip6/::1")?));
144        assert!(!is_public_address(&Multiaddr::from_str("/ip6/fe80::1")?));
145
146        Ok(())
147    }
148}