Mailing List Archive

gh-117566: fix IPv6Address.is_loopback for IPv4-mapped loopbacks (GH-117567)
https://github.com/python/cpython/commit/fb7f79b4da35b75cdc82ff3cf20816d2bf93d416
commit: fb7f79b4da35b75cdc82ff3cf20816d2bf93d416
branch: main
author: Faidon Liambotis <paravoid@debian.org>
committer: encukou <encukou@gmail.com>
date: 2024-04-25T15:17:40Z
summary:

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.

files:
A Misc/NEWS.d/next/Library/2024-04-05-15-51-01.gh-issue-117566.54nABf.rst
M Lib/ipaddress.py
M Lib/test/test_ipaddress.py

diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py
index 22cdfc93d8ad32..8e4d49c859534d 100644
--- a/Lib/ipaddress.py
+++ b/Lib/ipaddress.py
@@ -2142,6 +2142,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
@@ -2258,7 +2261,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):
diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py
index f1519df673747a..c3ecf2a742941a 100644
--- a/Lib/test/test_ipaddress.py
+++ b/Lib/test/test_ipaddress.py
@@ -2446,6 +2446,22 @@ def testIpv4MappedPrivateCheck(self):
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 00000000000000..56c2fb0e25d494
--- /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.

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-leave@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: list-python-checkins@lists.gossamer-threads.com