1package Net::DNS::Check::NSQuery;
2
3use strict;
4
5use Net::DNS;
6use Net::DNS::Check::Host;
7use Net::DNS::Check::HostsList;
8use Net::DNS::Check::Config;
9use Carp;
10# use Data::Dumper;
11
12sub new {
13	my ($class, %param) = @_;
14
15
16	return 0 if (!$param{domain} || ! $param{nserver});
17
18	my $self = {};
19
20	# Nome del dominio
21	$self->{domain} 		= $param{domain};
22	$self->{qdomain} 		= $param{domain};
23	$self->{qdomain}     	=~ s/\./\\./g;
24
25	# Nome del namserver da interrogare
26	$self->{nserver} 	= $param{nserver};
27
28	my $fatal = 0;
29	my $msg_error =<<ERROR;
30
31FATAL ERROR
32===============
33Wrong call of constructor: $class
34ERROR
35
36
37	unless ( $self->{domain} ) {
38		$fatal = 1;
39		$msg_error .= "\ndomain param not found!\n";
40	}
41
42	unless ( $self->{nserver} ) {
43		$fatal = 1;
44		$msg_error .= "\nnserver param not found!\n";
45	}
46
47	if ( $fatal ) {
48		confess($msg_error . "\n");
49	}
50
51
52
53	# IP del namserver da interrogare
54	# il parametro e' facoltativo. Se non viene passato
55	# viene utilizzata la ricorsione per determinare
56	# l'IP. Ovviamente per i nameserver appartenenti al dominio
57	# sul quale staimo operando DOVREBBE essere passato l'IP.
58	# Se non viene passato l'IP per quest'ultimi si utilizza
59	# la ricorsione che funzionera' solo se il dominio e' gia'
60	# esistente
61	$self->{ip} 	= $param{ip};
62
63
64	$self->{config} 	= $param{config} || new Net::DNS::Check::Config;
65
66	if ( defined $param{debug} ) {
67		$self->{debug}      = $param{debug};
68    	} else {
69        	$self->{debug}      = $self->{config}->debug_default();
70    	}
71
72
73
74	# External/General Hostslist.
75	$self->{hostslist} = $param{hostslist} ||  new Net::DNS::Check::HostsList(
76			domain 	=> $self->{domain},
77			debug 	=> ($self->{debug} > 2),
78			config 	=> $self->{config}
79		);
80
81	# Internal HostsList
82	$self->{myhostslist} = new Net::DNS::Check::HostsList(
83		domain 	=> $self->{domain},
84		debug 	=> ($self->{debug} > 2),
85		config 	=> $self->{config}
86	);
87
88
89
90
91	# Array of NS or MX hostnames
92	$self->{result}->{NS} = [];
93	$self->{result}->{MX} = [];
94
95
96	bless $self, $class;
97
98	if ($self->{debug} > 0 ) {
99		print <<DEBUG;
100
101Query for RR ANY for $self->{domain} to $self->{nserver}
102=======================================================
103DEBUG
104	}
105
106
107
108	# Creiamo l'oggetto resolver usando il resolver di sistema
109	$self->{res} = Net::DNS::Resolver->new(
110		recurse     	=> 0,
111		debug       	=> ($self->{debug} > 2),
112		retrans     	=> $self->{config}->query_retrans,
113		retry       	=> $self->{config}->query_retry,
114		tcp_timeout     => $self->{config}->query_tcp_timeout
115	);
116
117
118	# La add_host crea un oggetto host e lo aggiunge alla lista se non esiste
119	# o ritorna l'oggetto host gia' presente nella hostslist
120	# $self->{host} = $self->{hostslist}->add_host( $self->{nserver}, $self->{ip} );
121
122
123	# if an ip doesn't exist we try to find it using add_host function
124	# (that it uses hostslist object functions)
125	unless ( @{$self->{ip}} ) {
126
127		if ($self->{debug} > 0 ) {
128			my $ips = join(' ', @{$self->{ip}});
129			print <<DEBUG;
130 Search for $self->{nserver} IP
131
132DEBUG
133		}
134
135	    $self->{host} = $self->_add_host( $self->{nserver} );
136		$self->{ip} = $self->{host}->get_ip();
137
138	}
139
140	# We found an IP address to query so we make query
141	# .... otherwise we have an error
142	if ( @{$self->{ip}} ) {
143		# $self->{type} = $type;
144
145		if ($self->{debug} > 0 ) {
146			my $ips = join(' ', @{$self->{ip}});
147			print <<DEBUG;
148 $self->{nserver} IP : $ips
149DEBUG
150		}
151
152		# We set resolver to the ip found
153		$self->{res}->nameservers(@{ $self->{ip} });
154
155		if ($self->{debug} > 2) {
156			print "\n\n";
157			$self->{res}->print;
158		}
159
160		# Query of type ANY for $self->{domain} to $self->{ip}
161		$self->_queryANY();
162
163	} else {
164
165		$self->{error} = 'NOIP';
166
167		if ($self->{debug} > 0 ) {
168			my $ips = join(' ', @{$self->{ip}});
169			print <<DEBUG;
170 $self->{nserver} IP : Not Found
171DEBUG
172		}
173	}
174
175	return $self;
176}
177
178
179sub _queryANY() {
180	my $self = shift;
181
182	# Creazione query per il dominio
183	my $packet = $self->{res}->send($self->{domain},'ANY');
184
185	if ($packet) {
186		$self->{result}->{header} = $packet->header;
187
188		if ($self->{debug} > 0 ) {
189			print <<DEBUG;
190 Getting query answer
191
192DEBUG
193		}
194
195		if ($self->{debug} > 1 ) {
196			my $result = $packet->string;
197			print <<DEBUG;
198$result
199DEBUG
200		}
201
202
203		if ( $self->header_aa() && scalar $packet->answer() ) {
204			foreach my $rr ( $packet->answer ) {
205
206				if ($rr->type eq 'SOA') {
207					$self->{result}->{SOA} = $rr;
208					next;
209				}
210
211				if ($rr->type eq 'NS') {
212					push (@{$self->{result}->{NS}}, lc($rr->{nsdname}));
213					$self->_add_host( lc($rr->{nsdname}) );
214					next;
215				}
216
217				if ($rr->type eq 'MX') {
218					push (@{$self->{result}->{MX}}, lc($rr->{exchange}));
219					$self->_add_host( lc($rr->{exchange}) );
220					next;
221				}
222			}
223		} else {
224			$self->{error} = 'NOAUTH';
225		}
226	} else {
227
228		# Query Error... no answer (time out)
229		$self->{error} = 'NOANSWER';
230
231		if ($self->{debug} > 0 ) {
232			my $qerror = $self->{res}->errorstring;
233			print <<DEBUG;
234 Query Error:       $qerror
235DEBUG
236		}
237
238	}
239}
240
241
242
243sub _add_host() {
244	my $self 		= shift;
245	my ($hostname) 	= shift;
246
247	unless ($hostname) {
248		confess("hostname parm not found!\n");
249	}
250
251	my ($host, @temp);
252
253	@temp = split('\.', $self->{domain});
254	my $domcount = scalar @temp;
255
256	@temp = split('\.', $hostname);
257	my $hostcount = (scalar @temp)-1;
258
259
260	# Questo e' da rivedere.
261	if ( ($hostname eq $self->{domain}) || $hostname =~ /.*$self->{qdomain}$/ && $domcount == $hostcount ) {
262		# Se l'hostname fa parte del dominio lo aggiungiamo alla hostslist
263		# locale e usiamo per la risluzione l'ip del namserver
264		# con cui abbiamo creato l'oggetto NSQuery
265		#print "inside ";
266		$host = $self->{myhostslist}->add_host( hostname => $hostname, ip => $self->{ip} );
267	} else {
268		# Se l'hostname non fa parte del dominio lo aggiungiamo alla
269		# hostslist globale
270		#print "outside ";
271		$host = $self->{hostslist}->add_host( hostname => $hostname );
272	}
273	return $host;
274}
275
276
277
278# Riporta 1 se le risposte del dns sono autoritativo
279# Riporta 0 se la risposta non e' autoritativa
280# Riporta -1 se non c'e' nessun header
281sub header_aa() {
282	my $self = shift;
283
284	return undef if (! defined $self->{result}->{header});
285
286	return $self->{result}->{header}->aa();
287}
288
289
290# Riporta l'oggetto Net::DNS::Header oppure false se non c'e' l'oggetto
291sub header() {
292	my $self = shift;
293
294	return 0 if (! defined $self->{result}->{header});
295
296	return $self->{result}->{header};
297}
298
299
300# Riporta un array vuoto se non ci sono record NS altrimenti riporta
301# l'array contenente la lista dei DNS autoritativi
302sub ns_list() {
303	my $self = shift;
304
305	return () unless defined $self->{result}->{NS};
306
307	return @{ $self->{result}->{NS} };
308}
309
310
311# Riporta un array vuoto se non ci sono record MX altrimenti
312# Altrimenti riporta l'array dei contenente la lista degli
313# exchange server
314sub mx_list() {
315	my $self = shift;
316
317	return () unless defined $self->{result}->{MX};
318
319	return @{ $self->{result}->{MX} };
320}
321
322# Riporta undef se non esiste un'oggetto SOA o non esiste un master altrimenti riporta il master nameserver che appare nel SOA
323sub soa_mname() {
324	my $self = shift;
325
326	return if (! defined $self->{result}->{SOA} );
327
328	return lc($self->{result}->{SOA}->mname());
329}
330
331# Riporta undef se non esiste un'oggetto SOA altrimenti
332# Riporta il serial che appare nel SOA
333sub soa_serial() {
334	my $self = shift;
335
336	return if (! defined $self->{result}->{SOA} );
337
338	return $self->{result}->{SOA}->serial();
339}
340
341
342
343# Riporta 0 se non esiste un'oggetto SOA o non esiste un refresh
344# Riporta il refresh che che appare nel SOA
345sub soa_refresh() {
346	my $self = shift;
347
348	return 0 if (! defined $self->{result}->{SOA} );
349
350	return $self->{result}->{SOA}->refresh();
351}
352
353# Riporta 0 se non esiste un'oggetto SOA o non esiste un retry
354# Riporta il retry che che appare nel SOA
355sub soa_retry() {
356	my $self = shift;
357
358	return 0 if (! defined $self->{result}->{SOA} );
359
360	return $self->{result}->{SOA}->retry();
361}
362
363# Riporta 0 se non esiste un'oggetto SOA o non esiste un expire
364# Riporta il expire che che appare nel SOA
365sub soa_expire() {
366	my $self = shift;
367
368	return 0 if (! defined $self->{result}->{SOA} );
369
370	return $self->{result}->{SOA}->expire();
371}
372
373# Riporta 0 se non esiste un'oggetto SOA o non esiste un minimum
374# Riporta il minimum che che appare nel SOA
375sub soa_minimum() {
376	my $self = shift;
377
378	return 0 if (! defined $self->{result}->{SOA} );
379
380	return $self->{result}->{SOA}->minimum();
381}
382
383# Riporta 0 se non esiste un'oggetto SOA o non esiste un minimum
384# Riporta il minimum che che appare nel SOA
385sub soa_mail() {
386	my $self = shift;
387
388	return 0 if (! defined $self->{result}->{SOA} );
389
390	return $self->{result}->{SOA}->rname();
391}
392
393# Riporta il nome del nameserver che stiamo interrogando
394sub ns_name() {
395	my $self = shift;
396
397	return $self->{nserver};
398}
399
400
401sub error() {
402	my $self = shift;
403
404	return $self->{error};
405}
406
407sub hostslist() {
408	my $self = shift;
409
410	return $self->{myhostslist};
411}
412
4131;
414
415__END__
416
417=head1 NAME
418
419Net::DNS::Check::NSQuery - Class to query authoritative nameservers for the domain name you want to check.
420
421=head1 DESCRIPTION
422
423This class is used to query nameservers for the domain name you want to check.
424
425The are several methods implemented by this class, but at present are all for internal use only and L<Net::DNS::Check> users don't need to directly create  Net::DNS::Check::NSQuery object and call his methods.
426
427Anyway a complete documentation of all methods will be released as soon as possible.
428
429=head1 COPYRIGHT
430
431Copyright (c) 2005 Lorenzo Luconi Trombacchi - IIT-CNR
432
433All rights reserved.  This program is free software; you may redistribute
434it and/or modify it under the same terms as Perl itself.
435
436=head1 SEE ALSO
437
438L<perl(1)>
439
440=cut
441
442
443
444