| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | package template | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-04-22 08:34:35 +01:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"regexp" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 	gotmpl "text/template" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 18:14:41 +02:00
										 |  |  | 	"github.com/coredns/caddy" | 
					
						
							| 
									
										
										
										
											2019-07-03 16:10:56 +01:00
										 |  |  | 	"github.com/coredns/coredns/plugin/metadata" | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	"github.com/coredns/coredns/plugin/pkg/dnstest" | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 	"github.com/coredns/coredns/plugin/pkg/fall" | 
					
						
							| 
									
										
										
										
											2018-01-09 21:48:32 +00:00
										 |  |  | 	"github.com/coredns/coredns/plugin/test" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	"github.com/miekg/dns" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestHandler(t *testing.T) { | 
					
						
							|  |  |  | 	exampleDomainATemplate := template{ | 
					
						
							| 
									
										
										
										
											2018-01-09 21:48:32 +00:00
										 |  |  | 		regex:  []*regexp.Regexp{regexp.MustCompile("(^|[.])ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$")}, | 
					
						
							| 
									
										
										
										
											2022-09-15 12:25:58 -07:00
										 |  |  | 		answer: []*gotmpl.Template{gotmpl.Must(newTemplate("answer", "{{ .Name }} 60 IN A 10.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}"))}, | 
					
						
							|  |  |  | 		qclass: dns.ClassANY, | 
					
						
							|  |  |  | 		qtype:  dns.TypeANY, | 
					
						
							|  |  |  | 		fall:   fall.Root, | 
					
						
							|  |  |  | 		zones:  []string{"."}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	exampleDomainAParseIntTemplate := template{ | 
					
						
							|  |  |  | 		regex:  []*regexp.Regexp{regexp.MustCompile("^ip0a(?P<b>[a-f0-9]{2})(?P<c>[a-f0-9]{2})(?P<d>[a-f0-9]{2})[.]example[.]$")}, | 
					
						
							|  |  |  | 		answer: []*gotmpl.Template{gotmpl.Must(newTemplate("answer", "{{ .Name }} 60 IN A 10.{{ parseInt .Group.b 16 8 }}.{{ parseInt .Group.c 16 8 }}.{{ parseInt .Group.d 16 8 }}"))}, | 
					
						
							| 
									
										
										
										
											2018-01-09 21:48:32 +00:00
										 |  |  | 		qclass: dns.ClassANY, | 
					
						
							|  |  |  | 		qtype:  dns.TypeANY, | 
					
						
							| 
									
										
										
										
											2018-01-09 22:29:19 +00:00
										 |  |  | 		fall:   fall.Root, | 
					
						
							| 
									
										
										
										
											2018-01-09 21:48:32 +00:00
										 |  |  | 		zones:  []string{"."}, | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-10 10:38:18 +02:00
										 |  |  | 	exampleDomainIPATemplate := template{ | 
					
						
							|  |  |  | 		regex:  []*regexp.Regexp{regexp.MustCompile(".*")}, | 
					
						
							| 
									
										
										
										
											2022-09-15 12:25:58 -07:00
										 |  |  | 		answer: []*gotmpl.Template{gotmpl.Must(newTemplate("answer", "{{ .Name }} 60 IN A {{ .Remote }}"))}, | 
					
						
							| 
									
										
										
										
											2020-08-10 10:38:18 +02:00
										 |  |  | 		qclass: dns.ClassINET, | 
					
						
							|  |  |  | 		qtype:  dns.TypeA, | 
					
						
							|  |  |  | 		fall:   fall.Root, | 
					
						
							|  |  |  | 		zones:  []string{"."}, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	exampleDomainANSTemplate := template{ | 
					
						
							|  |  |  | 		regex:      []*regexp.Regexp{regexp.MustCompile("(^|[.])ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$")}, | 
					
						
							| 
									
										
										
										
											2022-09-15 12:25:58 -07:00
										 |  |  | 		answer:     []*gotmpl.Template{gotmpl.Must(newTemplate("answer", "{{ .Name }} 60 IN A 10.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}"))}, | 
					
						
							|  |  |  | 		additional: []*gotmpl.Template{gotmpl.Must(newTemplate("additional", "ns0.example. IN A 203.0.113.8"))}, | 
					
						
							|  |  |  | 		authority:  []*gotmpl.Template{gotmpl.Must(newTemplate("authority", "example. IN NS ns0.example.com."))}, | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 		qclass:     dns.ClassANY, | 
					
						
							|  |  |  | 		qtype:      dns.TypeANY, | 
					
						
							| 
									
										
										
										
											2018-01-09 22:29:19 +00:00
										 |  |  | 		fall:       fall.Root, | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 		zones:      []string{"."}, | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	exampleDomainMXTemplate := template{ | 
					
						
							|  |  |  | 		regex:      []*regexp.Regexp{regexp.MustCompile("(^|[.])ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$")}, | 
					
						
							| 
									
										
										
										
											2022-09-15 12:25:58 -07:00
										 |  |  | 		answer:     []*gotmpl.Template{gotmpl.Must(newTemplate("answer", "{{ .Name }} 60 MX 10 {{ .Name }}"))}, | 
					
						
							|  |  |  | 		additional: []*gotmpl.Template{gotmpl.Must(newTemplate("additional", "{{ .Name }} 60 IN A 10.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}"))}, | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 		qclass:     dns.ClassANY, | 
					
						
							|  |  |  | 		qtype:      dns.TypeANY, | 
					
						
							| 
									
										
										
										
											2018-01-09 22:29:19 +00:00
										 |  |  | 		fall:       fall.Root, | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 		zones:      []string{"."}, | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	invalidDomainTemplate := template{ | 
					
						
							| 
									
										
										
										
											2018-01-09 21:48:32 +00:00
										 |  |  | 		regex:  []*regexp.Regexp{regexp.MustCompile("[.]invalid[.]$")}, | 
					
						
							|  |  |  | 		rcode:  dns.RcodeNameError, | 
					
						
							| 
									
										
										
										
											2022-09-15 12:25:58 -07:00
										 |  |  | 		answer: []*gotmpl.Template{gotmpl.Must(newTemplate("answer", "invalid. 60 {{ .Class }} SOA a.invalid. b.invalid. (1 60 60 60 60)"))}, | 
					
						
							| 
									
										
										
										
											2018-01-09 21:48:32 +00:00
										 |  |  | 		qclass: dns.ClassANY, | 
					
						
							|  |  |  | 		qtype:  dns.TypeANY, | 
					
						
							| 
									
										
										
										
											2018-01-09 22:29:19 +00:00
										 |  |  | 		fall:   fall.Root, | 
					
						
							| 
									
										
										
										
											2018-01-09 21:48:32 +00:00
										 |  |  | 		zones:  []string{"."}, | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	rcodeServfailTemplate := template{ | 
					
						
							| 
									
										
										
										
											2018-01-09 21:48:32 +00:00
										 |  |  | 		regex:  []*regexp.Regexp{regexp.MustCompile(".*")}, | 
					
						
							|  |  |  | 		rcode:  dns.RcodeServerFailure, | 
					
						
							|  |  |  | 		qclass: dns.ClassANY, | 
					
						
							|  |  |  | 		qtype:  dns.TypeANY, | 
					
						
							| 
									
										
										
										
											2018-01-09 22:29:19 +00:00
										 |  |  | 		fall:   fall.Root, | 
					
						
							| 
									
										
										
										
											2018-01-09 21:48:32 +00:00
										 |  |  | 		zones:  []string{"."}, | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	brokenTemplate := template{ | 
					
						
							| 
									
										
										
										
											2018-01-09 21:48:32 +00:00
										 |  |  | 		regex:  []*regexp.Regexp{regexp.MustCompile("[.]example[.]$")}, | 
					
						
							| 
									
										
										
										
											2022-09-15 12:25:58 -07:00
										 |  |  | 		answer: []*gotmpl.Template{gotmpl.Must(newTemplate("answer", "{{ .Name }} 60 IN TXT \"{{ index .Match 2 }}\""))}, | 
					
						
							|  |  |  | 		qclass: dns.ClassANY, | 
					
						
							|  |  |  | 		qtype:  dns.TypeANY, | 
					
						
							|  |  |  | 		fall:   fall.Root, | 
					
						
							|  |  |  | 		zones:  []string{"."}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	brokenParseIntTemplate := template{ | 
					
						
							|  |  |  | 		regex:  []*regexp.Regexp{regexp.MustCompile("[.]example[.]$")}, | 
					
						
							|  |  |  | 		answer: []*gotmpl.Template{gotmpl.Must(newTemplate("answer", "{{ .Name }} 60 IN TXT \"{{ parseInt \"gg\" 16 8 }}\""))}, | 
					
						
							| 
									
										
										
										
											2018-01-09 21:48:32 +00:00
										 |  |  | 		qclass: dns.ClassANY, | 
					
						
							|  |  |  | 		qtype:  dns.TypeANY, | 
					
						
							| 
									
										
										
										
											2018-01-09 22:29:19 +00:00
										 |  |  | 		fall:   fall.Root, | 
					
						
							| 
									
										
										
										
											2018-01-09 21:48:32 +00:00
										 |  |  | 		zones:  []string{"."}, | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	nonRRTemplate := template{ | 
					
						
							| 
									
										
										
										
											2018-01-09 21:48:32 +00:00
										 |  |  | 		regex:  []*regexp.Regexp{regexp.MustCompile("[.]example[.]$")}, | 
					
						
							| 
									
										
										
										
											2022-09-15 12:25:58 -07:00
										 |  |  | 		answer: []*gotmpl.Template{gotmpl.Must(newTemplate("answer", "{{ .Name }}"))}, | 
					
						
							| 
									
										
										
										
											2018-01-09 21:48:32 +00:00
										 |  |  | 		qclass: dns.ClassANY, | 
					
						
							|  |  |  | 		qtype:  dns.TypeANY, | 
					
						
							| 
									
										
										
										
											2018-01-09 22:29:19 +00:00
										 |  |  | 		fall:   fall.Root, | 
					
						
							| 
									
										
										
										
											2018-01-09 21:48:32 +00:00
										 |  |  | 		zones:  []string{"."}, | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	nonRRAdditionalTemplate := template{ | 
					
						
							|  |  |  | 		regex:      []*regexp.Regexp{regexp.MustCompile("[.]example[.]$")}, | 
					
						
							| 
									
										
										
										
											2022-09-15 12:25:58 -07:00
										 |  |  | 		additional: []*gotmpl.Template{gotmpl.Must(newTemplate("answer", "{{ .Name }}"))}, | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 		qclass:     dns.ClassANY, | 
					
						
							|  |  |  | 		qtype:      dns.TypeANY, | 
					
						
							| 
									
										
										
										
											2018-01-09 22:29:19 +00:00
										 |  |  | 		fall:       fall.Root, | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 		zones:      []string{"."}, | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	nonRRAuthoritativeTemplate := template{ | 
					
						
							|  |  |  | 		regex:     []*regexp.Regexp{regexp.MustCompile("[.]example[.]$")}, | 
					
						
							| 
									
										
										
										
											2022-09-15 12:25:58 -07:00
										 |  |  | 		authority: []*gotmpl.Template{gotmpl.Must(newTemplate("answer", "{{ .Name }}"))}, | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 		qclass:    dns.ClassANY, | 
					
						
							|  |  |  | 		qtype:     dns.TypeANY, | 
					
						
							| 
									
										
										
										
											2018-01-09 22:29:19 +00:00
										 |  |  | 		fall:      fall.Root, | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 		zones:     []string{"."}, | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-21 14:38:29 -04:00
										 |  |  | 	cnameTemplate := template{ | 
					
						
							|  |  |  | 		regex:  []*regexp.Regexp{regexp.MustCompile("example[.]net[.]")}, | 
					
						
							| 
									
										
										
										
											2022-09-15 12:25:58 -07:00
										 |  |  | 		answer: []*gotmpl.Template{gotmpl.Must(newTemplate("answer", "example.net 60 IN CNAME target.example.com"))}, | 
					
						
							| 
									
										
										
										
											2018-06-21 14:38:29 -04:00
										 |  |  | 		qclass: dns.ClassANY, | 
					
						
							|  |  |  | 		qtype:  dns.TypeANY, | 
					
						
							|  |  |  | 		fall:   fall.Root, | 
					
						
							|  |  |  | 		zones:  []string{"."}, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-03 16:10:56 +01:00
										 |  |  | 	mdTemplate := template{ | 
					
						
							|  |  |  | 		regex:      []*regexp.Regexp{regexp.MustCompile("(^|[.])ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$")}, | 
					
						
							| 
									
										
										
										
											2022-09-15 12:25:58 -07:00
										 |  |  | 		answer:     []*gotmpl.Template{gotmpl.Must(newTemplate("answer", `{{ .Meta "foo" }}-{{ .Name }} 60 IN A 10.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}`))}, | 
					
						
							|  |  |  | 		additional: []*gotmpl.Template{gotmpl.Must(newTemplate("additional", `{{ .Meta "bar" }}.example. IN A 203.0.113.8`))}, | 
					
						
							|  |  |  | 		authority:  []*gotmpl.Template{gotmpl.Must(newTemplate("authority", `example. IN NS {{ .Meta "bar" }}.example.com.`))}, | 
					
						
							| 
									
										
										
										
											2019-07-03 16:10:56 +01:00
										 |  |  | 		qclass:     dns.ClassANY, | 
					
						
							|  |  |  | 		qtype:      dns.TypeANY, | 
					
						
							|  |  |  | 		fall:       fall.Root, | 
					
						
							|  |  |  | 		zones:      []string{"."}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	mdMissingTemplate := template{ | 
					
						
							|  |  |  | 		regex:  []*regexp.Regexp{regexp.MustCompile("(^|[.])ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$")}, | 
					
						
							| 
									
										
										
										
											2022-09-15 12:25:58 -07:00
										 |  |  | 		answer: []*gotmpl.Template{gotmpl.Must(newTemplate("answer", `{{ .Meta "foofoo" }}{{ .Name }} 60 IN A 10.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}`))}, | 
					
						
							| 
									
										
										
										
											2019-07-03 16:10:56 +01:00
										 |  |  | 		qclass: dns.ClassANY, | 
					
						
							|  |  |  | 		qtype:  dns.TypeANY, | 
					
						
							|  |  |  | 		fall:   fall.Root, | 
					
						
							|  |  |  | 		zones:  []string{"."}, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-10-03 17:04:56 +02:00
										 |  |  | 	templateWithEDE := template{ | 
					
						
							|  |  |  | 		rcode:     dns.RcodeNameError, | 
					
						
							|  |  |  | 		regex:     []*regexp.Regexp{regexp.MustCompile(".*")}, | 
					
						
							|  |  |  | 		authority: []*gotmpl.Template{gotmpl.Must(newTemplate("authority", "invalid. 60 {{ .Class }} SOA ns.invalid. hostmaster.invalid. (1 60 60 60 60)"))}, | 
					
						
							|  |  |  | 		qclass:    dns.ClassANY, | 
					
						
							|  |  |  | 		qtype:     dns.TypeANY, | 
					
						
							|  |  |  | 		fall:      fall.Root, | 
					
						
							|  |  |  | 		zones:     []string{"."}, | 
					
						
							|  |  |  | 		ederror:   &ederror{code: 21, reason: "Blocked due to RFC2606"}, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	tests := []struct { | 
					
						
							|  |  |  | 		tmpl           template | 
					
						
							|  |  |  | 		qname          string | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 		name           string | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 		qclass         uint16 | 
					
						
							|  |  |  | 		qtype          uint16 | 
					
						
							|  |  |  | 		expectedCode   int | 
					
						
							|  |  |  | 		expectedErr    string | 
					
						
							|  |  |  | 		verifyResponse func(*dns.Msg) error | 
					
						
							| 
									
										
										
										
											2019-07-03 16:10:56 +01:00
										 |  |  | 		md             map[string]string | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:         "RcodeServFail", | 
					
						
							|  |  |  | 			tmpl:         rcodeServfailTemplate, | 
					
						
							|  |  |  | 			qname:        "test.invalid.", | 
					
						
							|  |  |  | 			expectedCode: dns.RcodeServerFailure, | 
					
						
							|  |  |  | 			verifyResponse: func(r *dns.Msg) error { | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:         "ExampleDomainNameMismatch", | 
					
						
							|  |  |  | 			tmpl:         exampleDomainATemplate, | 
					
						
							|  |  |  | 			qclass:       dns.ClassINET, | 
					
						
							|  |  |  | 			qtype:        dns.TypeA, | 
					
						
							|  |  |  | 			qname:        "test.invalid.", | 
					
						
							|  |  |  | 			expectedCode: rcodeFallthrough, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:         "BrokenTemplate", | 
					
						
							|  |  |  | 			tmpl:         brokenTemplate, | 
					
						
							|  |  |  | 			qclass:       dns.ClassINET, | 
					
						
							|  |  |  | 			qtype:        dns.TypeANY, | 
					
						
							|  |  |  | 			qname:        "test.example.", | 
					
						
							|  |  |  | 			expectedCode: dns.RcodeServerFailure, | 
					
						
							|  |  |  | 			expectedErr:  `template: answer:1:26: executing "answer" at <index .Match 2>: error calling index: index out of range: 2`, | 
					
						
							|  |  |  | 			verifyResponse: func(r *dns.Msg) error { | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:         "NonRRTemplate", | 
					
						
							|  |  |  | 			tmpl:         nonRRTemplate, | 
					
						
							|  |  |  | 			qclass:       dns.ClassINET, | 
					
						
							|  |  |  | 			qtype:        dns.TypeANY, | 
					
						
							|  |  |  | 			qname:        "test.example.", | 
					
						
							|  |  |  | 			expectedCode: dns.RcodeServerFailure, | 
					
						
							|  |  |  | 			expectedErr:  `dns: not a TTL: "test.example." at line: 1:13`, | 
					
						
							|  |  |  | 			verifyResponse: func(r *dns.Msg) error { | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:         "NonRRAdditionalTemplate", | 
					
						
							|  |  |  | 			tmpl:         nonRRAdditionalTemplate, | 
					
						
							|  |  |  | 			qclass:       dns.ClassINET, | 
					
						
							|  |  |  | 			qtype:        dns.TypeANY, | 
					
						
							|  |  |  | 			qname:        "test.example.", | 
					
						
							|  |  |  | 			expectedCode: dns.RcodeServerFailure, | 
					
						
							|  |  |  | 			expectedErr:  `dns: not a TTL: "test.example." at line: 1:13`, | 
					
						
							|  |  |  | 			verifyResponse: func(r *dns.Msg) error { | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:         "NonRRAuthorityTemplate", | 
					
						
							|  |  |  | 			tmpl:         nonRRAuthoritativeTemplate, | 
					
						
							|  |  |  | 			qclass:       dns.ClassINET, | 
					
						
							|  |  |  | 			qtype:        dns.TypeANY, | 
					
						
							|  |  |  | 			qname:        "test.example.", | 
					
						
							|  |  |  | 			expectedCode: dns.RcodeServerFailure, | 
					
						
							|  |  |  | 			expectedErr:  `dns: not a TTL: "test.example." at line: 1:13`, | 
					
						
							|  |  |  | 			verifyResponse: func(r *dns.Msg) error { | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2020-08-10 10:38:18 +02:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			name:   "ExampleIPMatch", | 
					
						
							|  |  |  | 			tmpl:   exampleDomainIPATemplate, | 
					
						
							|  |  |  | 			qclass: dns.ClassINET, | 
					
						
							|  |  |  | 			qtype:  dns.TypeA, | 
					
						
							|  |  |  | 			qname:  "test.example.", | 
					
						
							|  |  |  | 			verifyResponse: func(r *dns.Msg) error { | 
					
						
							|  |  |  | 				if len(r.Answer) != 1 { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected 1 answer, got %v", len(r.Answer)) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if r.Answer[0].Header().Rrtype != dns.TypeA { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected an A record answer, got %v", dns.TypeToString[r.Answer[0].Header().Rrtype]) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if r.Answer[0].(*dns.A).A.String() != "10.240.0.1" { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected an A record for 10.95.12.8, got %v", r.Answer[0].String()) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			name:   "ExampleDomainMatch", | 
					
						
							|  |  |  | 			tmpl:   exampleDomainATemplate, | 
					
						
							|  |  |  | 			qclass: dns.ClassINET, | 
					
						
							|  |  |  | 			qtype:  dns.TypeA, | 
					
						
							|  |  |  | 			qname:  "ip-10-95-12-8.example.", | 
					
						
							|  |  |  | 			verifyResponse: func(r *dns.Msg) error { | 
					
						
							|  |  |  | 				if len(r.Answer) != 1 { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected 1 answer, got %v", len(r.Answer)) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if r.Answer[0].Header().Rrtype != dns.TypeA { | 
					
						
							| 
									
										
										
										
											2018-08-14 17:55:55 +02:00
										 |  |  | 					return fmt.Errorf("expected an A record answer, got %v", dns.TypeToString[r.Answer[0].Header().Rrtype]) | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				if r.Answer[0].(*dns.A).A.String() != "10.95.12.8" { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected an A record for 10.95.12.8, got %v", r.Answer[0].String()) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2022-09-15 12:25:58 -07:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			name:   "ExampleDomainMatchHexIp", | 
					
						
							|  |  |  | 			tmpl:   exampleDomainAParseIntTemplate, | 
					
						
							|  |  |  | 			qclass: dns.ClassINET, | 
					
						
							|  |  |  | 			qtype:  dns.TypeA, | 
					
						
							|  |  |  | 			qname:  "ip0a5f0c09.example.", | 
					
						
							|  |  |  | 			verifyResponse: func(r *dns.Msg) error { | 
					
						
							|  |  |  | 				if len(r.Answer) != 1 { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected 1 answer, got %v", len(r.Answer)) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if r.Answer[0].Header().Rrtype != dns.TypeA { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected an A record answer, got %v", dns.TypeToString[r.Answer[0].Header().Rrtype]) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if r.Answer[0].(*dns.A).A.String() != "10.95.12.9" { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected an A record for 10.95.12.9, got %v", r.Answer[0].String()) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:         "BrokenParseIntTemplate", | 
					
						
							|  |  |  | 			tmpl:         brokenParseIntTemplate, | 
					
						
							|  |  |  | 			qclass:       dns.ClassINET, | 
					
						
							|  |  |  | 			qtype:        dns.TypeANY, | 
					
						
							|  |  |  | 			qname:        "test.example.", | 
					
						
							|  |  |  | 			expectedCode: dns.RcodeServerFailure, | 
					
						
							| 
									
										
										
										
											2022-09-15 12:48:38 -07:00
										 |  |  | 			expectedErr:  "template: answer:1:26: executing \"answer\" at <parseInt \"gg\" 16 8>: error calling parseInt: strconv.ParseUint: parsing \"gg\": invalid syntax", | 
					
						
							| 
									
										
										
										
											2022-09-15 12:25:58 -07:00
										 |  |  | 			verifyResponse: func(r *dns.Msg) error { | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			name:   "ExampleDomainMXMatch", | 
					
						
							|  |  |  | 			tmpl:   exampleDomainMXTemplate, | 
					
						
							|  |  |  | 			qclass: dns.ClassINET, | 
					
						
							|  |  |  | 			qtype:  dns.TypeMX, | 
					
						
							|  |  |  | 			qname:  "ip-10-95-12-8.example.", | 
					
						
							|  |  |  | 			verifyResponse: func(r *dns.Msg) error { | 
					
						
							|  |  |  | 				if len(r.Answer) != 1 { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected 1 answer, got %v", len(r.Answer)) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if r.Answer[0].Header().Rrtype != dns.TypeMX { | 
					
						
							| 
									
										
										
										
											2018-08-14 17:55:55 +02:00
										 |  |  | 					return fmt.Errorf("expected an A record answer, got %v", dns.TypeToString[r.Answer[0].Header().Rrtype]) | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				if len(r.Extra) != 1 { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected 1 extra record, got %v", len(r.Extra)) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if r.Extra[0].Header().Rrtype != dns.TypeA { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected an additional A record, got %v", dns.TypeToString[r.Extra[0].Header().Rrtype]) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:   "ExampleDomainANSMatch", | 
					
						
							|  |  |  | 			tmpl:   exampleDomainANSTemplate, | 
					
						
							|  |  |  | 			qclass: dns.ClassINET, | 
					
						
							|  |  |  | 			qtype:  dns.TypeA, | 
					
						
							|  |  |  | 			qname:  "ip-10-95-12-8.example.", | 
					
						
							|  |  |  | 			verifyResponse: func(r *dns.Msg) error { | 
					
						
							|  |  |  | 				if len(r.Answer) != 1 { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected 1 answer, got %v", len(r.Answer)) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if r.Answer[0].Header().Rrtype != dns.TypeA { | 
					
						
							| 
									
										
										
										
											2018-08-14 17:55:55 +02:00
										 |  |  | 					return fmt.Errorf("expected an A record answer, got %v", dns.TypeToString[r.Answer[0].Header().Rrtype]) | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				if len(r.Extra) != 1 { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected 1 extra record, got %v", len(r.Extra)) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if r.Extra[0].Header().Rrtype != dns.TypeA { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected an additional A record, got %v", dns.TypeToString[r.Extra[0].Header().Rrtype]) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if len(r.Ns) != 1 { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected 1 authoritative record, got %v", len(r.Extra)) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if r.Ns[0].Header().Rrtype != dns.TypeNS { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected an authoritative NS record, got %v", dns.TypeToString[r.Extra[0].Header().Rrtype]) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:         "ExampleInvalidNXDOMAIN", | 
					
						
							|  |  |  | 			tmpl:         invalidDomainTemplate, | 
					
						
							|  |  |  | 			qclass:       dns.ClassINET, | 
					
						
							|  |  |  | 			qtype:        dns.TypeMX, | 
					
						
							|  |  |  | 			qname:        "test.invalid.", | 
					
						
							|  |  |  | 			expectedCode: dns.RcodeNameError, | 
					
						
							|  |  |  | 			verifyResponse: func(r *dns.Msg) error { | 
					
						
							|  |  |  | 				if len(r.Answer) != 1 { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected 1 answer, got %v", len(r.Answer)) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if r.Answer[0].Header().Rrtype != dns.TypeSOA { | 
					
						
							| 
									
										
										
										
											2018-08-14 17:55:55 +02:00
										 |  |  | 					return fmt.Errorf("expected an SOA record answer, got %v", dns.TypeToString[r.Answer[0].Header().Rrtype]) | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2018-06-21 14:38:29 -04:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			name:         "CNAMEWithoutUpstream", | 
					
						
							|  |  |  | 			tmpl:         cnameTemplate, | 
					
						
							|  |  |  | 			qclass:       dns.ClassINET, | 
					
						
							|  |  |  | 			qtype:        dns.TypeA, | 
					
						
							|  |  |  | 			qname:        "example.net.", | 
					
						
							|  |  |  | 			expectedCode: dns.RcodeSuccess, | 
					
						
							|  |  |  | 			verifyResponse: func(r *dns.Msg) error { | 
					
						
							|  |  |  | 				if len(r.Answer) != 1 { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected 1 answer, got %v", len(r.Answer)) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2019-07-03 16:10:56 +01:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			name:   "mdMatch", | 
					
						
							|  |  |  | 			tmpl:   mdTemplate, | 
					
						
							|  |  |  | 			qclass: dns.ClassINET, | 
					
						
							|  |  |  | 			qtype:  dns.TypeA, | 
					
						
							|  |  |  | 			qname:  "ip-10-95-12-8.example.", | 
					
						
							|  |  |  | 			verifyResponse: func(r *dns.Msg) error { | 
					
						
							|  |  |  | 				if len(r.Answer) != 1 { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected 1 answer, got %v", len(r.Answer)) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if r.Answer[0].Header().Rrtype != dns.TypeA { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected an A record answer, got %v", dns.TypeToString[r.Answer[0].Header().Rrtype]) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				name := "myfoo-ip-10-95-12-8.example." | 
					
						
							|  |  |  | 				if r.Answer[0].Header().Name != name { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected answer name %q, got %q", name, r.Answer[0].Header().Name) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if len(r.Extra) != 1 { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected 1 extra record, got %v", len(r.Extra)) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if r.Extra[0].Header().Rrtype != dns.TypeA { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected an additional A record, got %v", dns.TypeToString[r.Extra[0].Header().Rrtype]) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				name = "mybar.example." | 
					
						
							|  |  |  | 				if r.Extra[0].Header().Name != name { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected additional name %q, got %q", name, r.Extra[0].Header().Name) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if len(r.Ns) != 1 { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected 1 authoritative record, got %v", len(r.Extra)) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if r.Ns[0].Header().Rrtype != dns.TypeNS { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected an authoritative NS record, got %v", dns.TypeToString[r.Extra[0].Header().Rrtype]) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				ns, ok := r.Ns[0].(*dns.NS) | 
					
						
							|  |  |  | 				if !ok { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected NS record to be type NS, got %v", r.Ns[0]) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				rdata := "mybar.example.com." | 
					
						
							|  |  |  | 				if ns.Ns != rdata { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected ns rdata %q, got %q", rdata, ns.Ns) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			md: map[string]string{ | 
					
						
							|  |  |  | 				"foo":    "myfoo", | 
					
						
							|  |  |  | 				"bar":    "mybar", | 
					
						
							|  |  |  | 				"foobar": "myfoobar", | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:   "mdMissing", | 
					
						
							|  |  |  | 			tmpl:   mdMissingTemplate, | 
					
						
							|  |  |  | 			qclass: dns.ClassINET, | 
					
						
							|  |  |  | 			qtype:  dns.TypeA, | 
					
						
							|  |  |  | 			qname:  "ip-10-95-12-8.example.", | 
					
						
							|  |  |  | 			verifyResponse: func(r *dns.Msg) error { | 
					
						
							|  |  |  | 				if len(r.Answer) != 1 { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected 1 answer, got %v", len(r.Answer)) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if r.Answer[0].Header().Rrtype != dns.TypeA { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected an A record answer, got %v", dns.TypeToString[r.Answer[0].Header().Rrtype]) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				name := "ip-10-95-12-8.example." | 
					
						
							|  |  |  | 				if r.Answer[0].Header().Name != name { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected answer name %q, got %q", name, r.Answer[0].Header().Name) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			md: map[string]string{ | 
					
						
							|  |  |  | 				"foo": "myfoo", | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2022-10-03 17:04:56 +02:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			name:         "EDNS error", | 
					
						
							|  |  |  | 			tmpl:         templateWithEDE, | 
					
						
							|  |  |  | 			qclass:       dns.ClassINET, | 
					
						
							|  |  |  | 			qtype:        dns.TypeA, | 
					
						
							|  |  |  | 			qname:        "test.invalid.", | 
					
						
							|  |  |  | 			expectedCode: dns.RcodeNameError, | 
					
						
							|  |  |  | 			verifyResponse: func(r *dns.Msg) error { | 
					
						
							|  |  |  | 				if opt := r.IsEdns0(); opt != nil { | 
					
						
							|  |  |  | 					matched := false | 
					
						
							|  |  |  | 					for _, ednsopt := range opt.Option { | 
					
						
							|  |  |  | 						if ede, ok := ednsopt.(*dns.EDNS0_EDE); ok { | 
					
						
							|  |  |  | 							if ede.InfoCode != dns.ExtendedErrorCodeNotSupported { | 
					
						
							|  |  |  | 								return fmt.Errorf("unexpected EDE code = %v, want %v", ede.InfoCode, dns.ExtendedErrorCodeNotSupported) | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							matched = true | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					if !matched { | 
					
						
							|  |  |  | 						t.Error("Error: acl.ServeDNS() missing Extended DNS Error option") | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					return fmt.Errorf("expected EDNS enabled") | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ctx := context.TODO() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, tr := range tests { | 
					
						
							|  |  |  | 		handler := Handler{ | 
					
						
							|  |  |  | 			Next:      test.NextHandler(rcodeFallthrough, nil), | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 			Zones:     []string{"."}, | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 			Templates: []template{tr.tmpl}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		req := &dns.Msg{ | 
					
						
							|  |  |  | 			Question: []dns.Question{{ | 
					
						
							|  |  |  | 				Name:   tr.qname, | 
					
						
							|  |  |  | 				Qclass: tr.qclass, | 
					
						
							|  |  |  | 				Qtype:  tr.qtype, | 
					
						
							|  |  |  | 			}}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		rec := dnstest.NewRecorder(&test.ResponseWriter{}) | 
					
						
							| 
									
										
										
										
											2019-07-03 16:10:56 +01:00
										 |  |  | 		if tr.md != nil { | 
					
						
							|  |  |  | 			ctx = metadata.ContextWithMetadata(context.Background()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			for k, v := range tr.md { | 
					
						
							|  |  |  | 				// Go requires copying to a local variable for the closure to work | 
					
						
							|  |  |  | 				kk := k | 
					
						
							|  |  |  | 				vv := v | 
					
						
							|  |  |  | 				metadata.SetValueFunc(ctx, kk, func() string { | 
					
						
							|  |  |  | 					return vv | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 		code, err := handler.ServeDNS(ctx, rec, req) | 
					
						
							|  |  |  | 		if err == nil && tr.expectedErr != "" { | 
					
						
							|  |  |  | 			t.Errorf("Test %v expected error: %v, got nothing", tr.name, tr.expectedErr) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err != nil && tr.expectedErr == "" { | 
					
						
							|  |  |  | 			t.Errorf("Test %v expected no error got: %v", tr.name, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err != nil && tr.expectedErr != "" && err.Error() != tr.expectedErr { | 
					
						
							|  |  |  | 			t.Errorf("Test %v expected error: %v, got: %v", tr.name, tr.expectedErr, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if code != tr.expectedCode { | 
					
						
							|  |  |  | 			t.Errorf("Test %v expected response code %v, got %v", tr.name, tr.expectedCode, code) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err == nil && code != rcodeFallthrough { | 
					
						
							|  |  |  | 			// only verify if we got no error and expected no error | 
					
						
							|  |  |  | 			if err := tr.verifyResponse(rec.Msg); err != nil { | 
					
						
							|  |  |  | 				t.Errorf("Test %v could not verify the response: %v", tr.name, err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-21 16:08:55 -04:00
										 |  |  | // TestMultiSection verifies that a corefile with multiple but different template sections works | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | func TestMultiSection(t *testing.T) { | 
					
						
							|  |  |  | 	ctx := context.TODO() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	multisectionConfig := ` | 
					
						
							|  |  |  | 		# Implicit section (see c.ServerBlockKeys) | 
					
						
							|  |  |  | 		# test.:8053 { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	  # REFUSE IN A for the server zone (test.) | 
					
						
							|  |  |  | 		template IN A { | 
					
						
							|  |  |  | 			rcode REFUSED | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-01-15 18:27:47 -08:00
										 |  |  | 		# Fallthrough everything IN TXT for test. | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 		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\"" | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-08-14 17:55:55 +02:00
										 |  |  | 		# Answer example. ip templates and fallthrough otherwise | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 		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") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-05-13 23:23:28 -04:00
										 |  |  | 	if code != dns.RcodeServerFailure { | 
					
						
							|  |  |  | 		t.Fatalf("TestMultiSection expected SERVFAIL resolving something.example. IN MX, got %v, %v", code, dns.RcodeToString[code]) | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-09 22:29:19 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | const rcodeFallthrough = 3841 // reserved for private use, used to indicate a fallthrough |