1/* 2 * Copyright (C) 2004-2007, 2009-2011 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.54.62.2 2011/03/12 04:59:13 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_checknames(maps, &obj)) { 299 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { 300 zone_options |= DNS_ZONEOPT_CHECKNAMES; 301 zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL; 302 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { 303 zone_options |= DNS_ZONEOPT_CHECKNAMES; 304 zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL; 305 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { 306 zone_options &= ~DNS_ZONEOPT_CHECKNAMES; 307 zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL; 308 } else 309 INSIST(0); 310 } else { 311 zone_options |= DNS_ZONEOPT_CHECKNAMES; 312 zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL; 313 } 314 315 masterformat = dns_masterformat_text; 316 fmtobj = NULL; 317 result = config_get(maps, "masterfile-format", &fmtobj); 318 if (result == ISC_R_SUCCESS) { 319 const char *masterformatstr = cfg_obj_asstring(fmtobj); 320 if (strcasecmp(masterformatstr, "text") == 0) 321 masterformat = dns_masterformat_text; 322 else if (strcasecmp(masterformatstr, "raw") == 0) 323 masterformat = dns_masterformat_raw; 324 else 325 INSIST(0); 326 } 327 328 result = load_zone(mctx, zname, zfile, masterformat, zclass, NULL); 329 if (result != ISC_R_SUCCESS) 330 fprintf(stderr, "%s/%s/%s: %s\n", view, zname, zclass, 331 dns_result_totext(result)); 332 return(result); 333} 334 335/*% configure a view */ 336static isc_result_t 337configure_view(const char *vclass, const char *view, const cfg_obj_t *config, 338 const cfg_obj_t *vconfig, isc_mem_t *mctx) 339{ 340 const cfg_listelt_t *element; 341 const cfg_obj_t *voptions; 342 const cfg_obj_t *zonelist; 343 isc_result_t result = ISC_R_SUCCESS; 344 isc_result_t tresult; 345 346 voptions = NULL; 347 if (vconfig != NULL) 348 voptions = cfg_tuple_get(vconfig, "options"); 349 350 zonelist = NULL; 351 if (voptions != NULL) 352 (void)cfg_map_get(voptions, "zone", &zonelist); 353 else 354 (void)cfg_map_get(config, "zone", &zonelist); 355 356 for (element = cfg_list_first(zonelist); 357 element != NULL; 358 element = cfg_list_next(element)) 359 { 360 const cfg_obj_t *zconfig = cfg_listelt_value(element); 361 tresult = configure_zone(vclass, view, zconfig, vconfig, 362 config, mctx); 363 if (tresult != ISC_R_SUCCESS) 364 result = tresult; 365 } 366 return (result); 367} 368 369 370/*% load zones from the configuration */ 371static isc_result_t 372load_zones_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx) { 373 const cfg_listelt_t *element; 374 const cfg_obj_t *classobj; 375 const cfg_obj_t *views; 376 const cfg_obj_t *vconfig; 377 const char *vclass; 378 isc_result_t result = ISC_R_SUCCESS; 379 isc_result_t tresult; 380 381 views = NULL; 382 383 (void)cfg_map_get(config, "view", &views); 384 for (element = cfg_list_first(views); 385 element != NULL; 386 element = cfg_list_next(element)) 387 { 388 const char *vname; 389 390 vclass = "IN"; 391 vconfig = cfg_listelt_value(element); 392 if (vconfig != NULL) { 393 classobj = cfg_tuple_get(vconfig, "class"); 394 if (cfg_obj_isstring(classobj)) 395 vclass = cfg_obj_asstring(classobj); 396 } 397 vname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name")); 398 tresult = configure_view(vclass, vname, config, vconfig, mctx); 399 if (tresult != ISC_R_SUCCESS) 400 result = tresult; 401 } 402 403 if (views == NULL) { 404 tresult = configure_view("IN", "_default", config, NULL, mctx); 405 if (tresult != ISC_R_SUCCESS) 406 result = tresult; 407 } 408 return (result); 409} 410 411static void 412output(void *closure, const char *text, int textlen) { 413 UNUSED(closure); 414 if (fwrite(text, 1, textlen, stdout) != (size_t)textlen) { 415 perror("fwrite"); 416 exit(1); 417 } 418} 419 420/*% The main processing routine */ 421int 422main(int argc, char **argv) { 423 int c; 424 cfg_parser_t *parser = NULL; 425 cfg_obj_t *config = NULL; 426 const char *conffile = NULL; 427 isc_mem_t *mctx = NULL; 428 isc_result_t result; 429 int exit_status = 0; 430 isc_entropy_t *ectx = NULL; 431 isc_boolean_t load_zones = ISC_FALSE; 432 isc_boolean_t print = ISC_FALSE; 433 434 isc_commandline_errprint = ISC_FALSE; 435 436 while ((c = isc_commandline_parse(argc, argv, "dhjt:pvz")) != EOF) { 437 switch (c) { 438 case 'd': 439 debug++; 440 break; 441 442 case 'j': 443 nomerge = ISC_FALSE; 444 break; 445 446 case 't': 447 result = isc_dir_chroot(isc_commandline_argument); 448 if (result != ISC_R_SUCCESS) { 449 fprintf(stderr, "isc_dir_chroot: %s\n", 450 isc_result_totext(result)); 451 exit(1); 452 } 453 break; 454 455 case 'p': 456 print = ISC_TRUE; 457 break; 458 459 case 'v': 460 printf(VERSION "\n"); 461 exit(0); 462 463 case 'z': 464 load_zones = ISC_TRUE; 465 docheckmx = ISC_FALSE; 466 docheckns = ISC_FALSE; 467 dochecksrv = ISC_FALSE; 468 break; 469 470 case '?': 471 if (isc_commandline_option != '?') 472 fprintf(stderr, "%s: invalid argument -%c\n", 473 program, isc_commandline_option); 474 case 'h': 475 usage(); 476 477 default: 478 fprintf(stderr, "%s: unhandled option -%c\n", 479 program, isc_commandline_option); 480 exit(1); 481 } 482 } 483 484 if (isc_commandline_index + 1 < argc) 485 usage(); 486 if (argv[isc_commandline_index] != NULL) 487 conffile = argv[isc_commandline_index]; 488 if (conffile == NULL || conffile[0] == '\0') 489 conffile = NAMED_CONFFILE; 490 491#ifdef _WIN32 492 InitSockets(); 493#endif 494 495 RUNTIME_CHECK(isc_mem_create(0, 0, &mctx) == ISC_R_SUCCESS); 496 497 RUNTIME_CHECK(setup_logging(mctx, stdout, &logc) == ISC_R_SUCCESS); 498 499 RUNTIME_CHECK(isc_entropy_create(mctx, &ectx) == ISC_R_SUCCESS); 500 RUNTIME_CHECK(isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE) 501 == ISC_R_SUCCESS); 502 503 dns_result_register(); 504 505 RUNTIME_CHECK(cfg_parser_create(mctx, logc, &parser) == ISC_R_SUCCESS); 506 507 cfg_parser_setcallback(parser, directory_callback, NULL); 508 509 if (cfg_parse_file(parser, conffile, &cfg_type_namedconf, &config) != 510 ISC_R_SUCCESS) 511 exit(1); 512 513 result = bind9_check_namedconf(config, logc, mctx); 514 if (result != ISC_R_SUCCESS) 515 exit_status = 1; 516 517 if (result == ISC_R_SUCCESS && load_zones) { 518 result = load_zones_fromconfig(config, mctx); 519 if (result != ISC_R_SUCCESS) 520 exit_status = 1; 521 } 522 523 if (print && exit_status == 0) 524 cfg_print(config, output, NULL); 525 cfg_obj_destroy(parser, &config); 526 527 cfg_parser_destroy(&parser); 528 529 dns_name_destroy(); 530 531 isc_log_destroy(&logc); 532 533 isc_hash_destroy(); 534 isc_entropy_detach(&ectx); 535 536 isc_mem_destroy(&mctx); 537 538#ifdef _WIN32 539 DestroySockets(); 540#endif 541 542 return (exit_status); 543} 544