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