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   72467 use strict;
  92         188  
  92         2868  
4 92     92   518 use warnings;
  92         214  
  92         4502  
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   1575 use integer;
  92         215  
  92         476  
30 92     92   1995 use Carp;
  92         206  
  92         6763  
31              
32 92     92   1797 use Net::DNS::Parameters qw(:dsotype);
  92         193  
  92         10513  
33 92     92   659 use constant UDPSZ => 512;
  92         184  
  92         8080  
34              
35             BEGIN {
36 92     92   43669 require Net::DNS::Header;
37 92         40324 require Net::DNS::Question;
38 92         13129 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 2034770 my ( $class, @arg ) = @_;
62 214 100       645 return &decode if ref $arg[0];
63              
64 203         1204 my $self = bless {
65             status => 0,
66             question => [],
67             answer => [],
68             authority => [],
69             additional => [],
70             }, $class;
71              
72 203 100       1186 $self->{question} = [Net::DNS::Question->new(@arg)] if scalar @arg;
73              
74 202         652 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   554 use constant HEADER_LENGTH => length pack 'n6', (0) x 6;
  92         209  
  92         283627  
108              
109             sub decode {
110 207     207 0 10338 my $class = shift; # uncoverable pod
111 207         450 my $data = shift;
112 207   100     1104 my $debug = shift || 0;
113              
114 207         429 my $offset = 0;
115 207         466 my $self;
116 207         520 eval {
117 207         1368 local $SIG{__DIE__};
118 207         581 my $length = length $$data;
119 207 100       827 die 'corrupt wire-format data' if $length < HEADER_LENGTH;
120              
121             # header section
122 193         1304 my ( $id, $status, @count ) = unpack 'n6', $$data;
123 193         646 my ( $qd, $an, $ns, $ar ) = @count;
124              
125 193         2129 $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         536 my $hash = {};
138 193         441 my $record;
139 193         413 $offset = HEADER_LENGTH;
140 193         673 while ( $qd-- ) {
141 153         1122 ( $record, $offset ) = Net::DNS::Question->decode( $data, $offset, $hash );
142 136         320 CORE::push( @{$self->{question}}, $record );
  136         757  
143             }
144              
145             # RR sections
146 176         625 while ( $an-- ) {
147 8437         18735 ( $record, $offset ) = Net::DNS::RR->decode( $data, $offset, $hash );
148 8437         13145 CORE::push( @{$self->{answer}}, $record );
  8437         20737  
149             }
150              
151 176         660 while ( $ns-- ) {
152 422         1463 ( $record, $offset ) = Net::DNS::RR->decode( $data, $offset, $hash );
153 422         895 CORE::push( @{$self->{authority}}, $record );
  422         1441  
154             }
155              
156 176         556 while ( $ar-- ) {
157 868         2387 ( $record, $offset ) = Net::DNS::RR->decode( $data, $offset, $hash );
158 868         1729 CORE::push( @{$self->{additional}}, $record );
  868         3008  
159             }
160              
161 176 100       3176 return unless $offset == HEADER_LENGTH;
162 5 100       18 return unless $self->header->opcode eq 'DSO';
163              
164 1         3 $self->{dso} = [];
165 1         2 my $limit = $length - 4;
166 1         4 while ( $offset < $limit ) {
167 1         10 my ( $t, $l, $v ) = unpack "\@$offset n2a*", $$data;
168 1         2 CORE::push( @{$self->{dso}}, [$t, substr( $v, 0, $l )] );
  1         5  
169 1         6 $offset += ( $l + 4 );
170             }
171             };
172              
173 207 100       1464 if ($debug) {
174 2         6 local $@ = $@;
175 2 100       7 print $@ if $@;
176 2         4 eval { $self->print };
  2         12  
177             }
178              
179 207 100       941 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 687 return &encode;
197             }
198              
199             sub encode {
200 234     234 0 558 my ( $self, $size ) = @_; # uncoverable pod
201              
202 234         531 my $edns = $self->edns; # EDNS support
203 234         435 my @addl = grep { !$_->isa('Net::DNS::RR::OPT') } @{$self->{additional}};
  277         943  
  234         650  
204 234 100       860 $self->{additional} = [$edns, @addl] if $edns->_specified;
205              
206 234 100       633 return $self->truncate($size) if $size;
207              
208 233         677 my @part = qw(question answer authority additional);
209 233         508 my @size = map { scalar @{$self->{$_}} } @part;
  932         1264  
  932         2241  
210 233         688 my $data = pack 'n6', $self->header->id, $self->{status}, @size;
211 233         987 $self->{count} = [];
212              
213 233         495 my $hash = {}; # packet body
214 233         568 foreach my $component ( map { @{$self->{$_}} } @part ) {
  932         1392  
  932         2192  
215 986         3060 $data .= $component->encode( length $data, $hash, $self );
216             }
217              
218 233         1896 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 6553 my $self = shift;
233 1095         5173 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 1749 my $self = shift;
250 772         1576 my $link = \$self->{xedns};
251 772 100       2164 ($$link) = grep { $_->isa(qw(Net::DNS::RR::OPT)) } @{$self->{additional}} unless $$link;
  1005         3701  
  284         935  
252 772 100       2340 $$link = Net::DNS::RR->new( type => 'OPT' ) unless $$link;
253 772         2255 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 75 my ( $query, @UDPmax ) = @_;
271 7         19 my $qheadr = $query->header;
272 7 100       26 croak 'erroneous qr flag in query packet' if $qheadr->qr;
273              
274 6         29 my $reply = Net::DNS::Packet->new();
275 6         19 my $header = $reply->header;
276 6         23 $header->qr(1); # reply with same id, opcode and question
277 6         31 $header->id( $qheadr->id );
278 6         27 $header->opcode( $qheadr->opcode );
279 6         19 my @question = $query->question;
280 6         20 $reply->{question} = [@question];
281              
282 6         21 $header->rcode('FORMERR'); # no RCODE considered sinful!
283              
284 6         23 $header->rd( $qheadr->rd ); # copy these flags into reply
285 6         41 $header->cd( $qheadr->cd );
286              
287 6 100       11 return $reply unless grep { $_->isa('Net::DNS::RR::OPT') } @{$query->{additional}};
  4         47  
  6         25  
288              
289 1         4 my $edns = $reply->edns();
290 1         2 CORE::push( @{$reply->{additional}}, $edns );
  1         3  
291 1         5 $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 727 my @qr = @{shift->{question}};
  220         625  
310 220         794 return @qr;
311             }
312              
313 98     98 1 491 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 259     259 1 3339 my @rr = @{shift->{answer}};
  259         1108  
331 259         1923 return @rr;
332             }
333              
334 2     2 1 299 sub pre { return &answer }
335 1     1 1 280 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 5567 my @rr = @{shift->{authority}};
  104         283  
