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