diff --git a/DDNS.pm b/DDNS.pm index 448cc4f..d6f8f63 100644 --- a/DDNS.pm +++ b/DDNS.pm @@ -4,6 +4,9 @@ use strict; use warnings; use Net::DNS; use File::Temp qw/tempfile/; +use Memoize; + +memoize('_gethosts'); our $VERSION = '0.2'; @@ -21,7 +24,7 @@ sub _validateTypeOrDie { my ($t) = @_; die "Invalid type" - unless ($t =~ /^_(vhosts|hostedservers|pureslave|custom)$/); + unless ($t =~ /^_(vhosts|pureslave|custom)$/); } sub _fqdn { @@ -121,11 +124,34 @@ sub _gethosts { return @vh; } +# Find the type of domin that $dom is. If we don't find it, return +# undef. (The domain $dom ends in a dot; the DNS info we find won't; +# hence the concat of the extra "." after the lc.) +sub type { + my ($this, $dom) = @_; + + _validateOrDie($dom); + + my @vh = $this->get(); + foreach my $i (@vh) { + if (lc($i->{zone})."." eq lc($dom)) { + return $i->{type}; + } + } + + # Didn't find it. + return undef; +} + sub add { my ($this, $dom, $master, $type) = @_; _validateOrDie($dom); my $fqdn = _fqdn($dom, $type); + if (my $type = $this->type($dom)) { + die "Domain $dom already exists [of type $type]"; + } + $this->__docmd("update add $fqdn 60 TXT $master"); $this->cleanup(); } diff --git a/Makefile.PL b/Makefile.PL index 9819a95..a5a8a01 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -20,6 +20,7 @@ WriteMakefile( 'bin/sync-slave', 'bin/list-all', 'bin/is-managed', + 'bin/validate-master', ], 'AUTHOR' => 'Jorj Bauer ', ); diff --git a/bin/validate-master b/bin/validate-master new file mode 100755 index 0000000..6a23e7e --- /dev/null +++ b/bin/validate-master @@ -0,0 +1,60 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Martnet::DDNS; + +my $ddns = Martnet::DDNS->new(); + +my $path = '/etc/bind'; + +my %files = ( 'custom.zones.9' => '_custom', + 'martnet.zones.9' => '_custom', + 'hostedservers.zones.9' => '_custom', + 'vhost.zones.9' => '_vhosts', + 'martnet.slave.zones.9' => '*' ); + +our %fixes = ( '_custom' => 'add-custom', + '_vhosts' => 'add-vhost', + '_pureslave' => 'add-slave', + '*' => 'one of the add-* scripts', + ); + +foreach my $i (keys %files) { + validate_file($path . '/' . $i, $files{$i}); +} + +exit 0; + +sub validate_file { + my ($path, $expected_type) = @_; + + open(my $fh, $path) || die "Can't open $path: $!"; + + while (<$fh>) { + my ($zonename) = ($_ =~ /zone \"([^\"]+)\"/i); + next unless ($zonename); + $zonename = lc($zonename); + unless ($zonename =~ /\.$/) { + $zonename .= '.'; # must end with a dot + } + + findOrDie($path, $zonename, $expected_type); + } + close $fh; +} + +sub findOrDie { + my ($path, $zonename, $expected_type) = @_; + + my $type = $ddns->type($zonename); + unless ($type) { + my $fix = $fixes{$expected_type}; + die "domain $zonename is not managed, but is in $path [expected $expected_type]; to fix, either remove the domain from the file, or use $fix to fix it"; + } + if ($expected_type ne '*') { + unless ($type eq $expected_type) { + die "domain $zonename is managed in $path of type [$type] but should be [$expected_type] if it's in that file"; + } + } +}