352 104         357 return @rr;
353             }
354              
355 1     1 1 259 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 179     179 1 3527 my @rr = @{shift->{additional}};
  179         458  
369 179         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 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 333 my $self = shift;
398              
399 16         31 my $header = $self->header;
400 16         42 my $opcode = $header->opcode;
401 16         27 my $server = $self->{replyfrom};
402 16         27 my $length = $self->{replysize};
403 16 100       37 my $origin = $server ? ";; Response received from $server ($length octets)\n" : "";
404 16         49 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         2 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         29 my $edns = $self->edns;
416 15 100       39 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         37 my @question = $self->question;
420 15         24 my $qdcount = scalar @question;
421 15 100       49 my $qds = $qdcount != 1 ? 's' : '';
422 15         42 CORE::push( @record, ";; $section[0] SECTION ($qdcount record$qds)", map { ';; ' . $_->string } @question );
  10         29  
423              
424 15         37 my @answer = $self->answer;
425 15         29 my $ancount = scalar @answer;
426 15 100       44 my $ans = $ancount != 1 ? 's' : '';
427 15         44 CORE::push( @record, "\n;; $section[1] SECTION ($ancount record$ans)", map { $_->string } @answer );
  271         793  
428              
429 15         42 my @authority = $self->authority;
430 15         24 my $nscount = scalar @authority;
431 15 100       30 my $nss = $nscount != 1 ? 's' : '';
432 15         41 CORE::push( @record, "\n;; $section[2] SECTION ($nscount record$nss)", map { $_->string } @authority );
  9         24  
433              
434 15         33 my @additional = $self->additional;
435 15         26 my $arcount = scalar @additional;
436 15 100       29 my $ars = $arcount != 1 ? 's' : '';
437 15         51 my $EDNSmarker = join ' ', qq[;; {\t"EDNS-VERSION":], $edns->version, qq[}\n];
438 15         59 CORE::push( @record, "\n;; ADDITIONAL SECTION ($arcount record$ars)" );
439 15 100       26 CORE::push( @record, map { ( $_ eq $edns ) ? $EDNSmarker : $_->string } @additional );
  7         45  
440              
441 15         484 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 1553 my ( $self, @argument ) = @_;
456 127         322 for (@argument) { $self->{replyfrom} = $_ }
  122         406  
