Test server behavior when sending various UPDATE requests

Send update messages for zones with CLASS0, ANY and NONE.  The class
ANY UPDATE also attempts to delete a KX record in an existing IN
class zone to trigger a REQUIRE.

Test that the server is still running.
This commit is contained in:
Evan Hunt 2026-03-09 15:50:04 +11:00 committed by Michał Kępień
parent 3b596adbd2
commit bb24573580
No known key found for this signature in database
4 changed files with 71 additions and 20 deletions

View file

@ -12,7 +12,7 @@
import socket
import struct
from dns import rdataclass, rdatatype, update
from dns import message, rdataclass, rdatatype, update
import pytest
@ -35,6 +35,7 @@ def encode_name(name: str) -> bytes:
@pytest.mark.parametrize(
"rdtype,rdclass,ttl,rdata",
[
(rdatatype.SRV, rdataclass.NONE, 0, b"\x00\x00\x00\x00\x00\x00\x01"),
(rdatatype.SRV, rdataclass.NONE, 0, b"\x00"),
(rdatatype.KX, rdataclass.NONE, 0, b""),
(rdatatype.PX, rdataclass.NONE, 0, b""),
@ -69,7 +70,7 @@ def test_class_invalid(rdtype, rdclass, ttl, rdata, named_port):
s.sendall(packet)
try:
rwire = s.recv(4096)
res = dns.message.from_wire(rwire)
res = message.from_wire(rwire)
isctest.check.formerr(res)
except Exception: # pylint: disable=broad-except
pass
@ -94,3 +95,43 @@ def test_class_chaosupdate(rdtype, rdata):
up.add("foo.example.", 300, rdtype, rdata)
res = isctest.query.tcp(up, "10.53.0.2")
isctest.check.notimp(res)
def test_class_undefined(ns2):
up = update.UpdateMessage(".", rdclass=257)
up.present(".", 0)
up.answer[0].rdclass = rdataclass.NONE
with ns2.watch_log_from_here() as watcher:
res = isctest.query.tcp(up, "10.53.0.2")
isctest.check.notimp(res)
watcher.wait_for_line("invalid message class: CLASS257")
def test_class_zero(ns2):
up = update.UpdateMessage(".", rdclass=0)
up.present(".", 0)
up.answer[0].rdclass = rdataclass.NONE
with ns2.watch_log_from_here() as watcher:
res = isctest.query.tcp(up, "10.53.0.2")
isctest.check.formerr(res)
watcher.wait_for_line("message class could not be determined")
def test_class_any(ns2):
up = update.UpdateMessage(".", rdclass=rdataclass.ANY)
up.present(".", 0)
up.answer[0].rdclass = rdataclass.NONE
with ns2.watch_log_from_here() as watcher:
res = isctest.query.tcp(up, "10.53.0.2")
isctest.check.formerr(res)
watcher.wait_for_line("message parsing failed: FORMERR")
def test_class_none(ns2):
up = update.UpdateMessage(".", rdclass=rdataclass.NONE)
up.present(".", 0)
up.answer[0].rdclass = rdataclass.NONE
with ns2.watch_log_from_here() as watcher:
res = isctest.query.tcp(up, "10.53.0.2")
isctest.check.formerr(res)
watcher.wait_for_line("message parsing failed: FORMERR")

View file

@ -35,6 +35,7 @@ update.nil IN SOA ns1.example.nil. hostmaster.example.nil. (
3600 ; minimum (1 hour)
)
update.nil. NS ns1.update.nil.
update.nil. KX 0 .
ns1.update.nil. A 10.53.0.2
ns2.update.nil. AAAA ::1
EOF

View file

@ -485,8 +485,10 @@ grep "status: NOERROR" dig.out.ns1.$n >/dev/null || ret=1
n=$((n + 1))
ret=0
echo_i "check that TYPE=0 update is handled ($n)"
nextpart ns1/named.run >/dev/null
echo "a0e4280000010000000100000000060001c00c000000fe000000000000" \
| $PERL ../packet.pl -a 10.53.0.1 -p ${PORT} -t tcp >/dev/null || ret=1
| $PERL ../packet.pl -a 10.53.0.1 -p ${PORT} -t tcp -b >/dev/null || ret=1
wait_for_log 2 "message parsing failed: FORMERR" ns1/named.run || ret=1
$DIG $DIGOPTS +tcp version.bind txt ch @10.53.0.1 >dig.out.ns1.$n || ret=1
grep "status: NOERROR" dig.out.ns1.$n >/dev/null || ret=1
[ $ret = 0 ] || {
@ -497,20 +499,10 @@ grep "status: NOERROR" dig.out.ns1.$n >/dev/null || ret=1
n=$((n + 1))
ret=0
echo_i "check that TYPE=0 additional data is handled ($n)"
nextpart ns1/named.run >/dev/null
echo "a0e4280000010000000000010000060001c00c000000fe000000000000" \
| $PERL ../packet.pl -a 10.53.0.1 -p ${PORT} -t tcp >/dev/null || ret=1
$DIG $DIGOPTS +tcp version.bind txt ch @10.53.0.1 >dig.out.ns1.$n || ret=1
grep "status: NOERROR" dig.out.ns1.$n >/dev/null || ret=1
[ $ret = 0 ] || {
echo_i "failed"
status=1
}
n=$((n + 1))
ret=0
echo_i "check that update to undefined class is handled ($n)"
echo "a0e4280000010001000000000000060101c00c000000fe000000000000" \
| $PERL ../packet.pl -a 10.53.0.1 -p ${PORT} -t tcp >/dev/null || ret=1
| $PERL ../packet.pl -a 10.53.0.1 -p ${PORT} -t tcp -b >/dev/null || ret=1
wait_for_log 2 "message parsing failed: FORMERR" ns1/named.run || ret=1
$DIG $DIGOPTS +tcp version.bind txt ch @10.53.0.1 >dig.out.ns1.$n || ret=1
grep "status: NOERROR" dig.out.ns1.$n >/dev/null || ret=1
[ $ret = 0 ] || {

View file

@ -40,6 +40,7 @@
# -p <port>: specify port
# -t <protocol>: specify UDP or TCP
# -r <num>: send packet <num> times
# -b: blocking io
# -d: dump response packets
#
# If not specified, address defaults to 127.0.0.1, port to 53, protocol
@ -51,6 +52,8 @@ use strict;
use Getopt::Std;
use IO::File;
use IO::Socket;
use Net::DNS;
use Net::DNS::Packet;
sub usage {
print ("Usage: packet.pl [-a address] [-d] [-p port] [-t (tcp|udp)] [-r <repeats>] [file]\n");
@ -61,8 +64,6 @@ my $sock;
my $proto;
sub dumppacket {
use Net::DNS;
use Net::DNS::Packet;
my $rin;
my $rout;
@ -96,7 +97,7 @@ sub dumppacket {
}
my %options={};
getopts("a:dp:t:r:", \%options);
getopts("a:bdp:t:r:", \%options);
my $addr = "127.0.0.1";
$addr = $options{a} if defined $options{a};
@ -111,6 +112,8 @@ usage if ($proto !~ /^(udp|tcp)$/);
my $repeats = 1;
$repeats = $options{r} if defined $options{r};
my $blocking = defined $options{b} ? 1 : 0;
my $file = "STDIN";
if (@ARGV >= 1) {
my $filename = shift @ARGV;
@ -132,8 +135,22 @@ my $len = length $data;
my $output = unpack("H*", $data);
print ("sending $repeats time(s): $output\n");
if (defined $options{d}) {
my $request;
if ($Net::DNS::VERSION > 0.68) {
$request = new Net::DNS::Packet(\$data, 0);
$@ and die $@;
} else {
my $err;
($request, $err) = new Net::DNS::Packet(\$data, 0);
$err and die $err;
}
$request->print;
}
$sock = IO::Socket::INET->new(PeerAddr => $addr, PeerPort => $port,
Blocking => 0,
Blocking => $blocking,
Proto => $proto,) or die "$!";
STDOUT->autoflush(1);