Source code for dns_sprockets_lib.validators.nsec_chain

'''
nsec_chain - Zone test: NsecChain

.. Copyright (c) 2015 Neustar, Inc. All rights reserved.
.. See COPYRIGHT.txt for full notice.  See LICENSE.txt for terms and conditions.
'''


import dns.name

import dns_sprockets_lib.validators as validators


[docs]class NsecChain(validators.ZoneTest): # pylint: disable=too-few-public-methods ''' Checks for valid NSEC chain. ''' TEST_DNSSECTYPE = 'NSEC'
[docs] def run(self, context, suggested_tested): # Get all the NSEC's in a name.upper() -> nsec_rdata dict: nsec_dict = dict( [(str(nsec[0]).upper(), nsec[1][0]) for nsec in context.zone_obj.iterate_rdatasets('NSEC')]) # Check there's at least one NSEC: if len(nsec_dict) == 0: return (suggested_tested, 'No NSECs in the zone') result = None # Pick any one as the first, and follow the chain, should end up on same # one after len(nsec_dict) hops. Also keep track of number of times # an NSEC's next value is canonically less than the name[0]; it should # be exactly one, for a correctly constructed chain: for (first_name, nsec_rdata) in nsec_dict.iteritems(): break # pylint: disable=undefined-loop-variable name = first_name next_less_than_name_cnt = 0 for _ in xrange(len(nsec_dict)): # Get the next name upper'd: next_name = str(nsec_rdata.next).upper() # Count number of times NSEC's next is canonically less than name: (_, order, _) = dns.name.from_text(next_name).fullcompare( dns.name.from_text(name)) if order < 0: next_less_than_name_cnt += 1 # Make sure there's an NSEC for the next name: if next_name not in nsec_dict: result = 'Chain broken at %s (next=%s doesn\'t exist)' % ( name, next_name) break name = next_name nsec_rdata = nsec_dict[name] if not result and name != first_name: result = 'Chain walk didn\'t end up on start item' if not result and next_less_than_name_cnt != 1: result = 'Chain not ordered correctly' return (suggested_tested, result) # end of file