use std::{
borrow::Cow,
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
};
pub use ::multiaddr::{Error, Protocol};
use anyhow::{anyhow, Result};
use tracing::error;
pub const EPOCH_PORT_OFFSET: u16 = 10;
pub const EPOCH_PORT_COUNT: u64 = 10;
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Multiaddr(::multiaddr::Multiaddr);
impl Multiaddr {
pub fn empty() -> Self {
Self(::multiaddr::Multiaddr::empty())
}
#[allow(dead_code)]
pub fn new_internal(inner: ::multiaddr::Multiaddr) -> Self {
Self(inner)
}
pub fn iter(&self) -> ::multiaddr::Iter<'_> {
self.0.iter()
}
pub fn pop<'a>(&mut self) -> Option<Protocol<'a>> {
self.0.pop()
}
pub fn push(&mut self, p: Protocol<'_>) -> &mut Self {
self.0.push(p);
self
}
pub fn replace<'a, F>(&self, at: usize, by: F) -> Option<Multiaddr>
where
F: FnOnce(&Protocol<'_>) -> Option<Protocol<'a>>,
{
self.0.replace(at, by).map(Self)
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn udp_multiaddr_to_listen_address(&self) -> Option<std::net::SocketAddr> {
let mut iter = self.iter();
match (iter.next(), iter.next()) {
(Some(Protocol::Ip4(ipaddr)), Some(Protocol::Udp(port))) => Some((ipaddr, port).into()),
(Some(Protocol::Ip6(ipaddr)), Some(Protocol::Udp(port))) => Some((ipaddr, port).into()),
(Some(Protocol::Dns(_)), Some(Protocol::Udp(port))) => {
Some((std::net::Ipv4Addr::UNSPECIFIED, port).into())
}
_ => None,
}
}
pub fn to_socket_addr(&self) -> Result<SocketAddr> {
let mut iter = self.iter();
let ip = match iter.next().ok_or_else(|| {
anyhow!("failed to convert to SocketAddr: Multiaddr does not contain IP")
})? {
Protocol::Ip4(ip4_addr) => IpAddr::V4(ip4_addr),
Protocol::Ip6(ip6_addr) => IpAddr::V6(ip6_addr),
unsupported => return Err(anyhow!("unsupported protocol {unsupported}")),
};
let tcp_port = parse_tcp(&mut iter)?;
Ok(SocketAddr::new(ip, tcp_port))
}
pub fn is_loosely_valid_tcp_addr(&self) -> bool {
let mut iter = self.iter();
iter.next(); match iter.next() {
Some(Protocol::Tcp(_)) => true,
_ => false, }
}
pub fn with_zero_ip(&self) -> Self {
let mut new_address = self.0.clone();
let Some(protocol) = new_address.iter().next() else {
error!("Multiaddr is empty");
return Self(new_address);
};
match protocol {
multiaddr::Protocol::Ip4(_)
| multiaddr::Protocol::Dns(_)
| multiaddr::Protocol::Dns4(_) => {
new_address = new_address
.replace(0, |_| Some(multiaddr::Protocol::Ip4(Ipv4Addr::UNSPECIFIED)))
.unwrap();
}
multiaddr::Protocol::Ip6(_) | multiaddr::Protocol::Dns6(_) => {
new_address = new_address
.replace(0, |_| Some(multiaddr::Protocol::Ip6(Ipv6Addr::UNSPECIFIED)))
.unwrap();
}
p => {
error!("Unsupported protocol {} in Multiaddr {}!", p, new_address);
}
}
Self(new_address)
}
pub fn with_localhost_ip(&self) -> Self {
let mut new_address = self.0.clone();
let Some(protocol) = new_address.iter().next() else {
error!("Multiaddr is empty");
return Self(new_address);
};
match protocol {
multiaddr::Protocol::Ip4(_)
| multiaddr::Protocol::Dns(_)
| multiaddr::Protocol::Dns4(_) => {
new_address = new_address
.replace(0, |_| Some(multiaddr::Protocol::Ip4(Ipv4Addr::LOCALHOST)))
.unwrap();
}
multiaddr::Protocol::Ip6(_) | multiaddr::Protocol::Dns6(_) => {
new_address = new_address
.replace(0, |_| Some(multiaddr::Protocol::Ip6(Ipv6Addr::LOCALHOST)))
.unwrap();
}
p => {
error!("Unsupported protocol {} in Multiaddr {}!", p, new_address);
}
}
Self(new_address)
}
pub fn is_localhost_ip(&self) -> bool {
let Some(protocol) = self.0.iter().next() else {
error!("Multiaddr is empty");
return false;
};
match protocol {
multiaddr::Protocol::Ip4(addr) => addr == Ipv4Addr::LOCALHOST,
multiaddr::Protocol::Ip6(addr) => addr == Ipv6Addr::LOCALHOST,
_ => false,
}
}
pub fn hostname(&self) -> Option<String> {
for component in self.iter() {
match component {
Protocol::Ip4(ip) => return Some(ip.to_string()),
Protocol::Ip6(ip) => return Some(ip.to_string()),
Protocol::Dns(dns) => return Some(dns.to_string()),
_ => (),
}
}
None
}
pub fn port(&self) -> Option<u16> {
for component in self.iter() {
match component {
Protocol::Udp(port) | Protocol::Tcp(port) => return Some(port),
_ => (),
}
}
None
}
pub fn rewrite_udp_to_tcp(&self) -> Self {
let mut new = Self::empty();
for component in self.iter() {
if let Protocol::Udp(port) = component {
new.push(Protocol::Tcp(port));
} else {
new.push(component);
}
}
new
}
pub fn rewrite_port(&self, new_port: u16) -> Self {
let mut new = Self::empty();
for component in self.iter() {
match component {
Protocol::Tcp(_) => new.push(Protocol::Tcp(new_port)),
Protocol::Udp(_) => new.push(Protocol::Udp(new_port)),
_ => new.push(component),
};
}
new
}
pub fn with_epoch_port_offset(&self, epoch: u64) -> Result<Self> {
let base_port = self
.port()
.ok_or_else(|| anyhow!("Multiaddr has no port: {}", self))?;
let epoch_u16 = u16::try_from(epoch % EPOCH_PORT_COUNT).map_err(|_| {
anyhow!(
"epoch slot {} (= {} % {}) exceeds u16::MAX for port-offset arithmetic",
epoch % EPOCH_PORT_COUNT,
epoch,
EPOCH_PORT_COUNT
)
})?;
let offset = epoch_u16.checked_mul(EPOCH_PORT_OFFSET).ok_or_else(|| {
anyhow!(
"epoch {} * EPOCH_PORT_OFFSET ({}) overflows u16",
epoch,
EPOCH_PORT_OFFSET
)
})?;
let new_port = base_port.checked_add(offset).ok_or_else(|| {
anyhow!(
"base port {} + offset {} (epoch {}) overflows u16",
base_port,
offset,
epoch
)
})?;
Ok(self.rewrite_port(new_port))
}
pub fn epoch_rotation_addrs(&self) -> Result<Vec<Self>> {
(0..EPOCH_PORT_COUNT)
.map(|slot| self.with_epoch_port_offset(slot))
.collect()
}
pub fn on_the_fly_addrs(&self, count: u64) -> Result<Vec<Self>> {
if count == 0 {
return Ok(Vec::new());
}
let base_port = self
.port()
.ok_or_else(|| anyhow!("Multiaddr has no port: {}", self))?;
let rotation_span = (EPOCH_PORT_COUNT as u16 - 1)
.checked_mul(EPOCH_PORT_OFFSET)
.ok_or_else(|| anyhow!("rotation span overflows u16 for {}", self))?;
(0..count)
.map(|j| {
let offset = rotation_span
.checked_add(
1 + u16::try_from(j)
.map_err(|_| anyhow!("on-the-fly index {j} too large"))?,
)
.ok_or_else(|| anyhow!("on-the-fly offset overflows u16 for {}", self))?;
let port = base_port
.checked_add(offset)
.ok_or_else(|| anyhow!("on-the-fly port overflows u16 for {}", self))?;
Ok(self.rewrite_port(port))
})
.collect()
}
pub fn to_vec(&self) -> Vec<u8> {
self.0.to_vec()
}
pub fn from_socket_addr_udp(addr: SocketAddr) -> Self {
let mut multiaddr = ::multiaddr::Multiaddr::empty();
match addr.ip() {
IpAddr::V4(ip) => multiaddr.push(Protocol::Ip4(ip)),
IpAddr::V6(ip) => multiaddr.push(Protocol::Ip6(ip)),
}
multiaddr.push(Protocol::Udp(addr.port()));
Self(multiaddr)
}
}
impl From<IpAddr> for Multiaddr {
fn from(ip_address: IpAddr) -> Self {
Multiaddr(ip_address.into())
}
}
impl From<Ipv4Addr> for Multiaddr {
fn from(ip_address: Ipv4Addr) -> Self {
Multiaddr(ip_address.into())
}
}
impl From<Ipv6Addr> for Multiaddr {
fn from(ip_address: Ipv6Addr) -> Self {
Multiaddr(ip_address.into())
}
}
impl std::fmt::Display for Multiaddr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
impl std::str::FromStr for Multiaddr {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
::multiaddr::Multiaddr::from_str(s).map(Self)
}
}
impl<'a> TryFrom<&'a str> for Multiaddr {
type Error = Error;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
value.parse()
}
}
impl TryFrom<String> for Multiaddr {
type Error = Error;
fn try_from(value: String) -> Result<Self, Self::Error> {
value.parse()
}
}
impl TryFrom<Vec<u8>> for Multiaddr {
type Error = Error;
fn try_from(v: Vec<u8>) -> multiaddr::Result<Self> {
Ok(Self(multiaddr::Multiaddr::try_from(v)?))
}
}
impl serde::Serialize for Multiaddr {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.0.to_string())
}
}
impl<'de> serde::Deserialize<'de> for Multiaddr {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
s.parse()
.map(Self)
.map_err(|e| serde::de::Error::custom(e.to_string()))
}
}
impl std::net::ToSocketAddrs for Multiaddr {
type Iter = Box<dyn Iterator<Item = SocketAddr>>;
fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
let mut iter = self.iter();
match (iter.next(), iter.next()) {
(Some(Protocol::Ip4(ip4)), Some(Protocol::Tcp(port) | Protocol::Udp(port))) => {
(ip4, port)
.to_socket_addrs()
.map(|iter| Box::new(iter) as _)
}
(Some(Protocol::Ip6(ip6)), Some(Protocol::Tcp(port) | Protocol::Udp(port))) => {
(ip6, port)
.to_socket_addrs()
.map(|iter| Box::new(iter) as _)
}
(Some(Protocol::Dns(hostname)), Some(Protocol::Tcp(port) | Protocol::Udp(port))) => {
(hostname.as_ref(), port)
.to_socket_addrs()
.map(|iter| Box::new(iter) as _)
}
_ => Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"unable to convert Multiaddr to SocketAddr",
)),
}
}
}
pub(crate) fn parse_tcp<'a, T: Iterator<Item = Protocol<'a>>>(protocols: &mut T) -> Result<u16> {
if let Protocol::Tcp(port) = protocols
.next()
.ok_or_else(|| anyhow!("unexpected end of multiaddr"))?
{
Ok(port)
} else {
Err(anyhow!("expected tcp protocol"))
}
}
#[allow(dead_code)]
pub(crate) fn parse_http_https<'a, T: Iterator<Item = Protocol<'a>>>(
protocols: &mut T,
) -> Result<&'static str> {
match protocols.next() {
Some(Protocol::Http) => Ok("http"),
Some(Protocol::Https) => Ok("https"),
_ => Ok("http"),
}
}
#[allow(dead_code)]
pub(crate) fn parse_end<'a, T: Iterator<Item = Protocol<'a>>>(protocols: &mut T) -> Result<()> {
if protocols.next().is_none() {
Ok(())
} else {
Err(anyhow!("expected end of multiaddr"))
}
}
#[allow(dead_code)]
pub fn parse_dns(address: &Multiaddr) -> Result<(Cow<'_, str>, u16, &'static str)> {
let mut iter = address.iter();
let dns_name = match iter
.next()
.ok_or_else(|| anyhow!("unexpected end of multiaddr"))?
{
Protocol::Dns(dns_name) => dns_name,
other => return Err(anyhow!("expected dns found {other}")),
};
let tcp_port = parse_tcp(&mut iter)?;
let http_or_https = parse_http_https(&mut iter)?;
parse_end(&mut iter)?;
Ok((dns_name, tcp_port, http_or_https))
}
#[allow(dead_code)]
pub fn parse_dns4(address: &Multiaddr) -> Result<(Cow<'_, str>, u16, &'static str)> {
let mut iter = address.iter();
let dns_name = match iter
.next()
.ok_or_else(|| anyhow!("unexpected end of multiaddr"))?
{
Protocol::Dns4(dns_name) => dns_name,
other => return Err(anyhow!("expected dns4 found {other}")),
};
let tcp_port = parse_tcp(&mut iter)?;
let http_or_https = parse_http_https(&mut iter)?;
parse_end(&mut iter)?;
Ok((dns_name, tcp_port, http_or_https))
}
#[allow(dead_code)]
pub fn parse_dns6(address: &Multiaddr) -> Result<(Cow<'_, str>, u16, &'static str)> {
let mut iter = address.iter();
let dns_name = match iter
.next()
.ok_or_else(|| anyhow!("unexpected end of multiaddr"))?
{
Protocol::Dns6(dns_name) => dns_name,
other => return Err(anyhow!("expected dns6 found {other}")),
};
let tcp_port = parse_tcp(&mut iter)?;
let http_or_https = parse_http_https(&mut iter)?;
parse_end(&mut iter)?;
Ok((dns_name, tcp_port, http_or_https))
}
pub fn is_tcp_host_multiaddr(addr: &Multiaddr) -> bool {
parse_ip4(addr).is_ok()
|| parse_ip6(addr).is_ok()
|| parse_dns(addr).is_ok()
|| parse_dns4(addr).is_ok()
|| parse_dns6(addr).is_ok()
}
#[allow(dead_code)]
pub fn parse_ip4(address: &Multiaddr) -> Result<(SocketAddr, &'static str)> {
let mut iter = address.iter();
let ip_addr = match iter
.next()
.ok_or_else(|| anyhow!("unexpected end of multiaddr"))?
{
Protocol::Ip4(ip4_addr) => IpAddr::V4(ip4_addr),
other => return Err(anyhow!("expected ip4 found {other}")),
};
let tcp_port = parse_tcp(&mut iter)?;
let http_or_https = parse_http_https(&mut iter)?;
parse_end(&mut iter)?;
let socket_addr = SocketAddr::new(ip_addr, tcp_port);
Ok((socket_addr, http_or_https))
}
#[allow(dead_code)]
pub fn parse_ip6(address: &Multiaddr) -> Result<(SocketAddr, &'static str)> {
let mut iter = address.iter();
let ip_addr = match iter
.next()
.ok_or_else(|| anyhow!("unexpected end of multiaddr"))?
{
Protocol::Ip6(ip6_addr) => IpAddr::V6(ip6_addr),
other => return Err(anyhow!("expected ip6 found {other}")),
};
let tcp_port = parse_tcp(&mut iter)?;
let http_or_https = parse_http_https(&mut iter)?;
parse_end(&mut iter)?;
let socket_addr = SocketAddr::new(ip_addr, tcp_port);
Ok((socket_addr, http_or_https))
}
#[cfg(test)]
mod test {
use multiaddr::multiaddr;
use super::Multiaddr;
#[test]
fn test_to_socket_addr_basic() {
let multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)));
let socket_addr_ipv4 = multi_addr_ipv4
.to_socket_addr()
.expect("Couldn't convert to socket addr");
assert_eq!(socket_addr_ipv4.to_string(), "127.0.0.1:10500");
let multi_addr_ipv6 = Multiaddr(multiaddr!(Ip6([172, 0, 0, 1, 1, 1, 1, 1]), Tcp(10500u16)));
let socket_addr_ipv6 = multi_addr_ipv6
.to_socket_addr()
.expect("Couldn't convert to socket addr");
assert_eq!(socket_addr_ipv6.to_string(), "[ac::1:1:1:1:1]:10500");
}
#[test]
fn test_to_socket_addr_unsupported_protocol() {
let multi_addr_dns = Multiaddr(multiaddr!(Dnsaddr("mysten.sui"), Tcp(10500u16)));
let _ = multi_addr_dns
.to_socket_addr()
.expect_err("DNS is unsupported");
}
#[test]
fn test_is_loosely_valid_tcp_addr() {
let multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)));
assert!(multi_addr_ipv4.is_loosely_valid_tcp_addr());
let multi_addr_ipv6 = Multiaddr(multiaddr!(Ip6([172, 0, 0, 1, 1, 1, 1, 1]), Tcp(10500u16)));
assert!(multi_addr_ipv6.is_loosely_valid_tcp_addr());
let multi_addr_dns = Multiaddr(multiaddr!(Dnsaddr("mysten.sui"), Tcp(10500u16)));
assert!(multi_addr_dns.is_loosely_valid_tcp_addr());
let multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Udp(10500u16)));
assert!(!multi_addr_ipv4.is_loosely_valid_tcp_addr());
let multi_addr_ipv6 = Multiaddr(multiaddr!(Ip6([172, 0, 0, 1, 1, 1, 1, 1]), Udp(10500u16)));
assert!(!multi_addr_ipv6.is_loosely_valid_tcp_addr());
let multi_addr_dns = Multiaddr(multiaddr!(Dnsaddr("mysten.sui"), Udp(10500u16)));
assert!(!multi_addr_dns.is_loosely_valid_tcp_addr());
let invalid_multi_addr_ipv4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1])));
assert!(!invalid_multi_addr_ipv4.is_loosely_valid_tcp_addr());
}
#[test]
fn test_get_hostname_port() {
let multi_addr_ip4 = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)));
assert_eq!(Some("127.0.0.1".to_string()), multi_addr_ip4.hostname());
assert_eq!(Some(10500u16), multi_addr_ip4.port());
let multi_addr_dns = Multiaddr(multiaddr!(Dns("mysten.sui"), Tcp(10501u16)));
assert_eq!(Some("mysten.sui".to_string()), multi_addr_dns.hostname());
assert_eq!(Some(10501u16), multi_addr_dns.port());
}
#[test]
fn test_with_zero_ip() {
let multi_addr_ip4 =
Multiaddr(multiaddr!(Ip4([15, 15, 15, 1]), Tcp(10500u16))).with_zero_ip();
assert_eq!(Some("0.0.0.0".to_string()), multi_addr_ip4.hostname());
assert_eq!(Some(10500u16), multi_addr_ip4.port());
let multi_addr_ip6 = Multiaddr(multiaddr!(
Ip6([15, 15, 15, 15, 15, 15, 15, 1]),
Tcp(10500u16)
))
.with_zero_ip();
assert_eq!(Some("::".to_string()), multi_addr_ip6.hostname());
assert_eq!(Some(10500u16), multi_addr_ip4.port());
let multi_addr_dns = Multiaddr(multiaddr!(Dns("mysten.sui"), Tcp(10501u16))).with_zero_ip();
assert_eq!(Some("0.0.0.0".to_string()), multi_addr_dns.hostname());
assert_eq!(Some(10501u16), multi_addr_dns.port());
}
#[test]
fn test_with_localhost_ip() {
let multi_addr_ip4 =
Multiaddr(multiaddr!(Ip4([15, 15, 15, 1]), Tcp(10500u16))).with_localhost_ip();
assert_eq!(Some("127.0.0.1".to_string()), multi_addr_ip4.hostname());
assert_eq!(Some(10500u16), multi_addr_ip4.port());
let multi_addr_ip6 = Multiaddr(multiaddr!(
Ip6([15, 15, 15, 15, 15, 15, 15, 1]),
Tcp(10500u16)
))
.with_localhost_ip();
assert_eq!(Some("::1".to_string()), multi_addr_ip6.hostname());
assert_eq!(Some(10500u16), multi_addr_ip4.port());
let multi_addr_dns =
Multiaddr(multiaddr!(Dns("mysten.sui"), Tcp(10501u16))).with_localhost_ip();
assert_eq!(Some("127.0.0.1".to_string()), multi_addr_dns.hostname());
assert_eq!(Some(10501u16), multi_addr_dns.port());
}
#[test]
fn test_with_epoch_port_offset() {
let addr = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Udp(53203u16)));
let expected = |slot: u16| 53203 + slot * super::EPOCH_PORT_OFFSET;
let e0 = addr.with_epoch_port_offset(0).unwrap();
assert_eq!(e0.port(), Some(expected(0)));
let e1 = addr.with_epoch_port_offset(1).unwrap();
assert_eq!(e1.port(), Some(expected(1)));
let e9 = addr.with_epoch_port_offset(9).unwrap();
assert_eq!(e9.port(), Some(expected(9)));
let e10 = addr.with_epoch_port_offset(10).unwrap();
assert_eq!(e10.port(), Some(expected(0)));
let e11 = addr.with_epoch_port_offset(11).unwrap();
assert_eq!(e11.port(), Some(expected(1)));
assert_eq!(e9.hostname(), Some("127.0.0.1".to_string()));
let big = addr.with_epoch_port_offset(u64::MAX).unwrap();
assert_eq!(big.port(), Some(expected(5)));
let very_big = addr.with_epoch_port_offset(65_536).unwrap();
assert_eq!(very_big.port(), Some(expected(6)));
let overflow_base: u16 = u16::MAX - 9 * super::EPOCH_PORT_OFFSET + 1;
let too_high = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Udp(overflow_base)));
assert!(
too_high.with_epoch_port_offset(9).is_err(),
"base + slot-9 offset overflows u16; must surface as Err"
);
assert_eq!(
too_high.with_epoch_port_offset(0).unwrap().port(),
Some(overflow_base)
);
let no_port = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1])));
assert!(no_port.with_epoch_port_offset(1).is_err());
}
#[test]
fn test_epoch_rotation_addrs() {
let base = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Udp(53203u16)));
let rotation = base.epoch_rotation_addrs().unwrap();
assert_eq!(rotation.len(), super::EPOCH_PORT_COUNT as usize);
for (slot, addr) in rotation.iter().enumerate() {
assert_eq!(
addr.port(),
Some(53203 + slot as u16 * super::EPOCH_PORT_OFFSET),
"slot {slot} must equal base + slot * EPOCH_PORT_OFFSET"
);
}
assert_eq!(rotation[0], base);
let ports: std::collections::HashSet<u16> =
rotation.iter().filter_map(|a| a.port()).collect();
for epoch in [0u64, 1, 9, 10, 11, 19, 100, 999, u64::MAX] {
let key = base.with_epoch_port_offset(epoch).unwrap().port().unwrap();
assert!(
ports.contains(&key),
"epoch {epoch} lend key {key} not in rotation"
);
}
}
#[test]
fn test_on_the_fly_addrs() {
let base = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Udp(53203u16)));
assert!(base.on_the_fly_addrs(0).unwrap().is_empty());
let on_the_fly = base.on_the_fly_addrs(3).unwrap();
assert_eq!(on_the_fly.len(), 3);
let span = (super::EPOCH_PORT_COUNT as u16 - 1) * super::EPOCH_PORT_OFFSET;
for (j, addr) in on_the_fly.iter().enumerate() {
assert_eq!(addr.port(), Some(53203 + span + 1 + j as u16));
}
let rotation: std::collections::HashSet<u16> = base
.epoch_rotation_addrs()
.unwrap()
.iter()
.filter_map(|a| a.port())
.collect();
for addr in &on_the_fly {
assert!(
!rotation.contains(&addr.port().unwrap()),
"on-the-fly port {:?} collides with the active rotation",
addr.port()
);
}
assert_eq!(on_the_fly[0].hostname(), Some("127.0.0.1".to_string()));
let no_port = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1])));
assert!(no_port.on_the_fly_addrs(1).is_err());
}
#[test]
fn test_rewrite_port() {
let multi_addr_tcp = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Tcp(8000u16)));
let rewritten_tcp = multi_addr_tcp.rewrite_port(9000);
assert_eq!(Some(9000u16), rewritten_tcp.port());
assert_eq!(Some("127.0.0.1".to_string()), rewritten_tcp.hostname());
let multi_addr_udp = Multiaddr(multiaddr!(Ip4([127, 0, 0, 1]), Udp(8000u16)));
let rewritten_udp = multi_addr_udp.rewrite_port(9000);
assert_eq!(Some(9000u16), rewritten_udp.port());
assert_eq!(Some("127.0.0.1".to_string()), rewritten_udp.hostname());
let multi_addr_dns = Multiaddr(multiaddr!(Dns("example.com"), Tcp(8080u16)));
let rewritten_dns = multi_addr_dns.rewrite_port(8443);
assert_eq!(Some(8443u16), rewritten_dns.port());
assert_eq!(Some("example.com".to_string()), rewritten_dns.hostname());
}
#[test]
fn test_from_socket_addr_udp_ipv4() {
use std::net::SocketAddr;
let socket_addr: SocketAddr = "127.0.0.1:12345".parse().unwrap();
let multiaddr = Multiaddr::from_socket_addr_udp(socket_addr);
assert_eq!(multiaddr.to_string(), "/ip4/127.0.0.1/udp/12345");
assert_eq!(multiaddr.hostname(), Some("127.0.0.1".to_string()));
assert_eq!(multiaddr.port(), Some(12345));
}
#[test]
fn test_from_socket_addr_udp_ipv6() {
use std::net::SocketAddr;
let socket_addr: SocketAddr = "[::1]:54321".parse().unwrap();
let multiaddr = Multiaddr::from_socket_addr_udp(socket_addr);
assert_eq!(multiaddr.to_string(), "/ip6/::1/udp/54321");
assert_eq!(multiaddr.hostname(), Some("::1".to_string()));
assert_eq!(multiaddr.port(), Some(54321));
}
#[test]
fn test_from_socket_addr_udp_roundtrip() {
use std::net::SocketAddr;
let original: SocketAddr = "192.168.1.100:8080".parse().unwrap();
let multiaddr = Multiaddr::from_socket_addr_udp(original);
let recovered = multiaddr.udp_multiaddr_to_listen_address().unwrap();
assert_eq!(original, recovered);
}
use std::str::FromStr;
use super::is_tcp_host_multiaddr;
fn ma(s: &str) -> Multiaddr {
Multiaddr::from_str(s).expect("valid multiaddr literal for test")
}
#[test]
fn test_is_tcp_host_multiaddr_ip4_tcp_bare() {
assert!(is_tcp_host_multiaddr(&ma("/ip4/127.0.0.1/tcp/4300")));
}
#[test]
fn test_is_tcp_host_multiaddr_ip4_tcp_http() {
assert!(is_tcp_host_multiaddr(&ma("/ip4/127.0.0.1/tcp/4300/http")));
}
#[test]
fn test_is_tcp_host_multiaddr_ip4_tcp_https() {
assert!(is_tcp_host_multiaddr(&ma("/ip4/127.0.0.1/tcp/4300/https")));
}
#[test]
fn test_is_tcp_host_multiaddr_ip6_tcp_bare() {
assert!(is_tcp_host_multiaddr(&ma("/ip6/::1/tcp/4300")));
}
#[test]
fn test_is_tcp_host_multiaddr_ip6_tcp_http() {
assert!(is_tcp_host_multiaddr(&ma("/ip6/::1/tcp/4300/http")));
}
#[test]
fn test_is_tcp_host_multiaddr_dns_tcp_bare() {
assert!(is_tcp_host_multiaddr(&ma("/dns/host.example.com/tcp/4300")));
}
#[test]
fn test_is_tcp_host_multiaddr_dns_tcp_http() {
assert!(is_tcp_host_multiaddr(&ma(
"/dns/host.example.com/tcp/4300/http"
)));
}
#[test]
fn test_is_tcp_host_multiaddr_dns4_tcp_bare() {
assert!(is_tcp_host_multiaddr(&ma(
"/dns4/host.example.com/tcp/4300"
)));
}
#[test]
fn test_is_tcp_host_multiaddr_dns4_tcp_http() {
assert!(is_tcp_host_multiaddr(&ma(
"/dns4/host.example.com/tcp/4300/http"
)));
}
#[test]
fn test_is_tcp_host_multiaddr_dns6_tcp_bare() {
assert!(is_tcp_host_multiaddr(&ma(
"/dns6/host.example.com/tcp/4300"
)));
}
#[test]
fn test_is_tcp_host_multiaddr_dns6_tcp_https() {
assert!(is_tcp_host_multiaddr(&ma(
"/dns6/host.example.com/tcp/4300/https"
)));
}
#[test]
fn test_is_tcp_host_multiaddr_rejects_udp() {
assert!(!is_tcp_host_multiaddr(&ma("/ip4/127.0.0.1/udp/4300")));
assert!(!is_tcp_host_multiaddr(&ma("/ip6/::1/udp/4300")));
assert!(!is_tcp_host_multiaddr(&ma(
"/dns/host.example.com/udp/4300"
)));
assert!(!is_tcp_host_multiaddr(&ma(
"/dns4/host.example.com/udp/4300"
)));
assert!(!is_tcp_host_multiaddr(&ma(
"/dns6/host.example.com/udp/4300"
)));
}
#[test]
fn test_is_tcp_host_multiaddr_rejects_missing_port() {
assert!(!is_tcp_host_multiaddr(&ma("/ip4/127.0.0.1")));
assert!(!is_tcp_host_multiaddr(&ma("/dns/host.example.com")));
}
#[test]
fn test_is_tcp_host_multiaddr_rejects_missing_host() {
assert!(!is_tcp_host_multiaddr(&ma("/tcp/4300")));
}
#[test]
fn test_is_tcp_host_multiaddr_rejects_empty() {
assert!(!is_tcp_host_multiaddr(&Multiaddr::empty()));
}
#[test]
fn test_is_tcp_host_multiaddr_accepts_single_trailing_protocol_today() {
assert!(is_tcp_host_multiaddr(&ma("/ip4/127.0.0.1/tcp/4300/ws")));
assert!(is_tcp_host_multiaddr(&ma(
"/ip4/127.0.0.1/tcp/4300/quic-v1"
)));
}
#[test]
fn test_is_tcp_host_multiaddr_rejects_multi_trailing_junk() {
assert!(!is_tcp_host_multiaddr(&ma(
"/ip4/127.0.0.1/tcp/4300/tls/ws"
)));
}
#[test]
fn test_is_tcp_host_multiaddr_rejects_udp_quic() {
assert!(!is_tcp_host_multiaddr(&ma(
"/ip4/127.0.0.1/udp/4300/quic-v1"
)));
}
}