File Coverage

blib/lib/Net/DNS/Packet.pm
Criterion Covered Total %
statement 286 286 100.0
branch 80 80 100.0
path n/a
condition 21 21 100.0
subroutine 41 41 100.0
pod 27 31 100.0
total 455 459 100.0


line stmt bran path cond sub pod time code
1               package Net::DNS::Packet;
2                
3 94       94   334552 use strict;
  94           150  
  94           3079  
4 94       94   387 use warnings;
  94           170  
  94           6674  
5                
6               our $VERSION = (qw$Id: Packet.pm 2003 2025-01-21 12:06:06Z willem $)[2];
7                
8                
9               =head1 NAME
10                
11               Net::DNS::Packet - DNS protocol packet
12                
13               =head1 SYNOPSIS
14                
15               use Net::DNS::Packet;
16                
17               $query = Net::DNS::Packet->new( 'example.com', 'MX', 'IN' );
18                
19               $reply = $resolver->send( $query );
20                
21                
22               =head1 DESCRIPTION
23                
24               A Net::DNS::Packet object represents a DNS protocol packet.
25                
26               =cut
27                
28                
29 94       94   1367 use integer;
  94           197  
  94           521  
30 94       94   1755 use Carp;
  94           189  
  94           6568  
31                
32 94       94   1370 use Net::DNS::Parameters qw(:dsotype);
  94           185  
  94           12326  
33 94       94   515 use constant UDPSZ => 512;
  94           164  
  94           7169  
34                
35               BEGIN {
36 94       94   41447 require Net::DNS::Header;
37 94           41041 require Net::DNS::Question;
38 94           13784 require Net::DNS::RR;
39               }
40                
41                
42               =head1 METHODS
43                
44               =head2 new
45                
46               $packet = Net::DNS::Packet->new( 'example.com' );
47               $packet = Net::DNS::Packet->new( 'example.com', 'MX', 'IN' );
48                
49               $packet = Net::DNS::Packet->new();
50                
51               If passed a domain, type, and class, new() creates a Net::DNS::Packet
52               object which is suitable for making a DNS query for the specified
53               information. The type and class may be omitted; they default to A
54               and IN.
55                
56               If called with an empty argument list, new() creates an empty packet.
57                
58               =cut
59                
60               sub new {
61 195       195 1 2158859 my ( $class, @arg ) = @_;
62 195 100         525 return &decode if ref $arg[0];
63                
64 190           1030 my $self = bless {
65               status => 0,
66               question => [],
67               answer => [],
68               authority => [],
69               additional => [],
70               }, $class;
71                
72 190 100         931 $self->{question} = [Net::DNS::Question->new(@arg)] if scalar @arg;
73                
74 189           546 return $self;
75               }
76                
77                
78               =head2 decode
79                
80               $packet = Net::DNS::Packet->decode( \$data );
81               $packet = Net::DNS::Packet->decode( \$data, 1 ); # debug
82               $packet = Net::DNS::Packet->new( \$data ... );
83                
84               A new packet object is created by decoding the DNS packet data
85               contained in the scalar referenced by the first argument.
86               The optional second boolean argument enables debugging output.
87                
88               Returns undef if unable to create a packet object.
89                
90               Decoding errors, including data corruption and truncation, are
91               collected in the $@ ($EVAL_ERROR) variable.
92                
93                
94               ( $packet, $length ) = Net::DNS::Packet->decode( \$data );
95                
96               If called in array context, returns a packet object and the number
97               of octets successfully decoded.
98                
99               Note that the number of RRs in each section of the packet may differ
100               from the corresponding header value if the data has been truncated
101               or corrupted during transmission.
102                
103               =cut
104                
105 94       94   444 use constant HEADER_LENGTH => length pack 'n6', (0) x 6;
  94           140  
  94           267544  
