Home | History | Annotate | Line # | Download | only in dns
      1 /*	$NetBSD: dbversion_test.c,v 1.4 2025/05/21 14:48:06 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 #include <inttypes.h>
     17 #include <sched.h> /* IWYU pragma: keep */
     18 #include <setjmp.h>
     19 #include <stdarg.h>
     20 #include <stddef.h>
     21 #include <stdlib.h>
     22 #include <string.h>
     23 #include <unistd.h>
     24 
     25 #define UNIT_TESTING
     26 #include <cmocka.h>
     27 
     28 #include <isc/file.h>
     29 #include <isc/result.h>
     30 #include <isc/serial.h>
     31 #include <isc/stdtime.h>
     32 #include <isc/string.h>
     33 #include <isc/util.h>
     34 
     35 #include <dns/db.h>
     36 #include <dns/nsec3.h>
     37 #include <dns/rdatalist.h>
     38 #include <dns/rdataset.h>
     39 #include <dns/rdatasetiter.h>
     40 
     41 #include <tests/dns.h>
     42 
     43 static char tempname[11] = "dtXXXXXXXX";
     44 static dns_db_t *db1 = NULL, *db2 = NULL;
     45 static dns_dbversion_t *v1 = NULL, *v2 = NULL;
     46 
     47 /*
     48  * The code below enables us to trap assertion failures for testing
     49  * purposes. local_callback() is set as the callback function for
     50  * isc_assertion_failed(). It calls mock_assert() so that CMOCKA
     51  * will be able to see it, then returns to the calling function via
     52  * longjmp() so that the abort() call in isc_assertion_failed() will
     53  * never be reached. Use check_assertion() to check for assertions
     54  * instead of expect_assert_failure().
     55  */
     56 jmp_buf assertion;
     57 
     58 #define check_assertion(function_call)                        \
     59 	do {                                                  \
     60 		const int r = setjmp(assertion);              \
     61 		if (r == 0) {                                 \
     62 			expect_assert_failure(function_call); \
     63 		}                                             \
     64 	} while (false);
     65 
     66 static void
     67 local_callback(const char *file, int line, isc_assertiontype_t type,
     68 	       const char *cond) {
     69 	UNUSED(type);
     70 
     71 	mock_assert(1, cond, file, line);
     72 	longjmp(assertion, 1);
     73 }
     74 
     75 static int
     76 setup_test(void **state) {
     77 	isc_result_t res;
     78 
     79 	UNUSED(state);
     80 
     81 	isc_assertion_setcallback(local_callback);
     82 
     83 	res = dns_db_create(mctx, ZONEDB_DEFAULT, dns_rootname, dns_dbtype_zone,
     84 			    dns_rdataclass_in, 0, NULL, &db1);
     85 	assert_int_equal(res, ISC_R_SUCCESS);
     86 	dns_db_newversion(db1, &v1);
     87 	assert_non_null(v1);
     88 
     89 	res = dns_db_create(mctx, ZONEDB_DEFAULT, dns_rootname, dns_dbtype_zone,
     90 			    dns_rdataclass_in, 0, NULL, &db2);
     91 	assert_int_equal(res, ISC_R_SUCCESS);
     92 	dns_db_newversion(db2, &v2);
     93 	assert_non_null(v1);
     94 
     95 	return 0;
     96 }
     97 
     98 static int
     99 teardown_test(void **state) {
    100 	UNUSED(state);
    101 
    102 	if (strcmp(tempname, "dtXXXXXXXX") != 0) {
    103 		unlink(tempname);
    104 	}
    105 
    106 	if (v1 != NULL) {
    107 		dns_db_closeversion(db1, &v1, false);
    108 		assert_null(v1);
    109 	}
    110 	if (db1 != NULL) {
    111 		dns_db_detach(&db1);
    112 		assert_null(db1);
    113 	}
    114 
    115 	if (v2 != NULL) {
    116 		dns_db_closeversion(db2, &v2, false);
    117 		assert_null(v2);
    118 	}
    119 	if (db2 != NULL) {
    120 		dns_db_detach(&db2);
    121 		assert_null(db2);
    122 	}
    123 
    124 	return 0;
    125 }
    126 
    127 /*
    128  * Check dns_db_attachversion() passes with matching db and version, and
    129  * asserts with mis-matching db and version.
    130  */
    131 ISC_RUN_TEST_IMPL(attachversion) {
    132 	dns_dbversion_t *v = NULL;
    133 
    134 	UNUSED(state);
    135 
    136 	dns_db_attachversion(db1, v1, &v);
    137 	assert_ptr_equal(v, v1);
    138 	dns_db_closeversion(db1, &v, false);
    139 	assert_null(v);
    140 
    141 	check_assertion(dns_db_attachversion(db1, v2, &v));
    142 }
    143 
    144 /*
    145  * Check dns_db_closeversion() passes with matching db and version, and
    146  * asserts with mis-matching db and version.
    147  */
    148 ISC_RUN_TEST_IMPL(closeversion) {
    149 	UNUSED(state);
    150 
    151 	assert_non_null(v1);
    152 	dns_db_closeversion(db1, &v1, false);
    153 	assert_null(v1);
    154 
    155 	check_assertion(dns_db_closeversion(db1, &v2, false));
    156 }
    157 
    158 /*
    159  * Check dns_db_find() passes with matching db and version, and
    160  * asserts with mis-matching db and version.
    161  */
    162 ISC_RUN_TEST_IMPL(find) {
    163 	isc_result_t res;
    164 	dns_rdataset_t rdataset;
    165 	dns_fixedname_t fixed;
    166 	dns_name_t *name = NULL;
    167 
    168 	UNUSED(state);
    169 
    170 	name = dns_fixedname_initname(&fixed);
    171 
    172 	dns_rdataset_init(&rdataset);
    173 	res = dns_db_find(db1, dns_rootname, v1, dns_rdatatype_soa, 0, 0, NULL,
    174 			  name, &rdataset, NULL);
    175 	/*
    176 	 * Note: in the QPzone database, the root node always exists,
    177 	 * even if it's empty, so we would get DNS_R_NXRRSET from this
    178 	 * query. In other databases (including the old RBTDB) the root
    179 	 * node can be nonexistent, and the query would then return
    180 	 * DNS_R_NXDOMAIN. Allow for both possibilities.
    181 	 */
    182 	assert_true(res == DNS_R_NXRRSET || res == DNS_R_NXDOMAIN);
    183 
    184 	if (dns_rdataset_isassociated(&rdataset)) {
    185 		dns_rdataset_disassociate(&rdataset);
    186 	}
    187 
    188 	dns_rdataset_init(&rdataset);
    189 	check_assertion((void)dns_db_find(db1, dns_rootname, v2,
    190 					  dns_rdatatype_soa, 0, 0, NULL, name,
    191 					  &rdataset, NULL));
    192 }
    193 
    194 /*
    195  * Check dns_db_allrdatasets() passes with matching db and version, and
    196  * asserts with mis-matching db and version.
    197  */
    198 ISC_RUN_TEST_IMPL(allrdatasets) {
    199 	isc_result_t res;
    200 	dns_dbnode_t *node = NULL;
    201 	dns_rdatasetiter_t *iterator = NULL;
    202 
    203 	UNUSED(state);
    204 
    205 	res = dns_db_findnode(db1, dns_rootname, false, &node);
    206 	assert_int_equal(res, ISC_R_SUCCESS);
    207 
    208 	res = dns_db_allrdatasets(db1, node, v1, 0, 0, &iterator);
    209 	assert_int_equal(res, ISC_R_SUCCESS);
    210 
    211 	check_assertion(dns_db_allrdatasets(db1, node, v2, 0, 0, &iterator));
    212 
    213 	dns_rdatasetiter_destroy(&iterator);
    214 	assert_null(iterator);
    215 
    216 	dns_db_detachnode(db1, &node);
    217 	assert_null(node);
    218 }
    219 
    220 /*
    221  * Check dns_db_findrdataset() passes with matching db and version, and
    222  * asserts with mis-matching db and version.
    223  */
    224 ISC_RUN_TEST_IMPL(findrdataset) {
    225 	isc_result_t res;
    226 	dns_rdataset_t rdataset;
    227 	dns_dbnode_t *node = NULL;
    228 
    229 	UNUSED(state);
    230 
    231 	res = dns_db_findnode(db1, dns_rootname, false, &node);
    232 	assert_int_equal(res, ISC_R_SUCCESS);
    233 
    234 	dns_rdataset_init(&rdataset);
    235 	res = dns_db_findrdataset(db1, node, v1, dns_rdatatype_soa, 0, 0,
    236 				  &rdataset, NULL);
    237 	assert_int_equal(res, ISC_R_NOTFOUND);
    238 
    239 	if (dns_rdataset_isassociated(&rdataset)) {
    240 		dns_rdataset_disassociate(&rdataset);
    241 	}
    242 
    243 	dns_rdataset_init(&rdataset);
    244 	check_assertion(dns_db_findrdataset(db1, node, v2, dns_rdatatype_soa, 0,
    245 					    0, &rdataset, NULL));
    246 
    247 	dns_db_detachnode(db1, &node);
    248 	assert_null(node);
    249 }
    250 
    251 /*
    252  * Check dns_db_deleterdataset() passes with matching db and version, and
    253  * asserts with mis-matching db and version.
    254  */
    255 ISC_RUN_TEST_IMPL(deleterdataset) {
    256 	isc_result_t res;
    257 	dns_dbnode_t *node = NULL;
    258 
    259 	UNUSED(state);
    260 
    261 	res = dns_db_findnode(db1, dns_rootname, false, &node);
    262 	assert_int_equal(res, ISC_R_SUCCESS);
    263 
    264 	res = dns_db_deleterdataset(db1, node, v1, dns_rdatatype_soa, 0);
    265 	assert_int_equal(res, DNS_R_UNCHANGED);
    266 
    267 	check_assertion(
    268 		dns_db_deleterdataset(db1, node, v2, dns_rdatatype_soa, 0));
    269 	dns_db_detachnode(db1, &node);
    270 	assert_null(node);
    271 }
    272 
    273 /*
    274  * Check dns_db_subtractrdataset() passes with matching db and version, and
    275  * asserts with mis-matching db and version.
    276  */
    277 ISC_RUN_TEST_IMPL(subtract) {
    278 	isc_result_t res;
    279 	dns_rdataset_t rdataset;
    280 	dns_rdatalist_t rdatalist;
    281 	dns_dbnode_t *node = NULL;
    282 
    283 	UNUSED(state);
    284 
    285 	dns_rdataset_init(&rdataset);
    286 	dns_rdatalist_init(&rdatalist);
    287 
    288 	rdatalist.rdclass = dns_rdataclass_in;
    289 
    290 	dns_rdatalist_tordataset(&rdatalist, &rdataset);
    291 
    292 	res = dns_db_findnode(db1, dns_rootname, false, &node);
    293 	assert_int_equal(res, ISC_R_SUCCESS);
    294 
    295 	res = dns_db_subtractrdataset(db1, node, v1, &rdataset, 0, NULL);
    296 	assert_int_equal(res, DNS_R_UNCHANGED);
    297 
    298 	if (dns_rdataset_isassociated(&rdataset)) {
    299 		dns_rdataset_disassociate(&rdataset);
    300 	}
    301 
    302 	dns_rdataset_init(&rdataset);
    303 	dns_rdatalist_tordataset(&rdatalist, &rdataset);
    304 
    305 	check_assertion(
    306 		dns_db_subtractrdataset(db1, node, v2, &rdataset, 0, NULL));
    307 
    308 	dns_db_detachnode(db1, &node);
    309 	assert_null(node);
    310 }
    311 
    312 /*
    313  * Check dns_db_addrdataset() passes with matching db and version, and
    314  * asserts with mis-matching db and version.
    315  */
    316 ISC_RUN_TEST_IMPL(addrdataset) {
    317 	isc_result_t res;
    318 	dns_rdataset_t rdataset;
    319 	dns_dbnode_t *node = NULL;
    320 	dns_rdatalist_t rdatalist;
    321 
    322 	UNUSED(state);
    323 
    324 	dns_rdataset_init(&rdataset);
    325 	dns_rdatalist_init(&rdatalist);
    326 
    327 	rdatalist.rdclass = dns_rdataclass_in;
    328 
    329 	dns_rdatalist_tordataset(&rdatalist, &rdataset);
    330 
    331 	res = dns_db_findnode(db1, dns_rootname, false, &node);
    332 	assert_int_equal(res, ISC_R_SUCCESS);
    333 
    334 	res = dns_db_addrdataset(db1, node, v1, 0, &rdataset, 0, NULL);
    335 	assert_int_equal(res, ISC_R_SUCCESS);
    336 
    337 	check_assertion(
    338 		dns_db_addrdataset(db1, node, v2, 0, &rdataset, 0, NULL));
    339 
    340 	dns_db_detachnode(db1, &node);
    341 	assert_null(node);
    342 }
    343 
    344 /*
    345  * Check dns_db_getnsec3parameters() passes with matching db and version,
    346  * and asserts with mis-matching db and version.
    347  */
    348 ISC_RUN_TEST_IMPL(getnsec3parameters) {
    349 	isc_result_t res;
    350 	dns_hash_t hash;
    351 	uint8_t flags;
    352 	uint16_t iterations;
    353 	unsigned char salt[DNS_NSEC3_SALTSIZE];
    354 	size_t salt_length = sizeof(salt);
    355 
    356 	UNUSED(state);
    357 
    358 	res = dns_db_getnsec3parameters(db1, v1, &hash, &flags, &iterations,
    359 					salt, &salt_length);
    360 	assert_int_equal(res, ISC_R_NOTFOUND);
    361 
    362 	check_assertion(dns_db_getnsec3parameters(
    363 		db1, v2, &hash, &flags, &iterations, salt, &salt_length));
    364 }
    365 
    366 /*
    367  * Check that the correct node contents are found after a rollback.
    368  */
    369 ISC_RUN_TEST_IMPL(rollback) {
    370 	isc_result_t res;
    371 	dns_rdata_t rdata1 = DNS_RDATA_INIT, rdata2 = DNS_RDATA_INIT;
    372 	dns_rdataset_t input1 = DNS_RDATASET_INIT;
    373 	dns_rdataset_t input2 = DNS_RDATASET_INIT;
    374 	dns_rdataset_t rdataset1 = DNS_RDATASET_INIT;
    375 	dns_rdataset_t rdataset2 = DNS_RDATASET_INIT;
    376 	dns_rdatalist_t rdatalist1, rdatalist2;
    377 	dns_rdata_t out1 = DNS_RDATA_INIT, out2 = DNS_RDATA_INIT;
    378 	dns_dbnode_t *node = NULL;
    379 	char *txt1 = (char *)"\006text 1";
    380 	char *txt2 = (char *)"\006text 2";
    381 	size_t len1 = strlen(txt1), len2 = strlen(txt2);
    382 	char buf[1024];
    383 	isc_buffer_t b;
    384 
    385 	UNUSED(state);
    386 
    387 	isc_buffer_init(&b, buf, sizeof(buf));
    388 
    389 	/* Set up two rdatasets to insert */
    390 	rdata1.rdclass = dns_rdataclass_in;
    391 	rdata1.type = dns_rdatatype_txt;
    392 	rdata2 = rdata1;
    393 
    394 	rdata1.length = len1;
    395 	rdata1.data = (unsigned char *)txt1;
    396 	rdata2.length = len2;
    397 	rdata2.data = (unsigned char *)txt2;
    398 
    399 	dns_rdatalist_init(&rdatalist1);
    400 	rdatalist1.rdclass = dns_rdataclass_in;
    401 	rdatalist1.type = dns_rdatatype_txt;
    402 	rdatalist1.ttl = 3600;
    403 	rdatalist2 = rdatalist1;
    404 
    405 	ISC_LIST_APPEND(rdatalist1.rdata, &rdata1, link);
    406 	ISC_LIST_APPEND(rdatalist2.rdata, &rdata2, link);
    407 
    408 	dns_rdatalist_tordataset(&rdatalist1, &input1);
    409 	dns_rdatalist_tordataset(&rdatalist2, &input2);
    410 
    411 	/* db1: Insert the first version ("text 1"), and commit */
    412 	res = dns_db_findnode(db1, dns_rootname, true, &node);
    413 	assert_int_equal(res, ISC_R_SUCCESS);
    414 	res = dns_db_addrdataset(db1, node, v1, 0, &input1, 0, NULL);
    415 	assert_int_equal(res, ISC_R_SUCCESS);
    416 	dns_db_closeversion(db1, &v1, true); /* commit */
    417 	assert_null(v1);
    418 	dns_db_detachnode(db1, &node);
    419 	assert_null(node);
    420 
    421 	/* db2: Insert the first version ("text 1"), and commit */
    422 	res = dns_db_findnode(db2, dns_rootname, true, &node);
    423 	assert_int_equal(res, ISC_R_SUCCESS);
    424 	res = dns_db_addrdataset(db2, node, v2, 0, &input1, 0, NULL);
    425 	assert_int_equal(res, ISC_R_SUCCESS);
    426 	dns_db_closeversion(db2, &v2, true); /* commit */
    427 	assert_null(v2);
    428 	dns_db_detachnode(db2, &node);
    429 	assert_null(node);
    430 
    431 	/* Reopen the versions */
    432 	dns_db_newversion(db1, &v1);
    433 	assert_non_null(v1);
    434 	dns_db_newversion(db2, &v2);
    435 	assert_non_null(v2);
    436 
    437 	/* db1: Insert the second version ("text 2"), and roll back */
    438 	res = dns_db_findnode(db1, dns_rootname, true, &node);
    439 	assert_int_equal(res, ISC_R_SUCCESS);
    440 	res = dns_db_addrdataset(db1, node, v1, 0, &input2, 0, NULL);
    441 	assert_int_equal(res, ISC_R_SUCCESS);
    442 	dns_db_closeversion(db1, &v1, false); /* rollback */
    443 	assert_null(v1);
    444 	dns_db_detachnode(db1, &node);
    445 	assert_null(node);
    446 
    447 	/* db2: Insert the second version ("text 2"), and commit */
    448 	res = dns_db_findnode(db2, dns_rootname, true, &node);
    449 	assert_int_equal(res, ISC_R_SUCCESS);
    450 	res = dns_db_addrdataset(db2, node, v2, 0, &input2, 0, NULL);
    451 	assert_int_equal(res, ISC_R_SUCCESS);
    452 	dns_db_closeversion(db2, &v2, true); /* commit */
    453 	assert_null(v2);
    454 	dns_db_detachnode(db2, &node);
    455 	assert_null(node);
    456 
    457 	/* db1: Look it up and check that the first version is found */
    458 	dns_db_currentversion(db1, &v1);
    459 	assert_non_null(v1);
    460 	res = dns_db_findnode(db1, dns_rootname, true, &node);
    461 	assert_int_equal(res, ISC_R_SUCCESS);
    462 	res = dns_db_findrdataset(db1, node, v1, dns_rdatatype_txt, 0, 0,
    463 				  &rdataset1, NULL);
    464 	assert_int_equal(res, ISC_R_SUCCESS);
    465 
    466 	/* db1: Convert result to text */
    467 	res = dns_rdataset_first(&rdataset1);
    468 	assert_int_equal(res, ISC_R_SUCCESS);
    469 	dns_rdataset_current(&rdataset1, &out1);
    470 
    471 	res = dns_rdata_totext(&out1, NULL, &b);
    472 	assert_int_equal(res, ISC_R_SUCCESS);
    473 	isc_buffer_putuint8(&b, 0);
    474 
    475 	/* db1: We should have "text 1" */
    476 	assert_string_equal(buf, "\"text 1\"");
    477 
    478 	dns_rdataset_disassociate(&rdataset1);
    479 
    480 	dns_db_closeversion(db1, &v1, true);
    481 	assert_null(v1);
    482 	dns_db_detachnode(db1, &node);
    483 	assert_null(node);
    484 
    485 	/* db2: Look it up and check that the second version is found */
    486 	dns_db_currentversion(db2, &v2);
    487 	assert_non_null(v2);
    488 	res = dns_db_findnode(db2, dns_rootname, true, &node);
    489 	assert_int_equal(res, ISC_R_SUCCESS);
    490 	res = dns_db_findrdataset(db2, node, v2, dns_rdatatype_txt, 0, 0,
    491 				  &rdataset2, NULL);
    492 	assert_int_equal(res, ISC_R_SUCCESS);
    493 
    494 	/* db2: Convert result to text */
    495 	res = dns_rdataset_first(&rdataset2);
    496 	assert_int_equal(res, ISC_R_SUCCESS);
    497 	dns_rdataset_current(&rdataset2, &out2);
    498 	isc_buffer_init(&b, buf, sizeof(buf));
    499 	res = dns_rdata_totext(&out2, NULL, &b);
    500 	assert_int_equal(res, ISC_R_SUCCESS);
    501 	isc_buffer_putuint8(&b, 0);
    502 
    503 	/* db2: We should have "text 2" */
    504 	assert_string_equal(buf, "\"text 2\"");
    505 
    506 	dns_rdataset_disassociate(&rdataset2);
    507 
    508 	dns_db_closeversion(db2, &v2, true);
    509 	assert_null(v2);
    510 	dns_db_detachnode(db2, &node);
    511 	assert_null(node);
    512 }
    513 
    514 ISC_TEST_LIST_START
    515 ISC_TEST_ENTRY_CUSTOM(find, setup_test, teardown_test)
    516 ISC_TEST_ENTRY_CUSTOM(allrdatasets, setup_test, teardown_test)
    517 ISC_TEST_ENTRY_CUSTOM(findrdataset, setup_test, teardown_test)
    518 ISC_TEST_ENTRY_CUSTOM(deleterdataset, setup_test, teardown_test)
    519 ISC_TEST_ENTRY_CUSTOM(subtract, setup_test, teardown_test)
    520 ISC_TEST_ENTRY_CUSTOM(addrdataset, setup_test, teardown_test)
    521 ISC_TEST_ENTRY_CUSTOM(getnsec3parameters, setup_test, teardown_test)
    522 ISC_TEST_ENTRY_CUSTOM(attachversion, setup_test, teardown_test)
    523 ISC_TEST_ENTRY_CUSTOM(closeversion, setup_test, teardown_test)
    524 ISC_TEST_ENTRY_CUSTOM(rollback, setup_test, teardown_test)
    525 ISC_TEST_LIST_END
    526 
    527 ISC_TEST_MAIN
    528