Safeguard
Supply Chain Security•

DNS on the Wire: Message Format, Record Types and Resolver Hardening

DNS maps names to data using a 12 byte header and four sections over UDP or TCP port 53. Here is the wire format, record types and resolver hardening.

Safeguard Security Team
47 min read

The Domain Name System (DNS) is the Internet's distributed lookup protocol: it turns a name such as www.example.com into the data a client needs to connect, most often an IP address, by asking a chain of servers that each know one part of the namespace. It is an application layer protocol defined by RFC 1034 and RFC 1035 (STD 13, November 1987), and it runs over UDP and TCP port 53, with encrypted variants on port 853 and over HTTPS.

Every DNS exchange uses the same small binary message: a 12 byte header with a 16 bit ID and a set of flags, then a question and three sections of resource records. Most DNS security problems trace back to that format and to the trust a resolver places in whatever arrives on port 53. This post covers the format field by field, the resolution path, the record types that matter, the extensions that changed the protocol and how to harden and check a resolver you run.

We already have narrower posts on recursive versus authoritative servers, DNSSEC, DoH versus DoT versus DNSSEC, DNS attack techniques, zone transfers and CAA records. This is the protocol reference underneath them.

Key takeaways

  • RFC 1034 (concepts) and RFC 1035 (implementation) are still the core of DNS. Together they are Internet Standard 13 and obsolete RFC 882, RFC 883 and RFC 973. Dozens of later RFCs update them, and RFC 9499 (BCP 219, March 2024) is the current terminology reference.
  • A DNS message has a 12 byte header (ID, flags, opcode, response code and four section counts) and four sections: question, answer, authority and additional. The same format carries queries and responses.
  • Classic DNS uses UDP and TCP port 53. RFC 1035 caps plain UDP messages at 512 bytes; EDNS(0) (RFC 6891, STD 75, April 2013) lets a client advertise a larger buffer, and current resolver defaults sit at 1232 bytes to avoid IP fragmentation.
  • RFC 7766 (March 2016) makes TCP support mandatory for every general purpose DNS implementation, and RFC 9210 (BCP 235, March 2022) makes allowing DNS over TCP an operational Best Current Practice.
  • Plain DNS has no confidentiality, no integrity protection and no origin authentication. A resolver accepts an answer because the ID, ports, addresses and question match, which is why RFC 5452 requires random source ports and random IDs.
  • DNSSEC adds origin authentication and integrity; DNS over TLS (RFC 7858), DNS over HTTPS (RFC 8484) and DNS over QUIC (RFC 9250, May 2022) add confidentiality on one hop. None of them alone does both.
  • A safe resolver answers only its own clients (RFC 5358, BCP 140), validates DNSSEC, minimises QNAMEs (RFC 9156), keeps UDP answers under the fragmentation limit, accepts TCP and rate limits responses. BIND 9 and Unbound ship most of this on by default.

What DNS is for, the layer and the standard

The problem DNS solves

Before DNS, every host on the ARPANET relied on a single HOSTS.TXT file maintained centrally. RFC 1034 replaced it with a tree of labels (each up to 63 octets, a full name up to 255) with resource records attached to the nodes, name servers that hold authoritative data for parts of the tree called zones and delegate the rest, and resolvers that follow those delegations for clients and cache what they learn.

Where it sits

DNS is an application layer protocol. It runs over UDP and TCP on port 53 (RFC 1035, section 4.2), over TLS on TCP port 853 (RFC 7858), over QUIC on UDP port 853 (RFC 9250) and inside HTTPS (RFC 8484). Our TCP versus UDP post covers what each transport exposes, and the UDP explainer covers the datagram side.

The defining standards

DocumentDateStatusWhat it does
RFC 1034, Domain Names: Concepts and FacilitiesNovember 1987STD 13Name space, zones, delegation, resolver and server behaviour
RFC 1035, Domain Names: Implementation and SpecificationNovember 1987STD 13Message format, RR formats, master file format, transport
RFC 2181, Clarifications to the DNS SpecificationJuly 1997Standards TrackRRsets, TTL rules, data ranking and trust; updates 1034, 1035 and 1123
RFC 6891, Extension Mechanisms for DNS (EDNS(0))April 2013STD 75OPT pseudo record, larger UDP payloads, extended flags; obsoletes 2671 and 2673
RFC 7766, DNS Transport over TCPMarch 2016Standards TrackTCP mandatory; pipelining, out of order replies; obsoletes 5966, updates 1035 and 1123
RFC 9499, DNS TerminologyMarch 2024BCP 219Current definitions; obsoletes RFC 8499

RFC 1034 and RFC 1035 obsolete RFC 882, RFC 883 and RFC 973 and have never been replaced. Instead, each later RFC carries an "Updates: 1034, 1035" line for the part it changes.

What RFC 1034 and RFC 1035 still define today

Most of the 1987 text is still normative and still on the wire: the 12 byte header and four sections, the resource record format, length prefixed labels with compression pointers, the core types (A, NS, CNAME, SOA, PTR, MX and TXT), zones and delegation with the SOA timers that drive secondaries, the master file syntax that BIND, Knot and NSD still read, and port 53. Extensions reuse spare space (two reserved Z bits became AD and CD for DNSSEC) or add records (OPT for EDNS) rather than change the layout.

What changed is detail. The 512 byte UDP limit became a floor thanks to EDNS(0). TCP went from fallback to mandatory. The IQUERY opcode is marked obsolete in the IANA registry. TTL rules were clarified by RFC 2181 and extended by serve stale (RFC 8767). And the 1987 assumption that people on the network were colleagues has been replaced by the hardening RFCs below.

