Networking basics · Lesson 5 of 12 · about 25 minutes

DNS: turning names into addresses

What really happens in step 1 of every request: how your computer turns a name like api.civicgrid.org into an address, who it asks, the kinds of DNS records and what they're for, and why a DNS change can take a while to reach everyone. You'll follow a real lookup from the root of the internet down.

By the end you'll know

  • What DNS does, and who your computer asks
  • How a lookup travels from the root of the internet down to one name
  • The common record types: A, AAAA, CNAME, NS, MX and TXT
  • What a TTL is, and why DNS changes don't reach everyone at once
  • How to look up any name yourself with dig

Where you are

In Lesson 1, every request started with the same step: curl turned the name api.civicgrid.org into the addresses 104.21.69.165 and 172.67.210.139. Since then you've learned what those addresses are (Lesson 2), how they're grouped into ranges (Lesson 3), and how data reaches the right program once it gets there (Lesson 4).

This lesson goes back to that first step and opens it up. It's called DNS (Domain Name System), and Lesson 4 gave you a hint about it: it usually runs over UDP, on port 53.

A contacts list for the internet

You don't remember your friends' phone numbers. You tap a name in your contacts, and your phone looks up the number. DNS is that contacts list for the whole internet: you use names, because names are for people, and DNS finds the address, because addresses are what computers use.

The difference is that no single phone holds the whole internet's contacts. There are hundreds of millions of names, they change all the time, and each one is looked after by whoever owns it. So DNS is spread out: lots of servers, each one responsible for its own part of the list, and a way of finding which server knows which part.

Ask a name yourself

The tool for looking things up is dig, which comes with macOS and most Linux systems. (On Linux, if it's missing, it's in a package usually called dnsutils or bind-utils. On Windows, use WSL, or the similar nslookup command.) Ask for CivicGrid's API:

BASH
dig api.civicgrid.org
OUTPUT
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 11289
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; QUESTION SECTION:
;api.civicgrid.org.		IN	A

;; ANSWER SECTION:
api.civicgrid.org.	300	IN	A	104.21.69.165
api.civicgrid.org.	300	IN	A	172.67.210.139

;; Query time: 23 msec
;; SERVER: 192.168.1.1#53(192.168.1.1)

A few lines are trimmed from the top. It looks busy, but there are only four things to read:

  • status: NOERROR means the name exists and an answer came back.
  • The question section is what you asked: the A records, meaning IPv4 addresses, for api.civicgrid.org.
  • The answer section has one line per answer: the name, a number (300, explained below), IN for internet, the record type A, and the address. The same two addresses curl found in Lesson 1.
  • SERVER says who answered: 192.168.1.1, on port 53. That's the home router from Lesson 2, and port 53 from Lesson 4's table.

Most of the time you only want the answer, so dig has a short form:

BASH
dig +short api.civicgrid.org
OUTPUT
104.21.69.165
172.67.210.139

Who actually knows the answer

Your router didn't make that answer up, and it didn't know it in advance either. It's what's called a resolver: a server whose job is to find answers for you. Yours is probably your router, which passes questions on to your internet provider's resolver. Some people use public ones instead, like Cloudflare's 1.1.1.1 or Google's 8.8.8.8. You can ask a particular resolver by putting @ and its address first:

BASH
dig @1.1.1.1 +short api.civicgrid.org

The same two addresses come back, from a different resolver. To find the answer the first time, a resolver walks down the name from right to left. Names are really read backwards: api.civicgrid.org is "in .org, the one called civicgrid, the part called api". There's even an invisible dot at the end, which dig shows you: api.civicgrid.org.. That final dot is the root, the very top of DNS.

dig +trace does the walk itself, so you can watch each step:

BASH
dig +trace api.civicgrid.org

The real output is long, so this is trimmed to the important lines. The many lines about DNSSEC, which signs answers so they can't be faked, are left out:

OUTPUT
.			501565	IN	NS	a.root-servers.net.
.			501565	IN	NS	b.root-servers.net.
;; Received 1097 bytes from 192.168.1.1#53(192.168.1.1) in 8 ms

org.			172800	IN	NS	a0.org.afilias-nst.info.
org.			172800	IN	NS	b0.org.afilias-nst.org.
;; Received 825 bytes from 170.247.170.2#53(b.root-servers.net) in 11 ms

civicgrid.org.		3600	IN	NS	evelyn.ns.cloudflare.com.
civicgrid.org.		3600	IN	NS	aaron.ns.cloudflare.com.
;; Received 605 bytes from 199.19.56.1#53(a0.org.afilias-nst.info) in 176 ms

