summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org.ua>2016-02-02 19:46:43 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2016-02-02 19:46:43 (GMT)
commit64c398c8dd1bf734fdc9cc8e9638cd039d353b89 (patch) (side-by-side diff)
tree7e7efc2ad67e56dfc53aac2773e0ea79ccf2a243
parent0225fb12aa45a259f728951693d5b101c010c3fd (diff)
downloaddnstools-master.tar.gz
dnstools-master.tar.bz2
vhostcname: refuse to replace CNAMEs pointing to another host.HEADmaster
* vhostname/vhostcname (ns_update): Accept arrays as prereq values. Improve error reporting. (get_zone_resolver): If server name is not specified, get it from the SOA. (update_cnames_from_hash, nscleanup): Refuse to update zone if a CNAME already exists and points to another host.
Diffstat (more/less context) (ignore whitespace changes)
-rwxr-xr-xvhostcname/vhostcname140
1 files changed, 101 insertions, 39 deletions
diff --git a/vhostcname/vhostcname b/vhostcname/vhostcname
index 91a2c8d..78d0a4c 100755
--- a/vhostcname/vhostcname
+++ b/vhostcname/vhostcname
@@ -21,6 +21,7 @@ use Pod::Man;
use Sys::Hostname;
use Cwd qw(getcwd realpath);
use Net::DNS;
+use Data::Dumper;
my $progname; # This script name;
my $progdescr = "update DNS from Apache virtual host configuration";
@@ -342,6 +343,42 @@ sub write_cname_list {
close($fd);
}
+my %nsupdate_diag = (
+ NXRRSET => sub {
+ my $rr = shift;
+ return $rr->type ne 'ANY';
+ },
+ NXDOMAIN => sub {
+ my $rr = shift;
+ return $rr->type eq 'ANY';
+ }
+);
+
+sub nsupdate_strerror {
+ my ($reply, $hash) = @_;
+ my $s;
+ if (exists($hash->{prereq})
+ and exists($nsupdate_diag{$reply->header->rcode})) {
+ my $prereq;
+ if (ref($hash->{prereq}) eq 'ARRAY') {
+ $prereq = $hash->{prereq};
+ } else {
+ $prereq = [$hash->{prereq}];
+ }
+ my $diag = $nsupdate_diag{$reply->header->rcode};
+ foreach my $rr (@{$prereq}) {
+ if (&{$diag}($rr)) {
+ $s = "prerequisite \"".$rr->plain ."\" not met";
+ last;
+ }
+ }
+ }
+
+ $s = $reply->header->rcode unless defined $s;
+
+ return $s;
+}
+
sub ns_update {
my $name = shift;
my $domain = shift;
@@ -360,6 +397,10 @@ sub ns_update {
while (my ($k, $v) = each %hash) {
if ($k eq 'ignore') {
$ignorerr{$v} = 1;
+ } elsif (ref($v) eq 'ARRAY') {
+ foreach my $r (@{$v}) {
+ $update->push($k => $r);
+ }
} else {
$update->push($k => $v);
}
@@ -370,22 +411,17 @@ sub ns_update {
if ($reply->header->rcode eq 'NOERROR') {
print STDERR "$progname: update successful\n" if ($debug>3);
} elsif ($ignorerr{$reply->header->rcode}) {
- print STDERR "$progname: ignoring " . $reply->header->rcode . ': ' .
- join(',', map { "$_ => $hash{$_}" } keys %hash) . "\n"
- if ($debug>3);
+ print STDERR "$progname: ignoring: " .
+ nsupdate_strerror($reply, \%hash) ."\n"
+ if $debug > 2;
} else {
- err("updating $name failed: ",
- join(',', map { "$_ => $hash{$_}" } keys %hash),
- ': ',
- $reply->header->rcode);
+ err("updating $name failed: " .
+ nsupdate_strerror($reply, \%hash));
$status = EX_NOTUPDATED;
return 0;
}
} else {
- err("updating $name failed: ",
- join(',', map { "$_ => $hash{$_}" } keys %hash),
- ': ',
- $resolver->errorstring);
+ err("updating $name failed: " . $resolver->errorstring);
$status = EX_NOTUPDATED;
return 0;
}
@@ -396,8 +432,22 @@ sub get_zone_resolver {
my $zone = shift;
unless (defined($config{zone}{$zone}{resolver})) {
my $resolver = new Net::DNS::Resolver;
- $resolver->nameservers($config{zone}{$zone}{server})
- if defined $config{zone}{$zone}{server};
+ my @servers;
+
+ if (defined $config{zone}{$zone}{server}) {
+ push @servers, $config{zone}{$zone}{server};
+ } else {
+ my $query = $resolver->query($zone, "SOA");
+ if ($query) {
+ my @rr = $query->answer;
+ push @servers, $rr[0]->mname;
+ print STDERR "$progname: using ".$rr[0]->mname." as name server for $zone\n"
+ if $debug;
+ } else {
+ err("can't get soa for $zone: " . $resolver->errorstring);
+ }
+ }
+ $resolver->nameservers(@servers);
$config{zone}{$zone}{resolver} = $resolver;
}
return $config{zone}{$zone}{resolver};
@@ -439,23 +489,29 @@ sub update_cnames_from_hash {
foreach $name (@namelist) {
if ($oldhash{$name}) {
delete $oldhash{$name};
- } else {
- ns_update($name, $hash{$name},
- prereq => yxdomain($name),
- update => rr_del($name),
- ignore => 'NXDOMAIN');
+ } elsif (ns_update($name, $hash{$name},
+ prereq => [yxdomain($name),
+ yxrrset("$name CNAME $config{core}{hostname}")],
+ update => rr_del("$name CNAME"),
+ ignore => 'NXDOMAIN')) {
print STDERR "$progname: $name $config{core}{ttl} CNAME $config{core}{hostname}\n" if ($debug);
- delete $hash{$name}
- unless ns_update($name, $hash{$name},
- update => rr_add("$name $config{core}{ttl} CNAME $config{core}{hostname}"));
+ unless (ns_update($name, $hash{$name},
+ update => rr_add("$name $config{core}{ttl} CNAME $config{core}{hostname}"))) {
+ delete $hash{$name};
+ }
+ } else {
+ delete $hash{$name};
}
}
foreach $name (keys %oldhash) {
ns_update($name, $oldhash{$name},
- prereq => yxrrset("$name CNAME"),
+ prereq => [
+ yxdomain($name),
+ yxrrset("$name CNAME $config{core}{hostname}.")
+ ],
update => rr_del("$name CNAME"),
- ignore => 'NXRRSET');
+ ignore => 'NXDOMAIN');
}
write_cname_list($config{core}{cache}, %hash);
@@ -474,9 +530,12 @@ sub nscleanup {
if ($debug);
delete $hash{$name}
if ns_update($name, $hash{$name},
- prereq => yxrrset("$name CNAME"),
+ prereq => [
+ yxdomain($name),
+ yxrrset("$name CNAME $config{core}{hostname}.")
+ ],
update => rr_del("$name CNAME"),
- ignore => 'NXRRSET');
+ ignore => 'NXDOMAIN');
}
write_cname_list($config{core}{cache}, %hash);
@@ -635,7 +694,7 @@ GetOptions("help" => sub {
readconfig($config_file, \%config, kw => \%kw);
-unless (defined($config{core}{'apache-config-directory'})) {
+unless (exists($config{core}{'apache-config-directory'})) {
foreach my $dir ("/etc/apache2", "/etc/httpd") {
if (-e "$dir/sites-enabled" and -e "$dir/sites-available") {
$config{core}{'apache-config-directory'} = $dir;
@@ -648,7 +707,7 @@ unless (defined($config{core}{'apache-config-directory'})) {
}
abend(EX_CONFIG,
"don't know where virtual host configurations are located; define apache-config-directory")
- unless defined($config{core}{'apache-config-directory'});
+ unless exists($config{core}{'apache-config-directory'});
}
$config{core}{hostname} = hostname() unless defined($config{core}{hostname});
@@ -703,10 +762,13 @@ The program takes a list of DNS zones and scans Apache virtual host
configuration files. For each hostname found in B<ServerName> and
B<ServerAlias> statements, it checks whether this name ends in a
zone from the list, and if so, attempts to register this hostname
-using the DNS dynamic updates mechanism (B<RFC 2136>).
+using the DNS dynamic updates mechanism (B<RFC 2136>). For each
+such name a CNAME record is created, pointing to the name of the
+system's host. The program will refuse to update the hostname if
+a CNAME record already exists and points to another host.
-A reverse operation is also supported: deregister all host name
-registered on the previous run.
+A reverse operation is also supported: deregister all host names
+registered during the previous run.
The mode of operation is requested by the B<COMMAND> argument.
The available B<COMMAND>s have been chosen so as to allow
@@ -719,7 +781,7 @@ For example, on Debian-based GNU/Linux:
ln -sf /usr/bin/vhostcname /etc/init.d
update-rc.d vhostcname defaults
-The program can also be ised as a B<direvent>(8) handler. This use
+The program can also be used as a B<direvent>(8) handler. This use
allows for immediate updates of the DNS records upon any modifications
to the Apache configuration files. The following example shows the
corresponding B<direvent.conf>(5) entry:
@@ -809,8 +871,8 @@ Configuration is read from F</etc/vhostcname.conf> or a file specified
by the B<--config> (B<-c>) command line option. The file consists of
a number of variable assignments (I<variable> B<=> I<value>), grouped into
sections. Whitespace is ignored, except that it serves to separate input
-tokens. I<value> is read verbatim, including eventual whitespace characters
-that can appear within it.
+tokens. However, I<value> is read verbatim, including eventual whitespace
+characters that can appear within it.
A section begins with the line containing its name within square brackets
(e.g. B<[core]>). The name can be followed by one or more arguments, if
@@ -849,8 +911,8 @@ host names. Default is B</var/run/vhostcname.cache>.
=item B<hostname => I<HOSTNAME>
-Sets the hostname. Use this if B<vhostcname> is unable to correctly
-determine it.
+Sets the target hostname. This name will be used at the right side of
+CNAME records created. Defaults to the system's hostname.
=item B<allow-wildcards => I<BOOL>
@@ -890,7 +952,7 @@ Name of the key file. The argument should be the name of a file
generated by the B<dnssec-keygen> utility. Either B<.key> or B<.private>
file can be used.
-If both <ns-key> and B<ns-key-file> are used, the latter is given preference.
+If both B<ns-key> and B<ns-key-file> are used, the latter is given preference.
=back
@@ -898,8 +960,8 @@ If both <ns-key> and B<ns-key-file> are used, the latter is given preference.
The B<zone> section informs B<vhostcname> that it should handle names
in zone I<NAME>. Any number of B<[zone]> sections can be defined. If
-none is defined, B<vhostcname> will take hostname as the name of the zone
-to update.
+none is defined, B<vhostcname> will use the name of the host it runs on
+as the name of the zone to update.
The variables in a B<zone> section define parameters to be used for that
particular zone. As such, none of them is mandatory. If the zone I<NAME>
@@ -926,7 +988,7 @@ Name of the key file. The argument should be the name of a file
generated by the B<dnssec-keygen> utility. Either B<.key> or B<.private>
file can be used.
-If both <ns-key> and B<ns-key-file> are used, the latter is given preference.
+If both B<ns-key> and B<ns-key-file> are used, the latter is given preference.
=back

Return to:

Send suggestions and report system problems to the System administrator.