From 1539f9b7d69f22e443bd6c1dc1246e77615a415e Mon Sep 17 00:00:00 2001 From: Immanuel Tikhonov <122638311+immanuwell@users.noreply.github.com> Date: Fri, 26 Jun 2026 12:58:20 +0400 Subject: [PATCH] fix(log): synthesize deferred error responses (#8200) Signed-off-by: immanuwell --- plugin/log/log.go | 7 ++++ plugin/log/log_test.go | 74 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/plugin/log/log.go b/plugin/log/log.go index 37ecc3084..2e68780f8 100644 --- a/plugin/log/log.go +++ b/plugin/log/log.go @@ -35,6 +35,13 @@ func (l Logger) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) rrw := dnstest.NewRecorder(w) rc, err := plugin.NextOrFailure(l.Name(), l.Next, ctx, rrw, r) + if rrw.Msg == nil && !plugin.ClientWrite(rc) { + msg := new(dns.Msg) + msg.SetRcode(r, rc) + rrw.Msg = msg + rrw.Rcode = rc + rrw.Len = msg.Len() + } tpe, _ := response.Typify(rrw.Msg, time.Now().UTC()) metadata.SetValueFunc(ctx, "log/type", func() string { diff --git a/plugin/log/log_test.go b/plugin/log/log_test.go index 28040016c..015dd599d 100644 --- a/plugin/log/log_test.go +++ b/plugin/log/log_test.go @@ -112,6 +112,80 @@ func TestLoggedClassError(t *testing.T) { } } +func TestLoggedSynthesizesDeferredServerFailure(t *testing.T) { + rule := Rule{ + NameScope: ".", + Format: DefaultLogFormat, + Class: map[response.Class]struct{}{response.All: {}}, + } + + var f bytes.Buffer + log.SetOutput(&f) + + logger := Logger{ + Rules: []Rule{rule}, + repl: replacer.New(), + } + + ctx := context.TODO() + r := new(dns.Msg) + r.SetQuestion("example.org.", dns.TypeA) + + rec := dnstest.NewRecorder(&test.ResponseWriter{}) + + rcode, err := logger.ServeDNS(ctx, rec, r) + if err == nil { + t.Fatal("expected no next plugin error") + } + if rcode != dns.RcodeServerFailure { + t.Fatalf("expected SERVFAIL rcode, got %d", rcode) + } + + logged := f.String() + if !strings.Contains(logged, "SERVFAIL") { + t.Fatalf("expected SERVFAIL in log output, got %q", logged) + } + if strings.Contains(logged, "\"OTHERERROR ") { + t.Fatalf("expected synthesized server error classification, got %q", logged) + } +} + +func TestLoggedSynthesizesDeferredRefused(t *testing.T) { + rule := Rule{ + NameScope: ".", + Format: DefaultLogFormat, + Class: map[response.Class]struct{}{response.All: {}}, + } + + var f bytes.Buffer + log.SetOutput(&f) + + logger := Logger{ + Rules: []Rule{rule}, + Next: test.NextHandler(dns.RcodeRefused, nil), + repl: replacer.New(), + } + + ctx := context.TODO() + r := new(dns.Msg) + r.SetQuestion("example.org.", dns.TypeA) + + rec := dnstest.NewRecorder(&test.ResponseWriter{}) + + rcode, err := logger.ServeDNS(ctx, rec, r) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if rcode != dns.RcodeRefused { + t.Fatalf("expected REFUSED rcode, got %d", rcode) + } + + logged := f.String() + if !strings.Contains(logged, "REFUSED") { + t.Fatalf("expected REFUSED in log output, got %q", logged) + } +} + func TestLogged(t *testing.T) { tests := []struct { Rules []Rule