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