]> git.siccegge.de Git - dane-monitoring-plugins.git/blob - check_dane/resolve.py
Add DNSSEC check, inital version
[dane-monitoring-plugins.git] / check_dane / resolve.py
1 #!/usr/bin/python3
2
3 import struct
4 import logging
5 from datetime import datetime
6
7 from unbound import ub_ctx, ub_strerror
8 from unbound import RR_TYPE_A, RR_TYPE_AAAA, RR_TYPE_RRSIG
9
10 from ldns import ldns_wire2pkt
11 from ldns import LDNS_SECTION_ANSWER
12
13
14 def _parse_rrsig_date(expirestring):
15 expires = datetime(int(expirestring[:4]),
16 int(expirestring[4:6]),
17 int(expirestring[6:8]),
18 int(expirestring[8:10]),
19 int(expirestring[10:12]),
20 int(expirestring[12:14]))
21 return expires
22
23
24 def format_address(data, datatype):
25 """Given a answer packet for an A or AAAA query, return the string
26 representation of the address
27 """
28 if datatype == RR_TYPE_A:
29 return '.'.join([str(a) for a in data])
30 elif datatype == RR_TYPE_AAAA:
31 data = list(struct.iter_unpack("!H", data))
32 return ":".join(["%x" % a for a in data])
33 else:
34 return None
35
36
37 def dnssec_verify_rrsig_validity(data, warn=-1, critical=0):
38 """Given a answer packet confirm validity of rrsigs (with safety) """
39 now = datetime.utcnow()
40
41 s, packet = ldns_wire2pkt(data)
42 if s != 0:
43 logging.error("Parsing packet failed with errorcode %d", s)
44 return 2
45
46 rrsigs = packet.rr_list_by_type(RR_TYPE_RRSIG, LDNS_SECTION_ANSWER).rrs()
47 rrsig = next(rrsigs)
48
49 expire = _parse_rrsig_date(str(rrsig.rrsig_expiration()))
50 incept = _parse_rrsig_date(str(rrsig.rrsig_inception()))
51
52 if now < incept:
53 logging.error("Signature not yet valid, only from %s", incept)
54 return 2
55
56 stillvalid = expire - now
57 deltastr = str(stillvalid).split(",")
58
59 if stillvalid.days < max(0, critical):
60 logging.error("expires in %8s,%16s", deltastr[0], deltastr[1])
61 return 2
62 elif stillvalid.days < warn:
63 logging.warning("expires in %8s,%16s", deltastr[0], deltastr[1])
64 return 1
65
66
67 class ResolverException(BaseException):
68 def __init__(self, message):
69 BaseException.__init__(self)
70 self.message = message
71
72
73 class Resolver:
74 def __init__(self, ancor, fwd=None):
75 self._resolver = ub_ctx()
76 status = self._resolver.add_ta_file(ancor)
77 if status != 0:
78 raise ResolverException(ub_strerror(status))
79
80 if fwd is not None:
81 status = self._resolver.set_fwd(fwd)
82 if status != 0:
83 raise ResolverException(ub_strerror(status))
84
85
86 def resolve(self, name, rrtype, secure=False):
87 status, result = self._resolver.resolve(name, rrtype)
88 if 0 != status:
89 raise ResolverException(ub_strerror(status))
90
91 if secure and not result.secure:
92 raise ResolverException("Response was not signed")
93
94 return result