"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.11.23/bin/python/isc/coverage.py.in" (7 Sep 2020, 9980 Bytes) of package /linux/misc/dns/bind9/9.11.23/bind-9.11.23.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file.

    1 ############################################################################
    2 # Copyright (C) Internet Systems Consortium, Inc. ("ISC")
    3 #
    4 # This Source Code Form is subject to the terms of the Mozilla Public
    5 # License, v. 2.0. If a copy of the MPL was not distributed with this
    6 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
    7 #
    8 # See the COPYRIGHT file distributed with this work for additional
    9 # information regarding copyright ownership.
   10 ############################################################################
   11 
   12 from __future__ import print_function
   13 import os
   14 import sys
   15 import argparse
   16 import glob
   17 import re
   18 import time
   19 import calendar
   20 import pprint
   21 from collections import defaultdict
   22 
   23 prog = 'dnssec-coverage'
   24 
   25 from isc import dnskey, eventlist, keydict, keyevent, keyzone, utils
   26 
   27 ############################################################################
   28 # print a fatal error and exit
   29 ############################################################################
   30 def fatal(*args, **kwargs):
   31     print(*args, **kwargs)
   32     sys.exit(1)
   33 
   34 
   35 ############################################################################
   36 # output:
   37 ############################################################################
   38 _firstline = True
   39 def output(*args, **kwargs):
   40     """output text, adding a vertical space this is *not* the first
   41     first section being printed since a call to vreset()"""
   42     global _firstline
   43     if 'skip' in kwargs:
   44         skip = kwargs['skip']
   45         kwargs.pop('skip', None)
   46     else:
   47         skip = True
   48     if _firstline:
   49         _firstline = False
   50     elif skip:
   51         print('')
   52     if args:
   53         print(*args, **kwargs)
   54 
   55 
   56 def vreset():
   57     """reset vertical spacing"""
   58     global _firstline
   59     _firstline = True
   60 
   61 
   62 ############################################################################
   63 # parse_time
   64 ############################################################################
   65 def parse_time(s):
   66     """ convert a formatted time (e.g., 1y, 6mo, 15mi, etc) into seconds
   67     :param s: String with some text representing a time interval
   68     :return: Integer with the number of seconds in the time interval
   69     """
   70     s = s.strip()
   71 
   72     # if s is an integer, we're done already
   73     try:
   74         return int(s)
   75     except ValueError:
   76         pass
   77 
   78     # try to parse as a number with a suffix indicating unit of time
   79     r = re.compile(r'([0-9][0-9]*)\s*([A-Za-z]*)')
   80     m = r.match(s)
   81     if not m:
   82         raise ValueError("Cannot parse %s" % s)
   83     n, unit = m.groups()
   84     n = int(n)
   85     unit = unit.lower()
   86     if unit.startswith('y'):
   87         return n * 31536000
   88     elif unit.startswith('mo'):
   89         return n * 2592000
   90     elif unit.startswith('w'):
   91         return n * 604800
   92     elif unit.startswith('d'):
   93         return n * 86400
   94     elif unit.startswith('h'):
   95         return n * 3600
   96     elif unit.startswith('mi'):
   97         return n * 60
   98     elif unit.startswith('s'):
   99         return n
  100     else:
  101         raise ValueError("Invalid suffix %s" % unit)
  102 
  103 
  104 ############################################################################
  105 # set_path:
  106 ############################################################################
  107 def set_path(command, default=None):
  108     """ find the location of a specified command.  if a default is supplied
  109     and it works, we use it; otherwise we search PATH for a match.
  110     :param command: string with a command to look for in the path
  111     :param default: default location to use
  112     :return: detected location for the desired command
  113     """
  114 
  115     fpath = default
  116     if not fpath or not os.path.isfile(fpath) or not os.access(fpath, os.X_OK):
  117         path = os.environ["PATH"]
  118         if not path:
  119             path = os.path.defpath
  120         for directory in path.split(os.pathsep):
  121             fpath = os.path.join(directory, command)
  122             if os.path.isfile(fpath) and os.access(fpath, os.X_OK):
  123                 break
  124             fpath = None
  125 
  126     return fpath
  127 
  128 
  129 ############################################################################
  130 # parse_args:
  131 ############################################################################
  132 def parse_args():
  133     """Read command line arguments, set global 'args' structure"""
  134     compilezone = set_path('named-compilezone',
  135                            os.path.join(utils.prefix('sbin'),
  136                            'named-compilezone'))
  137 
  138     parser = argparse.ArgumentParser(description=prog + ': checks future ' +
  139                                      'DNSKEY coverage for a zone')
  140 
  141     parser.add_argument('zone', type=str, nargs='*', default=None,
  142                         help='zone(s) to check' +
  143                         '(default: all zones in the directory)')
  144     parser.add_argument('-K', dest='path', default='.', type=str,
  145                         help='a directory containing keys to process',
  146                         metavar='dir')
  147     parser.add_argument('-f', dest='filename', type=str,
  148                         help='zone master file', metavar='file')
  149     parser.add_argument('-m', dest='maxttl', type=str,
  150                         help='the longest TTL in the zone(s)',
  151                         metavar='time')
  152     parser.add_argument('-d', dest='keyttl', type=str,
  153                         help='the DNSKEY TTL', metavar='time')
  154     parser.add_argument('-r', dest='resign', default='1944000',
  155                         type=str, help='the RRSIG refresh interval '
  156                                        'in seconds [default: 22.5 days]',
  157                         metavar='time')
  158     parser.add_argument('-c', dest='compilezone',
  159                         default=compilezone, type=str,
  160                         help='path to \'named-compilezone\'',
  161                         metavar='path')
  162     parser.add_argument('-l', dest='checklimit',
  163                         type=str, default='0',
  164                         help='Length of time to check for '
  165                              'DNSSEC coverage [default: 0 (unlimited)]',
  166                         metavar='time')
  167     parser.add_argument('-z', dest='no_ksk',
  168                         action='store_true', default=False,
  169                         help='Only check zone-signing keys (ZSKs)')
  170     parser.add_argument('-k', dest='no_zsk',
  171                         action='store_true', default=False,
  172                         help='Only check key-signing keys (KSKs)')
  173     parser.add_argument('-D', '--debug', dest='debug_mode',
  174                         action='store_true', default=False,
  175                         help='Turn on debugging output')
  176     parser.add_argument('-v', '--version', action='version',
  177                         version=utils.version)
  178 
  179     args = parser.parse_args()
  180 
  181     if args.no_zsk and args.no_ksk:
  182         fatal("ERROR: -z and -k cannot be used together.")
  183     elif args.no_zsk or args.no_ksk:
  184         args.keytype = "KSK" if args.no_zsk else "ZSK"
  185     else:
  186         args.keytype = None
  187 
  188     if args.filename and len(args.zone) > 1:
  189         fatal("ERROR: -f can only be used with one zone.")
  190 
  191     # strip trailing dots if any
  192     args.zone = [x[:-1] if (len(x) > 1 and x[-1] == '.') else x
  193                         for x in args.zone]
  194 
  195     # convert from time arguments to seconds
  196     try:
  197         if args.maxttl:
  198             m = parse_time(args.maxttl)
  199             args.maxttl = m
  200     except ValueError:
  201         pass
  202 
  203     try:
  204         if args.keyttl:
  205             k = parse_time(args.keyttl)
  206             args.keyttl = k
  207     except ValueError:
  208         pass
  209 
  210     try:
  211         if args.resign:
  212             r = parse_time(args.resign)
  213             args.resign = r
  214     except ValueError:
  215         pass
  216 
  217     try:
  218         if args.checklimit:
  219             lim = args.checklimit
  220             r = parse_time(args.checklimit)
  221             if r == 0:
  222                 args.checklimit = None
  223             else:
  224                 args.checklimit = time.time() + r
  225     except ValueError:
  226         pass
  227 
  228     # if we've got the values we need from the command line, stop now
  229     if args.maxttl and args.keyttl:
  230         return args
  231 
  232     # load keyttl and maxttl data from zonefile
  233     if args.zone and args.filename:
  234         try:
  235             zone = keyzone(args.zone[0], args.filename, args.compilezone)
  236             args.maxttl = args.maxttl or zone.maxttl
  237             args.keyttl = args.maxttl or zone.keyttl
  238         except Exception as e:
  239             print("Unable to load zone data from %s: " % args.filename, e)
  240 
  241     if not args.maxttl:
  242         output("WARNING: Maximum TTL value was not specified.  Using 1 week\n"
  243                "\t (604800 seconds); re-run with the -m option to get more\n"
  244                "\t accurate results.")
  245         args.maxttl = 604800
  246 
  247     return args
  248 
  249 ############################################################################
  250 # Main
  251 ############################################################################
  252 def main():
  253     args = parse_args()
  254 
  255     print("PHASE 1--Loading keys to check for internal timing problems")
  256 
  257     try:
  258         kd = keydict(path=args.path, zones=args.zone, keyttl=args.keyttl)
  259     except Exception as e:
  260         fatal('ERROR: Unable to build key dictionary: ' + str(e))
  261 
  262     for key in kd:
  263         key.check_prepub(output)
  264         if key.sep:
  265             key.check_postpub(output)
  266         else:
  267             key.check_postpub(output, args.maxttl + args.resign)
  268 
  269     output("PHASE 2--Scanning future key events for coverage failures")
  270     vreset()
  271 
  272     try:
  273         elist = eventlist(kd)
  274     except Exception as e:
  275         fatal('ERROR: Unable to build event list: ' + str(e))
  276 
  277     errors = False
  278     if not args.zone:
  279         if not elist.coverage(None, args.keytype, args.checklimit, output):
  280             errors = True
  281     else:
  282         for zone in args.zone:
  283             try:
  284                 if not elist.coverage(zone, args.keytype,
  285                                       args.checklimit, output):
  286                     errors = True
  287             except:
  288                 output('ERROR: Coverage check failed for zone ' + zone)
  289 
  290     sys.exit(1 if errors else 0)