457 127         348 return $self->{replyfrom};
458             }
459              
460 1     1 0 6 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 477 return shift->{replysize};
475             }
476              
477 1     1 0 530 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 827 my ( $self, $section, @rr ) = @_;
499 322         715 my $list = $self->_section($section);
500 322         925 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 175 my ( $self, $section, @rr ) = @_;
524 93         179 my $list = $self->_section($section);
525              
526 93         179 my %unique = map { ( bless( {%$_, ttl => 0}, ref $_ )->canonical => $_ ) } @rr, @$list;
  237         1174  
527 93         469 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 18 my $self = shift;
543 5         11 my $list = $self->_section(shift);
544 5         19 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   608 my $self = shift;
558 420         569 my $name = shift;
559 420   100     1508 my $list = $_section{unpack 'a3', $name} || $name;
560 420   100     1184 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 2717 my ( $self, @argument ) = @_;
628 32   100     56 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 3394 my ( $self, @argument ) = @_;
660 40         107 my $sig = $self->sigrr;
661 40 100       210 return $sig ? $sig->verify( $self, @argument ) : shift @argument;
662             }
663              
664             sub verifyerr {
665 25     25 1 115 my $sig = shift->sigrr;
666 25 100       209 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 968 my $self = shift;
695 2         6 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 1490 my $self = shift;
726              
727 123         288 my ($sig) = reverse $self->additional;
728 123 100       378 return unless $sig;
729 107         331 for ( $sig->type ) {
730 107 100       958 return $sig if /TSIG|SIG/;
731             }
732 7         19 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 21 my $self = shift;
771 5   100     26 my $size = shift || UDPSZ;
772              
773 5         18 my $sigrr = $self->sigrr;
774 5 100       13 $size = UDPSZ unless $size > UDPSZ;
775 5 100       14 $size -= $sigrr->_size if $sigrr;
776              
777 5         13 my $data = pack 'x' x HEADER_LENGTH; # header placeholder
778 5         12 $self->{count} = [];
779              
780 5         9 my $tc;
781 5         8 my $hash = {};
782 5         12 foreach my $section ( map { $self->{$_} } qw(question answer authority) ) {
  15         36  
783 15         20 my @list;
784 15         26 foreach my $item (@$section) {
785 108         242 my $component = $item->encode( length $data, $hash );
786 108 100       238 last if length($data) + length($component) > $size;
787 105 100       185 last if $tc;
788 104         146 $data .= $component;
789 104         194 CORE::push @list, $item;
790             }
791 15 100       37 $tc++ if scalar(@list) < scalar(@$section);
792 15         41 @$section = @list;
793             }
794 5 100       18 $self->header->tc(1) if $tc; # only set if truncated here
795              
796 5         11 my %rrset;
797             my @order;
798 5         15 foreach my $item ( grep { ref($_) ne ref($sigrr) } $self->additional ) {
  146         230  
799 145         328 my $name = $item->{owner}->canonical;
800 145   100     309 my $class = $item->{class} || 0;
801 145         286 my $key = pack 'nna*', $class, $item->{type}, $name;
802 145 100       311 CORE::push @order, $key unless $rrset{$key};
803 145         263 CORE::push @{$rrset{$key}}, $item;
  145         425  
804             }
805              
806 5         18 my @list;
807 5         10 foreach my $key (@order) {
808 54         73 my $component = '';
809 54         68 my @item = @{$rrset{$key}};
  54         98  
810 54         83 foreach my $item (@item) {
811 66         149 $component .= $item->encode( length $data, $hash );
812             }
813 54 100       117 last if length($data) + length($component) > $size;
814 50         72 $data .= $component;
815 50         92 CORE::push @list, @item;
816             }
817              
818 5 100       19 if ($sigrr) {
819 1         6 $data .= $sigrr->encode( length $data, $hash, $self );
820 1         3 CORE::push @list, $sigrr;
821             }
822 5         21 $self->{'additional'} = \@list;
823              
824 5         15 my @part = qw(question answer authority additional);
825 5         9 my @size = map { scalar @{$self->{$_}} } @part;
  20         26  
  20         38  
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 275 my @data = @_; # uncoverable pod
834 3         17 require Data::Dumper;
835 3   100     13 local $Data::Dumper::Maxdepth = $Data::Dumper::Maxdepth || 3;
836 3   100     8 local $Data::Dumper::Sortkeys = $Data::Dumper::Sortkeys || 1;
837 3   100     9 local $Data::Dumper::Useqq = $Data::Dumper::Useqq || 1;
838 3         10 print Data::Dumper::Dumper(@data);
839 3         360 return;
840             }
841              
842              
843             1;
844             __END__