Skip to content
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

Introduce private helper method in_network for Ipv{4,6}Addr #86969

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 47 additions & 23 deletions library/std/src/net/ip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,12 +444,9 @@ impl Ipv4Addr {
#[stable(since = "1.7.0", feature = "ip_17")]
#[inline]
pub const fn is_private(&self) -> bool {
match self.octets() {
[10, ..] => true,
[172, b, ..] if b >= 16 && b <= 31 => true,
[192, 168, ..] => true,
_ => false,
}
self.in_network(&Ipv4Addr::new(10, 0, 0, 0), 8)
|| self.in_network(&Ipv4Addr::new(172, 16, 0, 0), 12)
|| self.in_network(&Ipv4Addr::new(192, 168, 0, 0), 16)
}

/// Returns [`true`] if the address is link-local (`169.254.0.0/16`).
Expand All @@ -471,7 +468,7 @@ impl Ipv4Addr {
#[stable(since = "1.7.0", feature = "ip_17")]
#[inline]
pub const fn is_link_local(&self) -> bool {
matches!(self.octets(), [169, 254, ..])
self.in_network(&Ipv4Addr::new(169, 254, 0, 0), 16)
}

/// Returns [`true`] if the address appears to be globally routable.
Expand Down Expand Up @@ -564,7 +561,7 @@ impl Ipv4Addr {
&& !self.is_reserved()
&& !self.is_benchmarking()
// Make sure the address is not in 0.0.0.0/8
&& self.octets()[0] != 0
&& !self.in_network(&Ipv4Addr::UNSPECIFIED, 8)
}

/// Returns [`true`] if this address is part of the Shared Address Space defined in
Expand All @@ -586,7 +583,7 @@ impl Ipv4Addr {
#[unstable(feature = "ip", issue = "27709")]
#[inline]
pub const fn is_shared(&self) -> bool {
self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000)
self.in_network(&Ipv4Addr::new(100, 64, 0, 0), 10)
}

/// Returns [`true`] if this address is part of `192.0.0.0/24`, which is reserved to
Expand Down Expand Up @@ -620,7 +617,7 @@ impl Ipv4Addr {
#[unstable(feature = "ip", issue = "27709")]
#[inline]
pub const fn is_ietf_protocol_assignment(&self) -> bool {
self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0
self.in_network(&Ipv4Addr::new(192, 0, 0, 0), 24)
}

/// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for
Expand All @@ -645,7 +642,7 @@ impl Ipv4Addr {
#[unstable(feature = "ip", issue = "27709")]
#[inline]
pub const fn is_benchmarking(&self) -> bool {
self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18
self.in_network(&Ipv4Addr::new(198, 18, 0, 0), 15)
}

/// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112]
Expand Down Expand Up @@ -679,7 +676,7 @@ impl Ipv4Addr {
#[unstable(feature = "ip", issue = "27709")]
#[inline]
pub const fn is_reserved(&self) -> bool {
self.octets()[0] & 240 == 240 && !self.is_broadcast()
self.in_network(&Ipv4Addr::new(240, 0, 0, 0), 4) && !self.is_broadcast()
}

/// Returns [`true`] if this is a multicast address (`224.0.0.0/4`).
Expand All @@ -702,7 +699,7 @@ impl Ipv4Addr {
#[stable(since = "1.7.0", feature = "ip_17")]
#[inline]
pub const fn is_multicast(&self) -> bool {
self.octets()[0] >= 224 && self.octets()[0] <= 239
self.in_network(&Ipv4Addr::new(224, 0, 0, 0), 4)
}

/// Returns [`true`] if this is a broadcast address (`255.255.255.255`).
Expand Down Expand Up @@ -750,12 +747,9 @@ impl Ipv4Addr {
#[stable(since = "1.7.0", feature = "ip_17")]
#[inline]
pub const fn is_documentation(&self) -> bool {
match self.octets() {
[192, 0, 2, _] => true,
[198, 51, 100, _] => true,
[203, 0, 113, _] => true,
_ => false,
}
self.in_network(&Ipv4Addr::new(192, 0, 2, 0), 24)
|| self.in_network(&Ipv4Addr::new(198, 51, 100, 0), 24)
|| self.in_network(&Ipv4Addr::new(203, 0, 113, 0), 24)
}

/// Converts this address to an IPv4-compatible [`IPv6` address].
Expand Down Expand Up @@ -810,6 +804,21 @@ impl Ipv4Addr {
inner: c::in6_addr { s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, d] },
}
}

// Internal helper method to check if an IPv4 address is in a network.
// Not intended to be made public in it's current form.
//
// `prefix` must be <=32 and `network` must have all bits not in the prefix set to 0.
#[inline]
const fn in_network(&self, network: &Self, prefix: u8) -> bool {
if prefix == 0 {
true
} else {
// shift will not overflow as prefix > 0, so u32::BITS - prefix < 32
let mask: u32 = u32::MAX << (u32::BITS as u8 - prefix);
u32::from_be_bytes(self.octets()) & mask == u32::from_be_bytes(network.octets())
}
}
}

#[stable(feature = "ip_addr", since = "1.7.0")]
Expand Down Expand Up @@ -1264,7 +1273,7 @@ impl Ipv6Addr {
#[unstable(feature = "ip", issue = "27709")]
#[inline]
pub const fn is_unique_local(&self) -> bool {
(self.segments()[0] & 0xfe00) == 0xfc00
self.in_network(&Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 0), 7)
}

/// Returns [`true`] if this is a unicast address, as defined by [IETF RFC 4291].
Expand Down Expand Up @@ -1343,7 +1352,7 @@ impl Ipv6Addr {
#[unstable(feature = "ip", issue = "27709")]
#[inline]
pub const fn is_unicast_link_local(&self) -> bool {
(self.segments()[0] & 0xffc0) == 0xfe80
self.in_network(&Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0), 10)
}

/// Returns [`true`] if this is an address reserved for documentation
Expand All @@ -1367,7 +1376,7 @@ impl Ipv6Addr {
#[unstable(feature = "ip", issue = "27709")]
#[inline]
pub const fn is_documentation(&self) -> bool {
(self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8)
self.in_network(&Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32)
}

/// Returns [`true`] if the address is a globally routable unicast address.
Expand Down Expand Up @@ -1465,7 +1474,7 @@ impl Ipv6Addr {
#[stable(since = "1.7.0", feature = "ip_17")]
#[inline]
pub const fn is_multicast(&self) -> bool {
(self.segments()[0] & 0xff00) == 0xff00
self.in_network(&Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0), 8)
}

/// Converts this address to an [`IPv4` address] if it's an "IPv4-mapped IPv6 address"
Expand Down Expand Up @@ -1546,6 +1555,21 @@ impl Ipv6Addr {
pub const fn octets(&self) -> [u8; 16] {
self.inner.s6_addr
}

// Internal helper method to check if an IPv6 address is in a network.
// Not intended to be made public in it's current form.
//
// `prefix` must be <=128 and `network` must have all bits not in the prefix set to 0.
#[inline]
const fn in_network(&self, network: &Self, prefix: u8) -> bool {
if prefix == 0 {
true
} else {
// shift will not overflow as prefix > 0, so u128::BITS - prefix < 128
let mask: u128 = u128::MAX << (u128::BITS as u8 - prefix);
u128::from_be_bytes(self.octets()) & mask == u128::from_be_bytes(network.octets())
}
}
}

/// Write an Ipv6Addr, conforming to the canonical style described by
Expand Down
50 changes: 50 additions & 0 deletions library/std/src/net/ip/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,56 @@ fn cmp() {
assert!(IpAddr::V4(v41) < v61);
}

#[test]
fn in_network_ipv4() {
let address = Ipv4Addr::new(1, 2, 3, 4);
assert!(address.in_network(&Ipv4Addr::new(0, 0, 0, 0), 0));
assert!(address.in_network(&Ipv4Addr::new(1, 0, 0, 0), 8));
assert!(address.in_network(&Ipv4Addr::new(1, 2, 0, 0), 16));
assert!(address.in_network(&Ipv4Addr::new(1, 2, 3, 0), 24));
assert!(address.in_network(&Ipv4Addr::new(1, 2, 3, 4), 32));

// test a prefix that is not a multiple of 8
let network = Ipv4Addr::new(0, 0b01100100, 0, 0);
assert_eq!(Ipv4Addr::new(0, 0b01100011, 0, 0).in_network(&network, 14), false);
assert_eq!(Ipv4Addr::new(0, 0b01100100, 0, 0).in_network(&network, 14), true);
assert_eq!(Ipv4Addr::new(0, 0b01100111, 0, 0).in_network(&network, 14), true);
assert_eq!(Ipv4Addr::new(0, 0b01101000, 0, 0).in_network(&network, 14), false);
}

#[test]
fn in_network_ipv6() {
let address = Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8);
assert!(address.in_network(&Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 0));
assert!(address.in_network(&Ipv6Addr::new(1, 0, 0, 0, 0, 0, 0, 0), 16));
assert!(address.in_network(&Ipv6Addr::new(1, 2, 0, 0, 0, 0, 0, 0), 32));
assert!(address.in_network(&Ipv6Addr::new(1, 2, 3, 0, 0, 0, 0, 0), 48));
assert!(address.in_network(&Ipv6Addr::new(1, 2, 3, 4, 0, 0, 0, 0), 64));
assert!(address.in_network(&Ipv6Addr::new(1, 2, 3, 4, 5, 0, 0, 0), 80));
assert!(address.in_network(&Ipv6Addr::new(1, 2, 3, 4, 5, 6, 0, 0), 96));
assert!(address.in_network(&Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 0), 112));
assert!(address.in_network(&Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 128));

// test a prefix that is not a multiple of 16
let network = Ipv6Addr::new(0, 0b01100100_00000000, 0, 0, 0, 0, 0, 0);
assert_eq!(
Ipv6Addr::new(0, 0b01100011_11111111, 0, 0, 0, 0, 0, 0).in_network(&network, 22),
false
);
assert_eq!(
Ipv6Addr::new(0, 0b01100100_00000000, 0, 0, 0, 0, 0, 0).in_network(&network, 22),
true
);
assert_eq!(
Ipv6Addr::new(0, 0b01100111_11111111, 0, 0, 0, 0, 0, 0).in_network(&network, 22),
true
);
assert_eq!(
Ipv6Addr::new(0, 0b01101000_00000000, 0, 0, 0, 0, 0, 0).in_network(&network, 22),
false
);
}

#[test]
fn is_v4() {
let ip = IpAddr::V4(Ipv4Addr::new(100, 64, 3, 3));
Expand Down