—#!/usr/bin/perl
# $Id: check_soa 1815 2020-10-14 21:55:18Z willem $
=head1 NAME
check_soa - Check a domain's nameservers
=head1 SYNOPSIS
B<check_soa> I<domain>
=head1 DESCRIPTION
B<check_soa> queries each of a domain's nameservers for the Start
of Authority (SOA) record and prints the serial number. Errors
are printed for nameservers that couldn't be reached or didn't
answer authoritatively.
=head1 AUTHOR
The original Bourne Shell and C versions were printed in
I<DNS and BIND> by Paul Albitz & Cricket Liu.
This Perl version was written by Michael Fuhr <mike@fuhr.org>.
=head1 SEE ALSO
L<perl(1)>, L<axfr>, L<check_zone>, L<mresolv>, L<mx>, L<perldig>, L<Net::DNS>
=cut
use
strict;
use
warnings;
use
File::Basename;
use
Net::DNS;
#------------------------------------------------------------------------------
# Get the domain from the command line.
#------------------------------------------------------------------------------
die
"Usage: "
, basename($0),
" domain\n"
unless
@ARGV
== 1;
my
(
$domain
) =
@ARGV
;
#------------------------------------------------------------------------------
# Find all the nameservers for the domain.
#------------------------------------------------------------------------------
my
$res
= Net::DNS::Resolver->new();
$res
->defnames(0);
$res
->retry(2);
my
$ns_req
=
$res
->query(
$domain
,
"NS"
);
die
"No nameservers found for $domain: "
,
$res
->errorstring,
"\n"
unless
defined
(
$ns_req
) and (
$ns_req
->header->ancount > 0);
# Send out non-recursive queries
$res
->recurse(0);
# Do not buffer standard out
local
$| = 1;
#------------------------------------------------------------------------------
# Check the SOA record on each nameserver.
#------------------------------------------------------------------------------
foreach
my
$nsrr
(
grep
{
$_
->type eq
"NS"
}
$ns_req
->answer) {
#----------------------------------------------------------------------
# Set the resolver to query this nameserver.
#----------------------------------------------------------------------
my
$ns
=
$nsrr
->nsdname;
# In order to lookup the IP(s) of the nameserver, we need a Resolver
# object that is set to our local, recursive nameserver. So we create
# a new object just to do that.
my
$local_res
= Net::DNS::Resolver->new();
my
$a_req
=
$local_res
->query(
$ns
,
'A'
);
unless
(
$a_req
) {
warn
"Can not find address for $ns: "
,
$res
->errorstring,
"\n"
;
next
;
}
foreach
my
$ip
(
map
{
$_
->address }
grep
{
$_
->type eq
'A'
}
$a_req
->answer) {
#----------------------------------------------------------------------
# Ask this IP.
#----------------------------------------------------------------------
$res
->nameservers(
$ip
);
"$ns ($ip): "
;
#----------------------------------------------------------------------
# Get the SOA record.
#----------------------------------------------------------------------
my
$soa_req
=
$res
->
send
(
$domain
,
'SOA'
,
'IN'
);
unless
(
defined
(
$soa_req
)) {
warn
$res
->errorstring,
"\n"
;
next
;
}
#----------------------------------------------------------------------
# Is this nameserver authoritative for the domain?
#----------------------------------------------------------------------
unless
(
$soa_req
->header->aa) {
warn
"isn't authoritative for $domain\n"
;
next
;
}
#----------------------------------------------------------------------
# We should have received exactly one answer.
#----------------------------------------------------------------------
unless
(
$soa_req
->header->ancount == 1) {
warn
"expected 1 answer, got "
,
$soa_req
->header->ancount,
"\n"
;
next
;
}
#----------------------------------------------------------------------
# Did we receive an SOA record?
#----------------------------------------------------------------------
unless
((
$soa_req
->answer)[0]->type eq
"SOA"
) {
warn
"expected SOA, got "
, (
$soa_req
->answer)[0]->type,
"\n"
;
next
;
}
#----------------------------------------------------------------------
# Print the serial number.
#----------------------------------------------------------------------
"has serial number "
, (
$soa_req
->answer)[0]->serial,
"\n"
;
}
}
0;