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 normalized = words.replace('-', " ");
108 let decoded = enc.decode(&normalized)?; let socket_addr: SocketAddr = decoded.parse()?; Ok(Self::new(socket_addr))
111 }
112
113 pub fn is_ipv4(&self) -> bool {
115 self.socket_addr.is_ipv4()
116 }
117
118 pub fn is_ipv6(&self) -> bool {
120 self.socket_addr.is_ipv6()
121 }
122
123 pub fn is_loopback(&self) -> bool {
125 self.socket_addr.ip().is_loopback()
126 }
127
128 pub fn is_private(&self) -> bool {
130 match self.socket_addr.ip() {
131 IpAddr::V4(ip) => ip.is_private(),
132 IpAddr::V6(ip) => {
133 let octets = ip.octets();
135 (octets[0] & 0xfe) == 0xfc
136 }
137 }
138 }
139}
140
141impl Display for NetworkAddress {
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 if let Some(ref words) = self.four_words {
144 write!(f, "{} ({})", self.socket_addr, words)
145 } else {
146 write!(f, "{}", self.socket_addr)
147 }
148 }
149}
150
151impl FromStr for NetworkAddress {
152 type Err = anyhow::Error;
153
154 fn from_str(s: &str) -> Result<Self> {
155 if let Ok(socket_addr) = SocketAddr::from_str(s) {
157 return Ok(Self::new(socket_addr));
158 }
159
160 if s.starts_with("/ip4/") || s.starts_with("/ip6/") {
162 let parts: Vec<&str> = s.split('/').filter(|p| !p.is_empty()).collect();
163 #[allow(clippy::collapsible_if)]
165 if parts.len() >= 4 && (parts[0] == "ip4" || parts[0] == "ip6") && parts[2] == "tcp" {
166 if let Ok(port) = parts[3].parse::<u16>() {
167 let ip_str = parts[1];
169 if let Ok(ip) = ip_str.parse::<IpAddr>() {
170 let socket_addr = SocketAddr::new(ip, port);
171 return Ok(Self::new(socket_addr));
172 }
173 }
174 }
175 }
176
177 if let Ok(addr) = Self::from_four_words(s) {
179 return Ok(addr);
180 }
181
182 Err(anyhow!("Invalid address format: {}", s))
183 }
184}
185
186impl From<SocketAddr> for NetworkAddress {
187 fn from(socket_addr: SocketAddr) -> Self {
188 Self::new(socket_addr)
189 }
190}
191
192impl From<&SocketAddr> for NetworkAddress {
193 fn from(socket_addr: &SocketAddr) -> Self {
194 Self::new(*socket_addr)
195 }
196}
197
198impl From<NetworkAddress> for SocketAddr {
199 fn from(addr: NetworkAddress) -> Self {
200 addr.socket_addr
201 }
202}
203
204impl From<&NetworkAddress> for SocketAddr {
205 fn from(addr: &NetworkAddress) -> Self {
206 addr.socket_addr
207 }
208}
209
210#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
212pub struct AddressBook {
213 pub addresses: Vec<NetworkAddress>,
215 pub last_known_good: Option<NetworkAddress>,
217}
218
219impl AddressBook {
220 pub fn new() -> Self {
222 Self {
223 addresses: Vec::new(),
224 last_known_good: None,
225 }
226 }
227
228 pub fn with_address(address: NetworkAddress) -> Self {
230 Self {
231 addresses: vec![address.clone()],
232 last_known_good: Some(address),
233 }
234 }
235
236 pub fn add_address(&mut self, address: NetworkAddress) {
238 if !self.addresses.contains(&address) {
239 self.addresses.push(address);
240 }
241 }
242
243 pub fn remove_address(&mut self, address: &NetworkAddress) {
245 self.addresses.retain(|a| a != address);
246 if self.last_known_good.as_ref() == Some(address) {
247 self.last_known_good = self.addresses.first().cloned();
248 }
249 }
250
251 pub fn update_last_known_good(&mut self, address: NetworkAddress) {
253 if self.addresses.contains(&address) {
254 self.last_known_good = Some(address);
255 }
256 }
257
258 pub fn best_address(&self) -> Option<&NetworkAddress> {
260 self.last_known_good
261 .as_ref()
262 .or_else(|| self.addresses.first())
263 }
264
265 pub fn addresses(&self) -> &[NetworkAddress] {
267 &self.addresses
268 }
269
270 pub fn is_empty(&self) -> bool {
272 self.addresses.is_empty()
273 }
274
275 pub fn len(&self) -> usize {
277 self.addresses.len()
278 }
279}
280
281impl Default for AddressBook {
282 fn default() -> Self {
283 Self::new()
284 }
285}
286
287impl Display for AddressBook {
288 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289 if self.addresses.is_empty() {
290 write!(f, "Empty address book")
291 } else {
292 write!(
293 f,
294 "Addresses: [{}]",
295 self.addresses
296 .iter()
297 .map(|a| a.to_string())
298 .collect::<Vec<_>>()
299 .join(", ")
300 )
301 }
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308 use std::net::{Ipv4Addr, Ipv6Addr};
309
310 #[test]
311 fn test_network_address_creation() {
312 let addr = NetworkAddress::from_ipv4(Ipv4Addr::new(127, 0, 0, 1), 8080);
313 assert_eq!(addr.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
314 assert_eq!(addr.port(), 8080);
315 assert!(addr.is_ipv4());
316 assert!(addr.is_loopback());
317 }
318
319 #[test]
320 fn test_network_address_from_string() {
321 let addr = "127.0.0.1:8080".parse::<NetworkAddress>().unwrap();
322 assert_eq!(addr.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
323 assert_eq!(addr.port(), 8080);
324 }
325
326 #[test]
327 fn test_network_address_display() {
328 let addr = NetworkAddress::from_ipv4(Ipv4Addr::new(192, 168, 1, 1), 9000);
329 let display = addr.to_string();
330 assert!(display.contains("192.168.1.1:9000"));
331 }
332
333 #[test]
334 fn test_address_book() {
335 let mut book = AddressBook::new();
336 let addr1 = NetworkAddress::from_ipv4(Ipv4Addr::new(192, 168, 1, 1), 9000);
337 let addr2 = NetworkAddress::from_ipv4(Ipv4Addr::new(192, 168, 1, 2), 9001);
338
339 book.add_address(addr1.clone());
340 book.add_address(addr2.clone());
341
342 assert_eq!(book.len(), 2);
343 assert_eq!(book.best_address(), Some(&addr1));
344
345 book.update_last_known_good(addr2.clone());
346 assert_eq!(book.best_address(), Some(&addr2));
347 }
348
349 #[test]
350 fn test_private_address_detection() {
351 let private_addr = NetworkAddress::from_ipv4(Ipv4Addr::new(192, 168, 1, 1), 9000);
352 assert!(private_addr.is_private());
353
354 let public_addr = NetworkAddress::from_ipv4(Ipv4Addr::new(8, 8, 8, 8), 53);
355 assert!(!public_addr.is_private());
356 }
357
358 #[test]
359 fn test_ipv6_address() {
360 let addr = NetworkAddress::from_ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080);
361 assert!(addr.is_ipv6());
362 assert!(addr.is_loopback());
363 }
364}