File Coverage

blib/lib/Net/DNS/RR.pm
Criterion Covered Total %
statement 294 294 100.0
branch 142 142 100.0
condition 42 42 100.0
subroutine 48 48 100.0
pod 19 21 100.0
total 545 547 100.0


line stmt bran cond sub pod time code
1             package Net::DNS::RR;
2              
3 96     96   549480 use strict;
  96         202  
  96         7570  
4 96     96   503 use warnings;
  96         221  
  96         10273  
5              
6             our $VERSION = (qw$Id: RR.pm 2037 2025-08-18 14:39:32Z willem $)[2];
7              
8              
9             =head1 NAME
10              
11             Net::DNS::RR - DNS resource record base class
12              
13             =head1 SYNOPSIS
14              
15             use Net::DNS;
16              
17             $rr = Net::DNS::RR->new('example.com IN AAAA 2001:DB8::1');
18              
19             $rr = Net::DNS::RR->new(
20             owner => 'example.com',
21             type => 'AAAA',
22             address => '2001:DB8::1'
23             );
24              
25              
26             =head1 DESCRIPTION
27              
28             Net::DNS::RR is the base class for DNS Resource Record (RR) objects.
29             See also the manual pages for each specific RR type.
30              
31             =cut
32              
33              
34 96     96   1352 use integer;
  96         204  
  96         684  
35 96     96   2757 use Carp;
  96         199  
  96         11561  
36              
37 96     96   670 use constant LIB => grep { $_ ne '.' } grep { !ref($_) } @INC;
  96         204  
  96         319  
  864         10420  
  864         1544  
38              
39 96     96   56108 use Net::DNS::Parameters qw(%classbyname :class :type);
  96         440  
  96         23280  
40 96     96   54881 use Net::DNS::DomainName;
  96         416  
  96         113323  
41              
42              
43             =head1 METHODS
44              
45             B Do not assume the RR objects you receive from a query
46             are of a particular type. You must always check the object type
47             before calling any of its methods. If you call an unknown method,
48             you will get an error message and execution will be terminated.
49              
50             =cut
51              
52             sub new {
53 1323     1323 1 15792739 my ( $class, @list ) = @_;
54 1323         4301 my $rr = eval {
55 1323         5627 local $SIG{__DIE__};
56 1323 100       6717 scalar @list > 1 ? &_new_hash : &_new_string;
57             };
58 1323 100       7038 return $rr if $rr;
59 24 100       51 my @param = map { defined($_) ? split /\s+/ : 'undef' } @list;
  30         126  
60 24         124 my $stmnt = substr "$class->new( @param )", 0, 80;
61 24         3007 croak "${@}in $stmnt\n";
62             }
63              
64              
65             =head2 new (from string)
66              
67             $aaaa = Net::DNS::RR->new('host.example.com. 86400 AAAA 2001:DB8::1');
68             $mx = Net::DNS::RR->new('example.com. 7200 MX 10 mailhost.example.com.');
69             $cname = Net::DNS::RR->new('www.example.com 300 IN CNAME host.example.com');
70             $txt = Net::DNS::RR->new('txt.example.com 3600 HS TXT "text data"');
71              
72             Returns an object of the appropriate RR type, or a L object
73             if the type is not implemented. The attribute values are extracted from the
74             string passed by the user. The syntax of the argument string follows the
75             RFC1035 specification for zone files, and is compatible with the result
76             returned by the string method.
77              
78             The owner and RR type are required; all other information is optional.
79             Omitting the optional fields is useful for creating the empty RDATA
80             sections required for certain dynamic update operations.
81             See the L manual page for additional examples.
82              
83             All names are interpreted as fully qualified domain names.
84             The trailing dot (.) is optional.
85              
86             =cut
87              
88             my $PARSE_REGEX = q/("[^"]*")|;[^\n]*|[ \t\n\r\f()]+/; # NB: *not* \s (matches Unicode white space)
89              
90             sub _new_string {
91 1020     1020   2269 my ( $base, $string ) = @_;
92 1020 100       2891 die 'argument absent or undefined' unless defined $string;
93 1019 100       2545 die 'non-scalar argument' if ref $string;
94              
95             # parse into quoted strings, contiguous non-whitespace and (discarded) comments
96 1017         1832 local $_ = $string;
97 1017         2560 s/\\\\/\\092/g; # disguise escaped escape
98 1017         1927 s/\\"/\\034/g; # disguise escaped quote
99 1017         1844 s/\\\(/\\040/g; # disguise escaped bracket
100 1017         1953 s/\\\)/\\041/g; # disguise escaped bracket
101 1017         1736 s/\\;/\\059/g; # disguise escaped semicolon
102 1017 100       42855 my ( $owner, @token ) = grep { defined && length } split /$PARSE_REGEX/o;
  9261         26375  
103              
104 1017 100       3312 die 'unable to parse RR string' unless scalar @token;
105 1016         2126 my $t1 = $token[0];
106 1016         1665 my $t2 = $token[1];
107              
108 1016         1726 my ( $ttl, $class );
109 1016 100 100     7009 if ( not defined $t2 ) { #
    100          
    100          
110 70 100       416 @token = ('ANY') if $classbyname{uc $t1}; #
111             } elsif ( $t1 =~ /^\d/ ) {
112 495         1029 $ttl = shift @token; # []
113 495 100 100     2115 $class = shift @token if $classbyname{uc $t2} || $t2 =~ /^CLASS\d/i;
114             } elsif ( $classbyname{uc $t1} || $t1 =~ /^CLASS\d/i ) {
115 53         125 $class = shift @token; # []
116 53 100       213 $ttl = shift @token if $t2 =~ /^\d/;
117             }
118              
119 1016         2226 my $type = shift(@token);
120 1016         1800 my $populated = scalar @token;
121              
122 1016         3551 my $self = $base->_subclass( $type, $populated ); # create RR object
123 1015         3666 $self->owner($owner);
124 1015         2990 &class( $self, $class ); # specify CLASS
125 1015         3010 &ttl( $self, $ttl ); # specify TTL
126              
127 1015 100       2784 return $self unless $populated; # empty RR
128              
129 926 100 100     3753 if ( $#token && $token[0] =~ /^[\\]?#$/ ) {
130 29         49 shift @token; # RFC3597 hexadecimal format
131 29   100     115 my $rdlen = shift(@token) || 0;
132 29         170 my $rdata = pack 'H*', join( '', @token );
133 29 100       128 die 'length and hexadecimal data inconsistent' unless $rdlen == length $rdata;
134 26         87 $self->rdata($rdata); # unpack RDATA
135             } else {
136 897         3050 $self->_parse_rdata(@token); # parse arguments
137             }
138              
139 901         3022 $self->_post_parse();
140 890         4600 return $self;
141             }
142              
143              
144             =head2 new (from hash)
145              
146             $rr = Net::DNS::RR->new(%hash);
147              
148             $rr = Net::DNS::RR->new(
149             owner => 'host.example.com',
150             ttl => 86400,
151             class => 'IN',
152             type => 'AAAA',
153             address => '2001:DB8::1'
154             );
155            
156             $rr = Net::DNS::RR->new(
157             owner => 'txt.example.com',
158             type => 'TXT',
159             txtdata => [ 'one', 'two' ]
160             );
161              
162             Returns an object of the appropriate RR type, or a L object
163             if the type is not implemented. Consult the relevant manual pages for the
164             usage of type specific attributes.
165              
166             The owner and RR type are required; all other information is optional.
167             Omitting optional attributes is useful for creating the empty RDATA
168             sections required for certain dynamic update operations.
169              
170             =cut
171              
172             my @core = qw(owner name type class ttl rdlength);
173              
174             sub _new_hash {
175 590     590   1511 my $base = shift;
176              
177 590         2910 my %argument = ( owner => '.', type => 'NULL' );
178 590         1121 my @attribute;
179 590         2278 while ( my $key = shift ) {
180 1376         2677 push @attribute, $key;
181 1376         7989 $argument{lc $key} = shift;
182             }
183              
184 590         2931 my ( $owner, $name, $type, $class, $ttl ) = delete @argument{@core};
185              
186 590         2669 my $self = $base->_subclass( $type, scalar(%argument) );
187 590 100       3550 $self->owner( $name ? $name : $owner );
188 590 100       1817 $self->class($class) if defined $class; # optional CLASS
189 590 100       1644 $self->ttl($ttl) if defined $ttl; # optional TTL
190              
191 590         1053 eval {
192 590         1377 foreach my $attribute (@attribute) {
193 1376         4423 my $value = $argument{lc $attribute};
194 1376 100       3382 next unless defined $value;
195 473 100       2424 $self->$attribute( ref($value) eq 'ARRAY' ? @$value : $value );
196             }
197             };
198 590 100       1555 die ref($self) eq __PACKAGE__ ? "type $type not implemented" : () if $@;
    100          
199              
200 588         2433 $self->_post_parse();
201 588         3731 return $self;
202             }
203              
204              
205             =head2 decode
206              
207             ( $rr, $next ) = Net::DNS::RR->decode( \$data, $offset, @opaque );
208              
209             Decodes a DNS resource record at the specified location within a
210             DNS packet.
211              
212             The argument list consists of a reference to the buffer containing
213             the packet data and offset indicating where resource record begins.
214             Any remaining arguments are passed as opaque data to subordinate
215             decoders and do not form part of the published interface.
216              
217             Returns a C object and the offset of the next record
218             in the packet.
219              
220             An exception is raised if the data buffer contains insufficient or
221             corrupt data.
222              
223             =cut
224              
225 96     96   932 use constant RRFIXEDSZ => length pack 'n2 N n', (0) x 4;
  96         204  
  96         399337  
226              
227             sub decode {
228 9346     9346 1 24716 my ( $base, @argument ) = @_;
229              
230 9346         27778 my ( $owner, $fixed ) = Net::DNS::DomainName1035->decode(@argument);
231 9345         14855 my $index = $fixed + RRFIXEDSZ;
232 9345         15504 my ( $data, $offset, @opaque ) = @argument;
233 9345 100       18786 die 'corrupt wire-format data' if length $$data < $index;
234 9344         27564 my $self = $base->_subclass( unpack "\@$fixed n", $$data );
235 9344         18452 $self->{owner} = $owner;
236 9344         23971 @{$self}{qw(class ttl rdlength)} = unpack "\@$fixed x2 n N n", $$data;
  9344         24905  
237              
238 9344         17991 my $next = $index + $self->{rdlength};
239 9344 100       21395 die 'corrupt wire-format data' if length $$data < $next;
240              
241 9343 100 100     24277 if ( $next > $index or $self->type eq 'OPT' ) {
242 9342         25765 local $self->{offset} = $offset;
243 9342         13410 eval { $self->_decode_rdata( $data, $index, @opaque ) };
  9342         23488  
244 9342 100       23640 warn $@ if $@;
245             }
246              
247 9343 100       31885 return wantarray ? ( $self, $next ) : $self;
248             }
249              
250              
251             =head2 encode
252              
253             $data = $rr->encode( $offset, @opaque );
254              
255             Returns the C in binary format suitable for inclusion
256             in a DNS packet buffer.
257              
258             The offset indicates the intended location within the packet data
259             where the C is to be stored.
260              
261             Any remaining arguments are opaque data which are passed intact to
262             subordinate encoders.
263              
264             =cut
265              
266             sub encode {
267 1145     1145 1 48007 my ( $self, $offset, @opaque ) = @_;
268 1145 100       2737 ( $offset, @opaque ) = ( 0x4000, {} ) unless defined $offset;
269              
270 1145         6249 my $owner = $self->{owner}->encode( $offset, @opaque );
271 1145         1990 my ( $type, $class, $ttl ) = @{$self}{qw(type class ttl)};
  1145         3112  
272 1145 100       2625 my $rdata = $self->_empty ? '' : $self->_encode_rdata( $offset + length($owner) + RRFIXEDSZ, @opaque );
273 1145   100     9993 return pack 'a* n2 N n a*', $owner, $type, $class || 1, $ttl || 0, length $rdata, $rdata;
      100        
274             }
275              
276              
277             =head2 canonical
278              
279             $data = $rr->canonical;
280              
281             Returns the C in canonical binary format suitable for
282             DNSSEC signature validation.
283              
284             The absence of the associative array argument signals to subordinate
285             encoders that the canonical uncompressed form of embedded domain
286             names is to be used.
287              
288             =cut
289              
290             sub canonical {
291 305     305 1 548 my $self = shift;
292              
293 305         896 my $owner = $self->{owner}->canonical;
294 305         496 my ( $type, $class, $ttl ) = @{$self}{qw(type class ttl)};
  305         811  
295 305 100       812 my $rdata = $self->_empty ? '' : $self->_encode_rdata( length($owner) + RRFIXEDSZ );
296 305   100     3084 return pack 'a* n2 N n a*', $owner, $type, $class || 1, $ttl || 0, length $rdata, $rdata;
      100        
297             }
298              
299              
300             =head2 print
301              
302             $rr->print;
303              
304             Prints the resource record to the currently selected output filehandle.
305             Calls the string method to get the formatted RR representation.
306              
307             =cut
308              
309             sub print {
310 27     27 1 2521 print shift->string, "\n";
311 27         545 return;
312             }
313              
314              
315             =head2 string
316              
317             print $rr->string, "\n";
318              
319             Returns a string representation of the RR using the master file format
320             mandated by RFC1035.
321             All domain names are fully qualified with trailing dot.
322             This differs from RR attribute methods, which omit the trailing dot.
323              
324             =cut
325              
326             sub string {
327 1118     1118 1 5765 my $self = shift;
328              
329 1118         5611 my $owner = $self->{owner}->string;
330 1118 100       3696 my @ttl = defined $self->{ttl} ? $self->{ttl} : ();
331 1118 100       3518 my @class = defined $self->{class} ? $self->class : ();
332 1118         4193 my @core = ( $owner, @ttl, @class, $self->type );
333              
334 1118         4672 local $SIG{__DIE__};
335 1118         3425 my $empty = $self->_empty;
336 1118 100       3159 my @rdata = $empty ? () : eval { $self->_format_rdata };
  1093         3587  
337 1118 100       3989 carp $@ if $@;
338              
339 1118 100       3703 my $tab = length($owner) < 72 ? "\t" : ' ';
340 1118         5686 my @line = _wrap( join( $tab, @core, '(' ), @rdata, ')' );
341              
342 1118         2681 my $last = pop(@line); # last or only line
343 1118 100       3460 $last = join $tab, @core, "@rdata" unless scalar(@line);
344              
345 1118 100       2881 $self->_annotation('no data') if $empty;
346 1118         3886 return join "\n\t", @line, _wrap( $last, $self->_annotation );
347             }
348              
349              
350             =head2 plain
351              
352             $plain = $rr->plain;
353              
354             Returns a simplified single-line representation of the RR.
355             This facilitates interaction with programs like nsupdate
356             which have rudimentary parsers.
357              
358             =cut
359              
360             sub plain {
361 8     8 1 72 return join ' ', shift->token;
362             }
363              
364              
365             =head2 token
366              
367             @token = $rr->token;
368              
369             Returns a token list representation of the RR zone file string.
370              
371             =cut
372              
373             sub token {
374 7     7 1 10 my $self = shift;
375              
376 7 100       24 my @ttl = defined $self->{ttl} ? $self->{ttl} : ();
377 7 100       20 my @class = defined $self->{class} ? $self->class : ();
378 7         23 my @core = ( $self->{owner}->string, @ttl, @class, $self->type );
379              
380             # parse into quoted strings, contiguous non-whitespace and (discarded) comments
381 7 100       25 local $_ = $self->_empty ? '' : join( ' ', $self->_format_rdata );
382 7         20 s/\\\\/\\092/g; # disguise escaped escape
383 7         11 s/\\"/\\034/g; # disguise escaped quote
384 7         13 s/\\\(/\\040/g; # disguise escaped bracket
385 7         11 s/\\\)/\\041/g; # disguise escaped bracket
386 7         9 s/\\;/\\059/g; # disguise escaped semicolon
387 7 100       151 return ( @core, grep { defined && length } split /$PARSE_REGEX/o );
  35         603  
388             }
389              
390              
391             =head2 generic
392              
393             $generic = $rr->generic;
394              
395             Returns the generic RR representation defined in RFC3597. This facilitates
396             creation of zone files containing RRs unrecognised by outdated nameservers
397             and provisioning software.
398              
399             =cut
400              
401             sub generic {
402 8     8 1 17 my $self = shift;
403              
404 8 100       30 my @ttl = defined $self->{ttl} ? $self->{ttl} : ();
405 8 100       32 my @class = defined $self->{class} ? "CLASS$self->{class}" : ();
406 8         34 my @core = ( $self->{owner}->string, @ttl, @class, "TYPE$self->{type}" );
407 8         34 my $data = $self->rdata;
408 8         64 my @data = ( '\\#', length($data), split /(\S{32})/, unpack 'H*', $data );
409 8         52 my @line = _wrap( "@core (", @data, ')' );
410 8 100       30 return join "\n\t", @line if scalar(@line) > 1;
411 7         42 return join ' ', @core, @data;
412             }
413              
414              
415             =head2 owner name
416              
417             $name = $rr->owner;
418              
419             Returns the owner name of the record.
420              
421             =cut
422              
423             sub owner {
424 2325     2325 1 5846 my ( $self, @name ) = @_;
425 2325         5164 for (@name) { $self->{owner} = Net::DNS::DomainName1035->new($_) }
  1605         7531  
426 2325 100       7305 return defined wantarray ? $self->{owner}->name : undef;
427             }
428              
429 425     425 1 11696 sub name { return &owner; } ## historical
430              
431              
432             =head2 type
433              
434             $type = $rr->type;
435              
436             Returns the record type.
437              
438             =cut
439              
440             sub type {
441 2225     2225 1 4766 my ( $self, @value ) = @_;
442 2225         4548 for (@value) { croak 'not possible to change RR->type' }
  1         164  
443 2224         7900 return typebyval( $self->{type} );
444             }
445              
446              
447             =head2 class
448              
449             $class = $rr->class;
450              
451             Resource record class.
452              
453             =cut
454              
455             sub class {
456 2527     2527 1 8824 my ( $self, $class ) = @_;
457 2527 100       7265 return $self->{class} = classbyname($class) if defined $class;
458 1560 100       5996 return defined $self->{class} ? classbyval( $self->{class} ) : 'IN';
459             }
460              
461              
462             =head2 ttl
463              
464             $ttl = $rr->ttl;
465             $ttl = $rr->ttl(3600);
466              
467             Resource record time to live in seconds.
468              
469             =cut
470              
471             # The following time units are recognised, but are not part of the
472             # published API. These are required for parsing BIND zone files but
473             # should not be used in other contexts.
474             my %unit = ( W => 604800, D => 86400, H => 3600, M => 60, S => 1 );
475              
476             sub ttl {
477 1129     1129 1 2223 my ( $self, $time ) = @_;
478              
479 1129 100 100     3533 return $self->{ttl} || 0 unless defined $time; # avoid defining rr->{ttl}
480              
481 596         1101 my $ttl = 0;
482 596         3742 my %time = reverse split /(\D)\D*/, $time . 'S';
483 596         2274 while ( my ( $u, $t ) = each %time ) {
484 596   100     1749 my $scale = $unit{uc $u} || die qq(bad time: $t$u);
485 595         2239 $ttl += $t * $scale;
486             }
487 595         2060 return $self->{ttl} = $ttl;
488             }
489              
490              
491             ################################################################################
492             ##
493             ## Default implementation for unknown RR type
494             ##
495             ################################################################################
496              
497             sub _decode_rdata { ## decode rdata from wire-format octet string
498 7     7   29 my ( $self, $data, $offset ) = @_;
499 7         18 return $self->{rdata} = substr $$data, $offset, $self->{rdlength};
500             }
501              
502              
503             sub _encode_rdata { ## encode rdata as wire-format octet string
504 12     12   33 return shift->{rdata};
505             }
506              
507              
508             sub _format_rdata { ## format rdata portion of RR string
509 7     7   18 my $rdata = shift->rdata; # RFC3597 unknown RR format
510 7         48 return ( '\\#', length($rdata), split /(\S{32})/, unpack 'H*', $rdata );
511             }
512              
513              
514             sub _parse_rdata { ## parse RR attributes in argument list
515 2     2   4 my $self = shift;
516 2 100       10 die join ' ', 'type', $self->type, 'not implemented' if ref($self) eq __PACKAGE__;
517 1         6 die join ' ', 'no zone file representation defined for', $self->type;
518             }
519              
520              
521       1435     sub _post_parse { } ## parser post processing
522              
523              
524       117     sub _defaults { } ## set attribute default values
525              
526              
527             sub dump { ## print internal data structure
528 2     2 0 146 my @data = @_; # uncoverable pod
529 2         12 require Data::Dumper;
530 2   100     7 local $Data::Dumper::Maxdepth = $Data::Dumper::Maxdepth || 6;
531 2   100     6 local $Data::Dumper::Sortkeys = $Data::Dumper::Sortkeys || 1;
532 2   100     6 local $Data::Dumper::Useqq = $Data::Dumper::Useqq || 1;
533 2         9 return print Data::Dumper::Dumper(@data);
534             }
535              
536             sub rdatastr { ## historical RR subtype method
537 2     2 0 3 my $self = shift; # uncoverable pod
538 2         9 $self->_deprecate('prefer $rr->rdstring()');
539 2         7 return $self->rdstring;
540             }
541              
542              
543             =head2 rdata
544              
545             $rr = Net::DNS::RR->new( type => NULL, rdata => 'arbitrary' );
546              
547             Resource record data section when viewed as opaque octets.
548              
549             =cut
550              
551             sub rdata {
552 173     173 1 4205 my $self = shift;
553              
554 173 100       861 return $self->_empty ? '' : eval { $self->_encode_rdata( 0x4000, {} ) } unless @_;
  116 100       540  
555              
556 34   100     90 my $data = shift || '';
557 34 100       170 $self->_decode_rdata( \$data, 0 ) if ( $self->{rdlength} = length $data );
558 32         65 return;
559             }
560              
561              
562             =head2 rdstring
563              
564             $rdstring = $rr->rdstring;
565              
566             Returns a string representation of the RR-specific data.
567              
568             =cut
569              
570             sub rdstring {
571 88     88 1 7392 my $self = shift;
572 88         296 local $SIG{__DIE__};
573              
574 88 100       276 my @rdata = $self->_empty ? () : eval { $self->_format_rdata };
  69         218  
575 88 100       404 carp $@ if $@;
576              
577 88         222 return join "\n\t", _wrap(@rdata);
578             }
579              
580              
581             =head2 rdlength
582              
583             $rdlength = $rr->rdlength;
584              
585             Returns the uncompressed length of the encoded RR-specific data.
586              
587             =cut
588              
589             sub rdlength {
590 7     7 1 90 return length shift->rdata;
591             }
592              
593              
594             ###################################################################################
595              
596             =head1 Sorting of RR arrays
597              
598             Sorting of RR arrays is done by Net::DNS::rrsort(), see documentation
599             for L. This package provides class methods to set the
600             comparator function used for a particular RR based on its attributes.
601              
602              
603             =head2 set_rrsort_func
604              
605             my $function = sub { ## numerically ascending order
606             $Net::DNS::a->{'preference'} <=> $Net::DNS::b->{'preference'};
607             };
608              
609             Net::DNS::RR::MX->set_rrsort_func( 'preference', $function );
610              
611             Net::DNS::RR::MX->set_rrsort_func( 'default_sort', $function );
612              
613             set_rrsort_func() must be called as a class method. The first argument is
614             the attribute name on which the sorting is to take place. If you specify
615             "default_sort" then that is the sort algorithm that will be used when
616             get_rrsort_func() is called without an RR attribute as argument.
617              
618             The second argument is a reference to a comparator function that uses the
619             global variables $a and $b in the Net::DNS package. During sorting, the
620             variables $a and $b will contain references to objects of the class whose
621             set_rrsort_func() was called. The above sorting function will only be
622             applied to Net::DNS::RR::MX objects.
623              
624             The above example is the sorting function implemented in MX.
625              
626             =cut
627              
628             our %rrsortfunct;
629              
630             sub set_rrsort_func {
631 42     42 1 95 my $class = shift;
632 42         72 my $attribute = shift;
633 42         83 my $function = shift;
634              
635 42         290 my ($type) = $class =~ m/::([^:]+)$/;
636 42         144 $rrsortfunct{$type}{$attribute} = $function;
637 42         108 return;
638             }
639              
640              
641             =head2 get_rrsort_func
642              
643             $function = Net::DNS::RR::MX->get_rrsort_func('preference');
644             $function = Net::DNS::RR::MX->get_rrsort_func();
645              
646             get_rrsort_func() returns a reference to the comparator function.
647              
648             =cut
649              
650             my $default = sub { return $Net::DNS::a->canonical() cmp $Net::DNS::b->canonical(); };
651              
652             sub get_rrsort_func {
653 13     13 1 2430 my $class = shift;
654 13   100     64 my $attribute = shift || 'default_sort';
655              
656 13         95 my ($type) = $class =~ m/::([^:]+)$/;
657              
658 13   100     56 return $rrsortfunct{$type}{$attribute} || return $default;
659             }
660              
661              
662             ################################################################################
663             #
664             # Net::DNS::RR->_subclass($rrname)
665             # Net::DNS::RR->_subclass($rrname, $default)
666             #
667             # Create a new object blessed into appropriate RR subclass, after
668             # loading the subclass module (if necessary). A subclass with no
669             # corresponding module will be regarded as unknown and blessed
670             # into the RR base class.
671             #
672             # The optional second argument indicates that default values are
673             # to be copied into the newly created object.
674              
675             our %_MINIMAL = ( 255 => bless ['type' => 255], __PACKAGE__ );
676             our %_LOADED = %_MINIMAL;
677              
678             sub _subclass {
679 10950     10950   21162 my ( $class, $rrname, $default ) = @_;
680              
681 10950 100       27590 unless ( $_LOADED{$rrname} ) {
682 184         1052 my $rrtype = typebyname($rrname);
683              
684 183 100       830 unless ( $_LOADED{$rrtype} ) { # load once only
685 173         1296 local @INC = LIB;
686              
687 173         651 my $identifier = typebyval($rrtype);
688 173         648 $identifier =~ s/\W/_/g; # kosher Perl identifier
689              
690 173         730 my $subclass = join '::', __PACKAGE__, $identifier;
691              
692 173 100       16499 unless ( eval "require $subclass" ) { ## no critic ProhibitStringyEval
693 1         12 my $perl = Net::DNS::Parameters::_typespec("$rrtype.RRTYPE");
694 1         4 $subclass = join '::', __PACKAGE__, "TYPE$rrtype";
695             push @INC, sub { # see perldoc -f require
696 1     1   5 my @line = split /\n/, $perl;
697 1         67 return ( sub { defined( $_ = shift @line ) } );
  1         33  
698 1         7 };
699 1         52 eval "require $subclass"; ## no critic ProhibitStringyEval
700             }
701              
702 173 100       1037 $subclass = __PACKAGE__ if $@;
703              
704             # cache pre-built minimal and populated default object images
705 173         755 my @base = ( 'type' => $rrtype );
706 173         1392 $_MINIMAL{$rrtype} = bless [@base], $subclass;
707              
708 173         793 my $object = bless {@base}, $subclass;
709 173         1479 $object->_defaults;
710 173         2436 $_LOADED{$rrtype} = bless [%$object], $subclass;
711             }
712              
713 183         770 $_MINIMAL{$rrname} = $_MINIMAL{$rrtype};
714 183         512 $_LOADED{$rrname} = $_LOADED{$rrtype};
715             }
716              
717 10949 100       25316 my $prebuilt = $default ? $_LOADED{$rrname} : $_MINIMAL{$rrname};
718 10949         46191 return bless {@$prebuilt}, ref($prebuilt); # create object
719             }
720              
721              
722             sub _annotation {
723 1177     1177   10987 my ( $self, @note ) = @_;
724 1177 100       2828 push @{$self->{annotation}}, "\t; @note" if scalar @note;
  59         298  
725 1177 100       2859 return wantarray ? @{delete( $self->{annotation} ) || []} : undef;
  1118 100       6976  
726             }
727              
728              
729             my %warned;
730              
731             sub _deprecate {
732 9     9   23 my ( undef, @note ) = @_;
733 9 100       1505 carp "deprecated method; @note" unless $warned{"@note"}++;
734 9         52 return;
735             }
736              
737              
738             my %ignore = map { ( $_ => 1 ) } @core, 'annotation', '#';
739              
740             sub _empty {
741 2802     2802   4153 my $self = shift;
742 2802   100     13344 return not( $self->{'#'} ||= scalar grep { !$ignore{$_} } keys %$self );
  8267         19979  
743             }
744              
745              
746             sub _wrap {
747 2363     2363   6969 my @text = @_;
748 2363         3795 my $cols = 80;
749 2363         3419 my $coln = 0;
750              
751 2363         3787 my ( @line, @fill );
752 2363         5151 foreach (@text) {
753 5615   100     12012 $coln += ( length || next ) + 1;
754 5579 100       10935 if ( $coln > $cols ) { # start new line
755 1779 100       5651 push( @line, join ' ', @fill ) if @fill;
756 1779         2999 $coln = length;
757 1779         3514 @fill = ();
758             }
759 5579 100       11739 $coln = $cols if chomp; # force line break
760 5579 100       13807 push( @fill, $_ ) if length;
761             }
762 2363         23038 return ( @line, join ' ', @fill );
763             }
764              
765              
766             ################################################################################
767              
768       1     sub DESTROY { } ## Avoid tickling AUTOLOAD (in cleanup)
769              
770             ## no critic
771             sub AUTOLOAD { ## Default method
772 11     11   74 my ($self) = @_;
773              
774 96     96   1001 no strict 'refs'; ## no critic ProhibitNoStrict
  96         204  
  96         49970  
775 11         21 our $AUTOLOAD;
776 11         62 my ($method) = reverse split /::/, $AUTOLOAD;
777              
778 11         40 my $canonical = lc($method); ## tolerate mixed-case attribute name
779 11 100       170 if ( $self->can($canonical) ) {
780 6     25   38 *{$AUTOLOAD} = sub { shift->$canonical(@_) };
  6         42  
  25         3299  
781 6         26 return &$AUTOLOAD;
782             }
783              
784 5         14 my $oref = ref($self);
785 5     1   24 *{$AUTOLOAD} = sub { }; ## suppress deep recursion
  5         65  
786 5 100       213 croak qq[$self has no class method "$method"] unless $oref;
787              
788 4         23 my $string = $self->string;
789 4         73 my @object = grep { defined($_) } $oref, $oref->VERSION;
  8         21  
790 4         20 my $module = join '::', __PACKAGE__, $self->type;
791 4 100       145 eval("require $module") if $oref eq __PACKAGE__; ## no critic ProhibitStringyEval
792              
793 4         27 @_ = (<<"END");
794             *** FATAL PROGRAM ERROR!! Unknown instance method "$method"
795             *** which the program has attempted to call for the object:
796             ***
797             $string
798             ***
799             *** THIS IS A BUG IN THE CALLING SOFTWARE, which incorrectly assumes
800             *** that the object would be of a particular type. The type of an
801             *** object should be checked before calling any of its methods.
802             ***
803             @object
804             $@
805             END
806 4         1427 goto &Carp::confess;
807             }
808              
809              
810             1;
811             __END__