From fab04b11bf7b85738b1b1fb48c98a2b8a5fb2ae8 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Thu, 13 Apr 2023 12:56:08 +0200 Subject: [PATCH 01/17] upstream GlobalIpOnly --- core/src/transport.rs | 1 + core/src/transport/global_only.rs | 100 ++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 core/src/transport/global_only.rs diff --git a/core/src/transport.rs b/core/src/transport.rs index 04196efca13..ca1796b3b35 100644 --- a/core/src/transport.rs +++ b/core/src/transport.rs @@ -37,6 +37,7 @@ use std::{ pub mod and_then; pub mod choice; pub mod dummy; +pub mod global_only; pub mod map; pub mod map_err; pub mod memory; diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs new file mode 100644 index 00000000000..48d2270eafc --- /dev/null +++ b/core/src/transport/global_only.rs @@ -0,0 +1,100 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use crate::{ + multiaddr::{Multiaddr, Protocol}, + transport::{ListenerId, TransportError, TransportEvent}, + Transport, +}; +use log::debug; +use std::{ + pin::Pin, + task::{Context, Poll}, +}; + +/// Dropping all dial requests to non-global IP addresses. +#[derive(Debug, Clone, Default)] +pub struct GlobalIpOnly { + inner: T, +} + +impl GlobalIpOnly { + pub fn new(transport: T) -> Self { + GlobalIpOnly { inner: transport } + } +} + +impl Transport for GlobalIpOnly { + type Output = ::Output; + type Error = ::Error; + type ListenerUpgrade = ::ListenerUpgrade; + type Dial = ::Dial; + + fn listen_on(&mut self, addr: Multiaddr) -> Result> { + self.inner.listen_on(addr) + } + + fn remove_listener(&mut self, id: ListenerId) -> bool { + self.inner.remove_listener(id) + } + + fn dial(&mut self, addr: Multiaddr) -> Result> { + match addr.iter().next() { + Some(Protocol::Ip4(a)) => { + if a.is_global() { + self.inner.dial(addr) + } else { + debug!("Not dialing non global IP address {:?}.", a); + Err(TransportError::MultiaddrNotSupported(addr)) + } + } + Some(Protocol::Ip6(a)) => { + if a.is_global() { + self.inner.dial(addr) + } else { + debug!("Not dialing non global IP address {:?}.", a); + Err(TransportError::MultiaddrNotSupported(addr)) + } + } + _ => { + debug!("Not dialing unsupported Multiaddress {:?}.", addr); + Err(TransportError::MultiaddrNotSupported(addr)) + } + } + } + + fn dial_as_listener( + &mut self, + addr: Multiaddr, + ) -> Result> { + self.inner.dial_as_listener(addr) + } + + fn address_translation(&self, listen: &Multiaddr, observed: &Multiaddr) -> Option { + self.inner.address_translation(listen, observed) + } + + fn poll( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + Pin::new(&mut self.inner).poll(cx) + } +} From 0521653a468b212451ebb08f372ec92c2175d327 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Sun, 23 Apr 2023 18:12:07 +0200 Subject: [PATCH 02/17] add is_global function and utils for ipv4 in GlobalOnly --- core/src/transport/global_only.rs | 140 +++++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 1 deletion(-) diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index 48d2270eafc..c79ac6e477c 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -25,6 +25,7 @@ use crate::{ }; use log::debug; use std::{ + net::Ipv4Addr, pin::Pin, task::{Context, Poll}, }; @@ -35,6 +36,143 @@ pub struct GlobalIpOnly { inner: T, } +pub struct Ipv4Global; + +impl Ipv4Global { + /// Returns [`true`] if this address part of the `0.0.0.0/8` range. + pub const fn is_this_network(a: Ipv4Addr) -> bool { + a.octets()[0] == 0 + } + + /// Returns [`true`] if this address part of the `0.0.0.0/32` range. + pub const fn is_this_host_on_this_network(a: Ipv4Addr) -> bool { + a.octets()[0] == 0 && a.octets()[1] == 0 && a.octets()[2] == 0 && a.octets()[3] == 0 + } + + /// Returns [`true`] if this address is part of the Shared Address Space defined in + /// [IETF RFC 6598] (`100.64.0.0/10`). + /// + /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 + pub const fn is_shared(a: Ipv4Addr) -> bool { + a.octets()[0] == 100 && (a.octets()[1] & 0b1100_0000 == 0b0100_0000) + } + + /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] + /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the + /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since + /// it is obviously not reserved for future use. + /// + /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 + pub const fn is_reserved(a: Ipv4Addr) -> bool { + a.octets()[0] & 240 == 240 && !a.is_broadcast() + } + + /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for + /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` + /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. + /// + /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 + /// [errata 423]: https://www.rfc-editor.org/errata/eid423 + pub const fn is_benchmarking(a: Ipv4Addr) -> bool { + a.octets()[0] == 198 && (a.octets()[1] & 0xfe) == 18 + } + + /// Returns [`true`] if this address part of the `192.0.0.0/29` range. + pub const fn is_ipv4_service_continuity_prefix(a: Ipv4Addr) -> bool { + a.octets()[0] == 192 + && a.octets()[1] == 0 + && a.octets()[2] == 0 + && (a.octets()[3] & 0b1111_1000 == 0) + } + + /// Returns [`true`] if this address part of the `192.0.0.0/24` range. + pub const fn is_ietf_protocol_assignments(a: Ipv4Addr) -> bool { + a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0 + } + + /// Returns [`true`] if this address part of the `192.0.0.8/32` range. + pub const fn is_ipv4_dummy_address(a: Ipv4Addr) -> bool { + a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0 && a.octets()[3] == 8 + } + + /// Returns [`true`] if this address part of the `192.0.0.9/32` range. + pub const fn is_port_control_protocol_anycast(a: Ipv4Addr) -> bool { + a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0 && a.octets()[3] == 9 + } + + /// Returns [`true`] if this address part of the `192.0.0.10/32` range. + pub const fn is_traversal_using_relays_around_nat_anycast(a: Ipv4Addr) -> bool { + a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0 && a.octets()[3] == 10 + } + + /// Returns [`true`] if this is NAT64/DNS64 Discovery. This includes: + /// + /// - `192.0.0.170/32` + /// - `192.0.0.171/32` + pub const fn is_nat64_dns64_discovery(a: Ipv4Addr) -> bool { + a.octets()[0] == 192 + && a.octets()[1] == 0 + && a.octets()[2] == 0 + && (a.octets()[3] == 170 || a.octets()[3] == 171) + } + + /// Returns [`true`] if this address part of the `192.31.196.0/24` range. + pub const fn is_as112_v4(a: Ipv4Addr) -> bool { + a.octets()[0] == 192 && a.octets()[1] == 31 && a.octets()[1] == 196 + } + + /// Returns [`true`] if this address part of the `192.52.193.0/24` range. + pub const fn is_amt(a: Ipv4Addr) -> bool { + a.octets()[0] == 192 && a.octets()[1] == 52 && a.octets()[2] == 193 + } + + /// Returns [`true`] if this address part of the `192.88.99.0/24` range. + pub const fn is_deprecated_6to6_relay_anycast(a: Ipv4Addr) -> bool { + a.octets()[0] == 192 && a.octets()[1] == 88 && a.octets()[2] == 99 + } + + /// Returns [`true`] if this address part of the `192.175.48.0/24` range. + pub const fn is_direct_delegation_as112_service(a: Ipv4Addr) -> bool { + a.octets()[0] == 192 && a.octets()[1] == 175 && a.octets()[2] == 48 + } + + /// The function checks if an IPv4 address is a global address by verifying that it does not belong + /// to any of the reserved or special use address ranges. + /// + /// Arguments: + /// + /// * `a`: `a` is an `Ipv4Addr` type variable representing an IPv4 address. The function + /// `is_global` checks whether the given IPv4 address is a global IP address or not by checking + /// against various criteria such as private IP address ranges, reserved IP address ranges, and + /// special use + /// + /// Returns: + /// + /// A boolean value indicating whether the given IPv4 address is a global address or not. + pub fn is_global(a: Ipv4Addr) -> bool { + !(Self::is_this_network(a) + || Self::is_this_host_on_this_network(a) + || a.is_private() + || Self::is_shared(a) + || a.is_loopback() + || a.is_link_local() + || Self::is_ietf_protocol_assignments(a) + || Self::is_ipv4_service_continuity_prefix(a) + || Self::is_ipv4_dummy_address(a) + || Self::is_port_control_protocol_anycast(a) + || Self::is_traversal_using_relays_around_nat_anycast(a) + || Self::is_nat64_dns64_discovery(a) + || a.is_documentation() + || Self::is_as112_v4(a) + || Self::is_amt(a) + || Self::is_deprecated_6to6_relay_anycast(a) + || Self::is_direct_delegation_as112_service(a)) + || Self::is_benchmarking(a) + || Self::is_reserved(a) + || a.is_broadcast() + } +} + impl GlobalIpOnly { pub fn new(transport: T) -> Self { GlobalIpOnly { inner: transport } @@ -58,7 +196,7 @@ impl Transport for GlobalIpOnly { fn dial(&mut self, addr: Multiaddr) -> Result> { match addr.iter().next() { Some(Protocol::Ip4(a)) => { - if a.is_global() { + if Ipv4Global::is_global(a) { self.inner.dial(addr) } else { debug!("Not dialing non global IP address {:?}.", a); From 5ab87b471b030ec534fe6956f841e03caafd124e Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Tue, 25 Apr 2023 00:07:04 +0200 Subject: [PATCH 03/17] add is_global for ipv6 --- core/src/transport/global_only.rs | 121 +++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 2 deletions(-) diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index c79ac6e477c..174242fce25 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -25,7 +25,7 @@ use crate::{ }; use log::debug; use std::{ - net::Ipv4Addr, + net::{Ipv4Addr, Ipv6Addr}, pin::Pin, task::{Context, Poll}, }; @@ -37,6 +37,7 @@ pub struct GlobalIpOnly { } pub struct Ipv4Global; +pub struct Ipv6Global; impl Ipv4Global { /// Returns [`true`] if this address part of the `0.0.0.0/8` range. @@ -173,6 +174,122 @@ impl Ipv4Global { } } +impl Ipv6Global { + /// Returns [`true`] if this address is `::ffff:0:0/96`. + pub const fn is_ipv4_mapped_address(a: Ipv6Addr) -> bool { + matches!(a.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) + } + + /// Returns [`true`] if this address is `64:ff9b::/96` or `64:ff9b:1::/48`. + pub const fn is_ipv4_ipv6_translat(a: Ipv6Addr) -> bool { + matches!(a.segments(), [0x64, 0xff9b, 0, 0, 0, 0, _, _]) + || matches!(a.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) + } + + /// Returns [`true`] if this address is `100::/64`. + pub const fn is_discard_only_address_block(a: Ipv6Addr) -> bool { + matches!(a.segments(), [0x100, 0, 0, 0, _, _, _, _]) + } + + /// Returns [`true`] if this address is `2001::/23`. + pub const fn is_ietf_protocol_assignments(a: Ipv6Addr) -> bool { + matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b <= 0x1FF) + } + + /// Returns [`true`] if this address is `2001::/32`. + pub const fn is_teredo(a: Ipv6Addr) -> bool { + matches!(a.segments(), [0x2001, 0, _, _, _, _, _, _]) + } + + /// Returns [`true`] if this address is `2001:1::1/128`. + pub const fn is_port_control_protocol_anycast(a: Ipv6Addr) -> bool { + u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 + } + + /// Returns [`true`] if this address is `2001:1::2/128`. + pub const fn is_traversal_relays_nat_anycast(a: Ipv6Addr) -> bool { + u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 + } + + /// Returns [`true`] if this address is `2001:2::/48`. + pub const fn is_benchmarking(a: Ipv6Addr) -> bool { + matches!(a.segments(), [0x2001, 2, 0, _, _, _, _, _]) + } + + /// Returns [`true`] if this address is `2001:3::/32`. + pub const fn is_amt(a: Ipv6Addr) -> bool { + matches!(a.segments(), [0x2001, 3, _, _, _, _, _, _]) + } + + /// Returns [`true`] if this address is `2001:4:112::/48`. + pub const fn is_as112_v6(a: Ipv6Addr) -> bool { + matches!(a.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) + } + + /// Returns [`true`] if this address is `2001:10::/28`. + pub const fn is_deprecated_previously_orchid(a: Ipv6Addr) -> bool { + matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x10 && b <= 0x1F) + } + + /// Returns [`true`] if this address is `2001:20::/28`. + pub const fn is_orchid_v2(a: Ipv6Addr) -> bool { + matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) + } + + /// Returns [`true`] if this address is `2001:30::/28`. + pub const fn is_drone_remote(a: Ipv6Addr) -> bool { + matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x30 && b <= 0x3F) + } + + /// Returns [`true`] if this address is `2001:db8::/32`. + pub const fn is_documentation(a: Ipv6Addr) -> bool { + matches!(a.segments(), [0x2001, 0xdb8, _, _, _, _, _, _]) + } + + /// Returns [`true`] if this address is `2002::/16`. + pub const fn is_6to4(a: Ipv6Addr) -> bool { + matches!(a.segments(), [0x2002, _, _, _, _, _, _, _]) + } + + /// Returns [`true`] if this address is `2620:4f:8000::/48`. + pub const fn is_direct_delegation_as112_service(a: Ipv6Addr) -> bool { + matches!(a.segments(), [0x2620, 0x4f, 0x8000, _, _, _, _, _]) + } + + /// Returns [`true`] if this address is `fc00::/7`. + pub const fn is_unique_local(a: Ipv6Addr) -> bool { + (a.segments()[0] & 0xfe00) == 0xfc00 + } + + /// Returns [`true`] if this address is `fe80::/10`. + pub const fn is_unicast_link_local(a: Ipv6Addr) -> bool { + (a.segments()[0] & 0xffc0) == 0xfe80 + } + + pub fn is_global(a: Ipv6Addr) -> bool { + !(a.is_loopback() + || a.is_unspecified() + || Self::is_ipv4_mapped_address(a) + || Self::is_ipv4_ipv6_translat(a) + || Self::is_discard_only_address_block(a) + || Self::is_ietf_protocol_assignments(a) + || Self::is_teredo(a) + || Self::is_port_control_protocol_anycast(a) + || Self::is_traversal_relays_nat_anycast(a) + || Self::is_benchmarking(a) + || Self::is_amt(a) + || Self::is_as112_v6(a) + || Self::is_deprecated_previously_orchid(a) + || Self::is_orchid_v2(a) + || Self::is_drone_remote(a) + || Self::is_documentation(a) + || Self::is_6to4(a) + || Self::is_direct_delegation_as112_service(a) + || Self::is_unique_local(a) + || Self::is_unicast_link_local(a)) + } +} + impl GlobalIpOnly { pub fn new(transport: T) -> Self { GlobalIpOnly { inner: transport } @@ -204,7 +321,7 @@ impl Transport for GlobalIpOnly { } } Some(Protocol::Ip6(a)) => { - if a.is_global() { + if Ipv6Global::is_global(a) { self.inner.dial(addr) } else { debug!("Not dialing non global IP address {:?}.", a); From d46b9e32573592cdfa456048db7901c7bf741848 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Tue, 25 Apr 2023 09:22:05 +0200 Subject: [PATCH 04/17] add changelog --- core/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 99dc371b595..25886f4ea70 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -4,7 +4,10 @@ If you depend on it, we suggest you vendor it. See [PR 3747]. +- Add `GlobalOnly` `Transport` implementation. See [PR 3814]. + [PR 3747]: https://github.com/libp2p/rust-libp2p/pull/3747 +[PR 3814]: https://github.com/libp2p/rust-libp2p/pull/3814 ## 0.39.1 From 6560de181b04459932a058ce0b10c744a8275049 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Tue, 25 Apr 2023 20:03:56 +0200 Subject: [PATCH 05/17] Update core/src/transport/global_only.rs Co-authored-by: Thomas Eizinger --- core/src/transport/global_only.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index 174242fce25..3aa7d99146a 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -1,4 +1,4 @@ -// Copyright 2019 Parity Technologies (UK) Ltd. +// Copyright 2023 Protocol Labs // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), From 6eaa8895f80c0ee7f37f0bcbed678a85bd302911 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Tue, 25 Apr 2023 23:16:56 +0200 Subject: [PATCH 06/17] Update core/src/transport/global_only.rs Co-authored-by: Thomas Eizinger --- core/src/transport/global_only.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index 3aa7d99146a..6aed30b1540 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -32,7 +32,7 @@ use std::{ /// Dropping all dial requests to non-global IP addresses. #[derive(Debug, Clone, Default)] -pub struct GlobalIpOnly { +pub struct Transport { inner: T, } From 16db216769f10d89f021292dfc66db1eea900e65 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Tue, 25 Apr 2023 23:25:23 +0200 Subject: [PATCH 07/17] pub const functions ipv4 and ipv6 to const functions --- core/src/transport/global_only.rs | 68 +++++++++++++++---------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index 6aed30b1540..e8bb2ea675a 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -32,7 +32,7 @@ use std::{ /// Dropping all dial requests to non-global IP addresses. #[derive(Debug, Clone, Default)] -pub struct Transport { +pub struct GlobalIpOnly { inner: T, } @@ -41,12 +41,12 @@ pub struct Ipv6Global; impl Ipv4Global { /// Returns [`true`] if this address part of the `0.0.0.0/8` range. - pub const fn is_this_network(a: Ipv4Addr) -> bool { + const fn is_this_network(a: Ipv4Addr) -> bool { a.octets()[0] == 0 } /// Returns [`true`] if this address part of the `0.0.0.0/32` range. - pub const fn is_this_host_on_this_network(a: Ipv4Addr) -> bool { + const fn is_this_host_on_this_network(a: Ipv4Addr) -> bool { a.octets()[0] == 0 && a.octets()[1] == 0 && a.octets()[2] == 0 && a.octets()[3] == 0 } @@ -54,7 +54,7 @@ impl Ipv4Global { /// [IETF RFC 6598] (`100.64.0.0/10`). /// /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 - pub const fn is_shared(a: Ipv4Addr) -> bool { + const fn is_shared(a: Ipv4Addr) -> bool { a.octets()[0] == 100 && (a.octets()[1] & 0b1100_0000 == 0b0100_0000) } @@ -64,7 +64,7 @@ impl Ipv4Global { /// it is obviously not reserved for future use. /// /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 - pub const fn is_reserved(a: Ipv4Addr) -> bool { + const fn is_reserved(a: Ipv4Addr) -> bool { a.octets()[0] & 240 == 240 && !a.is_broadcast() } @@ -74,12 +74,12 @@ impl Ipv4Global { /// /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 /// [errata 423]: https://www.rfc-editor.org/errata/eid423 - pub const fn is_benchmarking(a: Ipv4Addr) -> bool { + const fn is_benchmarking(a: Ipv4Addr) -> bool { a.octets()[0] == 198 && (a.octets()[1] & 0xfe) == 18 } /// Returns [`true`] if this address part of the `192.0.0.0/29` range. - pub const fn is_ipv4_service_continuity_prefix(a: Ipv4Addr) -> bool { + const fn is_ipv4_service_continuity_prefix(a: Ipv4Addr) -> bool { a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0 @@ -87,22 +87,22 @@ impl Ipv4Global { } /// Returns [`true`] if this address part of the `192.0.0.0/24` range. - pub const fn is_ietf_protocol_assignments(a: Ipv4Addr) -> bool { + const fn is_ietf_protocol_assignments(a: Ipv4Addr) -> bool { a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0 } /// Returns [`true`] if this address part of the `192.0.0.8/32` range. - pub const fn is_ipv4_dummy_address(a: Ipv4Addr) -> bool { + const fn is_ipv4_dummy_address(a: Ipv4Addr) -> bool { a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0 && a.octets()[3] == 8 } /// Returns [`true`] if this address part of the `192.0.0.9/32` range. - pub const fn is_port_control_protocol_anycast(a: Ipv4Addr) -> bool { + const fn is_port_control_protocol_anycast(a: Ipv4Addr) -> bool { a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0 && a.octets()[3] == 9 } /// Returns [`true`] if this address part of the `192.0.0.10/32` range. - pub const fn is_traversal_using_relays_around_nat_anycast(a: Ipv4Addr) -> bool { + const fn is_traversal_using_relays_around_nat_anycast(a: Ipv4Addr) -> bool { a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0 && a.octets()[3] == 10 } @@ -110,7 +110,7 @@ impl Ipv4Global { /// /// - `192.0.0.170/32` /// - `192.0.0.171/32` - pub const fn is_nat64_dns64_discovery(a: Ipv4Addr) -> bool { + const fn is_nat64_dns64_discovery(a: Ipv4Addr) -> bool { a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0 @@ -118,22 +118,22 @@ impl Ipv4Global { } /// Returns [`true`] if this address part of the `192.31.196.0/24` range. - pub const fn is_as112_v4(a: Ipv4Addr) -> bool { + const fn is_as112_v4(a: Ipv4Addr) -> bool { a.octets()[0] == 192 && a.octets()[1] == 31 && a.octets()[1] == 196 } /// Returns [`true`] if this address part of the `192.52.193.0/24` range. - pub const fn is_amt(a: Ipv4Addr) -> bool { + const fn is_amt(a: Ipv4Addr) -> bool { a.octets()[0] == 192 && a.octets()[1] == 52 && a.octets()[2] == 193 } /// Returns [`true`] if this address part of the `192.88.99.0/24` range. - pub const fn is_deprecated_6to6_relay_anycast(a: Ipv4Addr) -> bool { + const fn is_deprecated_6to6_relay_anycast(a: Ipv4Addr) -> bool { a.octets()[0] == 192 && a.octets()[1] == 88 && a.octets()[2] == 99 } /// Returns [`true`] if this address part of the `192.175.48.0/24` range. - pub const fn is_direct_delegation_as112_service(a: Ipv4Addr) -> bool { + const fn is_direct_delegation_as112_service(a: Ipv4Addr) -> bool { a.octets()[0] == 192 && a.octets()[1] == 175 && a.octets()[2] == 48 } @@ -176,93 +176,93 @@ impl Ipv4Global { impl Ipv6Global { /// Returns [`true`] if this address is `::ffff:0:0/96`. - pub const fn is_ipv4_mapped_address(a: Ipv6Addr) -> bool { + const fn is_ipv4_mapped_address(a: Ipv6Addr) -> bool { matches!(a.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) } /// Returns [`true`] if this address is `64:ff9b::/96` or `64:ff9b:1::/48`. - pub const fn is_ipv4_ipv6_translat(a: Ipv6Addr) -> bool { + const fn is_ipv4_ipv6_translat(a: Ipv6Addr) -> bool { matches!(a.segments(), [0x64, 0xff9b, 0, 0, 0, 0, _, _]) || matches!(a.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) } /// Returns [`true`] if this address is `100::/64`. - pub const fn is_discard_only_address_block(a: Ipv6Addr) -> bool { + const fn is_discard_only_address_block(a: Ipv6Addr) -> bool { matches!(a.segments(), [0x100, 0, 0, 0, _, _, _, _]) } /// Returns [`true`] if this address is `2001::/23`. - pub const fn is_ietf_protocol_assignments(a: Ipv6Addr) -> bool { + const fn is_ietf_protocol_assignments(a: Ipv6Addr) -> bool { matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b <= 0x1FF) } /// Returns [`true`] if this address is `2001::/32`. - pub const fn is_teredo(a: Ipv6Addr) -> bool { + const fn is_teredo(a: Ipv6Addr) -> bool { matches!(a.segments(), [0x2001, 0, _, _, _, _, _, _]) } /// Returns [`true`] if this address is `2001:1::1/128`. - pub const fn is_port_control_protocol_anycast(a: Ipv6Addr) -> bool { + const fn is_port_control_protocol_anycast(a: Ipv6Addr) -> bool { u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 } /// Returns [`true`] if this address is `2001:1::2/128`. - pub const fn is_traversal_relays_nat_anycast(a: Ipv6Addr) -> bool { + const fn is_traversal_relays_nat_anycast(a: Ipv6Addr) -> bool { u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 } /// Returns [`true`] if this address is `2001:2::/48`. - pub const fn is_benchmarking(a: Ipv6Addr) -> bool { + const fn is_benchmarking(a: Ipv6Addr) -> bool { matches!(a.segments(), [0x2001, 2, 0, _, _, _, _, _]) } /// Returns [`true`] if this address is `2001:3::/32`. - pub const fn is_amt(a: Ipv6Addr) -> bool { + const fn is_amt(a: Ipv6Addr) -> bool { matches!(a.segments(), [0x2001, 3, _, _, _, _, _, _]) } /// Returns [`true`] if this address is `2001:4:112::/48`. - pub const fn is_as112_v6(a: Ipv6Addr) -> bool { + const fn is_as112_v6(a: Ipv6Addr) -> bool { matches!(a.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) } /// Returns [`true`] if this address is `2001:10::/28`. - pub const fn is_deprecated_previously_orchid(a: Ipv6Addr) -> bool { + const fn is_deprecated_previously_orchid(a: Ipv6Addr) -> bool { matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x10 && b <= 0x1F) } /// Returns [`true`] if this address is `2001:20::/28`. - pub const fn is_orchid_v2(a: Ipv6Addr) -> bool { + const fn is_orchid_v2(a: Ipv6Addr) -> bool { matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) } /// Returns [`true`] if this address is `2001:30::/28`. - pub const fn is_drone_remote(a: Ipv6Addr) -> bool { + const fn is_drone_remote(a: Ipv6Addr) -> bool { matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x30 && b <= 0x3F) } /// Returns [`true`] if this address is `2001:db8::/32`. - pub const fn is_documentation(a: Ipv6Addr) -> bool { + const fn is_documentation(a: Ipv6Addr) -> bool { matches!(a.segments(), [0x2001, 0xdb8, _, _, _, _, _, _]) } /// Returns [`true`] if this address is `2002::/16`. - pub const fn is_6to4(a: Ipv6Addr) -> bool { + const fn is_6to4(a: Ipv6Addr) -> bool { matches!(a.segments(), [0x2002, _, _, _, _, _, _, _]) } /// Returns [`true`] if this address is `2620:4f:8000::/48`. - pub const fn is_direct_delegation_as112_service(a: Ipv6Addr) -> bool { + const fn is_direct_delegation_as112_service(a: Ipv6Addr) -> bool { matches!(a.segments(), [0x2620, 0x4f, 0x8000, _, _, _, _, _]) } /// Returns [`true`] if this address is `fc00::/7`. - pub const fn is_unique_local(a: Ipv6Addr) -> bool { + const fn is_unique_local(a: Ipv6Addr) -> bool { (a.segments()[0] & 0xfe00) == 0xfc00 } /// Returns [`true`] if this address is `fe80::/10`. - pub const fn is_unicast_link_local(a: Ipv6Addr) -> bool { + const fn is_unicast_link_local(a: Ipv6Addr) -> bool { (a.segments()[0] & 0xffc0) == 0xfe80 } From 65bbfbe24cb8ce20ae5d27c7606574f9fd26b487 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Tue, 25 Apr 2023 23:33:22 +0200 Subject: [PATCH 08/17] early returns in dial --- core/src/transport/global_only.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index e8bb2ea675a..c068343f42c 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -313,20 +313,18 @@ impl Transport for GlobalIpOnly { fn dial(&mut self, addr: Multiaddr) -> Result> { match addr.iter().next() { Some(Protocol::Ip4(a)) => { - if Ipv4Global::is_global(a) { - self.inner.dial(addr) - } else { + if !Ipv4Global::is_global(a) { debug!("Not dialing non global IP address {:?}.", a); - Err(TransportError::MultiaddrNotSupported(addr)) + return Err(TransportError::MultiaddrNotSupported(addr)); } + self.inner.dial(addr) } Some(Protocol::Ip6(a)) => { - if Ipv6Global::is_global(a) { - self.inner.dial(addr) - } else { + if !Ipv6Global::is_global(a) { debug!("Not dialing non global IP address {:?}.", a); - Err(TransportError::MultiaddrNotSupported(addr)) + return Err(TransportError::MultiaddrNotSupported(addr)); } + self.inner.dial(addr) } _ => { debug!("Not dialing unsupported Multiaddress {:?}.", addr); From 4d6f143b79e92786abf0e4cd4c488551bd951e78 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Tue, 25 Apr 2023 23:44:49 +0200 Subject: [PATCH 09/17] replace Ipv4Global and Ipv6Global pub struct by private mod --- core/src/transport/global_only.rs | 80 +++++++++++++++---------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index c068343f42c..2b6201a0aee 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -25,7 +25,6 @@ use crate::{ }; use log::debug; use std::{ - net::{Ipv4Addr, Ipv6Addr}, pin::Pin, task::{Context, Poll}, }; @@ -36,10 +35,9 @@ pub struct GlobalIpOnly { inner: T, } -pub struct Ipv4Global; -pub struct Ipv6Global; +mod ipv4_global { + use std::net::Ipv4Addr; -impl Ipv4Global { /// Returns [`true`] if this address part of the `0.0.0.0/8` range. const fn is_this_network(a: Ipv4Addr) -> bool { a.octets()[0] == 0 @@ -151,30 +149,32 @@ impl Ipv4Global { /// /// A boolean value indicating whether the given IPv4 address is a global address or not. pub fn is_global(a: Ipv4Addr) -> bool { - !(Self::is_this_network(a) - || Self::is_this_host_on_this_network(a) + !(is_this_network(a) + || is_this_host_on_this_network(a) || a.is_private() - || Self::is_shared(a) + || is_shared(a) || a.is_loopback() || a.is_link_local() - || Self::is_ietf_protocol_assignments(a) - || Self::is_ipv4_service_continuity_prefix(a) - || Self::is_ipv4_dummy_address(a) - || Self::is_port_control_protocol_anycast(a) - || Self::is_traversal_using_relays_around_nat_anycast(a) - || Self::is_nat64_dns64_discovery(a) + || is_ietf_protocol_assignments(a) + || is_ipv4_service_continuity_prefix(a) + || is_ipv4_dummy_address(a) + || is_port_control_protocol_anycast(a) + || is_traversal_using_relays_around_nat_anycast(a) + || is_nat64_dns64_discovery(a) || a.is_documentation() - || Self::is_as112_v4(a) - || Self::is_amt(a) - || Self::is_deprecated_6to6_relay_anycast(a) - || Self::is_direct_delegation_as112_service(a)) - || Self::is_benchmarking(a) - || Self::is_reserved(a) + || is_as112_v4(a) + || is_amt(a) + || is_deprecated_6to6_relay_anycast(a) + || is_direct_delegation_as112_service(a)) + || is_benchmarking(a) + || is_reserved(a) || a.is_broadcast() } } -impl Ipv6Global { +mod ipv6_global { + use std::net::Ipv6Addr; + /// Returns [`true`] if this address is `::ffff:0:0/96`. const fn is_ipv4_mapped_address(a: Ipv6Addr) -> bool { matches!(a.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) @@ -269,24 +269,24 @@ impl Ipv6Global { pub fn is_global(a: Ipv6Addr) -> bool { !(a.is_loopback() || a.is_unspecified() - || Self::is_ipv4_mapped_address(a) - || Self::is_ipv4_ipv6_translat(a) - || Self::is_discard_only_address_block(a) - || Self::is_ietf_protocol_assignments(a) - || Self::is_teredo(a) - || Self::is_port_control_protocol_anycast(a) - || Self::is_traversal_relays_nat_anycast(a) - || Self::is_benchmarking(a) - || Self::is_amt(a) - || Self::is_as112_v6(a) - || Self::is_deprecated_previously_orchid(a) - || Self::is_orchid_v2(a) - || Self::is_drone_remote(a) - || Self::is_documentation(a) - || Self::is_6to4(a) - || Self::is_direct_delegation_as112_service(a) - || Self::is_unique_local(a) - || Self::is_unicast_link_local(a)) + || is_ipv4_mapped_address(a) + || is_ipv4_ipv6_translat(a) + || is_discard_only_address_block(a) + || is_ietf_protocol_assignments(a) + || is_teredo(a) + || is_port_control_protocol_anycast(a) + || is_traversal_relays_nat_anycast(a) + || is_benchmarking(a) + || is_amt(a) + || is_as112_v6(a) + || is_deprecated_previously_orchid(a) + || is_orchid_v2(a) + || is_drone_remote(a) + || is_documentation(a) + || is_6to4(a) + || is_direct_delegation_as112_service(a) + || is_unique_local(a) + || is_unicast_link_local(a)) } } @@ -313,14 +313,14 @@ impl Transport for GlobalIpOnly { fn dial(&mut self, addr: Multiaddr) -> Result> { match addr.iter().next() { Some(Protocol::Ip4(a)) => { - if !Ipv4Global::is_global(a) { + if !ipv4_global::is_global(a) { debug!("Not dialing non global IP address {:?}.", a); return Err(TransportError::MultiaddrNotSupported(addr)); } self.inner.dial(addr) } Some(Protocol::Ip6(a)) => { - if !Ipv6Global::is_global(a) { + if !ipv6_global::is_global(a) { debug!("Not dialing non global IP address {:?}.", a); return Err(TransportError::MultiaddrNotSupported(addr)); } From 7e8a53d11ce6bf3cccaf5e9bc31ccd8c118efc56 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Wed, 26 Apr 2023 09:34:14 +0200 Subject: [PATCH 10/17] add test is_global address to dial_as_listener --- core/src/transport/global_only.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index 2b6201a0aee..da02c46bd4f 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -337,7 +337,26 @@ impl Transport for GlobalIpOnly { &mut self, addr: Multiaddr, ) -> Result> { - self.inner.dial_as_listener(addr) + match addr.iter().next() { + Some(Protocol::Ip4(a)) => { + if !ipv4_global::is_global(a) { + debug!("Not dialing non global IP address {:?}.", a); + return Err(TransportError::MultiaddrNotSupported(addr)); + } + self.inner.dial_as_listener(addr) + } + Some(Protocol::Ip6(a)) => { + if !ipv6_global::is_global(a) { + debug!("Not dialing non global IP address {:?}.", a); + return Err(TransportError::MultiaddrNotSupported(addr)); + } + self.inner.dial_as_listener(addr) + } + _ => { + debug!("Not dialing unsupported Multiaddress {:?}.", addr); + Err(TransportError::MultiaddrNotSupported(addr)) + } + } } fn address_translation(&self, listen: &Multiaddr, observed: &Multiaddr) -> Option { From cb61a13fd3c01a61fe2e83f4e53d34062f5aa2ea Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Mon, 8 May 2023 12:59:17 +0200 Subject: [PATCH 11/17] rename GlobalIpOnly to Transport --- core/src/transport/global_only.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index da02c46bd4f..d1dc73bb81d 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -21,8 +21,9 @@ use crate::{ multiaddr::{Multiaddr, Protocol}, transport::{ListenerId, TransportError, TransportEvent}, - Transport, }; + +use crate::Transport as TransportCore; use log::debug; use std::{ pin::Pin, @@ -31,7 +32,7 @@ use std::{ /// Dropping all dial requests to non-global IP addresses. #[derive(Debug, Clone, Default)] -pub struct GlobalIpOnly { +pub struct Transport { inner: T, } @@ -290,17 +291,17 @@ mod ipv6_global { } } -impl GlobalIpOnly { +impl Transport { pub fn new(transport: T) -> Self { - GlobalIpOnly { inner: transport } + Transport { inner: transport } } } -impl Transport for GlobalIpOnly { - type Output = ::Output; - type Error = ::Error; - type ListenerUpgrade = ::ListenerUpgrade; - type Dial = ::Dial; +impl TransportCore for Transport { + type Output = ::Output; + type Error = ::Error; + type ListenerUpgrade = ::ListenerUpgrade; + type Dial = ::Dial; fn listen_on(&mut self, addr: Multiaddr) -> Result> { self.inner.listen_on(addr) From 08f1bd380c5e88d082a717cb844385e07b002208 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Mon, 8 May 2023 23:47:14 +0200 Subject: [PATCH 12/17] Update core/CHANGELOG.md Co-authored-by: Thomas Eizinger --- core/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 41d9c37bb7e..a93afcb4756 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -29,7 +29,7 @@ - Deprecate `{In,Out}boundUpgradeExt`, as they are not used in rust-libp2p. See [PR 3807]. -- Add `GlobalOnly` `Transport` implementation. +- Add `global_only::Transport` that refuses to dial IP addresses from private ranges. See [PR 3814]. - Deprecate `OptionalUpgrade` without replacement. From c3c9d119fabab1b30129131fe13824e6a446e7bc Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Mon, 8 May 2023 23:48:18 +0200 Subject: [PATCH 13/17] move changelog item to top --- core/CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index a93afcb4756..ad06aefa872 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -16,9 +16,13 @@ - Remove `SingletonMuxer`. See [PR 3883]. +- Add `global_only::Transport` that refuses to dial IP addresses from private ranges. + See [PR 3814]. + [spec]: https://github.com/libp2p/specs/blob/master/connections/README.md#multistream-select [PR 3746]: https://github.com/libp2p/rust-libp2p/pull/3746 [PR 3883]: https://github.com/libp2p/rust-libp2p/pull/3883 +[PR 3814]: https://github.com/libp2p/rust-libp2p/pull/3814 ## 0.39.2 @@ -29,15 +33,11 @@ - Deprecate `{In,Out}boundUpgradeExt`, as they are not used in rust-libp2p. See [PR 3807]. -- Add `global_only::Transport` that refuses to dial IP addresses from private ranges. - See [PR 3814]. - - Deprecate `OptionalUpgrade` without replacement. See [PR 3806]. [PR 3747]: https://github.com/libp2p/rust-libp2p/pull/3747 [PR 3807]: https://github.com/libp2p/rust-libp2p/pull/3807 -[PR 3814]: https://github.com/libp2p/rust-libp2p/pull/3814 [PR 3806]: https://github.com/libp2p/rust-libp2p/pull/3806 ## 0.39.1 From 7d9640ca805e429e6ba7af04a7b3402657e0c275 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Mon, 8 May 2023 23:49:54 +0200 Subject: [PATCH 14/17] remove import to fully-qualifying --- core/src/transport/global_only.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index d1dc73bb81d..24b00e5f1a8 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -22,8 +22,6 @@ use crate::{ multiaddr::{Multiaddr, Protocol}, transport::{ListenerId, TransportError, TransportEvent}, }; - -use crate::Transport as TransportCore; use log::debug; use std::{ pin::Pin, @@ -297,11 +295,11 @@ impl Transport { } } -impl TransportCore for Transport { - type Output = ::Output; - type Error = ::Error; - type ListenerUpgrade = ::ListenerUpgrade; - type Dial = ::Dial; +impl crate::Transport for Transport { + type Output = ::Output; + type Error = ::Error; + type ListenerUpgrade = ::ListenerUpgrade; + type Dial = ::Dial; fn listen_on(&mut self, addr: Multiaddr) -> Result> { self.inner.listen_on(addr) From da5e61cb4bcb1d40383a49d50591903fae9ee91c Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Tue, 9 May 2023 00:18:00 +0200 Subject: [PATCH 15/17] add std implementation for is_global ipv4 and ipv6 --- core/src/transport/global_only.rs | 546 +++++++++++++++++++----------- 1 file changed, 349 insertions(+), 197 deletions(-) diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index 24b00e5f1a8..0000883f727 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -34,33 +34,43 @@ pub struct Transport { inner: T, } +/// This module contains an implementation of the `is_global` IPv4 address space. +/// +/// Credit for this implementation goes to the Rust standard library team. +/// +/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) mod ipv4_global { use std::net::Ipv4Addr; - /// Returns [`true`] if this address part of the `0.0.0.0/8` range. - const fn is_this_network(a: Ipv4Addr) -> bool { - a.octets()[0] == 0 - } - - /// Returns [`true`] if this address part of the `0.0.0.0/32` range. - const fn is_this_host_on_this_network(a: Ipv4Addr) -> bool { - a.octets()[0] == 0 && a.octets()[1] == 0 && a.octets()[2] == 0 && a.octets()[3] == 0 - } - - /// Returns [`true`] if this address is part of the Shared Address Space defined in - /// [IETF RFC 6598] (`100.64.0.0/10`). - /// - /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 - const fn is_shared(a: Ipv4Addr) -> bool { - a.octets()[0] == 100 && (a.octets()[1] & 0b1100_0000 == 0b0100_0000) - } - /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since /// it is obviously not reserved for future use. /// /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 + /// + /// # Warning + /// + /// As IANA assigns new addresses, this method will be + /// updated. This may result in non-reserved addresses being + /// treated as reserved in code that relies on an outdated version + /// of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(240, 0, 0, 0).is_reserved(), true); + /// assert_eq!(Ipv4Addr::new(255, 255, 255, 254).is_reserved(), true); + /// + /// assert_eq!(Ipv4Addr::new(239, 255, 255, 255).is_reserved(), false); + /// // The broadcast address is not considered as reserved for future use by this implementation + /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_reserved(), false); + /// ``` + #[must_use] + #[inline] const fn is_reserved(a: Ipv4Addr) -> bool { a.octets()[0] & 240 == 240 && !a.is_broadcast() } @@ -71,219 +81,361 @@ mod ipv4_global { /// /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 /// [errata 423]: https://www.rfc-editor.org/errata/eid423 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(198, 17, 255, 255).is_benchmarking(), false); + /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_benchmarking(), true); + /// assert_eq!(Ipv4Addr::new(198, 19, 255, 255).is_benchmarking(), true); + /// assert_eq!(Ipv4Addr::new(198, 20, 0, 0).is_benchmarking(), false); + /// ``` + #[must_use] + #[inline] const fn is_benchmarking(a: Ipv4Addr) -> bool { a.octets()[0] == 198 && (a.octets()[1] & 0xfe) == 18 } - /// Returns [`true`] if this address part of the `192.0.0.0/29` range. - const fn is_ipv4_service_continuity_prefix(a: Ipv4Addr) -> bool { - a.octets()[0] == 192 - && a.octets()[1] == 0 - && a.octets()[2] == 0 - && (a.octets()[3] & 0b1111_1000 == 0) - } - - /// Returns [`true`] if this address part of the `192.0.0.0/24` range. - const fn is_ietf_protocol_assignments(a: Ipv4Addr) -> bool { - a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0 - } - - /// Returns [`true`] if this address part of the `192.0.0.8/32` range. - const fn is_ipv4_dummy_address(a: Ipv4Addr) -> bool { - a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0 && a.octets()[3] == 8 - } - - /// Returns [`true`] if this address part of the `192.0.0.9/32` range. - const fn is_port_control_protocol_anycast(a: Ipv4Addr) -> bool { - a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0 && a.octets()[3] == 9 - } - - /// Returns [`true`] if this address part of the `192.0.0.10/32` range. - const fn is_traversal_using_relays_around_nat_anycast(a: Ipv4Addr) -> bool { - a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0 && a.octets()[3] == 10 - } - - /// Returns [`true`] if this is NAT64/DNS64 Discovery. This includes: - /// - /// - `192.0.0.170/32` - /// - `192.0.0.171/32` - const fn is_nat64_dns64_discovery(a: Ipv4Addr) -> bool { - a.octets()[0] == 192 - && a.octets()[1] == 0 - && a.octets()[2] == 0 - && (a.octets()[3] == 170 || a.octets()[3] == 171) - } - - /// Returns [`true`] if this address part of the `192.31.196.0/24` range. - const fn is_as112_v4(a: Ipv4Addr) -> bool { - a.octets()[0] == 192 && a.octets()[1] == 31 && a.octets()[1] == 196 - } - - /// Returns [`true`] if this address part of the `192.52.193.0/24` range. - const fn is_amt(a: Ipv4Addr) -> bool { - a.octets()[0] == 192 && a.octets()[1] == 52 && a.octets()[2] == 193 + /// Returns [`true`] if this address is part of the Shared Address Space defined in + /// [IETF RFC 6598] (`100.64.0.0/10`). + /// + /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(100, 64, 0, 0).is_shared(), true); + /// assert_eq!(Ipv4Addr::new(100, 127, 255, 255).is_shared(), true); + /// assert_eq!(Ipv4Addr::new(100, 128, 0, 0).is_shared(), false); + /// ``` + #[must_use] + #[inline] + const fn is_shared(a: Ipv4Addr) -> bool { + a.octets()[0] == 100 && (a.octets()[1] & 0b1100_0000 == 0b0100_0000) } - /// Returns [`true`] if this address part of the `192.88.99.0/24` range. - const fn is_deprecated_6to6_relay_anycast(a: Ipv4Addr) -> bool { - a.octets()[0] == 192 && a.octets()[1] == 88 && a.octets()[2] == 99 + /// Returns [`true`] if this is a private address. + /// + /// The private address ranges are defined in [IETF RFC 1918] and include: + /// + /// - `10.0.0.0/8` + /// - `172.16.0.0/12` + /// - `192.168.0.0/16` + /// + /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(10, 0, 0, 1).is_private(), true); + /// assert_eq!(Ipv4Addr::new(10, 10, 10, 10).is_private(), true); + /// assert_eq!(Ipv4Addr::new(172, 16, 10, 10).is_private(), true); + /// assert_eq!(Ipv4Addr::new(172, 29, 45, 14).is_private(), true); + /// assert_eq!(Ipv4Addr::new(172, 32, 0, 2).is_private(), false); + /// assert_eq!(Ipv4Addr::new(192, 168, 0, 2).is_private(), true); + /// assert_eq!(Ipv4Addr::new(192, 169, 0, 2).is_private(), false); + /// ``` + #[must_use] + #[inline] + const fn is_private(a: Ipv4Addr) -> bool { + match a.octets() { + [10, ..] => true, + [172, b, ..] if b >= 16 && b <= 31 => true, + [192, 168, ..] => true, + _ => false, + } } - /// Returns [`true`] if this address part of the `192.175.48.0/24` range. - const fn is_direct_delegation_as112_service(a: Ipv4Addr) -> bool { - a.octets()[0] == 192 && a.octets()[1] == 175 && a.octets()[2] == 48 - } + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv4 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// + /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) + /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) + /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) + /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) + /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) + /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) + /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) + /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) + /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. + /// + /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml + /// [unspecified address]: Ipv4Addr::UNSPECIFIED + /// [broadcast address]: Ipv4Addr::BROADCAST - /// The function checks if an IPv4 address is a global address by verifying that it does not belong - /// to any of the reserved or special use address ranges. /// - /// Arguments: + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv4Addr; + /// + /// // Most IPv4 addresses are globally reachable: + /// assert_eq!(Ipv4Addr::new(80, 9, 12, 3).is_global(), true); + /// + /// // However some addresses have been assigned a special meaning + /// // that makes them not globally reachable. Some examples are: + /// + /// // The unspecified address (`0.0.0.0`) + /// assert_eq!(Ipv4Addr::UNSPECIFIED.is_global(), false); + /// + /// // Addresses reserved for private use (`10.0.0.0/8`, `172.16.0.0/12`, 192.168.0.0/16) + /// assert_eq!(Ipv4Addr::new(10, 254, 0, 0).is_global(), false); + /// assert_eq!(Ipv4Addr::new(192, 168, 10, 65).is_global(), false); + /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_global(), false); + /// + /// // Addresses in the shared address space (`100.64.0.0/10`) + /// assert_eq!(Ipv4Addr::new(100, 100, 0, 0).is_global(), false); + /// + /// // The loopback addresses (`127.0.0.0/8`) + /// assert_eq!(Ipv4Addr::LOCALHOST.is_global(), false); /// - /// * `a`: `a` is an `Ipv4Addr` type variable representing an IPv4 address. The function - /// `is_global` checks whether the given IPv4 address is a global IP address or not by checking - /// against various criteria such as private IP address ranges, reserved IP address ranges, and - /// special use + /// // Link-local addresses (`169.254.0.0/16`) + /// assert_eq!(Ipv4Addr::new(169, 254, 45, 1).is_global(), false); /// - /// Returns: + /// // Addresses reserved for documentation (`192.0.2.0/24`, `198.51.100.0/24`, `203.0.113.0/24`) + /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_global(), false); + /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_global(), false); + /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_global(), false); /// - /// A boolean value indicating whether the given IPv4 address is a global address or not. - pub fn is_global(a: Ipv4Addr) -> bool { - !(is_this_network(a) - || is_this_host_on_this_network(a) - || a.is_private() + /// // Addresses reserved for benchmarking (`198.18.0.0/15`) + /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_global(), false); + /// + /// // Reserved addresses (`240.0.0.0/4`) + /// assert_eq!(Ipv4Addr::new(250, 10, 20, 30).is_global(), false); + /// + /// // The broadcast address (`255.255.255.255`) + /// assert_eq!(Ipv4Addr::BROADCAST.is_global(), false); + /// + /// // For a complete overview see the IANA IPv4 Special-Purpose Address Registry. + /// ``` + #[must_use] + #[inline] + pub const fn is_global(a: Ipv4Addr) -> bool { + !(a.octets()[0] == 0 // "This network" + || is_private(a) || is_shared(a) || a.is_loopback() || a.is_link_local() - || is_ietf_protocol_assignments(a) - || is_ipv4_service_continuity_prefix(a) - || is_ipv4_dummy_address(a) - || is_port_control_protocol_anycast(a) - || is_traversal_using_relays_around_nat_anycast(a) - || is_nat64_dns64_discovery(a) + // addresses reserved for future protocols (`192.0.0.0/24`) + ||(a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0) || a.is_documentation() - || is_as112_v4(a) - || is_amt(a) - || is_deprecated_6to6_relay_anycast(a) - || is_direct_delegation_as112_service(a)) || is_benchmarking(a) || is_reserved(a) - || a.is_broadcast() + || a.is_broadcast()) } } +/// This module contains an implementation of the `is_global` IPv6 address space. +/// +/// Credit for this implementation goes to the Rust standard library team. +/// +/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) mod ipv6_global { use std::net::Ipv6Addr; - /// Returns [`true`] if this address is `::ffff:0:0/96`. - const fn is_ipv4_mapped_address(a: Ipv6Addr) -> bool { - matches!(a.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) - } - - /// Returns [`true`] if this address is `64:ff9b::/96` or `64:ff9b:1::/48`. - const fn is_ipv4_ipv6_translat(a: Ipv6Addr) -> bool { - matches!(a.segments(), [0x64, 0xff9b, 0, 0, 0, 0, _, _]) - || matches!(a.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) - } - - /// Returns [`true`] if this address is `100::/64`. - const fn is_discard_only_address_block(a: Ipv6Addr) -> bool { - matches!(a.segments(), [0x100, 0, 0, 0, _, _, _, _]) - } - - /// Returns [`true`] if this address is `2001::/23`. - const fn is_ietf_protocol_assignments(a: Ipv6Addr) -> bool { - matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b <= 0x1FF) - } - - /// Returns [`true`] if this address is `2001::/32`. - const fn is_teredo(a: Ipv6Addr) -> bool { - matches!(a.segments(), [0x2001, 0, _, _, _, _, _, _]) - } - - /// Returns [`true`] if this address is `2001:1::1/128`. - const fn is_port_control_protocol_anycast(a: Ipv6Addr) -> bool { - u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 - } - - /// Returns [`true`] if this address is `2001:1::2/128`. - const fn is_traversal_relays_nat_anycast(a: Ipv6Addr) -> bool { - u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 - } - - /// Returns [`true`] if this address is `2001:2::/48`. - const fn is_benchmarking(a: Ipv6Addr) -> bool { - matches!(a.segments(), [0x2001, 2, 0, _, _, _, _, _]) - } - - /// Returns [`true`] if this address is `2001:3::/32`. - const fn is_amt(a: Ipv6Addr) -> bool { - matches!(a.segments(), [0x2001, 3, _, _, _, _, _, _]) - } - - /// Returns [`true`] if this address is `2001:4:112::/48`. - const fn is_as112_v6(a: Ipv6Addr) -> bool { - matches!(a.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) - } - - /// Returns [`true`] if this address is `2001:10::/28`. - const fn is_deprecated_previously_orchid(a: Ipv6Addr) -> bool { - matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x10 && b <= 0x1F) - } - - /// Returns [`true`] if this address is `2001:20::/28`. - const fn is_orchid_v2(a: Ipv6Addr) -> bool { - matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) - } - - /// Returns [`true`] if this address is `2001:30::/28`. - const fn is_drone_remote(a: Ipv6Addr) -> bool { - matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x30 && b <= 0x3F) - } - - /// Returns [`true`] if this address is `2001:db8::/32`. - const fn is_documentation(a: Ipv6Addr) -> bool { - matches!(a.segments(), [0x2001, 0xdb8, _, _, _, _, _, _]) - } - - /// Returns [`true`] if this address is `2002::/16`. - const fn is_6to4(a: Ipv6Addr) -> bool { - matches!(a.segments(), [0x2002, _, _, _, _, _, _, _]) - } - - /// Returns [`true`] if this address is `2620:4f:8000::/48`. - const fn is_direct_delegation_as112_service(a: Ipv6Addr) -> bool { - matches!(a.segments(), [0x2620, 0x4f, 0x8000, _, _, _, _, _]) + /// Returns `true` if the address is a unicast address with link-local scope, + /// as defined in [RFC 4291]. + /// + /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. + /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], + /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: + /// + /// ```text + /// | 10 bits | 54 bits | 64 bits | + /// +----------+-------------------------+----------------------------+ + /// |1111111010| 0 | interface ID | + /// +----------+-------------------------+----------------------------+ + /// ``` + /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, + /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, + /// and those addresses will have link-local scope. + /// + /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", + /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. + /// + /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 + /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 + /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 + /// [loopback address]: Ipv6Addr::LOCALHOST + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// // The loopback address (`::1`) does not actually have link-local scope. + /// assert_eq!(Ipv6Addr::LOCALHOST.is_unicast_link_local(), false); + /// + /// // Only addresses in `fe80::/10` have link-local scope. + /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), false); + /// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true); + /// + /// // Addresses outside the stricter `fe80::/64` also have link-local scope. + /// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0).is_unicast_link_local(), true); + /// assert_eq!(Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true); + /// ``` + #[must_use] + #[inline] + const fn is_unicast_link_local(a: Ipv6Addr) -> bool { + (a.segments()[0] & 0xffc0) == 0xfe80 } - /// Returns [`true`] if this address is `fc00::/7`. + /// Returns [`true`] if this is a unique local address (`fc00::/7`). + /// + /// This property is defined in [IETF RFC 4193]. + /// + /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unique_local(), false); + /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 0).is_unique_local(), true); + /// ``` + #[must_use] + #[inline] const fn is_unique_local(a: Ipv6Addr) -> bool { (a.segments()[0] & 0xfe00) == 0xfc00 } - /// Returns [`true`] if this address is `fe80::/10`. - const fn is_unicast_link_local(a: Ipv6Addr) -> bool { - (a.segments()[0] & 0xffc0) == 0xfe80 + /// Returns [`true`] if this is an address reserved for documentation + /// (`2001:db8::/32`). + /// + /// This property is defined in [IETF RFC 3849]. + /// + /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_documentation(), false); + /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_documentation(), true); + /// ``` + #[must_use] + #[inline] + const fn is_documentation(a: Ipv6Addr) -> bool { + (a.segments()[0] == 0x2001) && (a.segments()[1] == 0xdb8) } - pub fn is_global(a: Ipv6Addr) -> bool { - !(a.is_loopback() - || a.is_unspecified() - || is_ipv4_mapped_address(a) - || is_ipv4_ipv6_translat(a) - || is_discard_only_address_block(a) - || is_ietf_protocol_assignments(a) - || is_teredo(a) - || is_port_control_protocol_anycast(a) - || is_traversal_relays_nat_anycast(a) - || is_benchmarking(a) - || is_amt(a) - || is_as112_v6(a) - || is_deprecated_previously_orchid(a) - || is_orchid_v2(a) - || is_drone_remote(a) + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv6 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) + /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) + /// - IPv4-mapped addresses + /// - Addresses reserved for benchmarking + /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) + /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) + /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. + /// + /// Note that an address having global scope is not the same as being globally reachable, + /// and there is no direct relation between the two concepts: There exist addresses with global scope + /// that are not globally reachable (for example unique local addresses), + /// and addresses that are globally reachable without having global scope + /// (multicast addresses with non-global scope). + /// + /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml + /// [unspecified address]: Ipv6Addr::UNSPECIFIED + /// [loopback address]: Ipv6Addr::LOCALHOST + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// // Most IPv6 addresses are globally reachable: + /// assert_eq!(Ipv6Addr::new(0x26, 0, 0x1c9, 0, 0, 0xafc8, 0x10, 0x1).is_global(), true); + /// + /// // However some addresses have been assigned a special meaning + /// // that makes them not globally reachable. Some examples are: + /// + /// // The unspecified address (`::`) + /// assert_eq!(Ipv6Addr::UNSPECIFIED.is_global(), false); + /// + /// // The loopback address (`::1`) + /// assert_eq!(Ipv6Addr::LOCALHOST.is_global(), false); + /// + /// // IPv4-mapped addresses (`::ffff:0:0/96`) + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_global(), false); + /// + /// // Addresses reserved for benchmarking (`2001:2::/48`) + /// assert_eq!(Ipv6Addr::new(0x2001, 2, 0, 0, 0, 0, 0, 1,).is_global(), false); + /// + /// // Addresses reserved for documentation (`2001:db8::/32`) + /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1).is_global(), false); + /// + /// // Unique local addresses (`fc00::/7`) + /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 1).is_global(), false); + /// + /// // Unicast addresses with link-local scope (`fe80::/10`) + /// assert_eq!(Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 1).is_global(), false); + /// + /// // For a complete overview see the IANA IPv6 Special-Purpose Address Registry. + /// ``` + #[must_use] + #[inline] + pub const fn is_global(a: Ipv6Addr) -> bool { + !(a.is_unspecified() + || a.is_loopback() + // IPv4-mapped Address (`::ffff:0:0/96`) + || matches!(a.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) + // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) + || matches!(a.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) + // Discard-Only Address Block (`100::/64`) + || matches!(a.segments(), [0x100, 0, 0, 0, _, _, _, _]) + // IETF Protocol Assignments (`2001::/23`) + || (matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) + && !( + // Port Control Protocol Anycast (`2001:1::1`) + u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 + // Traversal Using Relays around NAT Anycast (`2001:1::2`) + || u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 + // AMT (`2001:3::/32`) + || matches!(a.segments(), [0x2001, 3, _, _, _, _, _, _]) + // AS112-v6 (`2001:4:112::/48`) + || matches!(a.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) + // ORCHIDv2 (`2001:20::/28`) + || matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) + )) || is_documentation(a) - || is_6to4(a) - || is_direct_delegation_as112_service(a) || is_unique_local(a) || is_unicast_link_local(a)) } From f01dc2bfd50cdb97ca9dcac0697e2d64f2a661d3 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Tue, 9 May 2023 08:12:06 +0200 Subject: [PATCH 16/17] fix clippy --- core/src/transport/global_only.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index 0000883f727..54844197f83 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -228,7 +228,7 @@ mod ipv4_global { /// ``` #[must_use] #[inline] - pub const fn is_global(a: Ipv4Addr) -> bool { + pub(crate) const fn is_global(a: Ipv4Addr) -> bool { !(a.octets()[0] == 0 // "This network" || is_private(a) || is_shared(a) @@ -412,7 +412,7 @@ mod ipv6_global { /// ``` #[must_use] #[inline] - pub const fn is_global(a: Ipv6Addr) -> bool { + pub(crate) const fn is_global(a: Ipv6Addr) -> bool { !(a.is_unspecified() || a.is_loopback() // IPv4-mapped Address (`::ffff:0:0/96`) From 13233a4a316152cc3c2a003864ade9ced5f669c7 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Tue, 9 May 2023 10:19:38 +0200 Subject: [PATCH 17/17] remove #in doc examples --- core/src/transport/global_only.rs | 177 ------------------------------ 1 file changed, 177 deletions(-) diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index 54844197f83..b0a12de0f70 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -55,20 +55,6 @@ mod ipv4_global { /// updated. This may result in non-reserved addresses being /// treated as reserved in code that relies on an outdated version /// of this method. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(240, 0, 0, 0).is_reserved(), true); - /// assert_eq!(Ipv4Addr::new(255, 255, 255, 254).is_reserved(), true); - /// - /// assert_eq!(Ipv4Addr::new(239, 255, 255, 255).is_reserved(), false); - /// // The broadcast address is not considered as reserved for future use by this implementation - /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_reserved(), false); - /// ``` #[must_use] #[inline] const fn is_reserved(a: Ipv4Addr) -> bool { @@ -81,18 +67,6 @@ mod ipv4_global { /// /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 /// [errata 423]: https://www.rfc-editor.org/errata/eid423 - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(198, 17, 255, 255).is_benchmarking(), false); - /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_benchmarking(), true); - /// assert_eq!(Ipv4Addr::new(198, 19, 255, 255).is_benchmarking(), true); - /// assert_eq!(Ipv4Addr::new(198, 20, 0, 0).is_benchmarking(), false); - /// ``` #[must_use] #[inline] const fn is_benchmarking(a: Ipv4Addr) -> bool { @@ -103,17 +77,6 @@ mod ipv4_global { /// [IETF RFC 6598] (`100.64.0.0/10`). /// /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(100, 64, 0, 0).is_shared(), true); - /// assert_eq!(Ipv4Addr::new(100, 127, 255, 255).is_shared(), true); - /// assert_eq!(Ipv4Addr::new(100, 128, 0, 0).is_shared(), false); - /// ``` #[must_use] #[inline] const fn is_shared(a: Ipv4Addr) -> bool { @@ -129,20 +92,6 @@ mod ipv4_global { /// - `192.168.0.0/16` /// /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(10, 0, 0, 1).is_private(), true); - /// assert_eq!(Ipv4Addr::new(10, 10, 10, 10).is_private(), true); - /// assert_eq!(Ipv4Addr::new(172, 16, 10, 10).is_private(), true); - /// assert_eq!(Ipv4Addr::new(172, 29, 45, 14).is_private(), true); - /// assert_eq!(Ipv4Addr::new(172, 32, 0, 2).is_private(), false); - /// assert_eq!(Ipv4Addr::new(192, 168, 0, 2).is_private(), true); - /// assert_eq!(Ipv4Addr::new(192, 169, 0, 2).is_private(), false); - /// ``` #[must_use] #[inline] const fn is_private(a: Ipv4Addr) -> bool { @@ -178,54 +127,6 @@ mod ipv4_global { /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml /// [unspecified address]: Ipv4Addr::UNSPECIFIED /// [broadcast address]: Ipv4Addr::BROADCAST - - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv4Addr; - /// - /// // Most IPv4 addresses are globally reachable: - /// assert_eq!(Ipv4Addr::new(80, 9, 12, 3).is_global(), true); - /// - /// // However some addresses have been assigned a special meaning - /// // that makes them not globally reachable. Some examples are: - /// - /// // The unspecified address (`0.0.0.0`) - /// assert_eq!(Ipv4Addr::UNSPECIFIED.is_global(), false); - /// - /// // Addresses reserved for private use (`10.0.0.0/8`, `172.16.0.0/12`, 192.168.0.0/16) - /// assert_eq!(Ipv4Addr::new(10, 254, 0, 0).is_global(), false); - /// assert_eq!(Ipv4Addr::new(192, 168, 10, 65).is_global(), false); - /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_global(), false); - /// - /// // Addresses in the shared address space (`100.64.0.0/10`) - /// assert_eq!(Ipv4Addr::new(100, 100, 0, 0).is_global(), false); - /// - /// // The loopback addresses (`127.0.0.0/8`) - /// assert_eq!(Ipv4Addr::LOCALHOST.is_global(), false); - /// - /// // Link-local addresses (`169.254.0.0/16`) - /// assert_eq!(Ipv4Addr::new(169, 254, 45, 1).is_global(), false); - /// - /// // Addresses reserved for documentation (`192.0.2.0/24`, `198.51.100.0/24`, `203.0.113.0/24`) - /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_global(), false); - /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_global(), false); - /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_global(), false); - /// - /// // Addresses reserved for benchmarking (`198.18.0.0/15`) - /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_global(), false); - /// - /// // Reserved addresses (`240.0.0.0/4`) - /// assert_eq!(Ipv4Addr::new(250, 10, 20, 30).is_global(), false); - /// - /// // The broadcast address (`255.255.255.255`) - /// assert_eq!(Ipv4Addr::BROADCAST.is_global(), false); - /// - /// // For a complete overview see the IANA IPv4 Special-Purpose Address Registry. - /// ``` #[must_use] #[inline] pub(crate) const fn is_global(a: Ipv4Addr) -> bool { @@ -276,25 +177,6 @@ mod ipv6_global { /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 /// [loopback address]: Ipv6Addr::LOCALHOST - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// // The loopback address (`::1`) does not actually have link-local scope. - /// assert_eq!(Ipv6Addr::LOCALHOST.is_unicast_link_local(), false); - /// - /// // Only addresses in `fe80::/10` have link-local scope. - /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), false); - /// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true); - /// - /// // Addresses outside the stricter `fe80::/64` also have link-local scope. - /// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0).is_unicast_link_local(), true); - /// assert_eq!(Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true); - /// ``` #[must_use] #[inline] const fn is_unicast_link_local(a: Ipv6Addr) -> bool { @@ -306,17 +188,6 @@ mod ipv6_global { /// This property is defined in [IETF RFC 4193]. /// /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unique_local(), false); - /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 0).is_unique_local(), true); - /// ``` #[must_use] #[inline] const fn is_unique_local(a: Ipv6Addr) -> bool { @@ -329,17 +200,6 @@ mod ipv6_global { /// This property is defined in [IETF RFC 3849]. /// /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_documentation(), false); - /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_documentation(), true); - /// ``` #[must_use] #[inline] const fn is_documentation(a: Ipv6Addr) -> bool { @@ -373,43 +233,6 @@ mod ipv6_global { /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml /// [unspecified address]: Ipv6Addr::UNSPECIFIED /// [loopback address]: Ipv6Addr::LOCALHOST - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// // Most IPv6 addresses are globally reachable: - /// assert_eq!(Ipv6Addr::new(0x26, 0, 0x1c9, 0, 0, 0xafc8, 0x10, 0x1).is_global(), true); - /// - /// // However some addresses have been assigned a special meaning - /// // that makes them not globally reachable. Some examples are: - /// - /// // The unspecified address (`::`) - /// assert_eq!(Ipv6Addr::UNSPECIFIED.is_global(), false); - /// - /// // The loopback address (`::1`) - /// assert_eq!(Ipv6Addr::LOCALHOST.is_global(), false); - /// - /// // IPv4-mapped addresses (`::ffff:0:0/96`) - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_global(), false); - /// - /// // Addresses reserved for benchmarking (`2001:2::/48`) - /// assert_eq!(Ipv6Addr::new(0x2001, 2, 0, 0, 0, 0, 0, 1,).is_global(), false); - /// - /// // Addresses reserved for documentation (`2001:db8::/32`) - /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1).is_global(), false); - /// - /// // Unique local addresses (`fc00::/7`) - /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 1).is_global(), false); - /// - /// // Unicast addresses with link-local scope (`fe80::/10`) - /// assert_eq!(Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 1).is_global(), false); - /// - /// // For a complete overview see the IANA IPv6 Special-Purpose Address Registry. - /// ``` #[must_use] #[inline] pub(crate) const fn is_global(a: Ipv6Addr) -> bool {