1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7  *
8  * See the COPYRIGHT file distributed with this work for additional
9  * information regarding copyright ownership.
10  */
11 
12 /*! \file */
13 
14 #include <errno.h>
15 #include <stdbool.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 
19 #include <isc/attributes.h>
20 #include <isc/commandline.h>
21 #include <isc/dir.h>
22 #include <isc/hash.h>
23 #include <isc/log.h>
24 #include <isc/mem.h>
25 #include <isc/print.h>
26 #include <isc/result.h>
27 #include <isc/string.h>
28 #include <isc/util.h>
29 
30 #include <dns/db.h>
31 #include <dns/fixedname.h>
32 #include <dns/log.h>
33 #include <dns/name.h>
34 #include <dns/rdataclass.h>
35 #include <dns/rootns.h>
36 #include <dns/zone.h>
37 
38 #include <isccfg/grammar.h>
39 #include <isccfg/namedconf.h>
40 
41 #include <bind9/check.h>
42 
43 #include "check-tool.h"
44 
45 static const char *program = "named-checkconf";
46 
47 static bool loadplugins = true;
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 ISC_NORETURN static void
60 usage(void);
61 
62 static void
usage(void)63 usage(void) {
64 	fprintf(stderr,
65 		"usage: %s [-chijlvz] [-p [-x]] [-t directory] "
66 		"[named.conf]\n",
67 		program);
68 	exit(1);
69 }
70 
71 /*% directory callback */
72 static isc_result_t
directory_callback(const char * clausename,const cfg_obj_t * obj,void * arg)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
get_maps(const cfg_obj_t ** maps,const char * name,const cfg_obj_t ** obj)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
get_checknames(const cfg_obj_t ** maps,const cfg_obj_t ** obj)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
configure_hint(const char * zfile,const char * zclass,isc_mem_t * mctx)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 	DE_CONST(zclass, r.base);
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
configure_zone(const char * vclass,const char * view,const cfg_obj_t * zconfig,const cfg_obj_t * vconfig,const cfg_obj_t * config,isc_mem_t * mctx,bool list)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 && strcmp("rbt", cfg_obj_asstring(dbobj)) != 0 &&
248 	    strcmp("rbt64", 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 slave?
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 			INSIST(0);
308 			ISC_UNREACHABLE();
309 		}
310 	} else {
311 		zone_options |= DNS_ZONEOPT_CHECKDUPRR;
312 		zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL;
313 	}
314 
315 	obj = NULL;
316 	if (get_maps(maps, "check-mx", &obj)) {
317 		if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
318 			zone_options |= DNS_ZONEOPT_CHECKMX;
319 			zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
320 		} else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
321 			zone_options |= DNS_ZONEOPT_CHECKMX;
322 			zone_options |= DNS_ZONEOPT_CHECKMXFAIL;
323 		} else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
324 			zone_options &= ~DNS_ZONEOPT_CHECKMX;
325 			zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
326 		} else {
327 			INSIST(0);
328 			ISC_UNREACHABLE();
329 		}
330 	} else {
331 		zone_options |= DNS_ZONEOPT_CHECKMX;
332 		zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
333 	}
334 
335 	obj = NULL;
336 	if (get_maps(maps, "check-integrity", &obj)) {
337 		if (cfg_obj_asboolean(obj)) {
338 			zone_options |= DNS_ZONEOPT_CHECKINTEGRITY;
339 		} else {
340 			zone_options &= ~DNS_ZONEOPT_CHECKINTEGRITY;
341 		}
342 	} else {
343 		zone_options |= DNS_ZONEOPT_CHECKINTEGRITY;
344 	}
345 
346 	obj = NULL;
347 	if (get_maps(maps, "check-mx-cname", &obj)) {
348 		if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
349 			zone_options |= DNS_ZONEOPT_WARNMXCNAME;
350 			zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME;
351 		} else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
352 			zone_options &= ~DNS_ZONEOPT_WARNMXCNAME;
353 			zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME;
354 		} else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
355 			zone_options |= DNS_ZONEOPT_WARNMXCNAME;
356 			zone_options |= DNS_ZONEOPT_IGNOREMXCNAME;
357 		} else {
358 			INSIST(0);
359 			ISC_UNREACHABLE();
360 		}
361 	} else {
362 		zone_options |= DNS_ZONEOPT_WARNMXCNAME;
363 		zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME;
364 	}
365 
366 	obj = NULL;
367 	if (get_maps(maps, "check-srv-cname", &obj)) {
368 		if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
369 			zone_options |= DNS_ZONEOPT_WARNSRVCNAME;
370 			zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME;
371 		} else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
372 			zone_options &= ~DNS_ZONEOPT_WARNSRVCNAME;
373 			zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME;
374 		} else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
375 			zone_options |= DNS_ZONEOPT_WARNSRVCNAME;
376 			zone_options |= DNS_ZONEOPT_IGNORESRVCNAME;
377 		} else {
378 			INSIST(0);
379 			ISC_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 			INSIST(0);
403 			ISC_UNREACHABLE();
404 		}
405 	} else {
406 		zone_options |= DNS_ZONEOPT_CHECKSPF;
407 	}
408 
409 	obj = NULL;
410 	if (get_checknames(maps, &obj)) {
411 		if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
412 			zone_options |= DNS_ZONEOPT_CHECKNAMES;
413 			zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL;
414 		} else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
415 			zone_options |= DNS_ZONEOPT_CHECKNAMES;
416 			zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL;
417 		} else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
418 			zone_options &= ~DNS_ZONEOPT_CHECKNAMES;
419 			zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL;
420 		} else {
421 			INSIST(0);
422 			ISC_UNREACHABLE();
423 		}
424 	} else {
425 		zone_options |= DNS_ZONEOPT_CHECKNAMES;
426 		zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL;
427 	}
428 
429 	masterformat = dns_masterformat_text;
430 	fmtobj = NULL;
431 	if (get_maps(maps, "masterfile-format", &fmtobj)) {
432 		const char *masterformatstr = cfg_obj_asstring(fmtobj);
433 		if (strcasecmp(masterformatstr, "text") == 0) {
434 			masterformat = dns_masterformat_text;
435 		} else if (strcasecmp(masterformatstr, "raw") == 0) {
436 			masterformat = dns_masterformat_raw;
437 		} else {
438 			INSIST(0);
439 			ISC_UNREACHABLE();
440 		}
441 	}
442 
443 	obj = NULL;
444 	if (get_maps(maps, "max-zone-ttl", &obj)) {
445 		maxttl = cfg_obj_asduration(obj);
446 		zone_options |= DNS_ZONEOPT_CHECKTTL;
447 	}
448 
449 	result = load_zone(mctx, zname, zfile, masterformat, zclass, maxttl,
450 			   NULL);
451 	if (result != ISC_R_SUCCESS) {
452 		fprintf(stderr, "%s/%s/%s: %s\n", view, zname, zclass,
453 			isc_result_totext(result));
454 	}
455 	return (result);
456 }
457 
458 /*% configure a view */
459 static isc_result_t
configure_view(const char * vclass,const char * view,const cfg_obj_t * config,const cfg_obj_t * vconfig,isc_mem_t * mctx,bool list)460 configure_view(const char *vclass, const char *view, const cfg_obj_t *config,
461 	       const cfg_obj_t *vconfig, isc_mem_t *mctx, bool list) {
462 	const cfg_listelt_t *element;
463 	const cfg_obj_t *voptions;
464 	const cfg_obj_t *zonelist;
465 	isc_result_t result = ISC_R_SUCCESS;
466 	isc_result_t tresult;
467 
468 	voptions = NULL;
469 	if (vconfig != NULL) {
470 		voptions = cfg_tuple_get(vconfig, "options");
471 	}
472 
473 	zonelist = NULL;
474 	if (voptions != NULL) {
475 		(void)cfg_map_get(voptions, "zone", &zonelist);
476 	} else {
477 		(void)cfg_map_get(config, "zone", &zonelist);
478 	}
479 
480 	for (element = cfg_list_first(zonelist); element != NULL;
481 	     element = cfg_list_next(element))
482 	{
483 		const cfg_obj_t *zconfig = cfg_listelt_value(element);
484 		tresult = configure_zone(vclass, view, zconfig, vconfig, config,
485 					 mctx, list);
486 		if (tresult != ISC_R_SUCCESS) {
487 			result = tresult;
488 		}
489 	}
490 	return (result);
491 }
492 
493 static isc_result_t
config_getclass(const cfg_obj_t * classobj,dns_rdataclass_t defclass,dns_rdataclass_t * classp)494 config_getclass(const cfg_obj_t *classobj, dns_rdataclass_t defclass,
495 		dns_rdataclass_t *classp) {
496 	isc_textregion_t r;
497 
498 	if (!cfg_obj_isstring(classobj)) {
499 		*classp = defclass;
500 		return (ISC_R_SUCCESS);
501 	}
502 	DE_CONST(cfg_obj_asstring(classobj), r.base);
503 	r.length = strlen(r.base);
504 	return (dns_rdataclass_fromtext(classp, &r));
505 }
506 
507 /*% load zones from the configuration */
508 static isc_result_t
load_zones_fromconfig(const cfg_obj_t * config,isc_mem_t * mctx,bool list_zones)509 load_zones_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx,
510 		      bool list_zones) {
511 	const cfg_listelt_t *element;
512 	const cfg_obj_t *views;
513 	const cfg_obj_t *vconfig;
514 	isc_result_t result = ISC_R_SUCCESS;
515 	isc_result_t tresult;
516 
517 	views = NULL;
518 
519 	(void)cfg_map_get(config, "view", &views);
520 	for (element = cfg_list_first(views); element != NULL;
521 	     element = cfg_list_next(element))
522 	{
523 		const cfg_obj_t *classobj;
524 		dns_rdataclass_t viewclass;
525 		const char *vname;
526 		char buf[sizeof("CLASS65535")];
527 
528 		vconfig = cfg_listelt_value(element);
529 		if (vconfig == NULL) {
530 			continue;
531 		}
532 
533 		classobj = cfg_tuple_get(vconfig, "class");
534 		tresult = config_getclass(classobj, dns_rdataclass_in,
535 					  &viewclass);
536 		if (tresult != ISC_R_SUCCESS) {
537 			CHECK(tresult);
538 		}
539 
540 		if (dns_rdataclass_ismeta(viewclass)) {
541 			CHECK(ISC_R_FAILURE);
542 		}
543 
544 		dns_rdataclass_format(viewclass, buf, sizeof(buf));
545 		vname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name"));
546 		tresult = configure_view(buf, vname, config, vconfig, mctx,
547 					 list_zones);
548 		if (tresult != ISC_R_SUCCESS) {
549 			result = tresult;
550 		}
551 	}
552 
553 	if (views == NULL) {
554 		tresult = configure_view("IN", "_default", config, NULL, mctx,
555 					 list_zones);
556 		if (tresult != ISC_R_SUCCESS) {
557 			result = tresult;
558 		}
559 	}
560 
561 cleanup:
562 	return (result);
563 }
564 
565 static void
output(void * closure,const char * text,int textlen)566 output(void *closure, const char *text, int textlen) {
567 	UNUSED(closure);
568 	if (fwrite(text, 1, textlen, stdout) != (size_t)textlen) {
569 		perror("fwrite");
570 		exit(1);
571 	}
572 }
573 
574 /*% The main processing routine */
575 int
main(int argc,char ** argv)576 main(int argc, char **argv) {
577 	int c;
578 	cfg_parser_t *parser = NULL;
579 	cfg_obj_t *config = NULL;
580 	const char *conffile = NULL;
581 	isc_mem_t *mctx = NULL;
582 	isc_result_t result;
583 	int exit_status = 0;
584 	bool load_zones = false;
585 	bool list_zones = false;
586 	bool print = false;
587 	bool nodeprecate = false;
588 	unsigned int flags = 0;
589 
590 	isc_commandline_errprint = false;
591 
592 	/*
593 	 * Process memory debugging argument first.
594 	 */
595 #define CMDLINE_FLAGS "cdhijlm:t:pvxz"
596 	while ((c = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
597 		switch (c) {
598 		case 'm':
599 			if (strcasecmp(isc_commandline_argument, "record") == 0)
600 			{
601 				isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
602 			}
603 			if (strcasecmp(isc_commandline_argument, "trace") == 0)
604 			{
605 				isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
606 			}
607 			if (strcasecmp(isc_commandline_argument, "usage") == 0)
608 			{
609 				isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
610 			}
611 			break;
612 		default:
613 			break;
614 		}
615 	}
616 	isc_commandline_reset = true;
617 
618 	isc_mem_create(&mctx);
619 
620 	while ((c = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != EOF) {
621 		switch (c) {
622 		case 'c':
623 			loadplugins = false;
624 			break;
625 
626 		case 'd':
627 			debug++;
628 			break;
629 
630 		case 'i':
631 			nodeprecate = true;
632 			break;
633 
634 		case 'j':
635 			nomerge = false;
636 			break;
637 
638 		case 'l':
639 			list_zones = true;
640 			break;
641 
642 		case 'm':
643 			break;
644 
645 		case 't':
646 			result = isc_dir_chroot(isc_commandline_argument);
647 			if (result != ISC_R_SUCCESS) {
648 				fprintf(stderr, "isc_dir_chroot: %s\n",
649 					isc_result_totext(result));
650 				exit(1);
651 			}
652 			break;
653 
654 		case 'p':
655 			print = true;
656 			break;
657 
658 		case 'v':
659 			printf("%s\n", PACKAGE_VERSION);
660 			exit(0);
661 
662 		case 'x':
663 			flags |= CFG_PRINTER_XKEY;
664 			break;
665 
666 		case 'z':
667 			load_zones = true;
668 			docheckmx = false;
669 			docheckns = false;
670 			dochecksrv = false;
671 			break;
672 
673 		case '?':
674 			if (isc_commandline_option != '?') {
675 				fprintf(stderr, "%s: invalid argument -%c\n",
676 					program, isc_commandline_option);
677 			}
678 		/* FALLTHROUGH */
679 		case 'h':
680 			usage();
681 
682 		default:
683 			fprintf(stderr, "%s: unhandled option -%c\n", program,
684 				isc_commandline_option);
685 			exit(1);
686 		}
687 	}
688 
689 	if (((flags & CFG_PRINTER_XKEY) != 0) && !print) {
690 		fprintf(stderr, "%s: -x cannot be used without -p\n", program);
691 		exit(1);
692 	}
693 	if (print && list_zones) {
694 		fprintf(stderr, "%s: -l cannot be used with -p\n", program);
695 		exit(1);
696 	}
697 
698 	if (isc_commandline_index + 1 < argc) {
699 		usage();
700 	}
701 	if (argv[isc_commandline_index] != NULL) {
702 		conffile = argv[isc_commandline_index];
703 	}
704 	if (conffile == NULL || conffile[0] == '\0') {
705 		conffile = NAMED_CONFFILE;
706 	}
707 
708 	RUNTIME_CHECK(setup_logging(mctx, stdout, &logc) == ISC_R_SUCCESS);
709 
710 	RUNTIME_CHECK(cfg_parser_create(mctx, logc, &parser) == ISC_R_SUCCESS);
711 
712 	if (nodeprecate) {
713 		cfg_parser_setflags(parser, CFG_PCTX_NODEPRECATED, true);
714 	}
715 	cfg_parser_setcallback(parser, directory_callback, NULL);
716 
717 	if (cfg_parse_file(parser, conffile, &cfg_type_namedconf, &config) !=
718 	    ISC_R_SUCCESS)
719 	{
720 		exit(1);
721 	}
722 
723 	result = bind9_check_namedconf(config, loadplugins, logc, mctx);
724 	if (result != ISC_R_SUCCESS) {
725 		exit_status = 1;
726 	}
727 
728 	if (result == ISC_R_SUCCESS && (load_zones || list_zones)) {
729 		result = load_zones_fromconfig(config, mctx, list_zones);
730 		if (result != ISC_R_SUCCESS) {
731 			exit_status = 1;
732 		}
733 	}
734 
735 	if (print && exit_status == 0) {
736 		cfg_printx(config, flags, output, NULL);
737 	}
738 	cfg_obj_destroy(parser, &config);
739 
740 	cfg_parser_destroy(&parser);
741 
742 	isc_log_destroy(&logc);
743 
744 	isc_mem_destroy(&mctx);
745 
746 	return (exit_status);
747 }
748