-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Change Ipv6Addr::is_loopback
to include IPv4-mapped loopback addresses
#85655
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,24 +807,25 @@ 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 | ||
/// | ||
/// ``` | ||
/// 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,19 +837,22 @@ 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 | ||
/// | ||
/// ``` | ||
/// 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()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not changed in this PR, but historical background for anyone wondering why the comparison is written like this:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
} | ||
|
||
/// 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")] | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ipv6Addr::to_ipv4_mapped
is unstable and unstable const.