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   75912 use strict;
  92         220  
  92         3064  
4 92     92   513 use warnings;
  92         207  
  92         4637  
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   1745 use integer;
  92         238  
  92         479  
30 92     92   2143 use Carp;
  92         189  
  92         7274  
31              
32 92     92   1981 use Net::DNS::Parameters qw(:dsotype);
  92         220  
  92         11195  
33 92     92   689 use constant UDPSZ => 512;
  92         229  
  92         8747  
34              
35             BEGIN {
36 92     92   46228 require Net::DNS::Header;
37 92         42400 require Net::DNS::Question;
38 92         13691 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 210     210 1 2038649 my ( $class, @arg ) = @_;
62 210 100       696 return &decode if ref $arg[0];
63              
64 199         1252 my $self = bless {
65             status => 0,
66             question => [],
67             answer => [],
68             authority => [],
69             additional => [],
70             }, $class;
71              
72 199 100       1626 $self->{question} = [Net::DNS::Question->new(@arg)] if scalar @arg;
73              
74 198         665 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   575 use constant HEADER_LENGTH => length pack 'n6', (0) x 6;
  92         243  
  92         297009  
108              
109             sub decode {
110 196     196 0 10535 my $class = shift; # uncoverable pod
111 196         482 my $data = shift;
112 196   100     1246 my $debug = shift || 0;
113              
114 196         497 my $offset = 0;
115 196         504 my $self;
116 196         518 eval {
117 196         1431 local $SIG{__DIE__};
118 196         549 my $length = length $$data;
119 196 100       853 die 'corrupt wire-format data' if $length < HEADER_LENGTH;
120              
121             # header section
122 182         1503 my ( $id, $status, @count ) = unpack 'n6', $$data;
123 182         667 my ( $qd, $an, $ns, $ar ) = @count;
124              
125 182         2750 $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 182         554 my $hash = {};
138 182         394 my $record;
139 182         365 $offset = HEADER_LENGTH;
140 182         669 while ( $qd-- ) {
141 142         1502 ( $record, $offset ) = Net::DNS::Question->decode( $data, $offset, $hash );
142 125         355 CORE::push( @{$self->{question}}, $record );
  125         792  
143             }
144              
145             # RR sections
146 165         681 while ( $an-- ) {
147 8446         19242 ( $record, $offset ) = Net::DNS::RR->decode( $data, $offset, $hash );
148 8446         12668 CORE::push( @{$self->{answer}}, $record );
  8446         22065  
149             }
150              
151 165         723 while ( $ns-- ) {
152 387         1632 ( $record, $offset ) = Net::DNS::RR->decode( $data, $offset, $hash );
153 387         753 CORE::push( @{$self->{authority}}, $record );
  387         1382  
154             }
155              
156 165         687 while ( $ar-- ) {
157 842         2920 ( $record, $offset ) = Net::DNS::RR->decode( $data, $offset, $hash );
158 842         1588 CORE::push( @{$self->{additional}}, $record );
  842         2974  
159             }
160              
161 165 100       3926 return unless $offset == HEADER_LENGTH;
162 5 100       22 return unless $self->header->opcode eq 'DSO';
163              
164 1         3 $self->{dso} = [];
165 1         2 my $limit = $length - 4;
166 1         3 while ( $offset < $limit ) {
167 1         6 my ( $t, $l, $v ) = unpack "\@$offset n2a*", $$data;
168 1         2 CORE::push( @{$self->{dso}}, [$t, substr( $v, 0, $l )] );
  1         4  
169 1         6 $offset += ( $l + 4 );
170             }
171             };
172              
173 196 100       1475 if ($debug) {
174 2         15 local $@ = $@;
175 2 100       7 print $@ if $@;
176 2         5 eval { $self->print };
  2         10  
177             }
178              
179 196 100       1119 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 221     221 1 740 return &encode;
197             }
198              
199             sub encode {
200 222     222 0 546 my ( $self, $size ) = @_; # uncoverable pod
201              
202 222         568 my $edns = $self->edns; # EDNS support
203 222         443 my @addl = grep { !$_->isa('Net::DNS::RR::OPT') } @{$self->{additional}};
  269         960  
  222         614  
204 222 100       1076 $self->{additional} = [$edns, @addl] if $edns->_specified;
205              
206 222 100       668 return $self->truncate($size) if $size;
207              
208 221         812 my @part = qw(question answer authority additional);
209 221         444 my @size = map { scalar @{$self->{$_}} } @part;
  884         1160  
  884         2150  
210 221         827 my $data = pack 'n6', $self->header->id, $self->{status}, @size;
211 221         1001 $self->{count} = [];
212              
213 221         639 my $hash = {}; # packet body
214 221         510 foreach my $component ( map { @{$self->{$_}} } @part ) {
  884         1357  
  884         2277  
215 962         3152 $data .= $component->encode( length $data, $hash, $self );
216             }
217              
218 221         1477 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 1022     1022 1 6410 my $self = shift;
233 1022         5050 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 722     722 1 1727 my $self = shift;
250 722         1544 my $link = \$self->{xedns};
251 722 100       2040 ($$link) = grep { $_->isa(qw(Net::DNS::RR::OPT)) } @{$self->{additional}} unless $$link;
  979         3715  
  269         876  
252 722 100       2855 $$link = Net::DNS::RR->new( type => 'OPT' ) unless $$link;
253 722         2062 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 102 my ( $query, @UDPmax ) = @_;
271 7         21 my $qheadr = $query->header;
272 7 100       37 croak 'erroneous qr flag in query packet' if $qheadr->qr;
273              
274 6         30 my $reply = Net::DNS::Packet->new();
275 6         24 my $header = $reply->header;
276 6         26 $header->qr(1); # reply with same id, opcode and question
277 6         22 $header->id( $qheadr->id );
278 6         42 $header->opcode( $qheadr->opcode );
279 6         24 my @question = $query->question;
280 6         18 $reply->{question} = [@question];
281              
282 6         30 $header->rcode('FORMERR'); # no RCODE considered sinful!
283              
284 6         27 $header->rd( $qheadr->rd ); # copy these flags into reply
285 6         49 $header->cd( $qheadr->cd );
286              
287 6 100       26 return $reply unless grep { $_->isa('Net::DNS::RR::OPT') } @{$query->{additional}};
  4         71  
  6         42  
288              
289 1         4 my $edns = $reply->edns();
290 1         3 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 204     204 1 670 my @qr = @{shift->{question}};
  204         731  
310 204         822 return @qr;
311             }
312              
313 98     98 1 452 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 226     226 1 3468 my @rr = @{shift->{answer}};
  226         1273  
331 226         1991 return @rr;
332             }
333              
334 2     2 1 273 sub pre { return &answer }
335 1     1 1 283 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 92     92 1 5760 my @rr = @{shift->{authority}};
  92         277  
