From 99b683aa41a647a46f60ce3c2c922631d830868c Mon Sep 17 00:00:00 2001 From: Ncesam <138844952+Ncesam@users.noreply.github.com> Date: Thu, 16 Jul 2026 05:05:01 +0400 Subject: [PATCH] plugin/transfer: collect all notify errors instead of shadowing (#8283) * plugin/transfer: collect all notify errors instead of shadowing Signed-off-by: ncesam * plugin/transfer: add regression test for notify multiple failures Signed-off-by: ncesam --------- Signed-off-by: ncesam --- plugin/transfer/notify.go | 7 ++-- plugin/transfer/notify_test.go | 61 ++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/plugin/transfer/notify.go b/plugin/transfer/notify.go index 9a938ae51..c6539878f 100644 --- a/plugin/transfer/notify.go +++ b/plugin/transfer/notify.go @@ -1,6 +1,7 @@ package transfer import ( + "errors" "fmt" "net" @@ -25,17 +26,17 @@ func (t *Transfer) Notify(zone string) error { } c := notifyClient(x) - var err1 error + var errs error for _, t := range x.to { if t == "*" { continue } if err := sendNotify(c, m, t); err != nil { - err1 = err + errs = errors.Join(errs, err) } } log.Debugf("Sent notifies for zone %q to %v", zone, x.to) - return err1 // this only captures the last error + return errs } func notifyClient(x *xfr) *dns.Client { diff --git a/plugin/transfer/notify_test.go b/plugin/transfer/notify_test.go index 214f74e5e..69a461bbc 100644 --- a/plugin/transfer/notify_test.go +++ b/plugin/transfer/notify_test.go @@ -2,7 +2,10 @@ package transfer import ( "net" + "strings" "testing" + + "github.com/miekg/dns" ) func TestNotifyClientSource(t *testing.T) { @@ -25,3 +28,61 @@ func TestNotifyClientSource(t *testing.T) { t.Fatalf("expected source address %s, got %s", source, addr.IP) } } + +func TestNotifyMultipleFailures(t *testing.T) { + // Start two local UDP listeners returning different rcodes + l1, err := net.ListenPacket("udp", "127.0.0.1:0") + if err != nil { + t.Fatalf("failed to listen: %v", err) + } + defer l1.Close() + + l2, err := net.ListenPacket("udp", "127.0.0.1:0") + if err != nil { + t.Fatalf("failed to listen: %v", err) + } + defer l2.Close() + + serve := func(conn net.PacketConn, rcode int) { + buf := make([]byte, 512) + for { + n, addr, err := conn.ReadFrom(buf) + if err != nil { + return + } + msg := new(dns.Msg) + if err := msg.Unpack(buf[:n]); err != nil { + continue + } + msg.SetReply(msg) + msg.Rcode = rcode + out, _ := msg.Pack() + conn.WriteTo(out, addr) + } + } + + go serve(l1, dns.RcodeServerFailure) + go serve(l2, dns.RcodeRefused) + + tr := &Transfer{ + xfrs: []*xfr{ + { + Zones: []string{"example.com."}, + to: []string{l1.LocalAddr().String(), l2.LocalAddr().String()}, + }, + }, + } + + err = tr.Notify("example.com.") + if err == nil { + t.Fatal("expected error from Notify, got nil") + } + + errStr := err.Error() + if !strings.Contains(errStr, l1.LocalAddr().String()) || !strings.Contains(errStr, "SERVFAIL") { + t.Errorf("expected error to contain %q and SERVFAIL, got: %v", l1.LocalAddr().String(), errStr) + } + if !strings.Contains(errStr, l2.LocalAddr().String()) || !strings.Contains(errStr, "REFUSED") { + t.Errorf("expected error to contain %q and REFUSED, got: %v", l2.LocalAddr().String(), errStr) + } +}