From 6a41acf2dbede6163bf829942beee06954c34f75 Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Fri, 4 Sep 2026 18:53:32 -0300 Subject: [PATCH] fix(xcat-core): the retried DNS update carries two TSIG records makedns reports "error was FORMERR" for an update that named rejected with NOTAUTH. The FORMERR is the answer to the retry, not to the first attempt. send_ddns_update in ddns.pm signs the packet the caller built, and signs that same packet again on each attempt. Net::DNS::Packet::sign_tsig appends the TSIG to the additional section, so the second attempt sends two TSIG records and named answers FORMERR. FORMERR is neither NOTAUTH nor SERVFAIL, so the routine stops and reports it. The NOTAUTH and SERVFAIL retry can never be accepted, on any algorithm. Each attempt now signs a request of its own. A packet cannot be unsigned again, so ddns_update_request copies the prerequisite and update records into a new Net::DNS::Update instead, and the caller keeps the unsigned original. ddns_update_retry.t fails before this change: the second attempt carries two TSIG records, and an update that the retry answers with NOERROR still reports failure. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- xCAT-server/lib/xcat/plugins/ddns.pm | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/xCAT-server/lib/xcat/plugins/ddns.pm b/xCAT-server/lib/xcat/plugins/ddns.pm index 99c291d67..c46cf9686 100644 --- a/xCAT-server/lib/xcat/plugins/ddns.pm +++ b/xCAT-server/lib/xcat/plugins/ddns.pm @@ -1579,6 +1579,26 @@ sub update_namedconf { } } +#------------------------------------------------------------------------------- + +=head3 ddns_update_request + + Descriptions: Copy the records of one dynamic DNS update into a new, unsigned request. + Arguments: the update the caller built, the zone name + Returns: a Net::DNS::Update holding the same prerequisite and update records + +=cut + +#------------------------------------------------------------------------------- +sub ddns_update_request { + my ($update, $zone) = @_; + + my $request = Net::DNS::Update->new($zone); + $request->push(pre => $_) for $update->answer; + $request->push(update => $_) for $update->authority; + return $request; +} + # Send a signed dynamic DNS update, retrying transient rejections. Right after a zone (re)load named # can reply NOTAUTH, or SERVFAIL before the zone is ready to accept dynamic updates; both are # recoverable, so retry a few times (pausing on SERVFAIL). Returns 0 only when the update was @@ -1589,8 +1609,11 @@ sub send_ddns_update { my ($ctx, $resolver, $update, $zone, $entry) = @_; for my $attempt (1 .. 3) { - ddns_sign_update($ctx, $update); - my $reply = $resolver->send($update); + # sign_tsig appends the TSIG to the additional section. A second signature on the same + # packet sends two TSIG records, and named answers FORMERR. Sign a copy for each attempt. + my $request = ddns_update_request($update, $zone); + ddns_sign_update($ctx, $request); + my $reply = $resolver->send($request); if (!$reply) { xCAT::SvrUtils::sendmsg([ 1, "No reply received when sending DNS update to zone $zone" ], $callback); return 1;