coredns/coredns
Transports
Active contributors: miekg, chrisohaver, johnbelamaric
Purpose
CoreDNS speaks classic DNS over UDP/TCP and four encrypted variants (DoT, DoH, DoH3, DoQ) plus DNS-over-gRPC. This page covers the listener-and-loop logic for each transport. The shared dispatch into the plugin chain is documented in DNS server.
Transport schemes
| Scheme | Default port | Server | RFC |
|---|---|---|---|
dns:// (default) |
53 | Server (UDP+TCP) |
RFC 1035 |
tls:// |
853 | ServerTLS |
RFC 7858 |
https:// |
443 | ServerHTTPS |
RFC 8484 |
https3:// |
443 | ServerHTTPS3 |
DoH over HTTP/3 |
quic:// |
853 | ServerQUIC |
RFC 9250 |
grpc:// |
443 | ServergRPC |
(non-standard, see pb/dns.proto) |
Default ports come from plugin/pkg/transport. dnsContext.InspectServerBlocks fills in the right default when a Corefile key omits the port. The constructors all live in core/dnsserver/server_*.go and are dispatched by makeServersForGroup (see DNS server).
Plain DNS (dns://)
Server in core/dnsserver/server.go listens on both UDP and TCP via dns.Server from github.com/miekg/dns. Each listener wraps Server.ServeDNS as its handler so the dispatch logic is shared.
- TCP: idle/read/write timeouts are 10s/3s/5s by default.
IdleTimeoutis read with a closure so reload changes take effect on the next idle. - UDP: a single
*net.UDPConnper port unless themultisocketplugin asks for several (NumSockets). On Linux this usesSO_REUSEPORTviaplugin/pkg/reuseport. - Graceful shutdown:
Server.Stopcallsdns.Server.ShutdownContexton both listeners up tograceTimeout(5s).
Server.Listen and ListenPacket are implementations of Caddy's caddy.TCPServer and caddy.UDPServer interfaces, so listeners can be passed across processes during reload.
DNS-over-TLS (tls://)
ServerTLS (core/dnsserver/server_tls.go) embeds *Server and wraps the TCP listener in crypto/tls. The tls plugin (plugin/tls/) populates Config.TLSConfig during setup; a missing TLS configuration fails validation.
Behavior:
- Only
dns.Server.Net = "tcp-tls"is started. UDP is not used for DoT. - Read timeouts can be raised relative to plain DNS via the
timeoutsplugin. - The QUIC and HTTPS servers reuse the same
TLSConfig.Clone()fromfirstConfigInBlock.
DNS-over-HTTPS (https://)
ServerHTTPS in core/dnsserver/server_https.go runs an http.Server with HTTP/2 (NextProtos = ["h2", "http/1.1"]).
graph LR
Client -->|HTTP/2 POST or GET| HTTPSrv[ServerHTTPS]
HTTPSrv -->|RequestToMsgWire| MSG[dns.Msg]
HTTPSrv -->|DoHWriter| Server.ServeDNS
Server.ServeDNS -->|reply| DoHWriter
DoHWriter -->|Pack| HTTPResp[HTTP response]Key bits:
validRequest: defaults to checking thatr.URL.Path == doh.Path(/dns-query). The plugin chain can override this viaConfig.HTTPRequestValidateFunc.RequestToMsgWire(plugin/pkg/doh) decodes both GET (?dns=…base64url) and POST (application/dns-message) bodies.- A
DoHWriter(core/dnsserver/https.go) is constructed with the local and remote addresses from the HTTP connection. The original*http.Requestis stashed on the context underHTTPRequestKey{}so plugins can read headers likeUser-Agent. MaxHTTPSConnectionsdefaults to 200; the listener is wrapped withgolang.org/x/net/netutil.LimitListener.- TSIG is supported on inbound DoH messages (uncommon but covered by
tsig). - TTL of the response is reflected back as
Cache-Control: max-age=<minTTL>per RFC 8484 §5.1.
PROXY protocol is supported via Server.connPolicy and pires/go-proxyproto.
DNS-over-HTTP/3 (https3://)
ServerHTTPS3 (core/dnsserver/server_https3.go) layers HTTP/3 from quic-go/quic-go/http3 on top of QUIC. Behavior is otherwise the same as DoH:
- Listener is a QUIC
EarlyListener. UDP is required. MaxHTTPS3Streamscaps the per-connection stream count.- ALPN is
"h3". - The same
DoHWriterand dispatch path are used.
DNS-over-QUIC (quic://)
ServerQUIC (core/dnsserver/server_quic.go) implements RFC 9250 with github.com/quic-go/quic-go. Each QUIC stream carries a single DNS message:
- ALPN is
"doq". - Stream framing prepends a 2-byte length, identical to DNS-over-TCP.
- Application error codes are exported (
DoQCodeNoError,DoQCodeInternalError,DoQCodeProtocolError) so plugins can signal protocol issues. MaxQUICStreamsdefaults to 256; a worker pool sized byMaxQUICWorkerPoolSize(default 1024) bounds concurrency per server.- PROXY-protocol-style UDP session tracking can be enabled per-config so Cloudflare Spectrum sessions resolve to a real client address (
udpSessionTrackingTTL,udpSessionTrackingMaxSessions).
DNS-over-gRPC (grpc://)
ServergRPC (core/dnsserver/server_grpc.go) registers a pb.DnsServiceServer (defined by pb/dns.proto) implementing a single Query(*pb.DnsPacket) returns (*pb.DnsPacket) RPC.
- HTTP/2 with TLS (
NextProtos = ["h2"]); plaingrpc://without TLS is also supported. MaxGRPCStreams(default 256) andMaxGRPCConnections(default 200) cap concurrency.- The OpenTracing interceptor
otgrpc.OpenTracingServerInterceptoris wired in whentraceis enabled. - Per-RPC the server unpacks the DNS message, builds a
request.Request, and dispatches through the same chain. - The protobuf wire format is generated from
pb/dns.protoviamake pb; the generated files arepb/dns.pb.goandpb/dns_grpc.pb.go.
Listener helpers
A few cross-cutting concerns are reused by every transport:
plugin/pkg/reuseport: thin wrappers that setSO_REUSEPORTon Linux so reload events can hand sockets between processes (or somultisocketcan fan UDP receive over multiple sockets on the same port).plugin/pkg/proxyproto: PROXY protocol v1/v2 connection wrappers. Plain DNS usespires/go-proxyproto.Listener; UDP uses a customcproxyproto.PacketConnwith optional session tracking.plugin/pkg/transport: scheme strings, default ports, and small constants like the maximum DNS message size used by gRPC and QUIC framing.
Per-transport configuration knobs
The Config struct in core/dnsserver/config.go exposes:
| Field | Effect | Set by |
|---|---|---|
TLSConfig |
TLS for DoT, DoH, DoH3, DoQ, gRPC | plugin/tls |
MaxHTTPSConnections, MaxHTTPS3Streams |
DoH and DoH3 limits | plugin/https, plugin/https3 |
MaxQUICStreams, MaxQUICWorkerPoolSize |
DoQ limits | plugin/quic |
MaxGRPCStreams, MaxGRPCConnections |
gRPC limits | plugin/grpc_server |
ReadTimeout / WriteTimeout / IdleTimeout |
Plain DNS, DoT, DoH | plugin/timeouts |
ProxyProtoConnPolicy |
PROXY protocol handling | plugin/proxyproto |
ProxyProtoUDPSessionTrackingTTL / MaxSessions |
UDP PPv2 (Cloudflare Spectrum) | plugin/proxyproto |
NumSockets |
Parallel listeners | plugin/multisocket |
HTTPRequestValidateFunc |
Custom DoH path validator | external plugins |
Plugins listed in the right column are documented in Plugins overview.
Key source files
| File | Purpose |
|---|---|
core/dnsserver/server.go |
Plain UDP+TCP server, dispatch core |
core/dnsserver/server_tls.go |
DoT |
core/dnsserver/server_https.go |
DoH (HTTP/2) |
core/dnsserver/server_https3.go |
DoH3 (HTTP/3) |
core/dnsserver/server_quic.go |
DoQ |
core/dnsserver/server_grpc.go |
DNS-over-gRPC |
core/dnsserver/https.go |
DoHWriter |
core/dnsserver/quic.go |
QUIC stream framing helpers |
pb/dns.proto, pb/dns.pb.go, pb/dns_grpc.pb.go |
gRPC service definition |
plugin/pkg/transport/ |
Scheme/port constants |
plugin/pkg/reuseport/ |
SO_REUSEPORT listeners |
plugin/pkg/proxyproto/ |
PROXY protocol wrappers |
plugin/pkg/doh/ |
DoH request/reply codecs |
Entry points for modification
- A new transport scheme: see DNS server.
- TLS-related changes: usually live in
plugin/tlsand are applied to all encrypted transports viaConfig.TLSConfig. - Connection limits: bump the per-server defaults in
core/dnsserver/server_*.goand the corresponding plugin inplugin/<scheme>/. - PROXY protocol semantics:
plugin/proxyproto/andplugin/pkg/proxyproto/.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.