File Coverage

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


line stmt bran cond sub pod time code
1             package Net::DNS::Packet;
2              
3 94     94   440436 use strict;
  94         216  
  94         4001  
4 94     94   535 use warnings;
  94         193  
  94         9163  
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   1772 use integer;
  94         224  
  94         655  
30 94     94   6053 use Carp;
  94         202  
  94         8730  
31              
32 94     94   2246 use Net::DNS::Parameters qw(:dsotype);
  94         225  
  94         15638  
33 94     94   720 use constant UDPSZ => 512;
  94         284  
  94         9700  
34              
35             BEGIN {
36 94     94   52176 require Net::DNS::Header;
37 94         53310 require Net::DNS::Question;
38 94         17550 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 197     197 1 2186922 my ( $class, @arg ) = @_;
62 197 100       711 return &decode if ref $arg[0];
63              
64 192         1551 my $self = bless {
65             status => 0,
66             question => [],
67             answer => [],
68             authority => [],
69             additional => [],
70             }, $class;
71              
72 192 100       1301 $self->{question} = [Net::DNS::Question->new(@arg)] if scalar @arg;
73              
74 191         713 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   527 use constant HEADER_LENGTH => length pack 'n6', (0) x 6;
  94         187  
  94         371434  
106              
107             sub decode {
108 170     170 1 13736 my $class = shift;
109 170         358 my $data = shift;
110 170   100     3326 my $debug = shift || 0;
111              
112 170         369 my $offset = 0;
113 170         397 my $self;
114 170         452 eval {
115 170         1334 local $SIG{__DIE__};
116 170         429 my $length = length $$data;
117 170 100       832 die 'corrupt wire-format data' if $length < HEADER_LENGTH;
118              
119             # header section
120 156         1067 my ( $id, $status, @count ) = unpack 'n6', $$data;
121 156         2642 my ( $qd, $an, $ns, $ar ) = @count;
122              
123 156         2636 $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 156         396 my $hash = {};
136 156         293 my $record;
137 156         377 $offset = HEADER_LENGTH;
138 156         617 while ( $qd-- ) {
139 119         1382 ( $record, $offset ) = Net::DNS::Question->decode( $data, $offset, $hash );
140 102         262 CORE::push( @{$self->{question}}, $record );
  102         525  
141             }
142              
143             # RR sections
144 139         451 while ( $an-- ) {
145 8436         21404 ( $record, $offset ) = Net::DNS::RR->decode( $data, $offset, $hash );
146 8436         12590 CORE::push( @{$self->{answer}}, $record );
  8436         22144  
147             }
148              
149 139         653 while ( $ns-- ) {
150 256         1380 ( $record, $offset ) = Net::DNS::RR->decode( $data, $offset, $hash );
151 256         490 CORE::push( @{$self->{authority}}, $record );
  256         5113  
152             }
153              
154 139         552 while ( $ar-- ) {
155 574         1770 ( $record, $offset ) = Net::DNS::RR->decode( $data, $offset, $hash );
156 574         1085 CORE::push( @{$self->{additional}}, $record );
  574         2109  
157             }
158              
159 139 100       7822 return unless $offset == HEADER_LENGTH;
160 5 100       24 return unless $self->header->opcode eq 'DSO';
161              
162 1         3 $self->{dso} = [];
163 1         3 my $limit = $length - 4;
164 1         8 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         4  
167 1         5 $offset += ( $l + 4 );
168             }
169             };
170              
171 170 100       843 if ($debug) {
172 2         6 local $@ = $@;
173 2 100       8 print $@ if $@;
174 2         4 eval { $self->print };
  2         13  
175             }
176              
177 170 100       1350 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 343 return &encode; # uncoverable pod
195             }
196              
197             sub encode {
198 199     199 1 585 my ( $self, $size ) = @_;
199              
200 199         599 my $edns = $self->edns; # EDNS support
201 199         366 my @addl = grep { !$_->isa('Net::DNS::RR::OPT') } @{$self->{additional}};
  253         871  
  199         740  
202 199 100       926 $self->{additional} = [$edns, @addl] if $edns->_specified;
203              
204 199 100       698 return $self->truncate($size) if $size;
205              
206 198         722 my @part = qw(question answer authority additional);
207 198         403 my @size = map { scalar @{$self->{$_}} } @part;
  792         1147  
  792         1845  
208 198         5388 my $data = pack 'n6', $self->_quid, $self->{status}, @size;
209 198         676 $self->{count} = [];
210              
211 198         359 my $hash = {}; # packet body
212 198         413 foreach my $component ( map { @{$self->{$_}} } @part ) {
  792         2625  
  792         2968  
213 914         2937 $data .= $component->encode( length $data, $hash, $self );
214             }
215              
216 198         1450 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 688     688 1 9296 my $self = shift;
231 688         4438 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 626     626 1 1474 my $self = shift;
247 626         1349 my $link = \$self->{xedns};
248 626 100       1748 ($$link) = grep { $_->isa(qw(Net::DNS::RR::OPT)) } @{$self->{additional}} unless $$link;
  712         2621  
  241         1403  
249 626 100       2402 $$link = Net::DNS::RR->new( type => 'OPT' ) unless $$link;
250 626         1920 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 76 my ( $query, @UDPmax ) = @_;
268 7         22 my $qheadr = $query->header;
269 7 100       33 croak 'erroneous qr flag in query packet' if $qheadr->qr;
270              
271 6         34 my $reply = Net::DNS::Packet->new();
272 6         20 my $header = $reply->header;
273 6         37 $header->qr(1); # reply with same id, opcode and question
274 6         26 $header->id( $qheadr->id );
275 6         25 $header->opcode( $qheadr->opcode );
276 6         23 my @question = $query->question;
277 6         21 $reply->{question} = [@question];
278              
279 6         24 $header->rcode('FORMERR'); # no RCODE considered sinful!
280              
281 6         26 $header->rd( $qheadr->rd ); # copy these flags into reply
282 6         22 $header->cd( $qheadr->cd );
283              
284 6 100       11 return $reply unless grep { $_->isa('Net::DNS::RR::OPT') } @{$query->{additional}};
  4         37  
  6         31  
285              
286 1         5 my $edns = $reply->edns();
287 1         2 CORE::push( @{$reply->{additional}}, $edns );
  1         4  
288 1         6 $edns->udpsize(@UDPmax);
289 1         5 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 165     165 1 728 my @qr = @{shift->{question}};
  165         525  
307 165         695 return @qr;
308             }
309              
310 98     98 1 604 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 145     145 1 3758 my @rr = @{shift->{answer}};
  145         1913  
