[PERL] DNS-checker met Net::DNS en IO::Select

Pagina: 1
Acties:

  • Gertjan
  • Registratie: Oktober 2001
  • Laatst online: 07-02 20:23

Gertjan

mmmm, beer...

Topicstarter
Ik ben bezig een script te maken dat voor een gegeven domeinnaam en een gegeven lijst met tld's controleert of er NS-, A-, of MX-records van bestaan. Dit wil ik doen op zo'n manier dat een time-out van een dns-server niet het hele script laat wachten. Via CPAN vond ik Net::DNS die me netjes vertelde dat ik hiervoor IO::Select kan gebruiken. Dit werkt inderdaad prima, maar ik loop tegen het volgende probleem aan. Als ik met mijn script bijvoorbeeld de naam 'dude' controleer met tld's 'nl,com,org,net,nu,co.uk,de,be', dan krijg ik daar de eerste keer de volgende output:
code:
1
2
3
4
5
6
7
8
dude.org:0
dude.be:0
dude.nl:1
dude.nu:1
dude.com:0
dude.co.uk:0
dude.de:0
dude.net:0

De 1 of 0 geeft aan of het domein vrij (0) is of bezet (1).
Draai ik het script echter direct nog eens, krijg ik als output:
code:
1
2
3
4
5
6
7
8
dude.org:0
dude.be:1
dude.nl:1
dude.nu:0
dude.com:1
dude.co.uk:0
dude.de:1
dude.net:1


De code:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#!/usr/bin/perl

use strict;
use warnings;
use Net::DNS;
use IO::Select;
use CGI::Carp qw(fatalsToBrowser);
use CGI qw/param/;
use Data::Dumper;

print "Content-Type: text/plain\n\n";

my $domain  = param('d');
my @tlds    = split(',', param('tlds'));

our $dns_   = Net::DNS::Resolver->new;
our $sel_   = IO::Select->new;
our $DEBUG  = 1;

my $timeout = 5;
my $counter = 0;
my @ready   = ();
my @bgsocks = ();
my @selects = ();
my %domains = ();

foreach my $t (@tlds) 
{
  $domains{"$domain.$t"} = 0;
#  print "$domain.$t: query sent\n";

  push( @bgsocks, $dns_->bgsend(     "$domain.$t", 'NS' ) );
  push( @bgsocks, $dns_->bgsend(     "$domain.$t", 'A'  ) );
  push( @bgsocks, $dns_->bgsend(     "$domain.$t", 'MX' ) );
  push( @bgsocks, $dns_->bgsend( "www.$domain.$t", 'A'  ) );

  $sel_->add( $bgsocks[ ($counter * 4) + 0 ] );
  $sel_->add( $bgsocks[ ($counter * 4) + 1 ] );
  $sel_->add( $bgsocks[ ($counter * 4) + 2 ] );
  $sel_->add( $bgsocks[ ($counter * 4) + 3 ] );

  push( @ready, $sel_->can_read( $timeout ) );

  $counter++;
}

#print Dumper(@ready);

if( @ready )
{
  foreach my $sock ( @ready )
  {
    foreach my $bgsock ( @bgsocks )
    {
      if( $sock == $bgsock )
      {
        my $packet   = $dns_->bgread($bgsock);
        my @answer   = $packet->answer;
        my @question = $packet->question;

        foreach my $q (@question)
        {
          if( @answer )
          {
            # er is een antwoord, dus domein is bezet
            my $temp_domain = $q->qname;

            # evt 'www.' van de naam kappen
            $temp_domain =~ s/^www\.//;

            $domains{$temp_domain} = 1;
          }
        }

        $bgsock = undef;
      }
    }
    $sel_->remove( $sock );
    $sock = undef;
  }
}

# print naar stdout
while( (my $c, my $free) = each( %domains ) )
{
  print "$c:$free\n";
}


Ik vermoed dat het script te snel ziet dat er een positief antwoord terugkomt van een nameserver (meestal de .nl), en daarna alles teruggeeft terwijl nog niet alle antwoorden binnen zijn. De 2e keer dat het script draait hebben de nameservers de aanvraag ge-cached en krijg ik wel op alle aanvragen een correct antwoord.
Ik denk dat ik op regel 42 wat extra logica in moet bouwen, maar ik weet zo niet hoe of wat. Weet iemand misschien duidelijk uit te leggen wat hier fout gaat en wat ik er aan zou kunnen doen?

[ Voor 6% gewijzigd door Gertjan op 26-02-2005 02:26 ]


  • Gertjan
  • Registratie: Oktober 2001
  • Laatst online: 07-02 20:23

Gertjan

mmmm, beer...

Topicstarter
Waar zijn de Perl-goeroes als je ze nodig hebt? :'(

  • Onno
  • Registratie: Juni 1999
  • Niet online
Ik geloof niet dat je IO::Select helemaal begrepen hebt. :)
Perl:
1
2
3
4
foreach my $t (@tlds) 
{
  push( @ready, $sel_->can_read( $timeout ) );
}

Nu wacht je alsnog per TLD maximaal 5 seconden, en dus worden de queries van TLD 2 pas uitgevoerd als de timeout van TLD 1 voorbij is.
Perl:
1
2
3
4
5
6
7
8
9
10
11
12
if( @ready )
{
  foreach my $sock ( @ready )
  {
    foreach my $bgsock ( @bgsocks )
    {
      if( $sock == $bgsock )
      {
      }
    }
  }
}

Die buitenste controle is totaal nutteloos, de foreach() doet immers al niks als @ready leeg is. Twee foreach'en is ook niet echt handig, dat kan veel simpeler. Al die sockets bijhouden hoef je ook niet te doen, je kunt aan het resultaat van je query immers al zien om welk TLD het gaat.

Wat je eigenlijk wilt is zoiets:
Perl:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
foreach my $t (@tlds)
{
  sel_->add($dns_->bgsend(     "$domain.$t", 'NS' ) );
  sel_->add($dns_->bgsend(     "$domain.$t", 'A'  ) );
  sel_->add($dns_->bgsend(     "$domain.$t", 'MX' ) );
  sel_->add($dns_->bgsend( "www.$domain.$t", 'A'  ) );
}

my $endtime = time() + $timeout;

while(($sel_->count() > 0) && (@ready = $sel_->can_read($timeout)))
{
  # verwerk replies
  foreach my $sock (@ready)
  {
    my $packet   = $dns_->bgread($sock);
    my @answer = $packet->answer;
    if (@answer)
    {
      my $temp_domain = $q->qname;
      $temp_domain =~ s/^www\.//;
      $domains{$temp_domain} = 1;
    }
    $sel_->remove($sock);
  }

  $timeout = $endtime - time();
  last if $timeout < 1;
}

# toon resultaten

Maar DNS gebruiken om te checken of een domein bestaat is niet slim. Daar moet je whois voor gebruiken.

[ Voor 26% gewijzigd door Onno op 27-02-2005 13:42 ]


  • zeroxcool
  • Registratie: Januari 2001
  • Laatst online: 04-05 13:54
offtopic:
Als een domein geen NS of SOA records heeft, wil het niet zeggen dat het domein vrij is.

zeroxcool.net - curity.eu