diff --git a/210.patch b/210.patch deleted file mode 100644 index 7f56cf8..0000000 --- a/210.patch +++ /dev/null @@ -1,23 +0,0 @@ -From 0add1649c91b4f56b32e73c3a6327519c6eb532e Mon Sep 17 00:00:00 2001 -From: Avram Lubkin -Date: Tue, 4 Oct 2016 07:32:20 -0400 -Subject: [PATCH] Test creates file in wrong location - -test_zone.ZoneTestCase.testToFileFilename fails with OSError: [Errno 2] No such file or directory ---- - tests/test_zone.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tests/test_zone.py b/tests/test_zone.py -index dbc67c7..3c497a6 100644 ---- tests/test_zone.py -+++ tests/test_zone.py -@@ -177,7 +177,7 @@ def testToFileBinary(self): - def testToFileFilename(self): - z = dns.zone.from_file(here('example'), 'example') - try: -- z.to_file('example3-filename.out') -+ z.to_file(here('example3-filename.out')) - ok = filecmp.cmp(here('example3-filename.out'), - here('example3.good')) - finally: diff --git a/dnspython-1.15.0.tar.gz b/dnspython-1.15.0.tar.gz deleted file mode 100644 index 82bc390..0000000 --- a/dnspython-1.15.0.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:11598ae5735746e63921f8eebdfdee4a2e7d0ba842ebd57ba02682d4aed8244b -size 144073 diff --git a/dnspython-1.15.0.tar.gz.asc b/dnspython-1.15.0.tar.gz.asc deleted file mode 100644 index 88929fb..0000000 --- a/dnspython-1.15.0.tar.gz.asc +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN PGP SIGNATURE----- -Version: GnuPG v2 - -iQIVAwUAV+6CivJLOvyMovXHAQih4RAAkKMyzk+JuVo+g7y2r3rmC+pek7RUxXvQ -Z7FkZyu/d6btCUm2UUbGe4yLdXhjhb9ABpdzq2W9Z+UKZVifvDkvzAUziVrA3oIl -xRwRfvzaCsB13n0uOnbFGeM2ocK2tEor4JyNY1ZMR6uEMuWlfi7eXHvuwnt7WNjv -pXBboE2aQN+jR/GM+l2wwnDJCFy65QTFZCiwyR/9Kcmrf+NdYJrkAfS0MjOc8Zyi -fV5OcrI2FR6CztCx5CL7CoiiHvxh0UDVzN9Er4dt6QMnUe8f+ygc3TJofXfZ4M+2 -fk/Lk6dOisU+gpOqcQuKmdk7ssO47q+Z29U/dDS7fLMf0xL7SLNTpmo9H5bEKsFb -S3Zmv54om2W0ltR5bUWLyZi6w8tATilHaIOKwBGAtPFiLNIgEApgP4GiTbysATEY -4IquLEg/Yqal3Xw2MNcCVq9WvKzAajO8z+5AHNzq0YY0hMfRjZVzb2kuaj3Vcukq -oEQGiDC5eHY3dLwXhj4QIC5fcgJGLapHy0YtUc3chF1qZsKytzKeB7wa9yFszcIb -wgOXWSmrjrlFB8RgmvWxQkTZIYHu+sYKGki0ekF76llxGLIisG/rFvwmFsbTnu/H -9L6TxKEIiwTMQoaXXteP9eJ6MEWkG04O2Hy94M7YPiBnWTlaJhe7HOuf/APYTI0u -OSJdhrag/p4= -=Ay5b ------END PGP SIGNATURE----- diff --git a/dnspython-1.16.0.tar.gz b/dnspython-1.16.0.tar.gz new file mode 100644 index 0000000..c71ff88 --- /dev/null +++ b/dnspython-1.16.0.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4bf5c5c12a4478ee7860ab176659cf64c4899ee76752d826b082f8af723c5cf9 +size 150524 diff --git a/dnspython-1.16.0.tar.gz.asc b/dnspython-1.16.0.tar.gz.asc new file mode 100644 index 0000000..d76e9cd --- /dev/null +++ b/dnspython-1.16.0.tar.gz.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJJBAABCAAzFiEEpYDe4FL+x42Kzzg98ks6/Iyi9ccFAlwMFb0VHGhhbGxleUBk +bnNweXRob24ub3JnAAoJEPJLOvyMovXHc8AP/itx1+byyZegYUqncJ2RITCr+Yjo +HYPTKBV5jb6Sbr6EGSLJxZtZxqoMiKEkhMXlKbQVM/PMgCdn6/C/HXvYB6yoy8R1 +M/7HFAp+7dPWk4i1l+xlwP/fr0T0iJ28xfR5kScRxMcSrc525X/vrcLOq3Omhi7n +nWTTYoaKjbNjOAuJ+Wo50H8ei3R7MmxZDvMpm759+GKjH3GeB/kjWIp4qIXiIJr+ +HHhHrecIYuiUNgQUjO9akbTjV3leZ2mAkKxY4ruP53yWwwW/PRX4vvtmFyjesH7d +j4WRDjY05lqzyuL5WhjSvtMHJtdFr/Hz7zy38AAX6TBklKYcKkESazYwKMUTPVsm +cw/JeSScprXX6mMrm7aX7EbGngDW5wqVUCgP6N01FqDrIY947cg4ua3NZvmtUoP6 +qRAYN28Q8LAd6EYZs2K0GrinCF1i3+2faTV83dgKpCJfTlMebSI/+ul5GndEERVE +gr2BvMc2fjyVNLX40YKWuza5lB7y7f/xHHw1lzFTsSzucHF83NPJ/p6Cr7PhVOVu +GN/NMxGYtttry/xRtwNowrynQ8PaLmMIXE8ZneQNQxvgF0SaQWsPYTqIlL6LOiuw +5bPThPeRUBA29WOHCdJx6nluLB5Pftste8rCZ7+v5l3tjHKdpwi2dp1UlfhIdgIU +n90/zIC/vck/Q/44 +=bDhR +-----END PGP SIGNATURE----- diff --git a/pycryptodome.patch b/pycryptodome.patch deleted file mode 100644 index 1f55d08..0000000 --- a/pycryptodome.patch +++ /dev/null @@ -1,448 +0,0 @@ -From 10b8a42c90d037880fbfc81fb71adb27251252e3 Mon Sep 17 00:00:00 2001 -From: Daniel Robbins -Date: Thu, 21 Dec 2017 09:24:40 -0700 -Subject: [PATCH] Update DNSSEC code to use pycryptodome instead of pycrypto. - These changes make dnspython *incompatible* with pycrypto -- pycryptodome - must be used. The ecdsa module continues to be used for ECDSA support. - ---- - ChangeLog | 5 ++ - dns/__init__.py | 1 - - dns/dnssec.py | 161 +++++++++++++++++++++++++++------------------------ - dns/hash.py | 31 ---------- - dns/tsig.py | 4 +- - doc/dnssec.rst | 9 ++- - doc/installation.rst | 4 +- - tests/test_dnssec.py | 16 ++--- - 8 files changed, 105 insertions(+), 126 deletions(-) - delete mode 100644 dns/hash.py - -Index: dnspython-1.15.0/dns/__init__.py -=================================================================== ---- dnspython-1.15.0.orig/dns/__init__.py -+++ dnspython-1.15.0/dns/__init__.py -@@ -22,7 +22,6 @@ __all__ = [ - 'entropy', - 'exception', - 'flags', -- 'hash', - 'inet', - 'ipv4', - 'ipv6', -Index: dnspython-1.15.0/dns/dnssec.py -=================================================================== ---- dnspython-1.15.0.orig/dns/dnssec.py -+++ dnspython-1.15.0/dns/dnssec.py -@@ -20,7 +20,6 @@ import struct - import time - - import dns.exception --import dns.hash - import dns.name - import dns.node - import dns.rdataset -@@ -28,7 +27,8 @@ import dns.rdata - import dns.rdatatype - import dns.rdataclass - from ._compat import string_types -- -+from Crypto.Hash import MD5, SHA1, SHA256, SHA384, SHA512 -+from Crypto.Signature import pkcs1_15, DSS - - class UnsupportedAlgorithm(dns.exception.DNSException): - -@@ -39,34 +39,34 @@ class ValidationFailure(dns.exception.DN - - """The DNSSEC signature is invalid.""" - --RSAMD5 = 1 --DH = 2 --DSA = 3 --ECC = 4 --RSASHA1 = 5 --DSANSEC3SHA1 = 6 --RSASHA1NSEC3SHA1 = 7 --RSASHA256 = 8 --RSASHA512 = 10 --ECDSAP256SHA256 = 13 --ECDSAP384SHA384 = 14 -+ALGO_RSAMD5 = 1 -+ALGO_DH = 2 -+ALGO_DSA = 3 -+ALGO_ECC = 4 -+ALGO_RSASHA1 = 5 -+ALGO_DSANSEC3SHA1 = 6 -+ALGO_RSASHA1NSEC3SHA1 = 7 -+ALGO_RSASHA256 = 8 -+ALGO_RSASHA512 = 10 -+ALGO_ECDSAP256SHA256 = 13 -+ALGO_ECDSAP384SHA384 = 14 - INDIRECT = 252 - PRIVATEDNS = 253 - PRIVATEOID = 254 - - _algorithm_by_text = { -- 'RSAMD5': RSAMD5, -- 'DH': DH, -- 'DSA': DSA, -- 'ECC': ECC, -- 'RSASHA1': RSASHA1, -- 'DSANSEC3SHA1': DSANSEC3SHA1, -- 'RSASHA1NSEC3SHA1': RSASHA1NSEC3SHA1, -- 'RSASHA256': RSASHA256, -- 'RSASHA512': RSASHA512, -+ 'RSAMD5': ALGO_RSAMD5, -+ 'DH': ALGO_DH, -+ 'DSA': ALGO_DSA, -+ 'ECC': ALGO_ECC, -+ 'RSASHA1': ALGO_RSASHA1, -+ 'DSANSEC3SHA1': ALGO_DSANSEC3SHA1, -+ 'RSASHA1NSEC3SHA1': ALGO_RSASHA1NSEC3SHA1, -+ 'RSASHA256': ALGO_RSASHA256, -+ 'RSASHA512': ALGO_RSASHA512, - 'INDIRECT': INDIRECT, -- 'ECDSAP256SHA256': ECDSAP256SHA256, -- 'ECDSAP384SHA384': ECDSAP384SHA384, -+ 'ECDSAP256SHA256': ALGO_ECDSAP256SHA256, -+ 'ECDSAP384SHA384': ALGO_ECDSAP384SHA384, - 'PRIVATEDNS': PRIVATEDNS, - 'PRIVATEOID': PRIVATEOID, - } -@@ -107,7 +107,7 @@ def _to_rdata(record, origin): - def key_id(key, origin=None): - rdata = _to_rdata(key, origin) - rdata = bytearray(rdata) -- if key.algorithm == RSAMD5: -+ if key.algorithm == ALGO_RSAMD5: - return (rdata[-3] << 8) + rdata[-2] - else: - total = 0 -@@ -123,10 +123,10 @@ def key_id(key, origin=None): - def make_ds(name, key, algorithm, origin=None): - if algorithm.upper() == 'SHA1': - dsalg = 1 -- hash = dns.hash.hashes['SHA1']() -+ hash = SHA1.new() - elif algorithm.upper() == 'SHA256': - dsalg = 2 -- hash = dns.hash.hashes['SHA256']() -+ hash = SHA256.new() - else: - raise UnsupportedAlgorithm('unsupported algorithm "%s"' % algorithm) - -@@ -162,51 +162,51 @@ def _find_candidate_keys(keys, rrsig): - - - def _is_rsa(algorithm): -- return algorithm in (RSAMD5, RSASHA1, -- RSASHA1NSEC3SHA1, RSASHA256, -- RSASHA512) -+ return algorithm in (ALGO_RSAMD5, ALGO_RSASHA1, -+ ALGO_RSASHA1NSEC3SHA1, ALGO_RSASHA256, -+ ALGO_RSASHA512) - - - def _is_dsa(algorithm): -- return algorithm in (DSA, DSANSEC3SHA1) -+ return algorithm in (ALGO_DSA, ALGO_DSANSEC3SHA1) - - - def _is_ecdsa(algorithm): -- return _have_ecdsa and (algorithm in (ECDSAP256SHA256, ECDSAP384SHA384)) -+ return _have_ecdsa and (algorithm in (ALGO_ECDSAP256SHA256, ALGO_ECDSAP384SHA384)) - - - def _is_md5(algorithm): -- return algorithm == RSAMD5 -+ return algorithm == ALGO_RSAMD5 - - - def _is_sha1(algorithm): -- return algorithm in (DSA, RSASHA1, -- DSANSEC3SHA1, RSASHA1NSEC3SHA1) -+ return algorithm in (ALGO_DSA, ALGO_RSASHA1, -+ ALGO_DSANSEC3SHA1, ALGO_RSASHA1NSEC3SHA1) - - - def _is_sha256(algorithm): -- return algorithm in (RSASHA256, ECDSAP256SHA256) -+ return algorithm in (ALGO_RSASHA256, ALGO_ECDSAP256SHA256) - - - def _is_sha384(algorithm): -- return algorithm == ECDSAP384SHA384 -+ return algorithm == ALGO_ECDSAP384SHA384 - - - def _is_sha512(algorithm): -- return algorithm == RSASHA512 -+ return algorithm == ALGO_RSASHA512 - - - def _make_hash(algorithm): - if _is_md5(algorithm): -- return dns.hash.hashes['MD5']() -+ return MD5.new() - if _is_sha1(algorithm): -- return dns.hash.hashes['SHA1']() -+ return SHA1.new() - if _is_sha256(algorithm): -- return dns.hash.hashes['SHA256']() -+ return SHA256.new() - if _is_sha384(algorithm): -- return dns.hash.hashes['SHA384']() -+ return SHA384.new() - if _is_sha512(algorithm): -- return dns.hash.hashes['SHA512']() -+ return SHA512.new() - raise ValidationFailure('unknown hash for algorithm %u' % algorithm) - - -@@ -284,11 +284,13 @@ def _validate_rrsig(rrset, rrsig, keys, - keyptr = keyptr[2:] - rsa_e = keyptr[0:bytes_] - rsa_n = keyptr[bytes_:] -- keylen = len(rsa_n) * 8 -- pubkey = Crypto.PublicKey.RSA.construct( -- (Crypto.Util.number.bytes_to_long(rsa_n), -- Crypto.Util.number.bytes_to_long(rsa_e))) -- sig = (Crypto.Util.number.bytes_to_long(rrsig.signature),) -+ try: -+ pubkey = Crypto.PublicKey.RSA.construct( -+ (Crypto.Util.number.bytes_to_long(rsa_n), -+ Crypto.Util.number.bytes_to_long(rsa_e))) -+ except ValueError: -+ raise ValidationFailure('invalid public key') -+ sig = rrsig.signature - elif _is_dsa(rrsig.algorithm): - keyptr = candidate_key.key - (t,) = struct.unpack('!B', keyptr[0:1]) -@@ -306,20 +308,19 @@ def _validate_rrsig(rrset, rrsig, keys, - Crypto.Util.number.bytes_to_long(dsa_g), - Crypto.Util.number.bytes_to_long(dsa_p), - Crypto.Util.number.bytes_to_long(dsa_q))) -- (dsa_r, dsa_s) = struct.unpack('!20s20s', rrsig.signature[1:]) -- sig = (Crypto.Util.number.bytes_to_long(dsa_r), -- Crypto.Util.number.bytes_to_long(dsa_s)) -+ sig = rrsig.signature[1:] - elif _is_ecdsa(rrsig.algorithm): -- if rrsig.algorithm == ECDSAP256SHA256: -+ # use ecdsa for NIST-384p -- not currently supported by pycryptodome -+ -+ keyptr = candidate_key.key -+ -+ if rrsig.algorithm == ALGO_ECDSAP256SHA256: - curve = ecdsa.curves.NIST256p - key_len = 32 -- elif rrsig.algorithm == ECDSAP384SHA384: -+ elif rrsig.algorithm == ALGO_ECDSAP384SHA384: - curve = ecdsa.curves.NIST384p - key_len = 48 -- else: -- # shouldn't happen -- raise ValidationFailure('unknown ECDSA curve') -- keyptr = candidate_key.key -+ - x = Crypto.Util.number.bytes_to_long(keyptr[0:key_len]) - y = Crypto.Util.number.bytes_to_long(keyptr[key_len:key_len * 2]) - assert ecdsa.ecdsa.point_is_valid(curve.generator, x, y) -@@ -331,6 +332,7 @@ def _validate_rrsig(rrset, rrsig, keys, - s = rrsig.signature[key_len:] - sig = ecdsa.ecdsa.Signature(Crypto.Util.number.bytes_to_long(r), - Crypto.Util.number.bytes_to_long(s)) -+ - else: - raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm) - -@@ -352,24 +354,31 @@ def _validate_rrsig(rrset, rrsig, keys, - hash.update(rrlen) - hash.update(rrdata) - -- digest = hash.digest() -- -- if _is_rsa(rrsig.algorithm): -- # PKCS1 algorithm identifier goop -- digest = _make_algorithm_id(rrsig.algorithm) + digest -- padlen = keylen // 8 - len(digest) - 3 -- digest = struct.pack('!%dB' % (2 + padlen + 1), -- *([0, 1] + [0xFF] * padlen + [0])) + digest -- elif _is_dsa(rrsig.algorithm) or _is_ecdsa(rrsig.algorithm): -- pass -- else: -- # Raise here for code clarity; this won't actually ever happen -- # since if the algorithm is really unknown we'd already have -- # raised an exception above -- raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm) -- -- if pubkey.verify(digest, sig): -+ try: -+ if _is_rsa(rrsig.algorithm): -+ verifier = pkcs1_15.new(pubkey) -+ # will raise ValueError if verify fails: -+ verifier.verify(hash, sig) -+ elif _is_dsa(rrsig.algorithm): -+ verifier = DSS.new(pubkey, 'fips-186-3') -+ verifier.verify(hash, sig) -+ elif _is_ecdsa(rrsig.algorithm): -+ digest = hash.digest() -+ if pubkey.verify(digest, sig): -+ return -+ else: -+ raise ValueError -+ else: -+ # Raise here for code clarity; this won't actually ever happen -+ # since if the algorithm is really unknown we'd already have -+ # raised an exception above -+ raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm) -+ # If we got here, we successfully verified so we can return without error - return -+ except ValueError: -+ # this happens on an individual validation failure -+ continue -+ # nothing verified -- raise failure: - raise ValidationFailure('verify failure') - - -@@ -401,10 +410,8 @@ def _validate(rrset, rrsigset, keys, ori - rrname = rrset.name - - if isinstance(rrsigset, tuple): -- rrsigname = rrsigset[0] - rrsigrdataset = rrsigset[1] - else: -- rrsigname = rrsigset.name - rrsigrdataset = rrsigset - - rrname = rrname.choose_relativity(origin) -@@ -422,7 +429,7 @@ def _validate(rrset, rrsigset, keys, ori - - - def _need_pycrypto(*args, **kwargs): -- raise NotImplementedError("DNSSEC validation requires pycrypto") -+ raise NotImplementedError("DNSSEC validation requires pycryptodome") - - try: - import Crypto.PublicKey.RSA -Index: dnspython-1.15.0/dns/hash.py -=================================================================== ---- dnspython-1.15.0.orig/dns/hash.py -+++ /dev/null -@@ -1,31 +0,0 @@ --# Copyright (C) 2011 Nominum, Inc. --# --# 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 NOMINUM 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. -- --"""Hashing backwards compatibility wrapper""" -- --import hashlib -- -- --hashes = {} --hashes['MD5'] = hashlib.md5 --hashes['SHA1'] = hashlib.sha1 --hashes['SHA224'] = hashlib.sha224 --hashes['SHA256'] = hashlib.sha256 --hashes['SHA384'] = hashlib.sha384 --hashes['SHA512'] = hashlib.sha512 -- -- --def get(algorithm): -- return hashes[algorithm.upper()] -Index: dnspython-1.15.0/dns/tsig.py -=================================================================== ---- dnspython-1.15.0.orig/dns/tsig.py -+++ dnspython-1.15.0/dns/tsig.py -@@ -19,9 +19,9 @@ import hmac - import struct - - import dns.exception --import dns.hash - import dns.rdataclass - import dns.name -+import dns.dnssec - from ._compat import long, string_types, text_type - - class BadTime(dns.exception.DNSException): -@@ -211,7 +211,7 @@ def get_algorithm(algorithm): - algorithm = dns.name.from_text(algorithm) - - try: -- return (algorithm.to_digestable(), dns.hash.hashes[_hashes[algorithm]]) -+ return (algorithm.to_digestable(), dns.dnssec._make_hash(algorithm)) - except KeyError: - raise NotImplementedError("TSIG algorithm " + str(algorithm) + - " is not supported") -Index: dnspython-1.15.0/tests/test_dnssec.py -=================================================================== ---- dnspython-1.15.0.orig/tests/test_dnssec.py -+++ dnspython-1.15.0/tests/test_dnssec.py -@@ -156,22 +156,22 @@ abs_other_ecdsa384_soa = dns.rrset.from_ - abs_ecdsa384_soa_rrsig = dns.rrset.from_text('example.', 86400, 'IN', 'RRSIG', - "SOA 14 1 86400 20130929021229 20130921230729 63571 example. CrnCu34EeeRz0fEhL9PLlwjpBKGYW8QjBjFQTwd+ViVLRAS8tNkcDwQE NhSV89NEjj7ze1a/JcCfcJ+/mZgnvH4NHLNg3Tf6KuLZsgs2I4kKQXEk 37oIHravPEOlGYNI") - --@unittest.skipUnless(import_ok, "skipping DNSSEC tests because pycrypto is not" -+@unittest.skipUnless(import_ok, "skipping DNSSEC tests because pycryptodome is not" - " installed") - class DNSSECValidatorTestCase(unittest.TestCase): - - @unittest.skipUnless(dns.dnssec._have_pycrypto, -- "PyCrypto cannot be imported") -+ "Pycryptodome cannot be imported") - def testAbsoluteRSAGood(self): - dns.dnssec.validate(abs_soa, abs_soa_rrsig, abs_keys, None, when) - - @unittest.skipUnless(dns.dnssec._have_pycrypto, -- "PyCrypto cannot be imported") -+ "Pycryptodome cannot be imported") - def testDuplicateKeytag(self): - dns.dnssec.validate(abs_soa, abs_soa_rrsig, abs_keys_duplicate_keytag, None, when) - - @unittest.skipUnless(dns.dnssec._have_pycrypto, -- "PyCrypto cannot be imported") -+ "Pycryptodome cannot be imported") - def testAbsoluteRSABad(self): - def bad(): - dns.dnssec.validate(abs_other_soa, abs_soa_rrsig, abs_keys, None, -@@ -179,13 +179,13 @@ class DNSSECValidatorTestCase(unittest.T - self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) - - @unittest.skipUnless(dns.dnssec._have_pycrypto, -- "PyCrypto cannot be imported") -+ "Pycryptodome cannot be imported") - def testRelativeRSAGood(self): - dns.dnssec.validate(rel_soa, rel_soa_rrsig, rel_keys, - abs_dnspython_org, when) - - @unittest.skipUnless(dns.dnssec._have_pycrypto, -- "PyCrypto cannot be imported") -+ "Pycryptodome cannot be imported") - def testRelativeRSABad(self): - def bad(): - dns.dnssec.validate(rel_other_soa, rel_soa_rrsig, rel_keys, -@@ -197,13 +197,13 @@ class DNSSECValidatorTestCase(unittest.T - self.failUnless(ds == good_ds) - - @unittest.skipUnless(dns.dnssec._have_pycrypto, -- "PyCrypto cannot be imported") -+ "Pycryptodome cannot be imported") - def testAbsoluteDSAGood(self): - dns.dnssec.validate(abs_dsa_soa, abs_dsa_soa_rrsig, abs_dsa_keys, None, - when2) - - @unittest.skipUnless(dns.dnssec._have_pycrypto, -- "PyCrypto cannot be imported") -+ "Pycryptodome cannot be imported") - def testAbsoluteDSABad(self): - def bad(): - dns.dnssec.validate(abs_other_dsa_soa, abs_dsa_soa_rrsig, diff --git a/python-dnspython.changes b/python-dnspython.changes index b05f1b2..32a0cb1 100644 --- a/python-dnspython.changes +++ b/python-dnspython.changes @@ -1,3 +1,13 @@ +------------------------------------------------------------------- +Fri Mar 8 13:55:40 UTC 2019 - Tomáš Chvátal + +- Update to 1.16.0: + * various fixes for wheel/cryptodome/etc +- Remove all patches, merged upstream: + * 210.patch + * pycryptodome.patch + * readme.patch + ------------------------------------------------------------------- Tue Dec 4 12:47:22 UTC 2018 - Matej Cepl diff --git a/python-dnspython.spec b/python-dnspython.spec index 63adf95..f87132d 100644 --- a/python-dnspython.spec +++ b/python-dnspython.spec @@ -1,7 +1,7 @@ # # spec file for package python-dnspython # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,32 +18,27 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-dnspython -Version: 1.15.0 +Version: 1.16.0 Release: 0 Summary: A DNS toolkit for Python License: ISC Group: Development/Languages/Python -Url: http://dnspython.org/ +URL: https://github.com/rthalley/dnspython Source: http://dnspython.org/kits/%{version}/dnspython-%{version}.tar.gz Source2: http://dnspython.org/kits/%{version}/dnspython-%{version}.tar.gz.asc Source3: python-dnspython.keyring -# PATCH-FIX-UPSTREAM 210.patch git_hub@avram.us -- https://github.com/rthalley/dnspython/pull/210.patch -Patch0: 210.patch -# PATCH-FEATURE-OPENSUSE readme.patch -- Add the readme as patch as not included in the tarball. -Patch1: readme.patch -# PATCH-FIX-UPSTREAM pycryptodome.patch tchvatal@suse.com -- use pycryptodome https://github.com/rthalley/dnspython/pull/290 -Patch2: pycryptodome.patch BuildRequires: %{python_module ecdsa} BuildRequires: %{python_module idna} BuildRequires: %{python_module pycryptodome} BuildRequires: %{python_module setuptools} +BuildRequires: %{python_module typing} BuildRequires: fdupes BuildRequires: netcfg BuildRequires: python-rpm-macros -Recommends: python-ecdsa -Recommends: python-idna +Requires: python-ecdsa Requires: python-pycryptodome BuildArch: noarch +Recommends: python-idna %description dnspython is a DNS toolkit for Python. It supports almost all @@ -64,9 +59,6 @@ allowed it to be opened under a BSD-style licence. %prep %setup -q -n dnspython-%{version} chmod -x examples/* -%patch0 -%patch1 -%patch2 -p1 %build %python_build @@ -77,12 +69,12 @@ chmod -x examples/* %check # Skip the resolver test suite as it requires Internet connection. -test -f tests/test_resolver.py && rm tests/test_resolver.py +#test -f tests/test_resolver.py && rm tests/test_resolver.py %python_exec setup.py test %files %{python_files} %license LICENSE -%doc ChangeLog README.md examples/ +%doc README.md examples/ %{python_sitelib}/dns/ %{python_sitelib}/dnspython-%{version}-py%{python_version}.egg-info diff --git a/readme.patch b/readme.patch deleted file mode 100644 index faafea8..0000000 --- a/readme.patch +++ /dev/null @@ -1,605 +0,0 @@ ---- /dev/null 1970-01-01 01:00:09.936046830 +0100 -+++ README.md 2016-10-06 15:42:00.112010665 +0200 -@@ -0,0 +1,602 @@ -+dnspython [![Build Status](https://travis-ci.org/rthalley/dnspython.svg?branch=master)](https://travis-ci.org/rthalley/dnspython) -+ -+## INTRODUCTION -+ -+dnspython is a DNS toolkit for Python. It supports almost all record types. It -+can be used for queries, zone transfers, and dynamic updates. It supports TSIG -+authenticated messages and EDNS0. -+ -+dnspython provides both high and low level access to DNS. The high level classes -+perform queries for data of a given name, type, and class, and return an answer -+set. The low level classes allow direct manipulation of DNS zones, messages, -+names, and records. -+ -+To see a few of the ways dnspython can be used, look in the examples/ directory. -+ -+dnspython is a utility to work with DNS, `/etc/hosts` is thus not used. For -+simple forward DNS lookups, better use `socket.gethostbyname()`. -+ -+dnspython originated at Nominum where it was developed to facilitate the testing -+of DNS software. Nominum has generously allowed it to be open sourced under a -+BSD-style license, and helps support its future development by continuing to -+employ the author :). -+ -+## INSTALLATION -+- Many distributions have dnspython packaged for you, so you should -+ check there first. -+- If you have pip installed, you can do `pip install dnspython` -+- If not just download the source file and unzip it, then run -+ `sudo python setup.py install` -+ -+## ABOUT THIS RELEASE -+ -+This is dnspython 1.15.0 -+ -+### New since 1.14.0: -+ -+* IDNA 2008 support is now available if the "idna" module has been -+ installed and IDNA 2008 is requested. The default IDNA behavior is -+ still IDNA 2003. The new IDNA codec mechanism is currently only -+ useful for direct calls to dns.name.from_text() or -+ dns.name.from_unicode(), but in future releases it will be deployed -+ throughout dnspython, e.g. so that you can read a masterfile with an -+ IDNA 2008 codec in force. -+ -+* By default, dns.name.to_unicode() is not strict about which -+ version of IDNA the input complies with. Strictness can be -+ requested by using one of the strict IDNA codecs. -+ -+* The AVC RR is now supported. -+ -+### Bugs fixed since 1.14.0: -+ -+* Some problems with newlines in various output modes have been -+ addressed. -+ -+* dns.name.to_text() now returns text and not bytes on Python 3.x -+ -+* Miscellaneous fixes for the Python 2/3 codeline merge. -+ -+* Many "lint" fixes after the addition of pylint support. -+ -+* The random number generator reseeds after a fork(). -+ -+### New since 1.13.0: -+ -+* CSYNC RRs are now supported. -+ -+* `dns/message.py` (`make_query`): Setting any value which implies EDNS will -+ turn on EDNS if `use_edns` has not been specified. -+ -+### Bugs fixed since 1.13.0: -+ -+* TSIG signature algorithm setting was broken by the Python 2 and Python 3 code -+ line merge. -+ -+* A bug in the LOC RR destroyed N/S and E/W distinctions within a degree of the -+ equator or prime merdian respectively. -+ -+* Misc. fixes to deal with fallout from the Python 2 & 3 merge. -+ Fixes #156, #157, #158, #159, #160. -+ -+* Running with python optimization on caused issues when stripped docstrings -+ were referenced. Fixes #154 -+ -+* `dns.zone.from_text()` erroneously required the zone to be provided. -+ Fixes #153 -+ -+### New since 1.12.0: -+ -+* Dnspython now uses a single source for Python 2 and Python 3, eliminating the -+ painful merging between the Python 2 and Python 3 branches. Thank you so much -+ to Arthur Gautier for taking on this challenge and making it work! It was a -+ big job! -+ -+* Support for Python older than 2.6 dropped. -+ -+* Support for Python older than 3.3 dropped. -+ -+* Zone origin can be specified as a string. -+ -+* A rich string representation for all DNSExceptions. -+ -+* setuptools has replaced distutils -+ -+* Added support for CAA, CDS, CDNSKEY, EUI48, EUI64, and URI RR types. -+ -+* Names now support the pickle protocol. -+ -+* Ports can be specified per-nameserver in the stub resolver. -+ -+### Bugs fixed since 1.12.0: -+ -+* A number of Unicode name bugs have been fixed. -+ -+* `resolv.conf` processing now rejects lines with too few tokens. -+ -+* NameDicts now keep the max-depth value correct, and update properly. -+ -+### New since 1.11.1: -+ -+* Added `dns.zone.to_text()`. -+ -+* Added support for "options rotate" in `/etc/resolv.conf`. -+ -+* `dns.rdtypes.ANY.DNSKEY` now has helpers functions to convert between the -+ numeric form of the flags and a set of human-friendly strings -+ -+* The reverse name of an IPv6 mapped IPv4 address is now in the IPv4 reverse -+ namespace. -+ -+* The test system can now run the tests without requiring dnspython to be -+ installed. -+ -+* Preliminary Elliptic Curve DNSSEC Validation (requires ecdsa module) -+ -+### Bugs fixed since 1.11.1: -+ -+* dnspython raised an exception when reading a masterfile starting with leading -+ whitespace -+ -+* dnspython was affected by a python slicing API bug present on 64-bit windows. -+ -+* Unicode escaping was applied at the wrong time. -+ -+* RRSIG `to_text()` did not respect the relativize setting. -+ -+* APL RRs with zero rdlength were rejected. -+ -+* The tokenizer could put back an unescaped token. -+ -+* Making a response to a message signed with TSIG was broken. -+ -+* The IXFR state machine didn't handle long IXFR diffs. -+ -+### New since 1.11.0: -+ -+* Nothing -+ -+### Bugs fixed since 1.11.0: -+ -+* `dns.resolver.Resolver` erroneously referred to `retry_servfail` -+ instead of `self.retry_servfail`. -+ -+* `dns.tsigkeyring.to_text()` would fail trying to convert the keyname to text. -+ -+* Multi-message TSIGs were broken for algorithms other than HMAC-MD5 because we -+ weren't passing the right digest module to the HMAC code. -+ -+* `dns.dnssec._find_candidate_keys()` tried to extract the key from the wrong -+ variable name. -+ -+* $GENERATE tests were not backward compatible with python 2.4. -+ -+### New since 1.10.0: -+ -+* $GENERATE support -+ -+* TLSA RR support -+ -+* Added set_flags() method to dns.resolver.Resolver -+ -+### Bugs fixed since 1.10.0: -+ -+* Names with offsets >= 2^14 are no longer added to the compression table. -+ -+* The "::" syntax is not used to shorten a single 16-bit section of the text -+ form an IPv6 address. -+ -+* Caches are now locked. -+ -+* YXDOMAIN is raised if seen by the resolver. -+ -+* Empty rdatasets are not printed. -+ -+* DNSKEY key tags are no longer assumed to be unique. -+ -+### New since 1.9.4: -+ -+* Added dns.resolver.LRUCache. In this cache implementation, the cache size is -+ limited to a user-specified number of nodes, and when adding a new node to a -+ full cache the least-recently used node is removed. If you're crawling the web -+ or otherwise doing lots of resolutions and you are using a cache, switching -+ to the LRUCache is recommended. -+ -+* `dns.resolver.query()` will try TCP if a UDP response is truncated. -+ -+* The python socket module's DNS methods can be now be overridden with -+ implementations that use dnspython's resolver. -+ -+* Old DNSSEC types KEY, NXT, and SIG have been removed. -+ -+* Whitespace is allowed in SSHFP fingerprints. -+ -+* Origin checking in `dns.zone.from_xfr()` can be disabled. -+ -+* Trailing junk checking can be disabled. -+ -+* A source port can be specified when creating a resolver query. -+ -+* All EDNS values may now be specified to `dns.message.make_query()`. -+ -+### Bugs fixed since 1.9.4: -+ -+* IPv4 and IPv6 address processing is now stricter. -+ -+* Bounds checking of slices in rdata wire processing is now more strict, and -+ bounds errors (e.g. we got less data than was expected) now raise -+ `dns.exception.FormError` rather than `IndexError`. -+ -+* Specifying a source port without specifying source used to have no effect, but -+ now uses the wildcard address and the specified port. -+ -+### New since 1.9.3: -+ -+* Nothing. -+ -+### Bugs fixed since 1.9.3: -+ -+* The rdata `_wire_cmp()` routine now handles relative names. -+ -+* The SIG RR implementation was missing `import struct`. -+ -+### New since 1.9.2: -+ -+* A boolean parameter, `raise_on_no_answer`, has been added to the `query()` -+ methods. In no-error, no-data situations, this parameter determines whether -+ `NoAnswer` should be raised or not. If True, `NoAnswer` is raised. If False, -+ then an `Answer()` object with a None rrset will be returned. -+ -+* Resolver `Answer()` objects now have a canonical_name field. -+ -+* Rdata now has a `__hash__` method. -+ -+### Bugs fixed since 1.9.2: -+ -+* Dnspython was erroneously doing case-insensitive comparisons of the names in -+ NSEC and RRSIG RRs. -+ -+* We now use `is` and not `==` when testing what section an RR is in. -+ -+* The resolver now disallows metaqueries. -+ -+### New since 1.9.1: -+ -+* Nothing. -+ -+### Bugs fixed since 1.9.1: -+ -+* The `dns.dnssec` module didn't work at all due to missing imports that escaped -+ detection in testing because the test suite also did the imports. The third -+ time is the charm! -+ -+### New since 1.9.0: -+ -+* Nothing. -+ -+### Bugs fixed since 1.9.0: -+ -+* The `dns.dnssec` module didn't work with DSA due to namespace contamination -+ from a "from"-style import. -+ -+### New since 1.8.0: -+ -+* dnspython now uses `poll()` instead of `select()` when available. -+ -+* Basic DNSSEC validation can be done using `dns.dnsec.validate()` and -+ `dns.dnssec.validate_rrsig()` if you have PyCrypto 2.3 or later installed. -+ Complete secure resolution is not yet available. -+ -+* Added `key_id()` to the DNSSEC module, which computes the DNSSEC key id of a -+ DNSKEY rdata. -+ -+* Added `make_ds()` to the DNSSEC module, which returns the DS RR for a given -+ DNSKEY rdata. -+ -+* dnspython now raises an exception if HMAC-SHA284 or HMAC-SHA512 are used with -+ a Python older than 2.5.2. (Older Pythons do not compute the correct value.) -+ -+* Symbolic constants are now available for TSIG algorithm names. -+ -+### Bugs fixed since 1.8.0 -+ -+* `dns.resolver.zone_for_name()` didn't handle a query response with a CNAME or -+ DNAME correctly in some cases. -+ -+* When specifying rdata types and classes as text, Unicode strings may now be -+ used. -+ -+* Hashlib compatibility issues have been fixed. -+ -+* `dns.message` now imports `dns.edns`. -+ -+* The TSIG algorithm value was passed incorrectly to `use_tsig()` in some cases. -+ -+### New since 1.7.1: -+ -+* Support for hmac-sha1, hmac-sha224, hmac-sha256, hmac-sha384 and hmac-sha512 -+ has been contributed by Kevin Chen. -+ -+* The tokenizer's tokens are now Token objects instead of (type, value) tuples. -+ -+### Bugs fixed since 1.7.1: -+ -+* Escapes in masterfiles now work correctly. Previously they were only working -+ correctly when the text involved was part of a domain name. -+ -+* When constructing a DDNS update, if the `present()` method was used with a -+ single rdata, a zero TTL was not added. -+ -+* The entropy pool needed locking to be thread safe. -+ -+* The entropy pool's reading of `/dev/random` could cause dnspython to block. -+ -+* The entropy pool did buffered reads, potentially consuming more randomness -+ than we needed. -+ -+* The entropy pool did not seed with high quality randomness on Windows. -+ -+* SRV records were compared incorrectly. -+ -+* In the e164 query function, the resolver parameter was not used. -+ -+### New since 1.7.0: -+ -+* Nothing -+ -+### Bugs fixed since 1.7.0: -+ -+* The 1.7.0 kitting process inadvertently omitted the code for the DLV RR. -+ -+* Negative DDNS prerequisites are now handled correctly. -+ -+### New since 1.6.0: -+ -+* Rdatas now have a `to_digestable()` method, which returns the DNSSEC canonical -+ form of the rdata, suitable for use in signature computations. -+ -+* The NSEC3, NSEC3PARAM, DLV, and HIP RR types are now supported. -+ -+* An entropy module has been added and is used to randomize query ids. -+ -+* EDNS0 options are now supported. -+ -+* UDP IXFR is now supported. -+ -+* The wire format parser now has a `one_rr_per_rrset` mode, which suppresses the -+ usual coalescing of all RRs of a given type into a single RRset. -+ -+* Various helpful DNSSEC-related constants are now defined. -+ -+* The resolver's `query()` method now has an optional `source` parameter, -+ allowing the source IP address to be specified. -+ -+### Bugs fixed since 1.6.0: -+ -+* On Windows, the resolver set the domain incorrectly. -+ -+* DS RR parsing only allowed one Base64 chunk. -+ -+* TSIG validation didn't always use absolute names. -+ -+* `NSEC.to_text()` only printed the last window. -+ -+* We did not canonicalize IPv6 addresses before comparing them; we -+ would thus treat equivalent but different textual forms, e.g. -+ "1:00::1" and "1::1" as being non-equivalent. -+ -+* If the peer set a TSIG error, we didn't raise an exception. -+ -+* Some EDNS bugs in the message code have been fixed (see the ChangeLog -+ for details). -+ -+### New since 1.5.0: -+ -+* Added dns.inet.is_multicast(). -+ -+### Bugs fixed since 1.5.0: -+ -+* If `select()` raises an exception due to EINTR, we should just `select()` -+ again. -+ -+* If the queried address is a multicast address, then don't check that the -+ address of the response is the same as the address queried. -+ -+* NAPTR comparisons didn't compare the preference field due to a typo. -+ -+* Testing of whether a Windows NIC is enabled now works on Vista thanks to code -+ contributed by Paul Marks. -+ -+### New since 1.4.0: -+ -+* Answer objects now support more of the python sequence protocol, forwarding -+ the requests to the answer rrset. E.g. `for a in answer` is equivalent to -+ `for a in answer.rrset`, `answer[i]` is equivalent to `answer.rrset[i]`, and -+ `answer[i:j]` is equivalent to `answer.rrset[i:j]`. -+ -+* Making requests using EDNS, including indicating DNSSEC awareness, -+ is now easier. For example, you can now say: -+ `q = dns.message.make_query('www.dnspython.org', 'MX', want_dnssec=True)` -+ -+* `dns.query.xfr()` can now be used for IXFR. -+ -+* Support has been added for the DHCID, IPSECKEY, and SPF RR types. -+ -+* UDP messages from unexpected sources can now be ignored by setting -+ `ignore_unexpected` to True when calling `dns.query.udp`. -+ -+### Bugs fixed since 1.4.0: -+ -+* If `/etc/resolv.conf` didn't exist, we raised an exception instead of simply -+ using the default resolver configuration. -+ -+* In `dns.resolver.Resolver._config_win32_fromkey()`, we were passing the wrong -+ variable to `self._config_win32_search()`. -+ -+### New since 1.3.5: -+ -+* You can now convert E.164 numbers to/from their ENUM name forms: -+ ```python -+ >>> import dns.e164 -+ >>> n = dns.e164.from_e164("+1 555 1212") -+ >>> n -+ -+ >>> dns.e164.to_e164(n) -+ '+15551212' -+ ``` -+ -+* You can now convert IPv4 and IPv6 address to/from their corresponding DNS -+ reverse map names: -+ ```python -+ >>> import dns.reversename -+ >>> n = dns.reversename.from_address("127.0.0.1") -+ >>> n -+ -+ >>> dns.reversename.to_address(n) -+ '127.0.0.1' -+ ``` -+ -+* You can now convert between Unicode strings and their IDN ACE form: -+ ```python -+ >>> n = dns.name.from_text(u'les-\u00e9l\u00e8ves.example.') -+ >>> n -+ -+ >>> n.to_unicode() -+ u'les-\xe9l\xe8ves.example.' -+ ``` -+ -+* The origin parameter to `dns.zone.from_text()` and `dns.zone.to_text()` is now -+ optional. If not specified, the origin will be taken from the first $ORIGIN -+ statement in the master file. -+ -+* Sanity checking of a zone can be disabled; this is useful when working with -+ files which are zone fragments. -+ -+### Bugs fixed since 1.3.5: -+ -+* The correct delimiter was not used when retrieving the list of nameservers -+ from the registry in certain versions of windows. -+ -+* The floating-point version of latitude and longitude in LOC RRs -+ (`float_latitude` and `float_longitude`) had incorrect signs for south -+ latitudes and west longitudes. -+ -+* BIND 8 TTL syntax is now accepted in all TTL-like places (i.e. SOA fields -+ refresh, retry, expire, and minimum; SIG/RRSIG field original_ttl). -+ -+* TTLs are now bounds checked when their text form is parsed, and their values -+ must be in the closed interval `[0, 2^31 - 1]`. -+ -+### New since 1.3.4: -+ -+* In the resolver, if time goes backward a little bit, ignore it. -+ -+* `zone_for_name()` has been added to the resolver module. It returns the zone -+ which is authoritative for the specified name, which is handy for dynamic -+ update. E.g. -+ -+ import dns.resolver -+ print dns.resolver.zone_for_name('www.dnspython.org') -+ -+ will output `"dnspython.org."` and -+ `print dns.resolver.zone_for_name('a.b.c.d.e.f.example.')` -+ will output `"."`. -+ -+* The default resolver can be fetched with the `get_default_resolver()` method. -+ -+* You can now get the parent (immediate superdomain) of a name by using the -+ `parent()` method. -+ -+* `Zone.iterate_rdatasets()` and `Zone.iterate_rdatas()` now have a default -+ rdtype of `dns.rdatatype.ANY` like the documentation says. -+ -+* A Dynamic DNS example, ddns.py, has been added. -+ -+### New since 1.3.3: -+ -+* The source address and port may now be specified when calling -+ `dns.query.{udp,tcp,xfr}`. -+ -+* The resolver now does exponential backoff each time it runs through all of the -+ nameservers. -+ -+* Rcodes which indicate a nameserver is likely to be a "permanent failure" for a -+ query cause the nameserver to be removed from the mix for that query. -+ -+### New since 1.3.2: -+ -+* `dns.message.Message.find_rrset()` now uses an index, vastly improving the -+ `from_wire()` performance of large messages such as zone transfers. -+ -+* Added `dns.message.make_response()`, which creates a skeletal response for the -+ specified query. -+ -+* Added `opcode()` and `set_opcode()` convenience methods to the -+ `dns.message.Message` class. Added the `request_payload` attribute to the -+ Message class. -+ -+* The `file` parameter of `dns.name.Name.to_wire()` is now optional; if omitted, -+ the wire form will be returned as the value of the function. -+ -+* `dns.zone.from_xfr()` in relativization mode incorrectly set `zone.origin` to -+ the empty name. -+ -+* The masterfile parser incorrectly rejected TXT records where a value was not -+ quoted. -+ -+### New since 1.3.1: -+ -+* The NSEC format doesn't allow specifying types by number, so we shouldn't -+ either. (Using the unknown type format is still OK though.) -+ -+* The resolver wasn't catching `dns.exception.Timeout`, so a timeout erroneously -+ caused the whole resolution to fail instead of just going on to the next -+ server. -+ -+* The renderer module didn't import random, causing an exception to be raised if -+ a query id wasn't provided when a Renderer was created. -+ -+* The conversion of LOC milliseconds values from text to binary was incorrect if -+ the length of the milliseconds string was not 3. -+ -+### New since 1.3.0: -+ -+* Added support for the SSHFP type. -+ -+### New since 1.2.0: -+ -+* Added support for new DNSSEC types RRSIG, NSEC, and DNSKEY. -+ -+* This release fixes all known bugs. -+ -+* See the ChangeLog file for more detailed information on changes since the -+ prior release. -+ -+ -+## REQUIREMENTS -+ -+Python 2.6 or later. -+ -+ -+## HOME PAGE -+ -+For the latest in releases, documentation, and information, visit the dnspython -+home page at http://www.dnspython.org/ -+ -+ -+ -+## DOCUMENTATION -+ -+Documentation is sparse at the moment. Use pydoc, or read the HTML documentation -+at the dnspython home page, or download the HTML documentation. -+ -+ -+## BUG REPORTS -+ -+Bug reports may be sent to bugs@dnspython.org -+ -+ -+## MAILING LISTS -+ -+A number of mailing lists are available. Visit the dnspython home page to -+subscribe or unsubscribe.