Show from and to address when detecting a loop they may aid in debugging. Hard to create a unit test, but this is a startup run with self induced loop: ~~~ corefile .:1053 { loop log forward . 127.0.0.1:1053 } ~~~~ ~~~ :1053 2018-12-16T10:11:03.695Z [INFO] CoreDNS-1.3.0 2018-12-16T10:11:03.695Z [INFO] linux/amd64, go1.11, CoreDNS-1.3.0 linux/amd64, go1.11, 2018-12-16T10:11:03.696Z [FATAL] plugin/loop: Loop (127.0.0.1:51384 -> :1053) detected for zone ".", see https://coredns.io/plugins/loop#troubleshooting. Query: "HINFO 2781022615773629442.4133547885299871809." ~~~ Update the docs and polished that a bit as well. Signed-off-by: Miek Gieben <miek@miek.nl>
109 lines
2.1 KiB
Go
109 lines
2.1 KiB
Go
package loop
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
|
|
"github.com/coredns/coredns/plugin"
|
|
clog "github.com/coredns/coredns/plugin/pkg/log"
|
|
"github.com/coredns/coredns/request"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
var log = clog.NewWithPlugin("loop")
|
|
|
|
// Loop is a plugin that implements loop detection by sending a "random" query.
|
|
type Loop struct {
|
|
Next plugin.Handler
|
|
|
|
zone string
|
|
qname string
|
|
addr string
|
|
|
|
sync.RWMutex
|
|
i int
|
|
off bool
|
|
}
|
|
|
|
// New returns a new initialized Loop.
|
|
func New(zone string) *Loop { return &Loop{zone: zone, qname: qname(zone)} }
|
|
|
|
// ServeDNS implements the plugin.Handler interface.
|
|
func (l *Loop) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
|
if r.Question[0].Qtype != dns.TypeHINFO {
|
|
return plugin.NextOrFailure(l.Name(), l.Next, ctx, w, r)
|
|
}
|
|
if l.disabled() {
|
|
return plugin.NextOrFailure(l.Name(), l.Next, ctx, w, r)
|
|
}
|
|
|
|
state := request.Request{W: w, Req: r}
|
|
|
|
zone := plugin.Zones([]string{l.zone}).Matches(state.Name())
|
|
if zone == "" {
|
|
return plugin.NextOrFailure(l.Name(), l.Next, ctx, w, r)
|
|
}
|
|
|
|
if state.Name() == l.qname {
|
|
l.inc()
|
|
}
|
|
|
|
if l.seen() > 2 {
|
|
log.Fatalf(`Loop (%s -> %s) detected for zone %q, see https://coredns.io/plugins/loop#troubleshooting. Query: "HINFO %s"`, state.RemoteAddr(), l.address(), l.zone, l.qname)
|
|
}
|
|
|
|
return plugin.NextOrFailure(l.Name(), l.Next, ctx, w, r)
|
|
}
|
|
|
|
// Name implements the plugin.Handler interface.
|
|
func (l *Loop) Name() string { return "loop" }
|
|
|
|
func (l *Loop) exchange(addr string) (*dns.Msg, error) {
|
|
m := new(dns.Msg)
|
|
m.SetQuestion(l.qname, dns.TypeHINFO)
|
|
|
|
return dns.Exchange(m, addr)
|
|
}
|
|
|
|
func (l *Loop) seen() int {
|
|
l.RLock()
|
|
defer l.RUnlock()
|
|
return l.i
|
|
}
|
|
|
|
func (l *Loop) inc() {
|
|
l.Lock()
|
|
defer l.Unlock()
|
|
l.i++
|
|
}
|
|
|
|
func (l *Loop) reset() {
|
|
l.Lock()
|
|
defer l.Unlock()
|
|
l.i = 0
|
|
}
|
|
|
|
func (l *Loop) setDisabled() {
|
|
l.Lock()
|
|
defer l.Unlock()
|
|
l.off = true
|
|
}
|
|
|
|
func (l *Loop) disabled() bool {
|
|
l.RLock()
|
|
defer l.RUnlock()
|
|
return l.off
|
|
}
|
|
|
|
func (l *Loop) setAddress(addr string) {
|
|
l.Lock()
|
|
defer l.Unlock()
|
|
l.addr = addr
|
|
}
|
|
|
|
func (l *Loop) address() string {
|
|
l.RLock()
|
|
defer l.RUnlock()
|
|
return l.addr
|
|
}
|