use IO::File;
use Data::Dumper;
-use vars qw($quis $mode
+use vars qw($quis
+ $mode $doall
$etcfile $where
$debug $needglue $localonly $verbosity);
$quis= $0; $quis =~ s,.*/,,;
$mode= '';
+$doall= 0;
$etcfile= "/etc/bind/chiark-conf-gen.zones";
$where= '<built-in>';
$debug= 0;
if (s/^\-\-//) {
last if m/^$/;
if (m/^(yes|no|force)$/) { m/^./; $mode= $&; }
+ elsif (m/^all$/) { $doall=1; }
elsif (m/^config$/) { $etcfile= loarg(); $where= '--config option'; }
elsif (m/^glueless$/) { $needglue--; }
elsif (m/^localonly$/) { $localonly=1; }
last if m/^$/;
while (m/^./) {
if (s/^[ynf]//) { $mode=$&; }
+ elsif (s/^A//) { $doall=1; }
elsif (s/^C//) { $etcfile= soarg(); $where= '-C option'; }
elsif (s/^D//) { $debug++; }
elsif (s/^g//) { $needglue--; }
@self= split /\s+/, $2;
@self_ns= @self if $1 ne '-soa';
@self_soa= @self if $1 ne '-ns';
- } elsif (m/^primary\-dir\s+(\S+)((?:\s+(\S+))?:\s+(\S+))?$/) {
- ($dir, $prefix, $suffix) = (qualify($1),$2,$3);
+ } elsif (m/^self\-addr\s+([0-9. \t]+)/) {
+ @self_addr= split /\s+/, $2;
+ } elsif (m/^primary\-dir([*?]?)\s+(\S+)((?:\s+(\S+))?:\s+(\S+))?$/) {
+ ($mod, $dir, $prefix, $suffix) = ($1,qualify($2),$3,$4);
$suffix= '_db' if !defined $suffix;
$prefix= '' if !defined $prefix;
opendir D, $dir or cfg_fail("open primary-dir $dir:\n $!");
next unless substr($_,0,$lprefix) eq $prefix;
next unless substr($_,length($_)-$lsuffix) eq $suffix;
$z= substr($_,$lprefix,length($_)-($lprefix+$lsuffix));
- zone_conf($z,'primary',"$dir/$_");
+ zone_conf($z,'primary',"p$mod","$dir/$_");
}
closedir D or cfg_fail("close primary-dir $dir:\n $!");
- } elsif (m/^primary\s+(\S+)\s+(\S+)$/) {
- zone_conf($1,'primary',qualify($2));
- } elsif (m/^secondary\s+(\S+)\s+([0-9.\t]+)$/) {
- zone_conf($1,'secondary','',$2);
- } elsif (m/^stealth\s+(\S+)\s+([0-9. \t]+)$/) {
- zone_conf($1,'stealth','',split /\s+/, $2);
+ } elsif (m/^primary([*?]?)\s+(\S+)\s+(\S+)$/) {
+ zone_conf($2,'primary',"p$1",qualify($3));
+ } elsif (m/^published([*?]?)\s+(\S+)\s+([0-9.\t]+)$/) {
+ zone_conf($2,'published',"s$1",'',$3);
+ } elsif (m/^stealth([*?]?)\s+(\S+)\s+([0-9. \t]+)$/) {
+ zone_conf($2,'stealth',"u$1",'',split /\s+/, $3);
} elsif (m/^slave\-dir\s+(\S+)((?:\s+(\S+))?:\s+(\S+))?$/) {
($slave_dir, $slave_prefix, $slave_suffix) = (qualify($1),$2,$3);
} elsif (m/^output\s+bind8\+(\S+)$/) {
return $i;
}
-sub zone_conf ($$@) {
- my ($zone,$style,$file,@servers) = @_;
+sub zone_conf ($$$@) {
+ my ($zone,$style,$s,$file,@servers) = @_;
$file= qualify("$slave_dir/$slave_prefix".$zone.$slave_suffix)
unless length $file;
if (!length $output) {
}
cfg_fail("redefined zone $zone") if exists $zone_cfg{$zone};
$zone_cfg{$zone}{'file'}= $file;
- $zone_cfg{$zone}{'style'}= $style;
+ $zone_cfg{$zone}{'style_p'}= $style.$mod;
+ $zone_cfg{$zone}{'s'}= $s; # p)rimary s)econdary u)npublished f)oreign
$zone_cfg{$zone}{'servers'}= [ @servers ];
$zone_cfg{$zone}{'self_soa'}= [ @self_soa ];
$zone_cfg{$zone}{'self_ns'}= [ @self_ns ];
foreach $zone (@zones) {
$cfg= $zone_cfg{$zone} || {
- 'style' => 'foreign',
+ 'style_p' => 'foreign',
+ 's' => 'f',
'servers' => [ ],
};
- progress(sprintf "%-40s %s", $zone, $$cfg{'style'});
- if ($check) {
+ progress(sprintf "%-40s %s", $zone, $$cfg{'style_p'});
+ if ($check && ($doall || $cfg{'s'} !~ m/\?/)) {
eval {
- if ($localonly && $cfg->{'style'} eq 'foreign') {
+ if ($localonly && $cfg->{'s'} =~ m/f/) {
zone_warning("foreign zone specified with -l");
- } elsif ($localonly && $cfg->{'style'} ne 'primary') {
+ } elsif ($cfg->{'s'} =~ m/\*/ ||
+ ($localonly && $cfg->{'s'} !~ m/p/)) {
zone_check_local();
} else {
zone_check_full();
$dig_rdata,
"$dig_owner, in glue from $ww",
1, 0;
- $addr_is_ok{$dig_rdata}= "$dig_owner (NS [$uaddr])"
- if $cfg->{'style'} eq 'stealth';
+ zone_server_addr($dig_rdata,$dig_owner,"NS [$uaddr]");
push @{ $s2g{$dig_owner} }, $dig_rdata;
}
},
push @{ $delgs_or_auths->{$s} }, $ww;
}
+sub zone_server_addr ($$$$) {
+ my ($addr,$name,$ww,$is_soa) = @_;
+ $addr_is_ok{$addr}= "$name ($ww)"
+ if $is_soa || $cfg{'s'} =~ m/u/;
+ zone_warning("configured as stealth but we [$addr]".
+ " are published ($name $ww)")
+ if $cfg->{'s'} =~ m/u/ && grep { $_ eq $addr } @self_addr;
+}
+
sub zone_check_soa ($$) {
my ($uaddr,$ww) = @_;
my ($lame,$origin,$got,$rcode,@soa_addrs,$soa_addr);
push @{ $soas{$got} }, $ww;
($rcode,@soa_addrs)= lookup($origin,'a','0');
foreach $soa_addr (@soa_addrs) {
- $addr_is_ok{$soa_addr}= "$origin (SOA [$uaddr])";
+ zone_server_addr($addr,$origin,"SOA [$uaddr]"
push @to_check,
$soa_addr,
"$origin, SOA ORIGIN from $ww";
zone_consistency_set("glue for $h", $glue{$h});
}
zone_consistency_set("SOA ORIGIN and SERIAL",\%soas);
- if ($cfg->{'style'} eq 'primary') {
+ if ($cfg->{'s'} =~ m/p/) {
foreach $org_ser (keys %soas) {
$org_ser =~ m/^(\S+) \d+$/ or die "$org_ser ?";
$origin= $1;
next if grep { $_ eq $origin } @self_soa;
- zone_warning("our name(s) @self_soa not in SOA ORIGIN $origin,".
+ zone_warning("our name (@self_soa) not in SOA ORIGIN $origin,".
" eg from ".((values %{ $soas{$org_ser} })[1]));
}
}
sub zone_servers_simplefind () {
my ($rcode,@nsnames,$ns,@soas,$origin);
- if ($cfg->{'style'} eq 'stealth') {
- ($rcode,@nsnames)= lookup($zone,'ns-','0');
- foreach $ns (@nsnames) { zone_server_simple($ns,'NS'); }
- }
+
+ ($rcode,@nsnames)= lookup($zone,'ns-','0');
+ foreach $ns (@nsnames) { zone_server_simple($ns,'NS'); }
+
($rcode,@soas)= lookup($zone,'soa','0');
die "multiple SOA RRs in set! @soas ?" if @soas!=1;
$soas[0] =~ m/^(\S+)\s/ or die "SOA ? $_";
my ($name,$why) = @_;
my ($rcode,@addrs,$addr);
($rcode,@addrs)= lookup($name,'a','0');
- foreach $addr (@addrs) { $addr_is_ok{$addr}= "$name ($why)"; }
+ foreach $addr (@addrs) { zone_server_addr($addr,$name,$why); }
}
#-------------------- outputting
};
END
$zone,
- $$cfg{'style'} eq 'primary' ? 'master' : 'slave',
+ $$cfg{'s'} =~ m/p/ ? 'master' : 'slave',
$$cfg{'file'});
}
die "domain $_ ?" unless m/^[0-9a-z]/i;
return lc $_;
}
-
-__DATA__
-
-
-
-
-sub lookup1 ($$) {
- my ($type,$domain) = @_;
- my (@result)= lookup($type,$domain);
- @result==1 or die "$quis: lookup -t$type $domain gave more than one RR\n";
- return $result[0];
-}
-
-sub check () {
- return unless $check;
- eval {
- $soa= lookup1('soa',$zone);
- $soa_origin=$soa; $soa_origin =~ s/ .*//;
- $soa_origin_addr= lookup1('a',$soa_origin);
-
- @zone_ns= lookup('ns-',$zone);
-
- @ok_sources= ($soa_origin_addr);
- $ok_sources_descr= "SOA ORIGIN $soa_origin [$soa_origin_addr]";
-
- if ($style eq 'unoff' || $style eq 'backup') {
- for $zone_ns (@zone_ns) {
- @zone_ns_addrs= lookup('a',$zone_ns);
- push @ok_sources, @zone_ns_addrs;
- $ok_sources_descr.= ", NS $zone_ns [@zone_ns_addrs]";
- }
- }
-
- for $server (@servers) {
- grep { $server eq $_ } @ok_sources
- or warn "secondarying from $server which is not ".
- "$ok_sources_desc\n";
- }
-
- if ($style eq 'secondary') {
- grep { $zone_ns=$_, grep { $myname eq $_ } @mynames } @zone_n
- or warn "supposedly published secondary but we ".
- "(@mynames) are not published ($@zone_ns)\n";
- }
- }
- check_after_eval();
-
-
-
-# $superzone= $zone; $superzone =~ s/^[^\.]+\.//;
-# @super_ns= lookup('ns-',$zone);
- }
-
- eval {
-
- for $super_ns (@super_ns) {
- @deleg_ns= ();
- open DIG, "dig @$super_ns. -t ns +norecurse $zone."
- or die "$quis: fork for dig:\n $!\n";
- while (<DIG>) {
-
-
- split /\n/, lookup("
-
- case "$style" in
- secondary|backup)
- if [ $meadvert = 0 ]
- then
- warning "$myname unlisted NS $nsnames"
- fi
- ;;
- unoff)
- if $meadvert = 0
- then
- warning "$myname advertised NS $nsnames"
- fi
- ;;
- esac
-
- addrs=''
- for ns in $names
- do
- set -e; a="`host -t a \"$ns\".`"; set +e
- taddrs="`echo \" $a\" | expand | sed -n '
- 1s/^ //
- s/^[^ ][^ ]* *A *\([0-9][.0-9]*\)/\1/p
- '`"
- equlines "A $ns" "$a" "$taddrs"
- addrs="$addrs $taddrs"
- done
- fi
-
- cat <<END
-zone "$zone" {
- type slave;
- file "$file";
- masters {
-END
- for server in $servers
- do
- echo " $server;"
- if $check
- then
- notfound=1
- for addr in $addrs
- do
- if [ "x$addr" = "x$server" ]
- then
- notfound=0
- fi
- done
- if $notfound
- then
- warning "server $server? but $rectype $names" $addrs
- fi
- fi
- done
- cat <<END
- };
-};
-END
- hostfirstwarn=1
- if $hostdelg
- then
- checkhostout -C
- fi
- if $hostzone
- then
- checkhostout -val localhost
- fi
-done
-endfile
-
-
-chdir "$base/primary" or die "$quis: chdir $base/primary:\n $!";
-beginfile('primary.zones');
-
-for $f (<*_db>) {
- $zone= $f; $zone =~ s/_db$//;
-
-
-for f in $zones
-do
- zone="`echo $f | sed -e 's/_db$//'`"
-END
-done
-endfile
-
-
-
-sub beginfile ($) {
- $currentfile= $_[0];
- $currentfile_opened= $install ? "$conf/$currentfile.new" : "/dev/null";
- open CFF, "> $toopen" or die "$quis: begin $currentfile_opened:\n $!\n";
-}
-
-endfile () {
- close CFF or die "$quis: close $currentfile_opened:\n $!\n";
- push @files, $currentfile;
-}
-
-sub installfiles () {
- return unless $install;
- chdir $conf or die "$quis: chdir $conf:\n $!\n";
- for $f (@files) {
- rename "$f.new", $f or die "$quis: install new $f:\n $!\n";
- }
-}
-
-warning () {
- echo >&2 "$zone $style: $*"
- warnings=$[$warnings+1]
-}
-
-equlines () {
- if [ "x`echo \" $2\" | wc -l`" != "x`echo \" $3\" | wc -l`" ]
- then
- warning "$1 >$2|$3<"
- fi
-}
-
-checkhostout () {
- set +e
- hostout="`host $1 \"$zone\" 2>&1 >/dev/null $2 | egrep -v \
-'^ \!\!\! .* SOA primary .* is not advertised via NS$'`"
- set -e
- if [ "x$hostout" = x ]; then return; fi
- if $hostfirstwarn
- then
- warning "warnings from host:"
- hostfirstwarn=0
- fi
- echo >&2 "$hostout"
-}
-
-progress () {
- if $progress
- then
- echo -n "$zone $style " >&2
- echo -ne '\r' >&2
- fi
-}
-
-myname=''
-
-if [ $warnings != 0 ]
-then
- echo >&2 "$warnings warnings "
-fi
-
-installfiles
.SS ADDITIONAL OPTIONS
.TP
+.BR \-A | \-\-all
+Checks even zones known to be broken. Ie, ignores the
+.B ?
+zone style modifier in the configuration.
+.TP
\fB\-C\fP|\-\-config \fIconfig\-file\fP
Use
.I config\-file
.TP
.BR \-l | \-\-local
Only checks for mistakes which are the responsibility of the local
-administrator. This means that for secondary and stealth zones we
-only check that we're slaving from the right place. For primary zones
-all checks are still done. It is a mistake to specify
+administrator (to fix or get fixed). This means that for published
+and stealth zones we only check that we're slaving from the right
+place and that any names and addresses for ourself are right. For
+primary zones all checks are still done. It is a mistake to specify
.B \-l
with foreign zones (zones supplied explictly on the command line but
not relevant to the local server); doing so produces a warning.
with the same set of names.
.TP
\fBslave\-dir\fP \fIdirectory\fP [[\fIprefix\fP] \fIsuffix\fP]
-Specifies the directory in which slave (secondary and stealth)
+Specifies the directory in which slave (published and stealth)
zonefiles should be placed. The default
.I directory
is
.SS ZONE DIRECTIVES
These directives specify one or more zones.
.TP
-\fBprimary\fP \fIzone\fP \fIfilename\fP
+.BR primary [ * | ? "] \fIzone filename\fP"
Specifies that this server is supposed to be the primary nameserver
for
.I zone
and that the zone data is to be found in
.IR filename .
.TP
-\fBprimary-dir\fP \fIdirectory\fP [[\fIprefix\fP] \fIsuffix\fP]
+.BR primary\-dir [ * | ? "] \fIdirectory\fP [[\fIprefix\fP] \fIsuffix\fP]"
Search
.I directory
for files whose names match the glob pattern
.I prefix
is empty.
.TP
-\fBsecondary\fP \fIzone\fP \fIorigin\-addr\fP
-Specifies that this server is supposed to be a published secondary for
-the zone in question.
+.BR published [ * | ? "] \fIzone origin\-addr\fP"
+Specifies that this server is supposed to be a published slave
+nameserver for the zone in question.
.TP
-\fBstealth\fP \fIzone\fP \fIserver\-addr ...\fP
+.BR stealth [ * | ? "] \fIzone server\-addr ...\fP"
Specifies that this server is supposed to be an unpublished secondary
(aka stealth secondary) for the zone in question.
+.SS ZONE DIRECTIVE STYLE MODIFIERS
+Each of the zone directives may optionally be followed by one of the
+following characters:
+.TP
+.B *
+Indicates that the zone is unofficial, ie that it is not delegated as
+part of the global Internet DNS and that no attempt should be made to
+find the superzone and check delegations. Note that unofficial, local
+zones should be created with caution. They should be in parts of the
+namespace which are reserved for private use, or belong to the actual
+zone maintainer.
+.TP
+.B ?
+Indicates that the zone is known to be broken and no checks should be
+carried out on it, unless the
+.B \-A
+option is specified.
.SS OTHER DIRECTIVES
.TP
\fBinclude\fP \fIfile\fP
.B primary
zones, the SOA origin should be one of the names specified with
.BR self\-soa " (or " self ). For
-.B secondary
+.B published
zones, the address should be that of the SOA origin. For
.B stealth
zones, the address should be that of the SOA origin or one of the