File Coverage

blib/lib/Net/DNS/ToolKit/RR.pm
Criterion Covered Total %
statement 123 138 89.1
branch 22 26 84.6
condition 12 21 57.1
subroutine 23 29 79.3
pod 1 7 14.2
total 181 221 81.9


line stmt bran cond sub pod time code
1             package Net::DNS::ToolKit::RR;
2              
3             #use 5.006;
4 14     14   10363 use strict;
  14         27  
  14         1011  
5             #use diagnostics;
6             #use warnings;
7              
8 14     14   77 use Net::DNS::Codes qw(:RRs);
  14         21  
  14         8632  
9 14         1117 use Net::DNS::ToolKit qw(
10             get16
11             get32
12             put16
13             put32
14             getstring
15             dn_comp
16             dn_expand
17 14     14   193 );
  14         31  
18 14     14   83 use vars qw($VERSION $autoload *sub);
  14         25  
  14         17115  
19             require Net::DNS::ToolKit::Question;
20              
21             $VERSION = do { my @r = (q$Revision: 0.09 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r };
22              
23             sub remoteload {
24             # *sub = $autoload;
25 23     23 0 151 (my $RRtype = $autoload ) =~ s/.*::(\w+):://;
26             # function = $1, one of get,put,parse
27 23         41 local $_;
28 23         76 ($autoload,$_) = instantiate($RRtype,$1);
29             # my $code = 'package '. __PACKAGE__ .'::'. $1 .'; '.'*'. $RRtype .'=\&'. $autoload;
30 23         128 my $code = 'package '. __PACKAGE__ .'::'. $1 .'; '.'*'. $RRtype .
31             q| = sub { unshift @_,'|. $autoload . q|'; &|. $_ .'};';
32 23     0   1967 eval "$code";
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
33              
34             # print "AUTOLOAD=",*sub,";\n";
35             # print "subname=$autoload RRtype=$RRtype func=$1\n";
36             # print 'code=', $code, "\n";
37              
38             # no strict;
39             # eval { *sub = sub { unshift @_,$autoload; &$_ } };
40             # goto &{*sub};
41 23         83 unshift @_,$autoload;
42 23         127 goto &$_;
43             }
44              
45             # return target function, target interpreter
46             sub instantiate {
47 28     28 0 76 my($RRtype,$func) = @_;
48 28 50       85 if ($RRtype eq 'DESTROY') { # should never get here
49 0         0 die __PACKAGE__.".pm: DESTROY must be defined internally in the calling package\n";
50             } else {
51 28         80 my $filename = __PACKAGE__.'::'.$RRtype.'.pm';
52 28         123 $filename =~ s#::#/#g;
53 28         51 my $save = $@;
54 28         46 eval { local $SIG{__DIE__}; require $filename };
  28         102  
  28         13765  
55 28 100       138 if ($@) {
56             # die __PACKAGE__.'::RR'.$func.' not implemented'
57             # if $func eq 'put';
58             # $@ = $save;
59             # $RRtype = 'NotImplemented';
60 12         18 my $generic;
61 12 100 66     116 if ( $RRtype =~ /^TYPE(\d+)$/ &&
      66        
62             ($generic = TypeTxt->{$1}) &&
63             $generic =~ /T_(.+)/) {
64 2         33 $generic = __PACKAGE__.'::'. $1;
65             } else {
66 10         95 $generic = __PACKAGE__.'::TYPE';
67             }
68 12         34 local $_ = $generic .'.pm';
69 12         51 s#::#/#g;
70 12         4994 require $_;
71 12         62 my $code = 'package '. __PACKAGE__ .'::'. $RRtype .';
72             *get = \&'. $generic .'::get;
73             *put = \&'. $generic .'::put;
74             *parse = \&'. $generic .'::parse;';
75 12         1122 eval "$code";
76             }
77             }
78             # package from local scope
79 28         158 return (__PACKAGE__.'::'.$RRtype.'::'.$func, __PACKAGE__.'::RR'.$func);
80             }
81              
82             # return instantiated function
83             sub make_function {
84 10     10 0 19 my $type = shift;
85 10         87 (caller(1))[3] =~ /RR(\w+)$/;
86 10         27 my $action = $1;
87 10         19 local $_;
88 10 100 66     39 if (($_ = TypeTxt->{$type}) && $_ =~ /T_(.+)/) { # type is real?
89 4         62 my $function = __PACKAGE__.'::'.$1;
90 4 100       62 if ($function->can($action)) { # if function is instantiated
91 3         13 return $function .= '::'.$action;
92             } else { # instantiate it or NotImplemented
93 1         4 return (instantiate($1,$action))[0];
94             }
95             } else {
96             # return __PACKAGE__.'::NotImplemented::'.$action;
97 6         55 my $function = __PACKAGE__.'::TYPE'. $type;
98 6 100       82 if ($function->can($action)) { # if function is instantiated
99 2         8 return $function .= '::'.$action;
100             } else { # instantiate it or NotImplemented
101 4         20 return (instantiate("TYPE$type",$action))[0];
102             }
103             }
104             }
105              
106             #########################################################
107             # implements the common portion of...
108             # ($newoff,$name,$type,$class,$ttl,$rdlength,$rdata,...)
109             # = $get->next(\$buffer,$offset);
110              
111             sub RRget {
112 12     12 0 30 my($function,$self,$bp,$newoff) = @_;
113 12         87 my ($off,$name) = dn_expand($bp,$newoff);
114 12         113 (my $type, $off) = get16($bp,$off);
115 12         45 (my $class, $off) = get16($bp,$off);
116 12         48 (my $ttl, $off) = get32($bp,$off);
117 12         38 my $rdlength = get16($bp,$off); # scalar context, don't get offset
118 12 100       71 $function = make_function($type) unless $function;
119 14     14   110 no strict;
  14         37  
  14         3102  
120 12         78 ($off, my @results) = &$function($self,$bp,$off);
121 12         201 return($off,$name,$type,$class,$ttl,$rdlength,@results);
122             }
123              
124             #########################################################
125             # implements the common portions of...
126             # ($newoff,@dnptrs)=$put->XYZ(\$buffer,$offset,\@dnptrs,
127             # $name,$type,$class,$ttl,$rdata,...);
128              
129             sub RRput {
130             # extract common elements from input, shrink input
131             # input was: $function,$self,\$buffer,$offset,\@dnptrs,$name,$type,$class,$ttl,@rdata
132 10     10 0 42 my ($func,$put,$bp,$off,$dnp,$name,$type,$class,$ttl) = @_;
133 10 100       80 if (exists $_[1]->{class}) {
134 1         5 ($func,$put,$bp,$off,$dnp,$name,$ttl) = splice(@_,0,7);
135 1         3 $class = $put->{class};
136 1         5 $func =~ /.+::(.+)::put$/;
137 1         2 $type = 'T_'.$1;
138 14     14   119 no strict;
  14         36  
  14         3157  
139 1         7 $type = &$type;
140             } else {
141 9         60 ($func,$put,$bp,$off,$dnp,$name,$type,$class,$ttl) = splice(@_,0,9);
142             }
143             # input is now: @rdata
144 10 50       70 die "'names' ending in '.' are not allowed per RFC's\n"
145             if $name =~ /\.$/;
146 10         296 ($off, my @dnptrs) = dn_comp($bp,$off,\$name,$dnp);
147 10 50       52 unless (@dnptrs) { # if not valid return
148 0         0 while(shift) {}; # empty the input array
149 0         0 return (); # error
150             }
151 10 50       70 return () unless ($off = put16($bp,$off,$type));
152             # the rest should work since offset has been checked
153 10         35 $off = put16($bp,$off,$class);# class
154 10         35 $off = put32($bp,$off,$ttl);# ttl
155 14     14   79 no strict;
  14         27  
  14         2312  
156 10         63 &$func($self,$bp,$off,\@dnptrs,@_);
157             }
158              
159             ####################################################################
160             # implements the common portion of...
161             # ($name,$typeTXT,$classTXT,$ttl,$rdlength,$RDATA,...)
162             # = $parse->XYZ($name,$type,$class,$ttl,$rdlength,$rdata,...)
163              
164             sub RRparse {
165             # extract common elements from input, shrink input
166             # input was: $function,$self,$name,$type,$class,$ttl,$rdlength,@rdata
167 11     11 0 34 my $function = shift;
168             # input is now: $name,$type,$class,$ttl,$rdlength,@rdata
169 11         51 my ($name,$type,$class,$ttl,$rdlength) = splice(@_,1,5); # pass $self,@rdata to $function call
170             # if length is ever needed, add it here
171             # $_[0]->{len} = $rdlength;
172 11         47 $name .= '.'; # terminate domain name
173 11 100       62 $function = make_function($type) unless $function;
174 14     14   82 no strict;
  14         32  
  14         1953  
175 11   66     50 my $typetxt = TypeTxt->{$type} || "TYPE$type";
176 11   33     118 my $classtxt = ClassTxt->{$class} || "CLASS$class";
177 11         86 return($name,$typetxt,$classtxt,$ttl,$rdlength,&{$function}(@_));
  11         79  
178             }
179              
180             #####################################################################
181             ######################### sub PACKAGES ##############################
182             #####################################################################
183              
184             # this entire sub package is obsolete as of v0.07
185             #{
186             # package Net::DNS::ToolKit::RR::NotImplemented;
187             #
188             # sub get {
189             # my($self,$bp,$offset) = @_;
190             # (my $rdlength, $offset) = &Net::DNS::ToolKit::get16($bp,$offset);
191             # $offset += $rdlength;
192             # return($offset,"\0");
193             # }
194             #
195             ## die in loader, unimplemented
196             ## sub put {
197             ## my($bp,$off,$dp) = @_;
198             ## return($off,@$dp);
199             ## }
200             #
201             # sub parse {
202             # shift; # $self
203             # return(@_); # garbage in, garbage out
204             # }
205             #}
206              
207             {
208             package Net::DNS::ToolKit::RR::get;
209 14     14   84 use vars qw($AUTOLOAD);
  14         29  
  14         2541  
210              
211             # preload Question
212             *Question = \&Net::DNS::ToolKit::Question::get;
213              
214             sub AUTOLOAD {
215 5     5   3074 $Net::DNS::ToolKit::RR::autoload = $AUTOLOAD;
216 5         16 goto &Net::DNS::ToolKit::RR::remoteload;
217             }
218             sub next {
219 7     7   5365 unshift @_,undef; # flag to RRget;
220 7         30 goto &Net::DNS::ToolKit::RR::RRget;
221             }
222 0     0   0 sub EmptyList {()};
223 0     0   0 sub DESTROY {};
224             }
225              
226             {
227             package Net::DNS::ToolKit::RR::put;
228 14     14   88 use vars qw($AUTOLOAD);
  14         53  
  14         1622  
229              
230             # preload Question
231             *Question = \&Net::DNS::ToolKit::Question::put;
232              
233             sub AUTOLOAD {
234 10     10   3693 $Net::DNS::ToolKit::RR::autoload = $AUTOLOAD;
235 10         43 goto &Net::DNS::ToolKit::RR::remoteload;
236             }
237 0     0   0 sub DESTROY {};
238             }
239              
240             {
241             package Net::DNS::ToolKit::RR::parse;
242 14     14   68 use vars qw($AUTOLOAD);
  14         25  
  14         6486  
243              
244             # preload Question
245             *Question = \&Net::DNS::ToolKit::Question::parse;
246              
247             sub AUTOLOAD {
248 8     8   10702 $Net::DNS::ToolKit::RR::autoload = $AUTOLOAD;
249 8         37 goto &Net::DNS::ToolKit::RR::remoteload;
250             }
251             # this next sub has been in the distro a long time
252             # $parse->RR
253             # this was unintentional but does not hurt anything
254             sub RR {
255 0     0   0 unshift @_,undef; # flag to RRparse;
256 0         0 goto &Net::DNS::ToolKit::RR::RRparse;
257             }
258             # this SHOULD of been here instead of the above
259             sub next {
260 3     3   1344 unshift @_,undef; # flag to RRparse;
261 3         14 goto &Net::DNS::ToolKit::RR::RRparse;
262             }
263 0     0   0 sub DESTROY {};
264             }
265              
266             =head1 NAME
267              
268             Net::DNS::ToolKit::RR - Resource Record class loader
269              
270             =head1 SYNOPSIS
271              
272             use Net::DNS::ToolKit::RR;
273              
274             ($get,$put,$parse) = new Net::DNS::ToolKit::RR;
275             or
276             ($get,$put,$parse) = Net::DNS::ToolKit::RR->new;
277              
278             retrieve the next record (type unknown)
279             ($newoff,$name,$type,$class,$ttl,$rdlength,$rdata,...)
280             = $get->next(\$buffer,$offset);
281              
282             parse the current record (type in input fields)
283             ($name,$typeTXT,$classTXT,$ttlTXT,$rdlength,$RDATA,...)
284             = $parse->RR($name,$type,$class,$ttl,$rdlength,
285             $rdata,...);
286              
287             ($newoff,@dnptrs)=$put->XYZ(\$buffer,$offset,\@dnptrs,
288             $name,$type,$class,$ttl,$rdata,...);
289              
290             The 'get' and 'parse' operations can also be done
291             by specific record type...
292             ...but why would you use them instead of 'next' & 'RR'?
293              
294             ($newoff,$name,$type,$class,$ttl,$rdlength,$rdata,...)
295             = $get->XYZ(\$buffer,$offset);
296              
297             ($name,$typeTXT,$classTXT,$ttlTXT,$rdlength,$RDATA,...)
298             = $parse->XYZ($name,$type,$class,$ttl,$rdlength,
299             $rdata,...);
300              
301             or you can use the individual methods
302             directly without calling "new"
303              
304             @output=Net::DNS::ToolKit::RR::get->next(@input);
305             @output=Net::DNS::ToolKit::RR::get->XYZ(@input);
306             @output=Net::DNS::ToolKit::RR::put->XYZ(@input);
307             @output=Net::DNS::ToolKit::RR::parse->RR(@input);
308             @output=Net::DNS::ToolKit::RR::parse->XYZ(@input);
309              
310             The Question section is a special case:
311              
312             ($newoff,$name,type,class) =
313             $get->Question(\$buffer,$offset);
314             ($newoff,@dnptrs) =
315             $put->Question(\$buffer,$offset,
316             $name,$type,$class,\@dnptrs);
317             ($name,$typeTXT,$classTXT) =
318             $parse->Question($name,$type,$class);
319              
320             =head1 ALTERNATE PUT METHOD SYNOPSIS
321              
322             An alternate method for B is available for class specific
323             submissions. This eliminates the need to specify TYPE and CLASS when doing a
324             put. The generic form of a put command using this method is shown below but
325             NOT detailed in the method descriptions.
326              
327             ($get,$put,$parse) = new Net::DNS::ToolKit::RR(class_type);
328             or
329             ($get,$put,$parse) = Net::DNS::ToolKit::RR->new(C_IN);
330              
331             The generic form of a C operation then becomes:
332              
333             ($newoff,@dnptrs)=$put->XYZ(\$buffer,$offset,\@dnptrs,
334             $name,$ttl,$rdate,...)
335              
336             The only class currently supported at this time is C_IN.
337              
338             NOTE: the use of this alternate method changes the number of required
339             arguments to ALL put RR operations. These changes are NOT noted below in the
340             method descriptions.
341              
342             =head1 DESCRIPTION
343              
344             B is the class loader for Resource Record classes.
345             It provides an extensible wrapper for existing
346             classes as well as the framework to easily add new RR classes. See:
347             B
348              
349             From RFC 1035
350              
351             3.2.1. Format
352              
353             All RRs have the same top level format shown below:
354              
355             1 1 1 1 1 1
356             0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
357             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
358             | NAME |
359             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
360             | TYPE |
361             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
362             | CLASS |
363             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
364             | TTL |
365             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
366             | RDLENGTH |
367             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
368             | RDATA |
369             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
370              
371             NAME an owner name, i.e., the name of the node to which this
372             resource record pertains.
373              
374             TYPE two octets containing one of the RR TYPE codes.
375              
376             CLASS two octets containing one of the RR CLASS codes.
377              
378             TTL a 32 bit signed integer that specifies the time interval
379             that the resource record may be cached before the source
380             of the information should again be consulted. Zero
381             values are interpreted to mean that the RR can only be
382             used for the transaction in progress, and should not be
383             cached. For example, SOA records are always distributed
384             with a zero TTL to prohibit caching. Zero values can
385             also be used for extremely volatile data.
386              
387             RDLENGTH an unsigned 16 bit integer that specifies the length
388             in octets of the RDATA field.
389              
390             RDATA a variable length string of octets that describes the
391             resource. The format of this information varies
392             according to the TYPE and CLASS of the resource record.
393              
394             =over 4
395              
396             =item * ($get,$put,$parse) = new Net::DNS::ToolKit::RR;
397              
398             Retrieves the method pointers to B, B, and B for Queston
399             section and Resource Records of a particular type.
400              
401             =cut
402              
403             sub new {
404 13     13 1 1899 my ($proto,$class) = @_;
405 13   33     103 my $package = ref($proto) || $proto;
406 13         30 my $get = {};
407 13         70 bless ($get, "${package}::get");
408 13 100 66     86 my $put = ($class && ClassTxt->{$class})
409             ? { class => $class, } : {};
410 13         76 bless ($put, "${package}::put");
411 13         28 my $parse = {};
412 13         55 bless ($parse, "${package}::parse");
413 13         48 return ($get,$put,$parse);
414             }
415              
416             =item * ($newoff,@common,$rdata,...) =
417             $get->next(\$buffer,$offset);
418              
419             Get the next Resource Record.
420              
421             input: pointer to buffer,
422             offset into buffer
423              
424             returns: offset to next RR or section,
425             (items common to all RR's)
426             i.e. $name,$type,$class,$ttl,$rdlength,
427             $rdata,.... for this RR
428             or undef if the RR is unsupported.
429              
430             HERE IS THE OPPORTUNITY FOR YOU TO ADD TO THIS PACKAGE.
431             If your RR of interest is not supported, see:
432              
433             Net::DNS::ToolKit::RR::Template in:
434             .../Net/DNS/ToolKit/Template/Template.pm
435              
436             Build the support for your Resource Record and submit it to CPAN as an
437             extension to this package.
438              
439             UN-IMPLEMENTED methods: $get->[unimplemented] returns a correct offset to
440             the following RR, correct @common data and a single $rdata element
441             containing a null ... "\0" to be precise. This works as either a numeric 0
442             (zero) or an end of string.
443              
444             =cut
445              
446             =item * ($newoff,@dnptrs)=$put->XYZ(\$buffer,$offset,\@dnptrs,
447             $name,$type,$class,$ttl,$rdata,...);
448              
449              
450             Append a resource record of type XYZ to the current buffer. This is the
451             generic form of a B.
452              
453             input: pointer to buffer,
454             offset, [should be end of buffer]
455             pointer to compressed name array,
456             (items common to all RR's)
457             i.e. $name,$type,$class,$ttl,
458             $rdata,.... for this RR
459             in binary form if appropriate
460              
461             returns: offset to end of RR,
462             new pointer array,
463             or empty list if the RR type is
464             unsupported
465              
466             See: note above about writing new RR's
467              
468             UN-IMPLEMENTED methods: $put->[unimplemented] fails miserably with a DIE
469             statement identifying the offending method.
470              
471             =cut
472              
473             =item * (@COMMON,$RDATA) = $parse->XYZ(@common,$rdata,...);
474              
475             Convert non-printable and numeric data common to all records and the RR
476             specific B into ascii text. In many cases this is a null
477             operation. i.e. for a TXT record. However, for a RR of type B, the
478             operation would be as follows:
479              
480             EXAMPLE
481             Common:
482              
483             name is already text.
484             type numeric to text
485             class numeric to text
486             ttl numeric to text
487             rdlength is a number
488             rdata RR specific conversion
489              
490             Resource Record B returns $rdata containing a packed IPv4 network
491             address. The parse operation would be:
492              
493             input:
494              
495             name foo.bar.com
496             type 1
497             class 1
498             ttl 123
499             rdlength 4
500             rdata a packed IPv4 address
501              
502             output:
503              
504             name foo.bar.com
505             type T_A
506             class C_IN
507             ttl 123 # 2m 3s
508             rdlength 4
509             rdata 192.168.20.40
510              
511             The rdata conversion is implemented internally as:
512              
513             $dotquad = inet_ntoa($networkaddress);
514              
515             where $dotquad is a printable IP address like
516             192.168.20.55
517              
518             UN-IMPLEMENTED methods: $parse->[unimplemented] returns correct @common
519             elements insofar as the type and class are present in Net::DNS::Codes.
520             Other elements are passed through unchanged. i.e. garbage-in, garbage-out.
521              
522             =item * ($newoff,$name,type,class) =
523             $get->Question(\$buffer,$offset);
524              
525             Get the Question.
526              
527             input: pointer to buffer,
528             offset
529             returns: domain name,
530             question type,
531             question class
532              
533             =item * ($newoff,@dnptrs) =
534             $put->Question(\$buffer,$offset,
535             $name,$type,$class,\@dnptrs);
536              
537             Append a question to the $buffer. Returns a new pointer array for compressed
538             names and the offset to the next RR.
539              
540             NOTE: it is up to the user to update the question count. See: L
541              
542             Since the B usually is the first record to be appended to the
543             buffer, @dnptrs may be ommitted. See the details at L.
544              
545             Usage: ($newoff,@dnptrs)=$put->Question(\$buffer,$offset,
546             $name,$type,$class);
547              
548             input: pointer to buffer,
549             offset into buffer,
550             domain name,
551             question type,
552             question class,
553             pointer to array of
554             previously compressed names,
555             returns: offset to next record,
556             updated array of offsets to
557             previous compressed names
558              
559             =item * ($name,$typeTXT,$classTXT) =
560             $parse->Question($name,$type,$class);
561              
562             Convert non-printable and numeric data
563             into ascii text.
564              
565             input: domain name,
566             question type (numeric)
567             question class (numeric)
568             returns: domain name,
569             type TEXT,
570             class TEXT
571              
572             =back
573              
574             =cut
575              
576             1;
577             __END__