328 145         2094 return @rr;
329             }
330              
331 2     2 1 418 sub pre { return &answer }
332 1     1 1 395 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 76     76 1 6910 my @rr = @{shift->{authority}};
  76         261  
349 76         251 return @rr;
350             }
351              
352 1     1 1 373 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 4578 my @rr = @{shift->{additional}};
  178         554  
366 178         554 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 5 print &string;
381 1         6 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 767 my $self = shift;
395              
396 17         45 my $header = $self->header;
397 17         55 my $opcode = $header->opcode;
398 17 100       50 my $packet = $header->qr ? 'Response' : 'Query';
399 17         32 my $server = $self->{replyfrom};
400 17         30 my $length = $self->{replysize};
401 17 100       35 my $origin = $server ? ";; $packet received from [$server] $length octets\n" : "";
402 17         52 my @record = ( "$origin;; HEADER SECTION", $header->string );
403              
404 17 100       42 if ( $opcode eq 'DSO' ) {
405 1         2 CORE::push( @record, ";; DSO SECTION" );
406 1         1 foreach ( @{$self->{dso}} ) {
  1         2  
407 1         2 my ( $t, $v ) = @$_;
408 1         4 CORE::push( @record, sprintf( ";;\t%s\t%s", dsotypebyval($t), unpack( 'H*', $v ) ) );
409             }
410 1         7 return join "\n", @record, "\n";
411             }
412              
413 16         30 my $edns = $self->edns;
414 16 100       44 CORE::push( @record, $edns->string ) if $edns->_specified;
415              
416 16 100       66 my @section = $opcode eq 'UPDATE' ? qw(ZONE PREREQUISITE UPDATE) : qw(QUESTION ANSWER AUTHORITY);
417 16         40 my @question = $self->question;
418 16         29 my $qdcount = scalar @question;
419 16 100       32 my $qds = $qdcount != 1 ? 's' : '';
420 16         44 CORE::push( @record, ";; $section[0] SECTION ($qdcount record$qds)", map { ';; ' . $_->string } @question );
  11         38  
