diff options
-rw-r--r-- | ChangeLog | 14 | ||||
-rw-r--r-- | dns/dnssec.py | 194 | ||||
-rw-r--r-- | dns/query.py | 2 | ||||
-rw-r--r-- | dns/rdatatype.py | 2 | ||||
-rw-r--r-- | dns/rdtypes/ANY/NSEC3.py | 6 | ||||
-rw-r--r-- | dns/rdtypes/ANY/TLSA.py | 89 | ||||
-rw-r--r-- | dns/rdtypes/ANY/__init__.py | 1 | ||||
-rw-r--r-- | dns/resolver.py | 12 | ||||
-rw-r--r-- | tests/bugs.py | 5 | ||||
-rw-r--r-- | tests/dnssec.py | 10 | ||||
-rw-r--r-- | tests/example | 3 | ||||
-rw-r--r-- | tests/example1.good | 3 | ||||
-rw-r--r-- | tests/example2.good | 3 | ||||
-rw-r--r-- | util/copyrights | 1 |
14 files changed, 246 insertions, 99 deletions
@@ -1,3 +1,17 @@ +2012-09-25 Sean Leach + + * added set_flags() method to dns.resolver.Resolver + +2012-09-25 Pieter Lexis + + * added support for TLSA RR + +2012-08-28 Bob Halley <halley@dnspython.org> + + * dns/rdtypes/ANY/NSEC3.py (NSEC3.from_text): The NSEC3 from_text() + method could erroneously emit empty bitmap windows (i.e. windows + with a count of 0 bytes); such bitmaps are illegal. + 2012-04-08 Bob Halley <halley@dnspython.org> * (Version 1.10.0 released) diff --git a/dns/dnssec.py b/dns/dnssec.py index dd6a27a..4f6fc98 100644 --- a/dns/dnssec.py +++ b/dns/dnssec.py @@ -126,7 +126,8 @@ def make_ds(name, key, algorithm, origin=None): return dns.rdata.from_wire(dns.rdataclass.IN, dns.rdatatype.DS, dsrdata, 0, len(dsrdata)) -def _find_key(keys, rrsig): +def _find_candidate_keys(keys, rrsig): + candidate_keys=[] value = keys.get(rrsig.signer) if value is None: return None @@ -141,8 +142,8 @@ def _find_key(keys, rrsig): for rdata in rdataset: if rdata.algorithm == rrsig.algorithm and \ key_id(rdata) == rrsig.key_tag: - return rdata - return None + candidate_keys.append(rdata) + return candidate_keys def _is_rsa(algorithm): return algorithm in (RSAMD5, RSASHA1, @@ -217,100 +218,101 @@ def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None): if isinstance(origin, (str, unicode)): origin = dns.name.from_text(origin, dns.name.root) - key = _find_key(keys, rrsig) - if not key: - raise ValidationFailure, 'unknown key' - - # For convenience, allow the rrset to be specified as a (name, rdataset) - # tuple as well as a proper rrset - if isinstance(rrset, tuple): - rrname = rrset[0] - rdataset = rrset[1] - else: - rrname = rrset.name - rdataset = rrset - - if now is None: - now = time.time() - if rrsig.expiration < now: - raise ValidationFailure, 'expired' - if rrsig.inception > now: - raise ValidationFailure, 'not yet valid' - - hash = _make_hash(rrsig.algorithm) - - if _is_rsa(rrsig.algorithm): - keyptr = key.key - (bytes,) = struct.unpack('!B', keyptr[0:1]) - keyptr = keyptr[1:] - if bytes == 0: - (bytes,) = struct.unpack('!H', keyptr[0:2]) - 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),) - elif _is_dsa(rrsig.algorithm): - keyptr = key.key - (t,) = struct.unpack('!B', keyptr[0:1]) - keyptr = keyptr[1:] - octets = 64 + t * 8 - dsa_q = keyptr[0:20] - keyptr = keyptr[20:] - dsa_p = keyptr[0:octets] - keyptr = keyptr[octets:] - dsa_g = keyptr[0:octets] - keyptr = keyptr[octets:] - dsa_y = keyptr[0:octets] - pubkey = Crypto.PublicKey.DSA.construct( - (Crypto.Util.number.bytes_to_long(dsa_y), - 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)) - else: - raise ValidationFailure, 'unknown algorithm %u' % rrsig.algorithm - - hash.update(_to_rdata(rrsig, origin)[:18]) - hash.update(rrsig.signer.to_digestable(origin)) - - if rrsig.labels < len(rrname) - 1: - suffix = rrname.split(rrsig.labels + 1)[1] - rrname = dns.name.from_text('*', suffix) - rrnamebuf = rrname.to_digestable(origin) - rrfixed = struct.pack('!HHI', rdataset.rdtype, rdataset.rdclass, - rrsig.original_ttl) - rrlist = sorted(rdataset); - for rr in rrlist: - hash.update(rrnamebuf) - hash.update(rrfixed) - rrdata = rr.to_digestable(origin) - rrlen = struct.pack('!H', len(rrdata)) - 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 = chr(0) + chr(1) + chr(0xFF) * padlen + chr(0) + digest - elif _is_dsa(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 + for candidate_key in _find_candidate_keys(keys, rrsig): + if not candidate_key: + raise ValidationFailure, 'unknown key' + + # For convenience, allow the rrset to be specified as a (name, rdataset) + # tuple as well as a proper rrset + if isinstance(rrset, tuple): + rrname = rrset[0] + rdataset = rrset[1] + else: + rrname = rrset.name + rdataset = rrset + + if now is None: + now = time.time() + if rrsig.expiration < now: + raise ValidationFailure, 'expired' + if rrsig.inception > now: + raise ValidationFailure, 'not yet valid' + + hash = _make_hash(rrsig.algorithm) + + if _is_rsa(rrsig.algorithm): + keyptr = candidate_key.key + (bytes,) = struct.unpack('!B', keyptr[0:1]) + keyptr = keyptr[1:] + if bytes == 0: + (bytes,) = struct.unpack('!H', keyptr[0:2]) + 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),) + elif _is_dsa(rrsig.algorithm): + keyptr = candidate_key.key + (t,) = struct.unpack('!B', keyptr[0:1]) + keyptr = keyptr[1:] + octets = 64 + t * 8 + dsa_q = keyptr[0:20] + keyptr = keyptr[20:] + dsa_p = keyptr[0:octets] + keyptr = keyptr[octets:] + dsa_g = keyptr[0:octets] + keyptr = keyptr[octets:] + dsa_y = keyptr[0:octets] + pubkey = Crypto.PublicKey.DSA.construct( + (Crypto.Util.number.bytes_to_long(dsa_y), + 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)) + else: + raise ValidationFailure, 'unknown algorithm %u' % rrsig.algorithm + + hash.update(_to_rdata(rrsig, origin)[:18]) + hash.update(rrsig.signer.to_digestable(origin)) + + if rrsig.labels < len(rrname) - 1: + suffix = rrname.split(rrsig.labels + 1)[1] + rrname = dns.name.from_text('*', suffix) + rrnamebuf = rrname.to_digestable(origin) + rrfixed = struct.pack('!HHI', rdataset.rdtype, rdataset.rdclass, + rrsig.original_ttl) + rrlist = sorted(rdataset); + for rr in rrlist: + hash.update(rrnamebuf) + hash.update(rrfixed) + rrdata = rr.to_digestable(origin) + rrlen = struct.pack('!H', len(rrdata)) + 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 = chr(0) + chr(1) + chr(0xFF) * padlen + chr(0) + digest + elif _is_dsa(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 not pubkey.verify(digest, sig): - raise ValidationFailure, 'verify failure' + if pubkey.verify(digest, sig): + return + raise ValidationFailure, 'verify failure' def _validate(rrset, rrsigset, keys, origin=None, now=None): """Validate an RRset diff --git a/dns/query.py b/dns/query.py index c141612..7a1b1f4 100644 --- a/dns/query.py +++ b/dns/query.py @@ -339,7 +339,7 @@ def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN, dns.rdatatype.AXFR. @type rdtype: int or string @param rdclass: The class of the zone transfer. The default is - dns.rdatatype.IN. + dns.rdataclass.IN. @type rdclass: int or string @param timeout: The number of seconds to wait for each response message. If None, the default, wait forever. diff --git a/dns/rdatatype.py b/dns/rdatatype.py index 380cfcd..f64307a 100644 --- a/dns/rdatatype.py +++ b/dns/rdatatype.py @@ -78,6 +78,7 @@ DNSKEY = 48 DHCID = 49 NSEC3 = 50 NSEC3PARAM = 51 +TLSA = 52 HIP = 55 SPF = 99 UNSPEC = 103 @@ -140,6 +141,7 @@ _by_text = { 'DHCID' : DHCID, 'NSEC3' : NSEC3, 'NSEC3PARAM' : NSEC3PARAM, + 'TLSA' : TLSA, 'HIP' : HIP, 'SPF' : SPF, 'UNSPEC' : UNSPEC, diff --git a/dns/rdtypes/ANY/NSEC3.py b/dns/rdtypes/ANY/NSEC3.py index c7ac737..b42fe4c 100644 --- a/dns/rdtypes/ANY/NSEC3.py +++ b/dns/rdtypes/ANY/NSEC3.py @@ -114,7 +114,8 @@ class NSEC3(dns.rdata.Rdata): prior_rdtype = nrdtype new_window = nrdtype // 256 if new_window != window: - windows.append((window, ''.join(bitmap[0:octets]))) + if octets != 0: + windows.append((window, ''.join(bitmap[0:octets]))) bitmap = ['\0'] * 32 window = new_window offset = nrdtype % 256 @@ -122,7 +123,8 @@ class NSEC3(dns.rdata.Rdata): bit = offset % 8 octets = byte + 1 bitmap[byte] = chr(ord(bitmap[byte]) | (0x80 >> bit)) - windows.append((window, ''.join(bitmap[0:octets]))) + if octets != 0: + windows.append((window, ''.join(bitmap[0:octets]))) return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, windows) from_text = classmethod(from_text) diff --git a/dns/rdtypes/ANY/TLSA.py b/dns/rdtypes/ANY/TLSA.py new file mode 100644 index 0000000..6ca8c0a --- /dev/null +++ b/dns/rdtypes/ANY/TLSA.py @@ -0,0 +1,89 @@ +# Copyright (C) 2005-2007, 2009-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. + +import struct + +import dns.rdata +import dns.rdatatype + +class TLSA(dns.rdata.Rdata): + """TLSA record + + @ivar usage: The certificate usage + @type usage: int + @ivar selector: The selector field + @type selector: int + @ivar mtype: The 'matching type' field + @type mtype: int + @ivar cert: The 'Certificate Association Data' field + @type cert: string + @see: RFC 6698""" + + __slots__ = ['usage', 'selector', 'mtype', 'cert'] + + def __init__(self, rdclass, rdtype, usage, selector, + mtype, cert): + super(TLSA, self).__init__(rdclass, rdtype) + self.usage = usage + self.selector = selector + self.mtype = mtype + self.cert = cert + + def to_text(self, origin=None, relativize=True, **kw): + return '%d %d %d %s' % (self.usage, + self.selector, + self.mtype, + dns.rdata._hexify(self.cert, + chunksize=128)) + + def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): + usage = tok.get_uint8() + selector = tok.get_uint8() + mtype = tok.get_uint8() + cert_chunks = [] + while 1: + t = tok.get().unescape() + if t.is_eol_or_eof(): + break + if not t.is_identifier(): + raise dns.exception.SyntaxError + cert_chunks.append(t.value) + cert = ''.join(cert_chunks) + cert = cert.decode('hex_codec') + return cls(rdclass, rdtype, usage, selector, mtype, cert) + + from_text = classmethod(from_text) + + def to_wire(self, file, compress = None, origin = None): + header = struct.pack("!BBB", self.usage, self.selector, self.mtype) + file.write(header) + file.write(self.cert) + + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): + header = struct.unpack("!BBB", wire[current : current + 3]) + current += 3 + rdlen -= 3 + cert = wire[current : current + rdlen].unwrap() + return cls(rdclass, rdtype, header[0], header[1], header[2], cert) + + from_wire = classmethod(from_wire) + + def _cmp(self, other): + hs = struct.pack("!BBB", self.usage, self.selector, self.mtype) + ho = struct.pack("!BBB", other.usage, other.selector, other.mtype) + v = cmp(hs, ho) + if v == 0: + v = cmp(self.cert, other.cert) + return v diff --git a/dns/rdtypes/ANY/__init__.py b/dns/rdtypes/ANY/__init__.py index 721e9dd..cfb0be6 100644 --- a/dns/rdtypes/ANY/__init__.py +++ b/dns/rdtypes/ANY/__init__.py @@ -33,6 +33,7 @@ __all__ = [ 'NSEC', 'NSEC3', 'NSEC3PARAM', + 'TLSA', 'PTR', 'RP', 'RRSIG', diff --git a/dns/resolver.py b/dns/resolver.py index ce77ac1..1b344a7 100644 --- a/dns/resolver.py +++ b/dns/resolver.py @@ -425,6 +425,8 @@ class Resolver(object): @type ednsflags: int @ivar payload: The EDNS payload size. The default is 0. @type payload: int + @ivar flags: The message flags to use. The default is None (i.e. not overwritten) + @type flags: int @ivar cache: The cache to use. The default is None. @type cache: dns.resolver.Cache object """ @@ -466,6 +468,7 @@ class Resolver(object): self.ednsflags = 0 self.payload = 0 self.cache = None + self.flags = None def read_resolv_conf(self, f): """Process f as a file in the /etc/resolv.conf format. If f is @@ -761,6 +764,8 @@ class Resolver(object): request.use_tsig(self.keyring, self.keyname, algorithm=self.keyalgorithm) request.use_edns(self.edns, self.ednsflags, self.payload) + if self.flags is not None: + request.flags = self.flags response = None # # make a copy of the servers list so we can alter it later. @@ -898,6 +903,13 @@ class Resolver(object): self.ednsflags = ednsflags self.payload = payload + def set_flags(self, flags): + """Overrides the default flags with your own + + @param flags: The flags to overwrite the default with + @type flags: int""" + self.flags = flags + default_resolver = None def get_default_resolver(): diff --git a/tests/bugs.py b/tests/bugs.py index c2fa6b6..312ec3e 100644 --- a/tests/bugs.py +++ b/tests/bugs.py @@ -40,5 +40,10 @@ class BugsTestCase(unittest.TestCase): ttl = dns.ttl.from_text("2147483648") self.failUnlessRaises(dns.ttl.BadTTL, bad) + def test_empty_NSEC3_window(self): + rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NSEC3, + "1 0 100 ABCD SCBCQHKU35969L2A68P3AD59LHF30715") + self.failUnless(rdata.windows == []) + if __name__ == '__main__': unittest.main() diff --git a/tests/dnssec.py b/tests/dnssec.py index 7b4546a..b997b17 100644 --- a/tests/dnssec.py +++ b/tests/dnssec.py @@ -30,6 +30,13 @@ abs_keys = { abs_dnspython_org : '256 3 5 AwEAAdSSghOGjU33IQZgwZM2Hh771VGXX05olJK49FxpSyuEAjDBXY58 LGU9R2Zgeecnk/b9EAhFu/vCV9oECtiTCvwuVAkt9YEweqYDluQInmgP NGMJCKdSLlnX93DkjDw8rMYv5dqXCuSGPlKChfTJOLQxIAxGloS7lL+c 0CTZydAF') } +abs_keys_duplicate_keytag = { abs_dnspython_org : + dns.rrset.from_text('dnspython.org.', 3600, 'IN', 'DNSKEY', + '257 3 5 AwEAAenVTr9L1OMlL1/N2ta0Qj9LLLnnmFWIr1dJoAsWM9BQfsbV7kFZ XbAkER/FY9Ji2o7cELxBwAsVBuWn6IUUAJXLH74YbC1anY0lifjgt29z SwDzuB7zmC7yVYZzUunBulVW4zT0tg1aePbpVL2EtTL8VzREqbJbE25R KuQYHZtFwG8S4iBxJUmT2Bbd0921LLxSQgVoFXlQx/gFV2+UERXcJ5ce iX6A6wc02M/pdg/YbJd2rBa0MYL3/Fz/Xltre0tqsImZGxzi6YtYDs45 NC8gH+44egz82e2DATCVM1ICPmRDjXYTLldQiWA2ZXIWnK0iitl5ue24 7EsWJefrIhE=', + '256 3 5 AwEAAdSSg++++THIS/IS/NOT/THE/CORRECT/KEY++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ AaOSydAF', + '256 3 5 AwEAAdSSghOGjU33IQZgwZM2Hh771VGXX05olJK49FxpSyuEAjDBXY58 LGU9R2Zgeecnk/b9EAhFu/vCV9oECtiTCvwuVAkt9YEweqYDluQInmgP NGMJCKdSLlnX93DkjDw8rMYv5dqXCuSGPlKChfTJOLQxIAxGloS7lL+c 0CTZydAF') + } + rel_keys = { dns.name.empty : dns.rrset.from_text('@', 3600, 'IN', 'DNSKEY', '257 3 5 AwEAAenVTr9L1OMlL1/N2ta0Qj9LLLnnmFWIr1dJoAsWM9BQfsbV7kFZ XbAkER/FY9Ji2o7cELxBwAsVBuWn6IUUAJXLH74YbC1anY0lifjgt29z SwDzuB7zmC7yVYZzUunBulVW4zT0tg1aePbpVL2EtTL8VzREqbJbE25R KuQYHZtFwG8S4iBxJUmT2Bbd0921LLxSQgVoFXlQx/gFV2+UERXcJ5ce iX6A6wc02M/pdg/YbJd2rBa0MYL3/Fz/Xltre0tqsImZGxzi6YtYDs45 NC8gH+44egz82e2DATCVM1ICPmRDjXYTLldQiWA2ZXIWnK0iitl5ue24 7EsWJefrIhE=', @@ -95,6 +102,9 @@ class DNSSECValidatorTestCase(unittest.TestCase): def testAbsoluteRSAGood(self): dns.dnssec.validate(abs_soa, abs_soa_rrsig, abs_keys, None, when) + def testDuplicateKeytag(self): + dns.dnssec.validate(abs_soa, abs_soa_rrsig, abs_keys_duplicate_keytag, None, when) + def testAbsoluteRSABad(self): def bad(): dns.dnssec.validate(abs_other_soa, abs_soa_rrsig, abs_keys, None, diff --git a/tests/example b/tests/example index 2f753a2..37a90a2 100644 --- a/tests/example +++ b/tests/example @@ -165,6 +165,9 @@ srv02 SRV 65535 65535 65535 old-slow-box.example.com. $TTL 301 ; 5 minutes 1 second t A 73.80.65.49 $TTL 3600 ; 1 hour +tlsa1 TLSA 3 1 1 a9cdf989b504fe5dca90c0d2167b6550570734f7c763e09fdf88904e06157065 +tlsa2 TLSA 1 0 1 efddf0d915c7bdc5782c0881e1b2a95ad099fbdd06d7b1f77982d9364338d955 +tlsa3 TLSA 1 0 2 81ee7f6c0ecc6b09b7785a9418f54432de630dd54dc6ee9e3c49de547708d236d4c413c3e97e44f969e635958aa410495844127c04883503e5b024cf7a8f6a94 txt01 TXT "foo" txt02 TXT "foo" "bar" txt03 TXT "foo" diff --git a/tests/example1.good b/tests/example1.good index 0834d17..100842e 100644 --- a/tests/example1.good +++ b/tests/example1.good @@ -90,6 +90,9 @@ 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" diff --git a/tests/example2.good b/tests/example2.good index de4bcd5..ac3261c 100644 --- a/tests/example2.good +++ b/tests/example2.good @@ -90,6 +90,9 @@ srv01.example. 3600 IN SRV 0 0 0 . srv02.example. 3600 IN SRV 65535 65535 65535 old-slow-box.example.com. sshfp1.example. 3600 IN SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab t.example. 301 IN A 73.80.65.49 +tlsa1.example. 3600 IN TLSA 3 1 1 a9cdf989b504fe5dca90c0d2167b6550570734f7c763e09fdf88904e06157065 +tlsa2.example. 3600 IN TLSA 1 0 1 efddf0d915c7bdc5782c0881e1b2a95ad099fbdd06d7b1f77982d9364338d955 +tlsa3.example. 3600 IN TLSA 1 0 2 81ee7f6c0ecc6b09b7785a9418f54432de630dd54dc6ee9e3c49de547708d236d4c413c3e97e44f969e635958aa410495844127c04883503e5b024cf7a8f6a94 txt01.example. 3600 IN TXT "foo" txt02.example. 3600 IN TXT "foo" "bar" txt03.example. 3600 IN TXT "foo" diff --git a/util/copyrights b/util/copyrights index f73e9f6..2e044f3 100644 --- a/util/copyrights +++ b/util/copyrights @@ -51,6 +51,7 @@ ./dns/rdtypes/ANY/SOA.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 ./dns/rdtypes/ANY/SPF.py PYTHON 2006,2007,2009,2010,2011 ./dns/rdtypes/ANY/SSHFP.py PYTHON 2005,2006,2007,2009,2010,2011 +./dns/rdtypes/ANY/TLSA.py PYTHON 2012 ./dns/rdtypes/ANY/TXT.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 ./dns/rdtypes/ANY/X25.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 ./dns/rdtypes/ANY/__init__.py PYTHON 2003,2004,2005,2006,2007,2009,2010,2011 |