xref: /netbsd-src/external/mpl/bind/dist/tests/dns/sigs_test.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: sigs_test.c,v 1.4 2025/01/26 16:25:48 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/buffer.h>
29 #include <isc/list.h>
30 #include <isc/region.h>
31 #include <isc/result.h>
32 #include <isc/stdtime.h>
33 #include <isc/types.h>
34 #include <isc/util.h>
35 
36 #include <dns/db.h>
37 #include <dns/diff.h>
38 #include <dns/dnssec.h>
39 #include <dns/fixedname.h>
40 #include <dns/name.h>
41 #include <dns/rdata.h>
42 #include <dns/rdatastruct.h>
43 #include <dns/rdatatype.h>
44 #include <dns/types.h>
45 #include <dns/zone.h>
46 
47 #include <dst/dst.h>
48 
49 #include "zone_p.h"
50 
51 #include <tests/dns.h>
52 
53 /*%
54  * Structure characterizing a single diff tuple in the dns_diff_t structure
55  * prepared by dns__zone_updatesigs().
56  */
57 typedef struct {
58 	dns_diffop_t op;
59 	const char *owner;
60 	dns_ttl_t ttl;
61 	const char *type;
62 } zonediff_t;
63 
64 #define ZONEDIFF_SENTINEL { 0, NULL, 0, NULL }
65 
66 /*%
67  * Structure defining a dns__zone_updatesigs() test.
68  */
69 typedef struct {
70 	const char *description;     /* test description */
71 	const zonechange_t *changes; /* array of "raw" zone changes */
72 	const zonediff_t *zonediff;  /* array of "processed" zone changes
73 				      * */
74 } updatesigs_test_params_t;
75 
76 static int
77 setup_test(void **state) {
78 	isc_result_t result;
79 
80 	UNUSED(state);
81 
82 	result = dst_lib_init(mctx, NULL);
83 
84 	if (result != ISC_R_SUCCESS) {
85 		return 1;
86 	}
87 
88 	return 0;
89 }
90 
91 static int
92 teardown_test(void **state) {
93 	UNUSED(state);
94 
95 	dst_lib_destroy();
96 
97 	return 0;
98 }
99 
100 /*%
101  * Check whether the 'found' tuple matches the 'expected' tuple.  'found' is
102  * the 'index'th tuple output by dns__zone_updatesigs() in test 'test'.
103  */
104 static void
105 compare_tuples(const zonediff_t *expected, dns_difftuple_t *found,
106 	       size_t index) {
107 	char found_covers[DNS_RDATATYPE_FORMATSIZE] = {};
108 	char found_type[DNS_RDATATYPE_FORMATSIZE] = {};
109 	char found_name[DNS_NAME_FORMATSIZE];
110 	isc_consttextregion_t typeregion;
111 	dns_fixedname_t expected_fname;
112 	dns_rdatatype_t expected_type;
113 	dns_name_t *expected_name;
114 	dns_rdata_rrsig_t rrsig;
115 	isc_buffer_t typebuf;
116 	isc_result_t result;
117 
118 	REQUIRE(expected != NULL);
119 	REQUIRE(found != NULL);
120 	REQUIRE(index > 0);
121 
122 	/*
123 	 * Check operation.
124 	 */
125 	assert_int_equal(expected->op, found->op);
126 
127 	/*
128 	 * Check owner name.
129 	 */
130 	expected_name = dns_fixedname_initname(&expected_fname);
131 	result = dns_name_fromstring(expected_name, expected->owner,
132 				     dns_rootname, 0, mctx);
133 	assert_int_equal(result, ISC_R_SUCCESS);
134 	dns_name_format(&found->name, found_name, sizeof(found_name));
135 	assert_true(dns_name_equal(expected_name, &found->name));
136 
137 	/*
138 	 * Check TTL.
139 	 */
140 	assert_int_equal(expected->ttl, found->ttl);
141 
142 	/*
143 	 * Parse expected RR type.
144 	 */
145 	typeregion.base = expected->type;
146 	typeregion.length = strlen(expected->type);
147 	result = dns_rdatatype_fromtext(&expected_type,
148 					(isc_textregion_t *)&typeregion);
149 	assert_int_equal(result, ISC_R_SUCCESS);
150 
151 	/*
152 	 * Format found RR type for reporting purposes.
153 	 */
154 	isc_buffer_init(&typebuf, found_type, sizeof(found_type));
155 	result = dns_rdatatype_totext(found->rdata.type, &typebuf);
156 	assert_int_equal(result, ISC_R_SUCCESS);
157 
158 	/*
159 	 * Check RR type.
160 	 */
161 	switch (expected->op) {
162 	case DNS_DIFFOP_ADDRESIGN:
163 	case DNS_DIFFOP_DELRESIGN:
164 		/*
165 		 * Found tuple must be of type RRSIG.
166 		 */
167 		assert_int_equal(found->rdata.type, dns_rdatatype_rrsig);
168 		if (found->rdata.type != dns_rdatatype_rrsig) {
169 			break;
170 		}
171 		/*
172 		 * The signature must cover an RRset of type 'expected->type'.
173 		 */
174 		result = dns_rdata_tostruct(&found->rdata, &rrsig, NULL);
175 		assert_int_equal(result, ISC_R_SUCCESS);
176 		isc_buffer_init(&typebuf, found_covers, sizeof(found_covers));
177 		result = dns_rdatatype_totext(rrsig.covered, &typebuf);
178 		assert_int_equal(result, ISC_R_SUCCESS);
179 		assert_int_equal(expected_type, rrsig.covered);
180 		break;
181 	default:
182 		/*
183 		 * Found tuple must be of type 'expected->type'.
184 		 */
185 		assert_int_equal(expected_type, found->rdata.type);
186 		break;
187 	}
188 }
189 
190 /*%
191  * Perform a single dns__zone_updatesigs() test defined in 'test'.  All other
192  * arguments are expected to remain constant between subsequent invocations of
193  * this function.
194  */
195 static void
196 updatesigs_test(const updatesigs_test_params_t *test, dns_zone_t *zone,
197 		dns_db_t *db, dst_key_t *zone_keys[], unsigned int nkeys,
198 		isc_stdtime_t now) {
199 	size_t tuples_expected, tuples_found, index;
200 	dns_dbversion_t *version = NULL;
201 	dns_diff_t raw_diff, zone_diff;
202 	const zonediff_t *expected;
203 	dns_difftuple_t *found;
204 	isc_result_t result;
205 
206 	dns__zonediff_t zonediff = {
207 		.diff = &zone_diff,
208 		.offline = false,
209 	};
210 
211 	REQUIRE(test != NULL);
212 	REQUIRE(test->description != NULL);
213 	REQUIRE(test->changes != NULL);
214 	REQUIRE(zone != NULL);
215 	REQUIRE(db != NULL);
216 	REQUIRE(zone_keys != NULL);
217 
218 	/*
219 	 * Create a new version of the zone's database.
220 	 */
221 	result = dns_db_newversion(db, &version);
222 	assert_int_equal(result, ISC_R_SUCCESS);
223 
224 	/*
225 	 * Create a diff representing the supplied changes.
226 	 */
227 	result = dns_test_difffromchanges(&raw_diff, test->changes, false);
228 	assert_int_equal(result, ISC_R_SUCCESS);
229 
230 	/*
231 	 * Apply the "raw" diff to the new version of the zone's database as
232 	 * this is what dns__zone_updatesigs() expects to happen before it is
233 	 * called.
234 	 */
235 	dns_diff_apply(&raw_diff, db, version);
236 
237 	/*
238 	 * Initialize the structure dns__zone_updatesigs() will modify.
239 	 */
240 	dns_diff_init(mctx, &zone_diff);
241 
242 	/*
243 	 * Check whether dns__zone_updatesigs() behaves as expected.
244 	 */
245 	result = dns__zone_updatesigs(&raw_diff, db, version, zone_keys, nkeys,
246 				      zone, now - 3600, now + 3600, 0, now,
247 				      &zonediff);
248 	assert_int_equal(result, ISC_R_SUCCESS);
249 	assert_true(ISC_LIST_EMPTY(raw_diff.tuples));
250 	assert_false(ISC_LIST_EMPTY(zone_diff.tuples));
251 
252 	/*
253 	 * Ensure that the number of tuples in the zone diff is as expected.
254 	 */
255 
256 	tuples_expected = 0;
257 	for (expected = test->zonediff; expected->owner != NULL; expected++) {
258 		tuples_expected++;
259 	}
260 
261 	tuples_found = 0;
262 	for (found = ISC_LIST_HEAD(zone_diff.tuples); found != NULL;
263 	     found = ISC_LIST_NEXT(found, link))
264 	{
265 		tuples_found++;
266 	}
267 
268 	assert_int_equal(tuples_expected, tuples_found);
269 
270 	/*
271 	 * Ensure that every tuple in the zone diff matches expectations.
272 	 */
273 	expected = test->zonediff;
274 	index = 1;
275 	for (found = ISC_LIST_HEAD(zone_diff.tuples); found != NULL;
276 	     found = ISC_LIST_NEXT(found, link))
277 	{
278 		compare_tuples(expected, found, index);
279 		expected++;
280 		index++;
281 	}
282 
283 	/*
284 	 * Apply changes to zone database contents and clean up.
285 	 */
286 	dns_db_closeversion(db, &version, true);
287 	dns_diff_clear(&zone_diff);
288 	dns_diff_clear(&raw_diff);
289 }
290 
291 /* dns__zone_updatesigs() tests */
292 ISC_RUN_TEST_IMPL(updatesigs_next) {
293 	dst_key_t *zone_keys[DNS_MAXZONEKEYS];
294 	dns_zone_t *zone = NULL;
295 	dns_db_t *db = NULL;
296 	isc_result_t result;
297 	unsigned int nkeys;
298 	isc_stdtime_t now = isc_stdtime_now();
299 	size_t i;
300 
301 	UNUSED(state);
302 
303 	/*
304 	 * Prepare a zone along with its signing keys.
305 	 */
306 
307 	result = dns_test_makezone("example", &zone, NULL, false);
308 	assert_int_equal(result, ISC_R_SUCCESS);
309 
310 	result = dns_test_loaddb(&db, dns_dbtype_zone, "example",
311 				 "testdata/master/master18.data");
312 	assert_int_equal(result, DNS_R_SEENINCLUDE);
313 
314 	result = dns_zone_setkeydirectory(zone, TESTS_DIR "/testkeys");
315 	assert_int_equal(result, ISC_R_SUCCESS);
316 
317 	result = dns_zone_findkeys(zone, db, NULL, now, mctx, DNS_MAXZONEKEYS,
318 				   zone_keys, &nkeys);
319 	assert_int_equal(result, ISC_R_SUCCESS);
320 	assert_int_equal(nkeys, 2);
321 
322 	/*
323 	 * Define the tests to be run.  Note that changes to zone database
324 	 * contents introduced by each test are preserved between tests.
325 	 */
326 
327 	const zonechange_t changes_add[] = {
328 		{ DNS_DIFFOP_ADD, "foo.example", 300, "TXT", "foo" },
329 		{ DNS_DIFFOP_ADD, "bar.example", 600, "TXT", "bar" },
330 		ZONECHANGE_SENTINEL,
331 	};
332 	const zonediff_t zonediff_add[] = {
333 		{ DNS_DIFFOP_ADDRESIGN, "foo.example", 300, "TXT" },
334 		{ DNS_DIFFOP_ADD, "foo.example", 300, "TXT" },
335 		{ DNS_DIFFOP_ADDRESIGN, "bar.example", 600, "TXT" },
336 		{ DNS_DIFFOP_ADD, "bar.example", 600, "TXT" },
337 		ZONEDIFF_SENTINEL,
338 	};
339 	const updatesigs_test_params_t test_add = {
340 		.description = "add new RRsets",
341 		.changes = changes_add,
342 		.zonediff = zonediff_add,
343 	};
344 
345 	const zonechange_t changes_append[] = {
346 		{ DNS_DIFFOP_ADD, "foo.example", 300, "TXT", "foo1" },
347 		{ DNS_DIFFOP_ADD, "foo.example", 300, "TXT", "foo2" },
348 		ZONECHANGE_SENTINEL,
349 	};
350 	const zonediff_t zonediff_append[] = {
351 		{ DNS_DIFFOP_DELRESIGN, "foo.example", 300, "TXT" },
352 		{ DNS_DIFFOP_ADDRESIGN, "foo.example", 300, "TXT" },
353 		{ DNS_DIFFOP_ADD, "foo.example", 300, "TXT" },
354 		{ DNS_DIFFOP_ADD, "foo.example", 300, "TXT" },
355 		ZONEDIFF_SENTINEL,
356 	};
357 	const updatesigs_test_params_t test_append = {
358 		.description = "append multiple RRs to an existing RRset",
359 		.changes = changes_append,
360 		.zonediff = zonediff_append,
361 	};
362 
363 	const zonechange_t changes_replace[] = {
364 		{ DNS_DIFFOP_DEL, "bar.example", 600, "TXT", "bar" },
365 		{ DNS_DIFFOP_ADD, "bar.example", 600, "TXT", "rab" },
366 		ZONECHANGE_SENTINEL,
367 	};
368 	const zonediff_t zonediff_replace[] = {
369 		{ DNS_DIFFOP_DELRESIGN, "bar.example", 600, "TXT" },
370 		{ DNS_DIFFOP_ADDRESIGN, "bar.example", 600, "TXT" },
371 		{ DNS_DIFFOP_DEL, "bar.example", 600, "TXT" },
372 		{ DNS_DIFFOP_ADD, "bar.example", 600, "TXT" },
373 		ZONEDIFF_SENTINEL,
374 	};
375 	const updatesigs_test_params_t test_replace = {
376 		.description = "replace an existing RRset",
377 		.changes = changes_replace,
378 		.zonediff = zonediff_replace,
379 	};
380 
381 	const zonechange_t changes_delete[] = {
382 		{ DNS_DIFFOP_DEL, "bar.example", 600, "TXT", "rab" },
383 		ZONECHANGE_SENTINEL,
384 	};
385 	const zonediff_t zonediff_delete[] = {
386 		{ DNS_DIFFOP_DELRESIGN, "bar.example", 600, "TXT" },
387 		{ DNS_DIFFOP_DEL, "bar.example", 600, "TXT" },
388 		ZONEDIFF_SENTINEL,
389 	};
390 	const updatesigs_test_params_t test_delete = {
391 		.description = "delete an existing RRset",
392 		.changes = changes_delete,
393 		.zonediff = zonediff_delete,
394 	};
395 
396 	const zonechange_t changes_mixed[] = {
397 		{ DNS_DIFFOP_ADD, "baz.example", 900, "TXT", "baz1" },
398 		{ DNS_DIFFOP_ADD, "baz.example", 900, "A", "127.0.0.1" },
399 		{ DNS_DIFFOP_ADD, "baz.example", 900, "TXT", "baz2" },
400 		{ DNS_DIFFOP_ADD, "baz.example", 900, "AAAA", "::1" },
401 		ZONECHANGE_SENTINEL,
402 	};
403 	const zonediff_t zonediff_mixed[] = {
404 		{ DNS_DIFFOP_ADDRESIGN, "baz.example", 900, "TXT" },
405 		{ DNS_DIFFOP_ADD, "baz.example", 900, "TXT" },
406 		{ DNS_DIFFOP_ADD, "baz.example", 900, "TXT" },
407 		{ DNS_DIFFOP_ADDRESIGN, "baz.example", 900, "A" },
408 		{ DNS_DIFFOP_ADD, "baz.example", 900, "A" },
409 		{ DNS_DIFFOP_ADDRESIGN, "baz.example", 900, "AAAA" },
410 		{ DNS_DIFFOP_ADD, "baz.example", 900, "AAAA" },
411 		ZONEDIFF_SENTINEL,
412 	};
413 	const updatesigs_test_params_t test_mixed = {
414 		.description = "add different RRsets with common owner name",
415 		.changes = changes_mixed,
416 		.zonediff = zonediff_mixed,
417 	};
418 
419 	const updatesigs_test_params_t *tests[] = {
420 		&test_add,    &test_append, &test_replace,
421 		&test_delete, &test_mixed,
422 	};
423 
424 	/*
425 	 * Run tests.
426 	 */
427 	for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
428 		updatesigs_test(tests[i], zone, db, zone_keys, nkeys, now);
429 	}
430 
431 	/*
432 	 * Clean up.
433 	 */
434 	for (i = 0; i < nkeys; i++) {
435 		dst_key_free(&zone_keys[i]);
436 	}
437 	dns_db_detach(&db);
438 	dns_zone_detach(&zone);
439 }
440 
441 ISC_TEST_LIST_START
442 ISC_TEST_ENTRY_CUSTOM(updatesigs_next, setup_test, teardown_test)
443 ISC_TEST_LIST_END
444 
445 ISC_TEST_MAIN
446