1use 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 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 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 pub fn is_loosely_valid_tcp_addr(&self) -> bool {
107 let mut iter = self.iter();
108 iter.next(); match iter.next() {
110 Some(Protocol::Tcp(_)) => true,
111 _ => false, }
113 }
114
115 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 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 pub fn is_private_or_unroutable(&self, allow_private_addresses: bool) -> bool {
240 let Some(protocol) = self.0.iter().next() else {
241 return true; };
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, }
260 }
261
262 pub fn is_valid_public_anemo_address(&self, allow_private_addresses: bool) -> bool {
266 if self.is_empty() {
268 return false;
269 }
270
271 if self.to_anemo_address().is_err() {
273 return false;
274 }
275
276 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
395pub 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
412pub 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
431pub 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
450fn is_ipv4_private_or_unroutable(addr: Ipv4Addr, allow_private_addresses: bool) -> bool {
453 if !allow_private_addresses && addr.is_private() {
454 return true;
456 }
457
458 addr.is_unspecified() ||
460 addr.is_loopback() ||
462 addr.is_link_local() ||
464 addr.is_multicast() ||
466 (addr.octets()[0] == 100 && (addr.octets()[1] & 0b11000000) == 64) ||
468 (addr.octets()[0] == 192 && addr.octets()[1] == 0 && addr.octets()[2] == 0) ||
470 (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 (addr.octets()[0] == 192 && addr.octets()[1] == 31 && addr.octets()[2] == 196) ||
476 (addr.octets()[0] == 192 && addr.octets()[1] == 52 && addr.octets()[2] == 193) ||
478 (addr.octets()[0] == 192 && addr.octets()[1] == 88 && addr.octets()[2] == 99) ||
480 (addr.octets()[0] == 198 && (addr.octets()[1] & 0b11111110) == 18) ||
482 (addr.octets()[0] >= 240)
484}
485
486fn is_ipv6_private_or_unroutable(addr: Ipv6Addr) -> bool {
489 addr.is_unspecified() ||
491 addr.is_loopback() ||
493 addr.is_multicast() ||
495 (addr.segments()[0] == 0x0100 && addr.segments()[1] == 0 &&
497 addr.segments()[2] == 0 && addr.segments()[3] == 0) ||
498 (addr.segments()[0] == 0x2001 && addr.segments()[1] == 0x0000) ||
500 (addr.segments()[0] == 0x2001 && addr.segments()[1] == 0x0002) ||
502 (addr.segments()[0] == 0x2001 && (addr.segments()[1] & 0xfff0) == 0x0010) ||
504 (addr.segments()[0] == 0x2001 && addr.segments()[1] == 0x0db8) ||
506 addr.segments()[0] == 0x2002 ||
508 (addr.segments()[0] & 0xfe00) == 0xfc00 ||
510 (addr.segments()[0] & 0xffc0) == 0xfec0 ||
512 (addr.segments()[0] & 0xffc0) == 0xfe80 ||
514 addr.to_ipv4_mapped().is_some_and(|addr| is_ipv4_private_or_unroutable(addr, false))
516}
517
518fn is_valid_fqdn(hostname: &str, allow_localhost_dns: bool) -> bool {
523 if hostname.ends_with('.') {
524 return false;
526 }
527
528 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 if hostname_lower == "localhost" || hostname_lower.ends_with(".local") {
537 return false;
538 }
539 } else if hostname_lower == "localhost" {
540 return true;
542 }
543
544 let ascii_hostname = match idna::domain_to_ascii(hostname) {
546 Ok(ascii) => ascii,
547 Err(_) => return false, };
549
550 let labels: Vec<&str> = ascii_hostname.split('.').collect();
552
553 if labels.len() < 2 {
555 return false;
556 }
557
558 for label in &labels {
560 if !is_valid_dns_label(label) {
561 return false;
562 }
563 }
564
565 let tld = *labels.last().unwrap();
569 if tld.len() < 2 {
570 return false;
571 }
572
573 let is_valid_tld = if tld.starts_with("xn--") {
575 true
577 } else {
578 tld.chars().all(|c| c.is_ascii_alphabetic())
580 };
581
582 if !is_valid_tld {
583 return false;
584 }
585
586 true
587}
588
589fn is_valid_dns_label(label: &str) -> bool {
591 if label.is_empty() || label.len() > 63 {
593 return false;
594 }
595
596 let bytes = label.as_bytes();
597
598 if !bytes[0].is_ascii_alphanumeric() || !bytes[bytes.len() - 1].is_ascii_alphanumeric() {
600 return false;
601 }
602
603 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 let path = "/tmp/foo";
619 let addr = Multiaddr::new_internal(multiaddr!(Unix(path), Http));
620
621 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 let test_cases = vec![
750 (
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 (
768 multiaddr!(Ip4([127, 0, 0, 1]), Udp(10500u16)),
769 "RFC 1122 loopback",
770 true,
771 ),
772 (
774 multiaddr!(Ip4([169, 254, 1, 1]), Udp(10500u16)),
775 "RFC 3927 link-local",
776 true,
777 ),
778 (
780 multiaddr!(Ip4([0, 0, 0, 0]), Udp(10500u16)),
781 "RFC 1122 unspecified",
782 true,
783 ),
784 (
786 multiaddr!(Ip4([224, 0, 0, 1]), Udp(10500u16)),
787 "RFC 3171 multicast",
788 true,
789 ),
790 (
792 multiaddr!(Ip4([100, 64, 0, 1]), Udp(10500u16)),
793 "RFC 6598 carrier-grade NAT",
794 true,
795 ),
796 (
798 multiaddr!(Ip4([192, 0, 0, 1]), Udp(10500u16)),
799 "RFC 6890 IETF Protocol Assignments - 192.0.0.0/24",
800 true,
801 ),
802 (
804 multiaddr!(Ip4([192, 31, 196, 1]), Udp(10500u16)),
805 "RFC 7535 AS112-v4 - 192.31.196.0/24",
806 true,
807 ),
808 (
810 multiaddr!(Ip4([192, 52, 193, 1]), Udp(10500u16)),
811 "RFC 7450 Automatic Multicast Tunneling - 192.52.193.0/24",
812 true,
813 ),
814 (
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 (
832 multiaddr!(Ip4([198, 18, 0, 1]), Udp(10500u16)),
833 "RFC 2544 benchmarking - 198.18.0.0/15",
834 true,
835 ),
836 (
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 let test_cases = vec![
868 (
870 multiaddr!(Ip6([0, 0, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
871 "RFC 4291 loopback",
872 true,
873 ),
874 (
876 multiaddr!(Ip6([0, 0, 0, 0, 0, 0, 0, 0]), Udp(10500u16)),
877 "RFC 4291 unspecified",
878 true,
879 ),
880 (
882 multiaddr!(Ip6([0x0100, 0, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
883 "RFC 6666 discard-only - 100::/64",
884 true,
885 ),
886 (
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 (
899 multiaddr!(Ip6([0xfe80, 0, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
900 "RFC 4862 link-local",
901 true,
902 ),
903 (
905 multiaddr!(Ip6([0x2001, 0x0002, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
906 "RFC 5180 benchmarking - 2001:2::/48",
907 true,
908 ),
909 (
911 multiaddr!(Ip6([0x2001, 0x0db8, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
912 "RFC 3849 documentation",
913 true,
914 ),
915 (
917 multiaddr!(Ip6([0xff02, 0, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
918 "IPv6 multicast",
919 true,
920 ),
921 (
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 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 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 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", "test.example.net",
1034 "api-v1.service.io",
1035 "very-long-subdomain-name-that-is-still-valid.example.org",
1036 "café.com", "москва.рф", "τεστ.gr", "测试.中国", "xn--nxasmq6b.com", ];
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 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 "", "localhost", "test.local", "hostname", "a.b", "example.1", "example.c1", ".", ".example.com", "example.com.", "-example.com", "example-.com", "exam_ple.com", "exam ple.com", &long_domain, &long_label_domain, "example..com", "192.168.1.1", "2001:db8::1", "example.com-", "example.", "invalid\u{200D}.com", "test\u{0000}.com", ];
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 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", &max_length_label, ];
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 let too_long_label = "a".repeat(64);
1117 let invalid_cases = vec![
1118 "", "-example", "example-", "ex_ample", "ex ample", "ex.ample", &too_long_label, ];
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 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 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 let invalid_dns_cases = vec![
1163 multiaddr!(Dns("localhost"), Udp(10500u16)),
1164 multiaddr!(Dns("hostname.local"), Udp(10500u16)),
1165 multiaddr!(Dns("hostname"), Udp(10500u16)), multiaddr!(Dns(""), Udp(10500u16)), ];
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}