\FF\D8\FF\E0\00JFIF\00\00\00d\00d\00\00\FF\FE\00\border bs:0 bc:#000000 ps:0 pc:#ffffff es:0 ec:#000000 ck:feee6c715d26fd9f38b0ca4278c05026\FF\DB\00C\00P7<F<2PFAFZUP_xxnnx\F5\AF\B9\91\C8\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\DB\00CUZZxix‚\EB\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\C0\00\00b\00d\00\FF\C4\00\00\00\00\00\00\00\00\00\00\00\00\00\00\FF\C4\00\00\00\00\00\00\00\00\00\00\001A!Q\FF\C4\00\00\00\00\00\00\00\00\00\00\00\00\00\FF\C4\00\00\00\00\00\00\00\00\00\00!1AQ\FF\DA\00\00\00?\00\F6\00\00\00\00\00\00\00\00\00\00\A0\00\00\C5\CF\F8\E7Ó\FCjD~\B9^\AD\%\B3]\D8cs-\BBs,-\A0\00"\80\00%\B83rÏ^K~F\A4ea\00E\00\C6\EE=u\B1\9B\D1\00@\00r\BE8\F9:\FE5#.M\00\E7\CB\C9q\CBq\D6\F2\BE\B7\C7>\C9n5×\D6?\BDê\9Ds\EBp\9F[`8m\B7)o\B5\E8\E6I\99\FE3]]A2\BA\8Cw\D6E\93\\DEv\C8\009\F2\F1NI?uc\\F5\EA\96k\xN<~buv\EA\C8\D7	\8B\84\CEcxI\BBg\AE\9E=\D6+n\EC\80\C8A\8C\AE\EB\CF\D5\DA\E9"2\A4\B9j5\EB\F3W\B63\96\B30Yu\DA\FC8\ED\DF\E7Ms\FB\F1\8E\B3\FA\EA\E8\E6(\883zs\F2_\8DFk\8Bh
\00\8C\DCw\D3R\B5+6X\BA\B2\C4j\AB0\B4\FCMw\C2I\8E\9B\E3\A9~9u\FA\D3l\80\C8%p\EE\FDn2€
\00$\FEj\C4e\A9\DB\~\95\A7\A5\80EK\BB\8DDsP\00@@AD'k\CF\E8\DB\D2(\80\9AK\D3\85\D6lb\F2\BA\8C*\80\00)\95
59\A3R:\F3\CE"\B6\80\88\00\00i1u4ê\E9\F2á\A6\A2\FACM\93WMb*\E0*\00\00\00\00\00(\A8\80\00\00\80\00\00\00\00\00\00\00\00\00\FF\D9<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>C///</title>
</head>
############################################################################
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.
############################################################################

from __future__ import print_function
import os
import sys
import argparse
import glob
import re
import time
import calendar
import pprint
from collections import defaultdict

prog = 'dnssec-coverage'

from isc import dnskey, eventlist, keydict, keyevent, keyzone, utils

############################################################################
# print a fatal error and exit
############################################################################
def fatal(*args, **kwargs):
    print(*args, **kwargs)
    sys.exit(1)


############################################################################
# output:
############################################################################
_firstline = True
def output(*args, **kwargs):
    """output text, adding a vertical space this is *not* the first
    first section being printed since a call to vreset()"""
    global _firstline
    if 'skip' in kwargs:
        skip = kwargs['skip']
        kwargs.pop('skip', None)
    else:
        skip = True
    if _firstline:
        _firstline = False
    elif skip:
        print('')
    if args:
        print(*args, **kwargs)


def vreset():
    """reset vertical spacing"""
    global _firstline
    _firstline = True


############################################################################
# parse_time
############################################################################
def parse_time(s):
    """ convert a formatted time (e.g., 1y, 6mo, 15mi, etc) into seconds
    :param s: String with some text representing a time interval
    :return: Integer with the number of seconds in the time interval
    """
    s = s.strip()

    # if s is an integer, we're done already
    try:
        return int(s)
    except ValueError:
        pass

    # try to parse as a number with a suffix indicating unit of time
    r = re.compile(r'([0-9][0-9]*)\s*([A-Za-z]*)')
    m = r.match(s)
    if not m:
        raise ValueError("Cannot parse %s" % s)
    n, unit = m.groups()
    n = int(n)
    unit = unit.lower()
    if unit.startswith('y'):
        return n * 31536000
    elif unit.startswith('mo'):
        return n * 2592000
    elif unit.startswith('w'):
        return n * 604800
    elif unit.startswith('d'):
        return n * 86400
    elif unit.startswith('h'):
        return n * 3600
    elif unit.startswith('mi'):
        return n * 60
    elif unit.startswith('s'):
        return n
    else:
        raise ValueError("Invalid suffix %s" % unit)


