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