mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-27 08:14:18 -04:00 
			
		
		
		
	Added minimal-responses plugin (#4417)
* Added minimal-responses plugin Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com> * Removed unnecessary comments * Updated tests Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com> * Reformated imports Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com> * Updated package name Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com> * Removed unnecessary comments Co-authored-by: Miek Gieben <miek@miek.nl> * Added changes Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com> * updated Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com> * Updated comment for NextOrFailure Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com> * Updated to test.Case for testing Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com> * Formated imports using goimports Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com> Co-authored-by: Miek Gieben <miek@miek.nl>
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							74ef6e00f1
						
					
				
				
					commit
					7651e6c4de
				
			| @@ -37,6 +37,7 @@ var Directives = []string{ | ||||
| 	"rewrite", | ||||
| 	"dnssec", | ||||
| 	"autopath", | ||||
| 	"minimal", | ||||
| 	"template", | ||||
| 	"transfer", | ||||
| 	"hosts", | ||||
|   | ||||
| @@ -36,6 +36,7 @@ import ( | ||||
| 	_ "github.com/coredns/coredns/plugin/loop" | ||||
| 	_ "github.com/coredns/coredns/plugin/metadata" | ||||
| 	_ "github.com/coredns/coredns/plugin/metrics" | ||||
| 	_ "github.com/coredns/coredns/plugin/minimal" | ||||
| 	_ "github.com/coredns/coredns/plugin/nsid" | ||||
| 	_ "github.com/coredns/coredns/plugin/pprof" | ||||
| 	_ "github.com/coredns/coredns/plugin/ready" | ||||
|   | ||||
| @@ -46,6 +46,7 @@ cache:cache | ||||
| rewrite:rewrite | ||||
| dnssec:dnssec | ||||
| autopath:autopath | ||||
| minimal:minimal | ||||
| template:template | ||||
| transfer:transfer | ||||
| hosts:hosts | ||||
|   | ||||
							
								
								
									
										36
									
								
								plugin/minimal/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								plugin/minimal/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| # minimal | ||||
|  | ||||
| ## Name | ||||
|  | ||||
| *minimal* - minimizes size of the DNS response message whenever possible. | ||||
|  | ||||
| ## Description | ||||
|  | ||||
| The *minimal* plugin tries to minimize the size of the response. Depending on the response type it  | ||||
| removes resource records from the AUTHORITY and ADDITIONAL sections. | ||||
|  | ||||
| Specifically this plugin looks at successful responses (this excludes negative responses, i.e. | ||||
| nodata or name error). If the successful response isn't a delegation only the RRs in the answer | ||||
| section are written to the client. | ||||
|  | ||||
| ## Syntax | ||||
|  | ||||
| ~~~ txt | ||||
| minimal | ||||
| ~~~ | ||||
|  | ||||
| ## Examples | ||||
|  | ||||
| Enable minimal responses: | ||||
|  | ||||
| ~~~ corefile | ||||
| example.org { | ||||
|     whoami | ||||
|     forward . 8.8.8.8 | ||||
|     minimal | ||||
| } | ||||
| ~~~ | ||||
|  | ||||
| ## See Also | ||||
|  | ||||
| [BIND 9 Configuration Reference](https://bind9.readthedocs.io/en/latest/reference.html#boolean-options) | ||||
							
								
								
									
										54
									
								
								plugin/minimal/minimal.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								plugin/minimal/minimal.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| package minimal | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/coredns/coredns/plugin" | ||||
| 	"github.com/coredns/coredns/plugin/pkg/nonwriter" | ||||
| 	"github.com/coredns/coredns/plugin/pkg/response" | ||||
| 	"github.com/miekg/dns" | ||||
| ) | ||||
|  | ||||
| // minimalHandler implements the plugin.Handler interface. | ||||
| type minimalHandler struct { | ||||
| 	Next plugin.Handler | ||||
| } | ||||
|  | ||||
| func (m *minimalHandler) Name() string { return "minimal" } | ||||
|  | ||||
| func (m *minimalHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { | ||||
| 	nw := nonwriter.New(w) | ||||
|  | ||||
| 	rcode, err := plugin.NextOrFailure(m.Name(), m.Next, ctx, nw, r) | ||||
| 	if err != nil { | ||||
| 		return rcode, err | ||||
| 	} | ||||
|  | ||||
| 	ty, _ := response.Typify(nw.Msg, time.Now().UTC()) | ||||
| 	cl := response.Classify(ty) | ||||
|  | ||||
| 	// if response is Denial or Error pass through also if the type is Delegation pass through | ||||
| 	if cl == response.Denial || cl == response.Error || ty == response.Delegation { | ||||
| 		w.WriteMsg(nw.Msg) | ||||
| 		return 0, nil | ||||
| 	} | ||||
| 	if ty != response.NoError { | ||||
| 		w.WriteMsg(nw.Msg) | ||||
| 		return 0, plugin.Error("minimal", fmt.Errorf("unhandled response type %q for %q", ty, nw.Msg.Question[0].Name)) | ||||
| 	} | ||||
|  | ||||
| 	// copy over the original Msg params, deep copy not required as RRs are not modified | ||||
| 	d := &dns.Msg{ | ||||
| 		MsgHdr:   nw.Msg.MsgHdr, | ||||
| 		Compress: nw.Msg.Compress, | ||||
| 		Question: nw.Msg.Question, | ||||
| 		Answer:   nw.Msg.Answer, | ||||
| 		Ns:       nil, | ||||
| 		Extra:    nil, | ||||
| 	} | ||||
|  | ||||
| 	w.WriteMsg(d) | ||||
| 	return 0, nil | ||||
| } | ||||
							
								
								
									
										152
									
								
								plugin/minimal/minimal_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								plugin/minimal/minimal_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| package minimal | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/coredns/coredns/plugin" | ||||
| 	"github.com/coredns/coredns/plugin/pkg/dnstest" | ||||
| 	"github.com/coredns/coredns/plugin/test" | ||||
| 	"github.com/miekg/dns" | ||||
| ) | ||||
|  | ||||
| // testHandler implements plugin.Handler and will be used to create a stub handler for the test | ||||
| type testHandler struct { | ||||
| 	Response *test.Case | ||||
| 	Next     plugin.Handler | ||||
| } | ||||
|  | ||||
| func (t *testHandler) Name() string { return "test-handler" } | ||||
|  | ||||
| func (t *testHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { | ||||
| 	d := new(dns.Msg) | ||||
| 	d.SetReply(r) | ||||
| 	if t.Response != nil { | ||||
| 		d.Answer = t.Response.Answer | ||||
| 		d.Ns = t.Response.Ns | ||||
| 		d.Extra = t.Response.Extra | ||||
| 		d.Rcode = t.Response.Rcode | ||||
| 	} | ||||
| 	w.WriteMsg(d) | ||||
| 	return 0, nil | ||||
| } | ||||
|  | ||||
| func TestMinimizeResponse(t *testing.T) { | ||||
| 	baseAnswer := []dns.RR{ | ||||
| 		test.A("example.com.  293 IN A 142.250.76.46"), | ||||
| 	} | ||||
| 	baseNs := []dns.RR{ | ||||
| 		test.NS("example.com.  157127 IN NS ns2.example.com."), | ||||
| 		test.NS("example.com.  157127 IN NS ns1.example.com."), | ||||
| 		test.NS("example.com.  157127 IN NS ns3.example.com."), | ||||
| 		test.NS("example.com.  157127 IN NS ns4.example.com."), | ||||
| 	} | ||||
|  | ||||
| 	baseExtra := []dns.RR{ | ||||
| 		test.A("ns2.example.com. 316273 IN A 216.239.34.10"), | ||||
| 		test.AAAA("ns2.example.com. 157127 IN AAAA 2001:4860:4802:34::a"), | ||||
| 		test.A("ns3.example.com. 316274 IN A 216.239.36.10"), | ||||
| 		test.AAAA("ns3.example.com. 157127 IN AAAA 2001:4860:4802:36::a"), | ||||
| 		test.A("ns1.example.com. 165555 IN A 216.239.32.10"), | ||||
| 		test.AAAA("ns1.example.com. 165555 IN AAAA 2001:4860:4802:32::a"), | ||||
| 		test.A("ns4.example.com. 190188 IN A 216.239.38.10"), | ||||
| 		test.AAAA("ns4.example.com. 157127 IN AAAA 2001:4860:4802:38::a"), | ||||
| 	} | ||||
|  | ||||
| 	tests := []struct { | ||||
| 		active   bool | ||||
| 		original test.Case | ||||
| 		minimal  test.Case | ||||
| 	}{ | ||||
| 		{ // minimization possible NoError case | ||||
| 			original: test.Case{ | ||||
| 				Answer: baseAnswer, | ||||
| 				Ns:     nil, | ||||
| 				Extra:  baseExtra, | ||||
| 				Rcode:  0, | ||||
| 			}, | ||||
| 			minimal: test.Case{ | ||||
| 				Answer: baseAnswer, | ||||
| 				Ns:     nil, | ||||
| 				Extra:  nil, | ||||
| 				Rcode:  0, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ // delegate response case | ||||
| 			original: test.Case{ | ||||
| 				Answer: nil, | ||||
| 				Ns:     baseNs, | ||||
| 				Extra:  baseExtra, | ||||
| 				Rcode:  0, | ||||
| 			}, | ||||
| 			minimal: test.Case{ | ||||
| 				Answer: nil, | ||||
| 				Ns:     baseNs, | ||||
| 				Extra:  baseExtra, | ||||
| 				Rcode:  0, | ||||
| 			}, | ||||
| 		}, { // negative response case | ||||
| 			original: test.Case{ | ||||
| 				Answer: baseAnswer, | ||||
| 				Ns:     baseNs, | ||||
| 				Extra:  baseExtra, | ||||
| 				Rcode:  2, | ||||
| 			}, | ||||
| 			minimal: test.Case{ | ||||
| 				Answer: baseAnswer, | ||||
| 				Ns:     baseNs, | ||||
| 				Extra:  baseExtra, | ||||
| 				Rcode:  2, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for i, tc := range tests { | ||||
| 		req := new(dns.Msg) | ||||
| 		req.SetQuestion("example.com", dns.TypeA) | ||||
|  | ||||
| 		tHandler := &testHandler{ | ||||
| 			Response: &tc.original, | ||||
| 			Next:     nil, | ||||
| 		} | ||||
| 		o := &minimalHandler{Next: tHandler} | ||||
| 		rec := dnstest.NewRecorder(&test.ResponseWriter{}) | ||||
| 		_, err := o.ServeDNS(context.TODO(), rec, req) | ||||
|  | ||||
| 		if err != nil { | ||||
| 			t.Errorf("Expected no error, but got %q", err) | ||||
| 		} | ||||
|  | ||||
| 		if len(tc.minimal.Answer) != len(rec.Msg.Answer) { | ||||
| 			t.Errorf("Test %d: Expected %d Answer, but got %d", i, len(tc.minimal.Answer), len(req.Answer)) | ||||
| 			continue | ||||
| 		} | ||||
| 		if len(tc.minimal.Ns) != len(rec.Msg.Ns) { | ||||
| 			t.Errorf("Test %d: Expected %d Ns, but got %d", i, len(tc.minimal.Ns), len(req.Ns)) | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		if len(tc.minimal.Extra) != len(rec.Msg.Extra) { | ||||
| 			t.Errorf("Test %d: Expected %d Extras, but got %d", i, len(tc.minimal.Extra), len(req.Extra)) | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		for j, a := range rec.Msg.Answer { | ||||
| 			if tc.minimal.Answer[j].String() != a.String() { | ||||
| 				t.Errorf("Test %d: Expected Answer %d to be %v, but got %v", i, j, tc.minimal.Answer[j], a) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		for j, a := range rec.Msg.Ns { | ||||
| 			if tc.minimal.Ns[j].String() != a.String() { | ||||
| 				t.Errorf("Test %d: Expected NS %d to be %v, but got %v", i, j, tc.minimal.Ns[j], a) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		for j, a := range rec.Msg.Extra { | ||||
| 			if tc.minimal.Extra[j].String() != a.String() { | ||||
| 				t.Errorf("Test %d: Expected Extra %d to be %v, but got %v", i, j, tc.minimal.Extra[j], a) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										24
									
								
								plugin/minimal/setup.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								plugin/minimal/setup.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| package minimal | ||||
|  | ||||
| import ( | ||||
| 	"github.com/coredns/caddy" | ||||
| 	"github.com/coredns/coredns/core/dnsserver" | ||||
| 	"github.com/coredns/coredns/plugin" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	plugin.Register("minimal", setup) | ||||
| } | ||||
|  | ||||
| func setup(c *caddy.Controller) error { | ||||
| 	c.Next() | ||||
| 	if c.NextArg() { | ||||
| 		return plugin.Error("minimal", c.ArgErr()) | ||||
| 	} | ||||
|  | ||||
| 	dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { | ||||
| 		return &minimalHandler{Next: next} | ||||
| 	}) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										19
									
								
								plugin/minimal/setup_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								plugin/minimal/setup_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| package minimal | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/coredns/caddy" | ||||
| ) | ||||
|  | ||||
| func TestSetup(t *testing.T) { | ||||
| 	c := caddy.NewTestController("dns", `minimal-response`) | ||||
| 	if err := setup(c); err != nil { | ||||
| 		t.Fatalf("Expected no errors, but got: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	c = caddy.NewTestController("dns", `minimal-response example.org`) | ||||
| 	if err := setup(c); err == nil { | ||||
| 		t.Fatalf("Expected errors, but got: %v", err) | ||||
| 	} | ||||
| } | ||||
| @@ -69,7 +69,7 @@ func (f HandlerFunc) Name() string { return "handlerfunc" } | ||||
| // Error returns err with 'plugin/name: ' prefixed to it. | ||||
| func Error(name string, err error) error { return fmt.Errorf("%s/%s: %s", "plugin", name, err) } | ||||
|  | ||||
| // NextOrFailure calls next.ServeDNS when next is not nil, otherwise it will return, a ServerFailure and a nil error. | ||||
| // NextOrFailure calls next.ServeDNS when next is not nil, otherwise it will return, a ServerFailure and a `no next plugin found` error. | ||||
| func NextOrFailure(name string, next Handler, ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { // nolint: golint | ||||
| 	if next != nil { | ||||
| 		if span := ot.SpanFromContext(ctx); span != nil { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user