Skip to main content

urma_node/
peer_address.rs

1//! Address shapes the overlay accepts. A direct address is `<host>/tcp/<port>`
2//! or `<host>/udp/<port>/quic-v1` (host an IP literal or DNS name), or
3//! `/dnsaddr/<name>`. A circuit address reaches its peer through a relay:
4//! `/ip4/<public relay IP>/<transport>/p2p/<relay>/p2p-circuit`. Either may
5//! end in `/p2p/<peer>`. Shape checks never resolve, dial or trust anything.
6use 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    /// Reached through this relay, which also coordinates hole punching.
14    Circuit(PeerId),
15}
16
17/// What a dial reached. A relayed-only peer is not reached yet: records/5
18/// (and with it the chain probe) never runs over a circuit, so the peer
19/// counts once DCUtR has given it a direct connection and the probe passed.
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub enum Dialed {
22    Direct(PeerId),
23    Relayed(PeerId),
24}
25
26/// A dialable address split into its bare form (no terminal `/p2p`), the
27/// identity it names and its kind.
28pub struct Shape {
29    pub bare: Multiaddr,
30    pub peer: Option<PeerId>,
31    pub kind: AddressKind,
32}
33
34/// Validate one address. IPs must be usable destinations; a circuit's relay
35/// hop is a public IPv4 literal and its relay is never the peer it names.
36pub 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
63/// Whether the address goes through a relay; shape is not checked.
64pub fn is_circuit(address: &Multiaddr) -> bool {
65    address.iter().any(|part| part == Protocol::P2pCircuit)
66}
67
68/// The relay of a valid circuit address.
69pub 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
85/// Exactly a nonzero TCP port, or a nonzero UDP port carrying QUIC v1.
86fn 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/// A syntactically validated sync source: a direct IP-literal address with a
101/// terminal `/p2p` naming the peer. Never a circuit: data stays off relays.
102/// Validation establishes neither reachability nor trust in the named peer.
103#[derive(Clone, Debug)]
104pub struct PeerAddress {
105    peer: PeerId,
106    address: Multiaddr,
107}
108
109impl PeerAddress {
110    /// Loopback and private addresses are allowed; unspecified, multicast and
111    /// IPv4 broadcast destinations are refused. No resolution or dial occurs.
112    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}