mirror of https://github.com/python/cpython
GH-113171: Fix "private" (non-global) IP address ranges (GH-113179)
* GH-113171: Fix "private" (really non-global) IP address ranges The _private_networks variables, used by various is_private implementations, were missing some ranges and at the same time had overly strict ranges (where there are more specific ranges considered globally reachable by the IANA registries). This patch updates the ranges with what was missing or otherwise incorrect. I left 100.64.0.0/10 alone, for now, as it's been made special in [1] and I'm not sure if we want to undo that as I don't quite understand the motivation behind it. The _address_exclude_many() call returns 8 networks for IPv4, 121 networks for IPv6. [1] https://github.com/python/cpython/issues/61602
This commit is contained in:
parent
3be9b9d872
commit
40d75c2b7f
|
@ -192,6 +192,18 @@ write code that handles both IP versions correctly. Address objects are
|
|||
``is_private`` has value opposite to :attr:`is_global`, except for the shared address space
|
||||
(``100.64.0.0/10`` range) where they are both ``False``.
|
||||
|
||||
.. versionchanged:: 3.13
|
||||
|
||||
Fixed some false positives and false negatives.
|
||||
|
||||
* ``192.0.0.0/24`` is considered private with the exception of ``192.0.0.9/32`` and
|
||||
``192.0.0.10/32`` (previously: only the ``192.0.0.0/29`` sub-range was considered private).
|
||||
* ``64:ff9b:1::/48`` is considered private.
|
||||
* ``2002::/16`` is considered private.
|
||||
* There are exceptions within ``2001::/23`` (otherwise considered private): ``2001:1::1/128``,
|
||||
``2001:1::2/128``, ``2001:3::/32``, ``2001:4:112::/48``, ``2001:20::/28``, ``2001:30::/28``.
|
||||
The exceptions are not considered private.
|
||||
|
||||
.. attribute:: is_global
|
||||
|
||||
``True`` if the address is defined as globally reachable by
|
||||
|
@ -209,6 +221,10 @@ write code that handles both IP versions correctly. Address objects are
|
|||
|
||||
.. versionadded:: 3.4
|
||||
|
||||
.. versionchanged:: 3.13
|
||||
|
||||
Fixed some false positives and false negatives, see :attr:`is_private` for details.
|
||||
|
||||
.. attribute:: is_unspecified
|
||||
|
||||
``True`` if the address is unspecified. See :RFC:`5735` (for IPv4)
|
||||
|
|
|
@ -401,6 +401,8 @@ ipaddress
|
|||
|
||||
* Add the :attr:`ipaddress.IPv4Address.ipv6_mapped` property, which returns the IPv4-mapped IPv6 address.
|
||||
(Contributed by Charles Machalow in :gh:`109466`.)
|
||||
* Fix ``is_global`` and ``is_private`` behavior in ``IPv4Address``, ``IPv6Address``, ``IPv4Network``
|
||||
and ``IPv6Network``.
|
||||
|
||||
itertools
|
||||
---------
|
||||
|
|
|
@ -1086,7 +1086,11 @@ class _BaseNetwork(_IPAddressBase):
|
|||
"""
|
||||
return any(self.network_address in priv_network and
|
||||
self.broadcast_address in priv_network
|
||||
for priv_network in self._constants._private_networks)
|
||||
for priv_network in self._constants._private_networks) and all(
|
||||
self.network_address not in network and
|
||||
self.broadcast_address not in network
|
||||
for network in self._constants._private_networks_exceptions
|
||||
)
|
||||
|
||||
@property
|
||||
def is_global(self):
|
||||
|
@ -1347,7 +1351,10 @@ class IPv4Address(_BaseV4, _BaseAddress):
|
|||
``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10``
|
||||
IPv4 range where they are both ``False``.
|
||||
"""
|
||||
return any(self in net for net in self._constants._private_networks)
|
||||
return (
|
||||
any(self in net for net in self._constants._private_networks)
|
||||
and all(self not in net for net in self._constants._private_networks_exceptions)
|
||||
)
|
||||
|
||||
@property
|
||||
@functools.lru_cache()
|
||||
|
@ -1578,13 +1585,15 @@ class _IPv4Constants:
|
|||
|
||||
_public_network = IPv4Network('100.64.0.0/10')
|
||||
|
||||
# Not globally reachable address blocks listed on
|
||||
# https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
|
||||
_private_networks = [
|
||||
IPv4Network('0.0.0.0/8'),
|
||||
IPv4Network('10.0.0.0/8'),
|
||||
IPv4Network('127.0.0.0/8'),
|
||||
IPv4Network('169.254.0.0/16'),
|
||||
IPv4Network('172.16.0.0/12'),
|
||||
IPv4Network('192.0.0.0/29'),
|
||||
IPv4Network('192.0.0.0/24'),
|
||||
IPv4Network('192.0.0.170/31'),
|
||||
IPv4Network('192.0.2.0/24'),
|
||||
IPv4Network('192.168.0.0/16'),
|
||||
|
@ -1595,6 +1604,11 @@ class _IPv4Constants:
|
|||
IPv4Network('255.255.255.255/32'),
|
||||
]
|
||||
|
||||
_private_networks_exceptions = [
|
||||
IPv4Network('192.0.0.9/32'),
|
||||
IPv4Network('192.0.0.10/32'),
|
||||
]
|
||||
|
||||
_reserved_network = IPv4Network('240.0.0.0/4')
|
||||
|
||||
_unspecified_address = IPv4Address('0.0.0.0')
|
||||
|
@ -2086,7 +2100,10 @@ class IPv6Address(_BaseV6, _BaseAddress):
|
|||
ipv4_mapped = self.ipv4_mapped
|
||||
if ipv4_mapped is not None:
|
||||
return ipv4_mapped.is_private
|
||||
return any(self in net for net in self._constants._private_networks)
|
||||
return (
|
||||
any(self in net for net in self._constants._private_networks)
|
||||
and all(self not in net for net in self._constants._private_networks_exceptions)
|
||||
)
|
||||
|
||||
@property
|
||||
def is_global(self):
|
||||
|
@ -2342,19 +2359,31 @@ class _IPv6Constants:
|
|||
|
||||
_multicast_network = IPv6Network('ff00::/8')
|
||||
|
||||
# Not globally reachable address blocks listed on
|
||||
# https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
|
||||
_private_networks = [
|
||||
IPv6Network('::1/128'),
|
||||
IPv6Network('::/128'),
|
||||
IPv6Network('::ffff:0:0/96'),
|
||||
IPv6Network('64:ff9b:1::/48'),
|
||||
IPv6Network('100::/64'),
|
||||
IPv6Network('2001::/23'),
|
||||
IPv6Network('2001:2::/48'),
|
||||
IPv6Network('2001:db8::/32'),
|
||||
IPv6Network('2001:10::/28'),
|
||||
# IANA says N/A, let's consider it not globally reachable to be safe
|
||||
IPv6Network('2002::/16'),
|
||||
IPv6Network('fc00::/7'),
|
||||
IPv6Network('fe80::/10'),
|
||||
]
|
||||
|
||||
_private_networks_exceptions = [
|
||||
IPv6Network('2001:1::1/128'),
|
||||
IPv6Network('2001:1::2/128'),
|
||||
IPv6Network('2001:3::/32'),
|
||||
IPv6Network('2001:4:112::/48'),
|
||||
IPv6Network('2001:20::/28'),
|
||||
IPv6Network('2001:30::/28'),
|
||||
]
|
||||
|
||||
_reserved_networks = [
|
||||
IPv6Network('::/8'), IPv6Network('100::/8'),
|
||||
IPv6Network('200::/7'), IPv6Network('400::/6'),
|
||||
|
|
|
@ -2288,6 +2288,10 @@ class IpaddrUnitTest(unittest.TestCase):
|
|||
self.assertEqual(True, ipaddress.ip_address(
|
||||
'172.31.255.255').is_private)
|
||||
self.assertEqual(False, ipaddress.ip_address('172.32.0.0').is_private)
|
||||
self.assertFalse(ipaddress.ip_address('192.0.0.0').is_global)
|
||||
self.assertTrue(ipaddress.ip_address('192.0.0.9').is_global)
|
||||
self.assertTrue(ipaddress.ip_address('192.0.0.10').is_global)
|
||||
self.assertFalse(ipaddress.ip_address('192.0.0.255').is_global)
|
||||
|
||||
self.assertEqual(True,
|
||||
ipaddress.ip_address('169.254.100.200').is_link_local)
|
||||
|
@ -2313,6 +2317,7 @@ class IpaddrUnitTest(unittest.TestCase):
|
|||
self.assertEqual(True, ipaddress.ip_network("169.254.0.0/16").is_private)
|
||||
self.assertEqual(True, ipaddress.ip_network("172.16.0.0/12").is_private)
|
||||
self.assertEqual(True, ipaddress.ip_network("192.0.0.0/29").is_private)
|
||||
self.assertEqual(False, ipaddress.ip_network("192.0.0.9/32").is_private)
|
||||
self.assertEqual(True, ipaddress.ip_network("192.0.0.170/31").is_private)
|
||||
self.assertEqual(True, ipaddress.ip_network("192.0.2.0/24").is_private)
|
||||
self.assertEqual(True, ipaddress.ip_network("192.168.0.0/16").is_private)
|
||||
|
@ -2329,8 +2334,8 @@ class IpaddrUnitTest(unittest.TestCase):
|
|||
self.assertEqual(True, ipaddress.ip_network("::/128").is_private)
|
||||
self.assertEqual(True, ipaddress.ip_network("::ffff:0:0/96").is_private)
|
||||
self.assertEqual(True, ipaddress.ip_network("100::/64").is_private)
|
||||
self.assertEqual(True, ipaddress.ip_network("2001::/23").is_private)
|
||||
self.assertEqual(True, ipaddress.ip_network("2001:2::/48").is_private)
|
||||
self.assertEqual(False, ipaddress.ip_network("2001:3::/48").is_private)
|
||||
self.assertEqual(True, ipaddress.ip_network("2001:db8::/32").is_private)
|
||||
self.assertEqual(True, ipaddress.ip_network("2001:10::/28").is_private)
|
||||
self.assertEqual(True, ipaddress.ip_network("fc00::/7").is_private)
|
||||
|
@ -2409,6 +2414,20 @@ class IpaddrUnitTest(unittest.TestCase):
|
|||
self.assertEqual(True, ipaddress.ip_address('0::0').is_unspecified)
|
||||
self.assertEqual(False, ipaddress.ip_address('::1').is_unspecified)
|
||||
|
||||
self.assertFalse(ipaddress.ip_address('64:ff9b:1::').is_global)
|
||||
self.assertFalse(ipaddress.ip_address('2001::').is_global)
|
||||
self.assertTrue(ipaddress.ip_address('2001:1::1').is_global)
|
||||
self.assertTrue(ipaddress.ip_address('2001:1::2').is_global)
|
||||
self.assertFalse(ipaddress.ip_address('2001:2::').is_global)
|
||||
self.assertTrue(ipaddress.ip_address('2001:3::').is_global)
|
||||
self.assertFalse(ipaddress.ip_address('2001:4::').is_global)
|
||||
self.assertTrue(ipaddress.ip_address('2001:4:112::').is_global)
|
||||
self.assertFalse(ipaddress.ip_address('2001:10::').is_global)
|
||||
self.assertTrue(ipaddress.ip_address('2001:20::').is_global)
|
||||
self.assertTrue(ipaddress.ip_address('2001:30::').is_global)
|
||||
self.assertFalse(ipaddress.ip_address('2001:40::').is_global)
|
||||
self.assertFalse(ipaddress.ip_address('2002::').is_global)
|
||||
|
||||
# some generic IETF reserved addresses
|
||||
self.assertEqual(True, ipaddress.ip_address('100::').is_reserved)
|
||||
self.assertEqual(True, ipaddress.ip_network('4000::1/128').is_reserved)
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
Fixed various false positives and false negatives in
|
||||
|
||||
* :attr:`ipaddress.IPv4Address.is_private` (see these docs for details)
|
||||
* :attr:`ipaddress.IPv4Address.is_global`
|
||||
* :attr:`ipaddress.IPv6Address.is_private`
|
||||
* :attr:`ipaddress.IPv6Address.is_global`
|
||||
|
||||
Also in the corresponding :class:`ipaddress.IPv4Network` and :class:`ipaddress.IPv6Network`
|
||||
attributes.
|
Loading…
Reference in New Issue