-fixme implement --nothing by checking $mode='x';
-fixme implement ~
fixme implement .* on self-soa self-ns
#!/usr/bin/perl -w
$mode $doall $domail
$etcfile $where
$debug $needglue $localonly $repeat $verbosity
- $progress_fh);
+ $progress_fh $warn_fh $modifiers
+ %group2modcmd %group2used);
$quis= $0; $quis =~ s,.*/,,;
$domail= '';
$progress_fh= 'STDOUT';
$warn_fh= 'STDERR';
+$modifiers= '';
use vars qw($dig_owner $dig_type $dig_rdata);
elsif (s/^D//) { $debug++; }
elsif (s/^g//) { $needglue=0; }
elsif (s/^l//) { $localonly=1; }
+ elsif (s/^m(\w+)(\W+)$//) {
+ my ($g,$m) = ($1,$2);
+ $group2modcmd{$g}=$m;
+ usageerr("modifiers $m for group $g: $@") if bad_modifiers($m);
+ }
elsif (s/^q//) { $verbosity--; }
elsif (s/^r//) { $repeat=1; }
elsif (s/^v//) { $verbosity++; }
%output_contents= ();
use vars qw($check $install);
-$check= $mode !~ m/^f/;
+$check= $mode !~ m/^[fx]/;
$install= $mode =~ m/^[yf]/;
read_config($etcfile);
sub read_config ($) {
my ($if) = @_;
- my ($fh,$z,@self,$before,
+ my ($fh,$z,@self,$before,$group,
$mod,$dir,$prefix,$suffix,$subfile,$lprefix,$lsuffix,$zf);
local ($_);
} elsif (m/^forbid\-addr(?:\s+([0-9. \t]+))?/) {
@forbid_addr= defined $1 ? split /\s+/, $1 : ();
} elsif (m,^
- primary\-dir ([\@*?~]*)
+ primary\-dir (\W*)
\s+ (\S+)/([^/ \t]*)
(?: \s+ ([^/ \t]*) (?: (/.+) )?
)?
$,x) {
($mod, $dir, $prefix, $suffix, $subfile) =
($1,qualify($2),$3,$4,$5);
+ cfg_fail("modifiers $mod for directory $dir: $@")
+ if bad_modifiers($mod);
$suffix= '' if !defined $suffix;
$subfile= '' if !defined $subfile;
$suffix= '_db' if !length $suffix && !length $subfile;
zone_conf($z,'primary','p',$mod,$zf);
}
closedir D or cfg_fail("close primary-dir $dir:\n $!");
- } elsif (m/^primary([\@*?~]*)\s+(\S+)\s+(\S+)$/) {
+ } elsif (m/^primary(\W*)\s+(\S+)\s+(\S+)$/) {
zone_conf($2,'primary','p',$1,qualify($3));
- } elsif (m/^published([\@*?~]*)\s+(\S+)\s+([0-9.\t]+)$/) {
+ } elsif (m/^published(\W*)\s+(\S+)\s+([0-9.\t]+)$/) {
zone_conf($2,'published','s',$1,'',$3);
- } elsif (m/^stealth([\@*?~]*)\s+(\S+)\s+([0-9. \t]+)$/) {
+ } elsif (m/^stealth(\W*)\s+(\S+)\s+([0-9. \t]+)$/) {
zone_conf($2,'stealth','u',$1,'',split /\s+/, $3);
+ } elsif (m/^modifiers\s+(\W+)(?:\s+(\w+))$/) {
+ ($mod,$group) = ($1,$2);
+ cfg_fail("modifiers $mod for group $group: $@")
+ if bad_modifiers($mod);
+ if (exists $group2modcmd{$group}) {
+ $mod= $group2modcmd{$group};
+ $group2used{$group}++;
+ }
+ $modifiers= $mod;
} elsif (m/^slave\-dir\s+(\S+)(?:(?:\s+(\S+))\s+(\S+))?$/) {
($slave_dir, $slave_prefix, $slave_suffix) = (qualify($1),$2,$3);
$slave_prefix='' if !defined $slave_prefix;
" \`$_'");
}
}
+ foreach $group (keys %group2modcmd) {
+ next if exists $group2used{$group);
+ cfg_fail("command line specifies modifier group $group".
+ " but missing in configuration file");
+ }
$fh->close or cfg_fail("close config file $if:\n $!");
}
return $i;
}
+sub bad_modifiers ($) {
+ local ($_) = @_;
+ if (!eval {
+ die "bad modifier $&" if m/[^!*\$\@~?]/;
+ die "repeated modifier $1" if m/(.).*$1/;
+ 1;
+ }) {
+ $@ =~ s/\n//;
+ return 1;
+ }
+ return 0;
+}
+
sub zone_conf ($$$$$@) {
my ($zone,$style,$sabbr,$mod,$file,@servers) = @_;
my ($sfx,$aref);
$zone_cfg{$zone}{'where'}= $where;
$zone_cfg{$zone}{'file'}= $file;
$zone_cfg{$zone}{'style_p'}= $style.$mod;
- $zone_cfg{$zone}{'s'}= $sabbr.$mod; # p)rimary s)econdary u)npub f)oreign
+ $zone_cfg{$zone}{'s'}= "$sabbr $mod $modifiers";
+ # p)rimary s)econdary u)npub f)oreign
+ # followed by modifiers, first per-zone, then default
$zone_cfg{$zone}{'servers'}= [ @servers ];
if ($domail) {
length $admin_email && length $mail_state_dir or
$output_contents{$output}= '';
}
-#-------------------- mailing
-
-use vars qw($m_base $m_lastok @m_ok @m_fail
- $m_info $m_time);
-
-sub mail_zone_before () {
- $m_base= $$cfg{'maildir'}.'/'.$zone;
- $m_lastok= '-';
- @m_ok= ();
- @m_fail= ();
-
- for (;;) {
- $m_lock= IO::File "${m_base}_lock", O_RDWR|O_CREAT, 0600
- or die "$quis: create lockfile ${m_base}_lock";
- if (!flock($m_lock, LOCK_EX|LOCK_NB)) {
- <$m_lock> =~ m/^\d+ /;
- die "$quis: $zone: concurrrency? - flock $&$!\n";
- $m_lock->close;
- continue;
- }
- (@s1= $m_lock->fstat) or die "$quis: fstat ${m_base}_lock: $!\n";
- (@s2= stat "${m_base}_lock") or
- die "$quis: stat ${m_base}_lock: $!\n";
- last if ($s1[0] eq $s2[0] && $s1[1] eq $s2[1]);
- $m_lock->close;
- }
- print $m_lock "$$ \n" or $m_lock->flush
- die "$quis: write pid to ${m_base}_lock: $!\n";
-
- if ($m_info= IO::File "${m_base}_info", 'r') {
- $!=0; $_= <INFO>;
- $_ =~ m/\n/ or die "$quis: read ${m_base}_info: $!\n";
- m/^\d+ (\d+|-) ([0-9:]*) ([0-9:]*) / or
- die "$quis: ${m_base}_info malformed\n";
- $m_lastok= $1;
- if ($domail ne 'first') {
- if ($2 eq ':' && $3 eq ':') {
- warn "$quis: $zone: mid/last run, but last".
- "run already done, ignoring zone\n";
- next;
- }
- @m_ok= split /\:/, $2;
- @m_fail= split /\:/, $3;
- }
- } elsif ($! != &ENOENT) {
- die "$quis: open ${m_base}_info: $!\n";
- } elsif ($domail eq 'first') {
- warn "$quis: $zone: first run, but not --mail-first,".
- " ignoring zone\n";
- next;
- }
- if ($domail eq 'first') {
- remove "${m_base}_history" or $!==&ENOENT
- or die "$quis: remove ${m_base}_history: $!\n";
- }
- $progress_fh= $warn_fh= new IO::File "${m_base}_history",'a',0666
- or die "$quis: open ${m_base}_history: $!\n";
- $m_info= new IO::File "${m_base}_info.tmp",'w',0666
- or die "$quis: open ${m_base}_info.tmp: $!\n";
-
- $m_time= time;
- progress(-1, "\n".('-'x70)."\n".ptime($m_time)."\n");
-}
-
-sub mail_zone_after () {
- if ($zone_warnings{$zone}) {
- push @m_fail, $m_time;
- progress(-1,"failed - $zone_warnings{$zone} warnings");
- } else {
- push @m_ok, $m_time;
- $m_lastok= $m_time;
- progress(-1,"everything is fine");
- }
- close $progress_fh or die "$quis: close ${m_base}_history: $!\n";
- $progress_fh= $warn_fh= 'STDERR';
-
- if ($domail eq 'final') {
- if (100*@m_fail <= $$cfg{'mailmwarn'}*(@m_mail + @m_ok)) {
- printf " %-40s ok\n" or die "$quis: mail ok report: $!\n";
- } elsif ($$cfg{'s'} =~ m/\@.*\@/) {
- printf " %-40s mail suppressed\n"
- or die "$quis: mail suppress report: $!\n";
- } else {
- mail_zone_mail();
- }
- }
-
- @m_fail= @m_ok= ('','')
- if $domail eq 'final';
-
- printf $m_info "%s %s %s %s \n",
- $m_time, $m_lastok, join(':',@m_ok), join(':',@m_fail)
- or die "$quis: write new ${m_base}_info: $!\n";
- $m_info->close or die "$quis: close new ${m_base}_info: $!\n";
- rename "${m_base}_info.tmp", "${m_base}_info"
- or die "$quis: install new ${m_base}_info: $!\n";
- $m_lock->close;
-}
-
-
-sub pmail ($) { print $m_m $_[0] or die "$quis: write ${m_base}_mail: $!\n"; }
-sub ptime ($) { my ($time)=@_; return gmtime($time)." GMT ($time)"; }
-
-sub mail_zone_mail () {
- my ($log, $zone_to, $zterr, $c, $r);
- $m_m= new IO::File, "${m_base}_mail", 'w', 0666
- or die "$quis: create ${m_base}_mail: $!\n";
- $zone_to=''; $zterr='';
- if ($$cfg{'s'} !~ m/[\@u]/) {
- eval {
- $_= lookup($zone,'soa','0','problem-addr');
- m/\n\n/ and die "multiple soas\n";
- m/^\S+ (\S.*\@\S+) [0-9 ]+$/ or
- die "bad soa \`$_'\n";
- $zone_to= $1;
- };
- $zterr= $@;
- $zterr =~ s/\n$/;
- }
- pmail <<END
-From: zone check system <$$cfg{'admin'}>
-Subject: $zone - configuration problems report
-END
-;
- pmail("To: ");
- pmail("SOA MNAME for $zone <$zone_to>\nCC: ")
- if length($zone_to);
- pmail($$cfg{'admin'}."\n\n");
- pmail <<END
-You are receiving this mail because your email address is listed
-in the SOA (Start Of Authority) record for $zone
-in the DNS, which means you are supposedly the DNS administrator
-responsible for this zone. This report is generated automatically
-on behalf of $$cfg{'admin'}, who
-is the administrator for a server for the zone. Please contact
-them if you have any problem with this report.
-
-END
-if length $zone_to;
- pmail <<END
-Sent to $$cfg{'admin'} since SOA MNAME unavailable:
-$zterr
-
-END
-if length $zterr;
- pmail <<END
-The zone has had configuration errors or persistent operational
-problems during recent checks. See the logs below for details.
-
-Check history for $zone:
-END
-;
- if ($m_lastok ne '-') {
- pmail(" Last seen to be fine: ".ptime($m_lastok)."\n");
- } else {
- pmail(" No record of this zone ever being fine.\n");
- }
- for $t (@m_fail) {
- pmail(" Errors/warnings at: ".ptime($t)."\n");
- }
- for $t (@m_ok) {
- pmail(" Everything in order at: ".ptime($t)."\n");
- }
- pmail("\n");
- $log= new IO::File "${m_base}_history",'r'
- or die "$quis: reopen ${m_base}_history: $!";
- undef $/;
- pmail($log);
- $/= "\n";
- $log->error && $log->close
- or die "$quis: reread or close ${m_base}_log: $!";
- $m_m->close or die "$quis: close ${m_base}_mail: $!\n";
- $m_m= new IO::File "${m_base}_mail"
- or die "$quis: reopen ${m_base}_mail: $!";
-
- defined($c= fork) or die "$quis: fork for mail: $!\n";
- if (!$c) {
- open STDIN, "<& ${m_m}" or die "$quis - sendmail: dup for stdin: $!\n";
- exec '/usr/sbin/sendmail','-odq','-oee','-oi',
- 'ijackson@chiark.greenend.org.uk'; # should be -t
- die "$quis - sendmail: exec: $!\n";
- }
- $m_m->close;
- $!=0; $r= waitpid $c,0;
- $r == $c or die "$quis: waitpid sendmail ($c): $r $!";
- $? and warn "$quis: sendmail failed: $?\n";
-
- printf " %-40s %s\n", $zone, $zone_to or
- die "$quis: write mailing report: $!\n";
-}
-
#-------------------- checking
use vars qw($zone $cfg $warnings %zone_warnings);
mail_zone_before() if $domail;
zone_reset();
progress(1, sprintf "%-20s %s", $zone, $$cfg{'style_p'});
- if ($check && ($doall || $cfg->{'s'} !~ m/\?/)) {
+ if ($check && ($doall || !zone_style('?',0)) {
eval {
if ($localonly && $cfg->{'s'} =~ m/f/) {
zone_warning("foreign zone specified with -l",'');
%nsrrset_checked, %soa_checked, $addr, $glueless_ok,
$rcode, $name, $is_ns);
- if ($cfg->{'s'} !~ m/\*/) {
+ if (!zone_style('*',0)) {
$super_zone= $zone;
for (;;) {
debug_trace("zone $zone superzone $super_zone");
@glue= @{ $s2g{$s} };
if (!@glue) {
zone_warning("glueless NS $s", $ww)
- unless $glueless_ok || !$needglue ||
+ unless $glueless_ok || zone_style('~',!$needglue) ||
grep { has_suffix_of($zone,".$_"); }
@{ $cfg->{'conv_glueless'} };
($rcode,@glue)= lookup($s,'a','0',"glueless NS from $ww");
foreach $addr (@addrs) { zone_server_addr($addr,$name,$ww,$ww,$is_soa); }
}
+sub zone_style ($$) {
+ my ($stylechar, $default) = @_;
+ local ($_) = $$cfg{'s'};
+ $stylechar =~ s/\W/\\$&/ or die;
+ return m/(\S*)$stylechar/ ? $1 !~ m/\!/ : $default;
+}
+
+#-------------------- mailing
+
+use vars qw($m_base $m_lastok @m_ok @m_fail
+ $m_info $m_time);
+
+sub mail_zone_before () {
+ $m_base= $$cfg{'maildir'}.'/'.$zone;
+ $m_lastok= '-';
+ @m_ok= ();
+ @m_fail= ();
+
+ for (;;) {
+ $m_lock= IO::File "${m_base}_lock", O_RDWR|O_CREAT, 0600
+ or die "$quis: create lockfile ${m_base}_lock";
+ if (!flock($m_lock, LOCK_EX|LOCK_NB)) {
+ <$m_lock> =~ m/^\d+ /;
+ die "$quis: $zone: concurrrency? - flock $&$!\n";
+ $m_lock->close;
+ continue;
+ }
+ (@s1= $m_lock->fstat) or die "$quis: fstat ${m_base}_lock: $!\n";
+ (@s2= stat "${m_base}_lock") or
+ die "$quis: stat ${m_base}_lock: $!\n";
+ last if ($s1[0] eq $s2[0] && $s1[1] eq $s2[1]);
+ $m_lock->close;
+ }
+ print $m_lock "$$ \n" or $m_lock->flush
+ die "$quis: write pid to ${m_base}_lock: $!\n";
+
+ if ($m_info= IO::File "${m_base}_info", 'r') {
+ $!=0; $_= <INFO>;
+ $_ =~ m/\n/ or die "$quis: read ${m_base}_info: $!\n";
+ m/^\d+ (\d+|-) ([0-9:]*) ([0-9:]*) / or
+ die "$quis: ${m_base}_info malformed\n";
+ $m_lastok= $1;
+ if ($domail ne 'first') {
+ if ($2 eq ':' && $3 eq ':') {
+ warn "$quis: $zone: mid/last run, but last".
+ "run already done, ignoring zone\n";
+ next;
+ }
+ @m_ok= split /\:/, $2;
+ @m_fail= split /\:/, $3;
+ }
+ } elsif ($! != &ENOENT) {
+ die "$quis: open ${m_base}_info: $!\n";
+ } elsif ($domail eq 'first') {
+ warn "$quis: $zone: first run, but not --mail-first,".
+ " ignoring zone\n";
+ next;
+ }
+ if ($domail eq 'first') {
+ remove "${m_base}_history" or $!==&ENOENT
+ or die "$quis: remove ${m_base}_history: $!\n";
+ }
+ $progress_fh= $warn_fh= new IO::File "${m_base}_history",'a',0666
+ or die "$quis: open ${m_base}_history: $!\n";
+ $m_info= new IO::File "${m_base}_info.tmp",'w',0666
+ or die "$quis: open ${m_base}_info.tmp: $!\n";
+
+ $m_time= time;
+ progress(-1, "\n".('-'x70)."\n".ptime($m_time)."\n");
+}
+
+sub mail_zone_after () {
+ if ($zone_warnings{$zone}) {
+ push @m_fail, $m_time;
+ progress(-1,"failed - $zone_warnings{$zone} warnings");
+ } else {
+ push @m_ok, $m_time;
+ $m_lastok= $m_time;
+ progress(-1,"everything is fine");
+ }
+ close $progress_fh or die "$quis: close ${m_base}_history: $!\n";
+ $progress_fh= $warn_fh= 'STDERR';
+
+ if ($domail eq 'final') {
+ if (100*@m_fail <= $$cfg{'mailmwarn'}*(@m_mail + @m_ok)) {
+ printf " %-40s ok\n" or die "$quis: mail ok report: $!\n";
+ } elsif (zone_style('@',0)) ) {
+ printf " %-40s mail suppressed\n"
+ or die "$quis: mail suppress report: $!\n";
+ } else {
+ mail_zone_mail();
+ }
+ }
+
+ @m_fail= @m_ok= ('','')
+ if $domail eq 'final';
+
+ printf $m_info "%s %s %s %s \n",
+ $m_time, $m_lastok, join(':',@m_ok), join(':',@m_fail)
+ or die "$quis: write new ${m_base}_info: $!\n";
+ $m_info->close or die "$quis: close new ${m_base}_info: $!\n";
+ rename "${m_base}_info.tmp", "${m_base}_info"
+ or die "$quis: install new ${m_base}_info: $!\n";
+ $m_lock->close;
+}
+
+
+sub pmail ($) { print $m_m $_[0] or die "$quis: write ${m_base}_mail: $!\n"; }
+sub ptime ($) { my ($time)=@_; return gmtime($time)." GMT ($time)"; }
+
+sub mail_zone_mail () {
+ my ($log, $zone_to, $zterr, $c, $r);
+ $m_m= new IO::File, "${m_base}_mail", 'w', 0666
+ or die "$quis: create ${m_base}_mail: $!\n";
+ $zone_to=''; $zterr='';
+ if (!zone_style("\$",$$cfg{'s'} =~ m/u/)) {
+ eval {
+ $_= lookup($zone,'soa','0','problem-addr');
+ m/\n\n/ and die "multiple soas\n";
+ m/^\S+ (\S.*\@\S+) [0-9 ]+$/ or
+ die "bad soa \`$_'\n";
+ $zone_to= $1;
+ };
+ $zterr= $@;
+ $zterr =~ s/\n$/;
+ }
+ pmail <<END
+From: zone check system <$$cfg{'admin'}>
+Subject: $zone - configuration problems report
+END
+;
+ pmail("To: ");
+ pmail("SOA MNAME for $zone <$zone_to>\nCC: ")
+ if length($zone_to);
+ pmail($$cfg{'admin'}."\n\n");
+ pmail <<END
+You are receiving this mail because your email address is listed
+in the SOA (Start Of Authority) record for $zone
+in the DNS, which means you are supposedly the DNS administrator
+responsible for this zone. This report is generated automatically
+on behalf of $$cfg{'admin'}, who
+is the administrator for a server for the zone. Please contact
+them if you have any problem with this report.
+
+END
+if length $zone_to;
+ pmail <<END
+Sent to $$cfg{'admin'} since SOA MNAME unavailable:
+$zterr
+
+END
+if length $zterr;
+ pmail <<END
+The zone has had configuration errors or persistent operational
+problems during recent checks. See the logs below for details.
+
+Check history for $zone:
+END
+;
+ if ($m_lastok ne '-') {
+ pmail(" Last seen to be fine: ".ptime($m_lastok)."\n");
+ } else {
+ pmail(" No record of this zone ever being fine.\n");
+ }
+ for $t (@m_fail) {
+ pmail(" Errors/warnings at: ".ptime($t)."\n");
+ }
+ for $t (@m_ok) {
+ pmail(" Everything in order at: ".ptime($t)."\n");
+ }
+ pmail("\n");
+ $log= new IO::File "${m_base}_history",'r'
+ or die "$quis: reopen ${m_base}_history: $!";
+ undef $/;
+ pmail($log);
+ $/= "\n";
+ $log->error && $log->close
+ or die "$quis: reread or close ${m_base}_log: $!";
+ $m_m->close or die "$quis: close ${m_base}_mail: $!\n";
+ $m_m= new IO::File "${m_base}_mail"
+ or die "$quis: reopen ${m_base}_mail: $!";
+
+ defined($c= fork) or die "$quis: fork for mail: $!\n";
+ if (!$c) {
+ open STDIN, "<& ${m_m}" or die "$quis - sendmail: dup for stdin: $!\n";
+ exec '/usr/sbin/sendmail','-odq','-oee','-oi',
+ 'ijackson@chiark.greenend.org.uk'; # should be -t
+ die "$quis - sendmail: exec: $!\n";
+ }
+ $m_m->close;
+ $!=0; $r= waitpid $c,0;
+ $r == $c or die "$quis: waitpid sendmail ($c): $r $!";
+ $? and warn "$quis: sendmail failed: $?\n";
+
+ printf " %-40s %s\n", $zone, $zone_to or
+ die "$quis: write mailing report: $!\n";
+}
+
#-------------------- outputting
sub zone_output () {
.BR -DD .)
.TP
.BR \-g | \-\-glueless
-Do not warn about glueless referrals. Not recommended - see
-the section GLUELESSNESS, below.
+Do not warn about glueless referrals (strictly, makes the zone style
+modifier
+.B ~
+the default). Not recommended - see the section GLUELESSNESS, below.
.TP
.BR \-l | \-\-local
Only checks for mistakes which are the responsibility of the local
with foreign zones (zones supplied explictly on the command line but
not relevant to the local server); doing so produces a warning.
.TP
+.BI \-m group !*$@~?
+Overrides a
+.B modifiers
+directive in the configuration file. The modifiers specified in the
+directive are completely replaced by those specified in this command
+line option. (Note that modifiers specified in per-zone directives
+still override these per-group settings.) If more than one
+.B modifiers
+directive specifies the same group, they are all affected.
+.B modifiers
+directives which don't specify a group cannot be affected. It is an
+error if the group does not appear in the config file. See ZONE STYLE
+MODIFIERS, below.
+.TP
.BR \-q | \-\-quiet
Suppress the usual report of the list of nameservers for each zone and
the serial number from each. When specified twice, do not print any
of the times \-\-mail\-* was used (since the last \-\-mail\-first).
The default is 50%.
.TP
+.BR modifiers " " !*$@~? "] [\fIgroup\fP]"
+Applies the specified zone style modifiers (see below) to subsequently
+declared zones (until the next
+.B modifiers
+directive), as if the modifiers specified were written out for
+each zone. You must specify at least one character for the modifiers;
+if you want to reset everything to the default, just say
+.BR ! .
+If style modifiers specified in the zone directive
+conflict with the
+.B modifiers
+directive, those specified in the zone directive take effect.
+.I group
+may contain alphanumerics and underscores, and is used for the
+.B -m
+command-line option.
+.TP
+\fBself\-addr\fP \fIip-address ...\fP
+Specifies the list of addresses that this server may be known by in
+A records. There is no default.
+.TP
\fBoutput\fP \fIformat\fP \fIfilename\fP [\fIformat\fP \fIfilename ...\fP]
Arranges that each
.I filename
.B output
directive.
.TP
-\fBself\-addr\fP \fIip-address ...\fP
-Specifies the list of addresses that this server may be known by in
-A records. There is no default.
-.TP
\fBself\-ns\fP \fIfqdn ...\fP
Specifies the list of names that this server may be known by in NS
records. There is no default. Any trailing * is replaced by the name
.SS ZONE DIRECTIVES
These directives specify one or more zones.
.TP
-.BR primary [ * | ? | @ | @@ | ~ "] \fIzone filename\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
-.BR primary\-dir [ * | ? | @ | @@ | ~ "] \fIdirectory\fP[" / "\fIprefix\fP] [\fIsuffix\fP[" / \fIsubfile\fP]]
+.BR primary\-dir [ !*$@~? "] \fIdirectory\fP[" / "\fIprefix\fP] [\fIsuffix\fP[" / \fIsubfile\fP]]
Search
.I directory
for files whose names start with
is specified then the default is
.BR _db .
.TP
-.BR published [ * | ? | @ | @@ | ~ "] \fIzone origin\-addr\fP"
+.BR published [ !*$@~? "] \fIzone origin\-addr\fP"
Specifies that this server is supposed to be a published slave
nameserver for the zone in question.
.TP
-.BR stealth [ * | ? | @ | @@ | ~ "] \fIzone server\-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
+.SS ZONE STYLE MODIFIERS
Each of the zone directives may optionally be followed by one or more
-of the following characters:
+of the following characters (each at most once):
+.TP
+.B !
+Reverses the meaning of all style modifiers after the
+.BR ! .
+Only one
+.BR !
+must appear in the modifier list. In this list, other modifiers which
+default to `enabled' are described by describing the effect of their
+inverse - see the description for
+.B !@
+below.
.TP
.B *
Indicates that the zone is unofficial, ie that it is not delegated as
namespace which are reserved for private use, or belong to the actual
zone maintainer.
.TP
-.B @
-Indicates that mails should be sent about the zone to the nameserver
-admin rather than to the zone SOA MNAME. This is always done for
-stealth zones.
+.B $
+Indicates that any mails should be sent about the zone to the
+nameserver admin rather than to the zone SOA MNAME. This is the
+default for stealth zones.
.TP
-.B @@
+.B !@
Indicates that no mails should be sent about the zone to anyone.
.TP
.B ~