jam_std_common/net/
mod.rs

1mod peer_id;
2
3pub use peer_id::{ParseErr as PeerIdParseErr, PeerId};
4use scale::{Decode, Encode, MaxEncodedLen};
5use std::{net::SocketAddr, str::FromStr};
6
7/// Address of a peer, including UDP port number. Convertible to/from [`SocketAddr`]. The reason we
8/// don't just use [`SocketAddr`] is that [`SocketAddr`] values cannot be directly SCALE
9/// encoded/decoded; `PeerAddr` values can be.
10#[derive(Clone, Copy, Encode, Decode, MaxEncodedLen, PartialEq, Eq)]
11pub struct PeerAddr {
12	/// IPv6 address. May be an [IPv4-mapped
13	/// address](https://www.rfc-editor.org/rfc/rfc4291#section-2.5.5.2).
14	pub ip: [u8; 16],
15	/// UDP port.
16	pub port: u16,
17}
18
19impl core::fmt::Debug for PeerAddr {
20	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
21		SocketAddr::from(*self).fmt(f)
22	}
23}
24
25impl core::fmt::Display for PeerAddr {
26	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
27		SocketAddr::from(*self).fmt(f)
28	}
29}
30
31impl TryFrom<&str> for PeerAddr {
32	type Error = std::net::AddrParseError;
33
34	fn try_from(s: &str) -> Result<Self, Self::Error> {
35		Ok(SocketAddr::from_str(s)?.into())
36	}
37}
38
39impl FromStr for PeerAddr {
40	type Err = std::net::AddrParseError;
41
42	fn from_str(s: &str) -> Result<Self, Self::Err> {
43		s.try_into()
44	}
45}
46
47impl From<SocketAddr> for PeerAddr {
48	fn from(addr: SocketAddr) -> Self {
49		let ip = match addr.ip() {
50			std::net::IpAddr::V4(ip) => ip.to_ipv6_mapped(),
51			std::net::IpAddr::V6(ip) => ip,
52		};
53		Self { ip: ip.octets(), port: addr.port() }
54	}
55}
56
57impl From<PeerAddr> for SocketAddr {
58	fn from(addr: PeerAddr) -> Self {
59		Self::new(std::net::Ipv6Addr::from(addr.ip).to_canonical(), addr.port)
60	}
61}
62
63/// ID and address of a peer.
64#[derive(Clone, Encode, Decode, MaxEncodedLen, PartialEq, Eq)]
65pub struct PeerDetails {
66	pub id: PeerId,
67	pub addr: PeerAddr,
68}
69
70impl core::fmt::Debug for PeerDetails {
71	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
72		write!(f, "{:?}@{:?}", self.id, self.addr)
73	}
74}
75
76impl core::fmt::Display for PeerDetails {
77	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
78		write!(f, "{}@{}", self.id, self.addr)
79	}
80}
81
82/// Error parsing peer details (ID and address).
83#[derive(Debug, thiserror::Error)]
84pub enum PeerDetailsParseErr {
85	#[error("No @ character; expected between peer ID and address")]
86	NoSeparator,
87	#[error("Bad peer ID: {0}")]
88	Id(#[from] PeerIdParseErr),
89	#[error("Bad address: {0}")]
90	Addr(#[from] std::net::AddrParseError),
91}
92
93impl TryFrom<&str> for PeerDetails {
94	type Error = PeerDetailsParseErr;
95
96	fn try_from(s: &str) -> Result<Self, Self::Error> {
97		let Some((id, addr)) = s.split_once('@') else {
98			return Err(PeerDetailsParseErr::NoSeparator)
99		};
100		Ok(Self { id: id.try_into()?, addr: addr.try_into()? })
101	}
102}
103
104impl FromStr for PeerDetails {
105	type Err = PeerDetailsParseErr;
106
107	fn from_str(s: &str) -> Result<Self, Self::Err> {
108		s.try_into()
109	}
110}
111
112#[cfg(any(test, feature = "rand"))]
113impl rand::distr::Distribution<PeerAddr> for rand::distr::StandardUniform {
114	fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> PeerAddr {
115		let ip = if rng.random() {
116			// IPv4-mapped address
117			(rng.random::<u32>() as u128) | (0xffff << 32)
118		} else {
119			// Global unicast address
120			(rng.random::<u128>() & !(0x7 << 125)) | (1 << 125)
121		};
122		PeerAddr { ip: ip.to_be_bytes(), port: rng.random() }
123	}
124}
125
126#[cfg(any(test, feature = "rand"))]
127impl rand::distr::Distribution<PeerDetails> for rand::distr::StandardUniform {
128	fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> PeerDetails {
129		PeerDetails { id: rng.random(), addr: rng.random() }
130	}
131}