Skip to content

Commit

Permalink
pythongh-117566: fix IPv6Address.is_loopback for IPv4-mapped loopbacks (
Browse files Browse the repository at this point in the history
pythonGH-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.
  • Loading branch information
paravoid committed Apr 25, 2024
1 parent eb20a7d commit fb7f79b
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 1 deletion.
5 changes: 4 additions & 1 deletion Lib/ipaddress.py
Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down
16 changes: 16 additions & 0 deletions Lib/test/test_ipaddress.py
Expand Up @@ -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')
Expand Down
@@ -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.

0 comments on commit fb7f79b

Please sign in to comment.