diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py index f8c9463430410b..5f70b3c5a71405 100644 --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -1960,12 +1960,21 @@ def __init__(self, address): def _explode_shorthand_ip_string(self): ipv4_mapped = self.ipv4_mapped if ipv4_mapped is None: - long_form = super()._explode_shorthand_ip_string() - else: - prefix_len = 30 - raw_exploded_str = super()._explode_shorthand_ip_string() - long_form = "%s%s" % (raw_exploded_str[:prefix_len], str(ipv4_mapped)) - return long_form + return super()._explode_shorthand_ip_string() + prefix_len = 30 + raw_exploded_str = super()._explode_shorthand_ip_string() + return f"{raw_exploded_str[:prefix_len]}{ipv4_mapped!s}" + + def _reverse_pointer(self): + ipv4_mapped = self.ipv4_mapped + if ipv4_mapped is None: + return super()._reverse_pointer() + prefix_len = 30 + raw_exploded_str = super()._explode_shorthand_ip_string()[:prefix_len] + # ipv4 encoded using hexadecimal nibbles instead of decimals + ipv4_int = ipv4_mapped._ip + reverse_chars = f"{raw_exploded_str}{ipv4_int:008x}"[::-1].replace(':', '') + return '.'.join(reverse_chars) + '.ip6.arpa' def _ipv4_mapped_ipv6_to_str(self): """Return convenient text representation of IPv4-mapped IPv6 address diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index 398880ae0fad55..5d798b6b9b8e83 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -2585,12 +2585,42 @@ def testExplodeShortHandIpStr(self): self.assertEqual('192.168.178.1', addr4.exploded) def testReversePointer(self): - addr1 = ipaddress.IPv4Address('127.0.0.1') - addr2 = ipaddress.IPv6Address('2001:db8::1') - self.assertEqual('1.0.0.127.in-addr.arpa', addr1.reverse_pointer) - self.assertEqual('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.' + - 'b.d.0.1.0.0.2.ip6.arpa', - addr2.reverse_pointer) + for addr_v4, expected in [ + ('127.0.0.1', '1.0.0.127.in-addr.arpa'), + # test vector: https://www.rfc-editor.org/rfc/rfc1035, §3.5 + ('10.2.0.52', '52.0.2.10.in-addr.arpa'), + ]: + with self.subTest('ipv4_reverse_pointer', addr=addr_v4): + addr = ipaddress.IPv4Address(addr_v4) + self.assertEqual(addr.reverse_pointer, expected) + + for addr_v6, expected in [ + ( + '2001:db8::1', ( + '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.' + '0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.' + 'ip6.arpa' + ) + ), + ( + '::FFFF:192.168.1.35', ( + '3.2.1.0.8.a.0.c.f.f.f.f.0.0.0.0.' + '0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.' + 'ip6.arpa' + ) + ), + # test vector: https://www.rfc-editor.org/rfc/rfc3596, §2.5 + ( + '4321:0:1:2:3:4:567:89ab', ( + 'b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.' + '2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.' + 'ip6.arpa' + ) + ) + ]: + with self.subTest('ipv6_reverse_pointer', addr=addr_v6): + addr = ipaddress.IPv6Address(addr_v6) + self.assertEqual(addr.reverse_pointer, expected) def testIntRepresentation(self): self.assertEqual(16909060, int(self.ipv4_address)) diff --git a/Misc/NEWS.d/next/Library/2024-08-28-13-03-36.gh-issue-123409.lW0YF-.rst b/Misc/NEWS.d/next/Library/2024-08-28-13-03-36.gh-issue-123409.lW0YF-.rst new file mode 100644 index 00000000000000..7c6aab632b674a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-08-28-13-03-36.gh-issue-123409.lW0YF-.rst @@ -0,0 +1,2 @@ +Fix :attr:`ipaddress.IPv6Address.reverse_pointer` output according to +:rfc:`RFC 3596, §2.5 <3596#section-2.5>`. Patch by Bénédikt Tran.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: