[Python-modules-commits] [dnspython3] 01/03: Import dnspython3_1.12.0.orig.tar.gz

Scott Kitterman kitterman at moszumanska.debian.org
Thu Jan 14 05:18:54 UTC 2016


This is an automated email from the git hooks/post-receive script.

kitterman pushed a commit to branch master
in repository dnspython3.

commit 11befa72e1fe52835c1e25557601cbef51abf2b6
Author: Scott Kitterman <scott at kitterman.com>
Date:   Thu Jan 14 00:16:13 2016 -0500

    Import dnspython3_1.12.0.orig.tar.gz
---
 ChangeLog                                          |  73 +++++++++++++
 PKG-INFO                                           |   4 +-
 README                                             |  40 ++++++-
 dns/dnssec.py                                      |   2 +
 dns/inet.py                                        |   7 +-
 dns/ipv6.py                                        |   5 +
 dns/message.py                                     |  15 ++-
 dns/name.py                                        |  24 ++++-
 dns/query.py                                       |  17 ++-
 dns/rdtypes/ANY/DNSKEY.py                          |  48 +++++++++
 dns/rdtypes/ANY/LOC.py                             |  28 +++--
 dns/rdtypes/ANY/NSEC3PARAM.py                      |   1 +
 dns/rdtypes/ANY/RRSIG.py                           |   2 +-
 dns/rdtypes/IN/APL.py                              |   4 +-
 dns/resolver.py                                    |   7 ++
 dns/reversename.py                                 |   9 +-
 dns/tsig.py                                        |  16 +++
 dns/version.py                                     |   4 +-
 dns/zone.py                                        |  28 ++++-
 setup.py                                           |   2 +-
 tests/Makefile                                     |   5 +-
 tests/example3.good                                | 117 +++++++++++++++++++++
 tests/{bugs.py => test_bugs.py}                    |   8 ++
 tests/{dnssec.py => test_dnssec.py}                |  14 +++
 tests/{flags.py => test_flags.py}                  |   0
 tests/{generate.py => test_generate.py}            |   0
 tests/{message.py => test_message.py}              |   0
 tests/{name.py => test_name.py}                    |   5 +
 tests/{namedict.py => test_namedict.py}            |   0
 tests/{ntoaaton.py => test_ntoaaton.py}            |   8 ++
 .../{rdtypeandclass.py => test_rdtypeandclass.py}  |   0
 tests/test_rdtypeanydnskey.py                      |  68 ++++++++++++
 tests/{resolver.py => test_resolver.py}            |   0
 tests/{rrset.py => test_rrset.py}                  |   0
 tests/{set.py => test_set.py}                      |   0
 tests/{tokenizer.py => test_tokenizer.py}          |   0
 tests/{update.py => test_update.py}                |   0
 tests/{zone.py => test_zone.py}                    |  23 ++++
 tests/utest.py                                     |   8 ++
 39 files changed, 551 insertions(+), 41 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index a66722c..b28613f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,76 @@
+2014-09-01  Bob Halley  <halley at dnspython.org>
+
+	* (Version 1.12.0 released)
+
+2014-08-31  Bob Halley  <halley at dnspython.org>
+
+	* The test system can now run the tests without requiring dnspython
+	  to be installed.
+
+	* 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 at 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.
+
+	* 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.
+
+	* 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 at 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 at dnspython.org>
+
+	* dns/zone.py: Do not put back an unescaped token.  This was
+	  causing escape processing for domain names to break.  Thanks to
+	  connormclaud for reporting the problem.
+
+2014-04-04  Bob Halley  <halley at dnspython.org>
+
+	* dns/message.py: Making a response didn't work correctly if the
+	  query was signed with TSIG and we knew the key.  Thanks to Jeffrey
+	  Stiles for reporting the problem.
+
+2014-01-10  Bob Halley  <halley at ndnspython.org>
+
+	* dns/inet.py (is_multicast): Removed lingering ord()s.
+
+2013-12-11  Bob Halley  <halley at dnspython.org>
+
+	* dns/query.py: Fix problems with the IXFR state machine which caused
+	  long diffs to fail.  Thanks to James Raftery for the fix and the
+	  repeated prodding to get it applied :)
+
 2013-09-02  Bob Halley  <halley at dnspython.org>
 
 	* (Version 1.11.1 released)
diff --git a/PKG-INFO b/PKG-INFO
index c80abaa..41339a7 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,12 +1,12 @@
 Metadata-Version: 1.1
 Name: dnspython3
-Version: 1.11.1
+Version: 1.12.0
 Summary: DNS toolkit
 Home-page: http://www.dnspython.org
 Author: Bob Halley
 Author-email: halley at dnspython.org
 License: BSD-like
-Download-URL: http://www.dnspython.org/kits3/1.11.1/dnspython3-1.11.1.tar.gz
+Download-URL: http://www.dnspython.org/kits3/1.12.0/dnspython3-1.12.0.tar.gz
 Description: dnspython3 is a DNS toolkit for Python 3. It supports almost all
         record types. It can be used for queries, zone transfers, and dynamic
         updates.  It supports TSIG authenticated messages and EDNS0.
diff --git a/README b/README
index f4bd391..2ea6151 100644
--- a/README
+++ b/README
@@ -22,7 +22,45 @@ development by continuing to employ the author :).
 
 ABOUT THIS RELEASE
 
-This is dnspython3 1.11.1
+This is dnspython3 1.12.0
+
+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.
+
+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.
+
+	dns.inet.is_multicast had python3 port issues.
 
 New since 1.11.0:
     
diff --git a/dns/dnssec.py b/dns/dnssec.py
index c1cd1cb..2dd4a67 100644
--- a/dns/dnssec.py
+++ b/dns/dnssec.py
@@ -370,6 +370,8 @@ 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
diff --git a/dns/inet.py b/dns/inet.py
index 3b7e88f..56a1bd7 100644
--- a/dns/inet.py
+++ b/dns/inet.py
@@ -45,7 +45,7 @@ def inet_pton(family, text):
     implemented.
     @rtype: string
     """
-    
+
     if family == AF_INET:
         return dns.ipv4.inet_aton(text)
     elif family == AF_INET6:
@@ -97,12 +97,11 @@ def is_multicast(text):
     @rtype: bool
     """
     try:
-        first = ord(dns.ipv4.inet_aton(text)[0])
+        first = dns.ipv4.inet_aton(text)[0]
         return (first >= 224 and first <= 239)
     except:
         try:
-            first = ord(dns.ipv6.inet_aton(text)[0])
+            first = dns.ipv6.inet_aton(text)[0]
             return (first == 255)
         except:
             raise ValueError
-    
diff --git a/dns/ipv6.py b/dns/ipv6.py
index 73f6232..e2c0164 100644
--- a/dns/ipv6.py
+++ b/dns/ipv6.py
@@ -155,3 +155,8 @@ def inet_aton(text):
         return bytes.fromhex(text)
     except:
         raise dns.exception.SyntaxError
+
+_mapped_prefix = b'\x00' * 10 + b'\xff\xff'
+
+def is_mapped(address):
+    return address.startswith(_mapped_prefix)
diff --git a/dns/message.py b/dns/message.py
index 85096f3..869a247 100644
--- a/dns/message.py
+++ b/dns/message.py
@@ -666,6 +666,10 @@ class _WireReader(object):
                 secret = self.message.keyring.get(absolute_name)
                 if secret is None:
                     raise UnknownTSIGKey("key '%s' unknown" % name)
