diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py index 70746f8de85c09..41796c33ff7bcd 100644 --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -1555,6 +1555,19 @@ def is_global(self): self.broadcast_address in IPv4Network('100.64.0.0/10')) and not self.is_private) + def _reverse_pointer(self): + """Return the reverse DNS pointer name for the IPv4 address. + + This implements the method described in RFC1035 3.5. + + For networks for which no exact domain is matched the covering domain is returned. + + >>> IPv4Network('127.0.0.0/13').reverse_pointer + '127.in-addr.arpa + """ + octets = str(self.network_address).split('.')[:self.prefixlen // 8] + return '.'.join(reversed(['arpa', 'in-addr'] + octets)) + class _IPv4Constants: _linklocal_network = IPv4Network('169.254.0.0/16') @@ -2262,6 +2275,15 @@ def is_site_local(self): return (self.network_address.is_site_local and self.broadcast_address.is_site_local) + def _reverse_pointer(self): + """Return the reverse DNS pointer name for the IPv6 network. + + This implements the method described in RFC3596 2.5. + + """ + chars = self.exploded.replace(':', '')[:self.prefixlen // 4] + return '.'.join(reversed(['arpa', 'ip6'] + list(chars))) + class _IPv6Constants: diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index 5d9633024f7064..a0c019f9095691 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -477,6 +477,26 @@ class InterfaceTestCase_v4(BaseTestCase, NetmaskTestMixin_v4): class NetworkTestCase_v4(BaseTestCase, NetmaskTestMixin_v4): factory = ipaddress.IPv4Network + def test_reverse_pointer(self): + self.assertEqual(self.factory('0.0.0.0/0').reverse_pointer, + 'in-addr.arpa') + self.assertEqual(self.factory('127.0.0.0/8').reverse_pointer, + '127.in-addr.arpa') + self.assertEqual(self.factory('127.128.0.0/9').reverse_pointer, + '127.in-addr.arpa') + self.assertEqual(self.factory('127.254.0.0/15').reverse_pointer, + '127.in-addr.arpa') + self.assertEqual(self.factory('127.255.0.0/16').reverse_pointer, + '255.127.in-addr.arpa') + self.assertEqual(self.factory('127.255.123.0/24').reverse_pointer, + '123.255.127.in-addr.arpa') + self.assertEqual(self.factory('127.255.123.128/25').reverse_pointer, + '123.255.127.in-addr.arpa') + self.assertEqual(self.factory('127.255.123.254/31').reverse_pointer, + '123.255.127.in-addr.arpa') + self.assertEqual(self.factory('127.111.222.255/32').reverse_pointer, + '255.222.111.127.in-addr.arpa') + class NetmaskTestMixin_v6(CommonTestMixin_v6): """Input validation on interfaces and networks is very similar""" @@ -540,6 +560,18 @@ class InterfaceTestCase_v6(BaseTestCase, NetmaskTestMixin_v6): class NetworkTestCase_v6(BaseTestCase, NetmaskTestMixin_v6): factory = ipaddress.IPv6Network + def test_reverse_pointer(self): + self.assertEqual(self.factory('::/0').reverse_pointer, + 'ip6.arpa') + self.assertEqual(self.factory('2001:db8::/32').reverse_pointer, + '8.b.d.0.1.0.0.2.ip6.arpa') + self.assertEqual(self.factory('2001:db8:1234::/48').reverse_pointer, + '4.3.2.1.8.b.d.0.1.0.0.2.ip6.arpa') + self.assertEqual(self.factory('2001:db8:1234::/49').reverse_pointer, + '4.3.2.1.8.b.d.0.1.0.0.2.ip6.arpa') + self.assertEqual(self.factory('2001:db8:1234:5678:90ab:cdef:cafe:b100').reverse_pointer, + '0.0.1.b.e.f.a.c.f.e.d.c.b.a.0.9.8.7.6.5.4.3.2.1.8.b.d.0.1.0.0.2.ip6.arpa') + class FactoryFunctionErrors(BaseTestCase):