iota_network_stack/
multiaddr.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
223impl std::fmt::Display for Multiaddr {
224    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225        std::fmt::Display::fmt(&self.0, f)
226    }
227}
228
229impl std::str::FromStr for Multiaddr {
230    type Err = Error;
231
232    fn from_str(s: &str) -> Result<Self, Self::Err> {
233        ::multiaddr::Multiaddr::from_str(s).map(Self)
234    }
235}
236
237impl<'a> TryFrom<&'a str> for Multiaddr {
238    type Error = Error;
239
240    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
241        value.parse()
242    }
243}
244
245impl TryFrom<String> for Multiaddr {
246    type Error = Error;
247
248    fn try_from(value: String) -> Result<Self, Self::Error> {
249        value.parse()
250    }
251}
252
253impl serde::Serialize for Multiaddr {
254    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
255    where
256        S: serde::Serializer,
257    {
258        serializer.serialize_str(&self.0.to_string())
259    }
260}
261
262impl<'de> serde::Deserialize<'de> for Multiaddr {
263    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
264    where
265        D: serde::Deserializer<'de>,
266    {
267        let s = String::deserialize(deserializer)?;
268        s.parse()
269            .map(Self)
270            .map_err(|e| serde::de::Error::custom(e.to_string()))
271    }
272}
273
274impl std::net::ToSocketAddrs for Multiaddr {
275    type Iter = Box<dyn Iterator<Item = SocketAddr>>;
276
277    fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
278        let mut iter = self.iter();
279
280        match (iter.next(), iter.next()) {
281            (Some(Protocol::Ip4(ip4)), Some(Protocol::Tcp(port) | Protocol::Udp(port))) => {
282                (ip4, port)
283                    .to_socket_addrs()
284                    .map(|iter| Box::new(iter) as _)
285            }
286            (Some(Protocol::Ip6(ip6)), Some(Protocol::Tcp(port) | Protocol::Udp(port))) => {
287                (ip6, port)
288                    .to_socket_addrs()
289                    .map(|iter| Box::new(iter) as _)
290            }
291            (Some(Protocol::Dns(hostname)), Some(Protocol::Tcp(port) | Protocol::Udp(port))) => {
292                (hostname.as_ref(), port)
293                    .to_socket_addrs()
294                    .map(|iter| Box::new(iter) as _)
295            }
296            _ => Err(std::io::Error::new(
297                std::io::ErrorKind::InvalidInput,
298                "unable to convert Multiaddr to SocketAddr",
299            )),
300        }
301    }
302}
303
304pub(crate) fn parse_tcp<'a, T: Iterator<Item = Protocol<'a>>>(protocols: &mut T) -> Result<u16> {
305    if let Protocol::Tcp(port) = protocols
306        .next()
307        .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
308    {
309        Ok(port)
310    } else {
311        Err(eyre!("expected tcp protocol"))
312    }
313}
314
315pub(crate) fn parse_http_https<'a, T: Iterator<Item = Protocol<'a>>>(
316    protocols: &mut T,
317) -> Result<&'static str> {
318    match protocols.next() {
319        Some(Protocol::Http) => Ok("http"),
320        Some(Protocol::Https) => Ok("https"),
321        _ => Ok("http"),
322    }
323}
324
325pub(crate) fn parse_end<'a, T: Iterator<Item = Protocol<'a>>>(protocols: &mut T) -> Result<()> {
326    if protocols.next().is_none() {
327        Ok(())
328    } else {
329        Err(eyre!("expected end of multiaddr"))
330    }
331}
332
333// Parse a full /dns/-/tcp/-/{http,https} address
334pub(crate) fn parse_dns(address: &Multiaddr) -> Result<(Cow<'_, str>, u16, &'static str)> {
335    let mut iter = address.iter();
336
337    let dns_name = match iter
338        .next()
339        .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
340    {
341        Protocol::Dns(dns_name) => dns_name,
342        other => return Err(eyre!("expected dns found {other}")),
343    };
344    let tcp_port = parse_tcp(&mut iter)?;
345    let http_or_https = parse_http_https(&mut iter)?;
346    parse_end(&mut iter)?;
347    Ok((dns_name, tcp_port, http_or_https))
348}
349
350// Parse a full /ip4/-/tcp/-/{http,https} address
351pub(crate) fn parse_ip4(address: &Multiaddr) -> Result<(SocketAddr, &'static str)> {
352    let mut iter = address.iter();
353
354    let ip_addr = match iter
355        .next()
356        .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
357    {
358        Protocol::Ip4(ip4_addr) => IpAddr::V4(ip4_addr),
359        other => return Err(eyre!("expected ip4 found {other}")),
360    };
361    let tcp_port = parse_tcp(&mut iter)?;
362    let http_or_https = parse_http_https(&mut iter)?;
363    parse_end(&mut iter)?;
364    let socket_addr = SocketAddr::new(ip_addr, tcp_port);
365
366    Ok((socket_addr, http_or_https))
367}
368
369// Parse a full /ip6/-/tcp/-/{http,https} address
370pub(crate) fn parse_ip6(address: &Multiaddr) -> Result<(SocketAddr, &'static str)> {
371    let mut iter = address.iter();
372
373    let ip_addr = match iter
374        .next()
375        .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
376    {
377        Protocol::Ip6(ip6_addr) => IpAddr::V6(ip6_addr),
378        other => return Err(eyre!("expected ip6 found {other}")),
379    };
380    let tcp_port = parse_tcp(&mut iter)?;
381    let http_or_https = parse_http_https(&mut iter)?;
382    parse_end(&mut iter)?;
383    let socket_addr = SocketAddr::new(ip_addr, tcp_port);
384
385    Ok((socket_addr, http_or_https))
386}
387
388#[cfg(test)]
389mod test {
390    use multiaddr::multiaddr;
391
392    use super::Multiaddr;
393
394    #[test]
395    fn test_to_socket_addr_basic() {
396        let multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)));
397        let socket_addr_ipv4 = multi_addr_ipv4
398            .to_socket_addr()
399            .expect("Couldn't convert to socket addr");
400        assert_eq!(socket_addr_ipv4.to_string(), "127.0.0.1:10500");
401
402        let multi_addr_ipv6 = Multiaddr(multiaddr!(Ip6([172, 0, 0, 1, 1, 1, 1, 1]), Tcp(10500u16)));
403        let socket_addr_ipv6 = multi_addr_ipv6
404            .to_socket_addr()
405            .expect("Couldn't convert to socket addr");
406        assert_eq!(socket_addr_ipv6.to_string(), "[ac::1:1:1:1:1]:10500");
407    }
408
409    #[test]
410    fn test_to_socket_addr_unsupported_protocol() {
411        let multi_addr_dns = Multiaddr(multiaddr!(Dnsaddr("iota.iota"), Tcp(10500u16)));
412        let _ = multi_addr_dns
413            .to_socket_addr()
414            .expect_err("DNS is unsupported");
415    }
416
417    #[test]
418    fn test_is_loosely_valid_tcp_addr() {
419        let multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)));
420        assert!(multi_addr_ipv4.is_loosely_valid_tcp_addr());
421        let multi_addr_ipv6 = Multiaddr(multiaddr!(Ip6([172, 0, 0, 1, 1, 1, 1, 1]), Tcp(10500u16)));
422        assert!(multi_addr_ipv6.is_loosely_valid_tcp_addr());
423        let multi_addr_dns = Multiaddr(multiaddr!(Dnsaddr("iota.iota"), Tcp(10500u16)));
424        assert!(multi_addr_dns.is_loosely_valid_tcp_addr());
425
426        let multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Udp(10500u16)));
427        assert!(!multi_addr_ipv4.is_loosely_valid_tcp_addr());
428        let multi_addr_ipv6 = Multiaddr(multiaddr!(Ip6([172, 0, 0, 1, 1, 1, 1, 1]), Udp(10500u16)));
429        assert!(!multi_addr_ipv6.is_loosely_valid_tcp_addr());
430        let multi_addr_dns = Multiaddr(multiaddr!(Dnsaddr("iota.iota"), Udp(10500u16)));
431        assert!(!multi_addr_dns.is_loosely_valid_tcp_addr());
432
433        let invalid_multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1])));
434        assert!(!invalid_multi_addr_ipv4.is_loosely_valid_tcp_addr());
435    }
436
437    #[test]
438    fn test_get_hostname_port() {
439        let multi_addr_ip4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)));
440        assert_eq!(Some("127.0.0.1".to_string()), multi_addr_ip4.hostname());
441        assert_eq!(Some(10500u16), multi_addr_ip4.port());
442
443        let multi_addr_dns = Multiaddr(multiaddr!(Dns("iota.iota"), Tcp(10501u16)));
444        assert_eq!(Some("iota.iota".to_string()), multi_addr_dns.hostname());
445        assert_eq!(Some(10501u16), multi_addr_dns.port());
446    }
447
448    #[test]
449    fn test_to_anemo_address() {
450        let addr_ip4 = Multiaddr(multiaddr!(Ip4([15, 15, 15, 1]), Udp(10500u16)))
451            .to_anemo_address()
452            .unwrap();
453        assert_eq!("15.15.15.1:10500".to_string(), addr_ip4.to_string());
454
455        let addr_ip6 = Multiaddr(multiaddr!(
456            Ip6([15, 15, 15, 15, 15, 15, 15, 1]),
457            Udp(10500u16)
458        ))
459        .to_anemo_address()
460        .unwrap();
461        assert_eq!("[f:f:f:f:f:f:f:1]:10500".to_string(), addr_ip6.to_string());
462
463        let addr_dns = Multiaddr(multiaddr!(Dns("iota.iota"), Udp(10501u16)))
464            .to_anemo_address()
465            .unwrap();
466        assert_eq!("iota.iota:10501".to_string(), addr_dns.to_string());
467
468        let addr_invalid =
469            Multiaddr(multiaddr!(Dns("iota.iota"), Tcp(10501u16))).to_anemo_address();
470        assert!(addr_invalid.is_err());
471    }
472
473    #[test]
474    fn test_with_zero_ip() {
475        let multi_addr_ip4 =
476            Multiaddr(multiaddr!(Ip4([15, 15, 15, 1]), Tcp(10500u16))).with_zero_ip();
477        assert_eq!(Some("0.0.0.0".to_string()), multi_addr_ip4.hostname());
478        assert_eq!(Some(10500u16), multi_addr_ip4.port());
479
480        let multi_addr_ip6 = Multiaddr(multiaddr!(
481            Ip6([15, 15, 15, 15, 15, 15, 15, 1]),
482            Tcp(10500u16)
483        ))
484        .with_zero_ip();
485        assert_eq!(Some("::".to_string()), multi_addr_ip6.hostname());
486        assert_eq!(Some(10500u16), multi_addr_ip4.port());
487
488        let multi_addr_dns = Multiaddr(multiaddr!(Dns("iota.iota"), Tcp(10501u16))).with_zero_ip();
489        assert_eq!(Some("0.0.0.0".to_string()), multi_addr_dns.hostname());
490        assert_eq!(Some(10501u16), multi_addr_dns.port());
491    }
492
493    #[test]
494    fn test_with_localhost_ip() {
495        let multi_addr_ip4 =
496            Multiaddr(multiaddr!(Ip4([15, 15, 15, 1]), Tcp(10500u16))).with_localhost_ip();
497        assert_eq!(Some("127.0.0.1".to_string()), multi_addr_ip4.hostname());
498        assert_eq!(Some(10500u16), multi_addr_ip4.port());
499
500        let multi_addr_ip6 = Multiaddr(multiaddr!(
501            Ip6([15, 15, 15, 15, 15, 15, 15, 1]),
502            Tcp(10500u16)
503        ))
504        .with_localhost_ip();
505        assert_eq!(Some("::1".to_string()), multi_addr_ip6.hostname());
506        assert_eq!(Some(10500u16), multi_addr_ip4.port());
507
508        let multi_addr_dns =
509            Multiaddr(multiaddr!(Dns("iota.iota"), Tcp(10501u16))).with_localhost_ip();
510        assert_eq!(Some("127.0.0.1".to_string()), multi_addr_dns.hostname());
511        assert_eq!(Some(10501u16), multi_addr_dns.port());
512    }
513}