diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip/mod.rs similarity index 99% rename from library/std/src/net/ip.rs rename to library/std/src/net/ip/mod.rs index 4b6d60d121e13..879eaee76885a 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,13 @@ 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; + +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..828effc3bdb8c --- /dev/null +++ b/library/std/src/net/ip/network.rs @@ -0,0 +1,391 @@ +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_raw: u32, + 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_raw: u128, + 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 masked = { + let mask = Ipv4AddrPrefix::mask(len); + u32::from_be_bytes(address.octets()) & mask + }; + + Ipv4AddrPrefix { address_raw: masked, 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 { + Ipv4Addr::from_u32(self.address_raw) + } + + /// 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. + #[inline] + 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 == 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, "{}/{}", Ipv4Addr::from_u32(self.address_raw), 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::new_unchecked(address, u32::BITS) + } +} + +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 masked = { + let mask = Ipv6AddrPrefix::mask(len); + u128::from_be_bytes(address.octets()) & mask + }; + + Ipv6AddrPrefix { address_raw: masked, 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 { + Ipv6Addr::from_u128(self.address_raw) + } + + /// 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 == 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, "{}/{}", Ipv6Addr::from_u128(self.address_raw), 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::new_unchecked(address, u128::BITS) + } +} + +#[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..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!( @@ -795,6 +870,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 +1081,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")] 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 ///