-
Notifications
You must be signed in to change notification settings - Fork 506
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extend check_dns.pl so it can take a list of domains #338
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,17 +47,18 @@ BEGIN | |
my $default_type = "A"; | ||
my $type = $default_type; | ||
my $record; | ||
my @records; | ||
my $server; | ||
my @servers; | ||
my $expected_result; | ||
my $expected_regex; | ||
my $expected_regex2; | ||
my $valid_expected_regex; | ||
my $no_uniq_results; | ||
my $randomize_servers; | ||
|
||
%options = ( | ||
"s|server=s" => [ \$server, "DNS server(s) to query, can be a comma separated list of servers" ], | ||
"r|record=s" => [ \$record, "DNS record to query" ], | ||
"r|record=s" => [ \$record, "DNS record(s) to query, can be a comma separated list of records" ], | ||
"q|type=s" => [ \$type, "DNS query type (defaults to '$default_type' record)" ], | ||
"e|expected-result=s" => [ \$expected_result, "Expected results, comma separated" ], | ||
"R|expected-regex=s" => [ \$expected_regex, "Expected regex to validate against each returned result (anchored, so if testing partial TXT records you may need to use .* before and after the regex, and if differing TXT records are returned then use alternation '|' to support the different regex, see tests/test_dns.sh for an example)" ], | ||
|
@@ -73,16 +74,23 @@ BEGIN | |
for(my $i=0; $i < scalar @servers; $i++){ | ||
$servers[$i] = validate_host($servers[$i]); | ||
} | ||
|
||
grep($type, @valid_types) or usage "unsupported type '$type' given, must be one of: " . join(",", @valid_types); | ||
if($type eq "PTR"){ | ||
$record = isIP($record) or usage "invalid record given for type PTR, should be an IP"; | ||
} elsif($type eq "SRV"){ | ||
$record =~ /^[A-Za-z_\.-]+\.$domain_regex/ or usage "invalid record given for type SRV, must contain only alphanumeric, underscores, dashes followed by a valid domain name format"; | ||
} else { | ||
$record = isDomain($record) or isFqdn($record) or usage "invalid record given, should be a domain or fully qualified host name"; | ||
|
||
$record or usage "record(s) not specified"; | ||
@records = split(/\s*[,\s]\s*/, $record); | ||
for(my $i=0; $i < scalar @records; $i++){ | ||
if($type eq "PTR"){ | ||
$records[$i] = isIP($records[$i]) or usage "invalid record " . $records[$i] . " given for type PTR, should be an IP"; | ||
} elsif($type eq "SRV"){ | ||
$records[$i] =~ /^[A-Za-z_\.-]+\.$domain_regex/ or usage "invalid record" . $records[$i] . " given for type SRV, must contain only alphanumeric, underscores, dashes followed by a valid domain name format"; | ||
} else { | ||
$records[$i] = isDomain($records[$i]) or isFqdn($records[$i]) or usage "invalid record " . $records[$i] . " given, should be a domain or fully qualified host name"; | ||
} | ||
} | ||
|
||
vlog_option "server", join(",", @servers); | ||
vlog_option "record", $record; | ||
vlog_option "record", join(",", @records); | ||
vlog_option "type", $type; | ||
if($randomize_servers){ | ||
vlog2 "randomizing nameserver list"; | ||
|
@@ -104,7 +112,7 @@ BEGIN | |
} | ||
vlog_option "expected results", $expected_result; | ||
} | ||
$expected_regex2 = validate_regex($expected_regex) if defined($expected_regex); | ||
$valid_expected_regex = validate_regex($expected_regex) if defined($expected_regex); | ||
|
||
vlog2; | ||
set_timeout(); | ||
|
@@ -128,101 +136,104 @@ BEGIN | |
$res->udp_timeout(2); | ||
vlog2 "set resolver timeout to 2 secs per server"; | ||
|
||
my @results; | ||
my @rogue_results; | ||
my @missing_results; | ||
|
||
vlog2 "sending query for $record $type record"; | ||
my $start = time; | ||
my $query = $res->query($record, $type); | ||
my $stop = time; | ||
my $total_time = sprintf("%.4f", $stop - $start); | ||
vlog2; | ||
plural @servers; | ||
$query or quit "CRITICAL", "query returned with no answer from server$plural " . join(",", @servers) . " in $total_time secs" . ( $verbose ? " for record '$record' type '$type'" : ""); | ||
vlog2 "query returned in $total_time secs"; | ||
my $perfdata = " | dns_query_time='${total_time}s'"; | ||
|
||
vlog3 "returned records:\n"; | ||
foreach my $rr ($query->answer){ | ||
vlog3 Dumper($rr); | ||
my $result; | ||
if($rr->type eq "A"){ | ||
$result = $rr->address; | ||
} elsif($rr->type eq "CNAME"){ | ||
$result = $rr->cname; | ||
$result =~ s/\.$//; | ||
} elsif($rr->type eq "MX"){ | ||
$result = $rr->exchange; | ||
} elsif($rr->type eq "NS"){ | ||
$result = $rr->nsdname; | ||
} elsif($rr->type eq "PTR"){ | ||
$result = $rr->ptrdname; | ||
} elsif($rr->type eq "SOA"){ | ||
$result = $rr->serial; | ||
} elsif($rr->type eq "SRV"){ | ||
$result = $rr->target; | ||
} elsif($rr->type eq "TXT"){ | ||
$result = $rr->txtdata; | ||
} else { | ||
quit "UNKNOWN", "unknown/unsupported record type '$rr->type' returned for record '$record'"; | ||
} | ||
vlog2 "got result: $result\n"; | ||
if($type eq "A"){ | ||
isIP($result) or quit "CRITICAL", "invalid result '$result' returned for A record by DNS server, expected IP address for A record$perfdata"; | ||
} elsif(grep { $type eq $_ } qw/CNAME MX NS PTR SRV/){ | ||
isFqdn($result) or quit "CRITICAL", "invalid result '$result' returned " . ($verbose ? "for record '$record' type '$type' ": "") . "by DNS server, expected FQDN for this record type$perfdata"; | ||
} elsif($type eq "SOA"){ | ||
isInt($result) or quit "CRITICAL", "invalid serial result '$result' returned for SOA record " . ($verbose ? "'$record' ": "") . "by DNS server, expected an unsigned integer$perfdata"; | ||
} | ||
push(@results, $result); | ||
if(@expected_results){ | ||
unless(grep(lc $_ eq lc $result, @expected_results)){ | ||
vlog3 "result '$result' wasn't found in expected results, added to rogue list"; | ||
push(@rogue_results, $result); | ||
my $start_calls = time; | ||
for(my $i=0; $i < scalar @records; $i++){ | ||
vlog2 "sending query for $records[$i] $type record"; | ||
my @results; | ||
my @rogue_results; | ||
my @missing_results; | ||
my $start_call = time; | ||
my $query = $res->query($records[$i], $type); | ||
my $stop_call = time; | ||
my $call_time = sprintf("%.4f", $stop_call - $start_call); | ||
vlog2; | ||
plural @servers; | ||
$query or quit "CRITICAL", "query returned with no answer from server$plural " . join(",", @servers) . " in $call_time secs" . ( $verbose ? " for record '$records[$i]' type '$type'" : ""); | ||
vlog2 "query returned in $call_time secs"; | ||
my $perfdata = " | dns_query_time='${call_time}s'"; | ||
|
||
vlog3 "returned records:\n"; | ||
foreach my $rr ($query->answer){ | ||
vlog3 Dumper($rr); | ||
my $result; | ||
if($rr->type eq "A"){ | ||
$result = $rr->address; | ||
} elsif($rr->type eq "CNAME"){ | ||
$result = $rr->cname; | ||
$result =~ s/\.$//; | ||
} elsif($rr->type eq "MX"){ | ||
$result = $rr->exchange; | ||
} elsif($rr->type eq "NS"){ | ||
$result = $rr->nsdname; | ||
} elsif($rr->type eq "PTR"){ | ||
$result = $rr->ptrdname; | ||
} elsif($rr->type eq "SOA"){ | ||
$result = $rr->serial; | ||
} elsif($rr->type eq "SRV"){ | ||
$result = $rr->target; | ||
} elsif($rr->type eq "TXT"){ | ||
$result = $rr->txtdata; | ||
} else { | ||
quit "UNKNOWN", "unknown/unsupported record type '$rr->type' returned for record '$records[$i]'"; | ||
} | ||
vlog2 "got result: $result\n"; | ||
if($type eq "A"){ | ||
isIP($result) or quit "CRITICAL", "invalid result '$result' returned for A record by DNS server, expected IP address for A record$perfdata"; | ||
} elsif(grep { $type eq $_ } qw/CNAME MX NS PTR SRV/){ | ||
isFqdn($result) or quit "CRITICAL", "invalid result '$result' returned " . ($verbose ? "for record '$record' type '$type' ": "") . "by DNS server, expected FQDN for this record type$perfdata"; | ||
} elsif($type eq "SOA"){ | ||
isInt($result) or quit "CRITICAL", "invalid serial result '$result' returned for SOA record " . ($verbose ? "'$records[$i]' ": "") . "by DNS server, expected an unsigned integer$perfdata"; | ||
} | ||
push(@results, $result); | ||
if(@expected_results){ | ||
unless(grep(lc $_ eq lc $result, @expected_results)){ | ||
vlog3 "result '$result' wasn't found in expected results, added to rogue list"; | ||
push(@rogue_results, $result); | ||
} | ||
} | ||
} | ||
} | ||
|
||
@results or quit "CRITICAL", "no result received for '$record' $type record from servers " . join(",", @servers) . " in $total_time secs"; | ||
@results or quit "CRITICAL", "no result received for '$records[$i]' $type record from servers " . join(",", @servers) . " in $call_time secs"; | ||
my @results_uniq = sort(uniq_array(@results)); | ||
|
||
my @results_uniq = sort(uniq_array(@results)); | ||
|
||
foreach my $expected_result2 (@expected_results){ | ||
unless(grep(lc $_ eq lc $expected_result2, @results_uniq)){ | ||
vlog3 "$expected_result2 wasn't found in results, adding to missing list"; | ||
push(@missing_results, $expected_result2); | ||
foreach my $exp_result (@expected_results){ | ||
unless(grep(lc $_ eq lc $exp_result, @results_uniq)){ | ||
vlog3 "$exp_result wasn't found in results, adding to missing list"; | ||
push(@missing_results, $exp_result); | ||
} | ||
} | ||
} | ||
|
||
my @regex_mismatches; | ||
if($expected_regex2){ | ||
foreach my $result (@results){ | ||
$result =~ /^$expected_regex2$/ or push(@regex_mismatches, $result); | ||
my @regex_mismatches; | ||
if($valid_expected_regex){ | ||
foreach my $result (@results){ | ||
$result =~ /^$valid_expected_regex$/ or push(@regex_mismatches, $result); | ||
} | ||
} | ||
@regex_mismatches = sort(uniq_array(@regex_mismatches)) if(@regex_mismatches); | ||
$msg .= "\n$records[$i] $type record "; | ||
if(scalar @rogue_results or scalar @missing_results){ | ||
critical; | ||
$msg .= "MISMATCH, expected '" . join(",", @expected_results) . "', got '"; | ||
} elsif(scalar @regex_mismatches){ | ||
critical; | ||
$msg .= "regex validation FAILED on '" . join("','", @regex_mismatches) . "' against regex '$expected_regex', returns '"; | ||
} elsif($type eq "SOA"){ | ||
$msg .= "OK return serial '"; | ||
} else { | ||
$msg .= "OK return '"; | ||
} | ||
if ($no_uniq_results){ | ||
$msg .= join("','", @results); | ||
} else { | ||
$msg .= join("','", @results_uniq); | ||
} | ||
$msg .= "'"; | ||
$msg .= " | dns_query_time='${call_time}s'" if $verbose; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this will mess up in --verbose mode |
||
} | ||
@regex_mismatches = sort(uniq_array(@regex_mismatches)) if(@regex_mismatches); | ||
|
||
$msg .= "$record $type record "; | ||
|
||
if(scalar @rogue_results or scalar @missing_results){ | ||
critical; | ||
$msg .= "mismatch, expected '" . join(",", @expected_results) . "', got '"; | ||
} elsif(scalar @regex_mismatches){ | ||
critical; | ||
$msg .= "regex validation failed on '" . join("','", @regex_mismatches) . "' against regex '$expected_regex', returns '"; | ||
} elsif($type eq "SOA"){ | ||
$msg .= "return serial '"; | ||
} else { | ||
$msg .= "returns '"; | ||
} | ||
if($no_uniq_results){ | ||
$msg .= join("','", @results); | ||
} else { | ||
$msg .= join("','", @results_uniq); | ||
} | ||
$msg .= "'"; | ||
$msg .= " in $total_time secs" if $verbose; | ||
|
||
my $stop_calls = time; | ||
my $total_time = sprintf("%.4f", $stop_calls - $start_calls); | ||
my $perfdata = "\n | total_dns_query_time='${total_time}s'"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. multi-line will break some monitoring servers output |
||
$msg .= $perfdata; | ||
|
||
my $extended_command = dirname $progname; | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
multi-line will break some monitoring servers output / dashboards as traditionally only 1 line is guaranteed to be displayed, you wouldn't want the output on some systems to be "DNS OK:" and nothing else...