1238106Sdes/*
2238106Sdes * checkconf/unbound-checkconf.c - config file checker for unbound.conf file.
3238106Sdes *
4238106Sdes * Copyright (c) 2007, NLnet Labs. All rights reserved.
5238106Sdes *
6238106Sdes * This software is open source.
7356345Scy *
8238106Sdes * Redistribution and use in source and binary forms, with or without
9238106Sdes * modification, are permitted provided that the following conditions
10238106Sdes * are met:
11356345Scy *
12238106Sdes * Redistributions of source code must retain the above copyright notice,
13238106Sdes * this list of conditions and the following disclaimer.
14356345Scy *
15238106Sdes * Redistributions in binary form must reproduce the above copyright notice,
16238106Sdes * this list of conditions and the following disclaimer in the documentation
17238106Sdes * and/or other materials provided with the distribution.
18356345Scy *
19238106Sdes * Neither the name of the NLNET LABS nor the names of its contributors may
20238106Sdes * be used to endorse or promote products derived from this software without
21238106Sdes * specific prior written permission.
22356345Scy *
23238106Sdes * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24266114Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25266114Sdes * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26266114Sdes * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27266114Sdes * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28266114Sdes * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29266114Sdes * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30266114Sdes * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31266114Sdes * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32266114Sdes * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33266114Sdes * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34238106Sdes */
35238106Sdes
36238106Sdes/**
37238106Sdes * \file
38238106Sdes *
39238106Sdes * The config checker checks for syntax and other errors in the unbound.conf
40238106Sdes * file, and can be used to check for errors before the server is started
41238106Sdes * or sigHUPped.
42238106Sdes * Exit status 1 means an error.
43238106Sdes */
44238106Sdes
45238106Sdes#include "config.h"
46356345Scy#include <ctype.h>
47238106Sdes#include "util/log.h"
48238106Sdes#include "util/config_file.h"
49238106Sdes#include "util/module.h"
50238106Sdes#include "util/net_help.h"
51238106Sdes#include "util/regional.h"
52238106Sdes#include "iterator/iterator.h"
53238106Sdes#include "iterator/iter_fwd.h"
54238106Sdes#include "iterator/iter_hints.h"
55238106Sdes#include "validator/validator.h"
56238106Sdes#include "services/localzone.h"
57356345Scy#include "services/view.h"
58356345Scy#include "services/authzone.h"
59356345Scy#include "respip/respip.h"
60287917Sdes#include "sldns/sbuffer.h"
61366095Scy#include "sldns/str2wire.h"
62238106Sdes#ifdef HAVE_GETOPT_H
63238106Sdes#include <getopt.h>
64238106Sdes#endif
65238106Sdes#ifdef HAVE_PWD_H
66238106Sdes#include <pwd.h>
67238106Sdes#endif
68238106Sdes#ifdef HAVE_SYS_STAT_H
69238106Sdes#include <sys/stat.h>
70238106Sdes#endif
71238106Sdes#ifdef HAVE_GLOB_H
72238106Sdes#include <glob.h>
73238106Sdes#endif
74238106Sdes#ifdef WITH_PYTHONMODULE
75238106Sdes#include "pythonmod/pythonmod.h"
76238106Sdes#endif
77356345Scy#ifdef CLIENT_SUBNET
78356345Scy#include "edns-subnet/subnet-whitelist.h"
79356345Scy#endif
80238106Sdes
81238106Sdes/** Give checkconf usage, and exit (1). */
82238106Sdesstatic void
83307729Sdesusage(void)
84238106Sdes{
85356345Scy	printf("Usage:	local-unbound-checkconf [file]\n");
86238106Sdes	printf("	Checks unbound configuration file for errors.\n");
87238106Sdes	printf("file	if omitted %s is used.\n", CONFIGFILE);
88238106Sdes	printf("-o option	print value of option to stdout.\n");
89282089Sdes	printf("-f 		output full pathname with chroot applied, eg. with -o pidfile.\n");
90238106Sdes	printf("-h		show this usage help.\n");
91238106Sdes	printf("Version %s\n", PACKAGE_VERSION);
92238106Sdes	printf("BSD licensed, see LICENSE in source package for details.\n");
93238106Sdes	printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
94238106Sdes	exit(1);
95238106Sdes}
96238106Sdes
97356345Scy/**
98356345Scy * Print given option to stdout
99238106Sdes * @param cfg: config
100356345Scy * @param opt: option name without trailing :.
101238106Sdes *	This is different from config_set_option.
102282089Sdes * @param final: if final pathname with chroot applied has to be printed.
103238106Sdes */
104238106Sdesstatic void
105282089Sdesprint_option(struct config_file* cfg, const char* opt, int final)
106238106Sdes{
107282089Sdes	if(strcmp(opt, "pidfile") == 0 && final) {
108356345Scy		char *p = fname_after_chroot(cfg->pidfile, cfg, 1);
109356345Scy		if(!p) fatal_exit("out of memory");
110356345Scy		printf("%s\n", p);
111356345Scy		free(p);
112282089Sdes		return;
113282089Sdes	}
114356345Scy	if(strcmp(opt, "auto-trust-anchor-file") == 0 && final) {
115356345Scy		struct config_strlist* s = cfg->auto_trust_anchor_file_list;
116356345Scy		for(; s; s=s->next) {
117356345Scy			char *p = fname_after_chroot(s->str, cfg, 1);
118356345Scy			if(!p) fatal_exit("out of memory");
119356345Scy			printf("%s\n", p);
120356345Scy			free(p);
121356345Scy		}
122356345Scy		return;
123356345Scy	}
124238106Sdes	if(!config_get_option(cfg, opt, config_print_func, stdout))
125238106Sdes		fatal_exit("cannot print option '%s'", opt);
126238106Sdes}
127238106Sdes
128238106Sdes/** check if module works with config */
129238106Sdesstatic void
130238106Sdescheck_mod(struct config_file* cfg, struct module_func_block* fb)
131238106Sdes{
132238106Sdes	struct module_env env;
133238106Sdes	memset(&env, 0, sizeof(env));
134238106Sdes	env.cfg = cfg;
135238106Sdes	env.scratch = regional_create();
136266114Sdes	env.scratch_buffer = sldns_buffer_new(BUFSIZ);
137238106Sdes	if(!env.scratch || !env.scratch_buffer)
138238106Sdes		fatal_exit("out of memory");
139356345Scy	if(!edns_known_options_init(&env))
140356345Scy		fatal_exit("out of memory");
141238106Sdes	if(!(*fb->init)(&env, 0)) {
142238106Sdes		fatal_exit("bad config for %s module", fb->name);
143238106Sdes	}
144238106Sdes	(*fb->deinit)(&env, 0);
145266114Sdes	sldns_buffer_free(env.scratch_buffer);
146238106Sdes	regional_destroy(env.scratch);
147356345Scy	edns_known_options_delete(&env);
148238106Sdes}
149238106Sdes
150356345Scy/** true if addr is a localhost address, 127.0.0.1 or ::1 (with maybe "@port"
151356345Scy * after it) */
152356345Scystatic int
153356345Scystr_addr_is_localhost(const char* a)
154356345Scy{
155356345Scy	if(strncmp(a, "127.", 4) == 0) return 1;
156356345Scy	if(strncmp(a, "::1", 3) == 0) return 1;
157356345Scy	return 0;
158356345Scy}
159356345Scy
160356345Scy/** check do-not-query-localhost */
161356345Scystatic void
162356345Scydonotquerylocalhostcheck(struct config_file* cfg)
163356345Scy{
164356345Scy	if(cfg->donotquery_localhost) {
165356345Scy		struct config_stub* p;
166356345Scy		struct config_strlist* s;
167356345Scy		for(p=cfg->forwards; p; p=p->next) {
168356345Scy			for(s=p->addrs; s; s=s->next) {
169356345Scy				if(str_addr_is_localhost(s->str)) {
170356345Scy					fprintf(stderr, "unbound-checkconf: warning: forward-addr: '%s' is specified for forward-zone: '%s', but do-not-query-localhost: yes means that the address will not be used for lookups.\n",
171356345Scy						s->str, p->name);
172356345Scy				}
173356345Scy			}
174356345Scy		}
175356345Scy		for(p=cfg->stubs; p; p=p->next) {
176356345Scy			for(s=p->addrs; s; s=s->next) {
177356345Scy				if(str_addr_is_localhost(s->str)) {
178356345Scy					fprintf(stderr, "unbound-checkconf: warning: stub-addr: '%s' is specified for stub-zone: '%s', but do-not-query-localhost: yes means that the address will not be used for lookups.\n",
179356345Scy						s->str, p->name);
180356345Scy				}
181356345Scy			}
182356345Scy		}
183356345Scy	}
184356345Scy}
185356345Scy
186238106Sdes/** check localzones */
187238106Sdesstatic void
188238106Sdeslocalzonechecks(struct config_file* cfg)
189238106Sdes{
190238106Sdes	struct local_zones* zs;
191238106Sdes	if(!(zs = local_zones_create()))
192238106Sdes		fatal_exit("out of memory");
193238106Sdes	if(!local_zones_apply_cfg(zs, cfg))
194238106Sdes		fatal_exit("failed local-zone, local-data configuration");
195238106Sdes	local_zones_delete(zs);
196238106Sdes}
197238106Sdes
198366095Scy/** checks for acl and views */
199366095Scystatic void
200366095Scyacl_view_tag_checks(struct config_file* cfg, struct views* views)
201366095Scy{
202366095Scy	int d;
203366095Scy	struct sockaddr_storage a;
204366095Scy	socklen_t alen;
205366095Scy	struct config_str2list* acl;
206366095Scy	struct config_str3list* s3;
207366095Scy	struct config_strbytelist* sb;
208366095Scy
209366095Scy	/* acl_view */
210366095Scy	for(acl=cfg->acl_view; acl; acl = acl->next) {
211366095Scy		struct view* v;
212366095Scy		if(!netblockstrtoaddr(acl->str, UNBOUND_DNS_PORT, &a, &alen,
213366095Scy			&d)) {
214366095Scy			fatal_exit("cannot parse access-control-view "
215366095Scy				"address %s %s", acl->str, acl->str2);
216366095Scy		}
217366095Scy		v = views_find_view(views, acl->str2, 0);
218366095Scy		if(!v) {
219366095Scy			fatal_exit("cannot find view for "
220366095Scy				"access-control-view: %s %s",
221366095Scy				acl->str, acl->str2);
222366095Scy		}
223366095Scy		lock_rw_unlock(&v->lock);
224366095Scy	}
225366095Scy
226366095Scy	/* acl_tags */
227366095Scy	for(sb=cfg->acl_tags; sb; sb = sb->next) {
228366095Scy		if(!netblockstrtoaddr(sb->str, UNBOUND_DNS_PORT, &a, &alen,
229366095Scy			&d)) {
230366095Scy			fatal_exit("cannot parse access-control-tags "
231366095Scy				"address %s", sb->str);
232366095Scy		}
233366095Scy	}
234366095Scy
235366095Scy	/* acl_tag_actions */
236366095Scy	for(s3=cfg->acl_tag_actions; s3; s3 = s3->next) {
237366095Scy		enum localzone_type t;
238366095Scy		if(!netblockstrtoaddr(s3->str, UNBOUND_DNS_PORT, &a, &alen,
239366095Scy			&d)) {
240366095Scy			fatal_exit("cannot parse access-control-tag-actions "
241366095Scy				"address %s %s %s",
242366095Scy				s3->str, s3->str2, s3->str3);
243366095Scy		}
244366095Scy		if(find_tag_id(cfg, s3->str2) == -1) {
245366095Scy			fatal_exit("cannot parse tag %s (define-tag it), "
246366095Scy				"for access-control-tag-actions: %s %s %s",
247366095Scy				s3->str2, s3->str, s3->str2, s3->str3);
248366095Scy		}
249366095Scy		if(!local_zone_str2type(s3->str3, &t)) {
250366095Scy			fatal_exit("cannot parse access control action type %s"
251366095Scy				" for access-control-tag-actions: %s %s %s",
252366095Scy				s3->str3, s3->str, s3->str2, s3->str3);
253366095Scy		}
254366095Scy	}
255366095Scy
256366095Scy	/* acl_tag_datas */
257366095Scy	for(s3=cfg->acl_tag_datas; s3; s3 = s3->next) {
258366095Scy		char buf[65536];
259366095Scy		uint8_t rr[LDNS_RR_BUF_SIZE];
260366095Scy		size_t len = sizeof(rr);
261366095Scy		int res;
262366095Scy		if(!netblockstrtoaddr(s3->str, UNBOUND_DNS_PORT, &a, &alen,
263366095Scy			&d)) {
264366095Scy			fatal_exit("cannot parse access-control-tag-datas address %s %s '%s'",
265366095Scy				s3->str, s3->str2, s3->str3);
266366095Scy		}
267366095Scy		if(find_tag_id(cfg, s3->str2) == -1) {
268366095Scy			fatal_exit("cannot parse tag %s (define-tag it), "
269366095Scy				"for access-control-tag-datas: %s %s '%s'",
270366095Scy				s3->str2, s3->str, s3->str2, s3->str3);
271366095Scy		}
272366095Scy		/* '.' is sufficient for validation, and it makes the call to
273366095Scy		 * sldns_wirerr_get_type() simpler below. */
274366095Scy		snprintf(buf, sizeof(buf), "%s %s", ".", s3->str3);
275366095Scy		res = sldns_str2wire_rr_buf(buf, rr, &len, NULL, 3600, NULL,
276366095Scy			0, NULL, 0);
277366095Scy		if(res != 0) {
278366095Scy			fatal_exit("cannot parse rr data [char %d] parse error %s, for access-control-tag-datas: %s %s '%s'",
279366095Scy				(int)LDNS_WIREPARSE_OFFSET(res)-2,
280366095Scy				sldns_get_errorstr_parse(res),
281366095Scy				s3->str, s3->str2, s3->str3);
282366095Scy		}
283366095Scy	}
284366095Scy}
285366095Scy
286356345Scy/** check view and response-ip configuration */
287356345Scystatic void
288356345Scyview_and_respipchecks(struct config_file* cfg)
289356345Scy{
290356345Scy	struct views* views = NULL;
291356345Scy	struct respip_set* respip = NULL;
292356345Scy	int ignored = 0;
293356345Scy	if(!(views = views_create()))
294356345Scy		fatal_exit("Could not create views: out of memory");
295356345Scy	if(!(respip = respip_set_create()))
296356345Scy		fatal_exit("Could not create respip set: out of memory");
297356345Scy	if(!views_apply_cfg(views, cfg))
298356345Scy		fatal_exit("Could not set up views");
299356345Scy	if(!respip_global_apply_cfg(respip, cfg))
300356345Scy		fatal_exit("Could not setup respip set");
301356345Scy	if(!respip_views_apply_cfg(views, cfg, &ignored))
302356345Scy		fatal_exit("Could not setup per-view respip sets");
303366095Scy	acl_view_tag_checks(cfg, views);
304356345Scy	views_delete(views);
305356345Scy	respip_set_delete(respip);
306356345Scy}
307356345Scy
308238106Sdes/** emit warnings for IP in hosts */
309238106Sdesstatic void
310238106Sdeswarn_hosts(const char* typ, struct config_stub* list)
311238106Sdes{
312238106Sdes	struct sockaddr_storage a;
313238106Sdes	socklen_t alen;
314238106Sdes	struct config_stub* s;
315238106Sdes	struct config_strlist* h;
316238106Sdes	for(s=list; s; s=s->next) {
317238106Sdes		for(h=s->hosts; h; h=h->next) {
318238106Sdes			if(extstrtoaddr(h->str, &a, &alen)) {
319238106Sdes				fprintf(stderr, "unbound-checkconf: warning:"
320238106Sdes				  " %s %s: \"%s\" is an IP%s address, "
321238106Sdes				  "and when looked up as a host name "
322356345Scy				  "during use may not resolve.\n",
323238106Sdes				  s->name, typ, h->str,
324238106Sdes				  addr_is_ip6(&a, alen)?"6":"4");
325238106Sdes			}
326238106Sdes		}
327238106Sdes	}
328238106Sdes}
329238106Sdes
330238106Sdes/** check interface strings */
331238106Sdesstatic void
332238106Sdesinterfacechecks(struct config_file* cfg)
333238106Sdes{
334307729Sdes	int d;
335238106Sdes	struct sockaddr_storage a;
336238106Sdes	socklen_t alen;
337238106Sdes	int i, j;
338238106Sdes	for(i=0; i<cfg->num_ifs; i++) {
339238106Sdes		if(!extstrtoaddr(cfg->ifs[i], &a, &alen)) {
340238106Sdes			fatal_exit("cannot parse interface specified as '%s'",
341238106Sdes				cfg->ifs[i]);
342238106Sdes		}
343238106Sdes		for(j=0; j<cfg->num_ifs; j++) {
344238106Sdes			if(i!=j && strcmp(cfg->ifs[i], cfg->ifs[j])==0)
345238106Sdes				fatal_exit("interface: %s present twice, "
346238106Sdes					"cannot bind same ports twice.",
347238106Sdes					cfg->ifs[i]);
348238106Sdes		}
349238106Sdes	}
350238106Sdes	for(i=0; i<cfg->num_out_ifs; i++) {
351307729Sdes		if(!ipstrtoaddr(cfg->out_ifs[i], UNBOUND_DNS_PORT, &a, &alen) &&
352307729Sdes		   !netblockstrtoaddr(cfg->out_ifs[i], UNBOUND_DNS_PORT, &a, &alen, &d)) {
353238106Sdes			fatal_exit("cannot parse outgoing-interface "
354238106Sdes				"specified as '%s'", cfg->out_ifs[i]);
355238106Sdes		}
356238106Sdes		for(j=0; j<cfg->num_out_ifs; j++) {
357238106Sdes			if(i!=j && strcmp(cfg->out_ifs[i], cfg->out_ifs[j])==0)
358238106Sdes				fatal_exit("outgoing-interface: %s present "
359238106Sdes					"twice, cannot bind same ports twice.",
360238106Sdes					cfg->out_ifs[i]);
361238106Sdes		}
362238106Sdes	}
363238106Sdes}
364238106Sdes
365238106Sdes/** check acl ips */
366238106Sdesstatic void
367238106Sdesaclchecks(struct config_file* cfg)
368238106Sdes{
369238106Sdes	int d;
370238106Sdes	struct sockaddr_storage a;
371238106Sdes	socklen_t alen;
372238106Sdes	struct config_str2list* acl;
373238106Sdes	for(acl=cfg->acls; acl; acl = acl->next) {
374356345Scy		if(!netblockstrtoaddr(acl->str, UNBOUND_DNS_PORT, &a, &alen,
375238106Sdes			&d)) {
376238106Sdes			fatal_exit("cannot parse access control address %s %s",
377238106Sdes				acl->str, acl->str2);
378238106Sdes		}
379238106Sdes	}
380238106Sdes}
381238106Sdes
382356345Scy/** check tcp connection limit ips */
383356345Scystatic void
384356345Scytcpconnlimitchecks(struct config_file* cfg)
385356345Scy{
386356345Scy	int d;
387356345Scy	struct sockaddr_storage a;
388356345Scy	socklen_t alen;
389356345Scy	struct config_str2list* tcl;
390356345Scy	for(tcl=cfg->tcp_connection_limits; tcl; tcl = tcl->next) {
391356345Scy		if(!netblockstrtoaddr(tcl->str, UNBOUND_DNS_PORT, &a, &alen,
392356345Scy			&d)) {
393356345Scy			fatal_exit("cannot parse tcp connection limit address %s %s",
394356345Scy				tcl->str, tcl->str2);
395356345Scy		}
396356345Scy	}
397356345Scy}
398356345Scy
399238106Sdes/** true if fname is a file */
400238106Sdesstatic int
401356345Scyis_file(const char* fname)
402238106Sdes{
403238106Sdes	struct stat buf;
404238106Sdes	if(stat(fname, &buf) < 0) {
405238106Sdes		if(errno==EACCES) {
406238106Sdes			printf("warning: no search permission for one of the directories in path: %s\n", fname);
407238106Sdes			return 1;
408238106Sdes		}
409238106Sdes		perror(fname);
410238106Sdes		return 0;
411238106Sdes	}
412238106Sdes	if(S_ISDIR(buf.st_mode)) {
413238106Sdes		printf("%s is not a file\n", fname);
414238106Sdes		return 0;
415238106Sdes	}
416238106Sdes	return 1;
417238106Sdes}
418238106Sdes
419238106Sdes/** true if fname is a directory */
420238106Sdesstatic int
421356345Scyis_dir(const char* fname)
422238106Sdes{
423238106Sdes	struct stat buf;
424238106Sdes	if(stat(fname, &buf) < 0) {
425238106Sdes		if(errno==EACCES) {
426238106Sdes			printf("warning: no search permission for one of the directories in path: %s\n", fname);
427238106Sdes			return 1;
428238106Sdes		}
429238106Sdes		perror(fname);
430238106Sdes		return 0;
431238106Sdes	}
432238106Sdes	if(!(S_ISDIR(buf.st_mode))) {
433238106Sdes		printf("%s is not a directory\n", fname);
434238106Sdes		return 0;
435238106Sdes	}
436238106Sdes	return 1;
437238106Sdes}
438238106Sdes
439238106Sdes/** get base dir of a fname */
440238106Sdesstatic char*
441238106Sdesbasedir(char* fname)
442238106Sdes{
443238106Sdes	char* rev;
444238106Sdes	if(!fname) fatal_exit("out of memory");
445238106Sdes	rev = strrchr(fname, '/');
446238106Sdes	if(!rev) return NULL;
447238106Sdes	if(fname == rev) return NULL;
448238106Sdes	rev[0] = 0;
449238106Sdes	return fname;
450238106Sdes}
451238106Sdes
452238106Sdes/** check chroot for a file string */
453238106Sdesstatic void
454238106Sdescheck_chroot_string(const char* desc, char** ss,
455238106Sdes	const char* chrootdir, struct config_file* cfg)
456238106Sdes{
457238106Sdes	char* str = *ss;
458238106Sdes	if(str && str[0]) {
459238106Sdes		*ss = fname_after_chroot(str, cfg, 1);
460238106Sdes		if(!*ss) fatal_exit("out of memory");
461238106Sdes		if(!is_file(*ss)) {
462238106Sdes			if(chrootdir && chrootdir[0])
463238106Sdes				fatal_exit("%s: \"%s\" does not exist in "
464238106Sdes					"chrootdir %s", desc, str, chrootdir);
465238106Sdes			else
466356345Scy				fatal_exit("%s: \"%s\" does not exist",
467238106Sdes					desc, str);
468238106Sdes		}
469238106Sdes		/* put in a new full path for continued checking */
470238106Sdes		free(str);
471238106Sdes	}
472238106Sdes}
473238106Sdes
474238106Sdes/** check file list, every file must be inside the chroot location */
475238106Sdesstatic void
476238106Sdescheck_chroot_filelist(const char* desc, struct config_strlist* list,
477238106Sdes	const char* chrootdir, struct config_file* cfg)
478238106Sdes{
479238106Sdes	struct config_strlist* p;
480238106Sdes	for(p=list; p; p=p->next) {
481238106Sdes		check_chroot_string(desc, &p->str, chrootdir, cfg);
482238106Sdes	}
483238106Sdes}
484238106Sdes
485238106Sdes/** check file list, with wildcard processing */
486238106Sdesstatic void
487238106Sdescheck_chroot_filelist_wild(const char* desc, struct config_strlist* list,
488238106Sdes	const char* chrootdir, struct config_file* cfg)
489238106Sdes{
490238106Sdes	struct config_strlist* p;
491238106Sdes	for(p=list; p; p=p->next) {
492238106Sdes#ifdef HAVE_GLOB
493356345Scy		if(strchr(p->str, '*') || strchr(p->str, '[') ||
494356345Scy			strchr(p->str, '?') || strchr(p->str, '{') ||
495238106Sdes			strchr(p->str, '~')) {
496238106Sdes			char* s = p->str;
497238106Sdes			/* adjust whole pattern for chroot and check later */
498238106Sdes			p->str = fname_after_chroot(p->str, cfg, 1);
499238106Sdes			free(s);
500238106Sdes		} else
501238106Sdes#endif /* HAVE_GLOB */
502238106Sdes			check_chroot_string(desc, &p->str, chrootdir, cfg);
503238106Sdes	}
504238106Sdes}
505238106Sdes
506356345Scy#ifdef CLIENT_SUBNET
507356345Scy/** check ECS configuration */
508356345Scystatic void
509356345Scyecs_conf_checks(struct config_file* cfg)
510356345Scy{
511356345Scy	struct ecs_whitelist* whitelist = NULL;
512356345Scy	if(!(whitelist = ecs_whitelist_create()))
513356345Scy		fatal_exit("Could not create ednssubnet whitelist: out of memory");
514356345Scy        if(!ecs_whitelist_apply_cfg(whitelist, cfg))
515356345Scy		fatal_exit("Could not setup ednssubnet whitelist");
516356345Scy	ecs_whitelist_delete(whitelist);
517356345Scy}
518356345Scy#endif /* CLIENT_SUBNET */
519356345Scy
520356345Scy/** check that the modules exist, are compiled in */
521356345Scystatic void
522356345Scycheck_modules_exist(const char* module_conf)
523356345Scy{
524356345Scy	const char** names = module_list_avail();
525356345Scy	const char* s = module_conf;
526356345Scy	while(*s) {
527356345Scy		int i = 0;
528356345Scy		int is_ok = 0;
529356345Scy		while(*s && isspace((unsigned char)*s))
530356345Scy			s++;
531356345Scy		if(!*s) break;
532356345Scy		while(names[i]) {
533356345Scy			if(strncmp(names[i], s, strlen(names[i])) == 0) {
534356345Scy				is_ok = 1;
535356345Scy				break;
536356345Scy			}
537356345Scy			i++;
538356345Scy		}
539356345Scy		if(is_ok == 0) {
540356345Scy			char n[64];
541356345Scy			size_t j;
542356345Scy			n[0]=0;
543356345Scy			n[sizeof(n)-1]=0;
544356345Scy			for(j=0; j<sizeof(n)-1; j++) {
545356345Scy				if(!s[j] || isspace((unsigned char)s[j])) {
546356345Scy					n[j] = 0;
547356345Scy					break;
548356345Scy				}
549356345Scy				n[j] = s[j];
550356345Scy			}
551356345Scy			fatal_exit("module_conf lists module '%s' but that "
552356345Scy				"module is not available.", n);
553356345Scy		}
554356345Scy		s += strlen(names[i]);
555356345Scy	}
556356345Scy}
557356345Scy
558238106Sdes/** check configuration for errors */
559238106Sdesstatic void
560356345Scymorechecks(struct config_file* cfg)
561238106Sdes{
562238106Sdes	warn_hosts("stub-host", cfg->stubs);
563238106Sdes	warn_hosts("forward-host", cfg->forwards);
564238106Sdes	interfacechecks(cfg);
565238106Sdes	aclchecks(cfg);
566356345Scy	tcpconnlimitchecks(cfg);
567238106Sdes
568238106Sdes	if(cfg->verbosity < 0)
569238106Sdes		fatal_exit("verbosity value < 0");
570238106Sdes	if(cfg->num_threads <= 0 || cfg->num_threads > 10000)
571238106Sdes		fatal_exit("num_threads value weird");
572238106Sdes	if(!cfg->do_ip4 && !cfg->do_ip6)
573238106Sdes		fatal_exit("ip4 and ip6 are both disabled, pointless");
574366095Scy	if(!cfg->do_ip4 && cfg->prefer_ip4)
575366095Scy		fatal_exit("cannot prefer and disable ip4, pointless");
576307729Sdes	if(!cfg->do_ip6 && cfg->prefer_ip6)
577307729Sdes		fatal_exit("cannot prefer and disable ip6, pointless");
578238106Sdes	if(!cfg->do_udp && !cfg->do_tcp)
579238106Sdes		fatal_exit("udp and tcp are both disabled, pointless");
580238106Sdes	if(cfg->edns_buffer_size > cfg->msg_buffer_size)
581238106Sdes		fatal_exit("edns-buffer-size larger than msg-buffer-size, "
582238106Sdes			"answers will not fit in processing buffer");
583292206Sdes#ifdef UB_ON_WINDOWS
584292206Sdes	w_config_adjust_directory(cfg);
585292206Sdes#endif
586356345Scy	if(cfg->chrootdir && cfg->chrootdir[0] &&
587238106Sdes		cfg->chrootdir[strlen(cfg->chrootdir)-1] == '/')
588238106Sdes		fatal_exit("chootdir %s has trailing slash '/' please remove.",
589238106Sdes			cfg->chrootdir);
590356345Scy	if(cfg->chrootdir && cfg->chrootdir[0] &&
591238106Sdes		!is_dir(cfg->chrootdir)) {
592238106Sdes		fatal_exit("bad chroot directory");
593238106Sdes	}
594238106Sdes	if(cfg->directory && cfg->directory[0]) {
595238106Sdes		char* ad = fname_after_chroot(cfg->directory, cfg, 0);
596238106Sdes		if(!ad) fatal_exit("out of memory");
597238106Sdes		if(!is_dir(ad)) fatal_exit("bad chdir directory");
598238106Sdes		free(ad);
599238106Sdes	}
600238106Sdes	if( (cfg->chrootdir && cfg->chrootdir[0]) ||
601238106Sdes	    (cfg->directory && cfg->directory[0])) {
602238106Sdes		if(cfg->pidfile && cfg->pidfile[0]) {
603238106Sdes			char* ad = (cfg->pidfile[0]=='/')?strdup(cfg->pidfile):
604238106Sdes				fname_after_chroot(cfg->pidfile, cfg, 1);
605238106Sdes			char* bd = basedir(ad);
606238106Sdes			if(bd && !is_dir(bd))
607238106Sdes				fatal_exit("pidfile directory does not exist");
608238106Sdes			free(ad);
609238106Sdes		}
610238106Sdes		if(cfg->logfile && cfg->logfile[0]) {
611238106Sdes			char* ad = fname_after_chroot(cfg->logfile, cfg, 1);
612238106Sdes			char* bd = basedir(ad);
613238106Sdes			if(bd && !is_dir(bd))
614238106Sdes				fatal_exit("logfile directory does not exist");
615238106Sdes			free(ad);
616238106Sdes		}
617238106Sdes	}
618238106Sdes
619356345Scy	check_chroot_filelist("file with root-hints",
620238106Sdes		cfg->root_hints, cfg->chrootdir, cfg);
621356345Scy	check_chroot_filelist("trust-anchor-file",
622238106Sdes		cfg->trust_anchor_file_list, cfg->chrootdir, cfg);
623356345Scy	check_chroot_filelist("auto-trust-anchor-file",
624238106Sdes		cfg->auto_trust_anchor_file_list, cfg->chrootdir, cfg);
625356345Scy	check_chroot_filelist_wild("trusted-keys-file",
626238106Sdes		cfg->trusted_keys_file_list, cfg->chrootdir, cfg);
627356345Scy#ifdef USE_IPSECMOD
628356345Scy	if(cfg->ipsecmod_enabled && strstr(cfg->module_conf, "ipsecmod")) {
629356345Scy		/* only check hook if enabled */
630356345Scy		check_chroot_string("ipsecmod-hook", &cfg->ipsecmod_hook,
631356345Scy			cfg->chrootdir, cfg);
632356345Scy	}
633356345Scy#endif
634238106Sdes	/* remove chroot setting so that modules are not stripping pathnames*/
635238106Sdes	free(cfg->chrootdir);
636238106Sdes	cfg->chrootdir = NULL;
637356345Scy
638356345Scy	/* check that the modules listed in module_conf exist */
639356345Scy	check_modules_exist(cfg->module_conf);
640356345Scy
641361435Scy	/* Respip is known to *not* work with dns64. */
642356345Scy	if(strcmp(cfg->module_conf, "iterator") != 0
643238106Sdes		&& strcmp(cfg->module_conf, "validator iterator") != 0
644276605Sdes		&& strcmp(cfg->module_conf, "dns64 validator iterator") != 0
645276605Sdes		&& strcmp(cfg->module_conf, "dns64 iterator") != 0
646356345Scy		&& strcmp(cfg->module_conf, "respip iterator") != 0
647356345Scy		&& strcmp(cfg->module_conf, "respip validator iterator") != 0
648238106Sdes#ifdef WITH_PYTHONMODULE
649356345Scy		&& strcmp(cfg->module_conf, "python iterator") != 0
650361435Scy		&& strcmp(cfg->module_conf, "python respip iterator") != 0
651356345Scy		&& strcmp(cfg->module_conf, "python validator iterator") != 0
652361435Scy		&& strcmp(cfg->module_conf, "python respip validator iterator") != 0
653238106Sdes		&& strcmp(cfg->module_conf, "validator python iterator") != 0
654356345Scy		&& strcmp(cfg->module_conf, "dns64 python iterator") != 0
655356345Scy		&& strcmp(cfg->module_conf, "dns64 python validator iterator") != 0
656276605Sdes		&& strcmp(cfg->module_conf, "dns64 validator python iterator") != 0
657356345Scy		&& strcmp(cfg->module_conf, "python dns64 iterator") != 0
658356345Scy		&& strcmp(cfg->module_conf, "python dns64 validator iterator") != 0
659238106Sdes#endif
660366095Scy#ifdef WITH_DYNLIBMODULE
661366095Scy		&& strcmp(cfg->module_conf, "dynlib iterator") != 0
662366095Scy		&& strcmp(cfg->module_conf, "dynlib dynlib iterator") != 0
663366095Scy		&& strcmp(cfg->module_conf, "dynlib dynlib dynlib iterator") != 0
664366095Scy		&& strcmp(cfg->module_conf, "python dynlib iterator") != 0
665366095Scy		&& strcmp(cfg->module_conf, "python dynlib dynlib iterator") != 0
666366095Scy		&& strcmp(cfg->module_conf, "python dynlib dynlib dynlib iterator") != 0
667366095Scy		&& strcmp(cfg->module_conf, "dynlib respip iterator") != 0
668366095Scy		&& strcmp(cfg->module_conf, "dynlib validator iterator") != 0
669366095Scy		&& strcmp(cfg->module_conf, "dynlib dynlib validator iterator") != 0
670366095Scy		&& strcmp(cfg->module_conf, "dynlib dynlib dynlib validator iterator") != 0
671366095Scy		&& strcmp(cfg->module_conf, "python dynlib validator iterator") != 0
672366095Scy		&& strcmp(cfg->module_conf, "python dynlib dynlib validator iterator") != 0
673366095Scy		&& strcmp(cfg->module_conf, "python dynlib dynlib dynlib validator iterator") != 0
674366095Scy		&& strcmp(cfg->module_conf, "dynlib respip validator iterator") != 0
675366095Scy		&& strcmp(cfg->module_conf, "validator dynlib iterator") != 0
676366095Scy		&& strcmp(cfg->module_conf, "dns64 dynlib iterator") != 0
677366095Scy		&& strcmp(cfg->module_conf, "dns64 dynlib validator iterator") != 0
678366095Scy		&& strcmp(cfg->module_conf, "dns64 validator dynlib iterator") != 0
679366095Scy		&& strcmp(cfg->module_conf, "dynlib dns64 iterator") != 0
680366095Scy		&& strcmp(cfg->module_conf, "dynlib dns64 validator iterator") != 0
681366095Scy		&& strcmp(cfg->module_conf, "dynlib dns64 cachedb iterator") != 0
682366095Scy		&& strcmp(cfg->module_conf, "dynlib dns64 validator cachedb iterator") != 0
683366095Scy		&& strcmp(cfg->module_conf, "dns64 dynlib cachedb iterator") != 0
684366095Scy		&& strcmp(cfg->module_conf, "dns64 dynlib validator cachedb iterator") != 0
685366095Scy		&& strcmp(cfg->module_conf, "dynlib cachedb iterator") != 0
686366095Scy		&& strcmp(cfg->module_conf, "dynlib respip cachedb iterator") != 0
687366095Scy		&& strcmp(cfg->module_conf, "dynlib validator cachedb iterator") != 0
688366095Scy		&& strcmp(cfg->module_conf, "dynlib respip validator cachedb iterator") != 0
689366095Scy		&& strcmp(cfg->module_conf, "cachedb dynlib iterator") != 0
690366095Scy		&& strcmp(cfg->module_conf, "respip cachedb dynlib iterator") != 0
691366095Scy		&& strcmp(cfg->module_conf, "validator cachedb dynlib iterator") != 0
692366095Scy		&& strcmp(cfg->module_conf, "respip validator cachedb dynlib iterator") != 0
693366095Scy		&& strcmp(cfg->module_conf, "validator dynlib cachedb iterator") != 0
694366095Scy		&& strcmp(cfg->module_conf, "respip validator dynlib cachedb iterator") != 0
695366095Scy		&& strcmp(cfg->module_conf, "dynlib subnetcache iterator") != 0
696366095Scy		&& strcmp(cfg->module_conf, "dynlib respip subnetcache iterator") != 0
697366095Scy		&& strcmp(cfg->module_conf, "subnetcache dynlib iterator") != 0
698366095Scy		&& strcmp(cfg->module_conf, "respip subnetcache dynlib iterator") != 0
699366095Scy		&& strcmp(cfg->module_conf, "dynlib subnetcache validator iterator") != 0
700366095Scy		&& strcmp(cfg->module_conf, "dynlib respip subnetcache validator iterator") != 0
701366095Scy		&& strcmp(cfg->module_conf, "subnetcache dynlib validator iterator") != 0
702366095Scy		&& strcmp(cfg->module_conf, "respip subnetcache dynlib validator iterator") != 0
703366095Scy		&& strcmp(cfg->module_conf, "subnetcache validator dynlib iterator") != 0
704366095Scy		&& strcmp(cfg->module_conf, "respip subnetcache validator dynlib iterator") != 0
705366095Scy		&& strcmp(cfg->module_conf, "dynlib ipsecmod iterator") != 0
706366095Scy		&& strcmp(cfg->module_conf, "dynlib ipsecmod respip iterator") != 0
707366095Scy		&& strcmp(cfg->module_conf, "ipsecmod dynlib iterator") != 0
708366095Scy		&& strcmp(cfg->module_conf, "ipsecmod dynlib respip iterator") != 0
709366095Scy		&& strcmp(cfg->module_conf, "ipsecmod validator iterator") != 0
710366095Scy		&& strcmp(cfg->module_conf, "ipsecmod respip validator iterator") != 0
711366095Scy		&& strcmp(cfg->module_conf, "dynlib ipsecmod validator iterator") != 0
712366095Scy		&& strcmp(cfg->module_conf, "dynlib ipsecmod respip validator iterator") != 0
713366095Scy		&& strcmp(cfg->module_conf, "ipsecmod dynlib validator iterator") != 0
714366095Scy		&& strcmp(cfg->module_conf, "ipsecmod dynlib respip validator iterator") != 0
715366095Scy		&& strcmp(cfg->module_conf, "ipsecmod validator dynlib iterator") != 0
716366095Scy		&& strcmp(cfg->module_conf, "ipsecmod respip validator dynlib iterator") != 0
717366095Scy#endif
718307729Sdes#ifdef USE_CACHEDB
719307729Sdes		&& strcmp(cfg->module_conf, "validator cachedb iterator") != 0
720361435Scy		&& strcmp(cfg->module_conf, "respip validator cachedb iterator") != 0
721307729Sdes		&& strcmp(cfg->module_conf, "cachedb iterator") != 0
722361435Scy		&& strcmp(cfg->module_conf, "respip cachedb iterator") != 0
723307729Sdes		&& strcmp(cfg->module_conf, "dns64 validator cachedb iterator") != 0
724307729Sdes		&& strcmp(cfg->module_conf, "dns64 cachedb iterator") != 0
725356345Scy#endif
726356345Scy#if defined(WITH_PYTHONMODULE) && defined(USE_CACHEDB)
727307729Sdes		&& strcmp(cfg->module_conf, "python dns64 cachedb iterator") != 0
728307729Sdes		&& strcmp(cfg->module_conf, "python dns64 validator cachedb iterator") != 0
729307729Sdes		&& strcmp(cfg->module_conf, "dns64 python cachedb iterator") != 0
730307729Sdes		&& strcmp(cfg->module_conf, "dns64 python validator cachedb iterator") != 0
731307729Sdes		&& strcmp(cfg->module_conf, "python cachedb iterator") != 0
732361435Scy		&& strcmp(cfg->module_conf, "python respip cachedb iterator") != 0
733307729Sdes		&& strcmp(cfg->module_conf, "python validator cachedb iterator") != 0
734361435Scy		&& strcmp(cfg->module_conf, "python respip validator cachedb iterator") != 0
735307729Sdes		&& strcmp(cfg->module_conf, "cachedb python iterator") != 0
736361435Scy		&& strcmp(cfg->module_conf, "respip cachedb python iterator") != 0
737307729Sdes		&& strcmp(cfg->module_conf, "validator cachedb python iterator") != 0
738361435Scy		&& strcmp(cfg->module_conf, "respip validator cachedb python iterator") != 0
739307729Sdes		&& strcmp(cfg->module_conf, "validator python cachedb iterator") != 0
740361435Scy		&& strcmp(cfg->module_conf, "respip validator python cachedb iterator") != 0
741307729Sdes#endif
742356345Scy#ifdef CLIENT_SUBNET
743356345Scy		&& strcmp(cfg->module_conf, "subnetcache iterator") != 0
744361435Scy		&& strcmp(cfg->module_conf, "respip subnetcache iterator") != 0
745356345Scy		&& strcmp(cfg->module_conf, "subnetcache validator iterator") != 0
746361435Scy		&& strcmp(cfg->module_conf, "respip subnetcache validator iterator") != 0
747356345Scy		&& strcmp(cfg->module_conf, "dns64 subnetcache iterator") != 0
748356345Scy		&& strcmp(cfg->module_conf, "dns64 subnetcache validator iterator") != 0
749366095Scy		&& strcmp(cfg->module_conf, "dns64 subnetcache respip iterator") != 0
750366095Scy		&& strcmp(cfg->module_conf, "dns64 subnetcache respip validator iterator") != 0
751356345Scy#endif
752356345Scy#if defined(WITH_PYTHONMODULE) && defined(CLIENT_SUBNET)
753356345Scy		&& strcmp(cfg->module_conf, "python subnetcache iterator") != 0
754361435Scy		&& strcmp(cfg->module_conf, "python respip subnetcache iterator") != 0
755356345Scy		&& strcmp(cfg->module_conf, "subnetcache python iterator") != 0
756361435Scy		&& strcmp(cfg->module_conf, "respip subnetcache python iterator") != 0
757356345Scy		&& strcmp(cfg->module_conf, "python subnetcache validator iterator") != 0
758361435Scy		&& strcmp(cfg->module_conf, "python respip subnetcache validator iterator") != 0
759356345Scy		&& strcmp(cfg->module_conf, "subnetcache python validator iterator") != 0
760361435Scy		&& strcmp(cfg->module_conf, "respip subnetcache python validator iterator") != 0
761356345Scy		&& strcmp(cfg->module_conf, "subnetcache validator python iterator") != 0
762361435Scy		&& strcmp(cfg->module_conf, "respip subnetcache validator python iterator") != 0
763356345Scy#endif
764356345Scy#ifdef USE_IPSECMOD
765356345Scy		&& strcmp(cfg->module_conf, "ipsecmod iterator") != 0
766361435Scy		&& strcmp(cfg->module_conf, "ipsecmod respip iterator") != 0
767356345Scy		&& strcmp(cfg->module_conf, "ipsecmod validator iterator") != 0
768361435Scy		&& strcmp(cfg->module_conf, "ipsecmod respip validator iterator") != 0
769356345Scy#endif
770356345Scy#if defined(WITH_PYTHONMODULE) && defined(USE_IPSECMOD)
771356345Scy		&& strcmp(cfg->module_conf, "python ipsecmod iterator") != 0
772361435Scy		&& strcmp(cfg->module_conf, "python ipsecmod respip iterator") != 0
773356345Scy		&& strcmp(cfg->module_conf, "ipsecmod python iterator") != 0
774361435Scy		&& strcmp(cfg->module_conf, "ipsecmod python respip iterator") != 0
775356345Scy		&& strcmp(cfg->module_conf, "ipsecmod validator iterator") != 0
776361435Scy		&& strcmp(cfg->module_conf, "ipsecmod respip validator iterator") != 0
777356345Scy		&& strcmp(cfg->module_conf, "python ipsecmod validator iterator") != 0
778361435Scy		&& strcmp(cfg->module_conf, "python ipsecmod respip validator iterator") != 0
779356345Scy		&& strcmp(cfg->module_conf, "ipsecmod python validator iterator") != 0
780361435Scy		&& strcmp(cfg->module_conf, "ipsecmod python respip validator iterator") != 0
781356345Scy		&& strcmp(cfg->module_conf, "ipsecmod validator python iterator") != 0
782361435Scy		&& strcmp(cfg->module_conf, "ipsecmod respip validator python iterator") != 0
783356345Scy#endif
784356345Scy#ifdef USE_IPSET
785356345Scy		&& strcmp(cfg->module_conf, "validator ipset iterator") != 0
786361435Scy		&& strcmp(cfg->module_conf, "validator ipset respip iterator") != 0
787356345Scy		&& strcmp(cfg->module_conf, "ipset iterator") != 0
788361435Scy		&& strcmp(cfg->module_conf, "ipset respip iterator") != 0
789356345Scy#endif
790238106Sdes		) {
791238106Sdes		fatal_exit("module conf '%s' is not known to work",
792238106Sdes			cfg->module_conf);
793238106Sdes	}
794238106Sdes
795238106Sdes#ifdef HAVE_GETPWNAM
796238106Sdes	if(cfg->username && cfg->username[0]) {
797238106Sdes		if(getpwnam(cfg->username) == NULL)
798238106Sdes			fatal_exit("user '%s' does not exist.", cfg->username);
799307729Sdes#  ifdef HAVE_ENDPWENT
800238106Sdes		endpwent();
801307729Sdes#  endif
802238106Sdes	}
803238106Sdes#endif
804356345Scy	if(cfg->remote_control_enable && options_remote_is_address(cfg)
805356345Scy		&& cfg->control_use_cert) {
806238106Sdes		check_chroot_string("server-key-file", &cfg->server_key_file,
807238106Sdes			cfg->chrootdir, cfg);
808238106Sdes		check_chroot_string("server-cert-file", &cfg->server_cert_file,
809238106Sdes			cfg->chrootdir, cfg);
810238106Sdes		if(!is_file(cfg->control_key_file))
811238106Sdes			fatal_exit("control-key-file: \"%s\" does not exist",
812238106Sdes				cfg->control_key_file);
813238106Sdes		if(!is_file(cfg->control_cert_file))
814238106Sdes			fatal_exit("control-cert-file: \"%s\" does not exist",
815238106Sdes				cfg->control_cert_file);
816238106Sdes	}
817238106Sdes
818356345Scy	donotquerylocalhostcheck(cfg);
819238106Sdes	localzonechecks(cfg);
820356345Scy	view_and_respipchecks(cfg);
821356345Scy#ifdef CLIENT_SUBNET
822356345Scy	ecs_conf_checks(cfg);
823356345Scy#endif
824238106Sdes}
825238106Sdes
826238106Sdes/** check forwards */
827238106Sdesstatic void
828238106Sdescheck_fwd(struct config_file* cfg)
829238106Sdes{
830238106Sdes	struct iter_forwards* fwd = forwards_create();
831238106Sdes	if(!fwd || !forwards_apply_cfg(fwd, cfg)) {
832238106Sdes		fatal_exit("Could not set forward zones");
833238106Sdes	}
834238106Sdes	forwards_delete(fwd);
835238106Sdes}
836238106Sdes
837238106Sdes/** check hints */
838238106Sdesstatic void
839238106Sdescheck_hints(struct config_file* cfg)
840238106Sdes{
841238106Sdes	struct iter_hints* hints = hints_create();
842238106Sdes	if(!hints || !hints_apply_cfg(hints, cfg)) {
843238106Sdes		fatal_exit("Could not set root or stub hints");
844238106Sdes	}
845238106Sdes	hints_delete(hints);
846238106Sdes}
847238106Sdes
848356345Scy/** check auth zones */
849356345Scystatic void
850356345Scycheck_auth(struct config_file* cfg)
851356345Scy{
852361435Scy	int is_rpz = 0;
853356345Scy	struct auth_zones* az = auth_zones_create();
854366095Scy	if(!az || !auth_zones_apply_cfg(az, cfg, 0, &is_rpz)) {
855356345Scy		fatal_exit("Could not setup authority zones");
856356345Scy	}
857356345Scy	auth_zones_delete(az);
858356345Scy}
859356345Scy
860238106Sdes/** check config file */
861238106Sdesstatic void
862282089Sdescheckconf(const char* cfgfile, const char* opt, int final)
863238106Sdes{
864356345Scy	char oldwd[4096];
865238106Sdes	struct config_file* cfg = config_create();
866238106Sdes	if(!cfg)
867238106Sdes		fatal_exit("out of memory");
868307729Sdes	oldwd[0] = 0;
869307729Sdes	if(!getcwd(oldwd, sizeof(oldwd))) {
870307729Sdes		log_err("cannot getcwd: %s", strerror(errno));
871307729Sdes		oldwd[0] = 0;
872307729Sdes	}
873238106Sdes	if(!config_read(cfg, cfgfile, NULL)) {
874238106Sdes		/* config_read prints messages to stderr */
875238106Sdes		config_delete(cfg);
876238106Sdes		exit(1);
877238106Sdes	}
878307729Sdes	if(oldwd[0] && chdir(oldwd) == -1)
879307729Sdes		log_err("cannot chdir(%s): %s", oldwd, strerror(errno));
880276605Sdes	if(opt) {
881282089Sdes		print_option(cfg, opt, final);
882276605Sdes		config_delete(cfg);
883276605Sdes		return;
884276605Sdes	}
885356345Scy	morechecks(cfg);
886238106Sdes	check_mod(cfg, iter_get_funcblock());
887238106Sdes	check_mod(cfg, val_get_funcblock());
888238106Sdes#ifdef WITH_PYTHONMODULE
889238106Sdes	if(strstr(cfg->module_conf, "python"))
890238106Sdes		check_mod(cfg, pythonmod_get_funcblock());
891238106Sdes#endif
892238106Sdes	check_fwd(cfg);
893238106Sdes	check_hints(cfg);
894356345Scy	check_auth(cfg);
895276605Sdes	printf("unbound-checkconf: no errors in %s\n", cfgfile);
896238106Sdes	config_delete(cfg);
897238106Sdes}
898238106Sdes
899238106Sdes/** getopt global, in case header files fail to declare it. */
900238106Sdesextern int optind;
901238106Sdes/** getopt global, in case header files fail to declare it. */
902238106Sdesextern char* optarg;
903238106Sdes
904238106Sdes/** Main routine for checkconf */
905238106Sdesint main(int argc, char* argv[])
906238106Sdes{
907238106Sdes	int c;
908282089Sdes	int final = 0;
909238106Sdes	const char* f;
910238106Sdes	const char* opt = NULL;
911238106Sdes	const char* cfgfile = CONFIGFILE;
912238106Sdes	log_ident_set("unbound-checkconf");
913238106Sdes	log_init(NULL, 0, NULL);
914238106Sdes	checklock_start();
915238106Sdes#ifdef USE_WINSOCK
916238106Sdes	/* use registry config file in preference to compiletime location */
917238106Sdes	if(!(cfgfile=w_lookup_reg_str("Software\\Unbound", "ConfigFile")))
918238106Sdes		cfgfile = CONFIGFILE;
919238106Sdes#endif /* USE_WINSOCK */
920238106Sdes	/* parse the options */
921282089Sdes	while( (c=getopt(argc, argv, "fho:")) != -1) {
922238106Sdes		switch(c) {
923282089Sdes		case 'f':
924282089Sdes			final = 1;
925282089Sdes			break;
926238106Sdes		case 'o':
927238106Sdes			opt = optarg;
928238106Sdes			break;
929238106Sdes		case '?':
930238106Sdes		case 'h':
931238106Sdes		default:
932238106Sdes			usage();
933238106Sdes		}
934238106Sdes	}
935238106Sdes	argc -= optind;
936238106Sdes	argv += optind;
937238106Sdes	if(argc != 0 && argc != 1)
938238106Sdes		usage();
939238106Sdes	if(argc == 1)
940238106Sdes		f = argv[0];
941238106Sdes	else	f = cfgfile;
942282089Sdes	checkconf(f, opt, final);
943238106Sdes	checklock_stop();
944238106Sdes	return 0;
945238106Sdes}
946