diff --git a/plugin/rewrite/edns0.go b/plugin/rewrite/edns0.go index fbb72ece3..ddaaf97e5 100644 --- a/plugin/rewrite/edns0.go +++ b/plugin/rewrite/edns0.go @@ -46,6 +46,9 @@ type edns0SetResponseRule struct { func (r *edns0SetResponseRule) RewriteResponse(res *dns.Msg, _ dns.RR) { ednsOpt := res.IsEdns0() + if ednsOpt == nil { + return + } for idx, opt := range ednsOpt.Option { if opt.Option() == r.code { ednsOpt.Option = append(ednsOpt.Option[:idx], ednsOpt.Option[idx+1:]...) @@ -61,6 +64,9 @@ type edns0ReplaceResponseRule[T dns.EDNS0] struct { func (r *edns0ReplaceResponseRule[T]) RewriteResponse(res *dns.Msg, _ dns.RR) { ednsOpt := res.IsEdns0() + if ednsOpt == nil { + return + } for idx, opt := range ednsOpt.Option { if opt.Option() == r.code { ednsOpt.Option[idx] = r.source diff --git a/plugin/rewrite/reverter_test.go b/plugin/rewrite/reverter_test.go index d15029915..59cebf714 100644 --- a/plugin/rewrite/reverter_test.go +++ b/plugin/rewrite/reverter_test.go @@ -195,3 +195,116 @@ func doValueReverterTests(t *testing.T, name string, rules []Rule) { } } } + +var edns0NoOPTRevertTests = []struct { + name string + args []string + req func() *dns.Msg +}{ + { + name: "local_set_revert", + args: []string{ + "edns0", "local", "set", + "0xffee", "0xabcdef", + "revert", + }, + req: func() *dns.Msg { + m := new(dns.Msg) + m.SetQuestion("example.org.", dns.TypeA) + m.Question[0].Qclass = dns.ClassINET + return m + }, + }, + { + name: "local_replace_revert", + args: []string{ + "edns0", "local", "replace", + "0xffee", "0x222222", + "revert", + }, + req: func() *dns.Msg { + m := new(dns.Msg) + m.SetQuestion("example.org.", dns.TypeA) + m.Question[0].Qclass = dns.ClassINET + + opt := new(dns.OPT) + opt.Hdr.Name = "." + opt.Hdr.Rrtype = dns.TypeOPT + + opt.Option = append(opt.Option, + &dns.EDNS0_LOCAL{ + Code: 0xffee, + Data: []byte{0x11, 0x11, 0x11}, + }, + ) + + m.Extra = append(m.Extra, opt) + + return m + }, + }, +} + +func TestEDNS0ResponseReverterNoOPT(t *testing.T) { + ctx := context.TODO() + + for _, tc := range edns0NoOPTRevertTests { + t.Run(tc.name, func(t *testing.T) { + r, err := newRule(tc.args...) + if err != nil { + t.Fatalf("cannot parse rule: %s", err) + } + + rw := Rewrite{ + Next: plugin.HandlerFunc(noOPTMsgPrinter), + Rules: []Rule{r}, + RevertPolicy: NewRevertPolicy(false, false), + } + + rec := dnstest.NewRecorder(&test.ResponseWriter{}) + + rw.ServeDNS(ctx, rec, tc.req()) + + resp := rec.Msg + if resp == nil { + t.Fatal("expected response") + } + + if resp.Rcode != dns.RcodeSuccess { + t.Fatalf( + "expected rcode %d got %d", + dns.RcodeSuccess, + resp.Rcode, + ) + } + + if len(resp.Answer) != 1 { + t.Fatalf( + "expected 1 answer got %d", + len(resp.Answer), + ) + } + + if resp.IsEdns0() != nil { + t.Fatalf("expected response without OPT record") + } + }) + } +} + +func noOPTMsgPrinter(_ context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { + m := new(dns.Msg) + m.SetReply(r) + + m.Answer = []dns.RR{ + test.A("example.org. 60 IN A 127.0.0.1"), + } + + // Deliberately do NOT add an OPT RR. + + if err := w.WriteMsg(m); err != nil { + return dns.RcodeServerFailure, err + } + + return dns.RcodeSuccess, nil +}