chiark / gitweb /
Can suppress multiple glue warnings
[chiark-utils.git] / scripts / named-conf
index 1c20660a77c747f948511830c8adff7f6d9b4dda..d863dfc545f404833004f16cb6ed24a1a4625897 100755 (executable)
@@ -4,14 +4,19 @@ use strict;
 use IO::File;
 use Data::Dumper;
 
-use vars qw($etcfile $where);
-$etcfile= "/etc/bind/chiark-conf-gen.zones";
-$where= '<built-in>';
+use vars qw($quis $mode
+           $etcfile $where
+           $debug $needglue $localonly $verbosity);
+
+$quis= $0; $quis =~ s,.*/,,;
 
-use vars qw($mode $verbosity $debug);
 $mode= '';
-$verbosity= 1;
+$etcfile= "/etc/bind/chiark-conf-gen.zones";
+$where= '<built-in>';
 $debug= 0;
+$needglue= 2;
+$localonly= 0;
+$verbosity= 1;
 
 use vars qw($dig_owner $dig_type $dig_rdata);
 
@@ -19,20 +24,24 @@ while (@ARGV && $ARGV[0] =~ m/^\-/) {
     $_= shift @ARGV;
     if (s/^\-\-//) {
        last if m/^$/;
-       if (m/^quiet$/) { $verbosity=0; }
-       elsif (m/^verbose$/) { $verbosity=2; }
-       elsif (m/^(yes|no|force)$/) { m/^./; $mode= $&; }
+       if (m/^(yes|no|force)$/) { m/^./; $mode= $&; }
        elsif (m/^config$/) { $etcfile= loarg(); $where= '--config option'; }
+       elsif (m/^glueless$/) { $needglue--; }
+       elsif (m/^localonly$/) { $localonly=1; }
+       elsif (m/^quiet$/) { $verbosity=0; }
+       elsif (m/^verbose$/) { $verbosity=2; }
        else { usageerr("unknown option --$_"); }
     } else {
        s/^\-//;
        last if m/^$/;
        while (m/^./) {
            if (s/^[ynf]//) { $mode=$&; }
-           elsif (s/^v//) { $verbosity=2; }
-           elsif (s/^q//) { $verbosity=0; }
-           elsif (s/^D//) { $debug++; }
            elsif (s/^C//) { $etcfile= soarg(); $where= '-C option'; }
+           elsif (s/^D//) { $debug++; }
+           elsif (s/^g//) { $needglue--; }
+           elsif (s/^l//) { $localonly=1; }
+           elsif (s/^q//) { $verbosity=0; }
+           elsif (s/^v//) { $verbosity=2; }
            else { usageerr("unknown option -$&"); }
        }
     }
@@ -41,6 +50,8 @@ while (@ARGV && $ARGV[0] =~ m/^\-/) {
 sub loarg() { usageerr("missing option value") if !@ARGV; return shift @ARGV; }
 sub soarg() { my ($rv); $rv=$_; $_=''; return length $rv ? $rv : loarg(); }
 
+usageerr("-g may be specified at most twice") if $needglue<0;
+usageerr("-D may be specified at most twice") if $debug>2;
 usageerr("must specify either -f|-y|-n or zones (and not both)")
     if !!$mode == !!@ARGV;
 
@@ -90,10 +101,11 @@ debug_dump('@zone_cfg_list %zone_cfg');
 process_zones($mode ? @zone_cfg_list : @ARGV);
 debug_dump('%output_contents');
 
+exit 0;
 
 #-------------------- configuration reading
 
-sub cfg_fail ($) { die "$0: $where:\n $_[0]\n"; }
+sub cfg_fail ($) { die "$quis: $where:\n $_[0]\n"; }
 
 sub read_config ($) {
     my ($if) = @_;
@@ -209,11 +221,21 @@ sub process_zones (@) {
            };
        progress(sprintf "%-40s %s", $zone, $$cfg{'style'});
        if ($check) {
-           eval { zone_check() };
+           eval {
+               if ($localonly && $cfg->{'style'} eq 'foreign') {
+                   zone_warning("foreign zone specified with -l");
+               } elsif ($localonly && $cfg->{'style'} ne 'primary') {
+                   zone_check_local();
+               } else {
+                   zone_check_full();
+               }
+           };
            zone_warning("checks failed: $@") if length $@;
        }
        zone_output() if $install;
     }
+    print STDERR "$quis: $warnings warnings\n" or die $!
+       if $warnings;
 }
 
 sub zone_warning ($) {
@@ -232,18 +254,27 @@ use vars qw(%delgs); # $delgs{$nameserver_list} = [ $whosaidandwhy ]
 use vars qw(%auths); # $auths{$nameserver_list} = [ $whosaidandwhy ]
 use vars qw(%glue);  # $glue{$name}{$addr_list} = [ $whosaidandwhy ]
 use vars qw(%soas);  # $soa{"$origin $serial"} = [ $whosaidandwhy ]
-use vars qw(%addr_is_ok);
+use vars qw(%addr_is_ok %gluelesswarned);
 use vars qw(@to_check); # ($addr,$whyask,$is_auth,$glueless_ok, ...)
 use vars qw(@to_check_soa); # ($addr,$whyask, ...)
 
-sub zone_check () {
+sub zone_check_full () {
+    zone_reset();
+    zone_investigate();
+    zone_consistency();
+    zone_servers_ok();
+}
+
+sub zone_reset() {
+    %delgs= %auths= %glue= %soas= %gluelesswarned= %addr_is_ok= ();
+    @to_check= @to_check_soa= ();
+}
+
+sub zone_investigate() {
     my ($super_zone, @super_nsnames,
        $super_ns, @super_ns_addrs, $s, $wa, $is_auth,
        %nsrrset_checked, %soa_checked, $addr, $glueless_ok, $rcode);
 
-    %delgs= %auths= %glue= %soas= %addr_is_ok= ();
-    @to_check= @to_check_soa= ();
-
     $super_zone= $zone;
     for (;;) {
        debug_trace("zone $zone superzone $super_zone");
@@ -279,7 +310,6 @@ sub zone_check () {
            last;
        }
     }
-    zone_consistency();
 }
 
 sub zone_check_nsrrset ($$$$) {
@@ -306,7 +336,9 @@ sub zone_check_nsrrset ($$$$) {
     foreach $s (@s) {
        @glue= @{ $s2g{$s} };
        if (!@glue) {
-           zone_warning("glueless NS $s, from $ww") unless $glueless_ok;
+           zone_warning("glueless NS $s, from $ww")
+               unless $glueless_ok || !$needglue ||
+                      ($needglue<=1 && $gluelesswarned{$s}++);
            next;
        }
        $glue= join ' ', sort @glue;
@@ -346,7 +378,7 @@ sub zone_check_soa ($$) {
 }
 
 sub zone_consistency() {
-    my ($d, $org_ser, $origin, $a, $showok, $h);
+    my ($d, $org_ser, $origin, $a, $h);
     zone_consistency_set('delegations',\%delgs);
     foreach $d (keys %delgs) { delete $auths{$d}; }
     zone_consistency_set('zone nameserver rrset',\%auths);
@@ -363,6 +395,10 @@ sub zone_consistency() {
                         " eg from ".((values %{ $soas{$org_ser} })[1]));
        }
     }
+}
+
+sub zone_servers_ok () {
+    my ($showok);
     if (%addr_is_ok) {
        $showok= 0;
        foreach $a (@{ $cfg->{'servers'} }) {
@@ -388,6 +424,30 @@ sub zone_consistency_set ($%) {
     }
 }
 
+sub zone_check_local () {
+    zone_reset();
+    zone_servers_simplefind();
+    zone_servers_ok();
+}
+
+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,@soas)= lookup($zone,'soa','0');
+    die "multiple SOA RRs in set!  @soas ?" if @soas!=1;
+    $soas[0] =~ m/^(\S+)\s/ or die "SOA ? $_";
+    zone_server_simple(domain_canon($1),'SOA');
+}
+
+sub zone_server_simple ($$) {
+    my ($name,$why) = @_;
+    my ($rcode,@addrs,$addr);
+    ($rcode,@addrs)= lookup($name,'a','0');
+    foreach $addr (@addrs) { $addr_is_ok{$addr}= "$name ($why)"; }
+}
 
 #-------------------- outputting
 
@@ -427,17 +487,17 @@ sub lookup ($$$) {
     debug_trace("lookup ==> (->$okrcodes) $domain $type");
     $h= new IO::Handle;
 
-    defined($c= open $h, "-|") or die "$0: fork adnshost:\n $!\n";
+    defined($c= open $h, "-|") or die "$quis: fork adnshost:\n $!\n";
     if (!$c) {
        exec 'adnshost','-Fi','+Do','+Dt','+Dc','-Cf',"-t$type",
             '-',"$domain.";
-       die "$0: exec adnshost:\n $!\n";
+       die "$quis: exec adnshost:\n $!\n";
     }
     @result= $h->getlines();
-    $h->error and die "$0: read from adnshost:\n $!\n";
+    $h->error and die "$quis: read from adnshost:\n $!\n";
     chomp @result;
     $!=0; $h->close;
-    die "$0: lookup -t$type $domain $okrcodes failed $? $!\n"
+    die "$quis: lookup -t$type $domain $okrcodes failed $? $!\n"
        if $! or $?>6 or index($okrcodes,$?)<0;
     debug_trace("lookup <== $? @result");
     return ($?,@result);
@@ -453,18 +513,18 @@ sub dig (&$$$) {
     debug_trace("dig ==> \@$qaddr $qowner $qtype");
 
     $h= new IO::Handle;
-    defined($c= open $h, "-|") or die "$0: fork dig:\n $!\n";
+    defined($c= open $h, "-|") or die "$quis: fork dig:\n $!\n";
     if (!$c) {
        open STDERR, ">&STDOUT" or die $!;
        exec ('dig',
              '+nodef','+nosea','+nodebug','+norecurse',
              "\@$qaddr",'-t',$qtype,$qowner);
-       die "$0: exec dig:\n $!\n";
+       die "$quis: exec dig:\n $!\n";
     }
     $inmid='';
     for (;;) {
        if (!defined($_= $h->getline())) {
-           $h->error() and die "$0: read from dig:\n $!\n";
+           $h->error() and die "$quis: read from dig:\n $!\n";
            last;
        }
        chomp;
@@ -511,6 +571,7 @@ sub dig (&$$$) {
 sub domain_canon ($) {
     local ($_) = @_;
     s/(.)\.$/$1/;
+    die "domain $_ ?" unless m/^[0-9a-z]/i;
     return lc $_;
 }
 
@@ -522,7 +583,7 @@ __DATA__
 sub lookup1 ($$) {
     my ($type,$domain) = @_;
     my (@result)= lookup($type,$domain);
-    @result==1 or die "$0: lookup -t$type $domain gave more than one RR\n";
+    @result==1 or die "$quis: lookup -t$type $domain gave more than one RR\n";
     return $result[0];
 }
 
@@ -571,7 +632,7 @@ sub check () {
        for $super_ns (@super_ns) {
            @deleg_ns= ();
            open DIG, "dig @$super_ns. -t ns +norecurse $zone."
-               or die "$0: fork for dig:\n $!\n";
+               or die "$quis: fork for dig:\n $!\n";
            while (<DIG>) {
                
 
@@ -647,7 +708,7 @@ done
 endfile
 
     
-chdir "$base/primary" or die "$0: chdir $base/primary:\n $!";
+chdir "$base/primary" or die "$quis: chdir $base/primary:\n $!";
 beginfile('primary.zones');
 
 for $f (<*_db>) {
@@ -666,19 +727,19 @@ endfile
 sub beginfile ($) {
     $currentfile= $_[0];
     $currentfile_opened= $install ? "$conf/$currentfile.new" : "/dev/null";
-    open CFF, "> $toopen" or die "$0: begin $currentfile_opened:\n $!\n";
+    open CFF, "> $toopen" or die "$quis: begin $currentfile_opened:\n $!\n";
 }
 
 endfile () {
-    close CFF or die "$0: close $currentfile_opened:\n $!\n";
+    close CFF or die "$quis: close $currentfile_opened:\n $!\n";
     push @files, $currentfile;
 }
 
 sub installfiles () {
     return unless $install;
-    chdir $conf or die "$0: chdir $conf:\n $!\n";
+    chdir $conf or die "$quis: chdir $conf:\n $!\n";
     for $f (@files) {
-       rename "$f.new", $f or die "$0: install new $f:\n $!\n";
+       rename "$f.new", $f or die "$quis: install new $f:\n $!\n";
     }
 }