#!/usr/bin/perl -w 
#
# Check things done with nrpep
#

# Required libraries.  Getopt for the command line parsing, CBC/DES for the
# encryption/decryption routines, Socket for the socket parts, and strict
# for the niceness. :)
use Getopt::Std;
use Crypt::TripleDES;
use Socket;
use strict;
use vars qw($handle $line $timeout $key $x $answer %opts $version $port $iaddr $paddr $proto $response $commandoutput $errorcode $defaulttimeout $defaultport);
$ENV{PATH} = "/bin";
$ENV{BASH_ENV} = "";

# Tasty variable definitions
$version = "0.2";
$timeout = 30;
$SIG{'ALRM'} = 'handler';

# Grab the command line args, shove them into %opts
getopts('h:c:p:t:f:d', \%opts);
# Check the command line args for required values; set default if optional
# not defined
&Check_Command;
# Connect the socket to handle SOCK
&Connect;
# Send the message to nrpep, and transmit the output and error code to
# Netsaint. 
&Send;

#
# The part that checks for the needed command line args
sub Check_Command {
# You must give me a host name and a command to check; otherwise you are
# a schmuck and need the usage info
	unless ($opts{'h'} && $opts{'c'}) {
		die <<USAGE;
Minimum arguments not supplied!

Check NRPEP Command - Version $version
Copyright (c) 1999 Adam Jacob

Usage: check_nrpep -h <host> -c <command> [-p <port>] [-t <timeout>] \
       [-d <debug>] [-f <config file>]

-h <host>    = The host running NRPEP
-c <command> = The command entry to execute on the remote host
-p <port>    = The port NRPEP is on (8086 by default)
-t <timeout> = How long to wait before timing the query out. Default is 15 
               seconds.
-d <debug>   = Turn on line for line printing of the transactions.
-f <config>  = The path to the configuration file containing the shared secret
               Default is /usr/local/netsaint/etc/check_nrpep.cfg
USAGE
	}
# Did you tell me where to look for my config?  If not, do check_nrpep.cfg
	if ($opts{'f'}) {
		&Get_Configs($opts{'f'});
	} else {
		&Get_Configs("/usr/local/etc/netsaint/check_nrpep.cfg");
	}
# Longer timeout?  If it's given on the command line use it, otherwise
# read from the config file
	if ($opts{'t'}) {
		$timeout = $opts{'t'};
	} else {
		$timeout = $defaulttimeout;
	}
# Different port?  It it is, grab it from the config file 
	if ($opts{'p'}) {
		$port = $opts{'p'};
	} else {
		$port = $defaultport;
	}
}

# Connect the socket to nrpep!
sub Connect {
# If it's got non-numeric parts, it's a named port definiton from the
# /etc/services file and needs to be looked up 
	if ($port =~ /\D/) {
		$port = getservbyname($port, 'tcp');
	}
	die "Cannot get port\n" unless $port;
# Grab the host address
	$iaddr = inet_aton($opts{'h'}) or die "No Host: $opts{'h'}";
# Build the port/ip data structure
	$paddr = sockaddr_in($port, $iaddr);
# Grab the tcp proto structure
	$proto = getprotobyname('tcp');
# Build the socket on the handle SOCK 
	socket(SOCK, PF_INET, SOCK_STREAM, $proto) or die "Socket failed: $!";
# Connect the socket
	connect(SOCK, $paddr) or die "Connect failed: $!";
# Set both SOCK and STDOUT to not buffer.
	select(SOCK);
	$| = 1;
	select(STDOUT);
	$| = 1;
}

# Send the info to nrpep; return the proper response info and such.
sub Send {
	my $cipher;

# Create the cipher with the key we got from the config file	
	$cipher = new Crypt::TripleDES;
# Set the timeout
	alarm($timeout);
# Grab the output of nrpep
	for ($x = 0; $answer = <SOCK>; $x++) {
		if ($opts{'d'}) {
			print $answer;
		}
# If it's the first line we get, it should be a banner containing nrpep;
# otherwise, we are dealing with something other than nrpep and should
# barf!
		if ($x == 0) {
			unless ($answer =~ /nrpep/) {
				die("This is not a nrpep server! (Banner was wrong)\r\n");
			} 
# If we are talking to nrpep, encrypt the command we want to send and
# transmit it to nrpep
 			$opts{'c'} = unpack("H*", $cipher->encrypt3($opts{'c'}, $key));
			print(SOCK $opts{'c'} . "\r\n");
# The next line of response is the output of the command we just 
# sent.  Decrypt it and tell nrpep to end the session
		} elsif ($x == 1) {
			$response = $cipher->decrypt3(pack("H*", $answer), $key);
			my $exit = unpack("H*", $cipher->encrypt3("exit", $key));
			print(SOCK $exit . "\r\n");
		}
	}
# The error code actuall looks like this:
#
# OP: whatever the pluguin says
# RC: return code of the plugin we executed
#
# So we split it on the new line, giving us the output of the command 
# and the errorcode.  We don't care about the OP and the RC part, so
# please kill them.  Then print out the commandoutput, and exit with
# the proper errorcode so netsaint knows what to do.
	($commandoutput, $errorcode) = split(/\n/, $response);
	$commandoutput =~ s/^OP: //;
	$errorcode =~ s/^RC: //;
	$errorcode = $errorcode >> 8;
	print "$commandoutput\n";
	exit $errorcode;
}

# What to do if we time out
sub handler {
	die("I have timed out captain!\r\n");
}

# Grab the config file
sub Get_Configs {
	my $opt_c = $_[0];
	my $line;
	my $garbage;

# Open the config file...
	open(FILE, "$opt_c") || die "Cannot open file at $opt_c";
	foreach $line (<FILE>) {
		chomp($line);
# Ignore comments
		unless ($line =~ /^#/) {
# If it's the secret, we want it!
			if ($line =~ /secret=/) {
				($garbage, $key) = split(/\=/, $line, 2);
			} elsif ($line =~ /timeout=/) {
				($garbage, $defaulttimeout) = split(/\=/, $line, 2);
			} elsif ($line =~ /port=/) {
				($garbage, $defaultport) = split(/\=/, $line, 2);
			}
		}
	}
	close(FILE);
}
