File Coverage

blib/lib/Net/DNS/RR/DELEG.pm
Criterion Covered Total %
statement 197 197 100.0
branch 68 68 100.0
condition 8 8 100.0
subroutine 29 29 100.0
pod 5 6 100.0
total 307 308 100.0


line stmt bran cond sub pod time code
1             package Net::DNS::RR::DELEG;
2              
3 1     1   9 use strict;
  1         2  
  1         44  
4 1     1   6 use warnings;
  1         1  
  1         127  
5             our $VERSION = (qw$Id: DELEG.pm 2043 2026-01-14 13:35:59Z willem $)[2];
6              
7 1     1   8 use base qw(Net::DNS::RR);
  1         2  
  1         133  
8              
9              
10             =head1 NAME
11              
12             Net::DNS::RR::DELEG - DNS DELEG resource record
13              
14             =cut
15              
16 1     1   26 use integer;
  1         3  
  1         8  
17              
18 1     1   758 use Net::DNS::RR::A;
  1         4  
  1         38  
19 1     1   710 use Net::DNS::RR::AAAA;
  1         3  
  1         41  
20 1     1   8 use Net::DNS::DomainName;
  1         3  
  1         34  
21 1     1   689 use Net::DNS::Text;
  1         5  
  1         2512  
22              
23             my %keybycode = (
24             0 => 'mandatory',
25             1 => 'server-ipv4',
26             2 => 'server-ipv6',
27             3 => 'server-name',
28             4 => 'include-delegi',
29             );
30             my %keybyname = reverse %keybycode;
31              
32              
33             sub _decode_rdata { ## decode rdata from wire-format octet string
34 2     2   36 my ( $self, $data, $offset ) = @_;
35              
36 2         5 my $limit = $self->{rdlength};
37 2         8 my $rdata = $self->{rdata} = substr $$data, $offset, $limit;
38 2         4 my $index = 0;
39              
40 2         8 my $params = $self->{parameters} = [];
41 2         8 while ( ( my $start = $index + 4 ) <= $limit ) {
42 7         20 my ( $key, $size ) = unpack( "\@$index n2", $rdata );
43 7 100       19 last if ( $index = $start + $size ) > $limit;
44 6         21 push @$params, ( $key, substr $rdata, $start, $size );
45             }
46 2 100       10 die $self->type . ': corrupt RDATA' unless $index == $limit;
47 1         5 return;
48             }
49              
50              
51             sub _encode_rdata { ## encode rdata as wire-format octet string
52 4     4   10 my $self = shift;
53              
54 4         5 my @packed;
55 4         13 my ($paramref) = grep {defined} $self->{parameters}, [];
  8         18  
56 4         137 my @parameters = @$paramref;
57 4         13 while (@parameters) {
58 16         27 my $key = shift @parameters;
59 16         27 my $val = shift @parameters;
60 16         58 push @packed, pack( 'n2a*', $key, length($val), $val );
61             }
62 4         29 return join '', @packed;
63             }
64              
65              
66             sub _format_rdata { ## format rdata portion of RR string.
67 2     2   3 my $self = shift;
68              
69 2         5 my @rdata;
70 2         19 my ($paramref) = grep {defined} $self->{parameters}, [];
  4         10  
71 2         8 my @parameters = @$paramref;
72 2         8 while (@parameters) {
73 6         13 my $key = shift @parameters;
74 6         10 my $val = shift @parameters;
75 6 100       21 if ( my $name = $keybycode{$key} ) {
76 5         20 my @val = grep {length} $self->$name;
  7         17  
77 5         17 my @rhs = grep {length} join ',', @val;
  5         10  
78 5         26 push @rdata, join '=', $name, @rhs;
79             } else {
80 1         7 my $txt = Net::DNS::Text->decode( \$val, 0, length $val );
81 1         7 push @rdata, join '=', "key$key", $txt->string;
82             }
83             }
84              
85 2         10 return @rdata;
86             }
87              
88              
89             sub _parse_rdata { ## populate RR from rdata in argument list
90 13     13   33 my ( $self, @argument ) = @_;
91              
92 13         41 while ( local $_ = shift @argument ) {
93 16         121 m/^([^=]+)(=?)(.*)$/;
94 16         49 my $key = $1;
95 16 100       66 my $val = length($3) ? $3 : $2 ? shift @argument : '';
    100          
96 16 100       43 if (/^key\d+/) {
97 1         9 $self->$key($val);
98             } else {
99 15         28 local $_ = $val;
100 15         40 s/^"([^"]*)"$/$1/s; # strip enclosing quotes
101 15         27 s/\\,/\\044/g; # disguise escaped comma
102 15         113 $self->$key( split /,/ );
103             }
104             }
105 10         31 return;
106             }
107              
108              
109             sub _post_parse { ## parser post processing
110 11     11   16 my $self = shift;
111              
112 11         25 my ($paramref) = grep {defined} $self->{parameters}, [];
  22         46  
113 11         35 my %parameter = @$paramref;
114              
115 11 100       60 if ( defined $parameter{0} ) {
116 3         6 my %unique;
117 3         12 foreach ( grep { !$unique{$_}++ } unpack 'n*', $parameter{0} ) {
  6         23  
118 4 100       15 die( $self->type . qq[: unexpected "key0" in mandatory list] ) if $unique{0};
119 3 100       12 die( $self->type . qq[: duplicate "key$_" in mandatory list] ) if --$unique{$_};
120 2 100       12 die( $self->type . qq[: mandatory "key$_" not present] ) unless defined $parameter{$_};
121             }
122             }
123              
124 8         18 foreach ( 3, 4 ) {
125 15 100       46 next unless defined $parameter{$_};
126 6 100       26 next if length( $parameter{$_} ) > 1;
127 2         10 die( $self->type . qq[: invalid $keybycode{$_}] );
128             }
129              
130 6 100       17 if ( defined $parameter{4} ) {
131 2 100       11 die( $self->type . qq[: parameter conflicts with $keybycode{4}] )
132             if scalar( keys %parameter ) > 1;
133             }
134 5         15 return;
135             }
136              
137              
138             sub mandatory { ## mandatory=key1,server-name,...
139 7     7 0 25 my ( $self, @value ) = @_; # uncoverable pod
140 7 100       16 my @list = map { $keybyname{lc $_} || $_ } @value;
  10         54  
141 7 100       17 my @keys = map { /(\d+)$/ ? $1 : die( $self->type . qq[: unexpected "$_"] ) } @list;
  10         89  
142 6 100       28 return $self->_parameter( 0, _integer16( sort { $a <=> $b } @keys ) ) if @keys;
  6         25  
143 2         9 my $packed = $self->_parameter(0);
144 2 100       14 return _list( defined($packed) ? map {"key$_"} unpack 'n*', $packed : return );
  3         56  
145             }
146              
147             sub server_ipv4 { ## server-ipv4=192.0.2.53
148 5     5 1 16 my ( $self, @value ) = @_;
149 5 100       25 return $self->_parameter( 1, _address4(@value) ) if @value;
150 3   100     11 my $packed = $self->_parameter(1) || return;
151 2         13 my @iplist = unpack 'a4' x ( length($packed) / 4 ), $packed;
152 2         5 return _list( map { Net::DNS::RR::A::address( {address => $_} ) } @iplist );
  3         15  
153             }
154              
155             sub server_ipv6 { ## server-ipv6=2001:DB8::53
156 5     5 1 15 my ( $self, @value ) = @_;
157 5 100       21 return $self->_parameter( 2, _address6(@value) ) if @value;
158 3   100     8 my $packed = $self->_parameter(2) || return;
159 2         11 my @iplist = unpack 'a16' x ( length($packed) / 16 ), $packed;
160 2         6 return _list( map { Net::DNS::RR::AAAA::address_short( {address => $_} ) } @iplist );
  3         14  
161             }
162              
163             sub server_name { ## server-name=nameserver.example
164 7     7 1 19 my ( $self, @value ) = @_;
165 7 100       27 return $self->_parameter( 3, _domain(@value) ) if @value;
166 3   100     11 my $packed = $self->_parameter(3) || return;
167 2         5 my $index = 0;
168 2         17 ( $value[++$#value], $index ) = Net::DNS::DomainName->decode( \$packed, $index ) while $index < length $packed;
169 2         6 return _list( map { $_->fqdn } @value );
  2         30  
170             }
171              
172             sub include_delegi { ## include-delegi=devolved.example
173 9     9 1 24 my ( $self, @value ) = @_;
174 9 100       36 return $self->_parameter( 4, _domain(@value) ) if @value;
175 3   100     8 my $packed = $self->_parameter(4) || return;
176 2         6 my $index = 0;
177 2         16 ( $value[++$#value], $index ) = Net::DNS::DomainName->decode( \$packed, $index ) while $index < length $packed;
178 2         6 return _list( map { $_->fqdn } @value );
  2         9  
179             }
180              
181              
182             ########################################
183              
184             sub AUTOLOAD { ## Dynamic constructor/accessor methods
185 12     12   1781 my ( $self, @argument ) = @_;
186              
187 12         18 our $AUTOLOAD;
188 12         52 my ($method) = reverse split /::/, $AUTOLOAD;
189 12         67 my $canonical = lc($method);
190 12         35 $canonical =~ s/-/_/g;
191 12 100       53 if ( $self->can($canonical) ) {
192 1     1   10 no strict 'refs'; ## no critic ProhibitNoStrict
  1         3  
  1         1418  
193 4     10   18 *{$AUTOLOAD} = sub { shift->$canonical(@_) };
  4         25  
  10         30  
194 4         14 return $self->$canonical(@argument);
195             }
196              
197 8         19 my $super = "SUPER::$method";
198 8 100       67 return $self->$super(@argument) unless $method =~ /^key[0]*(\d+)$/i;
199 7         24 my $key = $1;
200 7 100       25 return $self->_parameter($key) unless @argument;
201 3         7 my $first = shift @argument;
202 3 100       20 my $value = defined $first ? Net::DNS::Text->new($first)->raw : $first;
203 3         15 return $self->_parameter( $key, $value, @argument );
204             }
205              
206              
207             sub _parameter {
208 40     40   127 my ( $self, $key, @argument ) = @_;
209              
210 40         172 my ($paramref) = grep {defined} $self->{parameters}, [];
  80         182  
211 40         122 my %parameter = @$paramref;
212              
213 40 100       92 if ( scalar @argument ) {
214 22         40 my $arg = shift @argument; # key($value);
215 22 100       83 delete $parameter{$key} unless defined $arg;
216 22 100       74 die( $self->type . qq[: duplicate parameter key$key] ) if defined $parameter{$key};
217 21 100       63 die( $self->type . qq[: unexpected key$key value] ) if scalar @argument;
218 20         37 delete $self->{rdata};
219 20 100       59 $parameter{$key} = $arg if defined $arg;
220 20         75 $self->{parameters} = [map { ( $_, $parameter{$_} ) } sort { $a <=> $b } keys %parameter];
  41         130  
  32         67  
221             }
222              
223 38         278 return $parameter{$key};
224             }
225              
226              
227             sub _concatenate { ## concatenate octet string(s)
228 18     18   43 my @arg = @_;
229 18 100       124 return scalar(@arg) > 1 ? join( '', @arg ) : @arg;
230             }
231              
232             sub _list { ## context-dependent list or single value
233 9     9   25 my @arg = @_;
234 9 100       62 return wantarray ? @arg : shift @arg;
235             }
236              
237             sub _address4 {
238 2     2   6 my @arg = @_;
239 2         4 return _concatenate( map { Net::DNS::RR::A::address( {}, $_ ) } @arg );
  3         13  
240             }
241              
242             sub _address6 {
243 2     2   6 my @arg = @_;
244 2         6 return _concatenate( map { Net::DNS::RR::AAAA::address( {}, $_ ) } @arg );
  3         13  
245             }
246              
247             sub _domain {
248 10     10   22 my @arg = @_;
249 10         19 return _concatenate( map { Net::DNS::DomainName->new($_)->encode() } @arg );
  10         47  
250             }
251              
252             sub _integer16 {
253 4     4   13 my @arg = @_;
254 4         9 return _concatenate( map { pack( 'n', $_ ) } @arg );
  9         62  
255             }
256              
257              
258             sub generic {
259 4     4 1 13 my $self = shift;
260 4         7 my $size = 0;
261 4         6 my @rdata;
262 4         14 my ($paramref) = grep {defined} $self->{parameters}, [];
  8         41  
263 4         36 my @parameters = @$paramref;
264 4         14 while (@parameters) {
265 18         29 my $key = shift @parameters;
266 18         30 my $val = shift @parameters;
267 18         81 push @rdata, "\n", unpack 'H4H4', pack( 'n2', $key, length $val );
268 18         33 $size += 4 + length $val;
269 18         106 push @rdata, split /(\S{32})/, unpack 'H*', $val;
270             }
271              
272 4         12 my @ttl = grep {defined} $self->{ttl};
  4         13  
273 4 100       11 my @class = map { $_ ? "CLASS$_" : () } $self->{class};
  4         48  
274 4         24 my @core = ( $self->{owner}->string, @ttl, @class, "TYPE$self->{type}" );
275 4         29 return join "\n\t", Net::DNS::RR::_wrap( "@core ( \\# $size", @rdata, ')' );
276             }
277              
278             ########################################
279              
280              
281             1;
282             __END__