How to get public DNS server addresses

binddomain-name-systemtrace

I have a need to be able to identify which public DNS servers machines in internal networks are using for queries. To clarify, I do not want IPs or names of internal DNS servers or network devices. I need a scriptable way to identify what public servers they are calling to when queries are forwarded. For instance, I'm on a home network and my router's address is 172.16.1.1 – and it is programmed to used 4.2.2.1 and 4.2.2.2 for name resolution. I need to be able to capture these addresses. Parsing out of ip/ifconfig or logging into routers etc. are not options.

I've tried getting info out of nslookups and dig but I don't seem to be able to get the servers that are actually getting the queries at first before they start recursing. The internal DNS servers get identified but I'm not seeing the next hops.

Any ideas ? Unix or Dos or Windows etc, any solutions welcome.

Best Answer

There is really no way to accomplish your task, because the DNS server can resolve queries in any way it feels is appropriate, and it's a black box to the client. However, it is possible to get close to answering your question.

The trick is to set up a subdomain with a special nameserver whose response to any A query simply echoes the IP address of the host that issued the query. Such a nameserver can be implemented in Perl using the Net::DNS::Nameserver module:


#!/usr/bin/perl

use Net::DNS::Nameserver;

Net::DNS::Nameserver->new(
    # w.x.y.z is the IP address of the host where this code is running
    LocalAddr => ['w.x.y.z'],
    ReplyHandler => sub {
        my ($qname, $qclass, $qtype, $peerhost, $query) = @_;
        my ($rcode, @ans, @auth, @add) = ('NXDOMAIN');
        print "Received query for $qname from $peerhost\n";

        if ($qtype eq 'A') {
            $rcode = 'NOERROR';
            push @ans, Net::DNS::RR->new("$qname 1 $qclass $qtype $peerhost");
        }
        return ($rcode, \@ans, \@auth, \@add, { aa => 1});
    },
)->main_loop;

Next, configure some domain's NS record to point to this special nameserver. I've done this for a domain called resolverid.acrotect.com. (As a service to you, I'll leave the code running there for the next few days.)

Then, issue a few different DNS queries from a client inside your network:

dig +short cachebuster1.resolverid.acrotect.com
dig +short cachebuster2.resolverid.acrotect.com
dig +short cachebuster3.resolverid.acrotect.com

This will cause your nameserver to resolve the queries by contacting the rigged nameserver, which replies with the IP address of the machine that forwarded the query. In some cases, that address is the "public" side of your "private" nameserver, which is what you asked for.

In other cases, the infrastructure is more complicated. For example, if you are configured to use Google DNS servers 8.8.8.8 and 8.8.4.4, you are actually using a geographically distributed pool of nameservers. The Google server that makes the query to the rigged nameserver might not have an IP address anywhere near 8.8.8.8 or 8.8.4.4. However, you would be able to see that it belongs to Google by doing a reverse-IP or WHOIS lookup on the response. (Interestingly, if you do the reverse-IP lookup using Google DNS, you'll see that Google has optimized away the reverse-IP lookup based on the A lookup you just performed. The PTR that it returns will be something.resolverid.acrotect.com. To bust that optimization, you'll have to do dig +trace -x a.b.c.d.)