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