mirror of
https://github.com/python/cpython.git
synced 2026-01-06 15:32:22 +00:00
[3.11] gh-122792: Make IPv4-mapped IPv6 address properties consistent with IPv4 (GH-122793) (GH-123818)
Make IPv4-mapped IPv6 address properties consistent with IPv4.
(cherry picked from commit 76a1c5d183)
Co-authored-by: Seth Michael Larson <seth@python.org>
---------
Co-authored-by: Seth Michael Larson <seth@python.org>
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
This commit is contained in:
parent
2e161e2499
commit
66a01b068b
3 changed files with 49 additions and 5 deletions
|
|
@ -310,7 +310,7 @@ def collapse_addresses(addresses):
|
|||
[IPv4Network('192.0.2.0/24')]
|
||||
|
||||
Args:
|
||||
addresses: An iterator of IPv4Network or IPv6Network objects.
|
||||
addresses: An iterable of IPv4Network or IPv6Network objects.
|
||||
|
||||
Returns:
|
||||
An iterator of the collapsed IPv(4|6)Network objects.
|
||||
|
|
@ -1855,9 +1855,6 @@ def _string_from_ip_int(cls, ip_int=None):
|
|||
def _explode_shorthand_ip_string(self):
|
||||
"""Expand a shortened IPv6 address.
|
||||
|
||||
Args:
|
||||
ip_str: A string, the IPv6 address.
|
||||
|
||||
Returns:
|
||||
A string, the expanded IPv6 address.
|
||||
|
||||
|
|
@ -2004,6 +2001,9 @@ def is_multicast(self):
|
|||
See RFC 2373 2.7 for details.
|
||||
|
||||
"""
|
||||
ipv4_mapped = self.ipv4_mapped
|
||||
if ipv4_mapped is not None:
|
||||
return ipv4_mapped.is_multicast
|
||||
return self in self._constants._multicast_network
|
||||
|
||||
@property
|
||||
|
|
@ -2015,6 +2015,9 @@ def is_reserved(self):
|
|||
reserved IPv6 Network ranges.
|
||||
|
||||
"""
|
||||
ipv4_mapped = self.ipv4_mapped
|
||||
if ipv4_mapped is not None:
|
||||
return ipv4_mapped.is_reserved
|
||||
return any(self in x for x in self._constants._reserved_networks)
|
||||
|
||||
@property
|
||||
|
|
@ -2025,6 +2028,9 @@ def is_link_local(self):
|
|||
A boolean, True if the address is reserved per RFC 4291.
|
||||
|
||||
"""
|
||||
ipv4_mapped = self.ipv4_mapped
|
||||
if ipv4_mapped is not None:
|
||||
return ipv4_mapped.is_link_local
|
||||
return self in self._constants._linklocal_network
|
||||
|
||||
@property
|
||||
|
|
@ -2081,6 +2087,9 @@ def is_global(self):
|
|||
``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10``
|
||||
IPv4 range where they are both ``False``.
|
||||
"""
|
||||
ipv4_mapped = self.ipv4_mapped
|
||||
if ipv4_mapped is not None:
|
||||
return ipv4_mapped.is_global
|
||||
return not self.is_private
|
||||
|
||||
@property
|
||||
|
|
@ -2092,6 +2101,9 @@ def is_unspecified(self):
|
|||
RFC 2373 2.5.2.
|
||||
|
||||
"""
|
||||
ipv4_mapped = self.ipv4_mapped
|
||||
if ipv4_mapped is not None:
|
||||
return ipv4_mapped.is_unspecified
|
||||
return self._ip == 0
|
||||
|
||||
@property
|
||||
|
|
@ -2103,6 +2115,9 @@ def is_loopback(self):
|
|||
RFC 2373 2.5.3.
|
||||
|
||||
"""
|
||||
ipv4_mapped = self.ipv4_mapped
|
||||
if ipv4_mapped is not None:
|
||||
return ipv4_mapped.is_loopback
|
||||
return self._ip == 1
|
||||
|
||||
@property
|
||||
|
|
@ -2219,7 +2234,7 @@ def is_unspecified(self):
|
|||
|
||||
@property
|
||||
def is_loopback(self):
|
||||
return self._ip == 1 and self.network.is_loopback
|
||||
return super().is_loopback and self.network.is_loopback
|
||||
|
||||
|
||||
class IPv6Network(_BaseV6, _BaseNetwork):
|
||||
|
|
@ -2332,6 +2347,8 @@ class _IPv6Constants:
|
|||
IPv6Network('2001:db8::/32'),
|
||||
# IANA says N/A, let's consider it not globally reachable to be safe
|
||||
IPv6Network('2002::/16'),
|
||||
# RFC 9637: https://www.rfc-editor.org/rfc/rfc9637.html#section-6-2.2
|
||||
IPv6Network('3fff::/20'),
|
||||
IPv6Network('fc00::/7'),
|
||||
IPv6Network('fe80::/10'),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -2421,6 +2421,30 @@ def testIpv4Mapped(self):
|
|||
self.assertEqual(ipaddress.ip_address('::ffff:c0a8:101').ipv4_mapped,
|
||||
ipaddress.ip_address('192.168.1.1'))
|
||||
|
||||
def testIpv4MappedProperties(self):
|
||||
# Test that an IPv4 mapped IPv6 address has
|
||||
# the same properties as an IPv4 address.
|
||||
for addr4 in (
|
||||
"178.62.3.251", # global
|
||||
"169.254.169.254", # link local
|
||||
"127.0.0.1", # loopback
|
||||
"224.0.0.1", # multicast
|
||||
"192.168.0.1", # private
|
||||
"0.0.0.0", # unspecified
|
||||
"100.64.0.1", # public and not global
|
||||
):
|
||||
with self.subTest(addr4):
|
||||
ipv4 = ipaddress.IPv4Address(addr4)
|
||||
ipv6 = ipaddress.IPv6Address(f"::ffff:{addr4}")
|
||||
|
||||
self.assertEqual(ipv4.is_global, ipv6.is_global)
|
||||
self.assertEqual(ipv4.is_private, ipv6.is_private)
|
||||
self.assertEqual(ipv4.is_reserved, ipv6.is_reserved)
|
||||
self.assertEqual(ipv4.is_multicast, ipv6.is_multicast)
|
||||
self.assertEqual(ipv4.is_unspecified, ipv6.is_unspecified)
|
||||
self.assertEqual(ipv4.is_link_local, ipv6.is_link_local)
|
||||
self.assertEqual(ipv4.is_loopback, ipv6.is_loopback)
|
||||
|
||||
def testIpv4MappedPrivateCheck(self):
|
||||
self.assertEqual(
|
||||
True, ipaddress.ip_address('::ffff:192.168.1.1').is_private)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
Changed IPv4-mapped ``ipaddress.IPv6Address`` to consistently use the mapped IPv4
|
||||
address value for deciding properties. Properties which have their behavior fixed
|
||||
are ``is_multicast``, ``is_reserved``, ``is_link_local``, ``is_global``, and ``is_unspecified``.
|
||||
Loading…
Add table
Add a link
Reference in a new issue