File Coverage

blib/lib/Net/DNS/Dig.pm
Criterion Covered Total %
statement 498 542 91.8
branch 243 322 75.4
condition 74 111 66.6
subroutine 49 50 98.0
pod 8 13 61.5
total 872 1038 84.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             package Net::DNS::Dig;
3              
4 24     24   409402 use strict;
  24         51  
  24         999  
5             #use diagnostics;
6              
7 24         1633 use vars qw(
8             $VERSION
9             @ISA
10             @EXPORT_OK
11 24     24   315 );
  24         37  
12              
13 24     24   13203 use Net::DNS::Codes qw(:all);
  24         24366  
  24         25918  
14 24         2984 use Net::DNS::ToolKit qw(
15             get_ns
16             newhead
17             gethead
18             get16
19             put16
20             getflags
21             putflags
22             gettimeofday
23             strip
24 24     24   15547 );
  24         319147  
25 24         2233 use Net::DNS::ToolKit::Utilities qw(
26             id
27             question
28             revIP
29 24     24   29002 );
  24         701615  
30 24     24   198 use Net::DNS::ToolKit::RR;
  24         41  
  24         572  
31 24         2745 use Net::NBsocket qw(
32             connect_NB
33             open_udpNB
34             sockaddr_in
35 24     24   21747 );
  24         387429  
36 24         227 use NetAddr::IP::InetBase qw(
37             inet_ntoa
38             inet_aton
39             ipv6_aton
40             ipv6_ntoa
41             AF_INET
42             AF_INET6
43 24     24   211 );
  24         45  
44 24         198 use NetAddr::IP::Util qw(
45             havegethostbyname2
46 24     24   3595 );
  24         56  
47 24         1768 use Sys::Hostname::FQDN qw(
48             fqdn
49 24     24   24296 );
  24         26137  
50              
51 24         1590 use Net::DNS::ToolKit::Debug qw(
52             print_buf
53             print_head
54 24     24   21913 );
  24         53740  
55 24     24   27233 use Data::Dumper;
  24         269223  
  24         79210  
