summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog45
-rw-r--r--Makefile5
-rw-r--r--dns/dnssec.py2
-rw-r--r--dns/ipv6.py5
-rw-r--r--dns/name.py8
-rw-r--r--dns/rdtypes/ANY/DNSKEY.py48
-rw-r--r--dns/rdtypes/ANY/RRSIG.py2
-rw-r--r--dns/rdtypes/IN/APL.py4
-rw-r--r--dns/resolver.py7
-rw-r--r--dns/reversename.py9
-rw-r--r--dns/zone.py27
-rw-r--r--tests/Makefile5
-rw-r--r--tests/example3.good117
-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.py68
-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.py8
32 files changed, 408 insertions, 19 deletions
diff --git a/ChangeLog b/ChangeLog
index b413572..f210688 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/Makefile b/Makefile
index b46afb9..78360f1 100644
--- a/Makefile
+++ b/Makefile
@@ -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)