DNS Wire Format

Last update: 2025-05-29 15:04:54 +0800 +0800
Available translations: 日本語 / 中文(简体) / 中文(繁體)

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.

FieldBitsPurpose
ID16Client-selected identifier echoed in the response
QR1Query (0) or response (1)
Opcode4Operation; QUERY is zero
AA1Authoritative answer indicator in responses
TC1Message was truncated
RD1Client requests recursion
RA1Server offers recursion
Z3Reserved base bits; extensions use AD and CD
RCODE4Base response code
QDCOUNT16Question count
ANCOUNT16Answer record count
NSCOUNT16Authority record count
ARCOUNT16Additional 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:

text
03 www 07 example 03 com 00

The 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:

text
NAME | TYPE | CLASS | TTL | RDLENGTH | RDATA

TTL 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.

References #

Comments

Read or comment on GitHub (sign-in required to comment)