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