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