Rollup merge of #86984 - Smittyvb:ipv4-octal-zero, r=m-ou-se
Reject octal zeros in IPv4 addresses This fixes #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 #83652, but due to the way it was implemented octal zeros were still allowed.
This commit is contained in:
commit
09de34c107
3 changed files with 32 additions and 10 deletions
|
@ -59,7 +59,8 @@ pub enum IpAddr {
|
||||||
///
|
///
|
||||||
/// `Ipv4Addr` provides a [`FromStr`] implementation. The four octets are in decimal
|
/// `Ipv4Addr` provides a [`FromStr`] implementation. The four octets are in decimal
|
||||||
/// notation, divided by `.` (this is called "dot-decimal notation").
|
/// 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
|
/// [IETF RFC 6943]: https://tools.ietf.org/html/rfc6943#section-3.1.1
|
||||||
/// [`FromStr`]: crate::str::FromStr
|
/// [`FromStr`]: crate::str::FromStr
|
||||||
|
@ -72,6 +73,9 @@ pub enum IpAddr {
|
||||||
/// let localhost = Ipv4Addr::new(127, 0, 0, 1);
|
/// let localhost = Ipv4Addr::new(127, 0, 0, 1);
|
||||||
/// assert_eq!("127.0.0.1".parse(), Ok(localhost));
|
/// assert_eq!("127.0.0.1".parse(), Ok(localhost));
|
||||||
/// assert_eq!(localhost.is_loopback(), true);
|
/// 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)]
|
#[derive(Copy)]
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
|
|
@ -20,6 +20,14 @@ fn test_from_str_ipv4() {
|
||||||
// no number between dots
|
// no number between dots
|
||||||
let none: Option<Ipv4Addr> = "255.0..1".parse().ok();
|
let none: Option<Ipv4Addr> = "255.0..1".parse().ok();
|
||||||
assert_eq!(None, none);
|
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]
|
#[test]
|
||||||
|
|
|
@ -111,10 +111,12 @@ impl<'a> Parser<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
radix: u32,
|
radix: u32,
|
||||||
max_digits: Option<usize>,
|
max_digits: Option<usize>,
|
||||||
|
allow_zero_prefix: bool,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
self.read_atomically(move |p| {
|
self.read_atomically(move |p| {
|
||||||
let mut result = T::ZERO;
|
let mut result = T::ZERO;
|
||||||
let mut digit_count = 0;
|
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)) {
|
while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) {
|
||||||
result = result.checked_mul(radix)?;
|
result = result.checked_mul(radix)?;
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,10 +148,7 @@ impl<'a> Parser<'a> {
|
||||||
*slot = p.read_separator('.', i, |p| {
|
*slot = p.read_separator('.', i, |p| {
|
||||||
// Disallow octal number in IP string.
|
// Disallow octal number in IP string.
|
||||||
// https://tools.ietf.org/html/rfc6943#section-3.1.1
|
// https://tools.ietf.org/html/rfc6943#section-3.1.1
|
||||||
match (p.peek_char(), p.read_number(10, None)) {
|
p.read_number(10, Some(3), false)
|
||||||
(Some('0'), Some(number)) if number != 0 => None,
|
|
||||||
(_, number) => number,
|
|
||||||
}
|
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
match group {
|
||||||
Some(g) => *slot = g,
|
Some(g) => *slot = g,
|
||||||
|
@ -227,7 +232,7 @@ impl<'a> Parser<'a> {
|
||||||
fn read_port(&mut self) -> Option<u16> {
|
fn read_port(&mut self) -> Option<u16> {
|
||||||
self.read_atomically(|p| {
|
self.read_atomically(|p| {
|
||||||
p.read_given_char(':')?;
|
p.read_given_char(':')?;
|
||||||
p.read_number(10, None)
|
p.read_number(10, None, true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +240,7 @@ impl<'a> Parser<'a> {
|
||||||
fn read_scope_id(&mut self) -> Option<u32> {
|
fn read_scope_id(&mut self) -> Option<u32> {
|
||||||
self.read_atomically(|p| {
|
self.read_atomically(|p| {
|
||||||
p.read_given_char('%')?;
|
p.read_given_char('%')?;
|
||||||
p.read_number(10, None)
|
p.read_number(10, None, true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +286,12 @@ impl FromStr for IpAddr {
|
||||||
impl FromStr for Ipv4Addr {
|
impl FromStr for Ipv4Addr {
|
||||||
type Err = AddrParseError;
|
type Err = AddrParseError;
|
||||||
fn from_str(s: &str) -> Result<Ipv4Addr, 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())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue