|
|
@@ -1,152 +0,0 @@
|
|
|
-package Trog::Zone;
|
|
|
-
|
|
|
-=head1 Trog::Zone
|
|
|
-
|
|
|
-=head2 DESCRIPTION
|
|
|
-
|
|
|
-Zonefile CRUD
|
|
|
-
|
|
|
-=cut
|
|
|
-
|
|
|
-use strict;
|
|
|
-use warnings;
|
|
|
-
|
|
|
-use feature qw{signatures};
|
|
|
-no warnings qw{experimental};
|
|
|
-
|
|
|
-use Trog::Config;
|
|
|
-use Trog::Data;
|
|
|
-use Trog::Vars;
|
|
|
-use Trog::SQLite;
|
|
|
-
|
|
|
-use Net::IP;
|
|
|
-use Ref::Util;
|
|
|
-
|
|
|
-=head2 zone($domain) = @zonedata
|
|
|
-
|
|
|
-Returns the zone data for the requested zone.
|
|
|
-Like any other post in TCMS it's versioned.
|
|
|
-
|
|
|
-=cut
|
|
|
-
|
|
|
-sub zone ( $domain, $version = undef ) {
|
|
|
- my $conf = Trog::Config::get();
|
|
|
- my $data = Trog::Data->new($conf);
|
|
|
-
|
|
|
- my @zonedata = $data->get( tags => ['zone'], acls => [qw{admin}], title => $domain );
|
|
|
- @zonedata = grep { $_->{version} == $version } @zonedata if defined $version;
|
|
|
- return @zonedata;
|
|
|
-}
|
|
|
-
|
|
|
-=head2 addzone($domain, %options)
|
|
|
-
|
|
|
-Add a post of 'zone' type.
|
|
|
-
|
|
|
-=cut
|
|
|
-
|
|
|
-my $valid_ip = sub {
|
|
|
- return Net::IP->new(shift);
|
|
|
-};
|
|
|
-
|
|
|
-my $valid_rev_ip = sub {
|
|
|
- return shift =~ m/\.in-addr\.arpa\.$/;
|
|
|
-};
|
|
|
-
|
|
|
-my $valid_rev_ip6 = sub {
|
|
|
- return shift =~ m/\.ip6\.arpa\.$/;
|
|
|
-};
|
|
|
-
|
|
|
-my $spec = {
|
|
|
- ip => $valid_ip,
|
|
|
- ip6 => $valid_ip,
|
|
|
- ip_reversed => $valid_rev_ip,
|
|
|
- ip6_reversed => $valid_rev_ip6,
|
|
|
- nameservers => \&Ref::Util::is_arrayref,
|
|
|
- subdomains => \&Ref::Util::is_arrayref,
|
|
|
- cnames => \&Ref::Util::is_arrayref,
|
|
|
- gsv_string => $Trog::Vars::not_ref,
|
|
|
- dkim_pkey => $Trog::Vars::not_ref,
|
|
|
- acme_challenge => $Trog::Vars::not_ref,
|
|
|
-};
|
|
|
-
|
|
|
-sub addzone ($query) {
|
|
|
- my $domain = $query->{title};
|
|
|
- return unless $domain;
|
|
|
- my ($latest) = zone($domain);
|
|
|
- $latest //= {};
|
|
|
-
|
|
|
- my $conf = Trog::Config::get();
|
|
|
- my $data = Trog::Data->new($conf);
|
|
|
-
|
|
|
- #XXX TODO make this instead use @records2add, complexity demon BAD
|
|
|
- my $processor = Text::Xslate->new( path => 'www/templates/text' );
|
|
|
- $query->{data} = $processor->render( 'zone.tx', $query );
|
|
|
-
|
|
|
- %$latest = (
|
|
|
- %$latest,
|
|
|
- Trog::Vars::filter( $query, $spec ),
|
|
|
- );
|
|
|
-
|
|
|
- $data->add($latest);
|
|
|
-
|
|
|
- #import into pdns
|
|
|
- my ( $ttl, $prio, $disabled ) = ( 300, 0, 0 );
|
|
|
-
|
|
|
- my $insert_sql = q{insert into records (domain_id, name, type,content,ttl,prio,disabled) select id , ?, ?, ?, ?, ?, ? from domains where name=?};
|
|
|
- my @records2add = (
|
|
|
- [ $query->{title}, 'SOA', "$query->{title} soa.$query->{title} $query->{version} 10800 3600 604800 10800" ],
|
|
|
- [ $query->{title}, 'A', $query->{ip} ],
|
|
|
- [ $query->{title}, 'AAAA', $query->{ip6} ],
|
|
|
- [ $query->{ip_reversed}, 'PTR', $query->{title} ],
|
|
|
- [ $query->{ip6_reversed}, 'PTR', $query->{title} ],
|
|
|
- [ $query->{title}, 'MX', "mail.$query->{title}" ],
|
|
|
- [ "_smtps._tcp.mail.$query->{title}", 'SRV', "5 587 ." ],
|
|
|
- [ "_imaps._tcp.mail.$query->{title}", 'SRV', "5 993 ." ],
|
|
|
- [ "_pop3s._tcp.mail.$query->{title}", 'SRV', "5 995 ." ],
|
|
|
- [ "_dmarc.$query->{title}", 'TXT', "v=DMARC1; p=reject; rua=mailto:postmaster\@$query->{title}; ruf=mailto:postmaster\@$query->{title}" ],
|
|
|
- [ "mail._domainkey.$query->{title}", 'TXT', "v=DKIM1; h=sha256; k=rsa; t=y; p=$query->{dkim_pkey}" ],
|
|
|
- [ $query->{title}, 'TXT', "v=spf1 +mx +a +ip4:$query->{ip} +ip6:$query->{ip6} -all" ],
|
|
|
- [ $query->{title}, 'TXT', "google-site-verification=$query->{gsv_string}" ],
|
|
|
- [ "_acme-challenge.$query->{title}", 'TXT', $query->{acme_challenge} ],
|
|
|
- [ $query->{title}, 'CAA', '0 issue "letsencrypt.org"' ],
|
|
|
- );
|
|
|
-
|
|
|
- push( @records2add, ( map { [ "$_.$query->{title}", "CNAME", $query->{title} ] } @{ $query->{cnames} } ) );
|
|
|
- push( @records2add, ( map { [ $query->{title}, 'NS', $_ ] } @{ $query->{nameservers} } ) );
|
|
|
- foreach my $subdomain ( @{ $query->{subdomains} } ) {
|
|
|
- push( @records2add, [ "$subdomain->{name}.$query->{title}", 'A', $subdomain->{ip} ] );
|
|
|
- push( @records2add, [ "$subdomain->{name}.$query->{title}", 'AAAA', $subdomain->{ip6} ] );
|
|
|
- push( @records2add, ( map { [ "$subdomain->{name}.$query->{title}", 'NS', $_ ] } @{ $subdomain->{nameservers} } ) );
|
|
|
- }
|
|
|
-
|
|
|
- my $dbh = _dbh();
|
|
|
- $dbh->begin_work();
|
|
|
- $dbh->do("DELETE FROM records") or _roll_and_die($dbh);
|
|
|
- foreach my $record (@records2add) {
|
|
|
- $dbh->do( $insert_sql, undef, @$record, $ttl, $prio, $disabled, $query->{title} ) or _roll_and_die($dbh);
|
|
|
- }
|
|
|
- $dbh->commit() or _roll_and_die($dbh);
|
|
|
-
|
|
|
- return $latest;
|
|
|
-}
|
|
|
-
|
|
|
-sub delzone ($domain) {
|
|
|
- my $conf = Trog::Config::get();
|
|
|
- my $data = Trog::Data->new($conf);
|
|
|
-
|
|
|
- my ($latest) = zone($domain);
|
|
|
- return unless $latest;
|
|
|
- return $data->delete($latest);
|
|
|
-}
|
|
|
-
|
|
|
-sub _dbh {
|
|
|
- return Trog::SQLite::dbh( undef, "dns/zones.db" );
|
|
|
-}
|
|
|
-
|
|
|
-sub _roll_and_die ($dbh) {
|
|
|
- my $err = $dbh->errstr;
|
|
|
- $dbh->rollback();
|
|
|
- die $err;
|
|
|
-}
|
|
|
-
|
|
|
-1;
|