1#!/usr/bin/perl
2
3# loclist.pl -- check a list of hostnames for LOC records
4
5#  -v  -- verbose output (include NO results).  used to be the default
6#  -n  -- try looking for network LOC records as well (slower)
7#  -r  -- try doing reverse-resolution on IP-appearing hosts
8#  -d  -- debugging output
9
10# egrep 'loc2earth.*host' /serv/www/logs/wn.log |
11#    perl -pe 's/^.*host=//; s/([a-zA-Z0-9.-]+).*/$1/' |
12#    sort -u | ~/loclist.pl > loc.sites
13
14use Net::DNS '0.08';
15use Getopt::Std;
16
17getopts('vnrd');
18
19$res = new Net::DNS::Resolver;
20
21line:
22    foreach $_ (<>) {
23      chomp;
24      $foundloc = $namefound = 0;
25
26      next line if m/^$/;
27      next line if m/[^\w.-\/+_]/; # /, +, _ not actually valid in hostnames
28
29      print STDERR "$_ DEBUG looking up...\n" if $opt_d;
30
31      if (m/^\d+\.\d+\.\d+\.\d+$/) {
32	if ($opt_r) {
33	  $query = $res->query($_);
34
35	  if (defined ($query)) {
36	    foreach $ans ($query->answer) {
37	      if ($ans->type eq "PTR") {
38		$_ = $ans->ptrdname;
39		$namefound++;
40	      }
41	    }
42	  }
43	}
44	next line unless $namefound;
45      }
46
47      $query = $res->query($_,"LOC");
48
49      if (defined ($query)) {	# then we got an answer of some sort
50	foreach $ans ($query->answer) {
51	  if ($ans->type eq "LOC") {
52	    print "$_ YES ",$ans->rdatastr,"\n";
53	    $foundloc++;
54	  }
55	}
56      }
57      if ($opt_n && !$foundloc) {		# try the RFC 1101 search bit
58	@addrs = @netnames = ();
59	$query = $res->query($_,"A");
60	if (defined ($query)) {
61	  foreach $ans ($query->answer) {
62	    if ($ans->type eq "A") {
63	      push(@addrs,$ans->address);
64	    }
65	  }
66	}
67	if (@addrs) {
68	checkaddrs:
69	  foreach $ipstr (@addrs) {
70	    $ipnum = unpack("N",pack("CCCC",split(/\./,$ipstr,4)));
71	    ($ip1) = split(/\./,$ipstr);
72	    if ($ip1 >= 224) { # class D/E, treat as host addr
73	      $mask = 0xFFFFFFFF;
74	    } elsif ($ip1 >= 192) { # "class C"
75	      $mask = 0xFFFFFF00;
76	    } elsif ($ip1 >= 128) { # "class B"
77	      $mask = 0xFFFF0000;
78	    } else {	# class A
79	      $mask = 0xFF000000;
80	    }
81	    $oldmask = 0;
82	    while ($oldmask != $mask) {
83	      $oldmask = $mask;
84	      $querystr =
85		  join(".", reverse (unpack("CCCC",pack("N",$ipnum & $mask))))
86		      . ".in-addr.arpa";
87	      $query = $res->query($querystr,"PTR");
88	      if (defined ($query)) {
89		foreach $ans ($query->answer) {
90		  if ($ans->type eq "PTR") {
91		    # we want the list in LIFO order
92		    unshift(@netnames,$ans->ptrdname);
93		  }
94		}
95		$query = $res->query($querystr,"A");
96		if (defined ($query)) {
97		  foreach $ans ($query->answer) {
98		    if ($ans->type eq "A") {
99		      $mask = unpack("L",pack("CCCC",
100					      split(/\./,$ans->address,4)));
101		    }
102		  }
103		}
104	      }
105	    }
106	    if (@netnames) {
107	      foreach $network (@netnames) {
108		$query = $res->query($network,"LOC");
109		if (defined ($query)) {
110		  foreach $ans ($query->answer) {
111		    if ($ans->type eq "LOC") {
112		      print "$_ YES ",$ans->rdatastr,"\n";
113		      $foundloc++;
114		      last checkaddrs;
115		    } elsif ($ans->type eq "CNAME") {
116		      # XXX should follow CNAME chains here
117		    }
118		  }
119		}
120	      }
121	    }
122	  }
123	}
124      }
125      if ($opt_v && !$foundloc) {
126	print "$_ NO\n";
127      }
128    }
129