Sophie

Sophie

distrib > * > 2008.0 > x86_64 > by-pkgid > e9a5341b937ac662219c4e76dddc09dd > files > 1

nagios-check_zone_auth-1.1-2mdv2008.0.src.rpm

#!/usr/bin/perl

# (C) Duane Wessels, wessels@measurement-factory.com
#
# $Id: check_zone_auth,v 1.1 2007/10/25 20:04:33 wessels Exp $
#
# check_zone_auth
#
# nagios plugin to check that all authoritative nameservers for a zone
# have the same NS RRset and the same serial number.

# usage
#
# define command {
#   command_name    check-zone-auth
#   command_line    /usr/local/libexec/nagios-local/check_zone_auth -Z $HOSTADDRESS$
# }
# 
# define service {
#   name                    dns-auth-service
#   check_command           check-zone-auth
#   ...
# }
# 
# define host {
#   use dns-zone
#   host_name zone.example.com
#   alias ZONE example.com
# }
# 
# define service {
#   use dns-auth-service
#   host_name zone.example.com
# }

use warnings;
use strict;

use Getopt::Std;
use Net::DNS::Resolver;
use Net::DNS::Resolver::Recurse;
use Time::HiRes qw ( gettimeofday tv_interval);

my %opts;
getopts('Z:d', \%opts);
usage() unless $opts{Z};
usage() if $opts{h};
my $zone = $opts{Z};
$zone =~ s/^zone\.//;

my $data;

my $start;
my $stop;

$start = [gettimeofday()];
do_queries();
$stop = [gettimeofday()];
do_analyze();


sub do_queries {
	my $recres = Net::DNS::Resolver::Recurse->new;
	$recres->recursion_callback(sub {
		my $p = shift;
		print STDERR $p->string if $opts{d};
		add_nslist_to_data($p);
	});
	my $seed = $recres->query_dorecursion($zone, 'SOA');
	critical("No response to seed query") unless $seed;
	$recres = undef;

	critical($seed->header->rcode . " from " . $seed->answerfrom)
		unless ($seed->header->rcode eq 'NOERROR');
	print STDERR $seed->string if $opts{d};
	add_nslist_to_data($seed);
	
	my $n;
	do {
		$n = 0;
		foreach my $ns (keys %$data) {
			next if $data->{$ns}->{done};

			my $pkt = send_query($zone, 'SOA', $ns);
			add_nslist_to_data($pkt);
			$data->{$ns}->{queries}->{SOA} = $pkt;

			if ($pkt && $pkt->header->nscount == 0) {
				my $ns_pkt = send_query($zone, 'NS', $ns);
				add_nslist_to_data($ns_pkt);
				$data->{$ns}->{queries}->{NS} = $ns_pkt;
			}

			print STDERR "done with $ns\n" if $opts{d};
			$data->{$ns}->{done} = 1;
			$n++;
		}
	} while ($n);
}

sub do_analyze {
	my $maxserial = 0;
	my $nscount = 0;
	foreach my $ns (keys %$data) {
		my $soa_pkt = $data->{$ns}->{queries}->{SOA};
		critical("No response from $ns") unless $soa_pkt;
		print STDERR $soa_pkt->string if $opts{d};
		critical($soa_pkt->header->rcode . " from $ns")
			unless ($soa_pkt->header->rcode eq 'NOERROR');
		critical("$ns is lame") unless $soa_pkt->header->ancount;
		my $serial = soa_serial($soa_pkt);
		$maxserial = $serial if ($serial > $maxserial);
		$nscount++;
	}
	warning("No nameservers found.  Is '$zone' a zone?") if ($nscount < 1);
	warning("Only one auth NS") if ($nscount < 2);
	foreach my $ns (keys %$data) {
		my $soa_pkt = $data->{$ns}->{queries}->{SOA};
		my $ns_pkt = $data->{$ns}->{queries}->{NS};

		# see if this nameserver lists all nameservers
		#
		my %all_ns;
		foreach my $ns (keys %$data) { $all_ns{$ns} = 1; }
		foreach my $ns (get_nslist($soa_pkt)) { delete $all_ns{$ns}; }
		foreach my $ns (get_nslist($ns_pkt)) { delete $all_ns{$ns}; }
		if (keys %all_ns) {
			warning("$ns does not include " .
				join(',', keys %all_ns) .
				" in NS RRset");
		}

		warning("$ns claims is it not authoritative") unless $soa_pkt->header->aa;

		my $serial = soa_serial($soa_pkt);
		warning("$ns serial ($serial) is less than the maximum ($maxserial)") if ($serial < $maxserial);
	}
	success("$nscount nameservers, serial $maxserial");
}

sub add_nslist_to_data {
	my $pkt = shift;
	foreach my $ns (get_nslist($pkt)) {
		print STDERR "adding NS $ns\n" if $opts{d};
		$data->{$ns}->{done} |= 0;
	}
}

sub soa_serial {
	my $pkt = shift;
	foreach my $rr ($pkt->answer) {
		next unless ($rr->type eq 'SOA');
		next unless ($rr->name eq $zone);
		return $rr->serial;
	}
	return 0;
}

sub success {
	output('OK', shift);
	exit(0);
}

sub warning {
	output('WARNING', shift);
	exit(1);
}

sub critical {
	output('CRITICAL', shift);
	exit(2);
}

sub output {
	my $state = shift;
	my $msg = shift;
	$stop = [gettimeofday()] unless $stop;
	my $latency = tv_interval($start, $stop);
	printf "ZONE %s: %s; (%.2fs) |time=%.6fs;;;0.000000\n",
		$state,
		$msg,
		$latency,
		$latency;
}

sub usage {
	print STDERR "usage: $0 -Z zone\n";
	exit 3;
}

sub send_query {
	my $qname = shift;
	my $qtype = shift;
	my $server = shift;
	my $res = Net::DNS::Resolver->new;
	$res->nameserver($server) if $server;
	return $res->send($qname, $qtype);
}

sub get_nslist {
	my $pkt = shift;
	return () unless $pkt;
	return () unless $pkt->authority;
	my @nslist;
	foreach my $rr ($pkt->authority) {
		next unless ($rr->type eq 'NS');
		next unless ($rr->name eq $zone);
		push(@nslist, lc($rr->nsdname));
	}
	return @nslist;
}