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