named-checkconf.c revision 170222
1/* 2 * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 1999-2002 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and 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.14 2006/02/28 03:10:47 marka 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 } 228 229 obj = NULL; 230 if (get_maps(maps, "check-mx-cname", &obj)) { 231 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { 232 zone_options |= DNS_ZONEOPT_WARNMXCNAME; 233 zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME; 234 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { 235 zone_options &= ~DNS_ZONEOPT_WARNMXCNAME; 236 zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME; 237 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { 238 zone_options |= DNS_ZONEOPT_WARNMXCNAME; 239 zone_options |= DNS_ZONEOPT_IGNOREMXCNAME; 240 } else 241 INSIST(0); 242 } else { 243 zone_options |= DNS_ZONEOPT_WARNMXCNAME; 244 zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME; 245 } 246 247 obj = NULL; 248 if (get_maps(maps, "check-srv-cname", &obj)) { 249 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { 250 zone_options |= DNS_ZONEOPT_WARNSRVCNAME; 251 zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME; 252 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { 253 zone_options &= ~DNS_ZONEOPT_WARNSRVCNAME; 254 zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME; 255 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { 256 zone_options |= DNS_ZONEOPT_WARNSRVCNAME; 257 zone_options |= DNS_ZONEOPT_IGNORESRVCNAME; 258 } else 259 INSIST(0); 260 } else { 261 zone_options |= DNS_ZONEOPT_WARNSRVCNAME; 262 zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME; 263 } 264 265 obj = NULL; 266 if (get_maps(maps, "check-sibling", &obj)) { 267 if (cfg_obj_asboolean(obj)) 268 zone_options |= DNS_ZONEOPT_CHECKSIBLING; 269 else 270 zone_options &= ~DNS_ZONEOPT_CHECKSIBLING; 271 } 272 273 obj = NULL; 274 if (get_checknames(maps, &obj)) { 275 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { 276 zone_options |= DNS_ZONEOPT_CHECKNAMES; 277 zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL; 278 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { 279 zone_options |= DNS_ZONEOPT_CHECKNAMES; 280 zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL; 281 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { 282 zone_options &= ~DNS_ZONEOPT_CHECKNAMES; 283 zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL; 284 } else 285 INSIST(0); 286 } else { 287 zone_options |= DNS_ZONEOPT_CHECKNAMES; 288 zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL; 289 } 290 291 masterformat = dns_masterformat_text; 292 fmtobj = NULL; 293 result = config_get(maps, "masterfile-format", &fmtobj); 294 if (result == ISC_R_SUCCESS) { 295 const char *masterformatstr = cfg_obj_asstring(fmtobj); 296 if (strcasecmp(masterformatstr, "text") == 0) 297 masterformat = dns_masterformat_text; 298 else if (strcasecmp(masterformatstr, "raw") == 0) 299 masterformat = dns_masterformat_raw; 300 else 301 INSIST(0); 302 } 303 304 result = load_zone(mctx, zname, zfile, masterformat, zclass, NULL); 305 if (result != ISC_R_SUCCESS) 306 fprintf(stderr, "%s/%s/%s: %s\n", view, zname, zclass, 307 dns_result_totext(result)); 308 return(result); 309} 310 311/*% configure a view */ 312static isc_result_t 313configure_view(const char *vclass, const char *view, const cfg_obj_t *config, 314 const cfg_obj_t *vconfig, isc_mem_t *mctx) 315{ 316 const cfg_listelt_t *element; 317 const cfg_obj_t *voptions; 318 const cfg_obj_t *zonelist; 319 isc_result_t result = ISC_R_SUCCESS; 320 isc_result_t tresult; 321 322 voptions = NULL; 323 if (vconfig != NULL) 324 voptions = cfg_tuple_get(vconfig, "options"); 325 326 zonelist = NULL; 327 if (voptions != NULL) 328 (void)cfg_map_get(voptions, "zone", &zonelist); 329 else 330 (void)cfg_map_get(config, "zone", &zonelist); 331 332 for (element = cfg_list_first(zonelist); 333 element != NULL; 334 element = cfg_list_next(element)) 335 { 336 const cfg_obj_t *zconfig = cfg_listelt_value(element); 337 tresult = configure_zone(vclass, view, zconfig, vconfig, 338 config, mctx); 339 if (tresult != ISC_R_SUCCESS) 340 result = tresult; 341 } 342 return (result); 343} 344 345 346/*% load zones from the configuration */ 347static isc_result_t 348load_zones_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx) { 349 const cfg_listelt_t *element; 350 const cfg_obj_t *classobj; 351 const cfg_obj_t *views; 352 const cfg_obj_t *vconfig; 353 const char *vclass; 354 isc_result_t result = ISC_R_SUCCESS; 355 isc_result_t tresult; 356 357 views = NULL; 358 359 (void)cfg_map_get(config, "view", &views); 360 for (element = cfg_list_first(views); 361 element != NULL; 362 element = cfg_list_next(element)) 363 { 364 const char *vname; 365 366 vclass = "IN"; 367 vconfig = cfg_listelt_value(element); 368 if (vconfig != NULL) { 369 classobj = cfg_tuple_get(vconfig, "class"); 370 if (cfg_obj_isstring(classobj)) 371 vclass = cfg_obj_asstring(classobj); 372 } 373 vname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name")); 374 tresult = configure_view(vclass, vname, config, vconfig, mctx); 375 if (tresult != ISC_R_SUCCESS) 376 result = tresult; 377 } 378 379 if (views == NULL) { 380 tresult = configure_view("IN", "_default", config, NULL, mctx); 381 if (tresult != ISC_R_SUCCESS) 382 result = tresult; 383 } 384 return (result); 385} 386 387/*% The main processing routine */ 388int 389main(int argc, char **argv) { 390 int c; 391 cfg_parser_t *parser = NULL; 392 cfg_obj_t *config = NULL; 393 const char *conffile = NULL; 394 isc_mem_t *mctx = NULL; 395 isc_result_t result; 396 int exit_status = 0; 397 isc_entropy_t *ectx = NULL; 398 isc_boolean_t load_zones = ISC_FALSE; 399 400 while ((c = isc_commandline_parse(argc, argv, "djt:vz")) != EOF) { 401 switch (c) { 402 case 'd': 403 debug++; 404 break; 405 406 case 'j': 407 nomerge = ISC_FALSE; 408 break; 409 410 case 't': 411 result = isc_dir_chroot(isc_commandline_argument); 412 if (result != ISC_R_SUCCESS) { 413 fprintf(stderr, "isc_dir_chroot: %s\n", 414 isc_result_totext(result)); 415 exit(1); 416 } 417 result = isc_dir_chdir("/"); 418 if (result != ISC_R_SUCCESS) { 419 fprintf(stderr, "isc_dir_chdir: %s\n", 420 isc_result_totext(result)); 421 exit(1); 422 } 423 break; 424 425 case 'v': 426 printf(VERSION "\n"); 427 exit(0); 428 429 case 'z': 430 load_zones = ISC_TRUE; 431 docheckmx = ISC_FALSE; 432 docheckns = ISC_FALSE; 433 dochecksrv = ISC_FALSE; 434 break; 435 436 default: 437 usage(); 438 } 439 } 440 441 if (argv[isc_commandline_index] != NULL) 442 conffile = argv[isc_commandline_index]; 443 if (conffile == NULL || conffile[0] == '\0') 444 conffile = NAMED_CONFFILE; 445 446 RUNTIME_CHECK(isc_mem_create(0, 0, &mctx) == ISC_R_SUCCESS); 447 448 RUNTIME_CHECK(setup_logging(mctx, &logc) == ISC_R_SUCCESS); 449 450 RUNTIME_CHECK(isc_entropy_create(mctx, &ectx) == ISC_R_SUCCESS); 451 RUNTIME_CHECK(isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE) 452 == ISC_R_SUCCESS); 453 454 dns_result_register(); 455 456 RUNTIME_CHECK(cfg_parser_create(mctx, logc, &parser) == ISC_R_SUCCESS); 457 458 cfg_parser_setcallback(parser, directory_callback, NULL); 459 460 if (cfg_parse_file(parser, conffile, &cfg_type_namedconf, &config) != 461 ISC_R_SUCCESS) 462 exit(1); 463 464 result = bind9_check_namedconf(config, logc, mctx); 465 if (result != ISC_R_SUCCESS) 466 exit_status = 1; 467 468 if (result == ISC_R_SUCCESS && load_zones) { 469 result = load_zones_fromconfig(config, mctx); 470 if (result != ISC_R_SUCCESS) 471 exit_status = 1; 472 } 473 474 cfg_obj_destroy(parser, &config); 475 476 cfg_parser_destroy(&parser); 477 478 dns_name_destroy(); 479 480 isc_log_destroy(&logc); 481 482 isc_hash_destroy(); 483 isc_entropy_detach(&ectx); 484 485 isc_mem_destroy(&mctx); 486 487 return (exit_status); 488} 489