diff options
| author | Arthur Gautier <baloo@gandi.net> | 2016-03-29 17:51:13 +0000 |
|---|---|---|
| committer | Arthur Gautier <baloo@gandi.net> | 2016-04-21 15:33:05 +0000 |
| commit | 5f59c1f3071e2ba6aad4f163ec7884ca8e4f1cc4 (patch) | |
| tree | 3d4d523604f965db8e4ebea3d24d1aad24c41415 /dns/zone.py | |
| parent | c1a2e4585a1404ee0cfaa6d2d2ad072e8807ef3e (diff) | |
| download | dnspython-5f59c1f3071e2ba6aad4f163ec7884ca8e4f1cc4.tar.gz | |
python3 support
Signed-off-by: Arthur Gautier <baloo@gandi.net>
Diffstat (limited to 'dns/zone.py')
| -rw-r--r-- | dns/zone.py | 157 |
1 files changed, 85 insertions, 72 deletions
diff --git a/dns/zone.py b/dns/zone.py index 453bbb9..2b7826c 100644 --- a/dns/zone.py +++ b/dns/zone.py @@ -19,6 +19,7 @@ from __future__ import generators import sys import re +from io import BytesIO import dns.exception import dns.name @@ -30,25 +31,31 @@ import dns.rrset import dns.tokenizer import dns.ttl import dns.grange +from ._compat import string_types, text_type -try: - from cStringIO import StringIO -except ImportError: - from io import StringIO class BadZone(dns.exception.DNSException): + """The DNS zone is malformed.""" + class NoSOA(BadZone): + """The DNS zone has no SOA RR at its origin.""" + class NoNS(BadZone): + """The DNS zone has no NS RRset at its origin.""" + class UnknownOrigin(BadZone): + """The DNS zone's origin is unknown.""" + class Zone(object): + """A DNS zone. A Zone is a mapping from names to nodes. The zone object may be @@ -110,13 +117,14 @@ class Zone(object): return not self.__eq__(other) def _validate_name(self, name): - if isinstance(name, (str, unicode)): + if isinstance(name, string_types): name = dns.name.from_text(name, None) elif not isinstance(name, dns.name.Name): raise KeyError("name parameter must be convertable to a DNS name") if name.is_absolute(): if not name.is_subdomain(self.origin): - raise KeyError("name parameter must be a subdomain of the zone origin") + raise KeyError( + "name parameter must be a subdomain of the zone origin") if self.relativize: name = name.relativize(self.origin) return name @@ -148,12 +156,11 @@ class Zone(object): def values(self): return self.nodes.values() - def iteritems(self): - return self.nodes.iteritems() - def items(self): return self.nodes.items() + iteritems = items + def get(self, key): key = self._validate_name(key) return self.nodes.get(key) @@ -208,7 +215,7 @@ class Zone(object): """ name = self._validate_name(name) - if self.nodes.has_key(name): + if name in self.nodes: del self.nodes[name] def find_rdataset(self, name, rdtype, covers=dns.rdatatype.NONE, @@ -240,9 +247,9 @@ class Zone(object): """ name = self._validate_name(name) - if isinstance(rdtype, (str, unicode)): + if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) - if isinstance(covers, (str, unicode)): + if isinstance(covers, string_types): covers = dns.rdatatype.from_text(covers) node = self.find_node(name, create) return node.find_rdataset(self.rdclass, rdtype, covers, create) @@ -303,12 +310,12 @@ class Zone(object): """ name = self._validate_name(name) - if isinstance(rdtype, (str, unicode)): + if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) - if isinstance(covers, (str, unicode)): + if isinstance(covers, string_types): covers = dns.rdatatype.from_text(covers) node = self.get_node(name) - if not node is None: + if node is not None: node.delete_rdataset(self.rdclass, rdtype, covers) if len(node) == 0: self.delete_node(name) @@ -366,9 +373,9 @@ class Zone(object): """ name = self._validate_name(name) - if isinstance(rdtype, (str, unicode)): + if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) - if isinstance(covers, (str, unicode)): + if isinstance(covers, string_types): covers = dns.rdatatype.from_text(covers) rdataset = self.nodes[name].find_rdataset(self.rdclass, rdtype, covers) rrset = dns.rrset.RRset(name, self.rdclass, rdtype, covers) @@ -422,9 +429,9 @@ class Zone(object): @type covers: int or string """ - if isinstance(rdtype, (str, unicode)): + if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) - if isinstance(covers, (str, unicode)): + if isinstance(covers, string_types): covers = dns.rdatatype.from_text(covers) for (name, node) in self.iteritems(): for rds in node: @@ -445,9 +452,9 @@ class Zone(object): @type covers: int or string """ - if isinstance(rdtype, (str, unicode)): + if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) - if isinstance(covers, (str, unicode)): + if isinstance(covers, string_types): covers = dns.rdatatype.from_text(covers) for (name, node) in self.iteritems(): for rds in node: @@ -474,31 +481,32 @@ class Zone(object): @type nl: string or None """ - if sys.hexversion >= 0x02030000: - # allow Unicode filenames - str_type = basestring - else: - str_type = str + str_type = string_types + if nl is None: - opts = 'w' + opts = 'wb' else: opts = 'wb' + if isinstance(f, str_type): - f = file(f, opts) + f = open(f, opts) want_close = True else: want_close = False try: if sorted: - names = self.keys() + names = list(self.keys()) names.sort() else: names = self.iterkeys() for n in names: l = self[n].to_text(n, origin=self.origin, relativize=relativize) + if isinstance(l, text_type): + l = l.encode() if nl is None: - print >> f, l + f.write(l) + f.write('\n') else: f.write(l) f.write(nl) @@ -521,7 +529,7 @@ class Zone(object): LF on POSIX, CRLF on Windows, CR on Macintosh). @type nl: string or None """ - temp_buffer = StringIO() + temp_buffer = BytesIO() self.to_file(temp_buffer, sorted, relativize, nl) return_value = temp_buffer.getvalue() temp_buffer.close() @@ -545,6 +553,7 @@ class Zone(object): class _MasterReader(object): + """Read a DNS master file @ivar tok: The tokenizer @@ -573,7 +582,7 @@ class _MasterReader(object): def __init__(self, tok, origin, rdclass, relativize, zone_factory=Zone, allow_include=False, check_origin=True): - if isinstance(origin, (str, unicode)): + if isinstance(origin, string_types): origin = dns.name.from_text(origin) self.tok = tok self.current_origin = origin @@ -597,9 +606,10 @@ class _MasterReader(object): # Name if self.current_origin is None: raise UnknownOrigin - token = self.tok.get(want_leading = True) + token = self.tok.get(want_leading=True) if not token.is_whitespace(): - self.last_name = dns.name.from_text(token.value, self.current_origin) + self.last_name = dns.name.from_text( + token.value, self.current_origin) else: token = self.tok.get() if token.is_eol_or_eof(): @@ -639,7 +649,8 @@ class _MasterReader(object): try: rdtype = dns.rdatatype.from_text(token.value) except: - raise dns.exception.SyntaxError("unknown rdatatype '%s'" % token.value) + raise dns.exception.SyntaxError( + "unknown rdatatype '%s'" % token.value) n = self.zone.nodes.get(name) if n is None: n = self.zone.node_factory() @@ -658,7 +669,8 @@ class _MasterReader(object): # We convert them to syntax errors so that we can emit # helpful filename:line info. (ty, va) = sys.exc_info()[:2] - raise dns.exception.SyntaxError("caught exception %s: %s" % (str(ty), str(va))) + raise dns.exception.SyntaxError( + "caught exception %s: %s" % (str(ty), str(va))) rd.choose_relativity(self.zone.origin, self.relativize) covers = rd.covers() @@ -760,7 +772,7 @@ class _MasterReader(object): raise dns.exception.SyntaxError except: raise dns.exception.SyntaxError("unknown rdatatype '%s'" % - token.value) + token.value) # lhs (required) try: @@ -768,28 +780,26 @@ class _MasterReader(object): except: raise dns.exception.SyntaxError - lmod, lsign, loffset, lwidth, lbase = self._parse_modify(lhs) rmod, rsign, roffset, rwidth, rbase = self._parse_modify(rhs) for i in range(start, stop + 1, step): # +1 because bind is inclusive and python is exclusive - if lsign == '+': + if lsign == u'+': lindex = i + int(loffset) - elif lsign == '-': + elif lsign == u'-': lindex = i - int(loffset) - if rsign == '-': + if rsign == u'-': rindex = i - int(roffset) - elif rsign == '+': + elif rsign == u'+': rindex = i + int(roffset) lzfindex = str(lindex).zfill(int(lwidth)) rzfindex = str(rindex).zfill(int(rwidth)) - - name = lhs.replace('$%s' % (lmod), lzfindex) - rdata = rhs.replace('$%s' % (rmod), rzfindex) + name = lhs.replace(u'$%s' % (lmod), lzfindex) + rdata = rhs.replace(u'$%s' % (rmod), rzfindex) self.last_name = dns.name.from_text(name, self.current_origin) name = self.last_name @@ -818,7 +828,7 @@ class _MasterReader(object): # helpful filename:line info. (ty, va) = sys.exc_info()[:2] raise dns.exception.SyntaxError("caught exception %s: %s" % - (str(ty), str(va))) + (str(ty), str(va))) rd.choose_relativity(self.zone.origin, self.relativize) covers = rd.covers() @@ -836,7 +846,7 @@ class _MasterReader(object): while 1: token = self.tok.get(True, True) if token.is_eof(): - if not self.current_file is None: + if self.current_file is not None: self.current_file.close() if len(self.saved_state) > 0: (self.tok, @@ -851,29 +861,31 @@ class _MasterReader(object): elif token.is_comment(): self.tok.get_eol() continue - elif token.value[0] == '$': - u = token.value.upper() - if u == '$TTL': + elif token.value[0] == u'$': + c = token.value.upper() + if c == u'$TTL': token = self.tok.get() if not token.is_identifier(): raise dns.exception.SyntaxError("bad $TTL") self.ttl = dns.ttl.from_text(token.value) self.tok.get_eol() - elif u == '$ORIGIN': + elif c == u'$ORIGIN': self.current_origin = self.tok.get_name() self.tok.get_eol() if self.zone.origin is None: self.zone.origin = self.current_origin - elif u == '$INCLUDE' and self.allow_include: + elif c == u'$INCLUDE' and self.allow_include: token = self.tok.get() filename = token.value token = self.tok.get() if token.is_identifier(): - new_origin = dns.name.from_text(token.value, \ - self.current_origin) + new_origin =\ + dns.name.from_text(token.value, + self.current_origin) self.tok.get_eol() elif not token.is_eol_or_eof(): - raise dns.exception.SyntaxError("bad origin in $INCLUDE") + raise dns.exception.SyntaxError( + "bad origin in $INCLUDE") else: new_origin = self.current_origin self.saved_state.append((self.tok, @@ -881,29 +893,32 @@ class _MasterReader(object): self.last_name, self.current_file, self.ttl)) - self.current_file = file(filename, 'r') + self.current_file = open(filename, 'r') self.tok = dns.tokenizer.Tokenizer(self.current_file, filename) self.current_origin = new_origin - elif u == '$GENERATE': + elif c == u'$GENERATE': self._generate_line() else: - raise dns.exception.SyntaxError("Unknown master file directive '" + u + "'") + raise dns.exception.SyntaxError( + "Unknown master file directive '" + c + "'") continue self.tok.unget(token) self._rr_line() - except dns.exception.SyntaxError, detail: + except dns.exception.SyntaxError as detail: (filename, line_number) = self.tok.where() if detail is None: detail = "syntax error" - raise dns.exception.SyntaxError("%s:%d: %s" % (filename, line_number, detail)) + raise dns.exception.SyntaxError( + "%s:%d: %s" % (filename, line_number, detail)) # Now that we're done reading, do some basic checking of the zone. if self.check_origin: self.zone.check_origin() -def from_text(text, origin = None, rdclass = dns.rdataclass.IN, - relativize = True, zone_factory=Zone, filename=None, + +def from_text(text, origin=None, rdclass=dns.rdataclass.IN, + relativize=True, zone_factory=Zone, filename=None, allow_include=False, check_origin=True): """Build a zone object from a master file format string. @@ -945,8 +960,9 @@ def from_text(text, origin = None, rdclass = dns.rdataclass.IN, reader.read() return reader.zone -def from_file(f, origin = None, rdclass = dns.rdataclass.IN, - relativize = True, zone_factory=Zone, filename=None, + +def from_file(f, origin=None, rdclass=dns.rdataclass.IN, + relativize=True, zone_factory=Zone, filename=None, allow_include=True, check_origin=True): """Read a master file and build a zone object. @@ -976,17 +992,13 @@ def from_file(f, origin = None, rdclass = dns.rdataclass.IN, @rtype: dns.zone.Zone object """ - if sys.hexversion >= 0x02030000: - # allow Unicode filenames; turn on universal newline support - str_type = basestring - opts = 'rU' - else: - str_type = str - opts = 'r' + str_type = string_types + opts = 'rU' + if isinstance(f, str_type): if filename is None: filename = f - f = file(f, opts) + f = open(f, opts) want_close = True else: if filename is None: @@ -1001,6 +1013,7 @@ def from_file(f, origin = None, rdclass = dns.rdataclass.IN, f.close() return z + def from_xfr(xfr, zone_factory=Zone, relativize=True, check_origin=True): """Convert the output of a zone transfer generator into a zone object. |
