Blame demo/check_zone

Packit e6c8bb
#!/usr/local/bin/perl -w
Packit e6c8bb
# $Id: check_zone 264 2005-04-06 09:16:15Z olaf $
Packit e6c8bb
Packit e6c8bb
=head1 NAME
Packit e6c8bb
Packit e6c8bb
check_zone - Check a DNS zone for errors
Packit e6c8bb
Packit e6c8bb
=head1 SYNOPSIS
Packit e6c8bb
Packit e6c8bb
C<check_zone> [ C<-r> ] I<domain> [ I<class> ]
Packit e6c8bb
Packit e6c8bb
=head1 DESCRIPTION
Packit e6c8bb
Packit e6c8bb
Checks a DNS zone for errors.  Current checks are:
Packit e6c8bb
Packit e6c8bb
=over 4
Packit e6c8bb
Packit e6c8bb
=item *
Packit e6c8bb
Packit e6c8bb
Checks that all A records have corresponding PTR records.
Packit e6c8bb
Packit e6c8bb
=item *
Packit e6c8bb
Packit e6c8bb
Checks that hosts listed in NS, MX, and CNAME records have
Packit e6c8bb
A records.
Packit e6c8bb
Packit e6c8bb
=back
Packit e6c8bb
Packit e6c8bb
=head1 OPTIONS
Packit e6c8bb
Packit e6c8bb
=over 4
Packit e6c8bb
Packit e6c8bb
=item C<-r>
Packit e6c8bb
Packit e6c8bb
Perform a recursive check on subdomains.
Packit e6c8bb
Packit e6c8bb
=back
Packit e6c8bb
Packit e6c8bb
=head1 AUTHOR
Packit e6c8bb
Packit e6c8bb
Michael Fuhr <mike@fuhr.org>
Packit e6c8bb
Packit e6c8bb
=head1 SEE ALSO
Packit e6c8bb
Packit e6c8bb
L<perl(1)>, L<axfr>, L<check_soa>, L<mresolv>, L<mx>, L<perldig>, L<Net::DNS>
Packit e6c8bb
Packit e6c8bb
=cut
Packit e6c8bb
Packit e6c8bb
use strict;
Packit e6c8bb
use vars qw($opt_r);
Packit e6c8bb
Packit e6c8bb
use Getopt::Std;
Packit e6c8bb
use File::Basename;
Packit e6c8bb
use IO::Socket;
Packit e6c8bb
use Net::DNS;
Packit e6c8bb
Packit e6c8bb
getopts("r");
Packit e6c8bb
Packit e6c8bb
die "Usage: ", basename($0), " [ -r ] domain [ class ]\n"
Packit e6c8bb
	unless (@ARGV >= 1) && (@ARGV <= 2);
