Move ServeMux into seperate file (#753)
This reduces the clutter in server.go.
This commit is contained in:
parent
ab16005053
commit
60d113313c
|
@ -0,0 +1,118 @@
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
// ServeMux is an DNS request multiplexer. It matches the
|
||||||
|
// zone name of each incoming request against a list of
|
||||||
|
// registered patterns add calls the handler for the pattern
|
||||||
|
// that most closely matches the zone name. ServeMux is DNSSEC aware, meaning
|
||||||
|
// that queries for the DS record are redirected to the parent zone (if that
|
||||||
|
// is also registered), otherwise the child gets the query.
|
||||||
|
// ServeMux is also safe for concurrent access from multiple goroutines.
|
||||||
|
type ServeMux struct {
|
||||||
|
z map[string]Handler
|
||||||
|
m *sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServeMux allocates and returns a new ServeMux.
|
||||||
|
func NewServeMux() *ServeMux {
|
||||||
|
return &ServeMux{z: make(map[string]Handler), m: new(sync.RWMutex)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultServeMux is the default ServeMux used by Serve.
|
||||||
|
var DefaultServeMux = NewServeMux()
|
||||||
|
|
||||||
|
func (mux *ServeMux) match(q string, t uint16) Handler {
|
||||||
|
mux.m.RLock()
|
||||||
|
defer mux.m.RUnlock()
|
||||||
|
var handler Handler
|
||||||
|
b := make([]byte, len(q)) // worst case, one label of length q
|
||||||
|
off := 0
|
||||||
|
end := false
|
||||||
|
for {
|
||||||
|
l := len(q[off:])
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
b[i] = q[off+i]
|
||||||
|
if b[i] >= 'A' && b[i] <= 'Z' {
|
||||||
|
b[i] |= 'a' - 'A'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if h, ok := mux.z[string(b[:l])]; ok { // causes garbage, might want to change the map key
|
||||||
|
if t != TypeDS {
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
// Continue for DS to see if we have a parent too, if so delegeate to the parent
|
||||||
|
handler = h
|
||||||
|
}
|
||||||
|
off, end = NextLabel(q, off)
|
||||||
|
if end {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Wildcard match, if we have found nothing try the root zone as a last resort.
|
||||||
|
if h, ok := mux.z["."]; ok {
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
return handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle adds a handler to the ServeMux for pattern.
|
||||||
|
func (mux *ServeMux) Handle(pattern string, handler Handler) {
|
||||||
|
if pattern == "" {
|
||||||
|
panic("dns: invalid pattern " + pattern)
|
||||||
|
}
|
||||||
|
mux.m.Lock()
|
||||||
|
mux.z[Fqdn(pattern)] = handler
|
||||||
|
mux.m.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleFunc adds a handler function to the ServeMux for pattern.
|
||||||
|
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
|
||||||
|
mux.Handle(pattern, HandlerFunc(handler))
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleRemove deregistrars the handler specific for pattern from the ServeMux.
|
||||||
|
func (mux *ServeMux) HandleRemove(pattern string) {
|
||||||
|
if pattern == "" {
|
||||||
|
panic("dns: invalid pattern " + pattern)
|
||||||
|
}
|
||||||
|
mux.m.Lock()
|
||||||
|
delete(mux.z, Fqdn(pattern))
|
||||||
|
mux.m.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func failedHandler() Handler { return HandlerFunc(HandleFailed) }
|
||||||
|
|
||||||
|
// ServeDNS dispatches the request to the handler whose
|
||||||
|
// pattern most closely matches the request message. If DefaultServeMux
|
||||||
|
// is used the correct thing for DS queries is done: a possible parent
|
||||||
|
// is sought.
|
||||||
|
// If no handler is found a standard SERVFAIL message is returned
|
||||||
|
// If the request message does not have exactly one question in the
|
||||||
|
// question section a SERVFAIL is returned, unlesss Unsafe is true.
|
||||||
|
func (mux *ServeMux) ServeDNS(w ResponseWriter, request *Msg) {
|
||||||
|
var h Handler
|
||||||
|
if len(request.Question) < 1 { // allow more than one question
|
||||||
|
h = failedHandler()
|
||||||
|
} else {
|
||||||
|
if h = mux.match(request.Question[0].Name, request.Question[0].Qtype); h == nil {
|
||||||
|
h = failedHandler()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h.ServeDNS(w, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle registers the handler with the given pattern
|
||||||
|
// in the DefaultServeMux. The documentation for
|
||||||
|
// ServeMux explains how patterns are matched.
|
||||||
|
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
|
||||||
|
|
||||||
|
// HandleRemove deregisters the handle with the given pattern
|
||||||
|
// in the DefaultServeMux.
|
||||||
|
func HandleRemove(pattern string) { DefaultServeMux.HandleRemove(pattern) }
|
||||||
|
|
||||||
|
// HandleFunc registers the handler function with the given pattern
|
||||||
|
// in the DefaultServeMux.
|
||||||
|
func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
|
||||||
|
DefaultServeMux.HandleFunc(pattern, handler)
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestDotAsCatchAllWildcard(t *testing.T) {
|
||||||
|
mux := NewServeMux()
|
||||||
|
mux.Handle(".", HandlerFunc(HelloServer))
|
||||||
|
mux.Handle("example.com.", HandlerFunc(AnotherHelloServer))
|
||||||
|
|
||||||
|
handler := mux.match("www.miek.nl.", TypeTXT)
|
||||||
|
if handler == nil {
|
||||||
|
t.Error("wildcard match failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
handler = mux.match("www.example.com.", TypeTXT)
|
||||||
|
if handler == nil {
|
||||||
|
t.Error("example.com match failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
handler = mux.match("a.www.example.com.", TypeTXT)
|
||||||
|
if handler == nil {
|
||||||
|
t.Error("a.www.example.com match failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
handler = mux.match("boe.", TypeTXT)
|
||||||
|
if handler == nil {
|
||||||
|
t.Error("boe. match failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCaseFolding(t *testing.T) {
|
||||||
|
mux := NewServeMux()
|
||||||
|
mux.Handle("_udp.example.com.", HandlerFunc(HelloServer))
|
||||||
|
|
||||||
|
handler := mux.match("_dns._udp.example.com.", TypeSRV)
|
||||||
|
if handler == nil {
|
||||||
|
t.Error("case sensitive characters folded")
|
||||||
|
}
|
||||||
|
|
||||||
|
handler = mux.match("_DNS._UDP.EXAMPLE.COM.", TypeSRV)
|
||||||
|
if handler == nil {
|
||||||
|
t.Error("case insensitive characters not folded")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRootServer(t *testing.T) {
|
||||||
|
mux := NewServeMux()
|
||||||
|
mux.Handle(".", HandlerFunc(HelloServer))
|
||||||
|
|
||||||
|
handler := mux.match(".", TypeNS)
|
||||||
|
if handler == nil {
|
||||||
|
t.Error("root match failed")
|
||||||
|
}
|
||||||
|
}
|
135
server.go
135
server.go
|
@ -41,6 +41,17 @@ type Handler interface {
|
||||||
ServeDNS(w ResponseWriter, r *Msg)
|
ServeDNS(w ResponseWriter, r *Msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The HandlerFunc type is an adapter to allow the use of
|
||||||
|
// ordinary functions as DNS handlers. If f is a function
|
||||||
|
// with the appropriate signature, HandlerFunc(f) is a
|
||||||
|
// Handler object that calls f.
|
||||||
|
type HandlerFunc func(ResponseWriter, *Msg)
|
||||||
|
|
||||||
|
// ServeDNS calls f(w, r).
|
||||||
|
func (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg) {
|
||||||
|
f(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
// A ResponseWriter interface is used by an DNS handler to
|
// A ResponseWriter interface is used by an DNS handler to
|
||||||
// construct an DNS response.
|
// construct an DNS response.
|
||||||
type ResponseWriter interface {
|
type ResponseWriter interface {
|
||||||
|
@ -83,35 +94,6 @@ type response struct {
|
||||||
wg *sync.WaitGroup // for gracefull shutdown
|
wg *sync.WaitGroup // for gracefull shutdown
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeMux is an DNS request multiplexer. It matches the
|
|
||||||
// zone name of each incoming request against a list of
|
|
||||||
// registered patterns add calls the handler for the pattern
|
|
||||||
// that most closely matches the zone name. ServeMux is DNSSEC aware, meaning
|
|
||||||
// that queries for the DS record are redirected to the parent zone (if that
|
|
||||||
// is also registered), otherwise the child gets the query.
|
|
||||||
// ServeMux is also safe for concurrent access from multiple goroutines.
|
|
||||||
type ServeMux struct {
|
|
||||||
z map[string]Handler
|
|
||||||
m *sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewServeMux allocates and returns a new ServeMux.
|
|
||||||
func NewServeMux() *ServeMux { return &ServeMux{z: make(map[string]Handler), m: new(sync.RWMutex)} }
|
|
||||||
|
|
||||||
// DefaultServeMux is the default ServeMux used by Serve.
|
|
||||||
var DefaultServeMux = NewServeMux()
|
|
||||||
|
|
||||||
// The HandlerFunc type is an adapter to allow the use of
|
|
||||||
// ordinary functions as DNS handlers. If f is a function
|
|
||||||
// with the appropriate signature, HandlerFunc(f) is a
|
|
||||||
// Handler object that calls f.
|
|
||||||
type HandlerFunc func(ResponseWriter, *Msg)
|
|
||||||
|
|
||||||
// ServeDNS calls f(w, r).
|
|
||||||
func (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg) {
|
|
||||||
f(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleFailed returns a HandlerFunc that returns SERVFAIL for every request it gets.
|
// HandleFailed returns a HandlerFunc that returns SERVFAIL for every request it gets.
|
||||||
func HandleFailed(w ResponseWriter, r *Msg) {
|
func HandleFailed(w ResponseWriter, r *Msg) {
|
||||||
m := new(Msg)
|
m := new(Msg)
|
||||||
|
@ -120,8 +102,6 @@ func HandleFailed(w ResponseWriter, r *Msg) {
|
||||||
w.WriteMsg(m)
|
w.WriteMsg(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func failedHandler() Handler { return HandlerFunc(HandleFailed) }
|
|
||||||
|
|
||||||
// ListenAndServe Starts a server on address and network specified Invoke handler
|
// ListenAndServe Starts a server on address and network specified Invoke handler
|
||||||
// for incoming queries.
|
// for incoming queries.
|
||||||
func ListenAndServe(addr string, network string, handler Handler) error {
|
func ListenAndServe(addr string, network string, handler Handler) error {
|
||||||
|
@ -160,99 +140,6 @@ func ActivateAndServe(l net.Listener, p net.PacketConn, handler Handler) error {
|
||||||
return server.ActivateAndServe()
|
return server.ActivateAndServe()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mux *ServeMux) match(q string, t uint16) Handler {
|
|
||||||
mux.m.RLock()
|
|
||||||
defer mux.m.RUnlock()
|
|
||||||
var handler Handler
|
|
||||||
b := make([]byte, len(q)) // worst case, one label of length q
|
|
||||||
off := 0
|
|
||||||
end := false
|
|
||||||
for {
|
|
||||||
l := len(q[off:])
|
|
||||||
for i := 0; i < l; i++ {
|
|
||||||
b[i] = q[off+i]
|
|
||||||
if b[i] >= 'A' && b[i] <= 'Z' {
|
|
||||||
b[i] |= 'a' - 'A'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if h, ok := mux.z[string(b[:l])]; ok { // causes garbage, might want to change the map key
|
|
||||||
if t != TypeDS {
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
// Continue for DS to see if we have a parent too, if so delegeate to the parent
|
|
||||||
handler = h
|
|
||||||
}
|
|
||||||
off, end = NextLabel(q, off)
|
|
||||||
if end {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Wildcard match, if we have found nothing try the root zone as a last resort.
|
|
||||||
if h, ok := mux.z["."]; ok {
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
return handler
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle adds a handler to the ServeMux for pattern.
|
|
||||||
func (mux *ServeMux) Handle(pattern string, handler Handler) {
|
|
||||||
if pattern == "" {
|
|
||||||
panic("dns: invalid pattern " + pattern)
|
|
||||||
}
|
|
||||||
mux.m.Lock()
|
|
||||||
mux.z[Fqdn(pattern)] = handler
|
|
||||||
mux.m.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleFunc adds a handler function to the ServeMux for pattern.
|
|
||||||
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
|
|
||||||
mux.Handle(pattern, HandlerFunc(handler))
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleRemove deregistrars the handler specific for pattern from the ServeMux.
|
|
||||||
func (mux *ServeMux) HandleRemove(pattern string) {
|
|
||||||
if pattern == "" {
|
|
||||||
panic("dns: invalid pattern " + pattern)
|
|
||||||
}
|
|
||||||
mux.m.Lock()
|
|
||||||
delete(mux.z, Fqdn(pattern))
|
|
||||||
mux.m.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeDNS dispatches the request to the handler whose
|
|
||||||
// pattern most closely matches the request message. If DefaultServeMux
|
|
||||||
// is used the correct thing for DS queries is done: a possible parent
|
|
||||||
// is sought.
|
|
||||||
// If no handler is found a standard SERVFAIL message is returned
|
|
||||||
// If the request message does not have exactly one question in the
|
|
||||||
// question section a SERVFAIL is returned, unlesss Unsafe is true.
|
|
||||||
func (mux *ServeMux) ServeDNS(w ResponseWriter, request *Msg) {
|
|
||||||
var h Handler
|
|
||||||
if len(request.Question) < 1 { // allow more than one question
|
|
||||||
h = failedHandler()
|
|
||||||
} else {
|
|
||||||
if h = mux.match(request.Question[0].Name, request.Question[0].Qtype); h == nil {
|
|
||||||
h = failedHandler()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h.ServeDNS(w, request)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle registers the handler with the given pattern
|
|
||||||
// in the DefaultServeMux. The documentation for
|
|
||||||
// ServeMux explains how patterns are matched.
|
|
||||||
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
|
|
||||||
|
|
||||||
// HandleRemove deregisters the handle with the given pattern
|
|
||||||
// in the DefaultServeMux.
|
|
||||||
func HandleRemove(pattern string) { DefaultServeMux.HandleRemove(pattern) }
|
|
||||||
|
|
||||||
// HandleFunc registers the handler function with the given pattern
|
|
||||||
// in the DefaultServeMux.
|
|
||||||
func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
|
|
||||||
DefaultServeMux.HandleFunc(pattern, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writer writes raw DNS messages; each call to Write should send an entire message.
|
// Writer writes raw DNS messages; each call to Write should send an entire message.
|
||||||
type Writer interface {
|
type Writer interface {
|
||||||
io.Writer
|
io.Writer
|
||||||
|
|
|
@ -510,57 +510,6 @@ func BenchmarkServeCompress(b *testing.B) {
|
||||||
runtime.GOMAXPROCS(a)
|
runtime.GOMAXPROCS(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDotAsCatchAllWildcard(t *testing.T) {
|
|
||||||
mux := NewServeMux()
|
|
||||||
mux.Handle(".", HandlerFunc(HelloServer))
|
|
||||||
mux.Handle("example.com.", HandlerFunc(AnotherHelloServer))
|
|
||||||
|
|
||||||
handler := mux.match("www.miek.nl.", TypeTXT)
|
|
||||||
if handler == nil {
|
|
||||||
t.Error("wildcard match failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
handler = mux.match("www.example.com.", TypeTXT)
|
|
||||||
if handler == nil {
|
|
||||||
t.Error("example.com match failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
handler = mux.match("a.www.example.com.", TypeTXT)
|
|
||||||
if handler == nil {
|
|
||||||
t.Error("a.www.example.com match failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
handler = mux.match("boe.", TypeTXT)
|
|
||||||
if handler == nil {
|
|
||||||
t.Error("boe. match failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCaseFolding(t *testing.T) {
|
|
||||||
mux := NewServeMux()
|
|
||||||
mux.Handle("_udp.example.com.", HandlerFunc(HelloServer))
|
|
||||||
|
|
||||||
handler := mux.match("_dns._udp.example.com.", TypeSRV)
|
|
||||||
if handler == nil {
|
|
||||||
t.Error("case sensitive characters folded")
|
|
||||||
}
|
|
||||||
|
|
||||||
handler = mux.match("_DNS._UDP.EXAMPLE.COM.", TypeSRV)
|
|
||||||
if handler == nil {
|
|
||||||
t.Error("case insensitive characters not folded")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRootServer(t *testing.T) {
|
|
||||||
mux := NewServeMux()
|
|
||||||
mux.Handle(".", HandlerFunc(HelloServer))
|
|
||||||
|
|
||||||
handler := mux.match(".", TypeNS)
|
|
||||||
if handler == nil {
|
|
||||||
t.Error("root match failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type maxRec struct {
|
type maxRec struct {
|
||||||
max int
|
max int
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
|
|
Loading…
Reference in New Issue