File Coverage

blib/lib/Net/DNS/Create.pm
Criterion Covered Total %
statement 80 90 88.8
branch 38 44 86.3
condition 10 17 58.8
subroutine 18 22 81.8
pod 0 12 0.0
total 146 185 78.9


line stmt bran cond sub pod time code
1             # Copyright (c) 2011-2014 David Caldwell, All Rights Reserved.
2              
3             package Net::DNS::Create;
4 3     3   122080 use strict; use warnings;
  3     3   4  
  3         91  
  3         12  
  3         5  
  3         328  
5              
6             our $VERSION='1.0.0';
7              
8             require Exporter;
9             our @ISA = qw(Exporter);
10             our @EXPORT = qw(domain master soa);
11             our @EXPORT_OK = qw(domain master full_host local_host email interval);
12              
13             my $kind;
14             our %config = (default_ttl=>'1h');
15             sub import {
16 3     3   2350 use Data::Dumper;
  3         18601  
  3         2281  
17 6     6   31 my $package = shift;
18 6   50     19 my $import_kind = shift // 'bind';
19              
20             # Tricky junk: If the first thing in our import list is "internal" then we just pass the rest to
21             # Exporter::export_to_level so that our plugins can include us back and import the full_host, email, and
22             # interval utility functions. Otherwise we pass the rest of the import args to the plugin's import so that
23             # conf options pass all the way down. In that case we don't pass anything to Exporter::export_to_level so
24             # that default export happens.
25 6 100       19 if ($import_kind ne 'internal') {
26 3         7 $kind = __PACKAGE__ . "::" . $import_kind;
27 3 50       190 eval "require $kind"; die "$@" if $@;
  3         29  
28 3         28 $kind->import(@_);
29 3         17 %config = (%config, @_); # Keep around the config for ourselves so we get the default_ttl setting.
30 3         11 @_ = ();
31             }
32 6         894 __PACKAGE__->export_to_level(1, $package, @_);
33             }
34              
35             sub full_host($;$);
36             sub full_host($;$) {
37 304     304 0 359 my ($name,$domain) = @_;
38 304 50       1433 $name eq '@' ? (defined $domain ? full_host($domain) : die "Need a domain with @") :
    100          
    100          
    100          
39             $name =~ /\.$/ ? $name : "$name." . (defined $domain ? full_host($domain) : '')
40             }
41              
42             sub local_host($$) {
43 50     50 0 979 my ($fq,$domain) = (full_host(shift), full_host(shift));
44 50 100       133 return '@' if $fq eq $domain;
45 40         35 my $local = $fq;
46 40 100       300 return $local if substr($local, -length($domain)-1, length($domain)+1, '') eq ".$domain";
47 1         5 return $fq;
48             }
49              
50             sub email($) {
51 17     17 0 395 my ($email) = @_;
52 17         59 $email =~ s/@/./g;
53 17         37 full_host($email);
54             }
55              
56             sub interval($) {
57 49 50 33 49 0 730 $_[0] =~ /(\d+)([hmsdw])/ && $1 * { s=>1, m=>60, h=>3600, d=>3600*24, w=>3600*24*7 }->{$2} || $_[0];
58             }
59              
60             sub escape($) {
61 21     21 0 38 my $s = shift;
62             # Net::DNS::RR::TXT interpolates \xxx style octally encoded escapes. We don't want this so we escape the \s
63 21         45 $s =~ s/\\/\\\\/g;
64 21         118 $s;
65             }
66              
67             sub txt($) {
68 21     21 0 32 my ($t) = @_;
69 21 100       88 return escape($t) if length $t < 255;
70 3         6 my @part;
71 3         27 push @part, escape($1) while ($t =~ s/^(.{255})//);
72 3         22 (@part, $t);
73             }
74              
75              
76             sub arrayize($) { # [1,2,3,4] -> (1,2,3,4), 1 -> (1)
77 57 100   57 0 180 (ref $_[0] eq 'ARRAY' ? @{$_[0]} : $_[0])
  9         53  
78             }
79             sub arrayize2($) { # [[1,2],[3,4]] -> ([1,2],[3,4]), [1,2] -> ([1,2])
80 6 100 66 6 0 57 (ref $_[0] eq 'ARRAY' && ref $_[0]->[0] eq 'ARRAY' ? @{$_[0]} : $_[0])
  3         35  
81             }
82              
83 3     3   1900 use Hash::Merge::Simple qw(merge);
  3         1328  
  3         196  
84 3     3   1865 use Net::DNS::RR;
  3         79734  
  3         1827  
85             sub domain($@) {
86 3     3 0 2157 my ($domain, @entry_hashes) = @_;
87 3         7 my $entries = {};
88 3         7 for my $e (@entry_hashes) {
89 6         431 $entries = merge($entries, $e);
90             }
91              
92 3         173 my $fq_domain = full_host($domain);
93 3         15 my $ttl = interval($config{default_ttl});
94 3         16 $entries = [ map { my $node = $_;
  63         30705  
95 63         143 my $fqdn = full_host($_,$domain);
96 81         17926 map {
97 63         198 my $rr = lc $_;
98 81         136 my $val = $entries->{$node}->{$_};
99 81         290 my %common = (name => $fqdn,
100             ttl => $ttl,
101             type => uc $rr);
102 39         463 $rr eq 'cname' || $rr eq 'soa' ?
103             Net::DNS::RR->new(%common,
104             $rr eq 'cname' ? (cname => full_host($val, $fq_domain)) :
105             $rr eq 'soa' ? (mname => full_host($val->{primary_ns}, $domain),
106             rname => $val->{rp_email},
107             serial => $val->{serial} // 0,
108             refresh => interval($val->{refresh}),
109             retry => interval($val->{retry}),
110             expire => interval($val->{expire}),
111             minimum => interval($val->{min_ttl})) :
112             die "can't happen") :
113              
114 9         1585 $rr eq 'a' ? map { Net::DNS::RR->new(%common, address => $_)} sort(arrayize($val)) :
115              
116 21         6092 $rr eq 'rp' ? map { Net::DNS::RR->new(%common, mbox => email($_->[0]),
117             txtdname => full_host($_->[1], $fq_domain)) } sort(arrayize2($val)) :
118              
119 6         19 $rr eq 'txt' ? map { Net::DNS::RR->new(%common, char_str_list => [txt($_)]) } sort {$a cmp $b} arrayize($val) :
  6         4909  
120 9         2599 $rr eq 'mx' ? map { Net::DNS::RR->new(%common, preference => $_, exchange => full_host($val->{$_}, $fq_domain)) } sort(keys %$val) :
121 3         8 $rr eq 'ns' ? map { Net::DNS::RR->new(%common, nsdname => $_) } sort(arrayize($val)) :
122             $rr eq 'srv' ? map {
123 81 50 100     682 my $target = $_;
    100 50        
    50          
    100          
    100          
    100          
    100          
    100          
    100          
124 3   50     46 map {
      50        
125 0         0 Net::DNS::RR->new(%common,
126             priority => $_->{priority} // 0,
127             weight => $_->{weight} // 0,
128             port => $_->{port},
129             target => full_host($target))
130 3 50       19 } sort {$a cmp $b} (ref $val->{$_} eq 'ARRAY' ? @{$val->{$_}} : $val->{$_})
  0         0  
131             } sort(keys %$val) :
132             die uc($rr)." is not supported yet :-("; # Remember to add support for all the backends, too.
133 63         75 } keys %{$entries->{$node}};
134             } keys %$entries ];
135              
136 3         415 $kind->domain($fq_domain, $entries);
137             }
138              
139             sub master {
140 2     2 0 549 $kind->master(@_);
141             }
142              
143             sub list_files() {
144 3     3   31 no warnings;
  3         4  
  3         446  
145 0     0 0   *domain = *main::domain = \&{"$kind\::domain_list"};
  0            
146 0           *master = *main::master = \&{"$kind\::master_list"};
  0            
147             }
148              
149             sub list_domains() {
150 3     3   17 no warnings;
  3         5  
  3         338  
151 0     0 0   *domain = *main::domain = sub { print "$_[0]\n" };
  0     0      
152 0     0     *master = *main::master = sub {};
  0            
153             }
154              
155              
156             1;
157             __END__