api.civicgrid.org.	300	IN	A	172.67.210.139
api.civicgrid.org.	300	IN	A	104.21.69.165
;; Received 78 bytes from 108.162.195.150#53(aaron.ns.cloudflare.com) in 14 ms

Read it as four short conversations. Each Received ... from line says who gave the block of answers above it:

  1. The resolver, your router, gave the list of root servers. There are 13 names, a to m.root-servers.net, and they're the top of the whole system.
  2. A root server didn't know api.civicgrid.org, but it knew who looks after .org, and said "ask them". That's an NS record: name server, meaning "this server is in charge of that part of the name".
  3. A .org server didn't know either, but knew that civicgrid.org is looked after by two of Cloudflare's servers, evelyn and aaron, and said "ask them".
  4. Cloudflare's server gave the real answer: the two addresses. A server that holds the real records for a name, rather than pointing somewhere else, is called authoritative for it.

Nobody in the chain knew everything; each one knew who to ask next. That's what lets DNS hold hundreds of millions of names. Normally your resolver does this walk for you, and you just see the final answer.

Following one lookup downYour computer asks its resolver for api.civicgrid.org. The resolver asks a root server, which says ask the .org servers. It asks a .org server, which says ask Cloudflare's servers, evelyn and aaron. It asks aaron.ns.cloudflare.com, which answers with 104.21.69.165 and 172.67.210.139. The resolver passes the answer back to your computer and keeps a copy for 300 seconds.Your computer"api.civicgrid.org?"Resolver192.168.1.1keeps a copy for 300 s123Root server"I don't know it. Ask the .org servers.".org server"Ask Cloudflare's: evelyn or aaron."aaron.ns.cloudflare.com104.21.69.165, 172.67.210.139Each server only knows who to ask next. Only the last one knows the answer.
A resolver finds an answer by asking down the chain: the root, then .org, then the name servers for civicgrid.org. Then it keeps a copy, so the next person who asks gets an answer straight away.

Kinds of records

An A record is only one kind. DNS holds several kinds of record for a name, and each answers a different question. These are the ones you'll meet:

TypeAnswersExample
AWhat's the IPv4 address?104.21.69.165
AAAAWhat's the IPv6 address?2606:4700:3034::6815:45a5
CNAMEIs this name another name in disguise?www.kwamib.dev is really a Vercel name
NSWhich servers are in charge of this domain?aaron.ns.cloudflare.com
MXWhere should email for this domain go?Gmail's mail servers
TXTAny notes? Often proof you own the domainA code a service asked you to add

Ask for a type by naming it. Here's AAAA:

BASH
dig AAAA +short api.civicgrid.org
OUTPUT
2606:4700:3034::6815:45a5
2606:4700:3035::ac43:d28b

So api.civicgrid.org does have IPv6 addresses, written in the shortened :: style from Lesson 2. Then why did Lesson 1 say IPv6: (none)? Most likely because the network it ran on had no IPv6 connection, and computers usually don't ask for IPv6 addresses they have no way to reach. The records were there; the computer just didn't ask for them.

A CNAME (canonical name) is an alias: "this name is really that name, go and look that up instead". Here's the www version of this site:

BASH
dig www.kwamib.dev

Just the answer section this time:

OUTPUT
;; ANSWER SECTION:
www.kwamib.dev.		1799	IN	CNAME	f72a5c22fb5f96b6.vercel-dns-017.com.
f72a5c22fb5f96b6.vercel-dns-017.com. 300 IN A	64.29.17.1
f72a5c22fb5f96b6.vercel-dns-017.com. 300 IN A	216.198.79.1

www.kwamib.dev has no address of its own. Its record says "I'm really this long Vercel name", and that name has the addresses. The point is that Vercel can change its addresses whenever it needs to, and the site keeps working, because the alias always points at whatever Vercel's name currently says.

MX (mail exchange) records say where email goes. They come with a number, the priority, where the lowest is tried first:

BASH
dig MX +short gmail.com
OUTPUT
40 alt4.gmail-smtp-in.l.google.com.
30 alt3.gmail-smtp-in.l.google.com.
10 alt1.gmail-smtp-in.l.google.com.
20 alt2.gmail-smtp-in.l.google.com.
5 gmail-smtp-in.l.google.com.

They come back in any order, but email to a Gmail address tries the server marked 5 first, then 10, and so on: backups built into DNS.

TTL: how long to remember

Remember the 300 in every answer line? That's the TTL (time to live): how many seconds anyone may keep a copy of this answer before asking again. Resolvers keep copies, called caching, so that the next person who asks for api.civicgrid.org gets an answer straight away, without the whole walk down from the root.

You can watch it count down. Ask twice, a few seconds apart:

BASH
dig api.civicgrid.org | grep -A3 "ANSWER SECTION"

