vidns
changeset 5 70ecc1882968
parent 4 f77aa03e2d39
equal deleted inserted replaced
4:f77aa03e2d39 5:70ecc1882968
     3 use strict;
     3 use strict;
     4 use warnings;
     4 use warnings;
     5 use File::Temp;
     5 use File::Temp;
     6 use Smart::Comments;
     6 use Smart::Comments;
     7 use Digest::SHA qw(sha512_hex);
     7 use Digest::SHA qw(sha512_hex);
       
     8 use Getopt::Long;
       
     9 use Pod::Usage;
     8 
    10 
     9 sub parse {
    11 sub parse {
    10     my $file = shift;
    12     my $data = join '', @_;
    11     my @lines = split /\n/, do {
    13     my @lines = split /\n/, $data;
    12         local $/    = undef;
       
    13         local @ARGV = $file;
       
    14         <>;
       
    15     };
       
    16 
    14 
    17     my @zone;
    15     my @zone;
    18     my ($origin, $ttl, $last_label, $soa_seen);
    16     my ($origin, $ttl, $last_label, $soa_seen);
    19 
    17 
    20     foreach (@lines) {
    18     foreach (@lines) {
    21         s{;.*$}{};
    19         s{;.*$}{};
    22         given ($_) {
    20         given ($_) {
       
    21             when (m{^\s*$})                { next }
    23             when (m{^\s*\$ORIGIN\s+(\S+)}) { $origin = $1 }
    22             when (m{^\s*\$ORIGIN\s+(\S+)}) { $origin = $1 }
    24             when (m{^\s*\$TTL\s+(\S+)})    { $ttl    = $1 }
    23             when (m{^\s*\$TTL\s+(\S+)})    { $ttl = $1 }
    25             when (
    24             when (
    26                 m{^(?<label>\S+)?
    25                 m{^(?<label>\S+)?
    27 		    \s+(?<ttl>\S+(?=\s+))?
    26 		    \s+(?<ttl>\d[\dwdmhs]*(?=\s+))?
    28 		    \s+(?:(?:IN|ANY)\s+)?(?<rrtype>\S+(?=\s+))
    27 		    \s+(?:(?:IN|ANY)\s+)?(?<rrtype>[a-z]\S*(?=\s+))
    29 		    \s+(?<data>.*)
    28 		    \s+(?<data>.*)
    30 		  }x
    29 		  }ix
    31               )
    30               )
    32             {
    31             {
    33                 my %rrset = (
    32                 my %rrset = (
    34                     label => $last_label =
    33                     label => $last_label =
    35                       defined $+{label}
    34                       defined $+{label}
   161     my %zone2 = map { $_->{id}, $_->{rrset} } @$zone2;
   160     my %zone2 = map { $_->{id}, $_->{rrset} } @$zone2;
   162     my @keys1 = keys %zone1;
   161     my @keys1 = keys %zone1;
   163     my @keys2 = keys %zone2;
   162     my @keys2 = keys %zone2;
   164     delete @zone1{@keys2};
   163     delete @zone1{@keys2};
   165     delete @zone2{@keys1};
   164     delete @zone2{@keys1};
   166     ### %zone1
   165     say 'update add ', join ' ' => @{$_}{qw/label ttl rrtype data/}
   167     ### %zone2
   166       foreach values %zone2;
       
   167     say 'update delete ', join ' ' => @{$_}{qw/label ttl rrtype data/}
       
   168       foreach values %zone1;
   168     exit;
   169     exit;
   169 }
   170 }
   170 
   171 
   171 sub main {
   172 sub main {
   172     my ($file) = @_;
   173     my %o = (
       
   174         key    => undef,
       
   175         server => undef,
       
   176     );
       
   177 
       
   178     GetOptions(
       
   179         'k|key=s'    => \$o{key},
       
   180         's|server=s' => \$o{server},
       
   181       )
       
   182       && @ARGV == 1
       
   183       or pod2usage();
       
   184 
       
   185     my @dig = (
       
   186         dig => 'AXFR',
       
   187         defined $o{key} ? (-k => $o{key}) : (),
       
   188         defined $o{server} ? ("\@$o{server}") : (),
       
   189         $ARGV[0]
       
   190     );
       
   191 
   173     my @zone1 = grep {
   192     my @zone1 = grep {
   174 
   193         not $_->{rrset}{rrtype} ~~
   175         # get { id => $id, rrset => \%rrset }
   194           [qw(RRSIG NSEC3 NSEC3PARAM NSEC DNSKEY TSIG)]
   176         not $_->{rrset}{rrtype} ~~ [qw(RRSIG NSEC3 NSEC3PARAM NSEC DNSKEY TSIG)]
   195     } parse(`@dig`);
   177 
   196 
   178           #$_->{rrset}{rrtype} ~~ [qw(SOA NS MX)]
       
   179     } parse($file);
       
   180     my $tmp = File::Temp->new();
   197     my $tmp = File::Temp->new();
   181     $tmp->print(nice @zone1);
   198     $tmp->print(nice @zone1);
   182     $tmp->close();
   199     $tmp->flush();
   183     system $ENV{EDITOR} // 'vi' => $tmp->filename;
   200     system $ENV{EDITOR} // 'vi' => $tmp->filename;
   184     my @zone2 = parse($tmp->filename);
   201     $tmp->seek(0, 0);
       
   202     my @zone2 = parse(<$tmp>);
   185     delta(\@zone1, \@zone2);
   203     delta(\@zone1, \@zone2);
   186     exit;
   204     exit;
   187 }
   205 }
   188 
   206 
   189 exit main(@ARGV) if not caller;
   207 exit main(@ARGV) if not caller;
       
   208 
       
   209 __END__
       
   210 
       
   211 =head1 NAME
       
   212 
       
   213  vidns -- editor for dynamically maintained zones
       
   214 
       
   215 =head1 SYNOPSIS
       
   216 
       
   217  vidns [-k key] [-s server] <zone>
       
   218 
       
   219 =head1 DESCRIPTION
       
   220 
       
   221 =head1 PREREQUISITES
       
   222 
       
   223 We need some tools to be installed:
       
   224 
       
   225 =over
       
   226 
       
   227 =item B<dig>
       
   228 
       
   229 The domain information grabber is used for the zone transfer currently.
       
   230 
       
   231 =item B<nsupdate>
       
   232 
       
   233 The nsupdate tool is used to send the updates back to the server.
       
   234 
       
   235 =back
       
   236 
       
   237 =cut