Packit e6c8bb
Packit e6c8bb
check_domain(@ARGV);
Packit e6c8bb
exit;
Packit e6c8bb
Packit e6c8bb
sub check_domain {
Packit e6c8bb
	my ($domain, $class) = @_;
Packit e6c8bb
	$class ||= "IN";
Packit e6c8bb
Packit e6c8bb
	print "-" x 70, "\n";
Packit e6c8bb
	print "$domain (class $class)\n";
Packit e6c8bb
	print "\n";
Packit e6c8bb
Packit e6c8bb
	my $res = Net::DNS::Resolver->new;
Packit e6c8bb
	$res->defnames(0);
Packit e6c8bb
	$res->retry(2);
Packit e6c8bb
Packit e6c8bb
	my $nspack = $res->query($domain, "NS", $class);
Packit e6c8bb
Packit e6c8bb
	unless (defined($nspack)) {
Packit e6c8bb
		warn "Couldn't find nameservers for $domain: ",
Packit e6c8bb
		     $res->errorstring, "\n";
Packit e6c8bb
		return;
Packit e6c8bb
	}
Packit e6c8bb
Packit e6c8bb
	print "nameservers (will request zone from first available):\n";
Packit e6c8bb
	my $ns;
Packit e6c8bb
	foreach $ns (grep { $_->type eq "NS" } $nspack->answer) {
Packit e6c8bb
		print "\t", $ns->nsdname, "\n";
Packit e6c8bb
	}
Packit e6c8bb
	print "\n";
Packit e6c8bb
		
Packit e6c8bb
	$res->nameservers(map  { $_->nsdname }
Packit e6c8bb
			  grep { $_->type eq "NS" }
Packit e6c8bb
			  $nspack->answer);
Packit e6c8bb
Packit e6c8bb
	my @zone = $res->axfr($domain, $class);
Packit e6c8bb
	unless (@zone) {
Packit e6c8bb
		warn "Zone transfer failed: ", $res->errorstring, "\n";
Packit e6c8bb
		return;
Packit e6c8bb
	}
Packit e6c8bb
Packit e6c8bb
	print "checking PTR records\n";
Packit e6c8bb
	check_ptr($domain, $class, @zone);
Packit e6c8bb
	print "\n";
Packit e6c8bb
Packit e6c8bb
	print "checking NS records\n";
Packit e6c8bb
	check_ns($domain, $class, @zone);
Packit e6c8bb
	print "\n";
Packit e6c8bb
Packit e6c8bb
	print "checking MX records\n";
Packit e6c8bb
	check_mx($domain, $class, @zone);
Packit e6c8bb
	print "\n";
Packit e6c8bb
Packit e6c8bb
	print "checking CNAME records\n";
Packit e6c8bb
	check_cname($domain, $class, @zone);
Packit e6c8bb
	print "\n";
Packit e6c8bb
Packit e6c8bb
	if ($opt_r) {
Packit e6c8bb
		print "checking subdomains\n\n";
Packit e6c8bb
		my %subdomains;
Packit e6c8bb
		foreach (grep { $_->type eq "NS" and $_->name ne $domain } @zone) {
Packit e6c8bb
			$subdomains{$_->name} = 1;
Packit e6c8bb
		}
Packit e6c8bb
		foreach (sort keys %subdomains) {
Packit e6c8bb
			check_domain($_, $class);
Packit e6c8bb
		}
Packit e6c8bb
	}
Packit e6c8bb
}
Packit e6c8bb
Packit e6c8bb
sub check_ptr {
Packit e6c8bb
	my ($domain, $class, @zone) = @_;
Packit e6c8bb
	my $res = Net::DNS::Resolver->new;
Packit e6c8bb
	my $rr;
Packit e6c8bb
	foreach $rr (grep { $_->type eq "A" } @zone) {
Packit e6c8bb
		my $host = $rr->name;
Packit e6c8bb
		my $addr = $rr->address;
Packit e6c8bb
		my $ans = $res->send($addr, "A", $class);
Packit e6c8bb
		print "\t$host ($addr) has no PTR record\n"
Packit e6c8bb
			if ($ans->header->ancount < 1);
Packit e6c8bb
	}
Packit e6c8bb
}
Packit e6c8bb
Packit e6c8bb
sub check_ns {
Packit e6c8bb
	my ($domain, $class, @zone) = @_;
Packit e6c8bb
	my $res = Net::DNS::Resolver->new;
Packit e6c8bb
	my $rr;
Packit e6c8bb
	foreach $rr (grep { $_->type eq "NS" } @zone) {
Packit e6c8bb
		my $ans = $res->send($rr->nsdname, "A", $class);
Packit e6c8bb
		print "\t", $rr->nsdname, " has no A record\n"
Packit e6c8bb
			if ($ans->header->ancount < 1);
Packit e6c8bb
	}
Packit e6c8bb
}
Packit e6c8bb
Packit e6c8bb
sub check_mx {
Packit e6c8bb
	my ($domain, $class, @zone) = @_;
Packit e6c8bb
	my $res = Net::DNS::Resolver->new;
Packit e6c8bb
	my $rr;
Packit e6c8bb
	foreach $rr (grep { $_->type eq "MX" } @zone) {
Packit e6c8bb
		my $ans = $res->send($rr->exchange, "A", $class);
Packit e6c8bb
		print "\t", $rr->exchange, " has no A record\n"
Packit e6c8bb
			if ($ans->header->ancount < 1);
Packit e6c8bb
	}
Packit e6c8bb
}
Packit e6c8bb
Packit e6c8bb
sub check_cname {
Packit e6c8bb
	my ($domain, $class, @zone) = @_;
Packit e6c8bb
	my $res = Net::DNS::Resolver->new;
Packit e6c8bb
	my $rr;
Packit e6c8bb
	foreach $rr (grep { $_->type eq "CNAME" } @zone) {
Packit e6c8bb
		my $ans = $res->send($rr->cname, "A", $class);
Packit e6c8bb
		print "\t", $rr->cname, " has no A record\n"
Packit e6c8bb
			if ($ans->header->ancount < 1);
Packit e6c8bb
	}
Packit e6c8bb
}