1use 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#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
30pub struct NetworkAddress {
31 pub socket_addr: SocketAddr,
33 pub four_words: Option<String>,
35}
36
37impl NetworkAddress {
38 #[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 #[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 #[must_use]
57 pub fn from_ipv4(ip: Ipv4Addr, port: u16) -> Self {
58 Self::from_ip_port(IpAddr::V4(ip), port)
59 }
60
61 #[must_use]
63 pub fn from_ipv6(ip: Ipv6Addr, port: u16) -> Self {
64 Self::from_ip_port(IpAddr::V6(ip), port)
65 }
66
67 #[must_use]
69 pub fn ip(&self) -> IpAddr {
70 self.socket_addr.ip()
71 }
72
73 pub fn port(&self) -> u16 {
75 self.socket_addr.port()
76 }
77
78 pub fn socket_addr(&self) -> SocketAddr {
80 self.socket_addr
81 }
82
83 pub fn four_words(&self) -> Option<&str> {
85 self.four_words.as_deref()
86 }
87
88 pub fn regenerate_four_words(&mut self) {
90 self.four_words = Self::encode_four_words(&self.socket_addr);
91 }
92
93 fn encode_four_words(addr: &SocketAddr) -> Option<String> {
95 match FourWordAdaptiveEncoder::new().and_then(|enc| enc.encode(&addr.to_string())) {
96 Ok(s) => Some(s.replace(' ', "-")),
97 Err(e) => {
98 tracing::warn!("Failed to encode address {addr}: {e}");
99 None
100 }
101 }
102 }
103
104 pub fn from_four_words(words: &str) -> Result<Self> {
106 let enc = FourWordAdaptiveEncoder::new()?;
107 let decoded = enc.decode(words)?; let socket_addr: SocketAddr = decoded.parse()?; Ok(Self::new(socket_addr))
110 }
111
112 pub fn is_ipv4(&self) -> bool {
114 self.socket_addr.is_ipv4()
115 }
116
117 pub fn is_ipv6(&self) -> bool {
119 self.socket_addr.is_ipv6()
120 }
121
122 pub fn is_loopback(&self) -> bool {
124 self.socket_addr.ip().is_loopback()
125 }
126
127 pub fn is_private(&self) -> bool {
129 match self.socket_addr.ip() {
130 IpAddr::V4(ip) => ip.is_private(),
131 IpAddr::V6(ip) => {
132 let octets = ip.octets();
134 (octets[0] & 0xfe) == 0xfc
135 }
136 }
137 }
138}
139
140impl Display for NetworkAddress {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142 if let Some(ref words) = self.four_words {
143 write!(f, "{} ({})", self.socket_addr, words)
144 } else {
145 write!(f, "{}", self.socket_addr)
146 }
147 }
148}
149
150impl FromStr for NetworkAddress {
151 type Err = anyhow::Error;
152
153 fn from_str(s: &str) -> Result<Self> {
154 if let Ok(socket_addr) = SocketAddr::from_str(s) {
156 return Ok(Self::new(socket_addr));
157 }
158
159 if s.starts_with("/ip4/") || s.starts_with("/ip6/") {
161 let parts: Vec<&str> = s.split('/').filter(|p| !p.is_empty()).collect();
162 #[allow(clippy::collapsible_if)]
164 if parts.len() >= 4 && (parts[0] == "ip4" || parts[0] == "ip6") && parts[2] == "tcp" {
165 if let Ok(port) = parts[3].parse::<u16>() {
166 let ip_str = parts[1];
168 if let Ok(ip) = ip_str.parse::<IpAddr>() {
169 let socket_addr = SocketAddr::new(ip, port);
170 return Ok(Self::new(socket_addr));
171 }
172 }
173 }
174 }
175
176 if let Ok(addr) = Self::from_four_words(s) {
178 return Ok(addr);
179 }
180
181 Err(anyhow!("Invalid address format: {}", s))
182 }
183}
184
185impl From<SocketAddr> for NetworkAddress {
186 fn from(socket_addr: SocketAddr) -> Self {
187 Self::new(socket_addr)
188 }
189}
190
191impl From<&SocketAddr> for NetworkAddress {
192 fn from(socket_addr: &SocketAddr) -> Self {
193 Self::new(*socket_addr)
194 }
195}
196
197impl From<NetworkAddress> for SocketAddr {
198 fn from(addr: NetworkAddress) -> Self {
199 addr.socket_addr
200 }
201}
202
203impl From<&NetworkAddress> for SocketAddr {
204 fn from(addr: &NetworkAddress) -> Self {
205 addr.socket_addr
206 }
207}
208
209#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
211pub struct AddressBook {
212 pub addresses: Vec<NetworkAddress>,
214 pub last_known_good: Option<NetworkAddress>,
216}
217
218impl AddressBook {
219 pub fn new() -> Self {
221 Self {
222 addresses: Vec::new(),
223 last_known_good: None,
224 }
225 }
226
227 pub fn with_address(address: NetworkAddress) -> Self {
229 Self {
230 addresses: vec![address.clone()],
231 last_known_good: Some(address),
232 }
233 }
234
235 pub fn add_address(&mut self, address: NetworkAddress) {
237 if !self.addresses.contains(&address) {
238 self.addresses.push(address);
239 }
240 }
241
242 pub fn remove_address(&mut self, address: &NetworkAddress) {
244 self.addresses.retain(|a| a != address);
245 if self.last_known_good.as_ref() == Some(address) {
246 self.last_known_good = self.addresses.first().cloned();
247 }
248 }
249
250 pub fn update_last_known_good(&mut self, address: NetworkAddress) {
252 if self.addresses.contains(&address) {
253 self.last_known_good = Some(address);
254 }
255 }
256
257 pub fn best_address(&self) -> Option<&NetworkAddress> {
259 self.last_known_good
260 .as_ref()
261 .or_else(|| self.addresses.first())
262 }
263
264 pub fn addresses(&self) -> &[NetworkAddress] {
266 &self.addresses
267 }
268
269 pub fn is_empty(&self) -> bool {
271 self.addresses.is_empty()
272 }
273
274 pub fn len(&self) -> usize {
276 self.addresses.len()
277 }
278}
279
280impl Default for AddressBook {
281 fn default() -> Self {
282 Self::new()
283 }
284}
285
286impl Display for AddressBook {
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 if self.addresses.is_empty() {
289 write!(f, "Empty address book")
290 } else {
291 write!(
292 f,
293 "Addresses: [{}]",
294 self.addresses
295 .iter()
296 .map(|a| a.to_string())
297 .collect::<Vec<_>>()
298 .join(", ")
299 )
300 }
301 }
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307 use std::net::{Ipv4Addr, Ipv6Addr};
308
309 #[test]
310 fn test_network_address_creation() {
311 let addr = NetworkAddress::from_ipv4(Ipv4Addr::new(127, 0, 0, 1), 8080);
312 assert_eq!(addr.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
313 assert_eq!(addr.port(), 8080);
314 assert!(addr.is_ipv4());
315 assert!(addr.is_loopback());
316 }
317
318 #[test]
319 fn test_network_address_from_string() {
320 let addr = "127.0.0.1:8080".parse::<NetworkAddress>().unwrap();
321 assert_eq!(addr.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
322 assert_eq!(addr.port(), 8080);
323 }
324
325 #[test]
326 fn test_network_address_display() {
327 let addr = NetworkAddress::from_ipv4(Ipv4Addr::new(192, 168, 1, 1), 9000);
328 let display = addr.to_string();
329 assert!(display.contains("192.168.1.1:9000"));
330 }
331
332 #[test]
333 fn test_address_book() {
334 let mut book = AddressBook::new();
335 let addr1 = NetworkAddress::from_ipv4(Ipv4Addr::new(192, 168, 1, 1), 9000);
336 let addr2 = NetworkAddress::from_ipv4(Ipv4Addr::new(192, 168, 1, 2), 9001);
337
338 book.add_address(addr1.clone());
339 book.add_address(addr2.clone());
340
341 assert_eq!(book.len(), 2);
342 assert_eq!(book.best_address(), Some(&addr1));
343
344 book.update_last_known_good(addr2.clone());
345 assert_eq!(book.best_address(), Some(&addr2));
346 }
347
348 #[test]
349 fn test_private_address_detection() {
350 let private_addr = NetworkAddress::from_ipv4(Ipv4Addr::new(192, 168, 1, 1), 9000);
351 assert!(private_addr.is_private());
352
353 let public_addr = NetworkAddress::from_ipv4(Ipv4Addr::new(8, 8, 8, 8), 53);
354 assert!(!public_addr.is_private());
355 }
356
357 #[test]
358 fn test_ipv6_address() {
359 let addr = NetworkAddress::from_ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080);
360 assert!(addr.is_ipv6());
361 assert!(addr.is_loopback());
362 }
363}