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 is_private_or_unroutable(&self, allow_private_addresses: bool) -> bool {
226 let Some(protocol) = self.0.iter().next() else {
227 return true; };
229
230 match protocol {
231 multiaddr::Protocol::Ip4(addr) => {
232 is_ipv4_private_or_unroutable(addr, allow_private_addresses)
233 }
234 multiaddr::Protocol::Ip6(addr) => is_ipv6_private_or_unroutable(addr),
235 multiaddr::Protocol::Dns(hostname) => {
236 !is_valid_fqdn(hostname.as_ref(), allow_private_addresses)
237 }
238 multiaddr::Protocol::Dns4(hostname) => {
239 !is_valid_fqdn(hostname.as_ref(), allow_private_addresses)
240 }
241 multiaddr::Protocol::Dns6(hostname) => {
242 !is_valid_fqdn(hostname.as_ref(), allow_private_addresses)
243 }
244 _ => true, }
246 }
247
248 pub fn is_valid_public_anemo_address(&self, allow_private_addresses: bool) -> bool {
252 if self.is_empty() {
254 return false;
255 }
256
257 if self.to_anemo_address().is_err() {
259 return false;
260 }
261
262 if self.is_private_or_unroutable(allow_private_addresses) {
264 return false;
265 }
266
267 true
268 }
269}
270
271impl std::fmt::Display for Multiaddr {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273 std::fmt::Display::fmt(&self.0, f)
274 }
275}
276
277impl std::str::FromStr for Multiaddr {
278 type Err = Error;
279
280 fn from_str(s: &str) -> Result<Self, Self::Err> {
281 ::multiaddr::Multiaddr::from_str(s).map(Self)
282 }
283}
284
285impl<'a> TryFrom<&'a str> for Multiaddr {
286 type Error = Error;
287
288 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
289 value.parse()
290 }
291}
292
293impl TryFrom<String> for Multiaddr {
294 type Error = Error;
295
296 fn try_from(value: String) -> Result<Self, Self::Error> {
297 value.parse()
298 }
299}
300
301impl serde::Serialize for Multiaddr {
302 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
303 where
304 S: serde::Serializer,
305 {
306 serializer.serialize_str(&self.0.to_string())
307 }
308}
309
310impl<'de> serde::Deserialize<'de> for Multiaddr {
311 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
312 where
313 D: serde::Deserializer<'de>,
314 {
315 let s = String::deserialize(deserializer)?;
316 s.parse()
317 .map(Self)
318 .map_err(|e| serde::de::Error::custom(e.to_string()))
319 }
320}
321
322impl std::net::ToSocketAddrs for Multiaddr {
323 type Iter = Box<dyn Iterator<Item = SocketAddr>>;
324
325 fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
326 let mut iter = self.iter();
327
328 match (iter.next(), iter.next()) {
329 (Some(Protocol::Ip4(ip4)), Some(Protocol::Tcp(port) | Protocol::Udp(port))) => {
330 (ip4, port)
331 .to_socket_addrs()
332 .map(|iter| Box::new(iter) as _)
333 }
334 (Some(Protocol::Ip6(ip6)), Some(Protocol::Tcp(port) | Protocol::Udp(port))) => {
335 (ip6, port)
336 .to_socket_addrs()
337 .map(|iter| Box::new(iter) as _)
338 }
339 (Some(Protocol::Dns(hostname)), Some(Protocol::Tcp(port) | Protocol::Udp(port))) => {
340 (hostname.as_ref(), port)
341 .to_socket_addrs()
342 .map(|iter| Box::new(iter) as _)
343 }
344 _ => Err(std::io::Error::new(
345 std::io::ErrorKind::InvalidInput,
346 "unable to convert Multiaddr to SocketAddr",
347 )),
348 }
349 }
350}
351
352pub(crate) fn parse_tcp<'a, T: Iterator<Item = Protocol<'a>>>(protocols: &mut T) -> Result<u16> {
353 if let Protocol::Tcp(port) = protocols
354 .next()
355 .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
356 {
357 Ok(port)
358 } else {
359 Err(eyre!("expected tcp protocol"))
360 }
361}
362
363pub(crate) fn parse_http_https<'a, T: Iterator<Item = Protocol<'a>>>(
364 protocols: &mut T,
365) -> Result<&'static str> {
366 match protocols.next() {
367 Some(Protocol::Http) => Ok("http"),
368 Some(Protocol::Https) => Ok("https"),
369 _ => Ok("http"),
370 }
371}
372
373pub(crate) fn parse_end<'a, T: Iterator<Item = Protocol<'a>>>(protocols: &mut T) -> Result<()> {
374 if protocols.next().is_none() {
375 Ok(())
376 } else {
377 Err(eyre!("expected end of multiaddr"))
378 }
379}
380
381pub(crate) fn parse_dns(address: &Multiaddr) -> Result<(Cow<'_, str>, u16, &'static str)> {
383 let mut iter = address.iter();
384
385 let dns_name = match iter
386 .next()
387 .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
388 {
389 Protocol::Dns(dns_name) => dns_name,
390 other => return Err(eyre!("expected dns found {other}")),
391 };
392 let tcp_port = parse_tcp(&mut iter)?;
393 let http_or_https = parse_http_https(&mut iter)?;
394 parse_end(&mut iter)?;
395 Ok((dns_name, tcp_port, http_or_https))
396}
397
398pub(crate) fn parse_ip4(address: &Multiaddr) -> Result<(SocketAddr, &'static str)> {
400 let mut iter = address.iter();
401
402 let ip_addr = match iter
403 .next()
404 .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
405 {
406 Protocol::Ip4(ip4_addr) => IpAddr::V4(ip4_addr),
407 other => return Err(eyre!("expected ip4 found {other}")),
408 };
409 let tcp_port = parse_tcp(&mut iter)?;
410 let http_or_https = parse_http_https(&mut iter)?;
411 parse_end(&mut iter)?;
412 let socket_addr = SocketAddr::new(ip_addr, tcp_port);
413
414 Ok((socket_addr, http_or_https))
415}
416
417pub(crate) fn parse_ip6(address: &Multiaddr) -> Result<(SocketAddr, &'static str)> {
419 let mut iter = address.iter();
420
421 let ip_addr = match iter
422 .next()
423 .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
424 {
425 Protocol::Ip6(ip6_addr) => IpAddr::V6(ip6_addr),
426 other => return Err(eyre!("expected ip6 found {other}")),
427 };
428 let tcp_port = parse_tcp(&mut iter)?;
429 let http_or_https = parse_http_https(&mut iter)?;
430 parse_end(&mut iter)?;
431 let socket_addr = SocketAddr::new(ip_addr, tcp_port);
432
433 Ok((socket_addr, http_or_https))
434}
435
436fn is_ipv4_private_or_unroutable(addr: Ipv4Addr, allow_private_addresses: bool) -> bool {
439 if !allow_private_addresses && addr.is_private() {
440 return true;
442 }
443
444 addr.is_unspecified() ||
446 addr.is_loopback() ||
448 addr.is_link_local() ||
450 addr.is_multicast() ||
452 (addr.octets()[0] == 100 && (addr.octets()[1] & 0b11000000) == 64) ||
454 (addr.octets()[0] == 192 && addr.octets()[1] == 0 && addr.octets()[2] == 0) ||
456 (addr.octets()[0] == 192 && addr.octets()[1] == 0 && addr.octets()[2] == 2) ||
458 (addr.octets()[0] == 198 && addr.octets()[1] == 51 && addr.octets()[2] == 100) ||
459 (addr.octets()[0] == 203 && addr.octets()[1] == 0 && addr.octets()[2] == 113) ||
460 (addr.octets()[0] == 192 && addr.octets()[1] == 31 && addr.octets()[2] == 196) ||
462 (addr.octets()[0] == 192 && addr.octets()[1] == 52 && addr.octets()[2] == 193) ||
464 (addr.octets()[0] == 192 && addr.octets()[1] == 88 && addr.octets()[2] == 99) ||
466 (addr.octets()[0] == 198 && (addr.octets()[1] & 0b11111110) == 18) ||
468 (addr.octets()[0] >= 240)
470}
471
472fn is_ipv6_private_or_unroutable(addr: Ipv6Addr) -> bool {
475 addr.is_unspecified() ||
477 addr.is_loopback() ||
479 addr.is_multicast() ||
481 (addr.segments()[0] == 0x0100 && addr.segments()[1] == 0 &&
483 addr.segments()[2] == 0 && addr.segments()[3] == 0) ||
484 (addr.segments()[0] == 0x2001 && addr.segments()[1] == 0x0000) ||
486 (addr.segments()[0] == 0x2001 && addr.segments()[1] == 0x0002) ||
488 (addr.segments()[0] == 0x2001 && (addr.segments()[1] & 0xfff0) == 0x0010) ||
490 (addr.segments()[0] == 0x2001 && addr.segments()[1] == 0x0db8) ||
492 addr.segments()[0] == 0x2002 ||
494 (addr.segments()[0] & 0xfe00) == 0xfc00 ||
496 (addr.segments()[0] & 0xffc0) == 0xfec0 ||
498 (addr.segments()[0] & 0xffc0) == 0xfe80 ||
500 addr.to_ipv4_mapped().is_some_and(|addr| is_ipv4_private_or_unroutable(addr, false))
502}
503
504fn is_valid_fqdn(hostname: &str, allow_localhost_dns: bool) -> bool {
509 if hostname.ends_with('.') {
510 return false;
512 }
513
514 if hostname.is_empty() || hostname.len() > 253 {
516 return false;
517 }
518
519 let hostname_lower = hostname.to_lowercase();
520 if !allow_localhost_dns {
521 if hostname_lower == "localhost" || hostname_lower.ends_with(".local") {
523 return false;
524 }
525 } else if hostname_lower == "localhost" {
526 return true;
528 }
529
530 let ascii_hostname = match idna::domain_to_ascii(hostname) {
532 Ok(ascii) => ascii,
533 Err(_) => return false, };
535
536 let labels: Vec<&str> = ascii_hostname.split('.').collect();
538
539 if labels.len() < 2 {
541 return false;
542 }
543
544 for label in &labels {
546 if !is_valid_dns_label(label) {
547 return false;
548 }
549 }
550
551 let tld = *labels.last().unwrap();
555 if tld.len() < 2 {
556 return false;
557 }
558
559 let is_valid_tld = if tld.starts_with("xn--") {
561 true
563 } else {
564 tld.chars().all(|c| c.is_ascii_alphabetic())
566 };
567
568 if !is_valid_tld {
569 return false;
570 }
571
572 true
573}
574
575fn is_valid_dns_label(label: &str) -> bool {
577 if label.is_empty() || label.len() > 63 {
579 return false;
580 }
581
582 let bytes = label.as_bytes();
583
584 if !bytes[0].is_ascii_alphanumeric() || !bytes[bytes.len() - 1].is_ascii_alphanumeric() {
586 return false;
587 }
588
589 bytes
591 .iter()
592 .all(|&b| b.is_ascii_alphanumeric() || b == b'-')
593}
594
595#[cfg(test)]
596mod test {
597 use multiaddr::multiaddr;
598
599 use super::Multiaddr;
600
601 #[test]
602 fn test_to_socket_addr_basic() {
603 let multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)));
604 let socket_addr_ipv4 = multi_addr_ipv4
605 .to_socket_addr()
606 .expect("Couldn't convert to socket addr");
607 assert_eq!(socket_addr_ipv4.to_string(), "127.0.0.1:10500");
608
609 let multi_addr_ipv6 = Multiaddr(multiaddr!(Ip6([172, 0, 0, 1, 1, 1, 1, 1]), Tcp(10500u16)));
610 let socket_addr_ipv6 = multi_addr_ipv6
611 .to_socket_addr()
612 .expect("Couldn't convert to socket addr");
613 assert_eq!(socket_addr_ipv6.to_string(), "[ac::1:1:1:1:1]:10500");
614 }
615
616 #[test]
617 fn test_to_socket_addr_unsupported_protocol() {
618 let multi_addr_dns = Multiaddr(multiaddr!(Dnsaddr("iota.iota"), Tcp(10500u16)));
619 let _ = multi_addr_dns
620 .to_socket_addr()
621 .expect_err("DNS is unsupported");
622 }
623
624 #[test]
625 fn test_is_loosely_valid_tcp_addr() {
626 let multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)));
627 assert!(multi_addr_ipv4.is_loosely_valid_tcp_addr());
628 let multi_addr_ipv6 = Multiaddr(multiaddr!(Ip6([172, 0, 0, 1, 1, 1, 1, 1]), Tcp(10500u16)));
629 assert!(multi_addr_ipv6.is_loosely_valid_tcp_addr());
630 let multi_addr_dns = Multiaddr(multiaddr!(Dnsaddr("iota.iota"), Tcp(10500u16)));
631 assert!(multi_addr_dns.is_loosely_valid_tcp_addr());
632
633 let multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Udp(10500u16)));
634 assert!(!multi_addr_ipv4.is_loosely_valid_tcp_addr());
635 let multi_addr_ipv6 = Multiaddr(multiaddr!(Ip6([172, 0, 0, 1, 1, 1, 1, 1]), Udp(10500u16)));
636 assert!(!multi_addr_ipv6.is_loosely_valid_tcp_addr());
637 let multi_addr_dns = Multiaddr(multiaddr!(Dnsaddr("iota.iota"), Udp(10500u16)));
638 assert!(!multi_addr_dns.is_loosely_valid_tcp_addr());
639
640 let invalid_multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1])));
641 assert!(!invalid_multi_addr_ipv4.is_loosely_valid_tcp_addr());
642 }
643
644 #[test]
645 fn test_get_hostname_port() {
646 let multi_addr_ip4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)));
647 assert_eq!(Some("127.0.0.1".to_string()), multi_addr_ip4.hostname());
648 assert_eq!(Some(10500u16), multi_addr_ip4.port());
649
650 let multi_addr_dns = Multiaddr(multiaddr!(Dns("iota.iota"), Tcp(10501u16)));
651 assert_eq!(Some("iota.iota".to_string()), multi_addr_dns.hostname());
652 assert_eq!(Some(10501u16), multi_addr_dns.port());
653 }
654
655 #[test]
656 fn test_to_anemo_address() {
657 let addr_ip4 = Multiaddr(multiaddr!(Ip4([15, 15, 15, 1]), Udp(10500u16)))
658 .to_anemo_address()
659 .unwrap();
660 assert_eq!("15.15.15.1:10500".to_string(), addr_ip4.to_string());
661
662 let addr_ip6 = Multiaddr(multiaddr!(
663 Ip6([15, 15, 15, 15, 15, 15, 15, 1]),
664 Udp(10500u16)
665 ))
666 .to_anemo_address()
667 .unwrap();
668 assert_eq!("[f:f:f:f:f:f:f:1]:10500".to_string(), addr_ip6.to_string());
669
670 let addr_dns = Multiaddr(multiaddr!(Dns("iota.iota"), Udp(10501u16)))
671 .to_anemo_address()
672 .unwrap();
673 assert_eq!("iota.iota:10501".to_string(), addr_dns.to_string());
674
675 let addr_invalid =
676 Multiaddr(multiaddr!(Dns("iota.iota"), Tcp(10501u16))).to_anemo_address();
677 assert!(addr_invalid.is_err());
678 }
679
680 #[test]
681 fn test_with_zero_ip() {
682 let multi_addr_ip4 =
683 Multiaddr(multiaddr!(Ip4([15, 15, 15, 1]), Tcp(10500u16))).with_zero_ip();
684 assert_eq!(Some("0.0.0.0".to_string()), multi_addr_ip4.hostname());
685 assert_eq!(Some(10500u16), multi_addr_ip4.port());
686
687 let multi_addr_ip6 = Multiaddr(multiaddr!(
688 Ip6([15, 15, 15, 15, 15, 15, 15, 1]),
689 Tcp(10500u16)
690 ))
691 .with_zero_ip();
692 assert_eq!(Some("::".to_string()), multi_addr_ip6.hostname());
693 assert_eq!(Some(10500u16), multi_addr_ip4.port());
694
695 let multi_addr_dns = Multiaddr(multiaddr!(Dns("iota.iota"), Tcp(10501u16))).with_zero_ip();
696 assert_eq!(Some("0.0.0.0".to_string()), multi_addr_dns.hostname());
697 assert_eq!(Some(10501u16), multi_addr_dns.port());
698 }
699
700 #[test]
701 fn test_with_localhost_ip() {
702 let multi_addr_ip4 =
703 Multiaddr(multiaddr!(Ip4([15, 15, 15, 1]), Tcp(10500u16))).with_localhost_ip();
704 assert_eq!(Some("127.0.0.1".to_string()), multi_addr_ip4.hostname());
705 assert_eq!(Some(10500u16), multi_addr_ip4.port());
706
707 let multi_addr_ip6 = Multiaddr(multiaddr!(
708 Ip6([15, 15, 15, 15, 15, 15, 15, 1]),
709 Tcp(10500u16)
710 ))
711 .with_localhost_ip();
712 assert_eq!(Some("::1".to_string()), multi_addr_ip6.hostname());
713 assert_eq!(Some(10500u16), multi_addr_ip4.port());
714
715 let multi_addr_dns =
716 Multiaddr(multiaddr!(Dns("iota.iota"), Tcp(10501u16))).with_localhost_ip();
717 assert_eq!(Some("127.0.0.1".to_string()), multi_addr_dns.hostname());
718 assert_eq!(Some(10501u16), multi_addr_dns.port());
719 }
720
721 #[test]
722 fn test_is_private_or_unroutable_ipv4() {
723 let test_cases = vec![
725 (
727 multiaddr!(Ip4([10, 0, 0, 1]), Udp(10500u16)),
728 "RFC 1918 private - 10.0.0.0/8",
729 true,
730 ),
731 (
732 multiaddr!(Ip4([172, 16, 0, 1]), Udp(10500u16)),
733 "RFC 1918 private - 172.16.0.0/12",
734 true,
735 ),
736 (
737 multiaddr!(Ip4([192, 168, 1, 1]), Udp(10500u16)),
738 "RFC 1918 private - 192.168.0.0/16",
739 true,
740 ),
741 (
743 multiaddr!(Ip4([127, 0, 0, 1]), Udp(10500u16)),
744 "RFC 1122 loopback",
745 true,
746 ),
747 (
749 multiaddr!(Ip4([169, 254, 1, 1]), Udp(10500u16)),
750 "RFC 3927 link-local",
751 true,
752 ),
753 (
755 multiaddr!(Ip4([0, 0, 0, 0]), Udp(10500u16)),
756 "RFC 1122 unspecified",
757 true,
758 ),
759 (
761 multiaddr!(Ip4([224, 0, 0, 1]), Udp(10500u16)),
762 "RFC 3171 multicast",
763 true,
764 ),
765 (
767 multiaddr!(Ip4([100, 64, 0, 1]), Udp(10500u16)),
768 "RFC 6598 carrier-grade NAT",
769 true,
770 ),
771 (
773 multiaddr!(Ip4([192, 0, 0, 1]), Udp(10500u16)),
774 "RFC 6890 IETF Protocol Assignments - 192.0.0.0/24",
775 true,
776 ),
777 (
779 multiaddr!(Ip4([192, 31, 196, 1]), Udp(10500u16)),
780 "RFC 7535 AS112-v4 - 192.31.196.0/24",
781 true,
782 ),
783 (
785 multiaddr!(Ip4([192, 52, 193, 1]), Udp(10500u16)),
786 "RFC 7450 Automatic Multicast Tunneling - 192.52.193.0/24",
787 true,
788 ),
789 (
791 multiaddr!(Ip4([192, 0, 2, 1]), Udp(10500u16)),
792 "RFC 5737 documentation - 192.0.2.0/24",
793 true,
794 ),
795 (
796 multiaddr!(Ip4([198, 51, 100, 1]), Udp(10500u16)),
797 "RFC 5737 documentation - 198.51.100.0/24",
798 true,
799 ),
800 (
801 multiaddr!(Ip4([203, 0, 113, 1]), Udp(10500u16)),
802 "RFC 5737 documentation - 203.0.113.0/24",
803 true,
804 ),
805 (
807 multiaddr!(Ip4([198, 18, 0, 1]), Udp(10500u16)),
808 "RFC 2544 benchmarking - 198.18.0.0/15",
809 true,
810 ),
811 (
813 multiaddr!(Ip4([8, 8, 8, 8]), Udp(10500u16)),
814 "Google DNS - should not be filtered",
815 false,
816 ),
817 (
818 multiaddr!(Ip4([1, 1, 1, 1]), Udp(10500u16)),
819 "Cloudflare DNS - should not be filtered",
820 false,
821 ),
822 (
823 multiaddr!(Ip4([208, 67, 222, 222]), Udp(10500u16)),
824 "OpenDNS - should not be filtered",
825 false,
826 ),
827 ];
828
829 for (multiaddr, description, should_be_filtered) in test_cases {
830 let addr = Multiaddr(multiaddr);
831 let is_filtered = addr.is_private_or_unroutable(false);
832 assert_eq!(
833 is_filtered, should_be_filtered,
834 "Failed for {description}: expected {should_be_filtered} but got {is_filtered}",
835 );
836 }
837 }
838
839 #[test]
840 fn test_is_private_or_unroutable_ipv6() {
841 let test_cases = vec![
843 (
845 multiaddr!(Ip6([0, 0, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
846 "RFC 4291 loopback",
847 true,
848 ),
849 (
851 multiaddr!(Ip6([0, 0, 0, 0, 0, 0, 0, 0]), Udp(10500u16)),
852 "RFC 4291 unspecified",
853 true,
854 ),
855 (
857 multiaddr!(Ip6([0x0100, 0, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
858 "RFC 6666 discard-only - 100::/64",
859 true,
860 ),
861 (
863 multiaddr!(Ip6([0xfc00, 0, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
864 "RFC 4193 unique local - fc00::/7",
865 true,
866 ),
867 (
868 multiaddr!(Ip6([0xfd00, 0, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
869 "RFC 4193 unique local - fd00::/7",
870 true,
871 ),
872 (
874 multiaddr!(Ip6([0xfe80, 0, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
875 "RFC 4862 link-local",
876 true,
877 ),
878 (
880 multiaddr!(Ip6([0x2001, 0x0002, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
881 "RFC 5180 benchmarking - 2001:2::/48",
882 true,
883 ),
884 (
886 multiaddr!(Ip6([0x2001, 0x0db8, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
887 "RFC 3849 documentation",
888 true,
889 ),
890 (
892 multiaddr!(Ip6([0xff02, 0, 0, 0, 0, 0, 0, 1]), Udp(10500u16)),
893 "IPv6 multicast",
894 true,
895 ),
896 (
898 multiaddr!(
899 Ip6([0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x68]),
900 Udp(10500u16)
901 ),
902 "Google DNS IPv6 - should not be filtered",
903 false,
904 ),
905 (
906 multiaddr!(Ip6([0x2606, 0x4700, 0x10, 0, 0, 0, 0, 0x68]), Udp(10500u16)),
907 "Cloudflare DNS IPv6 - should not be filtered",
908 false,
909 ),
910 ];
911
912 for (multiaddr, description, should_be_filtered) in test_cases {
913 let addr = Multiaddr(multiaddr);
914 let is_filtered = addr.is_private_or_unroutable(false);
915 assert_eq!(
916 is_filtered, should_be_filtered,
917 "Failed for {description}: expected {should_be_filtered} but got {is_filtered}",
918 );
919 }
920 }
921
922 #[test]
923 fn test_is_private_or_unroutable_dns() {
924 let test_cases = vec![
926 (
927 multiaddr!(Dns("iota.org"), Udp(10500u16)),
928 "DNS addresses should be allowed for public discovery",
929 false,
930 ),
931 (
932 multiaddr!(Dns4("iota.org"), Udp(10500u16)),
933 "DNS4 addresses should be allowed for public discovery",
934 false,
935 ),
936 (
937 multiaddr!(Dns6("iota.org"), Udp(10500u16)),
938 "DNS6 addresses should be allowed for public discovery",
939 false,
940 ),
941 ];
942
943 for (multiaddr, description, should_be_private) in test_cases {
944 let addr = Multiaddr(multiaddr);
945 let is_private = addr.is_private_or_unroutable(false);
946 assert_eq!(
947 is_private, should_be_private,
948 "Failed for {description}: expected {should_be_private} but got {is_private}",
949 );
950 }
951 }
952
953 #[test]
954 fn test_is_valid_for_public_announcement() {
955 let test_cases = vec![
957 (
958 multiaddr!(Ip4([192, 168, 1, 1]), Udp(10500u16)),
959 "Private IPv4 address should be invalid",
960 false,
961 ),
962 (
963 multiaddr!(Ip4([127, 0, 0, 1]), Udp(10500u16)),
964 "Loopback IPv4 address should be invalid",
965 false,
966 ),
967 (
968 multiaddr!(Ip4([8, 8, 8, 8]), Udp(10500u16)),
969 "Valid public IPv4 address should be valid",
970 true,
971 ),
972 (
973 multiaddr!(Dns("example.com"), Udp(10500u16)),
974 "Valid DNS address should be valid",
975 true,
976 ),
977 (
978 multiaddr!(Ip4([8, 8, 8, 8]), Tcp(10500u16)),
979 "TCP instead of UDP should be invalid for anemo",
980 false,
981 ),
982 ];
983
984 for (multiaddr, description, should_be_valid) in test_cases {
985 let addr = Multiaddr(multiaddr);
986 let is_valid = addr.is_valid_public_anemo_address(false);
987 assert_eq!(
988 is_valid, should_be_valid,
989 "Failed for {description}: expected {should_be_valid} but got {is_valid}",
990 );
991 }
992 }
993
994 #[test]
995 fn test_is_valid_fqdn() {
996 use super::is_valid_fqdn;
997
998 let valid_cases = vec![
1000 "example.com",
1001 "subdomain.example.com",
1002 "iota.org",
1003 "api.iota.org",
1004 "test123.example-domain.org",
1005 "google.com",
1006 "github.com",
1007 "example.co", "test.example.net",
1009 "api-v1.service.io",
1010 "very-long-subdomain-name-that-is-still-valid.example.org",
1011 "café.com", "москва.рф", "τεστ.gr", "测试.中国", "xn--nxasmq6b.com", ];
1018
1019 for fqdn in valid_cases {
1020 assert!(
1021 is_valid_fqdn(fqdn, false),
1022 "Expected '{fqdn}' to be a valid FQDN",
1023 );
1024 }
1025
1026 let long_domain = "a".repeat(254);
1028 let long_label = "a".repeat(64);
1029 let long_label_domain = format!("{long_label}.com");
1030 let invalid_cases = vec![
1031 "", "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", ];
1056
1057 for fqdn in invalid_cases {
1058 assert!(
1059 !is_valid_fqdn(fqdn, false),
1060 "Expected '{fqdn}' to be an invalid FQDN"
1061 );
1062 }
1063 }
1064
1065 #[test]
1066 fn test_is_valid_dns_label() {
1067 use super::is_valid_dns_label;
1068
1069 let max_length_label = "a".repeat(63);
1071 let valid_cases = vec![
1072 "a",
1073 "ab",
1074 "example",
1075 "test123",
1076 "api-v1",
1077 "sub-domain",
1078 "a1b2c3",
1079 "123", &max_length_label, ];
1082
1083 for label in valid_cases {
1084 assert!(
1085 is_valid_dns_label(label),
1086 "Expected '{label}' to be a valid DNS label"
1087 );
1088 }
1089
1090 let too_long_label = "a".repeat(64);
1092 let invalid_cases = vec![
1093 "", "-example", "example-", "ex_ample", "ex ample", "ex.ample", &too_long_label, ];
1101
1102 for label in invalid_cases {
1103 assert!(
1104 !is_valid_dns_label(label),
1105 "Expected '{label}' to be an invalid DNS label"
1106 );
1107 }
1108 }
1109
1110 #[test]
1111 fn test_dns_validation_in_multiaddr() {
1112 let valid_dns_cases = vec![
1116 multiaddr!(Dns("example.com"), Udp(10500u16)),
1117 multiaddr!(Dns("iota.org"), Udp(10500u16)),
1118 multiaddr!(Dns("api.example.com"), Udp(10500u16)),
1119 multiaddr!(Dns("café.com"), Udp(10500u16)),
1121 multiaddr!(Dns("москва.рф"), Udp(10500u16)),
1122 ];
1123
1124 for addr in valid_dns_cases {
1125 let multiaddr = Multiaddr(addr);
1126 assert!(
1127 !multiaddr.is_private_or_unroutable(false),
1128 "Valid DNS address {multiaddr} should not be filtered as private/unroutable"
1129 );
1130 assert!(
1131 multiaddr.is_valid_public_anemo_address(false),
1132 "Valid DNS address {multiaddr} should be valid for public announcement"
1133 );
1134 }
1135
1136 let invalid_dns_cases = vec![
1138 multiaddr!(Dns("localhost"), Udp(10500u16)),
1139 multiaddr!(Dns("hostname.local"), Udp(10500u16)),
1140 multiaddr!(Dns("hostname"), Udp(10500u16)), multiaddr!(Dns(""), Udp(10500u16)), ];
1143
1144 for addr in invalid_dns_cases {
1145 let multiaddr = Multiaddr(addr);
1146 assert!(
1147 multiaddr.is_private_or_unroutable(false),
1148 "Invalid DNS address {multiaddr} should be filtered as private/unroutable"
1149 );
1150 assert!(
1151 !multiaddr.is_valid_public_anemo_address(false),
1152 "Invalid DNS address {multiaddr} should not be valid for public announcement"
1153 );
1154 }
1155 }
1156}