On this page
DNS Protocol and Resolution
The Internet's "phone book"—from root servers to recursive resolvers to authoritative servers, a single DNS query can trigger dozens of delegation lookups. Mechanisms such as EDNS0, Anycast, and negative caching keep DNS available under daily query volumes in the trillions.
Overview
DNS is the Internet's "phone book"—translating human-readable domain names into IP addresses. Designed in 1983 (RFC 882/883), its distributed and hierarchical architecture allows it to scale to Internet size: from root to TLD to authoritative, each layer is responsible only for its own namespace. Recursive resolvers do the "heavy lifting"—completing the entire process from root to answer on behalf of stub clients. DNS was originally designed as an insecure protocol (UDP cleartext); later additions like DNSSEC and encrypted transports (DoT/DoH/DoQ) have been added to address this deficiency.
DNS Query Flow
Three types of queries:
- Recursive (RD=1): The resolver recursively handles the entire process on behalf of the client (stub → recursive)
- Iterative: Returns a referral or answer in one step (recursive → authoritative)
- Inverse: PTR query (IP → name, rarely used)
DNS Wire Format
DNS Message (TCP or UDP):
Header (12 bytes):
Transaction ID (2B): Random, matches request ⇔ response
Flags (2B):
QR (bit 0): 0=Query, 1=Response
OPCODE (bits 1-4): 0=Standard, 1=Inverse, 2=Status, 4=Notify, 5=Update
AA (bit 5): Authoritative Answer
TC (bit 6): Truncated (use TCP)
RD (bit 7): Recursion Desired
RA (bit 8): Recursion Available
Z (bits 9-11): 0 (reserved)
RCODE (bits 12-15): 0=NoError, 1=FormErr, 2=SrvFail, 3=NXDomain, 5=Refused
QDCOUNT (2B): Questions count
ANCOUNT (2B): Answers count
NSCOUNT (2B): Authority records count
ARCOUNT (2B): Additional records count
Question Section (variable):
QNAME: label format (see below)
QTYPE (2B): 1=A, 28=AAAA, 5=CNAME, 15=MX, 2=NS, 6=SOA, 33=SRV, ...
QCLASS (2B): 1=IN (Internet)
Answer/Authority/Additional Sections (variable):
Each RR:
NAME: label format (usually compressed — pointer to previous name)
TYPE (2B)
CLASS (2B): 1=IN
TTL (4B): Cache validity period (seconds)
RDLENGTH (2B): RDATA length
RDATA: type-specific
Label Format and Compression
Label encoding:
[length 1B][label data ...] → length + data, each label is independent
Last label: length=0 (root, empty label)
Example: www.example.com → 0x03 'w' 'w' 'w' 0x07 'e' 'x' 'a' 'm' 'p' 'l' 'e' 0x03 'c' 'o' 'm' 0x00
DNS Compression (saves space):
High 2 bits of length byte = 11 → this is a pointer (not a label!)
pointer = low 14 bits of offset (from DNS message start)
Example: 0xC0 0x0C → points to offset 12 (QNAME of the first question)
This allows referencing domain names that appeared earlier in the same message
→ "example.com" appears in QNAME, NAME of A record, RDATA of NS record → three references → saves ~12+12+12 = 36 bytes
Key Record Types
A (type 1): RDATA = 4-byte IPv4 address
AAAA (type 28): RDATA = 16-byte IPv6 address
CNAME (type 5): RDATA = canonical name (label format) — alias resolution
→ Restriction: CNAME cannot coexist with other record types (except RRSIG/NSEC)
That is: if www.example.com is a CNAME, it cannot also have an A record
MX (type 15): RDATA = preference (2B) + exchange (label format)
→ Priority: lower preference value = higher priority
SOA (type 6): RDATA = mname + rname + serial + refresh + retry + expire + minimum TTL
→ Authoritative information for the zone: serial is used for zone transfer version comparison
NS (type 2): RDATA = nameserver (label format) — nameserver authorized for this zone
SRV (type 33): RDATA = priority + weight + port + target
→ _service._proto.name → target host:port
Example: _sip._tcp.example.com SRV 10 60 5060 sipserver.example.com
PTR (type 12): RDATA = name (label format) — reverse lookup (IP → name)
→ in-addr.arpa for IPv4, ip6.arpa for IPv6
TXT (type 16): RDATA = one or more text strings (length-prefixed)
→ SPF, DKIM, DMARC, verification tokens
CAA (type 257): RDATA = flags + tag + value
→ Restricts which CAs can issue certificates for this domain
→ Example: 0 issue "letsencrypt.org" → only Let's Encrypt can issue
EDNS0 (RFC 6891)
Traditional DNS limitation: UDP payload max 512B → large responses (DNSSEC RRSIG) require TCP fallback
EDNS0: Adds OPT pseudo-RR in the Additional section:
NAME=0 (root), TYPE=41 (OPT), CLASS=UDP payload size, TTL=flags+RCODE
UDP payload size: Announces the maximum UDP response size the receiver can handle (usually 1232 or 4096)
→ Reduces the number of TCP fallbacks
DNSSEC OK (DO bit): Client supports DNSSEC → server should include RRSIG
flags: DNSSEC OK (DO bit 15)
AXFR (Zone Transfer)
AXFR (full zone transfer):
Commonly used for: primary → secondary nameserver synchronization
TCP connection (zone transfer may be >512B):
secondary: AXFR request (type 252, class IN) → primary
primary: Returns all RRs in the zone (SOA ... SOA, with SOA at both ends)
IXFR (incremental zone transfer, RFC 1995):
Transfers only changes (based on SOA serial)
References
- RFC: 1034, 1035, 6891, 3596 (AAAA), 2782 (SRV), 6844 (CAA)
- Tools:
dig +trace,drill,kdig,delv,tcpdump -nn port 53 - Source Code: glibc
resolv/,systemd-resolved
Keywords: DNS, A/AAAA/MX/NS/SOA/SRV/CNAME/TXT/CAA, recursive resolver, EDNS0, label compression, zone transfer