#!/usr/bin/env python

# References:
#   http://www.cs.cornell.edu/People/egs/beehive/dnssurvey.html
#   http://www.dns.pl/cgi-bin/dnsexplorer.pl

debug_level = 2
exclude_root = False

import sys
import string

# DNS Python http://www.dnspython.org/
import dns.resolver

already_seen_domains = {}
already_seen_nameservers = {}
recursion_depth = 0

def error(message):
    sys.stderr.write("ERROR: %s\n" % message)
    sys.exit(1)

def debug(message, max_level=1):
    if debug_level >= max_level:
        indent = " " * (2*recursion_depth)
        sys.stdout.write("%sDEBUG: %s\n" % (indent, message))

def canonicalize(old_name):
    name = string.lower(old_name)
    if name[len(name)-1] != '.':
        name = name + '.'
    debug("Canonical form of %s is %s" % (old_name, name), max_level=3)
    return name

def ns_of(domain):
    global recursion_depth
    domain = canonicalize(domain)
    if already_seen_domains.has_key(domain): # A domain can have the same name as one of its
        # nameservers (example at 2005-07-18: tmc.edu)
        return []
    recursion_depth = recursion_depth + 1
    result = []
    try:
        answers = dns.resolver.query(domain, 'NS')
        debug("%i responses for %s" % (len(answers), domain), 2)
    except dns.resolver.NoAnswer:
        debug("No answer for %s" % domain)    
        answers = None # Try the parent, anyway
    except dns.resolver.NXDOMAIN:
        debug("No such domain %s" % domain)
        recursion_depth = recursion_depth - 1
        return result
    already_seen_domains[domain] = True
    (label, parent) = string.split(domain, ".", 1)
    if not exclude_root:
        if parent == "":
            parent = "." # The root
    if label != "": # We are not yet at the top (the root)
        if not already_seen_domains.has_key(parent):            
            debug("Parent of %s is %s" % (domain, parent))
            for ns in ns_of(parent):
                result.append(ns)
    if answers is not None:
        for rdata in answers:
            server = canonicalize(str(rdata))
            if not already_seen_nameservers.has_key(server):                
                result.append(server)
                for ns in ns_of(server):
                    result.append(ns)
            already_seen_nameservers[server] = True
    debug("Nameservers of %s are %s" % (domain, str(result)))
    recursion_depth = recursion_depth - 1
    return result

if len(sys.argv) != 2:
    error("Usage: %s domain" % sys.argv[0])
domain = sys.argv[1]
nameservers = ns_of(domain)
print "%i nameservers for %s" % (len(nameservers), domain)


	      

