plugins/check_dns-serial
changeset 1 cff9b7e57f19
parent 0 676230d2d1ae
child 2 2d11bbd7be1e
equal deleted inserted replaced
0:676230d2d1ae 1:cff9b7e57f19
     1 #! /usr/bin/perl
     1 #! /usr/bin/perl
     2 use 5.014;
     2 use 5.014;
     3 use strict;
     3 use strict;
     4 use warnings;
     4 use warnings;
     5 use GetOpt:::Long;
     5 use Getopt::Long qw(GetOptionsFromArray);
     6 use Net::DNS;
     6 use Net::DNS;
       
     7 use Pod::Usage;
       
     8 
       
     9 
       
    10 my %resolver;
       
    11 sub uniq { my %h; @h{@_} = (); return keys %h; }
     7 
    12 
     8 sub get_domains {
    13 sub get_domains {
     9     return qw(schlittermann.de);
    14     my @sources = @_;
       
    15     my @domains = ();
       
    16 
       
    17     foreach my $src (@sources) {
       
    18 
       
    19         if ($src =~ m{^(?:(/.*)|file://(/.*))}) {
       
    20             open(my $f, '<', $1) or die "$0: Can't open $1 for reading: $!\n";
       
    21             push @domains, map { /^\s*(\S+)\s*/ } <$f>;
       
    22             next;
       
    23         }
       
    24 
       
    25         push @domains, $src;
       
    26     }
       
    27 
       
    28     return @domains;
    10 }
    29 }
    11 
    30 
    12 # query the serial from
    31 # return a list of "official" nameservers
       
    32 sub get_ns {
       
    33     my ($nameserver) = map { /^\@(.*)/ } $_[0] =~ /^\@/ ? shift : '@8.8.8.8';
       
    34     my ($domain) = @_;
       
    35     my @ns;
       
    36 
       
    37     my $r = $resolver{$nameserver} //= Net::DNS::Resolver->new(nameservers => [$nameserver]);
       
    38     my $q = $r->query($domain, 'NS') or die $r->errorstring, "\n";
       
    39     push @ns, map { $_->nsdname } grep { $_->type eq 'NS' } $q->answer;
       
    40 
       
    41     return sort @ns;
       
    42 }
       
    43 
       
    44 sub get_serial {
       
    45     my ($nameserver) = map { /^\@(.*)/ } $_[0] =~ /^\@/ ? shift : '@8.8.8.8';
       
    46     my ($domain) = shift;
       
    47 
       
    48     my $r = $resolver{$nameserver} //=
       
    49     Net::DNS::Resolver->new(nameservers => [$nameserver]);
       
    50     my $q = $r->query($domain, 'SOA') or die $r->errorstring, "\n";
       
    51     return (map { $_->serial } grep { $_->type eq 'SOA' } $q->answer)[0];
       
    52 }
       
    53 
    13 # - the nameservers known from the ns records
    54 # - the nameservers known from the ns records
    14 # - from the primary master if this is not one of the 
    55 # - from the primary master if this is not one of the
    15 #   NS for the zone
    56 #   NS for the zone
    16 # - from a list of additional (hidden) servers
    57 # - from a list of additional (hidden) servers
    17 #
    58 #
    18 # OK - if the serial numbers are in sync
    59 # OK - if the serial numbers are in sync
    19 # WARNING - if there is some difference
    60 # WARNING - if there is some difference
    20 # CRITICAL - if the serial cannot be found at one of the sources
    61 # CRITICAL - if the serial cannot be found at one of the sources
       
    62 
       
    63 sub ns_ok {
       
    64     my ($reference, $domain) = @_;
       
    65 
       
    66     my @our = sort eval { get_ns($reference, $domain) };
       
    67     my @their = sort +get_ns($domain);
       
    68 
       
    69     {
       
    70 	local $" = "\0";
       
    71 	return 1 if "@our" eq "@their"; 
       
    72     }
       
    73 
       
    74     local $" = ', ';
       
    75     die "NS differ (our @our) vs (their @their)\n";
       
    76 };
       
    77 
       
    78 sub main {
       
    79     my @argv = @_;
       
    80     my $opt_reference = '212.80.235.130';
       
    81     my $opt_progress = -t;
       
    82 
       
    83     GetOptionsFromArray(\@argv,
       
    84 	'reference=s' => \$opt_reference) 
       
    85     and @argv or pod2usage;
       
    86     my @domains = get_domains(@argv);
       
    87 
       
    88     my (@OK, %CRITICAL);
       
    89     foreach my $domain (@domains) {
       
    90 	print STDERR "$domain " if $opt_progress;
       
    91 	eval { ns_ok('@212.80.235.130', $domain) };
       
    92 	if ($@) { $CRITICAL{$domain} = $@ }
       
    93 	else { push @OK, $domain }
       
    94 	say STDERR $@ ? 'not ok' : 'ok' if $opt_progress;
       
    95     }
       
    96 
       
    97 #    use DDP;
       
    98 #    p @OK;
       
    99 #    p %CRITICAL;
       
   100 
       
   101     if (my $n = keys %CRITICAL) {
       
   102 	print "CRITICAL: $n of ".@domains." domains\n", map { "$_: $CRITICAL{$_}" } sort keys %CRITICAL;
       
   103 	return 2;
       
   104     }
       
   105 
       
   106     say 'OK: ' . @OK . ' domains checked';
       
   107     return 0;
       
   108 
       
   109 }
       
   110 
       
   111 exit main @ARGV unless caller;
       
   112 
       
   113 __END__
       
   114 
       
   115 =head1 NAME
       
   116 
       
   117  check_dns-serial - check the dns serial number from multiple sources
       
   118 
       
   119 =head1 SYNOPSIS
       
   120 
       
   121  check_dns-serial [options] DOMAINS
       
   122 
       
   123 =head1 OPTIONS
       
   124 
       
   125 =over
       
   126 
       
   127 =item B<--reference>=I<address>
       
   128 
       
   129 The address of the reference server for our own domains (default: 212.80.235.130)
       
   130 
       
   131 =item B<--progress>
       
   132 
       
   133 Tell about the progress. (default: on if input is connected to a terminal)
       
   134 
       
   135 =back
       
   136 
       
   137 =cut