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