saorsa_core/
address.rs

1// Copyright 2024 Saorsa Labs Limited
2//
3// This software is dual-licensed under:
4// - GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later)
5// - Commercial License
6//
7// For AGPL-3.0 license, see LICENSE-AGPL-3.0
8// For commercial licensing, contact: saorsalabs@gmail.com
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under these licenses is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
14//! # Address Types
15//!
16//! This module provides address types for the P2P network using IP:port combinations
17//! and four-word human-readable representations.
18
19use std::fmt::{self, Display};
20use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
21use std::str::FromStr;
22
23use anyhow::{Result, anyhow};
24use serde::{Deserialize, Serialize};
25
26use four_word_networking::FourWordAdaptiveEncoder;
27
28/// Network address that can be represented as IP:port or four-word format
29#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
30pub struct NetworkAddress {
31    /// The socket address (IP + port)
32    pub socket_addr: SocketAddr,
33    /// Optional four-word representation
34    pub four_words: Option<String>,
35}
36
37impl NetworkAddress {
38    /// Create a new `NetworkAddress` from a `SocketAddr`
39    #[must_use]
40    pub fn new(socket_addr: SocketAddr) -> Self {
41        let four_words = Self::encode_four_words(&socket_addr);
42        Self {
43            socket_addr,
44            four_words,
45        }
46    }
47
48    /// Create a `NetworkAddress` from an IP address and port
49    #[must_use]
50    pub fn from_ip_port(ip: IpAddr, port: u16) -> Self {
51        let socket_addr = SocketAddr::new(ip, port);
52        Self::new(socket_addr)
53    }
54
55    /// Create a `NetworkAddress` from IPv4 address and port
56    #[must_use]
57    pub fn from_ipv4(ip: Ipv4Addr, port: u16) -> Self {
58        Self::from_ip_port(IpAddr::V4(ip), port)
59    }
60
61    /// Create a `NetworkAddress` from IPv6 address and port
62    #[must_use]
63    pub fn from_ipv6(ip: Ipv6Addr, port: u16) -> Self {
64        Self::from_ip_port(IpAddr::V6(ip), port)
65    }
66
67    /// Get the IP address
68    #[must_use]
69    pub fn ip(&self) -> IpAddr {
70        self.socket_addr.ip()
71    }
72
73    /// Get the port
74    pub fn port(&self) -> u16 {
75        self.socket_addr.port()
76    }
77
78    /// Get the socket address
79    pub fn socket_addr(&self) -> SocketAddr {
80        self.socket_addr
81    }
82
83    /// Get the four-word representation if available
84    pub fn four_words(&self) -> Option<&str> {
85        self.four_words.as_deref()
86    }
87
88    /// Force regeneration of four-word representation
89    pub fn regenerate_four_words(&mut self) {
90        self.four_words = Self::encode_four_words(&self.socket_addr);
91    }
92
93    /// Encode a SocketAddr to four-word format using four-word-networking
94    fn encode_four_words(addr: &SocketAddr) -> Option<String> {
95        match FourWordAdaptiveEncoder::new()
96            .and_then(|enc| enc.encode(&addr.to_string()))
97        {
98            Ok(s) => Some(s.replace(' ', "-")),
99            Err(e) => {
100                log::warn!("Failed to encode address {addr}: {e}");
101                None
102            }
103        }
104    }
105
106    /// Decode four-word format to NetworkAddress using four-word-networking
107    pub fn from_four_words(words: &str) -> Result<Self> {
108        let enc = FourWordAdaptiveEncoder::new()?;
109        let decoded = enc.decode(words)?; // returns a normalized address string
110        let socket_addr: SocketAddr = decoded.parse()?; // must include port
111        Ok(Self::new(socket_addr))
112    }
113
114    /// Check if this is an IPv4 address
115    pub fn is_ipv4(&self) -> bool {
116        self.socket_addr.is_ipv4()
117    }
118
119    /// Check if this is an IPv6 address
120    pub fn is_ipv6(&self) -> bool {
121        self.socket_addr.is_ipv6()
122    }
123
124    /// Check if this is a loopback address
125    pub fn is_loopback(&self) -> bool {
126        self.socket_addr.ip().is_loopback()
127    }
128
129    /// Check if this is a private/local address
130    pub fn is_private(&self) -> bool {
131        match self.socket_addr.ip() {
132            IpAddr::V4(ip) => ip.is_private(),
133            IpAddr::V6(ip) => {
134                // Check for unique local addresses (fc00::/7)
135                let octets = ip.octets();
136                (octets[0] & 0xfe) == 0xfc
137            }
138        }
139    }
140}
141
142impl Display for NetworkAddress {
143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144        if let Some(ref words) = self.four_words {
145            write!(f, "{} ({})", self.socket_addr, words)
146        } else {
147            write!(f, "{}", self.socket_addr)
148        }
149    }
150}
151
152impl FromStr for NetworkAddress {
153    type Err = anyhow::Error;
154
155    fn from_str(s: &str) -> Result<Self> {
156        // First try to parse as a socket address
157        if let Ok(socket_addr) = SocketAddr::from_str(s) {
158            return Ok(Self::new(socket_addr));
159        }
160
161        // Then try to parse as four-word format
162        if let Ok(addr) = Self::from_four_words(s) {
163            return Ok(addr);
164        }
165
166        Err(anyhow!("Invalid address format: {}", s))
167    }
168}
169
170impl From<SocketAddr> for NetworkAddress {
171    fn from(socket_addr: SocketAddr) -> Self {
172        Self::new(socket_addr)
173    }
174}
175
176impl From<&SocketAddr> for NetworkAddress {
177    fn from(socket_addr: &SocketAddr) -> Self {
178        Self::new(*socket_addr)
179    }
180}
181
182impl From<NetworkAddress> for SocketAddr {
183    fn from(addr: NetworkAddress) -> Self {
184        addr.socket_addr
185    }
186}
187
188impl From<&NetworkAddress> for SocketAddr {
189    fn from(addr: &NetworkAddress) -> Self {
190        addr.socket_addr
191    }
192}
193
194/// Collection of network addresses for a peer
195#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
196pub struct AddressBook {
197    /// Primary addresses for this peer
198    pub addresses: Vec<NetworkAddress>,
199    /// Last known good address
200    pub last_known_good: Option<NetworkAddress>,
201}
202
203impl AddressBook {
204    /// Create a new empty address book
205    pub fn new() -> Self {
206        Self {
207            addresses: Vec::new(),
208            last_known_good: None,
209        }
210    }
211
212    /// Create an address book with a single address
213    pub fn with_address(address: NetworkAddress) -> Self {
214        Self {
215            addresses: vec![address.clone()],
216            last_known_good: Some(address),
217        }
218    }
219
220    /// Add an address to the book
221    pub fn add_address(&mut self, address: NetworkAddress) {
222        if !self.addresses.contains(&address) {
223            self.addresses.push(address);
224        }
225    }
226
227    /// Remove an address from the book
228    pub fn remove_address(&mut self, address: &NetworkAddress) {
229        self.addresses.retain(|a| a != address);
230        if self.last_known_good.as_ref() == Some(address) {
231            self.last_known_good = self.addresses.first().cloned();
232        }
233    }
234
235    /// Update the last known good address
236    pub fn update_last_known_good(&mut self, address: NetworkAddress) {
237        if self.addresses.contains(&address) {
238            self.last_known_good = Some(address);
239        }
240    }
241
242    /// Get the best address to try first
243    pub fn best_address(&self) -> Option<&NetworkAddress> {
244        self.last_known_good
245            .as_ref()
246            .or_else(|| self.addresses.first())
247    }
248
249    /// Get all addresses
250    pub fn addresses(&self) -> &[NetworkAddress] {
251        &self.addresses
252    }
253
254    /// Check if the address book is empty
255    pub fn is_empty(&self) -> bool {
256        self.addresses.is_empty()
257    }
258
259    /// Get the number of addresses
260    pub fn len(&self) -> usize {
261        self.addresses.len()
262    }
263}
264
265impl Default for AddressBook {
266    fn default() -> Self {
267        Self::new()
268    }
269}
270
271impl Display for AddressBook {
272    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
273        if self.addresses.is_empty() {
274            write!(f, "Empty address book")
275        } else {
276            write!(
277                f,
278                "Addresses: [{}]",
279                self.addresses
280                    .iter()
281                    .map(|a| a.to_string())
282                    .collect::<Vec<_>>()
283                    .join(", ")
284            )
285        }
286    }
287}
288
289#[cfg(test)]
290mod tests {
291    use super::*;
292    use std::net::{Ipv4Addr, Ipv6Addr};
293
294    #[test]
295    fn test_network_address_creation() {
296        let addr = NetworkAddress::from_ipv4(Ipv4Addr::new(127, 0, 0, 1), 8080);
297        assert_eq!(addr.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
298        assert_eq!(addr.port(), 8080);
299        assert!(addr.is_ipv4());
300        assert!(addr.is_loopback());
301    }
302
303    #[test]
304    fn test_network_address_from_string() {
305        let addr = "127.0.0.1:8080".parse::<NetworkAddress>().unwrap();
306        assert_eq!(addr.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
307        assert_eq!(addr.port(), 8080);
308    }
309
310    #[test]
311    fn test_network_address_display() {
312        let addr = NetworkAddress::from_ipv4(Ipv4Addr::new(192, 168, 1, 1), 9000);
313        let display = addr.to_string();
314        assert!(display.contains("192.168.1.1:9000"));
315    }
316
317    #[test]
318    fn test_address_book() {
319        let mut book = AddressBook::new();
320        let addr1 = NetworkAddress::from_ipv4(Ipv4Addr::new(192, 168, 1, 1), 9000);
321        let addr2 = NetworkAddress::from_ipv4(Ipv4Addr::new(192, 168, 1, 2), 9001);
322
323        book.add_address(addr1.clone());
324        book.add_address(addr2.clone());
325
326        assert_eq!(book.len(), 2);
327        assert_eq!(book.best_address(), Some(&addr1));
328
329        book.update_last_known_good(addr2.clone());
330        assert_eq!(book.best_address(), Some(&addr2));
331    }
332
333    #[test]
334    fn test_private_address_detection() {
335        let private_addr = NetworkAddress::from_ipv4(Ipv4Addr::new(192, 168, 1, 1), 9000);
336        assert!(private_addr.is_private());
337
338        let public_addr = NetworkAddress::from_ipv4(Ipv4Addr::new(8, 8, 8, 8), 53);
339        assert!(!public_addr.is_private());
340    }
341
342    #[test]
343    fn test_ipv6_address() {
344        let addr = NetworkAddress::from_ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080);
345        assert!(addr.is_ipv6());
346        assert!(addr.is_loopback());
347    }
348}