Anatomy of a Miniature DNS Resolver

That iterative process—starting at the root nameservers and following referrals down the hierarchy until an answer arrives—is exactly what a DNS resolver does. Julia Evans has a tiny Go implementation worth reading: the core resolve function is only about 20 lines. The program relies on the miekg/dns library for the mechanical work of encoding and decoding DNS packets, which keeps the focus on the traversal logic rather than on binary format handling.

Four Sections, Not Two

A DNS response is commonly thought of as simply a question and an answer. In reality, each packet has up to four sections, and a resolver depends on all of them:

  • Question: the query name (e.g., example.com.), record type (e.g., A), and class.
  • Answer: the records that directly answer the question. This is where the IP address for a name like example.com shows up.
  • Authority (called Ns in miekg/dns): contains records that refer the resolver elsewhere. A nameserver that says “I don’t know, but a.iana-servers.net does” puts that information here as an NS record.
  • Additional: the “glue” section. When a referral names a server, it often includes that server’s IP address as a convenience so the resolver doesn’t have to chase it down separately.

All records in the latter three sections share a common header holding a name, type, class, and TTL.

The Core Loop

The resolver starts with the IP addresses of the root nameservers hardcoded. Then it loops:

  1. Send the query to the current nameserver and parse the response.
  2. If the Answer section has an A record for the target name, return it.
  3. Otherwise, check the Additional section for a glue record—if found, use that IP as the next nameserver.
  4. If no glue record is present, take the NS name from the Authority section, resolve that name’s IP address (usually via a quick recursive helper call), and use it for the next iteration.

That’s the entire algorithm. No caching, no retry logic, no DNSSEC—just referral-following.

Three Runs, Three Costs

The tiny resolver logs each query it makes, formatted as a dig -r @SERVER DOMAIN command so a human can replay the same query manually. The number of queries varies wildly by domain.

For jvns.ca, the resolver made six queries: three to walk from the root down to the ca. and jvns.ca. nameservers, and three more to look up the IP of the domain’s nameserver (art.ns.cloudflare.com) because no glue record was available.

archive.org needed only three queries. Its nameserver (ns1.archive.org.) shipped a glue record in the referral, so the resolver could skip the extra lookup entirely.

For www.maths.ox.ac.uk, the count climbed to seven. That domain sits behind a longer hierarchy: the root server, then uk., then ac.uk., then ox.ac.uk.—four levels instead of three. Each level costs a query, and occasionally an extra one for a nameserver address.

Real Resolvers Keep Going

My tiny resolver stops as soon as it has an answer. On 500 test domains, its recursion depth—call-chain length for resolving nameserver IPs—exceeded two only in rare manufactured cases. But a production resolver like unbound behaves differently.

When unbound on a laptop resolves reddit.com, it makes the same initial queries as the toy version (assuming it has the root and com. server addresses cached). But after the answer arrives, it continues querying for the addresses of other nameservers in the referral chain. The motivation is future-proofing: caching those addresses preemptively avoids latency on subsequent lookups.

Gaps Versus Production Resolvers

The tiny resolver is deliberately minimal. It handles only A records, ignores CNAMEs entirely, returns a single answer even when multiple exist, and panics if a lookup fails. Its glue-record handling doesn’t verify that the IP corresponds to the nameserver in the Authority section (though it works in practice). There’s no cache, no fallback when a nameserver times out, and no DNSSEC validation.

A separate bash version of the same algorithm exists too, around 36 lines and relying on grep in creative ways. Despite the jank, it resolves the same domains, which says something about how simple the core referral logic really is.

The model is a spectrum: on one end, a 20-line Go function demonstrating the mechanics; on the other, the sprawling complexity of DNS software that runs the internet. The toy version is good enough to make the iteration visible—you can see exactly what queries it issue and why today’s internet DNS delegation still follows that straightforward referral path today.