merge conflict fixed

This commit is contained in:
Miek Gieben
2016-11-24 21:51:38 +01:00
13 changed files with 158 additions and 13 deletions

View File

@@ -13,6 +13,8 @@ zonefile. New zones or changed zone are automatically picked up from disk.
~~~ ~~~
auto [ZONES...] { auto [ZONES...] {
directory DIR [REGEXP ORIGIN_TEMPLATE [TIMEOUT]] directory DIR [REGEXP ORIGIN_TEMPLATE [TIMEOUT]]
no_reload
upstream ADDRESS...
} }
~~~ ~~~
@@ -26,6 +28,10 @@ are used.
name `db.example.com`, the extracted origin will be `example.com`. **TIMEOUT** specifies how often name `db.example.com`, the extracted origin will be `example.com`. **TIMEOUT** specifies how often
CoreDNS should scan the directory, the default is every 60 seconds. This value is in seconds. CoreDNS should scan the directory, the default is every 60 seconds. This value is in seconds.
The minimum value is 1 second. The minimum value is 1 second.
* `no_reload` by default CoreDNS will reload a zone from disk whenever it detects a change to the
file. This option disables that behavior.
* `upstream` defines upstream resolvers to be used resolve external names found (think CNAMEs)
pointing to external names.
All directives from the *file* middleware are supported. Note that *auto* will load all zones found, All directives from the *file* middleware are supported. Note that *auto* will load all zones found,
even though the directive might only receive queries for a specific zone. I.e: even though the directive might only receive queries for a specific zone. I.e:

View File

@@ -9,6 +9,7 @@ import (
"github.com/miekg/coredns/middleware" "github.com/miekg/coredns/middleware"
"github.com/miekg/coredns/middleware/file" "github.com/miekg/coredns/middleware/file"
"github.com/miekg/coredns/middleware/metrics" "github.com/miekg/coredns/middleware/metrics"
"github.com/miekg/coredns/middleware/proxy"
"github.com/miekg/coredns/request" "github.com/miekg/coredns/request"
"github.com/miekg/dns" "github.com/miekg/dns"
@@ -33,6 +34,7 @@ type (
// In the future this should be something like ZoneMeta that contains all this stuff. // In the future this should be something like ZoneMeta that contains all this stuff.
transferTo []string transferTo []string
noReload bool noReload bool
proxy proxy.Proxy // Proxy for looking up names during the resolution process
duration time.Duration duration time.Duration
} }
@@ -73,7 +75,7 @@ func (a Auto) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i
return xfr.ServeDNS(ctx, w, r) return xfr.ServeDNS(ctx, w, r)
} }
answer, ns, extra, result := z.Lookup(qname, state.QType(), state.Do()) answer, ns, extra, result := z.Lookup(state, qname)
m := new(dns.Msg) m := new(dns.Msg)
m.SetReply(r) m.SetReply(r)

View File

