File: axfr

package info (click to toggle)
libnet-dns-perl 0.12-2
  • links: PTS
  • area: main
  • in suites: slink
  • size: 452 kB
  • ctags: 266
  • sloc: perl: 4,047; makefile: 52; sh: 17
file content (186 lines) | stat: -rwxr-xr-x 4,493 bytes parent folder | download | duplicates (2)
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;
	}
}