Commit a951fee2 authored by Miek Gieben's avatar Miek Gieben

merge conflict fixed

parents 4a8db8a4 8dd47dd9
...@@ -13,6 +13,8 @@ zonefile. New zones or changed zone are automatically picked up from disk. ...@@ -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. ...@@ -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:
......
...@@ -9,6 +9,7 @@ import ( ...@@ -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 ( ...@@ -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 ...@@ -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)
......
...@@ -2,6 +2,7 @@ package auto ...@@ -2,6 +2,7 @@ package auto
import ( import (
"log" "log"
"net"
"os" "os"
"path" "path"
"regexp" "regexp"
...@@ -12,6 +13,7 @@ import ( ...@@ -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) { ...@@ -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 {
......
...@@ -39,6 +39,7 @@ func TestAutoParse(t *testing.T) { ...@@ -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"},
}, },
......
...@@ -52,6 +52,7 @@ func (a Auto) Walk() error { ...@@ -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)
......
...@@ -9,7 +9,6 @@ import ( ...@@ -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 ( ...@@ -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"
) )
......
...@@ -27,6 +27,7 @@ TSIG key information, something like `transfer out [ADDRESS...] key [NAME[:ALG]] ...@@ -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... ] { ...@@ -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
......
...@@ -6,6 +6,7 @@ import ( ...@@ -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{ ...@@ -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{ ...@@ -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 ...@@ -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.`
...@@ -84,7 +84,7 @@ func (f File) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i ...@@ -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)
......
...@@ -2,6 +2,7 @@ package 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 ( ...@@ -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, ...@@ -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, ...@@ -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 { ...@@ -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) ...@@ -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: ...@@ -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 { ...@@ -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{}
......
...@@ -8,6 +8,7 @@ import ( ...@@ -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) { ...@@ -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)
} }
......
...@@ -2,12 +2,14 @@ package 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) { ...@@ -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) { ...@@ -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) { ...@@ -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
} }
} }
} }
......
...@@ -9,6 +9,7 @@ import ( ...@@ -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 { ...@@ -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.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment