#!/usr/local/bin/perl

use strict;
use IO::Socket::INET;
use Getopt::Std;

my %opt;
getopts('n:tu', \%opt);

my @proto;
push @proto, 'TCP' if($opt{t});
push @proto, 'UDP' if($opt{u});

warn "no -t and/or -u: performing ping test only\n" unless($opt{t} || $opt{u});

my %size = (32 => 1, 31 => 2, 30 => 4, 29 => 8, 28 => 16,
            27 => 32, 26 => 64, 25 => 128, 24 => 256,
           );

my %ports = (TCP => [qw/21 22 23 25 43 53 70 80 110 119 123 143 443 465 993 995 2049 4321 5060/],
				 UDP => [qw/53 69 123 161 1645 2049 5060/],
            );

my %name = (21 => 'FTP', 22 => 'SSH', 23 => 'Telnet', 25 => 'SMTP',
            43 => 'Whois', 53 => 'DNS', 69 => 'TFTP', 70 => 'Gopher',
            80 => 'HTTP', 110 => 'POP', 119 => 'NNTP', 123 => 'NTP',
            143 => 'IMAP', 161 => 'SNMP', 443 => 'HTTPS', 465 => 'SMTPS',
            993 => 'IMAPS', 995 => 'POPS', 1645 => 'RADIUS', 2049 => 'NFS',
            4321 => 'Rwhois', 5060 => 'SIP',
            ping => 'Ping',
           );

my ($netblock,$cidr) = split /\//, $opt{n};

if($netblock =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3})\.(\d{1,3})$/){
	my $net = $1;
	my $oct4 = $2;
	$cidr = 24 unless($cidr);
	die "cannot scan network that big\n" unless($cidr >= 24);
	
	if($oct4){
		die "insane network: $opt{n}\n" if($oct4 % $size{$cidr});
	}

	open(F, ">config.new") || die "cannot write to config.new: $!\n";

	my ($user,$pass,$input);
	until($user =~ /^[a-z0-9_-]{2,20}$/){
		print "enter administrative username to log into Argus web page with: ";
		chop($user=<STDIN>);
	}

	system "stty -echo";
	until($pass =~ /^\S{3,20}$/){
		print "invalid password  - 3-20 characters\n" if($pass);
		print "enter password for $user: ";
		chop($pass=<STDIN>);
	}
	system "stty echo";
	print "\n";
	
	srand(time() ^($$ + ($$ << 15)));
	my @a = grep {/[a-zA-Z0-9]/} map {chr($_)} (0..256);
	my $salt = $a[ rand( @a ) ] . $a[ rand(@a) ];
	my $crypt = crypt($pass, $salt);
	
	open (U, ">users.new") || die "cannot write to users.new: $!\n";
	print U "$user\t$crypt\tTop\troot\n";
	close U;

	my $n=1;
	my @email;
	until(($n > 1) && (! $input)){
		print "enter email address #${n} to send notifications";
		print " (enter to finish)" if($n > 1);
		print ": ";
		chop($input=<STDIN>);
		if($input =~ /^([a-z0-9_\.\+\-\=\?\^\#]){1,64}\@(([a-z0-9\-]){1,251}\.){1,252}[a-z0-9]{2,4}$/i){
			push @email, $input;
			$n++;
		}elsif(($input eq '') && ($n > 1)){
			last;
		}else{
			print "invalid email address: $input\n";
		}
	}

	my $string;
	foreach my $email (@email){
		$string .= "mail:${email} ";
	}
	chop($string);
	print F "notify:	$string\n";

	$input = undef;
	until(($input =~ /^\d+$/) && ($input > 9 && $input < 241)){
		print "invalid frequency: choose above 9 and below 241.\n" if($input);
		print "how often should Argus check things (seconds) [60]: ";
		chop($input=<STDIN>);
		$input = 60 unless($input);
	}
	print F "frequency:	$input\n";
	
	my $gw = '0.0.0.0';
	if(open(N, "netstat -nr 2>&1 |")){
		while(<N>){
			if(/^(?:default|0\.0\.0\.0)\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s/){
				$gw = $1;
				last;
			}
		}
		close N;
	}

	my $gateway;
	until($gateway =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/){
		print "enter your default gateway [$gw]: ";
		chop($gateway=<STDIN>);
		$gateway = $gw if($gateway eq '');
	}

	print F "Resolv {\n",
	        "\tfrequency:\t60\n",
	        "\ttimeout:\t20\n",
	        "}\n\n";

	my %data;
	if($cidr == 32){
		$data{$netblock} = scan($netblock);
	}else{
		$oct4++; # skip netblock
		my $i=1; # skip broadcast
		while($i < $size{$cidr}){
			$data{"$net.$oct4"} = scan("$net.$oct4");
			$i++;
			$oct4++;
		}
	}

	print F "Group \"Machines\" {\n";
	foreach my $key (keys %data){
		if(@{$data{$key}}){
			my $hostname;
			eval {
				local $SIG{ALRM} = sub { die "timed out on $key\n"; };
				my $packed = pack('C4', split /\./, $key);
				alarm(1);
				$hostname = gethostbyaddr($packed,2);
				alarm(0);
			};
			alarm(0);
			$hostname = $key unless($hostname);
			print F "\tHost \"$hostname\" {\n";
			foreach my $value (@{$data{$key}}){
				if(my $x = $name{$value->[0]}){
					print F "\t\tService ";
					print F "\t$value->[1]/" if($value->[1]);
					print F "\t$x\n";
				}else{
					warn "skipping $value->[0]/$value->[1]\n";
				}
	
			}
			print F "\t}\n";
		}
	}
	print F "}\n",
	        "Group \"Other\" {\n",
	        "\tGroup \"My Stats\" {\n",
	        "\t\tgraph:\tyes\n",
	        "\t\tService Self/Notifs\n",
	        "\t\tService Self/idle {\n",
	        "\t\t\ttitle:\tPercent Idle\n",
	        "\t\t\tcalc:\tave-rate\n",
	        "\t\t\tscale:\t0.01\n",
	        "\t\t\tminvalue:\t20\n",
	        "\t\t\tmessagedn:\ttime to buy a faster server\n",
	        "\t\t}\n",
	        "\t\tService Self/tested {\n",
	        "\t\t\ttitle\tMonitor Rate\n",
	        "\t\t\tylabel:\ttests per second\n",
	        "\t\t\tcalc:\tave-rate\n",
	        "\t\t}\n",
	        "\t\tService Prog {\n",
	        "\t\t\t# your ps may be different\n",
	        "\t\t\tcommand:\tps -p \$ARGUS_PID -o vsz | tail -1\n",
	        "\t\t\tuname:\tVSZ\n",
	        "\t\t\ttitle:\tMemory Use\n",
	        "\t\t\tylabel:\tkBytes\n",
	        "\t\t}\n",
	        "\t}\n",
	        "}\n",
		     "Group \"Upstream Connections\" {\n",
		     "\tHost \"$gateway\" {\n",
		     "\t\tService Ping\n",
		     "\t}\n",
		     "}\n";
	close F;

	print 'done.  rename "users.new" to "users" and "config.new" to "config".', "\n";

}else{
	die "invalid network: $opt{n}\n";
}

sub scan {
	my $host = shift;
	my @services;
	my $res = `ping $host 1`;
	if($res =~ /is alive/){
		push @services, ['ping'];
	}
	foreach my $proto (@proto){
		foreach my $port (@{$ports{$proto}}){
			warn "$host -> $port/$proto\n";
			my $sock = IO::Socket::INET->new(PeerAddr => $host,
			                                 PeerPort => $port,
			                                 Proto => $proto,
			                                 Timeout => 1);
			if($sock){
				push @services, [$port, $proto];
				close $sock;
			}
		}
	}
	return(\@services);
}
