diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py index af7aedfa6e5..4a6496a5da3 100644 --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -16,6 +16,7 @@ import functools IPV4LENGTH = 32 IPV6LENGTH = 128 + class AddressValueError(ValueError): """A Value Error related to the address.""" @@ -2002,9 +2003,13 @@ class IPv6Address(_BaseV6, _BaseAddress): Returns: A boolean, True if the address is reserved per - iana-ipv6-special-registry. + iana-ipv6-special-registry, or is ipv4_mapped and is + reserved in the iana-ipv4-special-registry. """ + 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) @property diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index 90559ce1242..ff77bdb1bbc 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -2339,6 +2339,12 @@ class IpaddrUnitTest(unittest.TestCase): self.assertEqual(ipaddress.ip_address('::ffff:c0a8:101').ipv4_mapped, ipaddress.ip_address('192.168.1.1')) + def testIpv4MappedPrivateCheck(self): + self.assertEqual( + True, ipaddress.ip_address('::ffff:192.168.1.1').is_private) + self.assertEqual( + False, ipaddress.ip_address('::ffff:172.32.0.0').is_private) + def testAddrExclude(self): addr1 = ipaddress.ip_network('10.1.1.0/24') addr2 = ipaddress.ip_network('10.1.1.0/26') diff --git a/Misc/NEWS.d/next/Library/2021-05-16-17-48-24.bpo-33433.MyzO71.rst b/Misc/NEWS.d/next/Library/2021-05-16-17-48-24.bpo-33433.MyzO71.rst new file mode 100644 index 00000000000..703e038fac9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-05-16-17-48-24.bpo-33433.MyzO71.rst @@ -0,0 +1 @@ +For IPv4 mapped IPv6 addresses (:rfc:`4291` Section 2.5.5.2), the :mod:`ipaddress.IPv6Address.is_private` check is deferred to the mapped IPv4 address. This solves a bug where public mapped IPv4 addresses were considered private by the IPv6 check.