Make the receiver a pointer so that the uptdateStubZones map update will retain the stubzones found, unlike the current case where the update will be applied and then promptly forgotten, because it is working on a copy. Add test/etcd_test.go to test a large part of the code. This didn't catch the chaos middleware hack though. The chaos middleware zones are now *not* automatically added. You have to take care of that by yourself (docs updates). When using debug queries and falling through to the next middleware in etcd, restore the original (with o-o.debug) query before passing it on.
85 lines
2 KiB
Go
85 lines
2 KiB
Go
package etcd
|
|
|
|
import (
|
|
"errors"
|
|
"log"
|
|
|
|
"github.com/miekg/coredns/middleware"
|
|
|
|
"github.com/miekg/dns"
|
|
"golang.org/x/net/context"
|
|
)
|
|
|
|
// Stub wraps an Etcd. We have this type so that it can have a ServeDNS method.
|
|
type Stub struct {
|
|
*Etcd
|
|
Zone string // for what zone (and thus what nameservers are we called)
|
|
}
|
|
|
|
func (s Stub) ServeDNS(ctx context.Context, w dns.ResponseWriter, req *dns.Msg) (int, error) {
|
|
if hasStubEdns0(req) {
|
|
log.Printf("[WARNING] Forwarding cycle detected, refusing msg: %s", req.Question[0].Name)
|
|
return dns.RcodeRefused, errors.New("stub forward cycle")
|
|
}
|
|
req = addStubEdns0(req)
|
|
proxy, ok := (*s.Etcd.Stubmap)[s.Zone]
|
|
if !ok { // somebody made a mistake..
|
|
return dns.RcodeServerFailure, nil
|
|
}
|
|
|
|
state := middleware.State{W: w, Req: req}
|
|
m, e := proxy.Forward(state)
|
|
if e != nil {
|
|
return dns.RcodeServerFailure, e
|
|
}
|
|
m.RecursionAvailable, m.Compress = true, true
|
|
state.SizeAndDo(m)
|
|
w.WriteMsg(m)
|
|
return dns.RcodeSuccess, nil
|
|
}
|
|
|
|
// hasStubEdns0 checks if the message is carrying our special edns0 zero option.
|
|
func hasStubEdns0(m *dns.Msg) bool {
|
|
option := m.IsEdns0()
|
|
if option == nil {
|
|
return false
|
|
}
|
|
for _, o := range option.Option {
|
|
if o.Option() == ednsStubCode && len(o.(*dns.EDNS0_LOCAL).Data) == 1 &&
|
|
o.(*dns.EDNS0_LOCAL).Data[0] == 1 {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// addStubEdns0 adds our special option to the message's OPT record.
|
|
func addStubEdns0(m *dns.Msg) *dns.Msg {
|
|
option := m.IsEdns0()
|
|
// Add a custom EDNS0 option to the packet, so we can detect loops when 2 stubs are forwarding to each other.
|
|
if option != nil {
|
|
option.Option = append(option.Option, &dns.EDNS0_LOCAL{Code: ednsStubCode, Data: []byte{1}})
|
|
return m
|
|
}
|
|
|
|
m.Extra = append(m.Extra, ednsStub)
|
|
return m
|
|
}
|
|
|
|
const (
|
|
ednsStubCode = dns.EDNS0LOCALSTART + 10
|
|
stubDomain = "stub.dns"
|
|
)
|
|
|
|
var ednsStub = func() *dns.OPT {
|
|
o := new(dns.OPT)
|
|
o.Hdr.Name = "."
|
|
o.Hdr.Rrtype = dns.TypeOPT
|
|
o.SetUDPSize(4096)
|
|
|
|
e := new(dns.EDNS0_LOCAL)
|
|
e.Code = ednsStubCode
|
|
e.Data = []byte{1}
|
|
o.Option = append(o.Option, e)
|
|
return o
|
|
}()
|