#!/usr/bin/perl # # DNS-PROXY # # Author: David J. Bianco # # This is a quick-n-dirty DNS proxy server written in Perl. It has one # use, and one use only: to help you hide your DNS activities from external # parties. I wrote this for intrusion analysts, whose analysis consoles # are always trying to do DNS lookups. Unfortunately, some sophisticated # attackers control their own DNS servers, and can monitor which sites # try to resolve the IPs from which their attacks originate. # # With the proxy, though, you can route all DNS requests for Internet # addresses to a third party DNS service (like OpenDNS, or maybe your ISP), # while still looking up your internal network IPs in your local DNS server. # # To use this, set up the @LOCAL_NETS list to contain a list of your # local network addresses and domains. Note that reverse lookups require # you to specify your addresses in "in-addr.arpa" format, like so: # # @LOCAL_NETS = ( "\.168\.192\.in-addr\.arpa", # "\.10\.in-addr\.arpa", # "\.mydomain\.com"); # # # You'll also need to set the $LOCAL_DNS and $PUBLIC_DNS strings to space # separated lists of the local and public DNS servers you want to use, # like so: # # $LOCAL_DNS = qw("10.10.10.2 10.10.10.3"); # My DNS servers # $PUBLIC_DNS = qw("208.67.222.222 208.67.220.220"); # OpenDNS # # Then just run the script (probably in the background) and it will start # listening for DNS requests on 127.0.0.1 port 53. Then configure your # system's resolv.conf file to point to localhost only. # # NOTE: You can allow other systems to use the proxy by changing the LocalAddr # parameter to the Nameserver->new() constructor in the main body below. # If you do this, set the IP to your system's IP and it will serve DNS # requests to anyone on your network. BEWARE that I have not extensively # tested the security of the code, so there may well be remotely exploitable # bugs if you run it this way. # use Net::DNS; use Net::DNS::Nameserver; @LOCAL_NETS = ( "\.168\.192\.in-addr\.arpa", "\.foo\.com"); $LOCAL_DNS = qw(192.168.1.1 192.168.1.2); # My Local DNS servers $PUBLIC_DNS = qw(208.67.222.222 208.67.220.220); # OpenDNS sub resolve { my($string, $qtype) = @_; my($packet); my($rcode, @ans); my($resolver); # Figure out which resolver (local or public) to use for this request. # Assume no resolver, then if the request matches any of the @LOCAL_NETS # entries, set it to the local resolver. $resolver = ""; foreach $expr (@LOCAL_NETS) { if($string =~ m/$expr$/) { $resolver = $local_resolver; } } # If we haven't already chosen the local resolver (in the loop above), # then it must be a public address. if(!$resolver) { $resolver = $public_resolver; } # Perform the query and make a list of RR record answers $packet = $resolver->search($string, $qtype); if($packet) { $rcode = "NOERROR"; foreach $rr ($packet->answer) { push @ans, $rr; } } else { print STDERR "Query failed: " . $resolver->errorstring . "\n"; } # If we got a list of answers, be sure to reverse it before returning. # Otherwise we'd give out answers in the exact opposite order as a # non-proxy DNS. if(@ans) { reverse(@ans); } return ($rcode, \@ans); } sub reply_handler { my ($qname, $qclass, $qtype, $peerhost) = @_; my ($rcode, @ans, @auth, @add); # We only do PTR and A requests. Anything else gets a NOTIMP (not # implemented) error. if(($qtype eq "PTR") || ($qtype eq "A")) { ($rcode, @ans) = resolve($qname, $qtype); } else { $rcode = "NOTIMP"; } # Return the answers (if any) to the caller. return ($rcode, @ans, @auth, @add); } # Set up the local and public resolvers $local_resolver = Net::DNS::Resolver->new( nameservers => [$LOCAL_DNS], recurse => 1, debug => 0, ); $public_resolver = Net::DNS::Resolver->new( nameservers => [$PUBLIC_DNS], recurse => 1, debug => 0, ); # Create the nameserver thread $ns = Net::DNS::Nameserver->new( LocalAddr => '127.0.0.1', LocalPort => 53, ReplyHandler => \&reply_handler, Verbose => 0, ) || die "couldn't create nameserver object\n"; # Go into the main nameserver and never return $ns->main_loop();