]> git.siccegge.de Git - dane-monitoring-plugins.git/blob - check_dane/resolve.py
Utility for TLSA lookup
[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, RR_TYPE_SRV
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 def srv_lookup(name, resolver):
68 retval = []
69 result = resolver.resolve(name, rrtype=RR_TYPE_SRV)
70 for bytevalue in result.data.raw:
71 priority, weight, port = struct.unpack("!HHH", bytevalue[:6])
72 hostname = '.'.join(result.data.dname2str(bytevalue[6:]))
73 retval.append(((hostname, port), {'priority': priority, 'weight': weight}))
74 return retval
75
76
77 class ResolverException(BaseException):
78 def __init__(self, message):
79 BaseException.__init__(self)
80 self.message = message
81
82
83 class Resolver:
84 def __init__(self, ancor, fwd=None):
85 self._resolver = ub_ctx()
86 status = self._resolver.add_ta_file(ancor)
87 if status != 0:
88 raise ResolverException(ub_strerror(status))
89
90 if fwd is not None:
91 status = self._resolver.set_fwd(fwd)
92 if status != 0:
93 raise ResolverException(ub_strerror(status))
94
95
96 def resolve(self, name, rrtype, secure=False):
97 status, result = self._resolver.resolve(name, rrtype)
98 if 0 != status:
99 raise ResolverException(ub_strerror(status))
100
101 if secure and not result.secure:
102 raise ResolverException("Response was not signed")
103
104 return result