106                
107               sub decode {
108 169       169 1 8091 my $class = shift;
109 169           296 my $data = shift;
110 169     100     767 my $debug = shift || 0;
111                
112 169           329 my $offset = 0;
113 169           284 my $self;
114 169           348 eval {
115 169           1034 local $SIG{__DIE__};
116 169           365 my $length = length $$data;
117 169 100         532 die 'corrupt wire-format data' if $length < HEADER_LENGTH;
118                
119               # header section
120 155           829 my ( $id, $status, @count ) = unpack 'n6', $$data;
121 155           427 my ( $qd, $an, $ns, $ar ) = @count;
122                
123 155           2007 $self = bless {
124               id => $id,
125               status => $status,
126               count => [@count],
127               question => [],
128               answer => [],
129               authority => [],
130               additional => [],
131               replysize => $length
132               }, $class;
133                
134               # question/zone section
135 155           370 my $hash = {};
136 155           276 my $record;
137 155           249 $offset = HEADER_LENGTH;
138 155           447 while ( $qd-- ) {
139 118           1013 ( $record, $offset ) = Net::DNS::Question->decode( $data, $offset, $hash );
140 101           173 CORE::push( @{$self->{question}}, $record );
  101           465  
141               }
142                
143               # RR sections
144 138           403 while ( $an-- ) {
145 9205           15579 ( $record, $offset ) = Net::DNS::RR->decode( $data, $offset, $hash );
146 9205           9729 CORE::push( @{$self->{answer}}, $record );
  9205           16372  
147               }
148                
149 138           482 while ( $ns-- ) {
150 235           929 ( $record, $offset ) = Net::DNS::RR->decode( $data, $offset, $hash );
151 235           372 CORE::push( @{$self->{authority}}, $record );
  235           706  
152               }
153                
154 138           413 while ( $ar-- ) {
155 512           1176 ( $record, $offset ) = Net::DNS::RR->decode( $data, $offset, $hash );
156 512           678 CORE::push( @{$self->{additional}}, $record );
  512           1436  
157               }
158                
159 138 100         3947 return unless $offset == HEADER_LENGTH;
160 5 100         19 return unless $self->header->opcode eq 'DSO';
161                
162 1           2 $self->{dso} = [];
163 1           3 my $limit = $length - 4;
164 1           3 while ( $offset < $limit ) {
165 1           5 my ( $t, $l, $v ) = unpack "\@$offset n2a*", $$data;
166 1           1 CORE::push( @{$self->{dso}}, [$t, substr( $v, 0, $l )] );
  1           3  
167 1           4 $offset += ( $l + 4 );
168               }
169               };
170                
171 169 100         612 if ($debug) {
172 2           3 local $@ = $@;
173 2 100         5 print $@ if $@;
174 2           3 eval { $self->print };
  2           20  
175               }
176                
177 169 100         812 return wantarray ? ( $self, $offset ) : $self;
178               }
179                
180                
181               =head2 encode
182                
183               $data = $packet->encode;
184               $data = $packet->encode( $size );
185                
186               Returns the packet data in binary format, suitable for sending as a
187               query or update request to a nameserver.
188                
189               Truncation may be specified using a non-zero optional size argument.
190                
191               =cut
192                
193               sub data {
194 1       1 0 239 return &encode; # uncoverable pod
195               }
196                
197               sub encode {
198 199       199 1 909 my ( $self, $size ) = @_;
199                
200 199           388 my $edns = $self->edns; # EDNS support
201 199           330 my @addl = grep { !$_->isa('Net::DNS::RR::OPT') } @{$self->{additional}};
  255           659  
  199           457  
202 199 100         667 $self->{additional} = [$edns, @addl] if $edns->_specified;
203                
204 199 100         401 return $self->truncate($size) if $size;
205                
206 198           542 my @part = qw(question answer authority additional);
207 198           349 my @size = map { scalar @{$self->{$_}} } @part;
  792           777  
  792           1483  
208 198           692 my $data = pack 'n6', $self->_quid, $self->{status}, @size;
209 198           613 $self->{count} = [];
210                
211 198           320 my $hash = {}; # packet body
212 198           325 foreach my $component ( map { @{$self->{$_}} } @part ) {
  792           837  
  792           1434  
213 916           1993 $data .= $component->encode( length $data, $hash, $self );
214               }
215                
216 198           1097 return $data;
217               }
218                
219                
220               =head2 header
221                
222               $header = $packet->header;
223                
224               Constructor method which returns a Net::DNS::Header object which
225               represents the header section of the packet.
226                
227               =cut
228                
229               sub header {
230 680       680 1 5860 my $self = shift;
231 680           2784 return bless \$self, q(Net::DNS::Header);
232               }
233                
234                
235               =head2 edns
236                
237               $version = $packet->edns->version;
238               $UDPsize = $packet->edns->size;
239                
240               Auxiliary function which provides access to the EDNS protocol
241               extension OPT RR.
242                
243               =cut
244                
245               sub edns {
246 701       701 1 998 my $self = shift;
247 701           1085 my $link = \$self->{xedns};
248 701 100         1519 ($$link) = grep { $_->isa(qw(Net::DNS::RR::OPT)) } @{$self->{additional}} unless $$link;
  650           1912  
  238           691  
249 701 100         1840 $$link = Net::DNS::RR->new( type => 'OPT' ) unless $$link;
250 701           1552 return $$link;
251               }
252                
253                
254               =head2 reply
255                
256               $reply = $query->reply( $UDPmax );
257                
258               Constructor method which returns a new reply packet.
259                
260               The optional UDPsize argument is the maximum UDP packet size which
261               can be reassembled by the local network stack, and is advertised in
262               response to an EDNS query.
263                
264               =cut
265                
266               sub reply {
267 7       7 1 47 my ( $query, @UDPmax ) = @_;
268 7           16 my $qheadr = $query->header;
269 7 100         21 croak 'erroneous qr flag in query packet' if $qheadr->qr;
270                
271 6           23 my $reply = Net::DNS::Packet->new();
272 6           11 my $header = $reply->header;
273 6           14 $header->qr(1); # reply with same id, opcode and question
274 6           16 $header->id( $qheadr->id );
275 6           16 $header->opcode( $qheadr->opcode );
276 6           14 my @question = $query->question;
277 6           13 $reply->{question} = [@question];
278                
279 6           13 $header->rcode('FORMERR'); # no RCODE considered sinful!
280                
281 6           13 $header->rd( $qheadr->rd ); # copy these flags into reply
282 6           11 $header->cd( $qheadr->cd );
283                
284 6 100         7 return $reply unless grep { $_->isa('Net::DNS::RR::OPT') } @{$query->{additional}};
  4           27  
  6           15  
285                
286 1           3 my $edns = $reply->edns();
287 1           1 CORE::push( @{$reply->{additional}}, $edns );
  1           2  
288 1           3 $edns->udpsize(@UDPmax);
289 1           3 return $reply;
290               }
291                
292                
293               =head2 question, zone
294                
295               @question = $packet->question;
296                
297               Returns a list of Net::DNS::Question objects representing the
298               question section of the packet.
299                
300               In dynamic update packets, this section is known as zone() and
301               specifies the DNS zone to be updated.
302                
303               =cut
304                
305               sub question {
306 163       163 1 429 my @qr = @{shift->{question}};
  163           344  
307 163           400 return @qr;
308               }
309                
310 98       98 1 310 sub zone { return &question }
311                
312                
313               =head2 answer, pre, prerequisite
314                
315               @answer = $packet->answer;
316                
317               Returns a list of Net::DNS::RR objects representing the answer
318               section of the packet.
319                
320               In dynamic update packets, this section is known as pre() or
321               prerequisite() and specifies the RRs or RRsets which must or must
322               not preexist.
323                
324               =cut
325                
326               sub answer {
327 142       142 1 2500 my @rr = @{shift->{answer}};
  142           1151  
328 142           1531 return @rr;
329               }
330                
331 2       2 1 205 sub pre { return &answer }
332 1       1 1 217 sub prerequisite { return &answer }
333                
334                
335               =head2 authority, update
336                
337               @authority = $packet->authority;
338                
339               Returns a list of Net::DNS::RR objects representing the authority
340               section of the packet.
341                
342               In dynamic update packets, this section is known as update() and
343               specifies the RRs or RRsets to be added or deleted.
344                
345               =cut
346                
347               sub authority {
348 77       77 1 4300 my @rr = @{shift->{authority}};
  77           270  
349 77           202 return @rr;
350               }
351                
352 1       1 1 185 sub update { return &authority }
353                
354                
355               =head2 additional
356                
357               @additional = $packet->additional;
358                
359               Returns a list of Net::DNS::RR objects representing the additional
360               section of the packet.
361                
362               =cut
363                
364               sub additional {
365 178       178 1 3180 my @rr = @{shift->{additional}};
  178           383  
366 178           360 return @rr;
367               }
368                
369                
370               =head2 print
371                
372               $packet->print;
373                
374               Prints the entire packet to the currently selected output filehandle
375               using the master file format mandated by RFC1035.
376                
377               =cut
378                
379               sub print {
380 1       1 1 4 print &string;
381 1           3 return;
382               }
383                
384                
385               =head2 string
386                
387               print $packet->string;
388                
389               Returns a string representation of the packet.
390                
391               =cut
392                
393               sub string {
394 17       17 1 505 my $self = shift;
395                
396 17           33 my $header = $self->header;
397 17           45 my $opcode = $header->opcode;
398 17 100         34 my $packet = $header->qr ? 'Response' : 'Query';
399 17           23 my $server = $self->{replyfrom};
400 17           21 my $length = $self->{replysize};
401 17 100         35 my $origin = $server ? ";; $packet received from [$server] $length octets\n" : "";
402 17           49 my @record = ( "$origin;; HEADER SECTION", $header->string );
403                
404 17 100         29 if ( $opcode eq 'DSO' ) {
405 1           3 CORE::push( @record, ";; DSO SECTION" );
406 1           1 foreach ( @{$self->{dso}} ) {
  1           2  
407 1           3 my ( $t, $v ) = @$_;
408 1           4 CORE::push( @record, sprintf( ";;\t%s\t%s", dsotypebyval($t), unpack( 'H*', $v ) ) );
409               }
410 1           6 return join "\n", @record, "\n";
411               }
412                
413 16           23 my $edns = $self->edns;
414 16 100         56 CORE::push( @record, $edns->string ) if $edns->_specified;
415                
416 16 100         45 my @section = $opcode eq 'UPDATE' ? qw(ZONE PREREQUISITE UPDATE) : qw(QUESTION ANSWER AUTHORITY);
417 16           25 my @question = $self->question;
418 16           61 my $qdcount = scalar @question;
419 16 100         25 my $qds = $qdcount != 1 ? 's' : '';
420 16           33 CORE::push( @record, ";; $section[0] SECTION ($qdcount record$qds)", map { ';; ' . $_->string } @question );
  11           30  
421                
422 16           28 my @answer = $self->answer;
423 16           21 my $ancount = scalar @answer;
424 16 100         27 my $ans = $ancount != 1 ? 's' : '';
425 16           31 CORE::push( @record, "\n;; $section[1] SECTION ($ancount record$ans)", map { $_->string } @answer );
  271           554  
426                
427 16           38 my @authority = $self->authority;
428 16           17 my $nscount = scalar @authority;
429 16 100         25 my $nss = $nscount != 1 ? 's' : '';
430 16           28 CORE::push( @record, "\n;; $section[2] SECTION ($nscount record$nss)", map { $_->string } @authority );
  9           15  
431                
432 16           28 my @additional = $self->additional;
433 16           16 my $arcount = scalar @additional;
434 16 100         44 my $ars = $arcount != 1 ? 's' : '';
435 16           39 my $EDNSmarker = join ' ', qq[;; {\t"EDNS-VERSION":], $edns->version, qq[}];
436 16           31 CORE::push( @record, "\n;; ADDITIONAL SECTION ($arcount record$ars)" );
437 16 100         18 CORE::push( @record, map { ( $_ eq $edns ) ? $EDNSmarker : $_->string } @additional );
  7           20  
438                
439 16           403 return join "\n", @record, "\n";
440               }
441                
442                
443               =head2 from
444                
445               print "packet received from ", $packet->from, "\n";
446                
447               Returns the IP address from which this packet was received.
448               This method will return undef for user-created packets.
449                
450               =cut
451                
452               sub from {
453 92       92 1 6314 my ( $self, @argument ) = @_;
454 92           215 for (@argument) { $self->{replyfrom} = $_ }
  87           256  
455 92           261 return $self->{replyfrom};
456               }
457                
458 1       1 0 4 sub answerfrom { return &from; } # uncoverable pod
459                
460                
461               =head2 size
462                
463               print "packet size: ", $packet->size, " octets\n";
464                
465               Returns the size of the packet in octets as it was received from a
466               nameserver. This method will return undef for user-created packets
467               (use length($packet->data) instead).
468                
469               =cut
470                
471               sub size {
472 2       2 1 499 return shift->{replysize};
473               }
474                
475 1       1 0 497 sub answersize { return &size; } # uncoverable pod
476                
477                
478               =head2 push
479                
480               $ancount = $packet->push( prereq => $rr );
481               $nscount = $packet->push( update => $rr );
482               $arcount = $packet->push( additional => $rr );
483                
484               $nscount = $packet->push( update => $rr1, $rr2, $rr3 );
485               $nscount = $packet->push( update => @rr );
486                
487               Adds RRs to the specified section of the packet.
488                
489               Returns the number of resource records in the specified section.
490                
491               Section names may be abbreviated to the first three characters.
492                
493               =cut
494                
495               sub push {
496 325       325 1 669 my ( $self, $section, @rr ) = @_;
497 325           559 my $list = $self->_section($section);
498 325           853 return CORE::push( @$list, @rr );
499               }
500                
501                
502               =head2 unique_push
503                
504               $ancount = $packet->unique_push( prereq => $rr );
505               $nscount = $packet->unique_push( update => $rr );
506               $arcount = $packet->unique_push( additional => $rr );
507                
508               $nscount = $packet->unique_push( update => $rr1, $rr2, $rr3 );
509               $nscount = $packet->unique_push( update => @rr );
510                
511               Adds RRs to the specified section of the packet provided that the
512               RRs are not already present in the same section.
513                
514               Returns the number of resource records in the specified section.
515                
516               Section names may be abbreviated to the first three characters.
517                
518               =cut
519                
520               sub unique_push {
521 93       93 1 130 my ( $self, $section, @rr ) = @_;
522 93           158 my $list = $self->_section($section);
523                
524 93           143 my %unique = map { ( bless( {%$_, ttl => 0}, ref $_ )->canonical => $_ ) } @rr, @$list;
  237           899  
525 93           397 return scalar( @$list = values %unique );
526               }
527                
528                
529               =head2 pop
530                
531               my $rr = $packet->pop( 'pre' );
532               my $rr = $packet->pop( 'update' );
533               my $rr = $packet->pop( 'additional' );
534                
535               Removes a single RR from the specified section of the packet.
536                
537               =cut
538                
539               sub pop {
540 5       5 1 22 my $self = shift;
541 5           12 my $list = $self->_section(shift);
542 5           12 return CORE::pop(@$list);
543               }
544                
545                
546               my %_section = ( ## section name abbreviation table
547               'ans' => 'answer',
548               'pre' => 'answer',
549               'aut' => 'authority',
550               'upd' => 'authority',
551               'add' => 'additional'
552               );
553                
554               sub _section { ## returns array reference for section
555 423       423   484 my $self = shift;
556 423           530 my $name = shift;
557 423     100     1244 my $list = $_section{unpack 'a3', $name} || $name;
558 423     100     1009 return $self->{$list} ||= [];
559               }
560                
561                
562               =head2 sign_tsig
563                
564               $query = Net::DNS::Packet->new( 'www.example.com', 'A' );
565                
566               $query->sign_tsig(
567               $keyfile,
568               fudge => 60
569               );
570                
571               $reply = $res->send( $query );
572                
573               $reply->verify( $query ) || die $reply->verifyerr;
574                
575               Attaches a TSIG resource record object, which will be used to sign
576               the packet (see RFC 2845).
577                
578               The TSIG record can be customised by optional additional arguments to
579               sign_tsig() or by calling the appropriate Net::DNS::RR::TSIG methods.
580                
581               If you wish to create a TSIG record using a non-standard algorithm,
582               you will have to create it yourself. In all cases, the TSIG name
583               must uniquely identify the key shared between the parties, and the
584               algorithm name must identify the signing function to be used with the
585               specified key.
586                
587               $tsig = Net::DNS::RR->new(
588               name => 'tsig.example',
589               type => 'TSIG',
590               algorithm => 'custom-algorithm',
591               key => '',
592               sig_function => sub {
593               my ($key, $data) = @_;
594               ...
595               }
596               );
597                
598               $query->sign_tsig( $tsig );
599                
600                
601               The response to an inbound request is signed by presenting the request
602               in place of the key parameter.
603                
604               $response = $request->reply;
605               $response->sign_tsig( $request, @options );
606                
607                
608               Multi-packet transactions are signed by chaining the sign_tsig()
609               calls together as follows:
610                
611               $opaque = $packet1->sign_tsig( 'Kexample.+165+13281.private' );
612               $opaque = $packet2->sign_tsig( $opaque );
613               $opaque = $packet3->sign_tsig( $opaque );
614                
615               The opaque intermediate object references returned during multi-packet
616               signing are not intended to be accessed by the end-user application.
617               Any such access is expressly forbidden.
618                
619               Note that a TSIG record is added to every packet; this implementation
620               does not support the suppressed signature scheme described in RFC2845.
621                
622               =cut
623                
624               sub sign_tsig {
625 32       32 1 2818 my ( $self, @argument ) = @_;
626 32     100     58 return eval {
627               local $SIG{__DIE__};
628               require Net::DNS::RR::TSIG;
629               my $tsig = Net::DNS::RR::TSIG->create(@argument);
630               $self->push( 'additional' => $tsig );
631               return $tsig;
632               } || return croak "$@\nTSIG: unable to sign packet";
633               }
634                
635                
636               =head2 verify and verifyerr
637                
638               $reply->verify($query) || die $reply->verifyerr;
639                
640               Verify TSIG signature of a reply to the corresponding query.
641                
642                
643               $opaque = $packet1->verify( $query ) || die $packet1->verifyerr;
644               $opaque = $packet2->verify( $opaque );
645               $verifed = $packet3->verify( $opaque ) || die $packet3->verifyerr;
646                
647               Verify TSIG signature of a multi-packet reply to the corresponding query.
648                
649               The opaque intermediate object references returned by verify() at each
650               stage will be undefined (Boolean false) if verification fails.
651               Testing at every stage is not necessary, which produces a BADSIG error
652               on the final packet in the absence of more specific information.
653               Access to the objects themselves, if they exist, is expressly forbidden.
654                
655               =cut
656                
657               sub verify {
658 39       39 1 3635 my ( $self, @argument ) = @_;
659 39           97 my $sig = $self->sigrr;
660 39 100         245 return $sig ? $sig->verify( $self, @argument ) : shift @argument;
661               }
662                
663               sub verifyerr {
664 25       25 1 108 my $sig = shift->sigrr;
665 25 100         109 return $sig ? $sig->vrfyerrstr : 'not signed';
666               }
667                
668                
669               =head2 sign_sig0
670                
671               SIG0 support is provided through the Net::DNS::RR::SIG class.
672               The requisite cryptographic components are not integrated into
673               Net::DNS but reside in the Net::DNS::SEC distribution available
674               from CPAN.
675                
676               $update = Net::DNS::Update->new('example.com');
677               $update->push( update => rr_add('foo.example.com A 10.1.2.3'));
678               $update->sign_sig0('Kexample.com+003+25317.private');
679                
680               Execution will be terminated if Net::DNS::SEC is not available.
681                
682                
683               =head2 verify SIG0
684                
685               $packet->verify( $keyrr ) || die $packet->verifyerr;
686               $packet->verify( [$keyrr, ...] ) || die $packet->verifyerr;
687                
688               Verify SIG0 packet signature against one or more specified KEY RRs.
689                
690               =cut
691                
692               sub sign_sig0 {
693 2       2 1 567 my $self = shift;
694 2           3 my $karg = shift;
695                
696 2     100     3 return eval {
697               local $SIG{__DIE__};
698                
699               my $sig0;
700               if ( ref($karg) eq 'Net::DNS::RR::SIG' ) {
701               $sig0 = $karg;
702                
703               } else {
704               require Net::DNS::RR::SIG;
705               $sig0 = Net::DNS::RR::SIG->create( '', $karg );
706               }
707                
708               $self->push( 'additional' => $sig0 );
709               return $sig0;
710               } || return croak "$@\nSIG0: unable to sign packet";
711               }
712                
713                
714               =head2 sigrr
715                
716               $sigrr = $packet->sigrr() || die 'unsigned packet';
717                
718               The sigrr method returns the signature RR from a signed packet
719               or undefined if the signature is absent.
720                
721               =cut
722                
723               sub sigrr {
724 122       122 1 1328 my $self = shift;
725                
726 122           289 my ($sig) = reverse $self->additional;
727 122 100         360 return unless $sig;
728 106           289 for ( $sig->type ) {
729 106 100         677 return $sig if /TSIG|SIG/;
730               }
731 7           16 return;
732               }
733                
734                
735               ########################################
736                
737               =head2 truncate
738                
739               The truncate method takes a maximum length as argument and then tries
740               to truncate the packet and set the TC bit according to the rules of
741               RFC2181 Section 9.
742                
743               The smallest length limit that is honoured is 512 octets.
744                
745               =cut
746                
747               # From RFC2181:
748               #
749               # 9. The TC (truncated) header bit
750               #
751               # The TC bit should be set in responses only when an RRSet is required
752               # as a part of the response, but could not be included in its entirety.
753               # The TC bit should not be set merely because some extra information
754               # could have been included, for which there was insufficient room. This
755               # includes the results of additional section processing. In such cases
756               # the entire RRSet that will not fit in the response should be omitted,
757               # and the reply sent as is, with the TC bit clear. If the recipient of
758               # the reply needs the omitted data, it can construct a query for that
759               # data and send that separately.
760               #
761               # Where TC is set, the partial RRSet that would not completely fit may
762               # be left in the response. When a DNS client receives a reply with TC
763               # set, it should ignore that response, and query again, using a
764               # mechanism, such as a TCP connection, that will permit larger replies.
765                
766               # Code developed from a contribution by Aaron Crane via rt.cpan.org 33547
767                
768               sub truncate {
769 5       5 1 21 my $self = shift;
770 5     100     14 my $size = shift || UDPSZ;
771                
772 5           13 my $sigrr = $self->sigrr;
773 5 100         11 $size = UDPSZ unless $size > UDPSZ;
774 5 100         12 $size -= $sigrr->_size if $sigrr;
775                
776 5           9 my $data = pack 'x' x HEADER_LENGTH; # header placeholder
777 5           11 $self->{count} = [];
778                
779 5           7 my $tc;
780 5           6 my $hash = {};
781 5           8 foreach my $section ( map { $self->{$_} } qw(question answer authority) ) {
  15           28  
782 15           17 my @list;
783 15           20 foreach my $item (@$section) {
784 108           163 my $component = $item->encode( length $data, $hash );
785 108 100         163 last if length($data) + length($component) > $size;
786 105 100         146 last if $tc;
787 104           111 $data .= $component;
788 104           136 CORE::push @list, $item;
789               }
790 15 100         29 $tc++ if scalar(@list) < scalar(@$section);
791 15           66 @$section = @list;
792               }
793 5 100         15 $self->header->tc(1) if $tc; # only set if truncated here
794                
795 5           7 my %rrset;
796               my @order;
797 5           9 foreach my $item ( grep { ref($_) ne ref($sigrr) } $self->additional ) {
  146           159  
798 145           192 my $name = $item->{owner}->canonical;
799 145     100     194 my $class = $item->{class} || 0;
800 145           184 my $key = pack 'nna*', $class, $item->{type}, $name;
801 145 100         198 CORE::push @order, $key unless $rrset{$key};
802 145           119 CORE::push @{$rrset{$key}}, $item;
  145           254  
803               }
804                
805 5           9 my @list;
806 5           7 foreach my $key (@order) {
807 54           45 my $component = '';
808 54           43 my @item = @{$rrset{$key}};
  54           66  
809 54           57 foreach my $item (@item) {
810 66           90 $component .= $item->encode( length $data, $hash );
811               }
812 54 100         70 last if length($data) + length($component) > $size;
813 50           48 $data .= $component;
814 50           60 CORE::push @list, @item;
815               }
816                
817 5 100         10 if ($sigrr) {
818 1           3 $data .= $sigrr->encode( length $data, $hash, $self );
819 1           2 CORE::push @list, $sigrr;
820               }
821 5           16 $self->{'additional'} = \@list;
822                
823 5           11 my @part = qw(question answer authority additional);
824 5           7 my @size = map { scalar @{$self->{$_}} } @part;
  20           18  
  20           29  
825 5           12 return pack 'n6 a*', $self->_quid, $self->{status}, @size, substr( $data, HEADER_LENGTH );
826               }
827                
828                
829               ########################################
830                
831               sub dump { ## print internal data structure
832 3       3 0 281 my @data = @_; # uncoverable pod
833 3           15 require Data::Dumper;
834 3     100     7 local $Data::Dumper::Maxdepth = $Data::Dumper::Maxdepth || 3;
835 3     100     7 local $Data::Dumper::Sortkeys = $Data::Dumper::Sortkeys || 1;
836 3     100     6 local $Data::Dumper::Useqq = $Data::Dumper::Useqq || 1;
837 3           6 print Data::Dumper::Dumper(@data);
838 3           274 return;
839               }
840                
841                
842               my ( $cache1, $cache2, $limit );
843                
844               sub _quid { ## generate (short-term) unique query ID
845 203       203   636 my $self = shift;
846 203           401 my $id = $self->{id};
847 203 100         441 $cache1->{$id}++ if $id; # cache non-zero ID
848 203 100         1065 return $id if defined $id;
849 148 100         404 ( $cache2, $cache1, $limit ) = ( $cache1, {0 => 1}, 50 ) unless $limit--;
850 148           767 $id = int rand(0xffff); # two layer ID cache
851 148           899 $id = int rand(0xffff) while $cache1->{$id}++ + exists( $cache2->{$id} );
852 148           888 return $self->{id} = $id;
853               }
854                
855                
856               1;
857               __END__