Home | History | Annotate | Line # | Download | only in libtest
      1 /*	$NetBSD: dns.c,v 1.5 2026/09/17 18:01:19 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 /*! \file */
     17 
     18 #include <inttypes.h>
     19 #include <sched.h> /* IWYU pragma: keep */
     20 #include <setjmp.h>
     21 #include <stdarg.h>
     22 #include <stdbool.h>
     23 #include <stddef.h>
     24 #include <stdlib.h>
     25 #include <string.h>
     26 #include <time.h>
     27 #include <unistd.h>
     28 
     29 #include <isc/buffer.h>
     30 #include <isc/file.h>
     31 #include <isc/hash.h>
     32 #include <isc/hex.h>
     33 #include <isc/lex.h>
     34 #include <isc/managers.h>
     35 #include <isc/mem.h>
     36 #include <isc/netmgr.h>
     37 #include <isc/os.h>
     38 #include <isc/random.h>
     39 #include <isc/result.h>
     40 #include <isc/stdio.h>
     41 #include <isc/string.h>
     42 #include <isc/timer.h>
     43 #include <isc/util.h>
     44 
     45 #include <dns/callbacks.h>
     46 #include <dns/db.h>
     47 #include <dns/dispatch.h>
     48 #include <dns/fixedname.h>
     49 #include <dns/log.h>
     50 #include <dns/name.h>
     51 #include <dns/view.h>
     52 #include <dns/zone.h>
     53 
     54 #include <tests/dns.h>
     55 
     56 dns_zonemgr_t *zonemgr = NULL;
     57 
     58 /*
     59  * Create a view.
     60  */
     61 isc_result_t
     62 dns_test_makeview(const char *name, bool with_dispatchmgr, bool with_cache,
     63 		  dns_view_t **viewp) {
     64 	isc_result_t result;
     65 	dns_view_t *view = NULL;
     66 	dns_cache_t *cache = NULL;
     67 	dns_dispatchmgr_t *dispatchmgr = NULL;
     68 
     69 	if (with_dispatchmgr) {
     70 		result = dns_dispatchmgr_create(mctx, loopmgr, netmgr,
     71 						&dispatchmgr);
     72 		if (result != ISC_R_SUCCESS) {
     73 			return result;
     74 		}
     75 	}
     76 
     77 	result = dns_view_create(mctx, loopmgr, dispatchmgr, dns_rdataclass_in,
     78 				 name, &view);
     79 
     80 	if (dispatchmgr != NULL) {
     81 		dns_dispatchmgr_detach(&dispatchmgr);
     82 	}
     83 
     84 	if (result != ISC_R_SUCCESS) {
     85 		return result;
     86 	}
     87 
     88 	if (with_cache) {
     89 		result = dns_cache_create(loopmgr, dns_rdataclass_in, "", mctx,
     90 					  &cache);
     91 		if (result != ISC_R_SUCCESS) {
     92 			dns_view_detach(&view);
     93 			return result;
     94 		}
     95 
     96 		dns_view_setcache(view, cache, false);
     97 		/*
     98 		 * Reference count for "cache" is now at 2, so decrement it in
     99 		 * order for the cache to be automatically freed when "view"
    100 		 * gets freed.
    101 		 */
    102 		dns_cache_detach(&cache);
    103 	}
    104 
    105 	*viewp = view;
    106 
    107 	return ISC_R_SUCCESS;
    108 }
    109 
    110 isc_result_t
    111 dns_test_makezone(const char *name, dns_zone_t **zonep, dns_view_t *view,
    112 		  bool createview) {
    113 	dns_fixedname_t fixed_origin;
    114 	dns_zone_t *zone = NULL;
    115 	isc_result_t result;
    116 	dns_name_t *origin;
    117 
    118 	REQUIRE(view == NULL || !createview);
    119 
    120 	/*
    121 	 * Create the zone structure.
    122 	 */
    123 	dns_zone_create(&zone, mctx, 0);
    124 
    125 	/*
    126 	 * Set zone type and origin.
    127 	 */
    128 	dns_zone_settype(zone, dns_zone_primary);
    129 	origin = dns_fixedname_initname(&fixed_origin);
    130 	result = dns_name_fromstring(origin, name, dns_rootname, 0, NULL);
    131 	if (result != ISC_R_SUCCESS) {
    132 		goto detach_zone;
    133 	}
    134 	result = dns_zone_setorigin(zone, origin);
    135 	if (result != ISC_R_SUCCESS) {
    136 		goto detach_zone;
    137 	}
    138 
    139 	/*
    140 	 * If requested, create a view.
    141 	 */
    142 	if (createview) {
    143 		result = dns_test_makeview("view", false, false, &view);
    144 		if (result != ISC_R_SUCCESS) {
    145 			goto detach_zone;
    146 		}
    147 	}
    148 
    149 	/*
    150 	 * If a view was passed as an argument or created above, attach the
    151 	 * created zone to it.  Otherwise, set the zone's class to IN.
    152 	 */
    153 	if (view != NULL) {
    154 		dns_zone_setview(zone, view);
    155 		dns_zone_setclass(zone, view->rdclass);
    156 		dns_view_addzone(view, zone);
    157 	} else {
    158 		dns_zone_setclass(zone, dns_rdataclass_in);
    159 	}
    160 
    161 	*zonep = zone;
    162 
    163 	return ISC_R_SUCCESS;
    164 
    165 detach_zone:
    166 	dns_zone_detach(&zone);
    167 
    168 	return result;
    169 }
    170 
    171 void
    172 dns_test_setupzonemgr(void) {
    173 	REQUIRE(zonemgr == NULL);
    174 
    175 	dns_zonemgr_create(mctx, netmgr, &zonemgr);
    176 }
    177 
    178 isc_result_t
    179 dns_test_managezone(dns_zone_t *zone) {
    180 	isc_result_t result;
    181 	REQUIRE(zonemgr != NULL);
    182 
    183 	result = dns_zonemgr_managezone(zonemgr, zone);
    184 	return result;
    185 }
    186 
    187 void
    188 dns_test_releasezone(dns_zone_t *zone) {
    189 	REQUIRE(zonemgr != NULL);
    190 	dns_zonemgr_releasezone(zonemgr, zone);
    191 }
    192 
    193 void
    194 dns_test_closezonemgr(void) {
    195 	REQUIRE(zonemgr != NULL);
    196 
    197 	dns_zonemgr_shutdown(zonemgr);
    198 	dns_zonemgr_detach(&zonemgr);
    199 }
    200 
    201 /*
    202  * Sleep for 'usec' microseconds.
    203  */
    204 void
    205 dns_test_nap(uint32_t usec) {
    206 	struct timespec ts;
    207 
    208 	ts.tv_sec = usec / (long)US_PER_SEC;
    209 	ts.tv_nsec = (usec % (long)US_PER_SEC) * (long)NS_PER_US;
    210 	nanosleep(&ts, NULL);
    211 }
    212 
    213 isc_result_t
    214 dns_test_loaddb(dns_db_t **db, dns_dbtype_t dbtype, const char *origin,
    215 		const char *testfile) {
    216 	isc_result_t result;
    217 	dns_fixedname_t fixed;
    218 	dns_name_t *name = NULL;
    219 	const char *dbimp = (dbtype == dns_dbtype_zone) ? ZONEDB_DEFAULT
    220 							: CACHEDB_DEFAULT;
    221 
    222 	name = dns_fixedname_initname(&fixed);
    223 
    224 	result = dns_name_fromstring(name, origin, dns_rootname, 0, NULL);
    225 	if (result != ISC_R_SUCCESS) {
    226 		return result;
    227 	}
    228 
    229 	result = dns_db_create(mctx, dbimp, name, dbtype, dns_rdataclass_in, 0,
    230 			       NULL, db);
    231 	if (result != ISC_R_SUCCESS) {
    232 		return result;
    233 	}
    234 
    235 	result = dns_db_load(*db, testfile, dns_masterformat_text, 0);
    236 	return result;
    237 }
    238 
    239 static int
    240 fromhex(char c) {
    241 	if (c >= '0' && c <= '9') {
    242 		return c - '0';
    243 	} else if (c >= 'a' && c <= 'f') {
    244 		return c - 'a' + 10;
    245 	} else if (c >= 'A' && c <= 'F') {
    246 		return c - 'A' + 10;
    247 	}
    248 
    249 	printf("bad input format: %02x\n", c);
    250 	exit(3);
    251 }
    252 
    253 /*
    254  * Format contents of given memory region as a hex string, using the buffer
    255  * of length 'buflen' pointed to by 'buf'. 'buflen' must be at least three
    256  * times 'len'. Always returns 'buf'.
    257  */
    258 char *
    259 dns_test_tohex(const unsigned char *data, size_t len, char *buf,
    260 	       size_t buflen) {
    261 	isc_constregion_t source = { .base = data, .length = len };
    262 	isc_buffer_t target;
    263 	isc_result_t result;
    264 
    265 	memset(buf, 0, buflen);
    266 	isc_buffer_init(&target, buf, buflen);
    267 	result = isc_hex_totext((isc_region_t *)&source, 1, " ", &target);
    268 	INSIST(result == ISC_R_SUCCESS);
    269 
    270 	return buf;
    271 }
    272 
    273 isc_result_t
    274 dns_test_getdata(const char *file, unsigned char *buf, size_t bufsiz,
    275 		 size_t *sizep) {
    276 	isc_result_t result;
    277 	unsigned char *bp;
    278 	char *rp, *wp;
    279 	char s[BUFSIZ];
    280 	size_t len, i;
    281 	FILE *f = NULL;
    282 	int n;
    283 
    284 	result = isc_stdio_open(file, "r", &f);
    285 	if (result != ISC_R_SUCCESS) {
    286 		return result;
    287 	}
    288 
    289 	bp = buf;
    290 	while (fgets(s, sizeof(s), f) != NULL) {
    291 		rp = s;
    292 		wp = s;
    293 		len = 0;
    294 		while (*rp != '\0') {
    295 			if (*rp == '#') {
    296 				break;
    297 			}
    298 			if (*rp != ' ' && *rp != '\t' && *rp != '\r' &&
    299 			    *rp != '\n')
    300 			{
    301 				*wp++ = *rp;
    302 				len++;
    303 			}
    304 			rp++;
    305 		}
    306 		if (len == 0U) {
    307 			continue;
    308 		}
    309 		if (len % 2 != 0U) {
    310 			result = ISC_R_UNEXPECTEDEND;
    311 			break;
    312 		}
    313 		if (len > bufsiz * 2) {
    314 			result = ISC_R_NOSPACE;
    315 			break;
    316 		}
    317 		rp = s;
    318 		for (i = 0; i < len; i += 2) {
    319 			n = fromhex(*rp++);
    320 			n *= 16;
    321 			n += fromhex(*rp++);
    322 			*bp++ = n;
    323 		}
    324 	}
    325 
    326 	if (result == ISC_R_SUCCESS) {
    327 		*sizep = bp - buf;
    328 	}
    329 
    330 	isc_stdio_close(f);
    331 	return result;
    332 }
    333 
    334 static void
    335 nullmsg(dns_rdatacallbacks_t *cb, const char *fmt, ...) {
    336 	UNUSED(cb);
    337 	UNUSED(fmt);
    338 }
    339 
    340 isc_result_t
    341 dns_test_rdatafromstring(dns_rdata_t *rdata, dns_rdataclass_t rdclass,
    342 			 dns_rdatatype_t rdtype, unsigned char *dst,
    343 			 size_t dstlen, const char *src, bool warnings) {
    344 	dns_rdatacallbacks_t callbacks;
    345 	isc_buffer_t source, target;
    346 	isc_lex_t *lex = NULL;
    347 	isc_lexspecials_t specials = { 0 };
    348 	isc_result_t result;
    349 	size_t length;
    350 
    351 	REQUIRE(rdata != NULL);
    352 	REQUIRE(DNS_RDATA_INITIALIZED(rdata));
    353 	REQUIRE(dst != NULL);
    354 	REQUIRE(src != NULL);
    355 
    356 	/*
    357 	 * Set up source to hold the input string.
    358 	 */
    359 	length = strlen(src);
    360 	isc_buffer_constinit(&source, src, length);
    361 	isc_buffer_add(&source, length);
    362 
    363 	/*
    364 	 * Create a lexer as one is required by dns_rdata_fromtext().
    365 	 */
    366 	isc_lex_create(mctx, 64, &lex);
    367 
    368 	/*
    369 	 * Set characters which will be treated as valid multi-line RDATA
    370 	 * delimiters while reading the source string.  These should match
    371 	 * specials from lib/dns/master.c.
    372 	 */
    373 	specials['('] = 1;
    374 	specials[')'] = 1;
    375 	specials['"'] = 1;
    376 	isc_lex_setspecials(lex, specials);
    377 
    378 	/*
    379 	 * Expect DNS masterfile comments.
    380 	 */
    381 	isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE);
    382 
    383 	/*
    384 	 * Point lexer at source.
    385 	 */
    386 	result = isc_lex_openbuffer(lex, &source);
    387 	if (result != ISC_R_SUCCESS) {
    388 		goto destroy_lexer;
    389 	}
    390 
    391 	/*
    392 	 * Set up target for storing uncompressed wire form of provided RDATA.
    393 	 */
    394 	isc_buffer_init(&target, dst, dstlen);
    395 
    396 	/*
    397 	 * Set up callbacks so warnings and errors are not printed.
    398 	 */
    399 	if (!warnings) {
    400 		dns_rdatacallbacks_init(&callbacks);
    401 		callbacks.warn = callbacks.error = nullmsg;
    402 	}
    403 
    404 	/*
    405 	 * Parse input string, determining result.
    406 	 */
    407 	result = dns_rdata_fromtext(rdata, rdclass, rdtype, lex, dns_rootname,
    408 				    0, mctx, &target, &callbacks);
    409 
    410 destroy_lexer:
    411 	isc_lex_destroy(&lex);
    412 
    413 	return result;
    414 }
    415 
    416 void
    417 dns_test_namefromstring(const char *namestr, dns_fixedname_t *fname) {
    418 	size_t length;
    419 	isc_buffer_t *b = NULL;
    420 	isc_result_t result;
    421 	dns_name_t *name;
    422 
    423 	length = strlen(namestr);
    424 
    425 	name = dns_fixedname_initname(fname);
    426 
    427 	isc_buffer_allocate(mctx, &b, length);
    428 
    429 	isc_buffer_putmem(b, (const unsigned char *)namestr, length);
    430 	result = dns_name_fromtext(name, b, NULL, 0, NULL);
    431 	INSIST(result == ISC_R_SUCCESS);
    432 
    433 	isc_buffer_free(&b);
    434 }
    435 
    436 isc_result_t
    437 dns_test_difffromchanges(dns_diff_t *diff, const zonechange_t *changes,
    438 			 bool warnings) {
    439 	isc_result_t result = ISC_R_SUCCESS;
    440 	unsigned char rdata_buf[1024];
    441 	dns_difftuple_t *tuple = NULL;
    442 	isc_consttextregion_t region;
    443 	dns_rdatatype_t rdatatype;
    444 	dns_fixedname_t fixedname;
    445 	dns_rdata_t rdata;
    446 	dns_name_t *name;
    447 	size_t i;
    448 
    449 	REQUIRE(diff != NULL);
    450 	REQUIRE(changes != NULL);
    451 
    452 	dns_diff_init(mctx, diff);
    453 
    454 	for (i = 0; changes[i].owner != NULL; i++) {
    455 		/*
    456 		 * Parse owner name.
    457 		 */
    458 		name = dns_fixedname_initname(&fixedname);
    459 		result = dns_name_fromstring(name, changes[i].owner,
    460 					     dns_rootname, 0, mctx);
    461 		if (result != ISC_R_SUCCESS) {
    462 			break;
    463 		}
    464 
    465 		/*
    466 		 * Parse RDATA type.
    467 		 */
    468 		region.base = changes[i].type;
    469 		region.length = strlen(changes[i].type);
    470 		result = dns_rdatatype_fromtext(&rdatatype,
    471 						(isc_textregion_t *)&region);
    472 		if (result != ISC_R_SUCCESS) {
    473 			break;
    474 		}
    475 
    476 		/*
    477 		 * Parse RDATA.
    478 		 */
    479 		dns_rdata_init(&rdata);
    480 		result = dns_test_rdatafromstring(
    481 			&rdata, dns_rdataclass_in, rdatatype, rdata_buf,
    482 			sizeof(rdata_buf), changes[i].rdata, warnings);
    483 		if (result != ISC_R_SUCCESS) {
    484 			break;
    485 		}
    486 
    487 		/*
    488 		 * Create a diff tuple for the parsed change and append it to
    489 		 * the diff.
    490 		 */
    491 		result = dns_difftuple_create(mctx, changes[i].op, name,
    492 					      changes[i].ttl, &rdata, &tuple);
    493 		if (result != ISC_R_SUCCESS) {
    494 			break;
    495 		}
    496 		dns_diff_append(diff, &tuple);
    497 	}
    498 
    499 	if (result != ISC_R_SUCCESS) {
    500 		dns_diff_clear(diff);
    501 	}
    502 
    503 	return result;
    504 }
    505