56             #use AutoLoader qw(AUTOLOAD);
57              
58             require Exporter;
59             @ISA = qw(Exporter);
60              
61              
62             $VERSION = do { my @r = (q$Revision: 0.12 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r };
63              
64             @EXPORT_OK = qw(
65             ndd_gethostbyaddr
66             ndd_gethostbyname
67             ndd_gethostbyname2
68             AF_INET
69             AF_INET6
70             );
71              
72             # used a lot, create once per session
73             my @_NS = get_ns() || die "this host has no usable name server\n";
74             my $ID = time % 65536; # seed unique
75             my $ME = fqdn(); # get my name
76              
77             # declare autoload prototypes
78             sub my_name2_gethostby;
79             sub my_addr_gethostby;
80             sub al_name_gethostby;
81             sub al_name2_gethostby;
82             sub al_addr_gethostby;
83             sub _check4addr;
84             sub _get_host;
85              
86             *ndd_gethostbyaddr = \&al_addr_gethostby;
87             *ndd_gethostbyname = \&al_name_gethostby;
88             *ndd_gethostbyname2 = \&al_name2_gethostby;
89              
90 0     0   0 sub DESTROY {};
91              
92             my $_Sock6ok = 1; # for testing gethostby emulations
93             # true uses Socket6 if present
94              
95             my $_gethostHelp = 1; # for testing gethostby emulations
96             # true uses core gethostby__ if present
97              
98             # for autoloading
99             sub _get_NS {
100 24     24   67 @_NS;
101             }
102              
103             sub _hostHelp {
104 8     8   55 $_gethostHelp;
105             }
106              
107             # for testing
108             sub _set_NS {
109 29     29   20249 @_NS = @_;
110             }
111             sub _set_Help {
112 1     1   6312 $_gethostHelp = shift;
113             }
114              
115             sub import {
116 24 100   24   239 if (grep { $_ eq ':noSock6' } @_) {
  39         170  
117 1         2 $_Sock6ok = 0;
118 1         2 @_ = grep { $_ ne ':noSock6' } @_;
  8         12  
119             }
120 24 100       56 if (grep { $_ eq ':forceEmu' } @_) {
  38         167  
121 1         2 $_gethostHelp = 0;
122 1         2 @_ = grep { $_ ne ':forceEmu' } @_;
  7         11  
123             }
124 24         58902 Net::DNS::Dig->export_to_level(1,@_);
125             }
126              
127             =head1 NAME
128              
129             Net::DNS::Dig - dig like methods
130              
131             =head1 SYNOPSIS
132              
133             IO methods and functions to return DNS information
134              
135             These functions do not use C and are safe to use in CGI programs.
136              
137             use Net::DNS::Dig qw(
138             :forceEmu
139             ndd_gethostbyaddr
140             ndd_gethostbyname
141             ndd_gethostbyname2
142             AF_INET
143             AF_INET6
144             );
145              
146             $dig = new Net::DNS::Dig();
147             $dig = new Net::DNS::Dig( %hash );
148             $dig = new Net::DNS::Dig( \%hash );
149              
150             $dobj = $dig->for(name, type);
151             $tobj = $dig->to_text();
152              
153             $array_ptr = $dig->data($section);
154             $array_ptr = $tobj->data($section);
155              
156             $array_ptr = $dig->records($type,$sect);
157             $array_ptr = $tobj->records($type,$sect);
158              
159             $text = $dig->sprintf;
160              
161             $rv = $dig->rcode(true/false);
162             $rdata = $dig->rdata($type,$sect);
163             $rdata = $tobj->rdata($type,$sect;
164              
165             or in array context
166             @array = $dig->data($section);
167             @array = $dig->records($type,$sect);
168             @rdata = $dig->rdata($type,$sect);
169              
170             ($name,$aliases,$addrtype,$length,@addrs)
171             = ndd_gethostbyaddr( naddr_naddr6 );
172              
173             ($name,$aliases,$addrtype,$length,@addrs)
174             = ndd_gethostbyname( name_ipV4_ipV6 );
175              
176             ($name,$aliases,$addrtype,$length,@addrs)
177             = ndd_gethostbyname2( name_ipV4_ipV6 AF_family);
178              
179             or in array context
180             $name = ndd_gethostbyaddr(netaddr);
181             $netaddr = ndd_gethostbyname(name_ipV4);
182             $netaddr = ndd_gethostbyname2(name_ipV4_ipV6,AF_family);
183              
184             =cut
185              
186             my %Defaults = (
187             Timeout => 15,
188             Class => 'IN',
189             # PeerAddr => filled in by _get_nameservers
190             PeerPort => 53,
191             Proto => 'UDP',
192             Recursion => 1
193             );
194              
195             # for autoloading
196             sub _timeout {
197 37     37   104 $Defaults{Timeout};
198             }
199              
200             # method to calculate elapsed run time
201             #
202             # input: two element start time from gettimeofday
203             # returns: milliseconds of elapsed time
204             #
205             sub _elapsed {
206 2     2   152129 my($self,$startsec,$startusec) = @_;
207 2         15 my($endsec,$endusec) = gettimeofday;
208              
209 2 50       15 if ($endusec < $startusec) {
210 0         0 $endusec += 1000000;
211 0         0 $endsec -= 1;
212             }
213 2         11 my $msec = ($endusec - $startusec)/1000;
214 2         8 $msec += ($endsec - $startsec) * 1000;
215 2         84 $self->{ELAPSED} = sprintf("%0.0f",$msec);
216             }
217              
218             #use POSIX qw(EINTR EAGAIN);
219              
220             # function to write to a non-blocking tcp socket
221             # input: socket
222             # buffer pointer
223             # length of message
224             # timeout
225             # returns: number of bytes written
226             #
227             sub _tcp_write {
228 6     6   11925 my($sock,$bp,$len,$timeout) = @_;
229 6         22 my($dummyin,$dummyout,$win,$wout,$ein,$eout,$delta,$wrote);
230 6         19 my $fileno = fileno($sock);
231 6         21 my $written = 0;
232 6         14 my $then = time;
233 6         143 my $buffer = $$bp;
234              
235 6         113 local $SIG{PIPE} = 'IGNORE';
236              
237 6         47 while ($len > 0) {
238 6         31 $dummyin = $win = '';
239 6         38 vec($win,fileno($sock),1) = 1;
240 6         23 $ein = $win;
241 6         69 my $nbfound = select($dummyout=$dummyin,$wout=$win,undef,0.1);
242 6 100       1006681 if ($nbfound > 0) {
    50          
243 5 50       32 if ($wout) { # ready to write
244 5         414 $wrote = syswrite($sock,$buffer,$len,$written);
245 5 100       36 return undef unless defined $wrote; # some error
246 4         9 $written += $wrote;
247 4         27 $len -= $wrote;
248             }
249             # hint
250             # if ( ! defined $wrote ) {
251             # next if $! == EAGAIN; # would block
252             # }
253             }
254             elsif ($delta = ($_ = time) - $then) {
255 1         2 $then = $_;
256 1         5 $timeout -= $delta;
257 1 50       6 last if $timeout < 0;
258             }
259             }
260 5 100       28 if ($timeout < 0) {
261 1         4 $! = 110;
262 1         22 return undef;
263             }
264 4         59 return $written; # return number of bytes written
265             }
266              
267             # function to read a non-blocking tcp socket
268             #
269             # input: socket
270             # buffer pointer
271             # length of message
272             # timeout
273             # returns: number of bytes read
274             # appends to $$bp
275             #
276             sub _tcp_read {
277 8     8   3591 my($sock,$bp,$len,$timeout) = @_;
278 8         29 my($dummyin,$dummyout,$rin,$rout,$ein,$eout,$delta,$rcv);
279 8         18 my $off = 0;
280 8         20 my $nleft = $len;
281 8         27 my $fileno = fileno($sock);
282 8         19 my $then = time;
283 8         22 my $buffer = '';
284              
285 8         118 local $SIG{PIPE} = 'IGNORE';
286              
287 8         92 while (1) {
288 61         325 $dummyin = $rin = '';
289 61         537 vec($rin,$fileno,1) = 1;
290 61         234 $ein = $rin;
291 61         5422840 my $nbfound = select($rout=$rin,$dummyout=$dummyin,undef,0.1);
292 61 100       1002050 if ($nbfound > 0) {
    100          
293 6 50       19 if ($rout) { # ready to read
294 6         195 $rcv = sysread($sock,$buffer,$nleft,$off);
295 6 100       45 if ($rcv) {
    50          
296 5         8 $off += $rcv;
297 5         9 $nleft -= $rcv;
298 5 100       16 unless ($off < $len) {
299 4         138 $$bp .= $buffer;
300 4         56 return $off;
301             }
302             }
303             elsif (defined $rcv ) { # must be zero
304 0         0 $$bp .= $buffer;
305 0         0 return $off;
306             } else {
307 1         34 return undef; # failed on some error
308             }
309             }
310             # hint
311             # if ( ! defined $wrote ) {
312             # next if $! == EAGAIN; # would block
313             # }
314             }
315             elsif ($delta = ($_ = time) - $then) {
316 7         24 $then = $_;
317 7         34 $timeout -= $delta;
318 7 100       49 last if $timeout < 0;
319             }
320             }
321 3 50       18 if ($timeout < 0) {
322 3         17 $! = 110;
323 3         121 return undef;
324             }
325 0         0 return $rcv; # return number of bytes read
326             }
327              
328             # method to do standard tcp DNS queries
329             #
330             # input: pointer to query buffer
331             # netaddr for name server
332             # returns: pointer to query response or undef,
333             # sock if wantarray
334             # else closes sock (also closes on error)
335             #
336             # errno: set on error
337             #
338             # see wrapper sub '_tquery' below
339              
340             sub _tcp_send {
341 1     1   21 my($self,$bp,$srv) = @_;
342 1         1 my $sock;
343 1         81 $sock = connect_NB($self->{PeerPort},$srv);
344 1 50       3323 return () unless $sock; # server error
345            
346 1         4 my $timeout = $self->{Timeout};
347              
348             ##### send TCP query
349              
350 1         9 my $msglen = length($$bp);
351 1         17 my $wbuf = '';
352 1         13 put16(\$wbuf,0,$msglen); # contains the length of message
353 1 50       29 unless (_tcp_write($sock,\$wbuf,2,$timeout)) {
354 0         0 close $sock;
355 0         0 return ();
356             }
357 1 50       4 unless (_tcp_write($sock,$bp,$msglen,$timeout)) {
358 0         0 close $sock;
359 0         0 return ();
360             }
361 1         13 return ($sock,$timeout);
362             }
363              
364             ##### read TCP answer
365              
366             sub _tcp_ans {
367 1     1   10 my($sock,$timeout) = @_;
368 1 50       5 return () unless $sock;
369 1         1 my $rcvd;
370 1         3 my $buf = '';
371 1 50       10 if (_tcp_read($sock,\$buf,2,$timeout)) {
372 1         5 my $msglen = get16(\$buf,0);
373 1         3 $buf = '';
374 1         3 $rcvd = _tcp_read($sock,\$buf,$msglen);
375             }
376 1 50       4 if (defined $rcvd) {
377 1         11 return (\$buf,$sock);
378             } else {
379 0         0 close $sock;
380 0         0 return ();
381             }
382             }
383              
384             sub _tquery {
385 1     1   3778 local $SIG{PIPE} = 'IGNORE';
386 1         48 my($bp,$sock) = _tcp_ans(_tcp_send(@_));
387 1 50       76 return wantarray ? ($bp,$sock) : $bp;
388             }
389              
390             # method to do standard udp DNS queries
391             #
392             # input: pointer to query buffer
393             # netaddr for name server
394             # returns: undef or pointer to response
395             #
396             # errno: set on error ENODATA, ETIMEDOUT, ECONNREFUSED
397             #
398             sub _query {
399 4     4   15526 my ($self,$bp,$srv) = @_;
400 4         69 my $timeout = $self->{Timeout};
401 4         368 my $sock = open_udpNB(); # open nonblocking UDP socket
402 4         813 my $sin = sockaddr_in($self->{PeerPort}, $srv); # port 53 or wherever of this hosts nameserver
403 4         66 my $fileno = fileno($sock);
404              
405 4         96 local $SIG{PIPE} = 'IGNORE';
406              
407 4         9 my ($dummyin,$dummyout,$rout,$delta,$urcv,$response);
408 4         19 my $rin = $dummyin= '';
409 4         10 my $then = time;
410 4         14 my $data = $$bp;
411 4         120 my $len = send $sock, $data, 0, $sin;
412 4         30 while (1) {
413 7         66 vec($rin,$fileno,1) = 1; # set read flags
414 7         578 my $nbfound = select($rout=$rin,$dummyout=$dummyin,undef,0.1); # tick every 100ms
415 7 100       6003002 if ($nbfound > 0) { # found something
    50          
416 3 50       324 if ($rout) { # if it is real
417 3         53 $urcv = recv($sock,$response,NS_PACKETSZ,0);
418 3         149 last;
419             }
420             } elsif ($delta = ($_ = time) - $then) {
421 4         13 $then = $_;
422 4         14 $timeout -= $delta;
423 4 100       24 last if $timeout < 0;
424             }
425             }
426 4         196 close $sock;
427              
428 4 100       45 if ($timeout < 0) {
    100          
    100          
429 1         6 $! = 110; # connection timed out
430 1         27 return undef;
431             }
432             elsif (! defined $urcv) { # undef is an error
433 1         4 $! = 111; # connection refused
434 1         25 return undef;
435             }
436             elsif ( $response) { # if there is data
437 1         18 return \$response;
438             }
439 1         5 $! = 61; # no data available
440 1         23 return undef;
441             }
442              
443             # function to process query header
444             #
445             #
446             # input:# response buffer pointer
447             # fill response object || false
448             # returns: offset
449             # rcode
450             # qdcount
451             # ancount
452             # nscount
453             # arcount
454             #
455             # When a response object is returned, the HEADER section is complete
456             #
457             sub _proc_head($;$) {
458 59     59   6879 my($bp,$obj) = @_;
459 59         402 my($newoff,$id,$qr,$opcode,$aa,$tc,$rd,$ra,$mbz,$ad,$cd,$rcode,
460             $qdcount,$ancount,$nscount,$arcount)
461             = gethead($bp);
462 59 100       202 unless ($obj) {
463 2 100 66     15 return (undef,$rcode,0,0,0,0)
464             unless $ancount && defined $newoff;
465             } else {
466 57         138 @{$obj->{HEADER}}{qw(
  57         499  
467             ID
468             QR
469             AA
470             TC
471             RD
472             RA
473             MBZ
474             AD
475             CD
476             RCODE
477             OPCODE
478             QDCOUNT
479             ANCOUNT
480             NSCOUNT
481             ARCOUNT
482             )} = ($id,$qr,$aa,$tc,$rd,$ra,$mbz,$ad,$cd,$rcode,$opcode,$qdcount,$ancount,$nscount,$arcount);
483             }
484 58         212 return ($newoff,$rcode,$qdcount,$ancount,$nscount,$arcount);
485             }
486              
487             # function to process question response question
488             #
489             # input: get pointer
490             # offset
491             # response buffer pointer
492             # fill response object || false
493             # returns: offset
494             #
495             # When response object is returned, the
496             # HEADER and QUESTION sections are complete
497             #
498             sub _proc_ques($$$;$) {
499 38     38   3315 my($get,$off,$bp,$self) = @_;
500 38         210 ($off,my($name,$type,$class)) = $get->Question($bp,$off);
501              
502 38 100       784 if ($self ) {
503 20         37 my $record = {};
504 20         37 @{$record}{qw( NAME TYPE CLASS )}
  20         77  
505             = ($name, $type, $class);
506 20         35 push @{$self->{QUESTION}}, $record;
  20         63  
507             }
508 38         90 return $off;
509             }
510              
511             # function to process query response data records
512             #
513             # input: get pointer
514             # offset
515             # response buffer pointer
516             # section, one of ANSWER AUTHORITY ADDITIONAL
517             # counter for section
518             # SOA count pointer
519             # fill response object pointer or false
520             # returns: offset
521             # @rdata
522             #
523             sub _proc_data($$$$$;$$) {
524 85     85   2832 my($get,$off,$bp,$sect,$count,$soap,$obj) = @_;
525 85         104 my @retdata;
526 85         216 foreach(0..$count -1) { # slurp all records
527 5534         16819 ($off,my($name,$type,$class,$ttl,$rdlen,@rdata)) = $get->next($bp,$off);
528 5534 100 100     443181 ++$$soap if $soap && $type == T_SOA;
529 5534 100       36866 if ($obj) {
530 5529         8958 my $record = {};
531 5529         8743 @{$record}{qw( NAME TYPE CLASS TTL RDLEN RDATA )}
  5529         22876  
532             = ($name,$type,$class,$ttl,$rdlen,\@rdata);
533 5529         8223 push @{$obj->{$sect}}, $record;
  5529         12212  
534             }
535 5534         16946 push @retdata, @rdata;
536             }
537 85         3926 return ($off,@retdata);
538             }
539              
540             # method to process entire body of response (recursively) for TCP
541             #
542             # input: self
543             # response pointer
544             # pointer to query buffer
545             # get
546             # put
547             # SOA counter pointer and flag that this is an AXFR
548             # returns: response pointer updated
549             #
550             sub _proc_body {
551 21     21   1509 my($self,$resptr,$bp,$get,$put,$soap) = @_;
552              
553 21 50       61 unless ($resptr) { # if server fails
554 0         0 my $flags = getflags($bp);
555 0         0 $flags &= RCODE_MASK;
556 0         0 $flags |= SERVFAIL;
557 0         0 putflags($bp,$flags);
558 0         0 $resptr = $bp;
559             }
560              
561 21         81 my ($off,$rcode,$qdcount,$ancount,$nscount,$arcount)
562             = _proc_head($resptr,$self);
563              
564 21 100       61 if ($rcode) { # some failure code
565 1         2 $ancount = $nscount = $arcount = 0;
566             }
567              
568 21         57 $self->{NRECS} += $qdcount + $ancount + $nscount + $arcount;
569              
570 21 100 100     107 if ($soap && $$soap > 0) { # this is an AXFR and question is processed
571 2 50       7 $off = _proc_ques($get,$off,$resptr) if $qdcount;
572             } else {
573 19 50       237 $off = _proc_ques($get,$off,$resptr,$self) if $qdcount;
574             }
575              
576 21         66 my @section = (qw( ANSWER AUTHORITY ADDITIONAL ));
577 21         38 my @counts = ($ancount,$nscount,$arcount);
578 21         65 foreach (0..$#section) {
579 63         192 ($off) = _proc_data($get,$off,$resptr,$section[$_],$counts[$_],$soap,$self);
580             }
581              
582 21         78 $self->{BYTES} += $off;
583 21         92 return $resptr;
584             }
585              
586             my $native; # sub pointer if native gethostbyname2 is present
587              
588             # for autoloading
589             sub _no_emulation {
590 8   33 8   41 return $native && $_Sock6ok && $_gethostHelp;
591             }
592              
593             # run time configuration for this HOST
594              
595             if (havegethostbyname2()) {
596             $native = \&Socket6::gethostbyname2;
597             }
598             else {
599             $native = undef;
600             }
601              
602             =head1 DESCRIPTION
603              
604             =over 1
605              
606             =item * $dig = new Net::DNS::Dig( # optional parameters
607              
608             Timeout => 15, # default
609             Class => 'IN', # default
610             PeerAddr => host or [name1, name2, ...] default local NS
611             PeerPort => 53, # default
612             Proto => 'UDP', # default
613             Recursion => 1, # default
614             QuesHead => 0, # default, print question header
615             QuesBody => 0, # default, print question body
616             RespHead => 0, # default, print response header
617             RespBody => 0, # default, print response body
618             );
619              
620             =cut
621              
622             # set up name servers for a query
623             #
624             # input: none
625             # returns: nothing
626             #
627             # sets value of _SS hash
628             #
629             sub _get_nameservers {
630 37     37   2989 my $obj = shift; # this section is IPv6 compatible
631 37         107 $obj->{_SS} = {}; # server names => netaddrs
632              
633 37 100       181 if ($obj->{PeerAddr}) {
634 14 100       80 $obj->{PeerAddr} = [$obj->{PeerAddr}]
635             unless ref $obj->{PeerAddr};
636              
637 14         26 foreach(@{$obj->{PeerAddr}}) {
  14         47  
638 22         80 my $naddr = ndd_gethostbyname($_);
639 22 100       6359 $naddr = ndd_gethostbyname2($_,AF_INET6())
640             unless $naddr;
641 22 100       161 $obj->{_SS}->{$_} = $naddr if $naddr;
642             }
643             }
644             else {
645 23         53 $obj->{_SS} = {};
646 23         69 $obj->{PeerAddr} = [];
647 23         57 foreach(@_NS) {
648 45 100       1435 my $ns = (length($_) == 4)
649             ? inet_ntoa($_)
650             : ipv6_ntoa($_);
651 45         4390 push @{$obj->{PeerAddr}}, $ns;
  45         114  
652 45         179 $obj->{_SS}->{$ns} = $_;
653             }
654             }
655             }
656              
657             sub new {
658 31     31 1 32104 my $proto = shift;
659 31   50     208 my $class = ref $proto || $proto || __PACKAGE__;
660              
661             # gather input hash if any
662 31 100 50     185 my $self = ref $_[0] ? $_[0] : {@_} || {};
663              
664             # special server name processing if user supplied NS
665 31         104 _get_nameservers($self);
666              
667 31         262 foreach(keys %Defaults) {
668 155 100       488 $self->{$_} = $Defaults{$_} unless exists $self->{$_};
669             }
670              
671 31 50       236 $self->{Recursion} = &RD
672             if $self->{Recursion};
673              
674             # correct case of Class and Proto
675 31         378 $self->{Class} = uc $self->{Class};
676 31         76 $self->{Proto} = uc $self->{Proto};
677              
678 31 100       114 die "unsupported Class '$self->{Class}'"
679             unless $self->{Class} eq 'IN';
680 30 100 100     132 die "unsupported Proto '$self->{Proto}'"
681             unless $self->{Proto} eq 'UDP'
682             || $self->{Proto} eq 'TCP';
683              
684 29         116 return bless $self, $class;
685             }
686              
687             =item * $dobj = $dig->for(name, type);
688              
689             This method returns a blessed object containing the binary query response object
690              
691             $dobj is $dig filled with the following data
692              
693             input: query name i.e. host name, ip address, etc...
694             type [optional type] A, MX, etc...
695             returns: blessed object of the form...
696              
697             $dobj = {
698             Timeout => input value,
699             Class => input value,
700             PeerAddr => [input value],
701             PeerPort => input value,
702             Proto => input value,
703             Recursion => input value,
704              
705             Errno => posix error number or set to zero
706              
707             ELAPSED => milliseconds, # query time
708             NRECS => number of records,
709             BYTES => number of bytes
710             TEXT => '', # this field is empty
711             SERVER => name, # of query server
712              
713             HEADER => {
714             ID => return value,
715             QR => return value,
716             AA => return value,
717             TC => return value,
718             RD => return value,
719             RA => return value,
720             MBZ => return value,
721             AD => return value,
722             CD => return value,
723             RCODE => return value,
724             OPCODE => return value,
725             QDCOUNT => return value, # question
726             ANCOUNT => return value, # answer
727             NSCOUNT => return value, # authority
728             ARCOUNT => return value, # additional
729             },
730             QUESTION => [
731             {
732             NAME => return name,
733             TYPE => return type,
734             CLASS => return class,
735             },
736             ],
737             ANSWER => [ # for each answer record
738             {
739             NAME => return name,
740             TYPE => return type,
741             CLASS => return class,
742             TTL => return ttl,
743             RDLEN => $n, # octets
744             RDATA => @rdata, # data fields
745             },
746             ],
747             AUTHORITY => [ # for each authority record
748             {
749             NAME => return name,
750             TYPE => return type,
751             CLASS => return class,
752             TTL => return ttl,
753             RDLEN => $n, # octets
754             RDATA => @rdata, # data fields
755             },
756             ],
757             ADDITIONAL => [ # for each glue record
758             {
759             NAME => return name,
760             TYPE => return type,
761             CLASS => return class,
762             TTL => return ttl,
763             RDLEN => $n, # octets
764             RDATA => @rdata, # data fields
765             },
766             ],
767             };
768              
769             =cut
770              
771             # deprecated
772             my %allowed = (
773             A => T_A,
774             AAAA => T_AAAA,
775             MX => T_MX,
776             NS => T_NS,
777             CNAME => T_CNAME,
778             SOA => T_SOA,
779             AXFR => T_AXFR,
780             ANY => T_ANY,
781             TXT => T_TXT,
782             PTR => T_PTR,
783             );
784              
785             my %not_allowed = (
786             IXFR => T_IXFR,
787             );
788              
789             sub for($$$) { # NOT LOADABLE
790 3     3 1 553572 my($self,$name,$Type) = @_;
791 3 50       10 $Type = 'A' unless $Type;
792              
793             # check arguments
794 3 50       13 die "you must provide name to look up\n" unless $name;
795              
796 3         9 $Type = uc $Type;
797 3         9 my $ttype = 'T_'. $Type;
798              
799 3 50 33     29 if ( $not_allowed{$Type} || ! exists $Net::DNS::Codes::{$ttype}) {
800 0         0 die "unsupported type '$Type'\n";
801 24     24   275 $ttype = do { no strict; &$ttype; };
  24         46  
  24         1326  
  0         0  
  0         0  
802             } else {
803 24     24   140 $ttype = do { no strict; &$ttype; };
  24         54  
  24         43629  
  3         5  
  3         21  
804             }
805              
806             # deprecated by above in v0.04
807             # my $ttype = $allowed{$Type};
808              
809 3 50       38 die "unsupported class '$self->{Class}'\n"
810             unless $self->{Class} eq 'IN';
811              
812 3         18 my @time = gettimeofday;
813              
814 3         22 my ($get,$put,$parse) = new Net::DNS::ToolKit::RR;
815              
816 3         69 my $buffer = '';
817 3         4 my $resptr;
818            
819 3         16 my $off = newhead(\$buffer,
820             $ID, # pid is always unique
821             BITS_QUERY | $self->{Recursion}, # query, recursion desired
822             1,0,0,0, # one question
823             );
824              
825 3         599 $off = $put->Question(\$buffer,$off,$name,$ttype,C_IN);
826              
827 3 100       126 my $isAXFR = $Type eq 'AXFR'
828             ? 1 : 0;
829 3 100 66     22 my $isTCP = ($isAXFR || $self->{Proto} eq 'TCP')
830             ? 1 : 0;
831 3         12 NameServer:
832 3         6 foreach my $h (@{$self->{PeerAddr}}) { # preserve order of name servers
833 3         10 $self->{SERVER} = $h;
834 3 50       11 next unless exists $self->{_SS}->{$h}; # punt if _get_nameservers could not find host
835 3         8 my $ns = $self->{_SS}->{$h};
836 3 100       10 if ($isTCP) {
837 2         5 my $soaCount = 0;
838 2 50       8 my $soap = $isAXFR ? \$soaCount : 0; # set flag for AXFR
839 2         4 my $sock;
840 2 50       5 if ($soap) {
841 2         12 ($resptr,$sock) = $self->_tquery(\$buffer,$ns);
842             } else {
843 0         0 $resptr = $self->_tquery(\$buffer,$ns);
844             }
845 2 50       19 unless ($resptr) {
846 0 0       0 close $sock if $sock;
847 0         0 next NameServer;
848             }
849 2         8 while ($soaCount < 2) {
850 4 50       13 $soaCount = 2 unless $isAXFR;
851 4         21 $resptr = $self->_proc_body($resptr,\$buffer,$get,$put,$soap);
852 4 50       55 unless ($resptr) {
853 0 0       0 close $sock if $sock;
854 0         0 next NameServer;
855             }
856             } continue {
857 4 100       21 if ($soaCount < 2) {
858 2         20 ($resptr,$sock) = _tcp_ans($sock,$self->{Timeout});
859 2 50       84 unless ($resptr) {
860 0 0       0 close $sock if $sock;
861 0         0 next NameServer;
862             }
863             }
864             }
865 2 50       7 close $sock if $sock;
866             } else { # is UDP
867 1         7 $resptr = $self->_query(\$buffer,$ns);
868 1 50       9 next NameServer unless $resptr;
869 1         6 $resptr = $self->_proc_body($resptr,\$buffer,$get,$put);
870             }
871 3 50       14 last if $resptr;
872             }
873 3 50       11 if ($resptr) {
874 3         11 $! = 0; # errno unconditionally set to zero
875             } else { # server failed
876 0         0 $self->_proc_body($resptr,\$buffer,$get,$put);
877             }
878 3         13 $self->{Errno} = $!;
879 3 50       11 print_head(\$buffer) if $self->{QuesHead};
880 3 50       13 print_buf(\$buffer) if $self->{QuesBody};
881 3 50       9 print_head($resptr) if $self->{RespHead};
882 3 50       10 print_buf($resptr) if $self->{RespBody};
883 3         20 $self->_elapsed(@time);
884 3         34 return $self;
885             }
886              
887             1;
888             #__END__
889              
890             =item * $tobj = $dig->to_text();
891              
892             This method returns a blessed object of the same form as B above with the
893             following fields converted to text:
894              
895             RCODE
896             OPCODE
897             QUESTION
898             ANSWER
899             AUTHORITY
900             ADDITIONAL
901              
902             The TEXT value scalar is filled with a formatted text string like that returned from *NIX C
903              
904             =cut
905              
906             sub to_text {
907 19     19 1 3716 my $self = shift;
908 19         223 my $d = Data::Dumper->new([$self],['tobj']);
909 19         1017 $d->Purity(1)->Deepcopy(1)->Indent(1);
910 19         438 my $tobj;
911 19         84 eval $d->Dump;
912              
913 19 100 66     2283 my $isAXFR = (exists $self->{QUESTION}->[0]->{TYPE} &&
914             $self->{QUESTION}->[0]->{TYPE} == &T_AXFR)
915             ? 1 : 0;
916              
917 19 50       202 my $tcp = $self->{Proto} eq 'TCP'
918             ? '+tcp ' : '';
919              
920             # HEADER
921 19         41 my $head = $tobj->{HEADER};
922 19   33     99 $head->{OPCODE} = OpcodeTxt->{$head->{OPCODE}}
923             || $head->{OPCODE}; # numeric if unknown
924              
925 19         190 my $Rcode = $head->{RCODE}; # true if not NOERROR
926 19   33     75 $head->{RCODE} = RcodeTxt->{$Rcode} || $Rcode; # numeric if unknown
927              
928 19         139 my $flags = ' ';
929 19 50       78 $flags .= 'qr ' if $head->{QR};
930 19 100       60 $flags .= 'aa ' if $head->{AA};
931 19 50       54 $flags .= 'tc ' if $head->{TC};
932 19 100       63 $flags .= 'rd ' if $head->{RD};
933 19 100       65 $flags .= 'ra ' if $head->{RA};
934 19 50       62 $flags .= 'z ' if $head->{MBZ};
935 19 50       56 $flags .= 'ad ' if $head->{AD};
936 19 50       56 $flags .= 'cd ' if $head->{CD};
937 19         46 chop $flags;
938              
939             # convert each section as appropriate
940              
941 19         36 my $text = '';
942 19         174 my($get,$put,$parse) = new Net::DNS::ToolKit::RR;
943              
944 19         512 foreach my $sect (qw( QUESTION ANSWER AUTHORITY ADDITIONAL )) {
945              
946 76 100       220 next unless exists $tobj->{$sect};
947             # append Section Text
948 67 100 66     322 $text .= qq|
949             ;; $sect SECTION:
950             | unless $isAXFR || $Rcode;
951              
952 67         82 my($name,$type,$class,$ttl,$rdlen,@rdata);
953              
954 67         90 my @data = @{$tobj->{$sect}};
  67         597  
955 67         116 foreach my $resp (@data) {
956 5550 100       17242 if ($sect eq 'QUESTION') {
    50          
957 20         104 @{$resp}{qw( NAME TYPE CLASS )} = $parse->Question(@{$resp}{qw( NAME TYPE CLASS )});
  20         295  
  20         127  
958 20         1229 $resp->{TYPE} = strip($resp->{TYPE});
959 20         4730 $resp->{CLASS} = strip($resp->{CLASS});
960 20         153 ($name,$type,$class,) = @{$resp}{qw( NAME TYPE CLASS )};
  20         77  
961 20 100       144 $text .= qq|;$name\t\t$class\t$type\n|
962             unless $isAXFR;
963             } elsif (! $Rcode) { # no error
964 5530         7267 ($name,$type,$class,$ttl,$rdlen,@rdata) = $parse->RR(@{$resp}{qw( NAME TYPE CLASS TTL RDLEN )}, @{$resp->{RDATA}});
  5530         13578  
  5530         24713  
965 5530         705168 $type = strip($type);
966 5530         200700 $class = strip($class);
967 5530         42788 @{$resp}{qw( NAME TYPE CLASS TTL RDLEN RDATA )} = ($name,$type,$class,$ttl,$rdlen,[@rdata]);
  5530         28667  
968 5530         19993 $text .= qq|$name\t$ttl\t$class\t$type\t|;
969 5530         10401 foreach (@rdata) {
970 5702         14347 $text .= ' '. $_;
971             }
972 5530         11411 $text .= "\n";
973             }
974             }
975             }
976              
977 19         55 my $sname = $self->{SERVER};
978 19         35 my $server = '';
979              
980 19 100 66     150 if (exists $self->{_SS}->{$sname} && ! $Rcode) { # if there really was a lookup
981 18         49 $server = $self->{_SS}->{$sname}; # address of server
982 18 50       557 $server = (length($server) == 4)
983             ? inet_ntoa($server)
984             : ipv6_ntoa($server);
985             }
986              
987 19 100 66     282 $text .= "\n" unless $isAXFR || $Rcode; # extra carrige return except for Rcode failure and AXFR
988 19 100       235 $text .= q
989             |;; Query time: |. $self->{ELAPSED} .qq| ms
990             ;; SERVER: ${server}# |. $self->{PeerPort} .qq|($sname)
991             ;; WHEN: | . scalar localtime() . q|
992             ;; MSG SIZE rcvd: |. $self->{BYTES} .q| -- XFR size: |. $self->{NRECS} .q| records
993             | unless $Rcode;
994              
995             # build header
996 19         476 $head = q|
997             ; <<>> |. __PACKAGE__ .' '. sprintf("%0.2f", $VERSION) .qq| <<>> ${tcp}-t |.
998             (lc $tobj->{QUESTION}->[0]->{TYPE}) .' '.
999             $tobj->{QUESTION}->[0]->{NAME} .q|
1000             ;;
1001             |;
1002              
1003 19         89 my($opcode,$rcode,$id,$qdcount,$ancount,$nscount,$arcount)
1004 19         81 = @{$tobj->{HEADER}}{qw( OPCODE RCODE ID QDCOUNT ANCOUNT NSCOUNT ARCOUNT )};
1005              
1006 19 100       65 $head .= "; Transfer failed.\n"
1007             if $Rcode;
1008              
1009 19 100       144 $head .= qq
1010             |;; Got answer.
1011             ;; ->>HEADER<<- opcode: $opcode, status: $rcode, id: $id
1012             ;; flags:$flags; QUERY: $qdcount, ANSWER: $ancount, AUTHORITY: $nscount, ADDITIONAL: $arcount
1013             | unless $isAXFR;
1014              
1015              
1016 19         243 $tobj->{TEXT} = $head . $text;
1017              
1018 19         282 return $tobj;
1019             }
1020              
1021             =item * $array_ptr = $dig->data($section);
1022              
1023             =item * $array_ptr = $tobj->data($section);
1024              
1025             or
1026              
1027             =item * @array = $dig->data($section);
1028              
1029             This method returns a pointer or array in binary or text form from
1030             from a $dig or $tobj object pointer, respectively.
1031              
1032             In scalar context returns a pointer to an array of query response hash's
1033             representing each record returned.
1034              
1035             In array context returns an array of hash's for each query response record.
1036              
1037             input: section name [optional, default ANSWER]
1038              
1039             returns: array pr pointer to array of one or more hash's
1040             ->[ {}, {}, {}, ...];
1041              
1042             where $section if one of:
1043             QUESTION, ANSWER, AUTHORITY, ADDITIONAL
1044              
1045             Each answer hash is of the form described above for:
1046              
1047             $dig->for();
1048              
1049             =cut
1050              
1051             sub data {
1052 22     22 1 1016 my($self,$sect) = @_;
1053 22 100       49 $sect = 'ANSWER' unless $sect;
1054 22 100 100     123 unless ( $sect eq 'ANSWER' ||
      100        
      100        
1055             $sect eq 'AUTHORITY' ||
1056             $sect eq 'ADDITIONAL' ||
1057             $sect eq 'QUESTION' ) {
1058             return wantarray
1059 2 100       12 ? () : [];
1060             }
1061             return wantarray
1062 20 100       58 ? @{$self->{$sect}}
  10         41  
1063             : $self->{$sect};
1064             }
1065              
1066             =item * $array_ptr = $dig->records($type,$sect);
1067              
1068             =item * $array_ptr = $tobj->records($type,$sect);
1069              
1070             or
1071              
1072             =item * @array = $dig->records($type,$sect);
1073              
1074             This method returns a pointer to or an array of RDATA arrays for each query response
1075             record.
1076              
1077             input: $type [optional record type, A, NS, etc...]
1078             not case sensitive, defaults to TYPE of original query
1079             $sect [optional section, defaults to ANSWER]
1080              
1081             returns: array or pointer to array of one or more rdata arrays
1082             ->[ [], [], [], ...] ];
1083              
1084             where $sect is one of ANSWER, AUTHORITY, ADDITIONAL
1085              
1086             Each answer array is of the form described above for RDATA in:
1087              
1088             $dig->for();
1089              
1090             =cut
1091              
1092             sub records {
1093 388     388 1 26253 my($self,$type,$sect) = @_;
1094 388 100       1945 $sect = 'ANSWER' unless $sect;
1095 388 100 100     2391 unless ( $sect eq 'ANSWER' ||
      100        
1096             $sect eq 'AUTHORITY' ||
1097             $sect eq 'ADDITIONAL' ) {
1098             return wantarray
1099 4 100       22 ? () : [];
1100             }
1101 384 100       3033 my $mode = $self->{QUESTION}->[0]->{CLASS} =~ /\d+/
1102             ? 0 # binary
1103             : 1; # text
1104              
1105 384 100       1021 unless ($type) {
1106 64         111 $type = $self->{QUESTION}->[0]->{TYPE};
1107 64 100       186 $type = TypeTxt->{$type} unless $mode; # convert to text if binary
1108 64         254 $type =~ s/T_//;
1109             }
1110              
1111 384   100     833 $type = eval {
1112 24     24   168 no strict;
  24         52  
  24         49869  
1113             &{'T_'. uc $type}; # convert type to binary
1114             } || 0;
1115              
1116 384 100       2099 unless ($type) {
1117             return wantarray
1118 64 100       380 ? () : [];
1119             }
1120              
1121 320 100       745 if ($mode) {
1122 160         372 ($type = TypeTxt->{$type}) =~ s/T_//;
1123             }
1124              
1125 320         1132 my @records;
1126 320         453 foreach (@{$self->{$sect}}) {
  320         8608  
1127 1840 100 100     11648 if (
      100        
      66        
1128             ($mode && $_->{TYPE} eq $type) ||
1129             (!$mode && $_->{TYPE} == $type) ) {
1130 320         709 push @records, $_->{RDATA};
1131             }
1132             }
1133             return wantarray
1134             ? @records
1135 320 100       1281 : \@records;
1136             }
1137              
1138             =item * $rdata = $dig->rdata($type,$sect);
1139              
1140             =item * $rdata = $tobj->rdata($type,$sect;
1141              
1142             or
1143              
1144             =item * @rdata = $dig->rdata($type,$sect);
1145              
1146             This method returns the first element or a list of rdata items. See the
1147             appropriate RFC's for types such as MX which have two elements per record.
1148              
1149             i.e. MX rdata => (priority, name)
1150              
1151             multiple records would be returned as a list of
1152             pri, name, pri, name, etc...
1153              
1154             input: $type [optional record type, A, NS, etc...]
1155             not case sensitive, defaults to TYPE of original query
1156             $sect [optional section, defaults to ANSWER]
1157              
1158             returns: a list or its first element
1159              
1160             =cut
1161              
1162             sub rdata {
1163 194     194 1 66192 my $records = &records;
1164 194 100       941 return $records->[0]->[0] unless wantarray;
1165 97         98 my @list;
1166 97         100 foreach my $rdata (@{$records}) {
  97         211  
1167 80         75 push @list, @{$rdata};
  80         206  
1168             }
1169 97         355 return @list;
1170             }
1171              
1172             =item * $rv = $dig->rcode(true/false);
1173              
1174             This method returns the query response code in numeric form if argument is false and the text response code if the argument is true.
1175              
1176             NOERROR => 0,
1177             FORMERR => 1,
1178             SERVFAIL => 2,
1179             NXDOMAIN => 3,
1180             NOTIMP => 4,
1181             REFUSED => 5,
1182             YXDOMAIN => 6,
1183             YXRRSET => 7,
1184             NXRRSET => 8,
1185             NOTAUTH => 9,
1186             NOTZONE => 10,
1187              
1188             =cut
1189              
1190             sub rcode {
1191 22 100   22 1 2915 return $_[1]
1192             ? RcodeTxt->{$_[0]->{HEADER}->{RCODE}}
1193             : $_[0]->{HEADER}->{RCODE};
1194             }
1195              
1196             =item * $text = $dig->sprintf;
1197              
1198             This method return the B query response text from either the dig
1199             object pointer or a dig text object pointer. C is called automatically
1200             if required.
1201              
1202             =cut
1203              
1204             sub sprintf : method {
1205 4     4 1 1164 my $self = shift;
1206              
1207 4 100       25 my $tobj = ($self->{QUESTION}->[0]->{CLASS} =~ /\D/) # are we text mode?
1208             ? $self
1209             : $self->to_text;
1210              
1211 4         48 return $tobj->{TEXT};
1212             }
1213              
1214             =item * $netaddr = ndd_gethostbyname($name);
1215              
1216             or
1217              
1218             =item * @array = ndd_gethostbyname($name);
1219              
1220             ($name,$aliases,$aftype,$len,@addrs)
1221             = ndd_gethostbyname($name);
1222              
1223             =item * $netaddr = ndd_gethostbyname2($name,$AF_family);
1224              
1225             or
1226              
1227             =item * @array = ndd_gethostbyname2
1228              
1229             ($name,$aliases,$aftype,$len,@addrs)
1230             = ndd_gethostbyname2($name,$AF_family);
1231              
1232             =item * $name = ndd_gethostbyaddr($iaddr,$AF_family);
1233              
1234             or
1235              
1236             =item * @array = ndd_gethostbyaddr
1237              
1238             ($name,$aliases,$aftype,$len,@addrs)
1239             = ndd_gethostbyaddr($iaddr,$AF_family);
1240              
1241             =back
1242              
1243             These functions use or emulate the underlying system calls of the same name,
1244             enhancing the capability of Perl to support IPv6 where needed. If the function
1245             is present in Perl and/or Socket6, the Perl function is called directly.
1246              
1247             If C is loaded with the force emulation
1248             tag, the Net::DNS;Dig's version of the function is always used. This is
1249             useful when the underlying system C and/or C
1250             functions are present but broken.
1251              
1252             NOTE: the emulations do not check NIS or system C file.
1253              
1254             Function Net::DNS::Dig (ndd_) gethostbyname
1255              
1256             input: $name text string or ip address
1257             [optional] $timeout seconds
1258              
1259             B emulates or uses Perl's gethostbyname system call.
1260              
1261             Function Net::DNS::Dig (ndd_) gethostbyname2
1262              
1263             input: $name text string or ip address
1264             [optional] $AF_family address family type
1265             [optional] $timeout seconds for the emulation
1266              
1267             B provides a fully functional gethostbyname2
1268             implementation that will work on systems that do not support IPv6 or have
1269             broken IPv6 socket libraries.
1270              
1271             If $AF_family is false, B will examine the supplied
1272             $name to try and determine the appropriate AF_family if the name is an
1273             IPv4 address of the form d+.d+.d+.d+ or and IPv6 address in one of the
1274             RFC1884 formats it will do the right thing and return a text version of
1275             the address. Otherwise, AF_INET is assumed by default
1276             and a DNS lookup will be performed.
1277              
1278             Function Net::DNS::Dig (ndd_) gethostbyaddr
1279              
1280             input: $naddr network address
1281             [optional] $AF_family address family type
1282             [optional] $timeout seconds for the emulation
1283              
1284             If $AF_family is false, B will examine the
1285             supplied $naddr and determine the appropriate AF_family.
1286              
1287             Common return, all functions
1288              
1289             $name text name
1290             $aliases space separated list of text names
1291             $atype address type - AF_family constant
1292             $len length of the address 4 or 16
1293             @addrs array of naddrs in network form
1294              
1295             =over 1
1296              
1297             =item * $constant = AF_INET;
1298              
1299             =item * $constant = AF_INET6;
1300              
1301             =back
1302              
1303             These two functions return the constant value for the AF_families, respectively,
1304             of the underlying operating system.
1305              
1306             =cut
1307              
1308             sub my_name2_gethostby { # LOADABLE
1309 8     8 0 17 my($self,$name,$af) = @_;
1310              
1311 8 50       25 return $native->($name,$af)
1312             if &_no_emulation; # sub LOADABLE
1313              
1314             # else use the emulator
1315              
1316 8         39 $self->{AddrType} = $af;
1317              
1318 8 100       52 my $type = ($af == &AF_INET)
1319             ? &T_A
1320             : &T_AAAA;
1321 8         56 return $self->_get_host($name,$type);
1322             }
1323              
1324             sub my_addr_gethostby { # LOADABLE
1325 6     6 0 14 my($self,$name,$af) = @_;
1326              
1327 6         17 $self->{NetAddr} = $name;
1328 6         13 $self->{AddrType} = $af;
1329              
1330 6 50       21 if ($af == &AF_INET) {
1331 0 0       0 return gethostbyaddr($name,$af) if &_hostHelp; # sub LOADABLE
1332 0         0 $name = revIP(inet_ntoa($name)) .'.in-addr.arpa'; # else use the emulation
1333             } else {
1334 6         99 $name = join('.',
1335             reverse split(//,
1336             sprintf ("%04x%04x%04x%04x%04x%04x%04x%04x",unpack("n8",$name))
1337             )
1338             ) .'.ip6.arpa';
1339             }
1340 6         39 return $self->_get_host($name,&T_PTR);
1341             }
1342              
1343             # sub ndd_gethostbyname { # LOADABLE
1344             sub al_name_gethostby {
1345 17     17 0 6283 my($name,$timeout) = @_;
1346 17 50       91 $timeout = &_timeout unless $timeout; # sub LOADABLE
1347              
1348 17         28 my @rv;
1349              
1350 17         56 my($addrtype,$length,@addrs) = _check4addr($name);
1351              
1352 17 100       49 if ($addrtype) {
    50          
1353 15         52 @rv = ($name,'',$addrtype,$length,@addrs);
1354             }
1355             elsif (&_hostHelp) { # using core routines # sub LOADABLE
1356 0         0 @rv = gethostbyname($name);
1357             }
1358             else { # use emulation
1359 2         7 my $self = bless { Timeout => $timeout };
1360 2         16 @rv = my_name2_gethostby($self,$name,&AF_INET);
1361             }
1362             return wantarray
1363             ? @rv
1364 17 100       111 : $rv[4]; # first address slot
1365             }
1366              
1367             # sub ndd_gethostbyname2 { # LOADABLE
1368             sub al_name2_gethostby {
1369 14     14 0 2682 my($name,$af,$timeout) = @_;
1370              
1371 14 100       48 $af = 0 unless $af;
1372 14 50       48 $timeout = &_timeout unless $timeout; # sub LOADABLE
1373              
1374 14         60 my @rv;
1375 14         33 my($addrtype,$length,@addrs) = _check4addr($name,$af);
1376              
1377             #
1378             # if $addrtype is FALSE, one of the following apply
1379             #
1380             # combinations action
1381             #
1382             # 1 gethostHelp af = AF_INET native gethostbyname
1383             # 2 gethostHelp af = AF_INET6 available gethostbyname2
1384             # 3 gethostHelp af = default AF_INET native gethostbyname
1385             # 4 ! gethostHelp af = AF_INET emulator
1386             # 5 ! gethostHelp af = AF_INET6 emulator
1387             # 6 ! gethostHelp af = default AF_INET6 emulator
1388             #
1389             # 1 and 3 must be tested, 2 tests itself and will do the righ thing
1390             #
1391 14 100 33     46 if ($addrtype) { # have the answer
    50          
1392 8         23 @rv = ($name,'',$addrtype,$length,@addrs); # was an IP address
1393             }
1394             # either ! $af or $af = AF_INET
1395             elsif ( &_hostHelp && $af != &AF_INET6) { # user said AF_INET or is default AF_INET # sub LOADABLE
1396 0         0 @rv = gethostbyname($name); # default to IPv4 & core functions
1397             }
1398             else {
1399 6         60 my $self = bless { Timeout => $timeout };
1400 6         25 @rv = my_name2_gethostby($self,$name,$af); # this function checks $_gethostHELP
1401             }
1402             return wantarray
1403             ? @rv
1404 14 100       96 : $rv[4];
1405             }
1406              
1407             # sub ndd_gethostbyaddr { # LOADABLE
1408             sub al_addr_gethostby {
1409 6     6 0 44 my($name,$af,$timeout) = @_;
1410              
1411 6 50       27 $timeout = &_timeout unless $timeout; # sub LOADABLE
1412 6         11 my @rv;
1413              
1414 6 100       20 unless ($af) {
1415 4 50       20 if ( length($name) == 4 ) { # looks like IPv4
    50          
1416 0         0 $af = &AF_INET;
1417             }
1418             elsif ( length($name) == 16) { # it is possible to screw this up
1419 4         13 $af = &AF_INET6;
1420             }
1421             else { # unknown naddr length, probably a real name
1422 0         0 goto &ndd_gethostbyname; # emulate behavior of perl gethostbyaddr, assume IPv4
1423             }
1424             }
1425              
1426 6 50       27 if ($af) {
1427 6 50 33     41 $af = 0 unless $af == &AF_INET || $af == &AF_INET6;
1428             }
1429              
1430 6 50 33     95 if ((length($name) == 4 && $af == &AF_INET) ||
    0 33        
      33        
      0        
1431             (length($name) == 16 && $af == &AF_INET6) ) {
1432 6         46 my $self = { Timeout => $timeout };
1433 6         10 bless $self;
1434 6         23 @rv = my_addr_gethostby($self,$name,$af);
1435             }
1436             elsif ($af == &AF_INET || $af == &AF_INET6) { # valid address family, funny naddr length
1437 0         0 my $self = { Timeout => $timeout };
1438 0         0 bless $self;
1439 0         0 @rv = my_name2_gethostby($self,$name,$af); # probably a real name, emulate behavior of gethostbyaddr
1440             }
1441             # else unknown, return empty
1442             return wantarray
1443             ? @rv
1444 6 50       50 : $rv[4];
1445             }
1446              
1447             # function to checkfor an IPv4 or IPv6 address and return value + family # LOADABLE used only by gethostby
1448             #
1449             # input: hostname or IP address
1450             # address family [optional]
1451             #
1452             # returns: array with addressfamily = 0 if not an address or bad family address match
1453             # else ($addressfamily, $length, $netaddr)
1454             #
1455              
1456             sub _check4addr {
1457 31     31   58 my($name,$af) = @_;
1458              
1459 31         46 my($len,$netaddr);
1460              
1461 31 100       81 if ($af) { # address family specified
1462 8 100 100     102 if ( $af == &AF_INET && $name !~/[^0-9\.]/ &&
    100 66        
      100        
      66        
1463             ($netaddr = inet_aton($name)) ) {
1464 2         150 $len = 4;
1465             }
1466             elsif ( $af == &AF_INET6 && $name !~ /[^0-9a-fA-F\:]/ &&
1467             ($netaddr = ipv6_aton($name)) ) {
1468 2         313 $len = 16;
1469             }
1470             else {
1471 4         56 return (0); # not an address or family does not match address
1472             }
1473             }
1474             else { # family is unknown
1475 23 100 66     403 if ( $name !~/[^0-9\.]/ && ($netaddr = inet_aton($name)) ) {
    100 66        
1476 15         957 $af = &AF_INET;
1477 15         30 $len = 4;
1478             }
1479             elsif ( $name !~ /[^0-9a-fA-F\:]/ && ($netaddr = ipv6_aton($name)) ) {
1480 4         327 $af = &AF_INET6;
1481 4         19 $len = 16;
1482             }
1483             else {
1484 4         13 return (0); # not an address
1485             }
1486             }
1487 23         104 return ($af, $len, $netaddr);
1488              
1489 0         0 $netaddr = inet_aton($name);
1490              
1491 0 0       0 if ( $netaddr ) { # is IPv4
    0          
1492 0         0 return (&AF_INET, 4, $netaddr);
1493             }
1494             elsif ( $netaddr = ipv6_aton($name) ) { # is IPv4
1495 0         0 return (&AF_INET6, 16, $netaddr);
1496             }
1497 0         0 return ();
1498             }
1499              
1500             #
1501             # function to execute gethostby
1502             #
1503             # input: name, type
1504             # where allowed types are T_PTR, T_A, T_AAAA
1505             # and PTR names are in appropriate ascii form
1506             #
1507             # returns: ($name,$aliases,$addrtype,$length,@addrs)
1508             #
1509             sub _get_host { # LOADABLE
1510 24     24   3492 my($self,$n,$t) = @_;
1511             #
1512             # HOST_NOT_FOUND 1 /* Authoritative Answer Host not found. */
1513             # TRY_AGAIN 2 /* Non-Authoritative Host not found,
1514             # or SERVERFAIL. */
1515             # NO_RECOVERY 3 /* Non recoverable errors, FORMERR, REFUSED,
1516             # NOTIMP. */
1517             # NO_DATA 4 /* Valid name, no data record of requested
1518             # type. */
1519              
1520             # find a name server
1521 24         34 my($buffer,$rp);
1522 24         52 foreach (&_get_NS) { # sub LOADABLE
1523 47         125 $buffer = question($n,$t);
1524 47 100       242 last if $rp = $self->_query(\$buffer,$_);
1525 24         173 $! = 3; # NO_RECOVERY - Non recoverable errors
1526             }
1527 24 100       165 return () unless $rp;
1528 23         73 my($off,$rcode,$qdcount,$ancount,$nscount,$arcount) = _proc_head($rp,$self);
1529 23 100       58 unless ($ancount) {
1530 7 100       28 unless ($self->{HEADER}->{RCODE} ) { # no answers, no data
    100          
    100          
1531 3 100       10 $! = $self->{HEADER}->{AA}
1532             ? 1 : 4; # HOST_NOT_FOUND - Authoritative Answer Host not found (wouldn't this be NXDOMAIN?)
1533             # NO_DATA - Valid name, no data record of requested
1534             }
1535             elsif ( $self->{HEADER}->{RCODE} == &NXDOMAIN) {
1536 2 100       13 $! = $self->{HEADER}->{AA}
1537             ? 1 : 2; # HOST_NOT_FOUND - Authoritative Answer Host not found
1538             # TRY_AGAIN - Non-Authoritative Host not found
1539             }
1540             elsif ($self->{HEADER}->{RCODE} == &SERVFAIL) {
1541 1         12 $! = 2; # TRY_AGAIN - Non-Authoritative Host not found or SERVER FAIL
1542             }
1543             else {
1544 1         8 $! = 3; # NO_RECOVERY - Non recoverable errors
1545             }
1546 7         21 return ();
1547             }
1548 16         25 $! = 0; # no error
1549 16         126 my ($get,$put,$parse) = new Net::DNS::ToolKit::RR;
1550 16         357 $off = _proc_ques($get,$off,$rp); # skip over question
1551              
1552 16         23 my @retdata;
1553              
1554 16         50 ($off,@retdata) = _proc_data($get,$off,$rp,'ANSWER',$ancount,undef,$self);
1555              
1556 16         27 my($name,$aliases,$addrtype,$length,@addrs);
1557 16         21 $aliases = '';
1558 16         28 $addrtype = $self->{AddrType};
1559              
1560 16         22 my @data = @{$self->{ANSWER}};
  16         40  
1561              
1562 16         32 foreach my $resp (@data) {
1563 16         24 my ($nam,$typ) = @{$resp}{qw( NAME TYPE )};
  16         37  
1564 16         22 my @rdata = @{$resp->{RDATA}};
  16         37  
1565 16 100 33     50 if ($typ == $t) { # if this is the requested type
    50          
    50          
1566 14 50 66     56 if ($name) { # and name present
    100          
    50          
1567 0         0 next; # skip
1568             }
1569             elsif ( $typ == &T_PTR ) {
1570 7         32 $name = $rdata[0];
1571 7 100       39 $length = ($self->{AddrType} == &AF_INET)
1572             ? 4 : 16;
1573 7         35 push @addrs, $self->{NetAddr}; # recover calling network address
1574             }
1575             elsif ( $typ == &T_A || $typ == &T_AAAA) {
1576 7         122 $name = $nam;
1577 7         16 $length = length($rdata[0]);
1578 7         41 push @addrs, @rdata;
1579             }
1580             # else type is unknown and we don't need it
1581             }
1582             elsif ( $typ == &T_CNAME ) {
1583 0         0 $aliases .= $nam .' ';
1584             }
1585             elsif ( $typ == &T_A || $typ == &T_AAAA) {
1586 2         27 push @addrs, @rdata;
1587             }
1588             # else type is unknown and we don't need it
1589             }
1590 16 50       49 chop $aliases if $aliases; # remove trailing space
1591 16         205 return ($name,$aliases,$addrtype,$length,@addrs);
1592             }
1593              
1594             =head1 EXAMPLES
1595              
1596             Example usage of Net::DNS::Dig
1597              
1598             ########### example 1 retrieving netaddrs
1599              
1600             use NetAddr::IP::Util qw(inet_ntoa);
1601             use Net::DNS::Dig;
1602              
1603             $name = 'gmail.com';
1604              
1605             # return one of the gmail 'A' records
1606              
1607             $netaddr = Net::DNS::Dig->new()->for($name)->rdata();
1608              
1609             print inet_ntoa($netaddr),"\n";
1610              
1611             ########### example 2 retrieve many netaddrs
1612              
1613             use NetAddr::IP::Util qw(inet_ntoa);
1614             use Net::DNS::Dig;
1615              
1616             $name = 'gmail.com';
1617              
1618             # return all of the gmail 'A' records
1619              
1620             @netaddrs = Net::DNS::Dig->new()->for( $name )->rdata();
1621              
1622             foreach ( @netaddrs ) {
1623             print inet_ntoa( $_ ),"\n";
1624             }
1625              
1626             ########### example 3 retrieve MX host for email
1627              
1628             use Net::DNS::Dig;
1629              
1630             $email = 'john.doe@gmail.com';
1631              
1632             ( $name = $email ) =~ s/.+\@(.+)/$1/;
1633              
1634             # return all of the gmail 'MX' records
1635             # records return PRIORITY, HOST, ...
1636             # hostnames are unique
1637              
1638             my %mx_info = reverse Net::DNS::Dig->new()->for( $name,'MX' )->rdata();
1639              
1640             my @host_by_priority;
1641              
1642             foreach ( sort {
1643             $mx_info{$a} <=> $mx_info{$b}
1644             } keys %mx_info ) {
1645             push @host_by_priority, $_;
1646             }
1647              
1648             foreach ( @host_by_priority ) {
1649             print "$_\t $mx_info{$_}\n";
1650            
1651             }
1652              
1653             ########### example 4 a simple 'dig' script
1654              
1655             #!/usr/bin/perl
1656             #
1657             # example simple 'dig.pl' script
1658             #
1659             use Net::DNS::Dig;
1660              
1661             my ($name,$type);
1662              
1663             while ( $_ = shift @ARGV ) {
1664             if ( $_ eq '-t' ) {
1665             $type = shift;
1666             } else {
1667             $name = $_;
1668             }
1669             }
1670              
1671             print Net::DNS::Dig->new()->for( $name, $type )->sprintf;
1672              
1673             # end of script simple dig.pl
1674              
1675             command prompt > dig.pl -t aaaa arpa.com
1676              
1677             [response]
1678              
1679            
1680             ; <<>> Net::DNS::Dig 0.01 <<>> -t aaaa arpa.com.
1681             ;;
1682             ;; Got answer.
1683             ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 35979
1684             ;; flags: qr ra; QUERY: 1, ANSWER: 0, AUTHORITY: 8, ADDITIONAL: 8
1685              
1686             ;; QUESTION SECTION:
1687             ;arpa.com. IN AAAA
1688              
1689             ;; AUTHORITY SECTION:
1690             arpa.com. 79436 IN NS pdns2.ultradns.net.
1691             arpa.com. 79436 IN NS pdns1.ultradns.net.
1692             arpa.com. 79436 IN NS pdns5.ultradns.info.
1693             arpa.com. 79436 IN NS pdns3.ultradns.org.
1694             arpa.com. 79436 IN NS udns2.ultradns.net.
1695             arpa.com. 79436 IN NS pdns4.ultradns.org.
1696             arpa.com. 79436 IN NS udns1.ultradns.net.
1697             arpa.com. 79436 IN NS pdns6.ultradns.co.uk.
1698              
1699             ;; ADDITIONAL SECTION:
1700             pdns1.ultradns.net. 54013 IN A 204.74.108.1
1701             pdns2.ultradns.net. 54013 IN A 204.74.109.1
1702             pdns3.ultradns.org. 67699 IN A 199.7.68.1
1703             pdns4.ultradns.org. 67699 IN A 199.7.69.1
1704             pdns5.ultradns.info. 67699 IN A 204.74.114.1
1705             pdns6.ultradns.co.uk. 67699 IN A 204.74.115.1
1706             udns1.ultradns.net. 67698 IN A 204.69.234.1
1707             udns2.ultradns.net. 67698 IN A 204.74.101.1
1708              
1709             ;; Query time: 27 ms
1710             ;; SERVER: 192.168.1.171# 53(192.168.1.171)
1711             ;; WHEN: Mon Oct 10 17:23:39 2011
1712             ;; MSG SIZE rcvd: 365 -- XFR size: 17 records
1713              
1714              
1715             ########### example 5 a complex 'dig' script
1716              
1717             #!/usr/bin/perl
1718             #
1719             # example complex 'dig.pl' script
1720             #
1721             use Net::DNS::Dig qw(
1722             ndd_gethostbyname
1723             );
1724              
1725             my($name, $type, $port, $server, $tcp, $time, $recurse);
1726              
1727             unless (@ARGV) {
1728             print qq|\nusage: $0 [options] name
1729              
1730             -t [type] a, mx, etc...
1731             -p [port number]
1732             +tcp use TCP
1733             +norecursive
1734             +time=[seconds] timeout
1735              
1736             |;
1737             exit;
1738             }
1739              
1740             while ( $_ = shift @ARGV ) {
1741             if ( $_ eq '-t' ) {
1742             $type = shift;
1743             }
1744             elsif ( $_ eq '-p' ) {
1745             $port = shift;
1746             }
1747             elsif ( $_ =~ /^\@(.+)/ ) {
1748             $server = $1;
1749             }
1750             elsif ( lc $_ eq '\+tcp' ) {
1751             $tcp = 'tcp';
1752             }
1753             elsif ( $_ =~ /^\+time=(\d+)/ ) {
1754             $time = $1;
1755             }
1756             elsif ( $_ =~ /^\+norecursive/ ) {
1757             $recurse = 1;
1758             }
1759             else {
1760             $name = $_;
1761             }
1762             }
1763              
1764             my $config = {
1765             Timeout => $time,
1766             PeerAddr => $server,
1767             PeerPort => $port,
1768             Proto => $tcp,
1769             Recursion => $recurse,
1770             };
1771            
1772             print Net::DNS::Dig->new($config)->for($name,$type)->to_text->sprintf;
1773              
1774             # end of script complex dig.pl
1775              
1776             =head1 EXPORTS_OK
1777              
1778             :forceEmu
1779             ndd_gethostbyaddr
1780             ndd_gethostbyname
1781             ndd_gethostbyname2
1782             AF_INET
1783             AF_INET6
1784              
1785             =head1 AUTHOR
1786              
1787             Michael Robinton
1788              
1789             =head1 COPYRIGHT 2011-2014
1790              
1791             Michael Robinton
1792              
1793             All rights reserved.
1794              
1795             This program is free software; you can redistribute it and/or modify
1796             it under the terms of either:
1797              
1798             a) the GNU General Public License as published by the Free
1799             Software Foundation; either version 2, or (at your option) any
1800             later version, or
1801              
1802             b) the "Artistic License" which comes with this distribution.
1803              
1804             This program is distributed in the hope that it will be useful,
1805             but WITHOUT ANY WARRANTY; without even the implied warranty of
1806             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See either
1807             the GNU General Public License or the Artistic License for more details.
1808              
1809             You should have received a copy of the Artistic License with this
1810             distribution, in the file named "Artistic". If not, I'll be glad to provide
1811             one.
1812              
1813             You should also have received a copy of the GNU General Public License
1814             along with this program in the file named "Copying". If not, write to the
1815              
1816             Free Software Foundation, Inc.
1817             59 Temple Place, Suite 330
1818             Boston, MA 02111-1307, USA
1819              
1820             or visit their web page on the internet at:
1821              
1822             http://www.gnu.org/copyleft/gpl.html.
1823              
1824             =head1 See also:
1825              
1826             Net::DNS::Codes(3),
1827             Net::DNS::ToolKit(3),
1828             Net::DNS::ToolKit::Utilities(3),
1829             NetAddr::IP::Util(3)
1830              
1831             =cut
1832              
1833             1;