[Mon-commit] mon/mon.d dns.monitor,1.2,1.3
Brought to you by:
trockij
From: David N. <vi...@us...> - 2006-09-05 12:52:43
|
Update of /cvsroot/mon/mon/mon.d In directory sc8-pr-cvs7.sourceforge.net:/tmp/cvs-serv18267 Modified Files: dns.monitor Log Message: pulled in various CMU updates, including multiple master support, configurable timeouts, and better errors in some cases Index: dns.monitor =================================================================== RCS file: /cvsroot/mon/mon/mon.d/dns.monitor,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** dns.monitor 15 Nov 2004 14:45:19 -0000 1.2 --- dns.monitor 5 Sep 2006 12:52:37 -0000 1.3 *************** *** 2,6 **** # # Copyright (C) 1998 David Eckelkamp ! # Copyright (C) 2002 David Nolan # # This program is free software; you can redistribute it and/or modify --- 2,6 ---- # # Copyright (C) 1998 David Eckelkamp ! # Copyright (C) 2002-2006 Carnegie Mellon University # # This program is free software; you can redistribute it and/or modify *************** *** 26,34 **** =head1 SYNOPSIS ! B<dns.monitor> I<-zone zone [-zone zone ...]> I<-master master> I<[-serial_threshold num]> I<[-tcp]> I<server [server ...]> - or ! B<dns.monitor> I<-caching_only> I<[-tcp]> I<-query record[:type] [-query record[:type] ...]> I<server [server ...]> =head1 DESCRIPTION --- 26,69 ---- =head1 SYNOPSIS ! B<dns.monitor> ! =over 12 ! ! ( [ I<-zone zone [-zone zone ...]> ! ! ! =over 4 ! ! ! I<-master server [-master server ...]> ! ! I<[-serial_threshold num]> ! ! I<[-failsingle]> ] ! ! =back ! ! | [ I<-caching_only> ! ! =over 4 ! ! I<-query record[:type[:value]] [-query record[:type[:value]] ...]> ] ) ! ! =back ! ! I<[-tcp]> ! ! I<[-retry num]> ! ! I<[-retransmit num]> ! ! I<[-timeout num]> ! ! I<[-debug num]> ! ! I<server [server ...]> ! ! =back =head1 DESCRIPTION *************** *** 40,68 **** server and one or more slave servers. The I<zone> argument is the zone to check. There can be multiple I<zone> arguments. The I<master> ! argument is the master server for the I<zone>. It will be queried for ! the base information. Then each I<server> will be queried to verify ! that it has the correct answers. If the I<serial_threshold> argument ! is provided, the slave servers must return a zone whose serial number ! is no more than the threshold from the serial number of the zone on ! the master. (Zone serial numbers may not be identical during zone ! propagation, or on Dynamic DNS zones which may be updated hundreds or ! thousands of times an hour) It is assumed that each I<server> is ! supposed to be authoritative for the I<zone>. The I<-tcp> option ! will cause lookups to be done via TCP instead of the default UDP. ! In caching mode, specified via the I<-caching_only> switch, B<dns.monitor> ! will perform a set of DNS queries to one or more servers. The I<query> ! argument is the query to perform. The query may have an optional query type ! specified as I<:type> on the end of the query. I.e your.zone.com:MX will ! cause B<dns.monitor> to fetch the MX records for your.zone.com. There can be ! multiple I<query> arguments. Each I<server> will be contacted to verify ! that it returns a valid response to the query. If you wish to use ! B<dns.monitor> to verify that a caching DNS server is actually fetching fresh ! data from other servers successfully, it is recommended that the DNS records ! you query should have very short TTLs. ! The exit code of B<dns.monitor> will be the highest number of servers which ! failed on a single zone/query, 0 if no problems occurred, or -1 if an error ! with the script arguments was detected. =head1 AUTHOR --- 75,116 ---- server and one or more slave servers. The I<zone> argument is the zone to check. There can be multiple I<zone> arguments. The I<master> ! argument is the master server for the I<zone>. There can be multiple ! I<master> arguments. The master server(s) will be queried for the ! base information. If the I<serial_threshold> argument is provided, ! the serials collected from the I<master> servers are checked to be ! within I<serial_threshold>. The greatest serial of all of the ! I<master> servers is chosen for comparison. Then each I<server> will ! be queried to verify that it has the correct answers. If the ! I<serial_threshold> argument is provided, the slave servers must ! return a zone whose serial number is no more than the threshold from ! the serial number of the zone on the master. (Zone serial numbers may ! not be identical during zone propagation, or on Dynamic DNS zones ! which may be updated hundreds or thousands of times an hour) It is ! assumed that each I<server> is supposed to be authoritative for the ! I<zone>. The I<-tcp> option will cause lookups to be done via TCP ! instead of the default UDP. ! In caching mode, specified via the I<-caching_only> switch, ! B<dns.monitor> will perform a set of DNS queries to one or more ! servers. The I<query> argument is the query to perform. The query ! may have an optional query type specified as I<:type> on the end of ! the query. I.e your.zone.com:MX will cause B<dns.monitor> to fetch ! the MX records for your.zone.com. There can be multiple I<query> ! arguments. The query type may also have an optional result specified ! as I<:value> on the end of the query (type must also be specified). ! Each I<server> will be contacted to verify that it returns a valid ! response to the query. If a query result is specified B<dns.monitor> ! will return an error is the DNS query returns an answer which differs ! from the supplied result. If you wish to use B<dns.monitor> to verify ! that a caching DNS server is actually fetching fresh data from other ! servers successfully, it is recommended that the DNS records you query ! should have very short TTLs. ! The exit code of B<dns.monitor> will be the highest number of servers ! which failed on a single zone/query, 0 if no problems occurred, or -1 ! if an error with the script arguments was detected. If all of the ! I<master> servers fail, the return code will be 252. If using the ! I<failsingle> option and any I<master> server fails, the return code ! will be 251. =head1 AUTHOR *************** *** 70,78 **** The script was originally written by David Eckelkamp <da...@tr...> ! The script was modified to support Caching DNS servers and frequently updating Zone serials by David Nolan <vi...@cm...> =cut ! #use strict; use Getopt::Long; use English; --- 118,129 ---- The script was originally written by David Eckelkamp <da...@tr...> ! The script was modified to support Caching DNS servers, configurable ! retry/timeout parameters, multiple DNS Master servers, and ! configurable Zone serials by David Nolan <vi...@cm...> and Jason ! Carr <jc...@an...> from Carnegie Mellon University. =cut ! use strict; use Getopt::Long; use English; *************** *** 81,100 **** use Net::DNS::Packet; use Net::DNS::RR; my($Program) = basename($0); my(@Zones) = (); my(@Queries) = (); ! my($Master) = undef; my($SerialThreshold) = (0); my($CachingServer) = (0); my($UseTCP) = (0); ! my(%OptVars) = ("master" => \$Master, "zone" => \@Zones, "serial_threshold" => \$SerialThreshold, "caching_only" => \$CachingServer, "query" => \@Queries, ! "tcp" => \$UseTCP); ! if (!GetOptions(\%OptVars, "master=s", "zone=s@", "serial_threshold=s", "caching_only", "tcp", "query=s@")) { print STDERR "Problems with Options, sorry\n"; exit -1; --- 132,163 ---- use Net::DNS::Packet; use Net::DNS::RR; + use Data::Dumper; my($Program) = basename($0); my(@Zones) = (); my(@Queries) = (); ! my(@Master) = (); my($SerialThreshold) = (0); my($CachingServer) = (0); my($UseTCP) = (0); ! my ($retries, $retrans, $timeout) = ( 2, 5, undef ); ! my $debug = 0; ! my $failsingle = 0; ! ! my(%OptVars) = ( ! "master" => \@Master, "zone" => \@Zones, "serial_threshold" => \$SerialThreshold, "caching_only" => \$CachingServer, "query" => \@Queries, ! "retry" => \$retries, ! "retransmit" => \$retrans, ! "timeout" => \$timeout, ! "tcp" => \$UseTCP, ! "debug" => \$debug, ! "failsingle" => \$failsingle ! ); ! if (!GetOptions(\%OptVars, "master=s@", "zone=s@", "serial_threshold=s", "caching_only", "tcp", "query=s@", "retry=i", "retransmit=i", "timeout=i", "debug", "failsingle")) { print STDERR "Problems with Options, sorry\n"; exit -1; *************** *** 102,114 **** if ( $#ARGV < 0 ) { print STDERR "$Program: at least one server must be specified\n"; exit -1; } if (!$CachingServer) { ! if (!defined($Master)) { print STDERR "$Program: The zone master server must be specified\n"; exit -1; } if ( !defined(@Zones) ) { print STDERR "$Program: At least one zone must be specified\n"; exit -1; } --- 165,180 ---- if ( $#ARGV < 0 ) { print STDERR "$Program: at least one server must be specified\n"; + usage(); exit -1; } if (!$CachingServer) { ! if (!defined(@Master)) { print STDERR "$Program: The zone master server must be specified\n"; + usage(); exit -1; } if ( !defined(@Zones) ) { print STDERR "$Program: At least one zone must be specified\n"; + usage(); exit -1; } *************** *** 116,119 **** --- 182,186 ---- if ( !defined(@Queries) ) { print STDERR "$Program: At least one query must be specified\n"; + usage(); exit -1; } *************** *** 125,131 **** my($bad_servers, $reason, $failcount, @FailedZones, @FailedServers, @Reasons); my($zone, $line, $i); - $maxfailcount = 0; foreach $zone (@Zones) { ! ($bad_servers, $reason, $failcount) = dns_verify($zone, $Master, @ARGV); if (defined($bad_servers)) { $err_cnt = $failcount if ($failcount > $err_cnt); --- 192,197 ---- my($bad_servers, $reason, $failcount, @FailedZones, @FailedServers, @Reasons); my($zone, $line, $i); foreach $zone (@Zones) { ! ($bad_servers, $reason, $failcount) = dns_verify($zone, \@Master, \@ARGV); if (defined($bad_servers)) { $err_cnt = $failcount if ($failcount > $err_cnt); *************** *** 160,171 **** my($err_cnt) = 0; my($bad_servers, $reason, $failcount, @FailedQuerys, @FailedServers, @Reasons); ! my($query, $type, $line, $i); foreach (@Queries) { ! ($query, $type) = split /:/; $type = 'A' if ($type eq ""); ! ($bad_servers, $reason, $failcount) = dns_test($query, $type, @ARGV); if (defined($bad_servers)) { $err_cnt = $failcount if ($failcount > $err_cnt); ! push(@FailedQuerys, "$query $type"); push(@FailedServers, $bad_servers); push(@Reasons, $reason); --- 226,238 ---- my($err_cnt) = 0; my($bad_servers, $reason, $failcount, @FailedQuerys, @FailedServers, @Reasons); ! my($query, $type, $line, $i, $target); foreach (@Queries) { ! ($query, $type, $target) = split /:/; $type = 'A' if ($type eq ""); ! ($bad_servers, $reason, $failcount) = dns_test($query, $type, $target, @ARGV); if (defined($bad_servers)) { $err_cnt = $failcount if ($failcount > $err_cnt); ! push(@FailedQuerys, "$query $type") if (!$target); ! push(@FailedQuerys, "$query $type == $target $type") if ($target); push(@FailedServers, $bad_servers); push(@Reasons, $reason); *************** *** 197,201 **** ! # dns_verity($zone, $master, $server, ...) # This subroutine takes 3 or more arguments. The first argument is the name of --- 264,268 ---- ! # dns_verify($zone, \@master, \@Servers) # This subroutine takes 3 or more arguments. The first argument is the name of *************** *** 218,225 **** sub dns_verify { # First verify that we have enough arguments. ! my($Zone, $Master, @Servers) = @_; my($result) = undef; my(@failed, $res, $soa_req, $Serial, $error_cnt, $server); # Query the $Master for the SOA of $Zone and get the serial number. $res = new Net::DNS::Resolver; --- 285,297 ---- sub dns_verify { # First verify that we have enough arguments. ! my($Zone) = shift; ! my(@Master) = @{shift()}; ! my(@Servers) = @{shift()}; my($result) = undef; my(@failed, $res, $soa_req, $Serial, $error_cnt, $server); + my(%serials) = (); + my(%errors) = (); + # Query the $Master for the SOA of $Zone and get the serial number. $res = new Net::DNS::Resolver; *************** *** 227,258 **** $res->defnames(0); # don't append default zone $res->recurse(0); # no recursion ! $res->retry(2); # 2 retries before failure ! $res->nameservers($Master); ! $soa_req = $res->query($Zone, "SOA"); ! if (!defined($soa_req) || ($soa_req->header->ancount <= 0)) { ! return($Master, ! sprintf("SOA query for $Zone from $Master failed %s\n", ! $res->errorstring)); } ! unless ($soa_req->header->aa) { ! return($Master, ! sprintf("$Master is not authoritative for $Zone\n")); } ! unless ($soa_req->header->ancount == 1) { ! return($Master, ! sprintf("Too many answers for SOA query to %s for %s\n", ! $Master, ! $Zone)); } ! unless (($soa_req->answer)[0]->type eq "SOA") { ! return($Master, ! printf("Query for SOA for %s from %s failed: " . ! "return type = %s\n", ! $Zone, ! $Master, ! ($soa_req->answer)[0]->type), ! scalar @Servers); } ! $Serial = ($soa_req->answer)[0]->serial; # Now, foreach server given on the command line, get the serial number from --- 299,380 ---- $res->defnames(0); # don't append default zone $res->recurse(0); # no recursion ! $res->retry($retries); # retries before failure ! $res->retrans($retrans); # retransmission interval ! $res->udp_timeout($timeout); # set udp timeout ! $res->tcp_timeout($timeout); # set tcp timeout ! ! $error_cnt=0; ! ! # Loop through each master server ! foreach my $qs (@Master) { ! $res->nameservers($qs); ! $soa_req = $res->query($Zone, "SOA"); ! if (!defined($soa_req) || ($soa_req->header->ancount <= 0)) { ! $error_cnt++; ! $errors{$qs} = sprintf("SOA query for $Zone from $qs failed %s\n", $res->errorstring); ! if ($res->errorstring eq 'NOERROR') { ! $errors{$qs} .= sprintf(" Empty answer received. (No zone on server?)\n") ! } ! if ($failsingle) { return ($qs, $errors{$qs}, 251); } ! next; ! } ! unless ($soa_req->header->aa) { ! $error_cnt++; ! $errors{$qs} = sprintf("$qs is not authoritative for $Zone\n"); ! if ($failsingle) { return ($qs, $errors{$qs}, 251); } ! next; ! } ! unless ($soa_req->header->ancount == 1) { ! $error_cnt++; ! $errors{$qs} = sprintf("Too many answers for SOA query to %s for %s\n", $qs, $Zone); ! if ($failsingle) { return ($qs, $errors{$qs}, 251); } ! next; ! } ! unless (($soa_req->answer)[0]->type eq "SOA") { ! $error_cnt++; ! $errors{$qs} = printf("Query for SOA for %s from %s failed: " . "return type = %s\n", $Zone, $qs, ($soa_req->answer)[0]->type); ! if ($failsingle) { return ($qs, $errors{$qs}, 251); } ! next; ! } ! ! $serials{$qs} = ($soa_req->answer)[0]->serial; } ! ! ! if ($debug >= 2) { ! print Data::Dumper->Dump([\%serials], ['serials']); } ! ! ! if ($error_cnt == scalar @Master) { ! # all masters errored ! return("", values %errors, 251); } ! ! my $maxvalue = undef; ! my $minvalue = undef; ! my $maxkey = undef; ! my $minkey = undef; ! ! foreach my $key (keys %serials) { ! if ($serials{$key} > $maxvalue) { ! $maxvalue = $serials{$key}; ! $maxkey = $key; ! } ! ! if (($serials{$key} < $minvalue) || (!defined $minkey)) { ! $minvalue = $serials{$key}; ! $minkey = $key; ! } ! } ! ! if (abs($maxvalue - $minvalue) > $SerialThreshold) { ! return ($minkey, "\nQuery to $minkey about $Zone failed\n" . ! "Serial number = $minvalue, should have been $maxvalue\n", 252) } ! ! $Serial = $maxvalue; ! ! return ("", "\nNo SOA Serial found for $Zone!?!?", 252) if (!$Serial); # Now, foreach server given on the command line, get the serial number from *************** *** 264,268 **** $res->defnames(0); # don't append default zone $res->recurse(0); # no recursion ! $res->retry(2); # 2 retries before failure $res->nameservers($server); $soa_req = $res->query($Zone, "SOA"); --- 386,394 ---- $res->defnames(0); # don't append default zone $res->recurse(0); # no recursion ! $res->retry($retries); ! $res->retrans($retrans); ! $res->udp_timeout($timeout); ! $res->tcp_timeout($timeout); ! $res->nameservers($server); $soa_req = $res->query($Zone, "SOA"); *************** *** 272,281 **** $result .= sprintf("\nSOA query for $Zone from $server failed %s\n", $res->errorstring); next; } ! unless(($soa_req->header->aa || $CachingServer) && $soa_req->header->ancount == 1 && ! ($soa_req->answer)[0]->type eq "SOA" && ! ((abs(($soa_req->answer)[0]->serial - $Serial)) ! <= $SerialThreshold)) { $error_cnt++; push(@failed, $server); --- 398,410 ---- $result .= sprintf("\nSOA query for $Zone from $server failed %s\n", $res->errorstring); + if ($res->errorstring eq 'NOERROR') { + $result .= sprintf(" Empty answer received. (No zone on server?)\n"); + } next; } ! unless($soa_req->header->aa ! && $soa_req->header->ancount == 1 ! && ($soa_req->answer)[0]->type eq "SOA" ! && ((abs(($soa_req->answer)[0]->serial - $Serial)) <= $SerialThreshold)) { $error_cnt++; push(@failed, $server); *************** *** 301,310 **** ! # dns_test($query, $type, $server, ...) ! # This subroutine takes 3 or more arguments. The first argument is the name of # the DNS record to query. The second argument is the type of the DNS ! # query to perform. The third and rest of the arguments are taken as ! # names of caching DNS servers. Each server will be queried for the # given record and type --- 430,440 ---- ! # dns_test($query, $type, $target, $server, ...) ! # This subroutine takes 4 or more arguments. The first argument is the name of # the DNS record to query. The second argument is the type of the DNS ! # query to perform. The third argument is the name of a second DNS record to query, ! # whose results should match the first query. The fourth and rest of the arguments are ! # taken as names of caching DNS servers. Each server will be queried for the # given record and type *************** *** 317,323 **** sub dns_test { # First verify that we have enough arguments. ! my($Query, $Master, @Servers) = @_; my($result) = undef; ! my(@failed, $res, $soa_req, $Serial, $error_cnt, $server); # Now, foreach server given on the command line, --- 447,453 ---- sub dns_test { # First verify that we have enough arguments. ! my($Query, $type, $target, @Servers) = @_; my($result) = undef; ! my(@failed, $res, $req, $treq, $Serial, $error_cnt, $server); # Now, foreach server given on the command line, *************** *** 325,347 **** $error_cnt = 0; foreach $server (@Servers) { ! $res = new Net::DNS::Resolver; ! $res->usevc(1) if ($UseTCP); ! $res->defnames(0); # don't append default zone ! $res->recurse(0); # no recursion ! $res->retry(2); # 2 retries before failure ! $res->nameservers($server); ! $soa_req = $res->query($Query, $type); ! if (!defined($soa_req) || ($soa_req->header->ancount <= 0)) { ! $error_cnt++; ! push(@failed, $server); ! $result .= sprintf("\n$type query for $Query from $server failed %s\n", ! $res->errorstring); ! next; ! } ! } if ($error_cnt == 0) { ! return(undef, undef, undef); } else { ! return("@failed", $result, $error_cnt); } } --- 455,519 ---- $error_cnt = 0; foreach $server (@Servers) { ! $res = new Net::DNS::Resolver; ! $res->defnames(0); # don't append default zone ! $res->retry($retries); # 2 retries before failure ! $res->retrans($retrans); ! $res->udp_timeout($timeout); ! $res->tcp_timeout($timeout); ! $res->nameservers($server); ! $req = $res->query($Query, $type); ! if (!defined($req) || ($req->header->ancount <= 0)) { ! $error_cnt++; ! push(@failed, $server); ! $result .= sprintf("\n$type query for $Query from $server failed %s\n", ! $res->errorstring); ! next; ! } elsif ($target) { ! $treq = $res->query($target, $type); ! my $status = 0; ! foreach my $qans ($req->answer) { ! print STDERR $qans->string."\n" if ($debug); ! print STDERR $qans->rdatastr."\n" if ($debug); ! foreach my $tans ($treq->answer) { ! print STDERR "target\n" if ($debug); ! print STDERR $tans->string."\n" if ($debug); ! print STDERR $tans->rdatastr."\n" if ($debug); ! if ($tans->rdatastr eq $qans->rdatastr) { ! print STDERR "match found\n" if ($debug); ! $status = 1; ! last; ! } ! } ! last if ($status); ! } ! if (!$status) { ! $error_cnt++; ! push @failed, $server; ! $result .= "Query $Query:$type failed to match $target\n"; ! } ! } ! } if ($error_cnt == 0) { ! return(undef, undef, undef); } else { ! return("@failed", $result, $error_cnt); } } + + sub usage { + print STDERR <<END_USAGE; + Usage: dns.monitor -zone zone [-zone zone ...] + -master master + [-serial_threshold num] + server [server ...] + or: dns.monitor -caching_only + -query record[:type] [-query record[:type] ...] + server [server ...] + Optional Arguments for either mode: + -retry num + -retransmit num + -timeout num + -debug num + + END_USAGE + } |