352 92         326 return @rr;
353             }
354              
355 1     1 1 281 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 176     176 1 3641 my @rr = @{shift->{additional}};
  176         508  
369 176         459 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 4 print &string;
384 1         3 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 358 my $self = shift;
398              
399 16         34 my $header = $self->header;
400 16         50 my $opcode = $header->opcode;
401 16         39 my $server = $self->{replyfrom};
402 16         24 my $length = $self->{replysize};
403 16 100       33 my $origin = $server ? ";; Response received from $server ($length octets)\n" : "";
404 16         58 my @record = ( "$origin;; HEADER SECTION", $header->string );
405              
406 16 100       42 if ( $opcode eq 'DSO' ) {
407 1         2 CORE::push( @record, ";; DSO SECTION" );
408 1         2 foreach ( @{$self->{dso}} ) {
  1         2  
409 1         3 my ( $t, $v ) = @$_;
410 1         3 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         30 my $edns = $self->edns;
416 15 100       32 CORE::push( @record, $edns->string ) if $edns->_specified;
417              
418 15 100       49 my @section = $opcode eq 'UPDATE' ? qw(ZONE PREREQUISITE UPDATE) : qw(QUESTION ANSWER AUTHORITY);
419 15         143 my @question = $self->question;
420 15         28 my $qdcount = scalar @question;
421 15 100       36 my $qds = $qdcount != 1 ? 's' : '';
422 15         43 CORE::push( @record, ";; $section[0] SECTION ($qdcount record$qds)", map { ';; ' . $_->string } @question );
  10         32  
423              
424 15         37 my @answer = $self->answer;
425 15         30 my $ancount = scalar @answer;
426 15 100       40 my $ans = $ancount != 1 ? 's' : '';
427 15         59 CORE::push( @record, "\n;; $section[1] SECTION ($ancount record$ans)", map { $_->string } @answer );
  271         740  
428              
429 15         49 my @authority = $self->authority;
430 15         26 my $nscount = scalar @authority;
431 15 100       33 my $nss = $nscount != 1 ? 's' : '';
432 15         45 CORE::push( @record, "\n;; $section[2] SECTION ($nscount record$nss)", map { $_->string } @authority );
  9         28  
433              
434 15         49 my @additional = $self->additional;
435 15         21 my $arcount = scalar @additional;
436 15 100       33 my $ars = $arcount != 1 ? 's' : '';
437 15         54 my $EDNSmarker = join ' ', qq[;; {\t"EDNS-VERSION":], $edns->version, qq[}\n];
438 15         46 CORE::push( @record, "\n;; ADDITIONAL SECTION ($arcount record$ars)" );
439 15 100       32 CORE::push( @record, map { ( $_ eq $edns ) ? $EDNSmarker : $_->string } @additional );
  7         36  
440              
441 15         525 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 116     116 1 1752 my ( $self, @argument ) = @_;
456 116         318 for (@argument) { $self->{replyfrom} = $_ }
  111         450  
457 116         314 return $self->{replyfrom};
458             }
459              
460 1     1 0 4 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 613 return shift->{replysize};
475             }
476              
477 1     1 0 529 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 941 my ( $self, $section, @rr ) = @_;
499 322         654 my $list = $self->_section($section);
500 322         984 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 196 my ( $self, $section, @rr ) = @_;
524 93         191 my $list = $self->_section($section);
525              
526 93         189 my %unique = map { ( bless( {%$_, ttl => 0}, ref $_ )->canonical => $_ ) } @rr, @$list;
  237         1272  
