1package Net::DNS::Nameserver;
2#
3# $Id: Nameserver.pm 709 2008-02-06 22:40:42Z olaf $
4#
5
6use Net::DNS;
7use IO::Socket;
8use IO::Socket::INET;
9use IO::Select;
10use Carp qw(cluck);
11
12use strict;
13use vars qw(	$VERSION
14 		$has_inet6
15 		);
16
17use constant	FORCE_INET4 => 0;
18
19use constant	DEFAULT_ADDR => 0;
20use constant	DEFAULT_PORT => 53;
21
22use constant	STATE_ACCEPTED => 1;
23use constant	STATE_GOT_LENGTH => 2;
24use constant	STATE_SENDING => 3;
25
26$VERSION = (qw$LastChangedRevision: 709 $)[1];
27
28
29
30BEGIN {
31	if ( FORCE_INET4 ) {
32		$has_inet6 = 0;
33	} elsif ( eval {require Socket6;} &&
34			# INET6 earlier than V2.01 will not work; sorry.
35			eval {require IO::Socket::INET6; IO::Socket::INET6->VERSION("2.01");} ) {
36 		import Socket6;
37		$has_inet6 = 1;
38	} else {
39		$has_inet6=0;
40	}
41}
42
43
44#------------------------------------------------------------------------------
45
46
47#------------------------------------------------------------------------------
48# Constructor.
49#------------------------------------------------------------------------------
50
51sub new {
52	my ($class, %self) = @_;
53
54	unless ( ref $self{ReplyHandler} ) {
55		cluck "No reply handler!";
56		return;
57	}
58
59	# local server addresses must also be accepted by a resolver
60	my @LocalAddr = ref $self{LocalAddr} ? @{$self{LocalAddr}} : ($self{LocalAddr});
61	my $resolver = Net::DNS::Resolver->new;
62	$resolver->force_v4(1) unless $has_inet6;
63	$resolver->nameservers(undef);
64	my @localaddresses = $resolver->nameservers(@LocalAddr);
65
66	my $port = $self{LocalPort} || DEFAULT_PORT;
67
68	my @sock_tcp;	# All the TCP sockets we will listen to.
69	my @sock_udp;	# All the UDP sockets we will listen to.
70
71	# while we are here, print incomplete lines as they come along.
72	local $| = 1 if $self{Verbose};
73
74	foreach my $addr ( @localaddresses ? @localaddresses : DEFAULT_ADDR ){
75
76 	    #--------------------------------------------------------------------------
77 	    # Create the TCP socket.
78 	    #--------------------------------------------------------------------------
79
80 	    print "\nCreating TCP socket $addr#$port - " if $self{Verbose};
81
82	    my $sock_tcp = inet_new(
83 						    LocalAddr => $addr,
84 						    LocalPort => $port,
85 						    Listen	  => 64,
86 						    Proto	  => "tcp",
87 						    Reuse	  => 1,
88 						    );
89	    if ( $sock_tcp ) {
90		push @sock_tcp, $sock_tcp;
91		print "done.\n" if $self{Verbose};
92	    } else {
93		cluck "Couldn't create TCP socket: $!";
94	    }
95
96 	    #--------------------------------------------------------------------------
97 	    # Create the UDP Socket.
98 	    #--------------------------------------------------------------------------
99
100 	    print "Creating UDP socket $addr#$port - " if $self{Verbose};
101
102 	    my $sock_udp = inet_new(
103 						   LocalAddr => $addr,
104 						   LocalPort => $port,
105 						   Proto => "udp",
106 						   );
107
108	    if ( $sock_udp ) {
109		push @sock_udp, $sock_udp;
110		print "done.\n" if $self{Verbose};
111	    } else {
112		cluck "Couldn't create UDP socket: $!";
113	    }
114
115 	}
116
117	#--------------------------------------------------------------------------
118	# Create the Select object.
119	#--------------------------------------------------------------------------
120
121 	my $select = $self{select} = IO::Select->new;
122
123 	$select->add(@sock_tcp);
124 	$select->add(@sock_udp);
125
126	return undef unless $select->count;
127
128	#--------------------------------------------------------------------------
129	# Return the object.
130	#--------------------------------------------------------------------------
131
132	my $self = bless \%self, $class;
133	return $self;
134}
135
136#------------------------------------------------------------------------------
137# inet_new - Calls the constructor in the correct module for making sockets.
138#------------------------------------------------------------------------------
139
140sub inet_new {
141	if ($has_inet6) {
142	    return IO::Socket::INET6->new(@_);
143	} else {
144	    return IO::Socket::INET->new(@_);
145	}
146}
147
148#------------------------------------------------------------------------------
149# make_reply - Make a reply packet.
150#------------------------------------------------------------------------------
151
152sub make_reply {
153	my ($self, $query, $peerhost) = @_;
154
155	my $reply = Net::DNS::Packet->new();	# create empty reply packet
156	$reply->header->qr(1);
157
158	my $headermask;
159
160	unless ($query) {
161		print "ERROR: invalid packet\n" if $self->{"Verbose"};
162		$reply->header->rcode("FORMERR");
163		return $reply;
164	}
165
166	if ($query->header->qr()) {
167		print "ERROR: invalid packet (qr was set, dropping)\n" if $self->{"Verbose"};
168		return;
169	}
170
171
172	# question section returned to caller
173	my @q = $query->question;
174	@q=( Net::DNS::Question->new('', 'ANY', 'ANY') ) unless @q;
175
176	$reply->push("question", @q);
177
178	if ($query->header->opcode eq "QUERY" ||
179	    $query->header->opcode eq "NS_NOTIFY_OP"   #RFC1996
180	    ) {
181		if ($query->header->qdcount == 1) {
182			my ($qr) = @q;
183			my $qname = $qr->qname;
184			my $qtype = $qr->qtype;
185			my $qclass = $qr->qclass;
186
187			print "query ", $query->header->id,
188			": ($qname, $qclass, $qtype) - " if $self->{"Verbose"};
189
190			my ($rcode, $ans, $auth, $add);
191
192			if  ($query->header->opcode eq "QUERY"){
193			  ($rcode, $ans, $auth, $add, $headermask) =
194			      &{$self->{"ReplyHandler"}}($qname, $qclass, $qtype, $peerhost, $query);
195			}else{
196			  $reply->header->rcode("SERVFAIL") unless
197			     ( ref $self->{"NotifyHandler"} eq "CODE");
198			  ($rcode, $ans, $auth, $add, $headermask) =
199			      &{$self->{"NotifyHandler"}}($qname, $qclass, $qtype, $peerhost, $query);
200			}
201			print "$rcode\n" if $self->{"Verbose"};
202
203			$reply->header->rcode($rcode);
204
205			$reply->push("answer",	   @$ans)  if $ans;
206			$reply->push("authority",  @$auth) if $auth;
207			$reply->push("additional", @$add)  if $add;
208		} else {
209			print "ERROR: qdcount ", $query->header->qdcount,
210				"unsupported\n" if $self->{"Verbose"};
211			$reply->header->rcode("FORMERR");
212		}
213	} else {
214		print "ERROR: opcode ", $query->header->opcode, " unsupported\n"
215			if $self->{"Verbose"};
216		$reply->header->rcode("FORMERR");
217	}
218
219
220
221	if (!defined ($headermask)) {
222		$reply->header->ra(1);
223		$reply->header->ad(0);
224	} else {
225		$reply->header->aa(1) if $headermask->{'aa'};
226		$reply->header->ra(1) if $headermask->{'ra'};
227		$reply->header->ad(1) if $headermask->{'ad'};
228	}
229
230
231	$reply->header->cd($query->header->cd);
232	$reply->header->rd($query->header->rd);
233	$reply->header->id($query->header->id);
234
235	$reply->header->print if $self->{"Verbose"} && defined $headermask;
236
237	return $reply;
238}
239
240#------------------------------------------------------------------------------
241# readfromtcp - read from a TCP client
242#------------------------------------------------------------------------------
243
244sub readfromtcp {
245  	my ($self, $sock) = @_;
246	return -1 unless defined $self->{"_tcp"}{$sock};
247	my $peer = $self->{"_tcp"}{$sock}{"peer"};
248	my $charsread = $sock->sysread(
249	    $self->{"_tcp"}{$sock}{"inbuffer"},
250	    16384);
251	$self->{"_tcp"}{$sock}{"timeout"} = time()+120; # Reset idle timer
252	print "Received $charsread octets from $peer\n" if $self->{"Verbose"};
253	if ($charsread == 0) { # 0 octets means socket has closed
254	  print "Connection to $peer closed or lost.\n" if $self->{"Verbose"};
255	  $self->{"select"}->remove($sock);
256	  $sock->close();
257	  delete $self->{"_tcp"}{$sock};
258	  return $charsread;
259	}
260	return $charsread;
261}
262
263#------------------------------------------------------------------------------
264# tcp_connection - Handle a TCP connection.
265#------------------------------------------------------------------------------
266
267sub tcp_connection {
268	my ($self, $sock) = @_;
269
270	if (not $self->{"_tcp"}{$sock}) {
271		# We go here if we are called with a listener socket.
272		my $client = $sock->accept;
273		if (not defined $client) {
274	  		print "TCP connection closed by peer before we could accept it.\n" if $self->{"Verbose"};
275	  		return 0;
276		}
277		my $peerport= $client->peerport;
278		my $peerhost = $client->peerhost;
279
280		print "TCP connection from $peerhost:$peerport\n" if $self->{"Verbose"};
281		$client->blocking(0);
282		$self->{"_tcp"}{$client}{"peer"} = "tcp:".$peerhost.":".$peerport;
283		$self->{"_tcp"}{$client}{"state"} = STATE_ACCEPTED;
284		$self->{"_tcp"}{$client}{"socket"} = $client;
285		$self->{"_tcp"}{$client}{"timeout"} = time()+120;
286 		$self->{"select"}->add($client);
287		# After we accepted we will look at the socket again
288		# to see if there is any data there. ---Olaf
289		$self->loop_once(0);
290	} else {
291		# We go here if we are called with a client socket
292		my $peer = $self->{"_tcp"}{$sock}{"peer"};
293
294		if ($self->{"_tcp"}{$sock}{"state"} == STATE_ACCEPTED) {
295		  if (not $self->{"_tcp"}{$sock}{"inbuffer"} =~ s/^(..)//s) {
296		    return; # Still not 2 octets ready
297		  }
298		  my $msglen = unpack("n", $1);
299		  print "Removed 2 octets from the input buffer from $peer.\n".
300		  	"$peer said his query contains $msglen octets.\n"
301		  	if $self->{"Verbose"};
302		  $self->{"_tcp"}{$sock}{"state"} = STATE_GOT_LENGTH;
303		  $self->{"_tcp"}{$sock}{"querylength"} = $msglen;
304		}
305		# Not elsif, because we might already have all the data
306		if ($self->{"_tcp"}{$sock}{"state"} == STATE_GOT_LENGTH) {
307			# return if not all data has been received yet.
308		  	return if $self->{"_tcp"}{$sock}{"querylength"} > length $self->{"_tcp"}{$sock}{"inbuffer"};
309
310			my $qbuf = substr($self->{"_tcp"}{$sock}{"inbuffer"}, 0, $self->{"_tcp"}{$sock}{"querylength"});
311			substr($self->{"_tcp"}{$sock}{"inbuffer"}, 0, $self->{"_tcp"}{$sock}{"querylength"}) = "";
312		  	my $query = Net::DNS::Packet->new(\$qbuf);
313		  	my $reply = $self->make_reply($query, $sock->peerhost);
314		  	if (not defined $reply) {
315		    		print "I couldn't create a reply for $peer. Closing socket.\n"
316		    			if $self->{"Verbose"};
317				$self->{"select"}->remove($sock);
318				$sock->close();
319				delete $self->{"_tcp"}{$sock};
320				return;
321		  	}
322		  	my $reply_data = $reply->data;
323			my $len = length $reply_data;
324			$self->{"_tcp"}{$sock}{"outbuffer"} = pack("n", $len) . $reply_data;
325			print "Queued ",
326				length $self->{"_tcp"}{$sock}{"outbuffer"},
327				" octets to $peer\n"
328				if $self->{"Verbose"};
329			# We are done.
330		  	$self->{"_tcp"}{$sock}{"state"} = STATE_SENDING;
331		}
332	}
333}
334
335#------------------------------------------------------------------------------
336# udp_connection - Handle a UDP connection.
337#------------------------------------------------------------------------------
338
339sub udp_connection {
340	my ($self, $sock) = @_;
341
342	my $buf = "";
343
344 	$sock->recv($buf, &Net::DNS::PACKETSZ);
345 	my ($peerhost,$peerport) = ($sock->peerhost, $sock->peerport);
346
347 	print "UDP connection from $peerhost:$peerport\n" if $self->{"Verbose"};
348
349	my $query = Net::DNS::Packet->new(\$buf);
350
351	my $reply = $self->make_reply($query, $peerhost) || return;
352	my $reply_data = $reply->data;
353
354	local $| = 1 if $self->{"Verbose"};
355	print "Writing response - " if $self->{"Verbose"};
356
357	if ($sock->send($reply_data)) { #
358	  print "done\n" if $self->{"Verbose"};
359	}
360	else {
361	  print "failed to send reply: $!\n" if $self->{"Verbose"};
362	}
363      }
364
365
366sub get_open_tcp {
367    my $self=shift;
368    return keys %{$self->{"_tcp"}};
369}
370
371
372#------------------------------------------------------------------------------
373# loop_once - Just check "once" on sockets already set up
374#------------------------------------------------------------------------------
375
376# This function might not actually return immediately. If an AXFR request is
377# coming in which will generate a huge reply, we will not relinquish control
378# until our outbuffers are empty.
379
380#
381#  NB  this method may be subject to change and is therefore left 'undocumented'
382#
383
384sub loop_once {
385  my ($self, $timeout) = @_;
386  $timeout=0 unless defined($timeout);
387  print ";loop_once called with $timeout \n" if $self->{"Verbose"} >4;
388  foreach my $sock (keys %{$self->{"_tcp"}}) {
389      $timeout = 0.1 if $self->{"_tcp"}{$sock}{"outbuffer"};
390  }
391  my @ready = $self->{"select"}->can_read($timeout);
392
393  foreach my $sock (@ready) {
394      my $protonum = $sock->protocol;
395      # This is a weird and nasty hack. Although not incorrect,
396      # I just don't know why ->protocol won't tell me the protocol
397      # on a connected socket. --robert
398      $protonum = getprotobyname('tcp') if not defined $protonum and $self->{"_tcp"}{$sock};
399
400      my $proto = getprotobynumber($protonum);
401      if (!$proto) {
402	  print "ERROR: connection with unknown protocol\n"
403	      if $self->{"Verbose"};
404      } elsif (lc($proto) eq "tcp") {
405
406	  $self->readfromtcp($sock) &&
407	      $self->tcp_connection($sock);
408      } elsif (lc($proto) eq "udp") {
409	  $self->udp_connection($sock);
410      } else {
411	  print "ERROR: connection with unsupported protocol $proto\n"
412	      if $self->{"Verbose"};
413      }
414  }
415  my $now = time();
416  # Lets check if any of our TCP clients has pending actions.
417  # (outbuffer, timeout)
418  foreach my $s (keys %{$self->{"_tcp"}}) {
419      my $sock = $self->{"_tcp"}{$s}{"socket"};
420      if ($self->{"_tcp"}{$s}{"outbuffer"}) {
421	  # If we have buffered output, then send as much as the OS will accept
422	  # and wait with the rest
423	  my $len = length $self->{"_tcp"}{$s}{"outbuffer"};
424	  my $charssent = $sock->syswrite($self->{"_tcp"}{$s}{"outbuffer"});
425	  print "Sent $charssent of $len octets to ",$self->{"_tcp"}{$s}{"peer"},".\n"
426	      if $self->{"Verbose"};
427	  substr($self->{"_tcp"}{$s}{"outbuffer"}, 0, $charssent) = "";
428	  if (length $self->{"_tcp"}{$s}{"outbuffer"} == 0) {
429	      delete $self->{"_tcp"}{$s}{"outbuffer"};
430	      $self->{"_tcp"}{$s}{"state"} = STATE_ACCEPTED;
431	      if (length $self->{"_tcp"}{$s}{"inbuffer"} >= 2) {
432		  # See if the client has send us enough data to process the
433		  # next query.
434		  # We do this here, because we only want to process (and buffer!!)
435		  # a single query at a time, per client. If we allowed a STATE_SENDING
436		  # client to have new requests processed. We could be easilier
437		  # victims of DoS (client sending lots of queries and never reading
438		  # from it's socket).
439		  # Note that this does not disable serialisation on part of the
440		  # client. The split second it should take for us to lookip the
441		  # next query, is likely faster than the time it takes to
442		  # send the response... well, unless it's a lot of tiny queries,
443		  # in which case we will be generating an entire TCP packet per
444		  # reply. --robert
445		  $self->tcp_connection($self->{"_tcp"}{"socket"});
446	      }
447	  }
448	  $self->{"_tcp"}{$s}{"timeout"} = time()+120;
449      } else {
450	  # Get rid of idle clients.
451	  my $timeout = $self->{"_tcp"}{$s}{"timeout"};
452	  if ($timeout - $now < 0) {
453	      print $self->{"_tcp"}{$s}{"peer"}," has been idle for too long and will be disconnected.\n"
454		  if $self->{"Verbose"};
455	      $self->{"select"}->remove($sock);
456	      $sock->close();
457	      delete $self->{"_tcp"}{$s};
458	  }
459      }
460  }
461}
462
463#------------------------------------------------------------------------------
464# main_loop - Main nameserver loop.
465#------------------------------------------------------------------------------
466
467sub main_loop {
468    my $self = shift;
469
470    while (1) {
471	print "Waiting for connections...\n" if $self->{"Verbose"};
472	# You really need an argument otherwise you'll be burning
473	# CPU.
474	$self->loop_once(10);
475    }
476}
477
4781;
479
480__END__
481
482=head1 NAME
483
484Net::DNS::Nameserver - DNS server class
485
486=head1 SYNOPSIS
487
488C<use Net::DNS::Nameserver;>
489
490=head1 DESCRIPTION
491
492Instances of the C<Net::DNS::Nameserver> class represent DNS server
493objects.  See L</EXAMPLE> for an example.
494
495=head1 METHODS
496
497=head2 new
498
499 my $ns = Net::DNS::Nameserver->new(
500	LocalAddr	 => "10.1.2.3",
501	LocalPort	 => "5353",
502	ReplyHandler => \&reply_handler,
503	Verbose		 => 1
504 );
505
506
507
508 my $ns = Net::DNS::Nameserver->new(
509	LocalAddr	 => ['::1' , '127.0.0.1' ],
510	LocalPort	 => "5353",
511	ReplyHandler => \&reply_handler,
512	Verbose		 => 1
513 );
514
515Creates a nameserver object.  Attributes are:
516
517  LocalAddr		IP address on which to listen.	Defaults to INADDR_ANY.
518  LocalPort		Port on which to listen.  	Defaults to 53.
519  ReplyHandler		Reference to reply-handling
520			subroutine			Required.
521  NotifyHandler         Reference to reply-handling
522                        subroutine for queries with
523                        opdcode NS_NOTIFY (RFC1996)
524  Verbose		Print info about received
525			queries.			Defaults to 0 (off).
526
527
528The LocalAddr attribute may alternatively be specified as a list of IP
529addresses to listen to.
530
531If IO::Socket::INET6 and Socket6 are available on the system you can
532also list IPv6 addresses and the default is '0' (listen on all interfaces on
533IPv6 and IPv4);
534
535
536The ReplyHandler subroutine is passed the query name, query class,
537query type and optionally an argument containing the peerhost the
538incoming query. It must return the response code and references to the
539answer, authority, and additional sections of the response.  Common
540response codes are:
541
542  NOERROR	No error
543  FORMERR	Format error
544  SERVFAIL	Server failure
545  NXDOMAIN	Non-existent domain (name doesn't exist)
546  NOTIMP	Not implemented
547  REFUSED	Query refused
548
549For advanced usage it may also contain a headermaks containing an
550hashref with the settings for the C<aa>, C<ra>, and C<ad>
551header bits. The argument is of the form
552C<< { ad => 1, aa => 0, ra => 1 } >>.
553
554
555See RFC 1035 and the IANA dns-parameters file for more information:
556
557  ftp://ftp.rfc-editor.org/in-notes/rfc1035.txt
558  http://www.isi.edu/in-notes/iana/assignments/dns-parameters
559
560The nameserver will listen for both UDP and TCP connections.  On
561Unix-like systems, the program will probably have to run as root
562to listen on the default port, 53.	A non-privileged user should
563be able to listen on ports 1024 and higher.
564
565Returns a Net::DNS::Nameserver object, or undef if the object
566couldn't be created.
567
568See L</EXAMPLE> for an example.
569
570=head2 main_loop
571
572	$ns->main_loop;
573
574Start accepting queries. Calling main_loop never returns.
575
576=cut
577
578#####
579#
580#  The functionality might change. Left "undocumented" for now.
581#
582=head2 loop_once
583
584	$ns->loop_once( [TIMEOUT_IN_SECONDS] );
585
586Start accepting queries, but returns. If called without a parameter,
587the call will not return until a request has been received (and
588replied to). If called with a number, that number specifies how many
589seconds (even fractional) to maximum wait before returning. If called
590with 0 it will return immediately unless there's something to do.
591
592Handling a request and replying obviously depends on the speed of
593ReplyHandler. Assuming ReplyHandler is super fast, loop_once should spend
594just a fraction of a second, if called with a timeout value of 0 seconds.
595One exception is when an AXFR has requested a huge amount of data that
596the OS is not ready to receive in full. In that case, it will keep
597running through a loop (while servicing new requests) until the reply
598has been sent.
599
600In case loop_once accepted a TCP connection it will immediatly check
601if there is data to be read from the socket. If not it will return and
602you will have to call loop_once() again to check if there is any data
603waiting on the socket to be processed. In most cases you will have to
604count on calling "loop_once" twice.
605
606A code fragment like:
607	$ns->loop_once(10);
608        while( $ns->get_open_tcp() ){
609	      $ns->loop_once(0);
610	}
611
612Would wait for 10 seconds for the initial connection and would then
613process all TCP sockets until none is left.
614
615=head2 get_open_tcp
616
617In scalar context returns the number of TCP connections for which state
618is maintained. In array context it returns IO::Socket objects, these could
619be useful for troubleshooting but be careful using them.
620
621=head1 EXAMPLE
622
623The following example will listen on port 5353 and respond to all queries
624for A records with the IP address 10.1.2.3.	 All other queries will be
625answered with NXDOMAIN.	 Authority and additional sections are left empty.
626The $peerhost variable catches the IP address of the peer host, so that
627additional filtering on its basis may be applied.
628
629 #!/usr/bin/perl
630
631 use Net::DNS::Nameserver;
632 use strict;
633 use warnings;
634
635 sub reply_handler {
636	 my ($qname, $qclass, $qtype, $peerhost,$query) = @_;
637	 my ($rcode, @ans, @auth, @add);
638
639	 print "Received query from $peerhost\n";
640	 $query->print;
641
642
643	 if ($qtype eq "A" && $qname eq "foo.example.com" ) {
644		 my ($ttl, $rdata) = (3600, "10.1.2.3");
645		 push @ans, Net::DNS::RR->new("$qname $ttl $qclass $qtype $rdata");
646		 $rcode = "NOERROR";
647	 }elsif( $qname eq "foo.example.com" ) {
648		 $rcode = "NOERROR";
649
650	 }else{
651  	          $rcode = "NXDOMAIN";
652	 }
653
654
655	 # mark the answer as authoritive (by setting the 'aa' flag
656	 return ($rcode, \@ans, \@auth, \@add, { aa => 1 });
657 }
658
659 my $ns = Net::DNS::Nameserver->new(
660     LocalPort    => 5353,
661     ReplyHandler => \&reply_handler,
662     Verbose      => 1,
663 ) || die "couldn't create nameserver object\n";
664
665 $ns->main_loop;
666
667=head1 BUGS
668
669Limitations in perl 5.8.6 makes it impossible to guarantee that
670replies to UDP queries from Net::DNS::Nameserver are sent from the
671IP-address they were received on. This is a problem for machines with
672multiple IP-addresses and causes violation of RFC2181 section 4.
673Thus a UDP socket created listening to INADDR_ANY (all available
674IP-addresses) will reply not necessarily with the source address being
675the one to which the request was sent, but rather with the address that
676the operating system choses. This is also often called "the closest
677address". This should really only be a problem on a server which has
678more than one IP-address (besides localhost - any experience with IPv6
679complications here, would be nice). If this is a problem for you, a
680work-around would be to not listen to INADDR_ANY but to specify each
681address that you want this module to listen on. A seperate set of
682sockets will then be created for each IP-address.
683
684=head1 COPYRIGHT
685
686Copyright (c) 1997-2002 Michael Fuhr.
687
688Portions Copyright (c) 2002-2004 Chris Reinhardt.
689
690Portions Copyright (c) 2005-2007 O.M, Kolkman, RIPE NCC.
691
692Portions Copyright (c) 2005 Robert Martin-Legene.
693
694All rights reserved.  This program is free software; you may redistribute
695it and/or modify it under the same terms as Perl itself.
696
697=head1 SEE ALSO
698
699L<perl(1)>, L<Net::DNS>, L<Net::DNS::Resolver>, L<Net::DNS::Packet>,
700L<Net::DNS::Update>, L<Net::DNS::Header>, L<Net::DNS::Question>,
701L<Net::DNS::RR>, RFC 1035
702
703=cut
704