ThinkAloud
Projects
ThinkAloud
  1. 01Projects21 ready to build and explain
Browse projects
Theme

Projects from Coding Challenges by John Crickett

Projects/DNS Resolver

Build your own DNS resolver

A command-line tool that turns a host name into an IP address by building DNS messages byte by byte, sending them over UDP and decoding the binary replies, and that ends by walking the name tree itself, from a root server down to the server that owns the answer. Building it teaches how a binary protocol is laid out, how a request and its reply find each other with no connection between them, and how the internet's naming system is split across many servers that each know only who to ask next.

From Coding Challenges #22 ↗ by John Crickett

  • Challenge #22
  • Intermediate
  • 8 concepts
  • 5 steps + 1 optional
  • Not yet checked
Start DNS Resolver

No sign-up. You tell the mentor your language: Java, Python or Node.js.

What you'll learn · 8 concepts

The concepts behind DNS Resolver

In the order the steps use them. Each one links to the best places to learn it, and you explain it in your own words once its steps are built.

  1. 1The DNS message: header, flags and sectionsStep 2, 4

    Step 1 writes a header and a question by hand, and step 3 reads all four sections back from the server's reply.

    Covers: one format for queries and responses · the fixed 12-byte header · the transaction ID · the flags and the response code · the four section counts · the question echoed in the response

    RFC 1035, section 4.1: Message format ↗ · Implement DNS in a weekend (Julia Evans), part 1: Build a DNS query ↗

  2. 2Byte order and bit fieldsStep 2

    Step 1 builds the header from 16-bit numbers and packed flags, and its check compares your bytes with a hex sample, where a swapped pair of bytes shows up at once.

    Covers: big-endian network byte order · how a processor stores numbers in memory · packing small fields into one number · shifts and masks · bit numbering in RFC diagrams · reading a hex dump

    Endianness (Wikipedia) ↗ · Beej's Guide to Network Programming: Byte Order ↗ · Mask (computing) (Wikipedia) ↗

  3. 3Domain names on the wire: labels and compression pointersStep 2, 4

    Step 1 encodes the query's name as labels, and step 3 must decode names in the reply that are partly or wholly pointers.

    Covers: length-prefixed labels · the empty root label · limits on label and name length · compression pointers and their offsets · where reading continues after a pointer · pointer loops and offsets out of range · names compared without case

    RFC 1035, section 4.1.4: Message compression ↗ · Implement DNS in a weekend (Julia Evans), part 2: Parse the response ↗ · RFC 4343: Domain Name System case insensitivity clarification ↗

  4. 4UDP exchanges: matching, timeouts and truncationStep 3

    Step 2 sends the query in one datagram and checks that the reply carries your ID, and step 4 sends many queries to servers that may never answer.

    Covers: datagrams versus a stream · no connection and no delivery guarantee · timeouts and retries · matching a reply by ID, address and port · forged replies and random IDs and ports · the size limit, the TC flag and falling back to TCP

    RFC 5452: Measures for making DNS more resilient against forged answers ↗ · RFC 7766: DNS transport over TCP ↗ · DNS Flag Day 2020: message size and fragmentation ↗

  5. 5Resource records: type, class, TTL and dataStep 4

    Step 3 reads the answer records with their types and TTLs, and the optional last step follows CNAME records to the address behind an alias.

    Covers: the shared record layout · record types such as A, AAAA, NS and CNAME · the data length and skipping unknown types · the TTL and caching · aliases and CNAME chains · no such name versus no data

    RFC 1035, section 3.2: Resource record definitions ↗ · RFC 2181: Clarifications to the DNS specification ↗ · List of DNS record types (Wikipedia) ↗

  6. 6The DNS hierarchy: root, top-level and authoritative serversStep 5

    Step 4 starts at a root server instead of a public resolver and walks down the tree one zone at a time.

    Covers: the name space as a tree · zones and delegation · root servers and anycast · top-level domain servers · authoritative servers and the AA flag · root hints as the starting point

    RFC 1034: Domain names, concepts and facilities ↗ · Root Server Technical Operations Association ↗ · Root Servers (IANA) ↗

  7. 7Recursive and iterative resolutionStep 5

    Steps 1 to 3 let a public resolver do the recursion for you, and step 4 clears the RD flag and does that work in your own program.

    Covers: stub resolvers, recursive resolvers and authoritative servers · the RD and RA flags · iterative queries and following referrals yourself · caching along the way · where your own resolver sits

    RFC 1034, section 4.3.1: Queries and responses ↗ · RFC 9499: DNS terminology ↗ · Implement DNS in a weekend (Julia Evans), part 3: Implement our resolver ↗

  8. 8Referrals and glue recordsStep 5

    Step 4 has to read each referral, use its glue when it is there, and resolve a name server on its own when it isn't.

    Covers: what a referral looks like · the authority and additional sections · glue and the chicken-and-egg problem · name servers without glue · trusting only in-bailiwick records · limits on loops and depth

    RFC 9471: DNS glue requirements in referral responses ↗ · An Illustrated Guide to the Kaminsky DNS Vulnerability (Steve Friedl) ↗

