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
283#[cfg(test)]
288mod tests {
289 use super::*;
290 use std::net::Ipv6Addr;
291
292 #[test]
293 fn test_network_address_creation() {
294 let addr = MultiAddr::from_ipv4(Ipv4Addr::new(127, 0, 0, 1), 8080);
295 assert_eq!(addr.ip(), Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))));
296 assert_eq!(addr.port(), Some(8080));
297 assert!(addr.is_ipv4());
298 assert!(addr.is_loopback());
299 }
300
301 #[test]
302 fn test_network_address_from_string() {
303 let addr = "/ip4/127.0.0.1/udp/8080/quic".parse::<MultiAddr>().unwrap();
304 assert_eq!(addr.ip(), Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))));
305 assert_eq!(addr.port(), Some(8080));
306 }
307
308 #[test]
309 fn test_network_address_display() {
310 let addr = MultiAddr::from_ipv4(Ipv4Addr::new(192, 168, 1, 1), 9000);
311 assert_eq!(addr.to_string(), "/ip4/192.168.1.1/udp/9000/quic");
312 }
313
314 #[test]
315 fn test_private_address_detection() {
316 let private_addr = MultiAddr::from_ipv4(Ipv4Addr::new(192, 168, 1, 1), 9000);
317 assert!(private_addr.is_private());
318
319 let public_addr = MultiAddr::from_ipv4(Ipv4Addr::new(8, 8, 8, 8), 53);
320 assert!(!public_addr.is_private());
321 }
322
323 #[test]
324 fn test_ipv6_address() {
325 let addr = MultiAddr::from_ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080);
326 assert!(addr.is_ipv6());
327 assert!(addr.is_loopback());
328 }
329
330 #[test]
331 fn test_multiaddr_tcp_parsing() {
332 let addr = "/ip4/192.168.1.1/tcp/9000".parse::<MultiAddr>().unwrap();
333 assert_eq!(addr.ip(), Some(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))));
334 assert_eq!(addr.port(), Some(9000));
335 assert!(matches!(addr.transport(), TransportAddr::Tcp(_)));
336 }
337
338 #[test]
339 fn test_multiaddr_quic_parsing() {
340 let addr = "/ip4/10.0.0.1/udp/9000/quic".parse::<MultiAddr>().unwrap();
341 assert_eq!(addr.ip(), Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))));
342 assert_eq!(addr.port(), Some(9000));
343 assert!(matches!(addr.transport(), TransportAddr::Quic(_)));
344 }
345
346 #[test]
347 fn test_multiaddr_raw_udp_parsing() {
348 let addr = "/ip4/10.0.0.1/udp/5000".parse::<MultiAddr>().unwrap();
349 assert_eq!(addr.port(), Some(5000));
350 assert!(matches!(addr.transport(), TransportAddr::Udp(_)));
351 }
352
353 #[test]
354 fn test_multiaddr_ipv6_quic_parsing() {
355 let addr = "/ip6/::1/udp/8080/quic".parse::<MultiAddr>().unwrap();
356 assert_eq!(
357 addr.ip(),
358 Some(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)))
359 );
360 assert_eq!(addr.port(), Some(8080));
361 assert!(addr.is_loopback());
362 }
363
364 #[test]
365 fn test_display_roundtrip_quic() {
366 let addr = MultiAddr::from_ipv4(Ipv4Addr::new(1, 2, 3, 4), 9000);
367 let s = addr.to_string();
368 let parsed: MultiAddr = s.parse().unwrap();
369 assert_eq!(addr, parsed);
370 }
371
372 #[test]
373 fn test_display_roundtrip_tcp() {
374 let addr = MultiAddr::tcp(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 80));
375 let s = addr.to_string();
376 let parsed: MultiAddr = s.parse().unwrap();
377 assert_eq!(addr, parsed);
378 }
379
380 #[test]
381 fn test_bluetooth_roundtrip() {
382 let addr = MultiAddr::new(TransportAddr::Bluetooth {
383 mac: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF],
384 channel: 5,
385 });
386 let s = addr.to_string();
387 assert_eq!(s, "/bt/AA:BB:CC:DD:EE:FF/rfcomm/5");
388 let parsed: MultiAddr = s.parse().unwrap();
389 assert_eq!(addr, parsed);
390 }
391
392 #[test]
393 fn test_ble_roundtrip() {
394 let addr = MultiAddr::new(TransportAddr::Ble {
395 mac: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06],
396 psm: 128,
397 });
398 let s = addr.to_string();
399 assert_eq!(s, "/ble/01:02:03:04:05:06/l2cap/128");
400 let parsed: MultiAddr = s.parse().unwrap();
401 assert_eq!(addr, parsed);
402 }
403
404 #[test]
405 fn test_lora_roundtrip() {
406 let addr = MultiAddr::new(TransportAddr::LoRa {
407 dev_addr: [0xDE, 0xAD, 0xBE, 0xEF],
408 freq_hz: 868_000_000,
409 });
410 let s = addr.to_string();
411 assert_eq!(s, "/lora/deadbeef/868000000");
412 let parsed: MultiAddr = s.parse().unwrap();
413 assert_eq!(addr, parsed);
414 }
415
416 #[test]
417 fn test_lorawan_roundtrip() {
418 let addr = MultiAddr::new(TransportAddr::LoRaWan {
419 dev_eui: 0x0011_2233_4455_6677,
420 });
421 let s = addr.to_string();
422 assert_eq!(s, "/lorawan/0011223344556677");
423 let parsed: MultiAddr = s.parse().unwrap();
424 assert_eq!(addr, parsed);
425 }
426
427 #[test]
428 fn test_peer_id_suffix() {
429 let peer_id = PeerId::from_bytes([0xAA; 32]);
430 let addr = MultiAddr::from_ipv4(Ipv4Addr::new(1, 2, 3, 4), 9000).with_peer_id(peer_id);
431 let s = addr.to_string();
432 assert!(s.starts_with("/ip4/1.2.3.4/udp/9000/quic/p2p/"));
433 let parsed: MultiAddr = s.parse().unwrap();
434 assert_eq!(addr, parsed);
435 assert_eq!(parsed.peer_id(), Some(&peer_id));
436 }
437
438 #[test]
439 fn test_non_ip_transport_accessors() {
440 let addr = MultiAddr::new(TransportAddr::Bluetooth {
441 mac: [0; 6],
442 channel: 1,
443 });
444 assert_eq!(addr.socket_addr(), None);
445 assert_eq!(addr.ip(), None);
446 assert_eq!(addr.port(), None);
447 assert!(!addr.is_loopback());
448 assert!(!addr.is_private());
449 assert!(!addr.is_ipv4());
450 assert!(!addr.is_ipv6());
451 }
452
453 #[test]
454 fn test_serde_direct_roundtrip() {
455 let addr = MultiAddr::from_ipv4(Ipv4Addr::new(10, 0, 0, 1), 9000);
456 let json = serde_json::to_string(&addr).unwrap();
457 assert_eq!(json, r#""/ip4/10.0.0.1/udp/9000/quic""#);
458 let recovered: MultiAddr = serde_json::from_str(&json).unwrap();
459 assert_eq!(addr, recovered);
460 }
461
462 #[test]
463 fn test_transport_kind() {
464 assert_eq!(
465 TransportAddr::Quic(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)).kind(),
466 "quic"
467 );
468 assert_eq!(
469 TransportAddr::Tcp(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)).kind(),
470 "tcp"
471 );
472 assert_eq!(
473 TransportAddr::Bluetooth {
474 mac: [0; 6],
475 channel: 0
476 }
477 .kind(),
478 "bluetooth"
479 );
480 }
481
482 #[test]
483 fn test_invalid_format_rejected() {
484 assert!("127.0.0.1:8080".parse::<MultiAddr>().is_err());
486 assert!("garbage".parse::<MultiAddr>().is_err());
487 assert!("/ip4/not-an-ip/tcp/80".parse::<MultiAddr>().is_err());
488 assert!("".parse::<MultiAddr>().is_err());
489 }
490
491 #[test]
493 fn test_serde_roundtrip_with_peer_id() {
494 let peer_id = PeerId::from_bytes([0xBB; 32]);
495 let addr = MultiAddr::from_ipv4(Ipv4Addr::new(10, 0, 0, 1), 9000).with_peer_id(peer_id);
496
497 let json = serde_json::to_string(&addr).unwrap();
498 assert!(
499 json.contains("/p2p/"),
500 "serialized form must contain /p2p/ suffix"
501 );
502
503 let recovered: MultiAddr = serde_json::from_str(&json).unwrap();
504 assert_eq!(addr, recovered, "serde roundtrip must be lossless");
505 assert_eq!(recovered.peer_id(), Some(&peer_id));
506 }
507
508 #[test]
510 fn test_dialable_socket_addr_none_for_tcp() {
511 let tcp_addr = MultiAddr::tcp(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 80));
512 assert!(
513 tcp_addr.dialable_socket_addr().is_none(),
514 "TCP addresses should not be dialable (QUIC-only policy)"
515 );
516
517 let quic_addr = MultiAddr::quic(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 80));
519 assert!(quic_addr.dialable_socket_addr().is_some());
520 }
521
522 #[test]
524 fn test_standalone_peer_id_rejected() {
525 let peer_hex = "aa".repeat(32); let input = format!("/p2p/{peer_hex}");
527 let result = input.parse::<MultiAddr>();
528 assert!(
529 result.is_err(),
530 "standalone /p2p/<id> without transport must be rejected"
531 );
532 }
533
534 #[test]
536 fn test_from_transport_addr() {
537 let transport = TransportAddr::Quic(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9000));
538 let addr: MultiAddr = transport.clone().into();
539 assert_eq!(addr.transport(), &transport);
540 assert_eq!(addr.peer_id(), None);
541 }
542}