mirror of
https://github.com/coredns/coredns.git
synced 2025-11-01 02:33:14 -04:00
Golint2 (#280)
* Fix linter errors * More linting fixes * More docs and making members private that dont need to be public * Fix linter errors * More linting fixes * More docs and making members private that dont need to be public * More lint fixes This leaves: ~~~ middleware/kubernetes/nametemplate/nametemplate.go:64:6: exported type NameTemplate should have comment or be unexported middleware/kubernetes/nametemplate/nametemplate.go:71:1: exported method NameTemplate.SetTemplate should have comment or be unexported middleware/kubernetes/nametemplate/nametemplate.go:108:1: exported method NameTemplate.GetZoneFromSegmentArray should have comment or be unexported middleware/kubernetes/nametemplate/nametemplate.go:116:1: exported method NameTemplate.GetNamespaceFromSegmentArray should have comment or be unexported middleware/kubernetes/nametemplate/nametemplate.go:120:1: exported method NameTemplate.GetServiceFromSegmentArray should have comment or be unexported middleware/kubernetes/nametemplate/nametemplate.go:124:1: exported method NameTemplate.GetTypeFromSegmentArray should have comment or be unexported middleware/kubernetes/nametemplate/nametemplate.go:135:1: exported method NameTemplate.GetSymbolFromSegmentArray should have comment or be unexported middleware/kubernetes/nametemplate/nametemplate.go:167:1: exported method NameTemplate.IsValid should have comment or be unexported middleware/kubernetes/nametemplate/nametemplate.go:182:6: exported type NameValues should have comment or be unexported middleware/kubernetes/util/util.go:1:1: package comment should be of the form "Package util ..." middleware/kubernetes/util/util.go:27:2: exported const WildcardStar should have comment (or a comment on this block) or be unexported middleware/proxy/lookup.go:66:1: exported method Proxy.Forward should have comment or be unexported middleware/proxy/proxy.go:24:6: exported type Client should have comment or be unexported middleware/proxy/proxy.go:107:1: exported function Clients should have comment or be unexported middleware/proxy/reverseproxy.go:10:6: exported type ReverseProxy should have comment or be unexported middleware/proxy/reverseproxy.go:16:1: exported method ReverseProxy.ServeDNS should have comment or be unexported middleware/proxy/upstream.go:42:6: exported type Options should have comment or be unexported ~~~ I plan on reworking the proxy anyway, so I'll leave that be.
This commit is contained in:
38
middleware/cache/cache.go
vendored
38
middleware/cache/cache.go
vendored
@@ -20,6 +20,7 @@ type Cache struct {
|
|||||||
cap time.Duration
|
cap time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewCache returns a new cache.
|
||||||
func NewCache(ttl int, zones []string, next middleware.Handler) Cache {
|
func NewCache(ttl int, zones []string, next middleware.Handler) Cache {
|
||||||
return Cache{Next: next, Zones: zones, cache: gcache.New(defaultDuration, purgeDuration), cap: time.Duration(ttl) * time.Second}
|
return Cache{Next: next, Zones: zones, cache: gcache.New(defaultDuration, purgeDuration), cap: time.Duration(ttl) * time.Second}
|
||||||
}
|
}
|
||||||
@@ -46,17 +47,20 @@ func cacheKey(m *dns.Msg, t response.Type, do bool) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
type CachingResponseWriter struct {
|
// ResponseWriter is a response writer that caches the reply message.
|
||||||
|
type ResponseWriter struct {
|
||||||
dns.ResponseWriter
|
dns.ResponseWriter
|
||||||
cache *gcache.Cache
|
cache *gcache.Cache
|
||||||
cap time.Duration
|
cap time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCachingResponseWriter(w dns.ResponseWriter, cache *gcache.Cache, cap time.Duration) *CachingResponseWriter {
|
// NewCachingResponseWriter returns a new ResponseWriter.
|
||||||
return &CachingResponseWriter{w, cache, cap}
|
func NewCachingResponseWriter(w dns.ResponseWriter, cache *gcache.Cache, cap time.Duration) *ResponseWriter {
|
||||||
|
return &ResponseWriter{w, cache, cap}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CachingResponseWriter) WriteMsg(res *dns.Msg) error {
|
// WriteMsg implements the dns.ResponseWriter interface.
|
||||||
|
func (c *ResponseWriter) WriteMsg(res *dns.Msg) error {
|
||||||
do := false
|
do := false
|
||||||
mt, opt := response.Classify(res)
|
mt, opt := response.Classify(res)
|
||||||
if opt != nil {
|
if opt != nil {
|
||||||
@@ -73,7 +77,7 @@ func (c *CachingResponseWriter) WriteMsg(res *dns.Msg) error {
|
|||||||
return c.ResponseWriter.WriteMsg(res)
|
return c.ResponseWriter.WriteMsg(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CachingResponseWriter) set(m *dns.Msg, key string, mt response.Type) {
|
func (c *ResponseWriter) set(m *dns.Msg, key string, mt response.Type) {
|
||||||
if key == "" {
|
if key == "" {
|
||||||
log.Printf("[ERROR] Caching called with empty cache key")
|
log.Printf("[ERROR] Caching called with empty cache key")
|
||||||
return
|
return
|
||||||
@@ -83,14 +87,14 @@ func (c *CachingResponseWriter) set(m *dns.Msg, key string, mt response.Type) {
|
|||||||
switch mt {
|
switch mt {
|
||||||
case response.Success, response.Delegation:
|
case response.Success, response.Delegation:
|
||||||
if c.cap == 0 {
|
if c.cap == 0 {
|
||||||
duration = minTtl(m.Answer, mt)
|
duration = minTTL(m.Answer, mt)
|
||||||
}
|
}
|
||||||
i := newItem(m, duration)
|
i := newItem(m, duration)
|
||||||
|
|
||||||
c.cache.Set(key, i, duration)
|
c.cache.Set(key, i, duration)
|
||||||
case response.NameError, response.NoData:
|
case response.NameError, response.NoData:
|
||||||
if c.cap == 0 {
|
if c.cap == 0 {
|
||||||
duration = minTtl(m.Ns, mt)
|
duration = minTTL(m.Ns, mt)
|
||||||
}
|
}
|
||||||
i := newItem(m, duration)
|
i := newItem(m, duration)
|
||||||
|
|
||||||
@@ -102,23 +106,25 @@ func (c *CachingResponseWriter) set(m *dns.Msg, key string, mt response.Type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CachingResponseWriter) Write(buf []byte) (int, error) {
|
// Write implements the dns.ResponseWriter interface.
|
||||||
|
func (c *ResponseWriter) Write(buf []byte) (int, error) {
|
||||||
log.Printf("[WARNING] Caching called with Write: not caching reply")
|
log.Printf("[WARNING] Caching called with Write: not caching reply")
|
||||||
n, err := c.ResponseWriter.Write(buf)
|
n, err := c.ResponseWriter.Write(buf)
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CachingResponseWriter) Hijack() {
|
// Hijack implements the dns.ResponseWriter interface.
|
||||||
|
func (c *ResponseWriter) Hijack() {
|
||||||
c.ResponseWriter.Hijack()
|
c.ResponseWriter.Hijack()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func minTtl(rrs []dns.RR, mt response.Type) time.Duration {
|
func minTTL(rrs []dns.RR, mt response.Type) time.Duration {
|
||||||
if mt != response.Success && mt != response.NameError && mt != response.NoData {
|
if mt != response.Success && mt != response.NameError && mt != response.NoData {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
minTtl := maxTtl
|
minTTL := maxTTL
|
||||||
for _, r := range rrs {
|
for _, r := range rrs {
|
||||||
switch mt {
|
switch mt {
|
||||||
case response.NameError, response.NoData:
|
case response.NameError, response.NoData:
|
||||||
@@ -126,17 +132,17 @@ func minTtl(rrs []dns.RR, mt response.Type) time.Duration {
|
|||||||
return time.Duration(r.(*dns.SOA).Minttl) * time.Second
|
return time.Duration(r.(*dns.SOA).Minttl) * time.Second
|
||||||
}
|
}
|
||||||
case response.Success, response.Delegation:
|
case response.Success, response.Delegation:
|
||||||
if r.Header().Ttl < minTtl {
|
if r.Header().Ttl < minTTL {
|
||||||
minTtl = r.Header().Ttl
|
minTTL = r.Header().Ttl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return time.Duration(minTtl) * time.Second
|
return time.Duration(minTTL) * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
purgeDuration = 1 * time.Minute
|
purgeDuration = 1 * time.Minute
|
||||||
defaultDuration = 20 * time.Minute
|
defaultDuration = 20 * time.Minute
|
||||||
baseTtl = 5 // minimum ttl that we will allow
|
baseTTL = 5 // minimum TTL that we will allow
|
||||||
maxTtl uint32 = 2 * 3600
|
maxTTL uint32 = 2 * 3600
|
||||||
)
|
)
|
||||||
|
|||||||
2
middleware/cache/cache_test.go
vendored
2
middleware/cache/cache_test.go
vendored
@@ -65,7 +65,7 @@ func cacheMsg(m *dns.Msg, tc cacheTestCase) *dns.Msg {
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestCache() (Cache, *CachingResponseWriter) {
|
func newTestCache() (Cache, *ResponseWriter) {
|
||||||
c := NewCache(0, []string{"."}, nil)
|
c := NewCache(0, []string{"."}, nil)
|
||||||
crr := NewCachingResponseWriter(nil, c.cache, time.Duration(0))
|
crr := NewCachingResponseWriter(nil, c.cache, time.Duration(0))
|
||||||
return c, crr
|
return c, crr
|
||||||
|
|||||||
10
middleware/cache/item.go
vendored
10
middleware/cache/item.go
vendored
@@ -15,7 +15,7 @@ type item struct {
|
|||||||
Ns []dns.RR
|
Ns []dns.RR
|
||||||
Extra []dns.RR
|
Extra []dns.RR
|
||||||
|
|
||||||
origTtl uint32
|
origTTL uint32
|
||||||
stored time.Time
|
stored time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ func newItem(m *dns.Msg, d time.Duration) *item {
|
|||||||
}
|
}
|
||||||
i.Extra = i.Extra[:j]
|
i.Extra = i.Extra[:j]
|
||||||
|
|
||||||
i.origTtl = uint32(d.Seconds())
|
i.origTTL = uint32(d.Seconds())
|
||||||
i.stored = time.Now().UTC()
|
i.stored = time.Now().UTC()
|
||||||
|
|
||||||
return i
|
return i
|
||||||
@@ -57,9 +57,9 @@ func (i *item) toMsg(m *dns.Msg) *dns.Msg {
|
|||||||
m1.Ns = i.Ns
|
m1.Ns = i.Ns
|
||||||
m1.Extra = i.Extra
|
m1.Extra = i.Extra
|
||||||
|
|
||||||
ttl := int(i.origTtl) - int(time.Now().UTC().Sub(i.stored).Seconds())
|
ttl := int(i.origTTL) - int(time.Now().UTC().Sub(i.stored).Seconds())
|
||||||
if ttl < baseTtl {
|
if ttl < baseTTL {
|
||||||
ttl = baseTtl
|
ttl = baseTTL
|
||||||
}
|
}
|
||||||
setCap(m1, uint32(ttl))
|
setCap(m1, uint32(ttl))
|
||||||
return m1
|
return m1
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ type Chaos struct {
|
|||||||
Authors map[string]bool
|
Authors map[string]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServeDNS implements the middleware.Handler interface.
|
||||||
func (c Chaos) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
func (c Chaos) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||||
state := request.Request{W: w, Req: r}
|
state := request.Request{W: w, Req: r}
|
||||||
if state.QClass() != dns.ClassCHAOS || state.QType() != dns.TypeTXT {
|
if state.QClass() != dns.ClassCHAOS || state.QType() != dns.TypeTXT {
|
||||||
@@ -32,7 +33,7 @@ func (c Chaos) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (
|
|||||||
default:
|
default:
|
||||||
return c.Next.ServeDNS(ctx, w, r)
|
return c.Next.ServeDNS(ctx, w, r)
|
||||||
case "authors.bind.":
|
case "authors.bind.":
|
||||||
for a, _ := range c.Authors {
|
for a := range c.Authors {
|
||||||
m.Answer = append(m.Answer, &dns.TXT{Hdr: hdr, Txt: []string{trim(a)}})
|
m.Answer = append(m.Answer, &dns.TXT{Hdr: hdr, Txt: []string{trim(a)}})
|
||||||
}
|
}
|
||||||
case "version.bind.", "version.server.":
|
case "version.bind.", "version.server.":
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DNSKEY holds a DNSSEC public and private key used for on-the-fly signing.
|
||||||
type DNSKEY struct {
|
type DNSKEY struct {
|
||||||
K *dns.DNSKEY
|
K *dns.DNSKEY
|
||||||
s crypto.Signer
|
s crypto.Signer
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
gcache "github.com/patrickmn/go-cache"
|
gcache "github.com/patrickmn/go-cache"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Dnssec signs the reply on-the-fly.
|
||||||
type Dnssec struct {
|
type Dnssec struct {
|
||||||
Next middleware.Handler
|
Next middleware.Handler
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ type Dnssec struct {
|
|||||||
cache *gcache.Cache
|
cache *gcache.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New returns a new Dnssec.
|
||||||
func New(zones []string, keys []*DNSKEY, next middleware.Handler) Dnssec {
|
func New(zones []string, keys []*DNSKEY, next middleware.Handler) Dnssec {
|
||||||
return Dnssec{Next: next,
|
return Dnssec{Next: next,
|
||||||
zones: zones,
|
zones: zones,
|
||||||
@@ -95,7 +97,7 @@ func (d Dnssec) sign(rrs []dns.RR, signerName string, ttl, incep, expir uint32)
|
|||||||
sigs := make([]dns.RR, len(d.keys))
|
sigs := make([]dns.RR, len(d.keys))
|
||||||
var e error
|
var e error
|
||||||
for i, k := range d.keys {
|
for i, k := range d.keys {
|
||||||
sig := k.NewRRSIG(signerName, ttl, incep, expir)
|
sig := k.newRRSIG(signerName, ttl, incep, expir)
|
||||||
e = sig.Sign(k.s, rrs)
|
e = sig.Sign(k.s, rrs)
|
||||||
sigs[i] = sig
|
sigs[i] = sig
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func (d Dnssec) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drr := NewDnssecResponseWriter(w, d)
|
drr := &ResponseWriter{w, d}
|
||||||
return d.Next.ServeDNS(ctx, drr, r)
|
return d.Next.ServeDNS(ctx, drr, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,15 +10,13 @@ import (
|
|||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ResponseWriter sign the response on the fly.
|
||||||
type ResponseWriter struct {
|
type ResponseWriter struct {
|
||||||
dns.ResponseWriter
|
dns.ResponseWriter
|
||||||
d Dnssec
|
d Dnssec
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDnssecResponseWriter(w dns.ResponseWriter, d Dnssec) *ResponseWriter {
|
// WriteMsg implements the dns.ResponseWriter interface.
|
||||||
return &ResponseWriter{w, d}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *ResponseWriter) WriteMsg(res *dns.Msg) error {
|
func (d *ResponseWriter) WriteMsg(res *dns.Msg) error {
|
||||||
// By definition we should sign anything that comes back, we should still figure out for
|
// By definition we should sign anything that comes back, we should still figure out for
|
||||||
// which zone it should be.
|
// which zone it should be.
|
||||||
@@ -38,13 +36,12 @@ func (d *ResponseWriter) WriteMsg(res *dns.Msg) error {
|
|||||||
return d.ResponseWriter.WriteMsg(res)
|
return d.ResponseWriter.WriteMsg(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write implements the dns.ResponseWriter interface.
|
||||||
func (d *ResponseWriter) Write(buf []byte) (int, error) {
|
func (d *ResponseWriter) Write(buf []byte) (int, error) {
|
||||||
log.Printf("[WARNING] Dnssec called with Write: not signing reply")
|
log.Printf("[WARNING] Dnssec called with Write: not signing reply")
|
||||||
n, err := d.ResponseWriter.Write(buf)
|
n, err := d.ResponseWriter.Write(buf)
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *ResponseWriter) Hijack() {
|
// Hijack implements the dns.ResponseWriter interface.
|
||||||
d.ResponseWriter.Hijack()
|
func (d *ResponseWriter) Hijack() { d.ResponseWriter.Hijack() }
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package dnssec
|
|||||||
import "github.com/miekg/dns"
|
import "github.com/miekg/dns"
|
||||||
|
|
||||||
// newRRSIG return a new RRSIG, with all fields filled out, except the signed data.
|
// newRRSIG return a new RRSIG, with all fields filled out, except the signed data.
|
||||||
func (k *DNSKEY) NewRRSIG(signerName string, ttl, incep, expir uint32) *dns.RRSIG {
|
func (k *DNSKEY) newRRSIG(signerName string, ttl, incep, expir uint32) *dns.RRSIG {
|
||||||
sig := new(dns.RRSIG)
|
sig := new(dns.RRSIG)
|
||||||
|
|
||||||
sig.Hdr.Rrtype = dns.TypeRRSIG
|
sig.Hdr.Rrtype = dns.TypeRRSIG
|
||||||
@@ -11,7 +11,7 @@ func (k *DNSKEY) NewRRSIG(signerName string, ttl, incep, expir uint32) *dns.RRSI
|
|||||||
sig.KeyTag = k.keytag
|
sig.KeyTag = k.keytag
|
||||||
sig.SignerName = signerName
|
sig.SignerName = signerName
|
||||||
sig.Hdr.Ttl = ttl
|
sig.Hdr.Ttl = ttl
|
||||||
sig.OrigTtl = origTtl
|
sig.OrigTtl = origTTL
|
||||||
|
|
||||||
sig.Inception = incep
|
sig.Inception = incep
|
||||||
sig.Expiration = expir
|
sig.Expiration = expir
|
||||||
@@ -50,4 +50,4 @@ func rrSets(rrs []dns.RR) map[rrset][]dns.RR {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const origTtl = 3600
|
const origTTL = 3600
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ func dnssecParse(c *caddy.Controller) ([]string, []*DNSKEY, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i, _ := range zones {
|
for i := range zones {
|
||||||
zones[i] = middleware.Host(zones[i]).Normalize()
|
zones[i] = middleware.Host(zones[i]).Normalize()
|
||||||
}
|
}
|
||||||
return zones, keys, nil
|
return zones, keys, nil
|
||||||
|
|||||||
@@ -15,15 +15,16 @@ import (
|
|||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrorHandler handles DNS errors (and errors from other middleware).
|
// errorHandler handles DNS errors (and errors from other middleware).
|
||||||
type ErrorHandler struct {
|
type errorHandler struct {
|
||||||
Next middleware.Handler
|
Next middleware.Handler
|
||||||
LogFile string
|
LogFile string
|
||||||
Log *log.Logger
|
Log *log.Logger
|
||||||
Debug bool // if true, errors are written out to client rather than to a log
|
Debug bool // if true, errors are written out to client rather than to a log
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h ErrorHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
// ServeDNS implements the middleware.Handler interface.
|
||||||
|
func (h errorHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||||
defer h.recovery(ctx, w, r)
|
defer h.recovery(ctx, w, r)
|
||||||
|
|
||||||
rcode, err := h.Next.ServeDNS(ctx, w, r)
|
rcode, err := h.Next.ServeDNS(ctx, w, r)
|
||||||
@@ -47,7 +48,7 @@ func (h ErrorHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns
|
|||||||
return rcode, err
|
return rcode, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h ErrorHandler) recovery(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) {
|
func (h errorHandler) recovery(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) {
|
||||||
rec := recover()
|
rec := recover()
|
||||||
if rec == nil {
|
if rec == nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
|
|
||||||
func TestErrors(t *testing.T) {
|
func TestErrors(t *testing.T) {
|
||||||
buf := bytes.Buffer{}
|
buf := bytes.Buffer{}
|
||||||
em := ErrorHandler{Log: log.New(&buf, "", 0)}
|
em := errorHandler{Log: log.New(&buf, "", 0)}
|
||||||
|
|
||||||
testErr := errors.New("test error")
|
testErr := errors.New("test error")
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@@ -68,7 +68,7 @@ func TestErrors(t *testing.T) {
|
|||||||
|
|
||||||
func TestVisibleErrorWithPanic(t *testing.T) {
|
func TestVisibleErrorWithPanic(t *testing.T) {
|
||||||
const panicMsg = "I'm a panic"
|
const panicMsg = "I'm a panic"
|
||||||
eh := ErrorHandler{
|
eh := errorHandler{
|
||||||
Debug: true,
|
Debug: true,
|
||||||
Next: middleware.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
Next: middleware.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||||
panic(panicMsg)
|
panic(panicMsg)
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ func setup(c *caddy.Controller) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func errorsParse(c *caddy.Controller) (ErrorHandler, error) {
|
func errorsParse(c *caddy.Controller) (errorHandler, error) {
|
||||||
handler := ErrorHandler{}
|
handler := errorHandler{}
|
||||||
|
|
||||||
optionalBlock := func() (bool, error) {
|
optionalBlock := func() (bool, error) {
|
||||||
var hadBlock bool
|
var hadBlock bool
|
||||||
|
|||||||
@@ -10,19 +10,19 @@ func TestErrorsParse(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
inputErrorsRules string
|
inputErrorsRules string
|
||||||
shouldErr bool
|
shouldErr bool
|
||||||
expectedErrorHandler ErrorHandler
|
expectedErrorHandler errorHandler
|
||||||
}{
|
}{
|
||||||
{`errors`, false, ErrorHandler{
|
{`errors`, false, errorHandler{
|
||||||
LogFile: "",
|
LogFile: "",
|
||||||
}},
|
}},
|
||||||
{`errors errors.txt`, false, ErrorHandler{
|
{`errors errors.txt`, false, errorHandler{
|
||||||
LogFile: "errors.txt",
|
LogFile: "errors.txt",
|
||||||
}},
|
}},
|
||||||
{`errors visible`, false, ErrorHandler{
|
{`errors visible`, false, errorHandler{
|
||||||
LogFile: "",
|
LogFile: "",
|
||||||
Debug: true,
|
Debug: true,
|
||||||
}},
|
}},
|
||||||
{`errors { log visible }`, false, ErrorHandler{
|
{`errors { log visible }`, false, errorHandler{
|
||||||
LogFile: "",
|
LogFile: "",
|
||||||
Debug: true,
|
Debug: true,
|
||||||
}},
|
}},
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Etcd is a middleware talks to an etcd cluster.
|
||||||
type Etcd struct {
|
type Etcd struct {
|
||||||
Next middleware.Handler
|
Next middleware.Handler
|
||||||
Zones []string
|
Zones []string
|
||||||
@@ -113,7 +114,7 @@ Nodes:
|
|||||||
bx[b] = true
|
bx[b] = true
|
||||||
|
|
||||||
serv.Key = n.Key
|
serv.Key = n.Key
|
||||||
serv.Ttl = e.TTL(n, serv)
|
serv.TTL = e.TTL(n, serv)
|
||||||
if serv.Priority == 0 {
|
if serv.Priority == 0 {
|
||||||
serv.Priority = priority
|
serv.Priority = priority
|
||||||
}
|
}
|
||||||
@@ -127,19 +128,19 @@ Nodes:
|
|||||||
func (e *Etcd) TTL(node *etcdc.Node, serv *msg.Service) uint32 {
|
func (e *Etcd) TTL(node *etcdc.Node, serv *msg.Service) uint32 {
|
||||||
etcdTTL := uint32(node.TTL)
|
etcdTTL := uint32(node.TTL)
|
||||||
|
|
||||||
if etcdTTL == 0 && serv.Ttl == 0 {
|
if etcdTTL == 0 && serv.TTL == 0 {
|
||||||
return ttl
|
return ttl
|
||||||
}
|
}
|
||||||
if etcdTTL == 0 {
|
if etcdTTL == 0 {
|
||||||
return serv.Ttl
|
return serv.TTL
|
||||||
}
|
}
|
||||||
if serv.Ttl == 0 {
|
if serv.TTL == 0 {
|
||||||
return etcdTTL
|
return etcdTTL
|
||||||
}
|
}
|
||||||
if etcdTTL < serv.Ttl {
|
if etcdTTL < serv.TTL {
|
||||||
return etcdTTL
|
return etcdTTL
|
||||||
}
|
}
|
||||||
return serv.Ttl
|
return serv.TTL
|
||||||
}
|
}
|
||||||
|
|
||||||
// etcNameError checks if the error is ErrorCodeKeyNotFound from etcd.
|
// etcNameError checks if the error is ErrorCodeKeyNotFound from etcd.
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ import (
|
|||||||
|
|
||||||
"github.com/miekg/coredns/middleware"
|
"github.com/miekg/coredns/middleware"
|
||||||
"github.com/miekg/coredns/middleware/etcd/msg"
|
"github.com/miekg/coredns/middleware/etcd/msg"
|
||||||
|
"github.com/miekg/coredns/middleware/pkg/dnsutil"
|
||||||
"github.com/miekg/coredns/request"
|
"github.com/miekg/coredns/request"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ServeDNS implements the middleware.Handler interface.
|
||||||
func (e *Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
func (e *Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||||
opt := Options{}
|
opt := Options{}
|
||||||
state := request.Request{W: w, Req: r}
|
state := request.Request{W: w, Req: r}
|
||||||
@@ -108,7 +110,7 @@ func (e *Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (
|
|||||||
m.Extra = append(m.Extra, servicesToTxt(debug)...)
|
m.Extra = append(m.Extra, servicesToTxt(debug)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
m = dedup(m)
|
m = dnsutil.Dedup(m)
|
||||||
state.SizeAndDo(m)
|
state.SizeAndDo(m)
|
||||||
m, _ = state.Scrub(m)
|
m, _ = state.Scrub(m)
|
||||||
w.WriteMsg(m)
|
w.WriteMsg(m)
|
||||||
@@ -133,11 +135,3 @@ func (e *Etcd) Err(zone string, rcode int, state request.Request, debug []msg.Se
|
|||||||
// Return success as the rcode to signal we have written to the client.
|
// Return success as the rcode to signal we have written to the client.
|
||||||
return dns.RcodeSuccess, nil
|
return dns.RcodeSuccess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func dedup(m *dns.Msg) *dns.Msg {
|
|
||||||
// TODO(miek): expensive!
|
|
||||||
m.Answer = dns.Dedup(m.Answer, nil)
|
|
||||||
m.Ns = dns.Dedup(m.Ns, nil)
|
|
||||||
m.Extra = dns.Dedup(m.Extra, nil)
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -14,8 +14,9 @@ import (
|
|||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Options are extra options that can be specified for a lookup.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Debug string
|
Debug string // This is a debug query. A query prefixed with debug.o-o
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e Etcd) records(state request.Request, exact bool, opt Options) (services, debug []msg.Service, err error) {
|
func (e Etcd) records(state request.Request, exact bool, opt Options) (services, debug []msg.Service, err error) {
|
||||||
@@ -30,6 +31,7 @@ func (e Etcd) records(state request.Request, exact bool, opt Options) (services,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A returns A records from etcd or an error.
|
||||||
func (e Etcd) A(zone string, state request.Request, previousRecords []dns.RR, opt Options) (records []dns.RR, debug []msg.Service, err error) {
|
func (e Etcd) A(zone string, state request.Request, previousRecords []dns.RR, opt Options) (records []dns.RR, debug []msg.Service, err error) {
|
||||||
services, debug, err := e.records(state, false, opt)
|
services, debug, err := e.records(state, false, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -92,6 +94,7 @@ func (e Etcd) A(zone string, state request.Request, previousRecords []dns.RR, op
|
|||||||
return records, debug, nil
|
return records, debug, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AAAA returns AAAA records from etcd or an error.
|
||||||
func (e Etcd) AAAA(zone string, state request.Request, previousRecords []dns.RR, opt Options) (records []dns.RR, debug []msg.Service, err error) {
|
func (e Etcd) AAAA(zone string, state request.Request, previousRecords []dns.RR, opt Options) (records []dns.RR, debug []msg.Service, err error) {
|
||||||
services, debug, err := e.records(state, false, opt)
|
services, debug, err := e.records(state, false, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -313,6 +316,7 @@ func (e Etcd) MX(zone string, state request.Request, opt Options) (records, extr
|
|||||||
return records, extra, debug, nil
|
return records, extra, debug, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CNAME returns CNAME records from etcd or an error.
|
||||||
func (e Etcd) CNAME(zone string, state request.Request, opt Options) (records []dns.RR, debug []msg.Service, err error) {
|
func (e Etcd) CNAME(zone string, state request.Request, opt Options) (records []dns.RR, debug []msg.Service, err error) {
|
||||||
services, debug, err := e.records(state, true, opt)
|
services, debug, err := e.records(state, true, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -343,6 +347,7 @@ func (e Etcd) PTR(zone string, state request.Request, opt Options) (records []dn
|
|||||||
return records, debug, nil
|
return records, debug, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TXT returns TXT records from etcd or an error.
|
||||||
func (e Etcd) TXT(zone string, state request.Request, opt Options) (records []dns.RR, debug []msg.Service, err error) {
|
func (e Etcd) TXT(zone string, state request.Request, opt Options) (records []dns.RR, debug []msg.Service, err error) {
|
||||||
services, debug, err := e.records(state, false, opt)
|
services, debug, err := e.records(state, false, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -358,6 +363,7 @@ func (e Etcd) TXT(zone string, state request.Request, opt Options) (records []dn
|
|||||||
return records, debug, nil
|
return records, debug, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NS returns NS records from etcd or an error.
|
||||||
func (e Etcd) NS(zone string, state request.Request, opt Options) (records, extra []dns.RR, debug []msg.Service, err error) {
|
func (e Etcd) NS(zone string, state request.Request, opt Options) (records, extra []dns.RR, debug []msg.Service, err error) {
|
||||||
// NS record for this zone live in a special place, ns.dns.<zone>. Fake our lookup.
|
// NS record for this zone live in a special place, ns.dns.<zone>. Fake our lookup.
|
||||||
// only a tad bit fishy...
|
// only a tad bit fishy...
|
||||||
@@ -390,7 +396,7 @@ func (e Etcd) NS(zone string, state request.Request, opt Options) (records, extr
|
|||||||
return records, extra, debug, nil
|
return records, extra, debug, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SOA Record returns a SOA record.
|
// SOA returns a SOA record from etcd.
|
||||||
func (e Etcd) SOA(zone string, state request.Request, opt Options) ([]dns.RR, []msg.Service, error) {
|
func (e Etcd) SOA(zone string, state request.Request, opt Options) ([]dns.RR, []msg.Service, error) {
|
||||||
header := dns.RR_Header{Name: zone, Rrtype: dns.TypeSOA, Ttl: 300, Class: dns.ClassINET}
|
header := dns.RR_Header{Name: zone, Rrtype: dns.TypeSOA, Ttl: 300, Class: dns.ClassINET}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func Domain(s string) string {
|
|||||||
return dns.Fqdn(strings.Join(l[1:len(l)-1], "."))
|
return dns.Fqdn(strings.Join(l[1:len(l)-1], "."))
|
||||||
}
|
}
|
||||||
|
|
||||||
// As Path, but if a name contains wildcards (* or any), the name will be
|
// PathWithWildcard ascts as Path, but if a name contains wildcards (* or any), the name will be
|
||||||
// chopped of before the (first) wildcard, and we do a highler evel search and
|
// chopped of before the (first) wildcard, and we do a highler evel search and
|
||||||
// later find the matching names. So service.*.skydns.local, will look for all
|
// later find the matching names. So service.*.skydns.local, will look for all
|
||||||
// services under skydns.local and will later check for names that match
|
// services under skydns.local and will later check for names that match
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import (
|
|||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This *is* the rdata from a SRV record, but with a twist.
|
// Service defines a discoverable service in etcd. It is the rdata from a SRV
|
||||||
// Host (Target in SRV) must be a domain name, but if it looks like an IP
|
// record, but with a twist. Host (Target in SRV) must be a domain name, but
|
||||||
// address (4/6), we will treat it like an IP address.
|
// if it looks like an IP address (4/6), we will treat it like an IP address.
|
||||||
type Service struct {
|
type Service struct {
|
||||||
Host string `json:"host,omitempty"`
|
Host string `json:"host,omitempty"`
|
||||||
Port int `json:"port,omitempty"`
|
Port int `json:"port,omitempty"`
|
||||||
@@ -18,7 +18,7 @@ type Service struct {
|
|||||||
Weight int `json:"weight,omitempty"`
|
Weight int `json:"weight,omitempty"`
|
||||||
Text string `json:"text,omitempty"`
|
Text string `json:"text,omitempty"`
|
||||||
Mail bool `json:"mail,omitempty"` // Be an MX record. Priority becomes Preference.
|
Mail bool `json:"mail,omitempty"` // Be an MX record. Priority becomes Preference.
|
||||||
Ttl uint32 `json:"ttl,omitempty"`
|
TTL uint32 `json:"ttl,omitempty"`
|
||||||
|
|
||||||
// When a SRV record with a "Host: IP-address" is added, we synthesize
|
// When a SRV record with a "Host: IP-address" is added, we synthesize
|
||||||
// a srv.Target domain name. Normally we convert the full Key where
|
// a srv.Target domain name. Normally we convert the full Key where
|
||||||
@@ -54,7 +54,7 @@ func (s *Service) RR() *dns.TXT {
|
|||||||
}
|
}
|
||||||
t := new(dns.TXT)
|
t := new(dns.TXT)
|
||||||
t.Hdr.Class = dns.ClassCHAOS
|
t.Hdr.Class = dns.ClassCHAOS
|
||||||
t.Hdr.Ttl = s.Ttl
|
t.Hdr.Ttl = s.TTL
|
||||||
t.Hdr.Rrtype = dns.TypeTXT
|
t.Hdr.Rrtype = dns.TypeTXT
|
||||||
t.Hdr.Name = Domain(s.Key)
|
t.Hdr.Name = Domain(s.Key)
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ func (s *Service) RR() *dns.TXT {
|
|||||||
func (s *Service) NewSRV(name string, weight uint16) *dns.SRV {
|
func (s *Service) NewSRV(name string, weight uint16) *dns.SRV {
|
||||||
host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip)
|
host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip)
|
||||||
|
|
||||||
return &dns.SRV{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeSRV, Class: dns.ClassINET, Ttl: s.Ttl},
|
return &dns.SRV{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeSRV, Class: dns.ClassINET, Ttl: s.TTL},
|
||||||
Priority: uint16(s.Priority), Weight: weight, Port: uint16(s.Port), Target: dns.Fqdn(host)}
|
Priority: uint16(s.Priority), Weight: weight, Port: uint16(s.Port), Target: dns.Fqdn(host)}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,39 +78,39 @@ func (s *Service) NewSRV(name string, weight uint16) *dns.SRV {
|
|||||||
func (s *Service) NewMX(name string) *dns.MX {
|
func (s *Service) NewMX(name string) *dns.MX {
|
||||||
host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip)
|
host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip)
|
||||||
|
|
||||||
return &dns.MX{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: s.Ttl},
|
return &dns.MX{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: s.TTL},
|
||||||
Preference: uint16(s.Priority), Mx: host}
|
Preference: uint16(s.Priority), Mx: host}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewA returns a new A record based on the Service.
|
// NewA returns a new A record based on the Service.
|
||||||
func (s *Service) NewA(name string, ip net.IP) *dns.A {
|
func (s *Service) NewA(name string, ip net.IP) *dns.A {
|
||||||
return &dns.A{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: s.Ttl}, A: ip}
|
return &dns.A{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: s.TTL}, A: ip}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAAAA returns a new AAAA record based on the Service.
|
// NewAAAA returns a new AAAA record based on the Service.
|
||||||
func (s *Service) NewAAAA(name string, ip net.IP) *dns.AAAA {
|
func (s *Service) NewAAAA(name string, ip net.IP) *dns.AAAA {
|
||||||
return &dns.AAAA{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: s.Ttl}, AAAA: ip}
|
return &dns.AAAA{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: s.TTL}, AAAA: ip}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCNAME returns a new CNAME record based on the Service.
|
// NewCNAME returns a new CNAME record based on the Service.
|
||||||
func (s *Service) NewCNAME(name string, target string) *dns.CNAME {
|
func (s *Service) NewCNAME(name string, target string) *dns.CNAME {
|
||||||
return &dns.CNAME{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: s.Ttl}, Target: dns.Fqdn(target)}
|
return &dns.CNAME{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: s.TTL}, Target: dns.Fqdn(target)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTXT returns a new TXT record based on the Service.
|
// NewTXT returns a new TXT record based on the Service.
|
||||||
func (s *Service) NewTXT(name string) *dns.TXT {
|
func (s *Service) NewTXT(name string) *dns.TXT {
|
||||||
return &dns.TXT{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: s.Ttl}, Txt: split255(s.Text)}
|
return &dns.TXT{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: s.TTL}, Txt: split255(s.Text)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPTR returns a new PTR record based on the Service.
|
// NewPTR returns a new PTR record based on the Service.
|
||||||
func (s *Service) NewPTR(name string, target string) *dns.PTR {
|
func (s *Service) NewPTR(name string, target string) *dns.PTR {
|
||||||
return &dns.PTR{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: s.Ttl}, Ptr: dns.Fqdn(target)}
|
return &dns.PTR{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: s.TTL}, Ptr: dns.Fqdn(target)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNS returns a new NS record based on the Service.
|
// NewNS returns a new NS record based on the Service.
|
||||||
func (s *Service) NewNS(name string) *dns.NS {
|
func (s *Service) NewNS(name string) *dns.NS {
|
||||||
host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip)
|
host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip)
|
||||||
return &dns.NS{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: s.Ttl}, Ns: host}
|
return &dns.NS{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: s.TTL}, Ns: host}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group checks the services in sx, it looks for a Group attribute on the shortest
|
// Group checks the services in sx, it looks for a Group attribute on the shortest
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ func newEtcdClient(endpoints []string, tlsCert, tlsKey, tlsCACert string) (etcdc
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newHTTPSTransport(tlsCertFile, tlsKeyFile, tlsCACertFile string) etcdc.CancelableTransport {
|
func newHTTPSTransport(tlsCertFile, tlsKeyFile, tlsCACertFile string) etcdc.CancelableTransport {
|
||||||
var cc *tls.Config = nil
|
var cc *tls.Config
|
||||||
|
|
||||||
if tlsCertFile != "" && tlsKeyFile != "" {
|
if tlsCertFile != "" && tlsKeyFile != "" {
|
||||||
var rpool *x509.CertPool
|
var rpool *x509.CertPool
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// UpdateStubZones checks etcd for an update on the stubzones.
|
||||||
func (e *Etcd) UpdateStubZones() {
|
func (e *Etcd) UpdateStubZones() {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ type Stub struct {
|
|||||||
Zone string // for what zone (and thus what nameservers are we called)
|
Zone string // for what zone (and thus what nameservers are we called)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServeDNS implements the middleware.Handler interface.
|
||||||
func (s Stub) ServeDNS(ctx context.Context, w dns.ResponseWriter, req *dns.Msg) (int, error) {
|
func (s Stub) ServeDNS(ctx context.Context, w dns.ResponseWriter, req *dns.Msg) (int, error) {
|
||||||
if hasStubEdns0(req) {
|
if hasStubEdns0(req) {
|
||||||
log.Printf("[WARNING] Forwarding cycle detected, refusing msg: %s", req.Question[0].Name)
|
log.Printf("[WARNING] Forwarding cycle detected, refusing msg: %s", req.Question[0].Name)
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ var delegationTestCases = []test.Case{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLookupDelegation(t *testing.T) {
|
func TestLookupDelegation(t *testing.T) {
|
||||||
zone, err := Parse(strings.NewReader(dbMiekNL_delegation), testzone, "stdin")
|
zone, err := Parse(strings.NewReader(dbMiekNLDelegation), testzone, "stdin")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expect no error when reading zone, got %q", err)
|
t.Fatalf("expect no error when reading zone, got %q", err)
|
||||||
}
|
}
|
||||||
@@ -85,7 +85,7 @@ func TestLookupDelegation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dbMiekNL_delegation = `
|
const dbMiekNLDelegation = `
|
||||||
$TTL 30M
|
$TTL 30M
|
||||||
$ORIGIN miek.nl.
|
$ORIGIN miek.nl.
|
||||||
@ IN SOA linode.atoom.net. miek.miek.nl. (
|
@ IN SOA linode.atoom.net. miek.miek.nl. (
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ var dnssecTestCases = []test.Case{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLookupDNSSEC(t *testing.T) {
|
func TestLookupDNSSEC(t *testing.T) {
|
||||||
zone, err := Parse(strings.NewReader(dbMiekNL_signed), testzone, "stdin")
|
zone, err := Parse(strings.NewReader(dbMiekNLSigned), testzone, "stdin")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expect no error when reading zone, got %q", err)
|
t.Fatalf("expect no error when reading zone, got %q", err)
|
||||||
}
|
}
|
||||||
@@ -147,7 +147,7 @@ func TestLookupDNSSEC(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkLookupDNSSEC(b *testing.B) {
|
func BenchmarkLookupDNSSEC(b *testing.B) {
|
||||||
zone, err := Parse(strings.NewReader(dbMiekNL_signed), testzone, "stdin")
|
zone, err := Parse(strings.NewReader(dbMiekNLSigned), testzone, "stdin")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -178,7 +178,7 @@ func BenchmarkLookupDNSSEC(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dbMiekNL_signed = `
|
const dbMiekNLSigned = `
|
||||||
; File written on Sun Mar 27 04:13:01 2016
|
; File written on Sun Mar 27 04:13:01 2016
|
||||||
; dnssec_signzone version 9.10.3-P4-Ubuntu
|
; dnssec_signzone version 9.10.3-P4-Ubuntu
|
||||||
miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. (
|
miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. (
|
||||||
|
|||||||
@@ -13,17 +13,20 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
// File is the middleware that reads zone data from disk.
|
||||||
File struct {
|
File struct {
|
||||||
Next middleware.Handler
|
Next middleware.Handler
|
||||||
Zones Zones
|
Zones Zones
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Zones maps zone names to a *Zone.
|
||||||
Zones struct {
|
Zones struct {
|
||||||
Z map[string]*Zone
|
Z map[string]*Zone
|
||||||
Names []string
|
Names []string
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ServeDNS implements the middleware.Handle interface.
|
||||||
func (f File) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
func (f File) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||||
state := request.Request{W: w, Req: r}
|
state := request.Request{W: w, Req: r}
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,15 @@ import (
|
|||||||
type Result int
|
type Result int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// Success is a successful lookup.
|
||||||
Success Result = iota
|
Success Result = iota
|
||||||
|
// NameError indicates a nameerror
|
||||||
NameError
|
NameError
|
||||||
|
// Delegation indicates the lookup resulted in a delegation.
|
||||||
Delegation
|
Delegation
|
||||||
|
// NoData indicates the lookup resulted in a NODATA.
|
||||||
NoData
|
NoData
|
||||||
|
// ServerFailure indicates a server failure during the lookup.
|
||||||
ServerFailure
|
ServerFailure
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestParseNSEC3PARAM(t *testing.T) {
|
func TestParseNSEC3PARAM(t *testing.T) {
|
||||||
_, err := Parse(strings.NewReader(nsec3param_test), "miek.nl", "stdin")
|
_, err := Parse(strings.NewReader(nsec3paramTest), "miek.nl", "stdin")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected error when reading zone, got nothing")
|
t.Fatalf("expected error when reading zone, got nothing")
|
||||||
}
|
}
|
||||||
@@ -14,17 +14,17 @@ func TestParseNSEC3PARAM(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseNSEC3(t *testing.T) {
|
func TestParseNSEC3(t *testing.T) {
|
||||||
_, err := Parse(strings.NewReader(nsec3_test), "miek.nl", "stdin")
|
_, err := Parse(strings.NewReader(nsec3Test), "miek.nl", "stdin")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected error when reading zone, got nothing")
|
t.Fatalf("expected error when reading zone, got nothing")
|
||||||
}
|
}
|
||||||
t.Logf("%v\n", err)
|
t.Logf("%v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
const nsec3param_test = `miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1460175181 14400 3600 604800 14400
|
const nsec3paramTest = `miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1460175181 14400 3600 604800 14400
|
||||||
miek.nl. 1800 IN NS omval.tednet.nl.
|
miek.nl. 1800 IN NS omval.tednet.nl.
|
||||||
miek.nl. 0 IN NSEC3PARAM 1 0 5 A3DEBC9CC4F695C7`
|
miek.nl. 0 IN NSEC3PARAM 1 0 5 A3DEBC9CC4F695C7`
|
||||||
|
|
||||||
const nsec3_test = `example.org. 1800 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2016082508 7200 3600 1209600 3600
|
const nsec3Test = `example.org. 1800 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2016082508 7200 3600 1209600 3600
|
||||||
aub8v9ce95ie18spjubsr058h41n7pa5.example.org. 284 IN NSEC3 1 1 5 D0CBEAAF0AC77314 AUB95P93VPKP55G6U5S4SGS7LS61ND85 NS SOA TXT RRSIG DNSKEY NSEC3PARAM
|
aub8v9ce95ie18spjubsr058h41n7pa5.example.org. 284 IN NSEC3 1 1 5 D0CBEAAF0AC77314 AUB95P93VPKP55G6U5S4SGS7LS61ND85 NS SOA TXT RRSIG DNSKEY NSEC3PARAM
|
||||||
aub8v9ce95ie18spjubsr058h41n7pa5.example.org. 284 IN RRSIG NSEC3 8 2 600 20160910232502 20160827231002 14028 example.org. XBNpA7KAIjorPbXvTinOHrc1f630aHic2U716GHLHA4QMx9cl9ss4QjR Wj2UpDM9zBW/jNYb1xb0yjQoez/Jv200w0taSWjRci5aUnRpOi9bmcrz STHb6wIUjUsbJ+NstQsUwVkj6679UviF1FqNwr4GlJnWG3ZrhYhE+NI6 s0k=`
|
aub8v9ce95ie18spjubsr058h41n7pa5.example.org. 284 IN RRSIG NSEC3 8 2 600 20160910232502 20160827231002 14028 example.org. XBNpA7KAIjorPbXvTinOHrc1f630aHic2U716GHLHA4QMx9cl9ss4QjR Wj2UpDM9zBW/jNYb1xb0yjQoez/Jv200w0taSWjRci5aUnRpOi9bmcrz STHb6wIUjUsbJ+NstQsUwVkj6679UviF1FqNwr4GlJnWG3ZrhYhE+NI6 s0k=`
|
||||||
|
|||||||
@@ -178,7 +178,6 @@ Restart:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The maximum difference between two serial numbers. If the difference between
|
// MaxSerialIncrement is the maximum difference between two serial numbers. If the difference between
|
||||||
// two serials is greater than this number, the smaller one is considered
|
// two serials is greater than this number, the smaller one is considered greater.
|
||||||
// greater.
|
|
||||||
const MaxSerialIncrement uint32 = 2147483647
|
const MaxSerialIncrement uint32 = 2147483647
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ func fileParse(c *caddy.Controller) (Zones, error) {
|
|||||||
return Zones{}, err
|
return Zones{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, _ := range origins {
|
for i := range origins {
|
||||||
origins[i] = middleware.Host(origins[i]).Normalize()
|
origins[i] = middleware.Host(origins[i]).Normalize()
|
||||||
zone, err := Parse(reader, origins[i], fileName)
|
zone, err := Parse(reader, origins[i], fileName)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -116,7 +116,7 @@ func TransferParse(c *caddy.Controller) (tos, froms []string, err error) {
|
|||||||
case "transfer":
|
case "transfer":
|
||||||
if value == "to" {
|
if value == "to" {
|
||||||
tos = c.RemainingArgs()
|
tos = c.RemainingArgs()
|
||||||
for i, _ := range tos {
|
for i := range tos {
|
||||||
if tos[i] != "*" {
|
if tos[i] != "*" {
|
||||||
if x := net.ParseIP(tos[i]); x == nil {
|
if x := net.ParseIP(tos[i]); x == nil {
|
||||||
return nil, nil, fmt.Errorf("must specify an IP addres: `%s'", tos[i])
|
return nil, nil, fmt.Errorf("must specify an IP addres: `%s'", tos[i])
|
||||||
@@ -127,7 +127,7 @@ func TransferParse(c *caddy.Controller) (tos, froms []string, err error) {
|
|||||||
}
|
}
|
||||||
if value == "from" {
|
if value == "from" {
|
||||||
froms = c.RemainingArgs()
|
froms = c.RemainingArgs()
|
||||||
for i, _ := range froms {
|
for i := range froms {
|
||||||
if froms[i] != "*" {
|
if froms[i] != "*" {
|
||||||
if x := net.ParseIP(froms[i]); x == nil {
|
if x := net.ParseIP(froms[i]); x == nil {
|
||||||
return nil, nil, fmt.Errorf("must specify an IP addres: `%s'", froms[i])
|
return nil, nil, fmt.Errorf("must specify an IP addres: `%s'", froms[i])
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package tree
|
|||||||
|
|
||||||
import "github.com/miekg/dns"
|
import "github.com/miekg/dns"
|
||||||
|
|
||||||
|
// Elem is an element in the tree.
|
||||||
type Elem struct {
|
type Elem struct {
|
||||||
m map[uint16][]dns.RR
|
m map[uint16][]dns.RR
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,13 +19,14 @@ package tree
|
|||||||
import "github.com/miekg/dns"
|
import "github.com/miekg/dns"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TD234 = iota
|
td234 = iota
|
||||||
BU23
|
bu23
|
||||||
)
|
)
|
||||||
|
|
||||||
// Result is a result of a Search.
|
// Result is a result of a Search.
|
||||||
type Result int
|
type Result int
|
||||||
|
|
||||||
|
// Various constants that indicated the type a resource returned.
|
||||||
const (
|
const (
|
||||||
Found Result = iota
|
Found Result = iota
|
||||||
NameError
|
NameError
|
||||||
@@ -34,10 +35,10 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Operation mode of the LLRB tree.
|
// Operation mode of the LLRB tree.
|
||||||
const Mode = BU23
|
const mode = bu23
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
if Mode != TD234 && Mode != BU23 {
|
if mode != td234 && mode != bu23 {
|
||||||
panic("tree: unknown mode")
|
panic("tree: unknown mode")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,8 +49,8 @@ type Color bool
|
|||||||
const (
|
const (
|
||||||
// Red as false give us the defined behaviour that new nodes are red. Although this
|
// Red as false give us the defined behaviour that new nodes are red. Although this
|
||||||
// is incorrect for the root node, that is resolved on the first insertion.
|
// is incorrect for the root node, that is resolved on the first insertion.
|
||||||
Red Color = false
|
red Color = false
|
||||||
Black Color = true
|
black Color = true
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Node represents a node in the LLRB tree.
|
// A Node represents a node in the LLRB tree.
|
||||||
@@ -70,7 +71,7 @@ type Tree struct {
|
|||||||
// color returns the effect color of a Node. A nil node returns black.
|
// color returns the effect color of a Node. A nil node returns black.
|
||||||
func (n *Node) color() Color {
|
func (n *Node) color() Color {
|
||||||
if n == nil {
|
if n == nil {
|
||||||
return Black
|
return black
|
||||||
}
|
}
|
||||||
return n.Color
|
return n.Color
|
||||||
}
|
}
|
||||||
@@ -82,7 +83,7 @@ func (n *Node) rotateLeft() (root *Node) {
|
|||||||
n.Right = root.Left
|
n.Right = root.Left
|
||||||
root.Left = n
|
root.Left = n
|
||||||
root.Color = n.Color
|
root.Color = n.Color
|
||||||
n.Color = Red
|
n.Color = red
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +94,7 @@ func (n *Node) rotateRight() (root *Node) {
|
|||||||
n.Left = root.Right
|
n.Left = root.Right
|
||||||
root.Right = n
|
root.Right = n
|
||||||
root.Color = n.Color
|
root.Color = n.Color
|
||||||
n.Color = Red
|
n.Color = red
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,16 +109,16 @@ func (n *Node) flipColors() {
|
|||||||
// fixUp ensures that black link balance is correct, that red nodes lean left,
|
// fixUp ensures that black link balance is correct, that red nodes lean left,
|
||||||
// and that 4 nodes are split in the case of BU23 and properly balanced in TD234.
|
// and that 4 nodes are split in the case of BU23 and properly balanced in TD234.
|
||||||
func (n *Node) fixUp() *Node {
|
func (n *Node) fixUp() *Node {
|
||||||
if n.Right.color() == Red {
|
if n.Right.color() == red {
|
||||||
if Mode == TD234 && n.Right.Left.color() == Red {
|
if mode == td234 && n.Right.Left.color() == red {
|
||||||
n.Right = n.Right.rotateRight()
|
n.Right = n.Right.rotateRight()
|
||||||
}
|
}
|
||||||
n = n.rotateLeft()
|
n = n.rotateLeft()
|
||||||
}
|
}
|
||||||
if n.Left.color() == Red && n.Left.Left.color() == Red {
|
if n.Left.color() == red && n.Left.Left.color() == red {
|
||||||
n = n.rotateRight()
|
n = n.rotateRight()
|
||||||
}
|
}
|
||||||
if Mode == BU23 && n.Left.color() == Red && n.Right.color() == Red {
|
if mode == bu23 && n.Left.color() == red && n.Right.color() == red {
|
||||||
n.flipColors()
|
n.flipColors()
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
@@ -125,11 +126,11 @@ func (n *Node) fixUp() *Node {
|
|||||||
|
|
||||||
func (n *Node) moveRedLeft() *Node {
|
func (n *Node) moveRedLeft() *Node {
|
||||||
n.flipColors()
|
n.flipColors()
|
||||||
if n.Right.Left.color() == Red {
|
if n.Right.Left.color() == red {
|
||||||
n.Right = n.Right.rotateRight()
|
n.Right = n.Right.rotateRight()
|
||||||
n = n.rotateLeft()
|
n = n.rotateLeft()
|
||||||
n.flipColors()
|
n.flipColors()
|
||||||
if Mode == TD234 && n.Right.Right.color() == Red {
|
if mode == td234 && n.Right.Right.color() == red {
|
||||||
n.Right = n.Right.rotateLeft()
|
n.Right = n.Right.rotateLeft()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,7 +139,7 @@ func (n *Node) moveRedLeft() *Node {
|
|||||||
|
|
||||||
func (n *Node) moveRedRight() *Node {
|
func (n *Node) moveRedRight() *Node {
|
||||||
n.flipColors()
|
n.flipColors()
|
||||||
if n.Left.Left.color() == Red {
|
if n.Left.Left.color() == red {
|
||||||
n = n.rotateRight()
|
n = n.rotateRight()
|
||||||
n.flipColors()
|
n.flipColors()
|
||||||
}
|
}
|
||||||
@@ -212,7 +213,7 @@ func (t *Tree) Insert(rr dns.RR) {
|
|||||||
var d int
|
var d int
|
||||||
t.Root, d = t.Root.insert(rr)
|
t.Root, d = t.Root.insert(rr)
|
||||||
t.Count += d
|
t.Count += d
|
||||||
t.Root.Color = Black
|
t.Root.Color = black
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) insert(rr dns.RR) (root *Node, d int) {
|
func (n *Node) insert(rr dns.RR) (root *Node, d int) {
|
||||||
@@ -223,8 +224,8 @@ func (n *Node) insert(rr dns.RR) (root *Node, d int) {
|
|||||||
return n, 1
|
return n, 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if Mode == TD234 {
|
if mode == td234 {
|
||||||
if n.Left.color() == Red && n.Right.color() == Red {
|
if n.Left.color() == red && n.Right.color() == red {
|
||||||
n.flipColors()
|
n.flipColors()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,15 +239,15 @@ func (n *Node) insert(rr dns.RR) (root *Node, d int) {
|
|||||||
n.Right, d = n.Right.insert(rr)
|
n.Right, d = n.Right.insert(rr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.Right.color() == Red && n.Left.color() == Black {
|
if n.Right.color() == red && n.Left.color() == black {
|
||||||
n = n.rotateLeft()
|
n = n.rotateLeft()
|
||||||
}
|
}
|
||||||
if n.Left.color() == Red && n.Left.Left.color() == Red {
|
if n.Left.color() == red && n.Left.Left.color() == red {
|
||||||
n = n.rotateRight()
|
n = n.rotateRight()
|
||||||
}
|
}
|
||||||
|
|
||||||
if Mode == BU23 {
|
if mode == bu23 {
|
||||||
if n.Left.color() == Red && n.Right.color() == Red {
|
if n.Left.color() == red && n.Right.color() == red {
|
||||||
n.flipColors()
|
n.flipColors()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -267,14 +268,14 @@ func (t *Tree) DeleteMin() {
|
|||||||
if t.Root == nil {
|
if t.Root == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.Root.Color = Black
|
t.Root.Color = black
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) deleteMin() (root *Node, d int) {
|
func (n *Node) deleteMin() (root *Node, d int) {
|
||||||
if n.Left == nil {
|
if n.Left == nil {
|
||||||
return nil, -1
|
return nil, -1
|
||||||
}
|
}
|
||||||
if n.Left.color() == Black && n.Left.Left.color() == Black {
|
if n.Left.color() == black && n.Left.Left.color() == black {
|
||||||
n = n.moveRedLeft()
|
n = n.moveRedLeft()
|
||||||
}
|
}
|
||||||
n.Left, d = n.Left.deleteMin()
|
n.Left, d = n.Left.deleteMin()
|
||||||
@@ -295,17 +296,17 @@ func (t *Tree) DeleteMax() {
|
|||||||
if t.Root == nil {
|
if t.Root == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.Root.Color = Black
|
t.Root.Color = black
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) deleteMax() (root *Node, d int) {
|
func (n *Node) deleteMax() (root *Node, d int) {
|
||||||
if n.Left != nil && n.Left.color() == Red {
|
if n.Left != nil && n.Left.color() == red {
|
||||||
n = n.rotateRight()
|
n = n.rotateRight()
|
||||||
}
|
}
|
||||||
if n.Right == nil {
|
if n.Right == nil {
|
||||||
return nil, -1
|
return nil, -1
|
||||||
}
|
}
|
||||||
if n.Right.color() == Black && n.Right.Left.color() == Black {
|
if n.Right.color() == black && n.Right.Left.color() == black {
|
||||||
n = n.moveRedRight()
|
n = n.moveRedRight()
|
||||||
}
|
}
|
||||||
n.Right, d = n.Right.deleteMax()
|
n.Right, d = n.Right.deleteMax()
|
||||||
@@ -345,26 +346,26 @@ func (t *Tree) DeleteNode(rr dns.RR) {
|
|||||||
if t.Root == nil {
|
if t.Root == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.Root.Color = Black
|
t.Root.Color = black
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) delete(rr dns.RR) (root *Node, d int) {
|
func (n *Node) delete(rr dns.RR) (root *Node, d int) {
|
||||||
if Less(n.Elem, rr.Header().Name) < 0 {
|
if Less(n.Elem, rr.Header().Name) < 0 {
|
||||||
if n.Left != nil {
|
if n.Left != nil {
|
||||||
if n.Left.color() == Black && n.Left.Left.color() == Black {
|
if n.Left.color() == black && n.Left.Left.color() == black {
|
||||||
n = n.moveRedLeft()
|
n = n.moveRedLeft()
|
||||||
}
|
}
|
||||||
n.Left, d = n.Left.delete(rr)
|
n.Left, d = n.Left.delete(rr)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if n.Left.color() == Red {
|
if n.Left.color() == red {
|
||||||
n = n.rotateRight()
|
n = n.rotateRight()
|
||||||
}
|
}
|
||||||
if n.Right == nil && Less(n.Elem, rr.Header().Name) == 0 {
|
if n.Right == nil && Less(n.Elem, rr.Header().Name) == 0 {
|
||||||
return nil, -1
|
return nil, -1
|
||||||
}
|
}
|
||||||
if n.Right != nil {
|
if n.Right != nil {
|
||||||
if n.Right.color() == Black && n.Right.Left.color() == Black {
|
if n.Right.color() == black && n.Right.Left.color() == black {
|
||||||
n = n.moveRedRight()
|
n = n.moveRedRight()
|
||||||
}
|
}
|
||||||
if Less(n.Elem, rr.Header().Name) == 0 {
|
if Less(n.Elem, rr.Header().Name) == 0 {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ var wildcardTestCases = []test.Case{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLookupWildcard(t *testing.T) {
|
func TestLookupWildcard(t *testing.T) {
|
||||||
zone, err := Parse(strings.NewReader(dbDnssexNL_signed), testzone1, "stdin")
|
zone, err := Parse(strings.NewReader(dbDnssexNLSigned), testzone1, "stdin")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expect no error when reading zone, got %q", err)
|
t.Fatalf("expect no error when reading zone, got %q", err)
|
||||||
}
|
}
|
||||||
@@ -85,7 +85,7 @@ func TestLookupWildcard(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dbDnssexNL_signed = `
|
const dbDnssexNLSigned = `
|
||||||
; File written on Tue Mar 29 21:02:24 2016
|
; File written on Tue Mar 29 21:02:24 2016
|
||||||
; dnssec_signzone version 9.10.3-P4-Ubuntu
|
; dnssec_signzone version 9.10.3-P4-Ubuntu
|
||||||
dnssex.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. (
|
dnssex.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. (
|
||||||
|
|||||||
@@ -10,13 +10,12 @@ import (
|
|||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// Xfr serves up an AXFR.
|
||||||
Xfr struct {
|
type Xfr struct {
|
||||||
*Zone
|
*Zone
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
// Serve an AXFR (and fallback of IXFR) as well.
|
// ServeDNS implements the middleware.Handler interface.
|
||||||
func (x Xfr) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
func (x Xfr) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||||
state := request.Request{W: w, Req: r}
|
state := request.Request{W: w, Req: r}
|
||||||
if !x.TransferAllowed(state) {
|
if !x.TransferAllowed(state) {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Zone defines a structure that contains all data related to a DNS zone.
|
||||||
type Zone struct {
|
type Zone struct {
|
||||||
origin string
|
origin string
|
||||||
file string
|
file string
|
||||||
@@ -31,6 +32,7 @@ type Zone struct {
|
|||||||
// TODO: shutdown watcher channel
|
// TODO: shutdown watcher channel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apex contains the apex records of a zone: SOA, NS and their potential signatures.
|
||||||
type Apex struct {
|
type Apex struct {
|
||||||
SOA *dns.SOA
|
SOA *dns.SOA
|
||||||
NS []dns.RR
|
NS []dns.RR
|
||||||
@@ -135,6 +137,7 @@ func (z *Zone) All() []dns.RR {
|
|||||||
return append([]dns.RR{z.Apex.SOA}, records...)
|
return append([]dns.RR{z.Apex.SOA}, records...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reload reloads a zone when it is changed on disk. If z.NoRoload is true, no reloading will be done.
|
||||||
func (z *Zone) Reload(shutdown chan bool) error {
|
func (z *Zone) Reload(shutdown chan bool) error {
|
||||||
if z.NoReload {
|
if z.NoReload {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -10,32 +10,33 @@ import (
|
|||||||
|
|
||||||
var once sync.Once
|
var once sync.Once
|
||||||
|
|
||||||
type Health struct {
|
type health struct {
|
||||||
Addr string
|
Addr string
|
||||||
|
|
||||||
ln net.Listener
|
ln net.Listener
|
||||||
mux *http.ServeMux
|
mux *http.ServeMux
|
||||||
}
|
}
|
||||||
|
|
||||||
func health(w http.ResponseWriter, r *http.Request) {
|
func (h *health) Startup() error {
|
||||||
io.WriteString(w, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Health) Startup() error {
|
|
||||||
if h.Addr == "" {
|
if h.Addr == "" {
|
||||||
h.Addr = defAddr
|
h.Addr = defAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
if ln, err := net.Listen("tcp", h.Addr); err != nil {
|
ln, err := net.Listen("tcp", h.Addr)
|
||||||
|
if err != nil {
|
||||||
log.Printf("[ERROR] Failed to start health handler: %s", err)
|
log.Printf("[ERROR] Failed to start health handler: %s", err)
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
h.ln = ln
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h.ln = ln
|
||||||
|
|
||||||
h.mux = http.NewServeMux()
|
h.mux = http.NewServeMux()
|
||||||
|
|
||||||
h.mux.HandleFunc(path, health)
|
h.mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
io.WriteString(w, ok)
|
||||||
|
})
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
http.Serve(h.ln, h.mux)
|
http.Serve(h.ln, h.mux)
|
||||||
}()
|
}()
|
||||||
@@ -43,7 +44,7 @@ func (h *Health) Startup() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Health) Shutdown() error {
|
func (h *health) Shutdown() error {
|
||||||
if h.ln != nil {
|
if h.ln != nil {
|
||||||
return h.ln.Close()
|
return h.ln.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ import (
|
|||||||
func TestHealth(t *testing.T) {
|
func TestHealth(t *testing.T) {
|
||||||
// We use a random port instead of a fixed port like 8080 that may have been
|
// We use a random port instead of a fixed port like 8080 that may have been
|
||||||
// occupied by some other process.
|
// occupied by some other process.
|
||||||
health := Health{Addr: ":0"}
|
h := health{Addr: ":0"}
|
||||||
if err := health.Startup(); err != nil {
|
if err := h.Startup(); err != nil {
|
||||||
t.Fatalf("Unable to startup the health server: %v", err)
|
t.Fatalf("Unable to startup the health server: %v", err)
|
||||||
}
|
}
|
||||||
defer health.Shutdown()
|
defer h.Shutdown()
|
||||||
|
|
||||||
// Reconstruct the http address based on the port allocated by operating system.
|
// Reconstruct the http address based on the port allocated by operating system.
|
||||||
address := fmt.Sprintf("http://%s%s", health.ln.Addr().String(), path)
|
address := fmt.Sprintf("http://%s%s", h.ln.Addr().String(), path)
|
||||||
|
|
||||||
response, err := http.Get(address)
|
response, err := http.Get(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ func setup(c *caddy.Controller) error {
|
|||||||
return middleware.Error("health", err)
|
return middleware.Error("health", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
health := &Health{Addr: addr}
|
h := &health{Addr: addr}
|
||||||
c.OnStartup(health.Startup)
|
c.OnStartup(h.Startup)
|
||||||
c.OnShutdown(health.Shutdown)
|
c.OnShutdown(h.Shutdown)
|
||||||
|
|
||||||
// Don't do AddMiddleware, as health is not *really* a middleware just a separate
|
// Don't do AddMiddleware, as health is not *really* a middleware just a separate
|
||||||
// webserver running.
|
// webserver running.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ServeDNS implements the middleware.Handler interface.
|
||||||
func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||||
state := request.Request{W: w, Req: r}
|
state := request.Request{W: w, Req: r}
|
||||||
if state.QClass() != dns.ClassINET {
|
if state.QClass() != dns.ClassINET {
|
||||||
@@ -29,7 +30,7 @@ func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.M
|
|||||||
srvPTR := &records[0]
|
srvPTR := &records[0]
|
||||||
m.Answer = append(m.Answer, srvPTR.NewPTR(state.QName(), ip))
|
m.Answer = append(m.Answer, srvPTR.NewPTR(state.QName(), ip))
|
||||||
|
|
||||||
m = dedup(m)
|
m = dnsutil.Dedup(m)
|
||||||
state.SizeAndDo(m)
|
state.SizeAndDo(m)
|
||||||
m, _ = state.Scrub(m)
|
m, _ = state.Scrub(m)
|
||||||
w.WriteMsg(m)
|
w.WriteMsg(m)
|
||||||
@@ -93,14 +94,14 @@ func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.M
|
|||||||
m.Answer = append(m.Answer, records...)
|
m.Answer = append(m.Answer, records...)
|
||||||
m.Extra = append(m.Extra, extra...)
|
m.Extra = append(m.Extra, extra...)
|
||||||
|
|
||||||
m = dedup(m)
|
m = dnsutil.Dedup(m)
|
||||||
state.SizeAndDo(m)
|
state.SizeAndDo(m)
|
||||||
m, _ = state.Scrub(m)
|
m, _ = state.Scrub(m)
|
||||||
w.WriteMsg(m)
|
w.WriteMsg(m)
|
||||||
return dns.RcodeSuccess, nil
|
return dns.RcodeSuccess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NoData write a nodata response to the client.
|
// Err writes an error response back to the client.
|
||||||
func (k Kubernetes) Err(zone string, rcode int, state request.Request) (int, error) {
|
func (k Kubernetes) Err(zone string, rcode int, state request.Request) (int, error) {
|
||||||
m := new(dns.Msg)
|
m := new(dns.Msg)
|
||||||
m.SetRcode(state.Req, rcode)
|
m.SetRcode(state.Req, rcode)
|
||||||
@@ -109,11 +110,3 @@ func (k Kubernetes) Err(zone string, rcode int, state request.Request) (int, err
|
|||||||
state.W.WriteMsg(m)
|
state.W.WriteMsg(m)
|
||||||
return rcode, nil
|
return rcode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func dedup(m *dns.Msg) *dns.Msg {
|
|
||||||
// TODO(miek): expensive!
|
|
||||||
m.Answer = dns.Dedup(m.Answer, nil)
|
|
||||||
m.Ns = dns.Dedup(m.Ns, nil)
|
|
||||||
m.Extra = dns.Dedup(m.Extra, nil)
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/miekg/coredns/middleware"
|
"github.com/miekg/coredns/middleware"
|
||||||
"github.com/miekg/coredns/middleware/kubernetes/msg"
|
"github.com/miekg/coredns/middleware/etcd/msg"
|
||||||
"github.com/miekg/coredns/middleware/kubernetes/nametemplate"
|
"github.com/miekg/coredns/middleware/kubernetes/nametemplate"
|
||||||
"github.com/miekg/coredns/middleware/kubernetes/util"
|
"github.com/miekg/coredns/middleware/kubernetes/util"
|
||||||
"github.com/miekg/coredns/middleware/pkg/dnsutil"
|
"github.com/miekg/coredns/middleware/pkg/dnsutil"
|
||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/labels"
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Kubernetes implements a middleware that connects to a Kubernetes cluster.
|
||||||
type Kubernetes struct {
|
type Kubernetes struct {
|
||||||
Next middleware.Handler
|
Next middleware.Handler
|
||||||
Zones []string
|
Zones []string
|
||||||
@@ -35,6 +36,8 @@ type Kubernetes struct {
|
|||||||
Selector *labels.Selector
|
Selector *labels.Selector
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitKubeCache initializes a new Kubernetes cache.
|
||||||
|
// TODO(miek): is this correct?
|
||||||
func (k *Kubernetes) InitKubeCache() error {
|
func (k *Kubernetes) InitKubeCache() error {
|
||||||
// For a custom api server or running outside a k8s cluster
|
// For a custom api server or running outside a k8s cluster
|
||||||
// set URL in env.KUBERNETES_MASTER or set endpoint in Corefile
|
// set URL in env.KUBERNETES_MASTER or set endpoint in Corefile
|
||||||
@@ -232,7 +235,7 @@ func (k *Kubernetes) getServiceRecordForIP(ip, name string) []msg.Service {
|
|||||||
const (
|
const (
|
||||||
priority = 10 // default priority when nothing is set
|
priority = 10 // default priority when nothing is set
|
||||||
ttl = 300 // default ttl when nothing is set
|
ttl = 300 // default ttl when nothing is set
|
||||||
minTtl = 60
|
minTTL = 60
|
||||||
hostmaster = "hostmaster"
|
hostmaster = "hostmaster"
|
||||||
k8sTimeout = 5 * time.Second
|
k8sTimeout = 5 * time.Second
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/miekg/coredns/middleware"
|
"github.com/miekg/coredns/middleware"
|
||||||
"github.com/miekg/coredns/middleware/kubernetes/msg"
|
"github.com/miekg/coredns/middleware/etcd/msg"
|
||||||
"github.com/miekg/coredns/middleware/pkg/dnsutil"
|
"github.com/miekg/coredns/middleware/pkg/dnsutil"
|
||||||
"github.com/miekg/coredns/request"
|
"github.com/miekg/coredns/request"
|
||||||
|
|
||||||
@@ -24,6 +24,7 @@ func (k Kubernetes) records(state request.Request, exact bool) ([]msg.Service, e
|
|||||||
return services, nil
|
return services, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A returns A records from kubernetes or an error.
|
||||||
func (k Kubernetes) A(zone string, state request.Request, previousRecords []dns.RR) (records []dns.RR, err error) {
|
func (k Kubernetes) A(zone string, state request.Request, previousRecords []dns.RR) (records []dns.RR, err error) {
|
||||||
services, err := k.records(state, false)
|
services, err := k.records(state, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -83,6 +84,7 @@ func (k Kubernetes) A(zone string, state request.Request, previousRecords []dns.
|
|||||||
return records, nil
|
return records, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AAAA returns AAAA records from kubernetes or an error.
|
||||||
func (k Kubernetes) AAAA(zone string, state request.Request, previousRecords []dns.RR) (records []dns.RR, err error) {
|
func (k Kubernetes) AAAA(zone string, state request.Request, previousRecords []dns.RR) (records []dns.RR, err error) {
|
||||||
services, err := k.records(state, false)
|
services, err := k.records(state, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -144,7 +146,8 @@ func (k Kubernetes) AAAA(zone string, state request.Request, previousRecords []d
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SRV returns SRV records from kubernetes.
|
// SRV returns SRV records from kubernetes.
|
||||||
// If the Target is not a name but an IP address, a name is created on the fly.
|
// If the Target is not a name but an IP address, a name is created on the fly and the IP address is put in
|
||||||
|
// the additional section.
|
||||||
func (k Kubernetes) SRV(zone string, state request.Request) (records []dns.RR, extra []dns.RR, err error) {
|
func (k Kubernetes) SRV(zone string, state request.Request) (records []dns.RR, extra []dns.RR, err error) {
|
||||||
services, err := k.records(state, false)
|
services, err := k.records(state, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -226,21 +229,22 @@ func (k Kubernetes) SRV(zone string, state request.Request) (records []dns.RR, e
|
|||||||
return records, extra, nil
|
return records, extra, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returning MX records from kubernetes not implemented.
|
// MX returns MX records from kubernetes. Not implemented!
|
||||||
func (k Kubernetes) MX(zone string, state request.Request) (records []dns.RR, extra []dns.RR, err error) {
|
func (k Kubernetes) MX(zone string, state request.Request) (records []dns.RR, extra []dns.RR, err error) {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returning CNAME records from kubernetes not implemented.
|
// CNAME returns CNAME records from kubernetes. Not implemented!
|
||||||
func (k Kubernetes) CNAME(zone string, state request.Request) (records []dns.RR, err error) {
|
func (k Kubernetes) CNAME(zone string, state request.Request) (records []dns.RR, err error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returning TXT records from kubernetes not implemented.
|
// TXT returns TXT records from kubernetes. Not implemented!
|
||||||
func (k Kubernetes) TXT(zone string, state request.Request) (records []dns.RR, err error) {
|
func (k Kubernetes) TXT(zone string, state request.Request) (records []dns.RR, err error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NS returns NS records from kubernetes.
|
||||||
func (k Kubernetes) NS(zone string, state request.Request) (records, extra []dns.RR, err error) {
|
func (k Kubernetes) NS(zone string, state request.Request) (records, extra []dns.RR, err error) {
|
||||||
// NS record for this zone live in a special place, ns.dns.<zone>. Fake our lookup.
|
// NS record for this zone live in a special place, ns.dns.<zone>. Fake our lookup.
|
||||||
// only a tad bit fishy...
|
// only a tad bit fishy...
|
||||||
@@ -273,7 +277,7 @@ func (k Kubernetes) NS(zone string, state request.Request) (records, extra []dns
|
|||||||
return records, extra, nil
|
return records, extra, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SOA Record returns a SOA record.
|
// SOA Record returns a SOA record from kubernetes.
|
||||||
func (k Kubernetes) SOA(zone string, state request.Request) *dns.SOA {
|
func (k Kubernetes) SOA(zone string, state request.Request) *dns.SOA {
|
||||||
header := dns.RR_Header{Name: zone, Rrtype: dns.TypeSOA, Ttl: 300, Class: dns.ClassINET}
|
header := dns.RR_Header{Name: zone, Rrtype: dns.TypeSOA, Ttl: 300, Class: dns.ClassINET}
|
||||||
return &dns.SOA{Hdr: header,
|
return &dns.SOA{Hdr: header,
|
||||||
@@ -287,6 +291,7 @@ func (k Kubernetes) SOA(zone string, state request.Request) *dns.SOA {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PTR Record returns PTR records from kubernetes.
|
||||||
func (k Kubernetes) PTR(zone string, state request.Request) ([]dns.RR, error) {
|
func (k Kubernetes) PTR(zone string, state request.Request) ([]dns.RR, error) {
|
||||||
reverseIP := dnsutil.ExtractAddressFromReverse(state.Name())
|
reverseIP := dnsutil.ExtractAddressFromReverse(state.Name())
|
||||||
if reverseIP == "" {
|
if reverseIP == "" {
|
||||||
|
|||||||
@@ -1,171 +0,0 @@
|
|||||||
package msg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This *is* the rdata from a SRV record, but with a twist.
|
|
||||||
// Host (Target in SRV) must be a domain name, but if it looks like an IP
|
|
||||||
// address (4/6), we will treat it like an IP address.
|
|
||||||
type Service struct {
|
|
||||||
Host string `json:"host,omitempty"`
|
|
||||||
Port int `json:"port,omitempty"`
|
|
||||||
Priority int `json:"priority,omitempty"`
|
|
||||||
Weight int `json:"weight,omitempty"`
|
|
||||||
Text string `json:"text,omitempty"`
|
|
||||||
Mail bool `json:"mail,omitempty"` // Be an MX record. Priority becomes Preference.
|
|
||||||
Ttl uint32 `json:"ttl,omitempty"`
|
|
||||||
|
|
||||||
// When a SRV record with a "Host: IP-address" is added, we synthesize
|
|
||||||
// a srv.Target domain name. Normally we convert the full Key where
|
|
||||||
// the record lives to a DNS name and use this as the srv.Target. When
|
|
||||||
// TargetStrip > 0 we strip the left most TargetStrip labels from the
|
|
||||||
// DNS name.
|
|
||||||
TargetStrip int `json:"targetstrip,omitempty"`
|
|
||||||
|
|
||||||
// Group is used to group (or *not* to group) different services
|
|
||||||
// together. Services with an identical Group are returned in the same
|
|
||||||
// answer.
|
|
||||||
Group string `json:"group,omitempty"`
|
|
||||||
|
|
||||||
// Etcd key where we found this service and ignored from json un-/marshalling
|
|
||||||
Key string `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSRV returns a new SRV record based on the Service.
|
|
||||||
func (s *Service) NewSRV(name string, weight uint16) *dns.SRV {
|
|
||||||
host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip)
|
|
||||||
|
|
||||||
return &dns.SRV{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeSRV, Class: dns.ClassINET, Ttl: s.Ttl},
|
|
||||||
Priority: uint16(s.Priority), Weight: weight, Port: uint16(s.Port), Target: dns.Fqdn(host)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMX returns a new MX record based on the Service.
|
|
||||||
func (s *Service) NewMX(name string) *dns.MX {
|
|
||||||
host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip)
|
|
||||||
|
|
||||||
return &dns.MX{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: s.Ttl},
|
|
||||||
Preference: uint16(s.Priority), Mx: host}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewA returns a new A record based on the Service.
|
|
||||||
func (s *Service) NewA(name string, ip net.IP) *dns.A {
|
|
||||||
return &dns.A{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: s.Ttl}, A: ip}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAAAA returns a new AAAA record based on the Service.
|
|
||||||
func (s *Service) NewAAAA(name string, ip net.IP) *dns.AAAA {
|
|
||||||
return &dns.AAAA{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: s.Ttl}, AAAA: ip}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCNAME returns a new CNAME record based on the Service.
|
|
||||||
func (s *Service) NewCNAME(name string, target string) *dns.CNAME {
|
|
||||||
return &dns.CNAME{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: s.Ttl}, Target: dns.Fqdn(target)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTXT returns a new TXT record based on the Service.
|
|
||||||
func (s *Service) NewTXT(name string) *dns.TXT {
|
|
||||||
return &dns.TXT{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: s.Ttl}, Txt: split255(s.Text)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNS returns a new NS record based on the Service.
|
|
||||||
func (s *Service) NewNS(name string) *dns.NS {
|
|
||||||
host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip)
|
|
||||||
return &dns.NS{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: s.Ttl}, Ns: host}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPTR returns a new PTR record based on the Service.
|
|
||||||
func (s *Service) NewPTR(name string, target string) *dns.PTR {
|
|
||||||
return &dns.PTR{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: s.Ttl}, Ptr: dns.Fqdn(target)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Group checks the services in sx, it looks for a Group attribute on the shortest
|
|
||||||
// keys. If there are multiple shortest keys *and* the group attribute disagrees (and
|
|
||||||
// is not empty), we don't consider it a group.
|
|
||||||
// If a group is found, only services with *that* group (or no group) will be returned.
|
|
||||||
func Group(sx []Service) []Service {
|
|
||||||
if len(sx) == 0 {
|
|
||||||
return sx
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shortest key with group attribute sets the group for this set.
|
|
||||||
group := sx[0].Group
|
|
||||||
slashes := strings.Count(sx[0].Key, "/")
|
|
||||||
length := make([]int, len(sx))
|
|
||||||
for i, s := range sx {
|
|
||||||
x := strings.Count(s.Key, "/")
|
|
||||||
length[i] = x
|
|
||||||
if x < slashes {
|
|
||||||
if s.Group == "" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
slashes = x
|
|
||||||
group = s.Group
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if group == "" {
|
|
||||||
return sx
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := []Service{} // with slice-tricks in sx we can prolly save this allocation (TODO)
|
|
||||||
|
|
||||||
for i, s := range sx {
|
|
||||||
if s.Group == "" {
|
|
||||||
ret = append(ret, s)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disagreement on the same level
|
|
||||||
if length[i] == slashes && s.Group != group {
|
|
||||||
return sx
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Group == group {
|
|
||||||
ret = append(ret, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split255 splits a string into 255 byte chunks.
|
|
||||||
func split255(s string) []string {
|
|
||||||
if len(s) < 255 {
|
|
||||||
return []string{s}
|
|
||||||
}
|
|
||||||
sx := []string{}
|
|
||||||
p, i := 0, 255
|
|
||||||
for {
|
|
||||||
if i <= len(s) {
|
|
||||||
sx = append(sx, s[p:i])
|
|
||||||
} else {
|
|
||||||
sx = append(sx, s[p:])
|
|
||||||
break
|
|
||||||
|
|
||||||
}
|
|
||||||
p, i = p+255, i+255
|
|
||||||
}
|
|
||||||
|
|
||||||
return sx
|
|
||||||
}
|
|
||||||
|
|
||||||
// targetStrip strips "targetstrip" labels from the left side of the fully qualified name.
|
|
||||||
func targetStrip(name string, targetStrip int) string {
|
|
||||||
if targetStrip == 0 {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
offset, end := 0, false
|
|
||||||
for i := 0; i < targetStrip; i++ {
|
|
||||||
offset, end = dns.NextLabel(name, offset)
|
|
||||||
}
|
|
||||||
if end {
|
|
||||||
// We overshot the name, use the orignal one.
|
|
||||||
offset = 0
|
|
||||||
}
|
|
||||||
name = name[offset:]
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
package msg
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestSplit255(t *testing.T) {
|
|
||||||
xs := split255("abc")
|
|
||||||
if len(xs) != 1 && xs[0] != "abc" {
|
|
||||||
t.Errorf("Failure to split abc")
|
|
||||||
}
|
|
||||||
s := ""
|
|
||||||
for i := 0; i < 255; i++ {
|
|
||||||
s += "a"
|
|
||||||
}
|
|
||||||
xs = split255(s)
|
|
||||||
if len(xs) != 1 && xs[0] != s {
|
|
||||||
t.Errorf("failure to split 255 char long string")
|
|
||||||
}
|
|
||||||
s += "b"
|
|
||||||
xs = split255(s)
|
|
||||||
if len(xs) != 2 || xs[1] != "b" {
|
|
||||||
t.Errorf("failure to split 256 char long string: %d", len(xs))
|
|
||||||
}
|
|
||||||
for i := 0; i < 255; i++ {
|
|
||||||
s += "a"
|
|
||||||
}
|
|
||||||
xs = split255(s)
|
|
||||||
if len(xs) != 3 || xs[2] != "a" {
|
|
||||||
t.Errorf("failure to split 510 char long string: %d", len(xs))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGroup(t *testing.T) {
|
|
||||||
// Key are in the wrong order, but for this test it does not matter.
|
|
||||||
sx := Group(
|
|
||||||
[]Service{
|
|
||||||
{Host: "127.0.0.1", Group: "g1", Key: "b/sub/dom1/skydns/test"},
|
|
||||||
{Host: "127.0.0.2", Group: "g2", Key: "a/dom1/skydns/test"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
// Expecting to return the shortest key with a Group attribute.
|
|
||||||
if len(sx) != 1 {
|
|
||||||
t.Fatalf("failure to group zeroth set: %v", sx)
|
|
||||||
}
|
|
||||||
if sx[0].Key != "a/dom1/skydns/test" {
|
|
||||||
t.Fatalf("failure to group zeroth set: %v, wrong Key", sx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Groups disagree, so we will not do anything.
|
|
||||||
sx = Group(
|
|
||||||
[]Service{
|
|
||||||
{Host: "server1", Group: "g1", Key: "region1/skydns/test"},
|
|
||||||
{Host: "server2", Group: "g2", Key: "region1/skydns/test"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if len(sx) != 2 {
|
|
||||||
t.Fatalf("failure to group first set: %v", sx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Group is g1, include only the top-level one.
|
|
||||||
sx = Group(
|
|
||||||
[]Service{
|
|
||||||
{Host: "server1", Group: "g1", Key: "a/dom/region1/skydns/test"},
|
|
||||||
{Host: "server2", Group: "g2", Key: "a/subdom/dom/region1/skydns/test"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if len(sx) != 1 {
|
|
||||||
t.Fatalf("failure to group second set: %v", sx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Groupless services must be included.
|
|
||||||
sx = Group(
|
|
||||||
[]Service{
|
|
||||||
{Host: "server1", Group: "g1", Key: "a/dom/region1/skydns/test"},
|
|
||||||
{Host: "server2", Group: "g2", Key: "a/subdom/dom/region1/skydns/test"},
|
|
||||||
{Host: "server2", Group: "", Key: "b/subdom/dom/region1/skydns/test"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if len(sx) != 2 {
|
|
||||||
t.Fatalf("failure to group third set: %v", sx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty group on the highest level: include that one also.
|
|
||||||
sx = Group(
|
|
||||||
[]Service{
|
|
||||||
{Host: "server1", Group: "g1", Key: "a/dom/region1/skydns/test"},
|
|
||||||
{Host: "server1", Group: "", Key: "b/dom/region1/skydns/test"},
|
|
||||||
{Host: "server2", Group: "g2", Key: "a/subdom/dom/region1/skydns/test"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if len(sx) != 2 {
|
|
||||||
t.Fatalf("failure to group fourth set: %v", sx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty group on the highest level: include that one also, and the rest.
|
|
||||||
sx = Group(
|
|
||||||
[]Service{
|
|
||||||
{Host: "server1", Group: "g5", Key: "a/dom/region1/skydns/test"},
|
|
||||||
{Host: "server1", Group: "", Key: "b/dom/region1/skydns/test"},
|
|
||||||
{Host: "server2", Group: "g5", Key: "a/subdom/dom/region1/skydns/test"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if len(sx) != 3 {
|
|
||||||
t.Fatalf("failure to group fith set: %v", sx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// One group.
|
|
||||||
sx = Group(
|
|
||||||
[]Service{
|
|
||||||
{Host: "server1", Group: "g6", Key: "a/dom/region1/skydns/test"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if len(sx) != 1 {
|
|
||||||
t.Fatalf("failure to group sixth set: %v", sx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// No group, once service
|
|
||||||
sx = Group(
|
|
||||||
[]Service{
|
|
||||||
{Host: "server1", Key: "a/dom/region1/skydns/test"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if len(sx) != 1 {
|
|
||||||
t.Fatalf("failure to group seventh set: %v", sx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -60,6 +60,7 @@ var requiredSymbols = []string{
|
|||||||
// Where the query string is longer than the template, need to define which
|
// Where the query string is longer than the template, need to define which
|
||||||
// symbol consumes the other segments. Most likely this would be the servicename.
|
// symbol consumes the other segments. Most likely this would be the servicename.
|
||||||
// Also consider how to handle static strings in the format template.
|
// Also consider how to handle static strings in the format template.
|
||||||
|
|
||||||
type NameTemplate struct {
|
type NameTemplate struct {
|
||||||
formatString string
|
formatString string
|
||||||
splitFormat []string
|
splitFormat []string
|
||||||
@@ -105,11 +106,11 @@ func (t *NameTemplate) SetTemplate(s string) error {
|
|||||||
// step down the stack to find the right element.
|
// step down the stack to find the right element.
|
||||||
|
|
||||||
func (t *NameTemplate) GetZoneFromSegmentArray(segments []string) string {
|
func (t *NameTemplate) GetZoneFromSegmentArray(segments []string) string {
|
||||||
if index, ok := t.Element["zone"]; !ok {
|
index, ok := t.Element["zone"]
|
||||||
|
if !ok {
|
||||||
return ""
|
return ""
|
||||||
} else {
|
|
||||||
return strings.Join(segments[index:len(segments)], ".")
|
|
||||||
}
|
}
|
||||||
|
return strings.Join(segments[index:len(segments)], ".")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *NameTemplate) GetNamespaceFromSegmentArray(segments []string) string {
|
func (t *NameTemplate) GetNamespaceFromSegmentArray(segments []string) string {
|
||||||
@@ -132,11 +133,11 @@ func (t *NameTemplate) GetTypeFromSegmentArray(segments []string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *NameTemplate) GetSymbolFromSegmentArray(symbol string, segments []string) string {
|
func (t *NameTemplate) GetSymbolFromSegmentArray(symbol string, segments []string) string {
|
||||||
if index, ok := t.Element[symbol]; !ok {
|
index, ok := t.Element[symbol]
|
||||||
|
if !ok {
|
||||||
return ""
|
return ""
|
||||||
} else {
|
|
||||||
return segments[index]
|
|
||||||
}
|
}
|
||||||
|
return segments[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRecordNameFromNameValues returns the string produced by applying the
|
// GetRecordNameFromNameValues returns the string produced by applying the
|
||||||
@@ -164,6 +165,7 @@ func (t *NameTemplate) GetRecordNameFromNameValues(values NameValues) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *NameTemplate) IsValid() bool {
|
func (t *NameTemplate) IsValid() bool {
|
||||||
|
// This is *only* used in a test, for the test this should be a private method.
|
||||||
result := true
|
result := true
|
||||||
|
|
||||||
// Ensure that all requiredSymbols are found in NameTemplate
|
// Ensure that all requiredSymbols are found in NameTemplate
|
||||||
|
|||||||
@@ -15,6 +15,6 @@ type RoundRobin struct {
|
|||||||
|
|
||||||
// ServeDNS implements the middleware.Handler interface.
|
// ServeDNS implements the middleware.Handler interface.
|
||||||
func (rr RoundRobin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
func (rr RoundRobin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||||
wrr := NewRoundRobinResponseWriter(w)
|
wrr := &RoundRobinResponseWriter{w}
|
||||||
return rr.Next.ServeDNS(ctx, wrr, r)
|
return rr.Next.ServeDNS(ctx, wrr, r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,12 @@ import (
|
|||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// RoundRobinResponseWriter is a response writer that shuffles A and AAAA records.
|
||||||
type RoundRobinResponseWriter struct {
|
type RoundRobinResponseWriter struct {
|
||||||
dns.ResponseWriter
|
dns.ResponseWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRoundRobinResponseWriter(w dns.ResponseWriter) *RoundRobinResponseWriter {
|
// WriteMsg implements the dns.ResponseWriter interface.
|
||||||
return &RoundRobinResponseWriter{w}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RoundRobinResponseWriter) WriteMsg(res *dns.Msg) error {
|
func (r *RoundRobinResponseWriter) WriteMsg(res *dns.Msg) error {
|
||||||
if res.Rcode != dns.RcodeSuccess {
|
if res.Rcode != dns.RcodeSuccess {
|
||||||
return r.ResponseWriter.WriteMsg(res)
|
return r.ResponseWriter.WriteMsg(res)
|
||||||
@@ -63,13 +61,15 @@ func roundRobin(in []dns.RR) []dns.RR {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should we pack and unpack here to fiddle with the packet... Not likely.
|
// Write implements the dns.ResponseWriter interface.
|
||||||
func (r *RoundRobinResponseWriter) Write(buf []byte) (int, error) {
|
func (r *RoundRobinResponseWriter) Write(buf []byte) (int, error) {
|
||||||
|
// Should we pack and unpack here to fiddle with the packet... Not likely.
|
||||||
log.Printf("[WARNING] RoundRobin called with Write: no shuffling records")
|
log.Printf("[WARNING] RoundRobin called with Write: no shuffling records")
|
||||||
n, err := r.ResponseWriter.Write(buf)
|
n, err := r.ResponseWriter.Write(buf)
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hijack implements the dns.ResponseWriter interface.
|
||||||
func (r *RoundRobinResponseWriter) Hijack() {
|
func (r *RoundRobinResponseWriter) Hijack() {
|
||||||
r.ResponseWriter.Hijack()
|
r.ResponseWriter.Hijack()
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ type Logger struct {
|
|||||||
ErrorFunc func(dns.ResponseWriter, *dns.Msg, int) // failover error handler
|
ErrorFunc func(dns.ResponseWriter, *dns.Msg, int) // failover error handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServeDNS implements the middleware.Handler interface.
|
||||||
func (l Logger) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
func (l Logger) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||||
state := request.Request{W: w, Req: r}
|
state := request.Request{W: w, Req: r}
|
||||||
for _, rule := range l.Rules {
|
for _, rule := range l.Rules {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ServeDNS implements the Handler interface.
|
||||||
func (m Metrics) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
func (m Metrics) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||||
state := request.Request{W: w, Req: r}
|
state := request.Request{W: w, Req: r}
|
||||||
|
|
||||||
|
|||||||
@@ -34,16 +34,19 @@ type Metrics struct {
|
|||||||
ZoneNames []string
|
ZoneNames []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metrics) Startup() error {
|
// OnStartup sets up the metrics on startup.
|
||||||
|
func (m *Metrics) OnStartup() error {
|
||||||
m.Once.Do(func() {
|
m.Once.Do(func() {
|
||||||
define()
|
define()
|
||||||
|
|
||||||
if ln, err := net.Listen("tcp", m.Addr); err != nil {
|
ln, err := net.Listen("tcp", m.Addr)
|
||||||
|
if err != nil {
|
||||||
log.Printf("[ERROR] Failed to start metrics handler: %s", err)
|
log.Printf("[ERROR] Failed to start metrics handler: %s", err)
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
m.ln = ln
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.ln = ln
|
||||||
|
|
||||||
m.mux = http.NewServeMux()
|
m.mux = http.NewServeMux()
|
||||||
|
|
||||||
prometheus.MustRegister(requestCount)
|
prometheus.MustRegister(requestCount)
|
||||||
@@ -66,7 +69,8 @@ func (m *Metrics) Startup() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metrics) Shutdown() error {
|
// OnShutdown tears down the metrics on shutdown.
|
||||||
|
func (m *Metrics) OnShutdown() error {
|
||||||
if m.ln != nil {
|
if m.ln != nil {
|
||||||
return m.ln.Close()
|
return m.ln.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ func setup(c *caddy.Controller) error {
|
|||||||
})
|
})
|
||||||
|
|
||||||
metricsOnce.Do(func() {
|
metricsOnce.Do(func() {
|
||||||
c.OnStartup(m.Startup)
|
c.OnStartup(m.OnStartup)
|
||||||
c.OnShutdown(m.Shutdown)
|
c.OnShutdown(m.OnShutdown)
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
12
middleware/pkg/dnsutil/dedup.go
Normal file
12
middleware/pkg/dnsutil/dedup.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package dnsutil
|
||||||
|
|
||||||
|
import "github.com/miekg/dns"
|
||||||
|
|
||||||
|
// Dedup de-duplicates a message.
|
||||||
|
func Dedup(m *dns.Msg) *dns.Msg {
|
||||||
|
// TODO(miek): expensive!
|
||||||
|
m.Answer = dns.Dedup(m.Answer, nil)
|
||||||
|
m.Ns = dns.Dedup(m.Ns, nil)
|
||||||
|
m.Extra = dns.Dedup(m.Extra, nil)
|
||||||
|
return m
|
||||||
|
}
|
||||||
@@ -7,12 +7,12 @@ import (
|
|||||||
pp "net/http/pprof"
|
pp "net/http/pprof"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Handler struct {
|
type handler struct {
|
||||||
ln net.Listener
|
ln net.Listener
|
||||||
mux *http.ServeMux
|
mux *http.ServeMux
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) Startup() error {
|
func (h *handler) Startup() error {
|
||||||
ln, err := net.Listen("tcp", addr)
|
ln, err := net.Listen("tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[ERROR] Failed to start pprof handler: %s", err)
|
log.Printf("[ERROR] Failed to start pprof handler: %s", err)
|
||||||
@@ -34,7 +34,7 @@ func (h *Handler) Startup() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) Shutdown() error {
|
func (h *handler) Shutdown() error {
|
||||||
if h.ln != nil {
|
if h.ln != nil {
|
||||||
return h.ln.Close()
|
return h.ln.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ func setup(c *caddy.Controller) error {
|
|||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
|
|
||||||
handler := &Handler{}
|
h := &handler{}
|
||||||
pprofOnce.Do(func() {
|
pprofOnce.Do(func() {
|
||||||
c.OnStartup(handler.Startup)
|
c.OnStartup(h.Startup)
|
||||||
c.OnShutdown(handler.Shutdown)
|
c.OnShutdown(h.Shutdown)
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ type staticUpstream struct {
|
|||||||
IgnoredSubDomains []string
|
IgnoredSubDomains []string
|
||||||
options Options
|
options Options
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Ecs []*net.IPNet // EDNS0 CLIENT SUBNET address (v4/v6) to add in CIDR notaton.
|
Ecs []*net.IPNet // EDNS0 CLIENT SUBNET address (v4/v6) to add in CIDR notaton.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package rewrite
|
|||||||
|
|
||||||
import "github.com/miekg/dns"
|
import "github.com/miekg/dns"
|
||||||
|
|
||||||
// ResponseRevert reverses the operations done on the question section of a packet.
|
// ResponseReverter reverses the operations done on the question section of a packet.
|
||||||
// This is need because the client will otherwise disregards the response, i.e.
|
// This is need because the client will otherwise disregards the response, i.e.
|
||||||
// dig will complain with ';; Question section mismatch: got miek.nl/HINFO/IN'
|
// dig will complain with ';; Question section mismatch: got miek.nl/HINFO/IN'
|
||||||
type ResponseReverter struct {
|
type ResponseReverter struct {
|
||||||
@@ -10,6 +10,7 @@ type ResponseReverter struct {
|
|||||||
original dns.Question
|
original dns.Question
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewResponseReverter returns a pointer to a new ResponseReverter.
|
||||||
func NewResponseReverter(w dns.ResponseWriter, r *dns.Msg) *ResponseReverter {
|
func NewResponseReverter(w dns.ResponseWriter, r *dns.Msg) *ResponseReverter {
|
||||||
return &ResponseReverter{
|
return &ResponseReverter{
|
||||||
ResponseWriter: w,
|
ResponseWriter: w,
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ type Rewrite struct {
|
|||||||
noRevert bool
|
noRevert bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeHTTP implements the middleware.Handler interface.
|
// ServeDNS implements the middleware.Handler interface.
|
||||||
func (rw Rewrite) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
func (rw Rewrite) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||||
wr := NewResponseReverter(w, r)
|
wr := NewResponseReverter(w, r)
|
||||||
for _, rule := range rw.Rules {
|
for _, rule := range rw.Rules {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package rewrite
|
package rewrite
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/miekg/coredns/core/dnsserver"
|
"github.com/miekg/coredns/core/dnsserver"
|
||||||
@@ -36,73 +35,76 @@ func rewriteParse(c *caddy.Controller) ([]Rule, error) {
|
|||||||
|
|
||||||
for c.Next() {
|
for c.Next() {
|
||||||
var rule Rule
|
var rule Rule
|
||||||
var err error
|
/*
|
||||||
var base = "."
|
var base = "."
|
||||||
var pattern, to string
|
var err error
|
||||||
var status int
|
var pattern, to string
|
||||||
var ext []string
|
var status int
|
||||||
|
var ifs []If
|
||||||
|
var ext []string
|
||||||
|
*/
|
||||||
|
|
||||||
args := c.RemainingArgs()
|
args := c.RemainingArgs()
|
||||||
|
|
||||||
var ifs []If
|
|
||||||
|
|
||||||
switch len(args) {
|
switch len(args) {
|
||||||
case 1:
|
case 1:
|
||||||
base = args[0]
|
/*
|
||||||
fallthrough
|
base = args[0]
|
||||||
|
fallthrough
|
||||||
|
*/
|
||||||
case 0:
|
case 0:
|
||||||
for c.NextBlock() {
|
/*
|
||||||
switch c.Val() {
|
for c.NextBlock() {
|
||||||
case "r", "regexp":
|
switch c.Val() {
|
||||||
if !c.NextArg() {
|
case "r", "regexp":
|
||||||
|
if !c.NextArg() {
|
||||||
|
return nil, c.ArgErr()
|
||||||
|
}
|
||||||
|
pattern = c.Val()
|
||||||
|
case "to":
|
||||||
|
args1 := c.RemainingArgs()
|
||||||
|
if len(args1) == 0 {
|
||||||
|
return nil, c.ArgErr()
|
||||||
|
}
|
||||||
|
to = strings.Join(args1, " ")
|
||||||
|
case "ext": // TODO(miek): fix or remove
|
||||||
|
args1 := c.RemainingArgs()
|
||||||
|
if len(args1) == 0 {
|
||||||
|
return nil, c.ArgErr()
|
||||||
|
}
|
||||||
|
ext = args1
|
||||||
|
case "if":
|
||||||
|
args1 := c.RemainingArgs()
|
||||||
|
if len(args1) != 3 {
|
||||||
|
return nil, c.ArgErr()
|
||||||
|
}
|
||||||
|
ifCond, err := NewIf(args1[0], args1[1], args1[2])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ifs = append(ifs, ifCond)
|
||||||
|
case "status": // TODO(miek): fix or remove
|
||||||
|
if !c.NextArg() {
|
||||||
|
return nil, c.ArgErr()
|
||||||
|
}
|
||||||
|
status, _ = strconv.Atoi(c.Val())
|
||||||
|
if status < 200 || (status > 299 && status < 400) || status > 499 {
|
||||||
|
return nil, c.Err("status must be 2xx or 4xx")
|
||||||
|
}
|
||||||
|
default:
|
||||||
return nil, c.ArgErr()
|
return nil, c.ArgErr()
|
||||||
}
|
}
|
||||||
pattern = c.Val()
|
}
|
||||||
case "to":
|
// ensure to or status is specified
|
||||||
args1 := c.RemainingArgs()
|
if to == "" && status == 0 {
|
||||||
if len(args1) == 0 {
|
|
||||||
return nil, c.ArgErr()
|
|
||||||
}
|
|
||||||
to = strings.Join(args1, " ")
|
|
||||||
case "ext": // TODO(miek): fix or remove
|
|
||||||
args1 := c.RemainingArgs()
|
|
||||||
if len(args1) == 0 {
|
|
||||||
return nil, c.ArgErr()
|
|
||||||
}
|
|
||||||
ext = args1
|
|
||||||
case "if":
|
|
||||||
args1 := c.RemainingArgs()
|
|
||||||
if len(args1) != 3 {
|
|
||||||
return nil, c.ArgErr()
|
|
||||||
}
|
|
||||||
ifCond, err := NewIf(args1[0], args1[1], args1[2])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ifs = append(ifs, ifCond)
|
|
||||||
case "status": // TODO(miek): fix or remove
|
|
||||||
if !c.NextArg() {
|
|
||||||
return nil, c.ArgErr()
|
|
||||||
}
|
|
||||||
status, _ = strconv.Atoi(c.Val())
|
|
||||||
if status < 200 || (status > 299 && status < 400) || status > 499 {
|
|
||||||
return nil, c.Err("status must be 2xx or 4xx")
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return nil, c.ArgErr()
|
return nil, c.ArgErr()
|
||||||
}
|
}
|
||||||
}
|
// TODO(miek): complex rules
|
||||||
// ensure to or status is specified
|
if rule, err = NewComplexRule(base, pattern, to, status, ext, ifs); err != nil {
|
||||||
if to == "" && status == 0 {
|
return nil, err
|
||||||
return nil, c.ArgErr()
|
}
|
||||||
}
|
regexpRules = append(regexpRules, rule)
|
||||||
// TODO(miek): complex rules
|
*/
|
||||||
base, pattern, to, status, ext, ifs = base, pattern, to, status, ext, ifs
|
|
||||||
err = err
|
|
||||||
// if rule, err = NewComplexRule(base, pattern, to, status, ext, ifs); err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
regexpRules = append(regexpRules, rule)
|
|
||||||
|
|
||||||
// the only unhandled case is 2 and above
|
// the only unhandled case is 2 and above
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package secondary
|
|||||||
|
|
||||||
import "github.com/miekg/coredns/middleware/file"
|
import "github.com/miekg/coredns/middleware/file"
|
||||||
|
|
||||||
|
// Secondary implements a secondary middleware that allows CoreDNS to retrieve (via AXFR)
|
||||||
|
// zone information from a primary server.
|
||||||
type Secondary struct {
|
type Secondary struct {
|
||||||
file.File
|
file.File
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,21 +7,26 @@ import (
|
|||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Sect int
|
type sect int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Answer Sect = iota
|
// Answer is the answer section in an Msg.
|
||||||
|
Answer sect = iota
|
||||||
|
// Ns is the authrotitative section in an Msg.
|
||||||
Ns
|
Ns
|
||||||
|
// Extra is the additional section in an Msg.
|
||||||
Extra
|
Extra
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// RRSet represents a list of RRs.
|
||||||
type RRSet []dns.RR
|
type RRSet []dns.RR
|
||||||
|
|
||||||
func (p RRSet) Len() int { return len(p) }
|
func (p RRSet) Len() int { return len(p) }
|
||||||
func (p RRSet) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
func (p RRSet) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||||
func (p RRSet) Less(i, j int) bool { return p[i].String() < p[j].String() }
|
func (p RRSet) Less(i, j int) bool { return p[i].String() < p[j].String() }
|
||||||
|
|
||||||
// If the TTL of a record is 303 we don't care what the TTL is.
|
// Case represents a test case that encapsulates various data from a query and response.
|
||||||
|
// Note that is the TTL of a record is 303 we don't compare it with the TTL.
|
||||||
type Case struct {
|
type Case struct {
|
||||||
Qname string
|
Qname string
|
||||||
Qtype uint16
|
Qtype uint16
|
||||||
@@ -32,6 +37,7 @@ type Case struct {
|
|||||||
Extra []dns.RR
|
Extra []dns.RR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Msg returns a *dns.Msg embedded in c.
|
||||||
func (c Case) Msg() *dns.Msg {
|
func (c Case) Msg() *dns.Msg {
|
||||||
m := new(dns.Msg)
|
m := new(dns.Msg)
|
||||||
m.SetQuestion(dns.Fqdn(c.Qname), c.Qtype)
|
m.SetQuestion(dns.Fqdn(c.Qname), c.Qtype)
|
||||||
@@ -46,19 +52,43 @@ func (c Case) Msg() *dns.Msg {
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func A(rr string) *dns.A { r, _ := dns.NewRR(rr); return r.(*dns.A) }
|
// A returns an A record from rr. It panics on errors.
|
||||||
func AAAA(rr string) *dns.AAAA { r, _ := dns.NewRR(rr); return r.(*dns.AAAA) }
|
func A(rr string) *dns.A { r, _ := dns.NewRR(rr); return r.(*dns.A) }
|
||||||
func CNAME(rr string) *dns.CNAME { r, _ := dns.NewRR(rr); return r.(*dns.CNAME) }
|
|
||||||
func SRV(rr string) *dns.SRV { r, _ := dns.NewRR(rr); return r.(*dns.SRV) }
|
// AAAA returns an AAAA record from rr. It panics on errors.
|
||||||
func SOA(rr string) *dns.SOA { r, _ := dns.NewRR(rr); return r.(*dns.SOA) }
|
func AAAA(rr string) *dns.AAAA { r, _ := dns.NewRR(rr); return r.(*dns.AAAA) }
|
||||||
func NS(rr string) *dns.NS { r, _ := dns.NewRR(rr); return r.(*dns.NS) }
|
|
||||||
func PTR(rr string) *dns.PTR { r, _ := dns.NewRR(rr); return r.(*dns.PTR) }
|
// CNAME returns a CNAME record from rr. It panics on errors.
|
||||||
func TXT(rr string) *dns.TXT { r, _ := dns.NewRR(rr); return r.(*dns.TXT) }
|
func CNAME(rr string) *dns.CNAME { r, _ := dns.NewRR(rr); return r.(*dns.CNAME) }
|
||||||
func MX(rr string) *dns.MX { r, _ := dns.NewRR(rr); return r.(*dns.MX) }
|
|
||||||
func RRSIG(rr string) *dns.RRSIG { r, _ := dns.NewRR(rr); return r.(*dns.RRSIG) }
|
// SRV returns a SRV record from rr. It panics on errors.
|
||||||
func NSEC(rr string) *dns.NSEC { r, _ := dns.NewRR(rr); return r.(*dns.NSEC) }
|
func SRV(rr string) *dns.SRV { r, _ := dns.NewRR(rr); return r.(*dns.SRV) }
|
||||||
|
|
||||||
|
// SOA returns a SOA record from rr. It panics on errors.
|
||||||
|
func SOA(rr string) *dns.SOA { r, _ := dns.NewRR(rr); return r.(*dns.SOA) }
|
||||||
|
|
||||||
|
// NS returns an NS record from rr. It panics on errors.
|
||||||
|
func NS(rr string) *dns.NS { r, _ := dns.NewRR(rr); return r.(*dns.NS) }
|
||||||
|
|
||||||
|
// PTR returns a PTR record from rr. It panics on errors.
|
||||||
|
func PTR(rr string) *dns.PTR { r, _ := dns.NewRR(rr); return r.(*dns.PTR) }
|
||||||
|
|
||||||
|
// TXT returns a TXT record from rr. It panics on errors.
|
||||||
|
func TXT(rr string) *dns.TXT { r, _ := dns.NewRR(rr); return r.(*dns.TXT) }
|
||||||
|
|
||||||
|
// MX returns an MX record from rr. It panics on errors.
|
||||||
|
func MX(rr string) *dns.MX { r, _ := dns.NewRR(rr); return r.(*dns.MX) }
|
||||||
|
|
||||||
|
// RRSIG returns an RRSIG record from rr. It panics on errors.
|
||||||
|
func RRSIG(rr string) *dns.RRSIG { r, _ := dns.NewRR(rr); return r.(*dns.RRSIG) }
|
||||||
|
|
||||||
|
// NSEC returns an NSEC record from rr. It panics on errors.
|
||||||
|
func NSEC(rr string) *dns.NSEC { r, _ := dns.NewRR(rr); return r.(*dns.NSEC) }
|
||||||
|
|
||||||
|
// DNSKEY returns a DNSKEY record from rr. It panics on errors.
|
||||||
func DNSKEY(rr string) *dns.DNSKEY { r, _ := dns.NewRR(rr); return r.(*dns.DNSKEY) }
|
func DNSKEY(rr string) *dns.DNSKEY { r, _ := dns.NewRR(rr); return r.(*dns.DNSKEY) }
|
||||||
|
|
||||||
|
// OPT returns an OPT record with UDP buffer size set to bufsize and the DO bit set to do.
|
||||||
func OPT(bufsize int, do bool) *dns.OPT {
|
func OPT(bufsize int, do bool) *dns.OPT {
|
||||||
o := new(dns.OPT)
|
o := new(dns.OPT)
|
||||||
o.Hdr.Name = "."
|
o.Hdr.Name = "."
|
||||||
@@ -71,6 +101,7 @@ func OPT(bufsize int, do bool) *dns.OPT {
|
|||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Header test if the header in resp matches the header as defined in tc.
|
||||||
func Header(t *testing.T, tc Case, resp *dns.Msg) bool {
|
func Header(t *testing.T, tc Case, resp *dns.Msg) bool {
|
||||||
if resp.Rcode != tc.Rcode {
|
if resp.Rcode != tc.Rcode {
|
||||||
t.Errorf("rcode is %q, expected %q", dns.RcodeToString[resp.Rcode], dns.RcodeToString[tc.Rcode])
|
t.Errorf("rcode is %q, expected %q", dns.RcodeToString[resp.Rcode], dns.RcodeToString[tc.Rcode])
|
||||||
@@ -92,9 +123,10 @@ func Header(t *testing.T, tc Case, resp *dns.Msg) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func Section(t *testing.T, tc Case, sect Sect, rr []dns.RR) bool {
|
// Section tests if the the section in tc matches rr.
|
||||||
|
func Section(t *testing.T, tc Case, sec sect, rr []dns.RR) bool {
|
||||||
section := []dns.RR{}
|
section := []dns.RR{}
|
||||||
switch sect {
|
switch sec {
|
||||||
case 0:
|
case 0:
|
||||||
section = tc.Answer
|
section = tc.Answer
|
||||||
case 1:
|
case 1:
|
||||||
@@ -224,7 +256,7 @@ func Section(t *testing.T, tc Case, sect Sect, rr []dns.RR) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorHanlder returns a Handler that returns ServerFailure error when called.
|
// ErrorHandler returns a Handler that returns ServerFailure error when called.
|
||||||
func ErrorHandler() Handler {
|
func ErrorHandler() Handler {
|
||||||
return HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
return HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||||
m := new(dns.Msg)
|
m := new(dns.Msg)
|
||||||
@@ -242,11 +274,13 @@ func NextHandler(rcode int, err error) Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Copied here to prevent an import cycle, so that we can define to above handlers.
|
// Copied here to prevent an import cycle, so that we can define to above handlers.
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// HandlerFunc is a convenience type like dns.HandlerFunc, except
|
// HandlerFunc is a convenience type like dns.HandlerFunc, except
|
||||||
// ServeDNS returns an rcode and an error.
|
// ServeDNS returns an rcode and an error.
|
||||||
HandlerFunc func(context.Context, dns.ResponseWriter, *dns.Msg) (int, error)
|
HandlerFunc func(context.Context, dns.ResponseWriter, *dns.Msg) (int, error)
|
||||||
|
|
||||||
|
// Handler interface defines a middleware.
|
||||||
Handler interface {
|
Handler interface {
|
||||||
ServeDNS(context.Context, dns.ResponseWriter, *dns.Msg) (int, error)
|
ServeDNS(context.Context, dns.ResponseWriter, *dns.Msg) (int, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,9 +25,20 @@ func (t *ResponseWriter) RemoteAddr() net.Addr {
|
|||||||
return &net.UDPAddr{IP: ip, Port: port, Zone: ""}
|
return &net.UDPAddr{IP: ip, Port: port, Zone: ""}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *ResponseWriter) WriteMsg(m *dns.Msg) error { return nil }
|
// WriteMsg implement dns.ResponseWriter interface.
|
||||||
|
func (t *ResponseWriter) WriteMsg(m *dns.Msg) error { return nil }
|
||||||
|
|
||||||
|
// Write implement dns.ResponseWriter interface.
|
||||||
func (t *ResponseWriter) Write(buf []byte) (int, error) { return len(buf), nil }
|
func (t *ResponseWriter) Write(buf []byte) (int, error) { return len(buf), nil }
|
||||||
func (t *ResponseWriter) Close() error { return nil }
|
|
||||||
func (t *ResponseWriter) TsigStatus() error { return nil }
|
// Close implement dns.ResponseWriter interface.
|
||||||
func (t *ResponseWriter) TsigTimersOnly(bool) { return }
|
func (t *ResponseWriter) Close() error { return nil }
|
||||||
func (t *ResponseWriter) Hijack() { return }
|
|
||||||
|
// TsigStatus implement dns.ResponseWriter interface.
|
||||||
|
func (t *ResponseWriter) TsigStatus() error { return nil }
|
||||||
|
|
||||||
|
// TsigTimersOnly implement dns.ResponseWriter interface.
|
||||||
|
func (t *ResponseWriter) TsigTimersOnly(bool) { return }
|
||||||
|
|
||||||
|
// Hijack implement dns.ResponseWriter interface.
|
||||||
|
func (t *ResponseWriter) Hijack() { return }
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TCPServer starts a DNS server with a TCP listener on laddr.
|
||||||
func TCPServer(t *testing.T, laddr string) (*dns.Server, string, error) {
|
func TCPServer(t *testing.T, laddr string) (*dns.Server, string, error) {
|
||||||
l, err := net.Listen("tcp", laddr)
|
l, err := net.Listen("tcp", laddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -30,6 +31,7 @@ func TCPServer(t *testing.T, laddr string) (*dns.Server, string, error) {
|
|||||||
return server, l.Addr().String(), nil
|
return server, l.Addr().String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UDPServer starts a DNS server with an UDP listener on laddr.
|
||||||
func UDPServer(t *testing.T, laddr string) (*dns.Server, string, error) {
|
func UDPServer(t *testing.T, laddr string) (*dns.Server, string, error) {
|
||||||
pc, err := net.ListenPacket("udp", laddr)
|
pc, err := net.ListenPacket("udp", laddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -11,10 +11,13 @@ import (
|
|||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Whoami is a middleware that returns your IP address, port and the protocol used for connecting
|
||||||
|
// to CoreDNS.
|
||||||
type Whoami struct {
|
type Whoami struct {
|
||||||
Next middleware.Handler
|
Next middleware.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServeDNS implements the middleware.Handler interface.
|
||||||
func (wh Whoami) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
func (wh Whoami) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||||
state := request.Request{W: w, Req: r}
|
state := request.Request{W: w, Req: r}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user