How Does DNS Resolution Exactly Work?

How Does DNS Resolution Exactly Work?

How the f*ck does DNS work?  This may sound stupid — but at a low level, it’s often something that remains unknown to many in the IT industry — even seasoned professionals.

At the highest level, DNS is the basic protocol that maps hostnames to IP addresses. But there’s a whole lot more going on behind the scenes…

Stub Listeners

What Cloudflare’s blog article failed to reference is the stub listener, which is the first process to actively try and resolve upstream network requests on the client’s side. In Unix and Linux, systemd-resolved has a special address that it uses to listen for these initial DNS lookups, and that address is 127.0.0.53. For Windows PCs, if you are using DHCP, your internal DNS server settings are the equivalent of this stub listener and will be set by your default gateway, which for most common folk, is their router. On *nix systems, systemd-resolved also forwards requests to the DNS server set by your default gateway if you are using DHCP (generally-speaking). In Unix and Linux, this can be manually overridden (or initially set) by editing /etc/resolv.conf directly or a plethora of other ways, including adding a line in /etc/network/interfaces — or in Windows, by manually setting the IP of your local DNS server or a remote (recursive) DNS server in your IPv4 settings (or IPv6 if you like to live on the edge).

DNS Caching

One important thing to note is that DNS caching can take place at both the OS level, as well as the browser level (not to mention at every single level thereafter), which will skip all additional DNS lookups further along the way if the IP address for the hostname is already saved in these locations. To view your DNS cache in Google Chrome, you can go to chrome://net-internals/#dns.

Host file entries

DNS lookups can always be prevented or overridden by the operating system if you have a hostname associated with an IP address in your hosts file. This file is located at /etc/hosts on Unix and Linux, and [most of the time] at C:\Windows\System32\Drivers\etc\hosts on Windows, and they both require root/admin level permissions to edit.

DNS Forwarders

If you are like me and hate ads, then in addition to blocking them in the browser via extensions such as uBlock Origin, you also use something like PiHole or pfBlockerNG for pfSense firewalls. You normally point either your stub resolver or default gateway’s default DNS server to the IP running PiHole or pfSenseNG, which offer network-level ad-blocking capabilities in order to restrain bandwidth hogs caused by unnecessary DNS lookups (not to mention blocking ads/malware sites/c2 domains/etc., especially for mobile and IOT devices. Personally, I run dnscrypt-proxy on my PiHole so that remote DNS queries are sent upstream to Cloudflare via DNS over HTTPS, but that is a story for another blog post altogether.

These forwarders are then pointed to recursive DNS servers upstream such as Cloudflare’s 1.1.1.1 or Google’s 8.8.8.8. The point of forwarders such as PiHole and pfBlockerNG is to intercept any local DNS queries and serve them out, and then to block any unwanted remote DNS queries before they go out to the recursive resolver.

DNS Recursors

These are the recursive DNS nameservers that reside on the public Internet outside of your local network. For plebs, this means the ones that came provided by your ISP. For most IT professionals, these are set to the IPs of Cloudflare, Quad9, Level3, Google, or other public DNS servers, depending on what your tastes are and what the use case is.

This is what an DNS A record lookup would look like when querying through a specific DNS recursor (in this case, Google’s 8.8.4.4 recursive nameserver):

dig +A amazon.com @8.8.4.4

; <<>> DiG 9.11.3-1ubuntu1.9-Ubuntu <<>> A amazon.com @8.8.4.4
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10439
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;amazon.com.			IN	A

;; ANSWER SECTION:
amazon.com.		44	IN	A	176.32.98.166
amazon.com.		44	IN	A	176.32.103.205
amazon.com.		44	IN	A	205.251.242.103

;; Query time: 1 msec
;; SERVER: 8.8.4.4#53(8.8.4.4)
;; WHEN: Mon Oct 14 18:44:15 UTC 2019
;; MSG SIZE  rcvd: 87

Root Nameservers

DNS recursors work a hostname from back-to-front, so first it’s got to find out which nameserver is responsible for the top level domain (TLD), which in this case is “.com”. To find out which TLD nameservers are responsible for “.com”, a request would pretty much go like this if querying Cloudflare’s 1.1.1.1 recursive nameserver:

dig NS com @1.1.1.1

; <<>> DiG 9.11.3-1ubuntu1.9-Ubuntu <<>> NS com @1.1.1.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 25454
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1452
;; QUESTION SECTION:
;com.				IN	NS

;; ANSWER SECTION:
com.			10495	IN	NS	l.gtld-servers.net.
com.			10495	IN	NS	m.gtld-servers.net.
com.			10495	IN	NS	a.gtld-servers.net.
com.			10495	IN	NS	b.gtld-servers.net.
com.			10495	IN	NS	c.gtld-servers.net.
com.			10495	IN	NS	d.gtld-servers.net.
com.			10495	IN	NS	e.gtld-servers.net.
com.			10495	IN	NS	f.gtld-servers.net.
com.			10495	IN	NS	g.gtld-servers.net.
com.			10495	IN	NS	h.gtld-servers.net.
com.			10495	IN	NS	i.gtld-servers.net.
com.			10495	IN	NS	j.gtld-servers.net.
com.			10495	IN	NS	k.gtld-servers.net.

;; Query time: 1 msec
;; SERVER: 1.1.1.1#53(1.1.1.1)
;; WHEN: Mon Oct 14 18:46:53 UTC 2019
;; MSG SIZE  rcvd: 256

TLD Nameservers

So we can see that the top response from the root nameserver for “.com” domains is “l.gtld-servers.net.”. In order to lookup the authoritative nameservers from this TLD nameserver, we would do the following:

dig NS amazon.com @l.gtld-servers.net

; <<>> DiG 9.11.3-1ubuntu1.9-Ubuntu <<>> NS amazon.com @l.gtld-servers.net
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26684
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 6, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;amazon.com.			IN	NS

;; AUTHORITY SECTION:
amazon.com.		172800	IN	NS	pdns1.ultradns.net.
amazon.com.		172800	IN	NS	pdns6.ultradns.co.uk.
amazon.com.		172800	IN	NS	ns1.p31.dynect.net.
amazon.com.		172800	IN	NS	ns3.p31.dynect.net.
amazon.com.		172800	IN	NS	ns2.p31.dynect.net.
amazon.com.		172800	IN	NS	ns4.p31.dynect.net.

;; Query time: 7 msec
;; SERVER: 192.41.162.30#53(192.41.162.30)
;; WHEN: Mon Oct 14 18:55:20 UTC 2019
;; MSG SIZE  rcvd: 188

Authoritative Nameservers

From the above output, we can see from the AUTHORITY section which nameservers are authoritative for “amazon.com”. These are the final servers that would be requested for the “amazon.com” domain. So you could do an A record lookup directly from these servers and return the IPs associated with “amazon.com”:

dig A amazon.com @pdns1.ultradns.net

; <<>> DiG 9.11.3-1ubuntu1.9-Ubuntu <<>> A amazon.com @pdns1.ultradns.net
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5599
;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 6, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;amazon.com.			IN	A

;; ANSWER SECTION:
amazon.com.		60	IN	A	176.32.103.205
amazon.com.		60	IN	A	176.32.98.166
amazon.com.		60	IN	A	205.251.242.103

;; AUTHORITY SECTION:
amazon.com.		3600	IN	NS	pdns1.ultradns.net.
amazon.com.		3600	IN	NS	ns4.p31.dynect.net.
amazon.com.		3600	IN	NS	ns3.p31.dynect.net.
amazon.com.		3600	IN	NS	ns2.p31.dynect.net.
amazon.com.		3600	IN	NS	ns1.p31.dynect.net.
amazon.com.		3600	IN	NS	pdns6.ultradns.co.uk.

;; Query time: 1 msec
;; SERVER: 204.74.108.1#53(204.74.108.1)
;; WHEN: Mon Oct 14 19:00:52 UTC 2019
;; MSG SIZE  rcvd: 236

However, these nameservers are only authoritative for domains, and not necessarily subdomains. So working back-to-front, if you wanted to lookup the IP associated with “prime.amazon.com”, for example, the authoritative nameservers for “amazon.com” would have to do an additional lookup from the authoritative nameservers for “amazon.com”:

dig NS prime.amazon.com @pdns1.ultradns.net

; <<>> DiG 9.11.3-1ubuntu1.9-Ubuntu <<>> NS prime.amazon.com @pdns1.ultradns.net
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37571
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 6, ADDITIONAL: 7
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;prime.amazon.com.		IN	NS

;; AUTHORITY SECTION:
prime.amazon.com.	900	IN	NS	ns-931.amazon.com.
prime.amazon.com.	900	IN	NS	ns-912.amazon.com.
prime.amazon.com.	900	IN	NS	ns-921.amazon.com.
prime.amazon.com.	900	IN	NS	ns-932.amazon.com.
prime.amazon.com.	900	IN	NS	ns-911.amazon.com.
prime.amazon.com.	900	IN	NS	ns-923.amazon.com.

;; ADDITIONAL SECTION:
ns-911.amazon.com.	900	IN	A	52.9.140.222
ns-912.amazon.com.	120	IN	A	52.9.146.37
ns-921.amazon.com.	900	IN	A	34.196.62.143
ns-923.amazon.com.	120	IN	A	52.86.96.73
ns-931.amazon.com.	900	IN	A	52.19.138.45
ns-932.amazon.com.	900	IN	A	52.16.221.207

;; Query time: 4 msec
;; SERVER: 204.74.108.1#53(204.74.108.1)
;; WHEN: Mon Oct 14 19:06:01 UTC 2019
;; MSG SIZE  rcvd: 267

Now that the authoritative nameservers for amazon.com know which nameservers are authoritative for “prime.amazon.com”, they can query one of those authoritative nameservers for an A record, which will finally return an IP address:

dig A prime.amazon.com @ns-931.amazon.com

; <<>> DiG 9.11.3-1ubuntu1.9-Ubuntu <<>> A prime.amazon.com @ns-931.amazon.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 56957
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;prime.amazon.com.		IN	A

;; ANSWER SECTION:
prime.amazon.com.	60	IN	A	72.21.215.251

;; Query time: 70 msec
;; SERVER: 52.19.138.45#53(52.19.138.45)
;; WHEN: Mon Oct 14 19:09:08 UTC 2019
;; MSG SIZE  rcvd: 50

In the ANSWER section above, you can see that the DNS A record “72.21.215.251” is associated with the hostname “prime.amazon.com”. The authoritative nameserver “ns-931.amazon.com” served the final A record that was mapped to “prime.amazon.com”, which it will send all the way back through the chain to your browser, which will cause you to end up at the right place on the web. Furthermore, when utilizing DNS caching at various points along the way, all of this information is served back to you in less than half a second when making the initial request through a stub resolver:

dig A prime.amazon.com

; <<>> DiG 9.11.3-1ubuntu1.9-Ubuntu <<>> A prime.amazon.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57070
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;prime.amazon.com.		IN	A

;; ANSWER SECTION:
prime.amazon.com.	60	IN	A	176.32.98.184

;; Query time: 26 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Mon Oct 14 19:13:13 UTC 2019
;; MSG SIZE  rcvd: 61


real	0m0.035s
user	0m0.004s
sys	0m0.004s

Order of Operations

I am no DNS expert, but from what I gather, below is the DNS order of operations (taking remote DNS caching out of the equation). Feel free to leave a comment if you see something in need of correction, and I will correct it in the post.

  • Host file entry lookup
  • Browser DNS cache lookup
  • OS DNS cache lookup
  • DNS stub forwarder lookup
  • Local DNS server lookup (set by default gateway, or manually pointing to a local DNS server or DNS forwarder
  • Recursive DNS server
  • Root DNS server
  • TLD DNS server
  • Authoritative DNS server

…and finally, the last response bounces all the way back from whence it came until it reaches the browser — so you can see why caching is used heavily with DNS — because it can reduce roundtrip times by leaps and bounds (or hops, to be more specific).


Further Reading:

Leave a Reply