Skip to main content

radicle/node/
address.rs

1pub mod store;
2pub use store::{Error, Store};
3
4use std::cell::RefCell;
5use std::ops::{Deref, DerefMut};
6use std::{hash, net};
7
8use cyphernet::addr::HostName;
9use localtime::LocalTime;
10use nonempty::NonEmpty;
11
12use crate::collections::RandomMap;
13use crate::node::{Address, Alias, Penalty, UserAgent};
14use crate::prelude::Timestamp;
15use crate::{node, profile};
16
17/// A map with the ability to randomly select values.
18#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
19#[serde(transparent)]
20pub struct AddressBook<K: hash::Hash + Eq, V> {
21    inner: RandomMap<K, V>,
22    #[serde(skip)]
23    rng: RefCell<fastrand::Rng>,
24}
25
26impl<K: hash::Hash + Eq, V> AddressBook<K, V> {
27    /// Create a new address book.
28    pub fn new(rng: fastrand::Rng) -> Self {
29        Self {
30            inner: RandomMap::with_hasher(rng.clone().into()),
31            rng: RefCell::new(rng),
32        }
33    }
34
35    /// Pick a random value in the book.
36    pub fn sample(&self) -> Option<(&K, &V)> {
37        self.sample_with(|_, _| true)
38    }
39
40    /// Pick a random value in the book matching a predicate.
41    pub fn sample_with(&self, mut predicate: impl FnMut(&K, &V) -> bool) -> Option<(&K, &V)> {
42        if let Some(pairs) = NonEmpty::from_vec(
43            self.inner
44                .iter()
45                .filter(|(k, v)| predicate(*k, *v))
46                .collect(),
47        ) {
48            let ix = self.rng.borrow_mut().usize(..pairs.len());
49            let pair = pairs[ix]; // Can't fail.
50
51            Some(pair)
52        } else {
53            None
54        }
55    }
56
57    /// Return a new address book with the given RNG.
58    pub fn with(self, rng: fastrand::Rng) -> Self {
59        Self {
60            inner: self.inner,
61            rng: RefCell::new(rng),
62        }
63    }
64}
65
66impl<K: hash::Hash + Eq + Ord + Copy, V> AddressBook<K, V> {
67    /// Return a shuffled iterator.
68    pub fn shuffled(&self) -> std::vec::IntoIter<(&K, &V)> {
69        let mut items = self.inner.iter().collect::<Vec<_>>();
70        items.sort_by_key(|(k, _)| *k);
71        self.rng.borrow_mut().shuffle(&mut items);
72
73        items.into_iter()
74    }
75
76    /// Turn this object into a shuffled iterator.
77    pub fn into_shuffled(self) -> impl Iterator<Item = (K, V)> {
78        let mut items = self.inner.into_iter().collect::<Vec<_>>();
79        items.sort_by_key(|(k, _)| *k);
80        self.rng.borrow_mut().shuffle(&mut items);
81
82        items.into_iter()
83    }
84
85    /// Cycle through the keys at random. The random cycle repeats ad-infintum.
86    pub fn cycle(&self) -> impl Iterator<Item = &K> {
87        self.shuffled().map(|(k, _)| k).cycle()
88    }
89}
90
91impl<K: hash::Hash + Eq, V> FromIterator<(K, V)> for AddressBook<K, V> {
92    fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
93        let rng = profile::env::rng();
94        let mut inner = RandomMap::with_hasher(rng.clone().into());
95
96        for (k, v) in iter {
97            inner.insert(k, v);
98        }
99        Self {
100            inner,
101            rng: RefCell::new(rng),
102        }
103    }
104}
105
106impl<K: hash::Hash + Eq, V> Deref for AddressBook<K, V> {
107    type Target = RandomMap<K, V>;
108
109    fn deref(&self) -> &Self::Target {
110        &self.inner
111    }
112}
113
114impl<K: hash::Hash + Eq, V> DerefMut for AddressBook<K, V> {
115    fn deref_mut(&mut self) -> &mut Self::Target {
116        &mut self.inner
117    }
118}
119
120/// Node public data.
121#[derive(Debug, Clone, PartialEq, Eq)]
122pub struct Node {
123    /// Protocol version.
124    pub version: u8,
125    /// Advertised alias.
126    pub alias: Alias,
127    /// Advertised features.
128    pub features: node::Features,
129    /// Advertised addresses
130    pub addrs: Vec<KnownAddress>,
131    /// Proof-of-work included in node announcement.
132    pub pow: u32,
133    /// When this data was published.
134    pub timestamp: Timestamp,
135    /// User agent string.
136    pub agent: UserAgent,
137    /// Node connection penalty.
138    pub penalty: Penalty,
139    /// Whether the node is banned.
140    pub banned: bool,
141}
142
143/// A known address.
144#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
145#[serde(rename_all = "camelCase")]
146#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
147pub struct KnownAddress {
148    /// Network address.
149    pub addr: Address,
150    /// Address of the peer who sent us this address.
151    pub source: Source,
152    /// Last time this address was used to successfully connect to a peer.
153    pub last_success: Option<LocalTime>,
154    /// Last time this address was tried.
155    pub last_attempt: Option<LocalTime>,
156    /// Whether this address has been banned.
157    pub banned: bool,
158}
159
160impl KnownAddress {
161    /// Create a new known address.
162    pub fn new(addr: Address, source: Source) -> Self {
163        Self {
164            addr,
165            source,
166            last_success: None,
167            last_attempt: None,
168            banned: false,
169        }
170    }
171}
172
173/// Address source. Specifies where an address originated from.
174#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
175#[serde(rename_all = "camelCase")]
176#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
177pub enum Source {
178    /// An address that was shared by another peer.
179    Peer,
180    /// A bootstrap node address.
181    Bootstrap,
182    /// An address that came from some source external to the system, eg.
183    /// specified by the user or added directly to the address manager.
184    Imported,
185}
186
187impl std::fmt::Display for Source {
188    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189        match self {
190            Self::Peer => write!(f, "Peer"),
191            Self::Bootstrap => write!(f, "Bootstrap"),
192            Self::Imported => write!(f, "Imported"),
193        }
194    }
195}
196
197/// Address type.
198#[repr(u8)]
199#[derive(Debug, Clone, Copy, PartialEq, Eq)]
200pub enum AddressType {
201    Ipv4 = 1,
202    Ipv6 = 2,
203    Dns = 3,
204    Onion = 4,
205}
206
207impl From<AddressType> for u8 {
208    fn from(other: AddressType) -> Self {
209        other as u8
210    }
211}
212
213impl From<&Address> for AddressType {
214    fn from(a: &Address) -> Self {
215        match a.host {
216            HostName::Ip(net::IpAddr::V4(_)) => AddressType::Ipv4,
217            HostName::Ip(net::IpAddr::V6(_)) => AddressType::Ipv6,
218            HostName::Dns(_) => AddressType::Dns,
219            HostName::Tor(_) => AddressType::Onion,
220            _ => todo!(), // FIXME(cloudhead): Maxim will remove `non-exhaustive`
221        }
222    }
223}
224
225impl TryFrom<u8> for AddressType {
226    type Error = u8;
227
228    fn try_from(other: u8) -> Result<Self, Self::Error> {
229        match other {
230            1 => Ok(AddressType::Ipv4),
231            2 => Ok(AddressType::Ipv6),
232            3 => Ok(AddressType::Dns),
233            4 => Ok(AddressType::Onion),
234            _ => Err(other),
235        }
236    }
237}
238/// Check whether an IP address is globally routable.
239pub fn is_routable(addr: &net::IpAddr) -> bool {
240    match addr {
241        net::IpAddr::V4(addr) => ipv4_is_routable(addr),
242        net::IpAddr::V6(addr) => ipv6_is_routable(addr),
243    }
244}
245
246/// Check whether an IP address is locally routable.
247pub fn is_local(addr: &net::IpAddr) -> bool {
248    match addr {
249        net::IpAddr::V4(addr) => {
250            addr.is_private() || addr.is_loopback() || addr.is_link_local() || addr.is_unspecified()
251        }
252        net::IpAddr::V6(addr) => {
253            addr.is_loopback() || addr.is_unicast_link_local() || addr.is_unspecified()
254        }
255    }
256}
257
258/// Check whether an IPv4 address is globally routable.
259///
260/// This implementation lacks many exceptions, and should be improved once
261/// corresponding functions in [`std::net::Ipv4Addr`] are stabilized.
262///
263/// See
264///  - <https://github.com/rust-lang/rust/issues/27709>
265///  - <https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml>
266fn ipv4_is_routable(addr: &net::Ipv4Addr) -> bool {
267    // https://datatracker.ietf.org/doc/html/rfc7723#section-4.1
268    if *addr == net::Ipv4Addr::new(192, 0, 0, 9) {
269        return true;
270    }
271
272    // https://datatracker.ietf.org/doc/html/rfc8155#section-8.1
273    if *addr == net::Ipv4Addr::new(192, 0, 0, 10) {
274        return true;
275    }
276
277    // https://datatracker.ietf.org/doc/html/rfc791#section-3.2
278    if addr.octets()[0] == 0 {
279        return false;
280    }
281
282    !addr.is_private()
283        && !addr.is_loopback()
284        && !addr.is_link_local()
285        && !addr.is_broadcast()
286        && !addr.is_documentation()
287}
288
289/// Check whether an IPv6 address is globally routable.
290///
291/// This implementation lacks many exceptions, and should be improved once
292/// corresponding functions in [`std::net::Ipv6Addr`] are stabilized.
293///
294/// See
295///  - <https://github.com/rust-lang/rust/issues/27709>
296///  - <https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml>
297fn ipv6_is_routable(addr: &net::Ipv6Addr) -> bool {
298    !addr.is_loopback() && !addr.is_unicast_link_local() && !addr.is_unspecified()
299}