#!/usr/bin/perl -w
+# This is chiark-named-conf, which is Copyright 2002 Ian Jackson.
+#
+# chiark-named-conf and its manpage are free software; you can
+# redistribute it and/or modify them under the terms of the GNU
+# General Public License as published by the Free Software Foundation;
+# either version 2, or (at your option) any later version.
+#
+# chiark-named-conf and its manpage are distributed in the hope that
+# it will be useful, but WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
use strict;
use IO::File;
use POSIX;
use Fcntl qw(:DEFAULT :flock);
-use vars qw($quis
+# bastard Perl wants me to do this now !
+sub loarg();
+sub soarg();
+sub usageerr ($);
+sub cfg_fail ($);
+sub read_config ($);
+sub qualify ($);
+sub bad_modifiers ($);
+sub zone_conf ($$$$$@);
+sub set_output($);
+sub progress ($$);
+sub verbose ($);
+sub process_zones (@);
+sub zone_warning ($$);
+sub zone_warnmore ($);
+sub zone_check_full ();
+sub zone_reset();
+sub zone_investigate();
+sub zone_check_nsrrset ($$$$);
+sub zone_ns_name ($$);
+sub zone_server_queue ($$$$$);
+sub zone_server_addr ($$$$$);
+sub zone_check_soa ($$$$);
+sub zone_consistency();
+sub zone_servers_ok ();
+sub zone_consistency_set ($%);
+sub zone_check_local ();
+sub zone_servers_simplefind ();
+sub zone_server_simple ($$$);
+sub zone_style ($$);
+sub mail_zone_before ();
+sub mail_zone_after ();
+sub pmail ($);
+sub ptime ($);
+sub mail_zone_mail ();
+sub zone_output ();
+sub output_files ();
+sub debug_dump ($);
+sub debug_trace ($);
+sub has_suffix_of ($$);
+sub lookup ($$$$);
+sub dig (&$$$);
+sub domain_canon ($$);
+
+use vars qw($quis $stdout_fh $stderr_fh
$mode $doall $domail
$etcfile $where
$debug $needglue $localonly $repeat $verbosity
$admin=''; $mail_state_dir=''; $mail_max_warnfreq= 50;
$repeat= 0;
$domail= '';
-$progress_fh= 'STDOUT';
-$warn_fh= 'STDERR';
$modifiers= '';
+$group2modcmd{'foreign'}= '$!*@?';
+$group2used{'foreign'}= 1;
+($progress_fh= $stdout_fh= new_from_fd IO::Handle(1,'w') and
+ $warn_fh= $stderr_fh = new_from_fd IO::Handle(2,'w'))
+ or die "$quis: setup standard filehandles: $!\n";
+
use vars qw($dig_owner $dig_type $dig_rdata);
while (@ARGV && $ARGV[0] =~ m/^\-/) {
last if m/^$/;
if (m/^(yes|no|force)$/) { m/^./; $mode= $&; $domail=''; }
elsif (m/^nothing$/) { $mode= 'x'; $domail=''; }
- elsif (m/^mail\-(first|middle|final|final-test)/) {
+ elsif (m/^mail\-(first|middle|final|final\-test)$/) {
$mode='n';
$domail=$1;
}
usageerr("-v may be specified at most once") if $verbosity>3;
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;
+ if !!$mode == !!@ARGV && !$domail;
sub usageerr ($) {
die <<END;
read_config($etcfile);
debug_dump('@zone_cfg_list %zone_cfg');
-process_zones($mode ? @zone_cfg_list : @ARGV);
+process_zones(!@ARGV ? @zone_cfg_list : @ARGV);
debug_dump('%output_contents');
output_files() if $install;
+$stdout_fh->close or die "$quis: write messages to stdout: $!\n";
+$stderr_fh->close or die "$quis: write messages to stderr: $!\n";
exit 0;
#-------------------- configuration reading
local ($_) = @_;
if (!eval {
die "bad modifier $&" if m/[^!*\$\@~?]/;
- die "repeated modifier $1" if m/(.).*$1/;
+ die "repeated modifier $1" if m/(.).*\1/;
1;
}) {
$@ =~ s/\n//;
$zone_cfg{$zone}{$sfx}= $aref;
}
foreach $sfx (qw(self_soa self_ns)) {
- $zone_cfg{$zone}{$sfx} =~ s/\*$/$zone/;
+ map { s/\*$/$zone/ } @{ $zone_cfg{$zone}{$sfx} };
}
$zone_cfg{$zone}{'output'}= $output;
push @zone_cfg_list, $zone;
foreach $zone (@zones) {
$cfg= $zone_cfg{$zone} || {
'style_p' => 'foreign',
- 's' => 'f',
+ 's' => "f $group2modcmd{'foreign'}",
'servers' => [ ],
};
- mail_zone_before() if $domail;
+ mail_zone_before() or next
+ if $domail;
zone_reset();
progress(1, sprintf "%-20s %s", $zone, $$cfg{'style_p'});
if ($check && ($doall || !zone_style('?',0))) {
for (;;) {
$m_lock= new IO::File "${m_base}_lock", O_RDWR|O_CREAT, 0600
- or die "$quis: create lockfile ${m_base}_lock";
+ or die "$quis: create lockfile ${m_base}_lock: $!\n";
if (!flock($m_lock, LOCK_EX|LOCK_NB)) {
<$m_lock> =~ m/^\d+ /;
die "$quis: $zone: concurrrency? - flock $&$!\n";
$m_lock->close;
- next;
+ return 0;
}
- (@s1= $m_lock->fstat) or die "$quis: fstat ${m_base}_lock: $!\n";
+ (@s1= $m_lock->stat) 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]);
if ($2 eq ':' && $3 eq ':') {
warn "$quis: $zone: mid/last run, but last".
"run already done, ignoring zone\n";
- next;
+ return 0;
}
@m_ok= split /\:/, $2;
@m_fail= split /\:/, $3;
}
} elsif ($! != &ENOENT) {
die "$quis: open ${m_base}_info: $!\n";
- } elsif ($domail eq 'first') {
+ } elsif ($domail ne 'first') {
warn "$quis: $zone: first run, but not --mail-first,".
" ignoring zone\n";
- next;
+ return 0;
}
if ($domail eq 'first') {
remove "${m_base}_history" or $!==&ENOENT
$m_time= time;
progress(-1, "\n".('-'x70)."\n".ptime($m_time)."\n");
+ return 1;
}
sub mail_zone_after () {
progress(-1,"everything is fine");
}
close $progress_fh or die "$quis: close ${m_base}_history: $!\n";
- $progress_fh= $warn_fh= 'STDERR';
+ $progress_fh= $warn_fh= $stderr_fh;
if ($domail =~ m/^final/) {
if (100*@m_fail <= $$cfg{'mailmwarn'}*(@m_fail + @m_ok)) {
- printf " %-40s ok\n" or die "$quis: mail ok report: $!\n";
+ printf " %-40s ok\n", $zone or die "$quis: mail ok report: $!\n";
} elsif (zone_style('@',0)) {
- printf " %-40s mail suppressed\n"
+ printf " %-40s mail suppressed\n", $zone
or die "$quis: mail suppress report: $!\n";
} else {
mail_zone_mail();
}
- }
+ } else {
+ printf " %-40s %d warns. OK %s Fail %s\n",
+ $zone,
+ defined $zone_warnings{$zone} ? $zone_warnings{$zone} : 0,
+ join(',', map { $_ - $m_time } @m_ok),
+ join(',', map { $_ - $m_time } @m_fail)
+ or die "$quis: checking progress report: $!\n";
+ }
@m_fail= @m_ok= ('','')
if $domail =~ m/^final/;
}
sub mail_zone_mail () {
- my ($log, $zone_to, $zterr, $c, $r, $t);
+ my ($log, $zone_to, $zterr, $c, $r, $t, @soas);
$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/)) {
+ if (!zone_style("\$", $$cfg{'s'} !~ m/[ps]/)) {
eval {
- $_= lookup($zone,'soa','0','problem-addr');
- m/\n\n/ and die "multiple soas\n";
- m/^\S+ (\S.*\@\S+) [0-9 ]+$/ or
+ ($r,@soas)= lookup($zone,'soa','0','problem-addr');
+ @soas==1 or die "multiple soas\n";
+ $soas[0] =~ m/^\S+ (\S.*\@\S+) [0-9 ]+$/ or
die "bad soa \`$_'\n";
$zone_to= $1;
};
$zterr =~ s/\n$//;
}
pmail <<END
-From: zone check system <$$cfg{'admin'}>
+From: zone checker <$$cfg{'admin'}>
Subject: $zone - configuration problems report
END
;
The zone has had configuration errors or persistent operational
problems during recent checks. See the logs below for details.
-Check history for $zone:
+Recent check history for $zone:
END
;
if ($m_lastok ne '-') {
pmail(" No record of this zone ever being fine.\n");
}
for $t (@m_fail) {
- pmail(" Errors/warnings at: ".ptime($t)."\n");
+ pmail(" Zone had problems at: ".ptime($t)."\n");
}
for $t (@m_ok) {
pmail(" Everything in order at: ".ptime($t)."\n");
$log= new IO::File "${m_base}_history",'r'
or die "$quis: reopen ${m_base}_history: $!";
undef $/;
- pmail($log);
+ pmail($log->getline);
$/= "\n";
- $log->error && $log->close
- or die "$quis: reread or close ${m_base}_log: $!";
+ (!$log->error and $log->close)
+ or die "$quis: reread or close ${m_base}_log: $!\n";
+ $log->eof or die;
$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 (qw(/usr/sbin/sendmail -odq -oee -oi),
+ defined dup2($m_m->fileno, 0)
+ or die "$quis - sendmail: dup for stdin: $!\n";
+ exec (qw(/usr/sbin/sendmail -odb -oee -oi),
($domail eq 'final' ? '-t' : $$cfg{'admin'}));
die "$quis - sendmail: exec: $!\n";
}
$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";
+ printf " %-40s %s\n", $zone,
+ length $zone_to ? $zone_to : 'reporting to admin'
+ or die "$quis: write mailing report: $!\n";
}
#-------------------- outputting