"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.16.7/bin/python/isc/policy.py.in" (4 Sep 2020, 24813 Bytes) of package /linux/misc/dns/bind9/9.16.7/bind-9.16.7.tar.xz:


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 import re
   13 import ply.lex as lex
   14 import ply.yacc as yacc
   15 from string import *
   16 from copy import copy
   17 
   18 
   19 ############################################################################
   20 # PolicyLex: a lexer for the policy file syntax.
   21 ############################################################################
   22 class PolicyLex:
   23     reserved = ('POLICY',
   24                 'ALGORITHM_POLICY',
   25                 'ZONE',
   26                 'ALGORITHM',
   27                 'DIRECTORY',
   28                 'KEYTTL',
   29                 'KEY_SIZE',
   30                 'ROLL_PERIOD',
   31                 'PRE_PUBLISH',
   32                 'POST_PUBLISH',
   33                 'COVERAGE',
   34                 'STANDBY',
   35                 'NONE')
   36 
   37     tokens = reserved + ('DATESUFFIX',
   38                          'KEYTYPE',
   39                          'ALGNAME',
   40                          'STR',
   41                          'QSTRING',
   42                          'NUMBER',
   43                          'LBRACE',
   44                          'RBRACE',
   45                          'SEMI')
   46     reserved_map = {}
   47 
   48     t_ignore           = ' \t'
   49     t_ignore_olcomment = r'(//|\#).*'
   50 
   51     t_LBRACE           = r'\{'
   52     t_RBRACE           = r'\}'
   53     t_SEMI             = r';';
   54 
   55     def t_newline(self, t):
   56         r'\n+'
   57         t.lexer.lineno += t.value.count("\n")
   58 
   59     def t_comment(self, t):
   60         r'/\*(.|\n)*?\*/'
   61         t.lexer.lineno += t.value.count('\n')
   62 
   63     def t_DATESUFFIX(self, t):
   64         r'(?i)(?<=[0-9 \t])(y(?:ears|ear|ea|e)?|mo(?:nths|nth|nt|n)?|w(?:eeks|eek|ee|e)?|d(?:ays|ay|a)?|h(?:ours|our|ou|o)?|mi(?:nutes|nute|nut|nu|n)?|s(?:econds|econd|econ|eco|ec|e)?)\b'
   65         t.value = re.match(r'(?i)(y|mo|w|d|h|mi|s)([a-z]*)', t.value).group(1).lower()
   66         return t
   67 
   68     def t_KEYTYPE(self, t):
   69         r'(?i)\b(KSK|ZSK)\b'
   70         t.value = t.value.upper()
   71         return t
   72 
   73     def t_ALGNAME(self, t):
   74         r'(?i)\b(DH|ECC|RSASHA1|NSEC3RSASHA1|RSASHA256|RSASHA512|ECDSAP256SHA256|ECDSAP384SHA384|ED25519|ED448)\b'
   75         t.value = t.value.upper()
   76         return t
   77 
   78     def t_STR(self, t):
   79         r'[A-Za-z._-][\w._-]*'
   80         t.type = self.reserved_map.get(t.value, "STR")
   81         return t
   82 
   83     def t_QSTRING(self, t):
   84         r'"([^"\n]|(\\"))*"'
   85         t.type = self.reserved_map.get(t.value, "QSTRING")
   86         t.value = t.value[1:-1]
   87         return t
   88 
   89     def t_NUMBER(self, t):
   90         r'\d+'
   91         t.value = int(t.value)
   92         return t
   93 
   94     def t_error(self, t):
   95         print("Illegal character '%s'" % t.value[0])
   96         t.lexer.skip(1)
   97 
   98     def __init__(self, **kwargs):
   99         if 'maketrans' in dir(str):
  100             trans = str.maketrans('_', '-')
  101         else:
  102             trans = maketrans('_', '-')
  103         for r in self.reserved:
  104             self.reserved_map[r.lower().translate(trans)] = r
  105         self.lexer = lex.lex(object=self, **kwargs)
  106 
  107     def test(self, text):
  108         self.lexer.input(text)
  109         while True:
  110             t = self.lexer.token()
  111             if not t:
  112                 break
  113             print(t)
  114 
  115 ############################################################################
  116 # Policy: this object holds a set of DNSSEC policy settings.
  117 ############################################################################
  118 class Policy:
  119     is_zone = False
  120     is_alg = False
  121     is_constructed = False
  122     ksk_rollperiod = None
  123     zsk_rollperiod = None
  124     ksk_prepublish = None
  125     zsk_prepublish = None
  126     ksk_postpublish = None
  127     zsk_postpublish = None
  128     ksk_keysize = None
  129     zsk_keysize = None
  130     ksk_standby = None
  131     zsk_standby = None
  132     keyttl = None
  133     coverage = None
  134     directory = None
  135     valid_key_sz_per_algo = {'RSASHA1': [1024, 4096],
  136                              'NSEC3RSASHA1': [512, 4096],
  137                              'RSASHA256': [1024, 4096],
  138                              'RSASHA512': [1024, 4096],
  139                              'ECDSAP256SHA256': None,
  140                              'ECDSAP384SHA384': None,
  141                              'ED25519': None,
  142                              'ED448': None}
  143 
  144     def __init__(self, name=None, algorithm=None, parent=None):
  145         self.name = name
  146         self.algorithm = algorithm
  147         self.parent = parent
  148         pass
  149 
  150     def __repr__(self):
  151         return ("%spolicy %s:\n"
  152                 "\tinherits %s\n"
  153                 "\tdirectory %s\n"
  154                 "\talgorithm %s\n"
  155                 "\tcoverage %s\n"
  156                 "\tksk_keysize %s\n"
  157                 "\tzsk_keysize %s\n"
  158                 "\tksk_rollperiod %s\n"
  159                 "\tzsk_rollperiod %s\n"
  160                 "\tksk_prepublish %s\n"
  161                 "\tksk_postpublish %s\n"
  162                 "\tzsk_prepublish %s\n"
  163                 "\tzsk_postpublish %s\n"
  164                 "\tksk_standby %s\n"
  165                 "\tzsk_standby %s\n"
  166                 "\tkeyttl %s\n"
  167                 %
  168                 ((self.is_constructed and 'constructed ' or \
  169                   self.is_zone and 'zone ' or \
  170                   self.is_alg and 'algorithm ' or ''),
  171                  self.name or 'UNKNOWN',
  172                  self.parent and self.parent.name or 'None',
  173                  self.directory and ('"' + str(self.directory) + '"') or 'None',
  174                  self.algorithm or 'None',
  175                  self.coverage and str(self.coverage) or 'None',
  176                  self.ksk_keysize and str(self.ksk_keysize) or 'None',
  177                  self.zsk_keysize and str(self.zsk_keysize) or 'None',
  178                  self.ksk_rollperiod and str(self.ksk_rollperiod) or 'None',
  179                  self.zsk_rollperiod and str(self.zsk_rollperiod) or 'None',
  180                  self.ksk_prepublish and str(self.ksk_prepublish) or 'None',
  181                  self.ksk_postpublish and str(self.ksk_postpublish) or 'None',
  182                  self.zsk_prepublish and str(self.zsk_prepublish) or 'None',
  183                  self.zsk_postpublish and str(self.zsk_postpublish) or 'None',
  184                  self.ksk_standby and str(self.ksk_standby) or 'None',
  185                  self.zsk_standby and str(self.zsk_standby) or 'None',
  186                  self.keyttl and str(self.keyttl) or 'None'))
  187 
  188     def __verify_size(self, key_size, size_range):
  189         return (size_range[0] <= key_size <= size_range[1])
  190 
  191     def get_name(self):
  192         return self.name
  193 
  194     def constructed(self):
  195         return self.is_constructed
  196 
  197     def validate(self):
  198         """ Check if the values in the policy make sense
  199         :return: True/False if the policy passes validation
  200         """
  201         if self.ksk_rollperiod and \
  202            self.ksk_prepublish is not None and \
  203            self.ksk_prepublish > self.ksk_rollperiod:
  204             print(self.ksk_rollperiod)
  205             return (False,
  206                     ('KSK pre-publish period (%d) exceeds rollover period %d'
  207                      % (self.ksk_prepublish, self.ksk_rollperiod)))
  208 
  209         if self.ksk_rollperiod and \
  210            self.ksk_postpublish is not None and \
  211            self.ksk_postpublish > self.ksk_rollperiod:
  212             return (False,
  213                     ('KSK post-publish period (%d) exceeds rollover period %d'
  214                      % (self.ksk_postpublish, self.ksk_rollperiod)))
  215 
  216         if self.zsk_rollperiod and \
  217            self.zsk_prepublish is not None and \
  218            self.zsk_prepublish >= self.zsk_rollperiod:
  219             return (False,
  220                     ('ZSK pre-publish period (%d) exceeds rollover period %d'
  221                      % (self.zsk_prepublish, self.zsk_rollperiod)))
  222 
  223         if self.zsk_rollperiod and \
  224            self.zsk_postpublish is not None and \
  225            self.zsk_postpublish >= self.zsk_rollperiod:
  226             return (False,
  227                     ('ZSK post-publish period (%d) exceeds rollover period %d'
  228                      % (self.zsk_postpublish, self.zsk_rollperiod)))
  229 
  230         if self.ksk_rollperiod and \
  231            self.ksk_prepublish and self.ksk_postpublish and \
  232            self.ksk_prepublish + self.ksk_postpublish >= self.ksk_rollperiod:
  233             return (False,
  234                     (('KSK pre/post-publish periods (%d/%d) ' +
  235                       'combined exceed rollover period %d') %
  236                      (self.ksk_prepublish,
  237                       self.ksk_postpublish,
  238                       self.ksk_rollperiod)))
  239 
  240         if self.zsk_rollperiod and \
  241            self.zsk_prepublish and self.zsk_postpublish and \
  242            self.zsk_prepublish + self.zsk_postpublish >= self.zsk_rollperiod:
  243             return (False,
  244                     (('ZSK pre/post-publish periods (%d/%d) ' +
  245                       'combined exceed rollover period %d') %
  246                      (self.zsk_prepublish,
  247                       self.zsk_postpublish,
  248                       self.zsk_rollperiod)))
  249 
  250         if self.algorithm is not None:
  251             # Validate the key size
  252             key_sz_range = self.valid_key_sz_per_algo.get(self.algorithm)
  253             if key_sz_range is not None:
  254                 # Verify KSK
  255                 if not self.__verify_size(self.ksk_keysize, key_sz_range):
  256                     return False, 'KSK key size %d outside valid range %s' \
  257                             % (self.ksk_keysize, key_sz_range)
  258 
  259                 # Verify ZSK
  260                 if not self.__verify_size(self.zsk_keysize, key_sz_range):
  261                     return False, 'ZSK key size %d outside valid range %s' \
  262                             % (self.zsk_keysize, key_sz_range)
  263 
  264             if self.algorithm in ['ECDSAP256SHA256', \
  265                                   'ECDSAP384SHA384', \
  266                                   'ED25519', \
  267                                   'ED448']:
  268                 self.ksk_keysize = None
  269                 self.zsk_keysize = None
  270 
  271         return True, ''
  272 
  273 ############################################################################
  274 # dnssec_policy:
  275 # This class reads a dnssec.policy file and creates a dictionary of
  276 # DNSSEC policy rules from which a policy for a specific zone can
  277 # be generated.
  278 ############################################################################
  279 class PolicyException(Exception):
  280     pass
  281 
  282 class dnssec_policy:
  283     alg_policy = {}
  284     named_policy = {}
  285     zone_policy = {}
  286     current = None
  287     filename = None
  288     initial = True
  289 
  290     def __init__(self, filename=None, **kwargs):
  291         self.plex = PolicyLex()
  292         self.tokens = self.plex.tokens
  293         if 'debug' not in kwargs:
  294             kwargs['debug'] = False
  295         if 'write_tables' not in kwargs:
  296             kwargs['write_tables'] = False
  297         self.parser = yacc.yacc(module=self, **kwargs)
  298 
  299         # set defaults
  300         self.setup('''policy global { algorithm rsasha256;
  301                                       key-size ksk 2048;
  302                                       key-size zsk 2048;
  303                                       roll-period ksk 0;
  304                                       roll-period zsk 1y;
  305                                       pre-publish ksk 1mo;
  306                                       pre-publish zsk 1mo;
  307                                       post-publish ksk 1mo;
  308                                       post-publish zsk 1mo;
  309                                       standby ksk 0;
  310                                       standby zsk 0;
  311                                       keyttl 1h;
  312                                       coverage 6mo; };
  313                       policy default { policy global; };''')
  314 
  315         p = Policy()
  316         p.algorithm = None
  317         p.is_alg = True
  318         p.ksk_keysize = 2048;
  319         p.zsk_keysize = 2048;
  320 
  321         # set default algorithm policies
  322 
  323         # these can use default settings
  324         self.alg_policy['RSASHA1'] = copy(p)
  325         self.alg_policy['RSASHA1'].algorithm = "RSASHA1"
  326         self.alg_policy['RSASHA1'].name = "RSASHA1"
  327 
  328         self.alg_policy['NSEC3RSASHA1'] = copy(p)
  329         self.alg_policy['NSEC3RSASHA1'].algorithm = "NSEC3RSASHA1"
  330         self.alg_policy['NSEC3RSASHA1'].name = "NSEC3RSASHA1"
  331 
  332         self.alg_policy['RSASHA256'] = copy(p)
  333         self.alg_policy['RSASHA256'].algorithm = "RSASHA256"
  334         self.alg_policy['RSASHA256'].name = "RSASHA256"
  335 
  336         self.alg_policy['RSASHA512'] = copy(p)
  337         self.alg_policy['RSASHA512'].algorithm = "RSASHA512"
  338         self.alg_policy['RSASHA512'].name = "RSASHA512"
  339 
  340         self.alg_policy['ECDSAP256SHA256'] = copy(p)
  341         self.alg_policy['ECDSAP256SHA256'].algorithm = "ECDSAP256SHA256"
  342         self.alg_policy['ECDSAP256SHA256'].name = "ECDSAP256SHA256"
  343         self.alg_policy['ECDSAP256SHA256'].ksk_keysize = None;
  344         self.alg_policy['ECDSAP256SHA256'].zsk_keysize = None;
  345 
  346         self.alg_policy['ECDSAP384SHA384'] = copy(p)
  347         self.alg_policy['ECDSAP384SHA384'].algorithm = "ECDSAP384SHA384"
  348         self.alg_policy['ECDSAP384SHA384'].name = "ECDSAP384SHA384"
  349         self.alg_policy['ECDSAP384SHA384'].ksk_keysize = None;
  350         self.alg_policy['ECDSAP384SHA384'].zsk_keysize = None;
  351 
  352         self.alg_policy['ED25519'] = copy(p)
  353         self.alg_policy['ED25519'].algorithm = "ED25519"
  354         self.alg_policy['ED25519'].name = "ED25519"
  355         self.alg_policy['ED25519'].ksk_keysize = None;
  356         self.alg_policy['ED25519'].zsk_keysize = None;
  357 
  358         self.alg_policy['ED448'] = copy(p)
  359         self.alg_policy['ED448'].algorithm = "ED448"
  360         self.alg_policy['ED448'].name = "ED448"
  361         self.alg_policy['ED448'].ksk_keysize = None;
  362         self.alg_policy['ED448'].zsk_keysize = None;
  363 
  364         if filename:
  365             self.load(filename)
  366 
  367     def load(self, filename):
  368         self.filename = filename
  369         self.initial = True
  370         with open(filename) as f:
  371             text = f.read()
  372             self.plex.lexer.lineno = 0
  373             self.parser.parse(text)
  374 
  375         self.filename = None
  376 
  377     def setup(self, text):
  378         self.initial = True
  379         self.plex.lexer.lineno = 0
  380         self.parser.parse(text)
  381 
  382     def policy(self, zone, **kwargs):
  383         z = zone.lower()
  384         p = None
  385 
  386         if z in self.zone_policy:
  387             p = self.zone_policy[z]
  388 
  389         if p is None:
  390             p = copy(self.named_policy['default'])
  391             p.name = zone
  392             p.is_constructed = True
  393 
  394         if p.algorithm is None:
  395             parent = p.parent or self.named_policy['default']
  396             while parent and not parent.algorithm:
  397                 parent = parent.parent
  398             p.algorithm = parent and parent.algorithm or None
  399 
  400         if p.algorithm in self.alg_policy:
  401             ap = self.alg_policy[p.algorithm]
  402         else:
  403             raise PolicyException('algorithm not found')
  404 
  405         if p.directory is None:
  406             parent = p.parent or self.named_policy['default']
  407             while parent is not None and not parent.directory:
  408                 parent = parent.parent
  409             p.directory = parent and parent.directory
  410 
  411         if p.coverage is None:
  412             parent = p.parent or self.named_policy['default']
  413             while parent and not parent.coverage:
  414                 parent = parent.parent
  415             p.coverage = parent and parent.coverage or ap.coverage
  416 
  417         if p.ksk_keysize is None:
  418             parent = p.parent or self.named_policy['default']
  419             while parent.parent and not parent.ksk_keysize:
  420                 parent = parent.parent
  421             p.ksk_keysize = parent and parent.ksk_keysize or ap.ksk_keysize
  422 
  423         if p.zsk_keysize is None:
  424             parent = p.parent or self.named_policy['default']
  425             while parent.parent and not parent.zsk_keysize:
  426                 parent = parent.parent
  427             p.zsk_keysize = parent and parent.zsk_keysize or ap.zsk_keysize
  428 
  429         if p.ksk_rollperiod is None:
  430             parent = p.parent or self.named_policy['default']
  431             while parent.parent and not parent.ksk_rollperiod:
  432                 parent = parent.parent
  433             p.ksk_rollperiod = parent and \
  434                 parent.ksk_rollperiod or ap.ksk_rollperiod
  435 
  436         if p.zsk_rollperiod is None:
  437             parent = p.parent or self.named_policy['default']
  438             while parent.parent and not parent.zsk_rollperiod:
  439                 parent = parent.parent
  440             p.zsk_rollperiod = parent and \
  441                 parent.zsk_rollperiod or ap.zsk_rollperiod
  442 
  443         if p.ksk_prepublish is None:
  444             parent = p.parent or self.named_policy['default']
  445             while parent.parent and not parent.ksk_prepublish:
  446                 parent = parent.parent
  447             p.ksk_prepublish = parent and \
  448                 parent.ksk_prepublish or ap.ksk_prepublish
  449 
  450         if p.zsk_prepublish is None:
  451             parent = p.parent or self.named_policy['default']
  452             while parent.parent and not parent.zsk_prepublish:
  453                 parent = parent.parent
  454             p.zsk_prepublish = parent and \
  455                 parent.zsk_prepublish or ap.zsk_prepublish
  456 
  457         if p.ksk_postpublish is None:
  458             parent = p.parent or self.named_policy['default']
  459             while parent.parent and not parent.ksk_postpublish:
  460                 parent = parent.parent
  461             p.ksk_postpublish = parent and \
  462                 parent.ksk_postpublish or ap.ksk_postpublish
  463 
  464         if p.zsk_postpublish is None:
  465             parent = p.parent or self.named_policy['default']
  466             while parent.parent and not parent.zsk_postpublish:
  467                 parent = parent.parent
  468             p.zsk_postpublish = parent and \
  469                 parent.zsk_postpublish or ap.zsk_postpublish
  470 
  471         if p.keyttl is None:
  472             parent = p.parent or self.named_policy['default']
  473             while parent is not None and not parent.keyttl:
  474                 parent = parent.parent
  475             p.keyttl = parent and parent.keyttl
  476 
  477         if 'novalidate' not in kwargs or not kwargs['novalidate']:
  478             (valid, msg) = p.validate()
  479             if not valid:
  480                 raise PolicyException(msg)
  481                 return None
  482 
  483         return p
  484 
  485 
  486     def p_policylist(self, p):
  487         '''policylist : init policy
  488                       | policylist policy'''
  489         pass
  490 
  491     def p_init(self, p):
  492         "init :"
  493         self.initial = False
  494 
  495     def p_policy(self, p):
  496         '''policy : alg_policy
  497                   | zone_policy
  498                   | named_policy'''
  499         pass
  500 
  501     def p_name(self, p):
  502         '''name : STR
  503                 | KEYTYPE
  504                 | DATESUFFIX'''
  505         p[0] = p[1]
  506         pass
  507 
  508     def p_domain(self, p):
  509         '''domain : STR
  510                   | QSTRING
  511                   | KEYTYPE
  512                   | DATESUFFIX'''
  513         p[0] = p[1].strip()
  514         if not re.match(r'^[\w.-][\w.-]*$', p[0]):
  515             raise PolicyException('invalid domain')
  516         pass
  517 
  518     def p_new_policy(self, p):
  519         "new_policy :"
  520         self.current = Policy()
  521 
  522     def p_alg_policy(self, p):
  523         "alg_policy : ALGORITHM_POLICY ALGNAME new_policy alg_option_group SEMI"
  524         self.current.name = p[2]
  525         self.current.is_alg = True
  526         self.alg_policy[p[2]] = self.current
  527         pass
  528 
  529     def p_zone_policy(self, p):
  530         "zone_policy : ZONE domain new_policy policy_option_group SEMI"
  531         self.current.name = p[2].rstrip('.')
  532         self.current.is_zone = True
  533         self.zone_policy[p[2].rstrip('.').lower()] = self.current
  534         pass
  535 
  536     def p_named_policy(self, p):
  537         "named_policy : POLICY name new_policy policy_option_group SEMI"
  538         self.current.name = p[2]
  539         self.named_policy[p[2].lower()] = self.current
  540         pass
  541 
  542     def p_duration_1(self, p):
  543         "duration : NUMBER"
  544         p[0] = p[1]
  545         pass
  546 
  547     def p_duration_2(self, p):
  548         "duration : NONE"
  549         p[0] = None
  550         pass
  551 
  552     def p_duration_3(self, p):
  553         "duration : NUMBER DATESUFFIX"
  554         if p[2] == "y":
  555             p[0] = p[1] * 31536000 # year
  556         elif p[2] == "mo":
  557             p[0] = p[1] * 2592000  # month
  558         elif p[2] == "w":
  559             p[0] = p[1] * 604800   # week
  560         elif p[2] == "d":
  561             p[0] = p[1] * 86400    # day
  562         elif p[2] == "h":
  563             p[0] = p[1] * 3600     # hour
  564         elif p[2] == "mi":
  565             p[0] = p[1] * 60       # minute
  566         elif p[2] == "s":
  567             p[0] = p[1]            # second
  568         else:
  569             raise PolicyException('invalid duration')
  570 
  571     def p_policy_option_group(self, p):
  572         "policy_option_group : LBRACE policy_option_list RBRACE"
  573         pass
  574 
  575     def p_policy_option_list(self, p):
  576         '''policy_option_list : policy_option SEMI
  577                               | policy_option_list policy_option SEMI'''
  578         pass
  579 
  580     def p_policy_option(self, p):
  581         '''policy_option : parent_option
  582                          | directory_option
  583                          | coverage_option
  584                          | rollperiod_option
  585                          | prepublish_option
  586                          | postpublish_option
  587                          | keysize_option
  588                          | algorithm_option
  589                          | keyttl_option
  590                          | standby_option'''
  591         pass
  592 
  593     def p_alg_option_group(self, p):
  594         "alg_option_group : LBRACE alg_option_list RBRACE"
  595         pass
  596 
  597     def p_alg_option_list(self, p):
  598         '''alg_option_list : alg_option SEMI
  599                            | alg_option_list alg_option SEMI'''
  600         pass
  601 
  602     def p_alg_option(self, p):
  603         '''alg_option : coverage_option
  604                       | rollperiod_option
  605                       | prepublish_option
  606                       | postpublish_option
  607                       | keyttl_option
  608                       | keysize_option
  609                       | standby_option'''
  610         pass
  611 
  612     def p_parent_option(self, p):
  613         "parent_option : POLICY name"
  614         self.current.parent = self.named_policy[p[2].lower()]
  615 
  616     def p_directory_option(self, p):
  617         "directory_option : DIRECTORY QSTRING"
  618         self.current.directory = p[2]
  619 
  620     def p_coverage_option(self, p):
  621         "coverage_option : COVERAGE duration"
  622         self.current.coverage = p[2]
  623 
  624     def p_rollperiod_option(self, p):
  625         "rollperiod_option : ROLL_PERIOD KEYTYPE duration"
  626         if p[2] == "KSK":
  627             self.current.ksk_rollperiod = p[3]
  628         else:
  629             self.current.zsk_rollperiod = p[3]
  630 
  631     def p_prepublish_option(self, p):
  632         "prepublish_option : PRE_PUBLISH KEYTYPE duration"
  633         if p[2] == "KSK":
  634             self.current.ksk_prepublish = p[3]
  635         else:
  636             self.current.zsk_prepublish = p[3]
  637 
  638     def p_postpublish_option(self, p):
  639         "postpublish_option : POST_PUBLISH KEYTYPE duration"
  640         if p[2] == "KSK":
  641             self.current.ksk_postpublish = p[3]
  642         else:
  643             self.current.zsk_postpublish = p[3]
  644 
  645     def p_keysize_option(self, p):
  646         "keysize_option : KEY_SIZE KEYTYPE NUMBER"
  647         if p[2] == "KSK":
  648             self.current.ksk_keysize = p[3]
  649         else:
  650             self.current.zsk_keysize = p[3]
  651 
  652     def p_standby_option(self, p):
  653         "standby_option : STANDBY KEYTYPE NUMBER"
  654         if p[2] == "KSK":
  655             self.current.ksk_standby = p[3]
  656         else:
  657             self.current.zsk_standby = p[3]
  658 
  659     def p_keyttl_option(self, p):
  660         "keyttl_option : KEYTTL duration"
  661         self.current.keyttl = p[2]
  662 
  663     def p_algorithm_option(self, p):
  664         "algorithm_option : ALGORITHM ALGNAME"
  665         self.current.algorithm = p[2]
  666 
  667     def p_error(self, p):
  668         if p:
  669             print("%s%s%d:syntax error near '%s'" %
  670                     (self.filename or "", ":" if self.filename else "",
  671                      p.lineno, p.value))
  672         else:
  673             if not self.initial:
  674                 raise PolicyException("%s%s%d:unexpected end of input" %
  675                     (self.filename or "", ":" if self.filename else "",
  676                      p and p.lineno or 0))
  677 
  678 if __name__ == "__main__":
  679     import sys
  680     if sys.argv[1] == "lex":
  681         file = open(sys.argv[2])
  682         text = file.read()
  683         file.close()
  684         plex = PolicyLex(debug=1)
  685         plex.test(text)
  686     elif sys.argv[1] == "parse":
  687         try:
  688             pp = dnssec_policy(sys.argv[2], write_tables=True, debug=True)
  689             print(pp.named_policy['default'])
  690             print(pp.policy("nonexistent.zone"))
  691         except Exception as e:
  692             print(e.args[0])