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