How DNS works on the wire

The message: one format, five parts

RFC 1035 section 4.1 defines a single message format used for queries and responses in both directions:

+====================-+
|        Header       |   12 bytes, always present
+====================-+
|       Question      |   the question for the name server
+====================-+
|        Answer       |   RRs answering the question
+====================-+
|      Authority      |   RRs pointing toward an authority
+====================-+
|      Additional     |   RRs holding additional information
+====================-+

A query carries one question (plus an OPT record if the client speaks EDNS). A response copies the question back and fills in the record sections that apply; a referral from a root or TLD server has an empty answer, NS records in the authority section and glue addresses in the additional section.

The header, field by field

The header is six 16 bit words. This is the layout from RFC 1035 section 4.1.1, with the two flag bits that DNSSEC later claimed from the reserved Z field:

                                1  1  1  1  1  1
  0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+
|                      ID                       |
+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+
|QR|   Opcode  |AA|TC|RD|RA| Z|AD|CD|   RCODE   |
+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+
|                    QDCOUNT                    |
+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+
|                    ANCOUNT                    |
+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+
|                    NSCOUNT                    |
+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+
|                    ARCOUNT                    |
+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+
FieldSizeMeaningSecurity note
ID16 bitsChosen by the querier, copied into the reply, used to match replies to queriesOnly 65,536 values. On its own it is far too small to stop forged replies, which is why source port randomisation matters
QR1 bit0 for a query, 1 for a response
Opcode4 bitsKind of query: 0 Query, 2 Status, 4 Notify, 5 Update, 6 DNS Stateful Operations; 1 (IQuery) is obsoleteNotify and Update should be restricted to known peers
AA1 bitAuthoritative Answer: the responding server is an authority for the name in the question
TC1 bitTruncated: the message did not fit the transport, so retry over TCPA client that ignores TC and never retries over TCP will fail on large answers
RD1 bitRecursion Desired, set by the client and copied into the reply
RA1 bitRecursion Available, set by the serverAn Internet facing server that sets RA for anyone is an open resolver
Z1 bitReserved, must be zero
AD1 bitAuthentic Data: the resolver validated the answer with DNSSEC (RFC 4035, RFC 6840)Only meaningful if the path from your resolver to you is trusted
CD1 bitChecking Disabled: the client asks the resolver not to validate
RCODE4 bitsResponse code; EDNS extends it to 12 bits
QDCOUNT16 bitsNumber of entries in the question section
ANCOUNT16 bitsNumber of RRs in the answer section
NSCOUNT16 bitsNumber of RRs in the authority section
ARCOUNT16 bitsNumber of RRs in the additional section

The response codes you will meet most often are in the IANA DNS RCODEs registry:

RCODENameWhat it means in practice
0NoErrorThe query succeeded. An answer section with zero records is NODATA: the name exists but not with that type
1FormErrThe server could not parse the query
2ServFailThe server failed to answer, for example because DNSSEC validation failed or no authority responded
3NXDomainThe name does not exist
5RefusedPolicy refusal, for example recursion requested by a client outside the allowed list
23BADCOOKIEMissing or bad server cookie (RFC 7873)

The question section and the resource record format

The question section has three fields: QNAME (the name being asked about), QTYPE (the record type, such as A or AAAA) and QCLASS (almost always IN, for Internet). Every resource record in the answer, authority and additional sections has the same six field layout from RFC 1035 section 4.1.3:

FieldSizeMeaning
NAMEvariableThe owner name, encoded as labels or as a compression pointer
TYPE16 bitsThe record type code from the IANA registry
CLASS16 bitsUsually IN (1)
TTL32 bitsHow many seconds the record may be cached
RDLENGTH16 bitsLength of RDATA in bytes
RDATAvariableThe record data, whose format depends on TYPE

Names on the wire are a sequence of labels, each a one byte length followed by that many bytes, ending with a zero length label for the root. The top two bits of the length byte are always zero for a normal label, which is where the 63 octet limit comes from. When both top bits are set, the byte starts a two byte compression pointer: the remaining 14 bits give the offset of an earlier name in the same message. Compression saves space, and it has historically been a rich source of parser bugs, because a pointer is just an offset that the parser must bounds check and guard against loops. That applies to the resolver library in your application runtime as much as to DNS servers.

Ports and transports

TransportPortDefined inNotes
UDP53RFC 1035Default for queries; 512 byte limit without EDNS
TCP53RFC 1035, RFC 7766Each message prefixed with a two byte length field; mandatory to support
DNS over TLS (DoT)TCP 853RFC 7858Same length prefixed framing inside TLS
DNS over QUIC (DoQ)UDP 853RFC 9250One query per QUIC stream
DNS over HTTPS (DoH)HTTPSRFC 8484DNS wire format in an HTTP request body or GET parameter

RFC 1035 says UDP is "not acceptable for zone transfers" and recommends a minimum retransmission interval of 2 to 5 seconds, trying other servers first.

How a query travels from stub to root, TLD and authoritative servers

RFC 9499 gives the roles their current names. A stub resolver "cannot perform all resolution itself" and depends on a recursive resolver, which acts in recursive mode and is normally expected to cache what it learns. Authoritative servers hold the zones. A forwarder sits in between and sends queries on to another resolver rather than walking the tree itself. Our post on recursive versus authoritative servers covers the roles in depth; here is what happens on the wire for a cold cache lookup of www.example.com, type A.

