|
33 | 33 | import re |
34 | 34 | import types |
35 | 35 | import warnings |
| 36 | +import ipaddress |
36 | 37 |
|
37 | 38 | __all__ = ["urlparse", "urlunparse", "urljoin", "urldefrag", |
38 | 39 | "urlsplit", "urlunsplit", "urlencode", "parse_qs", |
@@ -199,7 +200,7 @@ def _hostinfo(self): |
199 | 200 | _, _, hostinfo = netloc.rpartition('@') |
200 | 201 | _, have_open_br, bracketed = hostinfo.partition('[') |
201 | 202 | if have_open_br: |
202 | | - hostname, _, port = bracketed.partition(']') |
| 203 | + hostname, _, port = bracketed.rpartition(']') |
203 | 204 | _, _, port = port.partition(':') |
204 | 205 | else: |
205 | 206 | hostname, _, port = hostinfo.partition(':') |
@@ -229,7 +230,7 @@ def _hostinfo(self): |
229 | 230 | _, _, hostinfo = netloc.rpartition(b'@') |
230 | 231 | _, have_open_br, bracketed = hostinfo.partition(b'[') |
231 | 232 | if have_open_br: |
232 | | - hostname, _, port = bracketed.partition(b']') |
| 233 | + hostname, _, port = bracketed.rpartition(b']') |
233 | 234 | _, _, port = port.partition(b':') |
234 | 235 | else: |
235 | 236 | hostname, _, port = hostinfo.partition(b':') |
@@ -426,6 +427,15 @@ def _checknetloc(netloc): |
426 | 427 | if c in netloc2: |
427 | 428 | raise ValueError("netloc '" + netloc + "' contains invalid " + |
428 | 429 | "characters under NFKC normalization") |
| 430 | + |
| 431 | +def _check_bracketed_host(hostname): |
| 432 | + if hostname.startswith('v'): |
| 433 | + if not re.match(r"\Av[a-fA-F0-9]+\..+\Z", hostname): |
| 434 | + raise ValueError(f"IPvFuture address is invalid") |
| 435 | + else: |
| 436 | + ip = ipaddress.ip_address(hostname) # Throws Value Error if not IPv6 or IPv4 |
| 437 | + if isinstance(ip, ipaddress.IPv4Address): |
| 438 | + raise ValueError(f"An IPv4 address cannot be in brackets") |
429 | 439 |
|
430 | 440 | # typed=True avoids BytesWarnings being emitted during cache key |
431 | 441 | # comparison since this API supports both bytes and str input. |
@@ -466,12 +476,14 @@ def urlsplit(url, scheme='', allow_fragments=True): |
466 | 476 | break |
467 | 477 | else: |
468 | 478 | scheme, url = url[:i].lower(), url[i+1:] |
469 | | - |
470 | 479 | if url[:2] == '//': |
471 | 480 | netloc, url = _splitnetloc(url, 2) |
472 | 481 | if (('[' in netloc and ']' not in netloc) or |
473 | 482 | (']' in netloc and '[' not in netloc)): |
474 | 483 | raise ValueError("Invalid IPv6 URL") |
| 484 | + if '[' in netloc and ']' in netloc: |
| 485 | + bracketed_host = netloc.partition('[')[2].rpartition(']')[0] |
| 486 | + _check_bracketed_host(bracketed_host) |
475 | 487 | if allow_fragments and '#' in url: |
476 | 488 | url, fragment = url.split('#', 1) |
477 | 489 | if '?' in url: |
|
0 commit comments