xref: /minix3/external/bsd/bind/dist/bin/python/dnssec-coverage.py.in (revision 00b67f09dd46474d133c95011a48590a8e8f94c7)
1*00b67f09SDavid van Moolenbroek#!@PYTHON@
2*00b67f09SDavid van Moolenbroek############################################################################
3*00b67f09SDavid van Moolenbroek# Copyright (C) 2013, 2014  Internet Systems Consortium, Inc. ("ISC")
4*00b67f09SDavid van Moolenbroek#
5*00b67f09SDavid van Moolenbroek# Permission to use, copy, modify, and/or distribute this software for any
6*00b67f09SDavid van Moolenbroek# purpose with or without fee is hereby granted, provided that the above
7*00b67f09SDavid van Moolenbroek# copyright notice and this permission notice appear in all copies.
8*00b67f09SDavid van Moolenbroek#
9*00b67f09SDavid van Moolenbroek# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10*00b67f09SDavid van Moolenbroek# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11*00b67f09SDavid van Moolenbroek# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12*00b67f09SDavid van Moolenbroek# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13*00b67f09SDavid van Moolenbroek# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14*00b67f09SDavid van Moolenbroek# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15*00b67f09SDavid van Moolenbroek# PERFORMANCE OF THIS SOFTWARE.
16*00b67f09SDavid van Moolenbroek############################################################################
17*00b67f09SDavid van Moolenbroek
18*00b67f09SDavid van Moolenbroekimport argparse
19*00b67f09SDavid van Moolenbroekimport os
20*00b67f09SDavid van Moolenbroekimport glob
21*00b67f09SDavid van Moolenbroekimport sys
22*00b67f09SDavid van Moolenbroekimport re
23*00b67f09SDavid van Moolenbroekimport time
24*00b67f09SDavid van Moolenbroekimport calendar
25*00b67f09SDavid van Moolenbroekfrom collections import defaultdict
26*00b67f09SDavid van Moolenbroekimport pprint
27*00b67f09SDavid van Moolenbroek
28*00b67f09SDavid van Moolenbroekprog='dnssec-coverage'
29*00b67f09SDavid van Moolenbroek
30*00b67f09SDavid van Moolenbroek# These routines permit platform-independent location of BIND 9 tools
31*00b67f09SDavid van Moolenbroekif os.name == 'nt':
32*00b67f09SDavid van Moolenbroek    import win32con
33*00b67f09SDavid van Moolenbroek    import win32api
34*00b67f09SDavid van Moolenbroek
35*00b67f09SDavid van Moolenbroekdef prefix(bindir = ''):
36*00b67f09SDavid van Moolenbroek    if os.name != 'nt':
37*00b67f09SDavid van Moolenbroek        return os.path.join('@prefix@', bindir)
38*00b67f09SDavid van Moolenbroek
39*00b67f09SDavid van Moolenbroek    bind_subkey = "Software\\ISC\\BIND"
40*00b67f09SDavid van Moolenbroek    hKey = None
41*00b67f09SDavid van Moolenbroek    keyFound = True
42*00b67f09SDavid van Moolenbroek    try:
43*00b67f09SDavid van Moolenbroek        hKey = win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE, bind_subkey)
44*00b67f09SDavid van Moolenbroek    except:
45*00b67f09SDavid van Moolenbroek        keyFound = False
46*00b67f09SDavid van Moolenbroek    if keyFound:
47*00b67f09SDavid van Moolenbroek        try:
48*00b67f09SDavid van Moolenbroek            (namedBase, _) = win32api.RegQueryValueEx(hKey, "InstallDir")
49*00b67f09SDavid van Moolenbroek        except:
50*00b67f09SDavid van Moolenbroek            keyFound = False
51*00b67f09SDavid van Moolenbroek        win32api.RegCloseKey(hKey)
52*00b67f09SDavid van Moolenbroek    if keyFound:
53*00b67f09SDavid van Moolenbroek        return os.path.join(namedBase, bindir)
54*00b67f09SDavid van Moolenbroek    return os.path.join(win32api.GetSystemDirectory(), bindir)
55*00b67f09SDavid van Moolenbroek
56*00b67f09SDavid van Moolenbroek########################################################################
57*00b67f09SDavid van Moolenbroek# Class Event
58*00b67f09SDavid van Moolenbroek########################################################################
59*00b67f09SDavid van Moolenbroekclass Event:
60*00b67f09SDavid van Moolenbroek    """ A discrete key metadata event, e.g., Publish, Activate, Inactive,
61*00b67f09SDavid van Moolenbroek    Delete. Stores the date of the event, and identifying information about
62*00b67f09SDavid van Moolenbroek    the key to which the event will occur."""
63*00b67f09SDavid van Moolenbroek
64*00b67f09SDavid van Moolenbroek    def __init__(self, _what, _key):
65*00b67f09SDavid van Moolenbroek        now = time.time()
66*00b67f09SDavid van Moolenbroek        self.what = _what
67*00b67f09SDavid van Moolenbroek        self.when = _key.metadata[_what]
68*00b67f09SDavid van Moolenbroek        self.key = _key
69*00b67f09SDavid van Moolenbroek        self.keyid = _key.keyid
70*00b67f09SDavid van Moolenbroek        self.sep = _key.sep
71*00b67f09SDavid van Moolenbroek        self.zone = _key.zone
72*00b67f09SDavid van Moolenbroek        self.alg = _key.alg
73*00b67f09SDavid van Moolenbroek
74*00b67f09SDavid van Moolenbroek    def __repr__(self):
75*00b67f09SDavid van Moolenbroek        return repr((self.when, self.what, self.keyid, self.sep,
76*00b67f09SDavid van Moolenbroek                     self.zone, self.alg))
77*00b67f09SDavid van Moolenbroek
78*00b67f09SDavid van Moolenbroek    def showtime(self):
79*00b67f09SDavid van Moolenbroek        return time.strftime("%a %b %d %H:%M:%S UTC %Y", self.when)
80*00b67f09SDavid van Moolenbroek
81*00b67f09SDavid van Moolenbroek    def showkey(self):
82*00b67f09SDavid van Moolenbroek        return self.key.showkey()
83*00b67f09SDavid van Moolenbroek
84*00b67f09SDavid van Moolenbroek    def showkeytype(self):
85*00b67f09SDavid van Moolenbroek        return self.key.showkeytype()
86*00b67f09SDavid van Moolenbroek
87*00b67f09SDavid van Moolenbroek########################################################################
88*00b67f09SDavid van Moolenbroek# Class Key
89*00b67f09SDavid van Moolenbroek########################################################################
90*00b67f09SDavid van Moolenbroekclass Key:
91*00b67f09SDavid van Moolenbroek    """An individual DNSSEC key.  Identified by path, zone, algorithm, keyid.
92*00b67f09SDavid van Moolenbroek    Contains a dictionary of metadata events."""
93*00b67f09SDavid van Moolenbroek
94*00b67f09SDavid van Moolenbroek    def __init__(self, keyname):
95*00b67f09SDavid van Moolenbroek        directory = os.path.dirname(keyname)
96*00b67f09SDavid van Moolenbroek        key = os.path.basename(keyname)
97*00b67f09SDavid van Moolenbroek        (zone, alg, keyid) = key.split('+')
98*00b67f09SDavid van Moolenbroek        keyid = keyid.split('.')[0]
99*00b67f09SDavid van Moolenbroek        key = [zone, alg, keyid]
100*00b67f09SDavid van Moolenbroek        key_file = directory + os.sep + '+'.join(key) + ".key"
101*00b67f09SDavid van Moolenbroek        private_file = directory + os.sep + '+'.join(key) + ".private"
102*00b67f09SDavid van Moolenbroek
103*00b67f09SDavid van Moolenbroek        self.zone = zone[1:-1]
104*00b67f09SDavid van Moolenbroek        self.alg = int(alg)
105*00b67f09SDavid van Moolenbroek        self.keyid = int(keyid)
106*00b67f09SDavid van Moolenbroek
107*00b67f09SDavid van Moolenbroek        kfp = open(key_file, "r")
108*00b67f09SDavid van Moolenbroek        for line in kfp:
109*00b67f09SDavid van Moolenbroek            if line[0] == ';':
110*00b67f09SDavid van Moolenbroek                continue
111*00b67f09SDavid van Moolenbroek            tokens = line.split()
112*00b67f09SDavid van Moolenbroek            if not tokens:
113*00b67f09SDavid van Moolenbroek                continue
114*00b67f09SDavid van Moolenbroek
115*00b67f09SDavid van Moolenbroek            if tokens[1].lower() in ('in', 'ch', 'hs'):
116*00b67f09SDavid van Moolenbroek                septoken = 3
117*00b67f09SDavid van Moolenbroek                self.ttl = args.keyttl
118*00b67f09SDavid van Moolenbroek                if not self.ttl:
119*00b67f09SDavid van Moolenbroek                    vspace()
120*00b67f09SDavid van Moolenbroek                    print("WARNING: Unable to determine TTL for DNSKEY %s." %
121*00b67f09SDavid van Moolenbroek                           self.showkey())
122*00b67f09SDavid van Moolenbroek                    print("\t Using 1 day (86400 seconds); re-run with the -d "
123*00b67f09SDavid van Moolenbroek                          "option for more\n\t accurate results.")
124*00b67f09SDavid van Moolenbroek                    self.ttl = 86400
125*00b67f09SDavid van Moolenbroek            else:
126*00b67f09SDavid van Moolenbroek                septoken = 4
127*00b67f09SDavid van Moolenbroek                self.ttl = int(tokens[1]) if not args.keyttl else args.keyttl
128*00b67f09SDavid van Moolenbroek
129*00b67f09SDavid van Moolenbroek            if (int(tokens[septoken]) & 0x1) == 1:
130*00b67f09SDavid van Moolenbroek                self.sep = True
131*00b67f09SDavid van Moolenbroek            else:
132*00b67f09SDavid van Moolenbroek                self.sep = False
133*00b67f09SDavid van Moolenbroek        kfp.close()
134*00b67f09SDavid van Moolenbroek
135*00b67f09SDavid van Moolenbroek        pfp = open(private_file, "rU")
136*00b67f09SDavid van Moolenbroek        propDict = dict()
137*00b67f09SDavid van Moolenbroek        for propLine in pfp:
138*00b67f09SDavid van Moolenbroek            propDef = propLine.strip()
139*00b67f09SDavid van Moolenbroek            if len(propDef) == 0:
140*00b67f09SDavid van Moolenbroek                continue
141*00b67f09SDavid van Moolenbroek            if propDef[0] in ('!', '#'):
142*00b67f09SDavid van Moolenbroek                continue
143*00b67f09SDavid van Moolenbroek            punctuation = [propDef.find(c) for c in ':= '] + [len(propDef)]
144*00b67f09SDavid van Moolenbroek            found = min([ pos for pos in punctuation if pos != -1 ])
145*00b67f09SDavid van Moolenbroek            name = propDef[:found].rstrip()
146*00b67f09SDavid van Moolenbroek            value =  propDef[found:].lstrip(":= ").rstrip()
147*00b67f09SDavid van Moolenbroek            propDict[name] = value
148*00b67f09SDavid van Moolenbroek
149*00b67f09SDavid van Moolenbroek        if("Publish" in propDict):
150*00b67f09SDavid van Moolenbroek            propDict["Publish"] = time.strptime(propDict["Publish"],
151*00b67f09SDavid van Moolenbroek                                                "%Y%m%d%H%M%S")
152*00b67f09SDavid van Moolenbroek
153*00b67f09SDavid van Moolenbroek        if("Activate" in propDict):
154*00b67f09SDavid van Moolenbroek            propDict["Activate"] = time.strptime(propDict["Activate"],
155*00b67f09SDavid van Moolenbroek                                                 "%Y%m%d%H%M%S")
156*00b67f09SDavid van Moolenbroek
157*00b67f09SDavid van Moolenbroek        if("Inactive" in propDict):
158*00b67f09SDavid van Moolenbroek            propDict["Inactive"] = time.strptime(propDict["Inactive"],
159*00b67f09SDavid van Moolenbroek                                                 "%Y%m%d%H%M%S")
160*00b67f09SDavid van Moolenbroek
161*00b67f09SDavid van Moolenbroek        if("Delete" in propDict):
162*00b67f09SDavid van Moolenbroek            propDict["Delete"] = time.strptime(propDict["Delete"],
163*00b67f09SDavid van Moolenbroek                                               "%Y%m%d%H%M%S")
164*00b67f09SDavid van Moolenbroek
165*00b67f09SDavid van Moolenbroek        if("Revoke" in propDict):
166*00b67f09SDavid van Moolenbroek            propDict["Revoke"] = time.strptime(propDict["Revoke"],
167*00b67f09SDavid van Moolenbroek                                               "%Y%m%d%H%M%S")
168*00b67f09SDavid van Moolenbroek        pfp.close()
169*00b67f09SDavid van Moolenbroek        self.metadata = propDict
170*00b67f09SDavid van Moolenbroek
171*00b67f09SDavid van Moolenbroek    def showkey(self):
172*00b67f09SDavid van Moolenbroek        return "%s/%03d/%05d" % (self.zone, self.alg, self.keyid);
173*00b67f09SDavid van Moolenbroek
174*00b67f09SDavid van Moolenbroek    def showkeytype(self):
175*00b67f09SDavid van Moolenbroek        return ("KSK" if self.sep else "ZSK")
176*00b67f09SDavid van Moolenbroek
177*00b67f09SDavid van Moolenbroek    # ensure that the gap between Publish and Activate is big enough
178*00b67f09SDavid van Moolenbroek    def check_prepub(self):
179*00b67f09SDavid van Moolenbroek        now = time.time()
180*00b67f09SDavid van Moolenbroek
181*00b67f09SDavid van Moolenbroek        if (not "Activate" in self.metadata):
182*00b67f09SDavid van Moolenbroek            debug_print("No Activate information in key: %s" % self.showkey())
183*00b67f09SDavid van Moolenbroek            return False
184*00b67f09SDavid van Moolenbroek        a = calendar.timegm(self.metadata["Activate"])
185*00b67f09SDavid van Moolenbroek
186*00b67f09SDavid van Moolenbroek        if (not "Publish" in self.metadata):
187*00b67f09SDavid van Moolenbroek            debug_print("No Publish information in key: %s" % self.showkey())
188*00b67f09SDavid van Moolenbroek            if a > now:
189*00b67f09SDavid van Moolenbroek                vspace()
190*00b67f09SDavid van Moolenbroek                print("WARNING: Key %s (%s) is scheduled for activation but \n"
191*00b67f09SDavid van Moolenbroek                  "\t not for publication." %
192*00b67f09SDavid van Moolenbroek                  (self.showkey(), self.showkeytype()))
193*00b67f09SDavid van Moolenbroek            return False
194*00b67f09SDavid van Moolenbroek        p = calendar.timegm(self.metadata["Publish"])
195*00b67f09SDavid van Moolenbroek
196*00b67f09SDavid van Moolenbroek        now = time.time()
197*00b67f09SDavid van Moolenbroek        if p < now and a < now:
198*00b67f09SDavid van Moolenbroek            return True
199*00b67f09SDavid van Moolenbroek
200*00b67f09SDavid van Moolenbroek        if p == a:
201*00b67f09SDavid van Moolenbroek            vspace()
202*00b67f09SDavid van Moolenbroek            print ("WARNING: %s (%s) is scheduled to be published and\n"
203*00b67f09SDavid van Moolenbroek                   "\t activated at the same time. This could result in a\n"
204*00b67f09SDavid van Moolenbroek                   "\t coverage gap if the zone was previously signed." %
205*00b67f09SDavid van Moolenbroek                   (self.showkey(), self.showkeytype()))
206*00b67f09SDavid van Moolenbroek            print("\t Activation should be at least %s after publication."
207*00b67f09SDavid van Moolenbroek                    % duration(self.ttl))
208*00b67f09SDavid van Moolenbroek            return True
209*00b67f09SDavid van Moolenbroek
210*00b67f09SDavid van Moolenbroek        if a < p:
211*00b67f09SDavid van Moolenbroek            vspace()
212*00b67f09SDavid van Moolenbroek            print("WARNING: Key %s (%s) is active before it is published" %
213*00b67f09SDavid van Moolenbroek                  (self.showkey(), self.showkeytype()))
214*00b67f09SDavid van Moolenbroek            return False
215*00b67f09SDavid van Moolenbroek
216*00b67f09SDavid van Moolenbroek        if (a - p < self.ttl):
217*00b67f09SDavid van Moolenbroek            vspace()
218*00b67f09SDavid van Moolenbroek            print("WARNING: Key %s (%s) is activated too soon after\n"
219*00b67f09SDavid van Moolenbroek                  "\t publication; this could result in coverage gaps due to\n"
220*00b67f09SDavid van Moolenbroek                  "\t resolver caches containing old data."
221*00b67f09SDavid van Moolenbroek                  % (self.showkey(), self.showkeytype()))
222*00b67f09SDavid van Moolenbroek            print("\t Activation should be at least %s after publication." %
223*00b67f09SDavid van Moolenbroek                  duration(self.ttl))
224*00b67f09SDavid van Moolenbroek            return False
225*00b67f09SDavid van Moolenbroek
226*00b67f09SDavid van Moolenbroek        return True
227*00b67f09SDavid van Moolenbroek
228*00b67f09SDavid van Moolenbroek    # ensure that the gap between Inactive and Delete is big enough
229*00b67f09SDavid van Moolenbroek    def check_postpub(self, timespan = None):
230*00b67f09SDavid van Moolenbroek        if not timespan:
231*00b67f09SDavid van Moolenbroek            timespan = self.ttl
232*00b67f09SDavid van Moolenbroek
233*00b67f09SDavid van Moolenbroek        now = time.time()
234*00b67f09SDavid van Moolenbroek
235*00b67f09SDavid van Moolenbroek        if (not "Delete" in self.metadata):
236*00b67f09SDavid van Moolenbroek            debug_print("No Delete information in key: %s" % self.showkey())
237*00b67f09SDavid van Moolenbroek            return False
238*00b67f09SDavid van Moolenbroek        d = calendar.timegm(self.metadata["Delete"])
239*00b67f09SDavid van Moolenbroek
240*00b67f09SDavid van Moolenbroek        if (not "Inactive" in self.metadata):
241*00b67f09SDavid van Moolenbroek            debug_print("No Inactive information in key: %s" % self.showkey())
242*00b67f09SDavid van Moolenbroek            if d > now:
243*00b67f09SDavid van Moolenbroek                vspace()
244*00b67f09SDavid van Moolenbroek                print("WARNING: Key %s (%s) is scheduled for deletion but\n"
245*00b67f09SDavid van Moolenbroek                      "\t not for inactivation." %
246*00b67f09SDavid van Moolenbroek                      (self.showkey(), self.showkeytype()))
247*00b67f09SDavid van Moolenbroek            return False
248*00b67f09SDavid van Moolenbroek        i = calendar.timegm(self.metadata["Inactive"])
249*00b67f09SDavid van Moolenbroek
250*00b67f09SDavid van Moolenbroek        if d < now and i < now:
251*00b67f09SDavid van Moolenbroek            return True
252*00b67f09SDavid van Moolenbroek
253*00b67f09SDavid van Moolenbroek        if (d < i):
254*00b67f09SDavid van Moolenbroek            vspace()
255*00b67f09SDavid van Moolenbroek            print("WARNING: Key %s (%s) is scheduled for deletion before\n"
256*00b67f09SDavid van Moolenbroek                  "\t inactivation." % (self.showkey(), self.showkeytype()))
257*00b67f09SDavid van Moolenbroek            return False
258*00b67f09SDavid van Moolenbroek
259*00b67f09SDavid van Moolenbroek        if (d - i < timespan):
260*00b67f09SDavid van Moolenbroek            vspace()
261*00b67f09SDavid van Moolenbroek            print("WARNING: Key %s (%s) scheduled for deletion too soon after\n"
262*00b67f09SDavid van Moolenbroek                  "\t deactivation; this may result in coverage gaps due to\n"
263*00b67f09SDavid van Moolenbroek                  "\t resolver caches containing old data."
264*00b67f09SDavid van Moolenbroek                  % (self.showkey(), self.showkeytype()))
265*00b67f09SDavid van Moolenbroek            print("\t Deletion should be at least %s after inactivation." %
266*00b67f09SDavid van Moolenbroek                  duration(timespan))
267*00b67f09SDavid van Moolenbroek            return False
268*00b67f09SDavid van Moolenbroek
269*00b67f09SDavid van Moolenbroek        return True
270*00b67f09SDavid van Moolenbroek
271*00b67f09SDavid van Moolenbroek########################################################################
272*00b67f09SDavid van Moolenbroek# class Zone
273*00b67f09SDavid van Moolenbroek########################################################################
274*00b67f09SDavid van Moolenbroekclass Zone:
275*00b67f09SDavid van Moolenbroek    """Stores data about a specific zone"""
276*00b67f09SDavid van Moolenbroek
277*00b67f09SDavid van Moolenbroek    def __init__(self, _name, _keyttl = None, _maxttl = None):
278*00b67f09SDavid van Moolenbroek        self.name = _name
279*00b67f09SDavid van Moolenbroek        self.keyttl = _keyttl
280*00b67f09SDavid van Moolenbroek        self.maxttl = _maxttl
281*00b67f09SDavid van Moolenbroek
282*00b67f09SDavid van Moolenbroek    def load(self, filename):
283*00b67f09SDavid van Moolenbroek        if not args.compilezone:
284*00b67f09SDavid van Moolenbroek            sys.stderr.write(prog + ': FATAL: "named-compilezone" not found\n')
285*00b67f09SDavid van Moolenbroek            exit(1)
286*00b67f09SDavid van Moolenbroek
287*00b67f09SDavid van Moolenbroek        if not self.name:
288*00b67f09SDavid van Moolenbroek            return
289*00b67f09SDavid van Moolenbroek
290*00b67f09SDavid van Moolenbroek        maxttl = keyttl = None
291*00b67f09SDavid van Moolenbroek
292*00b67f09SDavid van Moolenbroek        fp = os.popen("%s -o - %s %s 2> /dev/null" %
293*00b67f09SDavid van Moolenbroek                      (args.compilezone, self.name, filename))
294*00b67f09SDavid van Moolenbroek        for line in fp:
295*00b67f09SDavid van Moolenbroek            fields = line.split()
296*00b67f09SDavid van Moolenbroek            if not maxttl or int(fields[1]) > maxttl:
297*00b67f09SDavid van Moolenbroek                maxttl = int(fields[1])
298*00b67f09SDavid van Moolenbroek            if fields[3] == "DNSKEY":
299*00b67f09SDavid van Moolenbroek                keyttl = int(fields[1])
300*00b67f09SDavid van Moolenbroek        fp.close()
301*00b67f09SDavid van Moolenbroek
302*00b67f09SDavid van Moolenbroek        self.keyttl = keyttl
303*00b67f09SDavid van Moolenbroek        self.maxttl = maxttl
304*00b67f09SDavid van Moolenbroek
305*00b67f09SDavid van Moolenbroek############################################################################
306*00b67f09SDavid van Moolenbroek# debug_print:
307*00b67f09SDavid van Moolenbroek############################################################################
308*00b67f09SDavid van Moolenbroekdef debug_print(debugVar):
309*00b67f09SDavid van Moolenbroek    """pretty print a variable iff debug mode is enabled"""
310*00b67f09SDavid van Moolenbroek    if not args.debug_mode:
311*00b67f09SDavid van Moolenbroek        return
312*00b67f09SDavid van Moolenbroek    if type(debugVar) == str:
313*00b67f09SDavid van Moolenbroek        print("DEBUG: " + debugVar)
314*00b67f09SDavid van Moolenbroek    else:
315*00b67f09SDavid van Moolenbroek        print("DEBUG: " + pprint.pformat(debugVar))
316*00b67f09SDavid van Moolenbroek    return
317*00b67f09SDavid van Moolenbroek
318*00b67f09SDavid van Moolenbroek############################################################################
319*00b67f09SDavid van Moolenbroek# vspace:
320*00b67f09SDavid van Moolenbroek############################################################################
321*00b67f09SDavid van Moolenbroek_firstline = True
322*00b67f09SDavid van Moolenbroekdef vspace():
323*00b67f09SDavid van Moolenbroek    """adds vertical space between two sections of output text if and only
324*00b67f09SDavid van Moolenbroek    if this is *not* the first section being printed"""
325*00b67f09SDavid van Moolenbroek    global _firstline
326*00b67f09SDavid van Moolenbroek    if _firstline:
327*00b67f09SDavid van Moolenbroek        _firstline = False
328*00b67f09SDavid van Moolenbroek    else:
329*00b67f09SDavid van Moolenbroek        print
330*00b67f09SDavid van Moolenbroek
331*00b67f09SDavid van Moolenbroek############################################################################
332*00b67f09SDavid van Moolenbroek# vreset:
333*00b67f09SDavid van Moolenbroek############################################################################
334*00b67f09SDavid van Moolenbroekdef vreset():
335*00b67f09SDavid van Moolenbroek    """reset vertical spacing"""
336*00b67f09SDavid van Moolenbroek    global _firstline
337*00b67f09SDavid van Moolenbroek    _firstline = True
338*00b67f09SDavid van Moolenbroek
339*00b67f09SDavid van Moolenbroek############################################################################
340*00b67f09SDavid van Moolenbroek# getunit
341*00b67f09SDavid van Moolenbroek############################################################################
342*00b67f09SDavid van Moolenbroekdef getunit(secs, size):
343*00b67f09SDavid van Moolenbroek    """given a number of seconds, and a number of seconds in a larger unit of
344*00b67f09SDavid van Moolenbroek    time, calculate how many of the larger unit there are and return both
345*00b67f09SDavid van Moolenbroek    that and a remainder value"""
346*00b67f09SDavid van Moolenbroek    bigunit = secs // size
347*00b67f09SDavid van Moolenbroek    if bigunit:
348*00b67f09SDavid van Moolenbroek        secs %= size
349*00b67f09SDavid van Moolenbroek    return (bigunit, secs)
350*00b67f09SDavid van Moolenbroek
351*00b67f09SDavid van Moolenbroek############################################################################
352*00b67f09SDavid van Moolenbroek# addtime
353*00b67f09SDavid van Moolenbroek############################################################################
354*00b67f09SDavid van Moolenbroekdef addtime(output, unit, t):
355*00b67f09SDavid van Moolenbroek    """add a formatted unit of time to an accumulating string"""
356*00b67f09SDavid van Moolenbroek    if t:
357*00b67f09SDavid van Moolenbroek        output += ("%s%d %s%s" %
358*00b67f09SDavid van Moolenbroek                  ((", " if output else ""),
359*00b67f09SDavid van Moolenbroek                   t, unit, ("s" if t > 1 else "")))
360*00b67f09SDavid van Moolenbroek
361*00b67f09SDavid van Moolenbroek    return output
362*00b67f09SDavid van Moolenbroek
363*00b67f09SDavid van Moolenbroek############################################################################
364*00b67f09SDavid van Moolenbroek# duration:
365*00b67f09SDavid van Moolenbroek############################################################################
366*00b67f09SDavid van Moolenbroekdef duration(secs):
367*00b67f09SDavid van Moolenbroek    """given a length of time in seconds, print a formatted human duration
368*00b67f09SDavid van Moolenbroek    in larger units of time
369*00b67f09SDavid van Moolenbroek    """
370*00b67f09SDavid van Moolenbroek    # define units:
371*00b67f09SDavid van Moolenbroek    minute = 60
372*00b67f09SDavid van Moolenbroek    hour = minute * 60
373*00b67f09SDavid van Moolenbroek    day = hour * 24
374*00b67f09SDavid van Moolenbroek    month = day * 30
375*00b67f09SDavid van Moolenbroek    year = day * 365
376*00b67f09SDavid van Moolenbroek
377*00b67f09SDavid van Moolenbroek    # calculate time in units:
378*00b67f09SDavid van Moolenbroek    (years, secs) = getunit(secs, year)
379*00b67f09SDavid van Moolenbroek    (months, secs) = getunit(secs, month)
380*00b67f09SDavid van Moolenbroek    (days, secs) = getunit(secs, day)
381*00b67f09SDavid van Moolenbroek    (hours, secs) = getunit(secs, hour)
382*00b67f09SDavid van Moolenbroek    (minutes, secs) = getunit(secs, minute)
383*00b67f09SDavid van Moolenbroek
384*00b67f09SDavid van Moolenbroek    output = ''
385*00b67f09SDavid van Moolenbroek    output = addtime(output, "year", years)
386*00b67f09SDavid van Moolenbroek    output = addtime(output, "month", months)
387*00b67f09SDavid van Moolenbroek    output = addtime(output, "day", days)
388*00b67f09SDavid van Moolenbroek    output = addtime(output, "hour", hours)
389*00b67f09SDavid van Moolenbroek    output = addtime(output, "minute", minutes)
390*00b67f09SDavid van Moolenbroek    output = addtime(output, "second", secs)
391*00b67f09SDavid van Moolenbroek    return output
392*00b67f09SDavid van Moolenbroek
393*00b67f09SDavid van Moolenbroek############################################################################
394*00b67f09SDavid van Moolenbroek# parse_time
395*00b67f09SDavid van Moolenbroek############################################################################
396*00b67f09SDavid van Moolenbroekdef parse_time(s):
397*00b67f09SDavid van Moolenbroek    """convert a formatted time (e.g., 1y, 6mo, 15mi, etc) into seconds"""
398*00b67f09SDavid van Moolenbroek    s = s.strip()
399*00b67f09SDavid van Moolenbroek
400*00b67f09SDavid van Moolenbroek    # if s is an integer, we're done already
401*00b67f09SDavid van Moolenbroek    try:
402*00b67f09SDavid van Moolenbroek        n = int(s)
403*00b67f09SDavid van Moolenbroek        return n
404*00b67f09SDavid van Moolenbroek    except:
405*00b67f09SDavid van Moolenbroek        pass
406*00b67f09SDavid van Moolenbroek
407*00b67f09SDavid van Moolenbroek    # try to parse as a number with a suffix indicating unit of time
408*00b67f09SDavid van Moolenbroek    r = re.compile('([0-9][0-9]*)\s*([A-Za-z]*)')
409*00b67f09SDavid van Moolenbroek    m = r.match(s)
410*00b67f09SDavid van Moolenbroek    if not m:
411*00b67f09SDavid van Moolenbroek        raise Exception("Cannot parse %s" % s)
412*00b67f09SDavid van Moolenbroek    (n, unit) = m.groups()
413*00b67f09SDavid van Moolenbroek    n = int(n)
414*00b67f09SDavid van Moolenbroek    unit = unit.lower()
415*00b67f09SDavid van Moolenbroek    if unit[0] == 'y':
416*00b67f09SDavid van Moolenbroek        return n * 31536000
417*00b67f09SDavid van Moolenbroek    elif unit[0] == 'm' and unit[1] == 'o':
418*00b67f09SDavid van Moolenbroek        return n * 2592000
419*00b67f09SDavid van Moolenbroek    elif unit[0] == 'w':
420*00b67f09SDavid van Moolenbroek        return n * 604800
421*00b67f09SDavid van Moolenbroek    elif unit[0] == 'd':
422*00b67f09SDavid van Moolenbroek        return n * 86400
423*00b67f09SDavid van Moolenbroek    elif unit[0] == 'h':
424*00b67f09SDavid van Moolenbroek        return n * 3600
425*00b67f09SDavid van Moolenbroek    elif unit[0] == 'm' and unit[1] == 'i':
426*00b67f09SDavid van Moolenbroek        return n * 60
427*00b67f09SDavid van Moolenbroek    elif unit[0] == 's':
428*00b67f09SDavid van Moolenbroek        return n
429*00b67f09SDavid van Moolenbroek    else:
430*00b67f09SDavid van Moolenbroek        raise Exception("Invalid suffix %s" % unit)
431*00b67f09SDavid van Moolenbroek
432*00b67f09SDavid van Moolenbroek############################################################################
433*00b67f09SDavid van Moolenbroek# algname:
434*00b67f09SDavid van Moolenbroek############################################################################
435*00b67f09SDavid van Moolenbroekdef algname(alg):
436*00b67f09SDavid van Moolenbroek    """return the mnemonic for a DNSSEC algorithm"""
437*00b67f09SDavid van Moolenbroek    names = (None, 'RSAMD5', 'DH', 'DSA', 'ECC', 'RSASHA1',
438*00b67f09SDavid van Moolenbroek            'NSEC3DSA', 'NSEC3RSASHA1', 'RSASHA256', None,
439*00b67f09SDavid van Moolenbroek            'RSASHA512', None, 'ECCGOST', 'ECDSAP256SHA256',
440*00b67f09SDavid van Moolenbroek            'ECDSAP384SHA384')
441*00b67f09SDavid van Moolenbroek    name = None
442*00b67f09SDavid van Moolenbroek    if alg in range(len(names)):
443*00b67f09SDavid van Moolenbroek        name = names[alg]
444*00b67f09SDavid van Moolenbroek    return (name if name else str(alg))
445*00b67f09SDavid van Moolenbroek
446*00b67f09SDavid van Moolenbroek############################################################################
447*00b67f09SDavid van Moolenbroek# list_events:
448*00b67f09SDavid van Moolenbroek############################################################################
449*00b67f09SDavid van Moolenbroekdef list_events(eventgroup):
450*00b67f09SDavid van Moolenbroek    """print a list of the events in an eventgroup"""
451*00b67f09SDavid van Moolenbroek    if not eventgroup:
452*00b67f09SDavid van Moolenbroek        return
453*00b67f09SDavid van Moolenbroek    print ("  " + eventgroup[0].showtime() + ":")
454*00b67f09SDavid van Moolenbroek    for event in eventgroup:
455*00b67f09SDavid van Moolenbroek        print ("    %s: %s (%s)" %
456*00b67f09SDavid van Moolenbroek               (event.what, event.showkey(), event.showkeytype()))
457*00b67f09SDavid van Moolenbroek
458*00b67f09SDavid van Moolenbroek############################################################################
459*00b67f09SDavid van Moolenbroek# process_events:
460*00b67f09SDavid van Moolenbroek############################################################################
461*00b67f09SDavid van Moolenbroekdef process_events(eventgroup, active, published):
462*00b67f09SDavid van Moolenbroek    """go through the events in an event group in time-order, add to active
463*00b67f09SDavid van Moolenbroek     list upon Activate event, add to published list upon Publish event,
464*00b67f09SDavid van Moolenbroek     remove from active list upon Inactive event, and remove from published
465*00b67f09SDavid van Moolenbroek     upon Delete event. Emit warnings when inconsistant states are reached"""
466*00b67f09SDavid van Moolenbroek    for event in eventgroup:
467*00b67f09SDavid van Moolenbroek        if event.what == "Activate":
468*00b67f09SDavid van Moolenbroek            active.add(event.keyid)
469*00b67f09SDavid van Moolenbroek        elif event.what == "Publish":
470*00b67f09SDavid van Moolenbroek            published.add(event.keyid)
471*00b67f09SDavid van Moolenbroek        elif event.what == "Inactive":
472*00b67f09SDavid van Moolenbroek            if event.keyid not in active:
473*00b67f09SDavid van Moolenbroek                vspace()
474*00b67f09SDavid van Moolenbroek                print ("\tWARNING: %s (%s) scheduled to become inactive "
475*00b67f09SDavid van Moolenbroek                       "before it is active" %
476*00b67f09SDavid van Moolenbroek                       (event.showkey(), event.showkeytype()))
477*00b67f09SDavid van Moolenbroek            else:
478*00b67f09SDavid van Moolenbroek                active.remove(event.keyid)
479*00b67f09SDavid van Moolenbroek        elif event.what == "Delete":
480*00b67f09SDavid van Moolenbroek            if event.keyid in published:
481*00b67f09SDavid van Moolenbroek                published.remove(event.keyid)
482*00b67f09SDavid van Moolenbroek            else:
483*00b67f09SDavid van Moolenbroek                vspace()
484*00b67f09SDavid van Moolenbroek                print ("WARNING: key %s (%s) is scheduled for deletion before "
485*00b67f09SDavid van Moolenbroek                       "it is published, at %s" %
486*00b67f09SDavid van Moolenbroek                       (event.showkey(), event.showkeytype()))
487*00b67f09SDavid van Moolenbroek        elif event.what == "Revoke":
488*00b67f09SDavid van Moolenbroek            # We don't need to worry about the logic of this one;
489*00b67f09SDavid van Moolenbroek            # just stop counting this key as either active or published
490*00b67f09SDavid van Moolenbroek            if event.keyid in published:
491*00b67f09SDavid van Moolenbroek                published.remove(event.keyid)
492*00b67f09SDavid van Moolenbroek            if event.keyid in active:
493*00b67f09SDavid van Moolenbroek                active.remove(event.keyid)
494*00b67f09SDavid van Moolenbroek
495*00b67f09SDavid van Moolenbroek    return (active, published)
496*00b67f09SDavid van Moolenbroek
497*00b67f09SDavid van Moolenbroek############################################################################
498*00b67f09SDavid van Moolenbroek# check_events:
499*00b67f09SDavid van Moolenbroek############################################################################
500*00b67f09SDavid van Moolenbroekdef check_events(eventsList, ksk):
501*00b67f09SDavid van Moolenbroek    """create lists of events happening at the same time, check for
502*00b67f09SDavid van Moolenbroek    inconsistancies"""
503*00b67f09SDavid van Moolenbroek    active = set()
504*00b67f09SDavid van Moolenbroek    published = set()
505*00b67f09SDavid van Moolenbroek    eventgroups = list()
506*00b67f09SDavid van Moolenbroek    eventgroup = list()
507*00b67f09SDavid van Moolenbroek    keytype = ("KSK" if ksk else "ZSK")
508*00b67f09SDavid van Moolenbroek
509*00b67f09SDavid van Moolenbroek    # collect up all events that have the same time
510*00b67f09SDavid van Moolenbroek    eventsfound = False
511*00b67f09SDavid van Moolenbroek    for event in eventsList:
512*00b67f09SDavid van Moolenbroek        # if checking ZSKs, skip KSKs, and vice versa
513*00b67f09SDavid van Moolenbroek        if (ksk and not event.sep) or (event.sep and not ksk):
514*00b67f09SDavid van Moolenbroek            continue
515*00b67f09SDavid van Moolenbroek
516*00b67f09SDavid van Moolenbroek        # we found an appropriate (ZSK or KSK event)
517*00b67f09SDavid van Moolenbroek        eventsfound = True
518*00b67f09SDavid van Moolenbroek
519*00b67f09SDavid van Moolenbroek        # add event to current eventgroup
520*00b67f09SDavid van Moolenbroek        if (not eventgroup or eventgroup[0].when == event.when):
521*00b67f09SDavid van Moolenbroek            eventgroup.append(event)
522*00b67f09SDavid van Moolenbroek
523*00b67f09SDavid van Moolenbroek        # if we're at the end of the list, we're done.  if
524*00b67f09SDavid van Moolenbroek        # we've found an event with a later time, start a new
525*00b67f09SDavid van Moolenbroek        # eventgroup
526*00b67f09SDavid van Moolenbroek        if (eventgroup[0].when != event.when):
527*00b67f09SDavid van Moolenbroek            eventgroups.append(eventgroup)
528*00b67f09SDavid van Moolenbroek            eventgroup = list()
529*00b67f09SDavid van Moolenbroek            eventgroup.append(event)
530*00b67f09SDavid van Moolenbroek
531*00b67f09SDavid van Moolenbroek    if eventgroup:
532*00b67f09SDavid van Moolenbroek        eventgroups.append(eventgroup)
533*00b67f09SDavid van Moolenbroek
534*00b67f09SDavid van Moolenbroek    for eventgroup in eventgroups:
535*00b67f09SDavid van Moolenbroek        if (args.checklimit and
536*00b67f09SDavid van Moolenbroek            calendar.timegm(eventgroup[0].when) > args.checklimit):
537*00b67f09SDavid van Moolenbroek            print("Ignoring events after %s" %
538*00b67f09SDavid van Moolenbroek                  time.strftime("%a %b %d %H:%M:%S UTC %Y",
539*00b67f09SDavid van Moolenbroek                      time.gmtime(args.checklimit)))
540*00b67f09SDavid van Moolenbroek            return True
541*00b67f09SDavid van Moolenbroek
542*00b67f09SDavid van Moolenbroek        (active, published) = \
543*00b67f09SDavid van Moolenbroek           process_events(eventgroup, active, published)
544*00b67f09SDavid van Moolenbroek
545*00b67f09SDavid van Moolenbroek        list_events(eventgroup)
546*00b67f09SDavid van Moolenbroek
547*00b67f09SDavid van Moolenbroek        # and then check for inconsistencies:
548*00b67f09SDavid van Moolenbroek        if len(active) == 0:
549*00b67f09SDavid van Moolenbroek            print ("ERROR: No %s's are active after this event" % keytype)
550*00b67f09SDavid van Moolenbroek            return False
551*00b67f09SDavid van Moolenbroek        elif len(published) == 0:
552*00b67f09SDavid van Moolenbroek            sys.stdout.write("ERROR: ")
553*00b67f09SDavid van Moolenbroek            print ("ERROR: No %s's are published after this event" % keytype)
554*00b67f09SDavid van Moolenbroek            return False
555*00b67f09SDavid van Moolenbroek        elif len(published.intersection(active)) == 0:
556*00b67f09SDavid van Moolenbroek            sys.stdout.write("ERROR: ")
557*00b67f09SDavid van Moolenbroek            print (("ERROR: No %s's are both active and published " +
558*00b67f09SDavid van Moolenbroek                    "after this event") % keytype)
559*00b67f09SDavid van Moolenbroek            return False
560*00b67f09SDavid van Moolenbroek
561*00b67f09SDavid van Moolenbroek    if not eventsfound:
562*00b67f09SDavid van Moolenbroek        print ("ERROR: No %s events found in '%s'" %
563*00b67f09SDavid van Moolenbroek               (keytype, args.path))
564*00b67f09SDavid van Moolenbroek        return False
565*00b67f09SDavid van Moolenbroek
566*00b67f09SDavid van Moolenbroek    return True
567*00b67f09SDavid van Moolenbroek
568*00b67f09SDavid van Moolenbroek############################################################################
569*00b67f09SDavid van Moolenbroek# check_zones:
570*00b67f09SDavid van Moolenbroek# ############################################################################
571*00b67f09SDavid van Moolenbroekdef check_zones(eventsList):
572*00b67f09SDavid van Moolenbroek    """scan events per zone, algorithm, and key type, in order of occurrance,
573*00b67f09SDavid van Moolenbroek    noting inconsistent states when found"""
574*00b67f09SDavid van Moolenbroek    global foundprob
575*00b67f09SDavid van Moolenbroek
576*00b67f09SDavid van Moolenbroek    foundprob = False
577*00b67f09SDavid van Moolenbroek    zonesfound = False
578*00b67f09SDavid van Moolenbroek    for zone in eventsList:
579*00b67f09SDavid van Moolenbroek        if args.zone and zone != args.zone:
580*00b67f09SDavid van Moolenbroek            continue
581*00b67f09SDavid van Moolenbroek
582*00b67f09SDavid van Moolenbroek        zonesfound = True
583*00b67f09SDavid van Moolenbroek        for alg in eventsList[zone]:
584*00b67f09SDavid van Moolenbroek            if not args.no_ksk:
585*00b67f09SDavid van Moolenbroek                vspace()
586*00b67f09SDavid van Moolenbroek                print("Checking scheduled KSK events for zone %s, algorithm %s..." %
587*00b67f09SDavid van Moolenbroek                       (zone, algname(alg)))
588*00b67f09SDavid van Moolenbroek                if not check_events(eventsList[zone][alg], True):
589*00b67f09SDavid van Moolenbroek                    foundprob = True
590*00b67f09SDavid van Moolenbroek                else:
591*00b67f09SDavid van Moolenbroek                    print ("No errors found")
592*00b67f09SDavid van Moolenbroek
593*00b67f09SDavid van Moolenbroek            if not args.no_zsk:
594*00b67f09SDavid van Moolenbroek                vspace()
595*00b67f09SDavid van Moolenbroek                print("Checking scheduled ZSK events for zone %s, algorithm %s..." %
596*00b67f09SDavid van Moolenbroek                      (zone, algname(alg)))
597*00b67f09SDavid van Moolenbroek                if not check_events(eventsList[zone][alg], False):
598*00b67f09SDavid van Moolenbroek                    foundprob = True
599*00b67f09SDavid van Moolenbroek                else:
600*00b67f09SDavid van Moolenbroek                    print ("No errors found")
601*00b67f09SDavid van Moolenbroek
602*00b67f09SDavid van Moolenbroek    if not zonesfound:
603*00b67f09SDavid van Moolenbroek        print("ERROR: No key events found for %s in '%s'" %
604*00b67f09SDavid van Moolenbroek               (args.zone, args.path))
605*00b67f09SDavid van Moolenbroek        exit(1)
606*00b67f09SDavid van Moolenbroek
607*00b67f09SDavid van Moolenbroek############################################################################
608*00b67f09SDavid van Moolenbroek# fill_eventsList:
609*00b67f09SDavid van Moolenbroek############################################################################
610*00b67f09SDavid van Moolenbroekdef fill_eventsList(eventsList):
611*00b67f09SDavid van Moolenbroek    """populate the list of events"""
612*00b67f09SDavid van Moolenbroek    for zone, algorithms in keyDict.items():
613*00b67f09SDavid van Moolenbroek        for alg, keys in  algorithms.items():
614*00b67f09SDavid van Moolenbroek            for keyid, keydata in keys.items():
615*00b67f09SDavid van Moolenbroek                if("Publish" in keydata.metadata):
616*00b67f09SDavid van Moolenbroek                    eventsList[zone][alg].append(Event("Publish", keydata))
617*00b67f09SDavid van Moolenbroek                if("Activate" in keydata.metadata):
618*00b67f09SDavid van Moolenbroek                    eventsList[zone][alg].append(Event("Activate", keydata))
619*00b67f09SDavid van Moolenbroek                if("Inactive" in keydata.metadata):
620*00b67f09SDavid van Moolenbroek                    eventsList[zone][alg].append(Event("Inactive", keydata))
621*00b67f09SDavid van Moolenbroek                if("Delete" in keydata.metadata):
622*00b67f09SDavid van Moolenbroek                    eventsList[zone][alg].append(Event("Delete", keydata))
623*00b67f09SDavid van Moolenbroek
624*00b67f09SDavid van Moolenbroek            eventsList[zone][alg] = sorted(eventsList[zone][alg],
625*00b67f09SDavid van Moolenbroek                                           key=lambda event: event.when)
626*00b67f09SDavid van Moolenbroek
627*00b67f09SDavid van Moolenbroek    foundprob = False
628*00b67f09SDavid van Moolenbroek    if not keyDict:
629*00b67f09SDavid van Moolenbroek        print("ERROR: No key events found in '%s'" % args.path)
630*00b67f09SDavid van Moolenbroek        exit(1)
631*00b67f09SDavid van Moolenbroek
632*00b67f09SDavid van Moolenbroek############################################################################
633*00b67f09SDavid van Moolenbroek# set_path:
634*00b67f09SDavid van Moolenbroek############################################################################
635*00b67f09SDavid van Moolenbroekdef set_path(command, default=None):
636*00b67f09SDavid van Moolenbroek    """find the location of a specified command.  if a default is supplied
637*00b67f09SDavid van Moolenbroek    and it works, we use it; otherwise we search PATH for a match.  If
638*00b67f09SDavid van Moolenbroek    not found, error and exit"""
639*00b67f09SDavid van Moolenbroek    fpath = default
640*00b67f09SDavid van Moolenbroek    if not fpath or not os.path.isfile(fpath) or not os.access(fpath, os.X_OK):
641*00b67f09SDavid van Moolenbroek        path = os.environ["PATH"]
642*00b67f09SDavid van Moolenbroek        if not path:
643*00b67f09SDavid van Moolenbroek            path = os.path.defpath
644*00b67f09SDavid van Moolenbroek        for directory in path.split(os.pathsep):
645*00b67f09SDavid van Moolenbroek            fpath = directory + os.sep + command
646*00b67f09SDavid van Moolenbroek            if os.path.isfile(fpath) or os.access(fpath, os.X_OK):
647*00b67f09SDavid van Moolenbroek                break
648*00b67f09SDavid van Moolenbroek            fpath = None
649*00b67f09SDavid van Moolenbroek
650*00b67f09SDavid van Moolenbroek    return fpath
651*00b67f09SDavid van Moolenbroek
652*00b67f09SDavid van Moolenbroek############################################################################
653*00b67f09SDavid van Moolenbroek# parse_args:
654*00b67f09SDavid van Moolenbroek############################################################################
655*00b67f09SDavid van Moolenbroekdef parse_args():
656*00b67f09SDavid van Moolenbroek    """Read command line arguments, set global 'args' structure"""
657*00b67f09SDavid van Moolenbroek    global args
658*00b67f09SDavid van Moolenbroek    compilezone = set_path('named-compilezone',
659*00b67f09SDavid van Moolenbroek                           os.path.join(prefix('bin'), 'named-compilezone'))
660*00b67f09SDavid van Moolenbroek
661*00b67f09SDavid van Moolenbroek    parser = argparse.ArgumentParser(description=prog + ': checks future ' +
662*00b67f09SDavid van Moolenbroek                                     'DNSKEY coverage for a zone')
663*00b67f09SDavid van Moolenbroek
664*00b67f09SDavid van Moolenbroek    parser.add_argument('zone', type=str, help='zone to check')
665*00b67f09SDavid van Moolenbroek    parser.add_argument('-K', dest='path', default='.', type=str,
666*00b67f09SDavid van Moolenbroek                        help='a directory containing keys to process',
667*00b67f09SDavid van Moolenbroek                        metavar='dir')
668*00b67f09SDavid van Moolenbroek    parser.add_argument('-f', dest='filename', type=str,
669*00b67f09SDavid van Moolenbroek                        help='zone master file', metavar='file')
670*00b67f09SDavid van Moolenbroek    parser.add_argument('-m', dest='maxttl', type=str,
671*00b67f09SDavid van Moolenbroek                        help='the longest TTL in the zone(s)',
672*00b67f09SDavid van Moolenbroek                        metavar='time')
673*00b67f09SDavid van Moolenbroek    parser.add_argument('-d', dest='keyttl', type=str,
674*00b67f09SDavid van Moolenbroek                        help='the DNSKEY TTL', metavar='time')
675*00b67f09SDavid van Moolenbroek    parser.add_argument('-r', dest='resign', default='1944000',
676*00b67f09SDavid van Moolenbroek                        type=int, help='the RRSIG refresh interval '
677*00b67f09SDavid van Moolenbroek                                       'in seconds [default: 22.5 days]',
678*00b67f09SDavid van Moolenbroek                        metavar='time')
679*00b67f09SDavid van Moolenbroek    parser.add_argument('-c', dest='compilezone',
680*00b67f09SDavid van Moolenbroek                        default=compilezone, type=str,
681*00b67f09SDavid van Moolenbroek                        help='path to \'named-compilezone\'',
682*00b67f09SDavid van Moolenbroek                        metavar='path')
683*00b67f09SDavid van Moolenbroek    parser.add_argument('-l', dest='checklimit',
684*00b67f09SDavid van Moolenbroek                        type=str, default='0',
685*00b67f09SDavid van Moolenbroek                        help='Length of time to check for '
686*00b67f09SDavid van Moolenbroek                             'DNSSEC coverage [default: 0 (unlimited)]',
687*00b67f09SDavid van Moolenbroek                        metavar='time')
688*00b67f09SDavid van Moolenbroek    parser.add_argument('-z', dest='no_ksk',
689*00b67f09SDavid van Moolenbroek                        action='store_true', default=False,
690*00b67f09SDavid van Moolenbroek                        help='Only check zone-signing keys (ZSKs)')
691*00b67f09SDavid van Moolenbroek    parser.add_argument('-k', dest='no_zsk',
692*00b67f09SDavid van Moolenbroek                        action='store_true', default=False,
693*00b67f09SDavid van Moolenbroek                        help='Only check key-signing keys (KSKs)')
694*00b67f09SDavid van Moolenbroek    parser.add_argument('-D', '--debug', dest='debug_mode',
695*00b67f09SDavid van Moolenbroek                        action='store_true', default=False,
696*00b67f09SDavid van Moolenbroek                        help='Turn on debugging output')
697*00b67f09SDavid van Moolenbroek    parser.add_argument('-v', '--version', action='version', version='9.9.1')
698*00b67f09SDavid van Moolenbroek
699*00b67f09SDavid van Moolenbroek    args = parser.parse_args()
700*00b67f09SDavid van Moolenbroek
701*00b67f09SDavid van Moolenbroek    if args.no_zsk and args.no_ksk:
702*00b67f09SDavid van Moolenbroek        print("ERROR: -z and -k cannot be used together.");
703*00b67f09SDavid van Moolenbroek        exit(1)
704*00b67f09SDavid van Moolenbroek
705*00b67f09SDavid van Moolenbroek    # convert from time arguments to seconds
706*00b67f09SDavid van Moolenbroek    try:
707*00b67f09SDavid van Moolenbroek        if args.maxttl:
708*00b67f09SDavid van Moolenbroek            m = parse_time(args.maxttl)
709*00b67f09SDavid van Moolenbroek            args.maxttl = m
710*00b67f09SDavid van Moolenbroek    except:
711*00b67f09SDavid van Moolenbroek        pass
712*00b67f09SDavid van Moolenbroek
713*00b67f09SDavid van Moolenbroek    try:
714*00b67f09SDavid van Moolenbroek        if args.keyttl:
715*00b67f09SDavid van Moolenbroek            k = parse_time(args.keyttl)
716*00b67f09SDavid van Moolenbroek            args.keyttl = k
717*00b67f09SDavid van Moolenbroek    except:
718*00b67f09SDavid van Moolenbroek        pass
719*00b67f09SDavid van Moolenbroek
720*00b67f09SDavid van Moolenbroek    try:
721*00b67f09SDavid van Moolenbroek        if args.resign:
722*00b67f09SDavid van Moolenbroek            r = parse_time(args.resign)
723*00b67f09SDavid van Moolenbroek            args.resign = r
724*00b67f09SDavid van Moolenbroek    except:
725*00b67f09SDavid van Moolenbroek        pass
726*00b67f09SDavid van Moolenbroek
727*00b67f09SDavid van Moolenbroek    try:
728*00b67f09SDavid van Moolenbroek        if args.checklimit:
729*00b67f09SDavid van Moolenbroek            lim = args.checklimit
730*00b67f09SDavid van Moolenbroek            r = parse_time(args.checklimit)
731*00b67f09SDavid van Moolenbroek            if r == 0:
732*00b67f09SDavid van Moolenbroek                args.checklimit = None
733*00b67f09SDavid van Moolenbroek            else:
734*00b67f09SDavid van Moolenbroek                args.checklimit = time.time() + r
735*00b67f09SDavid van Moolenbroek    except:
736*00b67f09SDavid van Moolenbroek        pass
737*00b67f09SDavid van Moolenbroek
738*00b67f09SDavid van Moolenbroek    # if we've got the values we need from the command line, stop now
739*00b67f09SDavid van Moolenbroek    if args.maxttl and args.keyttl:
740*00b67f09SDavid van Moolenbroek        return
741*00b67f09SDavid van Moolenbroek
742*00b67f09SDavid van Moolenbroek    # load keyttl and maxttl data from zonefile
743*00b67f09SDavid van Moolenbroek    if args.zone and args.filename:
744*00b67f09SDavid van Moolenbroek        try:
745*00b67f09SDavid van Moolenbroek            zone = Zone(args.zone)
746*00b67f09SDavid van Moolenbroek            zone.load(args.filename)
747*00b67f09SDavid van Moolenbroek            if not args.maxttl:
748*00b67f09SDavid van Moolenbroek                args.maxttl = zone.maxttl
749*00b67f09SDavid van Moolenbroek            if not args.keyttl:
750*00b67f09SDavid van Moolenbroek                args.keyttl = zone.maxttl
751*00b67f09SDavid van Moolenbroek        except Exception as e:
752*00b67f09SDavid van Moolenbroek            print("Unable to load zone data from %s: " % args.filename, e)
753*00b67f09SDavid van Moolenbroek
754*00b67f09SDavid van Moolenbroek    if not args.maxttl:
755*00b67f09SDavid van Moolenbroek        vspace()
756*00b67f09SDavid van Moolenbroek        print ("WARNING: Maximum TTL value was not specified.  Using 1 week\n"
757*00b67f09SDavid van Moolenbroek               "\t (604800 seconds); re-run with the -m option to get more\n"
758*00b67f09SDavid van Moolenbroek               "\t accurate results.")
759*00b67f09SDavid van Moolenbroek        args.maxttl = 604800
760*00b67f09SDavid van Moolenbroek
761*00b67f09SDavid van Moolenbroek############################################################################
762*00b67f09SDavid van Moolenbroek# Main
763*00b67f09SDavid van Moolenbroek############################################################################
764*00b67f09SDavid van Moolenbroekdef main():
765*00b67f09SDavid van Moolenbroek    global keyDict
766*00b67f09SDavid van Moolenbroek
767*00b67f09SDavid van Moolenbroek    parse_args()
768*00b67f09SDavid van Moolenbroek    path=args.path
769*00b67f09SDavid van Moolenbroek
770*00b67f09SDavid van Moolenbroek    print ("PHASE 1--Loading keys to check for internal timing problems")
771*00b67f09SDavid van Moolenbroek    keyDict = defaultdict(lambda : defaultdict(dict))
772*00b67f09SDavid van Moolenbroek    files = glob.glob(os.path.join(path, '*.private'))
773*00b67f09SDavid van Moolenbroek    for infile in files:
774*00b67f09SDavid van Moolenbroek        key = Key(infile)
775*00b67f09SDavid van Moolenbroek        if args.zone and key.zone != args.zone:
776*00b67f09SDavid van Moolenbroek            continue
777*00b67f09SDavid van Moolenbroek        keyDict[key.zone][key.alg][key.keyid] = key
778*00b67f09SDavid van Moolenbroek        key.check_prepub()
779*00b67f09SDavid van Moolenbroek        if key.sep:
780*00b67f09SDavid van Moolenbroek            key.check_postpub()
781*00b67f09SDavid van Moolenbroek        else:
782*00b67f09SDavid van Moolenbroek            key.check_postpub(args.maxttl + args.resign)
783*00b67f09SDavid van Moolenbroek
784*00b67f09SDavid van Moolenbroek    vspace()
785*00b67f09SDavid van Moolenbroek    print ("PHASE 2--Scanning future key events for coverage failures")
786*00b67f09SDavid van Moolenbroek    vreset()
787*00b67f09SDavid van Moolenbroek
788*00b67f09SDavid van Moolenbroek    eventsList = defaultdict(lambda : defaultdict(list))
789*00b67f09SDavid van Moolenbroek    fill_eventsList(eventsList)
790*00b67f09SDavid van Moolenbroek    check_zones(eventsList)
791*00b67f09SDavid van Moolenbroek
792*00b67f09SDavid van Moolenbroek    if foundprob:
793*00b67f09SDavid van Moolenbroek        exit(1)
794*00b67f09SDavid van Moolenbroek    else:
795*00b67f09SDavid van Moolenbroek        exit(0)
796*00b67f09SDavid van Moolenbroek
797*00b67f09SDavid van Moolenbroekif __name__ == "__main__":
798*00b67f09SDavid van Moolenbroek    main()
799