1use std::fmt::{self, Display};
36use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
37use std::str::FromStr;
38
39use anyhow::{Result, anyhow};
40use serde::{Deserialize, Serialize};
41
42pub use saorsa_transport::transport::{TransportAddr, WebRtcCertificateHash, WebRtcDirectAddr};
43
44use crate::identity::peer_id::PeerId;
45
46#[must_use]
49pub(crate) fn is_lan_ip(ip: IpAddr) -> bool {
50 match ip {
51 IpAddr::V4(ip) => is_lan_ipv4(ip),
52 IpAddr::V6(ip) => {
53 if let Some(ip) = ip.to_ipv4_mapped() {
54 return is_lan_ipv4(ip);
55 }
56 is_lan_ipv6(ip)
57 }
58 }
59}
60
61fn is_lan_ipv4(ip: Ipv4Addr) -> bool {
62 ip.is_loopback()
63 || ip.is_private()
64 || ip.is_link_local()
65 || (ip.octets()[0] == 100 && (ip.octets()[1] & 0b1100_0000) == 64)
66}
67
68fn is_lan_ipv6(ip: Ipv6Addr) -> bool {
69 let octets = ip.octets();
70 ip.is_loopback()
71 || (octets[0] & 0xfe) == 0xfc
72 || (octets[0] == 0xfe && (octets[1] & 0xc0) == 0x80)
73}
74
75#[derive(Debug, Clone, PartialEq, Eq, Hash)]
82pub struct MultiAddr {
83 transport: TransportAddr,
84 peer_id: Option<PeerId>,
85}
86
87impl From<TransportAddr> for MultiAddr {
88 fn from(transport: TransportAddr) -> Self {
89 Self::new(transport)
90 }
91}
92
93impl MultiAddr {
94 #[must_use]
96 pub fn new(transport: TransportAddr) -> Self {
97 Self {
98 transport,
99 peer_id: None,
100 }
101 }
102
103 #[must_use]
105 pub fn quic(addr: SocketAddr) -> Self {
106 Self::new(TransportAddr::Quic(addr))
107 }
108
109 #[must_use]
111 pub fn tcp(addr: SocketAddr) -> Self {
112 Self::new(TransportAddr::Tcp(addr))
113 }
114
115 #[must_use]
117 pub fn webrtc_direct(addr: WebRtcDirectAddr) -> Self {
118 Self::new(TransportAddr::WebRtcDirect(addr))
119 }
120
121 #[must_use]
123 pub fn with_peer_id(mut self, peer_id: PeerId) -> Self {
124 self.peer_id = Some(peer_id);
125 self
126 }
127
128 #[must_use]
130 pub fn from_ip_port(ip: IpAddr, port: u16) -> Self {
131 Self::quic(SocketAddr::new(ip, port))
132 }
133
134 #[must_use]
136 pub fn from_ipv4(ip: Ipv4Addr, port: u16) -> Self {
137 Self::from_ip_port(IpAddr::V4(ip), port)
138 }
139
140 #[must_use]
142 pub fn from_ipv6(ip: Ipv6Addr, port: u16) -> Self {
143 Self::from_ip_port(IpAddr::V6(ip), port)
144 }
145
146 #[must_use]
152 pub fn transport(&self) -> &TransportAddr {
153 &self.transport
154 }
155
156 #[must_use]
158 pub fn peer_id(&self) -> Option<&PeerId> {
159 self.peer_id.as_ref()
160 }
161
162 #[must_use]
164 pub fn webrtc_direct_addr(&self) -> Option<&WebRtcDirectAddr> {
165 match &self.transport {
166 TransportAddr::WebRtcDirect(address) => Some(address),
167 _ => None,
168 }
169 }
170
171 #[must_use]
173 pub fn is_webrtc_direct(&self) -> bool {
174 self.webrtc_direct_addr().is_some()
175 }
176
177 #[must_use]
181 pub fn is_quic(&self) -> bool {
182 matches!(self.transport, TransportAddr::Quic(_))
183 }
184
185 #[must_use]
192 pub fn dialable_socket_addr(&self) -> Option<SocketAddr> {
193 match self.transport {
194 TransportAddr::Quic(sa) => Some(sa),
195 _ => None,
196 }
197 }
198
199 #[must_use]
202 pub fn socket_addr(&self) -> Option<SocketAddr> {
203 match &self.transport {
204 TransportAddr::WebRtcDirect(address) => Some(address.socket_addr()),
205 _ => self.transport.as_socket_addr(),
206 }
207 }
208
209 #[must_use]
212 pub fn is_storable(&self) -> bool {
213 self.socket_addr()
214 .is_none_or(|sa| !sa.ip().is_unspecified() && sa.port() != 0)
215 }
216
217 #[must_use]
219 pub fn ip(&self) -> Option<IpAddr> {
220 self.socket_addr().map(|a| a.ip())
221 }
222
223 #[must_use]
225 pub fn port(&self) -> Option<u16> {
226 match &self.transport {
227 TransportAddr::WebRtcDirect(address) => Some(address.port()),
228 _ => self.socket_addr().map(|a| a.port()),
229 }
230 }
231
232 pub fn is_ipv4(&self) -> bool {
234 self.socket_addr().is_some_and(|a| a.is_ipv4())
235 }
236
237 pub fn is_ipv6(&self) -> bool {
239 self.socket_addr().is_some_and(|a| a.is_ipv6())
240 }
241
242 pub fn is_loopback(&self) -> bool {
244 self.ip().is_some_and(|ip| ip.is_loopback())
245 }
246
247 pub fn is_private(&self) -> bool {
250 self.ip().is_some_and(is_lan_ip)
251 }
252}
253
254impl Display for MultiAddr {
259 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260 write!(f, "{}", self.transport)?;
261 if let Some(pid) = &self.peer_id {
262 write!(f, "/p2p/{}", pid.to_hex())?;
263 }
264 Ok(())
265 }
266}
267
268impl FromStr for MultiAddr {
273 type Err = anyhow::Error;
274
275 fn from_str(s: &str) -> Result<Self> {
276 if s.is_empty() {
277 return Err(anyhow!("Invalid address format: empty string"));
278 }
279
280 if let Some(p2p_idx) = s.rfind("/p2p/") {
282 let transport_part = &s[..p2p_idx];
283 let peer_hex = &s[p2p_idx + 5..]; if transport_part.is_empty() {
287 return Err(anyhow!(
288 "Peer-only addresses (/p2p/<id>) are not yet supported as standalone MultiAddr"
289 ));
290 }
291
292 if peer_hex.contains('/') {
294 return Err(anyhow!(
295 "Unexpected trailing components after peer ID in: {}",
296 s
297 ));
298 }
299
300 let transport = transport_part
301 .parse::<TransportAddr>()
302 .map_err(|e| anyhow!("Invalid transport address: {}", e))?;
303 let peer_id = PeerId::from_hex(peer_hex)
304 .map_err(|e| anyhow!("Invalid peer ID in address: {}", e))?;
305
306 Ok(MultiAddr {
307 transport,
308 peer_id: Some(peer_id),
309 })
310 } else {
311 let transport = s
313 .parse::<TransportAddr>()
314 .map_err(|e| anyhow!("Invalid address: {}", e))?;
315
316 Ok(MultiAddr {
317 transport,
318 peer_id: None,
319 })
320 }
321 }
322}
323
324impl Serialize for MultiAddr {
329 fn serialize<S: serde::Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
330 s.serialize_str(&self.to_string())
331 }
332}
333
334impl<'de> Deserialize<'de> for MultiAddr {
335 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> std::result::Result<Self, D::Error> {
336 let s = String::deserialize(d)?;
337 s.parse::<MultiAddr>().map_err(serde::de::Error::custom)
338 }
339}
340
341#[cfg(test)]
346mod tests {
347 use super::*;
348 use std::net::Ipv6Addr;
349
350 #[test]
351 fn test_network_address_creation() {
352 let addr = MultiAddr::from_ipv4(Ipv4Addr::new(127, 0, 0, 1), 8080);
353 assert_eq!(addr.ip(), Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))));
354 assert_eq!(addr.port(), Some(8080));
355 assert!(addr.is_ipv4());
356 assert!(addr.is_loopback());
357 }
358
359 #[test]
360 fn test_network_address_from_string() {
361 let addr = "/ip4/127.0.0.1/udp/8080/quic".parse::<MultiAddr>().unwrap();
362 assert_eq!(addr.ip(), Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))));
363 assert_eq!(addr.port(), Some(8080));
364 }
365
366 #[test]
367 fn test_network_address_display() {
368 let addr = MultiAddr::from_ipv4(Ipv4Addr::new(192, 168, 1, 1), 9000);
369 assert_eq!(addr.to_string(), "/ip4/192.168.1.1/udp/9000/quic");
370 }
371
372 #[test]
373 fn test_private_address_detection() {
374 let private_addr = MultiAddr::from_ipv4(Ipv4Addr::new(192, 168, 1, 1), 9000);
375 assert!(private_addr.is_private());
376
377 let public_addr = MultiAddr::from_ipv4(Ipv4Addr::new(8, 8, 8, 8), 53);
378 assert!(!public_addr.is_private());
379 }
380
381 #[test]
382 fn test_ipv4_mapped_lan_address_detection() {
383 let mapped_private: IpAddr = "::ffff:192.168.1.10".parse().unwrap();
384 let mapped_loopback: IpAddr = "::ffff:127.0.0.1".parse().unwrap();
385 let mapped_link_local: IpAddr = "::ffff:169.254.1.10".parse().unwrap();
386 let mapped_cgnat: IpAddr = "::ffff:100.64.0.1".parse().unwrap();
387 let mapped_public: IpAddr = "::ffff:8.8.8.8".parse().unwrap();
388
389 assert!(is_lan_ip(mapped_private));
390 assert!(is_lan_ip(mapped_loopback));
391 assert!(is_lan_ip(mapped_link_local));
392 assert!(is_lan_ip(mapped_cgnat));
393 assert!(!is_lan_ip(mapped_public));
394 }
395
396 #[test]
397 fn test_ipv4_mapped_private_multiaddr_detection() {
398 let addr: MultiAddr = "/ip6/::ffff:192.168.1.10/udp/9000/quic".parse().unwrap();
399
400 assert!(addr.is_private());
401 }
402
403 #[test]
404 fn test_ipv6_address() {
405 let addr = MultiAddr::from_ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080);
406 assert!(addr.is_ipv6());
407 assert!(addr.is_loopback());
408 }
409
410 #[test]
411 fn test_multiaddr_tcp_parsing() {
412 let addr = "/ip4/192.168.1.1/tcp/9000".parse::<MultiAddr>().unwrap();
413 assert_eq!(addr.ip(), Some(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))));
414 assert_eq!(addr.port(), Some(9000));
415 assert!(matches!(addr.transport(), TransportAddr::Tcp(_)));
416 }
417
418 #[test]
419 fn test_multiaddr_quic_parsing() {
420 let addr = "/ip4/10.0.0.1/udp/9000/quic".parse::<MultiAddr>().unwrap();
421 assert_eq!(addr.ip(), Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))));
422 assert_eq!(addr.port(), Some(9000));
423 assert!(matches!(addr.transport(), TransportAddr::Quic(_)));
424 }
425
426 #[test]
427 fn test_multiaddr_webrtc_direct_roundtrip_with_peer_id() {
428 let peer_id = PeerId::from_bytes([0x55; 32]);
429 let certificate_hash = WebRtcCertificateHash::new([0x11; 32]);
430 let endpoint =
431 WebRtcDirectAddr::new("203.0.113.7:443".parse().unwrap(), certificate_hash).unwrap();
432 let address = MultiAddr::webrtc_direct(endpoint).with_peer_id(peer_id);
433
434 let encoded = address.to_string();
435 assert_eq!(encoded.matches("/certhash/").count(), 1);
436 assert!(encoded.ends_with(&format!("/p2p/{}", peer_id.to_hex())));
437 let parsed = encoded.parse::<MultiAddr>().unwrap();
438 assert_eq!(parsed, address);
439 assert_eq!(parsed.peer_id(), Some(&peer_id));
440 assert!(parsed.is_webrtc_direct());
441 assert!(!parsed.is_quic());
442 assert_eq!(parsed.port(), Some(443));
443 assert_eq!(
444 parsed.webrtc_direct_addr().unwrap().certificate_hash(),
445 certificate_hash
446 );
447 }
448
449 #[test]
450 fn test_webrtc_direct_multiaddr_postcard_roundtrip() {
451 let peer_id = PeerId::from_bytes([0x66; 32]);
452 let endpoint = WebRtcDirectAddr::new(
453 "127.0.0.1:443".parse().unwrap(),
454 WebRtcCertificateHash::new([0x33; 32]),
455 )
456 .unwrap();
457 let address = MultiAddr::webrtc_direct(endpoint).with_peer_id(peer_id);
458
459 let bytes = postcard::to_stdvec(&address).unwrap();
460 let decoded: MultiAddr = postcard::from_bytes(&bytes).unwrap();
461 assert_eq!(decoded, address);
462 }
463
464 #[test]
465 fn test_multiaddr_raw_udp_parsing() {
466 let addr = "/ip4/10.0.0.1/udp/5000".parse::<MultiAddr>().unwrap();
467 assert_eq!(addr.port(), Some(5000));
468 assert!(matches!(addr.transport(), TransportAddr::Udp(_)));
469 }
470
471 #[test]
472 fn test_multiaddr_ipv6_quic_parsing() {
473 let addr = "/ip6/::1/udp/8080/quic".parse::<MultiAddr>().unwrap();
474 assert_eq!(
475 addr.ip(),
476 Some(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)))
477 );
478 assert_eq!(addr.port(), Some(8080));
479 assert!(addr.is_loopback());
480 }
481
482 #[test]
483 fn test_display_roundtrip_quic() {
484 let addr = MultiAddr::from_ipv4(Ipv4Addr::new(1, 2, 3, 4), 9000);
485 let s = addr.to_string();
486 let parsed: MultiAddr = s.parse().unwrap();
487 assert_eq!(addr, parsed);
488 }
489
490 #[test]
491 fn test_display_roundtrip_tcp() {
492 let addr = MultiAddr::tcp(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 80));
493 let s = addr.to_string();
494 let parsed: MultiAddr = s.parse().unwrap();
495 assert_eq!(addr, parsed);
496 }
497
498 #[test]
499 fn test_bluetooth_roundtrip() {
500 let addr = MultiAddr::new(TransportAddr::Bluetooth {
501 mac: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF],
502 channel: 5,
503 });
504 let s = addr.to_string();
505 assert_eq!(s, "/bt/AA:BB:CC:DD:EE:FF/rfcomm/5");
506 let parsed: MultiAddr = s.parse().unwrap();
507 assert_eq!(addr, parsed);
508 }
509
510 #[test]
511 fn test_ble_roundtrip() {
512 let addr = MultiAddr::new(TransportAddr::Ble {
513 mac: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06],
514 psm: 128,
515 });
516 let s = addr.to_string();
517 assert_eq!(s, "/ble/01:02:03:04:05:06/l2cap/128");
518 let parsed: MultiAddr = s.parse().unwrap();
519 assert_eq!(addr, parsed);
520 }
521
522 #[test]
523 fn test_lora_roundtrip() {
524 let addr = MultiAddr::new(TransportAddr::LoRa {
525 dev_addr: [0xDE, 0xAD, 0xBE, 0xEF],
526 freq_hz: 868_000_000,
527 });
528 let s = addr.to_string();
529 assert_eq!(s, "/lora/deadbeef/868000000");
530 let parsed: MultiAddr = s.parse().unwrap();
531 assert_eq!(addr, parsed);
532 }
533
534 #[test]
535 fn test_lorawan_roundtrip() {
536 let addr = MultiAddr::new(TransportAddr::LoRaWan {
537 dev_eui: 0x0011_2233_4455_6677,
538 });
539 let s = addr.to_string();
540 assert_eq!(s, "/lorawan/0011223344556677");
541 let parsed: MultiAddr = s.parse().unwrap();
542 assert_eq!(addr, parsed);
543 }
544
545 #[test]
546 fn test_peer_id_suffix() {
547 let peer_id = PeerId::from_bytes([0xAA; 32]);
548 let addr = MultiAddr::from_ipv4(Ipv4Addr::new(1, 2, 3, 4), 9000).with_peer_id(peer_id);
549 let s = addr.to_string();
550 assert!(s.starts_with("/ip4/1.2.3.4/udp/9000/quic/p2p/"));
551 let parsed: MultiAddr = s.parse().unwrap();
552 assert_eq!(addr, parsed);
553 assert_eq!(parsed.peer_id(), Some(&peer_id));
554 }
555
556 #[test]
557 fn test_non_ip_transport_accessors() {
558 let addr = MultiAddr::new(TransportAddr::Bluetooth {
559 mac: [0; 6],
560 channel: 1,
561 });
562 assert_eq!(addr.socket_addr(), None);
563 assert_eq!(addr.ip(), None);
564 assert_eq!(addr.port(), None);
565 assert!(!addr.is_loopback());
566 assert!(!addr.is_private());
567 assert!(!addr.is_ipv4());
568 assert!(!addr.is_ipv6());
569 }
570
571 #[test]
572 fn test_serde_direct_roundtrip() {
573 let addr = MultiAddr::from_ipv4(Ipv4Addr::new(10, 0, 0, 1), 9000);
574 let json = serde_json::to_string(&addr).unwrap();
575 assert_eq!(json, r#""/ip4/10.0.0.1/udp/9000/quic""#);
576 let recovered: MultiAddr = serde_json::from_str(&json).unwrap();
577 assert_eq!(addr, recovered);
578 }
579
580 #[test]
581 fn test_transport_kind() {
582 assert_eq!(
583 TransportAddr::Quic(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)).kind(),
584 "quic"
585 );
586 assert_eq!(
587 TransportAddr::Tcp(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)).kind(),
588 "tcp"
589 );
590 assert_eq!(
591 TransportAddr::Bluetooth {
592 mac: [0; 6],
593 channel: 0
594 }
595 .kind(),
596 "bluetooth"
597 );
598 }
599
600 #[test]
601 fn test_invalid_format_rejected() {
602 assert!("127.0.0.1:8080".parse::<MultiAddr>().is_err());
604 assert!("garbage".parse::<MultiAddr>().is_err());
605 assert!("/ip4/not-an-ip/tcp/80".parse::<MultiAddr>().is_err());
606 assert!("".parse::<MultiAddr>().is_err());
607 }
608
609 #[test]
611 fn test_serde_roundtrip_with_peer_id() {
612 let peer_id = PeerId::from_bytes([0xBB; 32]);
613 let addr = MultiAddr::from_ipv4(Ipv4Addr::new(10, 0, 0, 1), 9000).with_peer_id(peer_id);
614
615 let json = serde_json::to_string(&addr).unwrap();
616 assert!(
617 json.contains("/p2p/"),
618 "serialized form must contain /p2p/ suffix"
619 );
620
621 let recovered: MultiAddr = serde_json::from_str(&json).unwrap();
622 assert_eq!(addr, recovered, "serde roundtrip must be lossless");
623 assert_eq!(recovered.peer_id(), Some(&peer_id));
624 }
625
626 #[test]
628 fn test_dialable_socket_addr_none_for_tcp() {
629 let tcp_addr = MultiAddr::tcp(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 80));
630 assert!(
631 tcp_addr.dialable_socket_addr().is_none(),
632 "TCP addresses should not be dialable (QUIC-only policy)"
633 );
634
635 let quic_addr = MultiAddr::quic(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 80));
637 assert!(quic_addr.dialable_socket_addr().is_some());
638 }
639
640 #[test]
642 fn test_standalone_peer_id_rejected() {
643 let peer_hex = "aa".repeat(32); let input = format!("/p2p/{peer_hex}");
645 let result = input.parse::<MultiAddr>();
646 assert!(
647 result.is_err(),
648 "standalone /p2p/<id> without transport must be rejected"
649 );
650 }
651
652 #[test]
654 fn test_from_transport_addr() {
655 let transport = TransportAddr::Quic(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9000));
656 let addr: MultiAddr = transport.clone().into();
657 assert_eq!(addr.transport(), &transport);
658 assert_eq!(addr.peer_id(), None);
659 }
660}