coredns/coredns
DNS server
Active contributors: miekg, chrisohaver, johnbelamaric, yongtang, stp-ip
Purpose
The core/dnsserver package is the host that turns a Corefile into one or more running DNS servers. It registers the dns server type with Caddy, parses each server block into a Config, builds a plugin chain per zone, and dispatches each query through the chain.
Directory layout
core/
├── coredns.go # alias coremain.Run + plugin var
├── dnsserver/
│ ├── register.go # Caddy server-type registration; InspectServerBlocks; MakeServers
│ ├── server.go # Server (UDP+TCP); Server.ServeDNS dispatch logic
│ ├── server_tls.go # ServerTLS (DoT)
│ ├── server_https.go # ServerHTTPS (DoH over HTTP/2)
│ ├── server_https3.go # ServerHTTPS3 (DoH3)
│ ├── server_quic.go # ServerQUIC (DoQ)
│ ├── server_grpc.go # ServergRPC (DNS-over-gRPC)
│ ├── config.go # Config struct (per-zone configuration)
│ ├── address.go # zoneAddr, overlap checks, scheme parsing
│ ├── view.go # Viewer interface for the view plugin
│ ├── onstartup.go # OnStartup output formatting
│ ├── https.go # DoHWriter response writer
│ ├── quic.go # QUIC stream framing helpers
│ └── zdirectives.go # generated Directives slice (do not edit)
└── plugin/
└── zplugin.go # generated blank imports (do not edit)How Corefiles are parsed
graph LR
A[caddy.Start] --> B[dnsserver.init: caddy.RegisterServerType]
B --> C[InspectServerBlocks per server block]
C -->|build| D[Config per key]
D --> E[plugin setup funcs append to Config.Plugin]
E --> F[MakeServers]
F -->|propagateConfigParams| G[group by listen addr]
G --> H[makeServersForGroup]
H -->|per transport| I[NewServer / NewServerTLS / NewServerHTTPS / ...]
I -->|fold right-to-left| J[pluginChain Handler]dnsContext.InspectServerBlocks
Walks every server block and key. For each key:
parse.Transportstripsdns://,tls://,https://,https3://,quic://, orgrpc://and returns the bare host:port.plugin.SplitHostPortparses CIDR notations and may expand a single key into several reverse zones (e.g.10.0.0.0/16becomes multiple*.in-addr.arpa.zones). Extra hosts are appended back tos.Keys.- Default ports are filled in by transport:
53fordns,transport.TLSPortfortls, etc. (plugin/pkg/transport). - Each key produces a
*ConfigwithZone,ListenHosts: [""],Port, andTransport. - The first config in a block is recorded as
firstConfigInBlockso plugin instances and shared settings can be propagated later.
Plugin setup phase
Caddy iterates the directives in zdirectives.go order — generated from plugin.cfg — and calls each plugin's registered setup for every server block that mentions it. Plugins call dnsserver.GetConfig(c) to obtain the right *Config and cfg.AddPlugin(plugin.Plugin) to append themselves.
Config.AddPlugin appends a func(plugin.Handler) plugin.Handler. The folding into a chain happens later in NewServer.
dnsContext.MakeServers
After all setups have run:
propagateConfigParamscopiesPlugin,ListenHosts,Debug,Stacktrace,NumSockets,TLSConfig.Clone(), the timeouts, andTsigSecretfrom the first config in each block to the rest, so all keys in the same block share one plugin chain.groupConfigsByListenAddrkeys configs by<transport>://<ip>:<port>. Multiple zones on the same listen address share a server.makeServersForGroupchooses the rightcaddy.Serverconstructor per transport. Each transport may instantiateNumSocketsindependent listeners (used withmultisocketto scale UDP receive on Linux).- View wiring. For each config, the plugin handler registry is checked for a
Viewer. If found, itsFilterbecomes aFilterFuncon the config. A secondviewin the same block errors out. - Overlap validation.
validateZonesAndListeningAddressesrejects two unfiltered configs that listen on the same zone and address. Filtered configs (viewetc.) are allowed to overlap.
Plugin chain compilation (NewServer)
Inside dnsserver.NewServer (called for dns:// listeners), the chain is folded right-to-left:
var stack plugin.Handler
for i := len(site.Plugin) - 1; i >= 0; i-- {
stack = site.Plugin[i](stack)
site.registerHandler(stack)
if mdc, ok := stack.(MetadataCollector); ok {
site.metaCollector = mdc
}
// bookmark trace plugin and chaos enablers
}
site.pluginChain = stacksite.registerHandler populates Config.registry with each plugin's named handler so other plugins can introspect the chain. Config.Handlers() returns the registered handlers in directive order.
The compile loop also bookmarks:
- The first
MetadataCollector(metadataplugin) for per-request metadata population. - The
traceplugin to expose the OpenTracing tracer atServer.Tracer(). - Any plugin in
EnableChaos(chaos,forward,proxy) so CH-class queries are not blanket-refused.
Server.ServeDNS
The dispatch logic for a single DNS query lives in Server.ServeDNS (core/dnsserver/server.go). The function:
- Sanity-checks the question section.
- Recovers from plugin panics (configurable via
DebugandStacktrace); incrementsvars.Panicand returns SERVFAIL. - Refuses non-INET class queries unless
EnableChaosis satisfied. - Validates the EDNS0 OPT record version with
edns.Version. - Wraps
win arequest.ScrubWriterso plugin replies fit the client's buffer. - Walks the question name label-by-label, using
dns.NextLabel, looking ups.zones[q[off:]]. The first matching zone whoseFilterFuncsall return true is dispatched to. IfpassAllFilterFuncsfails, the next zone is tried. - DS queries are special — the search continues even after a match so the parent zone can answer.
- If no specific zone matches, the root zone (
.) is tried as a last resort. - Otherwise:
errorAndMetricsFuncreturns REFUSED.
For the matched zone:
if h.metaCollector != nil {
ctx = h.metaCollector.Collect(ctx, request.Request{Req: r, W: w})
}
if h.ViewName != "" {
ctx = context.WithValue(ctx, ViewKey{}, h.ViewName)
}
rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)
if !plugin.ClientWrite(rcode) {
errorFunc(s.Addr, w, r, rcode)
}plugin.ClientWrite returns false for SERVFAIL/REFUSED/FORMERR/NOTIMP, in which case errorFunc synthesizes the error reply. For any other rcode, the plugin is assumed to have already written.
Server types
Each transport scheme constructs a different Server wrapper around the shared core:
| Constructor | Source | Wire format |
|---|---|---|
NewServer |
server.go |
UDP + TCP, plain DNS |
NewServerTLS |
server_tls.go |
DoT (RFC 7858) |
NewServerHTTPS |
server_https.go |
DoH over HTTP/2 (RFC 8484) |
NewServerHTTPS3 |
server_https3.go |
DoH over HTTP/3 |
NewServerQUIC |
server_quic.go |
DoQ (RFC 9250) |
NewServergRPC |
server_grpc.go |
gRPC unary streams (pb/dns.proto) |
All of them embed *Server and call s.ServeDNS(ctx, w, r). See Transports for the per-server lifecycle and listener wrapping (reuseport, PROXY protocol, TLS, max connections, max streams).
Key abstractions
| Symbol | File | Description |
|---|---|---|
Server |
core/dnsserver/server.go |
UDP+TCP DNS server with plugin chain, panic recovery, view filtering |
Config |
core/dnsserver/config.go |
Per-zone configuration: zone, port, transport, TLS, timeouts, plugin list, registry |
dnsContext |
core/dnsserver/register.go |
Caddy Context — holds the master config list during setup |
zoneAddr / zoneOverlap |
core/dnsserver/address.go |
Listening address bookkeeping and overlap detection |
Key / LoopKey / ViewKey / HTTPRequestKey |
core/dnsserver/server.go, server_https.go |
Context key types |
EnableChaos |
core/dnsserver/server.go |
Set of plugins that opt the server into CH-class queries |
MetadataCollector |
core/dnsserver/server.go |
Interface plugins implement to populate per-request metadata |
FilterFunc / Viewer |
core/dnsserver/config.go, view.go |
Hooks for the view plugin |
Quiet, Port, DefaultPort, GracefulTimeout |
core/dnsserver/register.go |
Global tunables, set by flags |
Integration points
- Plugins. Every plugin's
setupcallsdnsserver.GetConfig(c).AddPlugin(...)and may callConfig.Handler(name)to look up sibling plugins after the chain is built. plugin/metrics. The metrics plugin readsServerviadnsserver.Key{}from the context to attribute counters to a specific server.vars.Reportis called fromerrorAndMetricsFuncand from inside the metrics plugin's wrapped writer.plugin/trace. Bookmarked inNewServer. Exposed viaServer.Tracer()forplugin.NextOrFailureto start child spans.plugin/metadata. ImplementsMetadataCollector.Server.ServeDNScallsCollectonce per request before the chain runs.plugin/view. ProvidesFilterFuncs through theViewerinterface.plugin/multisocket. SetsNumSocketssomakeServersForGroupinstantiates multiple parallel servers on the same address.plugin/proxyproto. SetsProxyProtoConnPolicy(and optional UDP session tracking) so the listener wrappers handle PROXY protocol headers.
Entry points for modification
- A new transport scheme → add a constant to
plugin/pkg/transport, a constructor file likeserver_<scheme>.go, a switch case inmakeServersForGroup, and a port default inInspectServerBlocks. - Changing zone matching semantics →
Server.ServeDNSincore/dnsserver/server.go. - New per-config fields shared across a block → add them to
Configand copy them inpropagateConfigParams. - Plugin introspection from setup → use
dnsserver.GetConfig(c).Handler("name")after the directive of "name" has run (relies on directive ordering).
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.