Просмотр исходного кода

Auto merge of #414 - valenting:ipv4_overflow, r=SimonSapin

Fail IPv4 parsing when the number is overflowing

Right now we don't fail when parsing URLs such as http://10000000000

Parsing returns an overflow, but since we don't differentiate between overflow and it's a number, we just treat it as a regular domain.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/rust-url/414)
<!-- Reviewable:end -->
bors-servo 9 лет назад
Родитель
Сommit
d358a4f33a
2 измененных файлов с 44 добавлено и 9 удалено
  1. 29 9
      src/host.rs
  2. 15 0
      tests/urltestdata.json

+ 29 - 9
src/host.rs

@@ -329,7 +329,7 @@ fn longest_zero_sequence(pieces: &[u16; 8]) -> (isize, isize) {
 }
 
 /// <https://url.spec.whatwg.org/#ipv4-number-parser>
-fn parse_ipv4number(mut input: &str) -> Result<u32, ()> {
+fn parse_ipv4number(mut input: &str) -> Result<Option<u32>, ()> {
     let mut r = 10;
     if input.starts_with("0x") || input.starts_with("0X") {
         input = &input[2..];
@@ -338,14 +338,30 @@ fn parse_ipv4number(mut input: &str) -> Result<u32, ()> {
         input = &input[1..];
         r = 8;
     }
+
+    // At the moment we can't know the reason why from_str_radix fails
+    // https://github.com/rust-lang/rust/issues/22639
+    // So instead we check if the input looks like a real number and only return
+    // an error when it's an overflow.
+    let valid_number = match r {
+        8 => input.chars().all(|c| c >= '0' && c <='7'),
+        10 => input.chars().all(|c| c >= '0' && c <='9'),
+        16 => input.chars().all(|c| (c >= '0' && c <='9') || (c >='a' && c <= 'f') || (c >= 'A' && c <= 'F')),
+        _ => false
+    };
+
+    if !valid_number {
+        return Ok(None);
+    }
+
     if input.is_empty() {
-        return Ok(0);
+        return Ok(Some(0));
     }
     if input.starts_with('+') {
-        return Err(())
+        return Ok(None);
     }
     match u32::from_str_radix(input, r) {
-        Ok(number) => Ok(number),
+        Ok(number) => Ok(Some(number)),
         Err(_) => Err(()),
     }
 }
@@ -363,15 +379,19 @@ fn parse_ipv4addr(input: &str) -> ParseResult<Option<Ipv4Addr>> {
         return Ok(None);
     }
     let mut numbers: Vec<u32> = Vec::new();
+    let mut overflow = false;
     for part in parts {
         if part == "" {
             return Ok(None);
         }
-        if let Ok(n) = parse_ipv4number(part) {
-            numbers.push(n);
-        } else {
-            return Ok(None);
-        }
+        match parse_ipv4number(part) {
+            Ok(Some(n)) => numbers.push(n),
+            Ok(None) => return Ok(None),
+            Err(()) => overflow = true
+        };
+    }
+    if overflow {
+        return Err(ParseError::InvalidIpv4Address);
     }
     let mut ipv4 = numbers.pop().expect("a non-empty list of numbers");
     // Equivalent to: ipv4 >= 256 ** (4 − numbers.len())

+ 15 - 0
tests/urltestdata.json

@@ -4858,6 +4858,11 @@
     "search": "",
     "hash": ""
   },
+  {
+    "input": "http://10000000000",
+    "base": "http://other.com/",
+    "failure": true
+  },
   {
     "input": "http://10000000000.com",
     "base": "http://other.com/",
@@ -4888,6 +4893,11 @@
     "search": "",
     "hash": ""
   },
+  {
+    "input": "http://4294967296",
+    "base": "http://other.com/",
+    "failure": true
+  },
   {
     "input": "http://0xffffffff",
     "base": "http://other.com/",
@@ -4903,6 +4913,11 @@
     "search": "",
     "hash": ""
   },
+  {
+    "input": "http://0xffffffff1",
+    "base": "http://other.com/",
+    "failure": true
+  },
   {
     "input": "http://256.256.256.256",
     "base": "http://other.com/",