parent
0dc7cdea82
commit
e6da9f192e
@ -1,52 +1,180 @@ |
||||
package zonefile; |
||||
use v5.14; |
||||
use Net::DNS::RR; |
||||
use Net::DNS::ZoneFile; |
||||
use Moo; |
||||
use DNS::ZoneParse; |
||||
use utf8; |
||||
use URI; |
||||
use Data::Dumper; |
||||
|
||||
has zone => qw/is rw/ ; |
||||
has [ qw/domain/ ] => qw/ is ro required 1/; |
||||
has [ qw/zonefile/ ] => qw/ is rw required 1/; |
||||
|
||||
# Simple functions to manipulate lists of Net::DNS::RR |
||||
|
||||
sub rr_array_del { |
||||
my ($zones, $rr) = @_; |
||||
my @z = grep { $_->plain ne $rr->plain } @$zones; |
||||
[ @z ] |
||||
} |
||||
|
||||
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"; |
||||
} |
||||
} |
||||
|
||||
$dump |
||||
} |
||||
|
||||
|
||||
sub BUILD { |
||||
my ($self) = @_; |
||||
|
||||
my $filename = $$self{zonefile}; |
||||
if($filename =~ "://") |
||||
{ |
||||
my $fileuri = URI->new($filename); |
||||
$filename = $fileuri->path; |
||||
} |
||||
my $path = $$self{zonefile}; |
||||
|
||||
$$self{zone} = DNS::ZoneParse->new($filename, $$self{domain}); |
||||
# 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 ]; |
||||
} |
||||
|
||||
sub new_serial { |
||||
my $self = shift; |
||||
$self->zone->new_serial(); |
||||
$$self{zone} = rr_array_new_serial $$self{zone} |
||||
} |
||||
|
||||
sub origin { |
||||
sub dump { |
||||
my $self = shift; |
||||
$self->zone->origin(); |
||||
rr_array_dump $$self{zone} |
||||
} |
||||
|
||||
sub output { |
||||
my $self = shift; |
||||
$self->zone->output(); |
||||
sub serial { |
||||
my ($self, $rr) = @_; |
||||
rr_array_serial $$self{zone} |
||||
} |
||||
|
||||
sub dump { |
||||
my $self = shift; |
||||
$self->zone->dump(); |
||||
# remove a raw line that represents the RR |
||||
sub rr_del_raw { |
||||
my ($self, $rrline) = @_; |
||||
my $rr = Net::DNS::RR->new($rrline); |
||||
$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) = @_; |
||||
my $rr = Net::DNS::RR->new($rrline); |
||||
$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); |
||||
} |
||||
|
||||
# better encapsulation |
||||
sub a { my $self = shift; $self->zone->a } |
||||
sub aaaa { my $self = shift; $self->zone->aaaa } |
||||
sub cname { my $self = shift; $self->zone->cname } |
||||
sub ns { my $self = shift; $self->zone->ns } |
||||
sub mx { my $self = shift; $self->zone->mx } |
||||
sub ptr { my $self = shift; $self->zone->ptr } |
||||
sub txt { my $self = shift; $self->zone->txt } # TODO TEST THIS |
||||
|
||||
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]; |
||||
|
||||
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]; |
||||
} |
||||
elsif($list[3] =~ /^(A(AAA)?|CNAME|NS)$/) { |
||||
$$rr{rdata} = $list[4]; |
||||
} |
||||
elsif($list[3] =~ /^MX$/) { |
||||
$$rr{priority} = $list[4]; |
||||
$$rr{rdata} = $list[5]; |
||||
} |
||||
elsif($list[3] =~ /^TXT$/) { |
||||
$$rr{rdata} = $_->rdstring; |
||||
} |
||||
else { |
||||
die "This RR is not available : " . $_->plain; |
||||
} |
||||
|
||||
push @$rr_list, $rr; |
||||
|
||||
} |
||||
|
||||
$rr_list |
||||
} |
||||
|
||||
1; |
||||
|
@ -0,0 +1,18 @@ |
||||
use Test::More; |
||||
use Modern::Perl; |
||||
use lib 'lib'; |
||||
use util ':all'; |
||||
use zonefile; |
||||
|
||||
chdir 'lib'; # TODO hack at 2am |
||||
|
||||
#map { |
||||
# ok |
||||
# ( ( is_domain_name $_ ), "is '$_' a domain name" ) |
||||
#} qw( foo.bar bar localhost. localhost ); |
||||
# |
||||
#done_testing; |
||||
|
||||
my $zf = zonefile->new( zonefile => "../t/zonefile.txt" ); |
||||
$zf->new_serial(); |
||||
print $zf->dump(); |
@ -0,0 +1,17 @@ |
||||
$ORIGIN example.com. ; designates the start of this zone file in the namespace |
||||
$TTL 1h ; default expiration time of all resource records without their own TTL value |
||||
example.com. IN SOA ns.example.com. username.example.com. ( 2007120710 1d 2h 4w 1h ) |
||||
example.com. IN NS ns ; ns.example.com is a nameserver for example.com |
||||
example.com. IN NS ns.somewhere.example. ; ns.somewhere.example is a backup nameserver for example.com |
||||
example.com. IN MX 10 mail.example.com. ; mail.example.com is the mailserver for example.com |
||||
@ IN MX 20 mail2.example.com. ; equivalent to above line, "@" represents zone origin |
||||
@ IN MX 50 mail3 ; equivalent to above line, but using a relative host name |
||||
example.com. IN A 192.0.2.1 ; IPv4 address for example.com |
||||
IN AAAA 2001:db8:10::1 ; IPv6 address for example.com |
||||
ns IN A 192.0.2.2 ; IPv4 address for ns.example.com |
||||
IN AAAA 2001:db8:10::2 ; IPv6 address for ns.example.com |
||||
www IN CNAME example.com. ; www.example.com is an alias for example.com |
||||
wwwtest IN CNAME www ; wwwtest.example.com is another alias for www.example.com |
||||
mail IN A 192.0.2.3 ; IPv4 address for mail.example.com |
||||
mail2 IN A 192.0.2.4 ; IPv4 address for mail2.example.com |
||||
mail3 IN A 192.0.2.5 ; IPv4 address for mail3.example.com |
Loading…
Reference in new issue