8000 Update ipaddress (#5878) · RustPython/RustPython@9f3c34a · GitHub
[go: up one dir, main page]

Skip to content

Commit 9f3c34a

Browse files
authored
Update ipaddress (#5878)
1 parent 119e209 commit 9f3c34a

File tree

2 files changed

+344
-35
lines changed

2 files changed

+344
-35
lines changed

Lib/ipaddress.py

Lines changed: 167 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ def collapse_addresses(addresses):
310310
[IPv4Network('192.0.2.0/24')]
311311
312312
Args:
313-
addresses: An iterator of IPv4Network or IPv6Network objects.
313+
addresses: An iterable of IPv4Network or IPv6Network objects.
314314
315315
Returns:
316316
An iterator of the collapsed IPv(4|6)Network objects.
@@ -734,7 +734,7 @@ def __eq__(self, other):
734734
return NotImplemented
735735

736736
def __hash__(self):
737-
return hash(int(self.network_address) ^ int(self.netmask))
737+
return hash((int(self.network_address), int(self.netmask)))
738738

739739
def __contains__(self, other):
740740
# always false if one is v4 and the other is v6.
@@ -1086,7 +1086,11 @@ def is_private(self):
10861086
"""
10871087
return any(self.network_address in priv_network and
10881088
self.broadcast_address in priv_network
1089-
for priv_network in self._constants._private_networks)
1089+
for priv_network in self._constants._private_networks) and all(
1090+
self.network_address not in network and
1091+
self.broadcast_address not in network
1092+
for network in self._constants._private_networks_exceptions
1093+
)
10901094

10911095
@property
10921096
def is_global(self):
@@ -1333,18 +1337,41 @@ def is_reserved(self):
13331337
@property
13341338
@functools.lru_cache()
13351339
def is_private(self):
1336-
"""Test if this address is allocated for private networks.
1340+
"""``True`` if the address is defined as not globally reachable by
1341+
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
1342+
(for IPv6) with the following exceptions:
13371343
1338-
Returns:
1339-
A boolean, True if the address is reserved per
1340-
iana-ipv4-special-registry.
1344+
* ``is_private`` is ``False`` for ``100.64.0.0/10``
1345+
* For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
1346+
semantics of the underlying IPv4 addresses and the following condition holds
1347+
(see :attr:`IPv6Address.ipv4_mapped`)::
13411348
1349+
address.is_private == address.ipv4_mapped.is_private
1350+
1351+
``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10``
1352+
IPv4 range where they are both ``False``.
13421353
"""
1343-
return any(self in net for net in self._constants._private_networks)
1354+
return (
1355+
any(self in net for net in self._constants._private_networks)
1356+
and all(self not in net for net in self._constants._private_networks_exceptions)
1357+
)
13441358

13451359
@property
13461360
@functools.lru_cache()
13471361
def is_global(self):
1362+
"""``True`` if the address is defined as globally reachable by
1363+
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
1364+
(for IPv6) with the following exception:
1365+
1366+
For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
1367+
semantics of the underlying IPv4 addresses and the following condition holds
1368+
(see :attr:`IPv6Address.ipv4_mapped`)::
1369+
1370+
address.is_global == address.ipv4_mapped.is_global
1371+
1372+
``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10``
1373+
IPv4 range where they are both ``False``.
1374+
"""
13481375
return self not in self._constants._public_network and not self.is_private
13491376

13501377
@property
@@ -1389,6 +1416,16 @@ def is_link_local(self):
13891416
"""
13901417
return self in self._constants._linklocal_network
13911418

1419+
@property
1420+
def ipv6_mapped(self):
1421+
"""Return the IPv4-mapped IPv6 address.
1422+
1423+
Returns:
1424+
The IPv4-mapped IPv6 address per RFC 4291.
1425+
1426+
"""
1427+
return IPv6Address(f'::ffff:{self}')
1428+
13921429

13931430
class IPv4Interface(IPv4Address):
13941431

@@ -1548,13 +1585,15 @@ class _IPv4Constants:
15481585

15491586
_public_network = IPv4Network('100.64.0.0/10')
15501587

1588+
# Not globally reachable address blocks listed on
1589+
# https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
15511590
_private_networks = [
15521591
IPv4Network('0.0.0.0/8'),
15531592
IPv4Network('10.0.0.0/8'),
15541593
IPv4Network('127.0.0.0/8'),
15551594
IPv4Network('169.254.0.0/16'),
15561595
IPv4Network('172.16.0.0/12'),
1557-
IPv4Network('192.0.0.0/29'),
1596+
IPv4Network('192.0.0.0/24'),
15581597
IPv4Network('192.0.0.170/31'),
15591598
IPv4Network('192.0.2.0/24'),
15601599
IPv4Network('192.168.0.0/16'),
@@ -1565,6 +1604,11 @@ class _IPv4Constants:
15651604
IPv4Network('255.255.255.255/32'),
15661605
]
15671606

1607+
_private_networks_exceptions = [
1608+
IPv4Network('192.0.0.9/32'),
1609+
IPv4Network('192.0.0.10/32'),
1610+
]
1611+
15681612
_reserved_network = IPv4Network('240.0.0.0/4')
15691613

15701614
_unspecified_address = IPv4Address('0.0.0.0')
@@ -1630,8 +1674,18 @@ def _ip_int_from_string(cls, ip_str):
16301674
"""
16311675
if not ip_str:
16321676
raise AddressValueError('Address cannot be empty')
1633-
1634-
parts = ip_str.split(':')
1677+
if len(ip_str) > 45:
1678+
shorten = ip_str
1679+
if len(shorten) > 100:
1680+
shorten = f'{ip_str[:45]}({len(ip_str)-90} chars elided){ip_str[-45:]}'
1681+
raise AddressValueError(f"At most 45 characters expected in "
1682+
f"{shorten!r}")
1683+
1684+
# We want to allow more parts than the max to be 'split'
1685+
# to preserve the correct error message when there are
1686+
# too many parts combined with '::'
1687+
_max_parts = cls._HEXTET_COUNT + 1
1688+
parts = ip_str.split(':', maxsplit=_max_parts)
16351689

16361690
# An IPv6 address needs at least 2 colons (3 parts).
16371691
_min_parts = 3
@@ -1651,7 +1705,6 @@ def _ip_int_from_string(cls, ip_str):
16511705
# An IPv6 address can't have more than 8 colons (9 parts).
16521706
# The extra colon comes from using the "::" notation for a single
16531707
# leading or trailing zero part.
1654-
_max_parts = cls._HEXTET_COUNT + 1
16551708
if len(parts) > _max_parts:
16561709
msg = "At most %d colons permitted in %r" % (_max_parts-1, ip_str)
16571710
raise AddressValueError(msg)
@@ -1923,8 +1976,49 @@ def __init__(self, address):
19231976

19241977
self._ip = self._ip_int_from_string(addr_str)
19251978

1979+
def _explode_shorthand_ip_string(self):
1980+
ipv4_mapped = self.ipv4_mapped
1981+
if ipv4_mapped is None:
1982+
return super()._explode_shorthand_ip_string()
1983+
prefix_len = 30
1984+
raw_exploded_str = super()._explode_shorthand_ip_string()
1985+
return f"{raw_exploded_str[:prefix_len]}{ipv4_mapped!s}"
1986+
1987+
def _reverse_pointer(self):
1988+
ipv4_mapped = self.ipv4_mapped
1989+
if ipv4_mapped is None:
1990+
return super()._reverse_pointer()
1991+
prefix_len = 30
1992+
raw_exploded_str = super()._explode_shorthand_ip_string()[:prefix_len]
1993+
# ipv4 encoded using hexadecimal nibbles instead of decimals
1994+
ipv4_int = ipv4_mapped._ip
1995+
reverse_chars = f"{raw_exploded_str}{ipv4_int:008x}"[::-1].replace(':', '')
1996+
return '.'.join(reverse_chars) + '.ip6.arpa'
1997+
1998+
def _ipv4_mapped_ipv6_to_str(self):
1999+
"""Return convenient text representation of IPv4-mapped IPv6 address
2000+
2001+
See RFC 4291 2.5.5.2, 2.2 p.3 for details.
2002+
2003+
Returns:
2004+
A string, 'x:x:x:x:x:x:d.d.d.d', where the 'x's are the hexadecimal values of
2005+
the six high-order 16-bit pieces of the address, and the 'd's are
2006+
the decimal values of the four low-order 8-bit pieces of the
2007+
address (standard IPv4 representation) as defined in RFC 4291 2.2 p.3.
2008+
2009+
"""
2010+
ipv4_mapped = self.ipv4_mapped
2011+
if ipv4_mapped is None:
2012+
raise AddressValueError("Can not apply to non-IPv4-mapped IPv6 address %s" % str(self))
2013+
high_order_bits = self._ip >> 32
2014+
return "%s:%s" % (self._string_from_ip_int(high_order_bits), str(ipv4_mapped))
2015+
19262016
def __str__(self):
1927-
ip_str = super().__str__()
2017+
ipv4_mapped = self.ipv4_mapped
2018+
if ipv4_mapped is None:
2019+
ip_str = super().__str__()
2020+
else:
2021+
ip_str = self._ipv4_mapped_ipv6_to_str()
19282022
return ip_str + '%' + self._scope_id if self._scope_id else ip_str
19292023

19302024
def __hash__(self):
@@ -1967,6 +2061,9 @@ def is_multicast(self):
19672061
See RFC 2373 2.7 for details.
19682062
19692063
"""
2064+
ipv4_mapped = self.ipv4_mapped
2065+
if ipv4_mapped is not None:
2066+
return ipv4_mapped.is_multicast
19702067
return self in self._constants._multicast_network
19712068

19722069
@property
@@ -1978,6 +2075,9 @@ def is_reserved(self):
19782075
reserved IPv6 Network ranges.
19792076
19802077
"""
2078+
ipv4_mapped = self.ipv4_mapped
2079+
if ipv4_mapped is not None:
2080+
return ipv4_mapped.is_reserved
19812081
return any(self in x for x in self._constants._reserved_networks)
19822082

19832083
@property
@@ -1988,6 +2088,9 @@ def is_link_local(self):
19882088
A boolean, True if the address is reserved per RFC 4291.
19892089
19902090
"""
2091+
ipv4_mapped = self.ipv4_mapped
2092+
if ipv4_mapped is not None:
2093+
return ipv4_mapped.is_link_local
19912094
return self in self._constants._linklocal_network
19922095

19932096
@property
@@ -2007,28 +2110,46 @@ def is_site_local(self):
20072110
@property
20082111
@functools.lru_cache()
20092112
def is_private(self):
2010-
"""Test if this address is allocated for private networks.
2113+
"""``True`` if the address is defined as not globally reachable by
2114+
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
2115+
(for IPv6) with the following exceptions:
20112116
2012-
Returns:
2013-
A boolean, True if the address is reserved per
2014-
iana-ipv6-special-registry, or is ipv4_mapped and is
2015-
reserved in the iana-ipv4-special-registry.
2117+
* ``is_private`` is ``False`` for ``100.64.0.0/10``
2118+
* For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
2119+
semantics of the underlying IPv4 addresses and the following condition holds
2120+
(see :attr:`IPv6Address.ipv4_mapped`)::
2121+
2122+
address.is_private == address.ipv4_mapped.is_private
20162123
2124+
``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10``
2125+
IPv4 range where they are both ``False``.
20172126
"""
20182127
ipv4_mapped = self.ipv4_mapped
20192128
if ipv4_mapped is not None:
20202129
return ipv4_mapped.is_private
2021-
return any(self in net for net in self._constants._private_networks)
2130+
return (
2131+
any(self in net for net in self._constants._private_networks)
2132+
and all(self not in net for net in self._constants._private_networks_exceptions)
2133+
)
20222134

20232135
@property
20242136
def is_global(self):
2025-
"""Test if this address is allocated for public networks.
2137+
"""``True`` if the address is defined as globally reachable by
2138+
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
2139+
(for IPv6) with the following exception:
20262140
2027-
Returns:
2028-
A boolean, true if the address is not reserved per
2029-
iana-ipv6-special-registry.
2141+
For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
2142+
semantics of the underlying IPv4 addresses and the following condition holds
2143+
(see :attr:`IPv6Address.ipv4_mapped`)::
20302144
2145+
address.is_global == address.ipv4_mapped.is_global
2146+
2147+
``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10``
2148+
IPv4 range where they are both ``False``.
20312149
"""
2150+
ipv4_mapped = self.ipv4_mapped
2151+
if ipv4_mapped is not None:
2152+
return ipv4_mapped.is_global
20322153
return not self.is_private
20332154

20342155
@property
@@ -2040,6 +2161,9 @@ def is_unspecified(self):
20402161
RFC 2373 2.5.2.
20412162
20422163
"""
2164+
ipv4_mapped = self.ipv4_mapped
2165+
if ipv4_mapped is not None:
2166+
return ipv4_mapped.is_unspecified
20432167
return self._ip == 0
20442168

20452169
@property
@@ -2051,6 +2175,9 @@ def is_loopback(self):
20512175
RFC 2373 2.5.3.
20522176
20532177
"""
2178+
ipv4_mapped = self.ipv4_mapped
2179+
if ipv4_mapped is not None:
2180+
return ipv4_mapped.is_loopback
20542181
return self._ip == 1
20552182

20562183
@property
@@ -2167,7 +2294,7 @@ def is_unspecified(self):
21672294

21682295
@property
21692296
def is_loopback(self):
2170-
return self._ip == 1 and self.network.is_loopback
2297+
return super().is_loopback and self.network.is_loopback
21712298

21722299

21732300
class IPv6Network(_BaseV6, _BaseNetwork):
@@ -2268,19 +2395,33 @@ class _IPv6Constants:
22682395

22692396
_multicast_network = IPv6Network('ff00::/8')
22702397

2398+
# Not globally reachable address blocks listed on
2399+
# https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
22712400
_private_networks = [
22722401
IPv6Network('::1/128'),
22732402
IPv6Network('::/128'),
22742403
IPv6Network('::ffff:0:0/96'),
2404+
IPv6Network('64:ff9b:1::/48'),
22752405
IPv6Network('100::/64'),
22762406
IPv6Network('2001::/23'),
2277-
IPv6Network('2001:2::/48'),
22782407
IPv6Network('2001:db8::/32'),
2279-
IPv6Network('2001:10::/28'),
2408+
# IANA says N/A, let's consider it not globally reachable to be safe
2409+
IPv6Network('2002::/16'),
2410+
# RFC 9637: https://www.rfc-editor.org/rfc/rfc9637.html#section-6-2.2
2411+
IPv6Network('3fff::/20'),
22802412
IPv6Network('fc00::/7'),
22812413
IPv6Network('fe80::/10'),
22822414
]
22832415

2416+
_private_networks_exceptions = [
2417+
IPv6Network('2001:1::1/128'),
2418+
IPv6Network('2001:1::2/128'),
2419+
IPv6Network('2001:3::/32'),
2420+
IPv6Network('2001:4:112::/48'),
2421+
IPv6Network('2001:20::/28'),
2422+
IPv6Network('2001:30::/28'),
2423+
]
2424+
22842425
_reserved_networks = [
22852426
IPv6Network('::/8'), IPv6Network('100::/8'),
22862427
IPv6Network('200::/7'), IPv6Network('400::/6'),

0 commit comments

Comments
 (0)
0