Skip to content

Commit

Permalink
Rollup merge of rust-lang#86984 - Smittyvb:ipv4-octal-zero, r=m-ou-se
Browse files Browse the repository at this point in the history
Reject octal zeros in IPv4 addresses

This fixes rust-lang#86964 by rejecting octal zeros in IP addresses, such that `192.168.00.00000000` is rejected with a parse error, since having leading zeros in front of another zero indicates it is a zero written in octal notation, which is not allowed in the strict mode specified by RFC 6943 3.1.1. Octal rejection was implemented in rust-lang#83652, but due to the way it was implemented octal zeros were still allowed.
  • Loading branch information
JohnTitor authored Oct 21, 2021
2 parents efd0483 + 403d269 commit d03a803
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 10 deletions.
6 changes: 5 additions & 1 deletion library/std/src/net/ip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ pub enum IpAddr {
///
/// `Ipv4Addr` provides a [`FromStr`] implementation. The four octets are in decimal
/// notation, divided by `.` (this is called "dot-decimal notation").
/// Notably, octal numbers and hexadecimal numbers are not allowed per [IETF RFC 6943].
/// Notably, octal numbers (which are indicated with a leading `0`) and hexadecimal numbers (which
/// are indicated with a leading `0x`) are not allowed per [IETF RFC 6943].
///
/// [IETF RFC 6943]: https://tools.ietf.org/html/rfc6943#section-3.1.1
/// [`FromStr`]: crate::str::FromStr
Expand All @@ -72,6 +73,9 @@ pub enum IpAddr {
/// let localhost = Ipv4Addr::new(127, 0, 0, 1);
/// assert_eq!("127.0.0.1".parse(), Ok(localhost));
/// assert_eq!(localhost.is_loopback(), true);
/// assert!("012.004.002.000".parse::<Ipv4Addr>().is_err()); // all octets are in octal
/// assert!("0000000.0.0.0".parse::<Ipv4Addr>().is_err()); // first octet is a zero in octal
/// assert!("0xcb.0x0.0x71.0x00".parse::<Ipv4Addr>().is_err()); // all octets are in hex
/// ```
#[derive(Copy)]
#[stable(feature = "rust1", since = "1.0.0")]
Expand Down
8 changes: 8 additions & 0 deletions library/std/src/net/ip/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ fn test_from_str_ipv4() {
// no number between dots
let none: Option<Ipv4Addr> = "255.0..1".parse().ok();
assert_eq!(None, none);
// octal
let none: Option<Ipv4Addr> = "255.0.0.01".parse().ok();
assert_eq!(None, none);
// octal zero
let none: Option<Ipv4Addr> = "255.0.0.00".parse().ok();
assert_eq!(None, none);
let none: Option<Ipv4Addr> = "255.0.00.0".parse().ok();
assert_eq!(None, none);
}

#[test]
Expand Down
28 changes: 19 additions & 9 deletions library/std/src/net/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,12 @@ impl<'a> Parser<'a> {
&mut self,
radix: u32,
max_digits: Option<usize>,
allow_zero_prefix: bool,
) -> Option<T> {
self.read_atomically(move |p| {
let mut result = T::ZERO;
let mut digit_count = 0;
let has_leading_zero = p.peek_char() == Some('0');

while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) {
result = result.checked_mul(radix)?;
Expand All @@ -127,7 +129,13 @@ impl<'a> Parser<'a> {
}
}

if digit_count == 0 { None } else { Some(result) }
if digit_count == 0 {
None
} else if !allow_zero_prefix && has_leading_zero && digit_count > 1 {
None
} else {
Some(result)
}
})
}

Expand All @@ -140,10 +148,7 @@ impl<'a> Parser<'a> {
*slot = p.read_separator('.', i, |p| {
// Disallow octal number in IP string.
// https://tools.ietf.org/html/rfc6943#section-3.1.1
match (p.peek_char(), p.read_number(10, None)) {
(Some('0'), Some(number)) if number != 0 => None,
(_, number) => number,
}
p.read_number(10, Some(3), false)
})?;
}

Expand Down Expand Up @@ -175,7 +180,7 @@ impl<'a> Parser<'a> {
}
}

let group = p.read_separator(':', i, |p| p.read_number(16, Some(4)));
let group = p.read_separator(':', i, |p| p.read_number(16, Some(4), true));

match group {
Some(g) => *slot = g,
Expand Down Expand Up @@ -227,15 +232,15 @@ impl<'a> Parser<'a> {
fn read_port(&mut self) -> Option<u16> {
self.read_atomically(|p| {
p.read_given_char(':')?;
p.read_number(10, None)
p.read_number(10, None, true)
})
}

/// Read a `%` followed by a scope ID in base 10.
fn read_scope_id(&mut self) -> Option<u32> {
self.read_atomically(|p| {
p.read_given_char('%')?;
p.read_number(10, None)
p.read_number(10, None, true)
})
}

Expand Down Expand Up @@ -281,7 +286,12 @@ impl FromStr for IpAddr {
impl FromStr for Ipv4Addr {
type Err = AddrParseError;
fn from_str(s: &str) -> Result<Ipv4Addr, AddrParseError> {
Parser::new(s).parse_with(|p| p.read_ipv4_addr())
// don't try to parse if too long
if s.len() > 15 {
Err(AddrParseError(()))
} else {
Parser::new(s).parse_with(|p| p.read_ipv4_addr())
}
}
}

Expand Down

0 comments on commit d03a803

Please sign in to comment.