Home | History | Annotate | Line # | Download | only in check
named-checkconf.c revision 1.1.1.7
      1 /*	$NetBSD: named-checkconf.c,v 1.1.1.7 2023/01/25 20:36:34 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 /*! \file */
     17 
     18 #include <errno.h>
     19 #include <stdbool.h>
     20 #include <stdio.h>
     21 #include <stdlib.h>
     22 
     23 #include <isc/commandline.h>
     24 #include <isc/dir.h>
     25 #include <isc/hash.h>
     26 #include <isc/log.h>
     27 #include <isc/mem.h>
     28 #include <isc/print.h>
     29 #include <isc/result.h>
     30 #include <isc/string.h>
     31 #include <isc/util.h>
     32 
     33 #include <dns/db.h>
     34 #include <dns/fixedname.h>
     35 #include <dns/log.h>
     36 #include <dns/name.h>
     37 #include <dns/rdataclass.h>
     38 #include <dns/result.h>
     39 #include <dns/rootns.h>
     40 #include <dns/zone.h>
     41 
     42 #include <isccfg/grammar.h>
     43 #include <isccfg/namedconf.h>
     44 
     45 #include <bind9/check.h>
     46 
     47 #include "check-tool.h"
     48 
     49 static const char *program = "named-checkconf";
     50 
     51 static bool loadplugins = true;
     52 
     53 isc_log_t *logc = NULL;
     54 
     55 #define CHECK(r)                             \
     56 	do {                                 \
     57 		result = (r);                \
     58 		if (result != ISC_R_SUCCESS) \
     59 			goto cleanup;        \
     60 	} while (0)
     61 
     62 /*% usage */
     63 ISC_PLATFORM_NORETURN_PRE static void
     64 usage(void) ISC_PLATFORM_NORETURN_POST;
     65 
     66 static void
     67 usage(void) {
     68 	fprintf(stderr,
     69 		"usage: %s [-chijlvz] [-p [-x]] [-t directory] "
     70 		"[named.conf]\n",
     71 		program);
     72 	exit(1);
     73 }
     74 
     75 /*% directory callback */
     76 static isc_result_t
     77 directory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) {
     78 	isc_result_t result;
     79 	const char *directory;
     80 
     81 	REQUIRE(strcasecmp("directory", clausename) == 0);
     82 
     83 	UNUSED(arg);
     84 	UNUSED(clausename);
     85 
     86 	/*
     87 	 * Change directory.
     88 	 */
     89 	directory = cfg_obj_asstring(obj);
     90 	result = isc_dir_chdir(directory);
     91 	if (result != ISC_R_SUCCESS) {
     92 		cfg_obj_log(obj, logc, ISC_LOG_ERROR,
     93 			    "change directory to '%s' failed: %s\n", directory,
     94 			    isc_result_totext(result));
     95 		return (result);
     96 	}
     97 
     98 	return (ISC_R_SUCCESS);
     99 }
    100 
    101 static bool
    102 get_maps(const cfg_obj_t **maps, const char *name, const cfg_obj_t **obj) {
    103 	int i;
    104 	for (i = 0;; i++) {
    105 		if (maps[i] == NULL) {
    106 			return (false);
    107 		}
    108 		if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS) {
    109 			return (true);
    110 		}
    111 	}
    112 }
    113 
    114 static bool
    115 get_checknames(const cfg_obj_t **maps, const cfg_obj_t **obj) {
    116 	const cfg_listelt_t *element;
    117 	const cfg_obj_t *checknames;
    118 	const cfg_obj_t *type;
    119 	const cfg_obj_t *value;
    120 	isc_result_t result;
    121 	int i;
    122 
    123 	for (i = 0;; i++) {
    124 		if (maps[i] == NULL) {
    125 			return (false);
    126 		}
    127 		checknames = NULL;
    128 		result = cfg_map_get(maps[i], "check-names", &checknames);
    129 		if (result != ISC_R_SUCCESS) {
    130 			continue;
    131 		}
    132 		if (checknames != NULL && !cfg_obj_islist(checknames)) {
    133 			*obj = checknames;
    134 			return (true);
    135 		}
    136 		for (element = cfg_list_first(checknames); element != NULL;
    137 		     element = cfg_list_next(element))
    138 		{
    139 			value = cfg_listelt_value(element);
    140 			type = cfg_tuple_get(value, "type");
    141 			if ((strcasecmp(cfg_obj_asstring(type), "primary") !=
    142 			     0) &&
    143 			    (strcasecmp(cfg_obj_asstring(type), "master") != 0))
    144 			{
    145 				continue;
    146 			}
    147 			*obj = cfg_tuple_get(value, "mode");
    148 			return (true);
    149 		}
    150 	}
    151 }
    152 
    153 static isc_result_t
    154 configure_hint(const char *zfile, const char *zclass, isc_mem_t *mctx) {
    155 	isc_result_t result;
    156 	dns_db_t *db = NULL;
    157 	dns_rdataclass_t rdclass;
    158 	isc_textregion_t r;
    159 
    160 	if (zfile == NULL) {
    161 		return (ISC_R_FAILURE);
    162 	}
    163 
    164 	DE_CONST(zclass, r.base);
    165 	r.length = strlen(zclass);
    166 	result = dns_rdataclass_fromtext(&rdclass, &r);
    167 	if (result != ISC_R_SUCCESS) {
    168 		return (result);
    169 	}
    170 
    171 	result = dns_rootns_create(mctx, rdclass, zfile, &db);
    172 	if (result != ISC_R_SUCCESS) {
    173 		return (result);
    174 	}
    175 
    176 	dns_db_detach(&db);
    177 	return (ISC_R_SUCCESS);
    178 }
    179 
    180 /*% configure the zone */
    181 static isc_result_t
    182 configure_zone(const char *vclass, const char *view, const cfg_obj_t *zconfig,
    183 	       const cfg_obj_t *vconfig, const cfg_obj_t *config,
    184 	       isc_mem_t *mctx, bool list) {
    185 	int i = 0;
    186 	isc_result_t result;
    187 	const char *zclass;
    188 	const char *zname;
    189 	const char *zfile = NULL;
    190 	const cfg_obj_t *maps[4];
    191 	const cfg_obj_t *primariesobj = NULL;
    192 	const cfg_obj_t *inviewobj = NULL;
    193 	const cfg_obj_t *zoptions = NULL;
    194 	const cfg_obj_t *classobj = NULL;
    195 	const cfg_obj_t *typeobj = NULL;
    196 	const cfg_obj_t *fileobj = NULL;
    197 	const cfg_obj_t *dlzobj = NULL;
    198 	const cfg_obj_t *dbobj = NULL;
    199 	const cfg_obj_t *obj = NULL;
    200 	const cfg_obj_t *fmtobj = NULL;
    201 	dns_masterformat_t masterformat;
    202 	dns_ttl_t maxttl = 0;
    203 
    204 	zone_options = DNS_ZONEOPT_CHECKNS | DNS_ZONEOPT_MANYERRORS;
    205 
    206 	zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
    207 	classobj = cfg_tuple_get(zconfig, "class");
    208 	if (!cfg_obj_isstring(classobj)) {
    209 		zclass = vclass;
    210 	} else {
    211 		zclass = cfg_obj_asstring(classobj);
    212 	}
    213 
    214 	zoptions = cfg_tuple_get(zconfig, "options");
    215 	maps[i++] = zoptions;
    216 	if (vconfig != NULL) {
    217 		maps[i++] = cfg_tuple_get(vconfig, "options");
    218 	}
    219 	if (config != NULL) {
    220 		cfg_map_get(config, "options", &obj);
    221 		if (obj != NULL) {
    222 			maps[i++] = obj;
    223 		}
    224 	}
    225 	maps[i] = NULL;
    226 
    227 	cfg_map_get(zoptions, "in-view", &inviewobj);
    228 	if (inviewobj != NULL && list) {
    229 		const char *inview = cfg_obj_asstring(inviewobj);
    230 		printf("%s %s %s in-view %s\n", zname, zclass, view, inview);
    231 	}
    232 	if (inviewobj != NULL) {
    233 		return (ISC_R_SUCCESS);
    234 	}
    235 
    236 	cfg_map_get(zoptions, "type", &typeobj);
    237 	if (typeobj == NULL) {
    238 		return (ISC_R_FAILURE);
    239 	}
    240 
    241 	if (list) {
    242 		const char *ztype = cfg_obj_asstring(typeobj);
    243 		printf("%s %s %s %s\n", zname, zclass, view, ztype);
    244 		return (ISC_R_SUCCESS);
    245 	}
    246 
    247 	/*
    248 	 * Skip checks when using an alternate data source.
    249 	 */
    250 	cfg_map_get(zoptions, "database", &dbobj);
    251 	if (dbobj != NULL && strcmp("rbt", cfg_obj_asstring(dbobj)) != 0 &&
    252 	    strcmp("rbt64", cfg_obj_asstring(dbobj)) != 0)
    253 	{
    254 		return (ISC_R_SUCCESS);
    255 	}
    256 
    257 	cfg_map_get(zoptions, "dlz", &dlzobj);
    258 	if (dlzobj != NULL) {
    259 		return (ISC_R_SUCCESS);
    260 	}
    261 
    262 	cfg_map_get(zoptions, "file", &fileobj);
    263 	if (fileobj != NULL) {
    264 		zfile = cfg_obj_asstring(fileobj);
    265 	}
    266 
    267 	/*
    268 	 * Check hints files for hint zones.
    269 	 * Skip loading checks for any type other than
    270 	 * master and redirect
    271 	 */
    272 	if (strcasecmp(cfg_obj_asstring(typeobj), "hint") == 0) {
    273 		return (configure_hint(zfile, zclass, mctx));
    274 	} else if ((strcasecmp(cfg_obj_asstring(typeobj), "primary") != 0) &&
    275 		   (strcasecmp(cfg_obj_asstring(typeobj), "master") != 0) &&
    276 		   (strcasecmp(cfg_obj_asstring(typeobj), "redirect") != 0))
    277 	{
    278 		return (ISC_R_SUCCESS);
    279 	}
    280 
    281 	/*
    282 	 * Is the redirect zone configured as a slave?
    283 	 */
    284 	if (strcasecmp(cfg_obj_asstring(typeobj), "redirect") == 0) {
    285 		cfg_map_get(zoptions, "primaries", &primariesobj);
    286 		if (primariesobj == NULL) {
    287 			cfg_map_get(zoptions, "masters", &primariesobj);
    288 		}
    289 
    290 		if (primariesobj != NULL) {
    291 			return (ISC_R_SUCCESS);
    292 		}
    293 	}
    294 
    295 	if (zfile == NULL) {
    296 		return (ISC_R_FAILURE);
    297 	}
    298 
    299 	obj = NULL;
    300 	if (get_maps(maps, "check-dup-records", &obj)) {
    301 		if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
    302 			zone_options |= DNS_ZONEOPT_CHECKDUPRR;
    303 			zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL;
    304 		} else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
    305 			zone_options |= DNS_ZONEOPT_CHECKDUPRR;
    306 			zone_options |= DNS_ZONEOPT_CHECKDUPRRFAIL;
    307 		} else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
    308 			zone_options &= ~DNS_ZONEOPT_CHECKDUPRR;
    309 			zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL;
    310 		} else {
    311 			UNREACHABLE();
    312 		}
    313 	} else {
    314 		zone_options |= DNS_ZONEOPT_CHECKDUPRR;
    315 		zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL;
    316 	}
    317 
    318 	obj = NULL;
    319 	if (get_maps(maps, "check-mx", &obj)) {
    320 		if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
    321 			zone_options |= DNS_ZONEOPT_CHECKMX;
    322 			zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
    323 		} else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
    324 			zone_options |= DNS_ZONEOPT_CHECKMX;
    325 			zone_options |= DNS_ZONEOPT_CHECKMXFAIL;
    326 		} else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
    327 			zone_options &= ~DNS_ZONEOPT_CHECKMX;
    328 			zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
    329 		} else {
    330 			UNREACHABLE();
    331 		}
    332 	} else {
    333 		zone_options |= DNS_ZONEOPT_CHECKMX;
    334 		zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
    335 	}
    336 
    337 	obj = NULL;
    338 	if (get_maps(maps, "check-integrity", &obj)) {
    339 		if (cfg_obj_asboolean(obj)) {
    340 			zone_options |= DNS_ZONEOPT_CHECKINTEGRITY;
    341 		} else {
    342 			zone_options &= ~DNS_ZONEOPT_CHECKINTEGRITY;
    343 		}
    344 	} else {
    345 		zone_options |= DNS_ZONEOPT_CHECKINTEGRITY;
    346 	}
    347 
    348 	obj = NULL;
    349 	if (get_maps(maps, "check-mx-cname", &obj)) {
    350 		if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
    351 			zone_options |= DNS_ZONEOPT_WARNMXCNAME;
    352 			zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME;
    353 		} else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
    354 			zone_options &= ~DNS_ZONEOPT_WARNMXCNAME;
    355 			zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME;
    356 		} else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
    357 			zone_options |= DNS_ZONEOPT_WARNMXCNAME;
    358 			zone_options |= DNS_ZONEOPT_IGNOREMXCNAME;
    359 		} else {
    360 			UNREACHABLE();
    361 		}
    362 	} else {
    363 		zone_options |= DNS_ZONEOPT_WARNMXCNAME;
    364 		zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME;
    365 	}
    366 
    367 	obj = NULL;
    368 	if (get_maps(maps, "check-srv-cname", &obj)) {
    369 		if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
    370 			zone_options |= DNS_ZONEOPT_WARNSRVCNAME;
    371 			zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME;
    372 		} else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
    373 			zone_options &= ~DNS_ZONEOPT_WARNSRVCNAME;
    374 			zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME;
    375 		} else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
    376 			zone_options |= DNS_ZONEOPT_WARNSRVCNAME;
    377 			zone_options |= DNS_ZONEOPT_IGNORESRVCNAME;
    378 		} else {
    379 			UNREACHABLE();
    380 		}
    381 	} else {
    382 		zone_options |= DNS_ZONEOPT_WARNSRVCNAME;
    383 		zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME;
    384 	}
    385 
    386 	obj = NULL;
    387 	if (get_maps(maps, "check-sibling", &obj)) {
    388 		if (cfg_obj_asboolean(obj)) {
    389 			zone_options |= DNS_ZONEOPT_CHECKSIBLING;
    390 		} else {
    391 			zone_options &= ~DNS_ZONEOPT_CHECKSIBLING;
    392 		}
    393 	}
    394 
    395 	obj = NULL;
    396 	if (get_maps(maps, "check-spf", &obj)) {
    397 		if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
    398 			zone_options |= DNS_ZONEOPT_CHECKSPF;
    399 		} else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
    400 			zone_options &= ~DNS_ZONEOPT_CHECKSPF;
    401 		} else {
    402 			UNREACHABLE();
    403 		}
    404 	} else {
    405 		zone_options |= DNS_ZONEOPT_CHECKSPF;
    406 	}
    407 
    408 	obj = NULL;
    409 	if (get_maps(maps, "check-wildcard", &obj)) {
    410 		if (cfg_obj_asboolean(obj)) {
    411 			zone_options |= DNS_ZONEOPT_CHECKWILDCARD;
    412 		} else {
    413 			zone_options &= ~DNS_ZONEOPT_CHECKWILDCARD;
    414 		}
    415 	} else {
    416 		zone_options |= DNS_ZONEOPT_CHECKWILDCARD;
    417 	}
    418 
    419 	obj = NULL;
    420 	if (get_checknames(maps, &obj)) {
    421 		if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
    422 			zone_options |= DNS_ZONEOPT_CHECKNAMES;
    423 			zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL;
    424 		} else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
    425 			zone_options |= DNS_ZONEOPT_CHECKNAMES;
    426 			zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL;
    427 		} else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
    428 			zone_options &= ~DNS_ZONEOPT_CHECKNAMES;
    429 			zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL;
    430 		} else {
    431 			UNREACHABLE();
    432 		}
    433 	} else {
    434 		zone_options |= DNS_ZONEOPT_CHECKNAMES;
    435 		zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL;
    436 	}
    437 
    438 	masterformat = dns_masterformat_text;
    439 	fmtobj = NULL;
    440 	if (get_maps(maps, "masterfile-format", &fmtobj)) {
    441 		const char *masterformatstr = cfg_obj_asstring(fmtobj);
    442 		if (strcasecmp(masterformatstr, "text") == 0) {
    443 			masterformat = dns_masterformat_text;
    444 		} else if (strcasecmp(masterformatstr, "raw") == 0) {
    445 			masterformat = dns_masterformat_raw;
    446 		} else if (strcasecmp(masterformatstr, "map") == 0) {
    447 			masterformat = dns_masterformat_map;
    448 		} else {
    449 			UNREACHABLE();
    450 		}
    451 	}
    452 
    453 	obj = NULL;
    454 	if (get_maps(maps, "max-zone-ttl", &obj)) {
    455 		maxttl = cfg_obj_asduration(obj);
    456 		zone_options |= DNS_ZONEOPT_CHECKTTL;
    457 	}
    458 
    459 	result = load_zone(mctx, zname, zfile, masterformat, zclass, maxttl,
    460 			   NULL);
    461 	if (result != ISC_R_SUCCESS) {
    462 		fprintf(stderr, "%s/%s/%s: %s\n", view, zname, zclass,
    463 			dns_result_totext(result));
    464 	}
    465 	return (result);
    466 }
    467 
    468 /*% configure a view */
    469 static isc_result_t
    470 configure_view(const char *vclass, const char *view, const cfg_obj_t *config,
    471 	       const cfg_obj_t *vconfig, isc_mem_t *mctx, bool list) {
    472 	const cfg_listelt_t *element;
    473 	const cfg_obj_t *voptions;
    474 	const cfg_obj_t *zonelist;
    475 	isc_result_t result = ISC_R_SUCCESS;
    476 	isc_result_t tresult;
    477 
    478 	voptions = NULL;
    479 	if (vconfig != NULL) {
    480 		voptions = cfg_tuple_get(vconfig, "options");
    481 	}
    482 
    483 	zonelist = NULL;
    484 	if (voptions != NULL) {
    485 		(void)cfg_map_get(voptions, "zone", &zonelist);
    486 	} else {
    487 		(void)cfg_map_get(config, "zone", &zonelist);
    488 	}
    489 
    490 	for (element = cfg_list_first(zonelist); element != NULL;
    491 	     element = cfg_list_next(element))
    492 	{
    493 		const cfg_obj_t *zconfig = cfg_listelt_value(element);
    494 		tresult = configure_zone(vclass, view, zconfig, vconfig, config,
    495 					 mctx, list);
    496 		if (tresult != ISC_R_SUCCESS) {
    497 			result = tresult;
    498 		}
    499 	}
    500 	return (result);
    501 }
    502 
    503 static isc_result_t
    504 config_getclass(const cfg_obj_t *classobj, dns_rdataclass_t defclass,
    505 		dns_rdataclass_t *classp) {
    506 	isc_textregion_t r;
    507 
    508 	if (!cfg_obj_isstring(classobj)) {
    509 		*classp = defclass;
    510 		return (ISC_R_SUCCESS);
    511 	}
    512 	DE_CONST(cfg_obj_asstring(classobj), r.base);
    513 	r.length = strlen(r.base);
    514 	return (dns_rdataclass_fromtext(classp, &r));
    515 }
    516 
    517 /*% load zones from the configuration */
    518 static isc_result_t
    519 load_zones_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx,
    520 		      bool list_zones) {
    521 	const cfg_listelt_t *element;
    522 	const cfg_obj_t *views;
    523 	const cfg_obj_t *vconfig;
    524 	isc_result_t result = ISC_R_SUCCESS;
    525 	isc_result_t tresult;
    526 
    527 	views = NULL;
    528 
    529 	(void)cfg_map_get(config, "view", &views);
    530 	for (element = cfg_list_first(views); element != NULL;
    531 	     element = cfg_list_next(element))
    532 	{
    533 		const cfg_obj_t *classobj;
    534 		dns_rdataclass_t viewclass;
    535 		const char *vname;
    536 		char buf[sizeof("CLASS65535")];
    537 
    538 		vconfig = cfg_listelt_value(element);
    539 		if (vconfig == NULL) {
    540 			continue;
    541 		}
    542 
    543 		classobj = cfg_tuple_get(vconfig, "class");
    544 		tresult = config_getclass(classobj, dns_rdataclass_in,
    545 					  &viewclass);
    546 		if (tresult != ISC_R_SUCCESS) {
    547 			CHECK(tresult);
    548 		}
    549 
    550 		if (dns_rdataclass_ismeta(viewclass)) {
    551 			CHECK(ISC_R_FAILURE);
    552 		}
    553 
    554 		dns_rdataclass_format(viewclass, buf, sizeof(buf));
    555 		vname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name"));
    556 		tresult = configure_view(buf, vname, config, vconfig, mctx,
    557 					 list_zones);
    558 		if (tresult != ISC_R_SUCCESS) {
    559 			result = tresult;
    560 		}
    561 	}
    562 
    563 	if (views == NULL) {
    564 		tresult = configure_view("IN", "_default", config, NULL, mctx,
    565 					 list_zones);
    566 		if (tresult != ISC_R_SUCCESS) {
    567 			result = tresult;
    568 		}
    569 	}
    570 
    571 cleanup:
    572 	return (result);
    573 }
    574 
    575 static void
    576 output(void *closure, const char *text, int textlen) {
    577 	UNUSED(closure);
    578 	if (fwrite(text, 1, textlen, stdout) != (size_t)textlen) {
    579 		perror("fwrite");
    580 		exit(1);
    581 	}
    582 }
    583 
    584 /*% The main processing routine */
    585 int
    586 main(int argc, char **argv) {
    587 	int c;
    588 	cfg_parser_t *parser = NULL;
    589 	cfg_obj_t *config = NULL;
    590 	const char *conffile = NULL;
    591 	isc_mem_t *mctx = NULL;
    592 	isc_result_t result;
    593 	int exit_status = 0;
    594 	bool load_zones = false;
    595 	bool list_zones = false;
    596 	bool print = false;
    597 	bool nodeprecate = false;
    598 	unsigned int flags = 0;
    599 
    600 	isc_commandline_errprint = false;
    601 
    602 	/*
    603 	 * Process memory debugging argument first.
    604 	 */
    605 #define CMDLINE_FLAGS "cdhijlm:t:pvxz"
    606 	while ((c = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
    607 		switch (c) {
    608 		case 'm':
    609 			if (strcasecmp(isc_commandline_argument, "record") == 0)
    610 			{
    611 				isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
    612 			}
    613 			if (strcasecmp(isc_commandline_argument, "trace") == 0)
    614 			{
    615 				isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
    616 			}
    617 			if (strcasecmp(isc_commandline_argument, "usage") == 0)
    618 			{
    619 				isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
    620 			}
    621 			if (strcasecmp(isc_commandline_argument, "size") == 0) {
    622 				isc_mem_debugging |= ISC_MEM_DEBUGSIZE;
    623 			}
    624 			if (strcasecmp(isc_commandline_argument, "mctx") == 0) {
    625 				isc_mem_debugging |= ISC_MEM_DEBUGCTX;
    626 			}
    627 			break;
    628 		default:
    629 			break;
    630 		}
    631 	}
    632 	isc_commandline_reset = true;
    633 
    634 	isc_mem_create(&mctx);
    635 
    636 	while ((c = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != EOF) {
    637 		switch (c) {
    638 		case 'c':
    639 			loadplugins = false;
    640 			break;
    641 
    642 		case 'd':
    643 			debug++;
    644 			break;
    645 
    646 		case 'i':
    647 			nodeprecate = true;
    648 			break;
    649 
    650 		case 'j':
    651 			nomerge = false;
    652 			break;
    653 
    654 		case 'l':
    655 			list_zones = true;
    656 			break;
    657 
    658 		case 'm':
    659 			break;
    660 
    661 		case 't':
    662 			result = isc_dir_chroot(isc_commandline_argument);
    663 			if (result != ISC_R_SUCCESS) {
    664 				fprintf(stderr, "isc_dir_chroot: %s\n",
    665 					isc_result_totext(result));
    666 				exit(1);
    667 			}
    668 			break;
    669 
    670 		case 'p':
    671 			print = true;
    672 			break;
    673 
    674 		case 'v':
    675 			printf(VERSION "\n");
    676 			exit(0);
    677 
    678 		case 'x':
    679 			flags |= CFG_PRINTER_XKEY;
    680 			break;
    681 
    682 		case 'z':
    683 			load_zones = true;
    684 			docheckmx = false;
    685 			docheckns = false;
    686 			dochecksrv = false;
    687 			break;
    688 
    689 		case '?':
    690 			if (isc_commandline_option != '?') {
    691 				fprintf(stderr, "%s: invalid argument -%c\n",
    692 					program, isc_commandline_option);
    693 			}
    694 			FALLTHROUGH;
    695 		case 'h':
    696 			usage();
    697 
    698 		default:
    699 			fprintf(stderr, "%s: unhandled option -%c\n", program,
    700 				isc_commandline_option);
    701 			exit(1);
    702 		}
    703 	}
    704 
    705 	if (((flags & CFG_PRINTER_XKEY) != 0) && !print) {
    706 		fprintf(stderr, "%s: -x cannot be used without -p\n", program);
    707 		exit(1);
    708 	}
    709 	if (print && list_zones) {
    710 		fprintf(stderr, "%s: -l cannot be used with -p\n", program);
    711 		exit(1);
    712 	}
    713 
    714 	if (isc_commandline_index + 1 < argc) {
    715 		usage();
    716 	}
    717 	if (argv[isc_commandline_index] != NULL) {
    718 		conffile = argv[isc_commandline_index];
    719 	}
    720 	if (conffile == NULL || conffile[0] == '\0') {
    721 		conffile = NAMED_CONFFILE;
    722 	}
    723 
    724 #ifdef _WIN32
    725 	InitSockets();
    726 #endif /* ifdef _WIN32 */
    727 
    728 	RUNTIME_CHECK(setup_logging(mctx, stdout, &logc) == ISC_R_SUCCESS);
    729 
    730 	dns_result_register();
    731 
    732 	RUNTIME_CHECK(cfg_parser_create(mctx, logc, &parser) == ISC_R_SUCCESS);
    733 
    734 	if (nodeprecate) {
    735 		cfg_parser_setflags(parser, CFG_PCTX_NODEPRECATED, true);
    736 	}
    737 	cfg_parser_setcallback(parser, directory_callback, NULL);
    738 
    739 	if (cfg_parse_file(parser, conffile, &cfg_type_namedconf, &config) !=
    740 	    ISC_R_SUCCESS)
    741 	{
    742 		exit(1);
    743 	}
    744 
    745 	result = bind9_check_namedconf(config, loadplugins, logc, mctx);
    746 	if (result != ISC_R_SUCCESS) {
    747 		exit_status = 1;
    748 	}
    749 
    750 	if (result == ISC_R_SUCCESS && (load_zones || list_zones)) {
    751 		result = load_zones_fromconfig(config, mctx, list_zones);
    752 		if (result != ISC_R_SUCCESS) {
    753 			exit_status = 1;
    754 		}
    755 	}
    756 
    757 	if (print && exit_status == 0) {
    758 		cfg_printx(config, flags, output, NULL);
    759 	}
    760 	cfg_obj_destroy(parser, &config);
    761 
    762 	cfg_parser_destroy(&parser);
    763 
    764 	isc_log_destroy(&logc);
    765 
    766 	isc_mem_destroy(&mctx);
    767 
    768 #ifdef _WIN32
    769 	DestroySockets();
    770 #endif /* ifdef _WIN32 */
    771 
    772 	return (exit_status);
    773 }
    774