421              
422 16         40 my @answer = $self->answer;
423 16         28 my $ancount = scalar @answer;
424 16 100       34 my $ans = $ancount != 1 ? 's' : '';
425 16         36 CORE::push( @record, "\n;; $section[1] SECTION ($ancount record$ans)", map { $_->string } @answer );
  271         1029  
426              
427 16         49 my @authority = $self->authority;
428 16         22 my $nscount = scalar @authority;
429 16 100       35 my $nss = $nscount != 1 ? 's' : '';
430 16         38 CORE::push( @record, "\n;; $section[2] SECTION ($nscount record$nss)", map { $_->string } @authority );
  9         31  
431              
432 16         36 my @additional = $self->additional;
433 16         22 my $arcount = scalar @additional;
434 16 100       37 my $ars = $arcount != 1 ? 's' : '';
435 16         52 my $EDNSmarker = join ' ', qq[;; {\t"EDNS-VERSION":], $edns->version, qq[}];
436 16         39 CORE::push( @record, "\n;; ADDITIONAL SECTION ($arcount record$ars)" );
437 16 100       32 CORE::push( @record, map { ( $_ eq $edns ) ? $EDNSmarker : $_->string } @additional );
  7         36  
438              
439 16         589 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 93     93 1 10391 my ( $self, @argument ) = @_;
454 93         249 for (@argument) { $self->{replyfrom} = $_ }
  88         348  
455 93         366 return $self->{replyfrom};
456             }
457              
458 1     1 0 6 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 742 return shift->{replysize};
473             }
474              
475 1     1 0 953 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 957 my ( $self, $section, @rr ) = @_;
497 325         801 my $list = $self->_section($section);
498 325         1340 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 223 my ( $self, $section, @rr ) = @_;
522 93         236 my $list = $self->_section($section);
523              
524 93         225 my %unique = map { ( bless( {%$_, ttl => 0}, ref $_ )->canonical => $_ ) } @rr, @$list;
  237         1882  