Stub (laptop)      Recursive resolver        Root server     .com TLD server     example.com authoritative
     |                    |                       |                 |                       |
     |== Q www.example.com A, RD=1 ==>            |                 |                       |
     |                    |== Q com NS (minimised) ->               |                       |
     |                    |<- referral: com NS + glue, AA=0 ==      |                       |
     |                    |== Q example.com NS (minimised) ============>                    |
     |                    |<- referral: example.com NS + glue, AA=0 ==-                     |
     |                    |== Q www.example.com A (full name now) ========================->|
     |                    |<- answer: www.example.com A 192.0.2.10, AA=1 ==================-|
     |                    |   (validate DNSSEC if signed, cache for TTL)                    |
     |<- answer, RA=1, AD=1 if validated ==                                                 |

Three points matter for security. The stub trusts its resolver completely, and that hop, often across Wi-Fi or an ISP network, is where encrypted DNS earns its keep. The resolver trusts referrals, so RFC 2181 and RFC 5452 tell it to accept only records within the bailiwick of the server that sent them. And every hop is a separate UDP exchange with its own ID and port, each a chance for a forged reply to arrive first; whatever gets into cache is served to every client for its whole TTL.

The diagram shows QNAME minimisation, covered below; without it the root would see the full question.

Caching, TTLs and negative answers

  • Negative caching. RFC 2308 specifies negative caching, so answers saying a name or type does not exist are cached too. RFC 8020 (November 2016) says that NXDOMAIN means there is really nothing underneath a name, so a resolver can stop asking about names below it.
  • Caching failures. RFC 9520 (December 2023) makes resolvers cache resolution failures for "at least 1 second" and never longer than 5 minutes, so a broken zone does not trigger a storm of retries.
  • Serve stale. RFC 8767 (March 2020) lets a resolver serve expired data when the authoritative servers cannot be reached, with a suggested cap of 7 days. BIND 9 (stale-answer-enable) and Unbound (serve-expired) both ship it switched off.
  • Aggressive use of the DNSSEC chain. RFC 8198 (July 2017) lets a validating resolver use cached NSEC records to synthesise NXDOMAIN answers without asking upstream. Unbound's aggressive-nsec option defaults to yes.

The main record types and what they do

All type codes below come from the IANA Resource Record (RR) TYPEs registry.

TypeCodeDefined inWhat it holdsSecurity relevance
A1RFC 1035An IPv4 addressThe most forged record in history
NS2RFC 1035An authoritative name server for the zoneDelegation; a hijacked NS hands over the whole zone
CNAME5RFC 1035The canonical name for an aliasDangling CNAMEs to deprovisioned cloud resources enable subdomain takeover
SOA6RFC 1035Start of authority: primary server, contact, serial, timers, negative TTLSerial and timers drive secondaries and negative caching
PTR12RFC 1035A name for an address in reverse zonesNot proof of identity; anyone controlling the reverse zone sets it
MX15RFC 1035Mail exchanger with preferenceMail routing; protected by DNSSEC and MTA-STS, not by DNS itself
TXT16RFC 1035Text stringsUsed for SPF, DKIM keys, DMARC policy and domain ownership proofs
AAAA28RFC 3596An IPv6 addressSame risks as A; often forgotten in filtering
OPT41RFC 6891EDNS pseudo record, never stored in zonesCarries buffer size, DO bit, cookies, padding
DS43RFC 4034Hash of a child zone's key, held in the parentDNSSEC chain of trust
RRSIG46RFC 4034A DNSSEC signature over an RRset
DNSKEY48RFC 4034A zone's DNSSEC public key
SVCB64RFC 9460Service binding: endpoint and parameters for any service
HTTPS65RFC 9460SVCB for HTTPS originsCarries ALPN, ports, address hints and encrypted ClientHello configuration
CAA257RFC 8659Which certificate authorities may issue for the domainLimits mis-issuance by a CA that honours it

Two meta types appear only in queries and transfers: AXFR (252) for full zone transfers and IXFR (251) for incremental ones. Zone transfers are covered in our AXFR post; the short version is that only your own secondaries should be allowed to request them. The ANY query type (registered as "*", code 255) asks for all records at a name, and RFC 8482 (January 2019) lets servers answer it with a minimal subset, because large ANY answers were a favourite tool for amplification.

SVCB and HTTPS records

RFC 9460 (November 2023) added the most important new record types of the last decade. SVCB has two modes: AliasMode, which "simply delegates operational control for a resource", and ServiceMode, which "binds together configuration information for a service endpoint". ServiceMode records carry key and value parameters such as alpn (which application protocols the server speaks, for example HTTP/3), port, ipv4hint and ipv6hint. The HTTPS record is the SVCB form for web origins.

An HTTPS record lets a browser learn in one lookup that a site speaks HTTP/3 and where to connect, and RFC 9460 lists conveying Encrypted ClientHello keys among its uses. A forged HTTPS record can strip those hints, so DNS integrity matters more, with TLS certificate checks as the backstop.

CAA

CAA records tell certificate authorities which of them may issue for a domain, with issue, issuewild and iodef properties and a critical flag (RFC 8659, November 2019, which obsoletes RFC 6844). Our CAA post covers the syntax and rollout.

How EDNS(0), DNS over TCP and QNAME minimisation change the picture

EDNS(0): bigger messages and room for options

The 512 byte UDP limit from RFC 1035 could not hold DNSSEC signatures, IPv6 glue or long TXT records. EDNS(0), now RFC 6891 (STD 75, April 2013), solves this without changing the header. A client that supports it adds a single OPT pseudo record to the additional section:

OPT fieldNormal RR meaningEDNS meaning
NAMEOwner nameMust be 0 (the root)
TYPERecord typeOPT (41)
CLASSClassThe requester's UDP payload size
TTLTime to liveExtended RCODE (8 bits), EDNS version (8 bits), DO flag and other flags
RDLENData lengthLength of all options
RDATARecord dataA list of option code, length and value entries

The IANA EDNS flags registry defines DO (DNSSEC answer OK) as bit 0, which tells the server the client wants signatures. Common options in the EDNS option codes registry are edns-client-subnet (8, RFC 7871), COOKIE (10, RFC 7873), Padding (12, RFC 7830) and Extended DNS Error (15, RFC 8914).

On payload size, RFC 6891 treats values below 512 as 512 and suggests 4096 as a starting point, while warning that very large values guarantee fragmentation. RFC 9715 (January 2025) moved practice the other way: it describes the 1232 byte value from DNS Flag Day 2020 (the IPv6 minimum MTU of 1280 minus 48 bytes of headers) and recommends a 1400 byte maximum, with larger answers moving to TCP. BIND 9.20 (edns-udp-size, max-udp-size) and Unbound 1.26 (edns-buffer-size) both default to 1232.

DNS over TCP: from fallback to requirement

Many operators once treated TCP port 53 as "zone transfers only" and blocked it. RFC 7766 (March 2016) ended that: "All general-purpose DNS implementations MUST support both UDP and TCP transport", for authoritative servers, recursive servers and stub resolvers alike. It also describes connection reuse, pipelining and out of order responses, which make TCP far cheaper than one query per connection.

RFC 9210 (BCP 235, March 2022) makes permitting DNS over TCP on the Internet a Best Current Practice. In practice: allow TCP port 53 wherever UDP port 53 is allowed, retry over TCP when TC is set, and manage connections; RFC 7766's security considerations suggest limits on total connections, connections per source, idle time, transactions per connection and connection duration. TCP's handshake also means a forged source cannot complete a connection, so TCP answers are not a reflection vector and are much harder to forge off path.

QNAME minimisation: tell each server only what it needs

Traditionally a resolver sent the full name and type to every server on the path. RFC 9156 (November 2021, obsoleting the experimental RFC 7816) describes QNAME minimisation: send each server the name "stripped to just one label more than the longest matching domain name for which the name server is known to be authoritative", with a QTYPE chosen to obscure the original. RFC 9156 relaxes the earlier advice to use NS as that type and notes that A or AAAA "will also blend into traffic". It recommends capping the extra iterations (MAX_MINIMISE_COUNT) at 10.

Unbound's qname-minimisation defaults to yes and uses QTYPE A where possible; BIND 9's qname-minimization defaults to relaxed. Both fall back to the full name when an upstream misbehaves; strict modes never fall back and break some zones.

DNS cookies

DNS Cookies (RFC 7873, May 2016, updated by RFC 9018 in April 2021) add a lightweight proof of return routability to UDP DNS. The client sends an 8 byte client cookie in an EDNS option; the server returns a server cookie of 8 to 32 bytes; later queries carry both. A forged response without the right client cookie can be dropped. BIND answers cookies by default (answer-cookie yes), and Unbound's ip-ratelimit lets "clients with a valid DNS Cookie" bypass the per address limit. Cookies are not cryptographic authentication of data; they raise the bar for blind spoofing and reflection.

Versions and history

DNS has no version number in its header. It evolves through extensions negotiated in band (EDNS) and through updates to how implementations behave.

DateChangeWhy it mattered
November 1987RFC 1034 and RFC 1035 (STD 13), obsoleting RFC 882, 883 and 973The protocol as still deployed
March 2005RFC 4033 to 4035, DNSSEC as deployed todayOrigin authentication and integrity for DNS data
8 July 2008CVE-2008-1447 published: resolvers with insufficient randomness could be poisoned using in bailiwick referralsDrove source port randomisation everywhere
October 2008RFC 5358 (BCP 140), closing open recursive resolversReflection and amplification
January 2009RFC 5452, resilience against forged answersRandom ports and IDs become MUST
April 2013RFC 6891 (STD 75), EDNS(0)Larger messages, flags, options
March 2016RFC 7766, TCP mandatoryLarge answers and connection reuse
May 2016RFC 7858 DNS over TLS; RFC 7873 DNS CookiesEncrypted stub to resolver hop; anti spoofing
October 2018RFC 8484 DNS over HTTPSDNS inside HTTPS
November 2021RFC 9156 QNAME minimisation (Standards Track)Privacy toward upstream servers
March 2022RFC 9210 (BCP 235), DNS over TCP as an operational requirementEnds "block TCP 53"
May 2022RFC 9250 DNS over QUICEncryption without TCP head of line blocking
November 2023RFC 9460 SVCB and HTTPS recordsService bindings and Encrypted ClientHello delivery
January 2025RFC 9715 on avoiding IP fragmentation1400 byte recommended UDP maximum

Deprecated in practice: IQUERY, full ANY answers over UDP, 4096 byte UDP buffers and blocking TCP port 53. Still everywhere: plain UDP port 53, both inside networks and between resolvers and authoritative servers. RFC 9250 defines encryption for the resolver to authoritative leg, but the stub to resolver leg is where encryption is most deployed.

Security properties: what DNS does and does not guarantee

Start from what the protocol gives you by itself, then add each extension.

