diff options
Diffstat (limited to 'core/dnsserver/server.go')
-rw-r--r-- | core/dnsserver/server.go | 122 |
1 files changed, 90 insertions, 32 deletions
diff --git a/core/dnsserver/server.go b/core/dnsserver/server.go index 15d1f90ce..478287bf8 100644 --- a/core/dnsserver/server.go +++ b/core/dnsserver/server.go @@ -37,23 +37,28 @@ type Server struct { server [2]*dns.Server // 0 is a net.Listener, 1 is a net.PacketConn (a *UDPConn) in our case. m sync.Mutex // protects the servers - zones map[string]*Config // zones keyed by their address - dnsWg sync.WaitGroup // used to wait on outstanding connections - graceTimeout time.Duration // the maximum duration of a graceful shutdown - trace trace.Trace // the trace plugin for the server - debug bool // disable recover() - stacktrace bool // enable stacktrace in recover error log - classChaos bool // allow non-INET class queries + zones map[string][]*Config // zones keyed by their address + dnsWg sync.WaitGroup // used to wait on outstanding connections + graceTimeout time.Duration // the maximum duration of a graceful shutdown + trace trace.Trace // the trace plugin for the server + debug bool // disable recover() + stacktrace bool // enable stacktrace in recover error log + classChaos bool // allow non-INET class queries tsigSecret map[string]string } +// MetadataCollector is a plugin that can retrieve metadata functions from all metadata providing plugins +type MetadataCollector interface { + Collect(context.Context, request.Request) context.Context +} + // NewServer returns a new CoreDNS server and compiles all plugins in to it. By default CH class // queries are blocked unless queries from enableChaos are loaded. func NewServer(addr string, group []*Config) (*Server, error) { s := &Server{ Addr: addr, - zones: make(map[string]*Config), + zones: make(map[string][]*Config), graceTimeout: 5 * time.Second, tsigSecret: make(map[string]string), } @@ -72,8 +77,9 @@ func NewServer(addr string, group []*Config) (*Server, error) { log.D.Set() } s.stacktrace = site.Stacktrace - // set the config per zone - s.zones[site.Zone] = site + + // append the config to the zone's configs + s.zones[site.Zone] = append(s.zones[site.Zone], site) // copy tsig secrets for key, secret := range site.TsigSecret { @@ -88,6 +94,12 @@ func NewServer(addr string, group []*Config) (*Server, error) { // register the *handler* also site.registerHandler(stack) + // If the current plugin is a MetadataCollector, bookmark it for later use. This loop traverses the plugin + // list backwards, so the first MetadataCollector plugin wins. + if mdc, ok := stack.(MetadataCollector); ok { + site.metaCollector = mdc + } + if s.trace == nil && stack.Name() == "trace" { // we have to stash away the plugin, not the // Tracer object, because the Tracer won't be initialized yet @@ -254,24 +266,39 @@ func (s *Server) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) ) for { - if h, ok := s.zones[q[off:]]; ok { - if h.pluginChain == nil { // zone defined, but has not got any plugins - errorAndMetricsFunc(s.Addr, w, r, dns.RcodeRefused) - return - } - if r.Question[0].Qtype != dns.TypeDS { - rcode, _ := h.pluginChain.ServeDNS(ctx, w, r) - if !plugin.ClientWrite(rcode) { - errorFunc(s.Addr, w, r, rcode) + if z, ok := s.zones[q[off:]]; ok { + for _, h := range z { + if h.pluginChain == nil { // zone defined, but has not got any plugins + errorAndMetricsFunc(s.Addr, w, r, dns.RcodeRefused) + return + } + + if h.metaCollector != nil { + // Collect metadata now, so it can be used before we send a request down the plugin chain. + ctx = h.metaCollector.Collect(ctx, request.Request{Req: r, W: w}) + } + + // If all filter funcs pass, use this config. + if passAllFilterFuncs(ctx, h.FilterFuncs, &request.Request{Req: r, W: w}) { + if h.ViewName != "" { + // if there was a view defined for this Config, set the view name in the context + ctx = context.WithValue(ctx, ViewKey{}, h.ViewName) + } + if r.Question[0].Qtype != dns.TypeDS { + rcode, _ := h.pluginChain.ServeDNS(ctx, w, r) + if !plugin.ClientWrite(rcode) { + errorFunc(s.Addr, w, r, rcode) + } + return + } + // The type is DS, keep the handler, but keep on searching as maybe we are serving + // the parent as well and the DS should be routed to it - this will probably *misroute* DS + // queries to a possibly grand parent, but there is no way for us to know at this point + // if there is an actual delegation from grandparent -> parent -> zone. + // In all fairness: direct DS queries should not be needed. + dshandler = h } - return } - // The type is DS, keep the handler, but keep on searching as maybe we are serving - // the parent as well and the DS should be routed to it - this will probably *misroute* DS - // queries to a possibly grand parent, but there is no way for us to know at this point - // if there is an actual delegation from grandparent -> parent -> zone. - // In all fairness: direct DS queries should not be needed. - dshandler = h } off, end = dns.NextLabel(q, off) if end { @@ -289,18 +316,46 @@ func (s *Server) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) } // Wildcard match, if we have found nothing try the root zone as a last resort. - if h, ok := s.zones["."]; ok && h.pluginChain != nil { - rcode, _ := h.pluginChain.ServeDNS(ctx, w, r) - if !plugin.ClientWrite(rcode) { - errorFunc(s.Addr, w, r, rcode) + if z, ok := s.zones["."]; ok { + for _, h := range z { + if h.pluginChain == nil { + continue + } + + if h.metaCollector != nil { + // Collect metadata now, so it can be used before we send a request down the plugin chain. + ctx = h.metaCollector.Collect(ctx, request.Request{Req: r, W: w}) + } + + // If all filter funcs pass, use this config. + if passAllFilterFuncs(ctx, h.FilterFuncs, &request.Request{Req: r, W: w}) { + if h.ViewName != "" { + // if there was a view defined for this Config, set the view name in the context + ctx = context.WithValue(ctx, ViewKey{}, h.ViewName) + } + rcode, _ := h.pluginChain.ServeDNS(ctx, w, r) + if !plugin.ClientWrite(rcode) { + errorFunc(s.Addr, w, r, rcode) + } + return + } } - return } // Still here? Error out with REFUSED. errorAndMetricsFunc(s.Addr, w, r, dns.RcodeRefused) } +// passAllFilterFuncs returns true if all filter funcs evaluate to true for the given request +func passAllFilterFuncs(ctx context.Context, filterFuncs []FilterFunc, req *request.Request) bool { + for _, ff := range filterFuncs { + if !ff(ctx, req) { + return false + } + } + return true +} + // OnStartupComplete lists the sites served by this server // and any relevant information, assuming Quiet is false. func (s *Server) OnStartupComplete() { @@ -341,7 +396,7 @@ func errorAndMetricsFunc(server string, w dns.ResponseWriter, r *dns.Msg, rc int answer.SetRcode(r, rc) state.SizeAndDo(answer) - vars.Report(server, state, vars.Dropped, rcode.ToString(rc), "" /* plugin */, answer.Len(), time.Now()) + vars.Report(server, state, vars.Dropped, "", rcode.ToString(rc), "" /* plugin */, answer.Len(), time.Now()) w.WriteMsg(answer) } @@ -357,6 +412,9 @@ type ( // LoopKey is the context key to detect server wide loops. LoopKey struct{} + + // ViewKey is the context key for the current view, if defined + ViewKey struct{} ) // EnableChaos is a map with plugin names for which we should open CH class queries as we block these by default. |