1use libp2p::{multiaddr::Protocol, Multiaddr, PeerId};
2
3pub trait MultiaddrExt {
4 fn peer_id(&self) -> Option<PeerId>;
6
7 fn extract_peer_id(&mut self) -> Option<PeerId>;
8
9 fn relay_peer_id(&self) -> Option<PeerId>;
11
12 fn address(&self) -> Multiaddr;
14
15 fn is_relay(&self) -> bool;
17
18 fn is_relayed(&self) -> bool;
20
21 fn is_loopback(&self) -> bool;
23
24 fn is_private(&self) -> bool;
26}
27
28impl MultiaddrExt for Multiaddr {
29 fn peer_id(&self) -> Option<PeerId> {
30 if let Some(Protocol::P2p(peer)) = self.iter().last() {
31 return Some(peer);
32 }
33
34 None
35 }
36
37 fn extract_peer_id(&mut self) -> Option<PeerId> {
38 self.peer_id()?;
39
40 match self.pop() {
41 Some(Protocol::P2p(peer)) => Some(peer),
42 _ => None,
43 }
44 }
45
46 fn relay_peer_id(&self) -> Option<PeerId> {
47 if !self.is_relay() {
48 return None;
49 }
50
51 while let Some(protocol) = self.iter().next() {
52 if let Protocol::P2p(peer) = protocol {
54 return Some(peer);
55 }
56 }
57
58 None
59 }
60
61 fn address(&self) -> Multiaddr {
62 let mut addr = Multiaddr::empty();
63
64 for proto in self.iter() {
65 if matches!(
66 proto,
67 Protocol::Ip4(_)
68 | Protocol::Ip6(_)
69 | Protocol::Tcp(_)
70 | Protocol::Udp(_)
71 | Protocol::Quic
72 | Protocol::QuicV1
73 | Protocol::Dnsaddr(_)
74 ) {
75 addr.push(proto);
76 }
77 }
78
79 addr
80 }
81
82 fn is_relay(&self) -> bool {
83 self.iter()
84 .any(|proto| matches!(proto, Protocol::P2pCircuit))
85 }
86
87 fn is_relayed(&self) -> bool {
88 if !self.is_relay() {
89 return false;
90 }
91
92 if self.peer_id().is_none() {
93 return false;
94 }
95
96 true
97 }
98
99 fn is_loopback(&self) -> bool {
100 self.iter().any(|proto| match proto {
101 Protocol::Ip4(ip) => ip.is_loopback(),
102 Protocol::Ip6(ip) => ip.is_loopback(),
103 _ => false,
104 })
105 }
106
107 fn is_private(&self) -> bool {
108 self.iter().any(|proto| match proto {
109 Protocol::Ip4(ip) => ip.is_private(),
110 Protocol::Ip6(ip) => {
111 (ip.segments()[0] & 0xffc0) != 0xfe80 && (ip.segments()[0] & 0xfe00) != 0xfc00
112 }
113 _ => false,
114 })
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use std::str::FromStr;
121
122 use super::*;
123
124 #[test]
125 fn connection_targets() {
126 let peer_id = PeerId::from_str("QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ")
127 .expect("Valid peer id");
128 let multiaddr_wo_peer =
129 Multiaddr::from_str("/ip4/104.131.131.82/tcp/4001").expect("Valid multiaddr");
130 let multiaddr_with_peer =
131 Multiaddr::from_str(&format!("{multiaddr_wo_peer}/p2p/{peer_id}"))
132 .expect("valid multiaddr");
133 let p2p_peer = Multiaddr::from_str(&format!("/p2p/{peer_id}")).expect("Valid multiaddr");
134
135 assert!(multiaddr_wo_peer.peer_id().is_none());
136 assert!(multiaddr_with_peer.peer_id().is_some());
137 assert!(p2p_peer.peer_id().is_some());
138
139 let peer_id_target = multiaddr_with_peer.peer_id().expect("Valid peer id");
140
141 assert_eq!(peer_id_target, peer_id);
142 }
143}