1use std::fmt::{self, Display};
35use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
36use std::str::FromStr;
37
38use anyhow::{Result, anyhow};
39use serde::{Deserialize, Serialize};
40
41pub use saorsa_transport::transport::TransportAddr;
42
43use crate::identity::peer_id::PeerId;
44
45#[must_use]
48pub(crate) fn is_lan_ip(ip: IpAddr) -> bool {
49 match ip {
50 IpAddr::V4(ip) => is_lan_ipv4(ip),
51 IpAddr::V6(ip) => {
52 if let Some(ip) = ip.to_ipv4_mapped() {
53 return is_lan_ipv4(ip);
54 }
55 is_lan_ipv6(ip)
56 }
57 }
58}
59
60fn is_lan_ipv4(ip: Ipv4Addr) -> bool {
61 ip.is_loopback()
62 || ip.is_private()
63 || ip.is_link_local()
64 || (ip.octets()[0] == 100 && (ip.octets()[1] & 0b1100_0000) == 64)
65}
66
67fn is_lan_ipv6(ip: Ipv6Addr) -> bool {
68 let octets = ip.octets();
69 ip.is_loopback()
70 || (octets[0] & 0xfe) == 0xfc
71 || (octets[0] == 0xfe && (octets[1] & 0xc0) == 0x80)
72}
73
74#[derive(Debug, Clone, PartialEq, Eq, Hash)]
81pub struct MultiAddr {
82 transport: TransportAddr,
83 peer_id: Option<PeerId>,
84}
85
86impl From<TransportAddr> for MultiAddr {
87 fn from(transport: TransportAddr) -> Self {
88 Self::new(transport)
89 }
90}
91
92impl MultiAddr {
93 #[must_use]
95 pub fn new(transport: TransportAddr) -> Self {
96 Self {
97 transport,
98 peer_id: None,
99 }
100 }
101
102 #[must_use]
104 pub fn quic(addr: SocketAddr) -> Self {
105 Self::new(TransportAddr::Quic(addr))
106 }
107
108 #[must_use]
110 pub fn tcp(addr: SocketAddr) -> Self {
111 Self::new(TransportAddr::Tcp(addr))
112 }
113
114 #[must_use]
116 pub fn with_peer_id(mut self, peer_id: PeerId) -> Self {
117 self.peer_id = Some(peer_id);
118 self
119 }
120
121 #[must_use]
123 pub fn from_ip_port(ip: IpAddr, port: u16) -> Self {
124 Self::quic(SocketAddr::new(ip, port))
125 }
126
127 #[must_use]
129 pub fn from_ipv4(ip: Ipv4Addr, port: u16) -> Self {
130 Self::from_ip_port(IpAddr::V4(ip), port)
131 }
132
133 #[must_use]
135 pub fn from_ipv6(ip: Ipv6Addr, port: u16) -> Self {
136 Self::from_ip_port(IpAddr::V6(ip), port)
137 }
138
139 #[must_use]
145 pub fn transport(&self) -> &TransportAddr {
146 &self.transport
147 }
148
149 #[must_use]
151 pub fn peer_id(&self) -> Option<&PeerId> {
152 self.peer_id.as_ref()
153 }
154
155 #[must_use]
159 pub fn is_quic(&self) -> bool {
160 matches!(self.transport, TransportAddr::Quic(_))
161 }
162
163 #[must_use]
170 pub fn dialable_socket_addr(&self) -> Option<SocketAddr> {
171 match self.transport {
172 TransportAddr::Quic(sa) => Some(sa),
173 _ => None,
174 }
175 }
176
177 #[must_use]
180 pub fn socket_addr(&self) -> Option<SocketAddr> {
181 self.transport.as_socket_addr()
182 }
183
184 #[must_use]
186 pub fn ip(&self) -> Option<IpAddr> {
187 self.socket_addr().map(|a| a.ip())
188 }
189
190 #[must_use]
192 pub fn port(&self) -> Option<u16> {
193 self.socket_addr().map(|a| a.port())
194 }
195
196 pub fn is_ipv4(&self) -> bool {
198 self.socket_addr().is_some_and(|a| a.is_ipv4())
199 }
200
201 pub fn is_ipv6(&self) -> bool {
203 self.socket_addr().is_some_and(|a| a.is_ipv6())
204 }
205
206 pub fn is_loopback(&self) -> bool {
208 self.ip().is_some_and(|ip| ip.is_loopback())
209 }
210
211 pub fn is_private(&self) -> bool {
214 self.ip().is_some_and(is_lan_ip)
215 }
216}
217
218impl Display for MultiAddr {
223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224 write!(f, "{}", self.transport)?;
225 if let Some(pid) = &self.peer_id {
226 write!(f, "/p2p/{}", pid.to_hex())?;
227 }
228 Ok(())
229 }
230}
231
232impl FromStr for MultiAddr {
237 type Err = anyhow::Error;
238
239 fn from_str(s: &str) -> Result<Self> {
240 if s.is_empty() {
241 return Err(anyhow!("Invalid address format: empty string"));
242 }
243
244 if let Some(p2p_idx) = s.rfind("/p2p/") {
246 let transport_part = &s[..p2p_idx];
247 let peer_hex = &s[p2p_idx + 5..]; if transport_part.is_empty() {
251 return Err(anyhow!(
252 "Peer-only addresses (/p2p/<id>) are not yet supported as standalone MultiAddr"
253 ));
254 }
255
256 if peer_hex.contains('/') {
258 return Err(anyhow!(
259 "Unexpected trailing components after peer ID in: {}",
260 s
261 ));
262 }
263
264 let transport = transport_part
265 .parse::<TransportAddr>()
266 .map_err(|e| anyhow!("Invalid transport address: {}", e))?;
267 let peer_id = PeerId::from_hex(peer_hex)
268 .map_err(|e| anyhow!("Invalid peer ID in address: {}", e))?;
269
270 Ok(MultiAddr {
271 transport,
272 peer_id: Some(peer_id),
273 })
274 } else {
275 let transport = s
277 .parse::<TransportAddr>()
278 .map_err(|e| anyhow!("Invalid address: {}", e))?;
279
280 Ok(MultiAddr {
281 transport,
282 peer_id: None,
283 })
284 }
285 }
286}
287
288impl Serialize for MultiAddr {
293 fn serialize<S: serde::Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
294 s.serialize_str(&self.to_string())
295 }
296}
297
298impl<'de> Deserialize<'de> for MultiAddr {
299 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> std::result::Result<Self, D::Error> {
300 let s = String::deserialize(d)?;
301 s.parse::<MultiAddr>().map_err(serde::de::Error::custom)
302 }
303}
304
305#[cfg(test)]
310mod tests {
311 use super::*;
312 use std::net::Ipv6Addr;
313
314 #[test]
315 fn test_network_address_creation() {
316 let addr = MultiAddr::from_ipv4(Ipv4Addr::new(127, 0, 0, 1), 8080);
317 assert_eq!(addr.ip(), Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))));
318 assert_eq!(addr.port(), Some(8080));
319 assert!(addr.is_ipv4());
320 assert!(addr.is_loopback());
321 }
322
323 #[test]
324 fn test_network_address_from_string() {
325 let addr = "/ip4/127.0.0.1/udp/8080/quic".parse::<MultiAddr>().unwrap();
326 assert_eq!(addr.ip(), Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))));
327 assert_eq!(addr.port(), Some(8080));
328 }
329
330 #[test]
331 fn test_network_address_display() {
332 let addr = MultiAddr::from_ipv4(Ipv4Addr::new(192, 168, 1, 1), 9000);
333 assert_eq!(addr.to_string(), "/ip4/192.168.1.1/udp/9000/quic");
334 }
335
336 #[test]
337 fn test_private_address_detection() {
338 let private_addr = MultiAddr::from_ipv4(Ipv4Addr::new(192, 168, 1, 1), 9000);
339 assert!(private_addr.is_private());
340
341 let public_addr = MultiAddr::from_ipv4(Ipv4Addr::new(8, 8, 8, 8), 53);
342 assert!(!public_addr.is_private());
343 }
344
345 #[test]
346 fn test_ipv4_mapped_lan_address_detection() {
347 let mapped_private: IpAddr = "::ffff:192.168.1.10".parse().unwrap();
348 let mapped_loopback: IpAddr = "::ffff:127.0.0.1".parse().unwrap();
349 let mapped_link_local: IpAddr = "::ffff:169.254.1.10".parse().unwrap();
350 let mapped_cgnat: IpAddr = "::ffff:100.64.0.1".parse().unwrap();
351 let mapped_public: IpAddr = "::ffff:8.8.8.8".parse().unwrap();
352
353 assert!(is_lan_ip(mapped_private));
354 assert!(is_lan_ip(mapped_loopback));
355 assert!(is_lan_ip(mapped_link_local));
356 assert!(is_lan_ip(mapped_cgnat));
357 assert!(!is_lan_ip(mapped_public));
358 }
359
360 #[test]
361 fn test_ipv4_mapped_private_multiaddr_detection() {
362 let addr: MultiAddr = "/ip6/::ffff:192.168.1.10/udp/9000/quic".parse().unwrap();
363
364 assert!(addr.is_private());
365 }
366
367 #[test]
368 fn test_ipv6_address() {
369 let addr = MultiAddr::from_ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080);
370 assert!(addr.is_ipv6());
371 assert!(addr.is_loopback());
372 }
373
374 #[test]
375 fn test_multiaddr_tcp_parsing() {
376 let addr = "/ip4/192.168.1.1/tcp/9000".parse::<MultiAddr>().unwrap();
377 assert_eq!(addr.ip(), Some(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))));
378 assert_eq!(addr.port(), Some(9000));
379 assert!(matches!(addr.transport(), TransportAddr::Tcp(_)));
380 }
381
382 #[test]
383 fn test_multiaddr_quic_parsing() {
384 let addr = "/ip4/10.0.0.1/udp/9000/quic".parse::<MultiAddr>().unwrap();
385 assert_eq!(addr.ip(), Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))));
386 assert_eq!(addr.port(), Some(9000));
387 assert!(matches!(addr.transport(), TransportAddr::Quic(_)));
388 }
389
390 #[test]
391 fn test_multiaddr_raw_udp_parsing() {
392 let addr = "/ip4/10.0.0.1/udp/5000".parse::<MultiAddr>().unwrap();
393 assert_eq!(addr.port(), Some(5000));
394 assert!(matches!(addr.transport(), TransportAddr::Udp(_)));
395 }
396
397 #[test]
398 fn test_multiaddr_ipv6_quic_parsing() {
399 let addr = "/ip6/::1/udp/8080/quic".parse::<MultiAddr>().unwrap();
400 assert_eq!(
401 addr.ip(),
402 Some(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)))
403 );
404 assert_eq!(addr.port(), Some(8080));
405 assert!(addr.is_loopback());
406 }
407
408 #[test]
409 fn test_display_roundtrip_quic() {
410 let addr = MultiAddr::from_ipv4(Ipv4Addr::new(1, 2, 3, 4), 9000);
411 let s = addr.to_string();
412 let parsed: MultiAddr = s.parse().unwrap();
413 assert_eq!(addr, parsed);
414 }
415
416 #[test]
417 fn test_display_roundtrip_tcp() {
418 let addr = MultiAddr::tcp(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 80));
419 let s = addr.to_string();
420 let parsed: MultiAddr = s.parse().unwrap();
421 assert_eq!(addr, parsed);
422 }
423
424 #[test]
425 fn test_bluetooth_roundtrip() {
426 let addr = MultiAddr::new(TransportAddr::Bluetooth {
427 mac: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF],
428 channel: 5,
429 });
430 let s = addr.to_string();
431 assert_eq!(s, "/bt/AA:BB:CC:DD:EE:FF/rfcomm/5");
432 let parsed: MultiAddr = s.parse().unwrap();
433 assert_eq!(addr, parsed);
434 }
435
436 #[test]
437 fn test_ble_roundtrip() {
438 let addr = MultiAddr::new(TransportAddr::Ble {
439 mac: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06],
440 psm: 128,
441 });
442 let s = addr.to_string();
443 assert_eq!(s, "/ble/01:02:03:04:05:06/l2cap/128");
444 let parsed: MultiAddr = s.parse().unwrap();
445 assert_eq!(addr, parsed);
446 }
447
448 #[test]
449 fn test_lora_roundtrip() {
450 let addr = MultiAddr::new(TransportAddr::LoRa {
451 dev_addr: [0xDE, 0xAD, 0xBE, 0xEF],
452 freq_hz: 868_000_000,
453 });
454 let s = addr.to_string();
455 assert_eq!(s, "/lora/deadbeef/868000000");
456 let parsed: MultiAddr = s.parse().unwrap();
457 assert_eq!(addr, parsed);
458 }
459
460 #[test]
461 fn test_lorawan_roundtrip() {
462 let addr = MultiAddr::new(TransportAddr::LoRaWan {
463 dev_eui: 0x0011_2233_4455_6677,
464 });
465 let s = addr.to_string();
466 assert_eq!(s, "/lorawan/0011223344556677");
467 let parsed: MultiAddr = s.parse().unwrap();
468 assert_eq!(addr, parsed);
469 }
470
471 #[test]
472 fn test_peer_id_suffix() {
473 let peer_id = PeerId::from_bytes([0xAA; 32]);
474 let addr = MultiAddr::from_ipv4(Ipv4Addr::new(1, 2, 3, 4), 9000).with_peer_id(peer_id);
475 let s = addr.to_string();
476 assert!(s.starts_with("/ip4/1.2.3.4/udp/9000/quic/p2p/"));
477 let parsed: MultiAddr = s.parse().unwrap();
478 assert_eq!(addr, parsed);
479 assert_eq!(parsed.peer_id(), Some(&peer_id));
480 }
481
482 #[test]
483 fn test_non_ip_transport_accessors() {
484 let addr = MultiAddr::new(TransportAddr::Bluetooth {
485 mac: [0; 6],
486 channel: 1,
487 });
488 assert_eq!(addr.socket_addr(), None);
489 assert_eq!(addr.ip(), None);
490 assert_eq!(addr.port(), None);
491 assert!(!addr.is_loopback());
492 assert!(!addr.is_private());
493 assert!(!addr.is_ipv4());
494 assert!(!addr.is_ipv6());
495 }
496
497 #[test]
498 fn test_serde_direct_roundtrip() {
499 let addr = MultiAddr::from_ipv4(Ipv4Addr::new(10, 0, 0, 1), 9000);
500 let json = serde_json::to_string(&addr).unwrap();
501 assert_eq!(json, r#""/ip4/10.0.0.1/udp/9000/quic""#);
502 let recovered: MultiAddr = serde_json::from_str(&json).unwrap();
503 assert_eq!(addr, recovered);
504 }
505
506 #[test]
507 fn test_transport_kind() {
508 assert_eq!(
509 TransportAddr::Quic(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)).kind(),
510 "quic"
511 );
512 assert_eq!(
513 TransportAddr::Tcp(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)).kind(),
514 "tcp"
515 );
516 assert_eq!(
517 TransportAddr::Bluetooth {
518 mac: [0; 6],
519 channel: 0
520 }
521 .kind(),
522 "bluetooth"
523 );
524 }
525
526 #[test]
527 fn test_invalid_format_rejected() {
528 assert!("127.0.0.1:8080".parse::<MultiAddr>().is_err());
530 assert!("garbage".parse::<MultiAddr>().is_err());
531 assert!("/ip4/not-an-ip/tcp/80".parse::<MultiAddr>().is_err());
532 assert!("".parse::<MultiAddr>().is_err());
533 }
534
535 #[test]
537 fn test_serde_roundtrip_with_peer_id() {
538 let peer_id = PeerId::from_bytes([0xBB; 32]);
539 let addr = MultiAddr::from_ipv4(Ipv4Addr::new(10, 0, 0, 1), 9000).with_peer_id(peer_id);
540
541 let json = serde_json::to_string(&addr).unwrap();
542 assert!(
543 json.contains("/p2p/"),
544 "serialized form must contain /p2p/ suffix"
545 );
546
547 let recovered: MultiAddr = serde_json::from_str(&json).unwrap();
548 assert_eq!(addr, recovered, "serde roundtrip must be lossless");
549 assert_eq!(recovered.peer_id(), Some(&peer_id));
550 }
551
552 #[test]
554 fn test_dialable_socket_addr_none_for_tcp() {
555 let tcp_addr = MultiAddr::tcp(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 80));
556 assert!(
557 tcp_addr.dialable_socket_addr().is_none(),
558 "TCP addresses should not be dialable (QUIC-only policy)"
559 );
560
561 let quic_addr = MultiAddr::quic(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 80));
563 assert!(quic_addr.dialable_socket_addr().is_some());
564 }
565
566 #[test]
568 fn test_standalone_peer_id_rejected() {
569 let peer_hex = "aa".repeat(32); let input = format!("/p2p/{peer_hex}");
571 let result = input.parse::<MultiAddr>();
572 assert!(
573 result.is_err(),
574 "standalone /p2p/<id> without transport must be rejected"
575 );
576 }
577
578 #[test]
580 fn test_from_transport_addr() {
581 let transport = TransportAddr::Quic(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9000));
582 let addr: MultiAddr = transport.clone().into();
583 assert_eq!(addr.transport(), &transport);
584 assert_eq!(addr.peer_id(), None);
585 }
586}