DNS queries and responses begin with a 12-byte header followed by Question, Answer, Authority, and Additional sections. All multi-octet integer fields use network byte order. RFC 1035 defines the base format; EDNS, DNSSEC, and later extensions add records and header semantics without changing the base header.
Header #
| Field | Bits | Purpose |
|---|---|---|
| ID | 16 | Client-selected identifier echoed in the response |
| QR | 1 | Query (0) or response (1) |
| Opcode | 4 | Operation; QUERY is zero |
| AA | 1 | Authoritative answer indicator in responses |
| TC | 1 | Message was truncated |
| RD | 1 | Client requests recursion |
| RA | 1 | Server offers recursion |
| Z | 3 | Reserved base bits; extensions use AD and CD |
| RCODE | 4 | Base response code |
| QDCOUNT | 16 | Question count |
| ANCOUNT | 16 | Answer record count |
| NSCOUNT | 16 | Authority record count |
| ARCOUNT | 16 | Additional record count |
RD asks the receiving server to perform recursion; RA advertises that the
server can do so. AA says the responding server is authoritative for the
answer, not that every record in the message is authoritative. TC tells a
client that it must retry over TCP or another supported transport when the
truncated response is insufficient.
Names and Questions #
A domain name is a sequence of labels. Each ordinary label begins with an octet length from 0 to 63, and the zero-length root label terminates the name:
03 www 07 example 03 com 00The complete encoded name, including length octets and root label, is limited
to 255 octets. A Question contains QNAME, a 16-bit QTYPE, and a 16-bit
QCLASS; Internet class (IN) is value 1.
To avoid repeated suffixes, a name can use a compression pointer. A pointer’s
first two bits are 11; its remaining 14 bits are an offset from the message
start. Pointers may point only to an earlier location. Parsers must bound
pointer depth, validate offsets, and detect loops because DNS messages are
untrusted input.
Resource Records #
Answer, Authority, and Additional sections carry resource records:
NAME | TYPE | CLASS | TTL | RDLENGTH | RDATATTL is encoded in a 32-bit field and treated as a nonnegative cache lifetime
with an effective range of 0 through \(2^{31}-1\) under RFC 2181. RDLENGTH bounds the
type-specific RDATA bytes. An RRset is the set of records with the same owner
name, class, and type; DNSSEC signs RRsets rather than individual records.
Common RDATA includes four-byte IPv4 addresses (A), 16-byte IPv6 addresses
(AAAA), domain names (NS, CNAME, PTR), and structured values such as
MX preference plus exchange name or the SOA timers. Names in RDATA may also
use compression where the applicable RFC permits it.
EDNS uses an OPT pseudo-record in the Additional section to advertise a UDP
payload size, extended RCODE bits, flags, and options. A server must not assume
that UDP DNS is limited to 512 bytes when EDNS has been negotiated, but clients
must still handle truncation and retry.
Safe Parsing #
Treat the received datagram or stream frame as the only valid byte range. Before reading each fixed-width field, label, pointer, or RDATA value, verify that the complete field remains inside that range. Section counts are untrusted; stop if the claimed number of records cannot fit.
Name decompression needs two positions: the caller’s next unread offset and the pointer-following offset used to reconstruct the name. Count pointer hops or track visited offsets to reject cycles, reject reserved label prefixes, enforce the 63-octet label and 255-octet expanded-name limits, and never interpret a pointer target outside the message.
For TCP and TLS transports, a two-octet length prefix frames each DNS message
but is not part of the DNS header. Read exactly that many bytes before parsing.
For an OPT record, combine the extended RCODE from its TTL field with the base
header RCODE and parse each option within both RDLENGTH and the message bound.
Comments
Read or comment on GitHub (sign-in required to comment)