1 /* $NetBSD: master_test.c,v 1.9 2022/09/23 12:15:32 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 #if HAVE_CMOCKA
17
18 #include <sched.h> /* IWYU pragma: keep */
19 #include <setjmp.h>
20 #include <stdarg.h>
21 #include <stdbool.h>
22 #include <stddef.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26
27 #define UNIT_TESTING
28 #include <cmocka.h>
29
30 #include <isc/print.h>
31 #include <isc/string.h>
32 #include <isc/util.h>
33
34 #include <dns/cache.h>
35 #include <dns/callbacks.h>
36 #include <dns/db.h>
37 #include <dns/master.h>
38 #include <dns/masterdump.h>
39 #include <dns/name.h>
40 #include <dns/rdata.h>
41 #include <dns/rdatalist.h>
42 #include <dns/rdataset.h>
43
44 #include "dnstest.h"
45
46 static int
_setup(void ** state)47 _setup(void **state) {
48 isc_result_t result;
49
50 UNUSED(state);
51
52 result = dns_test_begin(NULL, false);
53 assert_int_equal(result, ISC_R_SUCCESS);
54
55 return (0);
56 }
57
58 static int
_teardown(void ** state)59 _teardown(void **state) {
60 UNUSED(state);
61
62 dns_test_end();
63
64 return (0);
65 }
66
67 static void
nullmsg(dns_rdatacallbacks_t * cb,const char * fmt,...)68 nullmsg(dns_rdatacallbacks_t *cb, const char *fmt, ...) {
69 UNUSED(cb);
70 UNUSED(fmt);
71 }
72
73 #define BUFLEN 255
74 #define BIGBUFLEN (70 * 1024)
75 #define TEST_ORIGIN "test"
76
77 static dns_masterrawheader_t header;
78 static bool headerset;
79
80 dns_name_t dns_origin;
81 char origin[sizeof(TEST_ORIGIN)];
82 unsigned char name_buf[BUFLEN];
83 dns_rdatacallbacks_t callbacks;
84 char *include_file = NULL;
85
86 static void
87 rawdata_callback(dns_zone_t *zone, dns_masterrawheader_t *header);
88
89 static isc_result_t
add_callback(void * arg,const dns_name_t * owner,dns_rdataset_t * dataset)90 add_callback(void *arg, const dns_name_t *owner, dns_rdataset_t *dataset) {
91 char buf[BIGBUFLEN];
92 isc_buffer_t target;
93 isc_result_t result;
94
95 UNUSED(arg);
96
97 isc_buffer_init(&target, buf, BIGBUFLEN);
98 result = dns_rdataset_totext(dataset, owner, false, false, &target);
99 return (result);
100 }
101
102 static void
rawdata_callback(dns_zone_t * zone,dns_masterrawheader_t * h)103 rawdata_callback(dns_zone_t *zone, dns_masterrawheader_t *h) {
104 UNUSED(zone);
105 header = *h;
106 headerset = true;
107 }
108
109 static isc_result_t
setup_master(void (* warn)(struct dns_rdatacallbacks *,const char *,...),void (* error)(struct dns_rdatacallbacks *,const char *,...))110 setup_master(void (*warn)(struct dns_rdatacallbacks *, const char *, ...),
111 void (*error)(struct dns_rdatacallbacks *, const char *, ...)) {
112 isc_result_t result;
113 int len;
114 isc_buffer_t source;
115 isc_buffer_t target;
116
117 strlcpy(origin, TEST_ORIGIN, sizeof(origin));
118 len = strlen(origin);
119 isc_buffer_init(&source, origin, len);
120 isc_buffer_add(&source, len);
121 isc_buffer_setactive(&source, len);
122 isc_buffer_init(&target, name_buf, BUFLEN);
123 dns_name_init(&dns_origin, NULL);
124 dns_master_initrawheader(&header);
125
126 result = dns_name_fromtext(&dns_origin, &source, dns_rootname, 0,
127 &target);
128 if (result != ISC_R_SUCCESS) {
129 return (result);
130 }
131
132 dns_rdatacallbacks_init_stdio(&callbacks);
133 callbacks.add = add_callback;
134 callbacks.rawdata = rawdata_callback;
135 callbacks.zone = NULL;
136 if (warn != NULL) {
137 callbacks.warn = warn;
138 }
139 if (error != NULL) {
140 callbacks.error = error;
141 }
142 headerset = false;
143 return (result);
144 }
145
146 static isc_result_t
test_master(const char * testfile,dns_masterformat_t format,void (* warn)(struct dns_rdatacallbacks *,const char *,...),void (* error)(struct dns_rdatacallbacks *,const char *,...))147 test_master(const char *testfile, dns_masterformat_t format,
148 void (*warn)(struct dns_rdatacallbacks *, const char *, ...),
149 void (*error)(struct dns_rdatacallbacks *, const char *, ...)) {
150 isc_result_t result;
151
152 result = setup_master(warn, error);
153 if (result != ISC_R_SUCCESS) {
154 return (result);
155 }
156
157 dns_rdatacallbacks_init_stdio(&callbacks);
158 callbacks.add = add_callback;
159 callbacks.rawdata = rawdata_callback;
160 callbacks.zone = NULL;
161 if (warn != NULL) {
162 callbacks.warn = warn;
163 }
164 if (error != NULL) {
165 callbacks.error = error;
166 }
167
168 result = dns_master_loadfile(testfile, &dns_origin, &dns_origin,
169 dns_rdataclass_in, true, 0, &callbacks,
170 NULL, NULL, dt_mctx, format, 0);
171 return (result);
172 }
173
174 static void
include_callback(const char * filename,void * arg)175 include_callback(const char *filename, void *arg) {
176 char **argp = (char **)arg;
177 *argp = isc_mem_strdup(dt_mctx, filename);
178 }
179
180 /*
181 * Successful load test:
182 * dns_master_loadfile() loads a valid master file and returns success
183 */
184 static void
load_test(void ** state)185 load_test(void **state) {
186 isc_result_t result;
187
188 UNUSED(state);
189
190 result = test_master("testdata/master/master1.data",
191 dns_masterformat_text, nullmsg, nullmsg);
192 assert_int_equal(result, ISC_R_SUCCESS);
193 }
194
195 /*
196 * Unexpected end of file test:
197 * dns_master_loadfile() returns DNS_R_UNEXPECTED when file ends too soon
198 */
199 static void
unexpected_test(void ** state)200 unexpected_test(void **state) {
201 isc_result_t result;
202
203 UNUSED(state);
204
205 result = test_master("testdata/master/master2.data",
206 dns_masterformat_text, nullmsg, nullmsg);
207 assert_int_equal(result, ISC_R_UNEXPECTEDEND);
208 }
209
210 /*
211 * No owner test:
212 * dns_master_loadfile() accepts broken zones with no TTL for first record
213 * if it is an SOA
214 */
215 static void
noowner_test(void ** state)216 noowner_test(void **state) {
217 isc_result_t result;
218
219 UNUSED(state);
220
221 result = test_master("testdata/master/master3.data",
222 dns_masterformat_text, nullmsg, nullmsg);
223 assert_int_equal(result, DNS_R_NOOWNER);
224 }
225
226 /*
227 * No TTL test:
228 * dns_master_loadfile() returns DNS_R_NOOWNER when no owner name is
229 * specified
230 */
231 static void
nottl_test(void ** state)232 nottl_test(void **state) {
233 isc_result_t result;
234
235 UNUSED(state);
236
237 result = test_master("testdata/master/master4.data",
238 dns_masterformat_text, nullmsg, nullmsg);
239 assert_int_equal(result, ISC_R_SUCCESS);
240 }
241
242 /*
243 * Bad class test:
244 * dns_master_loadfile() returns DNS_R_BADCLASS when record class doesn't
245 * match zone class
246 */
247 static void
badclass_test(void ** state)248 badclass_test(void **state) {
249 isc_result_t result;
250
251 UNUSED(state);
252
253 result = test_master("testdata/master/master5.data",
254 dns_masterformat_text, nullmsg, nullmsg);
255 assert_int_equal(result, DNS_R_BADCLASS);
256 }
257
258 /*
259 * Too big rdata test:
260 * dns_master_loadfile() returns ISC_R_NOSPACE when record is too big
261 */
262 static void
toobig_test(void ** state)263 toobig_test(void **state) {
264 isc_result_t result;
265
266 UNUSED(state);
267
268 result = test_master("testdata/master/master15.data",
269 dns_masterformat_text, nullmsg, nullmsg);
270 assert_int_equal(result, ISC_R_NOSPACE);
271 }
272
273 /*
274 * Maximum rdata test:
275 * dns_master_loadfile() returns ISC_R_SUCCESS when record is maximum size
276 */
277 static void
maxrdata_test(void ** state)278 maxrdata_test(void **state) {
279 isc_result_t result;
280
281 UNUSED(state);
282
283 result = test_master("testdata/master/master16.data",
284 dns_masterformat_text, nullmsg, nullmsg);
285 assert_int_equal(result, ISC_R_SUCCESS);
286 }
287
288 /*
289 * DNSKEY test:
290 * dns_master_loadfile() understands DNSKEY with key material
291 */
292 static void
dnskey_test(void ** state)293 dnskey_test(void **state) {
294 isc_result_t result;
295
296 UNUSED(state);
297
298 result = test_master("testdata/master/master6.data",
299 dns_masterformat_text, nullmsg, nullmsg);
300 assert_int_equal(result, ISC_R_SUCCESS);
301 }
302
303 /*
304 * DNSKEY with no key material test:
305 * dns_master_loadfile() understands DNSKEY with no key material
306 *
307 * RFC 4034 removed the ability to signal NOKEY, so empty key material should
308 * be rejected.
309 */
310 static void
dnsnokey_test(void ** state)311 dnsnokey_test(void **state) {
312 isc_result_t result;
313
314 UNUSED(state);
315
316 result = test_master("testdata/master/master7.data",
317 dns_masterformat_text, nullmsg, nullmsg);
318 assert_int_equal(result, ISC_R_UNEXPECTEDEND);
319 }
320
321 /*
322 * Include test:
323 * dns_master_loadfile() understands $INCLUDE
324 */
325 static void
include_test(void ** state)326 include_test(void **state) {
327 isc_result_t result;
328
329 UNUSED(state);
330
331 result = test_master("testdata/master/master8.data",
332 dns_masterformat_text, nullmsg, nullmsg);
333 assert_int_equal(result, DNS_R_SEENINCLUDE);
334 }
335
336 /*
337 * Include file list test:
338 * dns_master_loadfile4() returns names of included file
339 */
340 static void
master_includelist_test(void ** state)341 master_includelist_test(void **state) {
342 isc_result_t result;
343 char *filename = NULL;
344
345 UNUSED(state);
346
347 result = setup_master(nullmsg, nullmsg);
348 assert_int_equal(result, ISC_R_SUCCESS);
349
350 result = dns_master_loadfile(
351 "testdata/master/master8.data", &dns_origin, &dns_origin,
352 dns_rdataclass_in, 0, true, &callbacks, include_callback,
353 &filename, dt_mctx, dns_masterformat_text, 0);
354 assert_int_equal(result, DNS_R_SEENINCLUDE);
355 assert_non_null(filename);
356 if (filename != NULL) {
357 assert_string_equal(filename, "testdata/master/master6.data");
358 isc_mem_free(dt_mctx, filename);
359 }
360 }
361
362 /*
363 * Include failure test:
364 * dns_master_loadfile() understands $INCLUDE failures
365 */
366 static void
includefail_test(void ** state)367 includefail_test(void **state) {
368 isc_result_t result;
369
370 UNUSED(state);
371
372 result = test_master("testdata/master/master9.data",
373 dns_masterformat_text, nullmsg, nullmsg);
374 assert_int_equal(result, DNS_R_BADCLASS);
375 }
376
377 /*
378 * Non-empty blank lines test:
379 * dns_master_loadfile() handles non-empty blank lines
380 */
381 static void
blanklines_test(void ** state)382 blanklines_test(void **state) {
383 isc_result_t result;
384
385 UNUSED(state);
386
387 result = test_master("testdata/master/master10.data",
388 dns_masterformat_text, nullmsg, nullmsg);
389 assert_int_equal(result, ISC_R_SUCCESS);
390 }
391
392 /*
393 * SOA leading zeroes test:
394 * dns_master_loadfile() allows leading zeroes in SOA
395 */
396
397 static void
leadingzero_test(void ** state)398 leadingzero_test(void **state) {
399 isc_result_t result;
400
401 UNUSED(state);
402
403 result = test_master("testdata/master/master11.data",
404 dns_masterformat_text, nullmsg, nullmsg);
405 assert_int_equal(result, ISC_R_SUCCESS);
406 }
407
408 /* masterfile totext tests */
409 static void
totext_test(void ** state)410 totext_test(void **state) {
411 isc_result_t result;
412 dns_rdataset_t rdataset;
413 dns_rdatalist_t rdatalist;
414 isc_buffer_t target;
415 unsigned char buf[BIGBUFLEN];
416
417 UNUSED(state);
418
419 /* First, test with an empty rdataset */
420 dns_rdatalist_init(&rdatalist);
421 rdatalist.rdclass = dns_rdataclass_in;
422 rdatalist.type = dns_rdatatype_none;
423 rdatalist.covers = dns_rdatatype_none;
424
425 dns_rdataset_init(&rdataset);
426 result = dns_rdatalist_tordataset(&rdatalist, &rdataset);
427 assert_int_equal(result, ISC_R_SUCCESS);
428
429 isc_buffer_init(&target, buf, BIGBUFLEN);
430 result = dns_master_rdatasettotext(dns_rootname, &rdataset,
431 &dns_master_style_debug, NULL,
432 &target);
433 assert_int_equal(result, ISC_R_SUCCESS);
434 assert_int_equal(isc_buffer_usedlength(&target), 0);
435
436 /*
437 * XXX: We will also need to add tests for dumping various
438 * rdata types, classes, etc, and comparing the results against
439 * known-good output.
440 */
441 }
442
443 /*
444 * Raw load test:
445 * dns_master_loadfile() loads a valid raw file and returns success
446 */
447 static void
loadraw_test(void ** state)448 loadraw_test(void **state) {
449 isc_result_t result;
450
451 UNUSED(state);
452
453 /* Raw format version 0 */
454 result = test_master("testdata/master/master12.data",
455 dns_masterformat_raw, nullmsg, nullmsg);
456 assert_string_equal(isc_result_totext(result), "success");
457 assert_true(headerset);
458 assert_int_equal(header.flags, 0);
459
460 /* Raw format version 1, no source serial */
461 result = test_master("testdata/master/master13.data",
462 dns_masterformat_raw, nullmsg, nullmsg);
463 assert_string_equal(isc_result_totext(result), "success");
464 assert_true(headerset);
465 assert_int_equal(header.flags, 0);
466
467 /* Raw format version 1, source serial == 2011120101 */
468 result = test_master("testdata/master/master14.data",
469 dns_masterformat_raw, nullmsg, nullmsg);
470 assert_string_equal(isc_result_totext(result), "success");
471 assert_true(headerset);
472 assert_true((header.flags & DNS_MASTERRAW_SOURCESERIALSET) != 0);
473 assert_int_equal(header.sourceserial, 2011120101);
474 }
475
476 /*
477 * Raw dump test:
478 * dns_master_dump*() functions dump valid raw files
479 */
480 static void
dumpraw_test(void ** state)481 dumpraw_test(void **state) {
482 isc_result_t result;
483 dns_db_t *db = NULL;
484 dns_dbversion_t *version = NULL;
485 char myorigin[sizeof(TEST_ORIGIN)];
486 dns_name_t dnsorigin;
487 isc_buffer_t source, target;
488 unsigned char namebuf[BUFLEN];
489 int len;
490
491 UNUSED(state);
492
493 strlcpy(myorigin, TEST_ORIGIN, sizeof(myorigin));
494 len = strlen(myorigin);
495 isc_buffer_init(&source, myorigin, len);
496 isc_buffer_add(&source, len);
497 isc_buffer_setactive(&source, len);
498 isc_buffer_init(&target, namebuf, BUFLEN);
499 dns_name_init(&dnsorigin, NULL);
500 result = dns_name_fromtext(&dnsorigin, &source, dns_rootname, 0,
501 &target);
502 assert_int_equal(result, ISC_R_SUCCESS);
503
504 result = dns_db_create(dt_mctx, "rbt", &dnsorigin, dns_dbtype_zone,
505 dns_rdataclass_in, 0, NULL, &db);
506 assert_int_equal(result, ISC_R_SUCCESS);
507
508 result = dns_db_load(db, "testdata/master/master1.data",
509 dns_masterformat_text, 0);
510 assert_int_equal(result, ISC_R_SUCCESS);
511
512 dns_db_currentversion(db, &version);
513
514 result = dns_master_dump(dt_mctx, db, version,
515 &dns_master_style_default, "test.dump",
516 dns_masterformat_raw, NULL);
517 assert_int_equal(result, ISC_R_SUCCESS);
518
519 result = test_master("test.dump", dns_masterformat_raw, nullmsg,
520 nullmsg);
521 assert_string_equal(isc_result_totext(result), "success");
522 assert_true(headerset);
523 assert_int_equal(header.flags, 0);
524
525 dns_master_initrawheader(&header);
526 header.sourceserial = 12345;
527 header.flags |= DNS_MASTERRAW_SOURCESERIALSET;
528
529 unlink("test.dump");
530 result = dns_master_dump(dt_mctx, db, version,
531 &dns_master_style_default, "test.dump",
532 dns_masterformat_raw, &header);
533 assert_int_equal(result, ISC_R_SUCCESS);
534
535 result = test_master("test.dump", dns_masterformat_raw, nullmsg,
536 nullmsg);
537 assert_string_equal(isc_result_totext(result), "success");
538 assert_true(headerset);
539 assert_true((header.flags & DNS_MASTERRAW_SOURCESERIALSET) != 0);
540 assert_int_equal(header.sourceserial, 12345);
541
542 unlink("test.dump");
543 dns_db_closeversion(db, &version, false);
544 dns_db_detach(&db);
545 }
546
547 static const char *warn_expect_value;
548 static bool warn_expect_result;
549
550 static void
warn_expect(struct dns_rdatacallbacks * mycallbacks,const char * fmt,...)551 warn_expect(struct dns_rdatacallbacks *mycallbacks, const char *fmt, ...) {
552 char buf[4096];
553 va_list ap;
554
555 UNUSED(mycallbacks);
556
557 warn_expect_result = false;
558
559 va_start(ap, fmt);
560 vsnprintf(buf, sizeof(buf), fmt, ap);
561 va_end(ap);
562
563 if (warn_expect_value != NULL && strstr(buf, warn_expect_value) != NULL)
564 {
565 warn_expect_result = true;
566 }
567 }
568
569 /*
570 * Origin change test:
571 * dns_master_loadfile() rejects zones with inherited name following $ORIGIN
572 */
573 static void
neworigin_test(void ** state)574 neworigin_test(void **state) {
575 isc_result_t result;
576
577 UNUSED(state);
578
579 warn_expect_value = "record with inherited owner";
580 result = test_master("testdata/master/master17.data",
581 dns_masterformat_text, warn_expect, nullmsg);
582 assert_int_equal(result, ISC_R_SUCCESS);
583 assert_true(warn_expect_result);
584 }
585
586 int
main(void)587 main(void) {
588 const struct CMUnitTest tests[] = {
589 cmocka_unit_test_setup_teardown(load_test, _setup, _teardown),
590 cmocka_unit_test_setup_teardown(unexpected_test, _setup,
591 _teardown),
592 cmocka_unit_test_setup_teardown(noowner_test, _setup,
593 _teardown),
594 cmocka_unit_test_setup_teardown(nottl_test, _setup, _teardown),
595 cmocka_unit_test_setup_teardown(badclass_test, _setup,
596 _teardown),
597 cmocka_unit_test_setup_teardown(dnskey_test, _setup, _teardown),
598 cmocka_unit_test_setup_teardown(dnsnokey_test, _setup,
599 _teardown),
600 cmocka_unit_test_setup_teardown(include_test, _setup,
601 _teardown),
602 cmocka_unit_test_setup_teardown(master_includelist_test, _setup,
603 _teardown),
604 cmocka_unit_test_setup_teardown(includefail_test, _setup,
605 _teardown),
606 cmocka_unit_test_setup_teardown(blanklines_test, _setup,
607 _teardown),
608 cmocka_unit_test_setup_teardown(leadingzero_test, _setup,
609 _teardown),
610 cmocka_unit_test_setup_teardown(totext_test, _setup, _teardown),
611 cmocka_unit_test_setup_teardown(loadraw_test, _setup,
612 _teardown),
613 cmocka_unit_test_setup_teardown(dumpraw_test, _setup,
614 _teardown),
615 cmocka_unit_test_setup_teardown(toobig_test, _setup, _teardown),
616 cmocka_unit_test_setup_teardown(maxrdata_test, _setup,
617 _teardown),
618 cmocka_unit_test_setup_teardown(neworigin_test, _setup,
619 _teardown),
620 };
621
622 return (cmocka_run_group_tests(tests, NULL, NULL));
623 }
624
625 #else /* HAVE_CMOCKA */
626
627 #include <stdio.h>
628
629 int
main(void)630 main(void) {
631 printf("1..0 # Skipped: cmocka not available\n");
632 return (SKIPPED_TEST_EXIT_CODE);
633 }
634
635 #endif /* if HAVE_CMOCKA */
636