bin/dns-vi
changeset 25 50fbb0fb1120
parent 23 6ea7152f33a7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/dns-vi	Thu Jun 05 08:50:19 2014 +0200
@@ -0,0 +1,173 @@
+#! /usr/bin/perl
+#line 3
+use 5.010;
+use strict;
+use warnings;
+use if $ENV{DEBUG} // '' eq 'dns-vi' => 'Smart::Comments';
+use Getopt::Long;
+use Pod::Usage;
+use File::Copy;
+
+#use blib;
+use DNS::Vi;
+
+sub slurp {
+    local $/ = undef;
+    local @ARGV = @_;
+    <>;
+}
+
+sub main {
+    my %o = (
+        local  => undef,
+        key    => undef,
+        server => undef,
+        debug  => undef,
+        editor => $ENV{EDITOR} // 'vi',
+        skip => [qw/NS RRSIG NSEC3 NSEC3PARAM NSEC DNSKEY TSIG/],
+    );
+
+    GetOptions(
+        'k|key=s'    => \$o{key},
+        's|server=s' => \$o{server},
+        'd|debug!'   => \$o{debug},
+        'l|local!'   => \$o{local},
+        'editor=s'   => \$o{editor},
+      )
+      && @ARGV >= 1
+      or pod2usage();
+
+    my $zone = shift @ARGV;
+
+    $o{server} =
+      $o{local} ? 'localhost' : (split ' ', `dig +short soa $zone`)[0]
+      if not defined $o{server};
+
+    my @dig = (
+        dig => 'AXFR',
+        defined $o{key} ? (-k => $o{key}) : (),
+        defined $o{server} ? ("\@$o{server}") : (),
+        $zone
+    );
+
+    my @zone1 = parse($_ = `@dig`, { -skip => $o{skip} } )
+      or die "Empty zone\n";
+    my $fh2;
+    my @zone2 = do {
+	if (my $file = shift @ARGV) {
+	    parse(slurp($file), { -skip => $o{skip} });
+	} 
+	else {
+	    edit(@zone1, { -skip => $o{skip}, -editor => $o{editor}, -backup => \$fh2 });
+	}
+    };
+    ### @zone2
+
+    my ($add, $del) = delta(\@zone1, \@zone2);
+    if ((@$add + @$del) == 0) {
+	    say 'nothing changed';
+	    return 0;
+    }
+
+    say 'The following changes need your confirmation.';
+    say join "\n", show($add, $del);
+    print 'confirm (yes|NO): ';
+    return 1 if <STDIN> !~ /^y/i;
+
+    update(\@zone1, $add, $del, {
+	-server => $o{server},
+	-local => $o{local},
+	-debug => $o{debug},
+	-key => $o{key}})
+    or do { 
+	copy($fh2->filename, ",dns-vi-$$")
+	and say "Saved as ',dns-vi-$$'"
+	    if $fh2;
+    };
+
+    return 0;
+}
+
+exit main(@ARGV) if not caller;
+
+__END__
+
+=head1 NAME
+
+ dns-vi -- editor for dynamically maintained zones
+
+=head1 SYNOPSIS
+
+ dns-vi [[-l] | [[-k key] [-s server]]] [-d] <zone> [<file>]
+
+=head1 DESCRIPTION
+
+This tools supports you in maintaining a dynamic zone. Normally you'll
+use it with the name of zone. For batch mode you may use it with an
+additional parameter, die edited zone file.
+
+=head2 OPTIONS
+
+=over
+
+=item B<-l>|B<--local> 
+
+Local mode, when running on the server where the updates need to go to.
+But still zone transfers need to be enabled! (default: off)
+
+=item B<-s>|B<--server> B<server-name>
+
+The name of the server to contact for the AXFR and the update.
+(default: main nameserver from the SOA record)
+
+=item B<-k>|B<--key> B<key-file>
+
+The name of the key file we need for TSIG (the AXFR will use it,
+as well as the update).
+
+To create such a key you may use 
+
+    dnssec-keygen -a HMAC-MD5 -b 512 -n USER heiko
+
+Then copy the resulting files somewhere (you'll need both files).
+On the server side include the key into to configuration:
+
+    key "<name>" {
+	algorithm   HMAC-MD5;
+	secret	    "<the secret from the created key file>"
+    };
+
+Per zone you should use 
+
+    zone "<zone>" {
+	...
+	update-policy {
+	    grant local-ddns zonesub any;   // support for -l
+	    grant <key-name> zonesub;	    // support for -k
+	};
+	...
+    };
+
+=item B<-d>
+
+This option enables debugging of C<nsupdate>. (default: off)
+
+=back
+
+=head1 PREREQUISITES
+
+We need some tools to be installed:
+
+=over
+
+=item B<dig>
+
+The domain information grabber is used for the zone transfer currently.
+
+=item B<nsupdate>
+
+The nsupdate tool is used to send the updates back to the server.
+
+=back
+
+=cut