s2n_quic_core/inet/
ip.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::inet::{
5    ipv4::{IpV4Address, SocketAddressV4},
6    ipv6::{IpV6Address, SocketAddressV6},
7    unspecified::Unspecified,
8};
9use core::fmt;
10
11#[cfg(any(test, feature = "generator"))]
12use bolero_generator::prelude::*;
13
14/// An IP address, either IPv4 or IPv6.
15///
16/// Instead of using `std::net::IPAddr`, this implementation
17/// is geared towards `no_std` environments and zerocopy decoding.
18///
19/// The size is also consistent across target operating systems.
20#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
21#[cfg_attr(any(test, feature = "generator"), derive(TypeGenerator))]
22pub enum IpAddress {
23    Ipv4(IpV4Address),
24    Ipv6(IpV6Address),
25}
26
27impl IpAddress {
28    /// Converts the IP address into IPv4 if it is mapped, otherwise the address is unchanged
29    #[inline]
30    #[must_use]
31    pub fn unmap(self) -> Self {
32        match self {
33            Self::Ipv4(_) => self,
34            Self::Ipv6(addr) => addr.unmap(),
35        }
36    }
37
38    /// Returns `true` if the two addresses are equal from a network perspective.
39    ///
40    /// This will unmap IPv4-mapped addresses to IpV4 tagged enum values
41    #[inline]
42    pub fn unmapped_eq(&self, other: &Self) -> bool {
43        self.unmap() == other.unmap()
44    }
45
46    /// Converts the IP address into IPv6 if it is IPv4, otherwise the address is unchanged
47    #[inline]
48    #[must_use]
49    pub fn to_ipv6_mapped(self) -> IpV6Address {
50        match self {
51            Self::Ipv4(addr) => addr.to_ipv6_mapped(),
52            Self::Ipv6(addr) => addr,
53        }
54    }
55
56    #[inline]
57    #[must_use]
58    pub fn with_port(self, port: u16) -> SocketAddress {
59        match self {
60            Self::Ipv4(addr) => addr.with_port(port).into(),
61            Self::Ipv6(addr) => addr.with_port(port).into(),
62        }
63    }
64}
65
66impl From<IpV4Address> for IpAddress {
67    fn from(ip: IpV4Address) -> Self {
68        Self::Ipv4(ip)
69    }
70}
71
72impl From<IpV6Address> for IpAddress {
73    fn from(ip: IpV6Address) -> Self {
74        Self::Ipv6(ip)
75    }
76}
77
78impl Unspecified for IpAddress {
79    fn is_unspecified(&self) -> bool {
80        match self {
81            Self::Ipv4(addr) => addr.is_unspecified(),
82            Self::Ipv6(addr) => addr.is_unspecified(),
83        }
84    }
85}
86
87#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
88pub enum IpAddressRef<'a> {
89    IPv4(&'a IpV4Address),
90    IPv6(&'a IpV6Address),
91}
92
93impl IpAddressRef<'_> {
94    pub fn to_owned(self) -> IpAddress {
95        match self {
96            Self::IPv4(addr) => IpAddress::Ipv4(*addr),
97            Self::IPv6(addr) => IpAddress::Ipv6(*addr),
98        }
99    }
100}
101
102impl<'a> From<&'a IpV4Address> for IpAddressRef<'a> {
103    fn from(ip: &'a IpV4Address) -> Self {
104        Self::IPv4(ip)
105    }
106}
107
108impl<'a> From<&'a IpV6Address> for IpAddressRef<'a> {
109    fn from(ip: &'a IpV6Address) -> Self {
110        Self::IPv6(ip)
111    }
112}
113
114/// An IP socket address, either IPv4 or IPv6, with a specific port.
115///
116/// Instead of using `std::net::SocketAddr`, this implementation
117/// is geared towards `no_std` environments and zerocopy decoding.
118///
119/// The size is also consistent across target operating systems.
120#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
121#[cfg_attr(any(test, feature = "generator"), derive(TypeGenerator))]
122#[cfg_attr(kani, derive(kani::Arbitrary))]
123pub enum SocketAddress {
124    IpV4(SocketAddressV4),
125    IpV6(SocketAddressV6),
126}
127
128impl SocketAddress {
129    #[inline]
130    pub fn ip(&self) -> IpAddress {
131        match self {
132            SocketAddress::IpV4(addr) => IpAddress::Ipv4(*addr.ip()),
133            SocketAddress::IpV6(addr) => IpAddress::Ipv6(*addr.ip()),
134        }
135    }
136
137    #[inline]
138    pub fn port(&self) -> u16 {
139        match self {
140            SocketAddress::IpV4(addr) => addr.port(),
141            SocketAddress::IpV6(addr) => addr.port(),
142        }
143    }
144
145    #[inline]
146    pub fn set_port(&mut self, port: u16) {
147        match self {
148            SocketAddress::IpV4(addr) => addr.set_port(port),
149            SocketAddress::IpV6(addr) => addr.set_port(port),
150        }
151    }
152
153    #[inline]
154    pub const fn unicast_scope(&self) -> Option<UnicastScope> {
155        match self {
156            Self::IpV4(addr) => addr.unicast_scope(),
157            Self::IpV6(addr) => addr.unicast_scope(),
158        }
159    }
160
161    /// Converts the IP address into a IPv6 mapped address
162    pub fn to_ipv6_mapped(self) -> SocketAddressV6 {
163        match self {
164            Self::IpV4(addr) => addr.to_ipv6_mapped(),
165            Self::IpV6(addr) => addr,
166        }
167    }
168
169    /// Converts the IP address into IPv4 if it is mapped, otherwise the address is unchanged
170    #[inline]
171    #[must_use]
172    pub fn unmap(self) -> Self {
173        match self {
174            Self::IpV4(_) => self,
175            Self::IpV6(addr) => addr.unmap(),
176        }
177    }
178
179    /// Returns `true` if the two addresses are equal from a network perspective.
180    ///
181    /// This will unmap IPv4-mapped addresses to IpV4 tagged enum values
182    #[inline]
183    pub fn unmapped_eq(&self, other: &Self) -> bool {
184        self.unmap() == other.unmap()
185    }
186}
187
188impl Default for SocketAddress {
189    fn default() -> Self {
190        SocketAddress::IpV4(Default::default())
191    }
192}
193
194impl fmt::Display for SocketAddress {
195    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
196        match self {
197            SocketAddress::IpV4(addr) => write!(fmt, "{addr}"),
198            SocketAddress::IpV6(addr) => write!(fmt, "{addr}"),
199        }
200    }
201}
202
203impl Unspecified for SocketAddress {
204    fn is_unspecified(&self) -> bool {
205        match self {
206            SocketAddress::IpV4(addr) => addr.is_unspecified(),
207            SocketAddress::IpV6(addr) => addr.is_unspecified(),
208        }
209    }
210}
211
212impl From<SocketAddressV4> for SocketAddress {
213    fn from(addr: SocketAddressV4) -> Self {
214        SocketAddress::IpV4(addr)
215    }
216}
217
218impl From<SocketAddressV6> for SocketAddress {
219    fn from(addr: SocketAddressV6) -> Self {
220        SocketAddress::IpV6(addr)
221    }
222}
223
224/// An IP socket address, either IPv4 or IPv6, with a specific port.
225///
226/// This is the borrowed version of `SocketAddress`, aimed at zerocopy
227/// use cases.
228#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
229pub enum SocketAddressRef<'a> {
230    IpV4(&'a SocketAddressV4),
231    IpV6(&'a SocketAddressV6),
232}
233
234impl SocketAddressRef<'_> {
235    pub fn to_owned(self) -> SocketAddress {
236        match self {
237            Self::IpV4(addr) => SocketAddress::IpV4(*addr),
238            Self::IpV6(addr) => SocketAddress::IpV6(*addr),
239        }
240    }
241}
242
243//= https://www.rfc-editor.org/rfc/rfc9000#section-21.5.6
244//# Similarly, endpoints could regard a change in address to a link-local
245//# address [RFC4291] or an address in a private-use range [RFC1918] from
246//# a global, unique-local [RFC4193], or non-private address as a
247//# potential attempt at request forgery.
248#[derive(Clone, Copy, Debug, PartialEq, Eq)]
249pub enum UnicastScope {
250    Loopback,
251    LinkLocal,
252    Private,
253    Global,
254}
255
256#[cfg(any(test, feature = "std"))]
257mod std_conversion {
258    use super::*;
259    use std::net;
260
261    impl net::ToSocketAddrs for SocketAddress {
262        type Iter = std::iter::Once<net::SocketAddr>;
263
264        fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
265            match self {
266                Self::IpV4(addr) => addr.to_socket_addrs(),
267                Self::IpV6(addr) => addr.to_socket_addrs(),
268            }
269        }
270    }
271
272    impl net::ToSocketAddrs for SocketAddressRef<'_> {
273        type Iter = std::iter::Once<net::SocketAddr>;
274
275        fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
276            match self {
277                Self::IpV4(addr) => addr.to_socket_addrs(),
278                Self::IpV6(addr) => addr.to_socket_addrs(),
279            }
280        }
281    }
282
283    impl From<SocketAddress> for net::SocketAddr {
284        fn from(address: SocketAddress) -> Self {
285            match address {
286                SocketAddress::IpV4(addr) => addr.into(),
287                SocketAddress::IpV6(addr) => addr.into(),
288            }
289        }
290    }
291
292    impl From<(net::IpAddr, u16)> for SocketAddress {
293        fn from((ip, port): (net::IpAddr, u16)) -> Self {
294            match ip {
295                net::IpAddr::V4(ip) => Self::IpV4((ip, port).into()),
296                net::IpAddr::V6(ip) => Self::IpV6((ip, port).into()),
297            }
298        }
299    }
300
301    impl<'a> From<SocketAddressRef<'a>> for net::SocketAddr {
302        fn from(address: SocketAddressRef<'a>) -> Self {
303            match address {
304                SocketAddressRef::IpV4(addr) => addr.into(),
305                SocketAddressRef::IpV6(addr) => addr.into(),
306            }
307        }
308    }
309
310    impl From<net::SocketAddr> for SocketAddress {
311        fn from(addr: net::SocketAddr) -> Self {
312            match addr {
313                net::SocketAddr::V4(addr) => Self::IpV4(addr.into()),
314                net::SocketAddr::V6(addr) => Self::IpV6(addr.into()),
315            }
316        }
317    }
318}
319
320define_inet_type!(
321    pub struct Protocol {
322        id: u8,
323    }
324);
325
326impl fmt::Debug for Protocol {
327    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
328        f.debug_tuple("ip::Protocol")
329            .field(&format_args!("{self}"))
330            .finish()
331    }
332}
333
334impl fmt::Display for Protocol {
335    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
336        match *self {
337            Self::HOPOPT => "IPv6 Hop-by-Hop Option",
338            Self::ICMP => "Internet Control Message",
339            Self::IPV4 => "IPv4 Encapsulation",
340            Self::TCP => "Transmission Control",
341            Self::UDP => "User Datagram",
342            Self::IPV6 => "IPv6 Encapsulation",
343            Self::IPV6_ROUTE => "Routing Header for IPv6",
344            Self::IPV6_FRAG => "Fragment Header for IPv6",
345            Self::IPV6_ICMP => "ICMP for IPv6",
346            Self::IPV6_NO_NXT => "No Next Header for IPv6",
347            Self::IPV6_OPTS => "Destination Options for IPv6",
348            Self::UDPLITE => "Lightweight User Datagram",
349            Self { id } => return write!(f, "[unknown 0x{id:02x}]"),
350        }
351        .fmt(f)
352    }
353}
354
355macro_rules! impl_p {
356    ($fun:ident, $cap:ident, $val:literal) => {
357        pub const $cap: Self = Self { id: $val };
358
359        #[inline]
360        pub const fn $fun(self) -> bool {
361            self.id == $val
362        }
363    };
364}
365
366impl Protocol {
367    // https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
368    // NOTE: these variants were added as the ones we think we'll need. feel free to add more as
369    //       needed.
370    impl_p!(is_hop_by_hop, HOPOPT, 0);
371    impl_p!(is_icmp, ICMP, 1);
372    impl_p!(is_ipv4, IPV4, 4);
373    impl_p!(is_tcp, TCP, 6);
374    impl_p!(is_udp, UDP, 17);
375    impl_p!(is_ipv6, IPV6, 41);
376    impl_p!(is_ipv6_route, IPV6_ROUTE, 43);
377    impl_p!(is_ipv6_fragment, IPV6_FRAG, 44);
378    impl_p!(is_ipv6_icmp, IPV6_ICMP, 58);
379    impl_p!(is_ipv6_no_next, IPV6_NO_NXT, 59);
380    impl_p!(is_ipv6_options, IPV6_OPTS, 60);
381    impl_p!(is_udplite, UDPLITE, 136);
382}
383
384#[cfg(test)]
385mod tests {
386    use super::*;
387    use std::net::{SocketAddr, ToSocketAddrs};
388
389    const TESTS: &[&str] = &[
390        "127.0.0.1:80",
391        "192.168.1.1:40",
392        "255.255.255.255:12345",
393        "[::]:443",
394        "[::1]:123",
395        "[2001:0db8:85a3:0001:0002:8a2e:0370:7334]:9000",
396    ];
397
398    #[test]
399    //= https://www.rfc-editor.org/rfc/rfc5156#section-2.2
400    //= type=test
401    //# ::FFFF:0:0/96 are the IPv4-mapped addresses [RFC4291].
402    fn to_ipv6_mapped_test() {
403        for test in TESTS.iter() {
404            // assert that this implementation matches the standard library
405            let addr: SocketAddr = test.parse().unwrap();
406            let address: SocketAddress = addr.into();
407            let addr = match addr {
408                SocketAddr::V4(addr) => {
409                    let ip = addr.ip().to_ipv6_mapped();
410                    (ip, addr.port()).into()
411                }
412                _ => addr,
413            };
414            let address = address.to_ipv6_mapped().into();
415            assert_eq!(addr, address);
416        }
417    }
418
419    #[test]
420    fn unmap_test() {
421        for test in TESTS.iter() {
422            // assert that unmap correctly converts IPv4 mapped addresses
423            let addr: SocketAddr = test.parse().unwrap();
424            let address: SocketAddress = addr.into();
425            let actual: SocketAddress = address.to_ipv6_mapped().into();
426            let actual = actual.unmap();
427            assert_eq!(address, actual);
428        }
429    }
430
431    #[test]
432    fn display_test() {
433        for test in TESTS.iter() {
434            // assert that this implementation matches the standard library
435            let addr: SocketAddr = test.parse().unwrap();
436            let address: SocketAddress = addr.into();
437            assert_eq!(addr.to_string(), address.to_string());
438        }
439    }
440
441    #[test]
442    fn to_socket_addrs_test() {
443        for test in TESTS.iter() {
444            let addr: SocketAddr = test.parse().unwrap();
445            let address: SocketAddress = addr.into();
446            for address in address.to_socket_addrs().unwrap() {
447                assert_eq!(addr, address);
448            }
449        }
450    }
451}