1 /*	$NetBSD: named-checkconf.c,v 1.11 2015/07/08 17:28:54 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2004-2007, 2009-2014  Internet Systems Consortium, Inc. ("ISC")
5  * Copyright (C) 1999-2002  Internet Software Consortium.
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /* Id: named-checkconf.c,v 1.56 2011/03/12 04:59:46 tbox Exp  */
21 
22 /*! \file */
23 
24 #include <config.h>
25 
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 
30 #include <isc/commandline.h>
31 #include <isc/dir.h>
32 #include <isc/entropy.h>
33 #include <isc/hash.h>
34 #include <isc/log.h>
35 #include <isc/mem.h>
36 #include <isc/result.h>
37 #include <isc/string.h>
38 #include <isc/util.h>
39 
40 #include <isccfg/namedconf.h>
41 
42 #include <bind9/check.h>
43 
44 #include <dns/db.h>
45 #include <dns/fixedname.h>
46 #include <dns/log.h>
47 #include <dns/name.h>
48 #include <dns/rdataclass.h>
49 #include <dns/result.h>
50 #include <dns/rootns.h>
51 #include <dns/zone.h>
52 
53 #include "check-tool.h"
54 
55 static const char *program = "named-checkconf";
56 
57 isc_log_t *logc = NULL;
58 
59 #define CHECK(r)\
60 	do { \
61 		result = (r); \
62 		if (result != ISC_R_SUCCESS) \
63 			goto cleanup; \
64 	} while (/*CONSTCOND*/0)
65 
66 /*% usage */
67 ISC_PLATFORM_NORETURN_PRE static void
68 usage(void) ISC_PLATFORM_NORETURN_POST;
69 
70 static void
usage(void)71 usage(void) {
72 	fprintf(stderr, "usage: %s [-h] [-j] [-p] [-v] [-z] [-t directory] "
73 		"[named.conf]\n", program);
74 	exit(1);
75 }
76 
77 /*% directory callback */
78 static isc_result_t
directory_callback(const char * clausename,const cfg_obj_t * obj,void * arg)79 directory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) {
80 	isc_result_t result;
81 	const char *directory;
82 
83 	REQUIRE(strcasecmp("directory", clausename) == 0);
84 
85 	UNUSED(arg);
86 	UNUSED(clausename);
87 
88 	/*
89 	 * Change directory.
90 	 */
91 	directory = cfg_obj_asstring(obj);
92 	result = isc_dir_chdir(directory);
93 	if (result != ISC_R_SUCCESS) {
94 		cfg_obj_log(obj, logc, ISC_LOG_ERROR,
95 			    "change directory to '%s' failed: %s\n",
96 			    directory, isc_result_totext(result));
97 		return (result);
98 	}
99 
100 	return (ISC_R_SUCCESS);
101 }
102 
103 static isc_boolean_t
get_maps(const cfg_obj_t ** maps,const char * name,const cfg_obj_t ** obj)104 get_maps(const cfg_obj_t **maps, const char *name, const cfg_obj_t **obj) {
105 	int i;
106 	for (i = 0;; i++) {
107 		if (maps[i] == NULL)
108 			return (ISC_FALSE);
109 		if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS)
110 			return (ISC_TRUE);
111 	}
112 }
113 
114 static isc_boolean_t
get_checknames(const cfg_obj_t ** maps,const cfg_obj_t ** obj)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 (ISC_FALSE);
126 		checknames = NULL;
127 		result = cfg_map_get(maps[i], "check-names", &checknames);
128 		if (result != ISC_R_SUCCESS)
129 			continue;
130 		if (checknames != NULL && !cfg_obj_islist(checknames)) {
131 			*obj = checknames;
132 			return (ISC_TRUE);
133 		}
134 		for (element = cfg_list_first(checknames);
135 		     element != NULL;
136 		     element = cfg_list_next(element)) {
137 			value = cfg_listelt_value(element);
138 			type = cfg_tuple_get(value, "type");
139 			if (strcasecmp(cfg_obj_asstring(type), "master") != 0)
140 				continue;
141 			*obj = cfg_tuple_get(value, "mode");
142 			return (ISC_TRUE);
143 		}
144 	}
145 }
146 
147 static isc_result_t
configure_hint(const char * zfile,const char * zclass,isc_mem_t * mctx)148 configure_hint(const char *zfile, const char *zclass, isc_mem_t *mctx) {
149 	isc_result_t result;
150 	dns_db_t *db = NULL;
151 	dns_rdataclass_t rdclass;
152 	isc_textregion_t r;
153 
154 	if (zfile == NULL)
155 		return (ISC_R_FAILURE);
156 
157 	DE_CONST(zclass, r.base);
158 	r.length = strlen(zclass);
159 	result = dns_rdataclass_fromtext(&rdclass, &r);
160 	if (result != ISC_R_SUCCESS)
161 		return (result);
162 
163 	result = dns_rootns_create(mctx, rdclass, zfile, &db);
164 	if (result != ISC_R_SUCCESS)
165 		return (result);
166 
167 	dns_db_detach(&db);
168 	return (ISC_R_SUCCESS);
169 }
170 
171 /*% configure the zone */
172 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)173 configure_zone(const char *vclass, const char *view,
174 	       const cfg_obj_t *zconfig, const cfg_obj_t *vconfig,
175 	       const cfg_obj_t *config, isc_mem_t *mctx)
176 {
177 	int i = 0;
178 	isc_result_t result;
179 	const char *zclass;
180 	const char *zname;
181 	const char *zfile = NULL;
182 	const cfg_obj_t *maps[4];
183 	const cfg_obj_t *mastersobj = NULL;
184 	const cfg_obj_t *zoptions = NULL;
185 	const cfg_obj_t *classobj = NULL;
186 	const cfg_obj_t *typeobj = NULL;
187 	const cfg_obj_t *fileobj = NULL;
188 	const cfg_obj_t *dlzobj = NULL;
189 	const cfg_obj_t *dbobj = NULL;
190 	const cfg_obj_t *obj = NULL;
191 	const cfg_obj_t *fmtobj = NULL;
192 	dns_masterformat_t masterformat;
193 	dns_ttl_t maxttl = 0;
194 
195 	zone_options = DNS_ZONEOPT_CHECKNS | DNS_ZONEOPT_MANYERRORS;
196 
197 	zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
198 	classobj = cfg_tuple_get(zconfig, "class");
199 	if (!cfg_obj_isstring(classobj))
200 		zclass = vclass;
201 	else
202 		zclass = cfg_obj_asstring(classobj);
203 
204 	zoptions = cfg_tuple_get(zconfig, "options");
205 	maps[i++] = zoptions;
206 	if (vconfig != NULL)
207 		maps[i++] = cfg_tuple_get(vconfig, "options");
208 	if (config != NULL) {
209 		cfg_map_get(config, "options", &obj);
210 		if (obj != NULL)
211 			maps[i++] = obj;
212 	}
213 	maps[i] = NULL;
214 
215 	cfg_map_get(zoptions, "type", &typeobj);
216 	if (typeobj == NULL)
217 		return (ISC_R_FAILURE);
218 
219 	/*
220 	 * Skip checks when using an alternate data source.
221 	 */
222 	cfg_map_get(zoptions, "database", &dbobj);
223 	if (dbobj != NULL &&
224 	    strcmp("rbt", cfg_obj_asstring(dbobj)) != 0 &&
225 	    strcmp("rbt64", cfg_obj_asstring(dbobj)) != 0)
226 		return (ISC_R_SUCCESS);
227 
228 	cfg_map_get(zoptions, "dlz", &dlzobj);
229 	if (dlzobj != NULL)
230 		return (ISC_R_SUCCESS);
231 
232 	cfg_map_get(zoptions, "file", &fileobj);
233 	if (fileobj != NULL)
234 		zfile = cfg_obj_asstring(fileobj);
235 
236 	/*
237 	 * Check hints files for hint zones.
238 	 * Skip loading checks for any type other than
239 	 * master and redirect
240 	 */
241 	if (strcasecmp(cfg_obj_asstring(typeobj), "hint") == 0)
242 		return (configure_hint(zfile, zclass, mctx));
243 	else if ((strcasecmp(cfg_obj_asstring(typeobj), "master") != 0) &&
244 		  (strcasecmp(cfg_obj_asstring(typeobj), "redirect") != 0))
245 		return (ISC_R_SUCCESS);
246 
247 	/*
248 	 * Is the redirect zone configured as a slave?
249 	 */
250 	if (strcasecmp(cfg_obj_asstring(typeobj), "redirect") == 0) {
251 		cfg_map_get(zoptions, "masters", &mastersobj);
252 		if (mastersobj != NULL)
253 			return (ISC_R_SUCCESS);
254 	}
255 
256 	if (zfile == NULL)
257 		return (ISC_R_FAILURE);
258 
259 	obj = NULL;
260 	if (get_maps(maps, "check-dup-records", &obj)) {
261 		if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
262 			zone_options |= DNS_ZONEOPT_CHECKDUPRR;
263 			zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL;
264 		} else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
265 			zone_options |= DNS_ZONEOPT_CHECKDUPRR;
266 			zone_options |= DNS_ZONEOPT_CHECKDUPRRFAIL;
267 		} else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
268 			zone_options &= ~DNS_ZONEOPT_CHECKDUPRR;
269 			zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL;
270 		} else
271 			INSIST(0);
272 	} else {
273 		zone_options |= DNS_ZONEOPT_CHECKDUPRR;
274 		zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL;
275 	}
276 
277 	obj = NULL;
278 	if (get_maps(maps, "check-mx", &obj)) {
279 		if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
280 			zone_options |= DNS_ZONEOPT_CHECKMX;
281 			zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
282 		} else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
283 			zone_options |= DNS_ZONEOPT_CHECKMX;
284 			zone_options |= DNS_ZONEOPT_CHECKMXFAIL;
285 		} else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
286 			zone_options &= ~DNS_ZONEOPT_CHECKMX;
287 			zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
288 		} else
289 			INSIST(0);
290 	} else {
291 		zone_options |= DNS_ZONEOPT_CHECKMX;
292 		zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
293 	}
294 
295 	obj = NULL;
296 	if (get_maps(maps, "check-integrity", &obj)) {
297 		if (cfg_obj_asboolean(obj))
298 			zone_options |= DNS_ZONEOPT_CHECKINTEGRITY;
299 		else
300 			zone_options &= ~DNS_ZONEOPT_CHECKINTEGRITY;
301 	} else
302 		zone_options |= DNS_ZONEOPT_CHECKINTEGRITY;
303 
304 	obj = NULL;
305 	if (get_maps(maps, "check-mx-cname", &obj)) {
306 		if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
307 			zone_options |= DNS_ZONEOPT_WARNMXCNAME;
308 			zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME;
309 		} else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
310 			zone_options &= ~DNS_ZONEOPT_WARNMXCNAME;
311 			zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME;
312 		} else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
313 			zone_options |= DNS_ZONEOPT_WARNMXCNAME;
314 			zone_options |= DNS_ZONEOPT_IGNOREMXCNAME;
315 		} else
316 			INSIST(0);
317 	} else {
318 		zone_options |= DNS_ZONEOPT_WARNMXCNAME;
319 		zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME;
320 	}
321 
322 	obj = NULL;
323 	if (get_maps(maps, "check-srv-cname", &obj)) {
324 		if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
325 			zone_options |= DNS_ZONEOPT_WARNSRVCNAME;
326 			zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME;
327 		} else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
328 			zone_options &= ~DNS_ZONEOPT_WARNSRVCNAME;
329 			zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME;
330 		} else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
331 			zone_options |= DNS_ZONEOPT_WARNSRVCNAME;
332 			zone_options |= DNS_ZONEOPT_IGNORESRVCNAME;
333 		} else
334 			INSIST(0);
335 	} else {
336 		zone_options |= DNS_ZONEOPT_WARNSRVCNAME;
337 		zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME;
338 	}
339 
340 	obj = NULL;
341 	if (get_maps(maps, "check-sibling", &obj)) {
342 		if (cfg_obj_asboolean(obj))
343 			zone_options |= DNS_ZONEOPT_CHECKSIBLING;
344 		else
345 			zone_options &= ~DNS_ZONEOPT_CHECKSIBLING;
346 	}
347 
348 	obj = NULL;
349 	if (get_maps(maps, "check-spf", &obj)) {
350 		if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
351 			zone_options |= DNS_ZONEOPT_CHECKSPF;
352 		} else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
353 			zone_options &= ~DNS_ZONEOPT_CHECKSPF;
354 		} else
355 			INSIST(0);
356 	} else {
357 		zone_options |= DNS_ZONEOPT_CHECKSPF;
358 	}
359 
360 	obj = NULL;
361 	if (get_checknames(maps, &obj)) {
362 		if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
363 			zone_options |= DNS_ZONEOPT_CHECKNAMES;
364 			zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL;
365 		} else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
366 			zone_options |= DNS_ZONEOPT_CHECKNAMES;
367 			zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL;
368 		} else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
369 			zone_options &= ~DNS_ZONEOPT_CHECKNAMES;
370 			zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL;
371 		} else
372 			INSIST(0);
373 	} else {
374 	       zone_options |= DNS_ZONEOPT_CHECKNAMES;
375 	       zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL;
376 	}
377 
378 	masterformat = dns_masterformat_text;
379 	fmtobj = NULL;
380 	if (get_maps(maps, "masterfile-format", &fmtobj)) {
381 		const char *masterformatstr = cfg_obj_asstring(fmtobj);
382 		if (strcasecmp(masterformatstr, "text") == 0)
383 			masterformat = dns_masterformat_text;
384 		else if (strcasecmp(masterformatstr, "raw") == 0)
385 			masterformat = dns_masterformat_raw;
386 		else if (strcasecmp(masterformatstr, "map") == 0)
387 			masterformat = dns_masterformat_map;
388 		else
389 			INSIST(0);
390 	}
391 
392 	obj = NULL;
393 	if (get_maps(maps, "max-zone-ttl", &obj)) {
394 		maxttl = cfg_obj_asuint32(obj);
395 		zone_options2 |= DNS_ZONEOPT2_CHECKTTL;
396 	}
397 
398 	result = load_zone(mctx, zname, zfile, masterformat,
399 			   zclass, maxttl, NULL);
400 	if (result != ISC_R_SUCCESS)
401 		fprintf(stderr, "%s/%s/%s: %s\n", view, zname, zclass,
402 			dns_result_totext(result));
403 	return (result);
404 }
405 
406 /*% configure a view */
407 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)408 configure_view(const char *vclass, const char *view, const cfg_obj_t *config,
409 	       const cfg_obj_t *vconfig, isc_mem_t *mctx)
410 {
411 	const cfg_listelt_t *element;
412 	const cfg_obj_t *voptions;
413 	const cfg_obj_t *zonelist;
414 	isc_result_t result = ISC_R_SUCCESS;
415 	isc_result_t tresult;
416 
417 	voptions = NULL;
418 	if (vconfig != NULL)
419 		voptions = cfg_tuple_get(vconfig, "options");
420 
421 	zonelist = NULL;
422 	if (voptions != NULL)
423 		(void)cfg_map_get(voptions, "zone", &zonelist);
424 	else
425 		(void)cfg_map_get(config, "zone", &zonelist);
426 
427 	for (element = cfg_list_first(zonelist);
428 	     element != NULL;
429 	     element = cfg_list_next(element))
430 	{
431 		const cfg_obj_t *zconfig = cfg_listelt_value(element);
432 		tresult = configure_zone(vclass, view, zconfig, vconfig,
433 					 config, mctx);
434 		if (tresult != ISC_R_SUCCESS)
435 			result = tresult;
436 	}
437 	return (result);
438 }
439 
440 
441 /*% load zones from the configuration */
442 static isc_result_t
load_zones_fromconfig(const cfg_obj_t * config,isc_mem_t * mctx)443 load_zones_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx) {
444 	const cfg_listelt_t *element;
445 	const cfg_obj_t *classobj;
446 	const cfg_obj_t *views;
447 	const cfg_obj_t *vconfig;
448 	const char *vclass;
449 	isc_result_t result = ISC_R_SUCCESS;
450 	isc_result_t tresult;
451 
452 	views = NULL;
453 
454 	(void)cfg_map_get(config, "view", &views);
455 	for (element = cfg_list_first(views);
456 	     element != NULL;
457 	     element = cfg_list_next(element))
458 	{
459 		const char *vname;
460 
461 		vclass = "IN";
462 		vconfig = cfg_listelt_value(element);
463 		if (vconfig != NULL) {
464 			classobj = cfg_tuple_get(vconfig, "class");
465 			if (cfg_obj_isstring(classobj))
466 				vclass = cfg_obj_asstring(classobj);
467 		}
468 		vname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name"));
469 		tresult = configure_view(vclass, vname, config, vconfig, mctx);
470 		if (tresult != ISC_R_SUCCESS)
471 			result = tresult;
472 	}
473 
474 	if (views == NULL) {
475 		tresult = configure_view("IN", "_default", config, NULL, mctx);
476 		if (tresult != ISC_R_SUCCESS)
477 			result = tresult;
478 	}
479 	return (result);
480 }
481 
482 static void
output(void * closure,const char * text,int textlen)483 output(void *closure, const char *text, int textlen) {
484 	UNUSED(closure);
485 	if (fwrite(text, 1, textlen, stdout) != (size_t)textlen) {
486 		perror("fwrite");
487 		exit(1);
488 	}
489 }
490 
491 /*% The main processing routine */
492 int
main(int argc,char ** argv)493 main(int argc, char **argv) {
494 	int c;
495 	cfg_parser_t *parser = NULL;
496 	cfg_obj_t *config = NULL;
497 	const char *conffile = NULL;
498 	isc_mem_t *mctx = NULL;
499 	isc_result_t result;
500 	int exit_status = 0;
501 	isc_entropy_t *ectx = NULL;
502 	isc_boolean_t load_zones = ISC_FALSE;
503 	isc_boolean_t print = ISC_FALSE;
504 	unsigned int flags = 0;
505 
506 	isc__mem_register();
507 	isc_commandline_errprint = ISC_FALSE;
508 
509 	/*
510 	 * Process memory debugging argument first.
511 	 */
512 #define CMDLINE_FLAGS "dhjm:t:pvxz"
513 	while ((c = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
514 		switch (c) {
515 		case 'm':
516 			if (strcasecmp(isc_commandline_argument, "record") == 0)
517 				isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
518 			if (strcasecmp(isc_commandline_argument, "trace") == 0)
519 				isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
520 			if (strcasecmp(isc_commandline_argument, "usage") == 0)
521 				isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
522 			if (strcasecmp(isc_commandline_argument, "size") == 0)
523 				isc_mem_debugging |= ISC_MEM_DEBUGSIZE;
524 			if (strcasecmp(isc_commandline_argument, "mctx") == 0)
525 				isc_mem_debugging |= ISC_MEM_DEBUGCTX;
526 			break;
527 		default:
528 			break;
529 		}
530 	}
531 	isc_commandline_reset = ISC_TRUE;
532 
533 	RUNTIME_CHECK(isc_mem_create(0, 0, &mctx) == ISC_R_SUCCESS);
534 
535 	while ((c = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != EOF) {
536 		switch (c) {
537 		case 'd':
538 			debug++;
539 			break;
540 
541 		case 'j':
542 			nomerge = ISC_FALSE;
543 			break;
544 
545 		case 'm':
546 			break;
547 
548 		case 't':
549 			result = isc_dir_chroot(isc_commandline_argument);
550 			if (result != ISC_R_SUCCESS) {
551 				fprintf(stderr, "isc_dir_chroot: %s\n",
552 					isc_result_totext(result));
553 				exit(1);
554 			}
555 			break;
556 
557 		case 'p':
558 			print = ISC_TRUE;
559 			break;
560 
561 		case 'v':
562 			printf(VERSION "\n");
563 			exit(0);
564 
565 		case 'x':
566 			flags |= CFG_PRINTER_XKEY;
567 			break;
568 
569 		case 'z':
570 			load_zones = ISC_TRUE;
571 			docheckmx = ISC_FALSE;
572 			docheckns = ISC_FALSE;
573 			dochecksrv = ISC_FALSE;
574 			break;
575 
576 		case '?':
577 			if (isc_commandline_option != '?')
578 				fprintf(stderr, "%s: invalid argument -%c\n",
579 					program, isc_commandline_option);
580 			/* FALLTHROUGH */
581 		case 'h':
582 			usage();
583 
584 		default:
585 			fprintf(stderr, "%s: unhandled option -%c\n",
586 				program, isc_commandline_option);
587 			exit(1);
588 		}
589 	}
590 
591 	if (((flags & CFG_PRINTER_XKEY) != 0) && !print) {
592 		fprintf(stderr, "%s: -x cannot be used without -p\n", program);
593 		exit(1);
594 	}
595 
596 	if (isc_commandline_index + 1 < argc)
597 		usage();
598 	if (argv[isc_commandline_index] != NULL)
599 		conffile = argv[isc_commandline_index];
600 	if (conffile == NULL || conffile[0] == '\0')
601 		conffile = NAMED_CONFFILE;
602 
603 #ifdef _WIN32
604 	InitSockets();
605 #endif
606 
607 	RUNTIME_CHECK(setup_logging(mctx, stdout, &logc) == ISC_R_SUCCESS);
608 
609 	RUNTIME_CHECK(isc_entropy_create(mctx, &ectx) == ISC_R_SUCCESS);
610 	RUNTIME_CHECK(isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE)
611 		      == ISC_R_SUCCESS);
612 
613 	dns_result_register();
614 
615 	RUNTIME_CHECK(cfg_parser_create(mctx, logc, &parser) == ISC_R_SUCCESS);
616 
617 	cfg_parser_setcallback(parser, directory_callback, NULL);
618 
619 	if (cfg_parse_file(parser, conffile, &cfg_type_namedconf, &config) !=
620 	    ISC_R_SUCCESS)
621 		exit(1);
622 
623 	result = bind9_check_namedconf(config, logc, mctx);
624 	if (result != ISC_R_SUCCESS)
625 		exit_status = 1;
626 
627 	if (result == ISC_R_SUCCESS && load_zones) {
628 		result = load_zones_fromconfig(config, mctx);
629 		if (result != ISC_R_SUCCESS)
630 			exit_status = 1;
631 	}
632 
633 	if (print && exit_status == 0)
634 		cfg_printx(config, flags, output, NULL);
635 	cfg_obj_destroy(parser, &config);
636 
637 	cfg_parser_destroy(&parser);
638 
639 	dns_name_destroy();
640 
641 	isc_log_destroy(&logc);
642 
643 	isc_hash_destroy();
644 	isc_entropy_detach(&ectx);
645 
646 	isc_mem_destroy(&mctx);
647 
648 #ifdef _WIN32
649 	DestroySockets();
650 #endif
651 
652 	return (exit_status);
653 }
654