1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
|
#!/usr/local/bin/perl -w
# $Id: axfr,v 1.5 1997/10/02 05:00:51 mfuhr Exp $
=head1 NAME
axfr - Perform a DNS zone transfer
=head1 SYNOPSIS
B<axfr> S<[ B<-fqs> ]> S<[ B<-D> I<directory> ]> S<[ B<@>I<nameserver> ]>
I<zone>
=head1 DESCRIPTION
B<axfr> performs a DNS zone transfer, prints each record to the standard
output, and stores the zone to a file. If the zone has already been
stored in a file, B<axfr> will read the file instead of performing a
zone transfer.
Zones will be stored in a directory hierarchy. For example, the
zone transfer for foo.bar.com will be stored in the file
$HOME/.dns-zones/com/bar/foo/axfr. The directory can be changed
with the B<-D> option.
=head1 OPTIONS
=over 4
=item B<-f>
Force a zone transfer, even if the zone has already been stored
in a file.
=item B<-q>
Be quiet -- don't print the records from the zone.
=item B<-s>
Perform a zone transfer if the SOA serial number on the nameserver
is different than the serial number in the zone file.
=item B<-D> I<directory>
Store zone files under I<directory> instead of the default directory
(see L<"FILES">).
=item B<@>I<nameserver>
Query I<nameserver> instead of the default nameserver.
=back
=head1 FILES
=over 4
=item B<$HOME/.dns-zones>
Default directory for storing zone files.
=back
=head1 AUTHOR
Michael Fuhr <mfuhr@dimensional.com>
=head1 SEE ALSO
L<perl(1)>, L<check_soa>, L<check_zone>, L<mresolv>, L<mx>, L<perldig>,
L<Net::DNS>
=cut
use strict;
use vars qw($opt_f $opt_q $opt_s $opt_D);
use File::Basename;
use Getopt::Std;
use Net::DNS;
use Storable;
#------------------------------------------------------------------------------
# Read any command-line options and check syntax.
#------------------------------------------------------------------------------
getopts("fqsD:");
die "Usage: ", basename($0), " [ -fqs ] [ -D directory ] [ \@nameserver ] zone\n"
unless (@ARGV >= 1) && (@ARGV <= 2);
#------------------------------------------------------------------------------
# Get the nameserver (if specified) and set up the zone transfer directory
# hierarchy.
#------------------------------------------------------------------------------
my $nameserver = ($ARGV[0] =~ /^@/) ? shift @ARGV : "";
$nameserver =~ s/^@//;
my $zone = shift @ARGV;
my $basedir = defined $opt_D ? $opt_D : $ENV{"HOME"} . "/.dns-zones";
my $zonedir = join("/", reverse(split(/\./, $zone)));
my $zonefile = $basedir . "/" . $zonedir . "/axfr";
# Don't worry about the 0777 permissions here - the current umask setting
# will be applied.
unless (-d $basedir) {
mkdir($basedir, 0777) or die "can't mkdir $basedir: $!\n";
}
my $dir = $basedir;
my $subdir;
foreach $subdir (split(m#/#, $zonedir)) {
$dir .= "/" . $subdir;
unless (-d $dir) {
mkdir($dir, 0777) or die "can't mkdir $dir: $!\n";
}
}
#------------------------------------------------------------------------------
# Get the zone.
#------------------------------------------------------------------------------
my $res = new Net::DNS::Resolver;
$res->nameservers($nameserver) if $nameserver;
my(@zone, $zoneref);
if (-e $zonefile && !defined $opt_f) {
$zoneref = retrieve($zonefile);
die "couldn't retrieve zone from $zonefile: $!\n"
unless defined $zoneref;
#----------------------------------------------------------------------
# Check the SOA serial number if desired.
#----------------------------------------------------------------------
if (defined $opt_s) {
my($serial_file, $serial_zone);
my $rr;
foreach $rr (@$zoneref) {
if ($rr->type eq "SOA") {
$serial_file = $rr->serial;
last;
}
}
die "no SOA in $zonefile\n" unless defined $serial_file;
my $soa = $res->query($zone, "SOA");
die "couldn't get SOA for $zone: ", $res->errorstring, "\n"
unless defined $soa;
foreach $rr ($soa->answer) {
if ($rr->type eq "SOA") {
$serial_zone = $rr->serial;
last;
}
}
if ($serial_zone != $serial_file) {
$opt_f = 1;
}
}
}
else {
$opt_f = 1;
}
if (defined $opt_f) {
@zone = $res->axfr($zone);
die "couldn't transfer zone: ", $res->errorstring, "\n"
unless defined @zone;
store \@zone, $zonefile or die "couldn't store zone to $zonefile: $!\n";
$zoneref = \@zone;
}
#------------------------------------------------------------------------------
# Print the records in the zone.
#------------------------------------------------------------------------------
unless ($opt_q) {
my $rr;
foreach $rr (@$zoneref) {
$rr->print;
}
}
|