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/// Whether the address dials over QUIC; shape is not checked.
69pub fn is_quic(address: &Multiaddr) -> bool {
70    address.iter().any(|part| part == Protocol::QuicV1)
71}
72
73/// QUIC addresses first, each group in its given order: dialing prefers QUIC
74/// when the peer offers it (ยง3.1). Through a TCP proxy (Fly) only a QUIC
75/// connection shows the peer our real mapping.
76pub(crate) fn quic_first(mut addrs: Vec<Multiaddr>) -> Vec<Multiaddr> {
77    addrs.sort_by_key(|address| !is_quic(address));
78    addrs
79}
80
81/// Add `address` to a peer's list: QUIC ahead of the rest, anything else at
82/// the back. Existing order (a failed address rotated back) is kept.
83pub(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
91/// The relay of a valid circuit address.
92pub 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
108/// Exactly a nonzero TCP port, or a nonzero UDP port carrying QUIC v1.
109fn 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/// A syntactically validated sync source: a direct IP-literal address with a
124/// terminal `/p2p` naming the peer. Never a circuit: data stays off relays.
125/// Validation establishes neither reachability nor trust in the named peer.
126#[derive(Clone, Debug)]
127pub struct PeerAddress {
128    peer: PeerId,
129    address: Multiaddr,
130}
131
132impl PeerAddress {
133    /// Loopback and private addresses are allowed; unspecified, multicast and
134    /// IPv4 broadcast destinations are refused. No resolution or dial occurs.
135    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}