File Coverage

blib/lib/Net/DNS/Packet.pm
Criterion Covered Total %
statement 277 277 100.0
branch 72 72 100.0
condition 21 21 100.0
subroutine 40 40 100.0
pod 26 31 100.0
total 436 441 100.0


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