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