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 relay(address: &Multiaddr) -> Option<PeerId> {
70 match parse(address).ok()?.kind {
71 AddressKind::Circuit(relay) => Some(relay),
72 AddressKind::Direct => None,
73 }
74}
75
76fn direct_host(host: &Protocol<'_>) -> bool {
77 match host {
78 Protocol::Ip4(ip) => usable(IpAddr::V4(*ip)),
79 Protocol::Ip6(ip) => usable(IpAddr::V6(*ip)),
80 Protocol::Dns(_) | Protocol::Dns4(_) | Protocol::Dns6(_) => true,
81 _ => false,
82 }
83}
84
85fn transport_port(transport: &[Protocol<'_>]) -> bool {
87 match transport {
88 [Protocol::Tcp(port)] | [Protocol::Udp(port), Protocol::QuicV1] => *port != 0,
89 _ => false,
90 }
91}
92
93pub(crate) fn usable(ip: IpAddr) -> bool {
94 match ip {
95 IpAddr::V4(ip) => !ip.is_unspecified() && !ip.is_multicast() && !ip.is_broadcast(),
96 IpAddr::V6(ip) => !ip.is_unspecified() && !ip.is_multicast(),
97 }
98}
99
100#[derive(Clone, Debug)]
104pub struct PeerAddress {
105 peer: PeerId,
106 address: Multiaddr,
107}
108
109impl PeerAddress {
110 pub fn new(peer: PeerId, address: Multiaddr) -> Result<Self, Error> {
113 let refused = || {
114 Error::Invalid(
115 "peer address requires a usable IP, nonzero TCP or QUIC port and terminal matching peer ID"
116 .into(),
117 )
118 };
119 let shape = parse(&address).map_err(|_| refused())?;
120 let literal = matches!(
121 shape.bare.iter().next(),
122 Some(Protocol::Ip4(_) | Protocol::Ip6(_))
123 );
124 if shape.kind != AddressKind::Direct || !literal || shape.peer != Some(peer) {
125 return Err(refused());
126 }
127 Ok(Self { peer, address })
128 }
129
130 pub fn peer_id(&self) -> PeerId {
131 self.peer
132 }
133
134 pub fn address(&self) -> &Multiaddr {
135 &self.address
136 }
137}