Bump dnspython from 2.2.1 to 2.3.0 (#1975)

* Bump dnspython from 2.2.1 to 2.3.0

Bumps [dnspython](https://github.com/rthalley/dnspython) from 2.2.1 to 2.3.0.
- [Release notes](https://github.com/rthalley/dnspython/releases)
- [Changelog](https://github.com/rthalley/dnspython/blob/master/doc/whatsnew.rst)
- [Commits](https://github.com/rthalley/dnspython/compare/v2.2.1...v2.3.0)

---
updated-dependencies:
- dependency-name: dnspython
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update dnspython==2.3.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
This commit is contained in:
dependabot[bot] 2023-03-02 20:54:32 -08:00 committed by GitHub
parent 6910079330
commit 32c06a8b72
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
137 changed files with 7699 additions and 4277 deletions

View file

@ -17,6 +17,8 @@
"""DNS Zones."""
from typing import Any, Iterable, List, Optional, Set, Tuple, Union
import re
import sys
@ -49,29 +51,60 @@ def _check_cname_and_other_data(txn, name, rdataset):
# empty nodes are neutral.
return
node_kind = node.classify()
if node_kind == dns.node.NodeKind.CNAME and \
rdataset_kind == dns.node.NodeKind.REGULAR:
raise CNAMEAndOtherData('rdataset type is not compatible with a '
'CNAME node')
elif node_kind == dns.node.NodeKind.REGULAR and \
rdataset_kind == dns.node.NodeKind.CNAME:
raise CNAMEAndOtherData('CNAME rdataset is not compatible with a '
'regular data node')
if (
node_kind == dns.node.NodeKind.CNAME
and rdataset_kind == dns.node.NodeKind.REGULAR
):
raise CNAMEAndOtherData("rdataset type is not compatible with a CNAME node")
elif (
node_kind == dns.node.NodeKind.REGULAR
and rdataset_kind == dns.node.NodeKind.CNAME
):
raise CNAMEAndOtherData(
"CNAME rdataset is not compatible with a regular data node"
)
# Otherwise at least one of the node and the rdataset is neutral, so
# adding the rdataset is ok
SavedStateType = Tuple[
dns.tokenizer.Tokenizer,
Optional[dns.name.Name], # current_origin
Optional[dns.name.Name], # last_name
Optional[Any], # current_file
int, # last_ttl
bool, # last_ttl_known
int, # default_ttl
bool,
] # default_ttl_known
def _upper_dollarize(s):
s = s.upper()
if not s.startswith("$"):
s = "$" + s
return s
class Reader:
"""Read a DNS zone file into a transaction."""
def __init__(self, tok, rdclass, txn, allow_include=False,
allow_directives=True, force_name=None,
force_ttl=None, force_rdclass=None, force_rdtype=None,
default_ttl=None):
def __init__(
self,
tok: dns.tokenizer.Tokenizer,
rdclass: dns.rdataclass.RdataClass,
txn: dns.transaction.Transaction,
allow_include: bool = False,
allow_directives: Union[bool, Iterable[str]] = True,
force_name: Optional[dns.name.Name] = None,
force_ttl: Optional[int] = None,
force_rdclass: Optional[dns.rdataclass.RdataClass] = None,
force_rdtype: Optional[dns.rdatatype.RdataType] = None,
default_ttl: Optional[int] = None,
):
self.tok = tok
(self.zone_origin, self.relativize, _) = \
txn.manager.origin_information()
(self.zone_origin, self.relativize, _) = txn.manager.origin_information()
self.current_origin = self.zone_origin
self.last_ttl = 0
self.last_ttl_known = False
@ -86,10 +119,21 @@ class Reader:
self.last_name = self.current_origin
self.zone_rdclass = rdclass
self.txn = txn
self.saved_state = []
self.current_file = None
self.allow_include = allow_include
self.allow_directives = allow_directives
self.saved_state: List[SavedStateType] = []
self.current_file: Optional[Any] = None
self.allowed_directives: Set[str]
if allow_directives is True:
self.allowed_directives = {"$GENERATE", "$ORIGIN", "$TTL"}
if allow_include:
self.allowed_directives.add("$INCLUDE")
elif allow_directives is False:
# allow_include was ignored in earlier releases if allow_directives was
# False, so we continue that.
self.allowed_directives = set()
else:
# Note that if directives are explicitly specified, then allow_include
# is ignored.
self.allowed_directives = set(_upper_dollarize(d) for d in allow_directives)
self.force_name = force_name
self.force_ttl = force_ttl
self.force_rdclass = force_rdclass
@ -176,13 +220,17 @@ class Reader:
try:
rdtype = dns.rdatatype.from_text(token.value)
except Exception:
raise dns.exception.SyntaxError(
"unknown rdatatype '%s'" % token.value)
raise dns.exception.SyntaxError("unknown rdatatype '%s'" % token.value)
try:
rd = dns.rdata.from_text(rdclass, rdtype, self.tok,
self.current_origin, self.relativize,
self.zone_origin)
rd = dns.rdata.from_text(
rdclass,
rdtype,
self.tok,
self.current_origin,
self.relativize,
self.zone_origin,
)
except dns.exception.SyntaxError:
# Catch and reraise.
raise
@ -194,7 +242,8 @@ class Reader:
# helpful filename:line info.
(ty, va) = sys.exc_info()[:2]
raise dns.exception.SyntaxError(
"caught exception {}: {}".format(str(ty), str(va)))
"caught exception {}: {}".format(str(ty), str(va))
)
if not self.default_ttl_known and rdtype == dns.rdatatype.SOA:
# The pre-RFC2308 and pre-BIND9 behavior inherits the zone default
@ -213,7 +262,7 @@ class Reader:
self.txn.add(name, ttl, rd)
def _parse_modify(self, side):
def _parse_modify(self, side: str) -> Tuple[str, str, int, int, str]:
# Here we catch everything in '{' '}' in a group so we can replace it
# with ''.
is_generate1 = re.compile(r"^.*\$({(\+|-?)(\d+),(\d+),(.)}).*$")
@ -225,31 +274,36 @@ class Reader:
g1 = is_generate1.match(side)
if g1:
mod, sign, offset, width, base = g1.groups()
if sign == '':
sign = '+'
if sign == "":
sign = "+"
g2 = is_generate2.match(side)
if g2:
mod, sign, offset = g2.groups()
if sign == '':
sign = '+'
if sign == "":
sign = "+"
width = 0
base = 'd'
base = "d"
g3 = is_generate3.match(side)
if g3:
mod, sign, offset, width = g3.groups()
if sign == '':
sign = '+'
base = 'd'
if sign == "":
sign = "+"
base = "d"
if not (g1 or g2 or g3):
mod = ''
sign = '+'
mod = ""
sign = "+"
offset = 0
width = 0
base = 'd'
base = "d"
if base != 'd':
raise NotImplementedError()
offset = int(offset)
width = int(width)
if sign not in ["+", "-"]:
raise dns.exception.SyntaxError("invalid offset sign %s" % sign)
if base not in ["d", "o", "x", "X", "n", "N"]:
raise dns.exception.SyntaxError("invalid type %s" % base)
return mod, sign, offset, width, base
@ -313,37 +367,47 @@ class Reader:
if not token.is_identifier():
raise dns.exception.SyntaxError
except Exception:
raise dns.exception.SyntaxError("unknown rdatatype '%s'" %
token.value)
raise dns.exception.SyntaxError("unknown rdatatype '%s'" % token.value)
# rhs (required)
rhs = token.value
# The code currently only supports base 'd', so the last value
# in the tuple _parse_modify returns is ignored
lmod, lsign, loffset, lwidth, _ = self._parse_modify(lhs)
rmod, rsign, roffset, rwidth, _ = self._parse_modify(rhs)
def _calculate_index(counter: int, offset_sign: str, offset: int) -> int:
"""Calculate the index from the counter and offset."""
if offset_sign == "-":
offset *= -1
return counter + offset
def _format_index(index: int, base: str, width: int) -> str:
"""Format the index with the given base, and zero-fill it
to the given width."""
if base in ["d", "o", "x", "X"]:
return format(index, base).zfill(width)
# base can only be n or N here
hexa = _format_index(index, "x", width)
nibbles = ".".join(hexa[::-1])[:width]
if base == "N":
nibbles = nibbles.upper()
return nibbles
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 == '+':
lindex = i + int(loffset)
elif lsign == '-':
lindex = i - int(loffset)
lindex = _calculate_index(i, lsign, loffset)
rindex = _calculate_index(i, rsign, roffset)
if rsign == '-':
rindex = i - int(roffset)
elif rsign == '+':
rindex = i + int(roffset)
lzfindex = _format_index(lindex, lbase, lwidth)
rzfindex = _format_index(rindex, rbase, rwidth)
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('$%s' % (lmod), lzfindex)
rdata = rhs.replace('$%s' % (rmod), rzfindex)
self.last_name = dns.name.from_text(name, self.current_origin,
self.tok.idna_codec)
self.last_name = dns.name.from_text(
name, self.current_origin, self.tok.idna_codec
)
name = self.last_name
if not name.is_subdomain(self.zone_origin):
self._eat_line()
@ -352,9 +416,14 @@ class Reader:
name = name.relativize(self.zone_origin)
try:
rd = dns.rdata.from_text(rdclass, rdtype, rdata,
self.current_origin, self.relativize,
self.zone_origin)
rd = dns.rdata.from_text(
rdclass,
rdtype,
rdata,
self.current_origin,
self.relativize,
self.zone_origin,
)
except dns.exception.SyntaxError:
# Catch and reraise.
raise
@ -365,12 +434,13 @@ class Reader:
# 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))
)
self.txn.add(name, ttl, rd)
def read(self):
def read(self) -> None:
"""Read a DNS zone file and build a zone object.
@raises dns.zone.NoSOA: No SOA RR was found at the zone origin
@ -384,14 +454,16 @@ class Reader:
if self.current_file is not None:
self.current_file.close()
if len(self.saved_state) > 0:
(self.tok,
self.current_origin,
self.last_name,
self.current_file,
self.last_ttl,
self.last_ttl_known,
self.default_ttl,
self.default_ttl_known) = self.saved_state.pop(-1)
(
self.tok,
self.current_origin,
self.last_name,
self.current_file,
self.last_ttl,
self.last_ttl_known,
self.default_ttl,
self.default_ttl_known,
) = self.saved_state.pop(-1)
continue
break
elif token.is_eol():
@ -399,53 +471,62 @@ class Reader:
elif token.is_comment():
self.tok.get_eol()
continue
elif token.value[0] == '$' and self.allow_directives:
elif token.value[0] == "$" and len(self.allowed_directives) > 0:
# Note that we only run directive processing code if at least
# one directive is allowed in order to be backwards compatible
c = token.value.upper()
if c == '$TTL':
if c not in self.allowed_directives:
raise dns.exception.SyntaxError(
f"zone file directive '{c}' is not allowed"
)
if c == "$TTL":
token = self.tok.get()
if not token.is_identifier():
raise dns.exception.SyntaxError("bad $TTL")
self.default_ttl = dns.ttl.from_text(token.value)
self.default_ttl_known = True
self.tok.get_eol()
elif c == '$ORIGIN':
elif c == "$ORIGIN":
self.current_origin = self.tok.get_name()
self.tok.get_eol()
if self.zone_origin is None:
self.zone_origin = self.current_origin
self.txn._set_origin(self.current_origin)
elif c == '$INCLUDE' and self.allow_include:
elif c == "$INCLUDE":
token = self.tok.get()
filename = token.value
token = self.tok.get()
new_origin: Optional[dns.name.Name]
if token.is_identifier():
new_origin =\
dns.name.from_text(token.value,
self.current_origin,
self.tok.idna_codec)
new_origin = dns.name.from_text(
token.value, self.current_origin, self.tok.idna_codec
)
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,
self.current_origin,
self.last_name,
self.current_file,
self.last_ttl,
self.last_ttl_known,
self.default_ttl,
self.default_ttl_known))
self.current_file = open(filename, 'r')
self.tok = dns.tokenizer.Tokenizer(self.current_file,
filename)
self.saved_state.append(
(
self.tok,
self.current_origin,
self.last_name,
self.current_file,
self.last_ttl,
self.last_ttl_known,
self.default_ttl,
self.default_ttl_known,
)
)
self.current_file = open(filename, "r")
self.tok = dns.tokenizer.Tokenizer(self.current_file, filename)
self.current_origin = new_origin
elif c == '$GENERATE':
elif c == "$GENERATE":
self._generate_line()
else:
raise dns.exception.SyntaxError(
"Unknown zone file directive '" + c + "'")
f"Unknown zone file directive '{c}'"
)
continue
self.tok.unget(token)
self._rr_line()
@ -454,13 +535,13 @@ class Reader:
if detail is None:
detail = "syntax error"
ex = dns.exception.SyntaxError(
"%s:%d: %s" % (filename, line_number, detail))
"%s:%d: %s" % (filename, line_number, detail)
)
tb = sys.exc_info()[2]
raise ex.with_traceback(tb) from None
class RRsetsReaderTransaction(dns.transaction.Transaction):
def __init__(self, manager, replacement, read_only):
assert not read_only
super().__init__(manager, replacement, read_only)
@ -512,8 +593,9 @@ class RRsetsReaderTransaction(dns.transaction.Transaction):
if commit and self._changed():
rrsets = []
for (name, _, _), rdataset in self.rdatasets.items():
rrset = dns.rrset.RRset(name, rdataset.rdclass, rdataset.rdtype,
rdataset.covers)
rrset = dns.rrset.RRset(
name, rdataset.rdclass, rdataset.rdtype, rdataset.covers
)
rrset.update(rdataset)
rrsets.append(rrset)
self.manager.set_rrsets(rrsets)
@ -521,15 +603,22 @@ class RRsetsReaderTransaction(dns.transaction.Transaction):
def _set_origin(self, origin):
pass
def _iterate_rdatasets(self):
raise NotImplementedError # pragma: no cover
class RRSetsReaderManager(dns.transaction.TransactionManager):
def __init__(self, origin=dns.name.root, relativize=False,
rdclass=dns.rdataclass.IN):
def __init__(
self, origin=dns.name.root, relativize=False, rdclass=dns.rdataclass.IN
):
self.origin = origin
self.relativize = relativize
self.rdclass = rdclass
self.rrsets = []
def reader(self): # pragma: no cover
raise NotImplementedError
def writer(self, replacement=False):
assert replacement is True
return RRsetsReaderTransaction(self, True, False)
@ -548,10 +637,18 @@ class RRSetsReaderManager(dns.transaction.TransactionManager):
self.rrsets = rrsets
def read_rrsets(text, name=None, ttl=None, rdclass=dns.rdataclass.IN,
default_rdclass=dns.rdataclass.IN,
rdtype=None, default_ttl=None, idna_codec=None,
origin=dns.name.root, relativize=False):
def read_rrsets(
text: Any,
name: Optional[Union[dns.name.Name, str]] = None,
ttl: Optional[int] = None,
rdclass: Optional[Union[dns.rdataclass.RdataClass, str]] = dns.rdataclass.IN,
default_rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN,
rdtype: Optional[Union[dns.rdatatype.RdataType, str]] = None,
default_ttl: Optional[Union[int, str]] = None,
idna_codec: Optional[dns.name.IDNACodec] = None,
origin: Optional[Union[dns.name.Name, str]] = dns.name.root,
relativize: bool = False,
) -> List[dns.rrset.RRset]:
"""Read one or more rrsets from the specified text, possibly subject
to restrictions.
@ -610,15 +707,27 @@ def read_rrsets(text, name=None, ttl=None, rdclass=dns.rdataclass.IN,
if isinstance(default_ttl, str):
default_ttl = dns.ttl.from_text(default_ttl)
if rdclass is not None:
rdclass = dns.rdataclass.RdataClass.make(rdclass)
default_rdclass = dns.rdataclass.RdataClass.make(default_rdclass)
the_rdclass = dns.rdataclass.RdataClass.make(rdclass)
else:
the_rdclass = None
the_default_rdclass = dns.rdataclass.RdataClass.make(default_rdclass)
if rdtype is not None:
rdtype = dns.rdatatype.RdataType.make(rdtype)
the_rdtype = dns.rdatatype.RdataType.make(rdtype)
else:
the_rdtype = None
manager = RRSetsReaderManager(origin, relativize, default_rdclass)
with manager.writer(True) as txn:
tok = dns.tokenizer.Tokenizer(text, '<input>', idna_codec=idna_codec)
reader = Reader(tok, default_rdclass, txn, allow_directives=False,
force_name=name, force_ttl=ttl, force_rdclass=rdclass,
force_rdtype=rdtype, default_ttl=default_ttl)
tok = dns.tokenizer.Tokenizer(text, "<input>", idna_codec=idna_codec)
reader = Reader(
tok,
the_default_rdclass,
txn,
allow_directives=False,
force_name=name,
force_ttl=ttl,
force_rdclass=the_rdclass,
force_rdtype=the_rdtype,
default_ttl=default_ttl,
)
reader.read()
return manager.rrsets