diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2016-02-02 21:46:43 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2016-02-02 21:46:43 +0200 |
commit | 64c398c8dd1bf734fdc9cc8e9638cd039d353b89 (patch) | |
tree | 7e7efc2ad67e56dfc53aac2773e0ea79ccf2a243 /vhostcname/vhostcname | |
parent | 0225fb12aa45a259f728951693d5b101c010c3fd (diff) | |
download | dnstools-64c398c8dd1bf734fdc9cc8e9638cd039d353b89.tar.gz dnstools-64c398c8dd1bf734fdc9cc8e9638cd039d353b89.tar.bz2 |
vhostcname: refuse to replace CNAMEs pointing to another host.
* 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 (limited to 'vhostcname/vhostcname')
-rwxr-xr-x | vhostcname/vhostcname | 140 |
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 |