1#!/usr/bin/perl 2# 3# Copyright (c) 2003,2004,2005 Roy Arends & Jakob Schlyter. 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 3. The name of the authors may not be used to endorse or promote products 16# derived from this software without specific prior written permission. 17# 18# THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 19# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29use strict; 30use warnings; 31 32use Net::DNS; 33use Net::DNS::Fingerprint; 34 35use Getopt::Std; 36use vars qw/ %opt /; 37use POSIX ":sys_wait_h"; 38 39my $progname = "fpdns"; 40my $version = Net::DNS::Fingerprint->version(); 41 42my $IPv4RE = qr/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/; 43my $IPv6RE = qr/^(((?=(?>.*?::)(?!.*::)))(::)?([0-9A-F]{1,4}::?){0,5}|([0-9A-F]{1,4}:){6})(\2([0-9A-F]{1,4}(::?|$)){0,2}|((25[0-5]|(2[0-4]|1[0-9]|[1-9])?[0-9])(\.|$)){4}|[0-9A-F]{1,4}:[0-9A-F]{1,4})(?<![^:]:)(?<!\.)\z/i; 44 45sub main { 46 $opt{p} = 53; 47 $opt{t} = 5; 48 $opt{r} = 1; 49 $opt{F} = 10; 50 $opt{T} = 0; 51 $opt{Q} = undef; 52 $opt{S} = " "; 53 54 my %children; 55 56 my $concurrent = 0; 57 58 getopts('Q:DF:p:t:r:fsS:dTv', \%opt); 59 60 $opt{v} && die "$progname version $version\n"; 61 62 unless ($#ARGV >= 0) { 63 usage(); 64 exit(1); 65 } 66 67 my $engine = Net::DNS::Fingerprint->new( 68 source => $opt{Q}, 69 debug => $opt{d}, 70 timeout => $opt{t}, 71 retry => $opt{r}, 72 forcetcp => $opt{T}, 73 qversion => $opt{v}, 74 qchaos => $opt{f}, 75 separator => $opt{S}, 76 ); 77 78 if ($ARGV[0] eq "-") { 79 while (<STDIN>) { 80 my $pid = fork; 81 if ((not defined $pid) 82 and not($! =~ /Resource temporarily unavailable/)) 83 { 84 die "Can't fork: $!\n"; 85 } elsif ($pid == 0) { 86 chomp; 87 fingerprint($engine, $_); 88 exit(0); 89 } else { 90 $concurrent++; 91 $children{$pid} = 1; 92 while ($concurrent >= $opt{F}) { 93 my $child = waitpid -1, 0; 94 $concurrent--; 95 delete($children{$child}); 96 } 97 } 98 } 99 while ($concurrent) { 100 my $child = waitpid -1, 0; 101 $concurrent--; 102 delete($children{$child}); 103 } 104 } else { 105 while ($#ARGV + 1) { 106 fingerprint($engine, shift @ARGV); 107 } 108 } 109} 110 111sub fingerprint { 112 my ($engine, $server) = @_; 113 my @addresses = dnslookup($server); 114 if ($#addresses >= 0) { 115 for my $a (@addresses) { 116 my $fp = $engine->string($a, $opt{p}); 117 $opt{s} && (printf("%-15s %s\n", $a, $fp)) 118 || print "fingerprint ($server, $a): $fp\n"; 119 } 120 } else { 121 print STDERR "host not found ($server)\n"; 122 } 123} 124 125sub dnslookup { 126 my $arg = shift; 127 my @addresses = (); 128 return $arg if ($arg =~ $IPv4RE); 129 return $arg if ($arg =~ $IPv6RE); 130 131 my $resolver = Net::DNS::Resolver->new(usevc => $opt{T}); 132 $resolver->srcaddr($opt{Q}) if $opt{Q}; 133 if ($opt{D}) { 134 my $query = $resolver->send($arg, "NS"); 135 if ($query) { 136 for my $rr ($query->answer) { 137 next unless $rr->type eq "NS"; 138 my $query_address = $resolver->send($rr->rdatastr, "A"); 139 if ($query_address) { 140 for my $address_rr ($query_address->answer) { 141 push @addresses, $address_rr->address 142 if $address_rr->type eq "A"; 143 } 144 } 145 $query_address = $resolver->send($rr->rdatastr, "AAAA"); 146 if ($query_address) { 147 for my $address_rr ($query_address->answer) { 148 push @addresses, $address_rr->address 149 if $address_rr->type eq "AAAA"; 150 } 151 } 152 } 153 } 154 } else { 155 my $query = $resolver->send($arg, "A"); 156 if ($query) { 157 for my $rr ($query->answer) { 158 push @addresses, $rr->address if $rr->type eq "A"; 159 } 160 } 161 $query = $resolver->send($arg, "AAAA"); 162 if ($query) { 163 for my $rr ($query->answer) { 164 push @addresses, $rr->address if $rr->type eq "AAAA"; 165 } 166 } 167 } 168 169 return @addresses; 170} 171 172sub usage { 173 print STDERR <<EOF; 174Usage: $progname [-d] [-D] [-f] [-p port] [-Q srcaddr] [-r retry] [-s] [-t timeout] [-T] [-v] server(s)|Domain 175Where: server|Domain is an ip address, a resolvable name, or a domain name. 176 or '-' to read list of servers from stdin 177 -d (debug) [off] 178 -D (check all authoritative servers for Domain) 179 -f (force check CH TXT version) [off] 180 -F nchild (maximum forked processes) [10] 181 -p port (nameserver is on this port) [53] 182 -Q srcaddr (source IP address) [0.0.0.0] 183 -r retry (set number of attempts) [1] 184 -s (short form) [off] 185 -S (separator) [" "] 186 -t time (set query timeout) [5] 187 -T (use TCP) [off] 188 -v (show version) 189 190EOF 191 exit 2; 192 193} 194 195&main; 196 197=head1 NAME 198 199fpdns - DNS server fingeprinting tool 200 201=head1 SYNOPSIS 202 203B<fpdns> S<[ B<-d> ]> S<[ B<-f> ]> S<[ B<-F> I<nchild> ]> 204 S<[ B<-p> I<port> ]> S<[ B<-Q> I<srcaddr> ]> S<[ B<-r> I<retry> ]> 205 S<[ B<-s> ]> S<[ B<-S> I<separator> ]> S<[ B<-t> I<timeout> ]> S<[ B<-v> ]> [I<server(s)>] 206 207=head1 DESCRIPTION 208 209B<fpdns> is a program that remotely determines DNS server versions. 210It does this by sending a series of borderline DNS queries which are 211compared against a table of responses and server versions. 212 213False positives or incorrect versions may be reported when 214trying to identify a set of servers residing behind a 215load-balancing apparatus where the servers are of different 216implementations, when a specific implementation behaves like a 217forwarder, behind a firewall without statefull inspection or without 218I<Application Intelligence>. 219 220=head1 OPTIONS 221 222=over 5 223 224=item B<-d> 225 226Enable debugging. Off by default. 227 228=item B<-D> 229 230Check all authoritative servers of the specified domain name. 231 232=item B<-f> 233 234Force checking of CH TXT version. Off by default. 235 236=item B<-F> I<nchild> 237 238Maximum number of forked child processes. Defaults to 10. 239 240=item B<-p> I<port> 241 242Port to query remote nameserver on. Default is 53. 243 244=item B<-Q> I<srcaddr> 245 246Set the source IP address to use. 247 248=item B<-r> I<retry> 249 250Number of attempt to retry fingerprints. Defaults to 1. 251 252=item B<-s> 253 254Short display form. Useful for surveys. 255 256=item B<-S> 257 258Separator. Defaults to " ". 259 260=item B<-t> I<timeout> 261 262Set the query timeout in seconds. Defaults to 5. 263 264=item B<-T> 265 266Use TCP instead of UDP. 267 268=item B<-v> 269 270Show version of fpdns. 271 272=item I<server> 273 274IP address or name to query. Alternatively may be '-' to 275read from a list of these from stdin 276 277=back 278 279=head1 AUTHORS 280 281fpdns was written by Roy Arends and Jakob Schlyter. 282 283=head1 SEE ALSO 284 285L<perl(1)>, L<Net::DNS(1)> 286 287=cut 288