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