#!/usr/bin/perl -w # $Id: whoisnet,v 1.3 2003/02/14 15:39:27 candy Exp candy $ use strict; use POSIX; use Socket; use Getopt::Std; my $HOME = $ENV{HOME}; my $quit = 0; my $debug; my $hosts_file = "$HOME/bin/whois.db"; my $hosts_log_file = "$HOME/bin/whois.log"; my $read_log_mode = 0; my $wait_sec = 10; my $did_whois = 0; # whois ¼Â¹Ô¤·¤¿ sub nz($) { return defined($_[0]) ? $_[0] : ""; } sub p(@) { my @v = @_; my $i; for ($i = 0; $i <= $#v; $i++) { printf("%d %s\n", $i, $v[$i]); } } sub tobin($$) { my($x, $col) = @_; my($s, $i); $s = ""; for ($i = 0; $i < $col; $i++) { $s = (($x & 1) ? "1" : "0") . $s; $x >>= 1; } return $s; } sub tobin8($) { my($x) = @_; my $i; my $s = ""; for ($i = 0; $i < 4; $i++) { $s = ":" . tobin($x & 0xff, 8) . $s; $x >>= 8; } $s =~ s/.//; return $s; } sub inet_addr($) { my($ipstr) = @_; my(@v, $i, $s_addr); @v = split(/\./, $ipstr . ".0.0.0.0"); $s_addr = 0; for ($i = 0; $i < 4; $i++) { $s_addr = ($s_addr * 256) + $v[$i]; } return $s_addr; } sub s_addr_to_v($) { my($s_addr) = @_; my(@v); $v[0] = ($s_addr >> 24) & 0xff; $v[1] = ($s_addr >> 16) & 0xff; $v[2] = ($s_addr >> 8) & 0xff; $v[3] = $s_addr & 0xff; return @v; } sub s_addr_to_dec($) { my($s_addr) = @_; my(@v) = s_addr_to_v($s_addr); return sprintf("%d.%d.%d.%d", $v[0], $v[1], $v[2], $v[3]); } my @net_lo; my @net_hi; my @net_co; my $net_used = 0; my $tab_changed = 0; sub net_add($$$) { my($blo, $bhi, $co) = @_; $net_lo[$net_used] = $blo; $net_hi[$net_used] = $bhi; $net_co[$net_used] = $co; $net_used++; return $net_used; } sub net_lookup($) { my($ip) = @_; my($i) = 0; my($found) = -1; while ($found == -1 && $i < $net_used) { if ($ip >= $net_lo[$i] && $ip <= $net_hi[$i]) { $found = $i; } $i++; } return $found; } sub read_hosts($) { my($file, $line, @v) = @_; if (open(F, $file)) { while ($line = ) { if ($line =~ /^$|^#/) { # ignore } else { chomp($line); @v = split(/[ \t]+/, $line); if (nz($v[2]) ne "") { net_add(inet_addr($v[0]), inet_addr($v[1]), $v[2]); } } } close(F); } else { printf(STDERR "%s: $!\n", $file); } } sub write_hosts($) { my($file, $i, $ip) = @_; if (open(F, ">$file")) { my(%ptr_tabx); for ($i = 0; $i < $net_used; $i++) { $ptr_tabx{$net_lo[$i]} = $i; } foreach $ip (sort {$a <=> $b} @net_lo) { my($lo, $hi, $co); $i = $ptr_tabx{$ip}; $lo = s_addr_to_dec($net_lo[$i]); $hi = s_addr_to_dec($net_hi[$i]); $co = $net_co[$i]; printf(F "%s %s %s\n", $lo, $hi, $co); } close(F); $tab_changed = 0; } else { printf(STDERR "%s: $!\n", $file); } } sub cmd($) { my($cmd) = @_; my(@v); if (open(F, "$cmd")) { my ($i); @v = ; close(F); for ($i = 0; $i <= $#v; $i++) { chomp($v[$i]); } } else { logger("$cmd: $!\n"); } return @v; } sub getval($@) { my($k, @v) = @_; my $ret; my @ni = grep(/$k/, @v); if ($#ni >= 0) { my $x = $ni[0]; $x =~ s/[ \t]+(.*)//; $ret = $1; } return $ret; } sub whois($) { my($ip) = @_; my(@v) = cmd("whois $ip |"); $did_whois = 1; if ($#v == -1) { $quit = 1; } else { if (open(LOG, ">>$hosts_log_file")) { my($i); printf(LOG ">>> %s\n", $ip); printf(LOG "### %s\n", strftime("%Y/%m/%d %H:%M:%S", localtime())); for ($i = 0; $i <= $#v; $i++) { printf(LOG "%s\n", $v[$i]); } close(LOG); } else { printf(STDERR "%s: $!\n", $hosts_log_file); } } return @v; } sub whois_from_log($) { my($ip) = @_; my(@v); if (open(LOG, $hosts_log_file)) { my($lbuf, $found, $head); $found = -1; $head = ">>> $ip\n"; # search header while (defined($lbuf = ) && $lbuf ne $head) { } if (defined($lbuf)) { while (defined($lbuf = ) && $lbuf !~ /^>>> /) { chomp($lbuf); push(@v, $lbuf); } } close(LOG); } else { printf(STDERR "%s: $!\n", $hosts_log_file); } return @v; } sub do_whois($) { my($ip) = @_; my(@v); if ($read_log_mode) { @v = whois_from_log($ip); } else { @v = whois($ip); } return @v; } sub parse_net($) { my($range) = @_; my($blo, $bhi, $lo, $hi); ($lo = $range) =~ s/[ \t]*-.*//; ($hi = $range) =~ s/.*-[ \t]*//; $blo = inet_addr($lo); $bhi = inet_addr($hi); return ($blo, $bhi); } sub lookup_inetnum(@) { my(@v) = @_; my($net, $co); my @x = grep(/^inetnum:/, @v); if ($#x >= 0) { my $i = 0; my $found1 = -1; my $found2 = -1; for ($i = 0; $i <= $#v; $i++) { if ($v[$i] =~ /^inetnum:/) { $found1 = $i; } if ($found1 >= 0 && $found2 == -1 && $v[$i] =~ /^country:/) { $found2 = $i; } } if ($found1 >= 0 && $found2 >= 0) { ($net = $v[$found1]) =~ s/^inetnum:[ \t]*//; ($co = $v[$found2]) =~ s/^country:[ \t]*//; } } return ($net, $co); } sub lookup_ARIN(@) { my(@v) = @_; my($net, $co); my @x = grep(/^# ARIN Whois/, @v); if ($#x >= 0) { my $i = 0; my $found = -1; for ($i = 0; $i <= $#v; $i++) { if ($v[$i] =~ /\d+\.\d+\.\d+\.\d+[ \t]*-[ \t]*\d+\.\d+\.\d+\.\d+/) { $found = $i; } } if ($found >= 0) { ($net = $v[$found]) =~ s/^[ \t]+//; $co = nz($v[$found - 1]); } } return ($net, $co); } sub count_mask($$) { my($bip, $x) = @_; my($i) = 0; my($bw) = 32; while ($i < $bw && ($bip & 1) == $x) { $bip = $bip >> 1; $i++; } return $bw - $i; } my @bitmask; sub bitmask_setup() { my $i; my $bm = 0; for ($i = 0; $i < 32; $i++) { $bitmask[$i] = $bm; $bm = ($bm >> 1) | 0x80000000; } $bitmask[32] = 0xffffffff; } sub netaddr($$) { my($bip, $mlen) = @_; return $bip & $bitmask[$mlen]; } sub broaddr($$) { my($bip, $mlen) = @_; return ($bip & $bitmask[$mlen]) | ~$bitmask[$mlen]; } sub find_split_point($$$) { my($blo, $bhi, $l0) = @_; while (broaddr($blo, $l0) > $bhi) { $l0++; } return $l0; } sub range_to_cidr($$$$); sub range_to_cidr($$$$) { my($blo, $bhi, $lvl, $co) = @_; my($l0, $l1, $mlen); my($bnet, $bbro); #printf("%d>>%s-%s\n", $lvl, s_addr_to_dec($blo), s_addr_to_dec($bhi)); $l0 = count_mask($blo, 0); $l1 = count_mask($bhi, 1); #printf("%s/%d\n", tobin8($blo), $l0); #printf("%s/%d\n", tobin8($bhi), $l1); $mlen = ($l0 > $l1 ? $l0 : $l1); $bnet = netaddr($blo, $mlen); $bbro = broaddr($blo, $mlen); #printf("net:%s/%d\n", s_addr_to_dec($bnet), $mlen); #printf("bro:%s/%d\n", s_addr_to_dec($bbro), $mlen); if ($blo != $bnet || $bhi != $bbro) { my $split_mlen = find_split_point($blo, $bhi, $l0); my $split_bbro = broaddr($blo, $split_mlen); #printf("split1:%s/%d\n", s_addr_to_dec($split_bbro), $split_mlen); #printf("split2:%s-%s\n", s_addr_to_dec($split_bbro+1), s_addr_to_dec($bhi)); range_to_cidr($blo, $split_bbro, $lvl + 1, $co); range_to_cidr($split_bbro + 1, $bhi, $lvl + 1, $co); } else { my $anet = s_addr_to_dec($bnet); my $abro = s_addr_to_dec($bbro); printf("%s/%d - %s %s\n", $anet, $mlen, $abro, $co); } } sub net_to_cidr($$) { my($blo, $bhi) = @_; my($i) = 0; my($bm) = 0x80000000; while ($bm != 0 && ($blo & $bm) == ($bhi & $bm)) { $bm = 0x80000000 | ($bm >>= 1); $i++; } $bm = ($bm << 1) & 0xffffffff; my $net = $blo & $bm; my $bro = ($blo & $bm) | ~$bm; if ($net != $blo || $bro != $bhi) { # not CIDR $i = -$i; } return $i; } sub idx_to_cidr($) { my($i) = @_; my($blo, $bhi, $co, $lo, $hi, $mask); $blo = $net_lo[$i]; $bhi = $net_hi[$i]; $co = $net_co[$i]; $lo = s_addr_to_dec($blo); $hi = s_addr_to_dec($bhi); $mask = net_to_cidr($blo, $bhi); return sprintf("%s/%d %s %s", $lo, $mask, $hi, $co); } sub whereis($) { my($ip) = @_; my($bip) = inet_addr($ip); my ($idx) = net_lookup($bip); my $co; if ($idx == -1) { my @v = do_whois($ip); my $net; ($net, $co) = lookup_inetnum(@v); if (!defined($net) || !defined($co)) { $net = getval("^NetRange:", @v); $co = getval("^NetName:", @v); } if (!defined($net) || !defined($co)) { ($net, $co) = lookup_ARIN(@v); } if (defined($net) && defined($co)) { my($blo, $bhi) = parse_net($net); $idx = net_add($blo, $bhi, $co); $tab_changed++; } } else { $co = $net_co[$idx]; } if ($::opt_v) { if ($idx >= 0) { $co = $ip . " " . idx_to_cidr($idx); } } return $co; } sub nain($$) { my($type, $idx) = @_; my($line); read_hosts($hosts_file); while ($quit == 0 && defined($line = <>)) { my($i, @v, $host, $fqdn); $did_whois = 0; chomp($line); @v = split(/[ \t]+/, $line); $host = nz($v[$idx]); $fqdn = whereis($host); if (defined($fqdn)) { $v[$idx] .= " ". $fqdn; } $i = join(" ", @v); printf("%s\n", $i); if ($tab_changed) { write_hosts($hosts_file); } if ($wait_sec && $quit == 0 && $did_whois) { sleep($wait_sec); } } return 0; } sub whereis_to_cidr() { read_hosts($hosts_file); my($i, $blo, $bhi, $co, $lo, $hi, $mask); for ($i = 0; $i < $net_used; $i++) { my($blo, $bhi, $co, $lo, $hi, $mask); $blo = $net_lo[$i]; $bhi = $net_hi[$i]; $co = $net_co[$i]; $lo = s_addr_to_dec($blo); if ($::opt_v) { printf(">%s-%s %s\n", s_addr_to_dec($blo), s_addr_to_dec($bhi), $co); printf("%s\n", idx_to_cidr($i)); } range_to_cidr($blo, $bhi, 0, $co); } return 0; } my $usage = "Version 0.8087\n" ."usage: %s [-pL][-f whois.db][-l whois.log][-w wait] [file]\n" ."\t-p\twhois.db to CIDR\n" ."\t-L\tRead log instead of fork(2)ing whois(1)\n" ."\t-f whois.db\tSpecify db\n" ."\t-l whois.log\tSpecify log\n" ."\t-w n\tWait n sec\n" ; sub main() { my($ex, $type, $idx); $ex = 1; getopts('f:l:Lpw:vV'); nz($::opt_f); nz($::opt_l); nz($::opt_L); nz($::opt_p); nz($::opt_v); nz($::opt_V); nz($::opt_w); if (nz($::opt_V)) { printf(STDERR $usage, $0); } else { if (nz($::opt_f) ne "") { $hosts_file = $::opt_f; } if (nz($::opt_l) ne "") { $hosts_log_file = $::opt_l; } if (nz($::opt_L)) { $read_log_mode = 1; } if (nz($::opt_w)) { $wait_sec = $::opt_w; } $type = 0; # apache $idx = 0; bitmask_setup(); if (nz($::opt_p)) { $ex = whereis_to_cidr(); } else { $ex = nain($type, $idx) < 0; } } return $ex; } exit(main());