What you'll build · 5 steps

Build DNS Resolver step by step

Steps of 30 to 60 minutes, in your own editor and language. Each one has a check, so you know when it works.

  1. 01

    Set up

    Create the project in a language you're comfortable using for network programming, and keep a tool such as dig and a hex dump tool at hand to compare your bytes and answers with.

    Works whenYour program runs, and dig answers a lookup for a well-known name on your machine.

  2. 02

    Build the query

    Build a DNS query for a host name as raw bytes: a header with a random ID, only the recursion desired flag set, a question count of 1 and the other counts at 0, then a question holding the name as length-prefixed labels, type A and class IN. Let the ID be fixed for testing.

    Works whenWith the ID set to the one the challenge's sample uses, your bytes for dns.google.com match its hex exactly.

    Concepts: the DNS message · byte order and bit fields · domain names on the wire

  3. 03

    Send it over UDP

    Send the query in one UDP datagram to port 53 of a public recursive resolver, wait for the reply with a timeout, and print its bytes in hex. The server's address and port can be set from outside, with the public resolver as the default, so a test can point the program at a local server.

    Works whenA reply arrives, its first two bytes equal the ID you sent, and its layout looks like the challenge's sample response.

    Concepts: UDP exchanges

  4. 04

    Parse the response

    Decode the reply: the header and its flags, the question, and every record in the answer, authority and additional sections, following compression pointers to rebuild each name with dots. Confirm that the QR flag marks it as a response and that the ID is yours.

    Works whenFor dns.google.com the parsed reply has your ID, one question, two answers and empty authority and additional sections. Each answer has the queried name, type A, class IN, a TTL above zero and one of the two addresses of Google's public resolver.

    Concepts: the DNS message · domain names on the wire · resource records

  5. 05

    Resolve from the root

    Clear the recursion desired flag and resolve the name yourself. Start at a root server, and for each response either return the address from an A record or follow the referral: pick a listed name server, use its glue address if there is one, otherwise resolve that server's own name first, then ask it. Print each server you query. The starting root server's address, and the port used for every query, can be set from outside, with a real root server and port 53 as the defaults, so a sandbox can run the whole walk against local servers.

    Works whenResolving dns.google.com prints a root server, then a com server, then one of google.com's own servers, and ends with an address that step 3 also returned. A name whose name servers come without glue resolves too.

    Concepts: the DNS hierarchy · recursive and iterative resolution · referrals and glue records

  6. +

    Follow CNAME recordsOptional

    When the answer for a name is a CNAME, resolve the canonical name it points to, and keep going until you reach an address, with a limit so a loop of aliases can't run forever.

    Works whenA name that is an alias resolves to the address of its canonical name, and the output shows each name in the chain.

    Concepts: resource records

Ready to start DNS Resolver?

Start DNS Resolver

From Coding Challenges #22 ↗ by John Crickett. Notes written in our own words.

Projects from Coding Challenges by John Crickett