# HG changeset patch # User Heiko # Date 1401951019 -7200 # Node ID 50fbb0fb1120a0dc4430b71f7be7f014c3484cb0 # Parent 6d79c81a29313883d8eef6e9033903a2009c1e29 renamed to dns-vi diff -r 6d79c81a2931 -r 50fbb0fb1120 Build.PL --- a/Build.PL Thu Jun 05 00:10:22 2014 +0200 +++ b/Build.PL Thu Jun 05 08:50:19 2014 +0200 @@ -1,10 +1,10 @@ use Module::Build; Module::Build->new( - dist_name => 'dnsvi', - dist_version_from => 'bin/dnsvi', + dist_name => 'dns-vi', + dist_version_from => 'bin/dns-vi', dist_abstract => 'simple script to edit dynamic DNS zones', - script_files => ['bin/dnsvi'], + script_files => ['bin/dns-vi'], requires => { perl => 5.010, } diff -r 6d79c81a2931 -r 50fbb0fb1120 MANIFEST --- a/MANIFEST Thu Jun 05 00:10:22 2014 +0200 +++ b/MANIFEST Thu Jun 05 08:50:19 2014 +0200 @@ -1,5 +1,5 @@ .hgignore -bin/dnsvi +bin/dns-vi Build.PL lib/DNS/Vi.pm MANIFEST This list of files diff -r 6d79c81a2931 -r 50fbb0fb1120 bin/dns-vi --- /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 !~ /^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] [] + +=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 + +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 + +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 "" { + algorithm HMAC-MD5; + secret "" + }; + +Per zone you should use + + zone "" { + ... + update-policy { + grant local-ddns zonesub any; // support for -l + grant zonesub; // support for -k + }; + ... + }; + +=item B<-d> + +This option enables debugging of C. (default: off) + +=back + +=head1 PREREQUISITES + +We need some tools to be installed: + +=over + +=item B + +The domain information grabber is used for the zone transfer currently. + +=item B + +The nsupdate tool is used to send the updates back to the server. + +=back + +=cut diff -r 6d79c81a2931 -r 50fbb0fb1120 bin/dnsvi --- a/bin/dnsvi Thu Jun 05 00:10:22 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,173 +0,0 @@ -#! /usr/bin/perl -#line 3 -use 5.010; -use strict; -use warnings; -use if $ENV{DEBUG} // '' eq 'dnsvi' => '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 !~ /^y/i; - - update(\@zone1, $add, $del, { - -server => $o{server}, - -local => $o{local}, - -debug => $o{debug}, - -key => $o{key}}) - or do { - copy($fh2->filename, ",dnsvi-$$") - and say "Saved as ',dnsvi-$$'" - if $fh2; - }; - - return 0; -} - -exit main(@ARGV) if not caller; - -__END__ - -=head1 NAME - - dnsvi -- editor for dynamically maintained zones - -=head1 SYNOPSIS - - dnsvi [[-l] | [[-k key] [-s server]]] [-d] [] - -=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 - -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 - -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 "" { - algorithm HMAC-MD5; - secret "" - }; - -Per zone you should use - - zone "" { - ... - update-policy { - grant local-ddns zonesub any; // support for -l - grant zonesub; // support for -k - }; - ... - }; - -=item B<-d> - -This option enables debugging of C. (default: off) - -=back - -=head1 PREREQUISITES - -We need some tools to be installed: - -=over - -=item B - -The domain information grabber is used for the zone transfer currently. - -=item B - -The nsupdate tool is used to send the updates back to the server. - -=back - -=cut diff -r 6d79c81a2931 -r 50fbb0fb1120 t/10-dns-vi.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/t/10-dns-vi.t Thu Jun 05 08:50:19 2014 +0200 @@ -0,0 +1,47 @@ +#! perl + +use Test::More; +use strict; +use warnings; + +use_ok 'DNS::Vi' or BAIL_OUT 'DNS::Vi not found!'; +can_ok 'DNS::Vi', qw(ttl2h h2ttl parse nice delta edit update show); + +# TODO: more tests! +is ttl2h(86400), '1d', '-> 1d'; +is h2ttl('1d'), 86400, '<- 1d'; + +my $data1 = do { + local $/ = undef; + local @ARGV = 't/samples/kugelbus-axfr'; + <>; +}; + +# check parser with and without skip list +is parse($data1), 64 => '64 rrsets'; +my @zone1 = + parse($data1, { -skip => [qw(RRSIG NSEC3 NSEC3PARAM NSEC DNSKEY TSIG)] }); +is @zone1, 18 => '18 rrsets'; + +subtest 'parsed data' => sub { + my ($soa) = + map { $_->{rrset} } grep { $_->{rrset}{rrtype} eq 'SOA' } @zone1; + is ref $soa, 'HASH' => 'got result hash'; + is $soa->{rrtype}, 'SOA' => 'is SOA'; + is $soa->{data}, + 'pu.schlittermann.de. ' + . 'hostmaster.net.schlittermann.de. ' + . '18 86400 7200 604800 86400' => 'has expected data'; + + my @txt = map { $_->{rrset} } grep { $_->{rrset}{rrtype} eq 'TXT' } @zone1; + is @txt, 3 => 'got 3 txt records'; +}; + +# delta should find noting +subtest 'delta' => sub { + my ($add, $del) = delta(\@zone1, \@zone1); + is @$add, 0 => 'nothing added'; + is @$del, 0 => 'nothing deleted'; +}; + +done_testing(); diff -r 6d79c81a2931 -r 50fbb0fb1120 t/10-vidns.t --- a/t/10-vidns.t Thu Jun 05 00:10:22 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -#! perl - -use Test::More; -use strict; -use warnings; - -use_ok 'DNS::Vi' or BAIL_OUT 'DNS::Vi not found!'; -can_ok 'DNS::Vi', qw(ttl2h h2ttl parse nice delta edit update show); - -# TODO: more tests! -is ttl2h(86400), '1d', '-> 1d'; -is h2ttl('1d'), 86400, '<- 1d'; - -my $data1 = do { - local $/ = undef; - local @ARGV = 't/samples/kugelbus-axfr'; - <>; -}; - -# check parser with and without skip list -is parse($data1), 64 => '64 rrsets'; -my @zone1 = - parse($data1, { -skip => [qw(RRSIG NSEC3 NSEC3PARAM NSEC DNSKEY TSIG)] }); -is @zone1, 18 => '18 rrsets'; - -subtest 'parsed data' => sub { - my ($soa) = - map { $_->{rrset} } grep { $_->{rrset}{rrtype} eq 'SOA' } @zone1; - is ref $soa, 'HASH' => 'got result hash'; - is $soa->{rrtype}, 'SOA' => 'is SOA'; - is $soa->{data}, - 'pu.schlittermann.de. ' - . 'hostmaster.net.schlittermann.de. ' - . '18 86400 7200 604800 86400' => 'has expected data'; - - my @txt = map { $_->{rrset} } grep { $_->{rrset}{rrtype} eq 'TXT' } @zone1; - is @txt, 3 => 'got 3 txt records'; -}; - -# delta should find noting -subtest 'delta' => sub { - my ($add, $del) = delta(\@zone1, \@zone1); - is @$add, 0 => 'nothing added'; - is @$del, 0 => 'nothing deleted'; -}; - -done_testing();