seems to work so far
authorHeiko Schlittermann (JUMPER) <hs@schlittermann.de>
Thu, 22 May 2014 23:31:14 +0200
changeset 6 271dfe27e1d3
parent 5 70ecc1882968
child 7 286a373ab86b
seems to work so far needs to be done: -l for local auth
.hgignore
Build.PL
bin/vidns
lib/ViDNS.pm
t/10-vidns.t
t/kugelbus-axfr
vidns
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Thu May 22 23:31:14 2014 +0200
@@ -0,0 +1,4 @@
+syntax:glob
+_build/
+blib/
+Build
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Build.PL	Thu May 22 23:31:14 2014 +0200
@@ -0,0 +1,11 @@
+use Module::Build;
+
+Module::Build->new(
+    dist_name => 'vidns',
+    dist_version_from => 'bin/vidns',
+    dist_abstract => 'simple script to edit dynamic DNS zones',
+    script_files => ['bin/vidns'],
+    requires => {
+	perl => 5.010,
+    }
+)->create_build_script;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/vidns	Thu May 22 23:31:14 2014 +0200
@@ -0,0 +1,100 @@
+#! /usr/bin/perl
+#line 2
+use 5.010;
+use strict;
+use warnings;
+use if $ENV{DEBUG}//'' eq 'vidns' => 'Smart::Comments';
+use File::Temp;
+use Getopt::Long;
+use Pod::Usage;
+
+use blib;
+use ViDNS;
+
+sub main {
+    my %o = (
+        key    => undef,
+        server => undef,
+	debug => undef,
+    );
+
+    GetOptions(
+        'k|key=s'    => \$o{key},
+        's|server=s' => \$o{server},
+	'd|debug!'  => \$o{debug},
+      )
+      && @ARGV == 1
+      or pod2usage();
+
+    my @dig = (
+        dig => 'AXFR',
+        defined $o{key} ? (-k => $o{key}) : (),
+        defined $o{server} ? ("\@$o{server}") : (),
+        $ARGV[0]
+    );
+
+    my @zone1 = grep {
+        not $_->{rrset}{rrtype} ~~
+          [qw(RRSIG NSEC3 NSEC3PARAM NSEC DNSKEY TSIG)]
+    } parse($_ = `@dig`) or die $_;
+
+    my $tmp = File::Temp->new();
+    $tmp->print(nice @zone1);
+    $tmp->flush();
+    system $ENV{EDITOR} // 'vi' => $tmp->filename;
+    $tmp->seek(0, 0);
+    my @zone2 = parse(<$tmp>);
+    my ($add, $del) = delta(\@zone1, \@zone2);
+
+    my @cmds = ((map { "update add $_" } @$add),
+	       (map { "update delete $_" } @$del));
+
+    print <<_EOF, join "\n" => @cmds, '';
+# The following commands are about to be sent via nsupdate
+# to the master server:
+_EOF
+print '# Please confirm (yes/no): ';
+return 1 if <STDIN> ne "yes\n";
+
+    my @nsupdate = ('nsupdate', defined $o{debug} ? ('-d') : (), defined $o{key} ? (-k => $o{key}) : ());
+    open(my $nsupdate, '|-') or do {
+	exec @nsupdate;
+	die "Can't exec @nsupdate: $!\n";
+    };
+    print $nsupdate join "\n", @cmds, 'send', '';
+    close($nsupdate);
+    say "nsupdate returned $?";
+    return 0;
+}
+
+exit main(@ARGV) if not caller;
+
+__END__
+
+=head1 NAME
+
+ vidns -- editor for dynamically maintained zones
+
+=head1 SYNOPSIS
+
+ vidns [-k key] [-s server] [-d] <zone>
+
+=head1 DESCRIPTION
+
+=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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/ViDNS.pm	Thu May 22 23:31:14 2014 +0200
@@ -0,0 +1,175 @@
+use 5.010;
+use strict;
+use warnings;
+use if $ENV{DEBUG}//''  eq 'vidns' => 'Smart::Comments';
+use Digest::SHA qw(sha512_hex);
+
+use base 'Exporter';
+
+our @EXPORT = qw(ttl2h h2ttl parse delta);
+our @EXPORT_OK = ();
+
+sub parse {
+    my $data = join '', @_;
+    my @lines = split /\n/, $data;
+
+    my @zone;
+    my ($origin, $ttl, $last_label, $soa_seen);
+
+    foreach (@lines) {
+        s{;.*$}{};
+        given ($_) {
+            when (m{^\s*$})                { next }
+            when (m{^\s*\$ORIGIN\s+(\S+)}) { $origin = $1 }
+            when (m{^\s*\$TTL\s+(\S+)})    { $ttl = $1 }
+            when (
+                m{^(?<label>\S+)?
+		    \s+(?<ttl>\d[\dwdmhs]*(?=\s+))?
+		    \s+(?:(?:IN|ANY)\s+)?(?<rrtype>[a-z]\S*(?=\s+))
+		    \s+(?<data>.*)
+		  }ix
+              )
+            {
+                my %rrset = (
+                    label => $last_label =
+                      defined $+{label}
+                    ? $+{label} eq '@'
+                          ? $origin
+                          : $+{label}
+                    : $last_label,
+                      ttl => h2ttl($+{ttl} // $ttl),
+                      rrtype => uc $+{rrtype},
+                      data   => $+{data},
+                );
+
+                if ($rrset{rrtype} eq 'SOA') {
+                    next if $soa_seen;
+                    $soa_seen = 1;
+                }
+
+                # label ergänzen, wenn nicht FQDN
+                $rrset{label} .= ".$origin"
+                  unless substr($rrset{label}, -1) eq '.';
+
+                given ($rrset{rrtype}) {
+
+                    # origin steht im SOA
+                    when ('SOA') {
+                        $origin = $rrset{label};
+
+                        # fix the nameserver name
+                        $rrset{data} =~ s{^(\S+[^.])(?=\s)}{$1.$origin};
+
+                        # fix the hostmaster address
+                        $rrset{data} =~ s{^\S+\s+\K(\S+[^.])(?=\s)}{$1.$origin};
+                    }
+
+                    # bei einigen RRs müssen wir die Daten korrigieren
+                    when ([qw/MX NS PTR/]) {
+                        $rrset{data} .= ".$origin"
+                          unless substr($rrset{data}, -1) eq '.';
+                    }
+                }
+                my $id = sha512_hex(sort %rrset);
+                push @zone, { id => $id, rrset => \%rrset };
+            }
+        }
+    }
+
+    # list of {
+    #	id => $id,
+    #	rrset => { label => …, ttl => …, rrtype => …, data => … }
+    # }
+    ### @zone
+    return @zone;
+}
+
+sub ttl2h {
+    my $seconds = shift;
+    my @out;
+    my @units = ([w => 604800], [d => 86400], [h => 3600], [m => 60], [s => 1]);
+
+    foreach (@units) {
+        my $x = int($seconds / $_->[1]);
+        push @out, "$x$_->[0]" if $x;
+        $seconds %= $_->[1] or last;
+    }
+
+    return join '', @out;
+}
+
+sub h2ttl {
+    my $ttl = shift;
+    my $out;
+    my %factor = (
+        w => 604800,
+        d => 86400,
+        h => 3600,
+        m => 60,
+        s => 1,
+    );
+
+    while ($ttl =~ m{(\d+)([wdhms])}g) {
+        $out += $1 * $factor{$2};
+    }
+
+    return $out // $ttl;
+}
+
+sub nice {
+
+    # get a list of { id => $id, rrset => \%rrset }
+    my @zone =
+      sort {
+        length $a->{label} <=> length $b->{label}
+          or $a->{label}
+          cmp $b->{label}
+      } map { $_->{rrset} } @_;
+
+    my @out;
+    my $origin = (grep { $_->{rrtype} eq 'SOA' } @zone)[0]->{label};
+    my $ttl    = (grep { $_->{rrtype} eq 'SOA' } @zone)[0]->{ttl};
+    my $l1 = (sort map { index $_->{label}, '.' } @zone)[-1];
+    my $l2 = (sort map { length $_->{rrtype} } @zone)[-1];
+    push @out, "\$ORIGIN $origin", "\$TTL " . ttl2h($ttl);
+
+    my $print = sub {
+        my %r = %{ +shift };
+        state $last_label;
+
+        $r{label} = '@' if $r{label} eq $origin;
+        $r{label} =~ s{\.\Q$origin\E$}{};
+        $r{data} =~ s{\.\Q$origin\E$}{} if $r{rrtype} ~~ [qw(MX SOA PTR)];
+        $r{ttl} = $r{ttl} == $ttl ? '' : ttl2h($r{ttl});
+        $r{label} = do {
+            if (defined $last_label and $r{label} eq $last_label) { '' }
+            else { $last_label = $r{label} }
+        };
+
+        return sprintf '%-*s %6s %-*s    %s',
+          $l1 => $r{label},
+          $r{ttl},
+          $l2 => $r{rrtype},
+          $r{data};
+    };
+    push @out, $print->($_) foreach @zone;
+    return join "\n", @out;
+}
+
+sub delta {
+    my ($zone1, $zone2) = @_;
+    my %zone1 = map { $_->{id}, $_->{rrset} } @$zone1;
+    my %zone2 = map { $_->{id}, $_->{rrset} } @$zone2;
+    my @keys1 = keys %zone1;
+    my @keys2 = keys %zone2;
+    delete @zone1{@keys2};
+    delete @zone2{@keys1};
+
+    my (@add, @del);
+    push @add, "@{$_}{qw/label ttl rrtype data/}" foreach values %zone2;
+    push @del, "@{$_}{qw/label ttl rrtype data/}" foreach values %zone1;
+
+    return (\@add, \@del);
+}
+
+1;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/t/10-vidns.t	Thu May 22 23:31:14 2014 +0200
@@ -0,0 +1,30 @@
+#! perl
+
+use Test::More;
+use strict;
+use warnings;
+
+use_ok 'ViDNS' or BAIL_OUT 'ViDNS not found!';
+#can_ok 'ViDNS', qw(ttl2h h2ttl);
+
+is ttl2h(86400), '1d', '-> 1d';
+is h2ttl('1d'), 86400, '<- 1d';
+
+my $data = do { 
+    local $/ = undef;
+    local @ARGV = 't/kugelbus-axfr';
+    <>;
+};
+
+my @zone1 = parse($data);
+is @zone1, 64 => '64 rrsets';
+
+$data =~ s{^kugelbus.*?IN\s+MX.*?$}{}m;
+my @zone2 = parse($data);
+is @zone2, 63, => '63 rrsets';
+
+my ($add, $del) = delta(\@zone1, \@zone2);
+is @$add, 0 => 'nothing to add';
+is @$del, 1 => '1 set to delete';
+
+done_testing();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/t/kugelbus-axfr	Thu May 22 23:31:14 2014 +0200
@@ -0,0 +1,74 @@
+
+; <<>> DiG 9.8.4-rpz2+rl005.12-P1 <<>> @dns.net -k Kheiko.+157+01468.key kugelbus.de axfr
+; (1 server found)
+;; global options: +cmd
+kugelbus.de.		86400	IN	SOA	pu.schlittermann.de. hostmaster.net.schlittermann.de. 18 86400 7200 604800 86400
+kugelbus.de.		86400	IN	RRSIG	MX 7 2 86400 20140621150450 20140522140450 31247 kugelbus.de. MH9jNir2BQVLwnAs92Lks0W3MwsGf6MQqQ15RSGiMfbbgo8IaqgYrBlZ +IcGFuFVX7PI29Y5HtOyVtExKfwGQc2qPKoukFbQ3Ekxcr2+zgvpJZl+ 9U6+S0c78dUSLKsl1Sc1a3P7p2F2fb0nWPx0ZewgT3PmTxZ7OtjOQAaK /bY=
+kugelbus.de.		86400	IN	MX	10 ssl.schlittermann.de.
+kugelbus.de.		86400	IN	RRSIG	SOA 7 2 86400 20140621150450 20140522140450 31247 kugelbus.de. e2vsx6u/peYH0x/NzPE8dR/NHqORM30WWWL1SdRNcYra/Noo4gksv04z WwQXyIVKhQI6C0owgsel0MqD9JvfGMGNedR481ZZLTNiRe/94uLkazbk WCDjPb+1pD7+qGQHetiay+ODOqRDMZCmH+jyC3zHiS5TD6Hn7jQdRnyf hUs=
+kugelbus.de.		86400	IN	NS	hh.schlittermann.de.
+kugelbus.de.		86400	IN	NS	pu.schlittermann.de.
+kugelbus.de.		86400	IN	RRSIG	NS 7 2 86400 20140615074907 20140516074907 31247 kugelbus.de. zuaG0KnQn1cHzg4uqlaRb+JQyN+nRzuETI/mep0EShqOm5m95S9VS3RB lPzZaVYwXkAVycrKfL9Md9eXstofpYdFIHobEfKi1Mp0cgTe6IHqyRyD 6WiCEJ8F9DtBm1kZkbKAcwJLv2BH0lZXS1esa+Zjj17qFDxW7UpUc5jU prk=
+kugelbus.de.		86400	IN	TXT	"invoice: self"
+kugelbus.de.		86400	IN	TXT	"start: 2010-08-02"
+kugelbus.de.		86400	IN	TXT	"utf8: kugelbus.de"
+kugelbus.de.		86400	IN	RRSIG	TXT 7 2 86400 20140615074907 20140516074907 31247 kugelbus.de. qZ0G0EV4ZmxNNEZFvN/6yGqtBrhxHPNFpuqhMLqn+qqHdDZhL+iESH84 rhaoUgqj038pS12qt3rjcGxV1PbBPIFhLz3Gj9oxOi8f9fxahu53V65z sqEOoh67ZuqldBPGjFmUwBDP6BlYYFdaw+jtZxsXB8MTUepbmhWUdCgu Kt4=
+kugelbus.de.		86400	IN	DNSKEY	256 3 7 AwEAAdIZfJL/QFKHxvrZno72yGcqVDa1O815gjhRK8fy5LEHjTDJE2uk 5y1QWFBnW717WGnVgTBZ0xb6rpsNGYwhEKZQOfu7pFg7NimhOFSh6wvN 0v0DObM1ieFgnuHCG/h9SW8Sr1t+K+Fwuccg5psEvX6FEQp25CukOk+Z NCwSll1X
+kugelbus.de.		86400	IN	DNSKEY	257 3 7 AwEAAb4oHp7JRlcdwS5Mqui8PGdgMNWpPZCNJXOQsozIwtI4hznLZgOs 78FMXpaoHrv5BkHSfO41/Q7iq5wVrnCoQvxHS94V4LwcoFfHjVOrug1m by3X2zkNX+8vilh091fN3Ox3lqNO0mvfVzpLkQfWgSJOslxGDdqQmQtH rMfTiQMAUwYLoZz5RfwVl4iiIrzzChBiwtZqXgJZp4ZD9tnBwEXlavjU bv22oQmEawJ0zmvnM3Ynvu/5PQ1BMDgFmHbNCZNdyGKA0K37VUcaxxQ/ Cq9bMh3df8xJifRlJqQnkJqbGn27f+2+vaV1aDeh5ch/5/Nmffr8z+iv iQVBSxMT2e0=
+kugelbus.de.		86400	IN	RRSIG	DNSKEY 7 2 86400 20140615090152 20140516080152 14493 kugelbus.de. QideHWCqOJJE3eKRFoJFkT7GIpxl1bnCSlqXVwvpNCeCyaq8TpQgIIRo Ssoxn1fgb9YonCmw2eXqvAlE7R2fZL7f/aulHx8ZJreOyZGD3HUr95QY PpAckMkWeGba0N4QamKJw0pcqgiBAOb1gcZZBoSnlfB4i2JNQypKKdha 3L2I+CC1xElDZJ4fWbDanDIsQd2HAgRNeGnxciFxzghr3eBVjCuy+YUM VLqyoJLsLayJe5fr+bMu02cIPMMwr6XHPmm7Zvv8xrCQFJi7fzu34Hyp oXcWRmWaEYpUdMtTAqciMsTKkGL2OBT5oad1D4Zdhib7xr/QqA2feijd zDyddg==
+kugelbus.de.		86400	IN	RRSIG	DNSKEY 7 2 86400 20140615090152 20140516080152 31247 kugelbus.de. Zj3yV6cabdIe8QHOTm1bFyogap/Og2Fxk+x9jrIgXAbBnsA1Xkg5+mCZ Xu2KQiAq3rNPmTl88friIvqQ/uJI6MGq+SvwxroEn5mwdCNEvYIoeqUf bB8C21w2SLXILKSGScSgHDxXeQdQm2d+53yB+N5or21OIZnTU6tTyqps +ZE=
+kugelbus.de.		0	IN	NSEC3PARAM 1 0 10 0DDEAD910F3E8BCC238CDAF371F7838482DAFF67
+kugelbus.de.		0	IN	RRSIG	NSEC3PARAM 7 2 0 20140615074907 20140516074907 31247 kugelbus.de. Mt8iczuDi72mCoRz0A+zSs41eSGBVi9mRKaGzPqXVbv0fhtyzv83DBn7 Ls92FtOCPr+P3O1QzgMgxnBWS1sF31tCUghgj1TFmZ7qbQnuSYAOjVzx JZBWsOhWzyedHWq0KJRclu979tPLWwotUH8kBu2FmHC9nBKFeTmp/f4w MOA=
+a.kugelbus.de.		86400	IN	A	192.178.168.222
+a.kugelbus.de.		86400	IN	RRSIG	A 7 3 86400 20140615074907 20140516074907 31247 kugelbus.de. zfUMLhsf8hscpPLcCNg3SGH1DbP4yy4eQxc8EC8yiZski/i4f3pXeHKg M3U0y60rHSvfS0JKy85EkIMQg0csSUp/z78J1JZ7dyKoKJwoiffU8vUO Blpn+YPwrTILsyeyU1u5SyeYyqWVv+9s/6OxXQwgfqOHaeVuWLTUpebt O8Y=
+firma180.kugelbus.de.	86400	IN	MX	10 k180.kugelbus.de.
+firma180.kugelbus.de.	86400	IN	RRSIG	MX 7 3 86400 20140615074907 20140516074907 31247 kugelbus.de. Vaui/f1kz6HubIPypbo/lGI55hwqJWDj9XBzs1y6rowdDx31ia6Jpb27 Y92+W2gQwUEj9Ft/4cZcq3lK0JV9bgMEwL4KxSJbPb0lMAQA9afJ9Y35 +5UT8t05hzZJCFjvtqVjlhGTeKwjJ4WjTN7jVBonWExJuhpgSHVWHW/x MZ4=
+firma181.kugelbus.de.	86400	IN	MX	10 k181.kugelbus.de.
+firma181.kugelbus.de.	86400	IN	RRSIG	MX 7 3 86400 20140615074907 20140516074907 31247 kugelbus.de. xmyeEupCXUv5MqXRKfkAByEfsos0wRwKDMORAsmpkLKI3MBen+8Sz+VG cR5htim8hRWZzztrOT4SwJa7kR0N2Wolpg/Aylj8cOAozaU+iwTTusrE dgYWaP3uuWn8BkH8HsXUrq9XvuC59vWNkmiftDOgNx+y/OlhzSv5scfw r5I=
+firma182.kugelbus.de.	86400	IN	MX	10 k182.kugelbus.de.
+firma182.kugelbus.de.	86400	IN	RRSIG	MX 7 3 86400 20140615074907 20140516074907 31247 kugelbus.de. TDDLPx/rOFDc6FquH+9NGgLxndJ967NbaOPTf24up+MQfbXN/2IBwRKc LGK38eGJNX6SbWkgcvmTgS4bMcd/gUD7P+amaZ/2yOnG81PP7iAb8sYS TLkapOK8Z1Sw/TQkLfVKH3nXWua0ujjoAohVjChjjnNVir0rqW99lmZ2 lI8=
+firma183.kugelbus.de.	86400	IN	MX	10 k183.kugelbus.de.
+firma183.kugelbus.de.	86400	IN	RRSIG	MX 7 3 86400 20140615074907 20140516074907 31247 kugelbus.de. bg0Ag9swRX0FgS8pHI9LIEP03qIZoHvAe5Q3Qai513PPPr3zlPXdj9f3 P0u0UNd7EcBfYODGRIzVyqksCe5gkQ30WbHELQ/d0xwlLUFmXGOfXOl+ kNaYhTqa/CQexFlSIDt+S/o9hXI2LrRQxmVL6Yctbl02NwbZ13zzEuL2 Qg8=
+k180.kugelbus.de.	86400	IN	A	212.80.235.180
+k180.kugelbus.de.	86400	IN	RRSIG	A 7 3 86400 20140615074907 20140516074907 31247 kugelbus.de. D+k+z/5OJ7geyq6NjQF77ZzI1mBjWrX0cJz+ZVNBU3FKENiSHecprRg/ WSb30yjsKwiq0kz65qvBi///ymtf5rYa/6/5d3Px07n3Q6zXDjarEBR7 EPTZldDcbdKYfxpqyNun574tL2Yf8cP9jg4+3IEmQRTDPpBAVhLPr7a/ A4k=
+k181.kugelbus.de.	86400	IN	A	212.80.235.181
+k181.kugelbus.de.	86400	IN	RRSIG	A 7 3 86400 20140615074907 20140516074907 31247 kugelbus.de. e6lWuLkcL7HGCasqKnFijTmOUnk94Ib5JiuUaaFKQvBzyx/jQ6nRo2aq qZEnGn6+cE/cVALusIV6tB6VVh0Vv+MvguX43XehkCr5+d2cAyC2aHuc LISucaBj5Y46v6M/mvnHvKcXWAc2F5Ml4Z5tksHK47deAcvpHZ9Ybe+f t1s=
+k182.kugelbus.de.	86400	IN	A	212.80.235.182
+k182.kugelbus.de.	86400	IN	RRSIG	A 7 3 86400 20140615074907 20140516074907 31247 kugelbus.de. Ufz7b8jU0HeTwiSsWGf3BUso0Dh3l3dEuAUQj5ojCQ7wzePQoQWtw/jZ VTpXkd2ARV/L9W8oYnoxW/2HbTiqAaC4jkJOFEQxaAF3dzuSpdAm0/mI qbRznDd1KZYYVN5pROZNZ+FBI6P764ErUQLUcvR+osdqYrYJm4bz4S69 jF4=
+k183.kugelbus.de.	86400	IN	A	212.80.235.183
+k183.kugelbus.de.	86400	IN	RRSIG	A 7 3 86400 20140615074907 20140516074907 31247 kugelbus.de. iXVgdtt2ZPa/YhrqBa5NcFVwGIr8m7II4UwPgNlBZU2Z94UQm9KVZ359 Bs6/H7IGm9DMZcRMbE3qYjXUS3dc7hjd3/dCBuMd2tm9aEiFA1A0daAs +c2nOq6HSMyxbrskByMZzVSAmyIyHmVi/hIsskYwTTO+fdI1nSfCeDNB +PY=
+rp.kugelbus.de.		3600	IN	RRSIG	AAAA 7 3 3600 20140620114226 20140521104226 31247 kugelbus.de. bjETOc6PAR141lLJ9ASX1TAtktclScMhTSsTG4vVveF4GVLmWSAXioTv DQv7sL7BlymKYEPC5iofpqqQkViDv9a+JS9ZRLVL0gHDbmifAjQj/64L TPdoSM7qggTlbkTpdwx1xP7dnoX3y+8ji4mqWlk19+jyEojKI3nXk4ZJ M98=
+rp.kugelbus.de.		3600	IN	AAAA	2a01:1e8:e100:847c::1
+www.kugelbus.de.	86400	IN	A	212.80.235.150
+www.kugelbus.de.	86400	IN	RRSIG	A 7 3 86400 20140615074907 20140516074907 31247 kugelbus.de. IiBFYhBBRdTsH3PbKuL37EweOtctmxPMB9quMYkRkk/Vok2E+hRhv/Rl ZyiHE/kiiFSVreUc4L6ZN7ZZhpuNOVGakXKMiRDTd/pLbs9b28PgwEge 6TeSUdKNSlOS2TkbJLEUdXIP8iRmxRfjc7ISavg3dCPMOBXGUSno3UZw azI=
+0R9CKH4HK453VFREKG89VSMUV2FDN3SA.kugelbus.de. 86400 IN NSEC3 1 0 10 0DDEAD910F3E8BCC238CDAF371F7838482DAFF67 0U6GJVGAK0B95KBBUHB8R8P6HTL3Q0GH A RRSIG
+0R9CKH4HK453VFREKG89VSMUV2FDN3SA.kugelbus.de. 86400 IN RRSIG NSEC3 7 3 86400 20140615074907 20140516074907 31247 kugelbus.de. BJshX3u76wb7oJG2heXfljEIFrQQhqX4zhEqFQXiHeLA1GF9xl8v3b7w mGs3O/dnFWjzCy7Qy+zs59lE+0yvWlrnzdZzpLUWcJUsfZaTDTZSIU2u pEECtVAYl0eIopWERVs2nOAgDIStbxB/qcmAxtlhp8AfMwkKjTDQJnHp DYs=
+0U6GJVGAK0B95KBBUHB8R8P6HTL3Q0GH.kugelbus.de. 86400 IN NSEC3 1 0 10 0DDEAD910F3E8BCC238CDAF371F7838482DAFF67 2593VCBJBL3VTVHN2IK23CCIRUCGLEAA MX RRSIG
+0U6GJVGAK0B95KBBUHB8R8P6HTL3Q0GH.kugelbus.de. 86400 IN RRSIG NSEC3 7 3 86400 20140615074907 20140516074907 31247 kugelbus.de. wH3ECYBc8KHTfdE/lLCtYUQYrZ2eGqwTxJbZCkPLuVQ7WVuFGS7XfKT0 yBdaOesJdBIFNKRSM0uDh0mnxwaLDx8GY2jP4EMTzm6FXN9CFs00Nscm MrgyV+0ZrSvu8VCj/sbsXra9GvtrcgU9tM3L5nK+2p76oiPzglnqxoXn kDs=
+2593VCBJBL3VTVHN2IK23CCIRUCGLEAA.kugelbus.de. 86400 IN NSEC3 1 0 10 0DDEAD910F3E8BCC238CDAF371F7838482DAFF67 2DM84U3H3AA2VNMU33OIKA1QG7Q3TQHV A RRSIG
+2593VCBJBL3VTVHN2IK23CCIRUCGLEAA.kugelbus.de. 86400 IN RRSIG NSEC3 7 3 86400 20140615074907 20140516074907 31247 kugelbus.de. Pq3uZBOYVNmJrgkkDN1FsNwiMWtVTroUtA6m1zAxZGUU6kpTLdvhrdC8 X9h6WzeyR9Jbm83WNMnKxiSXq6vbYSOU9LQVnpJN9wREVqxqwwIJSgcX MuowjgjWmfjU7NH3RFR7YxPo4MUoAOanYXpSQy0GKd7dFPCbIfMj9U4U BFw=
+2DM84U3H3AA2VNMU33OIKA1QG7Q3TQHV.kugelbus.de. 86400 IN NSEC3 1 0 10 0DDEAD910F3E8BCC238CDAF371F7838482DAFF67 2TPQK9E8RVRMD8PQUDV9KNAHA4HQKBDN A RRSIG
+2DM84U3H3AA2VNMU33OIKA1QG7Q3TQHV.kugelbus.de. 86400 IN RRSIG NSEC3 7 3 86400 20140620114118 20140521104118 31247 kugelbus.de. cHJ43Qircpo71H+hPGFkXfNwa+C0R49tLJFoB1UVn9KGzl/X3yv88IVA Th4+nJMz2eSgCx80EXGr+XDxqDwwVYsRxmirg6eLqGyIYxzmuRYSFqfJ aNPeCPgS6eMgUj+5wIYOmtmvFaO/rYqzlZ+xiK5mLF73npFL9HsADSkR DjA=
+2TPQK9E8RVRMD8PQUDV9KNAHA4HQKBDN.kugelbus.de. 86400 IN NSEC3 1 0 10 0DDEAD910F3E8BCC238CDAF371F7838482DAFF67 8H88OTVRBHS3LE691SGLBI1MDR3HNCL3 MX RRSIG
+2TPQK9E8RVRMD8PQUDV9KNAHA4HQKBDN.kugelbus.de. 86400 IN RRSIG NSEC3 7 3 86400 20140615074907 20140516074907 31247 kugelbus.de. BtwkglyaKHGJM2YacX1dZAGhrQkG/5zpziK9JUErTfSJ+UHQCaFGdZX+ uhKIjgUWH5Np0rd7MtDEBvtewSX+tw8B+bYmZqGQtJkEdi44Vk40B5yk mchb0s7wfL0B5zDJcSwcHIvsXxGFMr4jyl0JOaOQ7Tp0bq+dAOqjm+Hl Phc=
+8H88OTVRBHS3LE691SGLBI1MDR3HNCL3.kugelbus.de. 86400 IN NSEC3 1 0 10 0DDEAD910F3E8BCC238CDAF371F7838482DAFF67 BOS537U20E73QOBE3N4QOHPHB4BPK192 MX RRSIG
+8H88OTVRBHS3LE691SGLBI1MDR3HNCL3.kugelbus.de. 86400 IN RRSIG NSEC3 7 3 86400 20140615074907 20140516074907 31247 kugelbus.de. JMc1AFtP3c+Od9TNS/d8P+lpkHQG/LnKrJAGi6IwZPiMXi+COTExUHUl E7+gBz5owogv9LwIfIFpulUMAjpx9XaCQF36qb1ke54ceKHV+zjhQZEs s8MGjwsxjLy65oN2tBfoWx07jiWLAYKUFpiFWjLuZIzDMw4trgWkn9QR jY8=
+BOS537U20E73QOBE3N4QOHPHB4BPK192.kugelbus.de. 86400 IN NSEC3 1 0 10 0DDEAD910F3E8BCC238CDAF371F7838482DAFF67 D8IBS9FUE7TQPC73J8LQD0NAKUU9NTL9 A RRSIG
+BOS537U20E73QOBE3N4QOHPHB4BPK192.kugelbus.de. 86400 IN RRSIG NSEC3 7 3 86400 20140618211808 20140519201808 31247 kugelbus.de. e1AkRwzd5FxcX23zD/wiC2dBOzXGGz4f54VfbjfazQaVy84gRSl7L7K4 I1zIkoE+DYeW9URhsEoiXQgjzmlG6GhSwwSLaOdbKyFLO5cY+u9sWoAW rnupf6/HzyLkqeC+OGdKq9mkZ2j+bU3oQgllKr3KphnxCp49BcKaswhE I+g=
+D8IBS9FUE7TQPC73J8LQD0NAKUU9NTL9.kugelbus.de. 86400 IN NSEC3 1 0 10 0DDEAD910F3E8BCC238CDAF371F7838482DAFF67 ELVRHMFILU1ER47ON682O2E7Q1HTA6T6 NS SOA MX TXT RRSIG DNSKEY NSEC3PARAM
+D8IBS9FUE7TQPC73J8LQD0NAKUU9NTL9.kugelbus.de. 86400 IN RRSIG NSEC3 7 3 86400 20140620114237 20140521104237 31247 kugelbus.de. lgRK+kouaMP6M+LhTgYkrDczfMQeUQiph85mb/45Wl/MRjw/E8bipLeI hMecImUz+Iusf0wJZ0zqNxS8sMMTQKxIfWtEkwjrkq/rF9x+izk+FIxj XhF6dOl3rbr6haUeRRY/GEIykPw4dFp39K2/usteoSyXEv/XDQ586Yxj kwA=
+ELVRHMFILU1ER47ON682O2E7Q1HTA6T6.kugelbus.de. 86400 IN RRSIG NSEC3 7 3 86400 20140620114226 20140521104226 31247 kugelbus.de. fq9Azb1gDrtyDo2bbh0SURvc4V9AlgFb08BPAQXAQjhABipLYq/7NupJ 0xGeQYGdQasAxNXvRPflRhdd6iwiAZrBDIOPxjAvZ4I5iRRFKu5m0n6R 8NXay50ymMbEPHASmoN7y78XxJqZIxlusfclv6hlTCaJeYABo1QnuZwg 4N4=
+ELVRHMFILU1ER47ON682O2E7Q1HTA6T6.kugelbus.de. 86400 IN NSEC3 1 0 10 0DDEAD910F3E8BCC238CDAF371F7838482DAFF67 NI0SJC95TMP2E2PF58A2460KK7LUD3VO AAAA RRSIG
+NI0SJC95TMP2E2PF58A2460KK7LUD3VO.kugelbus.de. 86400 IN NSEC3 1 0 10 0DDEAD910F3E8BCC238CDAF371F7838482DAFF67 R4SPOIPJ3LVFG96IPUEFH9SRARP9VV31 A RRSIG
+NI0SJC95TMP2E2PF58A2460KK7LUD3VO.kugelbus.de. 86400 IN RRSIG NSEC3 7 3 86400 20140618212211 20140519202211 31247 kugelbus.de. SBTnTWJ7OHxNm96xz9w5vDn4VEW8IejMCJUeox0Zk7CGfEErxVyFc4a7 ek0lkPi2p90c+Exh7zN0X9tHD6OfGSjCkMtdUERn+7OG7HIsTAwWJiwj Za/wj6rrZJ2rSPnDH9sNyMSSh4Sl9wtkTwZ4WCdRvUtUfwrXlIMO/YNK c5Q=
+R4SPOIPJ3LVFG96IPUEFH9SRARP9VV31.kugelbus.de. 86400 IN NSEC3 1 0 10 0DDEAD910F3E8BCC238CDAF371F7838482DAFF67 RMC55B74CLBU1C4I8I3NBFLM3OU5VVT2 A RRSIG
+R4SPOIPJ3LVFG96IPUEFH9SRARP9VV31.kugelbus.de. 86400 IN RRSIG NSEC3 7 3 86400 20140615074907 20140516074907 31247 kugelbus.de. jjr1yN9BTOSc5qWTfkO2JBGPEoQskcHGpgIfoqCeyaK/w+yf467AAB0n sZgmRcGve42LwZUwbe+GhCBr/QGgKEsMqNZYtSPcivrIhdySVkBxcoSS OIBTXiE0nYufgxcEuVt6n8lWoUq6kep6xZ/lxfcPpIyAFqL/QlWqv+q0 lVw=
+RMC55B74CLBU1C4I8I3NBFLM3OU5VVT2.kugelbus.de. 86400 IN NSEC3 1 0 10 0DDEAD910F3E8BCC238CDAF371F7838482DAFF67 0R9CKH4HK453VFREKG89VSMUV2FDN3SA MX RRSIG
+RMC55B74CLBU1C4I8I3NBFLM3OU5VVT2.kugelbus.de. 86400 IN RRSIG NSEC3 7 3 86400 20140615090338 20140516080338 31247 kugelbus.de. OUIDj0k1SXx1ua8SS3vKGJfojnCcTS4dcYnyuFfBxWD7N+Lbiwp4BuNI I8Z5V0IYCQy6x/rBcpiqJF11PTPHxtAk3gehB+Yu2KechP7n3UIcXFn0 LMNMcvjGa7Fa4ntv/zBWZZgMmbCsa3Jxjx8VnhXdsmLsMeoyvvODy164 BXo=
+kugelbus.de.		86400	IN	SOA	pu.schlittermann.de. hostmaster.net.schlittermann.de. 18 86400 7200 604800 86400
+heiko.			0	ANY	TSIG	hmac-md5.sig-alg.reg.int. 1400790918 300 16 Ithy5I1QiLQHkWU7i9GqXA== 49003 NOERROR 0 
+;; Query time: 51 msec
+;; SERVER: 84.19.194.5#53(84.19.194.5)
+;; WHEN: Thu May 22 22:35:18 2014
+;; XFR size: 64 records (messages 1, bytes 7506)
+
--- a/vidns	Thu May 22 17:18:07 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,237 +0,0 @@
-#! /usr/bin/perl
-use 5.010;
-use strict;
-use warnings;
-use File::Temp;
-use Smart::Comments;
-use Digest::SHA qw(sha512_hex);
-use Getopt::Long;
-use Pod::Usage;
-
-sub parse {
-    my $data = join '', @_;
-    my @lines = split /\n/, $data;
-
-    my @zone;
-    my ($origin, $ttl, $last_label, $soa_seen);
-
-    foreach (@lines) {
-        s{;.*$}{};
-        given ($_) {
-            when (m{^\s*$})                { next }
-            when (m{^\s*\$ORIGIN\s+(\S+)}) { $origin = $1 }
-            when (m{^\s*\$TTL\s+(\S+)})    { $ttl = $1 }
-            when (
-                m{^(?<label>\S+)?
-		    \s+(?<ttl>\d[\dwdmhs]*(?=\s+))?
-		    \s+(?:(?:IN|ANY)\s+)?(?<rrtype>[a-z]\S*(?=\s+))
-		    \s+(?<data>.*)
-		  }ix
-              )
-            {
-                my %rrset = (
-                    label => $last_label =
-                      defined $+{label}
-                    ? $+{label} eq '@'
-                          ? $origin
-                          : $+{label}
-                    : $last_label,
-                      ttl => h2ttl($+{ttl} // $ttl),
-                      rrtype => uc $+{rrtype},
-                      data   => $+{data},
-                );
-
-                if ($rrset{rrtype} eq 'SOA') {
-                    next if $soa_seen;
-                    $soa_seen = 1;
-                }
-
-                # label ergänzen, wenn nicht FQDN
-                $rrset{label} .= ".$origin"
-                  unless substr($rrset{label}, -1) eq '.';
-
-                given ($rrset{rrtype}) {
-
-                    # origin steht im SOA
-                    when ('SOA') {
-                        $origin = $rrset{label};
-
-                        # fix the nameserver name
-                        $rrset{data} =~ s{^(\S+[^.])(?=\s)}{$1.$origin};
-
-                        # fix the hostmaster address
-                        $rrset{data} =~ s{^\S+\s+\K(\S+[^.])(?=\s)}{$1.$origin};
-                    }
-
-                    # bei einigen RRs müssen wir die Daten korrigieren
-                    when ([qw/MX NS PTR/]) {
-                        $rrset{data} .= ".$origin"
-                          unless substr($rrset{data}, -1) eq '.';
-                    }
-                }
-                my $id = sha512_hex(sort %rrset);
-                push @zone, { id => $id, rrset => \%rrset };
-            }
-        }
-    }
-
-    # list of {
-    #	id => $id,
-    #	rrset => { label => …, ttl => …, rrtype => …, data => … }
-    # }
-    return @zone;
-}
-
-sub ttl2h {
-    my $seconds = shift;
-    my @out;
-    my @units = ([w => 604800], [d => 86400], [h => 3600], [m => 60], [s => 1]);
-
-    foreach (@units) {
-        my $x = int($seconds / $_->[1]);
-        push @out, "$x$_->[0]" if $x;
-        $seconds %= $_->[1] or last;
-    }
-
-    return join '', @out;
-}
-
-sub h2ttl {
-    my $ttl = shift;
-    my $out;
-    my %factor = (
-        w => 604800,
-        d => 86400,
-        h => 3600,
-        m => 60,
-        s => 1,
-    );
-
-    while ($ttl =~ m{(\d+)([wdhms])}g) {
-        $out += $1 * $factor{$2};
-    }
-
-    return $out // $ttl;
-}
-
-sub nice {
-
-    # get a list of { id => $id, rrset => \%rrset }
-    my @zone =
-      sort {
-        length $a->{label} <=> length $b->{label}
-          or $a->{label}
-          cmp $b->{label}
-      } map { $_->{rrset} } @_;
-
-    my @out;
-    my $origin = (grep { $_->{rrtype} eq 'SOA' } @zone)[0]->{label};
-    my $ttl    = (grep { $_->{rrtype} eq 'SOA' } @zone)[0]->{ttl};
-    my $l1 = (sort map { index $_->{label}, '.' } @zone)[-1];
-    my $l2 = (sort map { length $_->{rrtype} } @zone)[-1];
-    push @out, "\$ORIGIN $origin", "\$TTL " . ttl2h($ttl);
-
-    my $print = sub {
-        my %r = %{ +shift };
-        state $last_label;
-
-        $r{label} = '@' if $r{label} eq $origin;
-        $r{label} =~ s{\.\Q$origin\E$}{};
-        $r{data} =~ s{\.\Q$origin\E$}{} if $r{rrtype} ~~ [qw(MX SOA PTR)];
-        $r{ttl} = $r{ttl} == $ttl ? '' : ttl2h($r{ttl});
-        $r{label} = do {
-            if (defined $last_label and $r{label} eq $last_label) { '' }
-            else { $last_label = $r{label} }
-        };
-
-        return sprintf '%-*s %6s %-*s    %s',
-          $l1 => $r{label},
-          $r{ttl},
-          $l2 => $r{rrtype},
-          $r{data};
-    };
-    push @out, $print->($_) foreach @zone;
-    return join "\n", @out;
-}
-
-sub delta {
-    my ($zone1, $zone2) = @_;
-    my %zone1 = map { $_->{id}, $_->{rrset} } @$zone1;
-    my %zone2 = map { $_->{id}, $_->{rrset} } @$zone2;
-    my @keys1 = keys %zone1;
-    my @keys2 = keys %zone2;
-    delete @zone1{@keys2};
-    delete @zone2{@keys1};
-    say 'update add ', join ' ' => @{$_}{qw/label ttl rrtype data/}
-      foreach values %zone2;
-    say 'update delete ', join ' ' => @{$_}{qw/label ttl rrtype data/}
-      foreach values %zone1;
-    exit;
-}
-
-sub main {
-    my %o = (
-        key    => undef,
-        server => undef,
-    );
-
-    GetOptions(
-        'k|key=s'    => \$o{key},
-        's|server=s' => \$o{server},
-      )
-      && @ARGV == 1
-      or pod2usage();
-
-    my @dig = (
-        dig => 'AXFR',
-        defined $o{key} ? (-k => $o{key}) : (),
-        defined $o{server} ? ("\@$o{server}") : (),
-        $ARGV[0]
-    );
-
-    my @zone1 = grep {
-        not $_->{rrset}{rrtype} ~~
-          [qw(RRSIG NSEC3 NSEC3PARAM NSEC DNSKEY TSIG)]
-    } parse(`@dig`);
-
-    my $tmp = File::Temp->new();
-    $tmp->print(nice @zone1);
-    $tmp->flush();
-    system $ENV{EDITOR} // 'vi' => $tmp->filename;
-    $tmp->seek(0, 0);
-    my @zone2 = parse(<$tmp>);
-    delta(\@zone1, \@zone2);
-    exit;
-}
-
-exit main(@ARGV) if not caller;
-
-__END__
-
-=head1 NAME
-
- vidns -- editor for dynamically maintained zones
-
-=head1 SYNOPSIS
-
- vidns [-k key] [-s server] <zone>
-
-=head1 DESCRIPTION
-
-=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