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