]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-117566: fix IPv6Address.is_loopback for IPv4-mapped loopbacks (GH-117567...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Mon, 29 Apr 2024 14:41:26 +0000 (16:41 +0200)
committerGitHub <noreply@github.com>
Mon, 29 Apr 2024 14:41:26 +0000 (14:41 +0000)
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 <paravoid@debian.org>
Lib/ipaddress.py
Lib/test/test_ipaddress.py
Misc/NEWS.d/next/Library/2024-04-05-15-51-01.gh-issue-117566.54nABf.rst [new file with mode: 0644]

index 05e56437bcb1333195e1072650da03db8e2bde6a..d8f3b5e2e9e510fe57c8938bd19809ad300650f7 100644 (file)
@@ -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):
index 16c34163a007a2d2713382d11ca8d0759962adc2..e5cbf602770ba43e8a8db10e5eea52ba25bdae52 100644 (file)
@@ -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 (file)
index 0000000..56c2fb0
--- /dev/null
@@ -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.