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