use Getopt::Std;
use Net::DNS;
use Net::DNS::Update;
use Net::DNS::Resolver;
$opt_s = "127.0.0.1";
$opt_p = 53;
getopt('s:p:');
$res = new Net::DNS::Resolver;
$res->nameservers($opt_s);
$res->port($opt_p);
$res->defnames(0);
@ARGV == 1 or die
"usage: perl update_test.pl [-s server] [-p port] zone\n";
$zone = shift @ARGV;
my $failures = 0;
sub assert {
my ($cond, $explanation) = @_;
if (!$cond) {
print "I:Test Failed: $explanation ***\n";
$failures++
}
}
sub test {
my ($expected, @records) = @_;
my $update = new Net::DNS::Update("$zone");
foreach $rec (@records) {
$update->push(@$rec);
}
$reply = $res->send($update);
if (defined $reply) {
my $rcode = $reply->header->rcode;
assert($rcode eq $expected, "expected $expected, got $rcode");
} else {
print "I:Update failed: ", $res->errorstring, "\n";
}
}
sub section {
my ($msg) = @_;
print "I:$msg\n";
}
section("Delete any leftovers from previous tests");
test("NOERROR", ["update", rr_del("a.$zone")]);
test("NOERROR", ["update", rr_del("b.$zone")]);
test("NOERROR", ["update", rr_del("c.$zone")]);
test("NOERROR", ["update", rr_del("d.$zone")]);
test("NOERROR", ["update", rr_del("e.$zone")]);
test("NOERROR", ["update", rr_del("f.$zone")]);
test("NOERROR", ["update", rr_del("ns.s.$zone")]);
test("NOERROR", ["update", rr_del("s.$zone")]);
test("NOERROR", ["update", rr_del("t.$zone")]);
test("NOERROR", ["update", rr_del("*.$zone")]);
test("NOERROR", ["update", rr_del("u.$zone")]);
test("NOERROR", ["update", rr_del("a.u.$zone")]);
test("NOERROR", ["update", rr_del("b.u.$zone")]);
section("Simple prerequisites in the absence of data");
test("NXDOMAIN", ["pre", yxdomain("a.$zone")]);
test("NXRRSET", ["pre", yxrrset("a.$zone A")]);
test("NOERROR", ["pre", nxdomain("a.$zone")]);
test("NOERROR", ["pre", nxrrset("a.$zone A")]);
test("NXRRSET", ["pre", yxrrset("a.$zone 300 A 73.80.65.49")]);
section ("Simple creation of data");
test("NOERROR", ["update", rr_add("a.$zone 300 A 73.80.65.49")]);
section ("Simple prerequisites in the presence of data");
test("NOERROR", ["pre", yxdomain("a.$zone")]);
test("NOERROR", ["pre", yxrrset("a.$zone A")]);
test("YXDOMAIN", ["pre", nxdomain("a.$zone")]);
test("YXRRSET", ["pre", nxrrset("a.$zone A")]);
test("NOERROR", ["pre", yxrrset("a.$zone 300 A 73.80.65.49")]);
test("NOERROR", ["update", rr_add("a.$zone 300 A 73.80.65.50")]);
section("Detailed tests of \"RRset exists (value dependent)\" prerequisites");
test("NOERROR", ["pre",
yxrrset("a.$zone 300 A 73.80.65.49"),
yxrrset("a.$zone 300 A 73.80.65.50")]);
test("NOERROR", ["pre",
yxrrset("a.$zone 300 A 73.80.65.50"),
yxrrset("a.$zone 300 A 73.80.65.49")]);
test("NXRRSET", ["pre", yxrrset("a.$zone 300 A 73.80.65.49")]);
test("NXRRSET", ["pre", yxrrset("a.$zone 300 A 73.80.65.50")]);
test("NXRRSET", ["pre",
yxrrset("a.$zone 300 A 73.80.65.49"),
yxrrset("a.$zone 300 A 73.80.65.50"),
yxrrset("a.$zone 300 A 73.80.65.51")]);
section("Torture test of \"RRset exists (value dependent)\" prerequisites.");
test("NOERROR", ["update",
rr_add("e.$zone 300 A 73.80.65.49"),
rr_add("e.$zone 300 TXT 'one'"),
rr_add("e.$zone 300 A 73.80.65.50")]);
test("NOERROR", ["update",
rr_add("e.$zone 300 A 73.80.65.52"),
rr_add("f.$zone 300 A 73.80.65.52"),
rr_add("e.$zone 300 A 73.80.65.51")]);
test("NOERROR", ["update",
rr_add("e.$zone 300 TXT 'three'"),
rr_add("e.$zone 300 TXT 'two'")]);
test("NOERROR", ["update",
rr_add("e.$zone 300 MX 10 mail.$zone")]);
test("NOERROR", ["pre",
yxrrset("e.$zone 300 A 73.80.65.52"),
yxrrset("e.$zone 300 TXT 'two'"),
yxrrset("e.$zone 300 A 73.80.65.51"),
yxrrset("e.$zone 300 TXT 'three'"),
yxrrset("e.$zone 300 A 73.80.65.50"),
yxrrset("f.$zone 300 A 73.80.65.52"),
yxrrset("e.$zone 300 A 73.80.65.49"),
yxrrset("e.$zone 300 TXT 'one'")]);
section("Subtraction of RRsets");
test("NOERROR", ["update", rr_del("a.$zone 300 A 73.80.65.49")]);
test("NOERROR", ["pre",
yxrrset("a.$zone 300 A 73.80.65.50")]);
test("NOERROR", ["update", rr_del("a.$zone 300 A 73.80.65.50")]);
test("NOERROR", ["pre", nxrrset("a.$zone 300 A")]);
test("NOERROR", ["pre", nxdomain("a.$zone")]);
section("Other forms of deletion");
test("NOERROR", ["update", rr_add("a.$zone 300 A 73.80.65.49")]);
test("NOERROR", ["update", rr_add("a.$zone 300 A 73.80.65.50")]);
test("NOERROR", ["update", rr_add("a.$zone 300 MX 10 mail.$zone")]);
test("NOERROR", ["update", rr_del("a.$zone 300 A")]);
test("NOERROR", ["pre", nxrrset("a.$zone 300 A")]);
test("NOERROR", ["update", rr_add("a.$zone 300 A 73.80.65.49")]);
test("NOERROR", ["update", rr_add("a.$zone 300 A 73.80.65.50")]);
test("NOERROR", ["update", rr_del("a.$zone")]);
test("NOERROR", ["pre", nxdomain("a.$zone")]);
section("Case insensitivity");
test("NOERROR", ["update", rr_add("a.$zone 300 PTR foo.net.")]);
test("NOERROR", ["pre", yxrrset("A.$zone 300 PTR fOo.NeT.")]);
section("Special CNAME rules");
test("NOERROR", ["update", rr_add("b.$zone 300 CNAME foo.net.")]);
test("NOERROR", ["update", rr_add("b.$zone 300 A 73.80.65.49")]);
test("NOERROR", ["pre", yxrrset("b.$zone 300 CNAME foo.net.")]);
test("NOERROR", ["pre", nxrrset("b.$zone A")]);
test("NOERROR", ["update", rr_add("c.$zone 300 A 73.80.65.49")]);
test("NOERROR", ["update", rr_add("c.$zone 300 CNAME foo.net.")]);
test("NOERROR", ["pre", yxrrset("c.$zone A")]);
test("NOERROR", ["pre", nxrrset("c.$zone CNAME")]);
section("Special NS rules");
test("NOERROR", ["update",
rr_add("$zone 300 NS ns1.$zone"),
rr_add("$zone 300 NS ns2.$zone")]);
test("NOERROR", ["update", rr_del("$zone 300 NS ns1.$zone")]);
test("NOERROR", ["update", rr_del("$zone 300 NS ns2.$zone")]);
test("NOERROR", ["pre",
yxrrset("$zone 300 NS ns2.$zone")]);
test("NOERROR", ["update", rr_add("n.$zone 300 NS ns1.$zone")]);
test("NOERROR", ["update", rr_del("n.$zone 300 NS ns1.$zone")]);
test("NOERROR", ["pre", nxrrset("n.$zone 300 NS")]);
test("NOERROR", ["update", rr_add("n.$zone 300 NS ns1.$zone")]);
test("NOERROR", ["update", rr_del("n.$zone 300 NS")]);
test("NOERROR", ["pre", nxrrset("n.$zone 300 NS")]);
test("NOERROR", ["update", rr_add("n.$zone 300 NS ns1.$zone")]);
test("NOERROR", ["pre", yxrrset("n.$zone 300 NS")]);
test("NOERROR", ["update", rr_del("n.$zone")]);
test("NOERROR", ["pre", nxrrset("n.$zone 300 NS")]);
test("NOERROR", ["update", rr_del("$zone NS")]);
test("NOERROR", ["pre",
yxrrset("$zone 300 NS ns2.$zone")]);
test("NOERROR", ["update", rr_del("$zone")]);
test("NOERROR", ["pre",
yxrrset("$zone 300 NS ns2.$zone")]);
test("NOERROR", ["update", rr_del("$zone SOA")]);
test("NOERROR", ["pre", yxrrset("$zone SOA")]);
section("Idempotency");
test("NOERROR", ["update", rr_add("d.$zone 300 A 73.80.65.49")]);
test("NOERROR", ["pre", yxrrset("d.$zone 300 A 73.80.65.49")]);
test("NOERROR", ["update",
rr_add("d.$zone 300 A 73.80.65.49"),
rr_del("d.$zone A")]);
test("NOERROR", ["pre", nxrrset("d.$zone 300 A 73.80.65.49")]);
test("NOERROR", ["update", rr_del("d.$zone 300 A 73.80.65.49")]);
test("NOERROR", ["pre", nxrrset("d.$zone 300 A")]);
test("NOERROR", ["update",
rr_del("d.$zone 300 A"),
rr_add("d.$zone 300 A 73.80.65.49")]);
test("NOERROR", ["pre", yxrrset("d.$zone 300 A")]);
section("Out-of-zone prerequisites and updates");
test("NOTZONE", ["pre", yxrrset("a.somewhere.else. 300 A 73.80.65.49")]);
test("NOTZONE", ["update", rr_add("a.somewhere.else. 300 A 73.80.65.49")]);
section("Glue");
test("NOERROR", ["update", rr_add("s.$zone 300 NS ns.s.$zone")]);
test("NOERROR", ["update", rr_add("ns.s.$zone 300 A 73.80.65.49")]);
test("NOERROR", ["pre", yxrrset("ns.s.$zone 300 A 73.80.65.49")]);
section("Wildcards");
test("NOERROR", ["update", rr_add("*.$zone 300 MX 10 mail.$zone")]);
test("NOERROR", ["pre", yxrrset("*.$zone 300 MX 10 mail.$zone")]);
test("NXRRSET", ["pre", yxrrset("w.$zone 300 MX 10 mail.$zone")]);
test("NOERROR", ["pre", nxrrset("w.$zone MX")]);
test("NOERROR", ["pre", nxdomain("w.$zone")]);
section("SOA serial handling");
my $soatimers = "20 20 1814400 3600";
my $query = $res->query($zone, "SOA");
my ($old_soa) = $query->answer;
my $old_serial = $old_soa->serial;
my $new_serial = $old_serial + 10;
if ($new_serial > 0xFFFFFFFF) {
$new_serial -= 0x80000000;
$new_serial -= 0x80000000;
}
test("NOERROR", ["update", rr_add("$zone 300 SOA mname1. . $new_serial $soatimers")]);
($db_soa) = $res->query($zone, "SOA")->answer;
assert($db_soa->mname eq "mname1");
$new_serial = $old_serial - 10;
if ($new_serial < 0) {
$new_serial += 0x80000000;
$new_serial += 0x80000000;
}
test("NOERROR", ["update", rr_add("$zone 300 SOA mname2. . $new_serial $soatimers")]);
assert($db_soa->mname eq "mname1");
($old_soa) = $res->query($zone, "SOA")->answer;
$old_serial = $old_soa->serial;
test("NOERROR", ["update", rr_add("$zone 300 SOA mname3. . $old_serial " .
$soatimers)]);
($db_soa) = $res->query($zone, "SOA")->answer;
assert($db_soa->mname eq "mname1");
test("NOERROR", ["update", rr_add("a.$zone 300 TXT " . ("foo " x 3))]);
section("Updating TTLs only");
test("NOERROR", ["update", rr_add("t.$zone 300 A 73.80.65.49")]);
($a) = $res->query("t.$zone", "A")->answer;
$ttl = $a->ttl;
assert($ttl == 300, "incorrect TTL value $ttl != 300");
test("NOERROR", ["update",
rr_del("t.$zone 300 A 73.80.65.49"),
rr_add("t.$zone 301 A 73.80.65.49")]);
($a) = $res->query("t.$zone", "A")->answer;
$ttl = $a->ttl;
assert($ttl == 301, "incorrect TTL value $ttl != 301");
test("NOERROR", ["update", rr_add("t.$zone 302 A 73.80.65.50")]);
($a) = $res->query("t.$zone", "A")->answer;
$ttl = $a->ttl;
assert($ttl == 302, "incorrect TTL value $ttl != 302");
section("TTL normalization");
test("NOERROR", ["update", rr_add("t.$zone 303 A 73.80.65.51")]);
(@answers) = $res->query("t.$zone", "A")->answer;
$nanswers = scalar @answers;
assert($nanswers == 3, "wrong number of answers $nanswers != 3");
foreach $a (@answers) {
$ttl = $a->ttl;
assert($ttl == 303, "incorrect TTL value $ttl != 303");
}
section("Obscuring existing data by zone cut");
test("NOERROR", ["update", rr_add("a.u.$zone 300 A 73.80.65.49")]);
test("NOERROR", ["update", rr_add("b.u.$zone 300 A 73.80.65.49")]);
test("NOERROR", ["update", rr_add("u.$zone 300 TXT txt-not-in-nxt")]);
test("NOERROR", ["update", rr_add("u.$zone 300 NS ns.u.$zone")]);
test("NOERROR", ["update", rr_del("u.$zone 300 NS ns.u.$zone")]);
if ($failures) {
print "I:$failures tests failed.\n";
} else {
print "I:All tests successful.\n";
}
exit $failures;