Digging to the root with DNS queries

This post was written by eli on March 30, 2019
Posted Under: Internet,Server admin

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.

Add a Comment

required, use real name
required, will not be published
optional, your blog address