#!/usr/bin/perl
#
#  Copyright (c) 2011, 2012 Alexey Grachev <alxgrh@yandex.ru>
#  All rights reserved.
# 
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions
#  are met:
#  1. Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
#  2. Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
# 
#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
#  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
#  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
#  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
#  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
#  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#  SUCH DAMAGE.
# 
#  $Id: asntree.pl,v 1.5 2012/03/06 10:27:34 snar Exp $

use Net::IRR;
use Getopt::Std;
$have_g = eval "require GraphViz";

if ($have_g) { 
	getopts("g:");

	if($opt_g) { 
		require GraphViz;
		$graphfile = $opt_g;
		# layout = dot, neato, twopi, circo, fdp
		$gr = GraphViz->new(overlap => 'scalexy' ,   directed => false,
			layout =>'dot', edge => { dir => 'none',fontsize => 8}, 
			node => {shape=>'box'});
	}
};

if ($#ARGV<0){
	if ($have_g) { 
		print "usage: asntree.pl [-g grapfile.svg] <as-set>\n";
	} else { 
		print "usage: asntree.pl <as-set>\n";
	};
	exit 0;
}

$expanded = {};
$root_as_set = $ARGV[0];

my $host = 'whois.radb.net';

my $i = Net::IRR->connect( host => $host )
	or die "can't connect to $host\n";


$expanded{$root_as_set}=1;
$graphfile ? graph_asset($root_as_set,0,$gr) : print_asset ($root_as_set,0);
$graphfile ? $gr->as_svg("$graphfile") : 0;

$i->disconnect();

sub assorti($a, $b) {
	@aa=split(//, $a);
	@bb=split(//, $b);
	if ($aa[2] eq "-") {
		if ($bb[2] == '-') {
			return $a cmp $b; # as-macro and as-macro, text comparision
		} else {
			return -1; # as-macro is always less than ASn
		};
	} elsif ($bb[2] eq "-") {
		return 1; # as-macro is always less than ASn
	} else {
		return join("", @aa[2 .. $#aa]) <=> join("", @bb[2 .. $#bb]);
	};
};


sub print_asset {
	my $as_set = shift @_;
	my $depth = shift @_;

	print "$as_set ";
	if (my @ases = sort(assorti $i->get_as_set($as_set, 0))) {
		print scalar(@ases) . " members\n";
		foreach (@ases) {
			my $as = $_;
			print "\t" x $depth . "|-";
			if ($as =~ /AS\D/) {
				if (defined($expanded{$as})) { 
					print "$as is already expanded\n";
				} else { 
					$expanded{$as}=1;
					print_asset ($as, $depth+1);
				};
			} else {
				print "$as\n";
			}
		}
 	} else {
		print "none found\n";
	}
}

sub graph_asset {
       my $as_set = shift @_;
       my $depth = shift @_;
       my $g = shift @_;

       if (my @ases = sort(assorti $i->get_as_set($as_set, 0))) {
               foreach (@ases) {
                       my $as = $_;
                       if ($as =~ /AS\D/) {
                               if (!defined($expanded{$as})) {
                                       $expanded{$as}=1;
                                       $g->add_edge ( $as => $as_set);
                                       graph_asset ($as, $depth+1, $g);
                               }
                       }
                       else {
                               $g->add_edge ( $as => $as_set);
                       }
               }
       }
}
