Conceptually as a user there is not much difference between the topmost TLD, a domain within the TLD, and a "subdomain" (really, a "host") within a domain. Nor any other level under it.

The DNS root is .

Under the DNS root are the TLDs; com, net, org, and a bazillion other.

Then under those are domains. More or less. Some countries use for example co.tld instead of tld, and some use both.

Anyways, aside from things like glue records etc that the domain and tld owners have to concern themselves with, my claim is that for a user it is more or less the same.

If I tell you that my website is http://www.example.com/ then in theory you could do the following to resolve it:

- You don't know the IP of www.example.com so you have to find the Name Server for it.

- You don't know the NS of example.com so you decide that you should query the NS of com for it.

- You don't know the NS of com so you decide that you should query the DNS root . for it

https://en.wikipedia.org/wiki/DNS_root_zone

https://root-servers.org/

So let's query a root server

dig @198.41.0.4 com. NS

Command output with response and some tool specific stuff:

; <<>> DiG 9.10.6 <<>> @198.41.0.4 com. NS
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16618
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 13, ADDITIONAL: 27
;; WARNING: recursion requested but not available

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

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

;; ADDITIONAL SECTION:
e.gtld-servers.net. 172800 IN A 192.12.94.30
e.gtld-servers.net. 172800 IN AAAA 2001:502:1ca1::30
b.gtld-servers.net. 172800 IN A 192.33.14.30
b.gtld-servers.net. 172800 IN AAAA 2001:503:231d::2:30
j.gtld-servers.net. 172800 IN A 192.48.79.30
j.gtld-servers.net. 172800 IN AAAA 2001:502:7094::30
m.gtld-servers.net. 172800 IN A 192.55.83.30
m.gtld-servers.net. 172800 IN AAAA 2001:501:b1f9::30
i.gtld-servers.net. 172800 IN A 192.43.172.30
i.gtld-servers.net. 172800 IN AAAA 2001:503:39c1::30
f.gtld-servers.net. 172800 IN A 192.35.51.30
f.gtld-servers.net. 172800 IN AAAA 2001:503:d414::30
a.gtld-servers.net. 172800 IN A 192.5.6.30
a.gtld-servers.net. 172800 IN AAAA 2001:503:a83e::2:30
g.gtld-servers.net. 172800 IN A 192.42.93.30
g.gtld-servers.net. 172800 IN AAAA 2001:503:eea3::30
h.gtld-servers.net. 172800 IN A 192.54.112.30
h.gtld-servers.net. 172800 IN AAAA 2001:502:8cc::30
l.gtld-servers.net. 172800 IN A 192.41.162.30
l.gtld-servers.net. 172800 IN AAAA 2001:500:d937::30
k.gtld-servers.net. 172800 IN A 192.52.178.30
k.gtld-servers.net. 172800 IN AAAA 2001:503:d2d::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

;; Query time: 58 msec
;; SERVER: 198.41.0.4#53(198.41.0.4)
;; WHEN: Tue Jan 30 22:38:10 CET 2024
;; MSG SIZE rcvd: 828

Ok, and from that we can query one of the NS of com for example.com

dig @192.12.94.30 example.com NS

Output:

; <<>> DiG 9.10.6 <<>> @192.12.94.30 example.com NS
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4260
;; 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:
;example.com. IN NS

;; AUTHORITY SECTION:
example.com. 172800 IN NS a.iana-servers.net.
example.com. 172800 IN NS b.iana-servers.net.

;; Query time: 54 msec
;; SERVER: 192.12.94.30#53(192.12.94.30)
;; WHEN: Tue Jan 30 22:38:49 CET 2024
;; MSG SIZE rcvd: 88

And we can query the NS for example.com to find out the IP address of www.example.com

dig @a.iana-servers.net. www.example.com A

(You can see I skipped a couple of steps in the interest of brevity here, as I am suddenly querying an NS by its DNS name a.iana-servers.net. directly instead of via an IP address. But if you like you could imagine that we take the same steps to resolve a.iana-servers.net. from the DNS root up.)

And we get the following output:

; <<>> DiG 9.10.6 <<>> @a.iana-servers.net. www.example.com A
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54009
;; 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: 4096
;; QUESTION SECTION:
;www.example.com. IN A

;; ANSWER SECTION:
www.example.com. 86400 IN A 93.184.216.34

;; Query time: 114 msec
;; SERVER: 199.43.135.53#53(199.43.135.53)
;; WHEN: Tue Jan 30 22:39:10 CET 2024
;; MSG SIZE rcvd: 60

In reality most client devices will not resolve it from the root up. Instead, they will be told about local resolvers when they aquire DHCP lease on their local network, and they will ask those resolvers on the local network to resolve the domain, and they might do it either directly from cached values, or at least skipping a few steps because they already know which NS are in charge of which TLDs.

But what I am getting to is this:

We can ask the root servers for the NS for the TLD.

dig @198.41.0.4 ai. NS

which gives us

; <<>> DiG 9.10.6 <<>> @198.41.0.4 ai. NS
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 46879
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 8
;; WARNING: recursion requested but not available

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

;; AUTHORITY SECTION:
ai. 172800 IN NS anycastdns1-cz.nic.ai.
ai. 172800 IN NS anycastdns2-cz.nic.ai.
ai. 172800 IN NS pch.whois.ai.
ai. 172800 IN NS a.lactld.org.

;; ADDITIONAL SECTION:
anycastdns1-cz.nic.ai. 172800 IN A 185.28.194.194
anycastdns2-cz.nic.ai. 172800 IN A 185.38.108.108
anycastdns2-cz.nic.ai. 172800 IN AAAA 2a00:fea0:dead::beef
pch.whois.ai. 172800 IN A 204.61.216.123
pch.whois.ai. 172800 IN AAAA 2001:500:14:6123:ad::1
a.lactld.org. 172800 IN A 200.0.68.10
a.lactld.org. 172800 IN AAAA 2801:14:a000::10

;; Query time: 52 msec
;; SERVER: 198.41.0.4#53(198.41.0.4)
;; WHEN: Tue Jan 30 22:49:27 CET 2024
;; MSG SIZE rcvd: 291

And we can ask one of those for an IP address for the ai tld itself.

dig @204.61.216.123 ai. A

And in the case of the ai tld, the NS for the tld do indeed return an A record for the bare tld

; <<>> DiG 9.10.6 <<>> @204.61.216.123 ai. A
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 14414
;; 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: 4096
;; QUESTION SECTION:
;ai. IN A

;; ANSWER SECTION:
ai. 3600 IN A 209.59.119.34

;; Query time: 3074 msec
;; SERVER: 204.61.216.123#53(204.61.216.123)
;; WHEN: Tue Jan 30 22:50:38 CET 2024
;; MSG SIZE rcvd: 47

And there you have it. That's how it works.