mysten_network/
multiaddr.rs

1// Copyright (c) 2022, Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use eyre::{eyre, Result};
5use multiaddr::{Multiaddr, Protocol};
6use std::{
7    borrow::Cow,
8    net::{IpAddr, SocketAddr},
9};
10
11// Converts a /ip{4,6}/-/tcp/-[/-] Multiaddr to SocketAddr.
12// Useful when an external library only accepts SocketAddr, e.g. to start a local server.
13// See `client::endpoint_from_multiaddr()` for converting to Endpoint for clients.
14pub fn to_socket_addr(addr: &Multiaddr) -> Result<SocketAddr> {
15    let mut iter = addr.iter();
16    let ip = match iter
17        .next()
18        .ok_or_else(|| eyre!("failed to convert to SocketAddr: Multiaddr does not contain IP"))?
19    {
20        Protocol::Ip4(ip4_addr) => IpAddr::V4(ip4_addr),
21        Protocol::Ip6(ip6_addr) => IpAddr::V6(ip6_addr),
22        unsupported => return Err(eyre!("unsupported protocol {unsupported}")),
23    };
24    let tcp_port = parse_tcp(&mut iter)?;
25    Ok(SocketAddr::new(ip, tcp_port))
26}
27
28pub(crate) fn parse_tcp<'a, T: Iterator<Item = Protocol<'a>>>(protocols: &mut T) -> Result<u16> {
29    if let Protocol::Tcp(port) = protocols
30        .next()
31        .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
32    {
33        Ok(port)
34    } else {
35        Err(eyre!("expected tcp protocol"))
36    }
37}
38
39pub(crate) fn parse_http_https<'a, T: Iterator<Item = Protocol<'a>>>(
40    protocols: &mut T,
41) -> Result<&'static str> {
42    match protocols
43        .next()
44        .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
45    {
46        Protocol::Http => Ok("http"),
47        Protocol::Https => Ok("https"),
48        _ => Err(eyre!("expected http/https protocol")),
49    }
50}
51
52pub(crate) fn parse_end<'a, T: Iterator<Item = Protocol<'a>>>(protocols: &mut T) -> Result<()> {
53    if protocols.next().is_none() {
54        Ok(())
55    } else {
56        Err(eyre!("expected end of multiaddr"))
57    }
58}
59
60// Parse a full /dns/-/tcp/-/{http,https} address
61pub(crate) fn parse_dns(address: &Multiaddr) -> Result<(Cow<'_, str>, u16, &'static str)> {
62    let mut iter = address.iter();
63
64    let dns_name = match iter
65        .next()
66        .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
67    {
68        Protocol::Dns(dns_name) => dns_name,
69        other => return Err(eyre!("expected dns found {other}")),
70    };
71    let tcp_port = parse_tcp(&mut iter)?;
72    let http_or_https = parse_http_https(&mut iter)?;
73    parse_end(&mut iter)?;
74    Ok((dns_name, tcp_port, http_or_https))
75}
76
77// Parse a full /ip4/-/tcp/-/{http,https} address
78pub(crate) fn parse_ip4(address: &Multiaddr) -> Result<(SocketAddr, &'static str)> {
79    let mut iter = address.iter();
80
81    let ip_addr = match iter
82        .next()
83        .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
84    {
85        Protocol::Ip4(ip4_addr) => IpAddr::V4(ip4_addr),
86        other => return Err(eyre!("expected ip4 found {other}")),
87    };
88    let tcp_port = parse_tcp(&mut iter)?;
89    let http_or_https = parse_http_https(&mut iter)?;
90    parse_end(&mut iter)?;
91    let socket_addr = SocketAddr::new(ip_addr, tcp_port);
92
93    Ok((socket_addr, http_or_https))
94}
95
96// Parse a full /ip6/-/tcp/-/{http,https} address
97pub(crate) fn parse_ip6(address: &Multiaddr) -> Result<(SocketAddr, &'static str)> {
98    let mut iter = address.iter();
99
100    let ip_addr = match iter
101        .next()
102        .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
103    {
104        Protocol::Ip6(ip6_addr) => IpAddr::V6(ip6_addr),
105        other => return Err(eyre!("expected ip6 found {other}")),
106    };
107    let tcp_port = parse_tcp(&mut iter)?;
108    let http_or_https = parse_http_https(&mut iter)?;
109    parse_end(&mut iter)?;
110    let socket_addr = SocketAddr::new(ip_addr, tcp_port);
111
112    Ok((socket_addr, http_or_https))
113}
114
115// Parse a full /unix/-/{http,https} address
116#[cfg(unix)]
117pub(crate) fn parse_unix(address: &Multiaddr) -> Result<(Cow<'_, str>, &'static str)> {
118    let mut iter = address.iter();
119
120    let path = match iter
121        .next()
122        .ok_or_else(|| eyre!("unexpected end of multiaddr"))?
123    {
124        Protocol::Unix(path) => path,
125        other => return Err(eyre!("expected unix found {other}")),
126    };
127    let http_or_https = parse_http_https(&mut iter)?;
128    parse_end(&mut iter)?;
129
130    Ok((path, http_or_https))
131}
132
133#[cfg(test)]
134mod test {
135    use super::to_socket_addr;
136    use multiaddr::multiaddr;
137
138    #[test]
139    fn test_to_socket_addr_basic() {
140        let multi_addr_ipv4 = multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16));
141        let socket_addr_ipv4 =
142            to_socket_addr(&multi_addr_ipv4).expect("Couldn't convert to socket addr");
143        assert_eq!(socket_addr_ipv4.to_string(), "127.0.0.1:10500");
144
145        let multi_addr_ipv6 = multiaddr!(Ip6([172, 0, 0, 1, 1, 1, 1, 1]), Tcp(10500u16));
146        let socket_addr_ipv6 =
147            to_socket_addr(&multi_addr_ipv6).expect("Couldn't convert to socket addr");
148        assert_eq!(socket_addr_ipv6.to_string(), "[ac::1:1:1:1:1]:10500");
149    }
150
151    #[test]
152    fn test_to_socket_addr_unsupported_protocol() {
153        let multi_addr_dns = multiaddr!(Dnsaddr("mysten.sui"), Tcp(10500u16));
154        let _ = to_socket_addr(&multi_addr_dns).expect_err("DNS is unsupported");
155    }
156}