From 3989894a396c8c2abc78fcd8758542eed0f68912 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 29 Apr 2024 16:41:26 +0200 Subject: [PATCH] [3.12] gh-117566: fix IPv6Address.is_loopback for IPv4-mapped loopbacks (GH-117567) (GH-118391) gh-117566: fix IPv6Address.is_loopback for IPv4-mapped loopbacks (GH-117567) While properties like IPv6Address.is_private account for IPv4-mapped IPv6 addresses, such as for example: >>> ipaddress.ip_address("192.168.0.1").is_private True >>> ipaddress.ip_address("::ffff:192.168.0.1").is_private True ...the same doesn't currently apply to the is_loopback property: >>> ipaddress.ip_address("127.0.0.1").is_loopback True >>> ipaddress.ip_address("::ffff:127.0.0.1").is_loopback False At minimum, this inconsistency between different properties is counter-intuitive. Moreover, ::ffff:127.0.0.0/104 is for all intents and purposes a loopback address, and should be treated as such. (cherry picked from commit fb7f79b4da35b75cdc82ff3cf20816d2bf93d416) Co-authored-by: Faidon Liambotis --- Lib/ipaddress.py | 5 ++++- Lib/test/test_ipaddress.py | 16 ++++++++++++++++ ...024-04-05-15-51-01.gh-issue-117566.54nABf.rst | 3 +++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-04-05-15-51-01.gh-issue-117566.54nABf.rst diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py index 05e56437bcb..d8f3b5e2e9e 100644 --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -2100,6 +2100,9 @@ class IPv6Address(_BaseV6, _BaseAddress): 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 @@ -2216,7 +2219,7 @@ class IPv6Interface(IPv6Address): @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): diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index 16c34163a00..e5cbf602770 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -2427,6 +2427,22 @@ class IpaddrUnitTest(unittest.TestCase): self.assertEqual( False, ipaddress.ip_address('::ffff:172.32.0.0').is_private) + def testIpv4MappedLoopbackCheck(self): + # test networks + self.assertEqual(True, ipaddress.ip_network( + '::ffff:127.100.200.254/128').is_loopback) + self.assertEqual(True, ipaddress.ip_network( + '::ffff:127.42.0.0/112').is_loopback) + self.assertEqual(False, ipaddress.ip_network( + '::ffff:128.0.0.0').is_loopback) + # test addresses + self.assertEqual(True, ipaddress.ip_address( + '::ffff:127.100.200.254').is_loopback) + self.assertEqual(True, ipaddress.ip_address( + '::ffff:127.42.0.0').is_loopback) + self.assertEqual(False, ipaddress.ip_address( + '::ffff:128.0.0.0').is_loopback) + 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/2024-04-05-15-51-01.gh-issue-117566.54nABf.rst b/Misc/NEWS.d/next/Library/2024-04-05-15-51-01.gh-issue-117566.54nABf.rst new file mode 100644 index 00000000000..56c2fb0e25d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-05-15-51-01.gh-issue-117566.54nABf.rst @@ -0,0 +1,3 @@ +:meth:`ipaddress.IPv6Address.is_loopback` will now return ``True`` for +IPv4-mapped loopback addresses, i.e. addresses in the +``::ffff:127.0.0.0/104`` address space.