Deleted Added
full compact
unbound-checkconf.c (256281) unbound-checkconf.c (269257)
1/*
2 * checkconf/unbound-checkconf.c - config file checker for unbound.conf file.
3 *
4 * Copyright (c) 2007, NLnet Labs. All rights reserved.
5 *
6 * This software is open source.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 *
19 * Neither the name of the NLNET LABS nor the names of its contributors may
20 * be used to endorse or promote products derived from this software without
21 * specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1/*
2 * checkconf/unbound-checkconf.c - config file checker for unbound.conf file.
3 *
4 * Copyright (c) 2007, NLnet Labs. All rights reserved.
5 *
6 * This software is open source.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 *
19 * Neither the name of the NLNET LABS nor the names of its contributors may
20 * be used to endorse or promote products derived from this software without
21 * specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36/**
37 * \file
38 *
39 * The config checker checks for syntax and other errors in the unbound.conf
40 * file, and can be used to check for errors before the server is started
41 * or sigHUPped.
42 * Exit status 1 means an error.
43 */
44
45#include "config.h"
46#include "util/log.h"
47#include "util/config_file.h"
48#include "util/module.h"
49#include "util/net_help.h"
50#include "util/regional.h"
51#include "iterator/iterator.h"
52#include "iterator/iter_fwd.h"
53#include "iterator/iter_hints.h"
54#include "validator/validator.h"
55#include "services/localzone.h"
34 */
35
36/**
37 * \file
38 *
39 * The config checker checks for syntax and other errors in the unbound.conf
40 * file, and can be used to check for errors before the server is started
41 * or sigHUPped.
42 * Exit status 1 means an error.
43 */
44
45#include "config.h"
46#include "util/log.h"
47#include "util/config_file.h"
48#include "util/module.h"
49#include "util/net_help.h"
50#include "util/regional.h"
51#include "iterator/iterator.h"
52#include "iterator/iter_fwd.h"
53#include "iterator/iter_hints.h"
54#include "validator/validator.h"
55#include "services/localzone.h"
56#include "ldns/sbuffer.h"
56#ifdef HAVE_GETOPT_H
57#include <getopt.h>
58#endif
59#ifdef HAVE_PWD_H
60#include <pwd.h>
61#endif
62#ifdef HAVE_SYS_STAT_H
63#include <sys/stat.h>
64#endif
65#ifdef HAVE_GLOB_H
66#include <glob.h>
67#endif
68#ifdef WITH_PYTHONMODULE
69#include "pythonmod/pythonmod.h"
70#endif
71
72/** Give checkconf usage, and exit (1). */
73static void
74usage()
75{
76 printf("Usage: unbound-checkconf [file]\n");
77 printf(" Checks unbound configuration file for errors.\n");
78 printf("file if omitted %s is used.\n", CONFIGFILE);
79 printf("-o option print value of option to stdout.\n");
80 printf("-h show this usage help.\n");
81 printf("Version %s\n", PACKAGE_VERSION);
82 printf("BSD licensed, see LICENSE in source package for details.\n");
83 printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
84 exit(1);
85}
86
87/**
88 * Print given option to stdout
89 * @param cfg: config
90 * @param opt: option name without trailing :.
91 * This is different from config_set_option.
92 */
93static void
94print_option(struct config_file* cfg, const char* opt)
95{
96 if(!config_get_option(cfg, opt, config_print_func, stdout))
97 fatal_exit("cannot print option '%s'", opt);
98}
99
100/** check if module works with config */
101static void
102check_mod(struct config_file* cfg, struct module_func_block* fb)
103{
104 struct module_env env;
105 memset(&env, 0, sizeof(env));
106 env.cfg = cfg;
107 env.scratch = regional_create();
57#ifdef HAVE_GETOPT_H
58#include <getopt.h>
59#endif
60#ifdef HAVE_PWD_H
61#include <pwd.h>
62#endif
63#ifdef HAVE_SYS_STAT_H
64#include <sys/stat.h>
65#endif
66#ifdef HAVE_GLOB_H
67#include <glob.h>
68#endif
69#ifdef WITH_PYTHONMODULE
70#include "pythonmod/pythonmod.h"
71#endif
72
73/** Give checkconf usage, and exit (1). */
74static void
75usage()
76{
77 printf("Usage: unbound-checkconf [file]\n");
78 printf(" Checks unbound configuration file for errors.\n");
79 printf("file if omitted %s is used.\n", CONFIGFILE);
80 printf("-o option print value of option to stdout.\n");
81 printf("-h show this usage help.\n");
82 printf("Version %s\n", PACKAGE_VERSION);
83 printf("BSD licensed, see LICENSE in source package for details.\n");
84 printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
85 exit(1);
86}
87
88/**
89 * Print given option to stdout
90 * @param cfg: config
91 * @param opt: option name without trailing :.
92 * This is different from config_set_option.
93 */
94static void
95print_option(struct config_file* cfg, const char* opt)
96{
97 if(!config_get_option(cfg, opt, config_print_func, stdout))
98 fatal_exit("cannot print option '%s'", opt);
99}
100
101/** check if module works with config */
102static void
103check_mod(struct config_file* cfg, struct module_func_block* fb)
104{
105 struct module_env env;
106 memset(&env, 0, sizeof(env));
107 env.cfg = cfg;
108 env.scratch = regional_create();
108 env.scratch_buffer = ldns_buffer_new(BUFSIZ);
109 env.scratch_buffer = sldns_buffer_new(BUFSIZ);
109 if(!env.scratch || !env.scratch_buffer)
110 fatal_exit("out of memory");
111 if(!(*fb->init)(&env, 0)) {
112 fatal_exit("bad config for %s module", fb->name);
113 }
114 (*fb->deinit)(&env, 0);
110 if(!env.scratch || !env.scratch_buffer)
111 fatal_exit("out of memory");
112 if(!(*fb->init)(&env, 0)) {
113 fatal_exit("bad config for %s module", fb->name);
114 }
115 (*fb->deinit)(&env, 0);
115 ldns_buffer_free(env.scratch_buffer);
116 sldns_buffer_free(env.scratch_buffer);
116 regional_destroy(env.scratch);
117}
118
119/** check localzones */
120static void
121localzonechecks(struct config_file* cfg)
122{
123 struct local_zones* zs;
124 if(!(zs = local_zones_create()))
125 fatal_exit("out of memory");
126 if(!local_zones_apply_cfg(zs, cfg))
127 fatal_exit("failed local-zone, local-data configuration");
128 local_zones_delete(zs);
129}
130
131/** emit warnings for IP in hosts */
132static void
133warn_hosts(const char* typ, struct config_stub* list)
134{
135 struct sockaddr_storage a;
136 socklen_t alen;
137 struct config_stub* s;
138 struct config_strlist* h;
139 for(s=list; s; s=s->next) {
140 for(h=s->hosts; h; h=h->next) {
141 if(extstrtoaddr(h->str, &a, &alen)) {
142 fprintf(stderr, "unbound-checkconf: warning:"
143 " %s %s: \"%s\" is an IP%s address, "
144 "and when looked up as a host name "
145 "during use may not resolve.\n",
146 s->name, typ, h->str,
147 addr_is_ip6(&a, alen)?"6":"4");
148 }
149 }
150 }
151}
152
153/** check interface strings */
154static void
155interfacechecks(struct config_file* cfg)
156{
157 struct sockaddr_storage a;
158 socklen_t alen;
159 int i, j;
160 for(i=0; i<cfg->num_ifs; i++) {
161 if(!extstrtoaddr(cfg->ifs[i], &a, &alen)) {
162 fatal_exit("cannot parse interface specified as '%s'",
163 cfg->ifs[i]);
164 }
165 for(j=0; j<cfg->num_ifs; j++) {
166 if(i!=j && strcmp(cfg->ifs[i], cfg->ifs[j])==0)
167 fatal_exit("interface: %s present twice, "
168 "cannot bind same ports twice.",
169 cfg->ifs[i]);
170 }
171 }
172 for(i=0; i<cfg->num_out_ifs; i++) {
173 if(!ipstrtoaddr(cfg->out_ifs[i], UNBOUND_DNS_PORT,
174 &a, &alen)) {
175 fatal_exit("cannot parse outgoing-interface "
176 "specified as '%s'", cfg->out_ifs[i]);
177 }
178 for(j=0; j<cfg->num_out_ifs; j++) {
179 if(i!=j && strcmp(cfg->out_ifs[i], cfg->out_ifs[j])==0)
180 fatal_exit("outgoing-interface: %s present "
181 "twice, cannot bind same ports twice.",
182 cfg->out_ifs[i]);
183 }
184 }
185}
186
187/** check acl ips */
188static void
189aclchecks(struct config_file* cfg)
190{
191 int d;
192 struct sockaddr_storage a;
193 socklen_t alen;
194 struct config_str2list* acl;
195 for(acl=cfg->acls; acl; acl = acl->next) {
196 if(!netblockstrtoaddr(acl->str, UNBOUND_DNS_PORT, &a, &alen,
197 &d)) {
198 fatal_exit("cannot parse access control address %s %s",
199 acl->str, acl->str2);
200 }
201 }
202}
203
204/** true if fname is a file */
205static int
206is_file(const char* fname)
207{
208 struct stat buf;
209 if(stat(fname, &buf) < 0) {
210 if(errno==EACCES) {
211 printf("warning: no search permission for one of the directories in path: %s\n", fname);
212 return 1;
213 }
214 perror(fname);
215 return 0;
216 }
217 if(S_ISDIR(buf.st_mode)) {
218 printf("%s is not a file\n", fname);
219 return 0;
220 }
221 return 1;
222}
223
224/** true if fname is a directory */
225static int
226is_dir(const char* fname)
227{
228 struct stat buf;
229 if(stat(fname, &buf) < 0) {
230 if(errno==EACCES) {
231 printf("warning: no search permission for one of the directories in path: %s\n", fname);
232 return 1;
233 }
234 perror(fname);
235 return 0;
236 }
237 if(!(S_ISDIR(buf.st_mode))) {
238 printf("%s is not a directory\n", fname);
239 return 0;
240 }
241 return 1;
242}
243
244/** get base dir of a fname */
245static char*
246basedir(char* fname)
247{
248 char* rev;
249 if(!fname) fatal_exit("out of memory");
250 rev = strrchr(fname, '/');
251 if(!rev) return NULL;
252 if(fname == rev) return NULL;
253 rev[0] = 0;
254 return fname;
255}
256
257/** check chroot for a file string */
258static void
259check_chroot_string(const char* desc, char** ss,
260 const char* chrootdir, struct config_file* cfg)
261{
262 char* str = *ss;
263 if(str && str[0]) {
264 *ss = fname_after_chroot(str, cfg, 1);
265 if(!*ss) fatal_exit("out of memory");
266 if(!is_file(*ss)) {
267 if(chrootdir && chrootdir[0])
268 fatal_exit("%s: \"%s\" does not exist in "
269 "chrootdir %s", desc, str, chrootdir);
270 else
271 fatal_exit("%s: \"%s\" does not exist",
272 desc, str);
273 }
274 /* put in a new full path for continued checking */
275 free(str);
276 }
277}
278
279/** check file list, every file must be inside the chroot location */
280static void
281check_chroot_filelist(const char* desc, struct config_strlist* list,
282 const char* chrootdir, struct config_file* cfg)
283{
284 struct config_strlist* p;
285 for(p=list; p; p=p->next) {
286 check_chroot_string(desc, &p->str, chrootdir, cfg);
287 }
288}
289
290/** check file list, with wildcard processing */
291static void
292check_chroot_filelist_wild(const char* desc, struct config_strlist* list,
293 const char* chrootdir, struct config_file* cfg)
294{
295 struct config_strlist* p;
296 for(p=list; p; p=p->next) {
297#ifdef HAVE_GLOB
298 if(strchr(p->str, '*') || strchr(p->str, '[') ||
299 strchr(p->str, '?') || strchr(p->str, '{') ||
300 strchr(p->str, '~')) {
301 char* s = p->str;
302 /* adjust whole pattern for chroot and check later */
303 p->str = fname_after_chroot(p->str, cfg, 1);
304 free(s);
305 } else
306#endif /* HAVE_GLOB */
307 check_chroot_string(desc, &p->str, chrootdir, cfg);
308 }
309}
310
311/** check configuration for errors */
312static void
313morechecks(struct config_file* cfg, const char* fname)
314{
315 warn_hosts("stub-host", cfg->stubs);
316 warn_hosts("forward-host", cfg->forwards);
317 interfacechecks(cfg);
318 aclchecks(cfg);
319
320 if(cfg->verbosity < 0)
321 fatal_exit("verbosity value < 0");
322 if(cfg->num_threads <= 0 || cfg->num_threads > 10000)
323 fatal_exit("num_threads value weird");
324 if(!cfg->do_ip4 && !cfg->do_ip6)
325 fatal_exit("ip4 and ip6 are both disabled, pointless");
326 if(!cfg->do_udp && !cfg->do_tcp)
327 fatal_exit("udp and tcp are both disabled, pointless");
328 if(cfg->edns_buffer_size > cfg->msg_buffer_size)
329 fatal_exit("edns-buffer-size larger than msg-buffer-size, "
330 "answers will not fit in processing buffer");
331
332 if(cfg->chrootdir && cfg->chrootdir[0] &&
333 cfg->chrootdir[strlen(cfg->chrootdir)-1] == '/')
334 fatal_exit("chootdir %s has trailing slash '/' please remove.",
335 cfg->chrootdir);
336 if(cfg->chrootdir && cfg->chrootdir[0] &&
337 !is_dir(cfg->chrootdir)) {
338 fatal_exit("bad chroot directory");
339 }
340 if(cfg->chrootdir && cfg->chrootdir[0]) {
341 char buf[10240];
342 buf[0] = 0;
343 if(fname[0] != '/') {
344 if(getcwd(buf, sizeof(buf)) == NULL)
345 fatal_exit("getcwd: %s", strerror(errno));
117 regional_destroy(env.scratch);
118}
119
120/** check localzones */
121static void
122localzonechecks(struct config_file* cfg)
123{
124 struct local_zones* zs;
125 if(!(zs = local_zones_create()))
126 fatal_exit("out of memory");
127 if(!local_zones_apply_cfg(zs, cfg))
128 fatal_exit("failed local-zone, local-data configuration");
129 local_zones_delete(zs);
130}
131
132/** emit warnings for IP in hosts */
133static void
134warn_hosts(const char* typ, struct config_stub* list)
135{
136 struct sockaddr_storage a;
137 socklen_t alen;
138 struct config_stub* s;
139 struct config_strlist* h;
140 for(s=list; s; s=s->next) {
141 for(h=s->hosts; h; h=h->next) {
142 if(extstrtoaddr(h->str, &a, &alen)) {
143 fprintf(stderr, "unbound-checkconf: warning:"
144 " %s %s: \"%s\" is an IP%s address, "
145 "and when looked up as a host name "
146 "during use may not resolve.\n",
147 s->name, typ, h->str,
148 addr_is_ip6(&a, alen)?"6":"4");
149 }
150 }
151 }
152}
153
154/** check interface strings */
155static void
156interfacechecks(struct config_file* cfg)
157{
158 struct sockaddr_storage a;
159 socklen_t alen;
160 int i, j;
161 for(i=0; i<cfg->num_ifs; i++) {
162 if(!extstrtoaddr(cfg->ifs[i], &a, &alen)) {
163 fatal_exit("cannot parse interface specified as '%s'",
164 cfg->ifs[i]);
165 }
166 for(j=0; j<cfg->num_ifs; j++) {
167 if(i!=j && strcmp(cfg->ifs[i], cfg->ifs[j])==0)
168 fatal_exit("interface: %s present twice, "
169 "cannot bind same ports twice.",
170 cfg->ifs[i]);
171 }
172 }
173 for(i=0; i<cfg->num_out_ifs; i++) {
174 if(!ipstrtoaddr(cfg->out_ifs[i], UNBOUND_DNS_PORT,
175 &a, &alen)) {
176 fatal_exit("cannot parse outgoing-interface "
177 "specified as '%s'", cfg->out_ifs[i]);
178 }
179 for(j=0; j<cfg->num_out_ifs; j++) {
180 if(i!=j && strcmp(cfg->out_ifs[i], cfg->out_ifs[j])==0)
181 fatal_exit("outgoing-interface: %s present "
182 "twice, cannot bind same ports twice.",
183 cfg->out_ifs[i]);
184 }
185 }
186}
187
188/** check acl ips */
189static void
190aclchecks(struct config_file* cfg)
191{
192 int d;
193 struct sockaddr_storage a;
194 socklen_t alen;
195 struct config_str2list* acl;
196 for(acl=cfg->acls; acl; acl = acl->next) {
197 if(!netblockstrtoaddr(acl->str, UNBOUND_DNS_PORT, &a, &alen,
198 &d)) {
199 fatal_exit("cannot parse access control address %s %s",
200 acl->str, acl->str2);
201 }
202 }
203}
204
205/** true if fname is a file */
206static int
207is_file(const char* fname)
208{
209 struct stat buf;
210 if(stat(fname, &buf) < 0) {
211 if(errno==EACCES) {
212 printf("warning: no search permission for one of the directories in path: %s\n", fname);
213 return 1;
214 }
215 perror(fname);
216 return 0;
217 }
218 if(S_ISDIR(buf.st_mode)) {
219 printf("%s is not a file\n", fname);
220 return 0;
221 }
222 return 1;
223}
224
225/** true if fname is a directory */
226static int
227is_dir(const char* fname)
228{
229 struct stat buf;
230 if(stat(fname, &buf) < 0) {
231 if(errno==EACCES) {
232 printf("warning: no search permission for one of the directories in path: %s\n", fname);
233 return 1;
234 }
235 perror(fname);
236 return 0;
237 }
238 if(!(S_ISDIR(buf.st_mode))) {
239 printf("%s is not a directory\n", fname);
240 return 0;
241 }
242 return 1;
243}
244
245/** get base dir of a fname */
246static char*
247basedir(char* fname)
248{
249 char* rev;
250 if(!fname) fatal_exit("out of memory");
251 rev = strrchr(fname, '/');
252 if(!rev) return NULL;
253 if(fname == rev) return NULL;
254 rev[0] = 0;
255 return fname;
256}
257
258/** check chroot for a file string */
259static void
260check_chroot_string(const char* desc, char** ss,
261 const char* chrootdir, struct config_file* cfg)
262{
263 char* str = *ss;
264 if(str && str[0]) {
265 *ss = fname_after_chroot(str, cfg, 1);
266 if(!*ss) fatal_exit("out of memory");
267 if(!is_file(*ss)) {
268 if(chrootdir && chrootdir[0])
269 fatal_exit("%s: \"%s\" does not exist in "
270 "chrootdir %s", desc, str, chrootdir);
271 else
272 fatal_exit("%s: \"%s\" does not exist",
273 desc, str);
274 }
275 /* put in a new full path for continued checking */
276 free(str);
277 }
278}
279
280/** check file list, every file must be inside the chroot location */
281static void
282check_chroot_filelist(const char* desc, struct config_strlist* list,
283 const char* chrootdir, struct config_file* cfg)
284{
285 struct config_strlist* p;
286 for(p=list; p; p=p->next) {
287 check_chroot_string(desc, &p->str, chrootdir, cfg);
288 }
289}
290
291/** check file list, with wildcard processing */
292static void
293check_chroot_filelist_wild(const char* desc, struct config_strlist* list,
294 const char* chrootdir, struct config_file* cfg)
295{
296 struct config_strlist* p;
297 for(p=list; p; p=p->next) {
298#ifdef HAVE_GLOB
299 if(strchr(p->str, '*') || strchr(p->str, '[') ||
300 strchr(p->str, '?') || strchr(p->str, '{') ||
301 strchr(p->str, '~')) {
302 char* s = p->str;
303 /* adjust whole pattern for chroot and check later */
304 p->str = fname_after_chroot(p->str, cfg, 1);
305 free(s);
306 } else
307#endif /* HAVE_GLOB */
308 check_chroot_string(desc, &p->str, chrootdir, cfg);
309 }
310}
311
312/** check configuration for errors */
313static void
314morechecks(struct config_file* cfg, const char* fname)
315{
316 warn_hosts("stub-host", cfg->stubs);
317 warn_hosts("forward-host", cfg->forwards);
318 interfacechecks(cfg);
319 aclchecks(cfg);
320
321 if(cfg->verbosity < 0)
322 fatal_exit("verbosity value < 0");
323 if(cfg->num_threads <= 0 || cfg->num_threads > 10000)
324 fatal_exit("num_threads value weird");
325 if(!cfg->do_ip4 && !cfg->do_ip6)
326 fatal_exit("ip4 and ip6 are both disabled, pointless");
327 if(!cfg->do_udp && !cfg->do_tcp)
328 fatal_exit("udp and tcp are both disabled, pointless");
329 if(cfg->edns_buffer_size > cfg->msg_buffer_size)
330 fatal_exit("edns-buffer-size larger than msg-buffer-size, "
331 "answers will not fit in processing buffer");
332
333 if(cfg->chrootdir && cfg->chrootdir[0] &&
334 cfg->chrootdir[strlen(cfg->chrootdir)-1] == '/')
335 fatal_exit("chootdir %s has trailing slash '/' please remove.",
336 cfg->chrootdir);
337 if(cfg->chrootdir && cfg->chrootdir[0] &&
338 !is_dir(cfg->chrootdir)) {
339 fatal_exit("bad chroot directory");
340 }
341 if(cfg->chrootdir && cfg->chrootdir[0]) {
342 char buf[10240];
343 buf[0] = 0;
344 if(fname[0] != '/') {
345 if(getcwd(buf, sizeof(buf)) == NULL)
346 fatal_exit("getcwd: %s", strerror(errno));
346 strncat(buf, "/", sizeof(buf)-strlen(buf)-1);
347 (void)strlcat(buf, "/", sizeof(buf));
347 }
348 }
348 strncat(buf, fname, sizeof(buf)-strlen(buf)-1);
349 (void)strlcat(buf, fname, sizeof(buf));
349 if(strncmp(buf, cfg->chrootdir, strlen(cfg->chrootdir)) != 0)
350 fatal_exit("config file %s is not inside chroot %s",
351 buf, cfg->chrootdir);
352 }
353 if(cfg->directory && cfg->directory[0]) {
354 char* ad = fname_after_chroot(cfg->directory, cfg, 0);
355 if(!ad) fatal_exit("out of memory");
356 if(!is_dir(ad)) fatal_exit("bad chdir directory");
357 free(ad);
358 }
359 if( (cfg->chrootdir && cfg->chrootdir[0]) ||
360 (cfg->directory && cfg->directory[0])) {
361 if(cfg->pidfile && cfg->pidfile[0]) {
362 char* ad = (cfg->pidfile[0]=='/')?strdup(cfg->pidfile):
363 fname_after_chroot(cfg->pidfile, cfg, 1);
364 char* bd = basedir(ad);
365 if(bd && !is_dir(bd))
366 fatal_exit("pidfile directory does not exist");
367 free(ad);
368 }
369 if(cfg->logfile && cfg->logfile[0]) {
370 char* ad = fname_after_chroot(cfg->logfile, cfg, 1);
371 char* bd = basedir(ad);
372 if(bd && !is_dir(bd))
373 fatal_exit("logfile directory does not exist");
374 free(ad);
375 }
376 }
377
378 check_chroot_filelist("file with root-hints",
379 cfg->root_hints, cfg->chrootdir, cfg);
380 check_chroot_filelist("trust-anchor-file",
381 cfg->trust_anchor_file_list, cfg->chrootdir, cfg);
382 check_chroot_filelist("auto-trust-anchor-file",
383 cfg->auto_trust_anchor_file_list, cfg->chrootdir, cfg);
384 check_chroot_filelist_wild("trusted-keys-file",
385 cfg->trusted_keys_file_list, cfg->chrootdir, cfg);
386 check_chroot_string("dlv-anchor-file", &cfg->dlv_anchor_file,
387 cfg->chrootdir, cfg);
388 /* remove chroot setting so that modules are not stripping pathnames*/
389 free(cfg->chrootdir);
390 cfg->chrootdir = NULL;
391
392 if(strcmp(cfg->module_conf, "iterator") != 0
393 && strcmp(cfg->module_conf, "validator iterator") != 0
394#ifdef WITH_PYTHONMODULE
395 && strcmp(cfg->module_conf, "python iterator") != 0
396 && strcmp(cfg->module_conf, "python validator iterator") != 0
397 && strcmp(cfg->module_conf, "validator python iterator") != 0
398#endif
399 ) {
400 fatal_exit("module conf '%s' is not known to work",
401 cfg->module_conf);
402 }
403
404#ifdef HAVE_GETPWNAM
405 if(cfg->username && cfg->username[0]) {
406 if(getpwnam(cfg->username) == NULL)
407 fatal_exit("user '%s' does not exist.", cfg->username);
408 endpwent();
409 }
410#endif
411 if(cfg->remote_control_enable) {
412 check_chroot_string("server-key-file", &cfg->server_key_file,
413 cfg->chrootdir, cfg);
414 check_chroot_string("server-cert-file", &cfg->server_cert_file,
415 cfg->chrootdir, cfg);
416 if(!is_file(cfg->control_key_file))
417 fatal_exit("control-key-file: \"%s\" does not exist",
418 cfg->control_key_file);
419 if(!is_file(cfg->control_cert_file))
420 fatal_exit("control-cert-file: \"%s\" does not exist",
421 cfg->control_cert_file);
422 }
423
424 localzonechecks(cfg);
425}
426
427/** check forwards */
428static void
429check_fwd(struct config_file* cfg)
430{
431 struct iter_forwards* fwd = forwards_create();
432 if(!fwd || !forwards_apply_cfg(fwd, cfg)) {
433 fatal_exit("Could not set forward zones");
434 }
435 forwards_delete(fwd);
436}
437
438/** check hints */
439static void
440check_hints(struct config_file* cfg)
441{
442 struct iter_hints* hints = hints_create();
443 if(!hints || !hints_apply_cfg(hints, cfg)) {
444 fatal_exit("Could not set root or stub hints");
445 }
446 hints_delete(hints);
447}
448
449/** check config file */
450static void
451checkconf(const char* cfgfile, const char* opt)
452{
453 struct config_file* cfg = config_create();
454 if(!cfg)
455 fatal_exit("out of memory");
456 if(!config_read(cfg, cfgfile, NULL)) {
457 /* config_read prints messages to stderr */
458 config_delete(cfg);
459 exit(1);
460 }
461 morechecks(cfg, cfgfile);
462 check_mod(cfg, iter_get_funcblock());
463 check_mod(cfg, val_get_funcblock());
464#ifdef WITH_PYTHONMODULE
465 if(strstr(cfg->module_conf, "python"))
466 check_mod(cfg, pythonmod_get_funcblock());
467#endif
468 check_fwd(cfg);
469 check_hints(cfg);
470 if(opt) print_option(cfg, opt);
471 else printf("unbound-checkconf: no errors in %s\n", cfgfile);
472 config_delete(cfg);
473}
474
475/** getopt global, in case header files fail to declare it. */
476extern int optind;
477/** getopt global, in case header files fail to declare it. */
478extern char* optarg;
479
480/** Main routine for checkconf */
481int main(int argc, char* argv[])
482{
483 int c;
484 const char* f;
485 const char* opt = NULL;
486 const char* cfgfile = CONFIGFILE;
487 log_ident_set("unbound-checkconf");
488 log_init(NULL, 0, NULL);
489 checklock_start();
490#ifdef USE_WINSOCK
491 /* use registry config file in preference to compiletime location */
492 if(!(cfgfile=w_lookup_reg_str("Software\\Unbound", "ConfigFile")))
493 cfgfile = CONFIGFILE;
494#endif /* USE_WINSOCK */
495 /* parse the options */
496 while( (c=getopt(argc, argv, "ho:")) != -1) {
497 switch(c) {
498 case 'o':
499 opt = optarg;
500 break;
501 case '?':
502 case 'h':
503 default:
504 usage();
505 }
506 }
507 argc -= optind;
508 argv += optind;
509 if(argc != 0 && argc != 1)
510 usage();
511 if(argc == 1)
512 f = argv[0];
513 else f = cfgfile;
514 checkconf(f, opt);
515 checklock_stop();
516 return 0;
517}
350 if(strncmp(buf, cfg->chrootdir, strlen(cfg->chrootdir)) != 0)
351 fatal_exit("config file %s is not inside chroot %s",
352 buf, cfg->chrootdir);
353 }
354 if(cfg->directory && cfg->directory[0]) {
355 char* ad = fname_after_chroot(cfg->directory, cfg, 0);
356 if(!ad) fatal_exit("out of memory");
357 if(!is_dir(ad)) fatal_exit("bad chdir directory");
358 free(ad);
359 }
360 if( (cfg->chrootdir && cfg->chrootdir[0]) ||
361 (cfg->directory && cfg->directory[0])) {
362 if(cfg->pidfile && cfg->pidfile[0]) {
363 char* ad = (cfg->pidfile[0]=='/')?strdup(cfg->pidfile):
364 fname_after_chroot(cfg->pidfile, cfg, 1);
365 char* bd = basedir(ad);
366 if(bd && !is_dir(bd))
367 fatal_exit("pidfile directory does not exist");
368 free(ad);
369 }
370 if(cfg->logfile && cfg->logfile[0]) {
371 char* ad = fname_after_chroot(cfg->logfile, cfg, 1);
372 char* bd = basedir(ad);
373 if(bd && !is_dir(bd))
374 fatal_exit("logfile directory does not exist");
375 free(ad);
376 }
377 }
378
379 check_chroot_filelist("file with root-hints",
380 cfg->root_hints, cfg->chrootdir, cfg);
381 check_chroot_filelist("trust-anchor-file",
382 cfg->trust_anchor_file_list, cfg->chrootdir, cfg);
383 check_chroot_filelist("auto-trust-anchor-file",
384 cfg->auto_trust_anchor_file_list, cfg->chrootdir, cfg);
385 check_chroot_filelist_wild("trusted-keys-file",
386 cfg->trusted_keys_file_list, cfg->chrootdir, cfg);
387 check_chroot_string("dlv-anchor-file", &cfg->dlv_anchor_file,
388 cfg->chrootdir, cfg);
389 /* remove chroot setting so that modules are not stripping pathnames*/
390 free(cfg->chrootdir);
391 cfg->chrootdir = NULL;
392
393 if(strcmp(cfg->module_conf, "iterator") != 0
394 && strcmp(cfg->module_conf, "validator iterator") != 0
395#ifdef WITH_PYTHONMODULE
396 && strcmp(cfg->module_conf, "python iterator") != 0
397 && strcmp(cfg->module_conf, "python validator iterator") != 0
398 && strcmp(cfg->module_conf, "validator python iterator") != 0
399#endif
400 ) {
401 fatal_exit("module conf '%s' is not known to work",
402 cfg->module_conf);
403 }
404
405#ifdef HAVE_GETPWNAM
406 if(cfg->username && cfg->username[0]) {
407 if(getpwnam(cfg->username) == NULL)
408 fatal_exit("user '%s' does not exist.", cfg->username);
409 endpwent();
410 }
411#endif
412 if(cfg->remote_control_enable) {
413 check_chroot_string("server-key-file", &cfg->server_key_file,
414 cfg->chrootdir, cfg);
415 check_chroot_string("server-cert-file", &cfg->server_cert_file,
416 cfg->chrootdir, cfg);
417 if(!is_file(cfg->control_key_file))
418 fatal_exit("control-key-file: \"%s\" does not exist",
419 cfg->control_key_file);
420 if(!is_file(cfg->control_cert_file))
421 fatal_exit("control-cert-file: \"%s\" does not exist",
422 cfg->control_cert_file);
423 }
424
425 localzonechecks(cfg);
426}
427
428/** check forwards */
429static void
430check_fwd(struct config_file* cfg)
431{
432 struct iter_forwards* fwd = forwards_create();
433 if(!fwd || !forwards_apply_cfg(fwd, cfg)) {
434 fatal_exit("Could not set forward zones");
435 }
436 forwards_delete(fwd);
437}
438
439/** check hints */
440static void
441check_hints(struct config_file* cfg)
442{
443 struct iter_hints* hints = hints_create();
444 if(!hints || !hints_apply_cfg(hints, cfg)) {
445 fatal_exit("Could not set root or stub hints");
446 }
447 hints_delete(hints);
448}
449
450/** check config file */
451static void
452checkconf(const char* cfgfile, const char* opt)
453{
454 struct config_file* cfg = config_create();
455 if(!cfg)
456 fatal_exit("out of memory");
457 if(!config_read(cfg, cfgfile, NULL)) {
458 /* config_read prints messages to stderr */
459 config_delete(cfg);
460 exit(1);
461 }
462 morechecks(cfg, cfgfile);
463 check_mod(cfg, iter_get_funcblock());
464 check_mod(cfg, val_get_funcblock());
465#ifdef WITH_PYTHONMODULE
466 if(strstr(cfg->module_conf, "python"))
467 check_mod(cfg, pythonmod_get_funcblock());
468#endif
469 check_fwd(cfg);
470 check_hints(cfg);
471 if(opt) print_option(cfg, opt);
472 else printf("unbound-checkconf: no errors in %s\n", cfgfile);
473 config_delete(cfg);
474}
475
476/** getopt global, in case header files fail to declare it. */
477extern int optind;
478/** getopt global, in case header files fail to declare it. */
479extern char* optarg;
480
481/** Main routine for checkconf */
482int main(int argc, char* argv[])
483{
484 int c;
485 const char* f;
486 const char* opt = NULL;
487 const char* cfgfile = CONFIGFILE;
488 log_ident_set("unbound-checkconf");
489 log_init(NULL, 0, NULL);
490 checklock_start();
491#ifdef USE_WINSOCK
492 /* use registry config file in preference to compiletime location */
493 if(!(cfgfile=w_lookup_reg_str("Software\\Unbound", "ConfigFile")))
494 cfgfile = CONFIGFILE;
495#endif /* USE_WINSOCK */
496 /* parse the options */
497 while( (c=getopt(argc, argv, "ho:")) != -1) {
498 switch(c) {
499 case 'o':
500 opt = optarg;
501 break;
502 case '?':
503 case 'h':
504 default:
505 usage();
506 }
507 }
508 argc -= optind;
509 argv += optind;
510 if(argc != 0 && argc != 1)
511 usage();
512 if(argc == 1)
513 f = argv[0];
514 else f = cfgfile;
515 checkconf(f, opt);
516 checklock_stop();
517 return 0;
518}