1use alloc::borrow::ToOwned;
3use alloc::format;
4use alloc::string::String;
5use borsh::{BorshDeserialize, BorshSerialize};
6use core::{
7 fmt::Display,
8 net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
9 ops::Deref,
10 str::FromStr,
11};
12use ipnet::IpNet;
13use serde::{Deserialize, Serialize};
14#[cfg(feature = "peer-id")]
15use uuid::Uuid;
16use wasm_bindgen::prelude::*;
17
18#[wasm_bindgen(typescript_custom_section)]
20const TS_IP_ADDRESS: &'static str = r#"
21 /**
22 * Generic network address representation.
23 *
24 * @category General
25 */
26 export interface INetworkAddress {
27 /**
28 * IPv4 or IPv6 address.
29 */
30 ip: string;
31 /**
32 * Optional port number.
33 */
34 port?: number;
35 }
36"#;
37
38#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
43pub struct PrefixBucket(u64);
44
45impl PrefixBucket {
46 pub fn as_u64(&self) -> u64 {
47 self.0
48 }
49}
50
51impl From<&IpAddress> for PrefixBucket {
52 fn from(ip_address: &IpAddress) -> Self {
53 match ip_address.0 {
54 IpAddr::V4(ipv4) => {
55 let prefix_bytes = ipv4.octets();
56 Self(u64::from_be_bytes([0u8, 0u8, 0u8, 0u8, 0u8, 0u8, prefix_bytes[0], prefix_bytes[1]]))
57 }
58 IpAddr::V6(ipv6) => {
59 if let Some(ipv4) = ipv6.to_ipv4() {
60 let prefix_bytes = ipv4.octets();
61 Self(u64::from_be_bytes([0u8, 0u8, 0u8, 0u8, 0u8, 0u8, prefix_bytes[0], prefix_bytes[1]]))
62 } else {
63 Self(u64::from_be_bytes(ipv6.octets().as_slice()[..8].try_into().expect("Slice with incorrect length")))
65 }
66 }
67 }
68 }
69}
70
71impl From<&NetAddress> for PrefixBucket {
72 fn from(net_address: &NetAddress) -> Self {
73 Self::from(&net_address.ip)
74 }
75}
76
77#[derive(PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize, Debug)]
79#[repr(transparent)]
80pub struct IpAddress(pub IpAddr);
81
82impl IpAddress {
83 pub fn new(ip: IpAddr) -> Self {
84 Self(ip)
85 }
86
87 pub fn is_publicly_routable(&self) -> bool {
88 if self.is_loopback() || self.is_unspecified() {
89 return false;
90 }
91
92 match self.0 {
93 IpAddr::V4(ip) => {
94 if ip.is_broadcast() || ip.is_private() || ip.is_documentation() || ip.is_link_local() {
99 return false;
100 }
101 }
102 IpAddr::V6(_ip) => {
103 }
105 }
106
107 let unroutable_nets = [
109 "198.18.0.0/15", "2001:DB8::/32", "2002::/16", "FC00::/7", "2001::/32", "2001:10::/28", "FE80::/64", "64:FF9B::/96", "::FFFF:0:0:0/96", "100.64.0.0/10", "0.0.0.0/8", "2001:470::/32", ];
122
123 for curr_net in unroutable_nets {
124 if IpNet::from_str(curr_net).unwrap().contains(&self.0) {
125 return false;
126 }
127 }
128
129 true
130 }
131
132 pub fn prefix_bucket(&self) -> PrefixBucket {
133 PrefixBucket::from(self)
134 }
135}
136
137impl From<IpAddr> for IpAddress {
138 fn from(ip: IpAddr) -> Self {
139 Self(ip)
140 }
141}
142impl From<Ipv4Addr> for IpAddress {
143 fn from(value: Ipv4Addr) -> Self {
144 Self(value.into())
145 }
146}
147impl From<Ipv6Addr> for IpAddress {
148 fn from(value: Ipv6Addr) -> Self {
149 Self(value.into())
150 }
151}
152impl From<IpAddress> for IpAddr {
153 fn from(value: IpAddress) -> Self {
154 value.0
155 }
156}
157
158impl FromStr for IpAddress {
159 type Err = AddrParseError;
160
161 fn from_str(s: &str) -> Result<Self, Self::Err> {
162 IpAddr::from_str(s).map(IpAddress::from)
163 }
164}
165
166impl Display for IpAddress {
167 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
168 self.0.fmt(f)
169 }
170}
171
172impl Deref for IpAddress {
173 type Target = IpAddr;
174
175 fn deref(&self) -> &Self::Target {
176 &self.0
177 }
178}
179
180impl BorshSerialize for IpAddress {
186 fn serialize<W: borsh::io::Write>(&self, writer: &mut W) -> ::core::result::Result<(), borsh::io::Error> {
187 let variant_idx: u8 = match self.0 {
188 IpAddr::V4(..) => 0u8,
189 IpAddr::V6(..) => 1u8,
190 };
191 writer.write_all(&variant_idx.to_le_bytes())?;
192 match self.0 {
193 IpAddr::V4(id0) => {
194 borsh::BorshSerialize::serialize(&id0.octets(), writer)?;
195 }
196 IpAddr::V6(id0) => {
197 borsh::BorshSerialize::serialize(&id0.octets(), writer)?;
198 }
199 }
200 Ok(())
201 }
202}
203
204impl BorshDeserialize for IpAddress {
205 fn deserialize_reader<R: borsh::io::Read>(reader: &mut R) -> ::core::result::Result<Self, borsh::io::Error> {
206 let variant_idx: u8 = BorshDeserialize::deserialize_reader(reader)?;
207 let ip = match variant_idx {
208 0u8 => {
209 let octets: [u8; 4] = BorshDeserialize::deserialize_reader(reader)?;
210 IpAddr::V4(Ipv4Addr::from(octets))
211 }
212 1u8 => {
213 let octets: [u8; 16] = BorshDeserialize::deserialize_reader(reader)?;
214 IpAddr::V6(Ipv6Addr::from(octets))
215 }
216 _ => {
217 let msg = format!("Unexpected variant index: {:?}", variant_idx);
218 return Err(borsh::io::Error::new(borsh::io::ErrorKind::InvalidInput, msg));
219 }
220 };
221 Ok(Self(ip))
222 }
223}
224
225#[derive(PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize, Debug, BorshSerialize, BorshDeserialize)]
227pub struct NetAddress {
228 pub ip: IpAddress,
229 pub port: u16,
230}
231
232impl NetAddress {
233 pub fn new(ip: IpAddress, port: u16) -> Self {
234 Self { ip, port }
235 }
236
237 pub fn prefix_bucket(&self) -> PrefixBucket {
238 PrefixBucket::from(self)
239 }
240}
241
242impl From<SocketAddr> for NetAddress {
243 fn from(value: SocketAddr) -> Self {
244 Self::new(value.ip().into(), value.port())
245 }
246}
247
248impl From<NetAddress> for SocketAddr {
249 fn from(value: NetAddress) -> Self {
250 Self::new(value.ip.0, value.port)
251 }
252}
253
254impl FromStr for NetAddress {
255 type Err = AddrParseError;
256
257 fn from_str(s: &str) -> Result<Self, Self::Err> {
258 SocketAddr::from_str(s).map(NetAddress::from)
259 }
260}
261
262impl Display for NetAddress {
263 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
264 SocketAddr::from(self.to_owned()).fmt(f)
265 }
266}
267
268#[derive(PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize, Debug, BorshSerialize, BorshDeserialize)]
272pub struct ContextualNetAddress {
273 ip: IpAddress,
274 port: Option<u16>,
275}
276
277impl ContextualNetAddress {
278 pub fn new(ip: IpAddress, port: Option<u16>) -> Self {
279 Self { ip, port }
280 }
281
282 pub fn has_port(&self) -> bool {
283 self.port.is_some()
284 }
285
286 pub fn normalize(&self, default_port: u16) -> NetAddress {
287 NetAddress::new(self.ip, self.port.unwrap_or(default_port))
288 }
289
290 pub fn unspecified() -> Self {
291 Self { ip: IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)).into(), port: None }
292 }
293
294 pub fn loopback() -> Self {
295 Self { ip: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).into(), port: None }
296 }
297
298 pub fn port_not_specified(&self) -> bool {
299 self.port.is_none()
300 }
301
302 pub fn with_port(&self, port: u16) -> Self {
303 Self { ip: self.ip, port: Some(port) }
304 }
305}
306
307impl From<NetAddress> for ContextualNetAddress {
308 fn from(value: NetAddress) -> Self {
309 Self::new(value.ip, Some(value.port))
310 }
311}
312
313impl FromStr for ContextualNetAddress {
314 type Err = AddrParseError;
315
316 fn from_str(s: &str) -> Result<Self, Self::Err> {
317 match SocketAddr::from_str(s) {
318 Ok(socket) => Ok(Self::new(socket.ip().into(), Some(socket.port()))),
319 Err(_) => Ok(Self::new(IpAddress::from_str(s)?, None)),
320 }
321 }
322}
323
324impl TryFrom<&str> for ContextualNetAddress {
325 type Error = AddrParseError;
326
327 fn try_from(s: &str) -> Result<Self, Self::Error> {
328 ContextualNetAddress::from_str(s)
329 }
330}
331
332impl TryFrom<String> for ContextualNetAddress {
333 type Error = AddrParseError;
334
335 fn try_from(s: String) -> Result<Self, Self::Error> {
336 ContextualNetAddress::from_str(&s)
337 }
338}
339
340impl Display for ContextualNetAddress {
341 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
342 match self.port {
343 Some(port) => SocketAddr::new(self.ip.into(), port).fmt(f),
344 None => self.ip.fmt(f),
345 }
346 }
347}
348
349#[cfg(feature = "peer-id")]
350#[derive(PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize, Debug, Default)]
351#[repr(transparent)]
352pub struct PeerId(pub Uuid);
353
354#[cfg(feature = "peer-id")]
355impl PeerId {
356 pub fn new(id: Uuid) -> Self {
357 Self(id)
358 }
359
360 pub fn from_slice(bytes: &[u8]) -> Result<Self, uuid::Error> {
361 Ok(Uuid::from_slice(bytes)?.into())
362 }
363}
364
365#[cfg(feature = "peer-id")]
366impl From<Uuid> for PeerId {
367 fn from(id: Uuid) -> Self {
368 Self(id)
369 }
370}
371
372#[cfg(feature = "peer-id")]
373impl From<PeerId> for Uuid {
374 fn from(value: PeerId) -> Self {
375 value.0
376 }
377}
378
379#[cfg(feature = "peer-id")]
380impl FromStr for PeerId {
381 type Err = uuid::Error;
382
383 fn from_str(s: &str) -> Result<Self, Self::Err> {
384 Uuid::from_str(s).map(PeerId::from)
385 }
386}
387
388#[cfg(feature = "peer-id")]
389impl Display for PeerId {
390 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
391 self.0.fmt(f)
392 }
393}
394
395#[cfg(feature = "peer-id")]
396impl Deref for PeerId {
397 type Target = Uuid;
398
399 fn deref(&self) -> &Self::Target {
400 &self.0
401 }
402}
403
404#[cfg(feature = "peer-id")]
409impl BorshSerialize for PeerId {
410 fn serialize<W: borsh::io::Write>(&self, writer: &mut W) -> ::core::result::Result<(), borsh::io::Error> {
411 borsh::BorshSerialize::serialize(&self.0.as_bytes(), writer)?;
412 Ok(())
413 }
414}
415
416#[cfg(feature = "peer-id")]
417impl BorshDeserialize for PeerId {
418 fn deserialize_reader<R: borsh::io::Read>(reader: &mut R) -> ::core::result::Result<Self, borsh::io::Error> {
419 let bytes: uuid::Bytes = BorshDeserialize::deserialize_reader(reader)?;
420 Ok(Self::new(Uuid::from_bytes(bytes)))
421 }
422}
423
424#[cfg(test)]
425mod tests {
426 use super::*;
427 use std::str::FromStr;
428
429 #[test]
430 fn test_ip_address_borsh() {
431 let ip: IpAddress = Ipv4Addr::from([44u8; 4]).into();
433 let bin = borsh::to_vec(&ip).unwrap();
434 let ip2: IpAddress = BorshDeserialize::try_from_slice(&bin).unwrap();
435 assert_eq!(ip, ip2);
436
437 let ip: IpAddress = Ipv6Addr::from([66u8; 16]).into();
438 let bin = borsh::to_vec(&ip).unwrap();
439 let ip2: IpAddress = BorshDeserialize::try_from_slice(&bin).unwrap();
440 assert_eq!(ip, ip2);
441 }
442
443 #[test]
444 fn test_peer_id_borsh() {
445 let id: PeerId = Uuid::new_v4().into();
447 let bin = borsh::to_vec(&id).unwrap();
448 let id2: PeerId = BorshDeserialize::try_from_slice(&bin).unwrap();
449 assert_eq!(id, id2);
450
451 let id: PeerId = Uuid::from_bytes([123u8; 16]).into();
452 let bin = borsh::to_vec(&id).unwrap();
453 let id2: PeerId = BorshDeserialize::try_from_slice(&bin).unwrap();
454 assert_eq!(id, id2);
455 }
456
457 #[test]
458 fn test_net_address_from_str() {
459 let addr_v4 = NetAddress::from_str("1.2.3.4:5678");
460 assert!(addr_v4.is_ok());
461 let addr_v6 = NetAddress::from_str("[2a01:4f8:191:1143::2]:5678");
462 assert!(addr_v6.is_ok());
463 }
464
465 #[test]
466 fn test_prefix_bucket() {
467 let prefix_bytes: [u8; 2] = [42u8, 43u8];
468 let addr = NetAddress::from_str(format!("{0}.{1}.3.4:5678", prefix_bytes[0], prefix_bytes[1]).as_str()).unwrap();
469 assert!(addr.prefix_bucket() == PrefixBucket(u16::from_be_bytes(prefix_bytes) as u64));
470 }
471
472 #[test]
473 fn test_contextual_address_ser() {
474 let addr = IpAddress::from_str("127.0.0.1").unwrap();
475 let port = Some(1234);
476 let net_addr = ContextualNetAddress::new(addr, port);
477 let s = serde_json::to_string(&net_addr).unwrap();
478 assert_eq!(s, r#"{"ip":"127.0.0.1","port":1234}"#);
479 }
480
481 #[test]
482 fn test_is_publicly_routable() {
483 assert!(!IpAddress::from_str("198.18.0.0").unwrap().is_publicly_routable());
485 assert!(!IpAddress::from_str("198.19.255.255").unwrap().is_publicly_routable());
486 assert!(IpAddress::from_str("198.17.255.255").unwrap().is_publicly_routable());
487 assert!(IpAddress::from_str("198.20.0.0").unwrap().is_publicly_routable());
488
489 assert!(!IpAddress::from_str("0.0.0.0").unwrap().is_publicly_routable());
491 assert!(!IpAddress::from_str("0.0.0.1").unwrap().is_publicly_routable());
492 assert!(!IpAddress::from_str("0.0.1.0").unwrap().is_publicly_routable());
493 assert!(!IpAddress::from_str("0.1.0.0").unwrap().is_publicly_routable());
494
495 assert!(!IpAddress::from_str("2001:db8::").unwrap().is_publicly_routable());
497 assert!(!IpAddress::from_str("2001:db8:ffff:ffff:ffff:ffff:ffff:ffff").unwrap().is_publicly_routable());
498 assert!(IpAddress::from_str("2001:db7:ffff:ffff:ffff:ffff:ffff:ffff").unwrap().is_publicly_routable());
499 assert!(IpAddress::from_str("2001:db9::").unwrap().is_publicly_routable());
500
501 assert!(!IpAddress::from_str("127.0.0.1").unwrap().is_publicly_routable());
503
504 assert!(IpAddress::from_str("123.45.67.89").unwrap().is_publicly_routable());
506
507 assert!(!IpAddress::from_str("10.0.0.0").unwrap().is_publicly_routable());
509 assert!(!IpAddress::from_str("10.255.255.255").unwrap().is_publicly_routable());
510 assert!(IpAddress::from_str("9.255.255.255").unwrap().is_publicly_routable());
511 assert!(IpAddress::from_str("11.0.0.0").unwrap().is_publicly_routable());
512
513 assert!(!IpAddress::from_str("172.16.0.0").unwrap().is_publicly_routable());
514 assert!(!IpAddress::from_str("172.31.255.255").unwrap().is_publicly_routable());
515 assert!(IpAddress::from_str("172.15.255.255").unwrap().is_publicly_routable());
516 assert!(IpAddress::from_str("172.32.0.0").unwrap().is_publicly_routable());
517
518 assert!(!IpAddress::from_str("192.168.0.0").unwrap().is_publicly_routable());
519 assert!(!IpAddress::from_str("192.168.255.255").unwrap().is_publicly_routable());
520 assert!(IpAddress::from_str("192.167.255.255").unwrap().is_publicly_routable());
521 assert!(IpAddress::from_str("192.169.0.0").unwrap().is_publicly_routable());
522
523 assert!(!IpAddress::from_str("169.254.0.0").unwrap().is_publicly_routable());
525 assert!(!IpAddress::from_str("169.254.255.255").unwrap().is_publicly_routable());
526 assert!(IpAddress::from_str("169.253.255.255").unwrap().is_publicly_routable());
527 assert!(IpAddress::from_str("169.255.0.0").unwrap().is_publicly_routable());
528
529 assert!(!IpAddress::from_str("2002::").unwrap().is_publicly_routable());
531 assert!(!IpAddress::from_str("2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap().is_publicly_routable());
532 assert!(IpAddress::from_str("2001:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap().is_publicly_routable());
533 assert!(IpAddress::from_str("2003::").unwrap().is_publicly_routable());
534
535 assert!(!IpAddress::from_str("fc00::").unwrap().is_publicly_routable());
537 assert!(!IpAddress::from_str("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap().is_publicly_routable());
538 assert!(IpAddress::from_str("fb00:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap().is_publicly_routable());
539 assert!(IpAddress::from_str("fe00::").unwrap().is_publicly_routable());
540
541 assert!(!IpAddress::from_str("2001::").unwrap().is_publicly_routable());
543 assert!(!IpAddress::from_str("2001:0:ffff:ffff:ffff:ffff:ffff:ffff").unwrap().is_publicly_routable());
544 assert!(IpAddress::from_str("2000:0:ffff:ffff:ffff:ffff:ffff:ffff").unwrap().is_publicly_routable());
545 assert!(IpAddress::from_str("2001:1::").unwrap().is_publicly_routable());
546
547 assert!(!IpAddress::from_str("2001:10::").unwrap().is_publicly_routable());
549 assert!(!IpAddress::from_str("2001:1f:ffff:ffff:ffff:ffff:ffff:ffff").unwrap().is_publicly_routable());
550 assert!(IpAddress::from_str("2001:f:ffff:ffff:ffff:ffff:ffff:ffff").unwrap().is_publicly_routable());
551 assert!(IpAddress::from_str("2001:20::").unwrap().is_publicly_routable());
552
553 assert!(!IpAddress::from_str("fe80::").unwrap().is_publicly_routable());
555 assert!(!IpAddress::from_str("fe80::ffff:ffff:ffff:ffff").unwrap().is_publicly_routable());
556 assert!(IpAddress::from_str("fe7f::ffff:ffff:ffff:ffff").unwrap().is_publicly_routable());
557 assert!(IpAddress::from_str("fe81::").unwrap().is_publicly_routable());
558
559 assert!(!IpAddress::from_str("192.0.2.0").unwrap().is_publicly_routable());
561 assert!(!IpAddress::from_str("192.0.2.255").unwrap().is_publicly_routable());
562 assert!(IpAddress::from_str("192.0.1.255").unwrap().is_publicly_routable());
563 assert!(IpAddress::from_str("192.0.3.0").unwrap().is_publicly_routable());
564
565 assert!(!IpAddress::from_str("198.51.100.0").unwrap().is_publicly_routable());
566 assert!(!IpAddress::from_str("198.51.100.255").unwrap().is_publicly_routable());
567 assert!(IpAddress::from_str("198.51.99.255").unwrap().is_publicly_routable());
568 assert!(IpAddress::from_str("198.51.101.0").unwrap().is_publicly_routable());
569
570 assert!(!IpAddress::from_str("203.0.113.0").unwrap().is_publicly_routable());
571 assert!(!IpAddress::from_str("203.0.113.255").unwrap().is_publicly_routable());
572 assert!(IpAddress::from_str("203.0.112.255").unwrap().is_publicly_routable());
573 assert!(IpAddress::from_str("203.0.114.0").unwrap().is_publicly_routable());
574
575 assert!(!IpAddress::from_str("64:ff9b::").unwrap().is_publicly_routable());
577 assert!(!IpAddress::from_str("64:ff9b::ffff:ffff").unwrap().is_publicly_routable());
578 assert!(IpAddress::from_str("64:ff9a::ffff:ffff").unwrap().is_publicly_routable());
579 assert!(IpAddress::from_str("64:ff9b:1::").unwrap().is_publicly_routable());
580
581 assert!(!IpAddress::from_str("::ffff:0:0:0").unwrap().is_publicly_routable());
583 assert!(!IpAddress::from_str("::ffff:0:ffff:ffff").unwrap().is_publicly_routable());
584 assert!(IpAddress::from_str("::fffe:0:ffff:ffff").unwrap().is_publicly_routable());
585 assert!(IpAddress::from_str("::ffff:1:0:0").unwrap().is_publicly_routable());
586
587 assert!(!IpAddress::from_str("100.64.0.0").unwrap().is_publicly_routable());
589 assert!(!IpAddress::from_str("100.127.255.255").unwrap().is_publicly_routable());
590 assert!(IpAddress::from_str("100.63.255.255").unwrap().is_publicly_routable());
591 assert!(IpAddress::from_str("100.128.0.0").unwrap().is_publicly_routable());
592
593 assert!(!IpAddress::from_str("2001:470::").unwrap().is_publicly_routable());
595 assert!(!IpAddress::from_str("2001:470:ffff:ffff:ffff:ffff:ffff:ffff").unwrap().is_publicly_routable());
596 assert!(IpAddress::from_str("2001:46f:ffff:ffff:ffff:ffff:ffff:ffff").unwrap().is_publicly_routable());
597 assert!(IpAddress::from_str("2001:471::").unwrap().is_publicly_routable());
598
599 assert!(!IpAddress::from_str("255.255.255.255").unwrap().is_publicly_routable());
601 }
602}