#!/usr/bin/perl -w
use strict;

use CGI qw/:standard/;
use CGI::FastTemplate;
use Net::DNS;
use Net::Netmask;

use Unix::Syslog qw(:macros);
use Unix::Syslog qw(:subs);

use Juniper;
use Cisco;
use Extreme;

use config;

my %routers = config::routers;
my $badrtdir = config::forbidden_dir;
my $tmpdir = config::tmp_dir;
my $lockfile = config::lockfile;
my $mintime = config::min_time;
my $URL = config::url;

my $tpl = new CGI::FastTemplate("templates");
$tpl->define(
	header => "header.tpl",
	footer => "footer.tpl",
	input => "input.tpl",
	outputhdr => "outputhdr.tpl",
	outputftr => "outputftr.tpl",
	button => "button.tpl",
	selected => "selected.tpl",
	notenough => "notenough.tpl",
	router => "router.tpl",
	service => "service.tpl",
	lookup => "lookup.tpl",
	wrongip => "wrongip.tpl",
	toofast => "toofast.tpl",
	mask => "mask.tpl"

);

my @services = (
	[ "ping", "ping", 1 ],
	[ "traceroute", "traceroute" ],
	[ "route", "show ip route" ],
	[ "bgp", "show ip bgp" ],
	[ "bgpsum", "show ip bgp summary" ]
);


my %servnames;
foreach my $name (@services) {
	$servnames{@$name[0]} = @$name[1];
}

print header(-type=>"text/html; charset=iso8859-2");

$tpl->assign("URL", $URL);

if (!param()) {
	$tpl->assign("TITLE", "Looking glass");
	foreach my $rname (sort (keys %routers)) {
		$tpl->assign("NAME", "routername");
		$tpl->assign("LABEL", $routers{$rname}{label});
		$tpl->assign("VALUE", $rname);
		if ($routers{$rname}{preferred}) {
			$tpl->parse("SELECTED" => "selected");
		} else {
			$tpl->assign("SELECTED", "");
		}
		$tpl->parse("ROUTERS" => ".button");
	}

	for my $no (0 .. $#services) {
		$tpl->assign("NAME", "service");
		$tpl->assign("LABEL", $services[$no][1]);
		$tpl->assign("VALUE", $services[$no][0]);
		if (defined $services[$no][2]) {
			$tpl->parse("SELECTED" => "selected");
		} else {
			$tpl->assign("SELECTED", "");
		}
		$tpl->parse("SERVICES" => ".button");
	}

	$tpl->parse("CONTENT" => ["header", ".input", ".footer"]);
	$tpl->print();
	exit;
} elsif (!param("service") || !param("routername") || (param("service") ne "bgpsum" && !param("address"))) {
	$tpl->assign("TITLE", "Looking Glass - ERROR");
	$tpl->parse("CONTENT" => ["header", ".notenough", ".footer"]);
	$tpl->print();
	exit;
} elsif (!exists $routers{param("routername")}) {
	$tpl->assign("TITLE", "Looking Glass - ERROR");
	$tpl->parse("CONTENT" => ["header", ".router", ".footer"]);
	$tpl->print();
	exit;
} elsif (!exists $servnames{param("service")}) {
	$tpl->assign("TITLE", "Looking Glass - ERROR");
	$tpl->parse("CONTENT" => ["header", ".service", ".footer"]);
	$tpl->print();
	exit;
}

if (-f "$tmpdir/$lockfile") {
        my $now = time();
        if ($now - (stat "$tmpdir/$lockfile")[9] < $mintime) {
		$tpl->assign("TITLE", "Looking Glass - ERROR");
		$tpl->assign("MINTIME", $mintime);
		$tpl->parse("CONTENT" => ["header", ".toofast", ".footer"]);
		$tpl->print();
		exit;
        }
        utime $now, $now, "$tmpdir/$lockfile";
} else {
        system "touch $tmpdir/$lockfile";
}

my $rtype = $routers{param("routername")}{"type"};

my $ipaddr = "";
if (param("service") ne "bgpsum") {
	if (param("address") !~ /^(\d{1,3}\.){3}\d{1,3}(\/[0-9]{1,2})?$/) {
		my $resolv = new Net::DNS::Resolver;
		my $query = $resolv->search(param('address'));
		if ($query) {
			foreach my $rr ($query->answer) {
				next unless $rr->type eq "A";
				$ipaddr = ($rr->address);
				last;
			}
		}
		if ($ipaddr eq "") {
			$tpl->assign("TITLE", "Looking Glass - ERROR");
			$tpl->assign("PARAM", param("address"));
			$tpl->parse("CONTENT" => ["header", ".lookup", ".footer"]);
			$tpl->print();
			exit;
		}
	} else {
		$ipaddr = param("address");
		if ((param("service") ne "bgp")&&($ipaddr =~ /\//)) {
			$tpl->assign("TITLE", "Looking Glass - ERROR");
			$tpl->assign("PARAM", $ipaddr);
			$tpl->parse("CONTENT" => ["header", ".mask", ".footer"]);
			$tpl->print();
			exit;
		}
	}

	my $ipn = new2 Net::Netmask ($ipaddr, "255.255.255.255") or die;
	
	(new Net::Netmask ("127.0.0.0/8"))->storeNetblock;
	(new Net::Netmask ("10.0.0.0/8"))->storeNetblock;
	(new Net::Netmask ("172.16.0.0", "255.240.0.0"))->storeNetblock;
	(new Net::Netmask ("192.168.0.0/16"))->storeNetblock;
	(new Net::Netmask ("0.0.0.0/8"))->storeNetblock;
	(new Net::Netmask ("255.255.255.255/24"))->storeNetblock;
	(new Net::Netmask ("224.0.0.0", "224.0.0.0"))->storeNetblock;

	if ((param("service") eq "ping")||(param("service") eq "traceroute")) {
		open(INPUT, "$badrtdir/".param("routername")) or die;
		while (<INPUT>) {
			(new Net::Netmask ($_))->storeNetblock;
		}
		close(INPUT);
	}

	if (findNetblock($ipaddr)) {
		$tpl->assign("TITLE", "Looking Glass - ERROR");
		$tpl->assign("PARAM", $ipaddr);
		$tpl->parse("CONTENT" => ["header", ".wrongip", ".footer"]);
		$tpl->print();
		exit;
	}
}

$| = 1;
$tpl->assign("TITLE", "Looking Glass results");
$tpl->assign("QUERY", $servnames{param("service")});
$tpl->assign("PARAM", param("address"));
$tpl->assign("IPADDR", $ipaddr);
$tpl->parse("HEADER", ["header", ".outputhdr"]);
$tpl->print();

openlog "lg.pl", 0, LOG_DAEMON;
syslog LOG_INFO, "Query from ".$ENV{"REMOTE_ADDR"}.": router: ".param("routername").", service: ".param("service").", ipaddr: ".$ipaddr;
closelog;

my $session = $rtype->login($routers{param("routername")});
if (param("service") eq "ping") {
	$rtype->ping($session, $ipaddr);
} elsif (param("service") eq "traceroute") {
	$rtype->traceroute($session, $ipaddr);
} elsif (param("service") eq "bgpsum") {
	$rtype->bgp_summary($session);
} elsif (param("service") eq "bgp") {
	$rtype->bgp($session, $ipaddr);
} elsif (param("service") eq "route") {
	$rtype->route($session, $ipaddr);
}

$tpl->parse("FOOTER", ["outputftr", ".footer"]);
$tpl->print();
