Open-Source Wikis

/

CoreDNS

/

Systems

/

Request and response

coredns/coredns

Request and response

Active contributors: miekg, chrisohaver, johnbelamaric

Purpose

Plugins receive raw *dns.Msg and dns.ResponseWriter from miekg/dns. The request package wraps both with a higher-level Request type that caches commonly-needed values and a ScrubWriter that ensures every reply fits the client's advertised buffer.

Directory layout

request/
├── request.go            # Request struct and accessors
├── request_test.go
├── writer.go             # ScrubWriter
├── writer_test.go
├── edns0.go              # supportedOptions filter
└── edns0_test.go

request.Request

Request carries the response writer, the message, an optional zone, and a small cache so repeated calls to Name(), IP(), Size(), Do() don't repeat work.

Common accessors:

Method Returns
Name() / QName() Lowercase / unmodified question name (with trailing dot)
Type() / QType() Question type (string and uint16)
Class() / QClass() Question class
IP() / Port() / Family() Client address bits
LocalIP() / LocalPort() / LocalAddr() Server-side address
Proto() "udp" or "tcp"
Size() Effective UDP buffer size (after EDNS0 + normalization)
Do() True if the DO bit (DNSSEC OK) is set
Match(reply) True if a reply matches the question
Scrub(reply) Truncate / compress reply to fit Size()

NewWithQuestion(name, typ) returns a copy of the request with a different question — useful for plugins like cache that want to look up CNAME targets without mutating the original message.

Clear() invalidates the caches; cache calls this before reusing a Request for a prefetch.

EDNS0 / DO / SizeAndDo

Request.Size() consults the OPT record on the request:

size := uint16(0)
if o := r.Req.IsEdns0(); o != nil {
    r.do = o.Do()
    size = o.UDPSize()
}
size = edns.Size(r.Proto(), size)

edns.Size normalises a few corner cases — TCP always returns dns.MaxMsgSize, UDP enforces a minimum and a global maximum (both configurable via the bufsize plugin which sets OPT.UDPSize on the question itself).

SizeAndDo(m) ensures the reply has an OPT record that mirrors the request's intent. If the reply already has an OPT, the bits are normalized; otherwise the request's OPT is reused. Supported options are filtered by supportedOptions in edns0.go (NSID, EDNS-padding, COOKIE, EDE, etc.).

ScrubWriter

The DNS server installs ScrubWriter (request.NewScrubWriter) before the plugin chain runs:

w = request.NewScrubWriter(r, w)

ScrubWriter is a thin wrapper that overrides WriteMsg to call Request.Scrub(reply) first. Scrub truncates the message until it fits Size(), trying without compression first and then with compression. UDP messages over the IPv4 (1480) or IPv6 (1220) fragmentation thresholds are compressed even if they would otherwise fit, to avoid IP fragmentation. The TC bit is set on truncated UDP messages so clients retry over TCP.

This is why plugins can write dns.Msg instances of arbitrary size without worrying about UDP buffer math — the scrub layer handles it once, at the boundary.

Response classification

A second piece — plugin/pkg/response — classifies replies by content. response.Typify(msg, now) returns one of:

  • NoError — answer set is non-empty and matches the question.
  • NameError — NXDOMAIN.
  • NoData — NOERROR with empty answer.
  • Delegation — referral.
  • OtherError, Meta, Update, ServerError — used by the cache to decide what not to cache.

The cache plugin uses response.Typify to choose between positive and negative caches and to decide when to skip caching entirely.

Key abstractions

Symbol File Description
Request request/request.go The wrapped client query
ScrubWriter request/writer.go Response writer that scrubs to fit the buffer
supportedOptions request/edns0.go EDNS0 option allowlist for replies
Type (in plugin/pkg/response) plugin/pkg/response/typify.go Reply classification used by cache, dnssec, errors

Integration points

  • Server.ServeDNS wraps every reply with ScrubWriter. Plugins do not need to install it themselves.
  • plugin/cache constructs its own Request to look up cache keys (Name, Type, Class, DO/CD bits) and synthesises prefetch requests using a synthetic *net.TCPAddr so the response budget defaults to TCP sizes.
  • plugin/forward, plugin/grpc, plugin/dns64, plugin/rewrite all consume Request for routing and rewriting.
  • plugin/pkg/edns is shared with this package; the OPT record handling is intentionally split so plugins can call edns.Version(r) directly.

Entry points for modification

  • New cached field on Request → add a private field plus an accessor that lazily fills it (mirror Request.IP()).
  • New EDNS0 option supported in replies → extend supportedOptions in request/edns0.go.
  • New compression / truncation policy → Request.Scrub in request/request.go.
  • New reply classification → plugin/pkg/response.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Request and response – CoreDNS wiki | Factory