Open-Source Wikis

/

CoreDNS

/

Systems

/

Transports

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. IdleTimeout is read with a closure so reload changes take effect on the next idle.
  • UDP: a single *net.UDPConn per port unless the multisocket plugin asks for several (NumSockets). On Linux this uses SO_REUSEPORT via plugin/pkg/reuseport.
  • Graceful shutdown: Server.Stop calls dns.Server.ShutdownContext on both listeners up to graceTimeout (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 timeouts plugin.
  • The QUIC and HTTPS servers reuse the same TLSConfig.Clone() from firstConfigInBlock.

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 that r.URL.Path == doh.Path (/dns-query). The plugin chain can override this via Config.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.Request is stashed on the context under HTTPRequestKey{} so plugins can read headers like User-Agent.
  • MaxHTTPSConnections defaults to 200; the listener is wrapped with golang.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.
  • MaxHTTPS3Streams caps the per-connection stream count.
  • ALPN is "h3".
  • The same DoHWriter and 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.
  • MaxQUICStreams defaults to 256; a worker pool sized by MaxQUICWorkerPoolSize (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"]); plain grpc:// without TLS is also supported.
  • MaxGRPCStreams (default 256) and MaxGRPCConnections (default 200) cap concurrency.
  • The OpenTracing interceptor otgrpc.OpenTracingServerInterceptor is wired in when trace is 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.proto via make pb; the generated files are pb/dns.pb.go and pb/dns_grpc.pb.go.

Listener helpers

A few cross-cutting concerns are reused by every transport:

  • plugin/pkg/reuseport: thin wrappers that set SO_REUSEPORT on Linux so reload events can hand sockets between processes (or so multisocket can fan UDP receive over multiple sockets on the same port).
  • plugin/pkg/proxyproto: PROXY protocol v1/v2 connection wrappers. Plain DNS uses pires/go-proxyproto.Listener; UDP uses a custom cproxyproto.PacketConn with 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/tls and are applied to all encrypted transports via Config.TLSConfig.
  • Connection limits: bump the per-server defaults in core/dnsserver/server_*.go and the corresponding plugin in plugin/<scheme>/.
  • PROXY protocol semantics: plugin/proxyproto/ and plugin/pkg/proxyproto/.

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

Transports – CoreDNS wiki | Factory