diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs index 2b6d0d7d5daa7..c91df8f699095 100644 --- a/library/std/src/net/ip.rs +++ b/library/std/src/net/ip.rs @@ -84,13 +84,62 @@ pub struct Ipv4Addr { /// IPv6 addresses are defined as 128-bit integers in [IETF RFC 4291]. /// They are usually represented as eight 16-bit segments. /// -/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses. -/// /// The size of an `Ipv6Addr` struct may vary depending on the target operating /// system. /// /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 /// +/// # Embedding IPv4 Addresses +/// +/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses. +/// +/// To assist in the transition from IPv4 to IPv6 two types of IPv6 addresses that embed an IPv4 address were defined: +/// IPv4-compatible and IPv4-mapped addresses. Of these IPv4-compatible addresses have been officially deprecated and are only minimally supported. +/// +/// ## IPv4-Compatible IPv6 Addresses +/// +/// IPv4-compatible IPv6 addresses are defined in [IETF RFC 4291 Section 2.5.5.1], and have been officially deprecated. +/// The RFC describes the format of an "IPv4-Compatible IPv6 address" as follows: +/// +/// ```text +/// | 80 bits | 16 | 32 bits | +/// +--------------------------------------+--------------------------+ +/// |0000..............................0000|0000| IPv4 address | +/// +--------------------------------------+----+---------------------+ +/// ``` +/// So `::a.b.c.d` would be an IPv4-compatible IPv6 address representing the IPv4 address `a.b.c.d`. +/// +/// Despite IPv4-compatible IPv6 addresses having been officially deprecated, there is minimal support for handling these addresses: +/// Only converting to and from these addresses is supported, see [`Ipv4Addr::to_ipv6_compatible`] and [`Ipv6Addr::to_ipv4`]. +/// No further special meaning is ascribed to these addresses; for example [`is_loopback`](Ipv6Addr::is_loopback) will return `false` for `::127.0.0.1`, +/// even though it represents the IPv4 address `127.0.0.1` (which is a loopback address). +/// +/// [IETF RFC 4291 Section 2.5.5.1]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.1 +/// +/// ## IPv4-Mapped IPv6 Addresses +/// +/// IPv4-mapped IPv6 addresses are defined in [IETF RFC 4291 Section 2.5.5.2]. +/// The RFC describes the format of an "IPv4-Mapped IPv6 address" as follows: +/// +/// ```text +/// | 80 bits | 16 | 32 bits | +/// +--------------------------------------+--------------------------+ +/// |0000..............................0000|FFFF| IPv4 address | +/// +--------------------------------------+----+---------------------+ +/// ``` +/// So `::ffff:a.b.c.d` would be an IPv4-mapped IPv6 address representing the IPv4 address `a.b.c.d`. +/// +/// There is more support for handling these addresses than there is for IPv4-compatible addresses: +/// Converting to and from these addresses is supported, see [`Ipv4Addr::to_ipv6_mapped`] and [`Ipv6Addr::to_ipv4`]. +/// There is also rudimentary support for the embedded IPv4 addresses; for example [`is_loopback`](Ipv6Addr::is_loopback) will return `true` for `::ffff:127.0.0.1`, +/// because it represents the IPv4 address `127.0.0.1` (which is a loopback address). +/// +/// Note: Currently `is_loopback` is the only method that is aware of IPv4-mapped addresses. +/// +/// This support for the embedded IPv4 addresses is in line with other languages and the behaviour of many real-world networking hardware. +/// +/// [IETF RFC 4291 Section 2.5.5.2]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2 +/// /// # Textual representation /// /// `Ipv6Addr` provides a [`FromStr`] implementation. There are many ways to represent @@ -758,13 +807,14 @@ impl Ipv4Addr { } } - /// Converts this address to an IPv4-compatible [`IPv6` address]. + /// Converts this address to an [IPv4-compatible] [`IPv6` address]. /// /// `a.b.c.d` becomes `::a.b.c.d` /// - /// This isn't typically the method you want; these addresses don't typically - /// function on modern systems. Use `to_ipv6_mapped` instead. + /// Note that IPv4-compatible addresses have been officially deprecated. + /// If you don't explicitly need an IPv4-compatible address for legacy reasons, consider using `to_ipv6_mapped` instead. /// + /// [IPv4-compatible]: Ipv6Addr#ipv4-compatible-ipv6-addresses /// [`IPv6` address]: Ipv6Addr /// /// # Examples @@ -772,10 +822,10 @@ impl Ipv4Addr { /// ``` /// use std::net::{Ipv4Addr, Ipv6Addr}; /// - /// assert_eq!( - /// Ipv4Addr::new(192, 0, 2, 255).to_ipv6_compatible(), - /// Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x2ff) - /// ); + /// // ::192.0.2.255 + /// let ipv6_compatible = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x2ff); + /// + /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).to_ipv6_compatible(), ipv6_compatible); /// ``` #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(feature = "rust1", since = "1.0.0")] @@ -787,10 +837,11 @@ impl Ipv4Addr { } } - /// Converts this address to an IPv4-mapped [`IPv6` address]. + /// Converts this address to an [IPv4-mapped] [`IPv6` address]. /// /// `a.b.c.d` becomes `::ffff:a.b.c.d` /// + /// [IPv4-mapped]: Ipv6Addr#ipv4-mapped-ipv6-addresses /// [`IPv6` address]: Ipv6Addr /// /// # Examples @@ -798,8 +849,10 @@ impl Ipv4Addr { /// ``` /// use std::net::{Ipv4Addr, Ipv6Addr}; /// - /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).to_ipv6_mapped(), - /// Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x2ff)); + /// // ::ffff:192.0.2.255 + /// let ipv6_mapped = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x2ff); + /// + /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).to_ipv6_mapped(), ipv6_mapped); /// ``` #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(feature = "rust1", since = "1.0.0")] @@ -1193,25 +1246,47 @@ impl Ipv6Addr { u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets()) } - /// Returns [`true`] if this is a loopback address (::1). + /// Returns [`true`] if this is either: + /// - the [loopback address] as defined in [IETF RFC 4291 section 2.5.3] (`::1`). + /// - an [IPv4-mapped] address representing an IPv4 loopback address as defined in [IETF RFC 1122] (`::ffff:127.0.0.0/104`). /// - /// This property is defined in [IETF RFC 4291]. + /// Note that this returns [`false`] for an [IPv4-compatible] address representing an IPv4 loopback address (`::127.0.0.0/104`). /// - /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [loopback address]: Ipv6Addr::LOCALHOST + /// [IETF RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 + /// [IETF RFC 1122]: https://tools.ietf.org/html/rfc1122 + /// [IPv4-mapped]: Ipv6Addr#ipv4-mapped-ipv6-addresses + /// [IPv4-compatible]: Ipv6Addr#ipv4-compatible-ipv6-addresses /// /// # Examples /// /// ``` /// use std::net::Ipv6Addr; /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_loopback(), false); + /// // `true` for the IPv6 loopback address (`::1`) + /// assert_eq!(Ipv6Addr::LOCALHOST.is_loopback(), true); /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).is_loopback(), true); + /// + /// use std::net::Ipv4Addr; + /// + /// // `true` for an IPv4-mapped address representing an IPv4 loopback address (`::ffff:127.0.0.1`) + /// assert_eq!(Ipv4Addr::LOCALHOST.to_ipv6_mapped().is_loopback(), true); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1).is_loopback(), true); + /// + /// // `false` for an IPv4-compatible address representing an IPv4 loopback address (`::127.0.0.1`) + /// assert_eq!(Ipv4Addr::LOCALHOST.to_ipv6_compatible().is_loopback(), false); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x7f00, 0x1).is_loopback(), false); /// ``` #[rustc_const_stable(feature = "const_ipv6", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] + #[rustc_allow_const_fn_unstable(const_ipv6)] #[inline] pub const fn is_loopback(&self) -> bool { - u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets()) + if let Some(ipv4) = self.to_ipv4_mapped() { + ipv4.is_loopback() + } else { + u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets()) + } } /// Returns [`true`] if the address appears to be globally routable. @@ -1509,13 +1584,14 @@ impl Ipv6Addr { (self.segments()[0] & 0xff00) == 0xff00 } - /// Converts this address to an [`IPv4` address] if it's an "IPv4-mapped IPv6 address" + /// Converts this address to an [`IPv4` address] if it's an [IPv4-mapped] address as /// defined in [IETF RFC 4291 section 2.5.5.2], otherwise returns [`None`]. /// /// `::ffff:a.b.c.d` becomes `a.b.c.d`. /// All addresses *not* starting with `::ffff` will return `None`. /// /// [`IPv4` address]: Ipv4Addr + /// [IPv4-mapped IPv6 address]: Ipv6Addr /// [IETF RFC 4291 section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2 /// /// # Examples @@ -1525,10 +1601,18 @@ impl Ipv6Addr { /// /// use std::net::{Ipv4Addr, Ipv6Addr}; /// + /// let ipv4 = Ipv4Addr::new(192, 10, 2, 255); + /// let ipv6_compatible = ipv4.to_ipv6_compatible(); + /// let ipv6_mapped = ipv4.to_ipv6_mapped(); + /// + /// // Only IPv4-mapped addresses are converted. + /// assert_eq!(ipv6_compatible.to_ipv4_mapped(), None); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc00a, 0x2ff).to_ipv4_mapped(), None); + /// assert_eq!(ipv6_mapped.to_ipv4_mapped(), Some(ipv4)); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4_mapped(), Some(ipv4)); + /// + /// // Addresses that are neither an IPv4-compatible or IPv4-mapped address are not converted. /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4_mapped(), None); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4_mapped(), - /// Some(Ipv4Addr::new(192, 10, 2, 255))); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4_mapped(), None); /// ``` #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] #[unstable(feature = "ip", issue = "27709")] @@ -1542,23 +1626,34 @@ impl Ipv6Addr { } } - /// Converts this address to an [`IPv4` address]. Returns [`None`] if this address is - /// neither IPv4-compatible or IPv4-mapped. + /// Converts this address to an [`IPv4` address] if it's an [IPv4-compatible] address as defined in [IETF RFC 4291 section 2.5.5.1] + /// or an [IPv4-mapped] address as defined in [IETF RFC 4291 section 2.5.5.2], otherwise returns [`None`]. /// /// `::a.b.c.d` and `::ffff:a.b.c.d` become `a.b.c.d` /// /// [`IPv4` address]: Ipv4Addr + /// [IPv4-compatible]: Ipv6Addr#ipv4-compatible-ipv6-addresses + /// [IPv4-mapped]: Ipv6Addr#ipv4-mapped-ipv6-addresses + /// [IETF RFC 4291 section 2.5.5.1]: https://tools.ietf.org/html/rfc4291#section-2.5.5.1 + /// [IETF RFC 4291 section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2 /// /// # Examples /// /// ``` /// use std::net::{Ipv4Addr, Ipv6Addr}; /// + /// let ipv4 = Ipv4Addr::new(192, 10, 2, 255); + /// let ipv6_compatible = ipv4.to_ipv6_compatible(); + /// let ipv6_mapped = ipv4.to_ipv6_mapped(); + /// + /// // Both IPv4-compatible and IPv4-mapped addresses are converted. + /// assert_eq!(ipv6_compatible.to_ipv4(), Some(ipv4)); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc00a, 0x2ff).to_ipv4(), Some(ipv4)); + /// assert_eq!(ipv6_mapped.to_ipv4(), Some(ipv4)); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4(), Some(ipv4)); + /// + /// // Addresses that are neither an IPv4-compatible or IPv4-mapped address are not converted. /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4(), None); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4(), - /// Some(Ipv4Addr::new(192, 10, 2, 255))); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4(), - /// Some(Ipv4Addr::new(0, 0, 0, 1))); /// ``` #[rustc_const_stable(feature = "const_ipv6", since = "1.50.0")] #[stable(feature = "rust1", since = "1.0.0")]