############################################################################
# set_path:
############################################################################
def set_path(command, default=None):
    """ find the location of a specified command.  if a default is supplied
    and it works, we use it; otherwise we search PATH for a match.
    :param command: string with a command to look for in the path
    :param default: default location to use
    :return: detected location for the desired command
    """

    fpath = default
    if not fpath or not os.path.isfile(fpath) or not os.access(fpath, os.X_OK):
        path = os.environ["PATH"]
        if not path:
            path = os.path.defpath
        for directory in path.split(os.pathsep):
            fpath = os.path.join(directory, command)
            if os.path.isfile(fpath) and os.access(fpath, os.X_OK):
                break
            fpath = None

    return fpath


############################################################################
# parse_args:
############################################################################
def parse_args():
    """Read command line arguments, set global 'args' structure"""
    compilezone = set_path('named-compilezone',
                           os.path.join(utils.prefix('sbin'),
                           'named-compilezone'))

    parser = argparse.ArgumentParser(description=prog + ': checks future ' +
                                     'DNSKEY coverage for a zone')

    parser.add_argument('zone', type=str, nargs='*', default=None,
                        help='zone(s) to check' +
                        '(default: all zones in the directory)')
    parser.add_argument('-K', dest='path', default='.', type=str,
                        help='a directory containing keys to process',
                        metavar='dir')
    parser.add_argument('-f', dest='filename', type=str,
                        help='zone master file', metavar='file')
    parser.add_argument('-m', dest='maxttl', type=str,
                        help='the longest TTL in the zone(s)',
                        metavar='time')
    parser.add_argument('-d', dest='keyttl', type=str,
                        help='the DNSKEY TTL', metavar='time')
    parser.add_argument('-r', dest='resign', default='1944000',
                        type=str, help='the RRSIG refresh interval '
                                       'in seconds [default: 22.5 days]',
                        metavar='time')
    parser.add_argument('-c', dest='compilezone',
                        default=compilezone, type=str,
                        help='path to \'named-compilezone\'',
                        metavar='path')
    parser.add_argument('-l', dest='checklimit',
                        type=str, default='0',
                        help='Length of time to check for '
                             'DNSSEC coverage [default: 0 (unlimited)]',
                        metavar='time')
    parser.add_argument('-z', dest='no_ksk',
                        action='store_true', default=False,
                        help='Only check zone-signing keys (ZSKs)')
    parser.add_argument('-k', dest='no_zsk',
                        action='store_true', default=False,
                        help='Only check key-signing keys (KSKs)')
    parser.add_argument('-D', '--debug', dest='debug_mode',
                        action='store_true', default=False,
                        help='Turn on debugging output')
    parser.add_argument('-v', '--version', action='version',
                        version=utils.version)

    args = parser.parse_args()

    if args.no_zsk and args.no_ksk:
        fatal("ERROR: -z and -k cannot be used together.")
    elif args.no_zsk or args.no_ksk:
        args.keytype = "KSK" if args.no_zsk else "ZSK"
    else:
        args.keytype = None

    if args.filename and len(args.zone) > 1:
        fatal("ERROR: -f can only be used with one zone.")

    # convert from time arguments to seconds
    try:
        if args.maxttl:
            m = parse_time(args.maxttl)
            args.maxttl = m
    except ValueError:
        pass

    try:
        if args.keyttl:
            k = parse_time(args.keyttl)
            args.keyttl = k
    except ValueError:
        pass

    try:
        if args.resign:
            r = parse_time(args.resign)
            args.resign = r
    except ValueError:
        pass

    try:
        if args.checklimit:
            lim = args.checklimit
            r = parse_time(args.checklimit)
            if r == 0:
                args.checklimit = None
            else:
                args.checklimit = time.time() + r
    except ValueError:
        pass

    # if we've got the values we need from the command line, stop now
    if args.maxttl and args.keyttl:
        return args

    # load keyttl and maxttl data from zonefile
    if args.zone and args.filename:
        try:
            zone = keyzone(args.zone[0], args.filename, args.compilezone)
            args.maxttl = args.maxttl or zone.maxttl
    