diff --git a/plugin/pkg/replacer/replacer.go b/plugin/pkg/replacer/replacer.go index 81261ca79..845f11bc3 100644 --- a/plugin/pkg/replacer/replacer.go +++ b/plugin/pkg/replacer/replacer.go @@ -86,7 +86,7 @@ func appendValue(b []byte, state request.Request, rr *dnstest.Recorder, label st return strconv.AppendInt(b, int64(state.Size()), 10) // Recorded replacements. case "{rcode}": - if rr == nil { + if rr == nil || rr.Msg == nil { return append(b, EmptyValue...) } if rcode := dns.RcodeToString[rr.Rcode]; rcode != "" { diff --git a/plugin/pkg/replacer/replacer_test.go b/plugin/pkg/replacer/replacer_test.go index 3e0a50691..25dd37a5b 100644 --- a/plugin/pkg/replacer/replacer_test.go +++ b/plugin/pkg/replacer/replacer_test.go @@ -393,3 +393,52 @@ func TestMetadataMalformed(t *testing.T) { } } } + +func TestNoResponseWasWritten(t *testing.T) { + w := dnstest.NewRecorder(&test.ResponseWriter{}) + r := new(dns.Msg) + r.SetQuestion("example.org.", dns.TypeHINFO) + r.Id = 1053 + r.AuthenticatedData = true + r.CheckingDisabled = true + state := request.Request{W: w, Req: r} + + replacer := New() + ctx := context.TODO() + + // This couples the test very tightly to the code, but so be it. + expect := map[string]string{ + "{type}": "HINFO", + "{name}": "example.org.", + "{class}": "IN", + "{proto}": "udp", + "{size}": "29", + "{remote}": "10.240.0.1", + "{port}": "40212", + "{local}": "127.0.0.1", + headerReplacer + "id}": "1053", + headerReplacer + "opcode}": "0", + headerReplacer + "do}": "false", + headerReplacer + "bufsize}": "512", + "{rcode}": "-", + "{rsize}": "0", + "{duration}": "0", + headerReplacer + "rflags}": "-", + } + if len(expect) != len(labels) { + t.Fatalf("Expect %d labels, got %d", len(expect), len(labels)) + } + + for lbl := range labels { + repl := replacer.Replace(ctx, state, w, lbl) + if lbl == "{duration}" { + if repl[len(repl)-1] != 's' { + t.Errorf("Expected seconds, got %q", repl) + } + continue + } + if repl != expect[lbl] { + t.Errorf("Expected value %q, got %q", expect[lbl], repl) + } + } +}