diff options
-rw-r--r-- | middleware/file/README.md | 3 | ||||
-rw-r--r-- | middleware/file/notify.go | 11 | ||||
-rw-r--r-- | middleware/file/secondary.go | 7 | ||||
-rw-r--r-- | middleware/file/xfr.go | 2 | ||||
-rw-r--r-- | middleware/file/zone.go | 14 | ||||
-rw-r--r-- | middleware/secondary/README.md | 6 | ||||
-rw-r--r-- | middleware/secondary/setup.go | 14 | ||||
-rw-r--r-- | test/secondary_net_test.go | 126 |
8 files changed, 171 insertions, 12 deletions
diff --git a/middleware/file/README.md b/middleware/file/README.md index 283986945..30391ed47 100644 --- a/middleware/file/README.md +++ b/middleware/file/README.md @@ -38,7 +38,8 @@ file DBFILE [ZONES... ] { * `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. + pointing to external names. This is only really useful when CoreDNS is configured as a proxy, for + normal authoritative serving you don't need *or* want to use this. ## Examples diff --git a/middleware/file/notify.go b/middleware/file/notify.go index 90dfdc521..e8f518d25 100644 --- a/middleware/file/notify.go +++ b/middleware/file/notify.go @@ -3,8 +3,8 @@ package file import ( "fmt" "log" + "net" - "github.com/coredns/coredns/middleware" "github.com/coredns/coredns/middleware/pkg/rcode" "github.com/coredns/coredns/request" @@ -21,8 +21,13 @@ func (z *Zone) isNotify(state request.Request) bool { if len(z.TransferFrom) == 0 { return false } - remote := middleware.Addr(state.IP()).Normalize() - for _, from := range z.TransferFrom { + // If remote IP matches we accept. + remote := state.IP() + for _, f := range z.TransferFrom { + from, _, err := net.SplitHostPort(f) + if err != nil { + continue + } if from == remote { return true } diff --git a/middleware/file/secondary.go b/middleware/file/secondary.go index 70ec217f7..e371600e9 100644 --- a/middleware/file/secondary.go +++ b/middleware/file/secondary.go @@ -26,19 +26,19 @@ Transfer: t := new(dns.Transfer) c, err := t.In(m, tr) if err != nil { - log.Printf("[ERROR] Failed to setup transfer `%s' with `%s': %v", z.origin, tr, err) + log.Printf("[ERROR] Failed to setup transfer `%s' with `%q': %v", z.origin, tr, err) Err = err continue Transfer } for env := range c { if env.Error != nil { - log.Printf("[ERROR] Failed to parse transfer `%s': %v", z.origin, env.Error) + log.Printf("[ERROR] Failed to transfer `%s' from %q: %v", z.origin, tr, env.Error) Err = env.Error continue Transfer } for _, rr := range env.RR { if err := z1.Insert(rr); err != nil { - log.Printf("[ERROR] Failed to parse transfer `%s': %v", z.origin, err) + log.Printf("[ERROR] Failed to parse transfer `%s' from: %q: %v", z.origin, tr, err) Err = err continue Transfer } @@ -48,7 +48,6 @@ Transfer: break } if Err != nil { - log.Printf("[ERROR] Failed to transfer %s: %s", z.origin, Err) return Err } diff --git a/middleware/file/xfr.go b/middleware/file/xfr.go index 4d7f07a48..54f7b71f8 100644 --- a/middleware/file/xfr.go +++ b/middleware/file/xfr.go @@ -57,6 +57,6 @@ func (x Xfr) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (in } // Name implements the middleware.Hander interface. -func (x Xfr) Name() string { return "xfr" } // Or should we return "file" here? +func (x Xfr) Name() string { return "xfr" } const transferLength = 1000 // Start a new envelop after message reaches this size in bytes. Intentionally small to test multi envelope parsing. diff --git a/middleware/file/zone.go b/middleware/file/zone.go index 7592798f0..a216f8af8 100644 --- a/middleware/file/zone.go +++ b/middleware/file/zone.go @@ -2,6 +2,7 @@ package file import ( "fmt" + "net" "path" "strings" "sync" @@ -55,12 +56,12 @@ func NewZone(name, file string) *Zone { return z } -// Copy copies a zone *without* copying the zone's content. It is not a deep copy. func (z *Zone) Copy() *Zone { z1 := NewZone(z.origin, z.file) z1.TransferTo = z.TransferTo z1.TransferFrom = z.TransferFrom z1.Expired = z.Expired + z1.Apex = z.Apex return z1 } @@ -113,11 +114,20 @@ func (z *Zone) Insert(r dns.RR) error { func (z *Zone) Delete(r dns.RR) { z.Tree.Delete(r) } // TransferAllowed checks if incoming request for transferring the zone is allowed according to the ACLs. -func (z *Zone) TransferAllowed(req request.Request) bool { +func (z *Zone) TransferAllowed(state request.Request) bool { for _, t := range z.TransferTo { if t == "*" { return true } + // If remote IP matches we accept. + remote := state.IP() + to, _, err := net.SplitHostPort(t) + if err != nil { + continue + } + if to == remote { + return true + } } // TODO(miek): future matching against IP/CIDR notations return false diff --git a/middleware/secondary/README.md b/middleware/secondary/README.md index 559a06717..e37555211 100644 --- a/middleware/secondary/README.md +++ b/middleware/secondary/README.md @@ -16,13 +16,17 @@ A working syntax would be: ~~~ secondary [zones...] { transfer from ADDRESS - [transfer to ADDRESS] + transfer to ADDRESS + upstream ADDRESS... } ~~~ * `transfer from` specifies from which address to fetch the zone. It can be specified multiple times; if one does not work, another will be tried. * `transfer to` can be enabled to allow this secondary zone to be transferred again. +* `upstream` defines upstream resolvers to be used resolve external names found (think CNAMEs) + pointing to external names. This is only really useful when CoreDNS is configured as a proxy, for + normal authoritative serving you don't need *or* want to use this. ## Examples diff --git a/middleware/secondary/setup.go b/middleware/secondary/setup.go index 8a40a6710..901e76699 100644 --- a/middleware/secondary/setup.go +++ b/middleware/secondary/setup.go @@ -4,6 +4,8 @@ import ( "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/middleware" "github.com/coredns/coredns/middleware/file" + "github.com/coredns/coredns/middleware/pkg/dnsutil" + "github.com/coredns/coredns/middleware/proxy" "github.com/mholt/caddy" ) @@ -47,6 +49,7 @@ func secondaryParse(c *caddy.Controller) (file.Zones, error) { z := make(map[string]*file.Zone) names := []string{} origins := []string{} + prxy := proxy.Proxy{} for c.Next() { if c.Val() == "secondary" { @@ -74,6 +77,16 @@ func secondaryParse(c *caddy.Controller) (file.Zones, error) { if e != nil { return file.Zones{}, e } + case "upstream": + args := c.RemainingArgs() + if len(args) == 0 { + return file.Zones{}, c.ArgErr() + } + ups, err := dnsutil.ParseHostPortOrFile(args...) + if err != nil { + return file.Zones{}, err + } + prxy = proxy.NewLookup(ups) } for _, origin := range origins { @@ -83,6 +96,7 @@ func secondaryParse(c *caddy.Controller) (file.Zones, error) { if f != nil { z[origin].TransferFrom = append(z[origin].TransferFrom, f...) } + z[origin].Proxy = prxy } } } diff --git a/test/secondary_net_test.go b/test/secondary_net_test.go new file mode 100644 index 000000000..451195442 --- /dev/null +++ b/test/secondary_net_test.go @@ -0,0 +1,126 @@ +// +build net + +package test + +import ( + "testing" + + "github.com/miekg/dns" +) + +func TestSecondaryZoneTransfer(t *testing.T) { + /* + Test will only work when there is a CoreDNS running on part 32054 + with example.com and willing to transfer + coredns -conf Corefile -dns.port 32054 + Corefile: + example.com { + file example.com { + transfer to 127.0.0.1:32053 + } + } + example.com: + example.com. 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2017042730 7200 3600 1209600 3600 + + example.com. 65118 IN NS a.iana-servers.net. + example.com. 65118 IN NS b.iana-servers.net. + cname.example.com. 434334 IN CNAME a.miek.nl. + */ + + corefile := `example.com:32053 { + secondary { + transfer from 127.0.0.1:32054 + } + } + ` + + sec, err := CoreDNSServer(corefile) + if err != nil { + t.Fatalf("Could not get CoreDNS serving instance: %s", err) + } + + defer sec.Stop() + + m := new(dns.Msg) + m.SetQuestion("cname.example.com.", dns.TypeCNAME) + + r, err := dns.Exchange(m, "127.0.0.1:32053") + if err != nil { + t.Fatalf("Expected to receive reply, but didn't: %s", err) + } + + if len(r.Answer) == 0 { + t.Fatalf("Expected answer section") + } + + if r.Answer[0].(*dns.CNAME).Target != "a.miek.nl." { + t.Fatalf("Expected target of %s, got %s", "a.miek.nl.", r.Answer[0].(*dns.CNAME).Target) + } + + m = new(dns.Msg) + m.SetQuestion("example.com.", dns.TypeSOA) + r, err = dns.Exchange(m, "127.0.0.1:32053") + if err != nil { + t.Fatalf("Expected to receive reply, but didn't: %s", err) + } + if len(r.Answer) == 0 { + t.Fatalf("Expected answer section") + } + if r.Answer[0].(*dns.SOA).Serial != 2017042730 { + t.Fatalf("Expected serial of %d, got %d", 2017042730, r.Answer[0].(*dns.SOA).Serial) + } +} + +func TestSecondaryZoneTransferUpstream(t *testing.T) { + /* + Test will only work when there is a CoreDNS running on part 32054 + with example.com and willing to transfer + coredns -conf Corefile -dns.port 32054 + Corefile: + example.com { + file example.com { + transfer to 127.0.0.1:32053 + } + } + example.com: + example.com. 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2017042730 7200 3600 1209600 3600 + + example.com. 65118 IN NS a.iana-servers.net. + example.com. 65118 IN NS b.iana-servers.net. + cname.example.com. 434334 IN CNAME a.miek.nl. + */ + + corefile := `example.com:32053 { + secondary { + transfer from 127.0.0.1:32054 + upstream 8.8.8.8 + } + } + ` + + sec, err := CoreDNSServer(corefile) + if err != nil { + t.Fatalf("Could not get CoreDNS serving instance: %s", err) + } + + defer sec.Stop() + + m := new(dns.Msg) + m.SetQuestion("cname.example.com.", dns.TypeA) + + r, err := dns.Exchange(m, "127.0.0.1:32053") + if err != nil { + t.Fatalf("Expected to receive reply, but didn't: %s", err) + } + + if len(r.Answer) != 2 { + t.Fatalf("Expected answer section, with 2 records, got %d", len(r.Answer)) + } + + if r.Answer[0].(*dns.CNAME).Target != "a.miek.nl." { + t.Fatalf("Expected target of %s, got %s", "a.miek.nl.", r.Answer[0].(*dns.CNAME).Target) + } + if r.Answer[1].Header().Name != "a.miek.nl." { + t.Fatalf("Expected name of %s, got %s", "a.miek.nl.", r.Answer[1].Header().Name) + } +} |