The | from Lesson 4 passes dig's output to grep, which keeps only the line containing "ANSWER SECTION" and the three after it (-A3). The second time, a few seconds later:

OUTPUT
;; ANSWER SECTION:
api.civicgrid.org.	295	IN	A	172.67.210.139
api.civicgrid.org.	295	IN	A	104.21.69.165

300 became 295: the resolver is answering from its copy, and that copy has five seconds less to live. When it reaches 0, the resolver throws the copy away and asks again. The order of the two addresses changed too, which is normal: shuffling them spreads visitors across both.

TTLs are a trade-off. A long TTL means fewer lookups and faster answers, but when you change a record, some people keep the old answer until their copy runs out. A short TTL means changes spread quickly, at the cost of more lookups. Different records on the same domain often have different TTLs: here the name servers have 3,600 seconds, an hour, and the addresses 300, five minutes.

In my real setup

Look at the NS records in the trace: civicgrid.org is looked after by Cloudflare's name servers. That's what lets Cloudflare sit in front of the API, as Lesson 1 described: I keep the records there, and the addresses they give out are Cloudflare's, not my servers'. This site, kwamib.dev, is set up differently: its name servers belong to the company I registered the domain with, and the www record is a CNAME pointing at Vercel. Run dig NS +short kwamib.dev to see. Two common ways to do the same job.

Break it on purpose

Three cases. Work out the cause before you read the explanation.

A name that doesn't exist. Misspell the API's name:

BASH
dig apii.civicgrid.org
OUTPUT
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 20152
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

;; AUTHORITY SECTION:
civicgrid.org.		1800	IN	SOA	aaron.ns.cloudflare.com. dns.cloudflare.com. 2418182649 10000 2400 604800 1800

NXDOMAIN means "non-existent domain": the authoritative server was reached, and it says there's no such name. ANSWER: 0 agrees. The authority section shows who said so, Cloudflare's server, in a record called SOA (start of authority), which you only need to recognise. This is exactly what was behind Lesson 1's Could not resolve host: curl asked, and DNS said no such name. When you see that error, run dig on the name to check the spelling, or whether the record was ever created.

The right name, the wrong address. You can make one computer believe a name has a different address. curl's --resolve option does it for a single request:

BASH
curl -sv -o /dev/null --resolve api.civicgrid.org:443:127.0.0.1 https://api.civicgrid.org/
OUTPUT
* Added api.civicgrid.org:443:127.0.0.1 to DNS cache
* Hostname api.civicgrid.org was found in DNS cache
*   Trying 127.0.0.1:443...
* connect to 127.0.0.1 port 443 from 127.0.0.1 port 59130 failed: Connection refused

The name was fine, but curl was told its address is 127.0.0.1, your own computer, where nothing listens on port 443, so it's refused. Your computer also has a file that does this for every program: /etc/hosts on Mac and Linux. It's checked before DNS, and people add lines to it when testing, then forget them. If one machine can't reach a site that works everywhere else, cat /etc/hosts is worth a look.

Changed, but not for everyone. You move a website to a new server and update its A record, which had a TTL of 3,600. You check straight away and see the new address. Your friend still gets the old site for the next half hour. Why? Their resolver had a copy from before your change, and it's allowed to keep it until the TTL runs out: up to an hour. Nothing is broken; you just have to wait. The trick professionals use is to lower the TTL to something short, like 300, a day before a planned move, so that when the change happens, old copies expire within minutes.

Where you are now

You can now explain the whole of step 1 from Lesson 1: your computer asks a resolver, the resolver walks down from the root, and the answer comes back with a TTL that says how long to trust it. With Lessons 1 to 5 you've covered a single request end to end, from name to address to port to answer. Lesson 6 widens the view to how data travels between networks: routers, and the NAT that lets your whole home share one public address.

Check yourself

  1. What does a DNS resolver do?

  2. A root server is asked for api.civicgrid.org. What does it reply?

  3. Which record type says a name is an alias for another name?

  4. An answer has a TTL of 300. What does that mean?

  5. dig says status: NXDOMAIN. What happened?

  6. You changed an A record, but a friend still sees the old site. Why?

Key terms

  • DNS (Domain Name System): the system that turns names into addresses, spread across many servers.
  • Resolver: the server that finds answers for you, usually your router or internet provider.
  • Root servers: the top of DNS. They point to the servers for each ending, like .org.
  • Authoritative server: a server that holds the real records for a name.
  • Record: one piece of DNS information, like an A record for an address or an MX record for email.
  • CNAME (canonical name): a record that says one name is an alias for another.
  • TTL (time to live): how many seconds an answer may be kept and reused.
  • NXDOMAIN: the answer when a name doesn't exist.

Go deeper