mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-30 17:53:21 -04:00 
			
		
		
		
	Rewrite edns0 (#561)
* Add edns0 code rewrite * check arg count * change `new`; set EDNS0 if request doesn't have it set * change set to replace_or_append * change to append_or_replace * return error in new * update documents * fixt UT * return error * go fmt * Rework for more general EDNS0 use Also changed how rules are created and validated. Implements EDNS0 NSID in addition to local. * go fmt * README updates, NSID tests and fixes * gofmt -s -w * Fix tests for rewrite syntax change * Add tests, fix error message * Review nits * Missed on nit * More tests, integration test, fix edns0 parse issue * Fix README, use RewriteIgnored * go fmt
This commit is contained in:
		
				
					committed by
					
						 Miek Gieben
						Miek Gieben
					
				
			
			
				
	
			
			
			
						parent
						
							d1bb4ea130
						
					
				
				
					commit
					ef315ef3e2
				
			| @@ -1,6 +1,8 @@ | ||||
| package rewrite | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"reflect" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/coredns/coredns/middleware" | ||||
| @@ -16,14 +18,73 @@ func msgPrinter(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, err | ||||
| 	return 0, nil | ||||
| } | ||||
|  | ||||
| func TestNewRule(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		args        []string | ||||
| 		shouldError bool | ||||
| 		expType     reflect.Type | ||||
| 	}{ | ||||
| 		{[]string{}, true, nil}, | ||||
| 		{[]string{"foo"}, true, nil}, | ||||
| 		{[]string{"name"}, true, nil}, | ||||
| 		{[]string{"name", "a.com"}, true, nil}, | ||||
| 		{[]string{"name", "a.com", "b.com", "c.com"}, true, nil}, | ||||
| 		{[]string{"name", "a.com", "b.com"}, false, reflect.TypeOf(&nameRule{})}, | ||||
| 		{[]string{"type"}, true, nil}, | ||||
| 		{[]string{"type", "a"}, true, nil}, | ||||
| 		{[]string{"type", "any", "a", "a"}, true, nil}, | ||||
| 		{[]string{"type", "any", "a"}, false, reflect.TypeOf(&typeRule{})}, | ||||
| 		{[]string{"type", "XY", "WV"}, true, nil}, | ||||
| 		{[]string{"type", "ANY", "WV"}, true, nil}, | ||||
| 		{[]string{"class"}, true, nil}, | ||||
| 		{[]string{"class", "IN"}, true, nil}, | ||||
| 		{[]string{"class", "ch", "in", "in"}, true, nil}, | ||||
| 		{[]string{"class", "ch", "in"}, false, reflect.TypeOf(&classRule{})}, | ||||
| 		{[]string{"class", "XY", "WV"}, true, nil}, | ||||
| 		{[]string{"class", "IN", "WV"}, true, nil}, | ||||
| 		{[]string{"edns0"}, true, nil}, | ||||
| 		{[]string{"edns0", "local"}, true, nil}, | ||||
| 		{[]string{"edns0", "local", "set"}, true, nil}, | ||||
| 		{[]string{"edns0", "local", "set", "0xffee"}, true, nil}, | ||||
| 		{[]string{"edns0", "local", "set", "65518", "abcdefg"}, false, reflect.TypeOf(&edns0LocalRule{})}, | ||||
| 		{[]string{"edns0", "local", "set", "0xffee", "abcdefg"}, false, reflect.TypeOf(&edns0LocalRule{})}, | ||||
| 		{[]string{"edns0", "local", "append", "0xffee", "abcdefg"}, false, reflect.TypeOf(&edns0LocalRule{})}, | ||||
| 		{[]string{"edns0", "local", "replace", "0xffee", "abcdefg"}, false, reflect.TypeOf(&edns0LocalRule{})}, | ||||
| 		{[]string{"edns0", "local", "foo", "0xffee", "abcdefg"}, true, nil}, | ||||
| 		{[]string{"edns0", "local", "set", "0xffee", "0xabcdefg"}, true, nil}, | ||||
| 		{[]string{"edns0", "nsid", "set", "junk"}, true, nil}, | ||||
| 		{[]string{"edns0", "nsid", "set"}, false, reflect.TypeOf(&edns0NsidRule{})}, | ||||
| 		{[]string{"edns0", "nsid", "append"}, false, reflect.TypeOf(&edns0NsidRule{})}, | ||||
| 		{[]string{"edns0", "nsid", "replace"}, false, reflect.TypeOf(&edns0NsidRule{})}, | ||||
| 		{[]string{"edns0", "nsid", "foo"}, true, nil}, | ||||
| 	} | ||||
|  | ||||
| 	for i, tc := range tests { | ||||
| 		r, err := newRule(tc.args...) | ||||
| 		if err == nil && tc.shouldError { | ||||
| 			t.Errorf("Test %d: expected error but got success", i) | ||||
| 		} else if err != nil && !tc.shouldError { | ||||
| 			t.Errorf("Test %d: expected success but got error: %s", i, err) | ||||
| 		} | ||||
|  | ||||
| 		if !tc.shouldError && reflect.TypeOf(r) != tc.expType { | ||||
| 			t.Errorf("Test %d: expected %q but got %q", i, tc.expType, r) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestRewrite(t *testing.T) { | ||||
| 	rules := []Rule{} | ||||
| 	r, _ := newNameRule("from.nl.", "to.nl.") | ||||
| 	rules = append(rules, r) | ||||
| 	r, _ = newClassRule("CH", "IN") | ||||
| 	rules = append(rules, r) | ||||
| 	r, _ = newTypeRule("ANY", "HINFO") | ||||
| 	rules = append(rules, r) | ||||
|  | ||||
| 	rw := Rewrite{ | ||||
| 		Next: middleware.HandlerFunc(msgPrinter), | ||||
| 		Rules: []Rule{ | ||||
| 			Fields["name"].New("from.nl.", "to.nl."), | ||||
| 			Fields["class"].New("CH", "IN"), | ||||
| 			Fields["type"].New("ANY", "HINFO"), | ||||
| 		}, | ||||
| 		Next:     middleware.HandlerFunc(msgPrinter), | ||||
| 		Rules:    rules, | ||||
| 		noRevert: true, | ||||
| 	} | ||||
|  | ||||
| @@ -56,7 +117,7 @@ func TestRewrite(t *testing.T) { | ||||
|  | ||||
| 		resp := rec.Msg | ||||
| 		if resp.Question[0].Name != tc.to { | ||||
| 			t.Errorf("Test %d: Expected Name to be '%s' but was '%s'", i, tc.to, resp.Question[0].Name) | ||||
| 			t.Errorf("Test %d: Expected Name to be %q but was %q", i, tc.to, resp.Question[0].Name) | ||||
| 		} | ||||
| 		if resp.Question[0].Qtype != tc.toT { | ||||
| 			t.Errorf("Test %d: Expected Type to be '%d' but was '%d'", i, tc.toT, resp.Question[0].Qtype) | ||||
| @@ -65,79 +126,162 @@ func TestRewrite(t *testing.T) { | ||||
| 			t.Errorf("Test %d: Expected Class to be '%d' but was '%d'", i, tc.toC, resp.Question[0].Qclass) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 	/* | ||||
| 		regexps := [][]string{ | ||||
| 			{"/reg/", ".*", "/to", ""}, | ||||
| 			{"/r/", "[a-z]+", "/toaz", "!.html|"}, | ||||
| 			{"/url/", "a([a-z0-9]*)s([A-Z]{2})", "/to/{path}", ""}, | ||||
| 			{"/ab/", "ab", "/ab?{query}", ".txt|"}, | ||||
| 			{"/ab/", "ab", "/ab?type=html&{query}", ".html|"}, | ||||
| 			{"/abc/", "ab", "/abc/{file}", ".html|"}, | ||||
| 			{"/abcd/", "ab", "/a/{dir}/{file}", ".html|"}, | ||||
| 			{"/abcde/", "ab", "/a#{fragment}", ".html|"}, | ||||
| 			{"/ab/", `.*\.jpg`, "/ajpg", ""}, | ||||
| 			{"/reggrp", `/ad/([0-9]+)([a-z]*)`, "/a{1}/{2}", ""}, | ||||
| 			{"/reg2grp", `(.*)`, "/{1}", ""}, | ||||
| 			{"/reg3grp", `(.*)/(.*)/(.*)`, "/{1}{2}{3}", ""}, | ||||
| func TestRewriteEDNS0Local(t *testing.T) { | ||||
| 	rw := Rewrite{ | ||||
| 		Next:     middleware.HandlerFunc(msgPrinter), | ||||
| 		noRevert: true, | ||||
| 	} | ||||
|  | ||||
| 	tests := []struct { | ||||
| 		fromOpts []dns.EDNS0 | ||||
| 		args     []string | ||||
| 		toOpts   []dns.EDNS0 | ||||
| 	}{ | ||||
| 		{ | ||||
| 			[]dns.EDNS0{}, | ||||
| 			[]string{"local", "set", "0xffee", "0xabcdef"}, | ||||
| 			[]dns.EDNS0{&dns.EDNS0_LOCAL{Code: 0xffee, Data: []byte{0xab, 0xcd, 0xef}}}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			[]dns.EDNS0{}, | ||||
| 			[]string{"local", "append", "0xffee", "abcdefghijklmnop"}, | ||||
| 			[]dns.EDNS0{&dns.EDNS0_LOCAL{Code: 0xffee, Data: []byte("abcdefghijklmnop")}}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			[]dns.EDNS0{}, | ||||
| 			[]string{"local", "replace", "0xffee", "abcdefghijklmnop"}, | ||||
| 			[]dns.EDNS0{}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			[]dns.EDNS0{}, | ||||
| 			[]string{"nsid", "set"}, | ||||
| 			[]dns.EDNS0{&dns.EDNS0_NSID{Code: dns.EDNS0NSID, Nsid: ""}}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			[]dns.EDNS0{}, | ||||
| 			[]string{"nsid", "append"}, | ||||
| 			[]dns.EDNS0{&dns.EDNS0_NSID{Code: dns.EDNS0NSID, Nsid: ""}}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			[]dns.EDNS0{}, | ||||
| 			[]string{"nsid", "replace"}, | ||||
| 			[]dns.EDNS0{}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	ctx := context.TODO() | ||||
| 	for i, tc := range tests { | ||||
| 		m := new(dns.Msg) | ||||
| 		m.SetQuestion("example.com.", dns.TypeA) | ||||
| 		m.Question[0].Qclass = dns.ClassINET | ||||
|  | ||||
| 		r, err := newEdns0Rule(tc.args...) | ||||
| 		if err != nil { | ||||
| 			t.Errorf("Error creating test rule: %s", err) | ||||
| 			continue | ||||
| 		} | ||||
| 		rw.Rules = []Rule{r} | ||||
|  | ||||
| 		for _, regexpRule := range regexps { | ||||
| 			var ext []string | ||||
| 			if s := strings.Split(regexpRule[3], "|"); len(s) > 1 { | ||||
| 				ext = s[:len(s)-1] | ||||
| 			} | ||||
| 			rule, err := NewComplexRule(regexpRule[0], regexpRule[1], regexpRule[2], 0, ext, nil) | ||||
| 			if err != nil { | ||||
| 				t.Fatal(err) | ||||
| 			} | ||||
| 			rw.Rules = append(rw.Rules, rule) | ||||
| 		rec := dnsrecorder.New(&test.ResponseWriter{}) | ||||
| 		rw.ServeDNS(ctx, rec, m) | ||||
|  | ||||
| 		resp := rec.Msg | ||||
| 		o := resp.IsEdns0() | ||||
| 		if o == nil { | ||||
| 			t.Errorf("Test %d: EDNS0 options not set", i) | ||||
| 			continue | ||||
| 		} | ||||
| 	*/ | ||||
| 	/* | ||||
| 		statusTests := []struct { | ||||
| 			status         int | ||||
| 			base           string | ||||
| 			to             string | ||||
| 			regexp         string | ||||
| 			statusExpected bool | ||||
| 		}{ | ||||
| 			{400, "/status", "", "", true}, | ||||
| 			{400, "/ignore", "", "", false}, | ||||
| 			{400, "/", "", "^/ignore", false}, | ||||
| 			{400, "/", "", "(.*)", true}, | ||||
| 			{400, "/status", "", "", true}, | ||||
| 		if !optsEqual(o.Option, tc.toOpts) { | ||||
| 			t.Errorf("Test %d: Expected %v but got %v", i, tc.toOpts, o) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 		for i, s := range statusTests { | ||||
| 			urlPath := fmt.Sprintf("/status%d", i) | ||||
| 			rule, err := NewComplexRule(s.base, s.regexp, s.to, s.status, nil, nil) | ||||
| 			if err != nil { | ||||
| 				t.Fatalf("Test %d: No error expected for rule but found %v", i, err) | ||||
| 			} | ||||
| 			rw.Rules = []Rule{rule} | ||||
| 			req, err := http.NewRequest("GET", urlPath, nil) | ||||
| 			if err != nil { | ||||
| 				t.Fatalf("Test %d: Could not create HTTP request: %v", i, err) | ||||
| 			} | ||||
| func TestEdns0LocalMultiRule(t *testing.T) { | ||||
| 	rules := []Rule{} | ||||
| 	r, _ := newEdns0Rule("local", "replace", "0xffee", "abcdef") | ||||
| 	rules = append(rules, r) | ||||
| 	r, _ = newEdns0Rule("local", "set", "0xffee", "fedcba") | ||||
| 	rules = append(rules, r) | ||||
|  | ||||
| 			rec := httptest.NewRecorder() | ||||
| 			code, err := rw.ServeHTTP(rec, req) | ||||
| 			if err != nil { | ||||
| 				t.Fatalf("Test %d: No error expected for handler but found %v", i, err) | ||||
| 	rw := Rewrite{ | ||||
| 		Next:     middleware.HandlerFunc(msgPrinter), | ||||
| 		Rules:    rules, | ||||
| 		noRevert: true, | ||||
| 	} | ||||
|  | ||||
| 	tests := []struct { | ||||
| 		fromOpts []dns.EDNS0 | ||||
| 		toOpts   []dns.EDNS0 | ||||
| 	}{ | ||||
| 		{ | ||||
| 			nil, | ||||
| 			[]dns.EDNS0{&dns.EDNS0_LOCAL{Code: 0xffee, Data: []byte("fedcba")}}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			[]dns.EDNS0{&dns.EDNS0_LOCAL{Code: 0xffee, Data: []byte("foobar")}}, | ||||
| 			[]dns.EDNS0{&dns.EDNS0_LOCAL{Code: 0xffee, Data: []byte("abcdef")}}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	ctx := context.TODO() | ||||
| 	for i, tc := range tests { | ||||
| 		m := new(dns.Msg) | ||||
| 		m.SetQuestion("example.com.", dns.TypeA) | ||||
| 		m.Question[0].Qclass = dns.ClassINET | ||||
| 		if tc.fromOpts != nil { | ||||
| 			o := m.IsEdns0() | ||||
| 			if o == nil { | ||||
| 				m.SetEdns0(4096, true) | ||||
| 				o = m.IsEdns0() | ||||
| 			} | ||||
| 			if s.statusExpected { | ||||
| 				if rec.Body.String() != "" { | ||||
| 					t.Errorf("Test %d: Expected empty body but found %s", i, rec.Body.String()) | ||||
| 			o.Option = append(o.Option, tc.fromOpts...) | ||||
| 		} | ||||
| 		rec := dnsrecorder.New(&test.ResponseWriter{}) | ||||
| 		rw.ServeDNS(ctx, rec, m) | ||||
|  | ||||
| 		resp := rec.Msg | ||||
| 		o := resp.IsEdns0() | ||||
| 		if o == nil { | ||||
| 			t.Errorf("Test %d: EDNS0 options not set", i) | ||||
| 			continue | ||||
| 		} | ||||
| 		if !optsEqual(o.Option, tc.toOpts) { | ||||
| 			t.Errorf("Test %d: Expected %v but got %v", i, tc.toOpts, o) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func optsEqual(a, b []dns.EDNS0) bool { | ||||
| 	if len(a) != len(b) { | ||||
| 		return false | ||||
| 	} | ||||
| 	for i := range a { | ||||
| 		switch aa := a[i].(type) { | ||||
| 		case *dns.EDNS0_LOCAL: | ||||
| 			if bb, ok := b[i].(*dns.EDNS0_LOCAL); ok { | ||||
| 				if aa.Code != bb.Code { | ||||
| 					return false | ||||
| 				} | ||||
| 				if code != s.status { | ||||
| 					t.Errorf("Test %d: Expected status code %d found %d", i, s.status, code) | ||||
| 				if !bytes.Equal(aa.Data, bb.Data) { | ||||
| 					return false | ||||
| 				} | ||||
| 			} else { | ||||
| 				if code != 0 { | ||||
| 					t.Errorf("Test %d: Expected no status code found %d", i, code) | ||||
| 				} | ||||
| 				return false | ||||
| 			} | ||||
| 		case *dns.EDNS0_NSID: | ||||
| 			if bb, ok := b[i].(*dns.EDNS0_NSID); ok { | ||||
| 				if aa.Nsid != bb.Nsid { | ||||
| 					return false | ||||
| 				} | ||||
| 			} else { | ||||
| 				return false | ||||
| 			} | ||||
| 		default: | ||||
| 			return false | ||||
| 		} | ||||
| 	*/ | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user