mysten_network/
multiaddr.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use eyre::{Result, eyre};
5use std::{
6    borrow::Cow,
7    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
8};
9use tracing::error;
10
11pub use ::multiaddr::Error;
12pub use ::multiaddr::Protocol;
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    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 `/[ip4,ip6,dns]/{}/udp/{port}` into an anemo
55    /// 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 local server.
90    // See `client::endpoint_from_multiaddr()` for converting to Endpoint for clients.
91    pub fn to_socket_addr(&self) -> Result<SocketAddr> {
92        let mut iter = self.iter();
93        let ip = match iter.next().ok_or_else(|| {
94            eyre!("failed to convert to SocketAddr: Multiaddr does not contain IP")
95        })? {
96            Protocol::Ip4(ip4_addr) => IpAddr::V4(ip4_addr),
97            Protocol::Ip6(ip6_addr) => IpAddr::V6(ip6_addr),
98            unsupported => return Err(eyre!("unsupported protocol {unsupported}")),
99        };
100        let tcp_port = parse_tcp(&mut iter)?;
101        Ok(SocketAddr::new(ip, tcp_port))
102    }
103
104    // Returns true if the third component in the multiaddr is `Protocol::Tcp`
105    pub fn is_loosely_valid_tcp_addr(&self) -> bool {
106        let mut iter = self.iter();
107        iter.next(); // Skip the ip/dns part
108        match iter.next() {
109            Some(Protocol::Tcp(_)) => true,
110            _ => false, // including `None` and `Some(other)`
111        }
112    }
113
114    /// Set the ip address to `0.0.0.0`. For instance, it converts the following address
115    /// `/ip4/155.138.174.208/tcp/1500/http` into `/ip4/0.0.0.0/tcp/1500/http`.
116    /// This is useful when starting a server and you want to listen on all interfaces.
117    pub fn with_zero_ip(&self) -> Self {
118        let mut new_address = self.0.clone();
119        let Some(protocol) = new_address.iter().next() else {
120            error!("Multiaddr is empty");
121            return Self(new_address);
122        };
123        match protocol {
124            multiaddr::Protocol::Ip4(_)
125            | multiaddr::Protocol::Dns(_)
126            | multiaddr::Protocol::Dns4(_) => {
127                new_address = new_address
128                    .replace(0, |_| Some(multiaddr::Protocol::Ip4(Ipv4Addr::UNSPECIFIED)))
129                    .unwrap();
130            }
131            multiaddr::Protocol::Ip6(_) | multiaddr::Protocol::Dns6(_) => {
132                new_address = new_address
133                    .replace(0, |_| Some(multiaddr::Protocol::Ip6(Ipv6Addr::UNSPECIFIED)))
134                    .unwrap();
135            }
136            p => {
137                error!("Unsupported protocol {} in Multiaddr {}!", p, new_address);
138            }
139        }
140        Self(new_address)
141    }
142
143    /// Set the ip address to `127.0.0.1`. For instance, it converts the following address
144    /// `/ip4/155.138.174.208/tcp/1500/http` into `/ip4/127.0.0.1/tcp/1500/http`.
145    pub fn with_localhost_ip(&self) -> Self {
146        let mut new_address = self.0.clone();
147        let Some(protocol) = new_address.iter().next() else {
148            error!("Multiaddr is empty");
149            return Self(new_address);
150        };
151        match protocol {
152            multiaddr::Protocol::Ip4(_)
153            | multiaddr::Protocol::Dns(_)
154            | multiaddr::Protocol::Dns4(_) => {
155                new_address = new_address
156                    .replace(0, |_| Some(multiaddr::Protocol::Ip4(Ipv4Addr::LOCALHOST)))
157                    .unwrap();
158            }
159            multiaddr::Protocol::Ip6(_) | multiaddr::Protocol::Dns6(_) => {
160                new_address = new_address
161                    .replace(0, |_| Some(multiaddr::Protocol::Ip6(Ipv6Addr::LOCALHOST)))
162                    .unwrap();
163            }
164            p => {
165                error!("Unsupported protocol {} in Multiaddr {}!", p, new_address);
166            }
167        }
168        Self(new_address)
169    }
170
171    pub fn is_localhost_ip(&self) -> bool {
172        let Some(protocol) = self.0.iter().next() else {
173            error!("Multiaddr is empty");
174            return false;
175        };
176        match protocol {
177            multiaddr::Protocol::Ip4(addr) => addr == Ipv4Addr::LOCALHOST,
178            multiaddr::Protocol::Ip6(addr) => addr == Ipv6Addr::LOCALHOST,
179            _ => false,
180        }
181    }
182
183    pub fn hostname(&self) -> Option<String> {
184        for component in self.iter() {
185            match component {
186                Protocol::Ip4(ip) => return Some(ip.to_string()),
187                Protocol::Ip6(ip) => return Some(ip.to_string()),
188                Protocol::Dns(dns) => return Some(dns.to_string()),
189                _ => (),
190            }
191        }
192        None
193    }
194
195    pub fn port(&self) -> Option<u16> {
196        for component in self.iter() {
197            match component {
198                Protocol::Udp(port) | Protocol::Tcp(port) => return Some(port),
199                _ => (),
200            }
201        }
202        None
203    }
204
205    pub fn rewrite_udp_to_tcp(&self) -> Self {
206        let mut new = Self::empty();
207
208        for component in self.iter() {
209            if let Protocol::Udp(port) = component {
210                new.push(Protocol::Tcp(port));
211            } else {
212                new.push(component);
213            }
214        }
215
216        new
217    }
218
219    pub fn rewrite_http_to_https(&self) -> Self {
220        let mut new = Self::empty();
221
222        for component in self.iter() {
223            if let Protocol::Http = component {
224                new.push(Protocol::Https);
225            } else {
226                new.push(component);
227            }
228        }
229
230        new
231    }
232}
233
234impl std::fmt::Display for Multiaddr {
235    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236        std::fmt::Display::fmt(&self.0, f)
237    }
238}
239
240impl std::str::FromStr for Multiaddr {
241    type Err = Error;
242
243    fn from_str(s: &str) -> Result<Self, Self::Err> {
244        ::multiaddr::Multiaddr::from_str(s).map(Self)
245    }
246}
247
248impl<'a> TryFrom<&'a str> for Multiaddr {
249    type Error = Error;
250
251    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
252        value.parse()
253    }
254}
255
256impl TryFrom<String> for Multiaddr {
257    type Error = Error;
258
259    fn try_from(value: String) -> Result<Self, Self::Error> {
260        value.parse()
261    }
262}
263
264impl serde::Serialize for Multiaddr {
265    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
266    where
267        S: serde::Serializer,
268    {
269        serializer.serialize_str(&self.0.to_string())
270    }
271}
272
273impl<'de> serde::Deserialize<'de> for Multiaddr {
274    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
275    where
276        D: serde::Deserializer<'de>,
277    {
278        let s = String::deserialize(deserializer)?;
279        s.parse()
280            .map(Self)
281            .map_err(|e| serde::de::Error::custom(e.to_string()))
282    }
283}
284
285impl std::net::ToSocketAddrs for Multiaddr {
286    type Iter = Box<dyn Iterator<Item = SocketAddr>>;
287
288    fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
289        let mut iter = self.iter();
290
291        match (iter.next(), iter.next()) {
292            (Some(Protocol::Ip4(ip4)), Some(Protocol::Tcp(port) | Protocol::Udp(port))) => {
293                (ip4, port)
294                    .to_socket_addrs()
295                    .map(|iter| Box::new(iter) as _)
296            }
297            (Some(Protocol::Ip6(ip6)), Some(Protocol::Tcp(port) | Protocol::Udp(port))) => {
298                (ip6, port)
299                    .to_socket_addrs()
300                    .map(|iter| Box::new(iter) as _)
301            }
302            (Some(Protocol::Dns(hostname)), Some(Protocol::Tcp(port) | Protocol::Udp(port))) => {
303                (hostname.as_ref(), port)
304                    .to_socket_addrs()
305                    .map(|iter| Box::new(iter) as _)
306            }
307            _ => Err(std::io::Error::new(
308                std::io::ErrorKind::InvalidInput,
309                "unable to convert Multiaddr to SocketAddr",
310            )),
311        }
312    }
313}
314
315pub(crate) fn parse_tcp<'a, T: Iterator<Item = Protocol<'a>>>(protocols: &mut T) -> Result<u16> {
316    if let Protocol::Tcp(port) = protocols
317        .next()
318        .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
319    {
320        Ok(port)
321    } else {
322        Err(eyre!("expected tcp protocol"))
323    }
324}
325
326pub(crate) fn parse_http_https<'a, T: Iterator<Item = Protocol<'a>>>(
327    protocols: &mut T,
328) -> Result<&'static str> {
329    match protocols.next() {
330        Some(Protocol::Http) => Ok("http"),
331        Some(Protocol::Https) => Ok("https"),
332        _ => Ok("http"),
333    }
334}
335
336pub(crate) fn parse_end<'a, T: Iterator<Item = Protocol<'a>>>(protocols: &mut T) -> Result<()> {
337    if protocols.next().is_none() {
338        Ok(())
339    } else {
340        Err(eyre!("expected end of multiaddr"))
341    }
342}
343
344// Parse a full /dns/-/tcp/-/{http,https} address
345pub(crate) fn parse_dns(address: &Multiaddr) -> Result<(Cow<'_, str>, u16, &'static str)> {
346    let mut iter = address.iter();
347
348    let dns_name = match iter
349        .next()
350        .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
351    {
352        Protocol::Dns(dns_name) => dns_name,
353        other => return Err(eyre!("expected dns found {other}")),
354    };
355    let tcp_port = parse_tcp(&mut iter)?;
356    let http_or_https = parse_http_https(&mut iter)?;
357    parse_end(&mut iter)?;
358    Ok((dns_name, tcp_port, http_or_https))
359}
360
361// Parse a full /ip4/-/tcp/-/{http,https} address
362pub(crate) fn parse_ip4(address: &Multiaddr) -> Result<(SocketAddr, &'static str)> {
363    let mut iter = address.iter();
364
365    let ip_addr = match iter
366        .next()
367        .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
368    {
369        Protocol::Ip4(ip4_addr) => IpAddr::V4(ip4_addr),
370        other => return Err(eyre!("expected ip4 found {other}")),
371    };
372    let tcp_port = parse_tcp(&mut iter)?;
373    let http_or_https = parse_http_https(&mut iter)?;
374    parse_end(&mut iter)?;
375    let socket_addr = SocketAddr::new(ip_addr, tcp_port);
376
377    Ok((socket_addr, http_or_https))
378}
379
380// Parse a full /ip6/-/tcp/-/{http,https} address
381pub(crate) fn parse_ip6(address: &Multiaddr) -> Result<(SocketAddr, &'static str)> {
382    let mut iter = address.iter();
383
384    let ip_addr = match iter
385        .next()
386        .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
387    {
388        Protocol::Ip6(ip6_addr) => IpAddr::V6(ip6_addr),
389        other => return Err(eyre!("expected ip6 found {other}")),
390    };
391    let tcp_port = parse_tcp(&mut iter)?;
392    let http_or_https = parse_http_https(&mut iter)?;
393    parse_end(&mut iter)?;
394    let socket_addr = SocketAddr::new(ip_addr, tcp_port);
395
396    Ok((socket_addr, http_or_https))
397}
398
399#[cfg(test)]
400mod test {
401    use super::Multiaddr;
402    use multiaddr::multiaddr;
403
404    #[test]
405    fn test_to_socket_addr_basic() {
406        let multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)));
407        let socket_addr_ipv4 = multi_addr_ipv4
408            .to_socket_addr()
409            .expect("Couldn't convert to socket addr");
410        assert_eq!(socket_addr_ipv4.to_string(), "127.0.0.1:10500");
411
412        let multi_addr_ipv6 = Multiaddr(multiaddr!(Ip6([172, 0, 0, 1, 1, 1, 1, 1]), Tcp(10500u16)));
413        let socket_addr_ipv6 = multi_addr_ipv6
414            .to_socket_addr()
415            .expect("Couldn't convert to socket addr");
416        assert_eq!(socket_addr_ipv6.to_string(), "[ac::1:1:1:1:1]:10500");
417    }
418
419    #[test]
420    fn test_to_socket_addr_unsupported_protocol() {
421        let multi_addr_dns = Multiaddr(multiaddr!(Dnsaddr("mysten.sui"), Tcp(10500u16)));
422        let _ = multi_addr_dns
423            .to_socket_addr()
424            .expect_err("DNS is unsupported");
425    }
426
427    #[test]
428    fn test_is_loosely_valid_tcp_addr() {
429        let multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)));
430        assert!(multi_addr_ipv4.is_loosely_valid_tcp_addr());
431        let multi_addr_ipv6 = Multiaddr(multiaddr!(Ip6([172, 0, 0, 1, 1, 1, 1, 1]), Tcp(10500u16)));
432        assert!(multi_addr_ipv6.is_loosely_valid_tcp_addr());
433        let multi_addr_dns = Multiaddr(multiaddr!(Dnsaddr("mysten.sui"), Tcp(10500u16)));
434        assert!(multi_addr_dns.is_loosely_valid_tcp_addr());
435
436        let multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Udp(10500u16)));
437        assert!(!multi_addr_ipv4.is_loosely_valid_tcp_addr());
438        let multi_addr_ipv6 = Multiaddr(multiaddr!(Ip6([172, 0, 0, 1, 1, 1, 1, 1]), Udp(10500u16)));
439        assert!(!multi_addr_ipv6.is_loosely_valid_tcp_addr());
440        let multi_addr_dns = Multiaddr(multiaddr!(Dnsaddr("mysten.sui"), Udp(10500u16)));
441        assert!(!multi_addr_dns.is_loosely_valid_tcp_addr());
442
443        let invalid_multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1])));
444        assert!(!invalid_multi_addr_ipv4.is_loosely_valid_tcp_addr());
445    }
446
447    #[test]
448    fn test_get_hostname_port() {
449        let multi_addr_ip4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)));
450        assert_eq!(Some("127.0.0.1".to_string()), multi_addr_ip4.hostname());
451        assert_eq!(Some(10500u16), multi_addr_ip4.port());
452
453        let multi_addr_dns = Multiaddr(multiaddr!(Dns("mysten.sui"), Tcp(10501u16)));
454        assert_eq!(Some("mysten.sui".to_string()), multi_addr_dns.hostname());
455        assert_eq!(Some(10501u16), multi_addr_dns.port());
456    }
457
458    #[test]
459    fn test_to_anemo_address() {
460        let addr_ip4 = Multiaddr(multiaddr!(Ip4([15, 15, 15, 1]), Udp(10500u16)))
461            .to_anemo_address()
462            .unwrap();
463        assert_eq!("15.15.15.1:10500".to_string(), addr_ip4.to_string());
464
465        let addr_ip6 = Multiaddr(multiaddr!(
466            Ip6([15, 15, 15, 15, 15, 15, 15, 1]),
467            Udp(10500u16)
468        ))
469        .to_anemo_address()
470        .unwrap();
471        assert_eq!("[f:f:f:f:f:f:f:1]:10500".to_string(), addr_ip6.to_string());
472
473        let addr_dns = Multiaddr(multiaddr!(Dns("mysten.sui"), Udp(10501u16)))
474            .to_anemo_address()
475            .unwrap();
476        assert_eq!("mysten.sui:10501".to_string(), addr_dns.to_string());
477
478        let addr_invalid =
479            Multiaddr(multiaddr!(Dns("mysten.sui"), Tcp(10501u16))).to_anemo_address();
480        assert!(addr_invalid.is_err());
481    }
482
483    #[test]
484    fn test_with_zero_ip() {
485        let multi_addr_ip4 =
486            Multiaddr(multiaddr!(Ip4([15, 15, 15, 1]), Tcp(10500u16))).with_zero_ip();
487        assert_eq!(Some("0.0.0.0".to_string()), multi_addr_ip4.hostname());
488        assert_eq!(Some(10500u16), multi_addr_ip4.port());
489
490        let multi_addr_ip6 = Multiaddr(multiaddr!(
491            Ip6([15, 15, 15, 15, 15, 15, 15, 1]),
492            Tcp(10500u16)
493        ))
494        .with_zero_ip();
495        assert_eq!(Some("::".to_string()), multi_addr_ip6.hostname());
496        assert_eq!(Some(10500u16), multi_addr_ip4.port());
497
498        let multi_addr_dns = Multiaddr(multiaddr!(Dns("mysten.sui"), Tcp(10501u16))).with_zero_ip();
499        assert_eq!(Some("0.0.0.0".to_string()), multi_addr_dns.hostname());
500        assert_eq!(Some(10501u16), multi_addr_dns.port());
501    }
502
503    #[test]
504    fn test_with_localhost_ip() {
505        let multi_addr_ip4 =
506            Multiaddr(multiaddr!(Ip4([15, 15, 15, 1]), Tcp(10500u16))).with_localhost_ip();
507        assert_eq!(Some("127.0.0.1".to_string()), multi_addr_ip4.hostname());
508        assert_eq!(Some(10500u16), multi_addr_ip4.port());
509
510        let multi_addr_ip6 = Multiaddr(multiaddr!(
511            Ip6([15, 15, 15, 15, 15, 15, 15, 1]),
512            Tcp(10500u16)
513        ))
514        .with_localhost_ip();
515        assert_eq!(Some("::1".to_string()), multi_addr_ip6.hostname());
516        assert_eq!(Some(10500u16), multi_addr_ip4.port());
517
518        let multi_addr_dns =
519            Multiaddr(multiaddr!(Dns("mysten.sui"), Tcp(10501u16))).with_localhost_ip();
520        assert_eq!(Some("127.0.0.1".to_string()), multi_addr_dns.hostname());
521        assert_eq!(Some(10501u16), multi_addr_dns.port());
522    }
523
524    #[test]
525    fn document_multiaddr_limitation_for_unix_protocol() {
526        // You can construct a multiaddr by hand (ie binary format) just fine
527        let path = "/tmp/foo";
528        let addr = Multiaddr::new_internal(multiaddr::multiaddr!(Unix(path), Http));
529
530        // But it doesn't round-trip in the human readable format
531        let s = addr.to_string();
532        assert!(s.parse::<Multiaddr>().is_err());
533    }
534}