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::FourWordEncoder;
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    pub fn new(socket_addr: SocketAddr) -> Self {
40        let four_words = Self::encode_four_words(&socket_addr);
41        Self {
42            socket_addr,
43            four_words,
44        }
45    }
46
47    /// Create a NetworkAddress from an IP address and port
48    pub fn from_ip_port(ip: IpAddr, port: u16) -> Self {
49        let socket_addr = SocketAddr::new(ip, port);
50        Self::new(socket_addr)
51    }
52
53    /// Create a NetworkAddress from IPv4 address and port
54    pub fn from_ipv4(ip: Ipv4Addr, port: u16) -> Self {
55        Self::from_ip_port(IpAddr::V4(ip), port)
56    }
57
58    /// Create a NetworkAddress from IPv6 address and port
59    pub fn from_ipv6(ip: Ipv6Addr, port: u16) -> Self {
60        Self::from_ip_port(IpAddr::V6(ip), port)
61    }
62
63    /// Get the IP address
64    pub fn ip(&self) -> IpAddr {
65        self.socket_addr.ip()
66    }
67
68    /// Get the port
69    pub fn port(&self) -> u16 {
70        self.socket_addr.port()
71    }
72
73    /// Get the socket address
74    pub fn socket_addr(&self) -> SocketAddr {
75        self.socket_addr
76    }
77
78    /// Get the four-word representation if available
79    pub fn four_words(&self) -> Option<&str> {
80        self.four_words.as_deref()
81    }
82
83    /// Force regeneration of four-word representation
84    pub fn regenerate_four_words(&mut self) {
85        self.four_words = Self::encode_four_words(&self.socket_addr);
86    }
87
88    /// Encode a SocketAddr to four-word format using four-word-networking
89    fn encode_four_words(addr: &SocketAddr) -> Option<String> {
90        let encoder = FourWordEncoder::new();
91        match encoder.encode(*addr) {
92            Ok(encoding) => Some(encoding.to_string()),
93            Err(e) => {
94                log::warn!("Failed to encode address {addr}: {e}");
95                None
96            }
97        }
98    }
99
100    /// Decode four-word format to NetworkAddress using four-word-networking
101    pub fn from_four_words(words: &str) -> Result<Self> {
102        let encoder = FourWordEncoder::new();
103        let socket_addr = encoder.decode(words)?;
104        Ok(Self::new(socket_addr))
105    }
106
107    /// Check if this is an IPv4 address
108    pub fn is_ipv4(&self) -> bool {
109        self.socket_addr.is_ipv4()
110    }
111
112    /// Check if this is an IPv6 address
113    pub fn is_ipv6(&self) -> bool {
114        self.socket_addr.is_ipv6()
115    }
116
117    /// Check if this is a loopback address
118    pub fn is_loopback(&self) -> bool {
119        self.socket_addr.ip().is_loopback()
120    }
121
122    /// Check if this is a private/local address
123    pub fn is_private(&self) -> bool {
124        match self.socket_addr.ip() {
125            IpAddr::V4(ip) => ip.is_private(),
126            IpAddr::V6(ip) => {
127                // Check for unique local addresses (fc00::/7)
128                let octets = ip.octets();
129                (octets[0] & 0xfe) == 0xfc
130            }
131        }
132    }
133}
134
135impl Display for NetworkAddress {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        if let Some(ref words) = self.four_words {
138            write!(f, "{} ({})", self.socket_addr, words)
139        } else {
140            write!(f, "{}", self.socket_addr)
141        }
142    }
143}
144
145impl FromStr for NetworkAddress {
146    type Err = anyhow::Error;
147
148    fn from_str(s: &str) -> Result<Self> {
149        // First try to parse as a socket address
150        if let Ok(socket_addr) = SocketAddr::from_str(s) {
151            return Ok(Self::new(socket_addr));
152        }
153
154        // Then try to parse as four-word format
155        if let Ok(addr) = Self::from_four_words(s) {
156            return Ok(addr);
157        }
158
159        Err(anyhow!("Invalid address format: {}", s))
160    }
161}
162
163impl From<SocketAddr> for NetworkAddress {
164    fn from(socket_addr: SocketAddr) -> Self {
165        Self::new(socket_addr)
166    }
167}
168
169impl From<&SocketAddr> for NetworkAddress {
170    fn from(socket_addr: &SocketAddr) -> Self {
171        Self::new(*socket_addr)
172    }
173}
174
175impl From<NetworkAddress> for SocketAddr {
176    fn from(addr: NetworkAddress) -> Self {
177        addr.socket_addr
178    }
179}
180
181impl From<&NetworkAddress> for SocketAddr {
182    fn from(addr: &NetworkAddress) -> Self {
183        addr.socket_addr
184    }
185}
186
187/// Collection of network addresses for a peer
188#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
189pub struct AddressBook {
190    /// Primary addresses for this peer
191    pub addresses: Vec<NetworkAddress>,
192    /// Last known good address
193    pub last_known_good: Option<NetworkAddress>,
194}
195
196impl AddressBook {
197    /// Create a new empty address book
198    pub fn new() -> Self {
199        Self {
200            addresses: Vec::new(),
201            last_known_good: None,
202        }
203    }
204
205    /// Create an address book with a single address
206    pub fn with_address(address: NetworkAddress) -> Self {
207        Self {
208            addresses: vec![address.clone()],
209            last_known_good: Some(address),
210        }
211    }
212
213    /// Add an address to the book
214    pub fn add_address(&mut self, address: NetworkAddress) {
215        if !self.addresses.contains(&address) {
216            self.addresses.push(address);
217        }
218    }
219
220    /// Remove an address from the book
221    pub fn remove_address(&mut self, address: &NetworkAddress) {
222        self.addresses.retain(|a| a != address);
223        if self.last_known_good.as_ref() == Some(address) {
224            self.last_known_good = self.addresses.first().cloned();
225        }
226    }
227
228    /// Update the last known good address
229    pub fn update_last_known_good(&mut self, address: NetworkAddress) {
230        if self.addresses.contains(&address) {
231            self.last_known_good = Some(address);
232        }
233    }
234
235    /// Get the best address to try first
236    pub fn best_address(&self) -> Option<&NetworkAddress> {
237        self.last_known_good
238            .as_ref()
239            .or_else(|| self.addresses.first())
240    }
241
242    /// Get all addresses
243    pub fn addresses(&self) -> &[NetworkAddress] {
244        &self.addresses
245    }
246
247    /// Check if the address book is empty
248    pub fn is_empty(&self) -> bool {
249        self.addresses.is_empty()
250    }
251
252    /// Get the number of addresses
253    pub fn len(&self) -> usize {
254        self.addresses.len()
255    }
256}
257
258impl Default for AddressBook {
259    fn default() -> Self {
260        Self::new()
261    }
262}
263
264impl Display for AddressBook {
265    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266        if self.addresses.is_empty() {
267            write!(f, "Empty address book")
268        } else {
269            write!(
270                f,
271                "Addresses: [{}]",
272                self.addresses
273                    .iter()
274                    .map(|a| a.to_string())
275                    .collect::<Vec<_>>()
276                    .join(", ")
277            )
278        }
279    }
280}
281
282#[cfg(test)]
283mod tests {
284    use super::*;
285    use std::net::{Ipv4Addr, Ipv6Addr};
286
287    #[test]
288    fn test_network_address_creation() {
289        let addr = NetworkAddress::from_ipv4(Ipv4Addr::new(127, 0, 0, 1), 8080);
290        assert_eq!(addr.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
291        assert_eq!(addr.port(), 8080);
292        assert!(addr.is_ipv4());
293        assert!(addr.is_loopback());
294    }
295
296    #[test]
297    fn test_network_address_from_string() {
298        let addr = "127.0.0.1:8080".parse::<NetworkAddress>().unwrap();
299        assert_eq!(addr.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
300        assert_eq!(addr.port(), 8080);
301    }
302
303    #[test]
304    fn test_network_address_display() {
305        let addr = NetworkAddress::from_ipv4(Ipv4Addr::new(192, 168, 1, 1), 9000);
306        let display = addr.to_string();
307        assert!(display.contains("192.168.1.1:9000"));
308    }
309
310    #[test]
311    fn test_address_book() {
312        let mut book = AddressBook::new();
313        let addr1 = NetworkAddress::from_ipv4(Ipv4Addr::new(192, 168, 1, 1), 9000);
314        let addr2 = NetworkAddress::from_ipv4(Ipv4Addr::new(192, 168, 1, 2), 9001);
315
316        book.add_address(addr1.clone());
317        book.add_address(addr2.clone());
318
319        assert_eq!(book.len(), 2);
320        assert_eq!(book.best_address(), Some(&addr1));
321
322        book.update_last_known_good(addr2.clone());
323        assert_eq!(book.best_address(), Some(&addr2));
324    }
325
326    #[test]
327    fn test_private_address_detection() {
328        let private_addr = NetworkAddress::from_ipv4(Ipv4Addr::new(192, 168, 1, 1), 9000);
329        assert!(private_addr.is_private());
330
331        let public_addr = NetworkAddress::from_ipv4(Ipv4Addr::new(8, 8, 8, 8), 53);
332        assert!(!public_addr.is_private());
333    }
334
335    #[test]
336    fn test_ipv6_address() {
337        let addr = NetworkAddress::from_ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080);
338        assert!(addr.is_ipv6());
339        assert!(addr.is_loopback());
340    }
341}