How the Domain Name System encodes queries, responses, and resource records in binary format
Every time you type a domain name into your browser, your computer sends a DNS query — a compact binary message — to a DNS server to find out the corresponding IP address. I have analyzed hundreds of DNS packets over my career, and the protocol is beautifully efficient: it is a pure binary protocol with no text parsing overhead, no variable-length encoding ambiguity (except for the clever label compression scheme I will cover below), and a fixed-format 12-byte header that every DNS implementation in the world parses identically. The entire DNS specification fits in about 40 pages of RFC 1035, and most of that is the binary layout of each message section.
Every DNS message begins with a 12-byte (96-bit) header. I have memorized this layout from writing DNS parsers, and here it is field by field:
01001010 10111101 might be a transaction ID of 0x4ABD.0000 for a standard query, 0001 for an inverse query, 0010 for a server status request.0000 = No error, 0011 = NXDOMAIN (domain does not exist), 0101 = Refused.00000000 00000001 (1).I have written DNS debugging tools that parse this 12-byte header to detect common issues: truncated responses (TC bit set), NXDOMAIN responses (RCODE = 3), and responses with zero answers (ANCOUNT = 0). These four count fields (QDCOUNT, ANCOUNT, NSCOUNT, ARCOUNT) tell you exactly how many records follow in each section.
After the header comes the Question section. Each question contains three parts: the domain name (encoded in a special binary format), the query type (16 bits), and the query class (16 bits). The domain name encoding is one of my favorite parts of the DNS protocol because it is so space-efficient. Each label in the domain is prefixed with a 1-byte length counter. For example, www.example.com is encoded as:
00000011 (3) — "www" is 3 characters01110111 01110111 01110111 — ASCII for "www"00000111 (7) — "example" is 7 characters01100101 01111000 01100001 01101101 01110000 01101100 01100101 — ASCII for "example"00000011 (3) — "com" is 3 characters01100011 01101111 01101101 — ASCII for "com"00000000 (0) — The root label (zero-length), marks the end of the nameThe total for "www.example.com" is 15 bytes for the labels plus 4 bytes for the three length prefixes and 1 byte for the root label = 19 bytes. The root label (a single zero byte) terminates every domain name. After the name comes QTYPE (16 bits, e.g., 00000000 00000001 for an A record) and QCLASS (16 bits, 00000000 00000001 for the Internet class).
DNS uses a clever binary compression scheme to avoid repeating domain names within a single message. When the two highest bits of a label's length byte are both 1 (11xxxxxx), the byte is not a length — it is a pointer to another location in the message where the domain name continues. The pointer consists of 2 bytes: the first has bits 11 as the compression marker, and the remaining 14 bits are an offset from the start of the DNS message. For example, if a response contains the same domain name in the Question section, Answer section, and Authority section, only the first occurrence is spelled out in full. All subsequent occurrences use a 2-byte pointer (11000000 00001100 means "go to offset 12 in the message"). I have seen naive DNS implementations that forget to handle compression and fail to parse responses with large Answer sections — the presence of 11 in the top two bits of a label length byte must always be checked.
Pro Tip: When parsing DNS manually in a hex dump, look for bytes starting with 11 in the high bits (values 0xC0-0xFF) within the domain name area. Those are compression pointers, not label lengths. If you interpret them as ASCII character counts, you will completely misparse the record.
Each resource record in a DNS response follows a fixed binary format. Whether it is an A record, AAAA record, MX record, or CNAME record, the first part is always the same:
00000000 00000001 (1) = A record (IPv4 address), 00000000 00011100 (28) = AAAA record (IPv6 address), 00000000 00001111 (15) = MX record (mail exchange), 00000000 00000101 (5) = CNAME record (canonical name alias), 00000000 00000010 (2) = NS record (nameserver).00000000 00000001 (1) for the Internet class.00000000 00000000 00000000 00011100 is 300 seconds (5 minutes). A value of 00000000 00000000 00000111 01100000 is 86400 seconds (24 hours).I have built DNS response parsers that use this exact structure to extract records from raw packets. The TTL field (32 bits) is particularly important for cache management — a misconfigured TTL can cause stale DNS entries to persist for hours or days.
The QTYPE field in a DNS question tells the server which type of record you want. The most common types and their binary values are:
00000000 00000001 — Request an IPv4 address. This is the most common query type, used for nearly every website lookup.00000000 00011100 — Request an IPv6 address. Growing in usage as IPv6 adoption increases.00000000 00001111 — Request mail exchange records for email routing.00000000 00000101 — Request the canonical name (alias target).00000000 00000010 — Request authoritative name servers.00000000 00010000 — Request text records, used for SPF, DKIM, and verification tokens.In my network diagnostics, when a query returns zero answers (ANCOUNT = 0) but no error (RCODE = 0), it usually means the queried type does not exist for that domain. For example, querying for AAAA on a domain that only has A records will return an empty Answer section.
DNS traditionally runs over UDP port 53. The maximum size of a classic DNS message over UDP is 512 bytes. When the response exceeds 512 bytes, the server sets the TC (Truncation) bit in the header, and the client must re-query over TCP port 53. With EDNS0 (Extension Mechanisms for DNS), clients can advertise a larger UDP buffer size in an OPT pseudo-record, up to 4096 bytes. In binary, EDNS0 adds a record with TYPE = 00000000 01000001 (41) in the Additional section. I've observed that many public DNS resolvers now support EDNS0 buffer sizes of 4096 or even 8192, which avoids the TCP fallback overhead for most queries.
Let me walk through a complete DNS response for example.com in binary. The 12-byte header starts with the query ID (echoed from the query), followed by the flags byte with QR=1, then the 4 count fields. The Question section repeats the domain name example.com (possibly using a compression pointer to the query section), QTYPE = A (00000000 00000001), QCLASS = IN (00000000 00000001). The Answer section contains one A record: NAME (likely a 2-byte pointer to the name in the question), TYPE = A, CLASS = IN, TTL (32 bits, e.g., 3600 seconds = 00000000 00000000 00001110 00010000), RDLENGTH = 4, RDATA = 01011110 01110010 10101100 00001010 (93.184.216.10). The Authority and Additional sections may contain NS records and glue A records. Total response size: about 50-80 bytes for a simple lookup. I have traced through this exact binary structure more times than I can count when debugging DNS resolution issues.
Open the Binary Code Decoder in a new tab and enter some binary patterns to see the results instantly. All conversions happen in your browser — no data is sent to any server.