From 679a3ea5bff820cc7f1aa422c06d77aedc2552b8 Mon Sep 17 00:00:00 2001 From: Christiaan Dirkx Date: Fri, 9 Jul 2021 00:13:26 +0200 Subject: [PATCH 1/4] Change `ip.rs` to `mod.rs` --- library/std/src/net/{ip.rs => ip/mod.rs} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename library/std/src/net/{ip.rs => ip/mod.rs} (100%) diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip/mod.rs similarity index 100% rename from library/std/src/net/ip.rs rename to library/std/src/net/ip/mod.rs index 4b6d60d121e13..42e6a53ca7ba7 100644 --- a/library/std/src/net/ip.rs +++ b/library/std/src/net/ip/mod.rs @@ -1,7 +1,3 @@ -// Tests for this module -#[cfg(all(test, not(target_os = "emscripten")))] -mod tests; - use crate::cmp::Ordering; use crate::fmt::{self, Write as FmtWrite}; use crate::hash; @@ -10,6 +6,10 @@ use crate::mem::transmute; use crate::sys::net::netc as c; use crate::sys_common::{AsInner, FromInner, IntoInner}; +// Tests for this module +#[cfg(all(test, not(target_os = "emscripten")))] +mod tests; + /// An IP address, either IPv4 or IPv6. /// /// This enum can contain either an [`Ipv4Addr`] or an [`Ipv6Addr`], see their From 0a8b944dbed30da30166153e4d9ac40aa64ed670 Mon Sep 17 00:00:00 2001 From: Christiaan Dirkx Date: Fri, 9 Jul 2021 04:18:37 +0200 Subject: [PATCH 2/4] Add `Ipv{v,6}AddrPrefix` --- library/std/src/net/ip/mod.rs | 16 ++ library/std/src/net/ip/network.rs | 392 ++++++++++++++++++++++++++++++ library/std/src/net/ip/tests.rs | 117 +++++++++ library/std/src/net/mod.rs | 4 +- 4 files changed, 528 insertions(+), 1 deletion(-) create mode 100644 library/std/src/net/ip/network.rs diff --git a/library/std/src/net/ip/mod.rs b/library/std/src/net/ip/mod.rs index 42e6a53ca7ba7..879eaee76885a 100644 --- a/library/std/src/net/ip/mod.rs +++ b/library/std/src/net/ip/mod.rs @@ -10,6 +10,9 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; #[cfg(all(test, not(target_os = "emscripten")))] mod tests; +mod network; +pub use self::network::{Ipv4AddrPrefix, Ipv6AddrPrefix}; + /// An IP address, either IPv4 or IPv6. /// /// This enum can contain either an [`Ipv4Addr`] or an [`Ipv6Addr`], see their @@ -314,6 +317,12 @@ impl Ipv4Addr { Ipv4Addr { inner: c::in_addr { s_addr: u32::from_ne_bytes([a, b, c, d]) } } } + // Private constructor to simplify const code. + // FIXME: remove when From becomes usable in const contexts. + const fn from_u32(address: u32) -> Ipv4Addr { + Ipv4Addr { inner: c::in_addr { s_addr: address.to_be() } } + } + /// An IPv4 address with the address pointing to localhost: `127.0.0.1` /// /// # Examples @@ -1113,6 +1122,13 @@ impl Ipv6Addr { } } + // Private constructor to simplify const code. + // FIXME: remove when From becomes usable in const contexts. + #[rustc_allow_const_fn_unstable(const_fn_transmute)] + const fn from_u128(address: u128) -> Ipv6Addr { + Ipv6Addr { inner: c::in6_addr { s6_addr: address.to_be_bytes() } } + } + /// An IPv6 address representing localhost: `::1`. /// /// # Examples diff --git a/library/std/src/net/ip/network.rs b/library/std/src/net/ip/network.rs new file mode 100644 index 0000000000000..92110e449c276 --- /dev/null +++ b/library/std/src/net/ip/network.rs @@ -0,0 +1,392 @@ +use super::{Ipv4Addr, Ipv6Addr}; +use crate::error::Error; +use crate::fmt; + +/// Represents an [IPv4 address] prefix, describing a network of addresses. +/// +/// An IPv4 address prefix can be represented by an IPv4 address and a prefix length. +/// The prefix length specifies how many of the leftmost contiguous bits of the +/// address comprise the prefix. The prefix then describes the network containing all +/// addresses that start with the same bits as the prefix. +/// +/// The size of an `Ipv6AddrPrefix` struct may vary depending on the target operating system. +/// +/// # Textual representation +/// +/// `Ipv4AddrPrefix` provides a [`FromStr`](crate::str::FromStr) implementation. +/// The textual representation of an address prefix is the representation of an +/// IPv4 address, followed by a separating `/` and then the prefix length in decimal notation; +/// for example `192.0.2.0/24`. +/// +/// +/// # Examples +/// ``` +/// #![feature(ip_prefix)] +/// +/// use std::net::{Ipv4Addr, Ipv4AddrPrefix}; +/// +/// // Create the address prefix `192.0.2.0/24`. +/// let prefix = Ipv4AddrPrefix::new(Ipv4Addr::new(192, 0, 2, 0), 24).unwrap(); +/// +/// // The prefix is uniquely defined by a prefix address and prefix length. +/// assert_eq!(prefix.address(), Ipv4Addr::new(192, 0, 2, 0)); +/// assert_eq!(prefix.len(), 24); +/// +/// // The prefix describes the network of all addresses that start +/// // with the same bits as the prefix. +/// assert_eq!(prefix.contains(&Ipv4Addr::new(192, 0, 2, 0)), true); +/// assert_eq!(prefix.contains(&Ipv4Addr::new(192, 0, 2, 7)), true); +/// assert_eq!(prefix.contains(&Ipv4Addr::new(192, 0, 2, 255)), true); +/// assert_eq!(prefix.contains(&Ipv4Addr::new(192, 0, 3, 0)), false); +/// ``` +/// +/// [IPv4 address]: Ipv4Addr +#[derive(Copy, PartialEq, Eq, Clone, Hash)] +#[unstable(feature = "ip_prefix", issue = "86991")] +pub struct Ipv4AddrPrefix { + address: Ipv4Addr, + len: u8, +} + +/// Represents an [IPv6 address] prefix, as described in [IETF RFC 4291 section 2.3]. +/// +/// An IPv6 address prefix can be represented by an IPv4 address and a prefix length. +/// The prefix length specifies how many of the leftmost contiguous bits of the +/// address comprise the prefix. The prefix then describes the network containing all +/// addresses that start with the same bits as the prefix. +/// +/// The size of an `Ipv6AddrPrefix` struct may vary depending on the target operating system. +/// +/// # Textual representation +/// +/// `Ipv6AddrPrefix` provides a [`FromStr`](crate::str::FromStr) implementation. +/// The textual representation of an address prefix is the representation of an +/// IPv6 address, followed by a separating `/` and then the prefix length in decimal notation; +/// for example `2001:db8::/32`. +/// +/// # Examples +/// ``` +/// #![feature(ip_prefix)] +/// +/// use std::net::{Ipv6Addr, Ipv6AddrPrefix}; +/// +/// // Create the address prefix `2001:db8::/32`. +/// let prefix = Ipv6AddrPrefix::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32).unwrap(); +/// +/// // The prefix is uniquely defined by a prefix address and prefix length. +/// assert_eq!(prefix.address(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)); +/// assert_eq!(prefix.len(), 32); +/// +/// // The prefix describes the network of all addresses that start +/// // with the same bits as the prefix. +/// assert_eq!(prefix.contains(&Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)), true); +/// assert_eq!(prefix.contains(&Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 7)), true); +/// assert_eq!(prefix.contains(&Ipv6Addr::new(0x2001, 0xdb8, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff)), true); +/// assert_eq!(prefix.contains(&Ipv6Addr::new(0x2001, 0xdb9, 0, 0, 0, 0, 0, 0)), false); +/// ``` +/// +/// [IPv6 address]: Ipv6Addr +/// [IETF RFC 4291 section 2.3]: https://tools.ietf.org/html/rfc4291#section-2.3 +#[derive(Copy, PartialEq, Eq, Clone, Hash)] +#[unstable(feature = "ip_prefix", issue = "86991")] +pub struct Ipv6AddrPrefix { + address: Ipv6Addr, + len: u8, +} + +impl Ipv4AddrPrefix { + /// Creates a new IPv4 address prefix from an address and a prefix length. + /// + /// # Examples + /// ``` + /// #![feature(ip_prefix)] + /// + /// use std::net::{Ipv4Addr, Ipv4AddrPrefix}; + /// + /// // Create the address prefix `192.0.2.0/24`. + /// let prefix = Ipv4AddrPrefix::new(Ipv4Addr::new(192, 0, 2, 0), 24).unwrap(); + /// + /// // Error: Prefix length can not be more than 32 bits. + /// Ipv4AddrPrefix::new(Ipv4Addr::new(192, 0, 2, 0), 35).unwrap_err(); + /// ``` + #[unstable(feature = "ip_prefix", issue = "86991")] + #[inline] + pub const fn new(address: Ipv4Addr, len: u32) -> Result { + if len <= u32::BITS { + Ok(Ipv4AddrPrefix::new_unchecked(address, len)) + } else { + Err(InvalidPrefixError { max_len: u32::BITS as u8 }) + } + } + + // Private constructor that assumes len <= 32. + // Useful because `Result::unwrap` is not yet usable in const contexts, so `new` can't be used. + pub(crate) const fn new_unchecked(address: Ipv4Addr, len: u32) -> Ipv4AddrPrefix { + let address = { + let mask = Ipv4AddrPrefix::mask(len); + let masked = u32::from_be_bytes(address.octets()) & mask; + Ipv4Addr::from_u32(masked) + }; + + Ipv4AddrPrefix { address, len: len as u8 } + } + + /// Returns the address specifying this address prefix. + /// + /// The prefix address and the prefix length together uniquely define an address prefix. + /// + /// # Examples + /// ``` + /// #![feature(ip_prefix)] + /// + /// use std::net::{Ipv4Addr, Ipv4AddrPrefix}; + /// + /// // `192.0.2.0/24` + /// let prefix = Ipv4AddrPrefix::new(Ipv4Addr::new(192, 0, 2, 7), 24).unwrap(); + /// + /// // Note that the address can be different than the address the + /// // prefix was constructed with. The returned address only contains the bits + /// // specified by the prefix length. + /// assert_eq!(prefix.address(), Ipv4Addr::new(192, 0, 2, 0)); + /// assert_ne!(prefix.address(), Ipv4Addr::new(192, 0, 2, 7)); + /// ``` + #[unstable(feature = "ip_prefix", issue = "86991")] + #[inline] + pub const fn address(&self) -> Ipv4Addr { + self.address + } + + /// Returns the prefix length of this address prefix. + /// + /// The prefix address and the prefix length together uniquely define an address prefix. + /// + /// # Examples + /// ``` + /// #![feature(ip_prefix)] + /// + /// use std::net::{Ipv4Addr, Ipv4AddrPrefix}; + /// + /// // `192.0.2.0/24` + /// let prefix = Ipv4AddrPrefix::new(Ipv4Addr::new(192, 0, 2, 0), 24).unwrap(); + /// + /// assert_eq!(prefix.len(), 24); + /// ``` + #[unstable(feature = "ip_prefix", issue = "86991")] + #[inline] + pub const fn len(&self) -> u32 { + self.len as u32 + } + + // Compute the bitmask specified by a prefix length. + const fn mask(len: u32) -> u32 { + if len == 0 { + 0 + } else { + // shift will not overflow as len > 0, so u32::BITS - len < 32 + u32::MAX << (u32::BITS - len) + } + } + + /// Returns `true` if the given address is contained in the network described by this prefix, + /// meaning that it starts with the same bits as the prefix. + /// + /// # Examples + /// ``` + /// #![feature(ip_prefix)] + /// + /// use std::net::{Ipv4Addr, Ipv4AddrPrefix}; + /// + /// // `192.0.2.0/24` + /// let prefix = Ipv4AddrPrefix::new(Ipv4Addr::new(192, 0, 2, 0), 24).unwrap(); + /// + /// assert_eq!(prefix.contains(&Ipv4Addr::new(192, 0, 2, 7)), true); + /// assert_eq!(prefix.contains(&Ipv4Addr::new(192, 0, 3, 0)), false); + /// ``` + #[unstable(feature = "ip_prefix", issue = "86991")] + #[inline] + pub const fn contains(&self, address: &Ipv4Addr) -> bool { + let mask = Ipv4AddrPrefix::mask(self.len as u32); + u32::from_be_bytes(address.octets()) & mask == u32::from_be_bytes(self.address.octets()) + } +} + +#[unstable(feature = "ip_prefix", issue = "86991")] +impl fmt::Display for Ipv4AddrPrefix { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "{}/{}", self.address, self.len) + } +} + +#[unstable(feature = "ip_prefix", issue = "86991")] +impl fmt::Debug for Ipv4AddrPrefix { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } +} + +#[unstable(feature = "ip_prefix", issue = "86991")] +impl From for Ipv4AddrPrefix { + /// Converts an IPv4 address `a.b.c.d` to the prefix `a.b.c.d/32`. + fn from(address: Ipv4Addr) -> Self { + Ipv4AddrPrefix { address, len: u32::BITS as u8 } + } +} + +impl Ipv6AddrPrefix { + /// Creates a new IPv6 address prefix from an address and a prefix length. + /// + /// # Examples + /// ``` + /// #![feature(ip_prefix)] + /// + /// use std::net::{Ipv6Addr, Ipv6AddrPrefix}; + /// + /// // Create the address prefix `2001:db8::/32`. + /// let prefix = Ipv6AddrPrefix::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32).unwrap(); + /// + /// // Error: Prefix length can not be more than 128 bits. + /// Ipv6AddrPrefix::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 130).unwrap_err(); + /// ``` + #[unstable(feature = "ip_prefix", issue = "86991")] + #[inline] + pub const fn new(address: Ipv6Addr, len: u32) -> Result { + if len <= u128::BITS { + Ok(Ipv6AddrPrefix::new_unchecked(address, len)) + } else { + Err(InvalidPrefixError { max_len: u128::BITS as u8 }) + } + } + + // Private constructor that assumes len <= 128. + // Useful because `Result::unwrap` is not yet usable in const contexts, so `new` can't be used. + pub(crate) const fn new_unchecked(address: Ipv6Addr, len: u32) -> Ipv6AddrPrefix { + let address = { + let mask = Ipv6AddrPrefix::mask(len); + let masked = u128::from_be_bytes(address.octets()) & mask; + Ipv6Addr::from_u128(masked) + }; + + Ipv6AddrPrefix { address, len: len as u8 } + } + + /// Returns the address specifying this address prefix. + /// + /// The prefix address and the prefix length together uniquely define an address prefix. + /// + /// # Examples + /// ``` + /// #![feature(ip_prefix)] + /// + /// use std::net::{Ipv6Addr, Ipv6AddrPrefix}; + /// + /// // `2001:db8::/32` + /// let prefix = Ipv6AddrPrefix::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 7), 32).unwrap(); + /// + /// // Note that the address can be different than the address the + /// // prefix was constructed with. The returned address only contains the bits + /// // specified by the prefix length. + /// assert_eq!(prefix.address(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)); + /// assert_ne!(prefix.address(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 7)); + /// ``` + #[unstable(feature = "ip_prefix", issue = "86991")] + #[inline] + pub const fn address(&self) -> Ipv6Addr { + self.address + } + + /// Returns the prefix length of this address prefix. + /// + /// The prefix address and the prefix length together uniquely define an address prefix. + /// + /// # Examples + /// ``` + /// #![feature(ip_prefix)] + /// + /// use std::net::{Ipv6Addr, Ipv6AddrPrefix}; + /// + /// // `2001:db8::/32` + /// let prefix = Ipv6AddrPrefix::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32).unwrap(); + /// assert_eq!(prefix.len(), 32); + /// ``` + #[unstable(feature = "ip_prefix", issue = "86991")] + #[inline] + pub const fn len(&self) -> u32 { + self.len as u32 + } + + // Compute the bitmask specified by a prefix length. + const fn mask(len: u32) -> u128 { + if len == 0 { + 0 + } else { + // shift will not overflow as len > 0, so u128::BITS - len < 128 + u128::MAX << (u128::BITS - len) + } + } + + /// Returns `true` if the given address is contained in the network described by this prefix, + /// meaning that it starts with the same bits as the prefix. + /// + /// # Examples + /// ``` + /// #![feature(ip_prefix)] + /// + /// use std::net::{Ipv6Addr, Ipv6AddrPrefix}; + /// + /// // `2001:db8::/32` + /// let prefix = Ipv6AddrPrefix::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32).unwrap(); + /// + /// assert_eq!(prefix.contains(&Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 7)), true); + /// assert_eq!(prefix.contains(&Ipv6Addr::new(0x2001, 0xdb9, 0, 0, 0, 0, 0, 0)), false); + /// ``` + #[unstable(feature = "ip_prefix", issue = "86991")] + #[inline] + pub const fn contains(&self, address: &Ipv6Addr) -> bool { + let mask = Ipv6AddrPrefix::mask(self.len as u32); + u128::from_be_bytes(address.octets()) & mask == u128::from_be_bytes(self.address.octets()) + } +} + +#[unstable(feature = "ip_prefix", issue = "86991")] +impl fmt::Display for Ipv6AddrPrefix { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "{}/{}", self.address, self.len) + } +} + +#[unstable(feature = "ip_prefix", issue = "86991")] +impl fmt::Debug for Ipv6AddrPrefix { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } +} + +#[unstable(feature = "ip_prefix", issue = "86991")] +impl From for Ipv6AddrPrefix { + /// Converts an IPv6 address `a:b:c:d:e:f:g:h` to the prefix `a:b:c:d:e:f:g:h/128`. + fn from(address: Ipv6Addr) -> Self { + Ipv6AddrPrefix { address, len: u128::BITS as u8 } + } +} + +#[unstable(feature = "ip_prefix", issue = "86991")] +pub struct InvalidPrefixError { + max_len: u8, +} + +#[unstable(feature = "ip_prefix", issue = "86991")] +impl Error for InvalidPrefixError {} + +#[unstable(feature = "ip_prefix", issue = "86991")] +impl fmt::Display for InvalidPrefixError { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "Prefix length can not be more than {} bits.", self.max_len) + } +} + +#[unstable(feature = "ip_prefix", issue = "86991")] +impl fmt::Debug for InvalidPrefixError { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("InvalidPrefixError").finish_non_exhaustive() + } +} diff --git a/library/std/src/net/ip/tests.rs b/library/std/src/net/ip/tests.rs index 2109980ad058d..7946399bff2ce 100644 --- a/library/std/src/net/ip/tests.rs +++ b/library/std/src/net/ip/tests.rs @@ -795,6 +795,90 @@ fn is_v6() { assert!(ip.is_ipv6()); } +#[test] +fn ip_prefix_constructor() { + let ipv4 = Ipv4Addr::UNSPECIFIED; + assert!(Ipv4AddrPrefix::new(ipv4, 24).is_ok()); + assert!(Ipv4AddrPrefix::new(ipv4, 35).is_err()); + + let ipv6 = Ipv6Addr::UNSPECIFIED; + assert!(Ipv6AddrPrefix::new(ipv6, 96).is_ok()); + assert!(Ipv6AddrPrefix::new(ipv6, 130).is_err()); +} + +#[test] +fn ip_prefix_address() { + // Prefix address is not always the same as the address used to construct the prefix. + // The address only contains the bits specified by the prefix length. + + let ipv4 = Ipv4AddrPrefix::new_unchecked(Ipv4Addr::new(1, 2, 3, 4), 24); + assert_ne!(ipv4.address(), Ipv4Addr::new(1, 2, 3, 4)); + assert_eq!(ipv4.address(), Ipv4Addr::new(1, 2, 3, 0)); + + let ipv6 = Ipv6AddrPrefix::new_unchecked(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 96); + assert_ne!(ipv6.address(), Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)); + assert_eq!(ipv6.address(), Ipv6Addr::new(1, 2, 3, 4, 5, 6, 0, 0)); +} + +#[test] +fn ipv4_prefix_contains() { + let address = Ipv4Addr::new(1, 2, 3, 4); + assert!(Ipv4AddrPrefix::new_unchecked(Ipv4Addr::new(0, 0, 0, 0), 0).contains(&address)); + assert!(Ipv4AddrPrefix::new_unchecked(Ipv4Addr::new(1, 0, 0, 0), 8).contains(&address)); + assert!(Ipv4AddrPrefix::new_unchecked(Ipv4Addr::new(1, 2, 0, 0), 16).contains(&address)); + assert!(Ipv4AddrPrefix::new_unchecked(Ipv4Addr::new(1, 2, 3, 0), 24).contains(&address)); + assert!(Ipv4AddrPrefix::new_unchecked(Ipv4Addr::new(1, 2, 3, 4), 32).contains(&address)); + + // test a prefix that is not a multiple of 8 + let prefix = Ipv4AddrPrefix::new_unchecked(Ipv4Addr::new(0, 0b01100100, 0, 0), 14); + assert_eq!(prefix.contains(&Ipv4Addr::new(0, 0b01100011, 0, 0)), false); + assert_eq!(prefix.contains(&Ipv4Addr::new(0, 0b01100100, 0, 0)), true); + assert_eq!(prefix.contains(&Ipv4Addr::new(0, 0b01100111, 0, 0)), true); + assert_eq!(prefix.contains(&Ipv4Addr::new(0, 0b01101000, 0, 0)), false); +} + +#[test] +fn ipv6_prefix_contains() { + let address = Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8); + assert!( + Ipv6AddrPrefix::new_unchecked(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 0).contains(&address) + ); + assert!( + Ipv6AddrPrefix::new_unchecked(Ipv6Addr::new(1, 0, 0, 0, 0, 0, 0, 0), 16).contains(&address) + ); + assert!( + Ipv6AddrPrefix::new_unchecked(Ipv6Addr::new(1, 2, 0, 0, 0, 0, 0, 0), 32).contains(&address) + ); + assert!( + Ipv6AddrPrefix::new_unchecked(Ipv6Addr::new(1, 2, 3, 0, 0, 0, 0, 0), 48).contains(&address) + ); + assert!( + Ipv6AddrPrefix::new_unchecked(Ipv6Addr::new(1, 2, 3, 4, 0, 0, 0, 0), 64).contains(&address) + ); + assert!( + Ipv6AddrPrefix::new_unchecked(Ipv6Addr::new(1, 2, 3, 4, 5, 0, 0, 0), 80).contains(&address) + ); + assert!( + Ipv6AddrPrefix::new_unchecked(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 0, 0), 96).contains(&address) + ); + assert!( + Ipv6AddrPrefix::new_unchecked(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 0), 112) + .contains(&address) + ); + assert!( + Ipv6AddrPrefix::new_unchecked(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 128) + .contains(&address) + ); + + // test a prefix that is not a multiple of 16 + let prefix = + Ipv6AddrPrefix::new_unchecked(Ipv6Addr::new(0, 0b01100100_00000000, 0, 0, 0, 0, 0, 0), 22); + assert_eq!(prefix.contains(&Ipv6Addr::new(0, 0b01100011_11111111, 0, 0, 0, 0, 0, 0)), false); + assert_eq!(prefix.contains(&Ipv6Addr::new(0, 0b01100100_00000000, 0, 0, 0, 0, 0, 0)), true); + assert_eq!(prefix.contains(&Ipv6Addr::new(0, 0b01100111_11111111, 0, 0, 0, 0, 0, 0)), true); + assert_eq!(prefix.contains(&Ipv6Addr::new(0, 0b01101000_00000000, 0, 0, 0, 0, 0, 0)), false); +} + #[test] fn ipv4_const() { // test that the methods of `Ipv4Addr` are usable in a const context @@ -922,3 +1006,36 @@ fn ip_const() { const IS_IP_V6: bool = IP_ADDRESS.is_ipv6(); assert!(!IS_IP_V6); } + +#[test] +fn ipv4_prefix_const() { + // test that the methods of `Ipv4AddrPrefix` are usable in a const context + + const IP_PREFIX: Ipv4AddrPrefix = Ipv4AddrPrefix::new_unchecked(Ipv4Addr::new(1, 2, 3, 4), 24); + + const PREFIX_ADDRESS: Ipv4Addr = IP_PREFIX.address(); + assert_eq!(PREFIX_ADDRESS, Ipv4Addr::new(1, 2, 3, 0)); + + const PREFIX_LENGTH: u32 = IP_PREFIX.len(); + assert_eq!(PREFIX_LENGTH, 24); + + const CONTAINS: bool = IP_PREFIX.contains(&Ipv4Addr::new(1, 2, 3, 4)); + assert!(CONTAINS); +} + +#[test] +fn ipv6_prefix_const() { + // test that the methods of `Ipv6AddrPrefix` are usable in a const context + + const IP_PREFIX: Ipv6AddrPrefix = + Ipv6AddrPrefix::new_unchecked(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 96); + + const PREFIX_ADDRESS: Ipv6Addr = IP_PREFIX.address(); + assert_eq!(PREFIX_ADDRESS, Ipv6Addr::new(1, 2, 3, 4, 5, 6, 0, 0)); + + const PREFIX_LENGTH: u32 = IP_PREFIX.len(); + assert_eq!(PREFIX_LENGTH, 96); + + const CONTAINS: bool = IP_PREFIX.contains(&Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)); + assert!(CONTAINS); +} diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index d814e9b25ba9a..edc036c533f96 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -22,7 +22,9 @@ use crate::io::{self, Error, ErrorKind}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::ip::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; +pub use self::ip::{ + IpAddr, Ipv4Addr, Ipv4AddrPrefix, Ipv6Addr, Ipv6AddrPrefix, Ipv6MulticastScope, +}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::parser::AddrParseError; #[stable(feature = "rust1", since = "1.0.0")] From 02f141cffccad11d536b9ffb6fc06d827d5d10cf Mon Sep 17 00:00:00 2001 From: Christiaan Dirkx Date: Fri, 9 Jul 2021 04:19:07 +0200 Subject: [PATCH 3/4] Add `FromStr` implementation to `Ipv{v,6}AddrPrefix` --- library/std/src/net/ip/tests.rs | 75 +++++++++++++++++++++++++++++++++ library/std/src/net/parser.rs | 55 ++++++++++++++++++++++-- 2 files changed, 126 insertions(+), 4 deletions(-) diff --git a/library/std/src/net/ip/tests.rs b/library/std/src/net/ip/tests.rs index 7946399bff2ce..8cc68ef98a3ab 100644 --- a/library/std/src/net/ip/tests.rs +++ b/library/std/src/net/ip/tests.rs @@ -108,6 +108,68 @@ fn test_from_str_socket_addr() { assert_eq!(None, none); } +#[test] +fn test_from_str_ipv4_prefix() { + assert_eq!( + Ok(Ipv4AddrPrefix::new_unchecked(Ipv4Addr::new(127, 0, 0, 1), 16)), + "127.0.0.1/16".parse() + ); + assert_eq!( + Ok(Ipv4AddrPrefix::new_unchecked(Ipv4Addr::new(255, 255, 255, 255), 32)), + "255.255.255.255/32".parse() + ); + assert_eq!( + Ok(Ipv4AddrPrefix::new_unchecked(Ipv4Addr::new(0, 0, 0, 0), 0)), + "0.0.0.0/0".parse() + ); + + // no prefix + let none: Option = "255.0.0.1".parse().ok(); + assert_eq!(None, none); + // wrong prefix separator + let none: Option = "255.0.0.1:16".parse().ok(); + assert_eq!(None, none); + // prefix can not be longer than 32 bits + let none: Option = "255.0.0.1/35".parse().ok(); + assert_eq!(None, none); +} + +#[test] +fn test_from_str_ipv6_prefix() { + assert_eq!( + Ok(Ipv6AddrPrefix::new_unchecked(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 0)), + "0:0:0:0:0:0:0:0/0".parse() + ); + assert_eq!( + Ok(Ipv6AddrPrefix::new_unchecked(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 128)), + "0:0:0:0:0:0:0:1/128".parse() + ); + + assert_eq!( + Ok(Ipv6AddrPrefix::new_unchecked(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 128)), + "::1/128".parse() + ); + assert_eq!( + Ok(Ipv6AddrPrefix::new_unchecked(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 0)), + "::/0".parse() + ); + + assert_eq!( + Ok(Ipv6AddrPrefix::new_unchecked(Ipv6Addr::new(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11), 32)), + "2a02:6b8::11:11/32".parse() + ); + + // no prefix + let none: Option = "1:2:3:4::5:6:7:8".parse().ok(); + assert_eq!(None, none); + // wrong prefix separator + let none: Option = "1:2:3:4::5:6:7:8:16".parse().ok(); + assert_eq!(None, none); + // prefix can not be longer than 128 bits + let none: Option = "1:2:3:4::5:6:7:8/130".parse().ok(); + assert_eq!(None, none); +} + #[test] fn ipv4_addr_to_string() { assert_eq!(Ipv4Addr::new(127, 0, 0, 1).to_string(), "127.0.0.1"); @@ -171,6 +233,19 @@ fn ipv6_addr_to_string() { assert_eq!("1::4:5:0:0:8", &format!("{:#?}", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8))); } +#[test] +fn ip_prefix_to_string() { + assert_eq!( + Ipv4AddrPrefix::new_unchecked(Ipv4Addr::new(127, 0, 0, 1), 24).to_string(), + "127.0.0.0/24" + ); + assert_eq!( + Ipv6AddrPrefix::new_unchecked(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7F00, 1), 96) + .to_string(), + "::ffff:0.0.0.0/96" + ); +} + #[test] fn ipv4_to_ipv6() { assert_eq!( diff --git a/library/std/src/net/parser.rs b/library/std/src/net/parser.rs index 88a8cb76befbf..d1b757f28fee7 100644 --- a/library/std/src/net/parser.rs +++ b/library/std/src/net/parser.rs @@ -9,7 +9,10 @@ mod tests; use crate::convert::TryInto as _; use crate::error::Error; use crate::fmt; -use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; +use crate::net::{ + IpAddr, Ipv4Addr, Ipv4AddrPrefix, Ipv6Addr, Ipv6AddrPrefix, SocketAddr, SocketAddrV4, + SocketAddrV6, +}; use crate::str::FromStr; trait ReadNumberHelper: crate::marker::Sized { @@ -231,6 +234,16 @@ impl<'a> Parser<'a> { }) } + /// Read a `/` followed by a prefix length in base 10. + fn read_prefix(&mut self, max: u32) -> Option { + self.read_atomically(|p| { + p.read_given_char('/')?; + let len = p.read_number(10, None)?; + + if len <= max { Some(len) } else { None } + }) + } + /// Read a `%` followed by a scope ID in base 10. fn read_scope_id(&mut self) -> Option { self.read_atomically(|p| { @@ -267,6 +280,24 @@ impl<'a> Parser<'a> { .map(SocketAddr::V4) .or_else(|| self.read_socket_addr_v6().map(SocketAddr::V6)) } + + /// Read an IPv4 address prefix; an address followed by a prefix length + fn read_addr_v4_prefix(&mut self) -> Option { + self.read_atomically(|p| { + let address = p.read_ipv4_addr()?; + let len = p.read_prefix(u32::BITS)?; + Some(Ipv4AddrPrefix::new_unchecked(address, len)) + }) + } + + /// Read an IPv6 address prefix; an address followed by a prefix length + fn read_addr_v6_prefix(&mut self) -> Option { + self.read_atomically(|p| { + let address = p.read_ipv6_addr()?; + let len = p.read_prefix(u128::BITS)?; + Some(Ipv6AddrPrefix::new_unchecked(address, len)) + }) + } } #[stable(feature = "ip_addr", since = "1.7.0")] @@ -317,11 +348,27 @@ impl FromStr for SocketAddr { } } -/// An error which can be returned when parsing an IP address or a socket address. +#[unstable(feature = "ip_prefix", issue = "86991")] +impl FromStr for Ipv4AddrPrefix { + type Err = AddrParseError; + fn from_str(s: &str) -> Result { + Parser::new(s).parse_with(|p| p.read_addr_v4_prefix()) + } +} + +#[unstable(feature = "ip_prefix", issue = "86991")] +impl FromStr for Ipv6AddrPrefix { + type Err = AddrParseError; + fn from_str(s: &str) -> Result { + Parser::new(s).parse_with(|p| p.read_addr_v6_prefix()) + } +} + +/// An error which can be returned when parsing an IP address, IP address prefix or a socket address. /// /// This error is used as the error type for the [`FromStr`] implementation for -/// [`IpAddr`], [`Ipv4Addr`], [`Ipv6Addr`], [`SocketAddr`], [`SocketAddrV4`], and -/// [`SocketAddrV6`]. +/// [`IpAddr`], [`Ipv4Addr`], [`Ipv6Addr`], [`Ipv4AddrPrefix`], [`Ipv6AddrPrefix`], +/// [`SocketAddr`], [`SocketAddrV4`], and [`SocketAddrV6`]. /// /// # Potential causes /// From d361a9a2303d4f9dc400d09afe59d35fd64bc45d Mon Sep 17 00:00:00 2001 From: Christiaan Dirkx Date: Fri, 9 Jul 2021 13:27:23 +0200 Subject: [PATCH 4/4] Change backing of `Ipv{4,6}AddrPrefix` from `Ipv{4,6}Addr` to `u{32,128}`` --- library/std/src/net/ip/network.rs | 35 +++++++++++++++---------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/library/std/src/net/ip/network.rs b/library/std/src/net/ip/network.rs index 92110e449c276..828effc3bdb8c 100644 --- a/library/std/src/net/ip/network.rs +++ b/library/std/src/net/ip/network.rs @@ -44,7 +44,7 @@ use crate::fmt; #[derive(Copy, PartialEq, Eq, Clone, Hash)] #[unstable(feature = "ip_prefix", issue = "86991")] pub struct Ipv4AddrPrefix { - address: Ipv4Addr, + address_raw: u32, len: u8, } @@ -90,7 +90,7 @@ pub struct Ipv4AddrPrefix { #[derive(Copy, PartialEq, Eq, Clone, Hash)] #[unstable(feature = "ip_prefix", issue = "86991")] pub struct Ipv6AddrPrefix { - address: Ipv6Addr, + address_raw: u128, len: u8, } @@ -122,13 +122,12 @@ impl Ipv4AddrPrefix { // Private constructor that assumes len <= 32. // Useful because `Result::unwrap` is not yet usable in const contexts, so `new` can't be used. pub(crate) const fn new_unchecked(address: Ipv4Addr, len: u32) -> Ipv4AddrPrefix { - let address = { + let masked = { let mask = Ipv4AddrPrefix::mask(len); - let masked = u32::from_be_bytes(address.octets()) & mask; - Ipv4Addr::from_u32(masked) + u32::from_be_bytes(address.octets()) & mask }; - Ipv4AddrPrefix { address, len: len as u8 } + Ipv4AddrPrefix { address_raw: masked, len: len as u8 } } /// Returns the address specifying this address prefix. @@ -153,7 +152,7 @@ impl Ipv4AddrPrefix { #[unstable(feature = "ip_prefix", issue = "86991")] #[inline] pub const fn address(&self) -> Ipv4Addr { - self.address + Ipv4Addr::from_u32(self.address_raw) } /// Returns the prefix length of this address prefix. @@ -178,6 +177,7 @@ impl Ipv4AddrPrefix { } // Compute the bitmask specified by a prefix length. + #[inline] const fn mask(len: u32) -> u32 { if len == 0 { 0 @@ -206,14 +206,14 @@ impl Ipv4AddrPrefix { #[inline] pub const fn contains(&self, address: &Ipv4Addr) -> bool { let mask = Ipv4AddrPrefix::mask(self.len as u32); - u32::from_be_bytes(address.octets()) & mask == u32::from_be_bytes(self.address.octets()) + u32::from_be_bytes(address.octets()) & mask == self.address_raw } } #[unstable(feature = "ip_prefix", issue = "86991")] impl fmt::Display for Ipv4AddrPrefix { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "{}/{}", self.address, self.len) + write!(fmt, "{}/{}", Ipv4Addr::from_u32(self.address_raw), self.len) } } @@ -228,7 +228,7 @@ impl fmt::Debug for Ipv4AddrPrefix { impl From for Ipv4AddrPrefix { /// Converts an IPv4 address `a.b.c.d` to the prefix `a.b.c.d/32`. fn from(address: Ipv4Addr) -> Self { - Ipv4AddrPrefix { address, len: u32::BITS as u8 } + Ipv4AddrPrefix::new_unchecked(address, u32::BITS) } } @@ -260,13 +260,12 @@ impl Ipv6AddrPrefix { // Private constructor that assumes len <= 128. // Useful because `Result::unwrap` is not yet usable in const contexts, so `new` can't be used. pub(crate) const fn new_unchecked(address: Ipv6Addr, len: u32) -> Ipv6AddrPrefix { - let address = { + let masked = { let mask = Ipv6AddrPrefix::mask(len); - let masked = u128::from_be_bytes(address.octets()) & mask; - Ipv6Addr::from_u128(masked) + u128::from_be_bytes(address.octets()) & mask }; - Ipv6AddrPrefix { address, len: len as u8 } + Ipv6AddrPrefix { address_raw: masked, len: len as u8 } } /// Returns the address specifying this address prefix. @@ -291,7 +290,7 @@ impl Ipv6AddrPrefix { #[unstable(feature = "ip_prefix", issue = "86991")] #[inline] pub const fn address(&self) -> Ipv6Addr { - self.address + Ipv6Addr::from_u128(self.address_raw) } /// Returns the prefix length of this address prefix. @@ -343,14 +342,14 @@ impl Ipv6AddrPrefix { #[inline] pub const fn contains(&self, address: &Ipv6Addr) -> bool { let mask = Ipv6AddrPrefix::mask(self.len as u32); - u128::from_be_bytes(address.octets()) & mask == u128::from_be_bytes(self.address.octets()) + u128::from_be_bytes(address.octets()) & mask == self.address_raw } } #[unstable(feature = "ip_prefix", issue = "86991")] impl fmt::Display for Ipv6AddrPrefix { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "{}/{}", self.address, self.len) + write!(fmt, "{}/{}", Ipv6Addr::from_u128(self.address_raw), self.len) } } @@ -365,7 +364,7 @@ impl fmt::Debug for Ipv6AddrPrefix { impl From for Ipv6AddrPrefix { /// Converts an IPv6 address `a:b:c:d:e:f:g:h` to the prefix `a:b:c:d:e:f:g:h/128`. fn from(address: Ipv6Addr) -> Self { - Ipv6AddrPrefix { address, len: u128::BITS as u8 } + Ipv6AddrPrefix::new_unchecked(address, u128::BITS) } }