The DNS Lookup: Two Packets, Five Sections

The Domain Name System (DNS) acts as the internet's phonebook, translating human-readable domain names into machine-readable IP addresses. But how does this translation actually happen at the network level? The key lies in understanding the two packets involved in any DNS lookup: a query and a response. Both packets share a common structure, broken down into five essential parts: a header, a question section, an answer section, an authority section, and an additional section.

The header is always 12 bytes long and contains critical metadata about the DNS message. It includes fields such as the transaction ID, flags that indicate the message type (query or response) and status, and counts for the different sections that follow. This header is the control center of the DNS packet, providing context for the rest of the data.

Following the header is the question section. This part specifies what information the client is requesting. It typically includes the domain name being queried, the query type (e.g., A for IPv4 address, AAAA for IPv6 address, MX for mail exchange records), and the query class (usually IN for Internet).

The subsequent sections – answer, authority, and additional – are where the actual data resides. The answer section contains the resource records that directly answer the query. The authority section lists authoritative name servers for the domain, and the additional section can provide extra information related to the query, such as IP addresses of the name servers mentioned in the authority section.

Dissecting the DNS Header (12 Bytes)

The DNS header is the first 12 bytes of any DNS packet and is crucial for managing the communication. Let's break down its components:

  • Transaction ID (2 bytes): A unique identifier assigned by the client to match requests with their corresponding responses. This ensures that responses are correctly associated with the originating query, especially when multiple queries are in flight.
  • Flags (2 bytes): This field is a bitmask that conveys various pieces of information. Key bits include:
    • QR (Query/Response): 0 for query, 1 for response.
    • Opcode: Specifies the type of query (Standard Query is 0).
    • AA (Authoritative Answer): Indicates if the responding server is the authoritative source for the domain.
    • TC (Truncated): Signals if the message was too large to fit in a UDP packet and was truncated.
    • RD (Recursion Desired): Asks the server to perform recursive lookups if it doesn't have the answer directly.
    • RA (Recursion Available): Indicates if the server supports recursion.
    • Z (Reserved): Currently unused, must be zero.
    • RCODE (Response Code): Indicates the status of the response (e.g., NOERROR, NXDOMAIN, SERVFAIL).
  • QDCOUNT (Questions) (2 bytes): The number of question entries in the question section.
  • ANCOUNT (Answer RRs) (2 bytes): The number of resource record entries in the answer section.
  • NSCOUNT (Authority RRs) (2 bytes): The number of resource record entries in the authority section.
  • ARCOUNT (Additional RRs) (2 bytes): The number of resource record entries in the additional section.

Understanding these flags and counts is essential for parsing DNS packets correctly. The transaction ID, in particular, is vital for state management in any DNS client implementation.

The Question Section: What Are We Asking For?

The question section follows the header and describes the DNS record the client is looking for. It consists of three parts:

  • QNAME (Domain Name): This is the domain name for which the record is being queried. It's represented in a compressed or uncompressed format. In uncompressed format, labels are length-prefixed. For example, "www.example.com" would be represented as ` www extraple extcom ` (where ` ` is the byte representing the length of the following label).
  • QTYPE (Query Type): A 2-byte field specifying the type of DNS record being requested. Common types include:
    • A (1): IPv4 address
    • AAAA (28): IPv6 address
    • MX (15): Mail exchange records
    • CNAME (5): Canonical name (alias)
    • NS (2): Name server
    • TXT (16): Text records
  • QCLASS (Query Class): A 2-byte field that specifies the network class. The most common class is IN (Internet), represented by the value 1. Other classes exist but are rarely used.

The domain name representation is particularly interesting. DNS uses a technique called label compression where pointers are used to refer to previously occurring labels in the same message, saving bandwidth. For instance, if a domain name has already appeared in the header or an earlier question, subsequent occurrences can be represented by a pointer instead of repeating the full name.

Resource Records: The Answers, Authorities, and Additions

The remaining sections of a DNS packet contain Resource Records (RRs), which provide the actual information. Each RR has a standard format:

  • NAME: The domain name to which this resource record pertains. This field can also use label compression.
  • TYPE: A 2-byte field indicating the type of record (e.g., A, AAAA, MX, CNAME).
  • CLASS: A 2-byte field indicating the class of the record (usually IN for Internet).
  • TTL (Time To Live): A 4-byte unsigned integer representing the time in seconds that the record can be cached by a resolver. This is crucial for DNS propagation and caching strategies.
  • RDLENGTH: A 2-byte unsigned integer specifying the length, in bytes, of the RDATA field.
  • RDATA: The actual Rr data, the format of which depends on the TYPE. For an A record, it's an IPv4 address; for an AAAA record, it's an IPv6 address; for an MX record, it includes a preference value and the mail exchange server's domain name.

The answer section contains RRs that directly answer the query. The authority section contains RRs that point to the authoritative name servers for the queried domain. The additional section can contain RRs that provide supplementary information, such as the IP addresses of the name servers listed in the authority section, which can speed up subsequent lookups.

Building a `dig`-like Tool

Creating a tool like `dig` from scratch involves several key steps. First, you need to construct the DNS query packet. This means assembling the header with the correct flags and counts, and then formatting the question section with the domain name, type, and class.

The next critical step is sending this UDP packet to a DNS resolver. Common resolvers include Google's 8.8.8.8 or Cloudflare's 1.1.1.1. You'll need to bind to a UDP socket, send the query, and listen for the response.

Once a response is received, the real work of parsing begins. You must read the 12-byte header, interpret the flags and counts, and then parse the question, answer, authority, and additional sections based on the information in the header and the structure of DNS records. Handling label compression for domain names is a particularly important detail for accurate parsing.

The process of dissecting these packets byte by byte, as demonstrated by the companion code on GitHub, demystifies the fundamental workings of DNS. It moves beyond the abstract