File Coverage

blib/lib/Net/DNS/RR/DELEG.pm
Criterion Covered Total %
statement 198 198 100.0
branch 68 68 100.0
path n/a
condition 8 8 100.0
subroutine 29 29 100.0
pod 5 6 100.0
total 308 309 100.0


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