1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
#!/usr/bin/perl -w
#
# dns-update - manage DNS zone files under CVS
# Copyright (C) 1999 Tommi Virtanen <tv@havoc.fi>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is 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 vars qw($BASEDIR $FROM $PRIMARY $RUNAFTER $TO %SOA_DEFAULTS);
use File::Find;
use POSIX qw(strftime);
use Digest::MD5;
sub fail(@) { die "$0: @_\n" }
do '/etc/dns-update.conf' or fail "config error; $@\n";
chdir($FROM) or fail "Cannot chdir to $FROM: $!";
system('/usr/bin/cvs', '-q', 'update', '-Pd') == 0
or fail "CVS update failed: $!";
open PRIMARY, "> $PRIMARY.tmp"
or fail "Cannot open $PRIMARY.tmp for writing: $!";
print PRIMARY "// This file is auto-generated. Do not edit!\n"
or fail "Cannot write to $PRIMARY.tmp: $!";
find(\&wanted, '.');
close PRIMARY or fail "Closing $PRIMARY.tmp failed: $!";
rename "$PRIMARY.tmp", $PRIMARY
or fail "Renaming $PRIMARY.tmp to $PRIMARY failed: $!";
if (defined $RUNAFTER and $RUNAFTER ne '') {
exec $RUNAFTER or fail "Executing post-run commands failed: $!";
}
exit(0);
sub wanted {
return if $File::Find::dir =~ m{/CVS/};
return unless -f $_;
return unless /\.domain$/;
my $file = $File::Find::name;
$file =~ s{^\./}{};
$file =~ s{\.domain$}{};
my $zone = join '.', reverse split '/', $file;
my $tofile = "$TO/$file.dom";
-d "$TO/$File::Find::dir" or mkdir "$TO/$File::Find::dir", 0755
or fail "Cannot create directory $TO/$File::Find::dir";
convert($zone, "$FROM/$file.domain", $tofile);
print PRIMARY <<EOF
zone "$zone" {
type master;
file "$tofile";
};
EOF
};
sub today() {
return strftime "%Y%m%d", localtime();
}
sub newserial($;) {
my ($file) = @_;
my $serial;
if (-f $file) {
open SERIAL, "< $file"
or fail "Cannot open $file for reading: $!";
$serial = <SERIAL>;
defined $serial or fail "Cannot read serial $file: $!";
close SERIAL;
chomp $serial;
# Here's a Y3K problem: ( ;) )
my ($d,$n) = ($serial =~ /^([12][0-9][0-9][0-9] # year
(?:0[1-9]|1[0-2]) # month
(?:0[1-9]|[12][0-9]|3[01])) # day
([0-9][0-9]) # serial
$/x);
defined $d and defined $n or fail "Invalid serial: $file is $serial";
if ($d eq today()) {
$n++;
$n<100 or fail "Too big serial in $file";
$serial = $d . $n;
}
else { $serial = today() . '01' }
}
else { $serial = today() . '01' }
open SERIAL, "> $file" or fail "Cannot open $file for reading: $!";
print SERIAL "$serial\n" or fail "Cannot write to $file: $!";
close SERIAL or fail "Closing $file failed: $!";
return $serial;
}
sub md5_modified($*) {
my ($sumfile, $fh) = @_;
my $oldsum;
if (-e $sumfile) {
open OLD, "< $sumfile" or fail "Cannot open $sumfile for reading: $!";
$oldsum = <OLD>;
close OLD;
defined $oldsum or fail "No md5sum read from file $sumfile";
chomp $oldsum;
$oldsum =~ /^[0-9a-z]{32}$/ or fail "Bad md5sum from file $sumfile";
} else {
$oldsum = '';
}
my $md5 = new Digest::MD5;
$md5->addfile($fh);
seek($fh, 0, 0) or fail "Cannot rewind: $!";
my $newsum = $md5->hexdigest();
if ($newsum eq $oldsum) { return 0 }
else {
open SUM, "> $sumfile" or fail "Cannot open $sumfile for writing: $!";
print SUM "$newsum\n" or fail "Cannot write to $sumfile: $!";
close SUM or fail "Closing $sumfile failed: $!";
return 1;
}
}
sub convert($$$;) {
my ($name, $from, $to) = @_;
open(IN, "< $from") or fail "Cannot open $from for reading: $!";
return 0
unless md5_modified("$to.md5sum", *IN{IO})
or grep {$_ eq '-f'} @ARGV;
my $serial = newserial "$to.serial";
open(OUT, "> $to.tmp") or fail "Cannot open $to.tmp for writing: $!";
print OUT ";;; This file is auto-generated. Do not edit!\n"
or fail "Cannot write to $to.tmp: $!";
local $_;
while(<IN>) {
chomp;
while (s/\\$//) {
my $tmp=<IN>;
defined $tmp
or fail "End of file while combining lines in $name";
chomp $tmp;
$_.=$tmp;
}
if(s/^!template(?:$|\s+)//i) {
print OUT template($name, $serial, $_)
or fail "Cannot write to $to.tmp: $!";
}
else {
print OUT "$_\n" or fail "Cannot write to $to.tmp: $!";
}
}
close(IN) or fail "Close failed on $from: $!";
close(OUT) or fail "Close failed on $to.tmp: $!";
rename("$to.tmp", "$to") or "Rename $to.tmp to $to failed: $!";
}
sub template($$$;) {
my ($name, $serial, $opts) = @_;
my %vars=%::SOA_DEFAULTS;
my %allowed_opts=(origin=>`hostname -f`); #TODO *barf* *puke*
foreach (split ' ',$opts) {
s/^([a-z]+)=//
or fail "Invalid options to !template in $name.domain";
exists $vars{lc $1} or exists $allowed_opts{lc $1}
or fail "Invalid option $1 to !template in $name.domain";
$vars{lc $1}=$_;
}
return <<EOF
\$TTL $vars{minttl} ; time to live
@ IN SOA $vars{origin}. $vars{hostmaster}. (
$serial ; serial number
$vars{refresh} ; refresh
$vars{retry} ; retry
$vars{expire} ; expire
$vars{negttl} ) ; negative cache time-to-live
EOF
}
__END__
=head1 NAME
dns-update - manage DNS zone files under CVS
=head1 SYNOPSIS
dns-update [options]
=head1 DESCRIPTION
TODO write a description
=head1 OPTIONS
-f Force regeneration even if the zone has not changed.
=head1 BUGS
This manpage.
=head1 AUTHOR
Tommi Virtanen <tv@havoc.fi>
=cut
|