2015-10-13 19:06:37 +02:00
|
|
|
package zonefile;
|
|
|
|
use v5.14;
|
2015-12-21 09:39:50 +01:00
|
|
|
use Net::DNS::RR;
|
|
|
|
use Net::DNS::ZoneFile;
|
2015-10-13 19:06:37 +02:00
|
|
|
use Moo;
|
2015-12-21 09:39:50 +01:00
|
|
|
use utf8;
|
|
|
|
use URI;
|
|
|
|
use Data::Dumper;
|
2015-10-13 19:06:37 +02:00
|
|
|
|
|
|
|
has zone => qw/is rw/ ;
|
|
|
|
has [ qw/zonefile/ ] => qw/ is rw required 1/;
|
|
|
|
|
2015-12-21 09:39:50 +01:00
|
|
|
# Simple functions to manipulate lists of Net::DNS::RR
|
|
|
|
|
|
|
|
sub rr_array_del {
|
|
|
|
my ($zones, $rr) = @_;
|
2015-12-21 16:14:45 +01:00
|
|
|
my $todel = $rr->plain;
|
|
|
|
utf8::decode($todel);
|
|
|
|
[grep { my $v = $_->plain; utf8::decode($v); $v ne $rr->plain } @$zones]
|
2015-12-21 09:39:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sub rr_array_add {
|
|
|
|
my ($zone, $rr) = @_;
|
|
|
|
my @already_present = grep { $_->plain eq $rr->plain } @$zone;
|
|
|
|
push @$zone, $rr unless @already_present;
|
|
|
|
$zone
|
|
|
|
}
|
|
|
|
|
|
|
|
sub rr_array_new_serial {
|
|
|
|
my $zones = shift;
|
|
|
|
|
|
|
|
for(@{$zones}) {
|
|
|
|
if($_->type =~ /SOA/) {
|
|
|
|
my $serial = $_->serial;
|
|
|
|
$_->serial($serial + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$zones
|
|
|
|
}
|
|
|
|
|
|
|
|
sub rr_array_serial {
|
|
|
|
my $zones = shift;
|
|
|
|
|
|
|
|
for(@{$zones}) {
|
|
|
|
if($_->type =~ /SOA/) {
|
|
|
|
return $_->serial;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
die "Impossible to get the zone serial."
|
|
|
|
}
|
|
|
|
|
|
|
|
sub rr_array_dump {
|
|
|
|
my $zone = shift;
|
|
|
|
my $dump = '';
|
|
|
|
|
|
|
|
# write the SOA record first
|
|
|
|
for(@{$zone}) {
|
|
|
|
if($_->type =~ /SOA/i) {
|
|
|
|
$dump .= $_->string . "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for(@{$zone}) {
|
|
|
|
if($_->type !~ /SOA/i) {
|
|
|
|
$dump .= $_->string . "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-21 16:14:45 +01:00
|
|
|
utf8::decode($dump);
|
|
|
|
|
2015-12-21 09:39:50 +01:00
|
|
|
$dump
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-13 19:06:37 +02:00
|
|
|
sub BUILD {
|
|
|
|
my ($self) = @_;
|
|
|
|
|
2015-12-21 09:39:50 +01:00
|
|
|
my $path = $$self{zonefile};
|
2015-10-13 19:06:37 +02:00
|
|
|
|
2015-12-21 09:39:50 +01:00
|
|
|
# zonefile is the filename
|
|
|
|
if($$self{zonefile} =~ "://") {
|
|
|
|
my $fileuri = URI->new($$self{zonefile});
|
|
|
|
$path = $fileuri->path;
|
|
|
|
}
|
|
|
|
|
|
|
|
my $zonefile = Net::DNS::ZoneFile->new( $path );
|
|
|
|
my @zone = $zonefile->read;
|
|
|
|
$$self{zone} = [ @zone ];
|
2015-10-13 19:06:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
sub new_serial {
|
|
|
|
my $self = shift;
|
2015-12-21 09:39:50 +01:00
|
|
|
$$self{zone} = rr_array_new_serial $$self{zone}
|
2015-10-13 19:06:37 +02:00
|
|
|
}
|
|
|
|
|
2015-12-21 09:39:50 +01:00
|
|
|
sub dump {
|
2015-10-13 19:06:37 +02:00
|
|
|
my $self = shift;
|
2015-12-21 09:39:50 +01:00
|
|
|
rr_array_dump $$self{zone}
|
2015-10-13 19:06:37 +02:00
|
|
|
}
|
|
|
|
|
2015-12-21 09:39:50 +01:00
|
|
|
sub serial {
|
|
|
|
my ($self, $rr) = @_;
|
|
|
|
rr_array_serial $$self{zone}
|
2015-10-13 19:06:37 +02:00
|
|
|
}
|
|
|
|
|
2015-12-21 09:39:50 +01:00
|
|
|
# remove a raw line that represents the RR
|
|
|
|
sub rr_del_raw {
|
|
|
|
my ($self, $rrline) = @_;
|
2015-12-21 16:14:45 +01:00
|
|
|
utf8::decode($rrline);
|
|
|
|
say "to delete raw : $rrline";
|
2015-12-21 09:39:50 +01:00
|
|
|
my $rr = Net::DNS::RR->new($rrline);
|
2015-12-21 16:14:45 +01:00
|
|
|
say "to delete reformed : " . $rr->plain;
|
2015-12-21 09:39:50 +01:00
|
|
|
$self->rr_del($rr)
|
|
|
|
}
|
|
|
|
|
|
|
|
sub rr_del {
|
|
|
|
my ($self, $rr) = @_;
|
|
|
|
$$self{zone} = rr_array_del $$self{zone}, $rr
|
|
|
|
}
|
|
|
|
|
|
|
|
# add a raw line that represents the RR
|
|
|
|
sub rr_add_raw {
|
|
|
|
my ($self, $rrline) = @_;
|
2015-12-21 16:14:45 +01:00
|
|
|
utf8::decode($rrline);
|
2015-12-22 17:50:26 +01:00
|
|
|
say "to add raw : $rrline";
|
2015-12-21 09:39:50 +01:00
|
|
|
my $rr = Net::DNS::RR->new($rrline);
|
2015-12-22 17:50:26 +01:00
|
|
|
say "to add reformed : " . $rr->plain;
|
2015-12-21 09:39:50 +01:00
|
|
|
$self->rr_add($rr)
|
|
|
|
}
|
|
|
|
|
|
|
|
sub rr_add {
|
|
|
|
my ($self, $rr) = @_;
|
|
|
|
$$self{zone} = rr_array_add $$self{zone}, $rr
|
|
|
|
}
|
|
|
|
|
|
|
|
sub rr_mod {
|
|
|
|
my ($self, $rrline_old, $rrline_new) = @_;
|
|
|
|
$self->rr_del_raw($rrline_old);
|
|
|
|
$self->rr_add_raw($rrline_new);
|
2015-10-13 19:06:37 +02:00
|
|
|
}
|
|
|
|
|
2015-12-22 22:31:14 +01:00
|
|
|
sub rr_search {
|
|
|
|
my ($self, $name, $type) = @_;
|
|
|
|
my $rrlisttmp = $self->rr_array_to_array();
|
|
|
|
[ grep { $$_{name} eq $name && $$_{type} eq $type } @$rrlisttmp ]
|
|
|
|
}
|
|
|
|
|
2015-12-28 16:08:52 +01:00
|
|
|
sub search_domain {
|
|
|
|
my ($self) = @_;
|
|
|
|
my $rr_list = $self->rr_array_to_array();
|
|
|
|
|
|
|
|
my $soa = [ grep { $$_{type} eq "SOA" } @$rr_list ];
|
|
|
|
|
|
|
|
for(@$soa) {
|
|
|
|
return $$_{name};
|
|
|
|
}
|
|
|
|
|
|
|
|
die "No SOA in this domain.";
|
|
|
|
}
|
|
|
|
|
|
|
|
# to get all the records in relative
|
|
|
|
sub rr_array_to_array_stripped {
|
|
|
|
my $self = shift;
|
|
|
|
my $rr_list = $self->rr_array_to_array();
|
|
|
|
|
|
|
|
my $domain = $self->search_domain();
|
|
|
|
|
|
|
|
for (@$rr_list) {
|
|
|
|
$$_{name} =~ s/\.?$domain$//;
|
|
|
|
$$_{name} = "@" if($$_{name} =~ /^$/);
|
|
|
|
|
|
|
|
if($$_{type} =~ /^(CNAME|SRV|MX)$/) {
|
|
|
|
$$_{rdata} =~ s/\.?$domain$//;
|
|
|
|
$$_{rdata} = "@" if($$_{rdata} =~ /^$/);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$rr_list;
|
|
|
|
}
|
|
|
|
|
2015-12-21 09:39:50 +01:00
|
|
|
sub rr_array_to_array {
|
|
|
|
my ($self) = shift;
|
|
|
|
my $rr_list;
|
|
|
|
|
|
|
|
for(@{$$self{zone}}) {
|
|
|
|
|
|
|
|
my @list = split / /, $_->plain;
|
|
|
|
|
|
|
|
my $rr;
|
|
|
|
$$rr{name} = $list[0];
|
|
|
|
$$rr{ttl} = $list[1];
|
|
|
|
$$rr{class} = $list[2];
|
|
|
|
$$rr{type} = $list[3];
|
|
|
|
|
2015-12-21 16:14:45 +01:00
|
|
|
utf8::decode($$rr{name});
|
|
|
|
utf8::decode($$rr{ttl});
|
|
|
|
utf8::decode($$rr{class});
|
|
|
|
utf8::decode($$rr{type});
|
|
|
|
|
2015-12-21 09:39:50 +01:00
|
|
|
if($list[3] =~ /SOA/) {
|
|
|
|
$$rr{ns} = $list[4];
|
|
|
|
$$rr{postmaster} = $list[5];
|
|
|
|
$$rr{serial} = $list[6];
|
|
|
|
$$rr{refresh} = $list[7];
|
|
|
|
$$rr{retry} = $list[8];
|
|
|
|
$$rr{expire} = $list[9];
|
|
|
|
$$rr{minimum} = $list[10];
|
2015-12-21 16:14:45 +01:00
|
|
|
|
|
|
|
utf8::decode($$rr{ns});
|
|
|
|
utf8::decode($$rr{postmaster});
|
|
|
|
utf8::decode($$rr{serial});
|
|
|
|
utf8::decode($$rr{refresh});
|
|
|
|
utf8::decode($$rr{retry});
|
|
|
|
utf8::decode($$rr{expire});
|
|
|
|
utf8::decode($$rr{minimum});
|
2015-12-21 09:39:50 +01:00
|
|
|
}
|
|
|
|
elsif($list[3] =~ /^(A(AAA)?|CNAME|NS)$/) {
|
|
|
|
$$rr{rdata} = $list[4];
|
2015-12-21 16:14:45 +01:00
|
|
|
utf8::decode($$rr{rdata});
|
2015-12-21 09:39:50 +01:00
|
|
|
}
|
|
|
|
elsif($list[3] =~ /^MX$/) {
|
|
|
|
$$rr{priority} = $list[4];
|
|
|
|
$$rr{rdata} = $list[5];
|
2015-12-21 16:14:45 +01:00
|
|
|
|
|
|
|
utf8::decode($$rr{priority});
|
|
|
|
utf8::decode($$rr{rdata});
|
2015-12-21 09:39:50 +01:00
|
|
|
}
|
|
|
|
elsif($list[3] =~ /^TXT$/) {
|
|
|
|
$$rr{rdata} = $_->rdstring;
|
2015-12-21 16:14:45 +01:00
|
|
|
utf8::decode($$rr{rdata});
|
2015-12-21 09:39:50 +01:00
|
|
|
}
|
2015-12-22 17:50:26 +01:00
|
|
|
elsif($list[3] =~ /^SRV$/) {
|
|
|
|
# _service._proto.name. TTL class SRV priority weight port target.
|
|
|
|
# _sip._tcp.example.com. 86400 IN SRV 10 60 5060 bigbox.example.com.
|
|
|
|
$$rr{priority} = $list[4];
|
|
|
|
$$rr{weight} = $list[5];
|
|
|
|
$$rr{port} = $list[6];
|
|
|
|
$$rr{rdata} = $list[7];
|
|
|
|
|
|
|
|
utf8::decode($$rr{priority});
|
|
|
|
utf8::decode($$rr{weight});
|
|
|
|
utf8::decode($$rr{port});
|
|
|
|
utf8::decode($$rr{rdata});
|
|
|
|
}
|
2015-12-21 09:39:50 +01:00
|
|
|
else {
|
2015-12-22 17:50:26 +01:00
|
|
|
$$rr{rdata} = $_->rdstring;
|
|
|
|
utf8::decode($$rr{rdata});
|
|
|
|
say "This RR is not available : " . $_->plain;
|
2015-12-21 09:39:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
push @$rr_list, $rr;
|
|
|
|
}
|
|
|
|
|
|
|
|
$rr_list
|
|
|
|
}
|
2015-10-13 19:06:37 +02:00
|
|
|
|
|
|
|
1;
|