urma_node/
peer_address.rs1use crate::{Error, peer_gossip::is_public};
7use libp2p::{Multiaddr, PeerId, multiaddr::Protocol};
8use std::net::IpAddr;
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub enum AddressKind {
12 Direct,
13 Circuit(PeerId),
15}
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub enum Dialed {
22 Direct(PeerId),
23 Relayed(PeerId),
24}
25
26pub struct Shape {
29 pub bare: Multiaddr,
30 pub peer: Option<PeerId>,
31 pub kind: AddressKind,
32}
33
34pub fn parse(address: &Multiaddr) -> Result<Shape, Error> {
37 let invalid = || Error::Invalid("peer address shape is not dialable".into());
38 let parts: Vec<Protocol<'_>> = address.iter().collect();
39 let (peer, body) = match parts.split_last() {
40 Some((Protocol::P2p(peer), body)) => (Some(*peer), body),
41 _ => (None, &parts[..]),
42 };
43 let kind = match body {
44 [Protocol::Dnsaddr(_)] => AddressKind::Direct,
45 [host, transport @ ..] if direct_host(host) && transport_port(transport) => {
46 AddressKind::Direct
47 }
48 [
49 Protocol::Ip4(_),
50 transport @ ..,
51 Protocol::P2p(relay),
52 Protocol::P2pCircuit,
53 ] if transport_port(transport) && peer != Some(*relay) => AddressKind::Circuit(*relay),
54 _ => return Err(invalid()),
55 };
56 let bare: Multiaddr = body.iter().cloned().collect();
57 if matches!(kind, AddressKind::Circuit(_)) && !is_public(&bare) {
58 return Err(Error::Invalid("circuit relay must be a public IPv4".into()));
59 }
60 Ok(Shape { bare, peer, kind })
61}
62
63pub fn is_circuit(address: &Multiaddr) -> bool {
65 address.iter().any(|part| part == Protocol::P2pCircuit)
66}
67
68pub fn is_quic(address: &Multiaddr) -> bool {
70 address.iter().any(|part| part == Protocol::QuicV1)
71}
72
73pub(crate) fn quic_first(mut addrs: Vec<Multiaddr>) -> Vec<Multiaddr> {
77 addrs.sort_by_key(|address| !is_quic(address));
78 addrs
79}
80
81pub(crate) fn place_quic_first(addrs: &mut Vec<Multiaddr>, address: Multiaddr) {
84 let at = match is_quic(&address) {
85 true => 0,
86 false => addrs.len(),
87 };
88 addrs.insert(at, address);
89}
90
91pub fn relay(address: &Multiaddr) -> Option<PeerId> {
93 match parse(address).ok()?.kind {
94 AddressKind::Circuit(relay) => Some(relay),
95 AddressKind::Direct => None,
96 }
97}
98
99fn direct_host(host: &Protocol<'_>) -> bool {
100 match host {
101 Protocol::Ip4(ip) => usable(IpAddr::V4(*ip)),
102 Protocol::Ip6(ip) => usable(IpAddr::V6(*ip)),
103 Protocol::Dns(_) | Protocol::Dns4(_) | Protocol::Dns6(_) => true,
104 _ => false,
105 }
106}
107
108fn transport_port(transport: &[Protocol<'_>]) -> bool {
110 match transport {
111 [Protocol::Tcp(port)] | [Protocol::Udp(port), Protocol::QuicV1] => *port != 0,
112 _ => false,
113 }
114}
115
116pub(crate) fn usable(ip: IpAddr) -> bool {
117 match ip {
118 IpAddr::V4(ip) => !ip.is_unspecified() && !ip.is_multicast() && !ip.is_broadcast(),
119 IpAddr::V6(ip) => !ip.is_unspecified() && !ip.is_multicast(),
120 }
121}
122
123#[derive(Clone, Debug)]
127pub struct PeerAddress {
128 peer: PeerId,
129 address: Multiaddr,
130}
131
132impl PeerAddress {
133 pub fn new(peer: PeerId, address: Multiaddr) -> Result<Self, Error> {
136 let refused = || {
137 Error::Invalid(
138 "peer address requires a usable IP, nonzero TCP or QUIC port and terminal matching peer ID"
139 .into(),
140 )
141 };
142 let shape = parse(&address).map_err(|_| refused())?;
143 let literal = matches!(
144 shape.bare.iter().next(),
145 Some(Protocol::Ip4(_) | Protocol::Ip6(_))
146 );
147 if shape.kind != AddressKind::Direct || !literal || shape.peer != Some(peer) {
148 return Err(refused());
149 }
150 Ok(Self { peer, address })
151 }
152
153 pub fn peer_id(&self) -> PeerId {
154 self.peer
155 }
156
157 pub fn address(&self) -> &Multiaddr {
158 &self.address
159 }
160}