+}
+
+sub zone_check_nsrrset ($$$$) {
+ my ($uaddr,$ww, $is_auth, $glueless_ok) = @_;
+ my (@s, $s, %s2g, @glue, $glue, $delgs_or_auths, $wwn);
+ verbose("checking delegation by $ww");
+ dig(sub {
+ if ($dig_type eq 'ns' && $dig_owner eq $zone) {
+ $s2g{lc $dig_rdata} = [ ];
+ } elsif ($dig_type eq 'a' && exists $s2g{$dig_owner}) {
+ $wwn= "in glue from $ww";
+ push @to_check, $dig_rdata, "$dig_owner, $wwn", 1, 0;
+ zone_server_addr($dig_rdata,$dig_owner,$wwn,"NS [$uaddr]",0);
+ push @{ $s2g{$dig_owner} }, $dig_rdata;
+ }
+ },
+ $zone,'ns',$uaddr);
+ if (!%s2g) { zone_warning("unable to find NS RRset at $ww"); return; }
+ elsif (keys %s2g == 1) { zone_warning("only one nameserver at $ww"); }
+ @s= sort keys %s2g;
+ foreach $s (@s) {
+ @glue= @{ $s2g{$s} };
+ if (!@glue) {
+ zone_warning("glueless NS $s,".
+ ($needglue<=1 ? " (eg)" : "").
+ " from $ww")
+ unless $glueless_ok || !$needglue ||
+ ($needglue<=1 && $warned_glueless{$s}++);
+ next;
+ }
+ $glue= join ' ', sort @glue;
+ push @{ $glue{$s}{$glue} }, $ww;
+ }
+ $s= join ' ', @s;
+ $delgs_or_auths= $is_auth ? \%auths : \%delgs;
+ push @{ $delgs_or_auths->{$s} }, $ww;
+}
+
+sub zone_server_addr ($$$$$) {
+ my ($addr,$name,$ww,$wwq,$is_soa) = @_;
+ $addr_is_ok{$addr}= "$name ($wwq)"
+ if $is_soa || $cfg->{'s'} =~ m/u/;
+ zone_warning("configured as stealth but we [$addr]".
+ " are published ($name $wwq)")
+ if $cfg->{'s'} =~ m/u/ && grep { $_ eq $addr } @self_addr;
+ zone_warning("forbidden nameserver address [$addr] $name ($wwq)")
+ if grep { $_ eq $addr } @forbid_addr;
+
+ my ($name_is_self, $addr_is_self);
+ $name_is_self= grep { $_ eq $name }
+ @{ $cfg->{$is_soa ? 'self_soa' : 'self_ns'} };
+ $addr_is_self= grep { $_ eq $addr }
+ @{ $cfg->{'self_addr'} };
+ if ($name_is_self && !$addr_is_self) {
+ zone_warning("our name $name with wrong address [$addr], (eg) $ww")
+ unless $warned_nameaddr{$name}{$addr}++;
+ } elsif (!$name_is_self && $addr_is_self) {
+ zone_warning(($is_soa ? "SOA ORIGIN maps to" : "allegedly served by").
+ " us [$addr] with wrong name $name, (eg) $ww")
+ unless $warned_nameaddr{$name}{$addr}++;
+ }
+ $delg_to_us=1 if $name_is_self;
+}
+
+sub zone_check_soa ($$$) {
+ my ($uaddr,$ww,$wwq) = @_;
+ my ($lame,$origin,$got,$rcode,@soa_addrs,$soa_addr,$wwn);
+ verbose("checking service at $wwq");
+ $lame= 'dead or lame';
+ dig(sub {
+ if ($dig_type eq 'flags:') {
+ $lame= $dig_rdata =~ m/ aa / ? '' : 'lame';
+ } elsif ($dig_type eq 'soa' && $dig_owner eq $zone && !$lame) {
+ die "several SOAs ? $ww" if defined $origin;
+ $got= $dig_rdata;
+ $got =~ m/^(\S+) \d+/ or die "$got ?";
+ $origin= $1;
+ }
+ },
+ $zone,'soa',$uaddr);
+ $lame= 'broken' if !$lame && !defined $origin;
+ if ($lame) { zone_warning("$lame server $ww"); return; }
+ push @{ $soas{$got} }, $ww;
+ ($rcode,@soa_addrs)= lookup($origin,'a','0');
+ foreach $soa_addr (@soa_addrs) {
+ $wwn= "SOA ORIGIN from $ww";
+ zone_server_addr($soa_addr,$origin,$wwn,"SOA [$uaddr]",1);
+ push @to_check, $soa_addr, "$origin, $wwn";
+ }