xref: /netbsd-src/external/mpl/bind/dist/tests/libtest/dns.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: dns.c,v 1.4 2025/01/26 16:25:51 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[0] = 1;
374 	specials['('] = 1;
375 	specials[')'] = 1;
376 	specials['"'] = 1;
377 	isc_lex_setspecials(lex, specials);
378 
379 	/*
380 	 * Expect DNS masterfile comments.
381 	 */
382 	isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE);
383 
384 	/*
385 	 * Point lexer at source.
386 	 */
387 	result = isc_lex_openbuffer(lex, &source);
388 	if (result != ISC_R_SUCCESS) {
389 		goto destroy_lexer;
390 	}
391 
392 	/*
393 	 * Set up target for storing uncompressed wire form of provided RDATA.
394 	 */
395 	isc_buffer_init(&target, dst, dstlen);
396 
397 	/*
398 	 * Set up callbacks so warnings and errors are not printed.
399 	 */
400 	if (!warnings) {
401 		dns_rdatacallbacks_init(&callbacks);
402 		callbacks.warn = callbacks.error = nullmsg;
403 	}
404 
405 	/*
406 	 * Parse input string, determining result.
407 	 */
408 	result = dns_rdata_fromtext(rdata, rdclass, rdtype, lex, dns_rootname,
409 				    0, mctx, &target, &callbacks);
410 
411 destroy_lexer:
412 	isc_lex_destroy(&lex);
413 
414 	return result;
415 }
416 
417 void
418 dns_test_namefromstring(const char *namestr, dns_fixedname_t *fname) {
419 	size_t length;
420 	isc_buffer_t *b = NULL;
421 	isc_result_t result;
422 	dns_name_t *name;
423 
424 	length = strlen(namestr);
425 
426 	name = dns_fixedname_initname(fname);
427 
428 	isc_buffer_allocate(mctx, &b, length);
429 
430 	isc_buffer_putmem(b, (const unsigned char *)namestr, length);
431 	result = dns_name_fromtext(name, b, NULL, 0, NULL);
432 	INSIST(result == ISC_R_SUCCESS);
433 
434 	isc_buffer_free(&b);
435 }
436 
437 isc_result_t
438 dns_test_difffromchanges(dns_diff_t *diff, const zonechange_t *changes,
439 			 bool warnings) {
440 	isc_result_t result = ISC_R_SUCCESS;
441 	unsigned char rdata_buf[1024];
442 	dns_difftuple_t *tuple = NULL;
443 	isc_consttextregion_t region;
444 	dns_rdatatype_t rdatatype;
445 	dns_fixedname_t fixedname;
446 	dns_rdata_t rdata;
447 	dns_name_t *name;
448 	size_t i;
449 
450 	REQUIRE(diff != NULL);
451 	REQUIRE(changes != NULL);
452 
453 	dns_diff_init(mctx, diff);
454 
455 	for (i = 0; changes[i].owner != NULL; i++) {
456 		/*
457 		 * Parse owner name.
458 		 */
459 		name = dns_fixedname_initname(&fixedname);
460 		result = dns_name_fromstring(name, changes[i].owner,
461 					     dns_rootname, 0, mctx);
462 		if (result != ISC_R_SUCCESS) {
463 			break;
464 		}
465 
466 		/*
467 		 * Parse RDATA type.
468 		 */
469 		region.base = changes[i].type;
470 		region.length = strlen(changes[i].type);
471 		result = dns_rdatatype_fromtext(&rdatatype,
472 						(isc_textregion_t *)&region);
473 		if (result != ISC_R_SUCCESS) {
474 			break;
475 		}
476 
477 		/*
478 		 * Parse RDATA.
479 		 */
480 		dns_rdata_init(&rdata);
481 		result = dns_test_rdatafromstring(
482 			&rdata, dns_rdataclass_in, rdatatype, rdata_buf,
483 			sizeof(rdata_buf), changes[i].rdata, warnings);
484 		if (result != ISC_R_SUCCESS) {
485 			break;
486 		}
487 
488 		/*
489 		 * Create a diff tuple for the parsed change and append it to
490 		 * the diff.
491 		 */
492 		result = dns_difftuple_create(mctx, changes[i].op, name,
493 					      changes[i].ttl, &rdata, &tuple);
494 		if (result != ISC_R_SUCCESS) {
495 			break;
496 		}
497 		dns_diff_append(diff, &tuple);
498 	}
499 
500 	if (result != ISC_R_SUCCESS) {
501 		dns_diff_clear(diff);
502 	}
503 
504 	return result;
505 }
506