Testing tool for DNS migrations
Testing tool for DNS migrations

Very useful tool to test your DNS zone files using python. I have tested this script to validate changes done on a production zone file. The initial request was the following :

  • cleanup an existing Bind DNS zone file
  • remove unnecessary $TTL
  • sort DNS entry by type to make easy searching and editing

When the new version of the zone file has been completed, a comparison has been done between this file and the production zone file hosted on a bind server (for the example the ip address is 10.1.1.1) :
python /home/user/dns_compare.py -z yourzone.domain –file /home/user/yourzone.domain.dns-newfile –server 10.1.1.1

The output will show you all the differences and you will be able to check if there are errors in your new zone file before you use it in production :

(MIS-MATCH) query: nss4.yourzone.domain.
 Expected:  300 IN A 142.229.40.28
 Received:  900 IN A 142.229.40.28
...X
(MIS-MATCH) query: www.yourzone.domain.
 Expected:  200 IN A 91.139.129.128
 Received:  900 IN A 91.139.129.128
X
(MIS-MATCH) query: www.yourzone.domain.
 Expected:  300 IN AAAA 2001:4800:1078:2256:78C8:1542:FF04:6BCB
 Received:  900 IN AAAA 2001:4800:1078:2256:78c8:1542:ff04:6bcb
......................done

Results:
Matches:      69
Mis-matches:  3

By default, SOA and NS records are ignored because these records are likely to change when migrating a zone between DNS services.. Specify –soa or –ns option, respectively, to enable checking of SOA and NS records.

Comparing TTLs can be disabled with -t option. This is useful when transferring DNS to a provider that offers only specific TTL values.

dnspython is required to run this tool. Just run the following command to install it :
apt-get install python-dnspython

dns_compare.py:

#!/usr/bin/python
#
# dns_compare.py - Compare the data in a BIND zone file to the data being returned
#                  by an authoritative DNS server.
#
# Purpose:
#   Use this tool to verify the data being returned by an authoritative DNS server
#   matches the data in a zone file.
#
# Use case:
#   This is useful, for example, when you are migrating from one DNS server to another and
#   need to verify that all the records imported correctly.
#
#   In my case, I used this tool to help me during a migration of multiple domains from
#   Windows 2000 DNS and GoDaddy DNS (which can both export BIND zone files) into Amazon's
#   Route53 DNS service.  With this tool, I could confidently prove that all records
#   properly imported into Route53.
#
# Example usage:
#       $ dns_compare.py -z example.com --file example.com.zone --server 10.1.1.1
#       ....X...X..done
#       Results:
#       9 passed / 2 fail
#
#   NOTE: use -v to get a very verbose view of each dns record as it is checked
#
# Author:
#   joe miller, <joeym@joeym.net>, 12/16/2010
#

from optparse import OptionParser

import sys, socket
from pprint import pprint

try:
    import dns.resolver, dns.zone
    from dns.exception import DNSException
    from dns.rdataclass import *
    from dns.rdatatype import *
except ImportError:
    print "Please install dnspython:"
    print "$ sudo pip install dnspython"
    sys.exit(1)


if __name__ == '__main__':
    parser = OptionParser()
    # required options
    parser.add_option("-z", "--zone", dest="zone", metavar="DOMAIN",
                        help="name of the domain we're checking (eg: domain.com)")
    parser.add_option("-f", "--file", dest="zonefile", metavar="FILE",
                        help="zone file to load records from")
    parser.add_option("-s", "--server", dest="nameserver", metavar="HOST",
                        help="DNS server to compare zone file against")
    # optional ... options
    parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
                        help="print detailed results of each action")
    parser.add_option("-a","--soa", dest="compare_soa", action="store_true", default=False,
                        help="compare SOA records (default: false)")
    parser.add_option("-n","--ns", dest="compare_ns", action="store_true", default=False,
                        help="compare NS records (default: false)")
    parser.add_option("-t","--ttl", dest="compare_ttl", action="store_false", default=True,
                        help="compare TTL values (default: true)")
    (opts, remaining_args) = parser.parse_args()

    # check for required options, since optparse doesn't support required options
    if opts.zone == None or opts.zonefile == None or opts.nameserver == None:
        print "Error: required arguments: --zone, --file, --server (or --help)"
        sys.exit(2)

    z = dns.zone.from_file(opts.zonefile, origin=opts.zone, relativize=False)

    r = dns.resolver.Resolver(configure=False)
    try:
        r.nameservers = socket.gethostbyname_ex(opts.nameserver)[2]
    except socket.error:
        print "Error: could not resolve 'host' %s" % opts.nameserver
        sys.exit(3)

    matches=0
    mismatches=0
    for (name, rdataset) in z.iterate_rdatasets():
        if rdataset.rdtype == SOA and not opts.compare_soa:
            continue
        if rdataset.rdtype == NS and not opts.compare_ns:
            continue

        match = False
        result = None
        try:
            ans = r.query(name, rdataset.rdtype, rdataset.rdclass)
            result = ans.rrset.to_rdataset()
            if result == rdataset:
                if opts.compare_ttl:
                    if result.ttl == rdataset.ttl:
                        match = True
                else:
                    match = True
        except DNSException, e:
            pass

        if opts.verbose:
            description = 'Match' if match else 'MIS-MATCH'
            print "----"
            print "(%s) query: %s" % (description, name)
            print "Expected: ", rdataset
            print "Received: ", result

        if match:
            if not opts.verbose:
                sys.stdout.write('.')
                sys.stdout.flush()
            matches += 1
        else:
            if not opts.verbose:
                sys.stdout.write('X')
                print "\n(MIS-MATCH) query: %s" % name
                print " Expected: ", rdataset
                print " Received: ", result
                sys.stdout.flush()
            mismatches += 1
    print "done"

    print "\nResults:"
    print "Matches:     ", matches
    print "Mis-matches: ", mismatches

<>

My Powershell script categories


References

Github

Testing tool for DNS migrations

Leave a Reply

Your email address will not be published. Required fields are marked *