libp2p_core/transport/global_only.rs
1// Copyright 2023 Protocol Labs
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the "Software"),
5// to deal in the Software without restriction, including without limitation
6// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7// and/or sell copies of the Software, and to permit persons to whom the
8// Software is furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19// DEALINGS IN THE SOFTWARE.
20
21use crate::{
22 multiaddr::{Multiaddr, Protocol},
23 transport::{ListenerId, TransportError, TransportEvent},
24};
25use log::debug;
26use std::{
27 pin::Pin,
28 task::{Context, Poll},
29};
30
31/// Dropping all dial requests to non-global IP addresses.
32#[derive(Debug, Clone, Default)]
33pub struct Transport<T> {
34 inner: T,
35}
36
37/// This module contains an implementation of the `is_global` IPv4 address space.
38///
39/// Credit for this implementation goes to the Rust standard library team.
40///
41/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709)
42mod ipv4_global {
43 use std::net::Ipv4Addr;
44
45 /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112]
46 /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the
47 /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since
48 /// it is obviously not reserved for future use.
49 ///
50 /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112
51 ///
52 /// # Warning
53 ///
54 /// As IANA assigns new addresses, this method will be
55 /// updated. This may result in non-reserved addresses being
56 /// treated as reserved in code that relies on an outdated version
57 /// of this method.
58 #[must_use]
59 #[inline]
60 const fn is_reserved(a: Ipv4Addr) -> bool {
61 a.octets()[0] & 240 == 240 && !a.is_broadcast()
62 }
63
64 /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for
65 /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0`
66 /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`.
67 ///
68 /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544
69 /// [errata 423]: https://www.rfc-editor.org/errata/eid423
70 #[must_use]
71 #[inline]
72 const fn is_benchmarking(a: Ipv4Addr) -> bool {
73 a.octets()[0] == 198 && (a.octets()[1] & 0xfe) == 18
74 }
75
76 /// Returns [`true`] if this address is part of the Shared Address Space defined in
77 /// [IETF RFC 6598] (`100.64.0.0/10`).
78 ///
79 /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598
80 #[must_use]
81 #[inline]
82 const fn is_shared(a: Ipv4Addr) -> bool {
83 a.octets()[0] == 100 && (a.octets()[1] & 0b1100_0000 == 0b0100_0000)
84 }
85
86 /// Returns [`true`] if this is a private address.
87 ///
88 /// The private address ranges are defined in [IETF RFC 1918] and include:
89 ///
90 /// - `10.0.0.0/8`
91 /// - `172.16.0.0/12`
92 /// - `192.168.0.0/16`
93 ///
94 /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918
95 #[must_use]
96 #[inline]
97 const fn is_private(a: Ipv4Addr) -> bool {
98 match a.octets() {
99 [10, ..] => true,
100 [172, b, ..] if b >= 16 && b <= 31 => true,
101 [192, 168, ..] => true,
102 _ => false,
103 }
104 }
105
106 /// Returns [`true`] if the address appears to be globally reachable
107 /// as specified by the [IANA IPv4 Special-Purpose Address Registry].
108 /// Whether or not an address is practically reachable will depend on your network configuration.
109 ///
110 /// Most IPv4 addresses are globally reachable;
111 /// unless they are specifically defined as *not* globally reachable.
112 ///
113 /// Non-exhaustive list of notable addresses that are not globally reachable:
114 ///
115 /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified))
116 /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private))
117 /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared))
118 /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback))
119 /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local))
120 /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation))
121 /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking))
122 /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved))
123 /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast))
124 ///
125 /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry].
126 ///
127 /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
128 /// [unspecified address]: Ipv4Addr::UNSPECIFIED
129 /// [broadcast address]: Ipv4Addr::BROADCAST
130 #[must_use]
131 #[inline]
132 pub(crate) const fn is_global(a: Ipv4Addr) -> bool {
133 !(a.octets()[0] == 0 // "This network"
134 || is_private(a)
135 || is_shared(a)
136 || a.is_loopback()
137 || a.is_link_local()
138 // addresses reserved for future protocols (`192.0.0.0/24`)
139 ||(a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0)
140 || a.is_documentation()
141 || is_benchmarking(a)
142 || is_reserved(a)
143 || a.is_broadcast())
144 }
145}
146
147/// This module contains an implementation of the `is_global` IPv6 address space.
148///
149/// Credit for this implementation goes to the Rust standard library team.
150///
151/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709)
152mod ipv6_global {
153 use std::net::Ipv6Addr;
154
155 /// Returns `true` if the address is a unicast address with link-local scope,
156 /// as defined in [RFC 4291].
157 ///
158 /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4].
159 /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6],
160 /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format:
161 ///
162 /// ```text
163 /// | 10 bits | 54 bits | 64 bits |
164 /// +----------+-------------------------+----------------------------+
165 /// |1111111010| 0 | interface ID |
166 /// +----------+-------------------------+----------------------------+
167 /// ```
168 /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`,
169 /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated,
170 /// and those addresses will have link-local scope.
171 ///
172 /// 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",
173 /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it.
174 ///
175 /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
176 /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4
177 /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3
178 /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6
179 /// [loopback address]: Ipv6Addr::LOCALHOST
180 #[must_use]
181 #[inline]
182 const fn is_unicast_link_local(a: Ipv6Addr) -> bool {
183 (a.segments()[0] & 0xffc0) == 0xfe80
184 }
185
186 /// Returns [`true`] if this is a unique local address (`fc00::/7`).
187 ///
188 /// This property is defined in [IETF RFC 4193].
189 ///
190 /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193
191 #[must_use]
192 #[inline]
193 const fn is_unique_local(a: Ipv6Addr) -> bool {
194 (a.segments()[0] & 0xfe00) == 0xfc00
195 }
196
197 /// Returns [`true`] if this is an address reserved for documentation
198 /// (`2001:db8::/32`).
199 ///
200 /// This property is defined in [IETF RFC 3849].
201 ///
202 /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849
203 #[must_use]
204 #[inline]
205 const fn is_documentation(a: Ipv6Addr) -> bool {
206 (a.segments()[0] == 0x2001) && (a.segments()[1] == 0xdb8)
207 }
208
209 /// Returns [`true`] if the address appears to be globally reachable
210 /// as specified by the [IANA IPv6 Special-Purpose Address Registry].
211 /// Whether or not an address is practically reachable will depend on your network configuration.
212 ///
213 /// Most IPv6 addresses are globally reachable;
214 /// unless they are specifically defined as *not* globally reachable.
215 ///
216 /// Non-exhaustive list of notable addresses that are not globally reachable:
217 /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified))
218 /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback))
219 /// - IPv4-mapped addresses
220 /// - Addresses reserved for benchmarking
221 /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation))
222 /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local))
223 /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local))
224 ///
225 /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry].
226 ///
227 /// Note that an address having global scope is not the same as being globally reachable,
228 /// and there is no direct relation between the two concepts: There exist addresses with global scope
229 /// that are not globally reachable (for example unique local addresses),
230 /// and addresses that are globally reachable without having global scope
231 /// (multicast addresses with non-global scope).
232 ///
233 /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
234 /// [unspecified address]: Ipv6Addr::UNSPECIFIED
235 /// [loopback address]: Ipv6Addr::LOCALHOST
236 #[must_use]
237 #[inline]
238 pub(crate) const fn is_global(a: Ipv6Addr) -> bool {
239 !(a.is_unspecified()
240 || a.is_loopback()
241 // IPv4-mapped Address (`::ffff:0:0/96`)
242 || matches!(a.segments(), [0, 0, 0, 0, 0, 0xffff, _, _])
243 // IPv4-IPv6 Translat. (`64:ff9b:1::/48`)
244 || matches!(a.segments(), [0x64, 0xff9b, 1, _, _, _, _, _])
245 // Discard-Only Address Block (`100::/64`)
246 || matches!(a.segments(), [0x100, 0, 0, 0, _, _, _, _])
247 // IETF Protocol Assignments (`2001::/23`)
248 || (matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200)
249 && !(
250 // Port Control Protocol Anycast (`2001:1::1`)
251 u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001
252 // Traversal Using Relays around NAT Anycast (`2001:1::2`)
253 || u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002
254 // AMT (`2001:3::/32`)
255 || matches!(a.segments(), [0x2001, 3, _, _, _, _, _, _])
256 // AS112-v6 (`2001:4:112::/48`)
257 || matches!(a.segments(), [0x2001, 4, 0x112, _, _, _, _, _])
258 // ORCHIDv2 (`2001:20::/28`)
259 || matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F)
260 ))
261 || is_documentation(a)
262 || is_unique_local(a)
263 || is_unicast_link_local(a))
264 }
265}
266
267impl<T> Transport<T> {
268 pub fn new(transport: T) -> Self {
269 Transport { inner: transport }
270 }
271}
272
273impl<T: crate::Transport + Unpin> crate::Transport for Transport<T> {
274 type Output = <T as crate::Transport>::Output;
275 type Error = <T as crate::Transport>::Error;
276 type ListenerUpgrade = <T as crate::Transport>::ListenerUpgrade;
277 type Dial = <T as crate::Transport>::Dial;
278
279 fn listen_on(
280 &mut self,
281 id: ListenerId,
282 addr: Multiaddr,
283 ) -> Result<(), TransportError<Self::Error>> {
284 self.inner.listen_on(id, addr)
285 }
286
287 fn remove_listener(&mut self, id: ListenerId) -> bool {
288 self.inner.remove_listener(id)
289 }
290
291 fn dial(&mut self, addr: Multiaddr) -> Result<Self::Dial, TransportError<Self::Error>> {
292 match addr.iter().next() {
293 Some(Protocol::Ip4(a)) => {
294 if !ipv4_global::is_global(a) {
295 debug!("Not dialing non global IP address {:?}.", a);
296 return Err(TransportError::MultiaddrNotSupported(addr));
297 }
298 self.inner.dial(addr)
299 }
300 Some(Protocol::Ip6(a)) => {
301 if !ipv6_global::is_global(a) {
302 debug!("Not dialing non global IP address {:?}.", a);
303 return Err(TransportError::MultiaddrNotSupported(addr));
304 }
305 self.inner.dial(addr)
306 }
307 _ => {
308 debug!("Not dialing unsupported Multiaddress {:?}.", addr);
309 Err(TransportError::MultiaddrNotSupported(addr))
310 }
311 }
312 }
313
314 fn dial_as_listener(
315 &mut self,
316 addr: Multiaddr,
317 ) -> Result<Self::Dial, TransportError<Self::Error>> {
318 match addr.iter().next() {
319 Some(Protocol::Ip4(a)) => {
320 if !ipv4_global::is_global(a) {
321 debug!("Not dialing non global IP address {:?}.", a);
322 return Err(TransportError::MultiaddrNotSupported(addr));
323 }
324 self.inner.dial_as_listener(addr)
325 }
326 Some(Protocol::Ip6(a)) => {
327 if !ipv6_global::is_global(a) {
328 debug!("Not dialing non global IP address {:?}.", a);
329 return Err(TransportError::MultiaddrNotSupported(addr));
330 }
331 self.inner.dial_as_listener(addr)
332 }
333 _ => {
334 debug!("Not dialing unsupported Multiaddress {:?}.", addr);
335 Err(TransportError::MultiaddrNotSupported(addr))
336 }
337 }
338 }
339
340 fn address_translation(&self, listen: &Multiaddr, observed: &Multiaddr) -> Option<Multiaddr> {
341 self.inner.address_translation(listen, observed)
342 }
343
344 fn poll(
345 mut self: Pin<&mut Self>,
346 cx: &mut Context<'_>,
347 ) -> Poll<TransportEvent<Self::ListenerUpgrade, Self::Error>> {
348 Pin::new(&mut self.inner).poll(cx)
349 }
350}