1/* 2 * Copyright (C) 2004-2007, 2009-2014 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 1999-2002 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-checkconf.c,v 1.56 2011/03/12 04:59:46 tbox Exp $ */ 19 20/*! \file */ 21 22#include <config.h> 23 24#include <errno.h> 25#include <stdlib.h> 26#include <stdio.h> 27 28#include <isc/commandline.h> 29#include <isc/dir.h> 30#include <isc/entropy.h> 31#include <isc/hash.h> 32#include <isc/log.h> 33#include <isc/mem.h> 34#include <isc/result.h> 35#include <isc/string.h> 36#include <isc/util.h> 37 38#include <isccfg/namedconf.h> 39 40#include <bind9/check.h> 41 42#include <dns/db.h> 43#include <dns/fixedname.h> 44#include <dns/log.h> 45#include <dns/name.h> 46#include <dns/rdataclass.h> 47#include <dns/result.h> 48#include <dns/rootns.h> 49#include <dns/zone.h> 50 51#include "check-tool.h" 52 53static const char *program = "named-checkconf"; 54 55isc_log_t *logc = NULL; 56 57#define CHECK(r)\ 58 do { \ 59 result = (r); \ 60 if (result != ISC_R_SUCCESS) \ 61 goto cleanup; \ 62 } while (0) 63 64/*% usage */ 65ISC_PLATFORM_NORETURN_PRE static void 66usage(void) ISC_PLATFORM_NORETURN_POST; 67 68static void 69usage(void) { 70 fprintf(stderr, "usage: %s [-h] [-j] [-p] [-v] [-z] [-t directory] " 71 "[named.conf]\n", program); 72 exit(1); 73} 74 75/*% directory callback */ 76static isc_result_t 77directory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) { 78 isc_result_t result; 79 const char *directory; 80 81 REQUIRE(strcasecmp("directory", clausename) == 0); 82 83 UNUSED(arg); 84 UNUSED(clausename); 85 86 /* 87 * Change directory. 88 */ 89 directory = cfg_obj_asstring(obj); 90 result = isc_dir_chdir(directory); 91 if (result != ISC_R_SUCCESS) { 92 cfg_obj_log(obj, logc, ISC_LOG_ERROR, 93 "change directory to '%s' failed: %s\n", 94 directory, isc_result_totext(result)); 95 return (result); 96 } 97 98 return (ISC_R_SUCCESS); 99} 100 101static isc_boolean_t 102get_maps(const cfg_obj_t **maps, const char *name, const cfg_obj_t **obj) { 103 int i; 104 for (i = 0;; i++) { 105 if (maps[i] == NULL) 106 return (ISC_FALSE); 107 if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS) 108 return (ISC_TRUE); 109 } 110} 111 112static isc_boolean_t 113get_checknames(const cfg_obj_t **maps, const cfg_obj_t **obj) { 114 const cfg_listelt_t *element; 115 const cfg_obj_t *checknames; 116 const cfg_obj_t *type; 117 const cfg_obj_t *value; 118 isc_result_t result; 119 int i; 120 121 for (i = 0;; i++) { 122 if (maps[i] == NULL) 123 return (ISC_FALSE); 124 checknames = NULL; 125 result = cfg_map_get(maps[i], "check-names", &checknames); 126 if (result != ISC_R_SUCCESS) 127 continue; 128 if (checknames != NULL && !cfg_obj_islist(checknames)) { 129 *obj = checknames; 130 return (ISC_TRUE); 131 } 132 for (element = cfg_list_first(checknames); 133 element != NULL; 134 element = cfg_list_next(element)) { 135 value = cfg_listelt_value(element); 136 type = cfg_tuple_get(value, "type"); 137 if (strcasecmp(cfg_obj_asstring(type), "master") != 0) 138 continue; 139 *obj = cfg_tuple_get(value, "mode"); 140 return (ISC_TRUE); 141 } 142 } 143} 144 145static isc_result_t 146config_get(const cfg_obj_t **maps, const char *name, const cfg_obj_t **obj) { 147 int i; 148 149 for (i = 0;; i++) { 150 if (maps[i] == NULL) 151 return (ISC_R_NOTFOUND); 152 if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS) 153 return (ISC_R_SUCCESS); 154 } 155} 156 157static isc_result_t 158configure_hint(const char *zfile, const char *zclass, isc_mem_t *mctx) { 159 isc_result_t result; 160 dns_db_t *db = NULL; 161 dns_rdataclass_t rdclass; 162 isc_textregion_t r; 163 164 if (zfile == NULL) 165 return (ISC_R_FAILURE); 166 167 DE_CONST(zclass, r.base); 168 r.length = strlen(zclass); 169 result = dns_rdataclass_fromtext(&rdclass, &r); 170 if (result != ISC_R_SUCCESS) 171 return (result); 172 173 result = dns_rootns_create(mctx, rdclass, zfile, &db); 174 if (result != ISC_R_SUCCESS) 175 return (result); 176 177 dns_db_detach(&db); 178 return (ISC_R_SUCCESS); 179} 180 181/*% configure the zone */ 182static isc_result_t 183configure_zone(const char *vclass, const char *view, 184 const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, 185 const cfg_obj_t *config, isc_mem_t *mctx) 186{ 187 int i = 0; 188 isc_result_t result; 189 const char *zclass; 190 const char *zname; 191 const char *zfile = NULL; 192 const cfg_obj_t *maps[4]; 193 const cfg_obj_t *zoptions = NULL; 194 const cfg_obj_t *classobj = NULL; 195 const cfg_obj_t *typeobj = NULL; 196 const cfg_obj_t *fileobj = NULL; 197 const cfg_obj_t *dbobj = NULL; 198 const cfg_obj_t *obj = NULL; 199 const cfg_obj_t *fmtobj = NULL; 200 dns_masterformat_t masterformat; 201 202 zone_options = DNS_ZONEOPT_CHECKNS | DNS_ZONEOPT_MANYERRORS; 203 204 zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); 205 classobj = cfg_tuple_get(zconfig, "class"); 206 if (!cfg_obj_isstring(classobj)) 207 zclass = vclass; 208 else 209 zclass = cfg_obj_asstring(classobj); 210 211 zoptions = cfg_tuple_get(zconfig, "options"); 212 maps[i++] = zoptions; 213 if (vconfig != NULL) 214 maps[i++] = cfg_tuple_get(vconfig, "options"); 215 if (config != NULL) { 216 cfg_map_get(config, "options", &obj); 217 if (obj != NULL) 218 maps[i++] = obj; 219 } 220 maps[i] = NULL; 221 222 cfg_map_get(zoptions, "type", &typeobj); 223 if (typeobj == NULL) 224 return (ISC_R_FAILURE); 225 226 cfg_map_get(zoptions, "file", &fileobj); 227 if (fileobj != NULL) 228 zfile = cfg_obj_asstring(fileobj); 229 230 /* 231 * Check hints files for hint zones. 232 * Skip loading checks for any type other than 233 * master and redirect 234 */ 235 if (strcasecmp(cfg_obj_asstring(typeobj), "hint") == 0) 236 return (configure_hint(zfile, zclass, mctx)); 237 else if ((strcasecmp(cfg_obj_asstring(typeobj), "master") != 0) && 238 (strcasecmp(cfg_obj_asstring(typeobj), "redirect") != 0)) 239 return (ISC_R_SUCCESS); 240 241 if (zfile == NULL) 242 return (ISC_R_FAILURE); 243 244 cfg_map_get(zoptions, "database", &dbobj); 245 if (dbobj != NULL) 246 return (ISC_R_SUCCESS); 247 248 obj = NULL; 249 if (get_maps(maps, "check-dup-records", &obj)) { 250 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { 251 zone_options |= DNS_ZONEOPT_CHECKDUPRR; 252 zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL; 253 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { 254 zone_options |= DNS_ZONEOPT_CHECKDUPRR; 255 zone_options |= DNS_ZONEOPT_CHECKDUPRRFAIL; 256 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { 257 zone_options &= ~DNS_ZONEOPT_CHECKDUPRR; 258 zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL; 259 } else 260 INSIST(0); 261 } else { 262 zone_options |= DNS_ZONEOPT_CHECKDUPRR; 263 zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL; 264 } 265 266 obj = NULL; 267 if (get_maps(maps, "check-mx", &obj)) { 268 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { 269 zone_options |= DNS_ZONEOPT_CHECKMX; 270 zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL; 271 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { 272 zone_options |= DNS_ZONEOPT_CHECKMX; 273 zone_options |= DNS_ZONEOPT_CHECKMXFAIL; 274 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { 275 zone_options &= ~DNS_ZONEOPT_CHECKMX; 276 zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL; 277 } else 278 INSIST(0); 279 } else { 280 zone_options |= DNS_ZONEOPT_CHECKMX; 281 zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL; 282 } 283 284 obj = NULL; 285 if (get_maps(maps, "check-integrity", &obj)) { 286 if (cfg_obj_asboolean(obj)) 287 zone_options |= DNS_ZONEOPT_CHECKINTEGRITY; 288 else 289 zone_options &= ~DNS_ZONEOPT_CHECKINTEGRITY; 290 } else 291 zone_options |= DNS_ZONEOPT_CHECKINTEGRITY; 292 293 obj = NULL; 294 if (get_maps(maps, "check-mx-cname", &obj)) { 295 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { 296 zone_options |= DNS_ZONEOPT_WARNMXCNAME; 297 zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME; 298 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { 299 zone_options &= ~DNS_ZONEOPT_WARNMXCNAME; 300 zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME; 301 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { 302 zone_options |= DNS_ZONEOPT_WARNMXCNAME; 303 zone_options |= DNS_ZONEOPT_IGNOREMXCNAME; 304 } else 305 INSIST(0); 306 } else { 307 zone_options |= DNS_ZONEOPT_WARNMXCNAME; 308 zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME; 309 } 310 311 obj = NULL; 312 if (get_maps(maps, "check-srv-cname", &obj)) { 313 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { 314 zone_options |= DNS_ZONEOPT_WARNSRVCNAME; 315 zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME; 316 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { 317 zone_options &= ~DNS_ZONEOPT_WARNSRVCNAME; 318 zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME; 319 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { 320 zone_options |= DNS_ZONEOPT_WARNSRVCNAME; 321 zone_options |= DNS_ZONEOPT_IGNORESRVCNAME; 322 } else 323 INSIST(0); 324 } else { 325 zone_options |= DNS_ZONEOPT_WARNSRVCNAME; 326 zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME; 327 } 328 329 obj = NULL; 330 if (get_maps(maps, "check-sibling", &obj)) { 331 if (cfg_obj_asboolean(obj)) 332 zone_options |= DNS_ZONEOPT_CHECKSIBLING; 333 else 334 zone_options &= ~DNS_ZONEOPT_CHECKSIBLING; 335 } 336 337 obj = NULL; 338 if (get_maps(maps, "check-spf", &obj)) { 339 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { 340 zone_options |= DNS_ZONEOPT_CHECKSPF; 341 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { 342 zone_options &= ~DNS_ZONEOPT_CHECKSPF; 343 } else 344 INSIST(0); 345 } else { 346 zone_options |= DNS_ZONEOPT_CHECKSPF; 347 } 348 349 obj = NULL; 350 if (get_checknames(maps, &obj)) { 351 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { 352 zone_options |= DNS_ZONEOPT_CHECKNAMES; 353 zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL; 354 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { 355 zone_options |= DNS_ZONEOPT_CHECKNAMES; 356 zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL; 357 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { 358 zone_options &= ~DNS_ZONEOPT_CHECKNAMES; 359 zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL; 360 } else 361 INSIST(0); 362 } else { 363 zone_options |= DNS_ZONEOPT_CHECKNAMES; 364 zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL; 365 } 366 367 masterformat = dns_masterformat_text; 368 fmtobj = NULL; 369 result = config_get(maps, "masterfile-format", &fmtobj); 370 if (result == ISC_R_SUCCESS) { 371 const char *masterformatstr = cfg_obj_asstring(fmtobj); 372 if (strcasecmp(masterformatstr, "text") == 0) 373 masterformat = dns_masterformat_text; 374 else if (strcasecmp(masterformatstr, "raw") == 0) 375 masterformat = dns_masterformat_raw; 376 else 377 INSIST(0); 378 } 379 380 result = load_zone(mctx, zname, zfile, masterformat, zclass, NULL); 381 if (result != ISC_R_SUCCESS) 382 fprintf(stderr, "%s/%s/%s: %s\n", view, zname, zclass, 383 dns_result_totext(result)); 384 return (result); 385} 386 387/*% configure a view */ 388static isc_result_t 389configure_view(const char *vclass, const char *view, const cfg_obj_t *config, 390 const cfg_obj_t *vconfig, isc_mem_t *mctx) 391{ 392 const cfg_listelt_t *element; 393 const cfg_obj_t *voptions; 394 const cfg_obj_t *zonelist; 395 isc_result_t result = ISC_R_SUCCESS; 396 isc_result_t tresult; 397 398 voptions = NULL; 399 if (vconfig != NULL) 400 voptions = cfg_tuple_get(vconfig, "options"); 401 402 zonelist = NULL; 403 if (voptions != NULL) 404 (void)cfg_map_get(voptions, "zone", &zonelist); 405 else 406 (void)cfg_map_get(config, "zone", &zonelist); 407 408 for (element = cfg_list_first(zonelist); 409 element != NULL; 410 element = cfg_list_next(element)) 411 { 412 const cfg_obj_t *zconfig = cfg_listelt_value(element); 413 tresult = configure_zone(vclass, view, zconfig, vconfig, 414 config, mctx); 415 if (tresult != ISC_R_SUCCESS) 416 result = tresult; 417 } 418 return (result); 419} 420 421 422/*% load zones from the configuration */ 423static isc_result_t 424load_zones_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx) { 425 const cfg_listelt_t *element; 426 const cfg_obj_t *classobj; 427 const cfg_obj_t *views; 428 const cfg_obj_t *vconfig; 429 const char *vclass; 430 isc_result_t result = ISC_R_SUCCESS; 431 isc_result_t tresult; 432 433 views = NULL; 434 435 (void)cfg_map_get(config, "view", &views); 436 for (element = cfg_list_first(views); 437 element != NULL; 438 element = cfg_list_next(element)) 439 { 440 const char *vname; 441 442 vclass = "IN"; 443 vconfig = cfg_listelt_value(element); 444 if (vconfig != NULL) { 445 classobj = cfg_tuple_get(vconfig, "class"); 446 if (cfg_obj_isstring(classobj)) 447 vclass = cfg_obj_asstring(classobj); 448 } 449 vname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name")); 450 tresult = configure_view(vclass, vname, config, vconfig, mctx); 451 if (tresult != ISC_R_SUCCESS) 452 result = tresult; 453 } 454 455 if (views == NULL) { 456 tresult = configure_view("IN", "_default", config, NULL, mctx); 457 if (tresult != ISC_R_SUCCESS) 458 result = tresult; 459 } 460 return (result); 461} 462 463static void 464output(void *closure, const char *text, int textlen) { 465 UNUSED(closure); 466 if (fwrite(text, 1, textlen, stdout) != (size_t)textlen) { 467 perror("fwrite"); 468 exit(1); 469 } 470} 471 472/*% The main processing routine */ 473int 474main(int argc, char **argv) { 475 int c; 476 cfg_parser_t *parser = NULL; 477 cfg_obj_t *config = NULL; 478 const char *conffile = NULL; 479 isc_mem_t *mctx = NULL; 480 isc_result_t result; 481 int exit_status = 0; 482 isc_entropy_t *ectx = NULL; 483 isc_boolean_t load_zones = ISC_FALSE; 484 isc_boolean_t print = ISC_FALSE; 485 unsigned int flags = 0; 486 487 isc_commandline_errprint = ISC_FALSE; 488 489 while ((c = isc_commandline_parse(argc, argv, "dhjt:pvxz")) != EOF) { 490 switch (c) { 491 case 'd': 492 debug++; 493 break; 494 495 case 'j': 496 nomerge = ISC_FALSE; 497 break; 498 499 case 't': 500 result = isc_dir_chroot(isc_commandline_argument); 501 if (result != ISC_R_SUCCESS) { 502 fprintf(stderr, "isc_dir_chroot: %s\n", 503 isc_result_totext(result)); 504 exit(1); 505 } 506 break; 507 508 case 'p': 509 print = ISC_TRUE; 510 break; 511 512 case 'v': 513 printf(VERSION "\n"); 514 exit(0); 515 516 case 'x': 517 flags |= CFG_PRINTER_XKEY; 518 break; 519 520 case 'z': 521 load_zones = ISC_TRUE; 522 docheckmx = ISC_FALSE; 523 docheckns = ISC_FALSE; 524 dochecksrv = ISC_FALSE; 525 break; 526 527 case '?': 528 if (isc_commandline_option != '?') 529 fprintf(stderr, "%s: invalid argument -%c\n", 530 program, isc_commandline_option); 531 /* FALLTHROUGH */ 532 case 'h': 533 usage(); 534 535 default: 536 fprintf(stderr, "%s: unhandled option -%c\n", 537 program, isc_commandline_option); 538 exit(1); 539 } 540 } 541 542 if (((flags & CFG_PRINTER_XKEY) != 0) && !print) { 543 fprintf(stderr, "%s: -x cannot be used without -p\n", program); 544 exit(1); 545 } 546 547 if (isc_commandline_index + 1 < argc) 548 usage(); 549 if (argv[isc_commandline_index] != NULL) 550 conffile = argv[isc_commandline_index]; 551 if (conffile == NULL || conffile[0] == '\0') 552 conffile = NAMED_CONFFILE; 553 554#ifdef _WIN32 555 InitSockets(); 556#endif 557 558 RUNTIME_CHECK(isc_mem_create(0, 0, &mctx) == ISC_R_SUCCESS); 559 560 RUNTIME_CHECK(setup_logging(mctx, stdout, &logc) == ISC_R_SUCCESS); 561 562 RUNTIME_CHECK(isc_entropy_create(mctx, &ectx) == ISC_R_SUCCESS); 563 RUNTIME_CHECK(isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE) 564 == ISC_R_SUCCESS); 565 566 dns_result_register(); 567 568 RUNTIME_CHECK(cfg_parser_create(mctx, logc, &parser) == ISC_R_SUCCESS); 569 570 cfg_parser_setcallback(parser, directory_callback, NULL); 571 572 if (cfg_parse_file(parser, conffile, &cfg_type_namedconf, &config) != 573 ISC_R_SUCCESS) 574 exit(1); 575 576 result = bind9_check_namedconf(config, logc, mctx); 577 if (result != ISC_R_SUCCESS) 578 exit_status = 1; 579 580 if (result == ISC_R_SUCCESS && load_zones) { 581 result = load_zones_fromconfig(config, mctx); 582 if (result != ISC_R_SUCCESS) 583 exit_status = 1; 584 } 585 586 if (print && exit_status == 0) 587 cfg_printx(config, flags, output, NULL); 588 cfg_obj_destroy(parser, &config); 589 590 cfg_parser_destroy(&parser); 591 592 dns_name_destroy(); 593 594 isc_log_destroy(&logc); 595 596 isc_hash_destroy(); 597 isc_entropy_detach(&ectx); 598 599 isc_mem_destroy(&mctx); 600 601#ifdef _WIN32 602 DestroySockets(); 603#endif 604 605 return (exit_status); 606} 607