1#
2# This file is a part of DNSViz, a tool suite for DNS/DNSSEC monitoring,
3# analysis, and visualization.
4# Created by Casey Deccio (casey@deccio.net)
5#
6# Copyright 2015-2016 VeriSign, Inc.
7#
8# Copyright 2016-2021 Casey Deccio
9#
10# DNSViz is free software; you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation; either version 2 of the License, or
13# (at your option) any later version.
14#
15# DNSViz is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License along
21# with DNSViz.  If not, see <http://www.gnu.org/licenses/>.
22#
23
24from __future__ import unicode_literals
25
26import datetime
27
28# minimal support for python2.6
29try:
30    from collections import OrderedDict
31except ImportError:
32    from ordereddict import OrderedDict
33
34# python3/python2 dual compatibility
35try:
36    from html import escape
37except ImportError:
38    from cgi import escape
39
40import dns.dnssec
41
42import dnsviz.format as fmt
43from dnsviz.util import tuple_to_dict
44
45class DomainNameAnalysisError(object):
46    _abstract = True
47    code = None
48    description_template = '%(code)s'
49    terse_description_template = '%(code)s'
50    references = []
51    required_params = []
52    use_effective_query_tag = True
53
54    def __init__(self, **kwargs):
55        if self._abstract:
56            raise TypeError('Only subclasses may be instantiated.')
57
58        self.template_kwargs = { 'code': self.code }
59        self.servers_clients = {}
60        for param in self.required_params:
61            try:
62                self.template_kwargs[param] = kwargs[param]
63            except KeyError:
64                raise TypeError('The "%s" keyword argument is required for instantiation.' % param)
65
66    def __hash__(self):
67        return id(self)
68
69    def __str__(self):
70        return self.code
71
72    def __eq__(self, other):
73        return self.__class__ == other.__class__ and self.args == other.args
74
75    def copy(self):
76        return self.__class__(**dict(list(zip(self.required_params, self.args))))
77
78    @property
79    def args(self):
80        if not hasattr(self, '_args') or self._args is None:
81            self._args = [self.template_kwargs[p] for p in self.required_params]
82        return self._args
83
84    @property
85    def description(self):
86        return self.description_template % self.template_kwargs
87
88    @property
89    def terse_description(self):
90        return self.terse_description_template % self.template_kwargs
91
92    @property
93    def html_description(self):
94        description_template_escaped = escape(self.description_template, True)
95        template_kwargs_escaped = {}
96        for n, v in self.template_kwargs.items():
97            if isinstance(v, int):
98                template_kwargs_escaped[n] = v
99            else:
100                if isinstance(v, str):
101                    template_kwargs_escaped[n] = escape(v)
102                else:
103                    template_kwargs_escaped[n] = escape(str(v))
104        return description_template_escaped % template_kwargs_escaped
105
106    def add_server_client(self, server, client, response):
107        if (server, client) not in self.servers_clients:
108            self.servers_clients[(server, client)] = []
109        if response not in self.servers_clients[(server, client)]:
110            self.servers_clients[(server, client)].append(response)
111
112    def remove_server_client(self, server, client, response):
113        if (server, client) in self.servers_clients:
114            try:
115                self.servers_clients[(server, client)].remove(response)
116            except ValueError:
117                pass
118            else:
119                if not self.servers_clients[(server, client)]:
120                    del self.servers_clients[(server, client)]
121
122    def serialize(self, consolidate_clients=False, html_format=False):
123        d = OrderedDict()
124
125        if html_format:
126            d['description'] = self.html_description
127        else:
128            d['description'] = self.description
129
130        d['code'] = self.code
131        if self.servers_clients:
132            servers = tuple_to_dict(self.servers_clients)
133            if consolidate_clients:
134                servers = list(servers)
135                servers.sort()
136            d['servers'] = servers
137
138            tags = set()
139            for server,client in self.servers_clients:
140                for response in self.servers_clients[(server,client)]:
141                    # some errors are not in conjunction with responses, per
142                    # se, only servers, in which case, the response value is
143                    # None.
144                    if response is not None:
145                        if self.use_effective_query_tag:
146                            tag = response.effective_query_tag()
147                        else:
148                            tag = response.initial_query_tag()
149                        tags.add(tag)
150            if tags:
151                d['query_options'] = list(tags)
152                d['query_options'].sort()
153
154        return d
155
156    @classmethod
157    def insert_into_list(cls, error, error_list, server, client, response):
158        try:
159            index = error_list.index(error)
160        except ValueError:
161            error_list.append(error)
162        else:
163            error = error_list[index]
164        if server is not None and client is not None:
165            error.add_server_client(server, client, response)
166        return error
167
168class RRSIGError(DomainNameAnalysisError):
169    pass
170
171class SignerNotZone(RRSIGError):
172    '''
173    >>> e = SignerNotZone(zone_name='foo.', signer_name='bar.')
174    >>> e.args
175    ['foo.', 'bar.']
176    >>> e.description
177    "The Signer's Name field of the RRSIG RR (bar.) does not match the name of the zone containing the RRset (foo.)."
178    '''
179
180    _abstract = False
181    code = 'SIGNER_NOT_ZONE'
182    description_template = "The Signer's Name field of the RRSIG RR (%(signer_name)s) does not match the name of the zone containing the RRset (%(zone_name)s)."
183    references = ['RFC 4035, Sec. 5.3.1']
184    required_params = ['zone_name', 'signer_name']
185
186class RRsetTTLMismatch(RRSIGError):
187    '''
188    >>> e = RRsetTTLMismatch(rrset_ttl=50, rrsig_ttl=10)
189    >>> e.args
190    [50, 10]
191    >>> e.description
192    'The TTL of the RRSIG RR (10) does not match the TTL of the RRset it covers (50).'
193    '''
194
195    _abstract = False
196    code = 'RRSET_TTL_MISMATCH'
197    description_template = 'The TTL of the RRSIG RR (%(rrsig_ttl)d) does not match the TTL of the RRset it covers (%(rrset_ttl)d).'
198    references = ['RFC 4035, Sec. 2.2']
199    required_params = ['rrset_ttl', 'rrsig_ttl']
200
201class OriginalTTLExceeded(RRSIGError):
202    '''
203    >>> e = OriginalTTLExceeded(original_ttl=10, rrset_ttl=50)
204    >>> e.args
205    [10, 50]
206    >>> e.description
207    'The TTL of the RRset (50) exceeds the value of the Original TTL field of the RRSIG RR covering it (10).'
208    '''
209
210    _abstract = False
211    code = 'ORIGINAL_TTL_EXCEEDED'
212    description_template = 'The TTL of the RRset (%(rrset_ttl)d) exceeds the value of the Original TTL field of the RRSIG RR covering it (%(original_ttl)d).'
213    references = ['RFC 4035, Sec. 2.2']
214    required_params = ['original_ttl', 'rrset_ttl']
215
216class TTLBeyondExpiration(RRSIGError):
217    '''
218    >>> e = TTLBeyondExpiration(expiration=datetime.datetime(2015,1,10), rrsig_ttl=86401, reference_time=datetime.datetime(2015,1,9))
219    >>> e.args
220    [datetime.datetime(2015, 1, 10, 0, 0), 86401, datetime.datetime(2015, 1, 9, 0, 0)]
221    >>> e.description
222    'With a TTL of 86401 the RRSIG RR can be in the cache of a non-validating resolver until 1 second after it expires at 2015-01-10 00:00:00.'
223    '''
224
225    _abstract = False
226    code = 'TTL_BEYOND_EXPIRATION'
227    description_template = "With a TTL of %(rrsig_ttl)d the RRSIG RR can be in the cache of a non-validating resolver until %(difference)s after it expires at %(expiration)s."
228    references = ['RFC 4035, Sec. 5.3.3']
229    required_params = ['expiration', 'rrsig_ttl', 'reference_time']
230
231    def __init__(self, **kwargs):
232        super(TTLBeyondExpiration, self).__init__(**kwargs)
233        diff = self.template_kwargs['reference_time'] + datetime.timedelta(seconds=self.template_kwargs['rrsig_ttl']) - self.template_kwargs['expiration']
234        self.template_kwargs['difference'] = fmt.humanize_time(diff.seconds, diff.days)
235
236class AlgorithmNotSupported(RRSIGError):
237    '''
238    >>> e = AlgorithmNotSupported(algorithm=5)
239    >>> e.args
240    [5]
241    >>> e.description
242    'Validation of DNSSEC algorithm 5 (RSASHA1) is not supported by this code, so the cryptographic status of this RRSIG is unknown.'
243    '''
244
245    _abstract = False
246    code = 'ALGORITHM_NOT_SUPPORTED'
247    description_template = "Validation of DNSSEC algorithm %(algorithm)d (%(algorithm_text)s) is not supported by this code, so the cryptographic status of this RRSIG is unknown."
248    references = ['RFC 4035, Sec. 5.2']
249    required_params = ['algorithm']
250
251    def __init__(self, **kwargs):
252        super(AlgorithmNotSupported, self).__init__(**kwargs)
253        self.template_kwargs['algorithm_text'] = dns.dnssec.algorithm_to_text(self.template_kwargs['algorithm'])
254
255class AlgorithmValidationProhibited(RRSIGError):
256    '''
257    >>> e = AlgorithmValidationProhibited(algorithm=5)
258    >>> e.args
259    [5]
260    >>> e.description
261    'DNSSEC specification prohibits validation of RRSIGs with DNSSEC algorithm 5 (RSASHA1).'
262    '''
263
264    _abstract = False
265    code = 'ALGORITHM_VALIDATION_PROHIBITED'
266    description_template = "DNSSEC specification prohibits validation of RRSIGs with DNSSEC algorithm %(algorithm)d (%(algorithm_text)s)."
267    references = ['RFC 8624, Sec. 3.1']
268    required_params = ['algorithm']
269
270    def __init__(self, **kwargs):
271        super(AlgorithmValidationProhibited, self).__init__(**kwargs)
272        self.template_kwargs['algorithm_text'] = dns.dnssec.algorithm_to_text(self.template_kwargs['algorithm'])
273
274class AlgorithmProhibited(RRSIGError):
275    '''
276    >>> e = AlgorithmProhibited(algorithm=5)
277    >>> e.args
278    [5]
279    >>> e.description
280    'DNSSEC specification prohibits signing with DNSSEC algorithm 5 (RSASHA1).'
281    '''
282
283    _abstract = False
284    code = 'ALGORITHM_PROHIBITED'
285    description_template = "DNSSEC specification prohibits signing with DNSSEC algorithm %(algorithm)d (%(algorithm_text)s)."
286    references = ['RFC 8624, Sec. 3.1']
287    required_params = ['algorithm']
288
289    def __init__(self, **kwargs):
290        super(AlgorithmProhibited, self).__init__(**kwargs)
291        self.template_kwargs['algorithm_text'] = dns.dnssec.algorithm_to_text(self.template_kwargs['algorithm'])
292
293class AlgorithmNotRecommended(RRSIGError):
294    '''
295    >>> e = AlgorithmNotRecommended(algorithm=5)
296    >>> e.args
297    [5]
298    >>> e.description
299    'DNSSEC specification recommends not signing with DNSSEC algorithm 5 (RSASHA1).'
300    '''
301
302    _abstract = False
303    code = 'ALGORITHM_NOT_RECOMMENDED'
304    description_template = "DNSSEC specification recommends not signing with DNSSEC algorithm %(algorithm)d (%(algorithm_text)s)."
305    references = ['RFC 8624, Sec. 3.1']
306    required_params = ['algorithm']
307
308    def __init__(self, **kwargs):
309        super(AlgorithmNotRecommended, self).__init__(**kwargs)
310        self.template_kwargs['algorithm_text'] = dns.dnssec.algorithm_to_text(self.template_kwargs['algorithm'])
311
312class DNSKEYRevokedRRSIG(RRSIGError):
313    '''
314    >>> e = DNSKEYRevokedRRSIG()
315    >>> e.description
316    'The DNSKEY RR corresponding to the RRSIG RR has the REVOKE bit set.  A revoked key cannot be used to validate RRSIGs.'
317    '''
318
319    _abstract = False
320    code = 'DNSKEY_REVOKED_RRSIG'
321    description_template = "The DNSKEY RR corresponding to the RRSIG RR has the REVOKE bit set.  A revoked key cannot be used to validate RRSIGs."
322    references = ['RFC 5011, Sec. 2.1']
323    required_params = []
324
325class InceptionInFuture(RRSIGError):
326    '''
327    >>> e = InceptionInFuture(inception=datetime.datetime(2015,1,10), reference_time=datetime.datetime(2015,1,9))
328    >>> e.args
329    [datetime.datetime(2015, 1, 10, 0, 0), datetime.datetime(2015, 1, 9, 0, 0)]
330    >>> e.description
331    'The Signature Inception field of the RRSIG RR (2015-01-10 00:00:00) is 1 day in the future.'
332    '''
333
334    _abstract = False
335    code = 'INCEPTION_IN_FUTURE'
336    description_template = "The Signature Inception field of the RRSIG RR (%(inception)s) is %(premature_time)s in the future."
337    references = ['RFC 4035, Sec. 5.3.1']
338    required_params = ['inception', 'reference_time']
339
340    def __init__(self, **kwargs):
341        super(InceptionInFuture, self).__init__(**kwargs)
342        diff = self.template_kwargs['inception'] - self.template_kwargs['reference_time']
343        self.template_kwargs['premature_time'] = fmt.humanize_time(diff.seconds, diff.days)
344
345class ExpirationInPast(RRSIGError):
346    '''
347    >>> e = ExpirationInPast(expiration=datetime.datetime(2015,1,10), reference_time=datetime.datetime(2015,1,11))
348    >>> e.args
349    [datetime.datetime(2015, 1, 10, 0, 0), datetime.datetime(2015, 1, 11, 0, 0)]
350    >>> e.description
351    'The Signature Expiration field of the RRSIG RR (2015-01-10 00:00:00) is 1 day in the past.'
352    '''
353
354    _abstract = False
355    code = 'EXPIRATION_IN_PAST'
356    description_template = "The Signature Expiration field of the RRSIG RR (%(expiration)s) is %(expired_time)s in the past."
357    references = ['RFC 4035, Sec. 5.3.1']
358    required_params = ['expiration', 'reference_time']
359
360    def __init__(self, **kwargs):
361        super(ExpirationInPast, self).__init__(**kwargs)
362        diff = self.template_kwargs['reference_time'] - self.template_kwargs['expiration']
363        self.template_kwargs['expired_time'] = fmt.humanize_time(diff.seconds, diff.days)
364
365class InceptionWithinClockSkew(RRSIGError):
366    '''
367    >>> e = InceptionWithinClockSkew(inception=datetime.datetime(2015,1,10,0,0,0), reference_time=datetime.datetime(2015,1,10,0,0,1))
368    >>> e.description
369    'The value of the Signature Inception field of the RRSIG RR (2015-01-10 00:00:00) is within possible clock skew range (1 second) of the current time (2015-01-10 00:00:01).'
370    '''
371
372    _abstract = False
373    code = 'INCEPTION_WITHIN_CLOCK_SKEW'
374    description_template = "The value of the Signature Inception field of the RRSIG RR (%(inception)s) is within possible clock skew range (%(difference)s) of the current time (%(reference_time)s)."
375    references = ['RFC 4035, Sec. 5.3.1']
376    required_params = ['inception', 'reference_time']
377
378    def __init__(self, **kwargs):
379        super(InceptionWithinClockSkew, self).__init__(**kwargs)
380        diff = self.template_kwargs['reference_time'] - self.template_kwargs['inception']
381        self.template_kwargs['difference'] = fmt.humanize_time(diff.seconds, diff.days)
382
383class ExpirationWithinClockSkew(RRSIGError):
384    '''
385    >>> e = ExpirationWithinClockSkew(expiration=datetime.datetime(2015,1,10,0,0,1), reference_time=datetime.datetime(2015,1,10,0,0,0))
386    >>> e.description
387    'The value of the Signature Expiration field of the RRSIG RR (2015-01-10 00:00:01) is within possible clock skew range (1 second) of the current time (2015-01-10 00:00:00).'
388    '''
389
390    _abstract = False
391    code = 'EXPIRATION_WITHIN_CLOCK_SKEW'
392    description_template = "The value of the Signature Expiration field of the RRSIG RR (%(expiration)s) is within possible clock skew range (%(difference)s) of the current time (%(reference_time)s)."
393    references = ['RFC 4035, Sec. 5.3.1']
394    required_params = ['expiration', 'reference_time']
395
396    def __init__(self, **kwargs):
397        super(ExpirationWithinClockSkew, self).__init__(**kwargs)
398        diff = self.template_kwargs['expiration'] - self.template_kwargs['reference_time']
399        self.template_kwargs['difference'] = fmt.humanize_time(diff.seconds, diff.days)
400
401class SignatureInvalid(RRSIGError):
402    '''
403    >>> e = SignatureInvalid()
404    >>> e.description
405    'The cryptographic signature of the RRSIG RR does not properly validate.'
406    '''
407
408    _abstract = False
409    code = 'SIGNATURE_INVALID'
410    description_template = "The cryptographic signature of the RRSIG RR does not properly validate."
411    references = ['RFC 4035, Sec. 5.3.3']
412    required_params = []
413
414class RRSIGBadLength(RRSIGError):
415    pass
416
417class RRSIGBadLengthGOST(RRSIGBadLength):
418    '''
419    >>> e = RRSIGBadLengthGOST(length=500)
420    >>> e.description
421    'The length of the signature is 500 bits, but a GOST signature (DNSSEC algorithm 12) must be 512 bits long.'
422    '''
423    _abstract = False
424    description_template = 'The length of the signature is %(length)d bits, but a GOST signature (DNSSEC algorithm 12) must be 512 bits long.'
425    code = 'RRSIG_BAD_LENGTH_GOST'
426    references = ['RFC 5933, Sec. 5.2']
427    required_params = ['length']
428
429class RRSIGBadLengthECDSA(RRSIGBadLength):
430    curve = None
431    algorithm = None
432    correct_length = None
433    description_template = 'The length of the signature is %(length)d bits, but an ECDSA signature made with Curve %(curve)s (DNSSEC algorithm %(algorithm)d) must be %(correct_length)d bits long.'
434    references = ['RFC 6605, Sec. 4']
435    required_params = ['length']
436
437    def __init__(self, **kwargs):
438        super(RRSIGBadLengthECDSA, self).__init__(**kwargs)
439        self.template_kwargs['curve'] = self.curve
440        self.template_kwargs['algorithm'] = self.algorithm
441        self.template_kwargs['correct_length'] = self.correct_length
442
443class RRSIGBadLengthECDSA256(RRSIGBadLengthECDSA):
444    '''
445    >>> e = RRSIGBadLengthECDSA256(length=500)
446    >>> e.description
447    'The length of the signature is 500 bits, but an ECDSA signature made with Curve P-256 (DNSSEC algorithm 13) must be 512 bits long.'
448    '''
449    curve = 'P-256'
450    algorithm = 13
451    correct_length = 512
452    _abstract = False
453    code = 'RRSIG_BAD_LENGTH_ECDSA256'
454
455class RRSIGBadLengthECDSA384(RRSIGBadLengthECDSA):
456    '''
457    >>> e = RRSIGBadLengthECDSA384(length=500)
458    >>> e.description
459    'The length of the signature is 500 bits, but an ECDSA signature made with Curve P-384 (DNSSEC algorithm 14) must be 768 bits long.'
460    '''
461    curve = 'P-384'
462    algorithm = 14
463    correct_length = 768
464    _abstract = False
465    code = 'RRSIG_BAD_LENGTH_ECDSA384'
466
467class RRSIGBadLengthEdDSA(RRSIGBadLength):
468    curve = None
469    algorithm = None
470    correct_length = None
471    description_template = 'The length of the signature is %(length)d bits, but an %(curve)s signature (DNSSEC algorithm %(algorithm)d) must be %(correct_length)d bits long.'
472    references = ['RFC 8080, Sec. 4']
473    required_params = ['length']
474
475    def __init__(self, **kwargs):
476        super(RRSIGBadLengthEdDSA, self).__init__(**kwargs)
477        self.template_kwargs['curve'] = self.curve
478        self.template_kwargs['algorithm'] = self.algorithm
479        self.template_kwargs['correct_length'] = self.correct_length
480
481class RRSIGBadLengthEd25519(RRSIGBadLengthEdDSA):
482    '''
483    >>> e = RRSIGBadLengthEd25519(length=500)
484    >>> e.description
485    'The length of the signature is 500 bits, but an Ed25519 signature (DNSSEC algorithm 15) must be 512 bits long.'
486    '''
487    curve = 'Ed25519'
488    algorithm = 15
489    correct_length = 512
490    _abstract = False
491    code = 'RRSIG_BAD_LENGTH_ED25519'
492
493class RRSIGBadLengthEd448(RRSIGBadLengthEdDSA):
494    '''
495    >>> e = RRSIGBadLengthEd448(length=500)
496    >>> e.description
497    'The length of the signature is 500 bits, but an Ed448 signature (DNSSEC algorithm 16) must be 912 bits long.'
498    '''
499    curve = 'Ed448'
500    algorithm = 16
501    correct_length = 912
502    _abstract = False
503    code = 'RRSIG_BAD_LENGTH_ED448'
504
505class DSError(DomainNameAnalysisError):
506    pass
507
508class ReferralForDSQuery(DSError):
509    '''
510    >>> e = ReferralForDSQuery(parent='baz.')
511    >>> e.description
512    'The server(s) for the parent zone (baz.) responded with a referral instead of answering authoritatively for the DS RR type.'
513    '''
514    _abstract = False
515    code = 'REFERRAL_FOR_DS_QUERY'
516    description_template = 'The server(s) for the parent zone (%(parent)s) responded with a referral instead of answering authoritatively for the DS RR type.'
517    references = ['RFC 4034, Sec. 5']
518    required_params = ['parent']
519
520class DSDigestAlgorithmIgnored(DSError):
521    '''
522    >>> e = DSDigestAlgorithmIgnored(algorithm=1, new_algorithm=2)
523    >>> e.description
524    'DS records with digest type 1 (SHA-1) are ignored when DS records with digest type 2 (SHA-256) exist in the same RRset.'
525    '''
526    _abstract = False
527    code = 'DS_DIGEST_ALGORITHM_IGNORED'
528    description_template = "DS records with digest type %(algorithm)d (%(algorithm_text)s) are ignored when DS records with digest type %(new_algorithm)d (%(new_algorithm_text)s) exist in the same RRset."
529    references = ['RFC 4509, Sec. 3']
530    required_params = ['algorithm', 'new_algorithm']
531
532    def __init__(self, **kwargs):
533        super(DSDigestAlgorithmIgnored, self).__init__(**kwargs)
534        self.template_kwargs['algorithm_text'] = fmt.DS_DIGEST_TYPES.get(self.template_kwargs['algorithm'], str(self.template_kwargs['algorithm']))
535        self.template_kwargs['new_algorithm_text'] = fmt.DS_DIGEST_TYPES.get(self.template_kwargs['new_algorithm'], str(self.template_kwargs['algorithm']))
536
537class DSDigestAlgorithmMaybeIgnored(DSError):
538    '''
539    >>> e = DSDigestAlgorithmMaybeIgnored(algorithm=1, new_algorithm=2)
540    >>> e.description
541    'In the spirit of RFC 4509, DS records with digest type 1 (SHA-1) might be ignored when DS records with digest type 2 (SHA-256) exist in the same RRset.'
542    '''
543    _abstract = False
544    code = 'DS_DIGEST_ALGORITHM_MAYBE_IGNORED'
545    description_template = "In the spirit of RFC 4509, DS records with digest type %(algorithm)d (%(algorithm_text)s) might be ignored when DS records with digest type %(new_algorithm)d (%(new_algorithm_text)s) exist in the same RRset."
546    references = ['RFC 4509, Sec. 3']
547    required_params = ['algorithm', 'new_algorithm']
548
549    def __init__(self, **kwargs):
550        super(DSDigestAlgorithmMaybeIgnored, self).__init__(**kwargs)
551        self.template_kwargs['algorithm_text'] = fmt.DS_DIGEST_TYPES.get(self.template_kwargs['algorithm'], str(self.template_kwargs['algorithm']))
552        self.template_kwargs['new_algorithm_text'] = fmt.DS_DIGEST_TYPES.get(self.template_kwargs['new_algorithm'], str(self.template_kwargs['algorithm']))
553
554class DSDigestError(DSError):
555    pass
556
557class DigestAlgorithmNotSupported(DSDigestError):
558    '''
559    >>> e = DigestAlgorithmNotSupported(algorithm=5)
560    >>> e.description
561    'Generating cryptographic hashes using algorithm 5 (5) is not supported by this code, so the cryptographic status of the DS RR is unknown.'
562    '''
563
564    _abstract = False
565    code = 'DIGEST_ALGORITHM_NOT_SUPPORTED'
566    description_template = "Generating cryptographic hashes using algorithm %(algorithm)d (%(algorithm_text)s) is not supported by this code, so the cryptographic status of the DS RR is unknown."
567    references = ['RFC 4035, Sec. 5.2']
568    required_params = ['algorithm']
569
570    def __init__(self, **kwargs):
571        super(DigestAlgorithmNotSupported, self).__init__(**kwargs)
572        self.template_kwargs['algorithm_text'] = fmt.DS_DIGEST_TYPES.get(self.template_kwargs['algorithm'], self.template_kwargs['algorithm'])
573
574class DigestAlgorithmValidationProhibited(DSDigestError):
575    '''
576    >>> e = DigestAlgorithmValidationProhibited(algorithm=5)
577    >>> e.description
578    'DNSSEC specification prohibits validation of DS records that use digest algorithm 5 (5).'
579    '''
580
581    _abstract = False
582    code = 'DIGEST_ALGORITHM_VALIDATION_PROHIBITED'
583    description_template = "DNSSEC specification prohibits validation of DS records that use digest algorithm %(algorithm)d (%(algorithm_text)s)."
584    references = ['RFC 8624, Sec. 3.2']
585    required_params = ['algorithm']
586
587    def __init__(self, **kwargs):
588        super(DigestAlgorithmValidationProhibited, self).__init__(**kwargs)
589        self.template_kwargs['algorithm_text'] = fmt.DS_DIGEST_TYPES.get(self.template_kwargs['algorithm'], self.template_kwargs['algorithm'])
590
591class DigestAlgorithmProhibited(DSDigestError):
592    '''
593    >>> e = DigestAlgorithmProhibited(algorithm=5)
594    >>> e.description
595    'DNSSEC specification prohibits signing with DS records that use digest algorithm 5 (5).'
596    '''
597
598    _abstract = False
599    code = 'DIGEST_ALGORITHM_PROHIBITED'
600    description_template = "DNSSEC specification prohibits signing with DS records that use digest algorithm %(algorithm)d (%(algorithm_text)s)."
601    references = ['RFC 8624, Sec. 3.2']
602    required_params = ['algorithm']
603
604    def __init__(self, **kwargs):
605        super(DigestAlgorithmProhibited, self).__init__(**kwargs)
606        self.template_kwargs['algorithm_text'] = fmt.DS_DIGEST_TYPES.get(self.template_kwargs['algorithm'], self.template_kwargs['algorithm'])
607
608class DigestAlgorithmNotRecommended(DSDigestError):
609    '''
610    >>> e = DigestAlgorithmNotRecommended(algorithm=5)
611    >>> e.description
612    'DNSSEC specification recommends not signing with DS records that use digest algorithm 5 (5).'
613    '''
614
615    _abstract = False
616    code = 'DIGEST_ALGORITHM_NOT_RECOMMENDED'
617    description_template = "DNSSEC specification recommends not signing with DS records that use digest algorithm %(algorithm)d (%(algorithm_text)s)."
618    references = ['RFC 8624, Sec. 3.2']
619    required_params = ['algorithm']
620
621    def __init__(self, **kwargs):
622        super(DigestAlgorithmNotRecommended, self).__init__(**kwargs)
623        self.template_kwargs['algorithm_text'] = fmt.DS_DIGEST_TYPES.get(self.template_kwargs['algorithm'], self.template_kwargs['algorithm'])
624
625class DNSKEYRevokedDS(DSDigestError):
626    '''
627    >>> e = DNSKEYRevokedDS()
628    >>> e.description
629    'The DNSKEY RR corresponding to the DS RR has the REVOKE bit set.  A revoked key cannot be used with DS records.'
630    '''
631
632    _abstract = False
633    code = 'DNSKEY_REVOKED_DS'
634    description_template = "The DNSKEY RR corresponding to the DS RR has the REVOKE bit set.  A revoked key cannot be used with DS records."
635    references = ['RFC 5011, Sec. 2.1']
636    required_params = []
637
638class DigestInvalid(DSDigestError):
639    '''
640    >>> e = DigestInvalid()
641    >>> e.description
642    'The cryptographic hash in the Digest field of the DS RR does not match the computed value.'
643    '''
644
645    _abstract = False
646    code = 'DIGEST_INVALID'
647    description_template = "The cryptographic hash in the Digest field of the DS RR does not match the computed value."
648    references = ['RFC 4035, Sec. 5.2']
649    required_params = []
650
651class NSECError(DomainNameAnalysisError):
652    nsec_type = None
653
654    def __init__(self, *args, **kwargs):
655        super(NSECError, self).__init__(**kwargs)
656        self.template_kwargs['nsec_type'] = self.nsec_type
657
658class SnameNotCovered(NSECError):
659    code = 'SNAME_NOT_COVERED'
660    description_template = "No %(nsec_type)s RR covers the SNAME (%(sname)s)."
661    required_params = ['sname']
662    nsec_type = 'NSEC'
663
664class SnameNotCoveredNameError(SnameNotCovered):
665    '''
666    >>> e = SnameNotCoveredNameError(sname='foo.baz.')
667    >>> e.description
668    'No NSEC RR covers the SNAME (foo.baz.).'
669    '''
670
671    _abstract = False
672    references = ['RFC 4035, Sec. 3.1.3.2']
673
674class SnameNotCoveredWildcardAnswer(SnameNotCovered):
675    _abstract = False
676    references = ['RFC 4035, Sec. 3.1.3.3']
677
678class NextClosestEncloserNotCovered(NSECError):
679    code = 'NEXT_CLOSEST_ENCLOSER_NOT_COVERED'
680    description_template = "No %(nsec_type)s RR covers the next closest encloser (%(next_closest_encloser)s)."
681    required_params = ['next_closest_encloser']
682    nsec_type = 'NSEC3'
683
684class NextClosestEncloserNotCoveredNameError(NextClosestEncloserNotCovered):
685    '''
686    >>> e = NextClosestEncloserNotCoveredNameError(next_closest_encloser='foo.baz.')
687    >>> e.description
688    'No NSEC3 RR covers the next closest encloser (foo.baz.).'
689    '''
690
691    _abstract = False
692    references = ['RFC 5155, Sec. 8.4']
693
694class NextClosestEncloserNotCoveredNODATADS(NextClosestEncloserNotCovered):
695    _abstract = False
696    references = ['RFC 5155, Sec. 8.6']
697
698class NextClosestEncloserNotCoveredWildcardNODATA(NextClosestEncloserNotCovered):
699    _abstract = False
700    references = ['RFC 5155, Sec. 8.7']
701
702class NextClosestEncloserNotCoveredWildcardAnswer(NextClosestEncloserNotCovered):
703    _abstract = False
704    references = ['RFC 5155, Sec. 8.8']
705
706class WildcardNotCovered(NSECError):
707    code = 'WILDCARD_NOT_COVERED'
708    description_template = "No %(nsec_type)s RR covers the wildcard (%(wildcard)s)."
709    required_params = ['wildcard']
710
711class WildcardNotCoveredNSEC(WildcardNotCovered):
712    '''
713    >>> e = WildcardNotCoveredNSEC(wildcard='*.foo.baz.')
714    >>> e.description
715    'No NSEC RR covers the wildcard (*.foo.baz.).'
716    '''
717
718    _abstract = False
719    references = ['RFC 4035, Sec. 3.1.3.2']
720    nsec_type = 'NSEC'
721
722class WildcardNotCoveredNSEC3(WildcardNotCovered):
723    _abstract = False
724    references = ['RFC 5155, Sec. 8.4']
725    nsec_type = 'NSEC3'
726
727class NoClosestEncloser(NSECError):
728    code = 'NO_CLOSEST_ENCLOSER'
729    description_template = "No %(nsec_type)s RR corresponds to the closest encloser of the SNAME (%(sname)s)."
730    required_params = ['sname']
731    nsec_type = 'NSEC3'
732
733class NoClosestEncloserNameError(NoClosestEncloser):
734    '''
735    >>> e = NoClosestEncloserNameError(sname='foo.baz.')
736    >>> e.description
737    'No NSEC3 RR corresponds to the closest encloser of the SNAME (foo.baz.).'
738    '''
739
740    _abstract = False
741    references = ['RFC 5155, Sec. 8.4']
742
743class NoClosestEncloserNODATADS(NoClosestEncloser):
744    _abstract = False
745    references = ['RFC 5155, Sec. 8.6']
746
747class NoClosestEncloserWildcardNODATA(NoClosestEncloser):
748    _abstract = False
749    references = ['RFC 5155, Sec. 8.7']
750
751class NoClosestEncloserWildcardAnswer(NoClosestEncloser):
752    _abstract = False
753    references = ['RFC 5155, Sec. 8.8']
754
755class OptOutFlagNotSet(NSECError):
756    code = 'OPT_OUT_FLAG_NOT_SET'
757    description_template = "The opt-out flag was not set in the %(nsec_type)s RR covering the next closest encloser (%(next_closest_encloser)s) but was required for the NODATA response."
758    required_params = ['next_closest_encloser']
759    nsec_type = 'NSEC3'
760
761class OptOutFlagNotSetNODATA(OptOutFlagNotSet):
762    '''
763    >>> e = OptOutFlagNotSetNODATA(next_closest_encloser='foo.baz.')
764    >>> e.description
765    'The opt-out flag was not set in the NSEC3 RR covering the next closest encloser (foo.baz.) but was required for the NODATA response.'
766    '''
767
768    _abstract = False
769    references = ['RFC 5155, Sec. 8.5', 'RFC Errata 3441']
770
771class OptOutFlagNotSetNODATADS(OptOutFlagNotSet):
772    _abstract = False
773    references = ['RFC 5155, Sec. 8.6']
774
775class ReferralWithSOABit(NSECError):
776    code = 'REFERRAL_WITH_SOA'
777    description_template = "The SOA bit was set in the bitmap of the %(nsec_type)s RR corresponding to the delegated name (%(sname)s)."
778    required_params = ['sname']
779
780class ReferralWithSOABitNSEC(ReferralWithSOABit):
781    '''
782    >>> e = ReferralWithSOABitNSEC(sname='foo.baz.')
783    >>> e.description
784    'The SOA bit was set in the bitmap of the NSEC RR corresponding to the delegated name (foo.baz.).'
785    '''
786
787    _abstract = False
788    references = ['RFC 4034, Sec. 5.2']
789    nsec_type = 'NSEC'
790
791class ReferralWithSOABitNSEC3(ReferralWithSOABit):
792    _abstract = False
793    references = ['RFC 5155, Sec. 8.9']
794    nsec_type = 'NSEC3'
795
796class ReferralWithDSBit(NSECError):
797    code = 'REFERRAL_WITH_DS'
798    description_template = "The DS bit was set in the bitmap of the %(nsec_type)s RR corresponding to the delegated name (%(sname)s)."
799    required_params = ['sname']
800
801class ReferralWithDSBitNSEC(ReferralWithDSBit):
802    '''
803    >>> e = ReferralWithDSBitNSEC(sname='foo.baz.')
804    >>> e.description
805    'The DS bit was set in the bitmap of the NSEC RR corresponding to the delegated name (foo.baz.).'
806    '''
807
808    _abstract = False
809    references = ['RFC 4034, Sec. 5.2']
810    nsec_type = 'NSEC'
811
812class ReferralWithDSBitNSEC3(ReferralWithDSBit):
813    _abstract = False
814    references = ['RFC 5155, Sec. 8.9']
815    nsec_type = 'NSEC3'
816
817class ReferralWithoutNSBit(NSECError):
818    code = 'REFERRAL_WITHOUT_NS'
819    description_template = "The NS bit was not set in the bitmap of the %(nsec_type)s RR corresponding to the delegated name (%(sname)s)."
820    required_params = ['sname']
821
822class ReferralWithoutNSBitNSEC(ReferralWithoutNSBit):
823    '''
824    >>> e = ReferralWithoutNSBitNSEC(sname='foo.baz.')
825    >>> e.description
826    'The NS bit was not set in the bitmap of the NSEC RR corresponding to the delegated name (foo.baz.).'
827    '''
828
829    _abstract = False
830    references = ['RFC 6840, Sec. 4.4']
831    nsec_type = 'NSEC'
832
833class ReferralWithoutNSBitNSEC3(ReferralWithoutNSBit):
834    _abstract = False
835    references = ['RFC 5155, Sec. 8.9']
836    nsec_type = 'NSEC3'
837
838class StypeInBitmap(NSECError):
839    code = 'STYPE_IN_BITMAP'
840    description_template = "The %(stype)s bit was set in the bitmap of the %(nsec_type)s RR corresponding to the SNAME (%(sname)s)."
841    required_params = ['stype', 'sname']
842
843class StypeInBitmapNODATA(StypeInBitmap):
844    pass
845
846class StypeInBitmapNODATANSEC(StypeInBitmapNODATA):
847    '''
848    >>> e = StypeInBitmapNODATANSEC(stype='A', sname='foo.baz.')
849    >>> e.description
850    'The A bit was set in the bitmap of the NSEC RR corresponding to the SNAME (foo.baz.).'
851    '''
852
853    _abstract = False
854    references = ['RFC 4035, Sec. 3.1.3.1']
855    nsec_type = 'NSEC'
856
857class StypeInBitmapNODATANSEC3(StypeInBitmapNODATA):
858    _abstract = False
859    references = ['RFC 5155, Sec. 8.5']
860    nsec_type = 'NSEC3'
861
862class StypeInBitmapNODATADSNSEC3(StypeInBitmapNODATANSEC3):
863    _abstract = False
864    references = ['RFC 5155, Sec. 8.6']
865
866class StypeInBitmapWildcardNODATA(StypeInBitmap):
867    pass
868
869class StypeInBitmapWildcardNODATANSEC(StypeInBitmapWildcardNODATA):
870    _abstract = False
871    references = ['RFC 4035, Sec. 3.1.3.4']
872    nsec_type = 'NSEC'
873
874class StypeInBitmapWildcardNODATANSEC3(StypeInBitmapWildcardNODATA):
875    _abstract = False
876    references = ['RFC 5155, Sec. 8.7']
877    nsec_type = 'NSEC3'
878
879class NoNSECMatchingSname(NSECError):
880    code = 'NO_NSEC_MATCHING_SNAME'
881    description_template = "No %(nsec_type)s RR matches the SNAME (%(sname)s)."
882    required_params = ['sname']
883    nsec_type = 'NSEC'
884
885class NoNSECMatchingSnameNODATA(NoNSECMatchingSname):
886    '''
887    >>> e = NoNSECMatchingSnameNODATA(sname='foo.baz.')
888    >>> e.description
889    'No NSEC RR matches the SNAME (foo.baz.).'
890    '''
891
892    _abstract = False
893    references = ['RFC 4035, Sec. 3.1.3.1']
894
895class NoNSECMatchingSnameWildcardNODATA(NoNSECMatchingSname):
896    _abstract = False
897    references = ['RFC 4035, Sec. 3.1.3.4']
898
899class NoNSEC3MatchingSname(NSECError):
900    code = 'NO_NSEC3_MATCHING_SNAME'
901    description_template = "No %(nsec_type)s RR matches the SNAME (%(sname)s)."
902    required_params = ['sname']
903    nsec_type = 'NSEC3'
904
905class NoNSEC3MatchingSnameNODATA(NoNSEC3MatchingSname):
906    '''
907    >>> e = NoNSEC3MatchingSnameNODATA(sname='foo.baz.')
908    >>> e.description
909    'No NSEC3 RR matches the SNAME (foo.baz.).'
910    '''
911
912    _abstract = False
913    references = ['RFC 5155, Sec. 8.5']
914
915class NoNSEC3MatchingSnameDSNODATA(NoNSEC3MatchingSname):
916    _abstract = False
917    references = ['RFC 5155, Sec. 8.6']
918
919class WildcardExpansionInvalid(NSECError):
920    '''
921    >>> e = WildcardExpansionInvalid(sname='a.b.c.foo.baz.', wildcard='*.foo.baz.', next_closest_encloser='b.c.foo.baz.')
922    >>> e.description
923    'The wildcard expansion of *.foo.baz. to a.b.c.foo.baz. is invalid, as the NSEC RR indicates that the next closest encloser (b.c.foo.baz.) exists.'
924    '''
925
926    _abstract = False
927    code = 'WILDCARD_EXPANSION_INVALID'
928    description_template = "The wildcard expansion of %(wildcard)s to %(sname)s is invalid, as the %(nsec_type)s RR indicates that the next closest encloser (%(next_closest_encloser)s) exists."
929    references = ['RFC 1034, Sec. 4.4']
930    required_params = ['sname','wildcard','next_closest_encloser']
931    nsec_type = 'NSEC'
932
933class WildcardCovered(NSECError):
934    code = 'WILDCARD_COVERED'
935    description_template = "The %(nsec_type)s RR covers the wildcard itself (%(wildcard)s), indicating that it doesn't exist."
936    required_params = ['wildcard']
937
938class WildcardCoveredAnswer(WildcardCovered):
939    pass
940
941class WildcardCoveredAnswerNSEC(WildcardCoveredAnswer):
942    '''
943    >>> e = WildcardCoveredAnswerNSEC(wildcard='*.foo.baz.')
944    >>> e.description
945    "The NSEC RR covers the wildcard itself (*.foo.baz.), indicating that it doesn't exist."
946    '''
947
948    _abstract = False
949    references = ['RFC 4035, Sec. 3.1.3.3']
950    nsec_type = 'NSEC'
951
952class WildcardCoveredAnswerNSEC3(WildcardCoveredAnswer):
953    _abstract = False
954    references = ['RFC 5155, Sec. 8.8']
955    nsec_type = 'NSEC3'
956
957class WildcardCoveredNODATA(WildcardCovered):
958    pass
959
960class WildcardCoveredNODATANSEC(WildcardCoveredNODATA):
961    _abstract = False
962    references = ['RFC 4035, Sec. 3.1.3.4']
963    nsec_type = 'NSEC'
964
965class WildcardCoveredNODATANSEC3(WildcardCoveredNODATA):
966    _abstract = False
967    references = ['RFC 5155, Sec. 8.7']
968    nsec_type = 'NSEC3'
969
970class ExistingNSECError(NSECError):
971    required_params = ['queries']
972
973    def __init__(self, **kwargs):
974        super(ExistingNSECError, self).__init__(**kwargs)
975        queries_text = ['%s/%s' % (name, rdtype) for name, rdtype in self.template_kwargs['queries']]
976        self.template_kwargs['queries_text'] = ', '.join(queries_text)
977
978class ExistingCovered(ExistingNSECError):
979    description_template = 'The following queries resulted in an answer response, even though the %(nsec_type)s records indicate that the queried names don\'t exist: %(queries_text)s'
980    code = 'EXISTING_NAME_COVERED'
981
982class ExistingCoveredNSEC(ExistingCovered):
983    '''
984    >>> e = ExistingCoveredNSEC(queries=[('www.foo.baz.', 'A'), ('www1.foo.baz.', 'TXT')])
985    >>> e.description
986    "The following queries resulted in an answer response, even though the NSEC records indicate that the queried names don't exist: www.foo.baz./A, www1.foo.baz./TXT"
987    '''
988
989    _abstract = False
990    references = ['RFC 4035, Sec. 3.1.3.2']
991    nsec_type = 'NSEC'
992
993class ExistingCoveredNSEC3(ExistingCovered):
994    '''
995    >>> e = ExistingCoveredNSEC3(queries=[('www.foo.baz.', 'A'), ('www1.foo.baz.', 'TXT')])
996    >>> e.description
997    "The following queries resulted in an answer response, even though the NSEC3 records indicate that the queried names don't exist: www.foo.baz./A, www1.foo.baz./TXT"
998    '''
999
1000    _abstract = False
1001    references = ['RFC 5155, Sec. 8.4']
1002    nsec_type = 'NSEC3'
1003
1004class ExistingTypeNotInBitmap(ExistingNSECError):
1005    description_template = 'The following queries resulted in an answer response, even though the bitmap in the %(nsec_type)s RR indicates that the queried records don\'t exist: %(queries_text)s'
1006    code = 'EXISTING_TYPE_NOT_IN_BITMAP'
1007
1008class ExistingTypeNotInBitmapNSEC(ExistingTypeNotInBitmap):
1009    '''
1010    >>> e = ExistingTypeNotInBitmapNSEC(queries=[('www.foo.baz.', 'A'), ('www.foo.baz.', 'TXT')])
1011    >>> e.description
1012    "The following queries resulted in an answer response, even though the bitmap in the NSEC RR indicates that the queried records don't exist: www.foo.baz./A, www.foo.baz./TXT"
1013    '''
1014
1015    _abstract = False
1016    references = ['RFC 4035, Sec. 3.1.3.1']
1017    nsec_type = 'NSEC'
1018
1019class ExistingTypeNotInBitmapNSEC3(ExistingTypeNotInBitmap):
1020    '''
1021    >>> e = ExistingTypeNotInBitmapNSEC3(queries=[('www.foo.baz.', 'A'), ('www.foo.baz.', 'TXT')])
1022    >>> e.description
1023    "The following queries resulted in an answer response, even though the bitmap in the NSEC3 RR indicates that the queried records don't exist: www.foo.baz./A, www.foo.baz./TXT"
1024    '''
1025
1026    _abstract = False
1027    references = ['RFC 5155, Sec. 8.5']
1028    nsec_type = 'NSEC3'
1029
1030class SnameCoveredNODATANSEC(NSECError):
1031    '''
1032    >>> e = SnameCoveredNODATANSEC(sname='foo.baz.')
1033    >>> e.description
1034    "The NSEC RR covers the SNAME (foo.baz.), indicating that it doesn't exist."
1035    '''
1036
1037    _abstract = False
1038    code = 'SNAME_COVERED'
1039    description_template = "The %(nsec_type)s RR covers the SNAME (%(sname)s), indicating that it doesn't exist."
1040    references = ['RFC 4035, Sec. 3.1.3.1']
1041    required_params = ['sname']
1042    nsec_type = 'NSEC'
1043
1044
1045class LastNSECNextNotZone(NSECError):
1046    '''
1047    >>> e = LastNSECNextNotZone(nsec_owner='z.foo.baz.', next_name='a.foo.baz.', zone_name='foo.baz.')
1048    >>> e.description
1049    'The value of the Next Domain Name field in the NSEC RR with owner name z.foo.baz. indicates that it is the last NSEC RR in the zone, but the value (a.foo.baz.) did not match the name of the zone apex (foo.baz.).'
1050    '''
1051
1052    _abstract = False
1053    code = 'LAST_NSEC_NEXT_NOT_ZONE'
1054    description_template = "The value of the Next Domain Name field in the %(nsec_type)s RR with owner name %(nsec_owner)s indicates that it is the last %(nsec_type)s RR in the zone, but the value (%(next_name)s) did not match the name of the zone apex (%(zone_name)s)."
1055    references = ['RFC 4034, Sec. 4.1.1']
1056    required_params = ['nsec_owner','next_name','zone_name']
1057    nsec_type = 'NSEC'
1058
1059class UnsupportedNSEC3Algorithm(NSECError):
1060    '''
1061    >>> e = UnsupportedNSEC3Algorithm(algorithm=2)
1062    >>> e.description
1063    'Generating NSEC3 hashes using algorithm 2 is not supported by this code.'
1064    '''
1065
1066    _abstract = False
1067    code = 'UNSUPPORTED_NSEC3_ALGORITHM'
1068    description_template = "Generating %(nsec_type)s hashes using algorithm %(algorithm)d is not supported by this code."
1069    references = ['RFC 5155, Sec. 8.1']
1070    required_params = ['algorithm']
1071    nsec_type = 'NSEC3'
1072
1073class InvalidNSEC3OwnerName(NSECError):
1074    '''
1075    >>> e = InvalidNSEC3OwnerName(name='foo.com.')
1076    >>> e.description
1077    'The NSEC3 owner name (foo.com.) is invalid; it does not appear to be the Base32 Hex encoding of a hashed owner name.'
1078    '''
1079
1080    _abstract = False
1081    code = 'INVALID_NSEC3_OWNER_NAME'
1082    description_template = "The %(nsec_type)s owner name (%(name)s) is invalid; it does not appear to be the Base32 Hex encoding of a hashed owner name."
1083    references = ['RFC 5155, Sec. 3']
1084    required_params = ['name']
1085    nsec_type = 'NSEC3'
1086
1087class InvalidNSEC3Hash(NSECError):
1088    '''
1089    >>> e = InvalidNSEC3Hash(name='foo', nsec3_hash='foo===')
1090    >>> e.description
1091    'The NSEC3 record for foo is invalid; the value of the Next Hashed Owner Name field (foo===) does not appear to be a valid hash.'
1092    '''
1093
1094    _abstract = False
1095    code = 'INVALID_NSEC3_HASH'
1096    description_template = 'The NSEC3 record for %(name)s is invalid; the value of the Next Hashed Owner Name field (%(nsec3_hash)s) does not appear to be a valid hash.'
1097    references = ['RFC 5155, Sec. 3.1.7']
1098    required_params = ['name', 'nsec3_hash']
1099    nsec_type = 'NSEC3'
1100
1101class ResponseError(DomainNameAnalysisError):
1102    pass
1103
1104class InvalidResponseError(ResponseError):
1105    required_params = ['tcp']
1106
1107    def __init__(self, *args, **kwargs):
1108        super(ResponseError, self).__init__(**kwargs)
1109        if self.template_kwargs['tcp']:
1110            self.template_kwargs['proto'] = 'TCP'
1111        else:
1112            self.template_kwargs['proto'] = 'UDP'
1113
1114class NetworkError(InvalidResponseError):
1115    '''
1116    >>> e = NetworkError(tcp=False, errno='EHOSTUNREACH')
1117    >>> e.description
1118    'The server was not reachable over UDP (EHOSTUNREACH).'
1119    >>> e = NetworkError(tcp=False, errno='ECONNREFUSED')
1120    >>> e.description
1121    'The UDP connection was refused (ECONNREFUSED).'
1122    >>> e.terse_description
1123    'NETWORK_ERROR:ECONNREFUSED'
1124    '''
1125
1126    _abstract = False
1127    code = 'NETWORK_ERROR'
1128    description_template = '%(description)s'
1129    terse_description_template = '%(code)s:%(errno)s'
1130    required_params = InvalidResponseError.required_params + ['errno']
1131
1132    def __init__(self, *args, **kwargs):
1133        super(NetworkError, self).__init__(**kwargs)
1134        if self.template_kwargs['errno'] == 'ECONNRESET':
1135            self.template_kwargs['description'] = 'The %s connection was interrupted (%s).' % (self.template_kwargs['proto'], self.template_kwargs['errno'])
1136        elif self.template_kwargs['errno'] == 'ECONNREFUSED':
1137            self.template_kwargs['description'] = 'The %s connection was refused (%s).' % (self.template_kwargs['proto'], self.template_kwargs['errno'])
1138        elif self.template_kwargs['errno'] == 'EHOSTUNREACH':
1139            self.template_kwargs['description'] = 'The server was not reachable over %s (%s).' % (self.template_kwargs['proto'], self.template_kwargs['errno'])
1140        else:
1141            self.template_kwargs['description'] = 'There was an error communicating with the server over %s (%s).' % (self.template_kwargs['proto'], self.template_kwargs['errno'])
1142
1143class FormError(InvalidResponseError):
1144    '''
1145    >>> e = FormError(tcp=False, msg_size=30)
1146    >>> e.description
1147    'The response (30 bytes) was malformed.'
1148    '''
1149
1150    _abstract = False
1151    code = 'FORMERR'
1152    description_template = "The response (%(msg_size)d bytes) was malformed."
1153    required_params = InvalidResponseError.required_params + ['msg_size']
1154
1155class Timeout(InvalidResponseError):
1156    '''
1157    >>> e = Timeout(tcp=False, attempts=3)
1158    >>> e.description
1159    'No response was received from the server over UDP (tried 3 times).'
1160    '''
1161
1162    _abstract = False
1163    code = 'TIMEOUT'
1164    description_template = "No response was received from the server over %(proto)s (tried %(attempts)d times)."
1165    required_params = InvalidResponseError.required_params + ['attempts']
1166
1167class UnknownResponseError(InvalidResponseError):
1168    '''
1169    >>> e = UnknownResponseError(tcp=False)
1170    >>> e.description
1171    'An invalid response was received from the server over UDP.'
1172    '''
1173
1174    _abstract = False
1175    code = 'RESPONSE_ERROR'
1176    description_template = "An invalid response was received from the server over %(proto)s."
1177
1178    def __init__(self, *args, **kwargs):
1179        super(UnknownResponseError, self).__init__(**kwargs)
1180        self.template_kwargs['description'] = "An invalid response was received from the server over %s" % (self.template_kwargs['proto'])
1181
1182class InvalidRcode(InvalidResponseError):
1183    '''
1184    >>> e = InvalidRcode(tcp=False, rcode='SERVFAIL')
1185    >>> e.description
1186    'The response had an invalid RCODE (SERVFAIL).'
1187    >>> e.terse_description
1188    'INVALID_RCODE:SERVFAIL'
1189    '''
1190
1191    _abstract = False
1192    code = 'INVALID_RCODE'
1193    description_template = "The response had an invalid RCODE (%(rcode)s)."
1194    terse_description_template = '%(code)s:%(rcode)s'
1195    required_params = InvalidResponseError.required_params + ['rcode']
1196
1197class NotAuthoritative(ResponseError):
1198    '''
1199    >>> e = NotAuthoritative()
1200    >>> e.description
1201    'The Authoritative Answer (AA) flag was not set in the response.'
1202    '''
1203
1204    _abstract = False
1205    code = 'NOT_AUTHORITATVE'
1206    description_template = "The Authoritative Answer (AA) flag was not set in the response."
1207    references = ['RFC 1035, Sec. 4.1.1']
1208    required_params = []
1209
1210class AuthoritativeReferral(ResponseError):
1211    '''
1212    >>> e = AuthoritativeReferral()
1213    >>> e.description
1214    'The Authoritative Answer (AA) flag was set in the referral response.'
1215    '''
1216
1217    _abstract = False
1218    code = 'AUTHORITATIVE_REFERRAL'
1219    description_template = "The Authoritative Answer (AA) flag was set in the referral response."
1220    references = ['RFC 1035, Sec. 4.1.1']
1221    required_params = []
1222
1223class RecursionNotAvailable(ResponseError):
1224    '''
1225    >>> e = RecursionNotAvailable()
1226    >>> e.description
1227    'Recursion was desired, but the Recursion Available (RA) flag was not set in the response.'
1228    '''
1229
1230    _abstract = False
1231    code = 'RECURSION_NOT_AVAILABLE'
1232    description_template = "Recursion was desired, but the Recursion Available (RA) flag was not set in the response."
1233    references = ['RFC 1035, Sec. 4.1.1']
1234    required_params = []
1235
1236class ResponseErrorWithCondition(ResponseError):
1237    description_template = "%(response_error_description)s until %(change)s%(query_specific_text)s."
1238    required_params = ['response_error', 'query_specific']
1239    use_effective_query_tag = False
1240
1241    def __init__(self, *args, **kwargs):
1242        super(ResponseErrorWithCondition, self).__init__(**kwargs)
1243        self.template_kwargs['response_error_description'] = self.template_kwargs['response_error'].description[:-1]
1244        if self.template_kwargs['query_specific']:
1245            self.template_kwargs['query_specific_text'] = ' (however, this server appeared to respond legitimately to other queries with %s)' % (self.precondition % self.template_kwargs)
1246        else:
1247            self.template_kwargs['query_specific_text'] = ''
1248
1249class ResponseErrorWithRequestFlag(ResponseErrorWithCondition):
1250    '''
1251    >>> e = ResponseErrorWithRequestFlag(response_error=Timeout(tcp=False, attempts=3), flag='RD', query_specific=False)
1252    >>> e.description
1253    'No response was received from the server over UDP (tried 3 times) until the RD flag was cleared.'
1254    '''
1255
1256    _abstract = False
1257    code = 'ERROR_WITH_REQUEST_FLAG'
1258    references = ['RFC 1035, Sec. 4.1.1']
1259    required_params = ResponseErrorWithCondition.required_params + ['flag']
1260
1261    def __init__(self, *args, **kwargs):
1262        self.precondition = 'the %(flag)s flag set'
1263        super(ResponseErrorWithRequestFlag, self).__init__(**kwargs)
1264        self.template_kwargs['change'] = 'the %s flag was cleared' % (self.template_kwargs['flag'])
1265
1266class ResponseErrorWithoutRequestFlag(ResponseErrorWithCondition):
1267    '''
1268    >>> e = ResponseErrorWithoutRequestFlag(response_error=Timeout(tcp=False, attempts=3), flag='RD', query_specific=False)
1269    >>> e.description
1270    'No response was received from the server over UDP (tried 3 times) until the RD flag was set.'
1271    '''
1272
1273    _abstract = False
1274    code = 'ERROR_WITHOUT_REQUEST_FLAG'
1275    references = ['RFC 1035, Sec. 4.1.1']
1276    required_params = ResponseErrorWithCondition.required_params + ['flag']
1277
1278    def __init__(self, *args, **kwargs):
1279        self.precondition = 'the %(flag)s flag cleared'
1280        super(ResponseErrorWithoutRequestFlag, self).__init__(**kwargs)
1281        self.template_kwargs['change'] = 'the %s flag was set' % (self.template_kwargs['flag'])
1282
1283class ResponseErrorWithEDNS(ResponseErrorWithCondition):
1284    '''
1285    >>> e = ResponseErrorWithEDNS(response_error=Timeout(tcp=False, attempts=3), query_specific=False)
1286    >>> e.description
1287    'No response was received from the server over UDP (tried 3 times) until EDNS was disabled.'
1288    '''
1289
1290    _abstract = False
1291    code = 'ERROR_WITH_EDNS'
1292    references = ['RFC 6891, Sec. 6.2.6']
1293
1294    def __init__(self, *args, **kwargs):
1295        self.precondition = 'EDNS enabled'
1296        super(ResponseErrorWithEDNS, self).__init__(**kwargs)
1297        self.template_kwargs['change'] = 'EDNS was disabled'
1298
1299class ResponseErrorWithEDNSVersion(ResponseErrorWithCondition):
1300    '''
1301    >>> e = ResponseErrorWithEDNSVersion(response_error=Timeout(tcp=False, attempts=3), edns_old=3, edns_new=0, query_specific=False)
1302    >>> e.description
1303    'No response was received from the server over UDP (tried 3 times) until the version of EDNS was changed from 3 to 0.'
1304    '''
1305
1306    _abstract = False
1307    code = 'ERROR_WITH_EDNS_VERSION'
1308    references = ['RFC 6891, Sec. 6.1.3']
1309    required_params = ResponseErrorWithCondition.required_params + ['edns_old', 'edns_new']
1310
1311    def __init__(self, *args, **kwargs):
1312        self.precondition = 'EDNS version %(edns_old)d'
1313        super(ResponseErrorWithEDNSVersion, self).__init__(**kwargs)
1314        self.template_kwargs['change'] = 'the version of EDNS was changed from %d to %d' % \
1315                (self.template_kwargs['edns_old'], self.template_kwargs['edns_new'])
1316
1317class ResponseErrorWithEDNSFlag(ResponseErrorWithCondition):
1318    '''
1319    >>> e = ResponseErrorWithEDNSFlag(response_error=Timeout(tcp=False, attempts=3), flag='DO', query_specific=False)
1320    >>> e.description
1321    'No response was received from the server over UDP (tried 3 times) until the DO EDNS flag was cleared.'
1322    '''
1323
1324    _abstract = False
1325    code = 'ERROR_WITH_EDNS_FLAG'
1326    references = ['RFC 6891, Sec. 6.1.4']
1327    required_params = ResponseErrorWithCondition.required_params + ['flag']
1328
1329    def __init__(self, *args, **kwargs):
1330        self.precondition = 'the %(flag)s EDNS flag set'
1331        super(ResponseErrorWithEDNSFlag, self).__init__(**kwargs)
1332        self.template_kwargs['change'] = 'the %s EDNS flag was cleared' % (self.template_kwargs['flag'])
1333
1334class ResponseErrorWithoutEDNSFlag(ResponseErrorWithCondition):
1335    '''
1336    >>> e = ResponseErrorWithoutEDNSFlag(response_error=Timeout(tcp=False, attempts=3), flag='DO', query_specific=False)
1337    >>> e.description
1338    'No response was received from the server over UDP (tried 3 times) until the DO EDNS flag was set.'
1339    '''
1340
1341    _abstract = False
1342    code = 'ERROR_WITHOUT_EDNS_FLAG'
1343    references = ['RFC 6891, Sec. 6.1.4']
1344    required_params = ResponseErrorWithCondition.required_params + ['flag']
1345
1346    def __init__(self, *args, **kwargs):
1347        self.precondition = 'the %(flag)s EDNS flag cleared'
1348        super(ResponseErrorWithoutEDNSFlag, self).__init__(**kwargs)
1349        self.template_kwargs['change'] = 'the %s EDNS flag was set' % (self.template_kwargs['flag'])
1350
1351class ResponseErrorWithEDNSOption(ResponseErrorWithCondition):
1352    '''
1353    >>> e = ResponseErrorWithEDNSOption(response_error=Timeout(tcp=False, attempts=3), option='NSID', query_specific=False)
1354    >>> e.description
1355    'No response was received from the server over UDP (tried 3 times) until the NSID EDNS option was removed.'
1356    '''
1357
1358    _abstract = False
1359    code = 'ERROR_WITH_EDNS_OPTION'
1360    references = ['RFC 6891, Sec. 6.1.2']
1361    required_params = ResponseErrorWithCondition.required_params + ['option']
1362
1363    def __init__(self, *args, **kwargs):
1364        self.precondition = 'the %(option)s EDNS option present'
1365        super(ResponseErrorWithEDNSOption, self).__init__(**kwargs)
1366        self.template_kwargs['change'] = 'the %s EDNS option was removed' % (self.template_kwargs['option'])
1367
1368class ResponseErrorWithoutEDNSOption(ResponseErrorWithCondition):
1369    '''
1370    >>> e = ResponseErrorWithoutEDNSOption(response_error=Timeout(tcp=False, attempts=3), option='NSID', query_specific=False)
1371    >>> e.description
1372    'No response was received from the server over UDP (tried 3 times) until the NSID EDNS option was added.'
1373    '''
1374
1375    _abstract = False
1376    code = 'ERROR_WITHOUT_EDNS_OPTION'
1377    references = ['RFC 6891, Sec. 6.1.2']
1378    required_params = ResponseErrorWithCondition.required_params + ['option']
1379
1380    def __init__(self, *args, **kwargs):
1381        self.precondition = 'without the %(option)s EDNS option'
1382        super(ResponseErrorWithoutEDNSOption, self).__init__(**kwargs)
1383        self.template_kwargs['change'] = 'the %s EDNS option was added' % (self.template_kwargs['option'])
1384
1385class EDNSError(ResponseError):
1386    pass
1387
1388class EDNSVersionMismatch(EDNSError):
1389    '''
1390    >>> e = EDNSVersionMismatch(request_version=1, response_version=0)
1391    >>> e.description
1392    'The server responded with EDNS version 0 when a request with EDNS version 1 was sent, instead of responding with RCODE BADVERS.'
1393    '''
1394
1395    _abstract = False
1396    code = 'EDNS_VERSION_MISMATCH'
1397    description_template = "The server responded with EDNS version %(response_version)d when a request with EDNS version %(request_version)d was sent, instead of responding with RCODE BADVERS."
1398    references = ['RFC 6891, Sec. 6.1.3']
1399    required_params = ['request_version', 'response_version']
1400
1401class EDNSIgnored(EDNSError):
1402    '''
1403    >>> e = EDNSIgnored()
1404    >>> e.description
1405    'The server responded with no OPT record, rather than with RCODE FORMERR.'
1406    '''
1407
1408    _abstract = False
1409    code = 'EDNS_IGNORED'
1410    description_template = 'The server responded with no OPT record, rather than with RCODE FORMERR.'
1411    references = ['RFC 6891, Sec. 7']
1412    required_params = []
1413
1414class EDNSSupportNoOpt(EDNSError):
1415    '''
1416    >>> e = EDNSSupportNoOpt()
1417    >>> e.description
1418    'The server appeared to understand EDNS by including RRSIG records, but its response included no OPT record.'
1419    '''
1420
1421    _abstract = False
1422    code = 'EDNS_SUPPORT_NO_OPT'
1423    description_template = 'The server appeared to understand EDNS by including RRSIG records, but its response included no OPT record.'
1424    references = ['RFC 6891, Sec. 7']
1425    required_params = []
1426
1427class GratuitousOPT(EDNSError):
1428    '''
1429    >>> e = GratuitousOPT()
1430    >>> e.description
1431    'The server responded with an OPT record, even though none was sent in the request.'
1432    '''
1433
1434    _abstract = False
1435    code = 'GRATUITOUS_OPT'
1436    description_template = 'The server responded with an OPT record, even though none was sent in the request.'
1437    references = ['RFC 6891, Sec. 6.1.1']
1438    required_params = []
1439
1440class ImplementedEDNSVersionNotProvided(EDNSError):
1441    '''
1442    >>> e = ImplementedEDNSVersionNotProvided(request_version=100, response_version=100)
1443    >>> e.description
1444    'The server responded with BADVERS to EDNS version 100 but responded with version 100 instead of providing the highest EDNS version it implements.'
1445    '''
1446
1447    _abstract = False
1448    code = 'IMPLEMENTED_EDNS_VERSION_NOT_PROVIDED'
1449    description_template = "The server responded with BADVERS to EDNS version %(request_version)d but responded with version %(response_version)d instead of providing the highest EDNS version it implements."
1450    references = ['RFC 6891, Sec. 6.1.3']
1451    required_params = ['request_version', 'response_version']
1452
1453class EDNSUndefinedFlagsSet(EDNSError):
1454    '''
1455    >>> e = EDNSUndefinedFlagsSet(flags=0x80)
1456    >>> e.description
1457    'The server set EDNS flags that are undefined: 0x80.'
1458    '''
1459
1460    _abstract = False
1461    code = 'EDNS_UNDEFINED_FLAGS_SET'
1462    description_template = 'The server set EDNS flags that are undefined: %(flags_text)s.'
1463    references = ['RFC 6891, Sec. 6.1.4']
1464    required_params = ['flags']
1465
1466    def __init__(self, **kwargs):
1467        super(EDNSUndefinedFlagsSet, self).__init__(**kwargs)
1468        self.template_kwargs['flags_text'] = '0x%x' % (self.template_kwargs['flags'])
1469
1470class DNSSECDowngrade(EDNSError):
1471    description_template = "DNSSEC was effectively downgraded because %(response_error_description)s with %(precondition)s."
1472    required_params = ['response_error']
1473    precondition = None
1474
1475    def __init__(self, *args, **kwargs):
1476        super(DNSSECDowngrade, self).__init__(**kwargs)
1477        self.template_kwargs['response_error_description'] = self.template_kwargs['response_error'].description[0].lower() + self.template_kwargs['response_error'].description[1:-1]
1478        self.template_kwargs['precondition'] = self.precondition
1479
1480class DNSSECDowngradeDOBitCleared(DNSSECDowngrade):
1481    '''
1482    >>> e = DNSSECDowngradeDOBitCleared(response_error=Timeout(tcp=False, attempts=3))
1483    >>> e.description
1484    'DNSSEC was effectively downgraded because no response was received from the server over UDP (tried 3 times) with the DO bit set.'
1485    '''
1486
1487    _abstract = False
1488    code = 'DNSSEC_DOWNGRADE_DO_CLEARED'
1489    precondition = 'the DO bit set'
1490    references = ['RFC 4035, Sec. 3.2.1']
1491
1492class DNSSECDowngradeEDNSDisabled(DNSSECDowngrade):
1493    '''
1494    >>> e = DNSSECDowngradeEDNSDisabled(response_error=Timeout(tcp=False, attempts=3), query_specific=False)
1495    >>> e.description
1496    'DNSSEC was effectively downgraded because no response was received from the server over UDP (tried 3 times) with EDNS enabled.'
1497    '''
1498
1499    _abstract = False
1500    code = 'DNSSEC_DOWNGRADE_EDNS_DISABLED'
1501    precondition = 'EDNS enabled'
1502    references = ['RFC 6891, Sec. 7', 'RFC 2671, Sec. 5.3']
1503
1504class DNSCookieError(ResponseError):
1505    pass
1506
1507class GratuitousCookie(DNSCookieError):
1508    '''
1509    >>> e = GratuitousCookie()
1510    >>> e.description
1511    'The server sent a COOKIE option when none was sent by the client.'
1512    '''
1513
1514    _abstract = False
1515    code = 'GRATUITOUS_COOKIE'
1516    description_template = 'The server sent a COOKIE option when none was sent by the client.'
1517    references = ['RFC 7873, Sec. 5.2.1']
1518
1519class MalformedCookieWithoutFORMERR(DNSCookieError):
1520    '''
1521    >>> e = MalformedCookieWithoutFORMERR()
1522    >>> e.description
1523    'The server appears to support DNS cookies but did not return a FORMERR status when issued a malformed COOKIE option.'
1524    '''
1525
1526    _abstract = False
1527    code = 'MALFORMED_COOKIE_WITHOUT_FORMERR'
1528    description_template = 'The server appears to support DNS cookies but did not return a FORMERR status when issued a malformed COOKIE option.'
1529    references = ['RFC 7873, Sec. 5.2.2']
1530
1531class NoCookieOption(DNSCookieError):
1532    '''
1533    >>> e = NoCookieOption()
1534    >>> e.description
1535    'The server appears to support DNS cookies but did not return a COOKIE option.'
1536    '''
1537
1538    _abstract = False
1539    code = 'NO_COOKIE_OPTION'
1540    description_template = 'The server appears to support DNS cookies but did not return a COOKIE option.'
1541    references = ['RFC 7873, Sec. 5.2.3']
1542
1543class NoServerCookieWithoutBADCOOKIE(DNSCookieError):
1544    '''
1545    >>> e = NoServerCookieWithoutBADCOOKIE()
1546    >>> e.description
1547    'The server appears to support DNS cookies but did not return a BADCOOKIE status when no server cookie was sent.'
1548    '''
1549
1550    _abstract = False
1551    code = 'NO_SERVER_COOKIE_WITHOUT_BADCOOKIE'
1552    description_template = 'The server appears to support DNS cookies but did not return a BADCOOKIE status when no server cookie was sent.'
1553    references = ['RFC 7873, Sec. 5.2.3']
1554
1555class InvalidServerCookieWithoutBADCOOKIE(DNSCookieError):
1556    '''
1557    >>> e = InvalidServerCookieWithoutBADCOOKIE()
1558    >>> e.description
1559    'The server appears to support DNS cookies but did not return a BADCOOKIE status when an invalid server cookie was sent.'
1560    '''
1561
1562    _abstract = False
1563    code = 'INVALID_SERVER_COOKIE_WITHOUT_BADCOOKIE'
1564    description_template = 'The server appears to support DNS cookies but did not return a BADCOOKIE status when an invalid server cookie was sent.'
1565    references = ['RFC 7873, Sec. 5.2.4']
1566
1567class NoServerCookie(DNSCookieError):
1568    '''
1569    >>> e = NoServerCookie()
1570    >>> e.description
1571    'The server appears to support DNS cookies but did not return a server cookie with its COOKIE option.'
1572    '''
1573
1574    _abstract = False
1575    code = 'NO_SERVER_COOKIE'
1576    description_template = 'The server appears to support DNS cookies but did not return a server cookie with its COOKIE option.'
1577    references = ['RFC 7873, Sec. 5.2.3']
1578
1579class ClientCookieMismatch(DNSCookieError):
1580    '''
1581    >>> e = ClientCookieMismatch()
1582    >>> e.description
1583    'The client cookie returned by the server did not match what was sent.'
1584    '''
1585
1586    _abstract = False
1587    code = 'CLIENT_COOKIE_MISMATCH'
1588    description_template = 'The client cookie returned by the server did not match what was sent.'
1589    references = ['RFC 7873, Sec. 5.3']
1590
1591class CookieInvalidLength(DNSCookieError):
1592    '''
1593    >>> e = CookieInvalidLength(length=61)
1594    >>> e.description
1595    'The cookie returned by the server had an invalid length of 61 bytes.'
1596    '''
1597
1598    _abstract = False
1599    code = 'COOKIE_INVALID_LENGTH'
1600    description_template = 'The cookie returned by the server had an invalid length of %(length)d bytes.'
1601    references = ['RFC 7873, Sec. 5.3']
1602    required_params = ['length']
1603
1604class UnableToRetrieveDNSSECRecords(ResponseError):
1605    '''
1606    >>> e = UnableToRetrieveDNSSECRecords()
1607    >>> e.description
1608    'The DNSSEC records necessary to validate the response could not be retrieved from the server.'
1609    '''
1610
1611    _abstract = False
1612    code = 'UNABLE_TO_RETRIEVE_DNSSEC_RECORDS'
1613    description_template = 'The DNSSEC records necessary to validate the response could not be retrieved from the server.'
1614    references = ['RFC 4035, Sec. 3.1.1', 'RFC 4035, Sec. 3.1.3']
1615    required_params = []
1616    use_effective_query_tag = False
1617
1618class MissingRRSIG(ResponseError):
1619    '''
1620    >>> e = MissingRRSIG()
1621    >>> e.description
1622    'No RRSIG covering the RRset was returned in the response.'
1623    '''
1624
1625    _abstract = False
1626    code = 'MISSING_RRSIG'
1627    description_template = 'No RRSIG covering the RRset was returned in the response.'
1628    references = ['RFC 4035, Sec. 3.1.1']
1629    required_params = []
1630
1631class MissingRRSIGForAlg(ResponseError):
1632    description_template = 'The %(source)s RRset for the zone included algorithm %(algorithm)s (%(algorithm_text)s), but no RRSIG with algorithm %(algorithm)d covering the RRset was returned in the response.'
1633    references = ['RFC 4035, Sec. 2.2', 'RFC 6840, Sec. 5.11']
1634    required_params = ['algorithm']
1635    source = None
1636
1637    def __init__(self, **kwargs):
1638        super(MissingRRSIGForAlg, self).__init__(**kwargs)
1639        self.template_kwargs['algorithm_text'] = dns.dnssec.algorithm_to_text(self.template_kwargs['algorithm'])
1640        self.template_kwargs['source'] = self.source
1641
1642class MissingRRSIGForAlgDNSKEY(MissingRRSIGForAlg):
1643    '''
1644    >>> e = MissingRRSIGForAlgDNSKEY(algorithm=5)
1645    >>> e.description
1646    'The DNSKEY RRset for the zone included algorithm 5 (RSASHA1), but no RRSIG with algorithm 5 covering the RRset was returned in the response.'
1647    '''
1648
1649    _abstract = False
1650    code = 'MISSING_RRSIG_FOR_ALG_DNSKEY'
1651    source = 'DNSKEY'
1652
1653class MissingRRSIGForAlgDS(MissingRRSIGForAlg):
1654    '''
1655    >>> e = MissingRRSIGForAlgDS(algorithm=5)
1656    >>> e.description
1657    'The DS RRset for the zone included algorithm 5 (RSASHA1), but no RRSIG with algorithm 5 covering the RRset was returned in the response.'
1658    '''
1659
1660    _abstract = False
1661    code = 'MISSING_RRSIG_FOR_ALG_DS'
1662    source = 'DS'
1663
1664class MissingRRSIGForAlgDLV(MissingRRSIGForAlg):
1665    '''
1666    >>> e = MissingRRSIGForAlgDLV(algorithm=5)
1667    >>> e.description
1668    'The DLV RRset for the zone included algorithm 5 (RSASHA1), but no RRSIG with algorithm 5 covering the RRset was returned in the response.'
1669    '''
1670
1671    _abstract = False
1672    code = 'MISSING_RRSIG_FOR_ALG_DLV'
1673    source = 'DLV'
1674
1675class MissingNSEC(ResponseError):
1676    description_template = 'No NSEC RR(s) were returned to validate the %(response)s response.'
1677    response = None
1678
1679    def __init__(self, **kwargs):
1680        super(MissingNSEC, self).__init__(**kwargs)
1681        self.template_kwargs['response'] = self.response
1682
1683class MissingNSECForNXDOMAIN(MissingNSEC):
1684    '''
1685    >>> e = MissingNSECForNXDOMAIN()
1686    >>> e.description
1687    'No NSEC RR(s) were returned to validate the NXDOMAIN response.'
1688    '''
1689
1690    _abstract = False
1691    code = 'MISSING_NSEC_FOR_NXDOMAIN'
1692    references = ['RFC 4035, Sec. 3.1.3.2', 'RFC 5155, Sec. 7.2.2']
1693    response = 'NXDOMAIN'
1694
1695class MissingNSECForNODATA(MissingNSEC):
1696    '''
1697    >>> e = MissingNSECForNODATA()
1698    >>> e.description
1699    'No NSEC RR(s) were returned to validate the NODATA response.'
1700    '''
1701
1702    _abstract = False
1703    code = 'MISSING_NSEC_FOR_NODATA'
1704    references = ['RFC 4035, Sec. 3.1.3.1', 'RFC 5155, Sec. 7.2.3', 'RFC 5155, Sec. 7.2.4']
1705    response = 'NODATA'
1706
1707class MissingNSECForWildcard(MissingNSEC):
1708    '''
1709    >>> e = MissingNSECForWildcard()
1710    >>> e.description
1711    'No NSEC RR(s) were returned to validate the wildcard response.'
1712    '''
1713
1714    _abstract = False
1715    code = 'MISSING_NSEC_FOR_WILDCARD'
1716    references = ['RFC 4035, Sec. 3.1.3.3', 'RFC 4035, Sec. 3.1.3.4', 'RFC 5155, Sec. 7.2.5', 'RFC 5155, Sec. 7.2.6']
1717    response = 'wildcard'
1718
1719class MissingSOA(ResponseError):
1720    description_template = 'No SOA RR was returned with the %(response)s response.'
1721    references = ['RFC 1034, Sec. 4.3.4']
1722    response = None
1723
1724    def __init__(self, **kwargs):
1725        super(MissingSOA, self).__init__(**kwargs)
1726        self.template_kwargs['response'] = self.response
1727
1728class MissingSOAForNXDOMAIN(MissingSOA):
1729    '''
1730    >>> e = MissingSOAForNXDOMAIN()
1731    >>> e.description
1732    'No SOA RR was returned with the NXDOMAIN response.'
1733    '''
1734
1735    _abstract = False
1736    code = 'MISSING_SOA_FOR_NXDOMAIN'
1737    references = MissingSOA.references + ['RFC 2308, Sec. 2.1']
1738    response = 'NXDOMAIN'
1739
1740class MissingSOAForNODATA(MissingSOA):
1741    '''
1742    >>> e = MissingSOAForNODATA()
1743    >>> e.description
1744    'No SOA RR was returned with the NODATA response.'
1745    '''
1746
1747    _abstract = False
1748    code = 'MISSING_SOA_FOR_NODATA'
1749    references = MissingSOA.references + ['RFC 2308, Sec. 2.2']
1750    response = 'NODATA'
1751
1752class UpwardReferral(ResponseError):
1753    _abstract = False
1754    code = 'UPWARD_REFERRAL'
1755    description_template = 'The response was an upward referral.'
1756    references = ['https://www.dns-oarc.net/oarc/articles/upward-referrals-considered-harmful']
1757
1758class SOAOwnerNotZone(ResponseError):
1759    description_template = 'An SOA RR with owner name (%(soa_owner_name)s) not matching the zone name (%(zone_name)s) was returned with the %(response)s response.'
1760    references = ['RFC 1034, Sec. 4.3.4']
1761    required_params = ['soa_owner_name', 'zone_name']
1762    response = None
1763
1764    def __init__(self, **kwargs):
1765        super(SOAOwnerNotZone, self).__init__(**kwargs)
1766        self.template_kwargs['response'] = self.response
1767
1768class SOAOwnerNotZoneForNXDOMAIN(SOAOwnerNotZone):
1769    '''
1770    >>> e = SOAOwnerNotZoneForNXDOMAIN(soa_owner_name='foo.baz.', zone_name='bar.')
1771    >>> e.description
1772    'An SOA RR with owner name (foo.baz.) not matching the zone name (bar.) was returned with the NXDOMAIN response.'
1773    '''
1774
1775    _abstract = False
1776    code = 'SOA_NOT_OWNER_FOR_NXDOMAIN'
1777    references = SOAOwnerNotZone.references + ['RFC 2308, Sec. 2.1']
1778    response = 'NXDOMAIN'
1779
1780class SOAOwnerNotZoneForNODATA(SOAOwnerNotZone):
1781    '''
1782    >>> e = SOAOwnerNotZoneForNODATA(soa_owner_name='foo.baz.', zone_name='bar.')
1783    >>> e.description
1784    'An SOA RR with owner name (foo.baz.) not matching the zone name (bar.) was returned with the NODATA response.'
1785    '''
1786
1787    _abstract = False
1788    code = 'SOA_NOT_OWNER_FOR_NODATA'
1789    references = SOAOwnerNotZone.references + ['RFC 2308, Sec. 2.2']
1790    response = 'NODATA'
1791
1792class InconsistentNXDOMAIN(ResponseError):
1793    '''
1794    >>> e = InconsistentNXDOMAIN(qname='foo.baz.', rdtype_nxdomain='NS', rdtype_noerror='A')
1795    >>> e.description
1796    'The server returned a no error (NOERROR) response when queried for foo.baz. having record data of type A, but returned a name error (NXDOMAIN) when queried for foo.baz. having record data of type NS.'
1797    '''
1798
1799    _abstract = False
1800    code = 'INCONSISTENT_NXDOMAIN'
1801    description_template = 'The server returned a no error (NOERROR) response when queried for %(qname)s having record data of type %(rdtype_noerror)s, but returned a name error (NXDOMAIN) when queried for %(qname)s having record data of type %(rdtype_nxdomain)s.'
1802    required_params = ['qname', 'rdtype_nxdomain', 'rdtype_noerror']
1803    references = ['RFC 1034, Sec. 4.3.2']
1804
1805class InconsistentNXDOMAINAncestry(ResponseError):
1806    '''
1807    >>> e = InconsistentNXDOMAINAncestry(qname='foo.baz.', ancestor_qname='baz.')
1808    >>> e.description
1809    "A query for foo.baz. results in a NOERROR response, while a query for its ancestor, baz., returns a name error (NXDOMAIN), which indicates that subdomains of baz., including foo.baz., don't exist."
1810    '''
1811
1812    _abstract = False
1813    code = 'INCONSISTENT_NXDOMAIN_ANCESTOR'
1814    description_template = "A query for %(qname)s results in a NOERROR response, while a query for its ancestor, %(ancestor_qname)s, returns a name error (NXDOMAIN), which indicates that subdomains of %(ancestor_qname)s, including %(qname)s, don't exist."
1815    required_params = ['qname', 'ancestor_qname']
1816    references = []
1817
1818class PMTUExceeded(ResponseError):
1819    '''
1820    >>> e = PMTUExceeded(pmtu_lower_bound=None, pmtu_upper_bound=None)
1821    >>> e.description
1822    'No response was received until the UDP payload size was decreased, indicating that the server might be attempting to send a payload that exceeds the path maximum transmission unit (PMTU) size.'
1823    >>> e = PMTUExceeded(pmtu_lower_bound=511, pmtu_upper_bound=513)
1824    >>> e.description
1825    'No response was received until the UDP payload size was decreased, indicating that the server might be attempting to send a payload that exceeds the path maximum transmission unit (PMTU) size. The PMTU was bounded between 511 and 513 bytes.'
1826    '''
1827
1828    _abstract = False
1829    code = 'PMTU_EXCEEDED'
1830    description_template = '%(description)s'
1831    required_params = ['pmtu_lower_bound', 'pmtu_upper_bound']
1832    references = ['RFC 6891, Sec. 6.2.6']
1833    use_effective_query_tag = False
1834
1835    def __init__(self, **kwargs):
1836        super(PMTUExceeded, self).__init__(**kwargs)
1837        self.template_kwargs['description'] = 'No response was received until the UDP payload size was decreased, indicating that the server might be attempting to send a payload that exceeds the path maximum transmission unit (PMTU) size.'
1838        if self.template_kwargs['pmtu_lower_bound'] is not None and self.template_kwargs['pmtu_upper_bound'] is not None:
1839            self.template_kwargs['description'] += ' The PMTU was bounded between %(pmtu_lower_bound)d and %(pmtu_upper_bound)d bytes.' % self.template_kwargs
1840
1841class ForeignClassData(ResponseError):
1842    section = None
1843    description_template = 'Data of class %(cls)s was found in the %(section)s section of the response.'
1844    references = ['RFC 1034', 'RFC 1035']
1845    required_params = ['cls']
1846
1847    def __init__(self, **kwargs):
1848        super(ForeignClassData, self).__init__(**kwargs)
1849        self.template_kwargs['section'] = self.section
1850
1851class ForeignClassDataAnswer(ForeignClassData):
1852    '''
1853    >>> e = ForeignClassDataAnswer(cls='CH')
1854    >>> e.description
1855    'Data of class CH was found in the Answer section of the response.'
1856    '''
1857    section = 'Answer'
1858    _abstract = False
1859    code = 'FOREIGN_CLASS_DATA_ANSWER'
1860
1861class ForeignClassDataAuthority(ForeignClassData):
1862    '''
1863    >>> e = ForeignClassDataAuthority(cls='CH')
1864    >>> e.description
1865    'Data of class CH was found in the Authority section of the response.'
1866    '''
1867    section = 'Authority'
1868    _abstract = False
1869    code = 'FOREIGN_CLASS_DATA_AUTHORITY'
1870
1871class ForeignClassDataAdditional(ForeignClassData):
1872    '''
1873    >>> e = ForeignClassDataAdditional(cls='CH')
1874    >>> e.description
1875    'Data of class CH was found in the Additional section of the response.'
1876    '''
1877    section = 'Additional'
1878    _abstract = False
1879    code = 'FOREIGN_CLASS_DATA_ADDITIONAL'
1880
1881class CasePreservationError(ResponseError):
1882    '''
1883    >>> e = CasePreservationError(qname='ExAmPlE.CoM')
1884    >>> e.description
1885    'The case of the query name (ExAmPlE.CoM) was not preserved in the Question section of the response.'
1886    '''
1887
1888    _abstract = False
1889    code = 'CASE_NOT_PRESERVED'
1890    description_template = '%(description)s'
1891    description_template = 'The case of the query name (%(qname)s) was not preserved in the Question section of the response.'
1892    required_params = ['qname']
1893
1894class DelegationError(DomainNameAnalysisError):
1895    pass
1896
1897class MissingSEPForAlg(DelegationError):
1898    '''
1899    >>> e = MissingSEPForAlg(algorithm=5, source='DS')
1900    >>> e.description
1901    "The DS RRset for the zone included algorithm 5 (RSASHA1), but no DS RR matched a DNSKEY with algorithm 5 that signs the zone's DNSKEY RRset."
1902    '''
1903
1904    _abstract = False
1905    code = 'MISSING_SEP_FOR_ALG'
1906    description_template = "The %(source)s RRset for the zone included algorithm %(algorithm)s (%(algorithm_text)s), but no %(source)s RR matched a DNSKEY with algorithm %(algorithm)d that signs the zone's DNSKEY RRset."
1907    references = ['RFC 4035, Sec. 2.2', 'RFC 6840, Sec. 5.11']
1908    required_params = ['algorithm']
1909
1910    def __init__(self, **kwargs):
1911        super(MissingSEPForAlg, self).__init__(**kwargs)
1912        self.template_kwargs['algorithm_text'] = dns.dnssec.algorithm_to_text(self.template_kwargs['algorithm'])
1913        try:
1914            self.template_kwargs['source'] = kwargs['source']
1915        except KeyError:
1916            raise TypeError('The "source" keyword argument is required for instantiation.')
1917
1918class NoSEP(DelegationError):
1919    '''
1920    >>> e = NoSEP(source='DS')
1921    >>> e.description
1922    'No valid RRSIGs made by a key corresponding to a DS RR were found covering the DNSKEY RRset, resulting in no secure entry point (SEP) into the zone.'
1923    '''
1924
1925    _abstract = False
1926    code = 'NO_SEP'
1927    description_template = "No valid RRSIGs made by a key corresponding to a DS RR were found covering the DNSKEY RRset, resulting in no secure entry point (SEP) into the zone."
1928    references = ['RFC 4035, Sec. 2.2', 'RFC 6840, Sec. 5.11']
1929    required_params = []
1930
1931    def __init__(self, **kwargs):
1932        super(NoSEP, self).__init__(**kwargs)
1933        try:
1934            self.template_kwargs['source'] = kwargs['source']
1935        except KeyError:
1936            raise TypeError('The "source" keyword argument is required for instantiation.')
1937
1938class NoNSInParent(DelegationError):
1939    '''
1940    >>> e = NoNSInParent(parent='baz.')
1941    >>> e.description
1942    'No delegation NS records were detected in the parent zone (baz.).  This results in an NXDOMAIN response to a DS query (for DNSSEC), even if the parent servers are authoritative for the child.'
1943    '''
1944
1945    _abstract = False
1946    code = 'NO_NS_IN_PARENT'
1947    description_template = "No delegation NS records were detected in the parent zone (%(parent)s).  This results in an NXDOMAIN response to a DS query (for DNSSEC), even if the parent servers are authoritative for the child."
1948    references = ['RFC 1034, Sec. 4.2.2']
1949    required_params = ['parent']
1950
1951class NoNSAddressesForIPVersion(DelegationError):
1952    version = None
1953    required_params = ['reference']
1954
1955    def __init__(self, *args, **kwargs):
1956        super(NoNSAddressesForIPVersion, self).__init__(**kwargs)
1957        self.template_kwargs['version'] = self.version
1958
1959class NoNSAddressesForIPv4(NoNSAddressesForIPVersion):
1960    '''
1961    >>> e = NoNSAddressesForIPv4(reference='parent')
1962    >>> e.description
1963    'No IPv4 addresses were found for NS records in the parent zone.'
1964    '''
1965
1966    _abstract = False
1967    code = 'NO_NS_ADDRESSES_FOR_IPV4'
1968    description_template = "No IPv%(version)d addresses were found for NS records in the %(reference)s zone."
1969    references = []
1970    version = 4
1971
1972class NoNSAddressesForIPv6(NoNSAddressesForIPVersion):
1973    '''
1974    >>> e = NoNSAddressesForIPv6(reference='parent')
1975    >>> e.description
1976    'No IPv6 addresses were found for NS records in the parent zone.'
1977    '''
1978
1979    _abstract = False
1980    code = 'NO_NS_ADDRESSES_FOR_IPV6'
1981    description_template = "No IPv%(version)d addresses were found for NS records in the %(reference)s zone."
1982    references = []
1983    version = 6
1984
1985class NSNameError(DelegationError):
1986    required_params = ['names']
1987
1988    def __init__(self, **kwargs):
1989        super(NSNameError, self).__init__(**kwargs)
1990        self.template_kwargs['names_text'] = ', '.join(self.template_kwargs['names'])
1991
1992class NSNameNotInChild(NSNameError):
1993    '''
1994    >>> e = NSNameNotInChild(names=('ns1.foo.baz.',), parent='baz.')
1995    >>> e.description
1996    'The following NS name(s) were found in the delegation NS RRset (i.e., in the baz. zone), but not in the authoritative NS RRset: ns1.foo.baz.'
1997    '''
1998
1999    _abstract = False
2000    code = 'NS_NAME_NOT_IN_CHILD'
2001    description_template = "The following NS name(s) were found in the delegation NS RRset (i.e., in the %(parent)s zone), but not in the authoritative NS RRset: %(names_text)s"
2002    required_params = NSNameError.required_params + ['parent']
2003
2004class NSNameNotInParent(NSNameError):
2005    '''
2006    >>> e = NSNameNotInParent(names=('ns1.foo.baz.',), parent='baz.')
2007    >>> e.description
2008    'The following NS name(s) were found in the authoritative NS RRset, but not in the delegation NS RRset (i.e., in the baz. zone): ns1.foo.baz.'
2009    '''
2010
2011    _abstract = False
2012    code = 'NS_NAME_NOT_IN_PARENT'
2013    description_template = "The following NS name(s) were found in the authoritative NS RRset, but not in the delegation NS RRset (i.e., in the %(parent)s zone): %(names_text)s"
2014    required_params = NSNameError.required_params + ['parent']
2015
2016class ErrorResolvingNSName(NSNameError):
2017    '''
2018    >>> e = ErrorResolvingNSName(names=('ns1.foo.baz.',))
2019    >>> e.description
2020    'There was an error resolving the following NS name(s) to address(es): ns1.foo.baz.'
2021    '''
2022
2023    _abstract = False
2024    code = 'ERROR_RESOLVING_NS_NAME'
2025    description_template = 'There was an error resolving the following NS name(s) to address(es): %(names_text)s'
2026
2027class MissingGlueForNSName(NSNameError):
2028    '''
2029    >>> e = MissingGlueForNSName(names=('ns1.foo.baz.',))
2030    >>> e.description
2031    'The following NS name(s) required glue, but no glue was returned in the referral: ns1.foo.baz.'
2032    '''
2033
2034    _abstract = False
2035    code = 'MISSING_GLUE_FOR_NS_NAME'
2036    description_template = "The following NS name(s) required glue, but no glue was returned in the referral: %(names_text)s"
2037
2038class NoAddressForNSName(NSNameError):
2039    '''
2040    >>> e = NoAddressForNSName(names=('ns1.foo.baz.',))
2041    >>> e.description
2042    'The following NS name(s) did not resolve to address(es): ns1.foo.baz.'
2043    '''
2044
2045    _abstract = False
2046    code = 'NO_ADDRESS_FOR_NS_NAME'
2047    description_template = "The following NS name(s) did not resolve to address(es): %(names_text)s"
2048
2049class PrivateAddressNS(NSNameError):
2050    pass
2051
2052class NSNameResolvesToPrivateIP(PrivateAddressNS):
2053    '''
2054    >>> e = NSNameResolvesToPrivateIP(names=('ns1.foo.baz.',))
2055    >>> e.description
2056    'The following NS name(s) resolved to IP address(es) in private IP address space: ns1.foo.baz.'
2057    '''
2058
2059    _abstract = False
2060    code = 'NS_NAME_PRIVATE_IP'
2061    description_template = "The following NS name(s) resolved to IP address(es) in private IP address space: %(names_text)s"
2062
2063class GlueReferencesPrivateIP(PrivateAddressNS):
2064    '''
2065    >>> e = GlueReferencesPrivateIP(names=('ns1.foo.baz.',))
2066    >>> e.description
2067    'Glue for the following NS name(s) referenced IP address(es) in private IP address space: ns1.foo.baz.'
2068    '''
2069
2070    _abstract = False
2071    code = 'GLUE_PRIVATE_IP'
2072    description_template = "Glue for the following NS name(s) referenced IP address(es) in private IP address space: %(names_text)s"
2073
2074class GlueMismatchError(DelegationError):
2075    '''
2076    >>> e = GlueMismatchError(name='ns1.foo.baz.', glue_addresses=('192.0.2.1',), auth_addresses=('192.0.2.2',))
2077    >>> e.description
2078    'The glue address(es) for ns1.foo.baz. (192.0.2.1) differed from its authoritative address(es) (192.0.2.2).'
2079    '''
2080
2081    _abstract = False
2082    code = 'GLUE_MISMATCH'
2083    description_template = 'The glue address(es) for %(name)s (%(glue_addresses_text)s) differed from its authoritative address(es) (%(auth_addresses_text)s).'
2084    required_params = ['name', 'glue_addresses', 'auth_addresses']
2085
2086    def __init__(self, **kwargs):
2087        super(GlueMismatchError, self).__init__(**kwargs)
2088        self.template_kwargs['glue_addresses_text'] = ', '.join(self.template_kwargs['glue_addresses'])
2089        self.template_kwargs['auth_addresses_text'] = ', '.join(self.template_kwargs['auth_addresses'])
2090
2091class MissingGlueIPv4(DelegationError):
2092    '''
2093    >>> e = MissingGlueIPv4(name='ns1.foo.baz.')
2094    >>> e.description
2095    'Authoritative A records exist for ns1.foo.baz., but there are no corresponding A glue records.'
2096    '''
2097
2098    _abstract = False
2099    code = 'MISSING_GLUE_IPV4'
2100    description_template = "Authoritative A records exist for %(name)s, but there are no corresponding A glue records."
2101    required_params = ['name']
2102
2103class MissingGlueIPv6(DelegationError):
2104    '''
2105    >>> e = MissingGlueIPv6(name='ns1.foo.baz.')
2106    >>> e.description
2107    'Authoritative AAAA records exist for ns1.foo.baz., but there are no corresponding AAAA glue records.'
2108    '''
2109
2110    _abstract = False
2111    code = 'MISSING_GLUE_IPV6'
2112    description_template = "Authoritative AAAA records exist for %(name)s, but there are no corresponding AAAA glue records."
2113    required_params = ['name']
2114
2115class ExtraGlueIPv4(DelegationError):
2116    '''
2117    >>> e = ExtraGlueIPv4(name='ns1.foo.baz.')
2118    >>> e.description
2119    'A glue records exist for ns1.foo.baz., but there are no corresponding authoritative A records.'
2120    '''
2121
2122    _abstract = False
2123    code = 'EXTRA_GLUE_IPV4'
2124    description_template = "A glue records exist for %(name)s, but there are no corresponding authoritative A records."
2125    required_params = ['name']
2126
2127class ExtraGlueIPv6(DelegationError):
2128    '''
2129    >>> e = ExtraGlueIPv6(name='ns1.foo.baz.')
2130    >>> e.description
2131    'AAAA glue records exist for ns1.foo.baz., but there are no corresponding authoritative AAAA records.'
2132    '''
2133
2134    _abstract = False
2135    code = 'EXTRA_GLUE_IPV6'
2136    description_template = "AAAA glue records exist for %(name)s, but there are no corresponding authoritative AAAA records."
2137    required_params = ['name']
2138
2139class ServerUnresponsive(DelegationError):
2140    description_template = "The server(s) were not responsive to queries over %(proto)s."
2141    proto = None
2142
2143    def __init__(self, **kwargs):
2144        super(ServerUnresponsive, self).__init__(**kwargs)
2145        self.template_kwargs['proto'] = self.proto
2146
2147class ServerUnresponsiveUDP(ServerUnresponsive):
2148    '''
2149    >>> e = ServerUnresponsiveUDP()
2150    >>> e.description
2151    'The server(s) were not responsive to queries over UDP.'
2152    '''
2153
2154    _abstract = False
2155    code = 'SERVER_UNRESPONSIVE_UDP'
2156    proto = 'UDP'
2157
2158class ServerUnresponsiveTCP(ServerUnresponsive):
2159    '''
2160    >>> e = ServerUnresponsiveTCP()
2161    >>> e.description
2162    'The server(s) were not responsive to queries over TCP.'
2163    '''
2164
2165    _abstract = False
2166    code = 'SERVER_UNRESPONSIVE_TCP'
2167    proto = 'TCP'
2168
2169class ServerInvalidResponseUDP(DelegationError):
2170    '''
2171    >>> e = ServerInvalidResponseUDP()
2172    >>> e.description
2173    'The server(s) responded over UDP with a malformed response or with an invalid RCODE.'
2174    '''
2175
2176    _abstract = False
2177    code = 'SERVER_INVALID_RESPONSE_UDP'
2178    description_template = 'The server(s) responded over UDP with a malformed response or with an invalid RCODE.'
2179
2180class ServerInvalidResponseTCP(DelegationError):
2181    '''
2182    >>> e = ServerInvalidResponseTCP()
2183    >>> e.description
2184    'The server(s) responded over TCP with a malformed response or with an invalid RCODE.'
2185    '''
2186
2187    _abstract = False
2188    code = 'SERVER_INVALID_RESPONSE_TCP'
2189    description_template = 'The server(s) responded over TCP with a malformed response or with an invalid RCODE.'
2190
2191class ServerNotAuthoritative(DelegationError):
2192    '''
2193    >>> e = ServerNotAuthoritative()
2194    >>> e.description
2195    'The server(s) did not respond authoritatively for the namespace.'
2196    '''
2197
2198    _abstract = False
2199    code = 'SERVER_NOT_AUTHORITATIVE'
2200    description_template = "The server(s) did not respond authoritatively for the namespace."
2201
2202class DNAMEError(DomainNameAnalysisError):
2203    pass
2204
2205class DNAMENoCNAME(DNAMEError):
2206    '''
2207    >>> e = DNAMENoCNAME()
2208    >>> e.description
2209    'No synthesized CNAME RR was found accompanying the DNAME record.'
2210    '''
2211    _abstract = False
2212    description_template = "No synthesized CNAME RR was found accompanying the DNAME record."
2213    code = 'DNAME_NO_CNAME'
2214
2215class DNAMETargetMismatch(DNAMEError):
2216    '''
2217    >>> e = DNAMETargetMismatch(included_target='foo.baz.', synthesized_target='bar.baz.')
2218    >>> e.description
2219    'The included CNAME RR is not a valid synthesis of the DNAME record (foo.baz. != bar.baz.).'
2220    '''
2221    _abstract = False
2222    description_template = "The included CNAME RR is not a valid synthesis of the DNAME record (%(included_target)s != %(synthesized_target)s)."
2223    code = 'DNAME_TARGET_MISMATCH'
2224    required_params = ['included_target', 'synthesized_target']
2225
2226class DNAMETTLZero(DNAMEError):
2227    '''
2228    >>> e = DNAMETTLZero()
2229    >>> e.description
2230    'The TTL of the synthesized CNAME RR is 0.'
2231    '''
2232    _abstract = False
2233    description_template = "The TTL of the synthesized CNAME RR is 0."
2234    code = 'DNAME_TTL_ZERO'
2235
2236class DNAMETTLMismatch(DNAMEError):
2237    '''
2238    >>> e = DNAMETTLMismatch(cname_ttl=50, dname_ttl=60)
2239    >>> e.description
2240    'The TTL of the synthesized CNAME RR (50) does not match the TTL of the DNAME record (60).'
2241    '''
2242    _abstract = False
2243    description_template = "The TTL of the synthesized CNAME RR (%(cname_ttl)d) does not match the TTL of the DNAME record (%(dname_ttl)d)."
2244    code = 'DNAME_TTL_MISMATCH'
2245    required_params = ['cname_ttl', 'dname_ttl']
2246
2247class DNSKEYError(DomainNameAnalysisError):
2248    pass
2249
2250class DNSKEYMissingFromServers(DNSKEYError):
2251    '''
2252    >>> e = DNSKEYMissingFromServers()
2253    >>> e.description
2254    'The DNSKEY RR was not found in the DNSKEY RRset returned by one or more servers.'
2255    '''
2256    _abstract = False
2257    description_template = "The DNSKEY RR was not found in the DNSKEY RRset returned by one or more servers."
2258    code = 'DNSKEY_MISSING_FROM_SERVERS'
2259
2260class DNSKEYNotAtZoneApex(DNSKEYError):
2261    '''
2262    >>> e = DNSKEYNotAtZoneApex(zone='foo.baz.', name='bar.foo.baz.')
2263    >>> e.description
2264    'The owner name of the DNSKEY RRset (bar.foo.baz.) does not match the zone apex (foo.baz.).'
2265    '''
2266    _abstract = False
2267    description_template = "The owner name of the DNSKEY RRset (%(name)s) does not match the zone apex (%(zone)s)."
2268    code = 'DNSKEY_NOT_AT_ZONE_APEX'
2269    required_params = ['zone', 'name']
2270
2271class DNSKEYBadLength(DNSKEYError):
2272    pass
2273
2274class DNSKEYZeroLength(DNSKEYBadLength):
2275    '''
2276    >>> e = DNSKEYZeroLength()
2277    >>> e.description
2278    'The length of the key is 0 bits.'
2279    '''
2280    _abstract = False
2281    description_template = 'The length of the key is 0 bits.'
2282    code = 'DNSKEY_ZERO_LENGTH'
2283    references = []
2284    required_params = []
2285
2286class DNSKEYBadLengthGOST(DNSKEYBadLength):
2287    '''
2288    >>> e = DNSKEYBadLengthGOST(length=500)
2289    >>> e.description
2290    'The length of the key is 500 bits, but a GOST public key (DNSSEC algorithm 12) must be 512 bits long.'
2291    '''
2292    _abstract = False
2293    description_template = 'The length of the key is %(length)d bits, but a GOST public key (DNSSEC algorithm 12) must be 512 bits long.'
2294    code = 'DNSKEY_BAD_LENGTH_GOST'
2295    references = ['RFC 5933, Sec. 5.1']
2296    required_params = ['length']
2297
2298class DNSKEYBadLengthECDSA(DNSKEYBadLength):
2299    curve = None
2300    algorithm = None
2301    correct_length = None
2302    description_template = 'The length of the key is %(length)d bits, but an ECDSA public key using Curve %(curve)s (DNSSEC algorithm %(algorithm)d) must be %(correct_length)d bits long.'
2303    references = ['RFC 6605, Sec. 4']
2304    required_params = ['length']
2305
2306    def __init__(self, **kwargs):
2307        super(DNSKEYBadLengthECDSA, self).__init__(**kwargs)
2308        self.template_kwargs['curve'] = self.curve
2309        self.template_kwargs['algorithm'] = self.algorithm
2310        self.template_kwargs['correct_length'] = self.correct_length
2311
2312class DNSKEYBadLengthECDSA256(DNSKEYBadLengthECDSA):
2313    '''
2314    >>> e = DNSKEYBadLengthECDSA256(length=500)
2315    >>> e.description
2316    'The length of the key is 500 bits, but an ECDSA public key using Curve P-256 (DNSSEC algorithm 13) must be 512 bits long.'
2317    '''
2318    curve = 'P-256'
2319    algorithm = 13
2320    correct_length = 512
2321    _abstract = False
2322    code = 'DNSKEY_BAD_LENGTH_ECDSA256'
2323
2324class DNSKEYBadLengthECDSA384(DNSKEYBadLengthECDSA):
2325    '''
2326    >>> e = DNSKEYBadLengthECDSA384(length=500)
2327    >>> e.description
2328    'The length of the key is 500 bits, but an ECDSA public key using Curve P-384 (DNSSEC algorithm 14) must be 768 bits long.'
2329    '''
2330    curve = 'P-384'
2331    algorithm = 14
2332    correct_length = 768
2333    _abstract = False
2334    code = 'DNSKEY_BAD_LENGTH_ECDSA384'
2335
2336class DNSKEYBadLengthEdDSA(DNSKEYBadLength):
2337    curve = None
2338    algorithm = None
2339    correct_length = None
2340    description_template = 'The length of the key is %(length)d bits, but an %(curve)s public key (DNSSEC algorithm %(algorithm)d) must be %(correct_length)d bits long.'
2341    references = ['RFC 8080, Sec. 3']
2342    required_params = ['length']
2343
2344    def __init__(self, **kwargs):
2345        super(DNSKEYBadLengthEdDSA, self).__init__(**kwargs)
2346        self.template_kwargs['curve'] = self.curve
2347        self.template_kwargs['algorithm'] = self.algorithm
2348        self.template_kwargs['correct_length'] = self.correct_length
2349
2350class DNSKEYBadLengthEd25519(DNSKEYBadLengthEdDSA):
2351    '''
2352    >>> e = DNSKEYBadLengthEd25519(length=500)
2353    >>> e.description
2354    'The length of the key is 500 bits, but an Ed25519 public key (DNSSEC algorithm 15) must be 256 bits long.'
2355    '''
2356    curve = 'Ed25519'
2357    algorithm = 15
2358    correct_length = 256
2359    _abstract = False
2360    code = 'DNSKEY_BAD_LENGTH_ED25519'
2361
2362class DNSKEYBadLengthEd448(DNSKEYBadLengthEdDSA):
2363    '''
2364    >>> e = DNSKEYBadLengthEd448(length=500)
2365    >>> e.description
2366    'The length of the key is 500 bits, but an Ed448 public key (DNSSEC algorithm 16) must be 456 bits long.'
2367    '''
2368    curve = 'Ed448'
2369    algorithm = 16
2370    correct_length = 456
2371    _abstract = False
2372    code = 'DNSKEY_BAD_LENGTH_ED448'
2373
2374class TrustAnchorError(DomainNameAnalysisError):
2375    pass
2376
2377class NoTrustAnchorSigning(TrustAnchorError):
2378    '''
2379    >>> e = NoTrustAnchorSigning(zone='foo.baz.')
2380    >>> e.description
2381    'One or more keys were designated as trust anchors for foo.baz., but none were found signing the DNSKEY RRset.'
2382    '''
2383    _abstract = False
2384    description_template = "One or more keys were designated as trust anchors for %(zone)s, but none were found signing the DNSKEY RRset."
2385    code = 'NO_TRUST_ANCHOR_SIGNING'
2386    required_params = ['zone']
2387
2388class RevokedNotSigning(DNSKEYError):
2389    '''
2390    >>> e = RevokedNotSigning()
2391    >>> e.description
2392    'The key was revoked but was not found signing the RRset.'
2393    '''
2394    _abstract = False
2395    description_template = "The key was revoked but was not found signing the RRset."
2396    code = 'REVOKED_NOT_SIGNING'
2397
2398class ZoneDataError(DomainNameAnalysisError):
2399    pass
2400
2401class CNAMEWithOtherData(ZoneDataError):
2402    '''
2403    >>> e = CNAMEWithOtherData(name='foo.')
2404    >>> e.description
2405    'The server returned CNAME for foo., but records of other types exist at that name.'
2406    '''
2407    _abstract = False
2408    description_template = "The server returned CNAME for %(name)s, but records of other types exist at that name."
2409    code = 'CNAME_WITH_OTHER_DATA'
2410    required_params = ['name']
2411    references = ['RFC 2181, Sec. 10.1']
2412
2413class CNAMELoop(ZoneDataError):
2414    '''
2415    >>> e = CNAMELoop()
2416    >>> e.description
2417    'This record results in a CNAME loop.'
2418    '''
2419    _abstract = False
2420    description_template = "This record results in a CNAME loop."
2421    code = 'CNAME_LOOP'
2422    references = ['RFC 1034, Sec. 3.6.2']
2423