#!/usr/bin/perl #!/perl/bin/perl #!/perl5/perl #!your/perl/path/goes/on first line! use strict; # xdns.pl v. 0.05 # Another Analog DNS helper # (c) 2000 Marco Bernardini - webmaster@taggiasca.com # # latest version at # http://www.taggiasca.com/perl/xdns.htm # # This script is useful just # in case you have 2 DNS files and you need # # a) merge them # b) remove double entries # c) recover resolved IPs now unresolved # d) sort them by IP # e) be warned if the same IP has different names # f) modify the format of the DNS file # g) remove WINS names (GOOFY rather than modem01.foo.com) # h) count how much group A IPs you have # i) strip unresolved IPs to trade DNS files with friends # ######## # # You can use this utility freely, and you can # modify and improve it (let me know, if you do # this!): it is under Perl Artistic License. # It may be used for commercial use only by # prior arrangement with the author # (Marco Bernardini, webmaster of taggiasca.com). # # This program is free, but if you buy some # excellent extravirgin olive oil on my site # http://www.taggiasca.com # this make me a lot happy. # # Analog is (c) by Stephen Turner - see # http://www.analog.cx for more infos # ######## # # history: # # v. 0.05 # - added config file - MUST BE xdns.cfg # - some bug solved (I hope) # - now new file don't need to be the bigger one # # v. 0.04 (unreleased) # added # - WINS (on | off | save) to save removed WINS names # - WINSFILE to store removed WINS names # - BUG solved - WINS names don't contain . and are != * # v. 0.03 # added # - WINS (on | off) to remove WINS names # - WARNING (on | off) to see warnings on video # - SPINCOUNT (on | off) to see spinning counter # - COUNTA (on | off) to count A (123.*.*.*) groups # - JUSTCOUNT (on | off) to perform only the count of A groups # - STRIPUNR (on | off) to strip out unresolved IPs # - table format for $dns_err file # v. 0.02 # added # - warning are saved into $dns_err file # - BUG solved: now 2 files are really merged # # todo: # - sorting of a single file # - command line params # - CGI interface # - selection of other config files # ############################## my $adesso = time ; my %d1; my %d2; my @tmp; my @tmp2; my $i; my $n; my $wl = 1; my $newname; my $newtime; my $oldname; my $oldtime; my $ipx; my $decip; my $winsnames=0; # config file variables are contained in the hash %config my %config; my $configfile ="xdns.cfg"; ############################## &ReadCfg; # added in 0.05 if ($config{'WARNING'} eq "on") { print "Configuration:\n"; foreach $i (keys %config) { print "$i = $config{$i}\n"; } print "\n"; } if ($config{'JUSTCOUNT'} eq "on") { &CntA; &EndPrgm; } if ($config{'STRIPUNR'} eq "on") { &StripUnr; &EndPrgm; } if ($config{'WINS'} eq "file") { # create empty $config{'WINSFILE'} open (F, ">$config{'WINSFILE'}") || die $!; close F; } print "Reading $config{'DNS_ONE'}\n"; $n = 0; open (DNS , "$config{'DNS_ONE'}") || die $!; @tmp = ; close DNS; chomp @tmp; %d1 = &ScanFile (@tmp); print "\nReading $config{'DNS_TWO'}\n"; $n = 0; open (DNS , "$config{'DNS_TWO'}") || die $!; @tmp2 = ; close DNS; chomp @tmp2; %d2 = &ScanFile (@tmp2); # new in 1.02 # add to %d1 what is different in %d2 print "\nAdding different lines to $config{'DNS_ONE'}\n"; $n = 0; my $nl=0; foreach $i (keys %d2) { if ($d1{$i} eq "") { $d1{$i}=$d2{$i} ; $nl++; } &SpinC if ($config{'SPINCOUNT'} eq "on"); $n++; } print "\nAdded $nl lines to $config{'DNS_ONE'}\n\n"; $n=0; open DNSER, ">$config{'DNS_ERR'}" || die $!; print DNSER "LIST OF IPs GIVING DIFFERENT RESULTS\nIP\tOLD\tNEW\n\n"; close DNSER; open (DNS ,">$config{'DNS_OUT'}") || die $!; foreach $decip (sort keys %d1) { &SpinC if ($config{'SPINCOUNT'} eq "on"); $ipx = &Dec2IP ($decip); ($newname,$newtime) = split '\|',$d1{$decip} ; ($oldname,$oldtime) = split '\|',$d2{$decip} ; # new in 0.05 # swap $newname and $oldname if $newtime older than $oldtime - it happens! if ($oldtime > $newtime) { ($newname,$oldname) = ($oldname,$newname); ($newtime,$oldtime) = ($oldtime,$newtime); } if ($newname eq "*") { # new DNS query is * if ( ($oldname eq "*") # old DNS query is * or "" || ($oldname eq "") ) { print DNS &FrmOut($newtime,$ipx,$newname),"\n"; # use new DNS $wl++; } else { print DNS &FrmOut($oldtime,$ipx,$oldname),"\n"; # use old DNS $wl++; } } else { # new DNS query is not * if ( ($oldname eq "*") # old DNS query is * or "" || ($oldname eq "") ) { print DNS &FrmOut($newtime,$ipx,$newname),"\n"; # use new DNS $wl++; } } if ( ($newname ne "*") # new DNS query is not * && # and ( ($oldname ne "*") # old DNS query is something different than * && ($oldname ne "") ) && # and ($newname ne $oldname) # two queries don't match ) { my $ot = localtime($oldtime*60); my $nt = localtime($newtime*60); print "\n\nWARNING: $ipx gives two different results:\n$ot entry was $oldname\n$nt entry is $newname\nCheck manually please!\n\n" if ($config{'WARNING'} eq "on"); if ($config{'DNS_ERR'} gt "") { open DNSER, ">>$config{'DNS_ERR'}" || die $!; print DNSER "$ipx\t($ot) $oldname\t($nt) $newname\n"; # new format since v. 0.05 close DNSER } print DNS &FrmOut($newtime,$ipx,$newname),"\n"; # use new DNS $wl++; } if ( ($newname ne "*") # new DNS query is not * && # and ( ($oldname ne "*") # old DNS query is something different than * && ($oldname ne "") ) && # and ($newname eq $oldname) # two queries match ) { print DNS &FrmOut($newtime,$ipx,$newname),"\n"; # use new DNS $wl++; } # print DNS "$decip\t$ipx\t$newname\t$oldname\n"; # debug: print old and new together $n++; } close DNS; &CntA if ($config{'COUNTA'} eq "on"); my $dopo=time; my $tempo=$dopo-$adesso; print "\n\a$wl lines saved.\n"; print "$winsnames WINS names removed" if ($winsnames > 0); print " and saved into $config{'WINSFILE'}" if ($config{'WINS'} eq "file"); print "\nSee $config{'DNS_ERR'} for warnings\n"; &EndPrgm; ##################### # # scan DNS files sub ScanFile { my @tmp = @_; my %tmp2; my $i; foreach $i (@tmp) { my ($time,$ipx,$name) = split (' ',$i); # new in 0.03 - remove WINS names if ($config{'WINS'} eq "on") { if (($name !~ /\./) && ($name ne "*")) { # remove DUMMY names and make them * (unresolved) $name = "*"; $winsnames++; } } # new in 0.04 - save and remove WINS names if ($config{'WINS'} eq "file") { if (($name !~ /\./) && ($name ne "*")) { # remove DUMMY names save them and make them * (unresolved) open (F, ">>$config{'WINSFILE'}") || die $!; print F "$time $ipx $name\n"; close F; $name = "*"; $winsnames++; } } $tmp2{&IP2Dec ($ipx)}=$name."|".$time; if ($config{'SPINCOUNT'} eq "on") { # another spinning counter - new in v. 0.03 &SpinC; $n++; } } return %tmp2; } ##################### # # convert xx.xx.xx.xx to decimal # to sort domains (adding leading 0s) sub IP2Dec { my ($ipx) = @_; my ($ax,$bx,$cx,$dx) = split '\.',$ipx; my $ip = sprintf "%02X%02X%02X%02X", $ax,$bx,$cx,$dx; $ip = hex ($ip); $ip = sprintf "%010lu",$ip; #print "$ip\t$ax,$bx,$cx,$dx\t\n"; return $ip; } ##################### # # convert decimal to xx.xx.xx.xx # to show domains as usual sub Dec2IP { my ($ip) = @_; my $grk = sprintf "%08lx", $ip; my $ay = hex (substr ($grk,0,2)); my $by = hex (substr ($grk,2,2)); my $cy = hex (substr ($grk,4,2)); my $dy = hex (substr ($grk,6,2)); #print "$ip\t$grk\t$ay.$by.$cy.$dy\n"; return "$ay.$by.$cy.$dy"; } ##################### # # format the output # # fanalog pre-built Analog format # ftime time # fip ip # fname default name # fdate time (date format) # fdec decimal ip (for sorting) # fname1 new name # fname2 old name # ftime1 new time # ftime2 old time # fdate1 new time (date format) # fdate2 old time (date format) # for separators use # fspace space # ftab tab # fcomma , # fcolon : # fsemi ; # fcrlf new line sub FrmOut { my ($time,$ip,$name) = @_; my $fout; if ($config{'DNS_FORMAT'} eq "fanalog") { $fout = "$time $ip $name"; return $fout; # jump back now! } $fout = $config{'DNS_FORMAT'}; $fout =~ s/fspace/ /gi; $fout =~ s/fcomma/,/gi; $fout =~ s/fcolon/:/gi; $fout =~ s/fsemi/;/gi; $fout =~ s/ftab/\t/gi; $fout =~ s/fcrlf/\n/gi; $fout =~ s/fname1/$newname/gi; $fout =~ s/fname2/$oldname/gi; $fout =~ s/fname/$name/gi; $fout =~ s/ftime1/$newtime/gi; $fout =~ s/ftime2/$oldtime/gi; $fout =~ s/ftime/$time/gi; $fout =~ s/fip/$ip/gi; $fout =~ s/fdec/$decip/gi; $fout =~ s/fdate1/localtime($newtime*60)/gie; $fout =~ s/fdate2/localtime($oldtime*60)/gie; $fout =~ s/fdate/localtime($time*60)/gie; return $fout; } ################################### # # counting A (123.*.*.*) IPs - new in 0.03 # # new in 0.05 - for a faster response # these ranges are excluded: # 10.x.x.x # 65.x.x.x - 127.x.x.x # 173.x.x.x - 191.x.x.x # 197.x.x.x # 226.x.x.x - 255.x.x.x sub CntA { my $i; my %grA; my @tmp; print "\nCounting A groups into $config{'DNS_OUT'} "; if ($config{'CNT_SORT'} eq "value") { print "sorted by value\n"; } else { print "sorted by IP\n"; } open (DNS , "$config{'DNS_OUT'}") || die $!; @tmp = ; close DNS; chomp @tmp; for $i (1..9,11..64,128..172,192..196,198..225) { $grA{$i} = scalar (grep (/\s$i\.\d{1,3}\.\d{1,3}\.\d{1,3}\s/ , @tmp)) ; print "$i\t$grA{$i}\n"; } no strict; open (DNS , ">$config{'DNS_CNT'}") || die $!; if ($config{'CNT_SORT'} eq "ip") { foreach $i (sort {$a <=> $b} keys %grA) { print DNS "$i\t $grA{$i}\n" if ($grA{$i} > 0); } } else { foreach $i (sort {$grA{$b} <=> $grA{$a}} keys %grA) { print DNS "$i\t $grA{$i}\n" if ($grA{$i} != 0); } } close DNS; print "The count is saved into $config{'DNS_CNT'}\n"; } ################################### # # trashing unresolved IPs - new in 0.03 - debugged in 0.05 sub StripUnr { print "Reading $config{'DNS_ONE'} and removing unresolved IPs\n"; open (DNS , "$config{'DNS_ONE'}") || die $!; my @tmp = ; close DNS; my @tmp2 = grep !/\s\*/ , @tmp; #@tmp2 =~ s/\n /\n/g; open (DNS , ">$config{'DNS_OUT'}") || die $!; print DNS @tmp2; close DNS; print "The result is saved into $config{'DNS_OUT'}\n"; } ################################### # # spinning counter - modified in 0.03 sub SpinC { my $bs = $config{'BS'}; printf "%c%c%c%c%c%c%c%07.0f",$bs,$bs,$bs,$bs,$bs,$bs,$bs,$n; } ################################### # # end the program and count the time - modified in 0.03 sub EndPrgm { my $dopo=time; my $tempo=$dopo-$adesso; print "\aJob time: $tempo seconds\n"; exit; } ################################### # # read the configuration file xdns.cfg - new in 0.05 my $i; sub ReadCfg { open (CFG , "$configfile") || die $!; my @tmp = ; close CFG; @tmp = grep !/^[#|\n]/ , @tmp; chomp @tmp; foreach $i (@tmp) { my ($ky,$val) = split / /,$i; $ky = uc($ky); $val = lc($val); $config{$ky} = $val; } } ## EOF ##