diff options
-rw-r--r-- | ChangeLog | 45 | ||||
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | dns/dnssec.py | 2 | ||||
-rw-r--r-- | dns/ipv6.py | 5 | ||||
-rw-r--r-- | dns/name.py | 8 | ||||
-rw-r--r-- | dns/rdtypes/ANY/DNSKEY.py | 48 | ||||
-rw-r--r-- | dns/rdtypes/ANY/RRSIG.py | 2 | ||||
-rw-r--r-- | dns/rdtypes/IN/APL.py | 4 | ||||
-rw-r--r-- | dns/resolver.py | 7 | ||||
-rw-r--r-- | dns/reversename.py | 9 | ||||
-rw-r--r-- | dns/zone.py | 27 | ||||
-rw-r--r-- | tests/Makefile | 5 | ||||
-rw-r--r-- | tests/example3.good | 117 | ||||
-rw-r--r-- | tests/test_bugs.py (renamed from tests/bugs.py) | 7 | ||||
-rw-r--r-- | tests/test_dnssec.py (renamed from tests/dnssec.py) | 18 | ||||
-rw-r--r-- | tests/test_flags.py (renamed from tests/flags.py) | 0 | ||||
-rw-r--r-- | tests/test_generate.py (renamed from tests/generate.py) | 0 | ||||
-rw-r--r-- | tests/test_grange.py (renamed from tests/grange.py) | 0 | ||||
-rw-r--r-- | tests/test_message.py (renamed from tests/message.py) | 0 | ||||
-rw-r--r-- | tests/test_name.py (renamed from tests/name.py) | 9 | ||||
-rw-r--r-- | tests/test_namedict.py (renamed from tests/namedict.py) | 0 | ||||
-rw-r--r-- | tests/test_ntoaaton.py (renamed from tests/ntoaaton.py) | 8 | ||||
-rw-r--r-- | tests/test_rdtypeandclass.py (renamed from tests/rdtypeandclass.py) | 0 | ||||
-rw-r--r-- | tests/test_rdtypeanydnskey.py | 68 | ||||
-rw-r--r-- | tests/test_rdtypeanyloc.py (renamed from tests/rdtypeanyloc.py) | 0 | ||||
-rw-r--r-- | tests/test_resolver.py (renamed from tests/resolver.py) | 0 | ||||
-rw-r--r-- | tests/test_rrset.py (renamed from tests/rrset.py) | 0 | ||||
-rw-r--r-- | tests/test_set.py (renamed from tests/set.py) | 0 | ||||
-rw-r--r-- | tests/test_tokenizer.py (renamed from tests/tokenizer.py) | 0 | ||||
-rw-r--r-- | tests/test_update.py (renamed from tests/update.py) | 0 | ||||
-rw-r--r-- | tests/test_zone.py (renamed from tests/zone.py) | 25 | ||||
-rw-r--r-- | tests/utest.py | 8 |
32 files changed, 408 insertions, 19 deletions
@@ -1,3 +1,48 @@ +2014-06-21 Bob Halley <halley@dnspython.org> + + * When reading from a masterfile, if the first content line started + with leading whitespace, we raised an ugly exception instead of + doing the right thing, namely using the zone origin as the name. + [#73] Thanks to Tassatux for reporting the issue. + + * Added dns.zone.to_text() convenience method. Thanks to Brandon Whaley + <redkrieg@gmail.com> for the patch. + + * The /etc/resolv.conf setting "options rotate" is now understood by the + resolver. If present, the resolver will shuffle the nameserver list + each time dns.resolver.query() is called. Thanks to underrun for the + patch. Note that you don't want to add "options rotate" to your + /etc/resolv.conf if your system's resolver library does not understand + it. In this case, just set resolver.rotate = True by hand. + +2014-06-19 Bob Halley <halley@dnspython.org> + + * Escaping of Unicode has been corrected. Previously we escaped and + then converted to Unicode, but the right thing to do is convert to + Unicode, then escape. Also, characters > 0x7f should NOT be escaped + in Unicode mode. Thanks to Martin Basti for the patch. + + * dns.rdtypes.ANY.DNSKEY now has helpers functions to convert between + the numeric form of the flags and a set of human-friendly strings. + Thanks to Petr Spacek for the patch. + + * RRSIGs did not respect relativization settings in to_text(). Thanks + to Brian Smith for reporting the bug and submitting a (slightly different) patch. + +2014-06-18 Bob Halley <halley@dnspython.org> + + * dns/rdtypes/IN/APL.py: The APL from_wire() method did not accept an + rdata length of 0 as valid. Thanks to salzmdan for reporting the + problem. + +2014-05-31 Bob Halley <halley@dnspython.org> + + * 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 <halley@dnspython.org> * dns/zone.py: Do not put back an unescaped token. This was @@ -54,3 +54,8 @@ kits: tags: find . -name '*.py' -print | etags - + +check: test + +test: + cd tests; make test diff --git a/dns/dnssec.py b/dns/dnssec.py index edbe479..f1d70ce 100644 --- a/dns/dnssec.py +++ b/dns/dnssec.py @@ -405,9 +405,11 @@ try: import Crypto.Util.number validate = _validate validate_rrsig = _validate_rrsig + _have_pycrypto = True except ImportError: validate = _need_pycrypto validate_rrsig = _need_pycrypto + _have_pycrypto = False try: import ecdsa 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/name.py b/dns/name.py index 0d9b4f2..5eb925d 100644 --- a/dns/name.py +++ b/dns/name.py @@ -88,9 +88,9 @@ _escaped = { '$' : True } -def _escapify(label, whitespaces_only=False): +def _escapify(label, unicode_mode=False): """Escape the characters in label which need it. - @param whitespaces_only: escapify only special and whitespace (ord < 0x20) + @param unicode_mode: escapify only special and whitespace (<= 0x20) characters @returns: the escaped string @rtype: string""" @@ -101,7 +101,7 @@ def _escapify(label, whitespaces_only=False): elif ord(c) > 0x20 and ord(c) < 0x7F: text += c else: - if whitespaces_only and ord(c) >= 0x7F: + if unicode_mode and ord(c) >= 0x7F: text += c else: text += '\\%03d' % ord(c) @@ -357,7 +357,7 @@ class Name(object): l = self.labels[:-1] else: l = self.labels - s = u'.'.join([_escapify(encodings.idna.ToUnicode(x), whitespaces_only=True) for x in l]) + s = u'.'.join([_escapify(encodings.idna.ToUnicode(x), True) for x in l]) return s def to_digestable(self, origin=None): diff --git a/dns/rdtypes/ANY/DNSKEY.py b/dns/rdtypes/ANY/DNSKEY.py index 1d678d2..7bc5850 100644 --- a/dns/rdtypes/ANY/DNSKEY.py +++ b/dns/rdtypes/ANY/DNSKEY.py @@ -20,11 +20,54 @@ import dns.exception import dns.dnssec import dns.rdata + # flag constants SEP = 0x0001 REVOKE = 0x0080 ZONE = 0x0100 +_flag_by_text = { + 'SEP': SEP, + 'REVOKE': REVOKE, + 'ZONE': ZONE + } + +# We construct the inverse mapping programmatically to ensure that we +# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that +# would cause the mapping not to be true inverse. +_flag_by_value = dict([(y, x) for x, y in _flag_by_text.iteritems()]) + + +def flags_to_text_set(flags): + """Convert a DNSKEY flags value to set texts + @rtype: set([string])""" + + flags_set = set() + mask = 0x1 + while mask <= 0x8000: + if flags & mask: + text = _flag_by_value.get(mask) + if not text: + text = hex(mask) + flags_set.add(text) + mask <<= 1 + return flags_set + + +def flags_from_text_set(texts_set): + """Convert set of DNSKEY flag mnemonic texts to DNSKEY flag value + @rtype: int""" + + flags = 0 + for text in texts_set: + try: + flags += _flag_by_text[text] + except KeyError: + raise NotImplementedError( + "DNSKEY flag '%s' is not supported" % text) + return flags + + class DNSKEY(dns.rdata.Rdata): """DNSKEY record @@ -92,3 +135,8 @@ class DNSKEY(dns.rdata.Rdata): if v == 0: v = cmp(self.key, other.key) return v + + def flags_to_text_set(self): + """Convert a DNSKEY flags value to set texts + @rtype: set([string])""" + return flags_to_text_set(self.flags) diff --git a/dns/rdtypes/ANY/RRSIG.py b/dns/rdtypes/ANY/RRSIG.py index 63d389c..98c548d 100644 --- a/dns/rdtypes/ANY/RRSIG.py +++ b/dns/rdtypes/ANY/RRSIG.py @@ -93,7 +93,7 @@ class RRSIG(dns.rdata.Rdata): posixtime_to_sigtime(self.expiration), posixtime_to_sigtime(self.inception), self.key_tag, - self.signer, + self.signer.choose_relativity(origin, relativize), dns.rdata._base64ify(self.signature) ) diff --git a/dns/rdtypes/IN/APL.py b/dns/rdtypes/IN/APL.py index 260fd6f..59da75b 100644 --- a/dns/rdtypes/IN/APL.py +++ b/dns/rdtypes/IN/APL.py @@ -118,6 +118,8 @@ class APL(dns.rdata.Rdata): def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): items = [] while 1: + if rdlen == 0: + break if rdlen < 4: raise dns.exception.FormError header = struct.unpack('!HBB', wire[current : current + 4]) @@ -151,8 +153,6 @@ class APL(dns.rdata.Rdata): rdlen -= afdlen item = APLItem(header[0], negation, address, header[1]) items.append(item) - if rdlen == 0: - break return cls(rdclass, rdtype, items) from_wire = classmethod(from_wire) diff --git a/dns/resolver.py b/dns/resolver.py index 934d39b..f52b555 100644 --- a/dns/resolver.py +++ b/dns/resolver.py @@ -21,6 +21,7 @@ import socket import sys import time +import random try: import threading as _threading @@ -508,6 +509,7 @@ class Resolver(object): self.cache = None self.flags = None self.retry_servfail = False + self.rotate = False def read_resolv_conf(self, f): """Process f as a file in the /etc/resolv.conf format. If f is @@ -538,6 +540,9 @@ class Resolver(object): elif tokens[0] == 'search': for suffix in tokens[1:]: self.search.append(dns.name.from_text(suffix)) + elif tokens[0] == 'options': + if 'rotate' in tokens[1:]: + self.rotate = True finally: if want_close: f.close() @@ -811,6 +816,8 @@ class Resolver(object): # make a copy of the servers list so we can alter it later. # nameservers = self.nameservers[:] + if self.rotate: + random.shuffle(nameservers) backoff = 0.10 while response is None: if len(nameservers) == 0: 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/dns/zone.py b/dns/zone.py index ab57148..6f5adf5 100644 --- a/dns/zone.py +++ b/dns/zone.py @@ -31,6 +31,10 @@ import dns.tokenizer import dns.ttl import dns.grange +try: + from cStringIO import StringIO +except ImportError: + from io import StringIO class BadZone(dns.exception.DNSException): """The zone is malformed.""" @@ -506,6 +510,27 @@ class Zone(object): if want_close: f.close() + def to_text(self, sorted=True, relativize=True, nl=None): + """Return a zone's text as though it were written to a file. + + @param sorted: if True, the file will be written with the + names sorted in DNSSEC order from least to greatest. Otherwise + the names will be written in whatever order they happen to have + in the zone's dictionary. + @param relativize: if True, domain names in the output will be + relativized to the zone's origin (if possible). + @type relativize: bool + @param nl: The end of line string. If not specified, the + output will use the platform's native end-of-line marker (i.e. + LF on POSIX, CRLF on Windows, CR on Macintosh). + @type nl: string or None + """ + temp_buffer = StringIO() + self.to_file(temp_buffer, sorted, relativize, nl) + return_value = temp_buffer.getvalue() + temp_buffer.close() + return return_value + def check_origin(self): """Do some simple checking of the zone's origin. @@ -558,7 +583,7 @@ class _MasterReader(object): self.current_origin = origin self.relativize = relativize self.ttl = 0 - self.last_name = None + self.last_name = self.current_origin self.zone = zone_factory(origin, rdclass, relativize=relativize) self.saved_state = [] self.current_file = None diff --git a/tests/Makefile b/tests/Makefile index 6ab444f..1524552 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -20,7 +20,4 @@ PYTHON=python check: test test: - @for i in *.py; do \ - echo "Running $$i:"; \ - ${PYTHON} $$i || exit 1; \ - done + ${PYTHON} ./utest.py diff --git a/tests/example3.good b/tests/example3.good new file mode 100644 index 0000000..100842e --- /dev/null +++ b/tests/example3.good @@ -0,0 +1,117 @@ +@ 300 IN SOA ns1 hostmaster 1 2000 2000 1814400 3600 +@ 300 IN NS ns1 +@ 300 IN NS ns2 +* 300 IN MX 10 mail +a 300 IN TXT "foo foo foo" +a 300 IN PTR foo.net. +a01 3600 IN A 0.0.0.0 +a02 3600 IN A 255.255.255.255 +aaaa01 3600 IN AAAA ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff +aaaa02 3600 IN AAAA ::1 +afsdb01 3600 IN AFSDB 0 hostname +afsdb02 3600 IN AFSDB 65535 . +apl01 3600 IN APL 1:192.168.32.0/21 !1:192.168.38.0/28 +apl02 3600 IN APL 1:224.0.0.0/4 2:FF00:0:0:0:0:0:0:0/8 +b 300 IN CNAME foo.net. +c 300 IN A 73.80.65.49 +cert01 3600 IN CERT 65534 65535 PRIVATEOID MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= +cname01 3600 IN CNAME cname-target. +cname02 3600 IN CNAME cname-target +cname03 3600 IN CNAME . +d 300 IN A 73.80.65.49 +dhcid01 3600 IN DHCID AAIBY2/AuCccgoJbsaxcQc9TUapptP69 lOjxfNuVAA2kjEA= +dhcid02 3600 IN DHCID AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQd WL3b/NaiUDlW2No= +dhcid03 3600 IN DHCID AAABxLmlskllE0MVjd57zHcWmEH3pCQ6 VytcKD//7es/deY= +dlv01 3600 IN DLV 12345 3 1 123456789abcdef67890123456789abcdef67890 +dname01 3600 IN DNAME dname-target. +dname02 3600 IN DNAME dname-target +dname03 3600 IN DNAME . +dnskey01 3600 IN DNSKEY 512 255 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRu niJDBzC7w0aRyzWZriO6i2odGWWQVucZ qKVsENW91IOW4vqudngPZsY3GvQ/xVA8 /7pyFj6b7Esga60zyGW6LFe9r8n6paHr lG5ojqf0BaqHT+8= +dnskey02 3600 IN DNSKEY 257 3 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRu niJDBzC7w0aRyzWZriO6i2odGWWQVucZ qKVsENW91IOW4vqudngPZsY3GvQ/xVA8 /7pyFj6b7Esga60zyGW6LFe9r8n6paHr lG5ojqf0BaqHT+8= +ds01 3600 IN DS 12345 3 1 123456789abcdef67890123456789abcdef67890 +e 300 IN MX 10 mail +e 300 IN TXT "one" +e 300 IN TXT "three" +e 300 IN TXT "two" +e 300 IN A 73.80.65.49 +e 300 IN A 73.80.65.50 +e 300 IN A 73.80.65.52 +e 300 IN A 73.80.65.51 +f 300 IN A 73.80.65.52 +gpos01 3600 IN GPOS -22.6882 116.8652 250.0 +hinfo01 3600 IN HINFO "Generic PC clone" "NetBSD-1.4" +hinfo02 3600 IN HINFO "PC" "NetBSD" +hip01 3600 IN HIP 2 200100107b1a74df365639cc39f1d578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs1.example.com. rvs2 +ipseckey01 3600 IN IPSECKEY 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +ipseckey02 3600 IN IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +ipseckey03 3600 IN IPSECKEY 10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +ipseckey04 3600 IN IPSECKEY 10 2 2 2001:0DB8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +ipseckey05 3600 IN IPSECKEY 10 3 2 mygateway2 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +isdn01 3600 IN ISDN "isdn-address" +isdn02 3600 IN ISDN "isdn-address" "subaddress" +isdn03 3600 IN ISDN "isdn-address" +isdn04 3600 IN ISDN "isdn-address" "subaddress" +kx01 3600 IN KX 10 kdc +kx02 3600 IN KX 10 . +loc01 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m +loc02 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m +loc03 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m +loc04 3600 IN LOC 60 9 1.500 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m +loc05 3600 IN LOC 60 9 1.510 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m +mx01 3600 IN MX 10 mail +mx02 3600 IN MX 10 . +naptr01 3600 IN NAPTR 0 0 "" "" "" . +naptr02 3600 IN NAPTR 65535 65535 "blurgh" "blorf" "blegh" foo. +ns1 300 IN A 10.53.0.1 +ns2 300 IN A 10.53.0.2 +nsap-ptr01 3600 IN NSAP-PTR foo. +nsap-ptr01 3600 IN NSAP-PTR . +nsap01 3600 IN NSAP 0x47000580005a0000000001e133ffffff00016100 +nsap02 3600 IN NSAP 0x47000580005a0000000001e133ffffff00016100 +nsec01 3600 IN NSEC a.secure. A MX RRSIG NSEC TYPE1234 +nsec02 3600 IN NSEC . NSAP-PTR NSEC +nsec03 3600 IN NSEC . NSEC TYPE65535 +nsec301 3600 IN NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM +nsec302 3600 IN NSEC3 1 1 12 - 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM +nsec3param01 3600 IN NSEC3PARAM 1 1 12 aabbccdd +nsec3param02 3600 IN NSEC3PARAM 1 1 12 - +ptr01 3600 IN PTR @ +px01 3600 IN PX 65535 foo. bar. +px02 3600 IN PX 65535 . . +rp01 3600 IN RP mbox-dname txt-dname +rp02 3600 IN RP . . +rrsig01 3600 IN RRSIG NSEC 1 3 3600 20200101000000 20030101000000 2143 foo MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= +rt01 3600 IN RT 0 intermediate-host +rt02 3600 IN RT 65535 . +s 300 IN NS ns.s +ns.s 300 IN A 73.80.65.49 +spf 3600 IN SPF "v=spf1 mx -all" +srv01 3600 IN SRV 0 0 0 . +srv02 3600 IN SRV 65535 65535 65535 old-slow-box.example.com. +sshfp1 3600 IN SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab +t 301 IN A 73.80.65.49 +tlsa1 3600 IN TLSA 3 1 1 a9cdf989b504fe5dca90c0d2167b6550570734f7c763e09fdf88904e06157065 +tlsa2 3600 IN TLSA 1 0 1 efddf0d915c7bdc5782c0881e1b2a95ad099fbdd06d7b1f77982d9364338d955 +tlsa3 3600 IN TLSA 1 0 2 81ee7f6c0ecc6b09b7785a9418f54432de630dd54dc6ee9e3c49de547708d236d4c413c3e97e44f969e635958aa410495844127c04883503e5b024cf7a8f6a94 +txt01 3600 IN TXT "foo" +txt02 3600 IN TXT "foo" "bar" +txt03 3600 IN TXT "foo" +txt04 3600 IN TXT "foo" "bar" +txt05 3600 IN TXT "foo bar" +txt06 3600 IN TXT "foo bar" +txt07 3600 IN TXT "foo bar" +txt08 3600 IN TXT "foo\010bar" +txt09 3600 IN TXT "foo\010bar" +txt10 3600 IN TXT "foo bar" +txt11 3600 IN TXT "\"foo\"" +txt12 3600 IN TXT "\"foo\"" +txt13 3600 IN TXT "foo" +u 300 IN TXT "txt-not-in-nxt" +a.u 300 IN A 73.80.65.49 +b.u 300 IN A 73.80.65.49 +unknown2 3600 IN TYPE999 \# 8 0a0000010a000001 +unknown3 3600 IN A 127.0.0.2 +wks01 3600 IN WKS 10.0.0.1 6 0 1 2 21 23 +wks02 3600 IN WKS 10.0.0.1 17 0 1 2 53 +wks03 3600 IN WKS 10.0.0.2 6 65535 +x2501 3600 IN X25 "123456789" diff --git a/tests/bugs.py b/tests/test_bugs.py index 312ec3e..cee8757 100644 --- a/tests/bugs.py +++ b/tests/test_bugs.py @@ -45,5 +45,12 @@ class BugsTestCase(unittest.TestCase): "1 0 100 ABCD SCBCQHKU35969L2A68P3AD59LHF30715") self.failUnless(rdata.windows == []) + def test_zero_size_APL(self): + rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.APL, + "") + rdata2 = dns.rdata.from_wire(dns.rdataclass.IN, dns.rdatatype.APL, + "", 0, 0) + self.failUnless(rdata == rdata2) + if __name__ == '__main__': unittest.main() diff --git a/tests/dnssec.py b/tests/test_dnssec.py index edb028b..cbbd3e2 100644 --- a/tests/dnssec.py +++ b/tests/test_dnssec.py @@ -133,22 +133,32 @@ abs_ecdsa384_soa_rrsig = dns.rrset.from_text('example.', 86400, 'IN', 'RRSIG', class DNSSECValidatorTestCase(unittest.TestCase): + @unittest.skipIf(not dns.dnssec._have_pycrypto, + "PyCrypto cannot be imported") def testAbsoluteRSAGood(self): dns.dnssec.validate(abs_soa, abs_soa_rrsig, abs_keys, None, when) + @unittest.skipIf(not dns.dnssec._have_pycrypto, + "PyCrypto cannot be imported") def testDuplicateKeytag(self): dns.dnssec.validate(abs_soa, abs_soa_rrsig, abs_keys_duplicate_keytag, None, when) + @unittest.skipIf(not dns.dnssec._have_pycrypto, + "PyCrypto cannot be imported") def testAbsoluteRSABad(self): def bad(): dns.dnssec.validate(abs_other_soa, abs_soa_rrsig, abs_keys, None, when) self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) + @unittest.skipIf(not dns.dnssec._have_pycrypto, + "PyCrypto cannot be imported") def testRelativeRSAGood(self): dns.dnssec.validate(rel_soa, rel_soa_rrsig, rel_keys, abs_dnspython_org, when) + @unittest.skipIf(not dns.dnssec._have_pycrypto, + "PyCrypto cannot be imported") def testRelativeRSABad(self): def bad(): dns.dnssec.validate(rel_other_soa, rel_soa_rrsig, rel_keys, @@ -159,10 +169,14 @@ class DNSSECValidatorTestCase(unittest.TestCase): ds = dns.dnssec.make_ds(abs_dnspython_org, sep_key, 'SHA256') self.failUnless(ds == good_ds) + @unittest.skipIf(not dns.dnssec._have_pycrypto, + "PyCrypto cannot be imported") def testAbsoluteDSAGood(self): dns.dnssec.validate(abs_dsa_soa, abs_dsa_soa_rrsig, abs_dsa_keys, None, when2) + @unittest.skipIf(not dns.dnssec._have_pycrypto, + "PyCrypto cannot be imported") def testAbsoluteDSABad(self): def bad(): dns.dnssec.validate(abs_other_dsa_soa, abs_dsa_soa_rrsig, @@ -178,13 +192,13 @@ class DNSSECValidatorTestCase(unittest.TestCase): self.failUnless(ds == example_ds_sha256) @unittest.skipIf(not dns.dnssec._have_ecdsa, - "python ECDSA can not be imported") + "python ECDSA cannot be imported") def testAbsoluteECDSA256Good(self): dns.dnssec.validate(abs_ecdsa256_soa, abs_ecdsa256_soa_rrsig, abs_ecdsa256_keys, None, when3) @unittest.skipIf(not dns.dnssec._have_ecdsa, - "python ECDSA can not be imported") + "python ECDSA cannot be imported") def testAbsoluteECDSA256Bad(self): def bad(): dns.dnssec.validate(abs_other_ecdsa256_soa, abs_ecdsa256_soa_rrsig, diff --git a/tests/flags.py b/tests/test_flags.py index b3cf671..b3cf671 100644 --- a/tests/flags.py +++ b/tests/test_flags.py diff --git a/tests/generate.py b/tests/test_generate.py index 43c5f9a..43c5f9a 100644 --- a/tests/generate.py +++ b/tests/test_generate.py diff --git a/tests/grange.py b/tests/test_grange.py index 1bb7dbb..1bb7dbb 100644 --- a/tests/grange.py +++ b/tests/test_grange.py diff --git a/tests/message.py b/tests/test_message.py index 931bb19..931bb19 100644 --- a/tests/message.py +++ b/tests/test_message.py diff --git a/tests/name.py b/tests/test_name.py index e30e43d..894a1a4 100644 --- a/tests/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/namedict.py b/tests/test_namedict.py index e256bfe..e256bfe 100644 --- a/tests/namedict.py +++ b/tests/test_namedict.py diff --git a/tests/ntoaaton.py b/tests/test_ntoaaton.py index e93de2d..5e33a92 100644 --- a/tests/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() diff --git a/tests/rdtypeandclass.py b/tests/test_rdtypeandclass.py index f3c0628..f3c0628 100644 --- a/tests/rdtypeandclass.py +++ b/tests/test_rdtypeandclass.py diff --git a/tests/test_rdtypeanydnskey.py b/tests/test_rdtypeanydnskey.py new file mode 100644 index 0000000..d9e40d7 --- /dev/null +++ b/tests/test_rdtypeanydnskey.py @@ -0,0 +1,68 @@ +# Copyright (C) 2014 Red Hat, Inc. +# Author: Petr Spacek <pspacek@redhat.com> +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import unittest + +import dns.rrset +import dns.rdtypes.ANY.DNSKEY + + +class RdtypeAnyDnskeyTestCase(unittest.TestCase): + + def testFlagsEmpty(self): + '''Test DNSKEY flag to/from text conversion for zero flag/empty set.''' + good_s = set() + good_f = 0 + from_flags = dns.rdtypes.ANY.DNSKEY.flags_to_text_set(good_f) + self.failUnless(from_flags == good_s, + '"%s" != "%s"' % (from_flags, good_s)) + from_set = dns.rdtypes.ANY.DNSKEY.flags_from_text_set(good_s) + self.failUnless(from_set == good_f, + '"0x%x" != "0x%x"' % (from_set, good_f)) + + def testFlagsAll(self): + '''Test that all defined flags are recognized.''' + good_s = set(['SEP', 'REVOKE', 'ZONE']) + good_f = 0x181 + from_flags = dns.rdtypes.ANY.DNSKEY.flags_to_text_set(good_f) + self.failUnless(from_flags == good_s, + '"%s" != "%s"' % (from_flags, good_s)) + from_text = dns.rdtypes.ANY.DNSKEY.flags_from_text_set(good_s) + self.failUnless(from_text == good_f, + '"0x%x" != "0x%x"' % (from_text, good_f)) + + def testFlagsUnknownToText(self): + '''Test that undefined flags are returned in hexadecimal notation.''' + unk_s = set(['0x8000']) + flags_s = dns.rdtypes.ANY.DNSKEY.flags_to_text_set(0x8000) + self.failUnless(flags_s == unk_s, '"%s" != "%s"' % (flags_s, unk_s)) + + def testFlagsUnknownToFlags(self): + '''Test that conversion from undefined mnemonic raises error.''' + self.failUnlessRaises(NotImplementedError, + dns.rdtypes.ANY.DNSKEY.flags_from_text_set, + (['0x8000'])) + + def testFlagsRRToText(self): + '''Test that RR method returns correct flags.''' + rr = dns.rrset.from_text('foo', 300, 'IN', 'DNSKEY', '257 3 8 KEY=')[0] + rr_s = set(['ZONE', 'SEP']) + flags_s = rr.flags_to_text_set() + self.failUnless(flags_s == rr_s, '"%s" != "%s"' % (flags_s, rr_s)) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/rdtypeanyloc.py b/tests/test_rdtypeanyloc.py index 8d9838c..8d9838c 100644 --- a/tests/rdtypeanyloc.py +++ b/tests/test_rdtypeanyloc.py diff --git a/tests/resolver.py b/tests/test_resolver.py index 6be955b..6be955b 100644 --- a/tests/resolver.py +++ b/tests/test_resolver.py diff --git a/tests/rrset.py b/tests/test_rrset.py index be1324b..be1324b 100644 --- a/tests/rrset.py +++ b/tests/test_rrset.py diff --git a/tests/set.py b/tests/test_set.py index 583d20c..583d20c 100644 --- a/tests/set.py +++ b/tests/test_set.py diff --git a/tests/tokenizer.py b/tests/test_tokenizer.py index 1d561ae..1d561ae 100644 --- a/tests/tokenizer.py +++ b/tests/test_tokenizer.py diff --git a/tests/update.py b/tests/test_update.py index 92ddb56..92ddb56 100644 --- a/tests/update.py +++ b/tests/test_update.py diff --git a/tests/zone.py b/tests/test_zone.py index 31e7405..3b7c855 100644 --- a/tests/zone.py +++ b/tests/test_zone.py @@ -121,6 +121,20 @@ class ZoneTestCase(unittest.TestCase): os.unlink('example2.out') self.failUnless(ok) + def testToText(self): + z = dns.zone.from_file('example', 'example') + ok = False + try: + text_zone = z.to_text(nl='\x0a') + f = open('example3.out', 'wb') + f.write(text_zone) + f.close() + ok = filecmp.cmp('example3.out', 'example3.good') + finally: + if not _keep_output: + os.unlink('example3.out') + self.failUnless(ok) + def testFromText(self): z = dns.zone.from_text(example_text, 'example.', relativize=True) f = cStringIO.StringIO() @@ -129,7 +143,7 @@ class ZoneTestCase(unittest.TestCase): for n in names: print >> f, z[n].to_text(n) self.failUnless(f.getvalue() == example_text_output) - + def testTorture1(self): # # Read a zone containing all our supported RR types, and @@ -385,5 +399,14 @@ class ZoneTestCase(unittest.TestCase): relativize=True) self.failUnlessRaises(dns.exception.SyntaxError, bad) + def testFirstRRStartsWithWhitespace(self): + # no name is specified, so default to the intial origin + # no ttl is specified, so default to the initial TTL of 0 + z = dns.zone.from_text(' IN A 10.0.0.1', origin='example.', + check_origin=False) + n = z['@'] + rds = n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A) + self.failUnless(rds.ttl == 0) + if __name__ == '__main__': unittest.main() diff --git a/tests/utest.py b/tests/utest.py new file mode 100644 index 0000000..32c1d75 --- /dev/null +++ b/tests/utest.py @@ -0,0 +1,8 @@ +import os.path +import sys +import unittest + +if __name__ == '__main__': + sys.path.insert(0, os.path.realpath('..')) + suites = unittest.defaultTestLoader.discover('.') + unittest.TextTestRunner(verbosity=2).run(suites) |