#! /usr/bin/perl -w

# (c)2005 Sebastian Wiesinger
#  based on the check_ntp plugin
# Read the GNU copyright stuff for all the legalese

require 5.004;
use POSIX;
use strict;
use Getopt::Long;
use vars qw($opt_V $opt_h $opt_H $opt_p $opt_t $opt_w $opt_c $PROGNAME);
use lib "/opt/nagios/libexec" ; 
use utils qw($TIMEOUT %ERRORS);
use IO::Socket::INET;
use Data::Dumper;

$PROGNAME="check_tor";
my $version = "0.1";

sub print_help ();
sub print_usage ();

$ENV{'PATH'}='';
$ENV{'BASH_ENV'}='';
$ENV{'ENV'}='';

# defaults in sec
my $DEFAULT_TIMEOUT_WARN =  5;  # seconds
my $DEFAULT_TIMEOUT_CRIT = 10;  # seconds
my $DEFAULT_PORT = 9051;

Getopt::Long::Configure('bundling');
GetOptions
	("V"   => \$opt_V, "version"    => \$opt_V,
	 "h"   => \$opt_h, "help"       => \$opt_h,
	 "w=f" => \$opt_w, "warning=f"  => \$opt_w,   # offset|adjust warning if above this number
	 "c=f" => \$opt_c, "critical=f" => \$opt_c,   # offset|adjust critical if above this number
	 "t=s" => \$opt_t, "timeout=i"  => \$opt_t,
	 "p=s" => \$opt_H, "port=s"     => \$opt_p,
	 "H=s" => \$opt_H, "hostname=s" => \$opt_H);

if ($opt_V) {
	print "Version: $version\n";
	exit $ERRORS{'OK'};
}

if ($opt_h) {
	print_help();
	exit $ERRORS{'OK'};
}

$opt_H = shift unless ($opt_H);
my $host = $1 if ($opt_H && $opt_H =~ m/^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z][-a-zA-Z0-9]+(\.[a-zA-Z][-a-zA-Z0-9]+)*)$/);
unless ($host) {
	print "No target host specified\n";
	print_usage();
	exit $ERRORS{'UNKNOWN'};
}

my ($timeout, $warn, $crit);

$timeout = $TIMEOUT;
($opt_t) && ($opt_t =~ /^([0-9]+)$/) && ($timeout = $1);

$warn = $DEFAULT_TIMEOUT_WARN;
($opt_w) && ($opt_w =~ /^([0-9.]+)$/) && ($warn = $1);

$crit = $DEFAULT_TIMEOUT_CRIT;
($opt_c) && ($opt_c =~ /^([0-9.]+)$/) && ($crit = $1);

my $port = $DEFAULT_PORT;
($opt_p) && ($opt_p =~ /^([0-9]+)$/) && ($port = $1);

if ($crit < $warn ) {
	print "Critical timeout should be larger than warning timeout\n";
	print_usage();
	exit $ERRORS{"UNKNOWN"};
}


my $state = $ERRORS{'UNKNOWN'};

# Just in case of problems, let's not hang Nagios
$SIG{'ALRM'} = sub {
	print ("ERROR: No response from tor server (alarm)\n");
	exit $ERRORS{"UNKNOWN"};
};
alarm($timeout);

my $sock;

my $starttime = time;

$sock = IO::Socket::INET->new(
        PeerAddr => $host,
        PeerPort => $port,
        Proto    => 'tcp');

if (!$sock) {
	print "ERROR: Could not open socket to tor server\n";
	exit $ERRORS{"CRITICAL"};
}

# Authentication
my $string = "";
my $message = pack('n', length($string)).pack('n', 7).$string;
my $recvbuff;

if(!$sock -> print($message)) {
	print "ERROR: Could not send authentication to tor server.\n";
	exit $ERRORS{"CRITICAL"};
}
if($sock->recv($recvbuff, 4) ne "") {
	print "ERROR: Authentication failed(Failed to read from socket).\n";
	exit $ERRORS{"CRITICAL"};
}
unless($recvbuff eq "\x00\x00\x00\x01") {
	print "ERROR: Authentication failed.\n";
	exit $ERRORS{"UNKNOWN"};
}

$recvbuff = "";
$string = "version\x00";
$message = pack('n', length($string)).pack('n', 11).$string;
if(!$sock -> print($message)) {
	print "ERROR: Could not send version request to tor server.\n";
	exit $ERRORS{"CRITICAL"};
}
if($sock->recv($recvbuff, 1024) ne "") {
	print "ERROR: Failed to receive version reply from tor server.\n";
	exit $ERRORS{"CRITICAL"};
}
unless($recvbuff =~ /^....version\x00([^\x00]+)\x00$/) {
	print "ERROR: Failed to get version information.\n";
	exit $ERRORS{"CRITICAL"};
}

my $torversion = $1;
my $diff = time - $starttime;
print "Tor Version $torversion, response time $diff seconds\n";
exit $ERRORS{"CRITICAL"} if($diff >= $crit);
exit $ERRORS{"WARNING"} if($diff >= $warn);
exit $ERRORS{"OK"};


####
#### subs

sub print_usage () {
	print "Usage: $PROGNAME -H <host> [-p <port>] [-w <warn>] [-c <crit>]\n";
}

sub print_help () {
	print "Version: $version\n";
	print "Copyright (c) 2005 Sebastian Wiesinger\n";
	print "\n";
	print_usage();
	print "
Checks if the tor server is reachable
-p ( --port)
     Port to connect to.\n	Defaults to $DEFAULT_PORT.
-w ( --warning)
     Timeout in seconds at which a warning message will be generated.\n	Defaults to $DEFAULT_TIMEOUT_WARN.
-c (--critical) 
     Timeout in seconds at which a critical message will be generated.\n	Defaults to $DEFAULT_TIMEOUT_CRIT.
";
}
