The Runtime Theory
SystemArchitecturenetwork

What happens when you resolve a hostname?

A step-by-step walk from getaddrinfo to /etc/hosts, the recursive resolver, and the root/TLD/authoritative server chain on UDP port 53.

The Runtime Theory Team1 min read06 steps

trace spine

  1. 01 Application calls getaddrinfo
  2. 02 nsswitch dispatch and /etc/hosts
  3. 03 Libc resolver cache lookup
  4. 04 UDP query to the recursive resolver
  5. 05 Recursive walk: root, TLD, authoritative
  6. 06 Answer cached with TTL

Before a single TCP connection can open, the machine has to turn example.com into an IP address. DNS looks like a single lookup, but it is a small distributed system running on your behalf. Here is the literal sequence.

trace stepApplication

The application calls getaddrinfo("example.com", "443", …) — every higher-level API (curl, socket.connect in Node, InetAddress in Java) bottoms out here. This is a libc function, so the call stays entirely in user space for now: no syscall has happened yet.

trace stepSystem

libc consults /etc/nsswitch.conf, which orders the name service sources. On a typical Linux box that is files dns: check /etc/hosts first. A manually entered hostname returns immediately — sub-microsecond, zero packets. Only on a miss does the resolver path start.

trace stepSystem

The stub resolver (libc's own, or systemd-resolved via NSS) checks its in-process cache. If a previous answer for this name is alive within its TTL, it returns the cached address and the whole trace stops here — typically 50–200 µs of user-space work with no network I/O.

trace stepSystem

Cache miss. The stub builds a query: a 12-byte header plus a question section (example.com, type A, class IN), then sends it as a single UDP datagram to the configured recursive resolver — commonly a local resolver on 127.0.0.53 or the router at 192.168.1.1, port 53. One sendto, one recvfrom. Because UDP has no handshake, the entire query is one round trip on the LAN: ~0.3–1 ms.

trace stepSystem

The recursive resolver walks the delegation chain. It asks a root server for com (a root hint already cached, anycasted globally), gets the com TLD server list, asks a TLD server for example.com, and finally asks the authoritative server that owns the zone. Each hop is a fresh UDP exchange, 5–30 ms each on a typical WAN. This is the expensive path — one cold lookup can take 50–150 ms.

trace stepSystem

The answer — example.com → 93.184.216.34 with TTL 3600 — comes back as a response packet. The stub stores it in its cache keyed by name and record type, and returns to the application. Subsequent lookups inside the TTL window never leave the machine.

bash
strace -e trace=network,read,write -f curl -s https://example.com 2>&1 | grep -E "socket|sendto|recvfrom"

The layers involved read like a ladder: application → libc (user space) → resolver cache (user space) → kernel UDP socket → LAN → recursive server → internet. Every stage after step 3 is network-bound, and every stage before it is memory-bound. That asymmetry is why DNS performance is cache performance.