Digging to the root with DNS queries
Introduction
This is an explicit walkthrough on how a domain name is resolved. Doing the recursion manually, that is.
And then some remarks on the mess with DNS glue records.
Getting the root servers
$ dig NS .
; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> NS .
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 59540
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 14
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;. IN NS
;; ANSWER SECTION:
. 49053 IN NS b.root-servers.net.
. 49053 IN NS f.root-servers.net.
. 49053 IN NS a.root-servers.net.
. 49053 IN NS h.root-servers.net.
. 49053 IN NS k.root-servers.net.
. 49053 IN NS l.root-servers.net.
. 49053 IN NS e.root-servers.net.
. 49053 IN NS g.root-servers.net.
. 49053 IN NS j.root-servers.net.
. 49053 IN NS d.root-servers.net.
. 49053 IN NS c.root-servers.net.
. 49053 IN NS i.root-servers.net.
. 49053 IN NS m.root-servers.net.
;; ADDITIONAL SECTION:
a.root-servers.net. 567453 IN A 198.41.0.4
b.root-servers.net. 547997 IN A 199.9.14.201
c.root-servers.net. 314914 IN A 192.33.4.12
d.root-servers.net. 478361 IN A 199.7.91.13
e.root-servers.net. 326962 IN A 192.203.230.10
f.root-servers.net. 514616 IN A 192.5.5.241
g.root-servers.net. 575480 IN A 192.112.36.4
h.root-servers.net. 592754 IN A 198.97.190.53
i.root-servers.net. 596171 IN A 192.36.148.17
j.root-servers.net. 591102 IN A 192.58.128.30
k.root-servers.net. 580970 IN A 193.0.14.129
l.root-servers.net. 523957 IN A 199.7.83.42
m.root-servers.net. 603222 IN A 202.12.27.33
;; Query time: 19 msec
This was a very fast query, because the info is in any DNS’ zone files. This is the piece of info it must know to begin with.
Getting the name servers for .com
So, who are the top level domain servers? I’ll ask the authoritative server directly (this is unnecessary if you just want the answer, so “dig NS com” would have been enough):
$ dig NS com @e.root-servers.net. ; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> NS com @e.root-servers.net. ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 11329 ;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 13, ADDITIONAL: 27 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1472 ;; QUESTION SECTION: ;com. IN NS ;; AUTHORITY SECTION: com. 172800 IN NS l.gtld-servers.net. com. 172800 IN NS b.gtld-servers.net. com. 172800 IN NS c.gtld-servers.net. com. 172800 IN NS d.gtld-servers.net. com. 172800 IN NS e.gtld-servers.net. com. 172800 IN NS f.gtld-servers.net. com. 172800 IN NS g.gtld-servers.net. com. 172800 IN NS a.gtld-servers.net. com. 172800 IN NS h.gtld-servers.net. com. 172800 IN NS i.gtld-servers.net. com. 172800 IN NS j.gtld-servers.net. com. 172800 IN NS k.gtld-servers.net. com. 172800 IN NS m.gtld-servers.net. ;; ADDITIONAL SECTION: l.gtld-servers.net. 172800 IN A 192.41.162.30 l.gtld-servers.net. 172800 IN AAAA 2001:500:d937::30 b.gtld-servers.net. 172800 IN A 192.33.14.30 b.gtld-servers.net. 172800 IN AAAA 2001:503:231d::2:30 c.gtld-servers.net. 172800 IN A 192.26.92.30 c.gtld-servers.net. 172800 IN AAAA 2001:503:83eb::30 d.gtld-servers.net. 172800 IN A 192.31.80.30 d.gtld-servers.net. 172800 IN AAAA 2001:500:856e::30 e.gtld-servers.net. 172800 IN A 192.12.94.30 e.gtld-servers.net. 172800 IN AAAA 2001:502:1ca1::30 f.gtld-servers.net. 172800 IN A 192.35.51.30 f.gtld-servers.net. 172800 IN AAAA 2001:503:d414::30 g.gtld-servers.net. 172800 IN A 192.42.93.30 g.gtld-servers.net. 172800 IN AAAA 2001:503:eea3::30 a.gtld-servers.net. 172800 IN A 192.5.6.30 a.gtld-servers.net. 172800 IN AAAA 2001:503:a83e::2:30 h.gtld-servers.net. 172800 IN A 192.54.112.30 h.gtld-servers.net. 172800 IN AAAA 2001:502:8cc::30 i.gtld-servers.net. 172800 IN A 192.43.172.30 i.gtld-servers.net. 172800 IN AAAA 2001:503:39c1::30 j.gtld-servers.net. 172800 IN A 192.48.79.30 j.gtld-servers.net. 172800 IN AAAA 2001:502:7094::30 k.gtld-servers.net. 172800 IN A 192.52.178.30 k.gtld-servers.net. 172800 IN AAAA 2001:503:d2d::30 m.gtld-servers.net. 172800 IN A 192.55.83.30 m.gtld-servers.net. 172800 IN AAAA 2001:501:b1f9::30 ;; Query time: 73 msec ;; SERVER: 192.203.230.10#53(192.203.230.10)
The next step: Get the domain’s name server
I just picked one of the name servers from the queries above. Once again, “dig NS google.com” will most likely give the same result.
$ dig NS google.com @j.gtld-servers.net.
; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> NS google.com @j.gtld-servers.net.
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37174
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 9
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;google.com. IN NS
;; AUTHORITY SECTION:
google.com. 172800 IN NS ns2.google.com.
google.com. 172800 IN NS ns1.google.com.
google.com. 172800 IN NS ns3.google.com.
google.com. 172800 IN NS ns4.google.com.
;; ADDITIONAL SECTION:
ns2.google.com. 172800 IN AAAA 2001:4860:4802:34::a
ns2.google.com. 172800 IN A 216.239.34.10
ns1.google.com. 172800 IN AAAA 2001:4860:4802:32::a
ns1.google.com. 172800 IN A 216.239.32.10
ns3.google.com. 172800 IN AAAA 2001:4860:4802:36::a
ns3.google.com. 172800 IN A 216.239.36.10
ns4.google.com. 172800 IN AAAA 2001:4860:4802:38::a
ns4.google.com. 172800 IN A 216.239.38.10
;; Query time: 74 msec
;; SERVER: 192.48.79.30#53(192.48.79.30)
So what’s the point in asking .com’s servers directly? For one, if I just changed the servers for my domain, and I want to see that change take effect immediately. Besides, I want the advertised TTL values and not those my ISP’s DNS happens to count down:
$ dig NS google.com
; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> NS google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 15069
;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 9
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;google.com. IN NS
;; ANSWER SECTION:
google.com. 62895 IN NS ns2.google.com.
google.com. 62895 IN NS ns1.google.com.
google.com. 62895 IN NS ns4.google.com.
google.com. 62895 IN NS ns3.google.com.
;; ADDITIONAL SECTION:
ns1.google.com. 170367 IN A 216.239.32.10
ns2.google.com. 240223 IN A 216.239.34.10
ns3.google.com. 238882 IN A 216.239.36.10
ns4.google.com. 248264 IN A 216.239.38.10
ns1.google.com. 170367 IN AAAA 2001:4860:4802:32::a
ns2.google.com. 167252 IN AAAA 2001:4860:4802:34::a
ns3.google.com. 167252 IN AAAA 2001:4860:4802:36::a
ns4.google.com. 159090 IN AAAA 2001:4860:4802:38::a
;; Query time: 21 msec
;; SERVER: 10.2.0.1#53(10.2.0.1)
Final step: Get the address (or something)
This is a bit stupid, but let’s finish up:
$ dig A google.com @ns3.google.com.
; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> A google.com @ns3.google.com.
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48652
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;google.com. IN A
;; ANSWER SECTION:
google.com. 300 IN A 216.58.206.14
;; Query time: 92 msec
;; SERVER: 216.239.36.10#53(216.239.36.10)
The local DNS had another answer in its cache. That’s OK. It also bombarded me with some other records, something an authoritative server is much less keen to do on an A query.
$ dig A google.com ; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> A google.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 35893 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 9 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;google.com. IN A ;; ANSWER SECTION: google.com. 235 IN A 172.217.23.174 ;; AUTHORITY SECTION: google.com. 152204 IN NS ns3.google.com. google.com. 152204 IN NS ns4.google.com. google.com. 152204 IN NS ns2.google.com. google.com. 152204 IN NS ns1.google.com. ;; ADDITIONAL SECTION: ns1.google.com. 344065 IN A 216.239.32.10 ns2.google.com. 310954 IN A 216.239.34.10 ns3.google.com. 324838 IN A 216.239.36.10 ns4.google.com. 247342 IN A 216.239.38.10 ns1.google.com. 254137 IN AAAA 2001:4860:4802:32::a ns2.google.com. 345035 IN AAAA 2001:4860:4802:34::a ns3.google.com. 345503 IN AAAA 2001:4860:4802:36::a ns4.google.com. 172241 IN AAAA 2001:4860:4802:38::a ;; Query time: 19 msec ;; SERVER: 10.2.0.1#53(10.2.0.1)
Glue records: Place for improvisations
Note that the name servers for google.com are subdomains of google.com. This is fine, because there are glue records in the “Additional Section” the give the IP addresses explicitly. Without these, it would have been impossible to resolve any of google.com’s addresses (it would have got stuck on obtaining the address of e.g. ns1.google.com).
That isn’t so trivial. For example, let’s look at netvision.net.il’s name server record, as reported by the authoritative server for net.il:
$ dig NS netvision.net.il @ns2.ns.il. ; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> NS netvision.net.il @ns2.ns.il. ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43809 ;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 3, ADDITIONAL: 3 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;netvision.net.il. IN NS ;; AUTHORITY SECTION: netvision.net.il. 86400 IN NS dns.netvision.net.il. netvision.net.il. 86400 IN NS eupop.netvision.net.il. netvision.net.il. 86400 IN NS nypop.elron.net. ;; ADDITIONAL SECTION: dns.netvision.net.il. 86400 IN A 194.90.1.5 eupop.netvision.net.il. 86400 IN A 212.143.194.5 ;; Query time: 73 msec ;; SERVER: 162.88.57.1#53(162.88.57.1)
Note that there are glue records only for the NS records that belong to netvision.net.il. The nypop.elron.net. server (extra backup?) doesn’t have a glue record. It could have, as a DNS is allowed to answer for another domain in the special case of a glue record (see RFC 1033).
OK, so how do you resolve nypop.elron.net? You ask the nameserver for elron.net, of course! Let’s ask the authoritative server for .net:
$ dig NS @a.gtld-servers.net. elron.net. ; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> NS @a.gtld-servers.net. elron.net. ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 27469 ;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;elron.net. IN NS ;; AUTHORITY SECTION: elron.net. 172800 IN NS dns.netvision.net.il. elron.net. 172800 IN NS nypop.netvision.net.il. ;; Query time: 82 msec ;; SERVER: 192.5.6.30#53(192.5.6.30)
Oops. That went back to netvision.net.il. So it doesn’t get us out of the loop. But here comes the funny part: Ask Netvision’s own DNS the same question:
$ dig NS elron.net. @dns.netvision.net.il ; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> NS elron.net. @dns.netvision.net.il ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10023 ;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 3 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ; COOKIE: c38902309e53e8ef454a61b25c98a0863c28b2c747ce090a (good) ;; QUESTION SECTION: ;elron.net. IN NS ;; ANSWER SECTION: elron.net. 600 IN NS nypop.elron.net. elron.net. 600 IN NS dns.netvision.net.il. ;; ADDITIONAL SECTION: dns.netvision.net.il. 86400 IN A 194.90.1.5 nypop.elron.net. 600 IN A 199.203.1.20 ;; Query time: 23 msec ;; SERVER: 194.90.1.5#53(194.90.1.5)
Cute, isn’t it? Not only is it a different answer, but the glue records are there. So if you make these queries from within Netvision’s infrastructure, you’re blind to the lack of glue records.
And these are the records of one of Israel’s largest ISPs.
nslookup
OK, this is soooo unrelated, and still, I thought I should mention nslookup as an alternative for dig. The former creates output that is more human readable, the latter more like zone file records. Pick your poison.
$ nslookup -type=NS google.com Server: 10.2.0.1 Address: 10.2.0.1#53 Non-authoritative answer: google.com nameserver = ns1.google.com. google.com nameserver = ns3.google.com. google.com nameserver = ns4.google.com. google.com nameserver = ns2.google.com. Authoritative answers can be found from: ns1.google.com internet address = 216.239.32.10 ns2.google.com internet address = 216.239.34.10 ns3.google.com internet address = 216.239.36.10 ns4.google.com internet address = 216.239.38.10 ns1.google.com has AAAA address 2001:4860:4802:32::a ns2.google.com has AAAA address 2001:4860:4802:34::a ns3.google.com has AAAA address 2001:4860:4802:36::a ns4.google.com has AAAA address 2001:4860:4802:38::a
and then, let’s ask the authoritative server the same question?
$ nslookup -type=NS google.com. j.gtld-servers.net. Server: j.gtld-servers.net. Address: 192.48.79.30#53 Non-authoritative answer: *** Can't find google.com.: No answer Authoritative answers can be found from: google.com nameserver = ns2.google.com. google.com nameserver = ns1.google.com. google.com nameserver = ns3.google.com. google.com nameserver = ns4.google.com. ns2.google.com has AAAA address 2001:4860:4802:34::a ns2.google.com internet address = 216.239.34.10 ns1.google.com has AAAA address 2001:4860:4802:32::a ns1.google.com internet address = 216.239.32.10 ns3.google.com has AAAA address 2001:4860:4802:36::a ns3.google.com internet address = 216.239.36.10 ns4.google.com has AAAA address 2001:4860:4802:38::a ns4.google.com internet address = 216.239.38.10
So it’s like more readable, but I miss those TTL records.