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