Re: axfr bringing my dns config error to light

Matthew Dempsky <[email protected]> Tue, 6 Apr 2010 21:27:00 -0700
Newsgroups gmane.network.djbdns
Message-ID <[email protected]>
On Tue, Apr 6, 2010 at 6:02 PM, Jeremy Kister
<[email protected]> wrote:
> any ideas on how to differentiate 'essential' records from not ?

Are the extra records actually problematic for you?

But assuming they are, you could pipe the resulting file through a
script like the Python script I wrote below.  It's really only
flexible enough to handle the output of axfr-get, but the general idea
is how I'd tackle it.  Also, if you want to support DNSSEC, you'll
need to tweak the 'keep' function to keep DS records at zone cuts (and
their RRSIGs).


import sys

def inzone(n1, n2):
  return n1[-(1 + len(n2)):] == '.' + n2

zone = None
nsrecords = dict()

# First pass through the zone file just to collect the NS records (and
# the SOA record to determine what zone we're looking at).
for line in sys.stdin:
  line = line.rstrip(' \t\n')
  if line == '': continue
  if line[0] not in 'Z&': continue
  fields = line[1:].split(':')
  if line[0] == 'Z':
    zone = fields[0]
  else:
    nsrecords.setdefault(fields[0], set()).add(fields[2].rstrip('.'))

if not zone:
  print 'No SOA record?'
  sys.exit(1)

# Don't worry about NS records for the zone itself.
if zone in nsrecords:
  del nsrecords[zone]

zonecuts = set()
gluenames = set()

# Go through NS records and discard grandchildren records.  E.g., if
# we're processing example.com and there are NS records for both
# foo.example.com and bar.foo.example.com, we only want
# foo.example.com.
for n1, ns in nsrecords.items():
  for n2 in nsrecords.keys():
    if inzone(n1, n2):
      break
  else:
    zonecuts.add(n1)
    gluenames.update(ns)


def keep(name, rtype):
  # Keep A and AAAA records for names used in NS records.
  if name in gluenames and rtype in ['+', 28]: return True

  for zone in zonecuts:
    # If a name is at a zone cut, only keep it if it's an NS record.
    if name == zone: return rtype == '&'

    # Discard any other records within the zone cut.
    if inzone(name, zone): return False

  # Keep everything else.
  return True

sys.stdin.seek(0)

# Loop through the file a second time, only printing out records for
# names that we've decided to keep.
for line in sys.stdin:
  line = line.rstrip(' \t\n')
  if line == '': continue
  if line[0] in '-#':
    print line
    continue
  fields = line[1:].split(':')
  if keep(fields[0], line[0] == ':' and int(fields[1]) or line[0]):
    print line