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 std::{
22    pin::Pin,
23    task::{Context, Poll},
24};
25
26use crate::{
27    multiaddr::{Multiaddr, Protocol},
28    transport::{DialOpts, ListenerId, TransportError, TransportEvent},
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
109    /// configuration.
110    ///
111    /// Most IPv4 addresses are globally reachable;
112    /// unless they are specifically defined as *not* globally reachable.
113    ///
114    /// Non-exhaustive list of notable addresses that are not globally reachable:
115    ///
116    /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified))
117    /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private))
118    /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared))
119    /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback))
120    /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local))
121    /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation))
122    /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking))
123    /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved))
124    /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast))
125    ///
126    /// For the complete overview of which addresses are globally reachable, see the table at the
127    /// [IANA IPv4 Special-Purpose Address Registry].
128    ///
129    /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
130    /// [unspecified address]: Ipv4Addr::UNSPECIFIED
131    /// [broadcast address]: Ipv4Addr::BROADCAST
132    #[must_use]
133    #[inline]
134    pub(crate) const fn is_global(a: Ipv4Addr) -> bool {
135        !(a.octets()[0] == 0 // "This network"
136            || is_private(a)
137            || is_shared(a)
138            || a.is_loopback()
139            || a.is_link_local()
140            // addresses reserved for future protocols (`192.0.0.0/24`)
141            ||(a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0)
142            || a.is_documentation()
143            || is_benchmarking(a)
144            || is_reserved(a)
145            || a.is_broadcast())
146    }
147}
148
149/// This module contains an implementation of the `is_global` IPv6 address space.
150///
151/// Credit for this implementation goes to the Rust standard library team.
152///
153/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709)
154mod ipv6_global {
155    use std::net::Ipv6Addr;
156
157    /// Returns `true` if the address is a unicast address with link-local scope,
158    /// as defined in [RFC 4291].
159    ///
160    /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291
161    /// section 2.4]. Note that this encompasses more addresses than those defined in [RFC 4291
162    /// section 2.5.6], which describes "Link-Local IPv6 Unicast Addresses" as having the
163    /// following stricter format:
164    ///
165    /// ```text
166    /// | 10 bits  |         54 bits         |          64 bits           |
167    /// +----------+-------------------------+----------------------------+
168    /// |1111111010|           0             |       interface ID         |
169    /// +----------+-------------------------+----------------------------+
170    /// ```
171    /// So while currently the only addresses with link-local scope an application will encounter
172    /// are all in `fe80::/64`, this might change in the future with the publication of new
173    /// standards. More addresses in `fe80::/10` could be allocated, and those addresses will
174    /// have link-local scope.
175    ///
176    /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`)
177    /// that "it is treated as having Link-Local scope", this does not mean that the loopback
178    /// address actually has link-local scope and this method will return `false` on it.
179    ///
180    /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
181    /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4
182    /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3
183    /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6
184    /// [loopback address]: Ipv6Addr::LOCALHOST
185    #[must_use]
186    #[inline]
187    const fn is_unicast_link_local(a: Ipv6Addr) -> bool {
188        (a.segments()[0] & 0xffc0) == 0xfe80
189    }
190
191    /// Returns [`true`] if this is a unique local address (`fc00::/7`).
192    ///
193    /// This property is defined in [IETF RFC 4193].
194    ///
195    /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193
196    #[must_use]
197    #[inline]
198    const fn is_unique_local(a: Ipv6Addr) -> bool {
199        (a.segments()[0] & 0xfe00) == 0xfc00
200    }
201
202    /// Returns [`true`] if this is an address reserved for documentation
203    /// (`2001:db8::/32`).
204    ///
205    /// This property is defined in [IETF RFC 3849].
206    ///
207    /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849
208    #[must_use]
209    #[inline]
210    const fn is_documentation(a: Ipv6Addr) -> bool {
211        (a.segments()[0] == 0x2001) && (a.segments()[1] == 0xdb8)
212    }
213
214    /// Returns [`true`] if the address appears to be globally reachable
215    /// as specified by the [IANA IPv6 Special-Purpose Address Registry].
216    /// Whether or not an address is practically reachable will depend on your network
217    /// configuration.
218    ///
219    /// Most IPv6 addresses are globally reachable;
220    /// unless they are specifically defined as *not* globally reachable.
221    ///
222    /// Non-exhaustive list of notable addresses that are not globally reachable:
223    /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified))
224    /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback))
225    /// - IPv4-mapped addresses
226    /// - Addresses reserved for benchmarking
227    /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation))
228    /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local))
229    /// - Unicast addresses with link-local scope
230    ///   ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local))
231    ///
232    /// For the complete overview of which addresses are globally reachable, see the table at the
233    /// [IANA IPv6 Special-Purpose Address Registry].
234    ///
235    /// Note that an address having global scope is not the same as being globally reachable,
236    /// and there is no direct relation between the two concepts: There exist addresses with global
237    /// scope that are not globally reachable (for example unique local addresses),
238    /// and addresses that are globally reachable without having global scope
239    /// (multicast addresses with non-global scope).
240    ///
241    /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
242    /// [unspecified address]: Ipv6Addr::UNSPECIFIED
243    /// [loopback address]: Ipv6Addr::LOCALHOST
244    #[must_use]
245    #[inline]
246    pub(crate) const fn is_global(a: Ipv6Addr) -> bool {
247        !(a.is_unspecified()
248            || a.is_loopback()
249            // IPv4-mapped Address (`::ffff:0:0/96`)
250            || matches!(a.segments(), [0, 0, 0, 0, 0, 0xffff, _, _])
251            // IPv4-IPv6 Translat. (`64:ff9b:1::/48`)
252            || matches!(a.segments(), [0x64, 0xff9b, 1, _, _, _, _, _])
253            // Discard-Only Address Block (`100::/64`)
254            || matches!(a.segments(), [0x100, 0, 0, 0, _, _, _, _])
255            // IETF Protocol Assignments (`2001::/23`)
256            || (matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200)
257                && !(
258                    // Port Control Protocol Anycast (`2001:1::1`)
259                    u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001
260                    // Traversal Using Relays around NAT Anycast (`2001:1::2`)
261                    || u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002
262                    // AMT (`2001:3::/32`)
263                    || matches!(a.segments(), [0x2001, 3, _, _, _, _, _, _])
264                    // AS112-v6 (`2001:4:112::/48`)
265                    || matches!(a.segments(), [0x2001, 4, 0x112, _, _, _, _, _])
266                    // ORCHIDv2 (`2001:20::/28`)
267                    || matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F)
268                ))
269            || is_documentation(a)
270            || is_unique_local(a)
271            || is_unicast_link_local(a))
272    }
273}
274
275impl<T> Transport<T> {
276    pub fn new(transport: T) -> Self {
277        Transport { inner: transport }
278    }
279}
280
281impl<T: crate::Transport + Unpin> crate::Transport for Transport<T> {
282    type Output = <T as crate::Transport>::Output;
283    type Error = <T as crate::Transport>::Error;
284    type ListenerUpgrade = <T as crate::Transport>::ListenerUpgrade;
285    type Dial = <T as crate::Transport>::Dial;
286
287    fn listen_on(
288        &mut self,
289        id: ListenerId,
290        addr: Multiaddr,
291    ) -> Result<(), TransportError<Self::Error>> {
292        self.inner.listen_on(id, addr)
293    }
294
295    fn remove_listener(&mut self, id: ListenerId) -> bool {
296        self.inner.remove_listener(id)
297    }
298
299    fn dial(
300        &mut self,
301        addr: Multiaddr,
302        opts: DialOpts,
303    ) -> Result<Self::Dial, TransportError<Self::Error>> {
304        match addr.iter().next() {
305            Some(Protocol::Ip4(a)) => {
306                if !ipv4_global::is_global(a) {
307                    tracing::debug!(ip=%a, "Not dialing non global IP address");
308                    return Err(TransportError::MultiaddrNotSupported(addr));
309                }
310                self.inner.dial(addr, opts)
311            }
312            Some(Protocol::Ip6(a)) => {
313                if !ipv6_global::is_global(a) {
314                    tracing::debug!(ip=%a, "Not dialing non global IP address");
315                    return Err(TransportError::MultiaddrNotSupported(addr));
316                }
317                self.inner.dial(addr, opts)
318            }
319            _ => {
320                tracing::debug!(address=%addr, "Not dialing unsupported Multiaddress");
321                Err(TransportError::MultiaddrNotSupported(addr))
322            }
323        }
324    }
325
326    fn poll(
327        mut self: Pin<&mut Self>,
328        cx: &mut Context<'_>,
329    ) -> Poll<TransportEvent<Self::ListenerUpgrade, Self::Error>> {
330        Pin::new(&mut self.inner).poll(cx)
331    }
332}