1#!/usr/local/bin/perl 2# vim:ts=4 3# Check the agent on the remote host. Return 0 if ok, 1 if not. 4# 5 6use strict; 7use Socket; 8 9my($DEBUG) = 0; 10my($rv) = ""; 11my($HOST) = ""; 12my($ALL) = 0; 13 14sub timeout { die "TIMEOUT"; } 15sub pnsquery { 16 my($rv) = ''; 17 my($q); 18 my($iaddr,$paddr,$proto); 19 my($HOST,$PORT); 20 my($svc); 21 my($r); 22 my($fn,$rfd,$wfd,$xfd,$n,$t); 23 24 ($HOST,$svc)= @_; 25 $PORT = '1248'; 26 27 if($svc) { 28 $q = "5&ShowAll&$svc"; 29 } else { 30 $q = "1&&"; 31 } 32 33 $iaddr = inet_aton($HOST); 34 if(!$iaddr) { print "\nUnable to resolve host $HOST\n"; return ""; }; 35 $paddr = sockaddr_in($PORT,$iaddr); 36 if(!$paddr) { print "\nCreating socket failed: $!\n"; return ""; }; 37 $proto = getprotobyname('tcp'); 38 39 socket(SOCK,PF_INET,SOCK_STREAM,$proto) or do { 40 print "\nSocket failed: $!\n"; return ""; 41 }; 42 eval { 43 $SIG{ALRM} = \&timeout; 44 alarm(4); 45 connect(SOCK,$paddr) or do { die "$!"; }; 46 alarm(0); 47 }; 48 if($@=~/TIMEOUT/) { 49 print "No response: may be a firewall, or host down\n"; return ""; } 50 if($@) { return ""; } # connect failed, so no agent 51 52 setsockopt(SOCK,SOL_SOCKET,SO_REUSEADDR,1); 53 54 # Now send the request 55 56 eval { 57 $SIG{ALRM} = \&timeout; 58 print "Sending 'None&$q'\n" if($DEBUG); 59 alarm(2); # 2 second timeout on write should be enough 60 send SOCK, "None&$q", 0; 61 alarm(0); 62 }; 63 if($@) { 64 close SOCK; print "Connected, but unable to send data\n"; return "(agent recieve problem)"; } 65 66 #wait for reply 67 $fn = fileno SOCK; 68 $rfd = $wfd = $xfd = 0; 69 vec($rfd,$fn,1)=1; 70 ($n,$t) = select( $rfd,$wfd,$xfd,4 ); # 4 sec timeout on connect 71 if(!$n or !$rfd) { 72 print "Connected, but no response from pNSAgent.\n"; return "(agent reply problem)"; } 73 74 # read it 75 eval { 76 $SIG{ALRM} = \&timeout; 77 alarm(5); # 5 second timeout on read should be enough 78 recv SOCK, $r, 512,0; 79 alarm(0); 80 }; 81 if($@) { close SOCK; print "Agent detected, but timeout on reading.\n"; return "(agent reply problem)"; } 82 close SOCK; 83 84 $r =~ s/\n/~/g; 85 $r="(null)" if(!$r); 86 close SOCK; 87 return $r; 88} 89######################################################################## 90my($ctx,$ssl) = (0,0); 91my($SSL) = 1; 92my(%STATUS) = ( OK=>0, WARNING=>1, CRITICAL=>2, UNKNOWN=>3 ); 93 94# make crc 95my(@crctable); # array of 256 unsigned longs (4 bytes) 96sub gen_crc_table { 97 my($poly,$j,$i,$crc); 98 99 $poly=0xEDB88320; 100 $i = 0; 101 while($i<256){ 102 $crc=$i; 103 foreach $j ( 8,7,6,5,4,3,2,1 ){ 104 if($crc & 1) { 105 $crc=($crc>>1)^$poly; 106 } else { 107 $crc>>=1; 108 } 109 } 110 $crctable[$i]=$crc & 0xFFFFFFFF; 111 $i += 1; 112 } 113} 114 115sub crc($$) { 116 my($c) = 0; 117 my($v,$n) = @_; 118# my($oc); 119 120 gen_crc_table if(!@crctable); 121 122 # calculate CRC here 123 $c=0xFFFFFFFF; 124 # structure is short/short/long/short/char1024 == 1034 bytes 125# print "CRC[$n]:" if($DEBUG); 126 foreach ( unpack ("c$n", $v) ) { 127# $oc = $c; 128 $c = ( ($c>>8) & 0x00ffffff )^$crctable[($c^$_)&0xFF]; 129# printf "$_ [%X]=>[%x]\n",$oc,$c if($DEBUG); 130 } 131 $c ^= 0xFFFFFFFF; 132# printf "final=>[%X]\n",$c if($DEBUG); 133 return $c; 134} 135 136sub dumppkt($) { 137 my($v) = $_[0]; 138 my($n); 139 140 $n = length($v); 141 print "Packet:\n"; 142 foreach ( unpack ("c$n", $v) ) { print "$_,"; } 143 print "\n"; 144} 145 146# Init SSL 147sub init_ssl { 148 return 0 if(!$SSL); # dont have SSL 149 return 0 if($ctx); # already done it 150 require Net::SSLeay; 151 print "Init_ssl starting\n" if($DEBUG); 152 $Net::SSLeay::trace = 2 if($DEBUG); 153 Net::SSLeay::load_error_strings(); 154 Net::SSLeay::SSLeay_add_ssl_algorithms(); 155 Net::SSLeay::randomize(); 156 $ctx = Net::SSLeay::CTX_new() 157 or return("Failed to create SSL_CTX $!"); 158 Net::SSLeay::CTX_set_options($ctx, &Net::SSLeay::OP_ALL); 159# and return("ssl_ctx_set_options: $!"); 160# Net::SSLeay::CTX_set_cipher_list($ctx, "ADH") 161# and return("ssl ctx set cipher"); 162 return 0; 163} 164 165sub end_ssl { 166 print "end_ssl\n" if($DEBUG); 167 Net::SSLeay::free ($ssl); # Tear down connection 168 Net::SSLeay::CTX_free ($ctx); 169 $ssl = $ctx = 0; 170} 171 172sub end_connection { 173 end_ssl() if($SSL); 174 close SOCK; 175} 176 177# Connect to host, port 178sub do_connect($$) { 179 my($host,$port) = @_; 180 my($ip,$sin,$rv); 181 182 $port = 5666 if(!$port); 183 $port = getservbyname ($port, 'tcp') unless $port =~ /^\d+$/; 184 return "Bad port [$port]" if(!$port); 185 $ip = gethostbyname ($host); 186 return "Bad host [$host]" if(!$ip); 187 $sin = sockaddr_in($port, $ip); 188 return "sockaddr_in: $!" if(!$sin); 189 print "Connecting socket to $host:$port \n" if($DEBUG); 190 socket(SOCK, &AF_INET, &SOCK_STREAM, 0) or do { 191 print "socket failed: $!"; return "socket:$!"; }; 192 eval { 193 $SIG{ALRM} = sub { die("TIMEOUT"); }; 194 alarm(5); 195 $rv = connect(SOCK, $sin); 196 alarm(0); 197 }; 198 if($@) { 199 print "No response: Is host down, or firewall installed?\n"; 200 } 201 return "connect: $!" if(!$rv); 202 binmode SOCK; 203 select(SOCK); $|=1; select (STDOUT); # Eliminate STDIO buffering 204 if($SSL) { 205 $rv = init_ssl(); 206 return "Init SSL: $rv" if($rv); 207 print "Creating SSL object\n" if($DEBUG); 208 $ssl = Net::SSLeay::new($ctx); 209 if(!$ssl) { 210 return("Failed to create SSL $! ".Net::SSLeay::print_errs()); 211 } 212 print "Setting cipher list\n" if($DEBUG); 213 $rv = Net::SSLeay::CTX_set_cipher_list($ctx,'ADH'); 214 Net::SSLeay::set_fd($ssl, fileno(SOCK)); # Must use fileno 215 print "SSL connect...\n" if($DEBUG); 216 eval { 217 $SIG{ALRM} = sub { die("TIMEOUT"); }; 218 alarm(5); 219 $rv = Net::SSLeay::connect($ssl); 220 alarm(0); 221 }; 222 return "SSL Timeout: maybe remote server doesn't use SSL? Try again with -n" if($@); 223 if($rv!=1) { return("ssl connect: ".Net::SSLeay::print_errs()); } 224 } 225 return 0; # OK 226} 227 228sub send_msg($) { 229 my($msg) = $_[0]; 230 my($rv) = 0; 231 232# dumppkt($msg) if($DEBUG); 233 234 if($SSL) { 235 $rv = Net::SSLeay::write($ssl, $msg); # Perl knows how long $msg is 236 return ("ssl write: $rv: ".Net::SSLeay::print_errs()) 237 if($rv) ; 238 } else { 239 $rv = syswrite SOCK,$msg,length($msg); 240 return "syswrite: $!" if(!$rv); 241 } 242 243 return 0; 244} 245 246sub rcv_msg() { 247 my($rv) = undef; 248 my($n); 249 if($SSL) { 250 $rv = Net::SSLeay::read($ssl); # returns undef on failure 251 if(!defined $rv) { 252 $rv = Net::SSLeay::print_errs(); 253 } 254 } else { 255 $n = sysread SOCK,$rv,2048; 256 return "" if(!$n); 257 } 258 return $rv; 259} 260 261sub do_request($@) { 262 my($rv,$stat); 263 my($cmd,@arg) = @_; 264 my($resp,$req,$q,$crc,$rcrc); 265 my($v,$t,$c,$r,$b,$j); 266 267 print "Running $cmd\n" if ($DEBUG); 268 269 $SIG{PIPE} = 'IGNORE'; 270 271 $q = "_NRPE_CHECK"; 272 if($cmd) { $q = join '!', $cmd, @arg; } 273 # note we have to use a junk field to pad to word boundary 274 $req = pack "nnNna1024n",2,1,0,0,$q,0; 275 $crc = crc($req,length($req)); 276 $req = pack "nnNna1024n",2,1,$crc,0,$q,0; 277 printf "Sending [2,1,%X,0,$q,0]\n",$crc if($DEBUG); 278 send_msg($req); 279 CORE::shutdown SOCK, 1; # Half close 280 $rv = rcv_msg(); 281 return( $STATUS{UNKNOWN}, "Error on read: Problem with remote server?" ) 282 if(!defined $rv); 283 return( $STATUS{UNKNOWN}, "No data returned: wrong SSL option, or IP address not authorised?" ) if(!$rv); 284 ($v,$t,$c,$r,$b,$j) = unpack "nnNna1024a2", $rv; 285 printf "Received [$v,$t,%X,$r,$b]\n",$crc if($DEBUG); 286 return( $STATUS{UNKNOWN}, "Bad response version $v: Upgrade your server!" ) if($v != 2); 287 return( $STATUS{UNKNOWN}, "Bad response type $t: This should never happen??" ) if($t != 2); 288 return( $STATUS{UNKNOWN}, "Bad response status $r: Corrupted packet?" ) 289 if($r>3 or $r<0); 290 $resp = pack "nnNna1024a2",$v,$t,0,$r,$b,$j; 291 $crc = crc($resp,length($resp)); 292 return( $STATUS{UNKNOWN}, "Bad response CRC: wrong SSL option?" ) if($crc != $c); 293 294 $b =~ s/\0+$//; 295 return($r,$b); 296} 297 298sub nrpequery($) { 299 my($rv,$status); 300 my($host) = $_[0]; 301 my(@f); 302 303# Connect to server 304 $rv = do_connect( $host, 5666); 305 if($rv) { print "$rv\n"; return ""; } 306 307# Get response 308eval { 309 $SIG{ALRM} = sub { die("TIMEOUT"); }; 310 alarm(5); 311 ($status,$rv) = do_request("version"); 312 alarm(0); 313}; 314if($@) { 315 print "Timeout on NRPE (maybe SSL issue?)\n"; return "(agent problem)"; 316} 317end_connection; 318if($status eq $STATUS{UNKNOWN}) { 319 print "WARNING: NRPE gave unexpected response: [$rv]\n"; 320 return "(unknown)"; 321} 322 323$rv = "(null)" if (!$rv); 324@f = split " ",$rv; 325return $rv; 326return $f[0]; 327} 328 329######################################################################## 330# MAIN 331 332select STDOUT; $|=1; $HOST = ""; 333foreach ( @ARGV ) { 334 /^-d/ and do { $DEBUG = 1; next; }; 335 /^-a/ and do { $ALL = 1; next; }; 336 /^-h/ and do { $HOST = ""; last; }; 337 /^-n/ and do { $SSL = 0; next; }; 338 /^-/ and do { $HOST = ""; last; }; 339 $HOST = $_; last; 340} 341$HOST =~ s/^.*\///; 342$HOST =~ s/\.c(cfg|onf)$//; 343 344if(!$HOST) { 345 print "Usage: checkagent [-n][-d][-a] hostname\n"; 346 print " -n : Use NO ENCRYPTION for some NRPE clients\n"; 347 print " -d : Debug mode\n"; 348 print " -a : Check for ALL possible agents, not just the first.\n"; 349 exit 2; 350} 351 352if(! gethostbyname ($HOST) ) { 353 print "I can't resolve that hostname.\n"; 354 exit 1; 355} 356# Check for NRPE 357print "NRPE agent check....\r"; 358$rv = nrpequery($HOST); 359print "\r \r"; 360if($rv) { 361 print "NRPE : NRPE agent detected! $rv\n"; 362 exit(0) if(!$ALL); 363} else { 364 print "No valid NRPE agent detected.\n"; 365} 366 367# Check for pns 368print "PNS agent check....\r"; 369$rv = pnsquery($HOST); 370print "\r \r"; 371if($rv) { 372 print "PNS : pNSclient agent detected! $rv\n"; 373 print "NagEventLog passive agent check....\r"; 374 $rv = pnsquery($HOST,"NagiosEventLog"); 375 if( $rv =~ /0/ ) { 376 print "EVLOG : Nagios EventLog Agent detected! \n" ; 377 } elsif( $rv =~ /2/ ) { 378 print "EVLOG : Nagios EventLog Agent detected, but inactive.\n" ; 379 } 380 print " \r"; 381 print "BigBrother passive agent check....\r"; 382 $rv = pnsquery($HOST,"BigBrotherClient"); $rv =~ s/&.*//; 383 if( $rv =~ /0/ ) { 384 print "BB : BigBrother legacy agent detected! \n" ; 385 } elsif( $rv =~ /2/ ) { 386 print "BB : BigBrother legacy agent detected, but inactive.\n" ; 387 } 388 print " \r"; 389 390} else { 391 print "No pNSclient agent detected.\n"; 392} 393print "Search complete.\n"; 394 395#print "NOAGENT : No agent detected on remote host.\n"; 396#print "DOWN : No agent detected, no response. Host may be down or behind firewall.\n"; 397exit 1; 398 399 400