Open-Source Wikis

/

CoreDNS

/

Systems

/

DNS server

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:

  1. parse.Transport strips dns://, tls://, https://, https3://, quic://, or grpc:// and returns the bare host:port.
  2. plugin.SplitHostPort parses CIDR notations and may expand a single key into several reverse zones (e.g. 10.0.0.0/16 becomes multiple *.in-addr.arpa. zones). Extra hosts are appended back to s.Keys.
  3. Default ports are filled in by transport: 53 for dns, transport.TLSPort for tls, etc. (plugin/pkg/transport).
  4. Each key produces a *Config with Zone, ListenHosts: [""], Port, and Transport.
  5. The first config in a block is recorded as firstConfigInBlock so 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:

  1. propagateConfigParams copies Plugin, ListenHosts, Debug, Stacktrace, NumSockets, TLSConfig.Clone(), the timeouts, and TsigSecret from the first config in each block to the rest, so all keys in the same block share one plugin chain.
  2. groupConfigsByListenAddr keys configs by <transport>://<ip>:<port>. Multiple zones on the same listen address share a server.
  3. makeServersForGroup chooses the right caddy.Server constructor per transport. Each transport may instantiate NumSockets independent listeners (used with multisocket to scale UDP receive on Linux).
  4. View wiring. For each config, the plugin handler registry is checked for a Viewer. If found, its Filter becomes a FilterFunc on the config. A second view in the same block errors out.
  5. Overlap validation. validateZonesAndListeningAddresses rejects two unfiltered configs that listen on the same zone and address. Filtered configs (view etc.) 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 = stack

site.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 (metadata plugin) for per-request metadata population.
  • The trace plugin to expose the OpenTracing tracer at Server.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:

  1. Sanity-checks the question section.
  2. Recovers from plugin panics (configurable via Debug and Stacktrace); increments vars.Panic and returns SERVFAIL.
  3. Refuses non-INET class queries unless EnableChaos is satisfied.
  4. Validates the EDNS0 OPT record version with edns.Version.
  5. Wraps w in a request.ScrubWriter so plugin replies fit the client's buffer.
  6. Walks the question name label-by-label, using dns.NextLabel, looking up s.zones[q[off:]]. The first matching zone whose FilterFuncs all return true is dispatched to. If passAllFilterFuncs fails, the next zone is tried.
  7. DS queries are special — the search continues even after a match so the parent zone can answer.
  8. If no specific zone matches, the root zone (.) is tried as a last resort.
  9. Otherwise: errorAndMetricsFunc returns 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 setup calls dnsserver.GetConfig(c).AddPlugin(...) and may call Config.Handler(name) to look up sibling plugins after the chain is built.
  • plugin/metrics. The metrics plugin reads Server via dnsserver.Key{} from the context to attribute counters to a specific server. vars.Report is called from errorAndMetricsFunc and from inside the metrics plugin's wrapped writer.
  • plugin/trace. Bookmarked in NewServer. Exposed via Server.Tracer() for plugin.NextOrFailure to start child spans.
  • plugin/metadata. Implements MetadataCollector. Server.ServeDNS calls Collect once per request before the chain runs.
  • plugin/view. Provides FilterFuncs through the Viewer interface.
  • plugin/multisocket. Sets NumSockets so makeServersForGroup instantiates multiple parallel servers on the same address.
  • plugin/proxyproto. Sets ProxyProtoConnPolicy (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 like server_<scheme>.go, a switch case in makeServersForGroup, and a port default in InspectServerBlocks.
  • Changing zone matching semantics → Server.ServeDNS in core/dnsserver/server.go.
  • New per-config fields shared across a block → add them to Config and copy them in propagateConfigParams.
  • 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.

DNS server – CoreDNS wiki | Factory