#!/usr/bin/perl -w
#
# Input is taken from standard input and we expect something like:
# Tue Apr 17 13:50:55 2001 ( No Auth ): <none>@159.148.113.125 [unknown] R 0 0Kb 2 0Kb
# Tue Apr 17 13:50:58 2001 (G IP  max): <none>@192.167.9.240 [unknown] R 0 0Kb 2 0Kb
# Tue Apr 17 13:50:58 2001 ( Unknown ): <none>@ppp-40-94.15-151.iol.it [ffff] - 1 0Kb 2 0Kb
# Tue Apr 17 13:51:01 2001 (G IP  max): <none>@192.167.9.240 [unknown] R 0 0Kb 2 0Kb
#
# This script filters No Auth lines and numeric addresses, tries to
# make a whoisdb lookup and generates a list of hostmasks to which
# ip belong. It also arranges networks in their respective blocks.
#
# Output is something similar to:
#
# # PROCESSING SECTION
# Querying db for 159.148.113.0...
# inetnum:        159.148.0.0 - 159.148.255.255
# netname:        LATNET
# country:        LV
# route:  159.148.0.0/16
# 4 matches
# Added LATNET block from 159.148.0.0 to 159.148.255.255 route 159.148.0.0/16
# 159.148.113.0 belongs to LATNET block
# Querying db for 200.61.148.0...
# 0 matches
# ....
# # FILTERING RESUME SECTION
# ============================
# netname:        LATNET
# country:        LV
# inetnum:        159.148.0.0 - 159.148.255.255
# route:          159.148.0.0/16
# networks:       159.148.113.0   23 connection/s
# ============================
# ....
# # I-LINES GENERATION SECTION
# I:159.148.0.0/16::::2 # LV LATNET
# I:195.129.0.0/16::::2 # EU ALET-IT
# I:194.153.208.0/22::::2 # IT NOICOM-NET


# Written by Rocco Lucia (rlucia@iscanet.com, http://elisa.utopianet.net/~rlucia )
# Fri Apr 20 03:11:17 CEST 2001

# This code is distributed under a BSD style license.  You are free
# to do with it as you please. Yes, I bet you won't live without it.

use Socket;

$whoisbin = "/usr/bin/whois";
$ircd_iclass = "2";

# first of all we get numeric IP addresses and No Auth lines
# we also filter out duplicates

%seen = ();
while(<>) {
  if(/(\w+) (\w+) (\d+) (\d+):(\d+):(\d+) (\d+) \( No Auth \)\: \<(\w+)\>\@(\d+).(\d+).(\d+).(\d+) (.*)$/) {
    $elem = $9.".".$10.".".$11.".0";
    push(@ipaddr,$elem) unless $seen{$elem}++;
    $count{$elem}++;
  }
}

print "# PROCESSING SECTION\n";

# let's check each c-class

foreach $addr (@ipaddr) {
  $addrnum = inet_aton($addr);
  $not_exist = 1;

  # a c class may belong to a block
  foreach $network (@networks) {
    if( ($addrnum ge $b_start{$network}) && ($addrnum le $b_stop{$network})) {
      print $addr." belongs to ".$network." block\n";
      push(@{ $cclasses{$network} },$addr);
      $not_exist = 0;
      last;
    }
  }

  # or not belong to any block
  if($not_exist) {
    $#whois = 0;
    $m = 0; $netname = ""; $country = ""; $route= "";
    $netnum_start = ""; $netnum_stop= "";

    # so we query WHOISdb
    print "Querying db for ".$addr."...\n";
    open(WHOIS, $whoisbin." -h whois.ripe.net ".$addr." |");

    # ... parse whois output
    while($line = <WHOIS>) {
      push(@whois,$line);
      if($line =~ m/^netname\:\s+(.*)$/) {
        $netname = $1;
        print "netname:\t".$netname."\n";
        $m++;
      }
      if($line =~ m/^route\:\s+(\d+).(\d+).(\d+).(\d+)\/(\d+)(.*)$/) {
        $route = $1.".".$2.".".$3.".".$4."/".$5;
        print "route:\t".$route."\n";
        $m++;
      }
      if($line =~ m/^country\:\s+(\w+)(.*)$/) {
        $country = $1;
        print "country:\t".$country."\n";
        $m++;
      }
      if($line =~ m/^inetnum\:\s+(\d+).(\d+).(\d+).(\d+) - (\d+).(\d+).(\d+).(\d+)(.*)$/) {
        $netnum_start = $1.".".$2.".".$3.".".$4;
        $netnum_stop  = $5.".".$6.".".$7.".".$8;
        print "inetnum:\t".$netnum_start." - ".$netnum_stop."\n";
        $m++;
      }
    }
    close(WHOIS);
    print $m." matches\n";

    # we also fix route block if db doesn't have it
    if($m eq 3 && $route eq "") {
      print "I didn't find any route info for ".$netname.". Figuring out netblock... ";
      $numbits = 0; $mask = 0;
      $diff = inet_aton($netnum_start) ^ inet_aton($netnum_stop);
      $bits = unpack("B32", $diff);
      $bits =~ s/^0+(?=\d)//;
      print $bits." ";
      $_ = $bits;
      while(/(\d)/gc) {
         $numbits++;
	 if($1 eq "1") {
           $mask = $numbits;
         };
      }
      $route = $netnum_start."/".(32-$mask); 
      print $route."\n";
    }

    # and fill up our tables
    if($m eq 4) {
      $network = $netname;
      push(@networks,$netname);
      $block{$network} = $route;
      $country{$network} = $country;
      $b_start{$network} = inet_aton($netnum_start);
      $b_stop{$network}  = inet_aton($netnum_stop);
      print "Added ".$network." block from ".$netnum_start." to ".$netnum_stop." route ".$route."\n";
      print $addr." belongs to ".$network." block\n";
      push(@{ $cclasses{$network} },$addr);
    }
  }
}

# now we print netblocks with respective networks

print "# FILTERING RESUME SECTION\n";
foreach $network (@networks) {
  print "============================\n";
  print "netname:\t".$network."\n";
  print "country:\t".$country{$network}."\n";
  print "inetnum:\t".inet_ntoa($b_start{$network})." - ".inet_ntoa($b_stop{$network})."\n";
  print "route:\t\t".$block{$network}."\n";
  foreach $cclass (@{ $cclasses{$network}}) {
    print "networks:\t".$cclass."\t".$count{$cclass}." connection/s \n";
  }
}

# and I-lines as we will add them in ircd.conf

print "# I-LINES GENERATION SECTION\n"; 
foreach $network (@networks) {
  print "I:".$block{$network}."::::".$ircd_iclass." # ".$country{$network}." ".$network."\n"
};