@@ -2,6 +2,7 @@ package auto
import ( import (
"log" "log"
"net"
"os" "os"
"path" "path"
"regexp" "regexp"
@@ -12,6 +13,7 @@ import (
"github.com/miekg/coredns/middleware" "github.com/miekg/coredns/middleware"
"github.com/miekg/coredns/middleware/file" "github.com/miekg/coredns/middleware/file"
"github.com/miekg/coredns/middleware/metrics" "github.com/miekg/coredns/middleware/metrics"
"github.com/miekg/coredns/middleware/proxy"
"github.com/mholt/caddy" "github.com/mholt/caddy"
) )
@@ -142,6 +144,19 @@ func autoParse(c *caddy.Controller) (Auto, error) {
case "no_reload": case "no_reload":
a.loader.noReload = true a.loader.noReload = true
case "upstream":
args := c.RemainingArgs()
if len(args) == 0 {
return a, c.ArgErr()
}
for i := 0; i < len(args); i++ {
h, p, e := net.SplitHostPort(args[i])
if e != nil && p == "" {
args[i] = h + ":53"
}
}
a.loader.proxy = proxy.New(args)
default: default:
t, _, e := file.TransferParse(c, false) t, _, e := file.TransferParse(c, false)
if e != nil { if e != nil {

View File

@@ -39,6 +39,7 @@ func TestAutoParse(t *testing.T) {
directory /tmp (.*) bliep directory /tmp (.*) bliep
transfer to 127.0.0.1 transfer to 127.0.0.1
transfer to 127.0.0.2 transfer to 127.0.0.2
upstream 8.8.8.8
}`, }`,
false, "/tmp", "bliep", `(.*)`, []string{"127.0.0.1:53", "127.0.0.2:53"}, false, "/tmp", "bliep", `(.*)`, []string{"127.0.0.1:53", "127.0.0.2:53"},
}, },

View File

@@ -52,6 +52,7 @@ func (a Auto) Walk() error {
} }
zo.NoReload = a.loader.noReload zo.NoReload = a.loader.noReload
zo.Proxy = a.loader.proxy
zo.TransferTo = a.loader.transferTo zo.TransferTo = a.loader.transferTo
a.Zones.Add(zo, origin) a.Zones.Add(zo, origin)

View File

@@ -9,7 +9,6 @@ import (
"testing" "testing"
"time" "time"
"github.com/mholt/caddy"
"github.com/miekg/coredns/middleware/etcd/msg" "github.com/miekg/coredns/middleware/etcd/msg"
"github.com/miekg/coredns/middleware/pkg/dnsrecorder" "github.com/miekg/coredns/middleware/pkg/dnsrecorder"
"github.com/miekg/coredns/middleware/pkg/singleflight" "github.com/miekg/coredns/middleware/pkg/singleflight"
@@ -17,6 +16,7 @@ import (
"github.com/miekg/coredns/middleware/test" "github.com/miekg/coredns/middleware/test"
etcdc "github.com/coreos/etcd/client" etcdc "github.com/coreos/etcd/client"
"github.com/mholt/caddy"
"github.com/miekg/dns" "github.com/miekg/dns"
"golang.org/x/net/context" "golang.org/x/net/context"
) )

View File

@@ -27,6 +27,7 @@ TSIG key information, something like `transfer out [ADDRESS...] key [NAME[:ALG]]
file DBFILE [ZONES... ] { file DBFILE [ZONES... ] {
transfer to ADDRESS... transfer to ADDRESS...
no_reload no_reload
upstream ADDRESS...
} }
~~~ ~~~
@@ -36,6 +37,8 @@ file DBFILE [ZONES... ] {
When an address is specified a notify message will be send whenever the zone is reloaded. When an address is specified a notify message will be send whenever the zone is reloaded.
* `no_reload` by default CoreDNS will reload a zone from disk whenever it detects a change to the * `no_reload` by default CoreDNS will reload a zone from disk whenever it detects a change to the
file. This option disables that behavior. file. This option disables that behavior.
* `upstream` defines upstream resolvers to be used resolve external names found (think CNAMEs)
pointing to external names.
## Examples ## Examples

View File

@@ -6,6 +6,7 @@ import (
"testing" "testing"
"github.com/miekg/coredns/middleware/pkg/dnsrecorder" "github.com/miekg/coredns/middleware/pkg/dnsrecorder"
"github.com/miekg/coredns/middleware/proxy"
"github.com/miekg/coredns/middleware/test" "github.com/miekg/coredns/middleware/test"
"github.com/miekg/dns" "github.com/miekg/dns"
@@ -68,6 +69,12 @@ var cnameTestCases = []test.Case{
test.CNAME("www3.example.org. 1800 IN CNAME www2.example.org."), test.CNAME("www3.example.org. 1800 IN CNAME www2.example.org."),
}, },
}, },
{
Qname: "dangling.example.org.", Qtype: dns.TypeA,
Answer: []dns.RR{
test.CNAME("dangling.example.org. 1800 IN CNAME foo.example.org."),
},
},
{ {
Qname: "www3.example.org.", Qtype: dns.TypeA, Qname: "www3.example.org.", Qtype: dns.TypeA,
Answer: []dns.RR{ Answer: []dns.RR{
@@ -80,6 +87,61 @@ var cnameTestCases = []test.Case{
}, },
} }
func TestLookupCNAMEExternal(t *testing.T) {
name := "example.org."
zone, err := Parse(strings.NewReader(dbExampleCNAME), name, "stdin")
if err != nil {
t.Fatalf("Expected no error when reading zone, got %q", err)
}
zone.Proxy = proxy.New([]string{"8.8.8.8:53"}) // TODO(miek): point to local instance
fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{name: zone}, Names: []string{name}}}
ctx := context.TODO()
for _, tc := range exernalTestCases {
m := tc.Msg()
rec := dnsrecorder.New(&test.ResponseWriter{})
_, err := fm.ServeDNS(ctx, rec, m)
if err != nil {
t.Errorf("Expected no error, got %v\n", err)
return
}
resp := rec.Msg
sort.Sort(test.RRSet(resp.Answer))
sort.Sort(test.RRSet(resp.Ns))
sort.Sort(test.RRSet(resp.Extra))
if !test.Header(t, tc, resp) {
t.Logf("%v\n", resp)
continue
}
if !test.Section(t, tc, test.Answer, resp.Answer) {
t.Logf("%v\n", resp)
}
if !test.Section(t, tc, test.Ns, resp.Ns) {
t.Logf("%v\n", resp)
}
if !test.Section(t, tc, test.Extra, resp.Extra) {
t.Logf("%v\n", resp)
}
}
}
var exernalTestCases = []test.Case{
{
Qname: "external.example.org.", Qtype: dns.TypeA,
Answer: []dns.RR{
test.CNAME("external.example.org. 1800 CNAME www.example.net."),
// magic 303 TTL that says: don't check TTL.
test.A("www.example.net. 303 IN A 93.184.216.34"),
},
},
}
const dbExampleCNAME = ` const dbExampleCNAME = `
$TTL 30M $TTL 30M
$ORIGIN example.org. $ORIGIN example.org.
@@ -95,4 +157,5 @@ www3 IN CNAME www2
www2 IN CNAME www1 www2 IN CNAME www1
www1 IN CNAME www www1 IN CNAME www
www IN CNAME a www IN CNAME a
dangling IN CNAME foo` dangling IN CNAME foo
external IN CNAME www.example.net.`

View File

@@ -84,7 +84,7 @@ func (f File) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i
return xfr.ServeDNS(ctx, w, r) return xfr.ServeDNS(ctx, w, r)
} }
answer, ns, extra, result := z.Lookup(qname, state.QType(), state.Do()) answer, ns, extra, result := z.Lookup(state, qname)
m := new(dns.Msg) m := new(dns.Msg)
m.SetReply(r) m.SetReply(r)

View File

@@ -2,6 +2,7 @@ package file
import ( import (
"github.com/miekg/coredns/middleware/file/tree" "github.com/miekg/coredns/middleware/file/tree"
"github.com/miekg/coredns/request"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
@@ -24,7 +25,11 @@ const (
// Lookup looks up qname and qtype in the zone. When do is true DNSSEC records are included. // Lookup looks up qname and qtype in the zone. When do is true DNSSEC records are included.
// Three sets of records are returned, one for the answer, one for authority and one for the additional section. // Three sets of records are returned, one for the answer, one for authority and one for the additional section.
func (z *Zone) Lookup(qname string, qtype uint16, do bool) ([]dns.RR, []dns.RR, []dns.RR, Result) { func (z *Zone) Lookup(state request.Request, qname string) ([]dns.RR, []dns.RR, []dns.RR, Result) {
qtype := state.QType()
do := state.Do()
if !z.NoReload { if !z.NoReload {
z.reloadMu.RLock() z.reloadMu.RLock()
} }
@@ -118,9 +123,9 @@ func (z *Zone) Lookup(qname string, qtype uint16, do bool) ([]dns.RR, []dns.RR,
// Found entire name. // Found entire name.
if found && shot { if found && shot {
// DNAME... // DNAME...?
if rrs := elem.Types(dns.TypeCNAME); len(rrs) > 0 && qtype != dns.TypeCNAME { if rrs := elem.Types(dns.TypeCNAME); len(rrs) > 0 && qtype != dns.TypeCNAME {
return z.searchCNAME(elem, rrs, qtype, do) return z.searchCNAME(state, elem, rrs)
} }
rrs := elem.Types(qtype, qname) rrs := elem.Types(qtype, qname)
@@ -152,7 +157,7 @@ func (z *Zone) Lookup(qname string, qtype uint16, do bool) ([]dns.RR, []dns.RR,
auth := []dns.RR{} auth := []dns.RR{}
if rrs := wildElem.Types(dns.TypeCNAME, qname); len(rrs) > 0 { if rrs := wildElem.Types(dns.TypeCNAME, qname); len(rrs) > 0 {
return z.searchCNAME(wildElem, rrs, qtype, do) return z.searchCNAME(state, wildElem, rrs)
} }
rrs := wildElem.Types(qtype, qname) rrs := wildElem.Types(qtype, qname)
@@ -251,7 +256,11 @@ func (z *Zone) ns(do bool) []dns.RR {
return z.Apex.NS return z.Apex.NS
} }
func (z *Zone) searchCNAME(elem *tree.Elem, rrs []dns.RR, qtype uint16, do bool) ([]dns.RR, []dns.RR, []dns.RR, Result) { func (z *Zone) searchCNAME(state request.Request, elem *tree.Elem, rrs []dns.RR) ([]dns.RR, []dns.RR, []dns.RR, Result) {
qtype := state.QType()
do := state.Do()
if do { if do {
sigs := elem.Types(dns.TypeRRSIG) sigs := elem.Types(dns.TypeRRSIG)
sigs = signatureForSubType(sigs, dns.TypeCNAME) sigs = signatureForSubType(sigs, dns.TypeCNAME)
@@ -260,8 +269,12 @@ func (z *Zone) searchCNAME(elem *tree.Elem, rrs []dns.RR, qtype uint16, do bool)
} }
} }
elem, _ = z.Tree.Search(rrs[0].(*dns.CNAME).Target) targetName := rrs[0].(*dns.CNAME).Target
elem, _ = z.Tree.Search(targetName)
if elem == nil { if elem == nil {
if !dns.IsSubDomain(z.origin, targetName) {
rrs = append(rrs, z.externalLookup(state, targetName, qtype)...)
}
return rrs, nil, nil, Success return rrs, nil, nil, Success
} }
@@ -279,8 +292,14 @@ Redo:
rrs = append(rrs, sigs...) rrs = append(rrs, sigs...)
} }
} }
elem, _ = z.Tree.Search(cname[0].(*dns.CNAME).Target) targetName := cname[0].(*dns.CNAME).Target
elem, _ = z.Tree.Search(targetName)
if elem == nil { if elem == nil {
if !dns.IsSubDomain(z.origin, targetName) {
if !dns.IsSubDomain(z.origin, targetName) {
rrs = append(rrs, z.externalLookup(state, targetName, qtype)...)
}
}
return rrs, nil, nil, Success return rrs, nil, nil, Success
} }
@@ -318,6 +337,15 @@ func cnameForType(targets []dns.RR, origQtype uint16) []dns.RR {
return ret return ret
} }
func (z *Zone) externalLookup(state request.Request, target string, qtype uint16) []dns.RR {
m, e := z.Proxy.Lookup(state, target, qtype)
if e != nil {
// TODO(miek): debugMsg for this as well? Log?
return nil
}
return m.Answer
}
// signatureForSubType range through the signature and return the correct ones for the subtype. // signatureForSubType range through the signature and return the correct ones for the subtype.
func signatureForSubType(rrs []dns.RR, subtype uint16) []dns.RR { func signatureForSubType(rrs []dns.RR, subtype uint16) []dns.RR {
sigs := []dns.RR{} sigs := []dns.RR{}

View File

@@ -8,6 +8,7 @@ import (
"time" "time"
"github.com/miekg/coredns/middleware/test" "github.com/miekg/coredns/middleware/test"
"github.com/miekg/coredns/request"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
@@ -31,11 +32,17 @@ func TestZoneReload(t *testing.T) {
z.Reload() z.Reload()
if _, _, _, res := z.Lookup("miek.nl.", dns.TypeSOA, false); res != Success { r := new(dns.Msg)
r.SetQuestion("miek.nl", dns.TypeSOA)
state := request.Request{W: &test.ResponseWriter{}, Req: r}
if _, _, _, res := z.Lookup(state, "miek.nl."); res != Success {
t.Fatalf("failed to lookup, got %d", res) t.Fatalf("failed to lookup, got %d", res)
} }
if _, _, _, res := z.Lookup("miek.nl.", dns.TypeNS, false); res != Success { r = new(dns.Msg)
r.SetQuestion("miek.nl", dns.TypeNS)
state = request.Request{W: &test.ResponseWriter{}, Req: r}
if _, _, _, res := z.Lookup(state, "miek.nl."); res != Success {
t.Fatalf("failed to lookup, got %d", res) t.Fatalf("failed to lookup, got %d", res)
} }

View File

@@ -2,12 +2,14 @@ package file
import ( import (
"fmt" "fmt"
"net"
"os" "os"
"path" "path"
"github.com/miekg/coredns/core/dnsserver" "github.com/miekg/coredns/core/dnsserver"
"github.com/miekg/coredns/middleware" "github.com/miekg/coredns/middleware"
"github.com/miekg/coredns/middleware/pkg/dnsutil" "github.com/miekg/coredns/middleware/pkg/dnsutil"
"github.com/miekg/coredns/middleware/proxy"
"github.com/mholt/caddy" "github.com/mholt/caddy"
) )
@@ -90,6 +92,7 @@ func fileParse(c *caddy.Controller) (Zones, error) {
} }
noReload := false noReload := false
prxy := proxy.Proxy{}
for c.NextBlock() { for c.NextBlock() {
t, _, e := TransferParse(c, false) t, _, e := TransferParse(c, false)
if e != nil { if e != nil {
@@ -98,6 +101,19 @@ func fileParse(c *caddy.Controller) (Zones, error) {
switch c.Val() { switch c.Val() {
case "no_reload": case "no_reload":
noReload = true noReload = true
case "upstream":
args := c.RemainingArgs()
if len(args) == 0 {
return Zones{}, c.ArgErr()
}
for i := 0; i < len(args); i++ {
h, p, e := net.SplitHostPort(args[i])
if e != nil && p == "" {
args[i] = h + ":53"
}
}
prxy = proxy.New(args)
} }
for _, origin := range origins { for _, origin := range origins {
@@ -105,6 +121,7 @@ func fileParse(c *caddy.Controller) (Zones, error) {
z[origin].TransferTo = append(z[origin].TransferTo, t...) z[origin].TransferTo = append(z[origin].TransferTo, t...)
} }
z[origin].NoReload = noReload z[origin].NoReload = noReload
z[origin].Proxy = prxy
} }
} }
} }

View File

@@ -9,6 +9,7 @@ import (
"sync" "sync"
"github.com/miekg/coredns/middleware/file/tree" "github.com/miekg/coredns/middleware/file/tree"
"github.com/miekg/coredns/middleware/proxy"
"github.com/miekg/coredns/request" "github.com/miekg/coredns/request"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
@@ -31,6 +32,7 @@ type Zone struct {
NoReload bool NoReload bool
reloadMu sync.RWMutex reloadMu sync.RWMutex
ReloadShutdown chan bool ReloadShutdown chan bool
Proxy proxy.Proxy // Proxy for looking up names during the resolution process
} }
// Apex contains the apex records of a zone: SOA, NS and their potential signatures. // Apex contains the apex records of a zone: SOA, NS and their potential signatures.