iroh_net/relay/map.rs
1//! based on tailscale/tailcfg/derpmap.go
2
3use std::{collections::BTreeMap, fmt, sync::Arc};
4
5use anyhow::{ensure, Result};
6use serde::{Deserialize, Serialize};
7
8use super::RelayUrl;
9use crate::defaults::DEFAULT_STUN_PORT;
10
11/// Configuration of the relay servers for an [`Endpoint`].
12///
13/// [`Endpoint`]: crate::endpoint::Endpoint
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum RelayMode {
16 /// Disable relay servers completely.
17 Disabled,
18 /// Use the default relay map, with production relay servers from n0.
19 ///
20 /// See [`crate::defaults::prod`] for the severs used.
21 Default,
22 /// Use the staging relay servers from n0.
23 Staging,
24 /// Use a custom relay map.
25 Custom(RelayMap),
26}
27
28impl RelayMode {
29 /// Returns the relay map for this mode.
30 pub fn relay_map(&self) -> RelayMap {
31 match self {
32 RelayMode::Disabled => RelayMap::empty(),
33 RelayMode::Default => crate::defaults::prod::default_relay_map(),
34 RelayMode::Staging => crate::defaults::staging::default_relay_map(),
35 RelayMode::Custom(relay_map) => relay_map.clone(),
36 }
37 }
38}
39
40/// Configuration of all the relay servers that can be used.
41#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct RelayMap {
43 /// A map of the different relay IDs to the [`RelayNode`] information
44 nodes: Arc<BTreeMap<RelayUrl, Arc<RelayNode>>>,
45}
46
47impl RelayMap {
48 /// Returns the sorted relay URLs.
49 pub fn urls(&self) -> impl Iterator<Item = &RelayUrl> {
50 self.nodes.keys()
51 }
52
53 /// Create an empty relay map.
54 pub fn empty() -> Self {
55 Self {
56 nodes: Default::default(),
57 }
58 }
59
60 /// Returns an `Iterator` over all known nodes.
61 pub fn nodes(&self) -> impl Iterator<Item = &Arc<RelayNode>> {
62 self.nodes.values()
63 }
64
65 /// Is this a known node?
66 pub fn contains_node(&self, url: &RelayUrl) -> bool {
67 self.nodes.contains_key(url)
68 }
69
70 /// Get the given node.
71 pub fn get_node(&self, url: &RelayUrl) -> Option<&Arc<RelayNode>> {
72 self.nodes.get(url)
73 }
74
75 /// How many nodes are known?
76 pub fn len(&self) -> usize {
77 self.nodes.len()
78 }
79
80 /// Are there any nodes in this map?
81 pub fn is_empty(&self) -> bool {
82 self.nodes.is_empty()
83 }
84
85 /// Creates a new [`RelayMap`] with a single relay server configured.
86 ///
87 /// Allows to set a custom STUN port and different IP addresses for IPv4 and IPv6.
88 /// If IP addresses are provided, no DNS lookup will be performed.
89 pub fn default_from_node(url: RelayUrl, stun_port: u16) -> Self {
90 let mut nodes = BTreeMap::new();
91 nodes.insert(
92 url.clone(),
93 RelayNode {
94 url,
95 stun_only: false,
96 stun_port,
97 }
98 .into(),
99 );
100
101 RelayMap {
102 nodes: Arc::new(nodes),
103 }
104 }
105
106 /// Returns a [`RelayMap`] from a [`RelayUrl`].
107 ///
108 /// This will use the default STUN port and IP addresses resolved from the URL's host name via DNS.
109 /// relay nodes are specified at <../../docs/relay_nodes.md>
110 pub fn from_url(url: RelayUrl) -> Self {
111 Self::default_from_node(url, DEFAULT_STUN_PORT)
112 }
113
114 /// Constructs the [`RelayMap`] from an iterator of [`RelayNode`]s.
115 pub fn from_nodes(value: impl IntoIterator<Item = RelayNode>) -> Result<Self> {
116 let mut map = BTreeMap::new();
117 for node in value.into_iter() {
118 ensure!(!map.contains_key(&node.url), "Duplicate node url");
119 map.insert(node.url.clone(), node.into());
120 }
121 Ok(RelayMap { nodes: map.into() })
122 }
123}
124
125impl fmt::Display for RelayMap {
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 fmt::Debug::fmt(&self, f)
128 }
129}
130
131/// Information on a specific relay server.
132///
133/// Includes the Url where it can be dialed.
134// Please note that this is documented in the `iroh.computer` repository under
135// `src/app/docs/reference/config/page.mdx`. Any changes to this need to be updated there.
136#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
137pub struct RelayNode {
138 /// The [`RelayUrl`] where this relay server can be dialed.
139 pub url: RelayUrl,
140 /// Whether this relay server should only be used for STUN requests.
141 ///
142 /// This essentially allows you to use a normal STUN server as a relay node, no relay
143 /// functionality is used.
144 pub stun_only: bool,
145 /// The stun port of the relay server.
146 ///
147 /// Setting this to `0` means the default STUN port is used.
148 pub stun_port: u16,
149}
150
151impl fmt::Display for RelayNode {
152 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
153 write!(f, "{}", self.url)
154 }
155}