PropertyPlain DNS (UDP or TCP 53)With DNSSECWith DoT, DoH or DoQ
Confidentiality of queriesNone. Every query and answer is readable on the pathNone. RFC 4033 says DNSSEC does not provide confidentialityYes, on the encrypted hop only. The resolver still sees everything
Integrity of answersNone beyond UDP and TCP checksums, which an attacker can recomputeYes, for signed zones, end to end from the zone to the validating resolverYes, on the encrypted hop only
Origin authentication of dataNone. A reply is accepted if it matches ID, ports, addresses and questionYes, via the chain from the root keyNo. It authenticates the resolver, not the data
Authenticated denial of existenceNoneYes, via NSEC or NSEC3No
Replay protectionMatching rules onlySignatures have validity windowsProvided by TLS or QUIC on the hop
AvailabilityExposed to floods, reflection and resource exhaustionLarger answers add load and amplification potentialTCP and QUIC handshakes resist spoofing; servers carry more connection state

The key point is that plain DNS authenticates nothing. RFC 5452 section 9.1 lists what a resolver must match before accepting a response: source address, destination address, destination port, query ID, query name, class and type. Those are guessable facts, not secrets, and the protocol's security comes down to making them hard to guess.

Weakness classes, as concepts

This section explains what goes wrong and why the design allows it. It is drawn from the security considerations in the RFCs above. For a broader catalogue, see DNS attack techniques and defenses.

Forged answers and cache poisoning

An off path attacker cannot see a resolver's query but can try to get a forged reply accepted. RFC 5452 lists what the forgery must match (question, ID, addresses and port) and notes it must arrive before the genuine answer. Because an attacker can often trigger a query just by asking, RFC 5452 warns that "any constant or sequential state held by such a resolver can be measured".

With a fixed source port, the only secret is the 16 bit ID, small enough that many guesses during the response window can succeed, and a forgery carrying referral data can poison a whole delegation. CVE-2008-1447, published on 8 July 2008, describes exactly this class: implementations that allowed "a birthday attack that uses in-bailiwick referrals to conduct cache poisoning against recursive resolvers", related to insufficient randomness.

The defences in RFC 5452 section 9.2 are now baseline requirements: an unpredictable source port drawn from as large a range as practicable, multiple ports for concurrent queries, an unpredictable ID across the full 0 to 65535 range, and acceptance of only in bailiwick records. Unbound's experimental use-caps-for-id option adds randomised letter case in query names; it defaults to no. The structural fix is DNSSEC validation, which makes forged data detectable regardless of how it arrived. TCP, cookies and encrypted transports also remove the blind guessing window on the hops they protect.

Open resolvers, reflection and amplification

A DNS response is often much larger than its query and UDP does not verify source addresses, so a resolver that answers anyone can be made to send large answers to a spoofed victim. RFC 5358 (BCP 140, October 2008) recommends operators "provide recursive name lookup service to only the intended clients", using source address access lists, interface selection, or TSIG and SIG(0) signed queries.

Authoritative servers must answer everyone, so they reduce the gain instead: minimal ANY answers (RFC 8482), minimal responses and response rate limiting. BIND's rate-limit statement exists, in its own words, "to prevent BIND 9 from being used to amplify reflection denial-of-service (DoS) attacks".

NXDOMAIN floods and resource exhaustion

Queries for random, never repeated subdomains bypass the cache, so each one sends the resolver to the target's authoritative servers, tying up resolver memory and loading the authority. The design allows it because a resolver cannot know a name does not exist until it asks.