525 93         706 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 23 my $self = shift;
541 5         13 my $list = $self->_section(shift);
542 5         15 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   800 my $self = shift;
556 423         723 my $name = shift;
557 423   100     2027 my $list = $_section{unpack 'a3', $name} || $name;
558 423   100     1477 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 3343 my ( $self, @argument ) = @_;
626 32   100     61 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 4014 my ( $self, @argument ) = @_;
659 39         127 my $sig = $self->sigrr;
660 39 100       250 return $sig ? $sig->verify( $self, @argument ) : shift @argument;
661             }
662              
663             sub verifyerr {
664 25     25 1 101 my $sig = shift->sigrr;
665 25 100       106 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 982 my $self = shift;
694 2         5 my $karg = shift;
695              
696 2   100     5 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 1759 my $self = shift;
725              
726 122         374 my ($sig) = reverse $self->additional;
727 122 100       397 return unless $sig;
728 106         514 for ( $sig->type ) {
729 106 100       1298 return $sig if /TSIG|SIG/;
730             }
731 7         24 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 25 my $self = shift;
770 5   100     18 my $size = shift || UDPSZ;
771              
772 5         18 my $sigrr = $self->sigrr;
773 5 100       16 $size = UDPSZ unless $size > UDPSZ;
774 5 100       19 $size -= $sigrr->_size if $sigrr;
775              
776 5         13 my $data = pack 'x' x HEADER_LENGTH; # header placeholder
777 5         14 $self->{count} = [];
778              
779 5         9 my $tc;
780 5         9 my $hash = {};
781 5         10 foreach my $section ( map { $self->{$_} } qw(question answer authority) ) {
  15         34  
782 15         19 my @list;
783 15         64 foreach my $item (@$section) {
784 108         242 my $component = $item->encode( length $data, $hash );
785 108 100       192 last if length($data) + length($component) > $size;
786 105 100       162 last if $tc;
787 104         136 $data .= $component;
788 104         163 CORE::push @list, $item;
789             }
790 15 100       40 $tc++ if scalar(@list) < scalar(@$section);
791 15         49 @$section = @list;
792             }
793 5 100       18 $self->header->tc(1) if $tc; # only set if truncated here
794              
795 5         11 my %rrset;
796             my @order;
797 5         16 foreach my $item ( grep { ref($_) ne ref($sigrr) } $self->additional ) {
  146         239  
798 145         332 my $name = $item->{owner}->canonical;
799 145   100     331 my $class = $item->{class} || 0;
800 145         312 my $key = pack 'nna*', $class, $item->{type}, $name;
801 145 100       349 CORE::push @order, $key unless $rrset{$key};
802 145         212 CORE::push @{$rrset{$key}}, $item;
  145         430  
803             }
804              
805 5         18 my @list;
806 5         10 foreach my $key (@order) {
807 54         81 my $component = '';
808 54         80 my @item = @{$rrset{$key}};
  54         100  
809 54         127 foreach my $item (@item) {
810 66         138 $component .= $item->encode( length $data, $hash );
811             }
812 54 100       112 last if length($data) + length($component) > $size;
813 50         68 $data .= $component;
814 50         97 CORE::push @list, @item;
815             }
816              
817 5 100       15 if ($sigrr) {
818 1         7 $data .= $sigrr->encode( length $data, $hash, $self );
819 1         4 CORE::push @list, $sigrr;
820             }
821 5         36 $self->{'additional'} = \@list;
822              
823 5         23 my @part = qw(question answer authority additional);
824 5         13 my @size = map { scalar @{$self->{$_}} } @part;
  20         28  
  20         53  
825 5         19 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 519 my @data = @_; # uncoverable pod
833 3         52 require Data::Dumper;
834 3   100     19 local $Data::Dumper::Maxdepth = $Data::Dumper::Maxdepth || 3;
835 3   100     12 local $Data::Dumper::Sortkeys = $Data::Dumper::Sortkeys || 1;
836 3   100     11 local $Data::Dumper::Useqq = $Data::Dumper::Useqq || 1;
837 3         13 print Data::Dumper::Dumper(@data);
838 3         474 return;
839             }
840              
841              
842             my ( $cache1, $cache2, $limit );
843              
844             sub _quid { ## generate (short-term) unique query ID
845 203     203   407 my $self = shift;
846 203         454 my $id = $self->{id};
847 203 100       542 $cache1->{$id}++ if $id; # cache non-zero ID
848 203 100       772 return $id if defined $id;
849 150 100       578 ( $cache2, $cache1, $limit ) = ( $cache1, {0 => 1}, 50 ) unless $limit--;
850 150         927 $id = int rand(0xffff); # two layer ID cache
851 150         1026 $id = int rand(0xffff) while $cache1->{$id}++ + exists( $cache2->{$id} );
852 150         1224 return $self->{id} = $id;
853             }
854              
855              
856             1;
857             __END__