+                self.message.keyname = absolute_name
+                (self.message.keyalgorithm, self.message.mac) = \
+                    dns.tsig.get_algorithm_and_mac(self.wire, self.current,
+                                                   rdlen)
                 self.message.tsig_ctx = \
                                       dns.tsig.validate(self.wire,
                                           absolute_name,
@@ -1065,7 +1069,8 @@ def make_query(qname, rdtype, rdclass = dns.rdataclass.IN, use_edns=None,
     m.want_dnssec(want_dnssec)
     return m
 
-def make_response(query, recursion_available=False, our_payload=8192):
+def make_response(query, recursion_available=False, our_payload=8192,
+                  fudge=300):
     """Make a message which is a response for the specified query.
     The message returned is really a response skeleton; it has all
     of the infrastructure required of a response, but none of the
@@ -1082,6 +1087,8 @@ def make_response(query, recursion_available=False, our_payload=8192):
     @param our_payload: payload size to advertise in EDNS responses; default
     is 8192.
     @type our_payload: int
+    @param fudge: TSIG time fudge; default is 300 seconds.
+    @type fudge: int
     @rtype: dns.message.Message object"""
 
     if query.flags & dns.flags.QR:
@@ -1094,8 +1101,8 @@ def make_response(query, recursion_available=False, our_payload=8192):
     response.question = list(query.question)
     if query.edns >= 0:
         response.use_edns(0, 0, our_payload, query.payload)
-    if not query.keyname is None:
-        response.keyname = query.keyname
-        response.keyring = query.keyring
+    if query.had_tsig:
+        response.use_tsig(query.keyring, query.keyname, fudge, None, 0, '',
+                          query.keyalgorithm)
         response.request_mac = query.mac
     return response
diff --git a/dns/name.py b/dns/name.py
index 3d3b1be..daa5261 100644
--- a/dns/name.py
+++ b/dns/name.py
@@ -25,6 +25,7 @@ import encodings.idna
 import io
 import struct
 import sys
+import copy
 
 import dns.exception
 import dns.util
@@ -95,6 +96,20 @@ def _escapify(label):
             text += '\\%03d' % c
     return text
 
+def _escapify_unicode(label):
+    """Escape the characters in label which need it.
+    @returns: the escaped string
+    @rtype: string"""
+    text = ''
+    for c in label:
+        if ord(c) in _escaped:
+            text += '\\' + c
+        elif ord(c) > 0x20:
+            text += c
+        else:
+            text += '\\%03d' % ord(c)
+    return text
+
 def _bytesify(label):
     if isinstance(label, str):
         return label.encode('latin_1')
@@ -156,6 +171,12 @@ class Name(object):
     def __setattr__(self, name, value):
         raise TypeError("object doesn't support attribute assignment")
 
+    def __copy__(self):
+        return Name(self.labels)
+
+    def __deepcopy__(self, memo):
+        return Name(copy.deepcopy(self.labels, memo))
+
     def is_absolute(self):
         """Is the most significant label of this name the root label?
         @rtype: bool
@@ -353,7 +374,8 @@ class Name(object):
             l = self.labels[:-1]
         else:
             l = self.labels
-        s = '.'.join([encodings.idna.ToUnicode(_escapify(x)) for x in l])
+        s = '.'.join([_escapify_unicode(encodings.idna.ToUnicode(x))
+                      for x in l])
         return s
 
     def to_digestable(self, origin=None):
diff --git a/dns/query.py b/dns/query.py
index 2dc3f80..2a252c7 100644
--- a/dns/query.py
+++ b/dns/query.py
@@ -411,6 +411,8 @@ def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN,
         tcpmsg = struct.pack("!H", l) + wire
         _net_write(s, tcpmsg, expiration)
     done = False
+    delete_mode = True
+    expecting_SOA = False
     soa_rrset = None
     soa_count = 0
     if relativize:
@@ -439,18 +441,16 @@ def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN,
         tsig_ctx = r.tsig_ctx
         first = False
         answer_index = 0
-        delete_mode = False
-        expecting_SOA = False
         if soa_rrset is None:
             if not r.answer or r.answer[0].name != oname:
-                raise dns.exception.FormError
+                raise dns.exception.FormError("No answer or RRset not for qname")
             rrset = r.answer[0]
             if rrset.rdtype != dns.rdatatype.SOA:
                 raise dns.exception.FormError("first RRset is not an SOA")
             answer_index = 1
             soa_rrset = rrset.copy()
             if rdtype == dns.rdatatype.IXFR:
-                if soa_rrset[0].serial == serial:
+                if soa_rrset[0].serial <= serial:
                     #
                     # We're already up-to-date.
                     #
@@ -471,7 +471,14 @@ def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN,
                     expecting_SOA = False
                 elif rdtype == dns.rdatatype.IXFR:
                     delete_mode = not delete_mode
-                if rrset == soa_rrset and not delete_mode:
+                #
+                # If this SOA RRset is equal to the first we saw then we're
+                # finished. If this is an IXFR we also check that we're seeing
+                # the record in the expected part of the response.
+                #
+                if rrset == soa_rrset and \
+                        (rdtype == dns.rdatatype.AXFR or \
+                        (rdtype == dns.rdatatype.IXFR and delete_mode)):
                     done = True
             elif expecting_SOA:
                 #
diff --git a/dns/rdtypes/ANY/DNSKEY.py b/dns/rdtypes/ANY/DNSKEY.py
index 3673b2a..246a2fd 100644
--- a/dns/rdtypes/ANY/DNSKEY.py
+++ b/dns/rdtypes/ANY/DNSKEY.py
@@ -21,11 +21,54 @@ import dns.dnssec
 import dns.rdata
 import dns.util
 
+
 # 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.items()])
+
+
+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
 
@@ -91,3 +134,8 @@ class DNSKEY(dns.rdata.Rdata):
         if v == 0:
             v = dns.util.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/LOC.py b/dns/rdtypes/ANY/LOC.py
index 64b50c9..0c9bbdd 100644
--- a/dns/rdtypes/ANY/LOC.py
+++ b/dns/rdtypes/ANY/LOC.py
@@ -23,7 +23,14 @@ import dns.util
 _pows = (1, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
          100000000, 1000000000, 10000000000)
 
+# default values are in centimeters
+_default_size = 100.0
+_default_hprec = 1000000.0
+_default_vprec = 1000.0
+
 def _exponent_of(what, desc):
+    if what == 0:
+        return 0
     exp = None
     for i in range(len(_pows)):
         if what // _pows[i] == 0:
@@ -98,13 +105,14 @@ class LOC(dns.rdata.Rdata):
                  'horizontal_precision', 'vertical_precision']
 
     def __init__(self, rdclass, rdtype, latitude, longitude, altitude,
-                 size=1.0, hprec=10000.0, vprec=10.0):
+                 size=_default_size, hprec=_default_hprec, vprec=_default_vprec):
         """Initialize a LOC record instance.
 
         The parameters I{latitude} and I{longitude} may be either a 4-tuple
         of integers specifying (degrees, minutes, seconds, milliseconds),
         or they may be floating point values specifying the number of
-        degrees.  The other parameters are floats."""
+        degrees. The other parameters are floats. Size, horizontal precision,
+        and vertical precision are specified in centimeters."""
 
         super(LOC, self).__init__(rdclass, rdtype)
         if isinstance(latitude, int):
@@ -141,8 +149,10 @@ class LOC(dns.rdata.Rdata):
             self.longitude[3], long_hemisphere, self.altitude / 100.0
             )
 
-        if self.size != 1.0 or self.horizontal_precision != 10000.0 or \
-           self.vertical_precision != 10.0:
+        # do not print default values
+        if self.size != _default_size or \
+            self.horizontal_precision != _default_hprec or \
+            self.vertical_precision != _default_vprec:
             text += " %0.2fm %0.2fm %0.2fm" % (
                 self.size / 100.0, self.horizontal_precision / 100.0,
                 self.vertical_precision / 100.0
@@ -152,9 +162,9 @@ class LOC(dns.rdata.Rdata):
     def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
         latitude = [0, 0, 0, 0]
         longitude = [0, 0, 0, 0]
-        size = 1.0
-        hprec = 10000.0
-        vprec = 10.0
+        size = _default_size
+        hprec = _default_hprec
+        vprec = _default_vprec
 
         latitude[0] = tok.get_int()
         t = tok.get_string()
@@ -240,8 +250,8 @@ class LOC(dns.rdata.Rdata):
                     value = token.value
                     if value[-1] == 'm':
                         value = value[0 : -1]
-                        vprec = float(value) * 100.0	# m -> cm
-                        tok.get_eol()
+                    vprec = float(value) * 100.0	# m -> cm
+                    tok.get_eol()
 
         return cls(rdclass, rdtype, latitude, longitude, altitude,
                    size, hprec, vprec)
diff --git a/dns/rdtypes/ANY/NSEC3PARAM.py b/dns/rdtypes/ANY/NSEC3PARAM.py
index f323d3f..ede6863 100644
--- a/dns/rdtypes/ANY/NSEC3PARAM.py
+++ b/dns/rdtypes/ANY/NSEC3PARAM.py
@@ -58,6 +58,7 @@ class NSEC3PARAM(dns.rdata.Rdata):
             salt = b''
         else:
             salt = bytes.fromhex(salt)
+        tok.get_eol()
         return cls(rdclass, rdtype, algorithm, flags, iterations, salt)
 
     from_text = classmethod(from_text)
diff --git a/dns/rdtypes/ANY/RRSIG.py b/dns/rdtypes/ANY/RRSIG.py
index 2e69821..b45e0c9 100644
--- a/dns/rdtypes/ANY/RRSIG.py
+++ b/dns/rdtypes/ANY/RRSIG.py
@@ -95,7 +95,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 a7b8510..58e35b6 100644
--- a/dns/rdtypes/IN/APL.py
+++ b/dns/rdtypes/IN/APL.py
@@ -119,6 +119,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])
@@ -152,8 +154,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 2d28f70..3d3cccb 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
@@ -510,6 +511,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
@@ -540,6 +542,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()
@@ -808,6 +813,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 f8bd9e0..276393c 100644
--- a/dns/reversename.py
+++ b/dns/reversename.py
@@ -39,8 +39,13 @@ def from_address(text):
     @rtype: dns.name.Name object
     """
     try:
-        parts = ['%x.%x' % (byte & 0x0f, byte >> 4) for byte in dns.ipv6.inet_aton(text)]
-        origin = ipv6_reverse_domain
+        v6 = dns.ipv6.inet_aton(text)
+        if dns.ipv6.is_mapped(v6):
+            parts = ['%d' % byte for byte in v6[12:]]
+            origin = ipv4_reverse_domain
+        else:
+            parts = ['%x.%x' % (byte & 0x0f, byte >> 4) for byte in v6]
+            origin = ipv6_reverse_domain
     except:
         parts = ['%d' % byte for byte in dns.ipv4.inet_aton(text)]
         origin = ipv4_reverse_domain
diff --git a/dns/tsig.py b/dns/tsig.py
index 6d801d4..4333016 100644
--- a/dns/tsig.py
+++ b/dns/tsig.py
@@ -216,3 +216,19 @@ def get_algorithm(algorithm):
     except KeyError:
         raise NotImplementedError("TSIG algorithm " + str(algorithm) +
                                   " is not supported")
+
+def get_algorithm_and_mac(wire, tsig_rdata, tsig_rdlen):
+    """Return the tsig algorithm for the specified tsig_rdata
+    @raises FormError: The TSIG is badly formed.
+    """
+    current = tsig_rdata
+    (aname, used) = dns.name.from_wire(wire, current)
+    current = current + used
+    (upper_time, lower_time, fudge, mac_size) = \
+                 struct.unpack("!HIHH", wire[current:current + 10])
+    current += 10
+    mac = wire[current:current + mac_size]
+    current += mac_size
+    if current > tsig_rdata + tsig_rdlen:
+        raise dns.exception.FormError
+    return (aname, mac)
diff --git a/dns/version.py b/dns/version.py
index c3d305e..c382279 100644
--- a/dns/version.py
+++ b/dns/version.py
@@ -16,8 +16,8 @@
 """dnspython release version information."""
 
 MAJOR = 1
-MINOR = 11
-MICRO = 1
+MINOR = 12
+MICRO = 0
 RELEASELEVEL = 0x0f
 SERIAL = 0
 
diff --git a/dns/zone.py b/dns/zone.py
index 6d19409..ece898a 100644
--- a/dns/zone.py
+++ b/dns/zone.py
@@ -18,6 +18,7 @@
 from __future__ import generators
 
 import builtins
+import io
 import re
 import sys
 
@@ -491,6 +492,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 = io.BytesIO()
+        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.
 
@@ -543,7 +565,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
@@ -798,7 +820,7 @@ class _MasterReader(object):
 
         try:
             while 1:
-                token = self.tok.get(True, True).unescape()
+                token = self.tok.get(True, True)
                 if token.is_eof():
                     if not self.current_file is None:
                         self.current_file.close()
@@ -943,7 +965,7 @@ def from_file(f, origin = None, rdclass = dns.rdataclass.IN,
     if isinstance(f, str):
         if filename is None:
             filename = f
-        f = open(f, 'rU')
+        f = open(f, 'r')
         want_close = True
     else:
         if filename is None:
diff --git a/setup.py b/setup.py
index 03bf2ca..23c2126 100755
--- a/setup.py
+++ b/setup.py
@@ -18,7 +18,7 @@
 import sys
 from distutils.core import setup
 
-version = '1.11.1'
+version = '1.12.0'
 
 kwargs = {
     'name' : 'dnspython3',
diff --git a/tests/Makefile b/tests/Makefile
index cd6e4fa..8c465df 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -20,7 +20,4 @@ PYTHON=python3
 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
similarity index 88%
rename from tests/bugs.py
rename to tests/test_bugs.py
index 79b30eb..906e0fd 100644
--- a/tests/bugs.py
+++ b/tests/test_bugs.py
@@ -52,5 +52,13 @@ class BugsTestCase(unittest.TestCase):
         out4 = rd4.to_digestable(dns.name.from_text("test"))
         self.assertTrue(binascii.hexlify(out4).decode('ascii') == '000101817f')
 
+    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,
+                                     b"", 0, 0)
+        self.assertTrue(rdata == rdata2)
+
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/tests/dnssec.py b/tests/test_dnssec.py
similarity index 93%
rename from tests/dnssec.py
rename to tests/test_dnssec.py
index f5fd54a..98c4788 100644
--- a/tests/dnssec.py
+++ b/tests/test_dnssec.py
@@ -99,22 +99,32 @@ example_ds_sha256 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS,
 
 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.assertRaises(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,
@@ -125,10 +135,14 @@ class DNSSECValidatorTestCase(unittest.TestCase):
         ds = dns.dnssec.make_ds(abs_dnspython_org, sep_key, 'SHA256')
         self.assertTrue(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,
diff --git a/tests/flags.py b/tests/test_flags.py
similarity index 100%
rename from tests/flags.py
rename to tests/test_flags.py
diff --git a/tests/generate.py b/tests/test_generate.py
similarity index 100%
rename from tests/generate.py
rename to tests/test_generate.py
diff --git a/tests/message.py b/tests/test_message.py
similarity index 100%
rename from tests/message.py
rename to tests/test_message.py
diff --git a/tests/name.py b/tests/test_name.py
similarity index 99%
rename from tests/name.py
rename to tests/test_name.py
index 2139409..c125a15 100644
--- a/tests/name.py
... 193 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/dnspython3.git



More information about the Python-modules-commits mailing list