iroh_base/
endpoint_addr.rs

1//! Addressing for iroh endpoints.
2//!
3//! This module contains some common addressing types for iroh.  An endpoint is uniquely
4//! identified by the [`EndpointId`] but that does not make it addressable on the network layer.
5//! For this the addition of a [`RelayUrl`] and/or direct addresses are required.
6//!
7//! The primary way of addressing an endpoint is by using the [`EndpointAddr`].
8
9use std::{collections::BTreeSet, net::SocketAddr};
10
11use serde::{Deserialize, Serialize};
12
13use crate::{EndpointId, PublicKey, RelayUrl};
14
15/// Network-level addressing information for an iroh endpoint.
16///
17/// This combines an endpoint's identifier with network-level addressing information of how to
18/// contact the endpoint.
19///
20/// To establish a network connection to an endpoint both the [`EndpointId`] and one or more network
21/// paths are needed.  The network paths can come from various sources, current sources can come from
22///
23/// - A [discovery] service which can provide routing information for a given [`EndpointId`].
24///
25/// - A [`RelayUrl`] of the endpoint's [home relay], this allows establishing the connection via
26///   the Relay server and is very reliable.
27///
28/// - One or more *IP based addresses* on which the endpoint might be reachable.  Depending on the
29///   network location of both endpoints it might not be possible to establish a direct
30///   connection without the help of a [Relay server].
31///
32/// This structure will always contain the required [`EndpointId`] and will contain an optional
33/// number of other addressing information.  It is a generic addressing type used whenever a connection
34/// to other endpoints needs to be established.
35///
36/// [discovery]: https://docs.rs/iroh/*/iroh/index.html#endpoint-discovery
37/// [home relay]: https://docs.rs/iroh/*/iroh/relay/index.html
38/// [Relay server]: https://docs.rs/iroh/*/iroh/index.html#relay-servers
39#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
40pub struct EndpointAddr {
41    /// The endpoint's identifier.
42    pub id: EndpointId,
43    /// The endpoint's addresses
44    pub addrs: BTreeSet<TransportAddr>,
45}
46
47/// Available address types.
48#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
49#[non_exhaustive]
50pub enum TransportAddr {
51    /// Relays
52    Relay(RelayUrl),
53    /// IP based addresses
54    Ip(SocketAddr),
55}
56
57impl TransportAddr {
58    /// Whether this is a transport address via a relay server.
59    pub fn is_relay(&self) -> bool {
60        matches!(self, Self::Relay(_))
61    }
62
63    /// Whether this is an IP transport address.
64    pub fn is_ip(&self) -> bool {
65        matches!(self, Self::Ip(_))
66    }
67}
68
69impl EndpointAddr {
70    /// Creates a new [`EndpointAddr`] with no network level addresses.
71    ///
72    /// This still is usable with e.g. a discovery service to establish a connection,
73    /// depending on the situation.
74    pub fn new(id: PublicKey) -> Self {
75        EndpointAddr {
76            id,
77            addrs: Default::default(),
78        }
79    }
80
81    /// Creates a new [`EndpointAddr`] from its parts.
82    pub fn from_parts(id: PublicKey, addrs: impl IntoIterator<Item = TransportAddr>) -> Self {
83        Self {
84            id,
85            addrs: addrs.into_iter().collect(),
86        }
87    }
88
89    /// Adds a [`RelayUrl`] address.
90    pub fn with_relay_url(mut self, relay_url: RelayUrl) -> Self {
91        self.addrs.insert(TransportAddr::Relay(relay_url));
92        self
93    }
94
95    /// Adds an IP based address.
96    pub fn with_ip_addr(mut self, addr: SocketAddr) -> Self {
97        self.addrs.insert(TransportAddr::Ip(addr));
98        self
99    }
100
101    /// Adds a list of addresses.
102    pub fn with_addrs(mut self, addrs: impl IntoIterator<Item = TransportAddr>) -> Self {
103        for addr in addrs.into_iter() {
104            self.addrs.insert(addr);
105        }
106        self
107    }
108
109    /// Returns true, if only a [`EndpointId`] is present.
110    pub fn is_empty(&self) -> bool {
111        self.addrs.is_empty()
112    }
113
114    /// Returns a list of IP addresses of this peer.
115    pub fn ip_addrs(&self) -> impl Iterator<Item = &SocketAddr> {
116        self.addrs.iter().filter_map(|addr| match addr {
117            TransportAddr::Ip(addr) => Some(addr),
118            _ => None,
119        })
120    }
121
122    /// Returns a list of relay urls of this peer.
123    ///
124    ///  In practice this is expected to be zero or one home relay for all known cases currently.
125    pub fn relay_urls(&self) -> impl Iterator<Item = &RelayUrl> {
126        self.addrs.iter().filter_map(|addr| match addr {
127            TransportAddr::Relay(url) => Some(url),
128            _ => None,
129        })
130    }
131}
132
133impl From<EndpointId> for EndpointAddr {
134    fn from(endpoint_id: EndpointId) -> Self {
135        EndpointAddr::new(endpoint_id)
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
144    #[non_exhaustive]
145    enum NewAddrType {
146        /// Relays
147        Relay(RelayUrl),
148        /// IP based addresses
149        Ip(SocketAddr),
150        /// New addr type for testing
151        Cool(u16),
152    }
153
154    #[test]
155    fn test_roundtrip_new_addr_type() {
156        let old = vec![
157            TransportAddr::Ip("127.0.0.1:9".parse().unwrap()),
158            TransportAddr::Relay("https://example.com".parse().unwrap()),
159        ];
160        let old_ser = postcard::to_stdvec(&old).unwrap();
161        let old_back: Vec<TransportAddr> = postcard::from_bytes(&old_ser).unwrap();
162        assert_eq!(old, old_back);
163
164        let new = vec![
165            NewAddrType::Ip("127.0.0.1:9".parse().unwrap()),
166            NewAddrType::Relay("https://example.com".parse().unwrap()),
167            NewAddrType::Cool(4),
168        ];
169        let new_ser = postcard::to_stdvec(&new).unwrap();
170        let new_back: Vec<NewAddrType> = postcard::from_bytes(&new_ser).unwrap();
171
172        assert_eq!(new, new_back);
173
174        // serialize old into new
175        let old_new_back: Vec<NewAddrType> = postcard::from_bytes(&old_ser).unwrap();
176
177        assert_eq!(
178            old_new_back,
179            vec![
180                NewAddrType::Ip("127.0.0.1:9".parse().unwrap()),
181                NewAddrType::Relay("https://example.com".parse().unwrap()),
182            ]
183        );
184    }
185}