package Martnet::DDNS; use strict; use warnings; use Net::DNS; use File::Temp qw/tempfile/; our $VERSION = '0.1'; sub new { my $me = shift; my %opts = @_; return bless { 'keyfile' => '/etc/bind/Kprivate.invalid.+157+14348.key', %opts }, $me; } sub _fqdn { my ($dom) = @_; return $dom . "_vhosts.private.invalid."; } sub _validateOrDie { my ($dom) = @_; die "No domain provided" unless $dom; die "Invalid domain name (must end in a dot)" unless ($dom =~ /^[a-zA-Z0-9\.\-\_]+\.$/); } sub _lookupOrDie { my ($dom) = @_; _validateOrDie($dom); my $fqdn = _fqdn($dom); my $res = Net::DNS::Resolver->new; my $query = $res->query($fqdn, "TXT"); if ($query) { foreach my $rr (grep { $_->type eq 'TXT' } $query->answer) { # We found a record; that's all that I care about. #print $rr->nsdname, "\n"; return 1; } } else { die "query failed: ", $res->errorstring, "\n"; } die "Failed to find existing DNS record for $fqdn"; } sub __docmd { my ($this, $cmd) = @_; my ($tmpfh, $filename) = tempfile(); close $tmpfh; my $fh; open($fh, "|nsupdate -k $this->{keyfile} > $filename") || die "Can't open nsupdate: $!"; print $fh "server localhost\nzone private.invalid.\n$cmd\nshow\nsend\n"; close $fh; open($fh, $filename) || die "Can't re-open tmpfile $filename: $!"; while (<$fh>) { print; } close $fh; unlink $filename; } # Add a new vhost domain by adding a DDNS record that the slaves will notice. sub addvhost { my ($this, $dom, $master) = @_; _validateOrDie($dom); my $fqdn = _fqdn($dom); $this->__docmd("update add $fqdn 60 TXT $master"); $this->cleanup(); } sub delvhost { my ($this, $dom) = @_; _lookupOrDie($dom); my $fqdn = _fqdn($dom); $this->__docmd("update delete $fqdn TXT"); $this->cleanup(); } sub getvhosts { my ($this) = @_; my $fh; open($fh, "dig -t AXFR \@127.0.0.1 private.invalid. |") || die "Can't open dig: $!"; my @vh; while (<$fh>) { if (/^(\S+)._vhosts.private.invalid.\s+\d+\s+IN\s+TXT\s+\"(.+)\"$/) { push (@vh, { zone => $1, master => $2 }); } } return @vh; } sub cleanup { my ($this) = @_; # Merge the .jnl file in with the domain file system("rndc sync -clean private.invalid"); } 1;