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 EndpointAddr {
58    /// Creates a new [`EndpointAddr`] with no network level addresses.
59    ///
60    /// This still is usable with e.g. a discovery service to establish a connection,
61    /// depending on the situation.
62    pub fn new(id: PublicKey) -> Self {
63        EndpointAddr {
64            id,
65            addrs: Default::default(),
66        }
67    }
68
69    /// Creates a new [`EndpointAddr`] from its parts.
70    pub fn from_parts(id: PublicKey, addrs: impl IntoIterator<Item = TransportAddr>) -> Self {
71        Self {
72            id,
73            addrs: addrs.into_iter().collect(),
74        }
75    }
76
77    /// Adds a [`RelayUrl`] address.
78    pub fn with_relay_url(mut self, relay_url: RelayUrl) -> Self {
79        self.addrs.insert(TransportAddr::Relay(relay_url));
80        self
81    }
82
83    /// Adds an IP based address.
84    pub fn with_ip_addr(mut self, addr: SocketAddr) -> Self {
85        self.addrs.insert(TransportAddr::Ip(addr));
86        self
87    }
88
89    /// Adds a list of addresses.
90    pub fn with_addrs(mut self, addrs: impl IntoIterator<Item = TransportAddr>) -> Self {
91        for addr in addrs.into_iter() {
92            self.addrs.insert(addr);
93        }
94        self
95    }
96
97    /// Returns true, if only a [`EndpointId`] is present.
98    pub fn is_empty(&self) -> bool {
99        self.addrs.is_empty()
100    }
101
102    /// Returns a list of IP addresses of this peer.
103    pub fn ip_addrs(&self) -> impl Iterator<Item = &SocketAddr> {
104        self.addrs.iter().filter_map(|addr| match addr {
105            TransportAddr::Ip(addr) => Some(addr),
106            _ => None,
107        })
108    }
109
110    /// Returns a list of relay urls of this peer.
111    ///
112    ///  In practice this is expected to be zero or one home relay for all known cases currently.
113    pub fn relay_urls(&self) -> impl Iterator<Item = &RelayUrl> {
114        self.addrs.iter().filter_map(|addr| match addr {
115            TransportAddr::Relay(url) => Some(url),
116            _ => None,
117        })
118    }
119}
120
121impl From<EndpointId> for EndpointAddr {
122    fn from(endpoint_id: EndpointId) -> Self {
123        EndpointAddr::new(endpoint_id)
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
132    #[non_exhaustive]
133    enum NewAddrType {
134        /// Relays
135        Relay(RelayUrl),
136        /// IP based addresses
137        Ip(SocketAddr),
138        /// New addr type for testing
139        Cool(u16),
140    }
141
142    #[test]
143    fn test_roundtrip_new_addr_type() {
144        let old = vec![
145            TransportAddr::Ip("127.0.0.1:9".parse().unwrap()),
146            TransportAddr::Relay("https://example.com".parse().unwrap()),
147        ];
148        let old_ser = postcard::to_stdvec(&old).unwrap();
149        let old_back: Vec<TransportAddr> = postcard::from_bytes(&old_ser).unwrap();
150        assert_eq!(old, old_back);
151
152        let new = vec![
153            NewAddrType::Ip("127.0.0.1:9".parse().unwrap()),
154            NewAddrType::Relay("https://example.com".parse().unwrap()),
155            NewAddrType::Cool(4),
156        ];
157        let new_ser = postcard::to_stdvec(&new).unwrap();
158        let new_back: Vec<NewAddrType> = postcard::from_bytes(&new_ser).unwrap();
159
160        assert_eq!(new, new_back);
161
162        // serialize old into new
163        let old_new_back: Vec<NewAddrType> = postcard::from_bytes(&old_ser).unwrap();
164
165        assert_eq!(
166            old_new_back,
167            vec![
168                NewAddrType::Ip("127.0.0.1:9".parse().unwrap()),
169                NewAddrType::Relay("https://example.com".parse().unwrap()),
170            ]
171        );
172    }
173}