1 /* $NetBSD: named-checkzone.c,v 1.5 2020/08/03 17:23:36 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * See the COPYRIGHT file distributed with this work for additional 11 * information regarding copyright ownership. 12 */ 13 14 /*! \file */ 15 16 #include <inttypes.h> 17 #include <stdbool.h> 18 #include <stdlib.h> 19 20 #include <isc/app.h> 21 #include <isc/commandline.h> 22 #include <isc/dir.h> 23 #include <isc/hash.h> 24 #include <isc/log.h> 25 #include <isc/mem.h> 26 #include <isc/print.h> 27 #include <isc/socket.h> 28 #include <isc/string.h> 29 #include <isc/task.h> 30 #include <isc/timer.h> 31 #include <isc/util.h> 32 33 #include <dns/db.h> 34 #include <dns/fixedname.h> 35 #include <dns/log.h> 36 #include <dns/master.h> 37 #include <dns/masterdump.h> 38 #include <dns/name.h> 39 #include <dns/rdataclass.h> 40 #include <dns/rdataset.h> 41 #include <dns/result.h> 42 #include <dns/types.h> 43 #include <dns/zone.h> 44 45 #include "check-tool.h" 46 47 static int quiet = 0; 48 static isc_mem_t *mctx = NULL; 49 dns_zone_t *zone = NULL; 50 dns_zonetype_t zonetype = dns_zone_master; 51 static int dumpzone = 0; 52 static const char *output_filename; 53 static const char *prog_name = NULL; 54 static const dns_master_style_t *outputstyle = NULL; 55 static enum { progmode_check, progmode_compile } progmode; 56 57 #define ERRRET(result, function) \ 58 do { \ 59 if (result != ISC_R_SUCCESS) { \ 60 if (!quiet) \ 61 fprintf(stderr, "%s() returned %s\n", \ 62 function, dns_result_totext(result)); \ 63 return (result); \ 64 } \ 65 } while (/*CONSTCOND*/0) 66 67 ISC_PLATFORM_NORETURN_PRE static void 68 usage(void) ISC_PLATFORM_NORETURN_POST; 69 70 static void 71 usage(void) { 72 fprintf(stderr, 73 "usage: %s [-djqvD] [-c class] " 74 "[-f inputformat] [-F outputformat] [-J filename] " 75 "[-s (full|relative)] [-t directory] [-w directory] " 76 "[-k (ignore|warn|fail)] [-m (ignore|warn|fail)] " 77 "[-n (ignore|warn|fail)] [-r (ignore|warn|fail)] " 78 "[-i (full|full-sibling|local|local-sibling|none)] " 79 "[-M (ignore|warn|fail)] [-S (ignore|warn|fail)] " 80 "[-W (ignore|warn)] " 81 "%s zonename filename\n", 82 prog_name, 83 progmode == progmode_check ? "[-o filename]" : "-o filename"); 84 exit(1); 85 } 86 87 static void 88 destroy(void) { 89 if (zone != NULL) { 90 dns_zone_detach(&zone); 91 } 92 } 93 94 /*% main processing routine */ 95 int 96 main(int argc, char **argv) { 97 int c; 98 char *origin = NULL; 99 char *filename = NULL; 100 isc_log_t *lctx = NULL; 101 isc_result_t result; 102 char classname_in[] = "IN"; 103 char *classname = classname_in; 104 const char *workdir = NULL; 105 const char *inputformatstr = NULL; 106 const char *outputformatstr = NULL; 107 dns_masterformat_t inputformat = dns_masterformat_text; 108 dns_masterformat_t outputformat = dns_masterformat_text; 109 dns_masterrawheader_t header; 110 uint32_t rawversion = 1, serialnum = 0; 111 dns_ttl_t maxttl = 0; 112 bool snset = false; 113 bool logdump = false; 114 FILE *errout = stdout; 115 char *endp; 116 117 /* 118 * Uncomment the following line if memory debugging is needed: 119 * isc_mem_debugging |= ISC_MEM_DEBUGRECORD; 120 */ 121 122 outputstyle = &dns_master_style_full; 123 124 prog_name = strrchr(argv[0], '/'); 125 if (prog_name == NULL) { 126 prog_name = strrchr(argv[0], '\\'); 127 } 128 if (prog_name != NULL) { 129 prog_name++; 130 } else { 131 prog_name = argv[0]; 132 } 133 /* 134 * Libtool doesn't preserve the program name prior to final 135 * installation. Remove the libtool prefix ("lt-"). 136 */ 137 if (strncmp(prog_name, "lt-", 3) == 0) { 138 prog_name += 3; 139 } 140 141 #define PROGCMP(X) \ 142 (strcasecmp(prog_name, X) == 0 || strcasecmp(prog_name, X ".exe") == 0) 143 144 if (PROGCMP("named-checkzone")) { 145 progmode = progmode_check; 146 } else if (PROGCMP("named-compilezone")) { 147 progmode = progmode_compile; 148 } else { 149 INSIST(0); 150 ISC_UNREACHABLE(); 151 } 152 153 /* Compilation specific defaults */ 154 if (progmode == progmode_compile) { 155 zone_options |= (DNS_ZONEOPT_CHECKNS | DNS_ZONEOPT_FATALNS | 156 DNS_ZONEOPT_CHECKSPF | DNS_ZONEOPT_CHECKDUPRR | 157 DNS_ZONEOPT_CHECKNAMES | 158 DNS_ZONEOPT_CHECKNAMESFAIL | 159 DNS_ZONEOPT_CHECKWILDCARD); 160 } else { 161 zone_options |= (DNS_ZONEOPT_CHECKDUPRR | DNS_ZONEOPT_CHECKSPF); 162 } 163 164 #define ARGCMP(X) (strcmp(isc_commandline_argument, X) == 0) 165 166 isc_commandline_errprint = false; 167 168 while ((c = isc_commandline_parse(argc, argv, 169 "c:df:hi:jJ:k:L:l:m:n:qr:s:t:o:vw:DF:" 170 "M:S:T:W:")) != EOF) 171 { 172 switch (c) { 173 case 'c': 174 classname = isc_commandline_argument; 175 break; 176 177 case 'd': 178 debug++; 179 break; 180 181 case 'i': 182 if (ARGCMP("full")) { 183 zone_options |= DNS_ZONEOPT_CHECKINTEGRITY | 184 DNS_ZONEOPT_CHECKSIBLING; 185 docheckmx = true; 186 docheckns = true; 187 dochecksrv = true; 188 } else if (ARGCMP("full-sibling")) { 189 zone_options |= DNS_ZONEOPT_CHECKINTEGRITY; 190 zone_options &= ~DNS_ZONEOPT_CHECKSIBLING; 191 docheckmx = true; 192 docheckns = true; 193 dochecksrv = true; 194 } else if (ARGCMP("local")) { 195 zone_options |= DNS_ZONEOPT_CHECKINTEGRITY; 196 zone_options |= DNS_ZONEOPT_CHECKSIBLING; 197 docheckmx = false; 198 docheckns = false; 199 dochecksrv = false; 200 } else if (ARGCMP("local-sibling")) { 201 zone_options |= DNS_ZONEOPT_CHECKINTEGRITY; 202 zone_options &= ~DNS_ZONEOPT_CHECKSIBLING; 203 docheckmx = false; 204 docheckns = false; 205 dochecksrv = false; 206 } else if (ARGCMP("none")) { 207 zone_options &= ~DNS_ZONEOPT_CHECKINTEGRITY; 208 zone_options &= ~DNS_ZONEOPT_CHECKSIBLING; 209 docheckmx = false; 210 docheckns = false; 211 dochecksrv = false; 212 } else { 213 fprintf(stderr, "invalid argument to -i: %s\n", 214 isc_commandline_argument); 215 exit(1); 216 } 217 break; 218 219 case 'f': 220 inputformatstr = isc_commandline_argument; 221 break; 222 223 case 'F': 224 outputformatstr = isc_commandline_argument; 225 break; 226 227 case 'j': 228 nomerge = false; 229 break; 230 231 case 'J': 232 journal = isc_commandline_argument; 233 nomerge = false; 234 break; 235 236 case 'k': 237 if (ARGCMP("warn")) { 238 zone_options |= DNS_ZONEOPT_CHECKNAMES; 239 zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL; 240 } else if (ARGCMP("fail")) { 241 zone_options |= DNS_ZONEOPT_CHECKNAMES | 242 DNS_ZONEOPT_CHECKNAMESFAIL; 243 } else if (ARGCMP("ignore")) { 244 zone_options &= ~(DNS_ZONEOPT_CHECKNAMES | 245 DNS_ZONEOPT_CHECKNAMESFAIL); 246 } else { 247 fprintf(stderr, "invalid argument to -k: %s\n", 248 isc_commandline_argument); 249 exit(1); 250 } 251 break; 252 253 case 'L': 254 snset = true; 255 endp = NULL; 256 serialnum = strtol(isc_commandline_argument, &endp, 0); 257 if (*endp != '\0') { 258 fprintf(stderr, "source serial number " 259 "must be numeric"); 260 exit(1); 261 } 262 break; 263 264 case 'l': 265 zone_options |= DNS_ZONEOPT_CHECKTTL; 266 endp = NULL; 267 maxttl = strtol(isc_commandline_argument, &endp, 0); 268 if (*endp != '\0') { 269 fprintf(stderr, "maximum TTL " 270 "must be numeric"); 271 exit(1); 272 } 273 break; 274 275 case 'n': 276 if (ARGCMP("ignore")) { 277 zone_options &= ~(DNS_ZONEOPT_CHECKNS | 278 DNS_ZONEOPT_FATALNS); 279 } else if (ARGCMP("warn")) { 280 zone_options |= DNS_ZONEOPT_CHECKNS; 281 zone_options &= ~DNS_ZONEOPT_FATALNS; 282 } else if (ARGCMP("fail")) { 283 zone_options |= DNS_ZONEOPT_CHECKNS | 284 DNS_ZONEOPT_FATALNS; 285 } else { 286 fprintf(stderr, "invalid argument to -n: %s\n", 287 isc_commandline_argument); 288 exit(1); 289 } 290 break; 291 292 case 'm': 293 if (ARGCMP("warn")) { 294 zone_options |= DNS_ZONEOPT_CHECKMX; 295 zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL; 296 } else if (ARGCMP("fail")) { 297 zone_options |= DNS_ZONEOPT_CHECKMX | 298 DNS_ZONEOPT_CHECKMXFAIL; 299 } else if (ARGCMP("ignore")) { 300 zone_options &= ~(DNS_ZONEOPT_CHECKMX | 301 DNS_ZONEOPT_CHECKMXFAIL); 302 } else { 303 fprintf(stderr, "invalid argument to -m: %s\n", 304 isc_commandline_argument); 305 exit(1); 306 } 307 break; 308 309 case 'o': 310 output_filename = isc_commandline_argument; 311 break; 312 313 case 'q': 314 quiet++; 315 break; 316 317 case 'r': 318 if (ARGCMP("warn")) { 319 zone_options |= DNS_ZONEOPT_CHECKDUPRR; 320 zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL; 321 } else if (ARGCMP("fail")) { 322 zone_options |= DNS_ZONEOPT_CHECKDUPRR | 323 DNS_ZONEOPT_CHECKDUPRRFAIL; 324 } else if (ARGCMP("ignore")) { 325 zone_options &= ~(DNS_ZONEOPT_CHECKDUPRR | 326 DNS_ZONEOPT_CHECKDUPRRFAIL); 327 } else { 328 fprintf(stderr, "invalid argument to -r: %s\n", 329 isc_commandline_argument); 330 exit(1); 331 } 332 break; 333 334 case 's': 335 if (ARGCMP("full")) { 336 outputstyle = &dns_master_style_full; 337 } else if (ARGCMP("relative")) { 338 outputstyle = &dns_master_style_default; 339 } else { 340 fprintf(stderr, 341 "unknown or unsupported style: %s\n", 342 isc_commandline_argument); 343 exit(1); 344 } 345 break; 346 347 case 't': 348 result = isc_dir_chroot(isc_commandline_argument); 349 if (result != ISC_R_SUCCESS) { 350 fprintf(stderr, "isc_dir_chroot: %s: %s\n", 351 isc_commandline_argument, 352 isc_result_totext(result)); 353 exit(1); 354 } 355 break; 356 357 case 'v': 358 printf(VERSION "\n"); 359 exit(0); 360 361 case 'w': 362 workdir = isc_commandline_argument; 363 break; 364 365 case 'D': 366 dumpzone++; 367 break; 368 369 case 'M': 370 if (ARGCMP("fail")) { 371 zone_options &= ~DNS_ZONEOPT_WARNMXCNAME; 372 zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME; 373 } else if (ARGCMP("warn")) { 374 zone_options |= DNS_ZONEOPT_WARNMXCNAME; 375 zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME; 376 } else if (ARGCMP("ignore")) { 377 zone_options |= DNS_ZONEOPT_WARNMXCNAME; 378 zone_options |= DNS_ZONEOPT_IGNOREMXCNAME; 379 } else { 380 fprintf(stderr, "invalid argument to -M: %s\n", 381 isc_commandline_argument); 382 exit(1); 383 } 384 break; 385 386 case 'S': 387 if (ARGCMP("fail")) { 388 zone_options &= ~DNS_ZONEOPT_WARNSRVCNAME; 389 zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME; 390 } else if (ARGCMP("warn")) { 391 zone_options |= DNS_ZONEOPT_WARNSRVCNAME; 392 zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME; 393 } else if (ARGCMP("ignore")) { 394 zone_options |= DNS_ZONEOPT_WARNSRVCNAME; 395 zone_options |= DNS_ZONEOPT_IGNORESRVCNAME; 396 } else { 397 fprintf(stderr, "invalid argument to -S: %s\n", 398 isc_commandline_argument); 399 exit(1); 400 } 401 break; 402 403 case 'T': 404 if (ARGCMP("warn")) { 405 zone_options |= DNS_ZONEOPT_CHECKSPF; 406 } else if (ARGCMP("ignore")) { 407 zone_options &= ~DNS_ZONEOPT_CHECKSPF; 408 } else { 409 fprintf(stderr, "invalid argument to -T: %s\n", 410 isc_commandline_argument); 411 exit(1); 412 } 413 break; 414 415 case 'W': 416 if (ARGCMP("warn")) { 417 zone_options |= DNS_ZONEOPT_CHECKWILDCARD; 418 } else if (ARGCMP("ignore")) { 419 zone_options &= ~DNS_ZONEOPT_CHECKWILDCARD; 420 } 421 break; 422 423 case '?': 424 if (isc_commandline_option != '?') { 425 fprintf(stderr, "%s: invalid argument -%c\n", 426 prog_name, isc_commandline_option); 427 } 428 /* FALLTHROUGH */ 429 case 'h': 430 usage(); 431 432 default: 433 fprintf(stderr, "%s: unhandled option -%c\n", prog_name, 434 isc_commandline_option); 435 exit(1); 436 } 437 } 438 439 if (workdir != NULL) { 440 result = isc_dir_chdir(workdir); 441 if (result != ISC_R_SUCCESS) { 442 fprintf(stderr, "isc_dir_chdir: %s: %s\n", workdir, 443 isc_result_totext(result)); 444 exit(1); 445 } 446 } 447 448 if (inputformatstr != NULL) { 449 if (strcasecmp(inputformatstr, "text") == 0) { 450 inputformat = dns_masterformat_text; 451 } else if (strcasecmp(inputformatstr, "raw") == 0) { 452 inputformat = dns_masterformat_raw; 453 } else if (strncasecmp(inputformatstr, "raw=", 4) == 0) { 454 inputformat = dns_masterformat_raw; 455 fprintf(stderr, "WARNING: input format raw, version " 456 "ignored\n"); 457 } else if (strcasecmp(inputformatstr, "map") == 0) { 458 inputformat = dns_masterformat_map; 459 } else { 460 fprintf(stderr, "unknown file format: %s\n", 461 inputformatstr); 462 exit(1); 463 } 464 } 465 466 if (outputformatstr != NULL) { 467 if (strcasecmp(outputformatstr, "text") == 0) { 468 outputformat = dns_masterformat_text; 469 } else if (strcasecmp(outputformatstr, "raw") == 0) { 470 outputformat = dns_masterformat_raw; 471 } else if (strncasecmp(outputformatstr, "raw=", 4) == 0) { 472 char *end; 473 474 outputformat = dns_masterformat_raw; 475 rawversion = strtol(outputformatstr + 4, &end, 10); 476 if (end == outputformatstr + 4 || *end != '\0' || 477 rawversion > 1U) { 478 fprintf(stderr, "unknown raw format version\n"); 479 exit(1); 480 } 481 } else if (strcasecmp(outputformatstr, "map") == 0) { 482 outputformat = dns_masterformat_map; 483 } else { 484 fprintf(stderr, "unknown file format: %s\n", 485 outputformatstr); 486 exit(1); 487 } 488 } 489 490 if (progmode == progmode_compile) { 491 dumpzone = 1; /* always dump */ 492 logdump = !quiet; 493 if (output_filename == NULL) { 494 fprintf(stderr, "output file required, but not " 495 "specified\n"); 496 usage(); 497 } 498 } 499 500 if (output_filename != NULL) { 501 dumpzone = 1; 502 } 503 504 /* 505 * If we are printing to stdout then send the informational 506 * output to stderr. 507 */ 508 if (dumpzone && 509 (output_filename == NULL || strcmp(output_filename, "-") == 0 || 510 strcmp(output_filename, "/dev/fd/1") == 0 || 511 strcmp(output_filename, "/dev/stdout") == 0)) 512 { 513 errout = stderr; 514 logdump = false; 515 } 516 517 if (isc_commandline_index + 2 != argc) { 518 usage(); 519 } 520 521 #ifdef _WIN32 522 InitSockets(); 523 #endif /* ifdef _WIN32 */ 524 525 isc_mem_create(&mctx); 526 if (!quiet) { 527 RUNTIME_CHECK(setup_logging(mctx, errout, &lctx) == 528 ISC_R_SUCCESS); 529 } 530 531 dns_result_register(); 532 533 origin = argv[isc_commandline_index++]; 534 filename = argv[isc_commandline_index++]; 535 result = load_zone(mctx, origin, filename, inputformat, classname, 536 maxttl, &zone); 537 538 if (snset) { 539 dns_master_initrawheader(&header); 540 header.flags = DNS_MASTERRAW_SOURCESERIALSET; 541 header.sourceserial = serialnum; 542 dns_zone_setrawdata(zone, &header); 543 } 544 545 if (result == ISC_R_SUCCESS && dumpzone) { 546 if (logdump) { 547 fprintf(errout, "dump zone to %s...", output_filename); 548 fflush(errout); 549 } 550 result = dump_zone(origin, zone, output_filename, outputformat, 551 outputstyle, rawversion); 552 if (logdump) { 553 fprintf(errout, "done\n"); 554 } 555 } 556 557 if (!quiet && result == ISC_R_SUCCESS) { 558 fprintf(errout, "OK\n"); 559 } 560 destroy(); 561 if (lctx != NULL) { 562 isc_log_destroy(&lctx); 563 } 564 isc_mem_destroy(&mctx); 565 #ifdef _WIN32 566 DestroySockets(); 567 #endif /* ifdef _WIN32 */ 568 return ((result == ISC_R_SUCCESS) ? 0 : 1); 569 } 570