Skip to content

Commit acbaf2f

Browse files
Rollup merge of rust-lang#86984 - Smittyvb:ipv4-octal-zero, r=m-ou-se
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.
2 parents 3d71e74 + 403d269 commit acbaf2f

File tree

3 files changed

+32
-10
lines changed

3 files changed

+32
-10
lines changed

library/std/src/net/ip.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ pub enum IpAddr {
5959
///
6060
/// `Ipv4Addr` provides a [`FromStr`] implementation. The four octets are in decimal
6161
/// notation, divided by `.` (this is called "dot-decimal notation").
62-
/// Notably, octal numbers and hexadecimal numbers are not allowed per [IETF RFC 6943].
62+
/// Notably, octal numbers (which are indicated with a leading `0`) and hexadecimal numbers (which
63+
/// are indicated with a leading `0x`) are not allowed per [IETF RFC 6943].
6364
///
6465
/// [IETF RFC 6943]: https://tools.ietf.org/html/rfc6943#section-3.1.1
6566
/// [`FromStr`]: crate::str::FromStr
@@ -72,6 +73,9 @@ pub enum IpAddr {
7273
/// let localhost = Ipv4Addr::new(127, 0, 0, 1);
7374
/// assert_eq!("127.0.0.1".parse(), Ok(localhost));
7475
/// assert_eq!(localhost.is_loopback(), true);
76+
/// assert!("012.004.002.000".parse::<Ipv4Addr>().is_err()); // all octets are in octal
77+
/// assert!("0000000.0.0.0".parse::<Ipv4Addr>().is_err()); // first octet is a zero in octal
78+
/// assert!("0xcb.0x0.0x71.0x00".parse::<Ipv4Addr>().is_err()); // all octets are in hex
7579
/// ```
7680
#[derive(Copy)]
7781
#[stable(feature = "rust1", since = "1.0.0")]

library/std/src/net/ip/tests.rs

+8
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ fn test_from_str_ipv4() {
2020
// no number between dots
2121
let none: Option<Ipv4Addr> = "255.0..1".parse().ok();
2222
assert_eq!(None, none);
23+
// octal
24+
let none: Option<Ipv4Addr> = "255.0.0.01".parse().ok();
25+
assert_eq!(None, none);
26+
// octal zero
27+
let none: Option<Ipv4Addr> = "255.0.0.00".parse().ok();
28+
assert_eq!(None, none);
29+
let none: Option<Ipv4Addr> = "255.0.00.0".parse().ok();
30+
assert_eq!(None, none);
2331
}
2432

2533
#[test]

library/std/src/net/parser.rs

+19-9
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,12 @@ impl<'a> Parser<'a> {
111111
&mut self,
112112
radix: u32,
113113
max_digits: Option<usize>,
114+
allow_zero_prefix: bool,
114115
) -> Option<T> {
115116
self.read_atomically(move |p| {
116117
let mut result = T::ZERO;
117118
let mut digit_count = 0;
119+
let has_leading_zero = p.peek_char() == Some('0');
118120

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

130-
if digit_count == 0 { None } else { Some(result) }
132+
if digit_count == 0 {
133+
None
134+
} else if !allow_zero_prefix && has_leading_zero && digit_count > 1 {
135+
None
136+
} else {
137+
Some(result)
138+
}
131139
})
132140
}
133141

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

@@ -175,7 +180,7 @@ impl<'a> Parser<'a> {
175180
}
176181
}
177182

178-
let group = p.read_separator(':', i, |p| p.read_number(16, Some(4)));
183+
let group = p.read_separator(':', i, |p| p.read_number(16, Some(4), true));
179184

180185
match group {
181186
Some(g) => *slot = g,
@@ -227,15 +232,15 @@ impl<'a> Parser<'a> {
227232
fn read_port(&mut self) -> Option<u16> {
228233
self.read_atomically(|p| {
229234
p.read_given_char(':')?;
230-
p.read_number(10, None)
235+
p.read_number(10, None, true)
231236
})
232237
}
233238

234239
/// Read a `%` followed by a scope ID in base 10.
235240
fn read_scope_id(&mut self) -> Option<u32> {
236241
self.read_atomically(|p| {
237242
p.read_given_char('%')?;
238-
p.read_number(10, None)
243+
p.read_number(10, None, true)
239244
})
240245
}
241246

@@ -281,7 +286,12 @@ impl FromStr for IpAddr {
281286
impl FromStr for Ipv4Addr {
282287
type Err = AddrParseError;
283288
fn from_str(s: &str) -> Result<Ipv4Addr, AddrParseError> {
284-
Parser::new(s).parse_with(|p| p.read_ipv4_addr())
289+
// don't try to parse if too long
290+
if s.len() > 15 {
291+
Err(AddrParseError(()))
292+
} else {
293+
Parser::new(s).parse_with(|p| p.read_ipv4_addr())
294+
}
285295
}
286296
}
287297

0 commit comments

Comments
 (0)