527 93         483 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 24 my $self = shift;
543 5         12 my $list = $self->_section(shift);
544 5         22 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   630 my $self = shift;
558 420         580 my $name = shift;
559 420   100     1505 my $list = $_section{unpack 'a3', $name} || $name;
560 420   100     1233 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 2756 my ( $self, @argument ) = @_;
628 32   100     72 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 3554 my ( $self, @argument ) = @_;
660 40         192 my $sig = $self->sigrr;
661 40 100       265 return $sig ? $sig->verify( $self, @argument ) : shift @argument;
662             }
663              
664             sub verifyerr {
665 25     25 1 161 my $sig = shift->sigrr;
666 25 100       130 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 982 my $self = shift;
695 2         3 my $karg = shift;
696              
697 2   100     5 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 1580 my $self = shift;
726              
727 123         349 my ($sig) = reverse $self->additional;
728 123 100       436 return unless $sig;
729 107         368 for ( $sig->type ) {
730 107 100       1021 return $sig if /TSIG|SIG/;
731             }
732 7         38 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 27 my $self = shift;
771 5   100     24 my $size = shift || UDPSZ;
772              
773 5         18 my $sigrr = $self->sigrr;
774 5 100       14 $size = UDPSZ unless $size > UDPSZ;
775 5 100       15 $size -= $sigrr->_size if $sigrr;
776              
777 5         11 my $data = pack 'x' x HEADER_LENGTH; # header placeholder
778 5         13 $self->{count} = [];
779              
780 5         9 my $tc;
781 5         9 my $hash = {};
782 5         13 foreach my $section ( map { $self->{$_} } qw(question answer authority) ) {
  15         37  
783 15         21 my @list;
784 15         32 foreach my $item (@$section) {
785 108         223 my $component = $item->encode( length $data, $hash );
786 108 100       246 last if length($data) + length($component) > $size;
787 105 100       168 last if $tc;
788 104         159 $data .= $component;
789 104         197 CORE::push @list, $item;
790             }
791 15 100       34 $tc++ if scalar(@list) < scalar(@$section);
792 15         40 @$section = @list;
793             }
794 5 100       24 $self->header->tc(1) if $tc; # only set if truncated here
795              
796 5         10 my %rrset;
797             my @order;
798 5         15 foreach my $item ( grep { ref($_) ne ref($sigrr) } $self->additional ) {
  146         239  
799 145         361 my $name = $item->{owner}->canonical;
800 145   100     308 my $class = $item->{class} || 0;
801 145         299 my $key = pack 'nna*', $class, $item->{type}, $name;
802 145 100       302 CORE::push @order, $key unless $rrset{$key};
803 145         170 CORE::push @{$rrset{$key}}, $item;
  145         475  
804             }
805              
806 5         90 my @list;
807 5         14 foreach my $key (@order) {
808 54         76 my $component = '';
809 54         407 my @item = @{$rrset{$key}};
  54         101  
810 54         88 foreach my $item (@item) {
811 66         142 $component .= $item->encode( length $data, $hash );
812             }
813 54 100       116 last if length($data) + length($component) > $size;
814 50         72 $data .= $component;
815 50         99 CORE::push @list, @item;
816             }
817              
818 5 100       13 if ($sigrr) {
819 1         5 $data .= $sigrr->encode( length $data, $hash, $self );
820 1         3 CORE::push @list, $sigrr;
821             }
822 5         19 $self->{'additional'} = \@list;
823              
824 5         19 my @part = qw(question answer authority additional);
825 5         14 my @size = map { scalar @{$self->{$_}} } @part;
  20         23  
  20         43  
826 5         14 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 329 my @data = @_; # uncoverable pod
834 3         20 require Data::Dumper;
835 3   100     12 local $Data::Dumper::Maxdepth = $Data::Dumper::Maxdepth || 3;
836 3   100     9 local $Data::Dumper::Sortkeys = $Data::Dumper::Sortkeys || 1;
837 3   100     10 local $Data::Dumper::Useqq = $Data::Dumper::Useqq || 1;
838 3         13 print Data::Dumper::Dumper(@data);
839 3         410 return;
840             }
841              
842              
843             1;
844             __END__