Changeset 158

Show
Ignore:
Timestamp:
04/03/10 23:11:14 (5 months ago)
Author:
mbooth
Message:

Restructure the record tree that we build up so we can hold more information and display it in a better way when we render it.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • trunk/misc/dns-o-matic.py

    r157 r158  
    1717import DNS 
    1818from netaddr import IPNetwork, IPAddress 
     19from pprint import PrettyPrinter 
    1920 
    2021DNS.DiscoverNameServers() 
     
    3435 
    3536 
    36 def RecursiveLookup(data,record,type,nameserver,astart=None,ptrstart=None): 
    37     r = None 
    38     new_data = {'record':record, 'results':[], 'status':None} 
    39  
     37def RecursiveLookup(data,rec_lookup,type,nameserver,astart=None,ptrstart=None): 
    4038    # exit when a request loop is detected 
    4139    if type == 'PTR': 
    4240        if ptrstart == None: 
    43             ptrstart = record 
    44         elif ptrstart == record: 
    45             data['results'].append(new_data) 
    46             return data 
    47         else: 
    48             ptrstart = record 
     41            ptrstart = rec_lookup 
     42        elif ptrstart == rec_lookup: 
     43            return 
     44        else: 
     45            ptrstart = rec_lookup 
    4946    else: 
    5047        if astart == None: 
    51             astart = record 
    52         elif astart == record: 
    53             data['results'].append(new_data) 
    54             return data 
    55         else: 
    56             astart = record 
    57  
    58     # reverse lookup request gives you all the hostnames that the given ip address resolves to 
     48            astart = rec_lookup 
     49        elif astart == rec_lookup: 
     50            return 
     51        else: 
     52            astart = rec_lookup 
     53 
     54    r = None 
    5955    if type == 'PTR': 
    60         rec = IPAddress(record) 
     56        # reverse lookup request gives you all the hostnames that the given ip address resolves to 
     57        rec = IPAddress(rec_lookup) 
    6158        r = Request(rec.reverse_dns,type,nameserver) 
    62  
    63     # forward lookup request gives you all the ip addresses that the given hostname resolves to 
    64     else: 
    65         r = Request(record,type,nameserver) 
    66  
    67     # make note of status message and recurse if there were answers 
     59    else: 
     60        # forward lookup request gives you all the ip addresses that the given hostname resolves to 
     61        r = Request(rec_lookup,type,nameserver) 
     62 
     63    # we shouldn't ever time out 
    6864    if r == None: 
    69         new_data['status'] = 'TIMEOUT' 
    70         data['results'].append(new_data) 
    71     else: 
    72         new_data['status'] = r.header['status'] 
    73         for rec,rec_type in [(x['data'],x['type']) for x in r.answers]: 
     65        sys.stderr.write("ERROR: Timed out querying for %s records from %s\n" % (type, nameserver)) 
     66        sys.exit(1) 
     67 
     68    # loop through all the answers 
     69    if r.header['status'] == 'NOERROR': 
     70        for rec_result,rec_type in [(x['data'],x['type']) for x in r.answers]: 
    7471 
    7572            # despite only ever asking for PTR or A records, we sometimes get CNAMEs, so discard them 
    7673            if rec_type not in set([DNS.Type.A, DNS.Type.PTR]): 
    7774                continue 
     75 
     76            # add to the list of answers 
     77            answer = { 
     78                      'ns'          : nameserver, 
     79                      'lookup_of'   : rec_lookup, 
     80                      'lookup_type' : type, 
     81                      'resolves_to' : rec_result, 
     82                      'status'      : r.header['status'], 
     83                      'subrecords'  : [], 
     84                      } 
     85            data.append(answer) 
     86 
     87            # rinse and repeat 
    7888            if type == 'PTR': 
    79                 RecursiveLookup(new_data,rec,'A',nameserver,astart,ptrstart) 
     89                RecursiveLookup(answer['subrecords'],rec_result,'A',  nameserver,astart,ptrstart) 
    8090            else: 
    81                 RecursiveLookup(new_data,rec,'PTR',nameserver,astart,ptrstart) 
    82         data['results'].append(new_data) 
     91                RecursiveLookup(answer['subrecords'],rec_result,'PTR',nameserver,astart,ptrstart) 
     92    else: 
     93        # add the error to the list of answers 
     94        answer = { 
     95                  'ns'          : nameserver, 
     96                  'lookup_of'   : rec_lookup, 
     97                  'lookup_type' : type, 
     98                  'resolves_to' : '', 
     99                  'status'      : r.header['status'], 
     100                  'subrecords'  : [], 
     101                  } 
     102        data.append(answer) 
    83103 
    84104 
     
    130150 
    131151def GetSubnetAnalysis(nameservers,subnet): 
    132     data = {'subnet':subnet, 'nameservers':[], 'addresses':[]} 
    133     primary = -1 
    134     for i,(ns,pri) in enumerate(nameservers): 
     152    data = {'subnet':str(subnet), 'nameservers':[], 'addresses':[]} 
     153 
     154    # make a note of which is the primary nameserver 
     155    primary = '' 
     156    for ns,pri in nameservers: 
    135157        data['nameservers'].append({'name':ns, 'primary':pri}) 
    136158        if pri: 
    137             primary = i 
     159            primary = ns 
    138160 
    139161    # iterate through all valid addresses in the subnet 
    140     sys.stderr.write("Subnet %s...\n" % str(subnet)); 
     162    sys.stderr.write("Processing Subnet: %s\n" % str(subnet)) 
    141163    for ip in [str(x) for x in list(subnet) if x != subnet.broadcast and x != subnet.ip]: 
    142         sys.stderr.write("Processing %s\n" % ip); 
     164        sys.stderr.write(".") 
     165 
     166        # request the crap out of the DNS servers 
    143167        address = {'ip':ip, 'results':[]} 
    144168        for ns,pri in nameservers: 
    145             RecursiveLookup(address,ip,'PTR',ns) 
     169            RecursiveLookup(address['results'],ip,'PTR',ns) 
    146170 
    147171        # forget about addresses that don't have records in *any* nameservers 
    148172        for result in address['results']: 
    149             if result['status'] == 'NOERROR': 
     173            if result['status'] != 'NXDOMAIN': 
    150174                data['addresses'].append(address) 
    151175                break 
     176    sys.stderr.write("\n") 
    152177 
    153178    # iterate through all addresses for which we have results 
    154     for address in data['addresses']: 
    155         for ns_result in address['results']: 
    156             FindValidLoops(ns_result) 
     179#   for address in data['addresses']: 
     180#       for ns_result in address['results']: 
     181#           FindValidLoops(ns_result) 
    157182    return data 
    158183 
     
    160185def RecursivePrintResults(data,level=0): 
    161186    # display the record we're looking up 
    162     if data['valid']: 
    163         style = "class='good'" 
    164     else: 
    165         style = "" 
    166     if not data['status'] and len(data['results']) == 0: 
    167         print "    <p %s style='padding-left:%spx'><span class='record'>%s</span></p>" % (style, `level * 20`, data['record']) 
    168     else: 
    169         print "    <p %s style='padding-left:%spx'><span class='record'>%s</span> resolves to:</p>" % (style, `level * 20`, data['record']) 
    170  
    171     # recursively display lookup results or show error 
    172     next_level = level + 1 
    173     if len(data['results']) > 0: 
    174         for result in data['results']: 
    175             RecursivePrintResults(result,next_level) 
    176     elif data['status']: 
    177         print "    <p style='padding-left:%spx'><span class='record'>%s</span></p>" % (`next_level * 20`, data['status']) 
     187    if data['resolves_to'] != '': 
     188        print "    <p style='padding-left:%spx'><span class='record'>%s</span> resolves to: <span class='record'>%s</span></p>" % (`level * 20`, data['lookup_of'], data['resolves_to']) 
     189    else: 
     190        print "    <p style='padding-left:%spx'><span class='record'>%s</span> does not resolve to anything!</p>" % (`level * 20`, data['lookup_of']) 
     191 
     192    # recursively display subrecords 
     193    if len(data['subrecords']) > 0: 
     194        for rec in data['subrecords']: 
     195            RecursivePrintResults(rec,level + 1) 
    178196 
    179197 
    180198def PrintSubnetAnalysis(data): 
    181     print "<h2>Analysis of the %s subnet</h2>" % data['subnet'] 
    182     print "<table border='1'>" 
     199    subnet_id = "subnet-" + data['subnet'].replace('/','-').replace('.','-') 
     200    print "<h2 id='%s_heading'>Analysis of the %s subnet</h2>" % (subnet_id, data['subnet']) 
     201    print "<p><a class='debugs_on'  id='%s_on'  href='#%s_heading'>Show Debugs</a>" % (subnet_id, subnet_id) + \ 
     202             "<a class='debugs_off' id='%s_off' href='#%s_heading'>Hide Debugs</a></p>" % (subnet_id, subnet_id) 
     203    print "<table id='%s_table' border='1'>" % subnet_id 
    183204 
    184205    heading = "<tr><th>address</th>" 
     
    189210            heading += "<th>%s</th>" % ns['name'] 
    190211    heading += "</tr>" 
    191              
     212 
    192213    for i,address in enumerate(data['addresses']): 
    193214        if i % 25 == 0: 
    194215            print heading 
    195216        print "<tr>\n  <td><p>%s</p></td>" % address['ip'] 
    196         for ns_result in address['results']: 
     217        for ns in data['nameservers']: 
    197218            print "  <td>" 
    198             RecursivePrintResults(ns_result) 
     219            for result in [x for x in address['results'] if x['ns'] == ns['name']]: 
     220                RecursivePrintResults(result) 
    199221            print "  </td>" 
    200222        print "</tr>" 
    201223    print "</table>" 
    202224 
    203     # for debugging 
    204     import pprint 
    205     print "<pre style='display:none;'>" 
    206     pp = pprint.PrettyPrinter(indent=2) 
     225    # print the raw data structure for debugging 
     226    print "<pre id='%s_debugs' style='display:none;'>" % subnet_id 
     227    pp = PrettyPrinter(indent=2) 
    207228    pp.pprint(data) 
    208229    print "</pre>" 
     
    216237  <script type="text/javascript"> 
    217238    jQuery(document).ready(function($) { 
    218       $("a#debugs_on").css('display', 'inline'); 
    219       $("a#debugs_on").click(function() { 
    220         $("pre").css('display', 'block'); 
    221         $("table").css('display', 'none'); 
    222         $("a#debugs_on").css('display', 'none'); 
    223         $("a#debugs_off").css('display', 'inline'); 
     239      /* display the show debugs buttons by default */ 
     240      $("a.debugs_on").css('display', 'inline'); 
     241 
     242      /* show debugs button click event callback */ 
     243      $("a.debugs_on").click(function() { 
     244        var subnet = "#" + $(this).attr('id').split('_')[0]; 
     245        $(subnet+"_debugs").css('display', 'block'); 
     246        $(subnet+"_table").css('display', 'none'); 
     247        $(subnet+"_on").css('display', 'none'); 
     248        $(subnet+"_off").css('display', 'inline'); 
    224249      }); 
    225       $("a#debugs_off").click(function() { 
    226         $("pre").css('display', 'none'); 
    227         $("table").css('display', 'block'); 
    228         $("a#debugs_on").css('display', 'inline'); 
    229         $("a#debugs_off").css('display', 'none'); 
     250 
     251      /* hide debugs button click event callback */ 
     252      $("a.debugs_off").click(function() { 
     253        var subnet = "#" + $(this).attr('id').split('_')[0]; 
     254        $(subnet+"_debugs").css('display', 'none'); 
     255        $(subnet+"_table").css('display', 'table'); 
     256        $(subnet+"_on").css('display', 'inline'); 
     257        $(subnet+"_off").css('display', 'none'); 
    230258      }); 
    231259    }); 
     
    234262    th { 
    235263      font-family:sans-serif; 
     264      padding:5px 0px; 
    236265    } 
    237266    td { 
     
    240269      font-size:0.75em; 
    241270      color:#666; 
     271      padding:5px; 
    242272    } 
    243273    td p { 
     
    249279      color:black; 
    250280    } 
    251     .good { 
    252       background-color:#cec; 
    253     } 
    254     .good .record { 
    255       color:#393; 
    256     } 
    257     .bad { 
    258       background-color:#d99; 
     281    .debugs_on, .debugs_off { 
     282      display:none; 
    259283    } 
    260284  </style> 
     
    262286<body> 
    263287<h1>DNS-o-Matic Report</h1> 
    264   <a id='debugs_on' href='#' style='display:none;'>Show Debugs</a> 
    265   <a id='debugs_off' href='#' style='display:none;'>Hide Debugs</a> 
    266288""" 
    267289 
    268290domain = 'cse-servelec.com' 
    269 subnets = [IPNetwork('194.62.153.0/24'), 
     291subnets = [ 
     292           IPNetwork('194.62.153.0/24'), 
    270293           IPNetwork('194.62.154.0/24'), 
    271294           ]