Skip to main content

iota_multiaddr/
native.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{
6    borrow::Cow,
7    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
8};
9
10pub use ::multiaddr::{Error, Protocol};
11use eyre::{Result, eyre};
12use tracing::error;
13
14#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
15pub struct Multiaddr(::multiaddr::Multiaddr);
16
17impl Multiaddr {
18    pub fn empty() -> Self {
19        Self(::multiaddr::Multiaddr::empty())
20    }
21
22    #[cfg(test)]
23    pub(crate) fn new_internal(inner: ::multiaddr::Multiaddr) -> Self {
24        Self(inner)
25    }
26
27    pub fn iter(&self) -> ::multiaddr::Iter<'_> {
28        self.0.iter()
29    }
30
31    pub fn pop<'a>(&mut self) -> Option<Protocol<'a>> {
32        self.0.pop()
33    }
34
35    pub fn push(&mut self, p: Protocol<'_>) {
36        self.0.push(p)
37    }
38
39    pub fn replace<'a, F>(&self, at: usize, by: F) -> Option<Multiaddr>
40    where
41        F: FnOnce(&Protocol<'_>) -> Option<Protocol<'a>>,
42    {
43        self.0.replace(at, by).map(Self)
44    }
45
46    pub fn len(&self) -> usize {
47        self.0.len()
48    }
49
50    pub fn is_empty(&self) -> bool {
51        self.0.is_empty()
52    }
53
54    /// Attempts to convert a multiaddr of the form
55    /// `/[ip4,ip6,dns]/{}/udp/{port}` into an anemo address
56    pub fn to_anemo_address(&self) -> Result<anemo::types::Address, &'static str> {
57        let mut iter = self.iter();
58
59        match (iter.next(), iter.next()) {
60            (Some(Protocol::Ip4(ipaddr)), Some(Protocol::Udp(port))) => Ok((ipaddr, port).into()),
61            (Some(Protocol::Ip6(ipaddr)), Some(Protocol::Udp(port))) => Ok((ipaddr, port).into()),
62            (Some(Protocol::Dns(hostname)), Some(Protocol::Udp(port))) => {
63                Ok((hostname.as_ref(), port).into())
64            }
65
66            _ => {
67                tracing::warn!("unsupported p2p multiaddr: '{self}'");
68                Err("invalid address")
69            }
70        }
71    }
72
73    pub fn udp_multiaddr_to_listen_address(&self) -> Option<std::net::SocketAddr> {
74        let mut iter = self.iter();
75
76        match (iter.next(), iter.next()) {
77            (Some(Protocol::Ip4(ipaddr)), Some(Protocol::Udp(port))) => Some((ipaddr, port).into()),
78            (Some(Protocol::Ip6(ipaddr)), Some(Protocol::Udp(port))) => Some((ipaddr, port).into()),
79
80            (Some(Protocol::Dns(_)), Some(Protocol::Udp(port))) => {
81                Some((std::net::Ipv4Addr::UNSPECIFIED, port).into())
82            }
83
84            _ => None,
85        }
86    }
87
88    // Converts a /ip{4,6}/-/tcp/-[/-] Multiaddr to SocketAddr.
89    // Useful when an external library only accepts SocketAddr, e.g. to start a
90    // local server. See `client::endpoint_from_multiaddr()` for converting to
91    // Endpoint for clients.
92    pub fn to_socket_addr(&self) -> Result<SocketAddr> {
93        let mut iter = self.iter();
94        let ip = match iter.next().ok_or_else(|| {
95            eyre!("failed to convert to SocketAddr: Multiaddr does not contain IP")
96        })? {
97            Protocol::Ip4(ip4_addr) => IpAddr::V4(ip4_addr),
98            Protocol::Ip6(ip6_addr) => IpAddr::V6(ip6_addr),
99            unsupported => return Err(eyre!("unsupported protocol {unsupported}")),
100        };
101        let tcp_port = parse_tcp(&mut iter)?;
102        Ok(SocketAddr::new(ip, tcp_port))
103    }
104
105    // Returns true if the third component in the multiaddr is `Protocol::Tcp`
106    pub fn is_loosely_valid_tcp_addr(&self) -> bool {
107        let mut iter = self.iter();
108        iter.next(); // Skip the ip/dns part
109        match iter.next() {
110            Some(Protocol::Tcp(_)) => true,
111            _ => false, // including `None` and `Some(other)`
112        }
113    }
114
115    /// Set the ip address to `0.0.0.0`. For instance, it converts the following
116    /// address `/ip4/155.138.174.208/tcp/1500/http` into
117    /// `/ip4/0.0.0.0/tcp/1500/http`. This is useful when starting a server
118    /// and you want to listen on all interfaces.
119    pub fn with_zero_ip(&self) -> Self {
120        let mut new_address = self.0.clone();
121        let Some(protocol) = new_address.iter().next() else {
122            error!("Multiaddr is empty");
123            return Self(new_address);
124        };
125        match protocol {
126            multiaddr::Protocol::Ip4(_)
127            | multiaddr::Protocol::Dns(_)
128            | multiaddr::Protocol::Dns4(_) => {
129                new_address = new_address
130                    .replace(0, |_| Some(multiaddr::Protocol::Ip4(Ipv4Addr::UNSPECIFIED)))
131                    .unwrap();
132            }
133            multiaddr::Protocol::Ip6(_) | multiaddr::Protocol::Dns6(_) => {
134                new_address = new_address
135                    .replace(0, |_| Some(multiaddr::Protocol::Ip6(Ipv6Addr::UNSPECIFIED)))
136                    .unwrap();
137            }
138            p => {
139                error!("Unsupported protocol {} in Multiaddr {}!", p, new_address);
140            }
141        }
142        Self(new_address)
143    }
144
145    /// Set the ip address to `127.0.0.1`. For instance, it converts the
146    /// following address `/ip4/155.138.174.208/tcp/1500/http` into
147    /// `/ip4/127.0.0.1/tcp/1500/http`.
148    pub fn with_localhost_ip(&self) -> Self {
149        let mut new_address = self.0.clone();
150        let Some(protocol) = new_address.iter().next() else {
151            error!("Multiaddr is empty");
152            return Self(new_address);
153        };
154        match protocol {
155            multiaddr::Protocol::Ip4(_)
156            | multiaddr::Protocol::Dns(_)
157            | multiaddr::Protocol::Dns4(_) => {
158                new_address = new_address
159                    .replace(0, |_| Some(multiaddr::Protocol::Ip4(Ipv4Addr::LOCALHOST)))
160                    .unwrap();
161            }
162            multiaddr::Protocol::Ip6(_) | multiaddr::Protocol::Dns6(_) => {
163                new_address = new_address
164                    .replace(0, |_| Some(multiaddr::Protocol::Ip6(Ipv6Addr::LOCALHOST)))
165                    .unwrap();
166            }
167            p => {
168                error!("Unsupported protocol {} in Multiaddr {}!", p, new_address);
169            }
170        }
171        Self(new_address)
172    }
173
174    pub fn is_localhost_ip(&self) -> bool {
175        let Some(protocol) = self.0.iter().next() else {
176            error!("Multiaddr is empty");
177            return false;
178        };
179        match protocol {
180            multiaddr::Protocol::Ip4(addr) => addr == Ipv4Addr::LOCALHOST,
181            multiaddr::Protocol::Ip6(addr) => addr == Ipv6Addr::LOCALHOST,
182            _ => false,
183        }
184    }
185
186    pub fn hostname(&self) -> Option<String> {
187        for component in self.iter() {
188            match component {
189                Protocol::Ip4(ip) => return Some(ip.to_string()),
190                Protocol::Ip6(ip) => return Some(ip.to_string()),
191                Protocol::Dns(dns) => return Some(dns.to_string()),
192                _ => (),
193            }
194        }
195        None
196    }
197
198    pub fn port(&self) -> Option<u16> {
199        for component in self.iter() {
200            match component {
201                Protocol::Udp(port) | Protocol::Tcp(port) => return Some(port),
202                _ => (),
203            }
204        }
205        None
206    }
207
208    pub fn rewrite_udp_to_tcp(&self) -> Self {
209        let mut new = Self::empty();
210
211        for component in self.iter() {
212            if let Protocol::Udp(port) = component {
213                new.push(Protocol::Tcp(port));
214            } else {
215                new.push(component);
216            }
217        }
218
219        new
220    }
221
222    pub fn rewrite_http_to_https(&self) -> Self {
223        let mut new = Self::empty();
224
225        for component in self.iter() {
226            if let Protocol::Http = component {
227                new.push(Protocol::Https);
228            } else {
229                new.push(component);
230            }
231        }
232
233        new
234    }
235
236    /// Checks if the multiaddr contains a private/unroutable IP address or
237    /// invalid DNS. Returns true if the address should is private or
238    /// unroutable.
239    pub fn is_private_or_unroutable(&self, allow_private_addresses: bool) -> bool {
240        let Some(protocol) = self.0.iter().next() else {
241            return true; // Empty address is not routable
242        };
243
244        match protocol {
245            multiaddr::Protocol::Ip4(addr) => {
246                is_ipv4_private_or_unroutable(addr, allow_private_addresses)
247            }
248            multiaddr::Protocol::Ip6(addr) => is_ipv6_private_or_unroutable(addr),
249            multiaddr::Protocol::Dns(hostname) => {
250                !is_valid_fqdn(hostname.as_ref(), allow_private_addresses)
251            }
252            multiaddr::Protocol::Dns4(hostname) => {
253                !is_valid_fqdn(hostname.as_ref(), allow_private_addresses)
254            }
255            multiaddr::Protocol::Dns6(hostname) => {
256                !is_valid_fqdn(hostname.as_ref(), allow_private_addresses)
257            }
258            _ => true, // Other protocol types are not supported
259        }
260    }
261
262    /// Checks if the multiaddr is suitable for public announcement for anemo.
263    /// This includes checking for private/unroutable addresses and valid
264    /// format.
265    pub fn is_valid_public_anemo_address(&self, allow_private_addresses: bool) -> bool {
266        // Check if address is empty
267        if self.is_empty() {
268            return false;
269        }
270
271        // Check if it can be converted to anemo address (format validation)
272        if self.to_anemo_address().is_err() {
273            return false;
274        }
275
276        // Check if it's a private or unroutable address
277        if self.is_private_or_unroutable(allow_private_addresses) {
278            return false;
279        }
280
281        true
282    }
283}
284
285impl std::fmt::Display for Multiaddr {
286    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
287        std::fmt::Display::fmt(&self.0, f)
288    }
289}
290
291impl std::str::FromStr for Multiaddr {
292    type Err = Error;
293
294    fn from_str(s: &str) -> Result<Self, Self::Err> {
295        ::multiaddr::Multiaddr::from_str(s).map(Self)
296    }
297}
298
299impl<'a> TryFrom<&'a str> for Multiaddr {
300    type Error = Error;
301
302    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
303        value.parse()
304    }
305}
306
307impl TryFrom<String> for Multiaddr {
308    type Error = Error;
309
310    fn try_from(value: String) -> Result<Self, Self::Error> {
311        value.parse()
312    }
313}
314
315impl serde::Serialize for Multiaddr {
316    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
317    where
318        S: serde::Serializer,
319    {
320        serializer.serialize_str(&self.0.to_string())
321    }
322}
323
324impl<'de> serde::Deserialize<'de> for Multiaddr {
325    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
326    where
327        D: serde::Deserializer<'de>,
328    {
329        let s = String::deserialize(deserializer)?;
330        s.parse()
331            .map(Self)
332            .map_err(|e| serde::de::Error::custom(e.to_string()))
333    }
334}
335
336impl std::net::ToSocketAddrs for Multiaddr {
337    type Iter = Box<dyn Iterator<Item = SocketAddr>>;
338
339    fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
340        let mut iter = self.iter();
341
342        match (iter.next(), iter.next()) {
343            (Some(Protocol::Ip4(ip4)), Some(Protocol::Tcp(port) | Protocol::Udp(port))) => {
344                (ip4, port)
345                    .to_socket_addrs()
346                    .map(|iter| Box::new(iter) as _)
347            }
348            (Some(Protocol::Ip6(ip6)), Some(Protocol::Tcp(port) | Protocol::Udp(port))) => {
349                (ip6, port)
350                    .to_socket_addrs()
351                    .map(|iter| Box::new(iter) as _)
352            }
353            (Some(Protocol::Dns(hostname)), Some(Protocol::Tcp(port) | Protocol::Udp(port))) => {
354                (hostname.as_ref(), port)
355                    .to_socket_addrs()
356                    .map(|iter| Box::new(iter) as _)
357            }
358            _ => Err(std::io::Error::new(
359                std::io::ErrorKind::InvalidInput,
360                "unable to convert Multiaddr to SocketAddr",
361            )),
362        }
363    }
364}
365
366pub(crate) fn parse_tcp<'a, T: Iterator<Item = Protocol<'a>>>(protocols: &mut T) -> Result<u16> {
367    if let Protocol::Tcp(port) = protocols
368        .next()
369        .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
370    {
371        Ok(port)
372    } else {
373        Err(eyre!("expected tcp protocol"))
374    }
375}
376
377pub(crate) fn parse_http_https<'a, T: Iterator<Item = Protocol<'a>>>(
378    protocols: &mut T,
379) -> Result<&'static str> {
380    match protocols.next() {
381        Some(Protocol::Http) => Ok("http"),
382        Some(Protocol::Https) => Ok("https"),
383        _ => Ok("http"),
384    }
385}
386
387pub(crate) fn parse_end<'a, T: Iterator<Item = Protocol<'a>>>(protocols: &mut T) -> Result<()> {
388    if protocols.next().is_none() {
389        Ok(())
390    } else {
391        Err(eyre!("expected end of multiaddr"))
392    }
393}
394
395// Parse a full /dns/-/tcp/-/{http,https} address
396pub fn parse_dns(address: &Multiaddr) -> Result<(Cow<'_, str>, u16, &'static str)> {
397    let mut iter = address.iter();
398
399    let dns_name = match iter
400        .next()
401        .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
402    {
403        Protocol::Dns(dns_name) => dns_name,
404        other => return Err(eyre!("expected dns found {other}")),
405    };
406    let tcp_port = parse_tcp(&mut iter)?;
407    let http_or_https = parse_http_https(&mut iter)?;
408    parse_end(&mut iter)?;
409    Ok((dns_name, tcp_port, http_or_https))
410}
411
412// Parse a full /ip4/-/tcp/-/{http,https} address
413pub fn parse_ip4(address: &Multiaddr) -> Result<(SocketAddr, &'static str)> {
414    let mut iter = address.iter();
415
416    let ip_addr = match iter
417        .next()
418        .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
419    {
420        Protocol::Ip4(ip4_addr) => IpAddr::V4(ip4_addr),
421        other => return Err(eyre!("expected ip4 found {other}")),
422    };
423    let tcp_port = parse_tcp(&mut iter)?;
424    let http_or_https = parse_http_https(&mut iter)?;
425    parse_end(&mut iter)?;
426    let socket_addr = SocketAddr::new(ip_addr, tcp_port);
427
428    Ok((socket_addr, http_or_https))
429}
430
431// Parse a full /ip6/-/tcp/-/{http,https} address
432pub fn parse_ip6(address: &Multiaddr) -> Result<(SocketAddr, &'static str)> {
433    let mut iter = address.iter();
434
435    let ip_addr = match iter
436        .next()
437        .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
438    {
439        Protocol::Ip6(ip6_addr) => IpAddr::V6(ip6_addr),
440        other => return Err(eyre!("expected ip6 found {other}")),
441    };
442    let tcp_port = parse_tcp(&mut iter)?;
443    let http_or_https = parse_http_https(&mut iter)?;
444    parse_end(&mut iter)?;
445    let socket_addr = SocketAddr::new(ip_addr, tcp_port);
446
447    Ok((socket_addr, http_or_https))
448}
449
450/// Checks if an IPv4 address is private, reserved, or otherwise unroutable on
451/// the public internet.
452fn is_ipv4_private_or_unroutable(addr: Ipv4Addr, allow_private_addresses: bool) -> bool {
453    if !allow_private_addresses && addr.is_private() {
454        // RFC 1918 - Private Networks (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
455        return true;
456    }
457
458    // RFC 1122 - "This" Network (0.0.0.0/8) and Unspecified (0.0.0.0/32)
459    addr.is_unspecified() ||
460    // RFC 1122 - Loopback (127.0.0.0/8)
461    addr.is_loopback() ||
462    // RFC 3927 - Link-Local (169.254.0.0/16)
463    addr.is_link_local() ||
464    // RFC 1112 / RFC 3171 - Multicast (224.0.0.0/4)
465    addr.is_multicast() ||
466    // RFC 6598 - Shared Address Space / Carrier-Grade NAT (100.64.0.0/10)
467    (addr.octets()[0] == 100 && (addr.octets()[1] & 0b11000000) == 64) ||
468    // RFC 6890 - IETF Protocol Assignments (192.0.0.0/24)
469    (addr.octets()[0] == 192 && addr.octets()[1] == 0 && addr.octets()[2] == 0) ||
470    // RFC 5737 - Documentation/TEST-NET addresses (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24)
471    (addr.octets()[0] == 192 && addr.octets()[1] == 0 && addr.octets()[2] == 2) ||
472    (addr.octets()[0] == 198 && addr.octets()[1] == 51 && addr.octets()[2] == 100) ||
473    (addr.octets()[0] == 203 && addr.octets()[1] == 0 && addr.octets()[2] == 113) ||
474    // RFC 7535 - AS112-v4 (192.31.196.0/24)
475    (addr.octets()[0] == 192 && addr.octets()[1] == 31 && addr.octets()[2] == 196) ||
476    // RFC 7450 - Automatic Multicast Tunneling (192.52.193.0/24)
477    (addr.octets()[0] == 192 && addr.octets()[1] == 52 && addr.octets()[2] == 193) ||
478    // RFC 3068 - 6to4 Relay Anycast (192.88.99.0/24)
479    (addr.octets()[0] == 192 && addr.octets()[1] == 88 && addr.octets()[2] == 99) ||
480    // RFC 2544 - Network Interconnect Device Benchmark Testing (198.18.0.0/15)
481    (addr.octets()[0] == 198 && (addr.octets()[1] & 0b11111110) == 18) ||
482    // RFC 1112 - Reserved for Future Use (240.0.0.0/4)
483    (addr.octets()[0] >= 240)
484}
485
486/// Checks if an IPv6 address is private, reserved, or otherwise unroutable on
487/// the public internet.
488fn is_ipv6_private_or_unroutable(addr: Ipv6Addr) -> bool {
489    // RFC 4291 - Unspecified Address (::/128)
490    addr.is_unspecified() ||
491    // RFC 4291 - Loopback Address (::1/128)
492    addr.is_loopback() ||
493    // RFC 4291 - Multicast (ff00::/8)
494    addr.is_multicast() ||
495    // RFC 6666 - Discard-Only Address Block (100::/64)
496    (addr.segments()[0] == 0x0100 && addr.segments()[1] == 0 &&
497     addr.segments()[2] == 0 && addr.segments()[3] == 0) ||
498    // RFC 4380 - Teredo (2001::/32)
499    (addr.segments()[0] == 0x2001 && addr.segments()[1] == 0x0000) ||
500    // RFC 5180 - Benchmarking (2001:2::/48)
501    (addr.segments()[0] == 0x2001 && addr.segments()[1] == 0x0002) ||
502    // RFC 4843 - ORCHID (2001:10::/28)
503    (addr.segments()[0] == 0x2001 && (addr.segments()[1] & 0xfff0) == 0x0010) ||
504    // RFC 3849 - Documentation (2001:db8::/32)
505    (addr.segments()[0] == 0x2001 && addr.segments()[1] == 0x0db8) ||
506    // RFC 3056 - 6to4 (2002::/16)
507    addr.segments()[0] == 0x2002 ||
508    // RFC 4193 - Unique Local Addresses (fc00::/7)
509    (addr.segments()[0] & 0xfe00) == 0xfc00 ||
510    // RFC 3513 - Site-Local (deprecated, fec0::/10)
511    (addr.segments()[0] & 0xffc0) == 0xfec0 ||
512    // RFC 4862 - Link-Local (fe80::/10)
513    (addr.segments()[0] & 0xffc0) == 0xfe80 ||
514    // RFC 4291 - IPv4-mapped IPv6 addresses (::ffff:0:0/96) - check embedded IPv4
515    addr.to_ipv4_mapped().is_some_and(|addr| is_ipv4_private_or_unroutable(addr, false))
516}
517
518/// Checks if a hostname is a valid FQDN (Fully Qualified Domain Name).
519/// Returns true if the hostname is valid for DNS resolution.
520/// This implements RFC-compliant hostname validation with IDNA support for
521/// Unicode domains.
522fn is_valid_fqdn(hostname: &str, allow_localhost_dns: bool) -> bool {
523    if hostname.ends_with('.') {
524        // we do not allow hostnames with dot at the end
525        return false;
526    }
527
528    // Basic length check
529    if hostname.is_empty() || hostname.len() > 253 {
530        return false;
531    }
532
533    let hostname_lower = hostname.to_lowercase();
534    if !allow_localhost_dns {
535        // Reject localhost and local domain variants
536        if hostname_lower == "localhost" || hostname_lower.ends_with(".local") {
537            return false;
538        }
539    } else if hostname_lower == "localhost" {
540        // Skip further checks for exact "localhost"
541        return true;
542    }
543
544    // Try to convert to ASCII using IDNA (handles Unicode domains)
545    let ascii_hostname = match idna::domain_to_ascii(hostname) {
546        Ok(ascii) => ascii,
547        Err(_) => return false, // Invalid Unicode domain
548    };
549
550    // Split into labels
551    let labels: Vec<&str> = ascii_hostname.split('.').collect();
552
553    // Must have at least 2 labels (hostname.tld) for public use
554    if labels.len() < 2 {
555        return false;
556    }
557
558    // Validate each label using ASCII rules (after IDNA conversion)
559    for label in &labels {
560        if !is_valid_dns_label(label) {
561            return false;
562        }
563    }
564
565    // Basic TLD validation: last label should be valid DNS label and >= 2 chars
566    // After IDNA conversion, TLDs can be punycode (xn--...) so we use general DNS
567    // label validation
568    let tld = *labels.last().unwrap();
569    if tld.len() < 2 {
570        return false;
571    }
572
573    // For TLD, we allow punycode (xn--) or pure alphabetic
574    let is_valid_tld = if tld.starts_with("xn--") {
575        // Punycode TLD - already validated as DNS label above
576        true
577    } else {
578        // Regular TLD - should be alphabetic only
579        tld.chars().all(|c| c.is_ascii_alphabetic())
580    };
581
582    if !is_valid_tld {
583        return false;
584    }
585
586    true
587}
588
589/// Validates a single DNS label according to RFC 1035
590fn is_valid_dns_label(label: &str) -> bool {
591    // Label length: 1-63 characters (RFC 1035)
592    if label.is_empty() || label.len() > 63 {
593        return false;
594    }
595
596    let bytes = label.as_bytes();
597
598    // Must start and end with alphanumeric character (no hyphens at boundaries)
599    if !bytes[0].is_ascii_alphanumeric() || !bytes[bytes.len() - 1].is_ascii_alphanumeric() {
600        return false;
601    }
602
603    // Can only contain alphanumeric characters and hyphens
604    bytes
605        .iter()
606        .all(|&b| b.is_ascii_alphanumeric() || b == b'-')
607}
608
609#[cfg(test)]
610mod test {
611    use multiaddr::multiaddr;
612
613    use super::Multiaddr;
614
615    #[test]
616    fn document_multiaddr_limitation_for_unix_protocol() {
617        // You can construct a multiaddr by hand (ie binary format) just fine
618        let path = "/tmp/foo";
619        let addr = Multiaddr::new_internal(multiaddr!(Unix(path), Http));
620
621        // But it doesn't round-trip in the human readable format
622        let s = addr.to_string();
623        assert!(s.parse::<Multiaddr>().is_err());
624    }
625
626    #[test]
627    fn test_to_socket_addr_basic() {
628        let multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)));
629        let socket_addr_ipv4 = multi_addr_ipv4
630            .to_socket_addr()
631            .expect("Couldn't convert to socket addr");
632        assert_eq!(socket_addr_ipv4.to_string(), "127.0.0.1:10500");
633
634        let multi_addr_ipv6 = Multiaddr(multiaddr!(Ip6([172, 0, 0, 1, 1, 1, 1, 1]), Tcp(10500u16)));
635        let socket_addr_ipv6 = multi_addr_ipv6
636            .to_socket_addr()
637            .expect("Couldn't convert to socket addr");
638        assert_eq!(socket_addr_ipv6.to_string(), "[ac::1:1:1:1:1]:10500");
639    }
640
641    #[test]
642    fn test_to_socket_addr_unsupported_protocol() {
643        let multi_addr_dns = Multiaddr(multiaddr!(Dnsaddr("iota.iota"), Tcp(10500u16)));
644        let _ = multi_addr_dns
645            .to_socket_addr()
646            .expect_err("DNS is unsupported");
647    }
648
649    #[test]
650    fn test_is_loosely_valid_tcp_addr() {
651        let multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)));
652        assert!(multi_addr_ipv4.is_loosely_valid_tcp_addr());
653        let multi_addr_ipv6 = Multiaddr(multiaddr!(Ip6([172, 0, 0, 1, 1, 1, 1, 1]), Tcp(10500u16)));
654        assert!(multi_addr_ipv6.is_loosely_valid_tcp_addr());
655        let multi_addr_dns = Multiaddr(multiaddr!(Dnsaddr("iota.iota"), Tcp(10500u16)));
656        assert!(multi_addr_dns.is_loosely_valid_tcp_addr());
657
658        let multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Udp(10500u16)));
659        assert!(!multi_addr_ipv4.is_loosely_valid_tcp_addr());
660        let multi_addr_ipv6 = Multiaddr(multiaddr!(Ip6([172, 0, 0, 1, 1, 1, 1, 1]), Udp(10500u16)));
661        assert!(!multi_addr_ipv6.is_loosely_valid_tcp_addr());
662        let multi_addr_dns = Multiaddr(multiaddr!(Dnsaddr("iota.iota"), Udp(10500u16)));
663        assert!(!multi_addr_dns.is_loosely_valid_tcp_addr());
664
665        let invalid_multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1])));
666        assert!(!invalid_multi_addr_ipv4.is_loosely_valid_tcp_addr());
667    }
668
669    #[test]
670    fn test_get_hostname_port() {
671        let multi_addr_ip4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)));
672        assert_eq!(Some("127.0.0.1".to_string()), multi_addr_ip4.hostname());
673        assert_eq!(Some(10500u16), multi_addr_ip4.port());
674
675        let multi_addr_dns = Multiaddr(multiaddr!(Dns("iota.iota"), Tcp(10501u16)));
676        assert_eq!(Some("iota.iota".to_string()), multi_addr_dns.hostname());
677        assert_eq!(Some(10501u16), multi_addr_dns.port());
678    }
679
680    #[test]
681    fn test_to_anemo_address() {
682        let addr_ip4 = Multiaddr(multiaddr!(Ip4([15, 15, 15, 1]), Udp(10500u16)))
683            .to_anemo_address()
684            .unwrap();
685        assert_eq!("15.15.15.1:10500".to_string(), addr_ip4.to_string());
686
687        let addr_ip6 = Multiaddr(multiaddr!(
688            Ip6([15, 15, 15, 15, 15, 15, 15, 1]),
689            Udp(10500u16)
690        ))
691        .to_anemo_address()
692        .unwrap();
693        assert_eq!("[f:f:f:f:f:f:f:1]:10500".to_string(), addr_ip6.to_string());
694
695        let addr_dns = Multiaddr(multiaddr!(Dns("iota.iota"), Udp(10501u16)))
696            .to_anemo_address()
697            .unwrap();
698        assert_eq!("iota.iota:10501".to_string(), addr_dns.to_string());
699
700        let addr_invalid =
701            Multiaddr(multiaddr!(Dns("iota.iota"), Tcp(10501u16))).to_anemo_address();
702        assert!(addr_invalid.is_err());
703    }
704
705    #[test]
706    fn test_with_zero_ip() {
707        let multi_addr_ip4 =
708            Multiaddr(multiaddr!(Ip4([15, 15, 15, 1]), Tcp(10500u16))).with_zero_ip();
709        assert_eq!(Some("0.0.0.0".to_string()), multi_addr_ip4.hostname());
710        assert_eq!(Some(10500u16), multi_addr_ip4.port());
711
712        let multi_addr_ip6 = Multiaddr(multiaddr!(
713            Ip6([15, 15, 15, 15, 15, 15, 15, 1]),
714            Tcp(10500u16)
715        ))
716        .with_zero_ip();
717        assert_eq!(Some("::".to_string()), multi_addr_ip6.hostname());
718        assert_eq!(Some(10500u16), multi_addr_ip4.port());
719
720        let multi_addr_dns = Multiaddr(multiaddr!(Dns("iota.iota"), Tcp(10501u16))).with_zero_ip();
721        assert_eq!(Some("0.0.0.0".to_string()), multi_addr_dns.hostname());
722        assert_eq!(Some(10501u16), multi_addr_dns.port());
723    }
724
725    #[test]
726    fn test_with_localhost_ip() {
727        let multi_addr_ip4 =
728            Multiaddr(multiaddr!(Ip4([15, 15, 15, 1]), Tcp(10500u16))).with_localhost_ip();
729        assert_eq!(Some("127.0.0.1".to_string()), multi_addr_ip4.hostname());
730        assert_eq!(Some(10500u16), multi_addr_ip4.port());
731
732        let multi_addr_ip6 = Multiaddr(multiaddr!(
733            Ip6([15, 15, 15, 15, 15, 15, 15, 1]),
734            Tcp(10500u16)
735        ))
736        .with_localhost_ip();
737        assert_eq!(Some("::1".to_string()), multi_addr_ip6.hostname());
738        assert_eq!(Some(10500u16), multi_addr_ip4.port());
739
740        let multi_addr_dns =
741            Multiaddr(multiaddr!(Dns("iota.iota"), Tcp(10501u16))).with_localhost_ip();
742        assert_eq!(Some("127.0.0.1".to_string()), multi_addr_dns.hostname());
743        assert_eq!(Some(10501u16), multi_addr_dns.port());
744    }
745
746    #[test]
747    fn test_is_private_or_unroutable_ipv4() {
748        // Test cases: (multiaddr, description, should_be_filtered)
749        let test_cases = vec![
750            // Private addresses (RFC 1918)
751            (
752                multiaddr!(Ip4([10, 0, 0, 1]), Udp(10500u16)),
753                "RFC 1918 private - 10.0.0.0/8",
754                true,
755            ),
756            (
757                multiaddr!(Ip4([172, 16, 0, 1]), Udp(10500u16)),
758                "RFC 1918 private - 172.16.0.0/12",
759                true,
760            ),
761            (
762                multiaddr!(Ip4([192, 168, 1, 1]), Udp(10500u16)),
763                "RFC 1918 private - 192.168.0.0/16",
764                true,
765            ),
766            // Loopback (RFC 1122)
767            (
768                multiaddr!(Ip4([127, 0, 0, 1]), Udp(10500u16)),
769                "RFC 1122 loopback",
770                true,
771            ),
772            // Link-local (RFC 3927)
773            (
774                multiaddr!(Ip4([169, 254, 1, 1]), Udp(10500u16)),
775                "RFC 3927 link-local",
776                true,
777            ),
778            // Unspecified (RFC 1122)
779            (
780                multiaddr!(Ip4([0, 0, 0, 0]), Udp(10500u16)),
781                "RFC 1122 unspecified",
782                true,
783            ),
784            // Multicast (RFC 3171)
785            (
786                multiaddr!(Ip4([224, 0, 0, 1]), Udp(10500u16)),
787                "RFC 3171 multicast",
788                true,
789            ),
790            // Carrier-grade NAT (RFC 6598)
791            (
792                multiaddr!(Ip4([100, 64, 0, 1]), Udp(10500u16)),
793                "RFC 6598 carrier-grade NAT",
794                true,
795            ),
796            // IETF Protocol Assignments (RFC 6890)
797            (
798                multiaddr!(Ip4([192, 0, 0, 1]), Udp(10500u16)),
799                "RFC 6890 IETF Protocol Assignments - 192.0.0.0/24",
800                true,
801            ),
802            // AS112-v4 (RFC 7535)
803            (
804                multiaddr!(Ip4([192, 31, 196, 1]), Udp(10500u16)),
805                "RFC 7535 AS112-v4 - 192.31.196.0/24",
806                true,
807            ),
808            // Automatic Multicast Tunneling (RFC 7450)
809            (
810                multiaddr!(Ip4([192, 52, 193, 1]), Udp(10500u16)),
811                "RFC 7450 Automatic Multicast Tunneling - 192.52.193.0/24",
812                true,
813            ),
814            // Documentation addresses (RFC 5737)
815            (
816                multiaddr!(Ip4([192, 0, 2, 1]), Udp(10500u16)),
817                "RFC 5737 documentation - 192.0.2.0/24",
818                true,
819            ),
820            (
821                multiaddr!(Ip4([198, 51, 100, 1]), Udp(10500u16)),
822                "RFC 5737 documentation - 198.51.100.0/24",
823                true,
824            ),
825            (
826                multiaddr!(Ip4([203, 0, 113, 1]), Udp(10500u16)),
827                "RFC 5737 documentation - 203.0.113.0/24",
828                true,
829            ),
830            // Benchmarking (RFC 2544)
831            (
832                multiaddr!(Ip4([198, 18, 0, 1]), Udp(10500u16)),
833                "RFC 2544 benchmarking - 198.18.0.0/15",
834                true,
835            ),
836            // Public addresses should not be filtered
837            (
838                multiaddr!(Ip4([8, 8, 8, 8]), Udp(10500u16)),
839                "Google DNS - should not be filtered",
840                false,
841            ),
842            (
843                multiaddr!(Ip4([1, 1, 1, 1]), Udp(10500u16)),
844                "Cloudflare DNS - should not be filtered",
845                false,
846            ),
847            (
848                multiaddr!(Ip4([208, 67, 222, 222]), Udp(10500u16)),
849                "OpenDNS - should not be filtered",
850                false,
851            ),
852        ];
853
854        for (multiaddr, description, should_be_filtered) in test_cases {
855            let addr = Multiaddr(multiaddr);
856            let is_filtered = addr.is_private_or_unroutable(false);
857            assert_eq!(
858                is_filtered, should_be_filtered,
859                "Failed for {description}: expected {should_be_filtered} but got {is_filtered}",
860            );
861        }
862    }
863
864    #[test]
865    fn test_is_private_or_unroutable_ipv6() {
866        // Test cases: (multiaddr, description, should_be_filtered)
867        let test_cases = vec![
868            // Loopback (RFC 4291)
869            (
870                multiaddr!(Ip6([0, 0, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
871                "RFC 4291 loopback",
872                true,
873            ),
874            // Unspecified (RFC 4291)
875            (
876                multiaddr!(Ip6([0, 0, 0, 0, 0, 0, 0, 0]), Udp(10500u16)),
877                "RFC 4291 unspecified",
878                true,
879            ),
880            // Discard-Only Address Block (RFC 6666)
881            (
882                multiaddr!(Ip6([0x0100, 0, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
883                "RFC 6666 discard-only - 100::/64",
884                true,
885            ),
886            // Unique local addresses (RFC 4193) - fc00::/7
887            (
888                multiaddr!(Ip6([0xfc00, 0, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
889                "RFC 4193 unique local - fc00::/7",
890                true,
891            ),
892            (
893                multiaddr!(Ip6([0xfd00, 0, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
894                "RFC 4193 unique local - fd00::/7",
895                true,
896            ),
897            // Link-local addresses (RFC 4862) - fe80::/10
898            (
899                multiaddr!(Ip6([0xfe80, 0, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
900                "RFC 4862 link-local",
901                true,
902            ),
903            // Benchmarking (RFC 5180) - 2001:2::/48
904            (
905                multiaddr!(Ip6([0x2001, 0x0002, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
906                "RFC 5180 benchmarking - 2001:2::/48",
907                true,
908            ),
909            // Documentation addresses (RFC 3849) - 2001:db8::/32
910            (
911                multiaddr!(Ip6([0x2001, 0x0db8, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
912                "RFC 3849 documentation",
913                true,
914            ),
915            // Multicast addresses
916            (
917                multiaddr!(Ip6([0xff02, 0, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
918                "IPv6 multicast",
919                true,
920            ),
921            // Public addresses should not be filtered
922            (
923                multiaddr!(
924                    Ip6([0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x68]),
925                    Udp(10500u16)
926                ),
927                "Google DNS IPv6 - should not be filtered",
928                false,
929            ),
930            (
931                multiaddr!(Ip6([0x2606, 0x4700, 0x10, 0, 0, 0, 0, 0x68]), Udp(10500u16)),
932                "Cloudflare DNS IPv6 - should not be filtered",
933                false,
934            ),
935        ];
936
937        for (multiaddr, description, should_be_filtered) in test_cases {
938            let addr = Multiaddr(multiaddr);
939            let is_filtered = addr.is_private_or_unroutable(false);
940            assert_eq!(
941                is_filtered, should_be_filtered,
942                "Failed for {description}: expected {should_be_filtered} but got {is_filtered}",
943            );
944        }
945    }
946
947    #[test]
948    fn test_is_private_or_unroutable_dns() {
949        // Test cases: (multiaddr, description, should_be_private)
950        let test_cases = vec![
951            (
952                multiaddr!(Dns("iota.org"), Udp(10500u16)),
953                "DNS addresses should be allowed for public discovery",
954                false,
955            ),
956            (
957                multiaddr!(Dns4("iota.org"), Udp(10500u16)),
958                "DNS4 addresses should be allowed for public discovery",
959                false,
960            ),
961            (
962                multiaddr!(Dns6("iota.org"), Udp(10500u16)),
963                "DNS6 addresses should be allowed for public discovery",
964                false,
965            ),
966        ];
967
968        for (multiaddr, description, should_be_private) in test_cases {
969            let addr = Multiaddr(multiaddr);
970            let is_private = addr.is_private_or_unroutable(false);
971            assert_eq!(
972                is_private, should_be_private,
973                "Failed for {description}: expected {should_be_private} but got {is_private}",
974            );
975        }
976    }
977
978    #[test]
979    fn test_is_valid_for_public_announcement() {
980        // Test cases: (multiaddr, description, should_be_valid)
981        let test_cases = vec![
982            (
983                multiaddr!(Ip4([192, 168, 1, 1]), Udp(10500u16)),
984                "Private IPv4 address should be invalid",
985                false,
986            ),
987            (
988                multiaddr!(Ip4([127, 0, 0, 1]), Udp(10500u16)),
989                "Loopback IPv4 address should be invalid",
990                false,
991            ),
992            (
993                multiaddr!(Ip4([8, 8, 8, 8]), Udp(10500u16)),
994                "Valid public IPv4 address should be valid",
995                true,
996            ),
997            (
998                multiaddr!(Dns("example.com"), Udp(10500u16)),
999                "Valid DNS address should be valid",
1000                true,
1001            ),
1002            (
1003                multiaddr!(Ip4([8, 8, 8, 8]), Tcp(10500u16)),
1004                "TCP instead of UDP should be invalid for anemo",
1005                false,
1006            ),
1007        ];
1008
1009        for (multiaddr, description, should_be_valid) in test_cases {
1010            let addr = Multiaddr(multiaddr);
1011            let is_valid = addr.is_valid_public_anemo_address(false);
1012            assert_eq!(
1013                is_valid, should_be_valid,
1014                "Failed for {description}: expected {should_be_valid} but got {is_valid}",
1015            );
1016        }
1017    }
1018
1019    #[test]
1020    fn test_is_valid_fqdn() {
1021        use super::is_valid_fqdn;
1022
1023        // Valid FQDNs - domains with proper structure
1024        let valid_cases = vec![
1025            "example.com",
1026            "subdomain.example.com",
1027            "iota.org",
1028            "api.iota.org",
1029            "test123.example-domain.org",
1030            "google.com",
1031            "github.com",
1032            "example.co", // Two-letter TLD
1033            "test.example.net",
1034            "api-v1.service.io",
1035            "very-long-subdomain-name-that-is-still-valid.example.org",
1036            // Unicode domains (IDNA)
1037            "café.com",         // Unicode characters
1038            "москва.рф",        // Cyrillic
1039            "τεστ.gr",          // Greek
1040            "测试.中国",        // Chinese
1041            "xn--nxasmq6b.com", // Already punycode-encoded domain
1042        ];
1043
1044        for fqdn in valid_cases {
1045            assert!(
1046                is_valid_fqdn(fqdn, false),
1047                "Expected '{fqdn}' to be a valid FQDN",
1048            );
1049        }
1050
1051        // Invalid FQDNs
1052        let long_domain = "a".repeat(254);
1053        let long_label = "a".repeat(64);
1054        let long_label_domain = format!("{long_label}.com");
1055        let invalid_cases = vec![
1056            "",                 // Empty string
1057            "localhost",        // Localhost should be rejected
1058            "test.local",       // .local domain
1059            "hostname",         // Single label (no TLD)
1060            "a.b",              // Single char TLD
1061            "example.1",        // Numeric TLD
1062            "example.c1",       // Alphanumeric TLD
1063            ".",                // Just a dot
1064            ".example.com",     // Leading dot
1065            "example.com.",     // Trailing dot (absolute FQDN)
1066            "-example.com",     // Label starting with hyphen
1067            "example-.com",     // Label ending with hyphen
1068            "exam_ple.com",     // Underscore not allowed
1069            "exam ple.com",     // Space not allowed
1070            &long_domain,       // Domain name too long (254 chars)
1071            &long_label_domain, // Label too long (64 chars)
1072            "example..com",     // Double dot
1073            "192.168.1.1",      // IP address (should use IP protocols)
1074            "2001:db8::1",      // IPv6 address (should use IP protocols)
1075            "example.com-",     // Trailing hyphen in TLD
1076            "example.",         // Empty TLD
1077            // Invalid Unicode domains
1078            "invalid\u{200D}.com", // Zero-width joiner (invalid in domain)
1079            "test\u{0000}.com",    // Null character (invalid)
1080        ];
1081
1082        for fqdn in invalid_cases {
1083            assert!(
1084                !is_valid_fqdn(fqdn, false),
1085                "Expected '{fqdn}' to be an invalid FQDN"
1086            );
1087        }
1088    }
1089
1090    #[test]
1091    fn test_is_valid_dns_label() {
1092        use super::is_valid_dns_label;
1093
1094        // Valid DNS labels
1095        let max_length_label = "a".repeat(63);
1096        let valid_cases = vec![
1097            "a",
1098            "ab",
1099            "example",
1100            "test123",
1101            "api-v1",
1102            "sub-domain",
1103            "a1b2c3",
1104            "123",             // All numeric is valid per RFC 1035
1105            &max_length_label, // Max length (63 chars)
1106        ];
1107
1108        for label in valid_cases {
1109            assert!(
1110                is_valid_dns_label(label),
1111                "Expected '{label}' to be a valid DNS label"
1112            );
1113        }
1114
1115        // Invalid DNS labels
1116        let too_long_label = "a".repeat(64);
1117        let invalid_cases = vec![
1118            "",              // Empty
1119            "-example",      // Starts with hyphen
1120            "example-",      // Ends with hyphen
1121            "ex_ample",      // Contains underscore
1122            "ex ample",      // Contains space
1123            "ex.ample",      // Contains dot
1124            &too_long_label, // Too long (64 chars)
1125        ];
1126
1127        for label in invalid_cases {
1128            assert!(
1129                !is_valid_dns_label(label),
1130                "Expected '{label}' to be an invalid DNS label"
1131            );
1132        }
1133    }
1134
1135    #[test]
1136    fn test_dns_validation_in_multiaddr() {
1137        // Test that DNS validation is properly integrated into multiaddr validation
1138
1139        // Valid DNS addresses should not be filtered
1140        let valid_dns_cases = vec![
1141            multiaddr!(Dns("example.com"), Udp(10500u16)),
1142            multiaddr!(Dns("iota.org"), Udp(10500u16)),
1143            multiaddr!(Dns("api.example.com"), Udp(10500u16)),
1144            // Unicode domains
1145            multiaddr!(Dns("café.com"), Udp(10500u16)),
1146            multiaddr!(Dns("москва.рф"), Udp(10500u16)),
1147        ];
1148
1149        for addr in valid_dns_cases {
1150            let multiaddr = Multiaddr(addr);
1151            assert!(
1152                !multiaddr.is_private_or_unroutable(false),
1153                "Valid DNS address {multiaddr} should not be filtered as private/unroutable"
1154            );
1155            assert!(
1156                multiaddr.is_valid_public_anemo_address(false),
1157                "Valid DNS address {multiaddr} should be valid for public announcement"
1158            );
1159        }
1160
1161        // Invalid DNS addresses should be filtered
1162        let invalid_dns_cases = vec![
1163            multiaddr!(Dns("localhost"), Udp(10500u16)),
1164            multiaddr!(Dns("hostname.local"), Udp(10500u16)),
1165            multiaddr!(Dns("hostname"), Udp(10500u16)), // Single label
1166            multiaddr!(Dns(""), Udp(10500u16)),         // Empty
1167        ];
1168
1169        for addr in invalid_dns_cases {
1170            let multiaddr = Multiaddr(addr);
1171            assert!(
1172                multiaddr.is_private_or_unroutable(false),
1173                "Invalid DNS address {multiaddr} should be filtered as private/unroutable"
1174            );
1175            assert!(
1176                !multiaddr.is_valid_public_anemo_address(false),
1177                "Invalid DNS address {multiaddr} should not be valid for public announcement"
1178            );
1179        }
1180    }
1181}