diff options
Diffstat (limited to 'plugin/template/template_test.go')
-rw-r--r-- | plugin/template/template_test.go | 245 |
1 files changed, 197 insertions, 48 deletions
diff --git a/plugin/template/template_test.go b/plugin/template/template_test.go index 0e1470644..a815e8c7c 100644 --- a/plugin/template/template_test.go +++ b/plugin/template/template_test.go @@ -7,80 +7,100 @@ import ( "testing" "github.com/coredns/coredns/plugin/test" + "github.com/mholt/caddy" gotmpl "text/template" "github.com/coredns/coredns/plugin/pkg/dnstest" + "github.com/coredns/coredns/plugin/pkg/fall" "github.com/miekg/dns" ) func TestHandler(t *testing.T) { rcodeFallthrough := 3841 // reserved for private use, used to indicate a fallthrough exampleDomainATemplate := template{ - class: dns.ClassINET, - qtype: dns.TypeA, - regex: []*regexp.Regexp{regexp.MustCompile("(^|[.])ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$")}, - answer: []*gotmpl.Template{gotmpl.Must(gotmpl.New("answer").Parse("{{ .Name }} 60 IN A 10.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}"))}, + regex: []*regexp.Regexp{regexp.MustCompile("(^|[.])ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$")}, + answer: []*gotmpl.Template{gotmpl.Must(gotmpl.New("answer").Parse("{{ .Name }} 60 IN A 10.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}"))}, + qclass: dns.ClassANY, + qtype: dns.TypeANY, + fthrough: fall.F{Zones: []string{"."}}, + zones: []string{"."}, } exampleDomainANSTemplate := template{ - class: dns.ClassINET, - qtype: dns.TypeA, regex: []*regexp.Regexp{regexp.MustCompile("(^|[.])ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$")}, answer: []*gotmpl.Template{gotmpl.Must(gotmpl.New("answer").Parse("{{ .Name }} 60 IN A 10.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}"))}, additional: []*gotmpl.Template{gotmpl.Must(gotmpl.New("additional").Parse("ns0.example. IN A 203.0.113.8"))}, authority: []*gotmpl.Template{gotmpl.Must(gotmpl.New("authority").Parse("example. IN NS ns0.example.com."))}, + qclass: dns.ClassANY, + qtype: dns.TypeANY, + fthrough: fall.F{Zones: []string{"."}}, + zones: []string{"."}, } exampleDomainMXTemplate := template{ - class: dns.ClassINET, - qtype: dns.TypeMX, regex: []*regexp.Regexp{regexp.MustCompile("(^|[.])ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$")}, answer: []*gotmpl.Template{gotmpl.Must(gotmpl.New("answer").Parse("{{ .Name }} 60 MX 10 {{ .Name }}"))}, additional: []*gotmpl.Template{gotmpl.Must(gotmpl.New("additional").Parse("{{ .Name }} 60 IN A 10.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}"))}, + qclass: dns.ClassANY, + qtype: dns.TypeANY, + fthrough: fall.F{Zones: []string{"."}}, + zones: []string{"."}, } invalidDomainTemplate := template{ - class: dns.ClassANY, - qtype: dns.TypeANY, - regex: []*regexp.Regexp{regexp.MustCompile("[.]invalid[.]$")}, - rcode: dns.RcodeNameError, - answer: []*gotmpl.Template{gotmpl.Must(gotmpl.New("answer").Parse("invalid. 60 {{ .Class }} SOA a.invalid. b.invalid. (1 60 60 60 60)"))}, + regex: []*regexp.Regexp{regexp.MustCompile("[.]invalid[.]$")}, + rcode: dns.RcodeNameError, + answer: []*gotmpl.Template{gotmpl.Must(gotmpl.New("answer").Parse("invalid. 60 {{ .Class }} SOA a.invalid. b.invalid. (1 60 60 60 60)"))}, + qclass: dns.ClassANY, + qtype: dns.TypeANY, + fthrough: fall.F{Zones: []string{"."}}, + zones: []string{"."}, } rcodeServfailTemplate := template{ - class: dns.ClassANY, - qtype: dns.TypeANY, - regex: []*regexp.Regexp{regexp.MustCompile(".*")}, - rcode: dns.RcodeServerFailure, + regex: []*regexp.Regexp{regexp.MustCompile(".*")}, + rcode: dns.RcodeServerFailure, + qclass: dns.ClassANY, + qtype: dns.TypeANY, + fthrough: fall.F{Zones: []string{"."}}, + zones: []string{"."}, } brokenTemplate := template{ - class: dns.ClassINET, - qtype: dns.TypeA, - regex: []*regexp.Regexp{regexp.MustCompile("[.]example[.]$")}, - answer: []*gotmpl.Template{gotmpl.Must(gotmpl.New("answer").Parse("{{ .Name }} 60 IN TXT \"{{ index .Match 2 }}\""))}, + regex: []*regexp.Regexp{regexp.MustCompile("[.]example[.]$")}, + answer: []*gotmpl.Template{gotmpl.Must(gotmpl.New("answer").Parse("{{ .Name }} 60 IN TXT \"{{ index .Match 2 }}\""))}, + qclass: dns.ClassANY, + qtype: dns.TypeANY, + fthrough: fall.F{Zones: []string{"."}}, + zones: []string{"."}, } nonRRTemplate := template{ - class: dns.ClassINET, - qtype: dns.TypeA, - regex: []*regexp.Regexp{regexp.MustCompile("[.]example[.]$")}, - answer: []*gotmpl.Template{gotmpl.Must(gotmpl.New("answer").Parse("{{ .Name }}"))}, + regex: []*regexp.Regexp{regexp.MustCompile("[.]example[.]$")}, + answer: []*gotmpl.Template{gotmpl.Must(gotmpl.New("answer").Parse("{{ .Name }}"))}, + qclass: dns.ClassANY, + qtype: dns.TypeANY, + fthrough: fall.F{Zones: []string{"."}}, + zones: []string{"."}, } nonRRAdditionalTemplate := template{ - class: dns.ClassINET, - qtype: dns.TypeA, regex: []*regexp.Regexp{regexp.MustCompile("[.]example[.]$")}, additional: []*gotmpl.Template{gotmpl.Must(gotmpl.New("answer").Parse("{{ .Name }}"))}, + qclass: dns.ClassANY, + qtype: dns.TypeANY, + fthrough: fall.F{Zones: []string{"."}}, + zones: []string{"."}, } nonRRAuthoritativeTemplate := template{ - class: dns.ClassINET, - qtype: dns.TypeA, regex: []*regexp.Regexp{regexp.MustCompile("[.]example[.]$")}, authority: []*gotmpl.Template{gotmpl.Must(gotmpl.New("authority").Parse("{{ .Name }}"))}, + qclass: dns.ClassANY, + qtype: dns.TypeANY, + fthrough: fall.F{Zones: []string{"."}}, + zones: []string{"."}, } tests := []struct { tmpl template qname string + name string qclass uint16 qtype uint16 - name string expectedCode int expectedErr string verifyResponse func(*dns.Msg) error @@ -88,8 +108,6 @@ func TestHandler(t *testing.T) { { name: "RcodeServFail", tmpl: rcodeServfailTemplate, - qclass: dns.ClassANY, - qtype: dns.TypeANY, qname: "test.invalid.", expectedCode: dns.RcodeServerFailure, verifyResponse: func(r *dns.Msg) error { @@ -222,22 +240,6 @@ func TestHandler(t *testing.T) { }, }, { - name: "ExampleDomainMismatchType", - tmpl: exampleDomainATemplate, - qclass: dns.ClassINET, - qtype: dns.TypeMX, - qname: "ip-10-95-12-8.example.", - expectedCode: rcodeFallthrough, - }, - { - name: "ExampleDomainMismatchClass", - tmpl: exampleDomainATemplate, - qclass: dns.ClassCHAOS, - qtype: dns.TypeA, - qname: "ip-10-95-12-8.example.", - expectedCode: rcodeFallthrough, - }, - { name: "ExampleInvalidNXDOMAIN", tmpl: invalidDomainTemplate, qclass: dns.ClassINET, @@ -261,6 +263,7 @@ func TestHandler(t *testing.T) { for _, tr := range tests { handler := Handler{ Next: test.NextHandler(rcodeFallthrough, nil), + Zones: []string{"."}, Templates: []template{tr.tmpl}, } req := &dns.Msg{ @@ -292,3 +295,149 @@ func TestHandler(t *testing.T) { } } } + +// TestMultiSection verfies that a corefile with mutliple but different template sections works +func TestMultiSection(t *testing.T) { + rcodeFallthrough := 3841 // reserved for private use, used to indicate a fallthrough + ctx := context.TODO() + + multisectionConfig := ` + # Implicit section (see c.ServerBlockKeys) + # test.:8053 { + + # REFUSE IN A for the server zone (test.) + template IN A { + rcode REFUSED + } + # Fallthrough everyting IN TXT for test. + template IN TXT { + match "$^" + rcode SERVFAIL + fallthrough + } + # Answer CH TXT *.coredns.invalid. / coredns.invalid. + template CH TXT coredns.invalid { + answer "{{ .Name }} 60 CH TXT \"test\"" + } + # Anwser example. ip templates and fallthrough otherwise + template IN A example { + match ^ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$ + answer "{{ .Name }} 60 IN A 10.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}" + fallthrough + } + # Answer MX record requests for ip templates in example. and never fall through + template IN MX example { + match ^ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$ + answer "{{ .Name }} 60 IN MX 10 {{ .Name }}" + additional "{{ .Name }} 60 IN A 10.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}" + } + ` + c := caddy.NewTestController("dns", multisectionConfig) + c.ServerBlockKeys = []string{"test.:8053"} + + handler, err := templateParse(c) + if err != nil { + t.Fatalf("TestMultiSection could not parse config: %v", err) + } + + handler.Next = test.NextHandler(rcodeFallthrough, nil) + + rec := dnstest.NewRecorder(&test.ResponseWriter{}) + + // Asking for test. IN A -> REFUSED + + req := &dns.Msg{Question: []dns.Question{{Name: "some.test.", Qclass: dns.ClassINET, Qtype: dns.TypeA}}} + code, err := handler.ServeDNS(ctx, rec, req) + if err != nil { + t.Fatalf("TestMultiSection expected no error resolving some.test. A, got: %v", err) + } + if code != dns.RcodeRefused { + t.Fatalf("TestMultiSection expected response code REFUSED got: %v", code) + } + + // Asking for test. IN TXT -> fallthrough + + req = &dns.Msg{Question: []dns.Question{{Name: "some.test.", Qclass: dns.ClassINET, Qtype: dns.TypeTXT}}} + code, err = handler.ServeDNS(ctx, rec, req) + if err != nil { + t.Fatalf("TestMultiSection expected no error resolving some.test. TXT, got: %v", err) + } + if code != rcodeFallthrough { + t.Fatalf("TestMultiSection expected response code fallthrough got: %v", code) + } + + // Asking for coredns.invalid. CH TXT -> TXT "test" + + req = &dns.Msg{Question: []dns.Question{{Name: "coredns.invalid.", Qclass: dns.ClassCHAOS, Qtype: dns.TypeTXT}}} + code, err = handler.ServeDNS(ctx, rec, req) + if err != nil { + t.Fatalf("TestMultiSection expected no error resolving coredns.invalid. TXT, got: %v", err) + } + if code != dns.RcodeSuccess { + t.Fatalf("TestMultiSection expected success response for coredns.invalid. TXT got: %v", code) + } + if len(rec.Msg.Answer) != 1 { + t.Fatalf("TestMultiSection expected one answer for coredns.invalid. TXT got: %v", rec.Msg.Answer) + } + if rec.Msg.Answer[0].Header().Rrtype != dns.TypeTXT || rec.Msg.Answer[0].(*dns.TXT).Txt[0] != "test" { + t.Fatalf("TestMultiSection a \"test\" answer for coredns.invalid. TXT got: %v", rec.Msg.Answer[0]) + } + + // Asking for an ip template in example + + req = &dns.Msg{Question: []dns.Question{{Name: "ip-10-11-12-13.example.", Qclass: dns.ClassINET, Qtype: dns.TypeA}}} + code, err = handler.ServeDNS(ctx, rec, req) + if err != nil { + t.Fatalf("TestMultiSection expected no error resolving ip-10-11-12-13.example. IN A, got: %v", err) + } + if code != dns.RcodeSuccess { + t.Fatalf("TestMultiSection expected success response ip-10-11-12-13.example. IN A got: %v, %v", code, dns.RcodeToString[code]) + } + if len(rec.Msg.Answer) != 1 { + t.Fatalf("TestMultiSection expected one answer for ip-10-11-12-13.example. IN A got: %v", rec.Msg.Answer) + } + if rec.Msg.Answer[0].Header().Rrtype != dns.TypeA { + t.Fatalf("TestMultiSection an A RR answer for ip-10-11-12-13.example. IN A got: %v", rec.Msg.Answer[0]) + } + + // Asking for an MX ip template in example + + req = &dns.Msg{Question: []dns.Question{{Name: "ip-10-11-12-13.example.", Qclass: dns.ClassINET, Qtype: dns.TypeMX}}} + code, err = handler.ServeDNS(ctx, rec, req) + if err != nil { + t.Fatalf("TestMultiSection expected no error resolving ip-10-11-12-13.example. IN MX, got: %v", err) + } + if code != dns.RcodeSuccess { + t.Fatalf("TestMultiSection expected success response ip-10-11-12-13.example. IN MX got: %v, %v", code, dns.RcodeToString[code]) + } + if len(rec.Msg.Answer) != 1 { + t.Fatalf("TestMultiSection expected one answer for ip-10-11-12-13.example. IN MX got: %v", rec.Msg.Answer) + } + if rec.Msg.Answer[0].Header().Rrtype != dns.TypeMX { + t.Fatalf("TestMultiSection an A RR answer for ip-10-11-12-13.example. IN MX got: %v", rec.Msg.Answer[0]) + } + + // Test that something.example. A does fall through but something.example. MX does not + + req = &dns.Msg{Question: []dns.Question{{Name: "something.example.", Qclass: dns.ClassINET, Qtype: dns.TypeA}}} + code, err = handler.ServeDNS(ctx, rec, req) + if err != nil { + t.Fatalf("TestMultiSection expected no error resolving something.example. IN A, got: %v", err) + } + if code != rcodeFallthrough { + t.Fatalf("TestMultiSection expected a fall through resolving something.example. IN A, got: %v, %v", code, dns.RcodeToString[code]) + } + + req = &dns.Msg{Question: []dns.Question{{Name: "something.example.", Qclass: dns.ClassINET, Qtype: dns.TypeMX}}} + code, err = handler.ServeDNS(ctx, rec, req) + if err != nil { + t.Fatalf("TestMultiSection expected no error resolving something.example. IN MX, got: %v", err) + } + if code == rcodeFallthrough { + t.Fatalf("TestMultiSection expected no fall through resolving something.example. IN MX") + } + if code != dns.RcodeNameError { + t.Fatalf("TestMultiSection expected NXDOMAIN resolving something.example. IN MX, got %v, %v", code, dns.RcodeToString[code]) + } + +} |