1package dns
2
3import (
4	"sync"
5)
6
7// ServeMux is an DNS request multiplexer. It matches the zone name of
8// each incoming request against a list of registered patterns add calls
9// the handler for the pattern that most closely matches the zone name.
10//
11// ServeMux is DNSSEC aware, meaning that queries for the DS record are
12// redirected to the parent zone (if that is also registered), otherwise
13// the child gets the query.
14//
15// ServeMux is also safe for concurrent access from multiple goroutines.
16//
17// The zero ServeMux is empty and ready for use.
18type ServeMux struct {
19	z map[string]Handler
20	m sync.RWMutex
21}
22
23// NewServeMux allocates and returns a new ServeMux.
24func NewServeMux() *ServeMux {
25	return new(ServeMux)
26}
27
28// DefaultServeMux is the default ServeMux used by Serve.
29var DefaultServeMux = NewServeMux()
30
31func (mux *ServeMux) match(q string, t uint16) Handler {
32	mux.m.RLock()
33	defer mux.m.RUnlock()
34	if mux.z == nil {
35		return nil
36	}
37
38	q = CanonicalName(q)
39
40	var handler Handler
41	for off, end := 0, false; !end; off, end = NextLabel(q, off) {
42		if h, ok := mux.z[q[off:]]; ok {
43			if t != TypeDS {
44				return h
45			}
46			// Continue for DS to see if we have a parent too, if so delegate to the parent
47			handler = h
48		}
49	}
50
51	// Wildcard match, if we have found nothing try the root zone as a last resort.
52	if h, ok := mux.z["."]; ok {
53		return h
54	}
55
56	return handler
57}
58
59// Handle adds a handler to the ServeMux for pattern.
60func (mux *ServeMux) Handle(pattern string, handler Handler) {
61	if pattern == "" {
62		panic("dns: invalid pattern " + pattern)
63	}
64	mux.m.Lock()
65	if mux.z == nil {
66		mux.z = make(map[string]Handler)
67	}
68	mux.z[CanonicalName(pattern)] = handler
69	mux.m.Unlock()
70}
71
72// HandleFunc adds a handler function to the ServeMux for pattern.
73func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
74	mux.Handle(pattern, HandlerFunc(handler))
75}
76
77// HandleRemove deregisters the handler specific for pattern from the ServeMux.
78func (mux *ServeMux) HandleRemove(pattern string) {
79	if pattern == "" {
80		panic("dns: invalid pattern " + pattern)
81	}
82	mux.m.Lock()
83	delete(mux.z, CanonicalName(pattern))
84	mux.m.Unlock()
85}
86
87// ServeDNS dispatches the request to the handler whose pattern most
88// closely matches the request message.
89//
90// ServeDNS is DNSSEC aware, meaning that queries for the DS record
91// are redirected to the parent zone (if that is also registered),
92// otherwise the child gets the query.
93//
94// If no handler is found, or there is no question, a standard REFUSED
95// message is returned
96func (mux *ServeMux) ServeDNS(w ResponseWriter, req *Msg) {
97	var h Handler
98	if len(req.Question) >= 1 { // allow more than one question
99		h = mux.match(req.Question[0].Name, req.Question[0].Qtype)
100	}
101
102	if h != nil {
103		h.ServeDNS(w, req)
104	} else {
105		handleRefused(w, req)
106	}
107}
108
109// Handle registers the handler with the given pattern
110// in the DefaultServeMux. The documentation for
111// ServeMux explains how patterns are matched.
112func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
113
114// HandleRemove deregisters the handle with the given pattern
115// in the DefaultServeMux.
116func HandleRemove(pattern string) { DefaultServeMux.HandleRemove(pattern) }
117
118// HandleFunc registers the handler function with the given pattern
119// in the DefaultServeMux.
120func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
121	DefaultServeMux.HandleFunc(pattern, handler)
122}
123