From 7a277cecc6227edf746a40dbad250503ef3f7d20 Mon Sep 17 00:00:00 2001 From: Bob Halley Date: Sat, 31 May 2014 11:17:38 -0700 Subject: Add dns.ipv6.is_mapped(); Reverse IPv6 mapped IPv4 into v4 space --- ChangeLog | 8 ++++++++ dns/ipv6.py | 5 +++++ dns/reversename.py | 9 +++++++-- tests/test_name.py | 9 +++++++-- tests/test_ntoaaton.py | 8 ++++++++ 5 files changed, 35 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index b413572..9ee3bf7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2014-05-31 Bob Halley + + * dns/ipv6.py: Add is_mapped() + + * dns/reversename.py: Lookup IPv6 mapped IPv4 addresses in the v4 + reverse namespace. Thanks to Devin Bayer. Yes, I finally fixed + this one :) + 2014-04-11 Bob Halley * dns/zone.py: Do not put back an unescaped token. This was diff --git a/dns/ipv6.py b/dns/ipv6.py index 1ab00da..bf658af 100644 --- a/dns/ipv6.py +++ b/dns/ipv6.py @@ -161,3 +161,8 @@ def inet_aton(text): return text.decode('hex_codec') except TypeError: raise dns.exception.SyntaxError + +_mapped_prefix = '\x00' * 10 + '\xff\xff' + +def is_mapped(address): + return address.startswith(_mapped_prefix) diff --git a/dns/reversename.py b/dns/reversename.py index 4925cfd..b80c60a 100644 --- a/dns/reversename.py +++ b/dns/reversename.py @@ -37,8 +37,13 @@ def from_address(text): @rtype: dns.name.Name object """ try: - parts = list(dns.ipv6.inet_aton(text).encode('hex_codec')) - origin = ipv6_reverse_domain + v6 = dns.ipv6.inet_aton(text) + if dns.ipv6.is_mapped(v6): + parts = ['%d' % ord(byte) for byte in v6[12:]] + origin = ipv4_reverse_domain + else: + parts = list(v6.encode('hex_codec')) + origin = ipv6_reverse_domain except: parts = ['%d' % ord(byte) for byte in dns.ipv4.inet_aton(text)] origin = ipv4_reverse_domain diff --git a/tests/test_name.py b/tests/test_name.py index e30e43d..894a1a4 100644 --- a/tests/test_name.py +++ b/tests/test_name.py @@ -25,7 +25,7 @@ import dns.e164 class NameTestCase(unittest.TestCase): def setUp(self): self.origin = dns.name.from_text('example.') - + def testFromTextRel1(self): n = dns.name.from_text('foo.bar') self.failUnless(n.labels == ('foo', 'bar', '')) @@ -352,7 +352,7 @@ class NameTestCase(unittest.TestCase): n = dns.name.from_text('FOO.bar', None) d = n.to_digestable(dns.name.root) self.failUnless(d == '\x03foo\x03bar\x00') - + def testBadDigestable(self): def bad(): n = dns.name.from_text('FOO.bar', None) @@ -659,6 +659,11 @@ class NameTestCase(unittest.TestCase): n = dns.reversename.from_address('::1') self.failUnless(e == n) + def testReverseIPv6MappedIpv4(self): + e = dns.name.from_text('1.0.0.127.in-addr.arpa.') + n = dns.reversename.from_address('::ffff:127.0.0.1') + self.failUnless(e == n) + def testBadReverseIPv4(self): def bad(): n = dns.reversename.from_address('127.0.foo.1') diff --git a/tests/test_ntoaaton.py b/tests/test_ntoaaton.py index e93de2d..5e33a92 100644 --- a/tests/test_ntoaaton.py +++ b/tests/test_ntoaaton.py @@ -199,5 +199,13 @@ class NtoAAtoNTestCase(unittest.TestCase): t1 = ntoa6(b1) self.failUnless(t1 == addr) + def test_is_mapped(self): + t1 = '2001:db8:0:1:1:1:1:1' + t2 = '::ffff:127.0.0.1' + t3 = '1::ffff:127.0.0.1' + self.failIf(dns.ipv6.is_mapped(aton6(t1))) + self.failUnless(dns.ipv6.is_mapped(aton6(t2))) + self.failIf(dns.ipv6.is_mapped(aton6(t3))) + if __name__ == '__main__': unittest.main() -- cgit v1.2.1