or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

client.mdconstants.mddnssec.mddynamic-updates.mdedns0.mdindex.mdmessaging.mdrr-types.mdserver.mdtsig.mdutilities.mdzone-parsing.mdzone-transfer.md
tile.json

server.mddocs/

DNS Server Operations

DNS server framework with handler pattern for building authoritative servers and resolvers.

Core Types

Server

DNS server with configurable parameters.

type Server struct {
	Addr              string           // Address to listen on (":dns" if empty)
	Net               string           // Network: "" or "udp" (default), "tcp", "tcp-tls" (DNS over TLS)
	Listener          net.Listener     // TCP listener (for systemd socket activation)
	TLSConfig         *tls.Config      // TLS connection configuration
	PacketConn        net.PacketConn   // UDP listener (for systemd socket activation)
	Handler           Handler          // Request handler (DefaultServeMux if nil)
	UDPSize           int              // UDP buffer size (MinMsgSize/512 if not set)
	ReadTimeout       time.Duration    // Read timeout (default: 2 seconds)
	WriteTimeout      time.Duration    // Write timeout (default: 2 seconds)
	IdleTimeout       func() time.Duration // TCP idle timeout (default: 8 seconds per RFC 5966)
	TsigProvider      TsigProvider     // Custom TSIG implementation
	TsigSecret        map[string]string // TSIG secrets: map[zonename]base64secret
	NotifyStartedFunc func()           // Callback when server starts
	DecorateReader    DecorateReader   // Customize DNS message reading
	DecorateWriter    DecorateWriter   // Customize DNS message writing
	MaxTCPQueries     int              // Max TCP queries per connection (unlimited if -1)
	ReusePort         bool             // SO_REUSEPORT socket option
	ReuseAddr         bool             // SO_REUSEADDR socket option
	MsgAcceptFunc     MsgAcceptFunc    // Message acceptance function
	MsgInvalidFunc    MsgInvalidFunc   // Invalid message handler
}

Handler

Interface for DNS request handlers.

type Handler interface {
	ServeDNS(w ResponseWriter, r *Msg)
}

HandlerFunc

Function adapter for Handler interface.

type HandlerFunc func(ResponseWriter, *Msg)

// ServeDNS calls f(w, r)
func (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg)

ResponseWriter

Interface for writing DNS responses.

type ResponseWriter interface {
	LocalAddr() net.Addr        // Local server address
	RemoteAddr() net.Addr       // Client address
	WriteMsg(*Msg) error        // Write DNS message
	Write([]byte) (int, error)  // Write raw bytes
	Close() error               // Close connection
	TsigStatus() error          // TSIG validation status
	TsigTimersOnly(bool)        // Set TSIG timers only mode
	Hijack()                    // Take over connection
}

ConnectionStater

Interface for accessing TLS connection state.

type ConnectionStater interface {
	ConnectionState() *tls.ConnectionState
}

ServeMux

DNS request multiplexer.

type ServeMux struct {
	// contains filtered or unexported fields
}

// NewServeMux creates a new ServeMux
func NewServeMux() *ServeMux

// Handle registers handler for pattern
func (mux *ServeMux) Handle(pattern string, handler Handler)

// HandleFunc registers handler function for pattern
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Msg))

// HandleRemove removes handler for pattern
func (mux *ServeMux) HandleRemove(pattern string)

// ServeDNS dispatches request to registered handler
func (mux *ServeMux) ServeDNS(w ResponseWriter, r *Msg)

// Match returns handler for name
func (mux *ServeMux) Match(name string) Handler

Server Methods

// ListenAndServe starts DNS server
func (srv *Server) ListenAndServe() error

// ActivateAndServe activates with existing listener/conn
func (srv *Server) ActivateAndServe() error

// Shutdown performs graceful shutdown
func (srv *Server) Shutdown() error

// ShutdownContext performs graceful shutdown with context
func (srv *Server) ShutdownContext(ctx context.Context) error

Package-Level Functions

// ListenAndServe starts DNS server
func ListenAndServe(addr string, network string, handler Handler) error

// ListenAndServeTLS starts DNS server with TLS
func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error

// ActivateAndServe activates with existing listener/conn
func ActivateAndServe(l net.Listener, p net.PacketConn, handler Handler) error

// Handle registers handler to DefaultServeMux
func Handle(pattern string, handler Handler)

// HandleFunc registers handler func to DefaultServeMux
func HandleFunc(pattern string, handler func(ResponseWriter, *Msg))

// HandleRemove removes handler from DefaultServeMux
func HandleRemove(pattern string)

// HandleFailed default SERVFAIL handler
func HandleFailed(w ResponseWriter, r *Msg)

Message Accept Function

// MsgAcceptAction defines message acceptance
type MsgAcceptAction int

const (
	MsgAccept               MsgAcceptAction = iota // Accept message
	MsgReject                                       // Reject with FORMERR
	MsgIgnore                                       // Ignore silently
	MsgRejectNotImplemented                         // Reject with NOTIMPL
)

// MsgAcceptFunc checks incoming messages
type MsgAcceptFunc func(Header) MsgAcceptAction

// DefaultMsgAcceptFunc is the default accept function
func DefaultMsgAcceptFunc(hdr Header) MsgAcceptAction

UDP Session Handling

// SessionUDP holds UDP session information
type SessionUDP struct {
	// contains filtered or unexported fields
}

// ReadFromSessionUDP reads from UDP session
func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error)

// WriteToSessionUDP writes to UDP session
func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error)

Reader and Writer Interfaces

// Reader reads raw DNS messages
type Reader interface {
	ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error)
	ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error)
}

// PacketConnReader for generic packet connections
type PacketConnReader interface {
	Reader
	ReadPacketConn(conn net.PacketConn, timeout time.Duration) ([]byte, net.Addr, error)
}

// Writer writes raw DNS messages
type Writer interface {
	io.Writer
}

// DecorateReader customizes message reading
type DecorateReader func(Reader) Reader

// DecorateWriter customizes message writing
type DecorateWriter func(Writer) Writer

// MsgInvalidFunc handles invalid messages
type MsgInvalidFunc func(m []byte, err error)

// DefaultMsgInvalidFunc is the default invalid handler
func DefaultMsgInvalidFunc(m []byte, err error)

Variables

// DefaultServeMux is the default ServeMux instance
var DefaultServeMux = NewServeMux()

Usage Examples

Basic UDP Server

dns.HandleFunc(".", func(w dns.ResponseWriter, r *dns.Msg) {
	m := new(dns.Msg)
	m.SetReply(r)
	m.Authoritative = true

	// Add answer
	rr := &dns.A{
		Hdr: dns.RR_Header{
			Name:   r.Question[0].Name,
			Rrtype: dns.TypeA,
			Class:  dns.ClassINET,
			Ttl:    3600,
		},
		A: net.ParseIP("192.0.2.1"),
	}
	m.Answer = append(m.Answer, rr)

	w.WriteMsg(m)
})

server := &dns.Server{Addr: ":53", Net: "udp"}
log.Fatal(server.ListenAndServe())

Basic TCP Server

dns.HandleFunc(".", handleRequest)

server := &dns.Server{
	Addr: ":53",
	Net:  "tcp",
}
log.Fatal(server.ListenAndServe())

DNS over TLS Server

cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
	log.Fatal(err)
}

tlsConfig := &tls.Config{
	Certificates: []tls.Certificate{cert},
}

dns.HandleFunc(".", handleRequest)

server := &dns.Server{
	Addr:      ":853",
	Net:       "tcp-tls",
	TLSConfig: tlsConfig,
}
log.Fatal(server.ListenAndServe())

Using ServeMux

mux := dns.NewServeMux()

// Handle example.com
mux.HandleFunc("example.com.", func(w dns.ResponseWriter, r *dns.Msg) {
	m := new(dns.Msg)
	m.SetReply(r)
	m.Authoritative = true
	// ... add records
	w.WriteMsg(m)
})

// Handle example.net
mux.HandleFunc("example.net.", func(w dns.ResponseWriter, r *dns.Msg) {
	m := new(dns.Msg)
	m.SetReply(r)
	m.Authoritative = true
	// ... add records
	w.WriteMsg(m)
})

// Catch-all
mux.HandleFunc(".", func(w dns.ResponseWriter, r *dns.Msg) {
	m := new(dns.Msg)
	m.SetRcode(r, dns.RcodeRefused)
	w.WriteMsg(m)
})

server := &dns.Server{
	Addr:    ":53",
	Net:     "udp",
	Handler: mux,
}
log.Fatal(server.ListenAndServe())

Handler Type

type MyHandler struct {
	zones map[string][]dns.RR
}

func (h *MyHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
	m := new(dns.Msg)
	m.SetReply(r)

	// Lookup records
	if records, ok := h.zones[r.Question[0].Name]; ok {
		m.Answer = records
		m.Authoritative = true
	} else {
		m.SetRcode(r, dns.RcodeNameError)
	}

	w.WriteMsg(m)
}

handler := &MyHandler{
	zones: make(map[string][]dns.RR),
}

server := &dns.Server{
	Addr:    ":53",
	Net:     "udp",
	Handler: handler,
}

With TSIG

server := &dns.Server{
	Addr: ":53",
	Net:  "udp",
	TsigSecret: map[string]string{
		"example.com.": "base64secret==",
	},
}

dns.HandleFunc(".", func(w dns.ResponseWriter, r *dns.Msg) {
	// Check TSIG status
	if r.IsTsig() != nil {
		if w.TsigStatus() == nil {
			// TSIG valid
		}
	}

	m := new(dns.Msg)
	m.SetReply(r)
	w.WriteMsg(m)
})

log.Fatal(server.ListenAndServe())

Graceful Shutdown

server := &dns.Server{Addr: ":53", Net: "udp"}

go func() {
	log.Fatal(server.ListenAndServe())
}()

// Wait for signal
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
<-sigChan

// Graceful shutdown
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

if err := server.ShutdownContext(ctx); err != nil {
	log.Printf("Shutdown error: %v", err)
}

Custom Message Accept Function

server := &dns.Server{
	Addr: ":53",
	Net:  "udp",
	MsgAcceptFunc: func(hdr dns.Header) dns.MsgAcceptAction {
		// Reject queries with more than 1 question
		if hdr.Qdcount > 1 {
			return dns.MsgReject
		}
		// Ignore status requests
		if hdr.Bits>>11&0xF == dns.OpcodeStatus {
			return dns.MsgIgnore
		}
		return dns.MsgAccept
	},
}

Socket Options

server := &dns.Server{
	Addr:      ":53",
	Net:       "udp",
	ReusePort: true, // SO_REUSEPORT
	ReuseAddr: true, // SO_REUSEADDR
}

Custom Timeouts

server := &dns.Server{
	Addr:         ":53",
	Net:          "tcp",
	ReadTimeout:  5 * time.Second,
	WriteTimeout: 5 * time.Second,
	IdleTimeout: func() time.Duration {
		return 10 * time.Second
	},
}

Hijacking Connection

dns.HandleFunc(".", func(w dns.ResponseWriter, r *dns.Msg) {
	// Take over the connection
	w.Hijack()

	// Now you have full control
	// Write custom responses, etc.
})

Systemd Socket Activation

// Get listener from systemd
listeners, err := systemd.ListenersWithNames()
if err != nil {
	log.Fatal(err)
}

server := &dns.Server{
	Listener: listeners["dns.socket"][0].(net.Listener),
	Handler:  dns.HandlerFunc(handleRequest),
}

log.Fatal(server.ActivateAndServe())

Dual Stack (IPv4 + IPv6)

// IPv4 server
server4 := &dns.Server{
	Addr:    "0.0.0.0:53",
	Net:     "udp",
	Handler: handler,
}

// IPv6 server
server6 := &dns.Server{
	Addr:    "[::]:53",
	Net:     "udp",
	Handler: handler,
}

go server4.ListenAndServe()
go server6.ListenAndServe()

UDP and TCP Servers

udpServer := &dns.Server{
	Addr:    ":53",
	Net:     "udp",
	Handler: handler,
}

tcpServer := &dns.Server{
	Addr:    ":53",
	Net:     "tcp",
	Handler: handler,
}

go udpServer.ListenAndServe()
go tcpServer.ListenAndServe()

Pattern Matching

ServeMux uses longest match for patterns:

  • "." matches all names
  • "example.com." matches example.com and all subdomains
  • "www.example.com." matches only www.example.com

More specific patterns take precedence:

mux.HandleFunc(".", catchAll)                    // Lowest priority
mux.HandleFunc("example.com.", handleExample)    // Medium priority
mux.HandleFunc("www.example.com.", handleWWW)    // Highest priority

Related Topics