Created
March 2, 2014 19:40
-
-
Save rustyconover/9312610 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/perl | |
| # Resolve many domain names at a TLD for the zone asynchronously. | |
| # | |
| # Author: Rusty Conover <rusty@luckydinosaur.com> | |
| # | |
| use strict; | |
| use warnings; | |
| use AnyEvent::DNS; | |
| use AnyEvent; | |
| # This is a resolver that uses Google's public dns, we're going to be using it to | |
| # look up the servers for the TLD and translating those hostnames into ip addresses. | |
| my $google_resolver = new AnyEvent::DNS | |
| server => [map { AnyEvent::Socket::parse_address($_) } ('8.8.8.8', '8.8.4.4')]; | |
| # Get a list of hostnames that are the NS servers for a particular TLD. | |
| sub get_tld_servers { | |
| my $tld = shift; | |
| my $cv = AnyEvent->condvar; | |
| my @servers; | |
| my $handle_reply =sub { | |
| my $result = shift; | |
| if($result->{rc} eq 'noerror') { | |
| push @servers, map { $_->[4] } @{$result->{an}}; | |
| } | |
| $cv->send; | |
| }; | |
| $google_resolver->request ({ rd => 0, qd => [ [$tld, "ns"] ]}, $handle_reply); | |
| $cv->recv; | |
| return @servers; | |
| } | |
| # Resolve a hostname into an ip address into the socket address format. | |
| sub resolve_to_ip_addresses { | |
| my @servers = @_; | |
| my $cv = AnyEvent->condvar; | |
| my $lc = 0; | |
| my @addresses; | |
| foreach my $hostname (@servers) { | |
| $lc++; | |
| AnyEvent::DNS::a $hostname, sub { | |
| my $result = shift; | |
| push @addresses, AnyEvent::Socket::parse_address($result); | |
| if(!--$lc) { | |
| $cv->send; | |
| } | |
| }; | |
| } | |
| $cv->recv; | |
| return @addresses; | |
| } | |
| sub resolve_domains { | |
| my $resolver = shift; | |
| my $domains = shift; | |
| my $cv = AnyEvent->condvar; | |
| my $started = 0; | |
| my $finished = 0; | |
| my $queue_done = 0; | |
| my $handle_domain_reply =sub { | |
| my $result = shift; | |
| my $domain = $result->{qd}->[0]->[0]; | |
| if($result->{rc} eq 'nxdomain') { | |
| print "$domain 0\n"; | |
| } elsif($result->{rc} eq 'noerror') { | |
| print "$domain 1\n"; | |
| } else { | |
| die("Unknown result: " . $result->{rc}); | |
| } | |
| ++$finished; | |
| if($finished % 500 == 0) { | |
| print STDERR "Finished $finished - $domain\n"; | |
| } | |
| if($queue_done && $finished == $started) { | |
| $cv->send; | |
| } | |
| }; | |
| foreach my $domain (@$domains) { | |
| $resolver->request ({ rd => 0, qd => [ [$domain, "ns"] ]}, $handle_domain_reply); | |
| $started++; | |
| } | |
| $queue_done = 1; | |
| $cv->recv; | |
| } | |
| # Read the first domain so we can parse out the tld. | |
| my $first_domain = <>; | |
| chomp($first_domain); | |
| # The TLD is everything after the first dot for our purposes. | |
| my $tld = $first_domain; | |
| $tld =~ s/^[^.]+\.//; | |
| # Look up the TLD servers and get their ip addresses. | |
| my @tld_servers = get_tld_servers($tld); | |
| scalar(@tld_servers) > 0 || die("No TLD servers found for tld: $tld"); | |
| my @resolver_addresses = resolve_to_ip_addresses(@tld_servers); | |
| scalar(@resolver_addresses) > 0 || die("No IP addresses found for tld servers in tld: $tld"); | |
| # The number of requests that can be outstanding to a DNS server at one time. | |
| my $batch_size = 300; | |
| # Create a new resolver that talks directly to the TLD. | |
| my $tld_resolver = new AnyEvent::DNS | |
| server => \@resolver_addresses, | |
| max_outstanding => $batch_size; | |
| # Read all of the domains we're supposed to resolve, but process them | |
| # in batches of size $batch_size, this is because sometimes you can't | |
| # just push out 300,000 DNS requests at one time and expect to receive | |
| # all of the replies, change $batch_size to suit your needs. | |
| while(1) { | |
| my @batch; | |
| my $limited =0; | |
| while(my $line = <>) { | |
| chomp($line); | |
| if(defined($first_domain)) { | |
| push @batch, $first_domain; | |
| $first_domain = undef; | |
| } | |
| push @batch, $line; | |
| if(scalar(@batch) == $batch_size) { | |
| $limited = 1; | |
| last; | |
| } | |
| } | |
| if(scalar(@batch)) { | |
| resolve_domains($tld_resolver, \@batch); | |
| } | |
| if(!$limited) { | |
| last; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment