/* * Copyright (C) Internet Systems Consortium, Inc. ("ISC") * * SPDX-License-Identifier: MPL-2.0 * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, you can obtain one at https://mozilla.org/MPL/2.0/. * * See the COPYRIGHT file distributed with this work for additional * information regarding copyright ownership. */ #if HAVE_CMOCKA #include /* IWYU pragma: keep */ #include #include #include #include #include #include #define UNIT_TESTING #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../zone_p.h" #include "dnstest.h" static int _setup(void **state) { isc_result_t result; UNUSED(state); result = dns_test_begin(NULL, false); assert_int_equal(result, ISC_R_SUCCESS); return (0); } static int _teardown(void **state) { UNUSED(state); dns_test_end(); return (0); } /*% * Structure characterizing a single diff tuple in the dns_diff_t structure * prepared by dns__zone_updatesigs(). */ typedef struct { dns_diffop_t op; const char *owner; dns_ttl_t ttl; const char *type; } zonediff_t; #define ZONEDIFF_SENTINEL \ { \ 0, NULL, 0, NULL \ } /*% * Structure defining a dns__zone_updatesigs() test. */ typedef struct { const char *description; /* test description */ const zonechange_t *changes; /* array of "raw" zone changes */ const zonediff_t *zonediff; /* array of "processed" zone changes * */ } updatesigs_test_params_t; /*% * Check whether the 'found' tuple matches the 'expected' tuple. 'found' is * the 'index'th tuple output by dns__zone_updatesigs() in test 'test'. */ static void compare_tuples(const zonediff_t *expected, dns_difftuple_t *found, size_t index) { char found_covers[DNS_RDATATYPE_FORMATSIZE] = {}; char found_type[DNS_RDATATYPE_FORMATSIZE] = {}; char found_name[DNS_NAME_FORMATSIZE]; isc_consttextregion_t typeregion; dns_fixedname_t expected_fname; dns_rdatatype_t expected_type; dns_name_t *expected_name; dns_rdata_rrsig_t rrsig; isc_buffer_t typebuf; isc_result_t result; REQUIRE(expected != NULL); REQUIRE(found != NULL); REQUIRE(index > 0); /* * Check operation. */ assert_int_equal(expected->op, found->op); /* * Check owner name. */ expected_name = dns_fixedname_initname(&expected_fname); result = dns_name_fromstring(expected_name, expected->owner, 0, dt_mctx); assert_int_equal(result, ISC_R_SUCCESS); dns_name_format(&found->name, found_name, sizeof(found_name)); assert_true(dns_name_equal(expected_name, &found->name)); /* * Check TTL. */ assert_int_equal(expected->ttl, found->ttl); /* * Parse expected RR type. */ typeregion.base = expected->type; typeregion.length = strlen(expected->type); result = dns_rdatatype_fromtext(&expected_type, (isc_textregion_t *)&typeregion); assert_int_equal(result, ISC_R_SUCCESS); /* * Format found RR type for reporting purposes. */ isc_buffer_init(&typebuf, found_type, sizeof(found_type)); result = dns_rdatatype_totext(found->rdata.type, &typebuf); assert_int_equal(result, ISC_R_SUCCESS); /* * Check RR type. */ switch (expected->op) { case DNS_DIFFOP_ADDRESIGN: case DNS_DIFFOP_DELRESIGN: /* * Found tuple must be of type RRSIG. */ assert_int_equal(found->rdata.type, dns_rdatatype_rrsig); if (found->rdata.type != dns_rdatatype_rrsig) { break; } /* * The signature must cover an RRset of type 'expected->type'. */ result = dns_rdata_tostruct(&found->rdata, &rrsig, NULL); assert_int_equal(result, ISC_R_SUCCESS); isc_buffer_init(&typebuf, found_covers, sizeof(found_covers)); result = dns_rdatatype_totext(rrsig.covered, &typebuf); assert_int_equal(result, ISC_R_SUCCESS); assert_int_equal(expected_type, rrsig.covered); break; default: /* * Found tuple must be of type 'expected->type'. */ assert_int_equal(expected_type, found->rdata.type); break; } } /*% * Perform a single dns__zone_updatesigs() test defined in 'test'. All other * arguments are expected to remain constant between subsequent invocations of * this function. */ static void updatesigs_test(const updatesigs_test_params_t *test, dns_zone_t *zone, dns_db_t *db, dst_key_t *zone_keys[], unsigned int nkeys, isc_stdtime_t now) { size_t tuples_expected, tuples_found, index; dns_dbversion_t *version = NULL; dns_diff_t raw_diff, zone_diff; const zonediff_t *expected; dns_difftuple_t *found; isc_result_t result; dns__zonediff_t zonediff = { .diff = &zone_diff, .offline = false, }; REQUIRE(test != NULL); REQUIRE(test->description != NULL); REQUIRE(test->changes != NULL); REQUIRE(zone != NULL); REQUIRE(db != NULL); REQUIRE(zone_keys != NULL); /* * Create a new version of the zone's database. */ result = dns_db_newversion(db, &version); assert_int_equal(result, ISC_R_SUCCESS); /* * Create a diff representing the supplied changes. */ result = dns_test_difffromchanges(&raw_diff, test->changes, false); assert_int_equal(result, ISC_R_SUCCESS); /* * Apply the "raw" diff to the new version of the zone's database as * this is what dns__zone_updatesigs() expects to happen before it is * called. */ dns_diff_apply(&raw_diff, db, version); /* * Initialize the structure dns__zone_updatesigs() will modify. */ dns_diff_init(dt_mctx, &zone_diff); /* * Check whether dns__zone_updatesigs() behaves as expected. */ result = dns__zone_updatesigs(&raw_diff, db, version, zone_keys, nkeys, zone, now - 3600, now + 3600, 0, now, true, false, &zonediff); assert_int_equal(result, ISC_R_SUCCESS); assert_true(ISC_LIST_EMPTY(raw_diff.tuples)); assert_false(ISC_LIST_EMPTY(zone_diff.tuples)); /* * Ensure that the number of tuples in the zone diff is as expected. */ tuples_expected = 0; for (expected = test->zonediff; expected->owner != NULL; expected++) { tuples_expected++; } tuples_found = 0; for (found = ISC_LIST_HEAD(zone_diff.tuples); found != NULL; found = ISC_LIST_NEXT(found, link)) { tuples_found++; } assert_int_equal(tuples_expected, tuples_found); /* * Ensure that every tuple in the zone diff matches expectations. */ expected = test->zonediff; index = 1; for (found = ISC_LIST_HEAD(zone_diff.tuples); found != NULL; found = ISC_LIST_NEXT(found, link)) { compare_tuples(expected, found, index); expected++; index++; } /* * Apply changes to zone database contents and clean up. */ dns_db_closeversion(db, &version, true); dns_diff_clear(&zone_diff); dns_diff_clear(&raw_diff); } /* dns__zone_updatesigs() tests */ static void updatesigs_next_test(void **state) { dst_key_t *zone_keys[DNS_MAXZONEKEYS]; dns_zone_t *zone = NULL; dns_db_t *db = NULL; isc_result_t result; unsigned int nkeys; isc_stdtime_t now; size_t i; UNUSED(state); /* * Prepare a zone along with its signing keys. */ result = dns_test_makezone("example", &zone, NULL, false); assert_int_equal(result, ISC_R_SUCCESS); result = dns_test_loaddb(&db, dns_dbtype_zone, "example", "testdata/master/master18.data"); assert_int_equal(result, DNS_R_SEENINCLUDE); result = dns_zone_setkeydirectory(zone, "testkeys"); assert_int_equal(result, ISC_R_SUCCESS); isc_stdtime_get(&now); result = dns__zone_findkeys(zone, db, NULL, now, dt_mctx, DNS_MAXZONEKEYS, zone_keys, &nkeys); assert_int_equal(result, ISC_R_SUCCESS); assert_int_equal(nkeys, 2); /* * Define the tests to be run. Note that changes to zone database * contents introduced by each test are preserved between tests. */ const zonechange_t changes_add[] = { { DNS_DIFFOP_ADD, "foo.example", 300, "TXT", "foo" }, { DNS_DIFFOP_ADD, "bar.example", 600, "TXT", "bar" }, ZONECHANGE_SENTINEL, }; const zonediff_t zonediff_add[] = { { DNS_DIFFOP_ADDRESIGN, "foo.example", 300, "TXT" }, { DNS_DIFFOP_ADD, "foo.example", 300, "TXT" }, { DNS_DIFFOP_ADDRESIGN, "bar.example", 600, "TXT" }, { DNS_DIFFOP_ADD, "bar.example", 600, "TXT" }, ZONEDIFF_SENTINEL, }; const updatesigs_test_params_t test_add = { .description = "add new RRsets", .changes = changes_add, .zonediff = zonediff_add, }; const zonechange_t changes_append[] = { { DNS_DIFFOP_ADD, "foo.example", 300, "TXT", "foo1" }, { DNS_DIFFOP_ADD, "foo.example", 300, "TXT", "foo2" }, ZONECHANGE_SENTINEL, }; const zonediff_t zonediff_append[] = { { DNS_DIFFOP_DELRESIGN, "foo.example", 300, "TXT" }, { DNS_DIFFOP_ADDRESIGN, "foo.example", 300, "TXT" }, { DNS_DIFFOP_ADD, "foo.example", 300, "TXT" }, { DNS_DIFFOP_ADD, "foo.example", 300, "TXT" }, ZONEDIFF_SENTINEL, }; const updatesigs_test_params_t test_append = { .description = "append multiple RRs to an existing RRset", .changes = changes_append, .zonediff = zonediff_append, }; const zonechange_t changes_replace[] = { { DNS_DIFFOP_DEL, "bar.example", 600, "TXT", "bar" }, { DNS_DIFFOP_ADD, "bar.example", 600, "TXT", "rab" }, ZONECHANGE_SENTINEL, }; const zonediff_t zonediff_replace[] = { { DNS_DIFFOP_DELRESIGN, "bar.example", 600, "TXT" }, { DNS_DIFFOP_ADDRESIGN, "bar.example", 600, "TXT" }, { DNS_DIFFOP_DEL, "bar.example", 600, "TXT" }, { DNS_DIFFOP_ADD, "bar.example", 600, "TXT" }, ZONEDIFF_SENTINEL, }; const updatesigs_test_params_t test_replace = { .description = "replace an existing RRset", .changes = changes_replace, .zonediff = zonediff_replace, }; const zonechange_t changes_delete[] = { { DNS_DIFFOP_DEL, "bar.example", 600, "TXT", "rab" }, ZONECHANGE_SENTINEL, }; const zonediff_t zonediff_delete[] = { { DNS_DIFFOP_DELRESIGN, "bar.example", 600, "TXT" }, { DNS_DIFFOP_DEL, "bar.example", 600, "TXT" }, ZONEDIFF_SENTINEL, }; const updatesigs_test_params_t test_delete = { .description = "delete an existing RRset", .changes = changes_delete, .zonediff = zonediff_delete, }; const zonechange_t changes_mixed[] = { { DNS_DIFFOP_ADD, "baz.example", 900, "TXT", "baz1" }, { DNS_DIFFOP_ADD, "baz.example", 900, "A", "127.0.0.1" }, { DNS_DIFFOP_ADD, "baz.example", 900, "TXT", "baz2" }, { DNS_DIFFOP_ADD, "baz.example", 900, "AAAA", "::1" }, ZONECHANGE_SENTINEL, }; const zonediff_t zonediff_mixed[] = { { DNS_DIFFOP_ADDRESIGN, "baz.example", 900, "TXT" }, { DNS_DIFFOP_ADD, "baz.example", 900, "TXT" }, { DNS_DIFFOP_ADD, "baz.example", 900, "TXT" }, { DNS_DIFFOP_ADDRESIGN, "baz.example", 900, "A" }, { DNS_DIFFOP_ADD, "baz.example", 900, "A" }, { DNS_DIFFOP_ADDRESIGN, "baz.example", 900, "AAAA" }, { DNS_DIFFOP_ADD, "baz.example", 900, "AAAA" }, ZONEDIFF_SENTINEL, }; const updatesigs_test_params_t test_mixed = { .description = "add different RRsets with common owner name", .changes = changes_mixed, .zonediff = zonediff_mixed, }; const updatesigs_test_params_t *tests[] = { &test_add, &test_append, &test_replace, &test_delete, &test_mixed, }; /* * Run tests. */ for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { updatesigs_test(tests[i], zone, db, zone_keys, nkeys, now); } /* * Clean up. */ for (i = 0; i < nkeys; i++) { dst_key_free(&zone_keys[i]); } dns_db_detach(&db); dns_zone_detach(&zone); } int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown(updatesigs_next_test, _setup, _teardown), }; return (cmocka_run_group_tests(tests, NULL, NULL)); } #else /* HAVE_CMOCKA */ #include int main(void) { printf("1..0 # Skipped: cmocka not available\n"); return (SKIPPED_TEST_EXIT_CODE); } #endif /* if HAVE_CMOCKA */