2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-21 08:33:20 +00:00

test(xcat-core): capture the DNS update retry sending two TSIG records

send_ddns_update signs the same packet on every attempt. Net::DNS appends the
TSIG to the additional section, so the second attempt carries two TSIG records.
named answers FORMERR to that message, which is neither NOTAUTH nor SERVFAIL, so
the routine stops and reports FORMERR. The retry path can never be accepted.

ddns_update_retry.t drives send_ddns_update with a resolver that answers FORMERR
to a message with more than one TSIG record, as named does, and otherwise
answers a scripted rcode. It asserts that every attempt carries exactly one TSIG
record and the same update records, and that a retry answered NOERROR reports
success. Both subtests fail before the fix.

The header of each new test records that XCATROOT must name the tree under test,
because xCAT::Table adds the installed /opt/xcat/lib/perl to @INC.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
Daniel Hilst
2026-09-04 18:49:28 -03:00
parent 4eb9718548
commit 69be7872f2
2 changed files with 148 additions and 0 deletions
@@ -3,6 +3,10 @@
# makedns must sign every update with the algorithm the named.conf key stanza declares.
# named matches a TSIG key by name AND algorithm, so a stanza that does not agree with the
# signature makes named reject every update and makedns exit 1.
#
# Run this test with XCATROOT set to the tree under test. xCAT::Table does
# "use lib $::XCATROOT/lib/perl", so an installed /opt/xcat shadows the modules under test:
# XCATROOT=$PWD/xCAT-server prove xCAT-test/unit/ddns_named_key_algorithm.t
use strict;
use warnings;
+144
View File
@@ -0,0 +1,144 @@
#!/usr/bin/env perl
# send_ddns_update retries a rejected dynamic DNS update. Net::DNS appends the TSIG to the
# additional section, so every attempt must sign its own request: named answers FORMERR to a
# message that carries two TSIG records (measured on BIND 9.18.33).
#
# Run this test with XCATROOT set to the tree under test. xCAT::Table does
# "use lib $::XCATROOT/lib/perl", so an installed /opt/xcat shadows the modules under test:
# XCATROOT=$PWD/xCAT-server prove xCAT-test/unit/ddns_update_retry.t
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/../../xCAT-server/lib";
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
use lib "$FindBin::Bin/../../perl-xCAT";
use Test::More;
$ENV{XCATCFG} ||= 'SQLite:/tmp';
$ENV{XCATROOT} ||= "$FindBin::Bin/../../xCAT-server";
my $ddns_plugin_path =
"$FindBin::Bin/../../xCAT-server/lib/xcat/plugins/ddns.pm";
if ( -f $ddns_plugin_path ) {
require $ddns_plugin_path;
}
else {
require xCAT_plugin::ddns;
}
my $SECRET = 'c2VjcmV0LXNlY3JldC1zZWNyZXQtc2VjcmV0LXNlY3I=';
my $ZONE = 'test.lab';
subtest 'every attempt of a rejected update carries one TSIG' => sub {
my $resolver = Local::DDNS::Resolver->new( replies => [ 'NOTAUTH', 'NOTAUTH', 'NOTAUTH' ] );
my $rc = send_update($resolver);
is( $rc, 1, 'a persistently rejected update reports failure' );
is( scalar( @{ $resolver->{sent} } ), 3, 'the update is sent three times' );
is_deeply(
[ map { $_->{tsig_count} } @{ $resolver->{sent} } ],
[ 1, 1, 1 ],
'each attempt carries exactly one TSIG record'
);
is_deeply(
[ map { $_->{updates} } @{ $resolver->{sent} } ],
[ ( ['n1.test.lab. 300 IN A 10.0.0.1'] ) x 3 ],
'each attempt carries the same update records'
);
};
subtest 'a retry can be accepted' => sub {
my $resolver = Local::DDNS::Resolver->new( replies => [ 'NOTAUTH', 'NOERROR' ] );
my $rc = send_update($resolver);
is( $rc, 0, 'the accepted retry reports success' );
is( scalar( @{ $resolver->{sent} } ), 2, 'the update is sent twice' );
};
done_testing();
#---------------------------------------------------------------------------
=head3 send_update
Description: Send one dynamic DNS update through send_ddns_update.
Arguments: the recording resolver
Returns: the send_ddns_update return code
=cut
#---------------------------------------------------------------------------
sub send_update {
my ($resolver) = @_;
my $settings = xCAT::DHCP::OmapiPolicy->settings(
site_values => {
dhcpomapialgorithm => 'hmac-sha256',
dhcpomapikeyname => undef,
dhcpomshellpath => undef,
}
);
die "Unusable OMAPI settings: $settings->{error}" if $settings->{error};
my $ctx = {
omapi_settings => $settings,
privkey => $SECRET,
};
my $update = Net::DNS::Update->new($ZONE);
$update->push( update => Net::DNS::RR->new('n1.test.lab. 300 IN A 10.0.0.1') );
no warnings qw(redefine once);
local *xCAT::SvrUtils::sendmsg = sub { return; };
# Net::DNS 1.36 removed sign_tsig($name, $secret) and signs from a key file. Report the
# version that signs from a KEY RR, so the test needs no key file.
local $Net::DNS::VERSION = '1.25';
return xCAT_plugin::ddns::send_ddns_update( $ctx, $resolver, $update, $ZONE, 'n1.test.lab' );
}
{
package Local::DDNS::Resolver;
# Answer FORMERR to a message with more than one TSIG record, as named does, and otherwise
# answer the next scripted rcode.
sub new {
my ( $class, %args ) = @_;
return bless { replies => $args{replies}, sent => [] }, $class;
}
sub send {
my ( $self, $packet ) = @_;
my @tsig = grep { $_->type eq 'TSIG' } $packet->additional;
push @{ $self->{sent} },
{
tsig_count => scalar(@tsig),
updates => [ map { $_->plain } $packet->authority ],
};
my $rcode = shift @{ $self->{replies} };
$rcode = 'SERVFAIL' unless defined $rcode;
$rcode = 'FORMERR' if @tsig > 1;
return Local::DDNS::Reply->new($rcode);
}
}
{
package Local::DDNS::Reply;
sub new {
my ( $class, $rcode ) = @_;
return bless { rcode => $rcode }, $class;
}
sub header { return $_[0]; }
sub rcode { return $_[0]->{rcode}; }
}