diff -r a220ea51b60b -r b2a26d05b063 plugins/check_dns-serial --- a/plugins/check_dns-serial Tue Dec 30 23:27:37 2014 +0100 +++ b/plugins/check_dns-serial Tue Jan 06 13:35:59 2015 +0100 @@ -1,4 +1,5 @@ #! /usr/bin/perl +# source: https://ssl.schlittermann.de/hg/ius/nagios/nagios-plugin-dns-serial # © 2014 Heiko Schlittermann =head1 NAME @@ -47,6 +48,16 @@ use if $ENV{DEBUG} => 'Smart::Comments'; sub uniq { my %h; @h{@_} = (); return keys %h; } +my @extns = qw(8.8.8.8 8.8.4.4); + +package Net::DNS::Resolver { + use Storable qw(freeze); + sub new { + my $class = shift; + state %cache; + return $cache{freeze \@_} //= $class->SUPER::new(@_); + } +} # return a list of the zones known to the local # bind @@ -97,15 +108,20 @@ sub ns { my $domain = shift; ### assert: @_ % 2 == 0 - my %resflags = (nameservers => [qw/8.8.8.8/], @_); + my %resflags = (nameservers => \@extns, @_); my $aa = delete $resflags{aa}; - my $nameservers = $resflags{nameservers}; + my $nameservers = join ',' => @{$resflags{nameservers}}; my @ns; my $r = Net::DNS::Resolver->new(%resflags); - my $q = $r->query($domain, 'NS') or die $r->errorstring, "\@@$nameservers\n"; + my $q; - die "no aa @@$nameservers\n" if $aa and not $q->header->aa; + for (my $i = 3; $i; --$i) { + $q = $r->query($domain, 'NS') and last; + } + die $r->errorstring . "\@$nameservers\n" if not $q; + + die "no aa \@$nameservers\n" if $aa and not $q->header->aa; push @ns, map { $_->nsdname } grep { $_->type eq 'NS' } $q->answer; return sort @ns; @@ -113,11 +129,17 @@ sub serial { my $domain = shift; - my %resflags = (nameservers => [qw/8.8.8.8/], @_); - my $nameservers = $resflags{nameservers}; + my %resflags = (nameservers => \@extns, @_); + my $nameservers = join ',' => @{$resflags{nameservers}}; my $r = Net::DNS::Resolver->new(%resflags); - my $q = $r->query($domain, 'SOA') or die $r->errorstring, "\@@$nameservers\n"; + my $q; + + for (my $i = 3; $i; --$i) { + $q = $r->query($domain, 'SOA') and last; + } + die $r->errorstring, "\@$nameservers\n" if not $q; + return (map { $_->serial } grep { $_->type eq 'SOA' } $q->answer)[0]; } @@ -139,16 +161,29 @@ my @their = eval { sort +ns($domain) }; push @errs, $@ if $@; - if (!@errs) { + if (@errs) { + chomp @errs; + die join(', ' => @errs) . "\n"; + } + + if ("@our" ne "@their") { local $" = ', '; - if ("@our" eq "@their") { - return 1; - } die "NS differ (our @our) vs (their @their)\n"; } - chomp @errs; - die join(', ', @errs) . "\n"; + + return uniq sort @our, @their; +} +sub serial_ok { + my ($domain, @ns) = @_; + my @serials = map { my $s = serial $domain, nameservers => [$_]; "$s\@$_" } @ns; + + if (uniq(map { /(\d+)/ } @serials) != 1) { + die "serials do not match: @serials\n"; + } + + $serials[0] =~ /(\d+)/; + return $1; } sub main { @@ -176,10 +211,24 @@ my (@OK, %CRITICAL); foreach my $domain (@domains) { print STDERR "$domain " if $opt_progress; - eval { ns_ok($domain, $opt_reference) }; - if ($@) { $CRITICAL{$domain} = $@ } - else { push @OK, $domain } - say STDERR $@ ? 'not ok' : 'ok' if $opt_progress; + + my @ns = eval { ns_ok($domain, $opt_reference) }; + if ($@) { + $CRITICAL{$domain} = $@; + say STDERR 'ns not ok' if $opt_progress; + next; + } + print STDERR 'ok(ns) '; + + my @serial = eval { serial_ok($domain, @ns) }; + if ($@) { + $CRITICAL{$domain} = $@; + say STDERR 'serial not ok' if $opt_progress; + next; + } + say STDERR 'ok(serial)' if $opt_progress; + push @OK, $domain; + } # use DDP; @@ -216,3 +265,5 @@ =back =cut + +# vim:sts=4 ts=8 sw=4 et: