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 |