The mitigations are layered: aggressive use of NSEC records from signed zones (RFC 8198) to answer nonexistent names from cache, NXDOMAIN cut handling (RFC 8020), caching of resolution failures (RFC 9520), per zone or per server outbound rate limits (Unbound's ratelimit option, which its documentation says "stops recursive floods, eg. random query names, but not spoofed reflection floods"), and limits on concurrent recursive clients.

Fragmentation and middlebox weaknesses

Only the first IP fragment of a large UDP answer carries the DNS header and ID; later fragments are matched by weaker IP reassembly rules. RFC 9715 notes that "IP fragmentation has exposed weaknesses in application protocols" and recommends responses within 1400 bytes and that requesters drop fragmented DNS responses.

Privacy leakage

Plain DNS exposes every query to the local network, the ISP, transit networks and, without minimisation, root and TLD operators. EDNS Client Subnet sends part of the client's address upstream, and the resolver operator always sees everything. QNAME minimisation reduces what upstream servers learn; encrypted DNS protects one hop; padding (RFC 7830) hides message lengths inside encrypted transports. None of these hides queries from the resolver you choose to trust.

Other classes worth knowing

Unrestricted zone transfers disclose every name in a zone. Changed NS records at the registrar hijack a domain while DNS works exactly as designed, so registry locks and account security are the controls. Dangling CNAME or NS records let whoever claims the abandoned resource serve content under your name. Rebinding, where a name flips from a public to an internal address, is covered in the attack techniques post.

Secure configuration for resolvers and authoritative servers

The following examples use BIND 9.20 and Unbound 1.26 option names checked against their official documentation. Addresses are from the documentation ranges (192.0.2.0/24 and 2001:db8::/32); replace them with your own networks.

A recursive resolver in BIND 9

In BIND 9.20, recursion defaults to yes, and if you set neither allow-recursion nor allow-query-cache, recursion is limited to localnets and localhost. dnssec-validation defaults to auto, using the built in root trust anchor. qname-minimization defaults to relaxed. edns-udp-size and max-udp-size both default to 1232. Being explicit is still worth it, because defaults change across versions and a single broad ACL elsewhere in the file can open the server.

acl "internal" { 192.0.2.0/24; 2001:db8::/32; localhost; };

options {
    recursion yes;
    allow-recursion { internal; };
    allow-query-cache { internal; };
    allow-transfer { none; };

    dnssec-validation auto;
    qname-minimization relaxed;

    edns-udp-size 1232;
    max-udp-size 1232;
    minimal-any yes;

    version none;
    hostname none;
};

Per the BIND reference, version none disables processing of version.bind queries, and minimal-any yes answers ANY over UDP with a single RRset. Serve stale (stale-answer-enable) is off by default; turn it on only if you accept serving expired data during an outage.

A recursive resolver in Unbound

Unbound is restrictive by default: per its documentation, only localhost is allowed, and anything without a matching access-control rule is refused. It listens on localhost unless you add interfaces. qname-minimisation, harden-glue, aggressive-nsec and minimal-responses all default to yes, and edns-buffer-size defaults to 1232.

server:
    interface: 192.0.2.53
    interface: 2001:db8::53

    access-control: 0.0.0.0/0 refuse
    access-control: ::0/0 refuse
    access-control: 127.0.0.0/8 allow
    access-control: 192.0.2.0/24 allow
    access-control: 2001:db8::/32 allow

    auto-trust-anchor-file: "/var/lib/unbound/root.key"
    harden-dnssec-stripped: yes
    harden-glue: yes
    aggressive-nsec: yes
    qname-minimisation: yes

    edns-buffer-size: 1232
    minimal-responses: yes
    deny-any: yes

    hide-identity: yes
    hide-version: yes
    unwanted-reply-threshold: 10000000

Notes from the Unbound reference: hide-identity and hide-version default to no and, when enabled, make id.server, hostname.bind, version.server and version.bind queries return REFUSED. unwanted-reply-threshold counts unexpected replies and, when the threshold is reached, clears the caches and logs a warning; the documentation suggests 10 million. deny-any answers ANY with an empty response. use-caps-for-id is experimental and off by default; test it before enabling. ratelimit and ip-ratelimit are marked experimental.

An authoritative server in BIND 9

An authoritative only server should not recurse at all, should transfer zones only to its secondaries and should rate limit responses:

options {
    recursion no;
    allow-transfer { none; };
    minimal-any yes;
    version none;

    rate-limit {
        responses-per-second 10;
        window 5;
    };
};

zone "example.com" {
    type primary;
    file "/var/named/example.com.db";
    allow-transfer { key "xfer-key"; };
};

The rate-limit values above are illustrative, not defaults; the BIND reference documents the full set of knobs (responses-per-second, nxdomains-per-second, errors-per-second, slip, window, exempt-clients and the IPv4 and IPv6 prefix lengths). Start in log-only mode and tune from what you see. With recursion no, BIND's allow-query-cache default becomes none, so the cache cannot be read either.

Hardening checklist

  1. Recursion only for your own clients, on servers separate from your authoritative service.
  2. DNSSEC validation on, with an automatically maintained root trust anchor, and QNAME minimisation on.
  3. UDP payload limits of 1232, never above 1400, and TCP port 53 open wherever UDP port 53 is.
  4. Zone transfers only to named secondaries with TSIG; version and identity queries disabled on public servers.
  5. Response rate limiting on authoritative servers; outbound limits and failure caching on resolvers.
  6. Encrypted DNS on the stub to resolver hop for clients that leave your network, and patched DNS software and DNS client libraries everywhere.

Checking a resolver and a zone with dig, delv and kdig

Only run these commands against systems you own or are authorised to test. Every option below is documented in the BIND 9.20 manual pages (dig, delv, named-checkconf, rndc), the Unbound 1.26 manual pages (unbound-checkconf, unbound-control) or the Knot DNS 3.6 kdig manual page.

Validate the configuration before you reload

# BIND: syntax check of named.conf
named-checkconf /etc/named.conf

# Unbound: syntax check, then print one effective option value
unbound-checkconf /etc/unbound/unbound.conf
unbound-checkconf -o qname-minimisation /etc/unbound/unbound.conf

# Unbound, running server: status and a live option value
unbound-control status
unbound-control get_option access-control

# BIND, running server: is serve stale on, and with which TTLs?
rndc serve-stale status

Is the resolver open?

From a host outside your network, ask your resolver's public address to resolve a name it should not resolve for strangers. A correctly closed resolver answers with status REFUSED or does not answer.

dig @198.51.100.53 example.com A

Look at the header line of the output. status: REFUSED with no ra flag is what you want. status: NOERROR with an answer and the ra flag from an outside address means you are running an open resolver.

Does it validate DNSSEC?

# Ask for DNSSEC records; a validating resolver sets the ad flag
dig @192.0.2.53 example.com A +dnssec +multiline

# delv validates locally and tells you why an answer is or is not secure
delv @192.0.2.53 example.com A

# Trace the resolution from a cold cache, logging every iterative query
delv +ns example.com A

In dig's flags line, ad means the resolver validated the answer. delv performs the validation itself and reports whether the answer validated and, if not, why. The delv +ns mode instantiates a full resolver and, per its manual page, "much more accurately replicates the behavior of a recursive name server with a cold cache" than dig +trace.

Does TCP work, and what is the UDP buffer size?

# Force TCP; this should succeed on both resolvers and authoritative servers
dig @192.0.2.53 example.com SOA +tcp

# Show the query dig sent and the OPT record in the reply
dig @192.0.2.53 example.com DNSKEY +dnssec +qr

# Request with a specific advertised buffer size
dig @192.0.2.53 example.com DNSKEY +dnssec +bufsize=1232

The OPT pseudosection of the reply shows the UDP payload size the server advertises. If a large answer comes back with the tc flag and the +tcp query succeeds, truncation and fallback are working as designed.

Cookies, NSID and version hiding

# dig sends a client cookie by default; look for COOKIE in the reply's OPT section
dig @192.0.2.53 example.com A +cookie

# Ask which server instance answered (useful behind anycast or load balancers)
dig @192.0.2.53 example.com A +nsid

# Should be REFUSED or empty on a hardened public server
dig @192.0.2.53 version.bind TXT CH

Zone checks on your authoritative servers

# Walk the delegation from the root to see each referral
dig +trace www.example.com A

# Ask an authority directly, without recursion; expect the aa flag
dig @ns1.example.com www.example.com A +norecurse

# Confirm zone transfers are refused to unauthorised hosts (run from a host that should NOT be allowed)
dig @ns1.example.com example.com AXFR

# Inspect service binding and CAA records
dig example.com HTTPS
dig example.com CAA

Encrypted transports with dig and kdig

# DNS over TLS with dig; port defaults to 853
dig @192.0.2.53 example.com A +tls

# DNS over TLS with certificate and hostname checks in kdig
kdig @192.0.2.53 +tls-ca +tls-hostname=resolver.example.net example.com A

# DNS over HTTPS
kdig @192.0.2.53 +https example.com A

# DNS over QUIC
kdig @192.0.2.53 +quic example.com A

kdig's +tls on its own uses the opportunistic privacy profile from RFC 7858, which encrypts but does not authenticate the server. Use +tls-ca with +tls-hostname when you want the strict behaviour clients should use.

Where DNSSEC, DoT, DoH and DoQ fit

These four are often treated as alternatives. They solve different problems, and our DoH versus DoT versus DNSSEC post goes deeper on the trade offs.

DNSSECDNS over TLS (DoT)DNS over HTTPS (DoH)DNS over QUIC (DoQ)
StandardRFC 4033 to 4035 (March 2005)RFC 7858 (May 2016)RFC 8484 (October 2018)RFC 9250 (May 2022)
ProtectsIntegrity and origin of data, authenticated denialConfidentiality and integrity of one hopConfidentiality and integrity of one hopConfidentiality and integrity of one hop
ScopeEnd to end, zone to validating resolverClient to resolver (or other hop)Client to resolverStub to resolver, resolver to authoritative, zone transfer
Transport and portAny DNS transportTCP 853HTTPSUDP 853
Hides queries from the networkNoYesYes, and blends with web trafficYes
Stops a lying resolverYes, if the client validatesNoNoNo
Operational costKey management, signing, DS at the parent, larger answersTLS certificates, connection stateHTTP stack, certificates; can bypass enterprise DNS policyQUIC stack, certificates; newer client support

For DNSSEC deployment details, see how to configure DNSSEC and what DNSSEC protects and what it does not. The short rule: sign your zones and validate on your resolvers, then encrypt the hops that leave networks you control.

When to use which DNS setup

Use caseRecommended setupSecurity propertiesOperational cost
Internal resolver for an office or data centreRecursive resolver with ACLs, DNSSEC validation, QNAME minimisation, plain DNS on the LANIntegrity via DNSSEC; LAN traffic readable by insidersLow
Laptops and phones on untrusted networksStub to resolver over DoT, DoH or DoQ, with the resolver validating DNSSECConfidentiality on the risky hop; integrity if validatedMedium: client configuration, certificates
Public authoritative DNS for your domainsAuthoritative only servers, DNSSEC signed zones, RRL, minimal ANY, TSIG for transfersIntegrity for validating clients; reduced reflectionMedium: key rollovers, monitoring
Resolver to authoritative privacyQNAME minimisation now; DoQ to authorities where both sides support itUpstream sees less; encryption where supportedLow for minimisation; higher for DoQ
Zone distribution between your serversAXFR or IXFR over TCP with TSIG, or over TLS or QUIC where supportedAuthenticated transfers; encryption if TLS or QUICLow

Common mistakes

  • Running an open resolver by accident. A cloud VM with a resolver bound to its public interface and a broad ACL is the classic case. Check from outside, as above.
  • Blocking TCP port 53. It breaks large answers, DNSSEC and zone transfers, and RFC 9210 makes allowing it a Best Current Practice.
  • Advertising a 4096 byte UDP buffer. Large buffers invite fragmentation, which some networks drop and which weakens spoofing resistance. Use 1232 or at most 1400.
  • Treating the AD bit as proof on an untrusted path. The AD bit is just a header flag. A client should trust it only if the path to its validating resolver is secure, or validate locally.
  • Assuming DoH or DoT replaces DNSSEC. Encryption protects one hop. A compromised or misled resolver still returns wrong data over a perfectly encrypted channel.
  • Mixing authoritative and recursive service on one public address. It exposes your cache to the Internet and makes ACLs harder to reason about.
  • Leaving dangling CNAME and NS records. Remove DNS records before you decommission the resource they point to.
  • Ignoring DNS libraries inside applications. Resolver libraries in runtimes and base images parse untrusted messages too and need patching like the server.
  • Using strict QNAME minimisation without testing. Unbound's documentation warns that strict mode leaves "a lot of domains" unresolvable; the relaxed defaults exist for a reason.

FAQ

What port does DNS use?

Classic DNS uses port 53 over both UDP and TCP (RFC 1035). DNS over TLS uses TCP port 853 (RFC 7858), DNS over QUIC uses UDP port 853 (RFC 9250) and DNS over HTTPS runs inside ordinary HTTPS (RFC 8484).

Does DNS use TCP or UDP?

Both. Most queries use UDP because it is a single round trip. TCP is used for zone transfers, for answers too large for UDP (signalled by the TC bit) and by any client that chooses it. RFC 7766 requires every general purpose implementation to support both.

Is DNS encrypted?

Not by default. Plain DNS on port 53 is readable and alterable by anyone on the path. DoT, DoH and DoQ encrypt the hop between a client and the server it talks to. DNSSEC signs data but does not encrypt it.

What is the maximum size of a DNS message?

RFC 1035 limits UDP messages without EDNS to 512 bytes. With EDNS(0) a client can advertise a larger buffer; current guidance in RFC 9715 recommends no more than 1400 bytes and BIND and Unbound default to 1232. Over TCP the two byte length prefix allows messages up to 65,535 bytes.

What do the DNS header flags mean?

QR marks a response, AA an authoritative answer, TC a truncated message, RD a request for recursion, RA that recursion is available, AD that the resolver validated the answer with DNSSEC and CD that the client asked it not to validate. The 4 bit RCODE gives the result, such as NoError, NXDomain or ServFail.

What is EDNS(0)?

EDNS(0), defined in RFC 6891, extends DNS by adding an OPT pseudo record to the additional section. It carries the client's UDP buffer size, the DO flag and options such as cookies and padding.

What is QNAME minimisation?

A privacy technique from RFC 9156 where a resolver sends each upstream server only as much of the name as it needs to give a referral, rather than the full name and type.

DNS over HTTPS vs DNS over TLS: which is more secure?

They provide the same cryptographic protection for the hop they cover. DoT runs on its own port, which makes it easy for network operators to see and manage. DoH runs inside HTTPS, which hides it among web traffic but can bypass enterprise DNS controls. Neither authenticates the data the resolver returns; DNSSEC does that.

How do I check if my DNS resolver is an open resolver?

From a network outside your own, run dig against the resolver's public address for any name. If it returns an answer with the ra flag, it is open. A correctly configured resolver returns REFUSED or does not respond. Only test resolvers you operate.

Where Safeguard fits

Safeguard does not sit in your DNS path, inspect queries or act as a resolver. It covers the software that implements DNS inside what you build and ship: resolver libraries in runtimes, DNS client packages pulled in transitively, and DNS servers baked into container images. Software composition analysis with a full transitive inventory, SBOM generation in SPDX and CycloneDX, container scanning, vulnerability management with EPSS, CISA KEV and reachability, and policy gates are all generally available, so you can find which projects carry a vulnerable DNS library and stop it shipping again. It does not validate DNSSEC, close open resolvers or configure BIND or Unbound for you; the steps above are yours to run.

Fix it without waiting for upstream

DNS weaknesses that reach production usually live in a library or daemon your software depends on: a resolver library in a runtime, a DNS client package several levels down your dependency tree, or the DNS server shipped in a base image. That is exactly the kind of exposure the steps below are built for.

Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.

1. Autonomous remediation: pull requests, not tickets

When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.

2. Gold Open Source: the safe version in seconds, free

gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.

3. Gold Registry: start from components that are already fixed

registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.

When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.

Your situationWhat to use
A fixed version existsAutonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version
You need to know today whether you are affectedGold Open Source lookup and "Am I affected?" checker, free and without login
You want vulnerable versions to stop shippingThe Gold scan GitHub Action in CI, plus Safeguard policies and gates
Upstream has no fix yet, or will not ship oneA custom Gold request; fork and patch (early access)
You want to start clean on new projectsPull hardened components and images from Gold Registry

One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.

See how Safeguard would help

These demos show dependency and SBOM workflows, not DNS monitoring. You can download Safeguard and follow the CLI docs to try them on your own projects.

This demo sets up the Safeguard runner on your own machine, approves it with its device code and then scans from the terminal. Pointed at a container image that bundles a DNS server or forwarder, the same scan lists the resolver packages and DNS client libraries inside it with their versions and any known vulnerabilities, so you know which DNS code you are actually running.

Click through it at your own pace, or open it full screen.

This demo is a real CLI session: install, open the interactive terminal, ask it to fix the lodash CVE in a repository, and watch it read the finding and advisory, ask before each command, move lodash to 4.17.21, run npm install and npm test with approval, and print a summary without pushing anything. The same approve each step flow applies when the package to bump is a DNS client library pinned in your manifest.

Click through it at your own pace, or open it full screen.

This demo connects Claude to the Safeguard MCP server with OAuth and lets you choose which tools Claude can use. Once connected, you can ask in plain language which of your projects include a particular resolver library version, and get the answer from your own scan data rather than from memory.

Click through it at your own pace, or open it full screen.

Quick check

Your resolver returns answers with the ad flag set. Does that mean a client on public Wi-Fi can trust them?

Only if the path between the client and the resolver is protected. The AD bit is a single header flag, and plain DNS has no integrity protection, so anyone on that path could set it. Use DoT, DoH or DoQ to the validating resolver, or validate DNSSEC on the client itself.

Why do modern resolvers advertise a 1232 byte EDNS buffer instead of 4096?

Large UDP answers fragment, and later fragments carry no DNS header, so they are matched only by weaker IP reassembly rules and are often dropped by middleboxes. 1232 bytes fits within the IPv6 minimum MTU of 1280 after headers. Anything larger is truncated and retried over TCP, which RFC 7766 makes mandatory to support.

Sources

Last reviewed: 23 October 2026. Defaults quoted are from BIND 9.20, Unbound 1.26 and Knot DNS 3.6 documentation; check the documentation for the version you run.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.