--- a/plugins/check_dns-serial Sun Dec 28 16:44:41 2014 +0100
+++ b/plugins/check_dns-serial Tue Dec 30 00:37:59 2014 +0100
@@ -2,19 +2,136 @@
use 5.014;
use strict;
use warnings;
-use GetOpt:::Long;
+use Getopt::Long qw(GetOptionsFromArray);
use Net::DNS;
+use Pod::Usage;
+
+
+my %resolver;
+sub uniq { my %h; @h{@_} = (); return keys %h; }
sub get_domains {
- return qw(schlittermann.de);
+ my @sources = @_;
+ my @domains = ();
+
+ foreach my $src (@sources) {
+
+ if ($src =~ m{^(?:(/.*)|file://(/.*))}) {
+ open(my $f, '<', $1) or die "$0: Can't open $1 for reading: $!\n";
+ push @domains, map { /^\s*(\S+)\s*/ } <$f>;
+ next;
+ }
+
+ push @domains, $src;
+ }
+
+ return @domains;
}
-# query the serial from
+# return a list of "official" nameservers
+sub get_ns {
+ my ($nameserver) = map { /^\@(.*)/ } $_[0] =~ /^\@/ ? shift : '@8.8.8.8';
+ my ($domain) = @_;
+ my @ns;
+
+ my $r = $resolver{$nameserver} //= Net::DNS::Resolver->new(nameservers => [$nameserver]);
+ my $q = $r->query($domain, 'NS') or die $r->errorstring, "\n";
+ push @ns, map { $_->nsdname } grep { $_->type eq 'NS' } $q->answer;
+
+ return sort @ns;
+}
+
+sub get_serial {
+ my ($nameserver) = map { /^\@(.*)/ } $_[0] =~ /^\@/ ? shift : '@8.8.8.8';
+ my ($domain) = shift;
+
+ my $r = $resolver{$nameserver} //=
+ Net::DNS::Resolver->new(nameservers => [$nameserver]);
+ my $q = $r->query($domain, 'SOA') or die $r->errorstring, "\n";
+ return (map { $_->serial } grep { $_->type eq 'SOA' } $q->answer)[0];
+}
+
# - the nameservers known from the ns records
-# - from the primary master if this is not one of the
+# - from the primary master if this is not one of the
# NS for the zone
# - from a list of additional (hidden) servers
#
# OK - if the serial numbers are in sync
# WARNING - if there is some difference
# CRITICAL - if the serial cannot be found at one of the sources
+
+sub ns_ok {
+ my ($reference, $domain) = @_;
+
+ my @our = sort eval { get_ns($reference, $domain) };
+ my @their = sort +get_ns($domain);
+
+ {
+ local $" = "\0";
+ return 1 if "@our" eq "@their";
+ }
+
+ local $" = ', ';
+ die "NS differ (our @our) vs (their @their)\n";
+};
+
+sub main {
+ my @argv = @_;
+ my $opt_reference = '212.80.235.130';
+ my $opt_progress = -t;
+
+ GetOptionsFromArray(\@argv,
+ 'reference=s' => \$opt_reference)
+ and @argv or pod2usage;
+ my @domains = get_domains(@argv);
+
+ my (@OK, %CRITICAL);
+ foreach my $domain (@domains) {
+ print STDERR "$domain " if $opt_progress;
+ eval { ns_ok('@212.80.235.130', $domain) };
+ if ($@) { $CRITICAL{$domain} = $@ }
+ else { push @OK, $domain }
+ say STDERR $@ ? 'not ok' : 'ok' if $opt_progress;
+ }
+
+# use DDP;
+# p @OK;
+# p %CRITICAL;
+
+ if (my $n = keys %CRITICAL) {
+ print "CRITICAL: $n of ".@domains." domains\n", map { "$_: $CRITICAL{$_}" } sort keys %CRITICAL;
+ return 2;
+ }
+
+ say 'OK: ' . @OK . ' domains checked';
+ return 0;
+
+}
+
+exit main @ARGV unless caller;
+
+__END__
+
+=head1 NAME
+
+ check_dns-serial - check the dns serial number from multiple sources
+
+=head1 SYNOPSIS
+
+ check_dns-serial [options] DOMAINS
+
+=head1 OPTIONS
+
+=over
+
+=item B<--reference>=I<address>
+
+The address of the reference server for our own domains (default: 212.80.235.130)
+
+=item B<--progress>
+
+Tell about the progress. (default: on if input is connected to a terminal)
+
+=back
+
+=cut