DNS server framework with handler pattern for building authoritative servers and resolvers.
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
}Interface for DNS request handlers.
type Handler interface {
ServeDNS(w ResponseWriter, r *Msg)
}Function adapter for Handler interface.
type HandlerFunc func(ResponseWriter, *Msg)
// ServeDNS calls f(w, r)
func (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg)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
}Interface for accessing TLS connection state.
type ConnectionStater interface {
ConnectionState() *tls.ConnectionState
}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// 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// 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)// 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// 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 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)// DefaultServeMux is the default ServeMux instance
var DefaultServeMux = NewServeMux()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())dns.HandleFunc(".", handleRequest)
server := &dns.Server{
Addr: ":53",
Net: "tcp",
}
log.Fatal(server.ListenAndServe())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())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())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,
}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())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)
}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
},
}server := &dns.Server{
Addr: ":53",
Net: "udp",
ReusePort: true, // SO_REUSEPORT
ReuseAddr: true, // SO_REUSEADDR
}server := &dns.Server{
Addr: ":53",
Net: "tcp",
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
IdleTimeout: func() time.Duration {
return 10 * time.Second
},
}dns.HandleFunc(".", func(w dns.ResponseWriter, r *dns.Msg) {
// Take over the connection
w.Hijack()
// Now you have full control
// Write custom responses, etc.
})// 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())// 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()udpServer := &dns.Server{
Addr: ":53",
Net: "udp",
Handler: handler,
}
tcpServer := &dns.Server{
Addr: ":53",
Net: "tcp",
Handler: handler,
}
go udpServer.ListenAndServe()
go tcpServer.ListenAndServe()ServeMux uses longest match for patterns:
"." matches all names"example.com." matches example.com and all subdomains"www.example.com." matches only www.example.comMore specific patterns take precedence:
mux.HandleFunc(".", catchAll) // Lowest priority
mux.HandleFunc("example.com.", handleExample) // Medium priority
mux.HandleFunc("www.example.com.", handleWWW) // Highest priority