#!/usr/bin/python

#PyDNSmap DNS mapper by Chrisg 
#based on dnsmap - DNS Network Mapper by pagvac from http://ikwt.com
# performs a dictornary attack against name servers.  The only real difference
# between this and dnsmap is that it will query the proper name servers for a domain
# instead of using your local nameserver (which can give you cached information)
#
# usage: pydnsmap.py -d Target Domain -f (optional dictionary file)

#importing our stuff
import os
import sys
#uses pydns 2.3.0 availble from http://pydns.sourceforge.net/
import DNS
#optparse so we can  have our nifty command line
from optparse import OptionParser

banner = "\nPyDNSmap.py v0.1 - DNS Network Mapper by chrisg\n"

#setting up the command line and  help menu
#if you change the default dictonary you'll need to cange the default="blist.txt" to your default dictonary name
#or you can just call your dictonary and override the default

parser = OptionParser(usage="%prog [-d] [-f]", version="%prog 0.1")
parser.add_option("-d", "--domain", dest="hostname",
                  help="The target domain to map. Example: google.com (Required)\n", metavar="Target Domain"),
parser.add_option("-f", "--file", dest="dict", default="blist.txt", 
                  help="The Dictionary File to use (optional)", metavar="Dictonary File")

(options, args) = parser.parse_args()

print banner

#shameless stolen from Foundations of Python Network Programing by John Goerzen Chapter4 DNS
def hierquery(qstring, qtype):
    """Given a query type qtype, returns answers of that type for lookup
    qstring.  If no answers are found, removes the most specific component
    (the part before the leftmost period) and retries the query with the
    result.  If the topmost query fails, returns None."""
    reqobj = DNS.Request()
    try:
        answerobj = reqobj.req(name = qstring, qtype = qtype)
        answers = [x['data'] for x in answerobj.answers if x['type'] == qtype]
    except DNS.Base.DNSError:
        answers = []                    # Fake an empty return
    if len(answers):
        return answers
    else:
        remainder = qstring.split(".", 1)
        if len(remainder) == 1:
            return None
        else:
            return hierquery(remainder[1], qtype)

#shameless stolen from Foundations of Python Network Programing by John Goerzen Chapter4 DNS
def findnameservers(hostname):
    """Attempts to determine the authoritative nameservers for a given
    hostname.  Returns None on failure."""
    return hierquery(hostname, DNS.Type.NS)

#shameless stolen from Foundations of Python Network Programing by John Goerzen Chapter4 DNS
def getrecordsfromnameserver(qstring, qtype, nslist):
    """Given a list of nameservers in nslist, executes the query requested
    by qstring and qtype on each in order, returning the data from the first
    server that returned 1 or more answers.  If no server returned any answers,
    returns []."""
    for ns in nslist:
        reqobj = DNS.Request(server = ns)
        try:
            answers = reqobj.req(name = qstring, qtype = qtype).answers
            if len(answers):
                return answers
        except DNS.Base.DNSError:
            pass
    return []
        
#shameless stolen from Foundations of Python Network Programing by John Goerzen Chapter4 DNS
def nslookup(qstring, qtype, verbose = 0):
    nslist = findnameservers(qstring)
    if nslist == None:
        raise RuntimeError, "Could not find nameserver to use."
    if verbose:
        print "Using nameservers:", ", ".join(nslist)
    return getrecordsfromnameserver(qstring, qtype, nslist)

try:
#try the intial domain name to make sure exists before we dig into it, should return a not found if its invalid
	query = options.hostname
    	DNS.DiscoverNameServers()
    	answers = nslookup(query, DNS.Type.ANY)
    	if not len(answers):
		print "Domain Name: "+ query +" Not found."
		
	else:
#if the domain is valid, begin the dictionary attack to find servers

		print "Beginning the mapping process...\n"
		if __name__ == '__main__':
#open the dictonary file and read it in line by line
			inp = open(options.dict,"r")
			line = inp.readline()
			while line:
					stripped = line.rstrip() #strip off the new line so we can pass it correctly to the DNSlookup
					var = stripped+"."+options.hostname
					query = var
					DNS.DiscoverNameServers()	
					answers = nslookup(query, DNS.Type.A) #return the A record, change this to ANY if you want everything			
					for item in answers:
						print query
						print "%-5s %s" % (item['typename'], item['data'])
					line = inp.readline()
	
			inp.close()

#hopefully enough exception handling
except IOError:
	print"Dictionary File Not Found! Check Filename\n"
except KeyboardInterrupt:
                print
                print "Caught CTRL+C...exiting..."
except RuntimeError:
		print "Domain Name not found...check that your domain name exists!"
except:
	print "PyDNSmap requires the -d Target Domain argument to run or use -h for options \n"
	