# -*- Perl -*- # Tests for CNAME records # Note that not all possible cases are caught! # Template records mean not all published records are natively present in the DB use strict; use warnings; use Test::More; use Data::Dumper; use lib 't'; use DNSTest; my $dtest = DNSTest::new; my ($code,$msg); my $rectype = 5; my $newname; my $newval; my $expirystamp; my $rcount; ## Domain tests subtest 'Domain tests' => sub { subtest 'CNAME add - new name' => sub { $newname = 'newname.example.com'; $newval = 'fredshosting.example.net'; ($code, $msg) = $dnsdb->addRec('n', 'n', 1, \$newname, \$rectype, \$newval, 900); ok( $code eq 'OK', "addRec() claimed succeess" ); if ($code eq 'OK') { # crosscheck in the DB ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE domain_id = 1 AND host = '$newname'"); ok( $rcount == 1, " ... [$rcount] yep, hostname only occurs once" ); } else { print "not ok: $msg"; } }; subtest 'CNAME add - existing/colliding non-CNAME' => sub { $newname = 'mx1.example.com'; ($code, $msg) = $dnsdb->addRec('n', 'n', 1, \$newname, \$rectype, \$newval, 900); ok( $code eq 'FAIL', "addRec() claimed failure" ); if ($code eq 'FAIL') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE domain_id = 1 AND host = '$newname' AND type <> 5"); ok( $rcount == 2, " ... [$rcount] record(s) with $newname already exist" ); like( $msg, qr/One or more non-CNAME records/, " ... returned matching error" ); } }; subtest 'CNAME add - existing/colliding CNAME' => sub { $newname = 'www.example.com'; ($code, $msg) = $dnsdb->addRec('n', 'n', 1, \$newname, \$rectype, \$newval, 900); ok( $code eq 'FAIL', "addRec() claimed failure" ); if ($code eq 'FAIL') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE domain_id = 1 AND host = '$newname' AND type = 5"); ok( $rcount == 1, " ... [$rcount] CNAME already exists" ); like( $msg, qr/already a CNAME present/, " ... returned matching error" ); } }; subtest 'CNAME update - non-CNAME to CNAME, non-colliding' => sub { $newname = 'smtp.example.com'; $newval = 'example.com'; ($code, $msg) = $dnsdb->updateRec('n', 'n', 39, 1, \$newname, \$rectype, \$newval, 900); ok( $code eq 'OK', "updateRec() claimed success" ); if ($code eq 'OK') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE domain_id = 1 AND host = '$newname'"); ok( $rcount == 1, " ... [$rcount] yep, hostname only occurs once" ); } else { print "not ok: $msg"; } }; subtest 'CNAME update - non-CNAME to CNAME, colliding' => sub { $newname = 'mx1.example.com'; ($code, $msg) = $dnsdb->updateRec('n', 'n', 39, 1, \$newname, \$rectype, \$newval, 900); ok( $code eq 'FAIL', "updateRec() claimed failure" ); if ($code eq 'FAIL') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE domain_id = 1 AND host = '$newname' AND type <> 5"); ok( $rcount == 2, " ... [$rcount] record(s) with $newname already exist" ); like( $msg, qr/One or more non-CNAME records/, " ... returned matching error" ); } }; subtest 'CNAME update - name to non-colliding name' => sub { $newname = 'imap.example.com'; ($code, $msg) = $dnsdb->updateRec('n', 'n', 37, 1, \$newname, \$rectype, \$newval, 900); ok( $code eq 'OK', "updateRec() claimed success" ); if ($code eq 'OK') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE domain_id = 1 AND host = '$newname'"); ok( $rcount == 1, " ... [$rcount] yep, hostname only occurs once" ); } else { print "not ok: $msg"; } }; subtest 'CNAME update - name to colliding name' => sub { $newname = 'mx1.example.com'; ($code, $msg) = $dnsdb->updateRec('n', 'n', 41, 1, \$newname, \$rectype, \$newval, 900); ok( $code eq 'FAIL', "updateRec() claimed failure" ); if ($code eq 'FAIL') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE domain_id = 1 AND host = '$newname' AND type <> 5"); ok( $rcount == 2, " ... [$rcount] record(s) with $newname already exist" ); like( $msg, qr/One or more non-CNAME records/, " ... returned matching error" ); } }; }; # domain tests ## Reverse zone tests subtest 'Reverse zone tests' => sub { subtest 'CNAME add - new reverse name' => sub { $newval = '192.168.2.12'; $newname = '12.8-29.2.168.192.in-addr.arpa'; ($code, $msg) = $dnsdb->addRec('n', 'y', 1, \$newname, \$rectype, \$newval, 900); ok( $code eq 'OK', "addRec() claimed succeess" ); if ($code eq 'OK') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE rdns_id = 1 AND val = '$newval'"); ok( $rcount == 1, " ... [$rcount] yep, IP only occurs once" ); } else { print "not ok: $msg\n"; } }; subtest 'CNAME add - existing/colliding non-CNAME' => sub { $newval = '192.168.2.14'; $newname = '14.8-29.2.168.192.in-addr.arpa'; ($code, $msg) = $dnsdb->addRec('n', 'y', 1, \$newname, \$rectype, \$newval, 900); ok( $code eq 'FAIL', "addRec() claimed failure" ); if ($code eq 'FAIL') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE rdns_id = 1 AND val = '$newval' AND type <> 5"); ok( $rcount == 2, " ... [$rcount] record(s) with $newname already exist" ); like( $msg, qr/One or more non-CNAME records/, " ... returned matching error" ); } }; subtest 'CNAME add - existing/colliding CNAME' => sub { $newval = '192.168.2.13'; $newname = '13.8-29.2.168.192.in-addr.arpa'; ($code, $msg) = $dnsdb->addRec('n', 'y', 1, \$newname, \$rectype, \$newval, 900); ok( $code eq 'FAIL', "addRec() claimed failure" ); if ($code eq 'FAIL') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE rdns_id = 1 AND val = '$newval' AND type = 5"); ok( $rcount == 1, " ... [$rcount] CNAME already exists" ); like( $msg, qr/already a CNAME present/, " ... returned matching error" ); } }; subtest 'CNAME update - non-CNAME to CNAME, non-colliding' => sub { $newval = '192.168.2.15'; $newname = '15-29.arpa.example.net'; ($code, $msg) = $dnsdb->updateRec('n', 'y', 43, 1, \$newname, \$rectype, \$newval, 900); ok( $code eq 'OK', "updateRec() claimed success" ); if ($code eq 'OK') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE rdns_id = 1 AND val = '$newval'"); ok( $rcount == 1, " ... [$rcount] yep, IP only occurs once" ); } else { print "not ok: $msg\n"; } }; subtest 'CNAME update - non-CNAME to CNAME, colliding' => sub { $newval = '192.168.2.14'; $newname = 'arpa14.rev.example.net'; ($code, $msg) = $dnsdb->updateRec('n', 'y', 42, 1, \$newname, \$rectype, \$newval, 900); ok( $code eq 'FAIL', "updateRec() claimed failure updating revzone record type to CNAME" ); if ($code eq 'FAIL') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE rdns_id = 1 AND val = '$newval' AND type <> 5"); ok( $rcount == 2, " ... [$rcount] record(s) with $newval already exist" ); like( $msg, qr/One or more non-CNAME records/, " ... returned matching error" ); } }; subtest 'CNAME update - name to non-colliding name' => sub { $newval = '192.168.2.11'; ($code, $msg) = $dnsdb->updateRec('n', 'y', 34, 1, \$newname, \$rectype, \$newval, 900); ok( $code eq 'OK', "updateRec() claimed success updating revzone CNAME \"hostname\" (non-colliding)" ); if ($code eq 'OK') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE rdns_id = 1 AND val = '$newval'"); ok( $rcount == 1, " ... [$rcount] yep, IP only occurs once" ); } else { print "not ok: $msg"; } }; subtest 'CNAME update - name to colliding name' => sub { $newval = '192.168.2.17'; ($code, $msg) = $dnsdb->updateRec('n', 'y', 46, 1, \$newname, \$rectype, \$newval, 900); ok( $code eq 'FAIL', "updateRec() claimed failure" ); if ($code eq 'FAIL') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE rdns_id = 1 AND val = '$newval' AND type <> 5"); ok( $rcount == 1, " ... [$rcount] record(s) with $newval already exist" ); like( $msg, qr/One or more non-CNAME records/, " ... returned matching error" ); } }; }; # reverse zone tests ## Record expiry/valid-after cases subtest 'Record expiry/valid-after' => sub { subtest 'CNAME add - nonexpiring' => sub { subtest ' - collision with expired record' => sub { $newname = 'expired1.expiry1.test'; $newval = 'target.example.com'; ($code, $msg) = $dnsdb->addRec('n', 'n', 4, \$newname, \$rectype, \$newval, 900); ok( $code eq 'OK', "addRec() claimed success" ); if ($code eq 'OK') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE domain_id = 4 AND host = '$newname' AND stampactive = 'f'"); ok( $rcount == 1, " ... [$rcount] yep, hostname only occurs once" ); } else { print "not ok: $msg"; } }; # this test arguably overkill, subsumed by earlier test for nonexpiring collision subtest ' - collision with soon to expire record' => sub { $newname = 'expired2.expiry1.test'; ($code, $msg) = $dnsdb->addRec('n', 'n', 4, \$newname, \$rectype, \$newval, 900); ok( $code eq 'FAIL', "addRec() claimed failure" ); if ($code eq 'FAIL') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE domain_id = 4 AND host = '$newname' AND type <> 5 AND stampactive = 't'"); ok( $rcount == 1, " ... [$rcount] record(s) with $newname already exist" ); # somewhat less overkill if we try to target a unique return based around the expiry bit like( $msg, qr/One or more non-CNAME records/, " ... returned matching error" ); } }; subtest ' - collision with pending active-after record' => sub { $newname = 'active-after1.expiry1.test'; ($code, $msg) = $dnsdb->addRec('n', 'n', 4, \$newname, \$rectype, \$newval, 900); ok( $code eq 'WARN', "addRec() claimed success with warning" ); if ($code eq 'WARN') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE domain_id = 4 AND host = '$newname'"); ok( $rcount == 2, " ... [$rcount] correct number of records for $newname" ); like( $msg, qr/added with expiry time; conflicting valid-after record found/, " ... returned appropriate warning message" ); my ($newstamp) = $dbh->selectrow_array("SELECT stamp FROM records WHERE domain_id = 4 AND host = '$newname' ". "AND stampactive = 't' AND expires = 't'"); my ($oldstamp) = $dbh->selectrow_array("SELECT stamp FROM records WHERE domain_id = 4 AND host = '$newname' ". "AND stampactive = 't' AND expires = 'f'"); ok( $newstamp eq $oldstamp, " ... coerced timestamp matches existing active-after timestamp" ); } else { print "not ok: $msg"; } }; subtest ' - collision with active active-after record' => sub { $newname = 'active-after2.expiry1.test'; ($code, $msg) = $dnsdb->addRec('n', 'n', 4, \$newname, \$rectype, \$newval, 900); ok( $code eq 'FAIL', "addRec() claimed failure" ); if ($code eq 'FAIL') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE domain_id = 4 AND host = '$newname' AND type <> 5"); ok( $rcount == 1, " ... [$rcount] record(s) with $newname already exist" ); like( $msg, qr/record with a valid-after time in the past/, " ... returned matching error" ); } }; }; # add non-timestamp CNAME subtest 'CNAME add - expires soon' => sub { my @ltime = localtime; $expirystamp = sprintf "%i-%i-%i %i:%i", $ltime[5] + 1900, $ltime[4] + 1, $ltime[3] + 3, 15, $ltime[1]; $newval = 'target.example.com'; subtest 'collision with nonexpiring record' => sub { $newname = 'expires-at1.expiry2.test'; ($code, $msg) = $dnsdb->addRec('n', 'n', 5, \$newname, \$rectype, \$newval, 900, undef, 't', $expirystamp); ok($code eq 'FAIL', "addRec() claimed failure"); if ($code eq 'FAIL') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE domain_id = 5 AND host = '$newname'"); ok( $rcount == 1, " ... [$rcount] yep, hostname only occurs once" ); like( $msg, qr/One or more non-CNAME records/, " ... returned matching error" ); } }; subtest 'collision with expiring record' => sub { $newname = 'expires-at2.expiry2.test'; ($code, $msg) = $dnsdb->addRec('n', 'n', 5, \$newname, \$rectype, \$newval, 900, undef, 't', $expirystamp); ok( $code eq 'FAIL', "addRec() claimed failure"); if ($code eq 'FAIL') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE domain_id = 5 AND host = '$newname' AND type <> 5"); ok( $rcount == 1, " ... [$rcount] record(s) with $newname already exist" ); like( $msg, qr/One or more non-CNAME records/, " ... returned matching error" ); } }; subtest 'expire before valid-after record' => sub { $newname = 'expires-at3.expiry2.test'; ($code, $msg) = $dnsdb->addRec('n', 'n', 5, \$newname, \$rectype, \$newval, 900, undef, 't', $expirystamp); ok( $code eq 'OK', "addRec() claimed success" ); if ($code eq 'OK') { # crosscheck in the DB ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE domain_id = 5 AND host = '$newname'"); ok( $rcount == 2, " ... [$rcount] correct number of records for $newname" ); my ($newstamp) = $dbh->selectrow_array("SELECT extract(epoch from stamp) FROM records WHERE domain_id = 5 AND host = '$newname' ". "AND stampactive = 't' AND expires = 't'"); my ($oldstamp) = $dbh->selectrow_array("SELECT extract(epoch from stamp) FROM records WHERE domain_id = 5 AND host = '$newname' ". "AND stampactive = 't' AND expires = 'f'"); ok( $newstamp <= $oldstamp, " ... added record expires before existing active-after record goes active" ); } else { print "not ok: $msg"; } }; subtest 'expire after valid-after record' => sub { $newname = 'expires-at4.expiry2.test'; ($code, $msg) = $dnsdb->addRec('n', 'n', 5, \$newname, \$rectype, \$newval, 900, undef, 't', $expirystamp); ok( $code eq 'WARN', "addRec() claimed success with warning" ); if ($code eq 'WARN') { ($rcount) = $dbh->selectrow_array("SELECT count(*) FROM records WHERE domain_id = 5 AND host = '$newname'"); ok( $rcount == 2, " ... [$rcount] correct number of records for $newname" ); like( $msg, qr/added with modified expiry time; conflicting valid-after record found/, " ... returned appropriate warning message" ); my ($newstamp) = $dbh->selectrow_array("SELECT stamp FROM records WHERE domain_id = 5 AND host = '$newname' ". "AND stampactive = 't' AND expires = 't'"); my ($oldstamp) = $dbh->selectrow_array("SELECT stamp FROM records WHERE domain_id = 5 AND host = '$newname' ". "AND stampactive = 't' AND expires = 'f'"); ok( $newstamp eq $oldstamp, " ... coerced timestamp matches existing active-after timestamp" ); } else { print "not ok: $msg"; } }; }; # add expiring CNAME }; # record expiry/valid-after done_testing();