]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/unbound/smallapp/unbound-checkconf.c
MFV r326007: less v529.
[FreeBSD/FreeBSD.git] / contrib / unbound / smallapp / unbound-checkconf.c
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
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"
56 #include "sldns/sbuffer.h"
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). */
74 static void
75 usage(void)
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("-f              output full pathname with chroot applied, eg. with -o pidfile.\n");
82         printf("-h              show this usage help.\n");
83         printf("Version %s\n", PACKAGE_VERSION);
84         printf("BSD licensed, see LICENSE in source package for details.\n");
85         printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
86         exit(1);
87 }
88
89 /** 
90  * Print given option to stdout 
91  * @param cfg: config
92  * @param opt: option name without trailing :. 
93  *      This is different from config_set_option.
94  * @param final: if final pathname with chroot applied has to be printed.
95  */
96 static void
97 print_option(struct config_file* cfg, const char* opt, int final)
98 {
99         if(strcmp(opt, "pidfile") == 0 && final) {
100                 printf("%s\n", fname_after_chroot(cfg->pidfile, cfg, 1));
101                 return;
102         }
103         if(!config_get_option(cfg, opt, config_print_func, stdout))
104                 fatal_exit("cannot print option '%s'", opt);
105 }
106
107 /** check if module works with config */
108 static void
109 check_mod(struct config_file* cfg, struct module_func_block* fb)
110 {
111         struct module_env env;
112         memset(&env, 0, sizeof(env));
113         env.cfg = cfg;
114         env.scratch = regional_create();
115         env.scratch_buffer = sldns_buffer_new(BUFSIZ);
116         if(!env.scratch || !env.scratch_buffer)
117                 fatal_exit("out of memory");
118         if(!(*fb->init)(&env, 0)) {
119                 fatal_exit("bad config for %s module", fb->name);
120         }
121         (*fb->deinit)(&env, 0);
122         sldns_buffer_free(env.scratch_buffer);
123         regional_destroy(env.scratch);
124 }
125
126 /** check localzones */
127 static void
128 localzonechecks(struct config_file* cfg)
129 {
130         struct local_zones* zs;
131         if(!(zs = local_zones_create()))
132                 fatal_exit("out of memory");
133         if(!local_zones_apply_cfg(zs, cfg))
134                 fatal_exit("failed local-zone, local-data configuration");
135         local_zones_delete(zs);
136 }
137
138 /** emit warnings for IP in hosts */
139 static void
140 warn_hosts(const char* typ, struct config_stub* list)
141 {
142         struct sockaddr_storage a;
143         socklen_t alen;
144         struct config_stub* s;
145         struct config_strlist* h;
146         for(s=list; s; s=s->next) {
147                 for(h=s->hosts; h; h=h->next) {
148                         if(extstrtoaddr(h->str, &a, &alen)) {
149                                 fprintf(stderr, "unbound-checkconf: warning:"
150                                   " %s %s: \"%s\" is an IP%s address, "
151                                   "and when looked up as a host name "
152                                   "during use may not resolve.\n", 
153                                   s->name, typ, h->str,
154                                   addr_is_ip6(&a, alen)?"6":"4");
155                         }
156                 }
157         }
158 }
159
160 /** check interface strings */
161 static void
162 interfacechecks(struct config_file* cfg)
163 {
164         int d;
165         struct sockaddr_storage a;
166         socklen_t alen;
167         int i, j;
168         for(i=0; i<cfg->num_ifs; i++) {
169                 if(!extstrtoaddr(cfg->ifs[i], &a, &alen)) {
170                         fatal_exit("cannot parse interface specified as '%s'",
171                                 cfg->ifs[i]);
172                 }
173                 for(j=0; j<cfg->num_ifs; j++) {
174                         if(i!=j && strcmp(cfg->ifs[i], cfg->ifs[j])==0)
175                                 fatal_exit("interface: %s present twice, "
176                                         "cannot bind same ports twice.",
177                                         cfg->ifs[i]);
178                 }
179         }
180         for(i=0; i<cfg->num_out_ifs; i++) {
181                 if(!ipstrtoaddr(cfg->out_ifs[i], UNBOUND_DNS_PORT, &a, &alen) &&
182                    !netblockstrtoaddr(cfg->out_ifs[i], UNBOUND_DNS_PORT, &a, &alen, &d)) {
183                         fatal_exit("cannot parse outgoing-interface "
184                                 "specified as '%s'", cfg->out_ifs[i]);
185                 }
186                 for(j=0; j<cfg->num_out_ifs; j++) {
187                         if(i!=j && strcmp(cfg->out_ifs[i], cfg->out_ifs[j])==0)
188                                 fatal_exit("outgoing-interface: %s present "
189                                         "twice, cannot bind same ports twice.",
190                                         cfg->out_ifs[i]);
191                 }
192         }
193 }
194
195 /** check acl ips */
196 static void
197 aclchecks(struct config_file* cfg)
198 {
199         int d;
200         struct sockaddr_storage a;
201         socklen_t alen;
202         struct config_str2list* acl;
203         for(acl=cfg->acls; acl; acl = acl->next) {
204                 if(!netblockstrtoaddr(acl->str, UNBOUND_DNS_PORT, &a, &alen, 
205                         &d)) {
206                         fatal_exit("cannot parse access control address %s %s",
207                                 acl->str, acl->str2);
208                 }
209         }
210 }
211
212 /** true if fname is a file */
213 static int
214 is_file(const char* fname) 
215 {
216         struct stat buf;
217         if(stat(fname, &buf) < 0) {
218                 if(errno==EACCES) {
219                         printf("warning: no search permission for one of the directories in path: %s\n", fname);
220                         return 1;
221                 }
222                 perror(fname);
223                 return 0;
224         }
225         if(S_ISDIR(buf.st_mode)) {
226                 printf("%s is not a file\n", fname);
227                 return 0;
228         }
229         return 1;
230 }
231
232 /** true if fname is a directory */
233 static int
234 is_dir(const char* fname) 
235 {
236         struct stat buf;
237         if(stat(fname, &buf) < 0) {
238                 if(errno==EACCES) {
239                         printf("warning: no search permission for one of the directories in path: %s\n", fname);
240                         return 1;
241                 }
242                 perror(fname);
243                 return 0;
244         }
245         if(!(S_ISDIR(buf.st_mode))) {
246                 printf("%s is not a directory\n", fname);
247                 return 0;
248         }
249         return 1;
250 }
251
252 /** get base dir of a fname */
253 static char*
254 basedir(char* fname)
255 {
256         char* rev;
257         if(!fname) fatal_exit("out of memory");
258         rev = strrchr(fname, '/');
259         if(!rev) return NULL;
260         if(fname == rev) return NULL;
261         rev[0] = 0;
262         return fname;
263 }
264
265 /** check chroot for a file string */
266 static void
267 check_chroot_string(const char* desc, char** ss,
268         const char* chrootdir, struct config_file* cfg)
269 {
270         char* str = *ss;
271         if(str && str[0]) {
272                 *ss = fname_after_chroot(str, cfg, 1);
273                 if(!*ss) fatal_exit("out of memory");
274                 if(!is_file(*ss)) {
275                         if(chrootdir && chrootdir[0])
276                                 fatal_exit("%s: \"%s\" does not exist in "
277                                         "chrootdir %s", desc, str, chrootdir);
278                         else
279                                 fatal_exit("%s: \"%s\" does not exist", 
280                                         desc, str);
281                 }
282                 /* put in a new full path for continued checking */
283                 free(str);
284         }
285 }
286
287 /** check file list, every file must be inside the chroot location */
288 static void
289 check_chroot_filelist(const char* desc, struct config_strlist* list,
290         const char* chrootdir, struct config_file* cfg)
291 {
292         struct config_strlist* p;
293         for(p=list; p; p=p->next) {
294                 check_chroot_string(desc, &p->str, chrootdir, cfg);
295         }
296 }
297
298 /** check file list, with wildcard processing */
299 static void
300 check_chroot_filelist_wild(const char* desc, struct config_strlist* list,
301         const char* chrootdir, struct config_file* cfg)
302 {
303         struct config_strlist* p;
304         for(p=list; p; p=p->next) {
305 #ifdef HAVE_GLOB
306                 if(strchr(p->str, '*') || strchr(p->str, '[') || 
307                         strchr(p->str, '?') || strchr(p->str, '{') || 
308                         strchr(p->str, '~')) {
309                         char* s = p->str;
310                         /* adjust whole pattern for chroot and check later */
311                         p->str = fname_after_chroot(p->str, cfg, 1);
312                         free(s);
313                 } else
314 #endif /* HAVE_GLOB */
315                         check_chroot_string(desc, &p->str, chrootdir, cfg);
316         }
317 }
318
319 /** check configuration for errors */
320 static void
321 morechecks(struct config_file* cfg, const char* fname)
322 {
323         warn_hosts("stub-host", cfg->stubs);
324         warn_hosts("forward-host", cfg->forwards);
325         interfacechecks(cfg);
326         aclchecks(cfg);
327
328         if(cfg->verbosity < 0)
329                 fatal_exit("verbosity value < 0");
330         if(cfg->num_threads <= 0 || cfg->num_threads > 10000)
331                 fatal_exit("num_threads value weird");
332         if(!cfg->do_ip4 && !cfg->do_ip6)
333                 fatal_exit("ip4 and ip6 are both disabled, pointless");
334         if(!cfg->do_ip6 && cfg->prefer_ip6)
335                 fatal_exit("cannot prefer and disable ip6, pointless");
336         if(!cfg->do_udp && !cfg->do_tcp)
337                 fatal_exit("udp and tcp are both disabled, pointless");
338         if(cfg->edns_buffer_size > cfg->msg_buffer_size)
339                 fatal_exit("edns-buffer-size larger than msg-buffer-size, "
340                         "answers will not fit in processing buffer");
341 #ifdef UB_ON_WINDOWS
342         w_config_adjust_directory(cfg);
343 #endif
344         if(cfg->chrootdir && cfg->chrootdir[0] && 
345                 cfg->chrootdir[strlen(cfg->chrootdir)-1] == '/')
346                 fatal_exit("chootdir %s has trailing slash '/' please remove.",
347                         cfg->chrootdir);
348         if(cfg->chrootdir && cfg->chrootdir[0] && 
349                 !is_dir(cfg->chrootdir)) {
350                 fatal_exit("bad chroot directory");
351         }
352         if(cfg->chrootdir && cfg->chrootdir[0]) {
353                 char buf[10240];
354                 buf[0] = 0;
355                 if(fname[0] != '/') {
356                         if(getcwd(buf, sizeof(buf)) == NULL)
357                                 fatal_exit("getcwd: %s", strerror(errno));
358                         (void)strlcat(buf, "/", sizeof(buf));
359                 }
360                 (void)strlcat(buf, fname, sizeof(buf));
361                 if(strncmp(buf, cfg->chrootdir, strlen(cfg->chrootdir)) != 0)
362                         fatal_exit("config file %s is not inside chroot %s",
363                                 buf, cfg->chrootdir);
364         }
365         if(cfg->directory && cfg->directory[0]) {
366                 char* ad = fname_after_chroot(cfg->directory, cfg, 0);
367                 if(!ad) fatal_exit("out of memory");
368                 if(!is_dir(ad)) fatal_exit("bad chdir directory");
369                 free(ad);
370         }
371         if( (cfg->chrootdir && cfg->chrootdir[0]) ||
372             (cfg->directory && cfg->directory[0])) {
373                 if(cfg->pidfile && cfg->pidfile[0]) {
374                         char* ad = (cfg->pidfile[0]=='/')?strdup(cfg->pidfile):
375                                 fname_after_chroot(cfg->pidfile, cfg, 1);
376                         char* bd = basedir(ad);
377                         if(bd && !is_dir(bd))
378                                 fatal_exit("pidfile directory does not exist");
379                         free(ad);
380                 }
381                 if(cfg->logfile && cfg->logfile[0]) {
382                         char* ad = fname_after_chroot(cfg->logfile, cfg, 1);
383                         char* bd = basedir(ad);
384                         if(bd && !is_dir(bd))
385                                 fatal_exit("logfile directory does not exist");
386                         free(ad);
387                 }
388         }
389
390         check_chroot_filelist("file with root-hints", 
391                 cfg->root_hints, cfg->chrootdir, cfg);
392         check_chroot_filelist("trust-anchor-file", 
393                 cfg->trust_anchor_file_list, cfg->chrootdir, cfg);
394         check_chroot_filelist("auto-trust-anchor-file", 
395                 cfg->auto_trust_anchor_file_list, cfg->chrootdir, cfg);
396         check_chroot_filelist_wild("trusted-keys-file", 
397                 cfg->trusted_keys_file_list, cfg->chrootdir, cfg);
398         check_chroot_string("dlv-anchor-file", &cfg->dlv_anchor_file, 
399                 cfg->chrootdir, cfg);
400         /* remove chroot setting so that modules are not stripping pathnames*/
401         free(cfg->chrootdir);
402         cfg->chrootdir = NULL;
403         
404         if(strcmp(cfg->module_conf, "iterator") != 0 
405                 && strcmp(cfg->module_conf, "validator iterator") != 0
406                 && strcmp(cfg->module_conf, "dns64 validator iterator") != 0
407                 && strcmp(cfg->module_conf, "dns64 iterator") != 0
408 #ifdef WITH_PYTHONMODULE
409                 && strcmp(cfg->module_conf, "python iterator") != 0 
410                 && strcmp(cfg->module_conf, "python validator iterator") != 0 
411                 && strcmp(cfg->module_conf, "validator python iterator") != 0
412                 && strcmp(cfg->module_conf, "dns64 python iterator") != 0 
413                 && strcmp(cfg->module_conf, "dns64 python validator iterator") != 0 
414                 && strcmp(cfg->module_conf, "dns64 validator python iterator") != 0
415                 && strcmp(cfg->module_conf, "python dns64 iterator") != 0 
416                 && strcmp(cfg->module_conf, "python dns64 validator iterator") != 0 
417 #endif
418 #ifdef USE_CACHEDB
419                 && strcmp(cfg->module_conf, "validator cachedb iterator") != 0
420                 && strcmp(cfg->module_conf, "cachedb iterator") != 0
421                 && strcmp(cfg->module_conf, "dns64 validator cachedb iterator") != 0
422                 && strcmp(cfg->module_conf, "dns64 cachedb iterator") != 0
423                 && strcmp(cfg->module_conf, "python dns64 cachedb iterator") != 0
424                 && strcmp(cfg->module_conf, "python dns64 validator cachedb iterator") != 0
425                 && strcmp(cfg->module_conf, "dns64 python cachedb iterator") != 0
426                 && strcmp(cfg->module_conf, "dns64 python validator cachedb iterator") != 0
427                 && strcmp(cfg->module_conf, "python cachedb iterator") != 0
428                 && strcmp(cfg->module_conf, "python validator cachedb iterator") != 0
429                 && strcmp(cfg->module_conf, "cachedb python iterator") != 0
430                 && strcmp(cfg->module_conf, "validator cachedb python iterator") != 0
431                 && strcmp(cfg->module_conf, "validator python cachedb iterator") != 0
432 #endif
433                 ) {
434                 fatal_exit("module conf '%s' is not known to work",
435                         cfg->module_conf);
436         }
437
438 #ifdef HAVE_GETPWNAM
439         if(cfg->username && cfg->username[0]) {
440                 if(getpwnam(cfg->username) == NULL)
441                         fatal_exit("user '%s' does not exist.", cfg->username);
442 #  ifdef HAVE_ENDPWENT
443                 endpwent();
444 #  endif
445         }
446 #endif
447         if(cfg->remote_control_enable && cfg->remote_control_use_cert) {
448                 check_chroot_string("server-key-file", &cfg->server_key_file,
449                         cfg->chrootdir, cfg);
450                 check_chroot_string("server-cert-file", &cfg->server_cert_file,
451                         cfg->chrootdir, cfg);
452                 if(!is_file(cfg->control_key_file))
453                         fatal_exit("control-key-file: \"%s\" does not exist",
454                                 cfg->control_key_file);
455                 if(!is_file(cfg->control_cert_file))
456                         fatal_exit("control-cert-file: \"%s\" does not exist",
457                                 cfg->control_cert_file);
458         }
459
460         localzonechecks(cfg);
461 }
462
463 /** check forwards */
464 static void
465 check_fwd(struct config_file* cfg)
466 {
467         struct iter_forwards* fwd = forwards_create();
468         if(!fwd || !forwards_apply_cfg(fwd, cfg)) {
469                 fatal_exit("Could not set forward zones");
470         }
471         forwards_delete(fwd);
472 }
473
474 /** check hints */
475 static void
476 check_hints(struct config_file* cfg)
477 {
478         struct iter_hints* hints = hints_create();
479         if(!hints || !hints_apply_cfg(hints, cfg)) {
480                 fatal_exit("Could not set root or stub hints");
481         }
482         hints_delete(hints);
483 }
484
485 /** check config file */
486 static void
487 checkconf(const char* cfgfile, const char* opt, int final)
488 {
489         char oldwd[PATH_MAX];
490         struct config_file* cfg = config_create();
491         if(!cfg)
492                 fatal_exit("out of memory");
493         oldwd[0] = 0;
494         if(!getcwd(oldwd, sizeof(oldwd))) {
495                 log_err("cannot getcwd: %s", strerror(errno));
496                 oldwd[0] = 0;
497         }
498         if(!config_read(cfg, cfgfile, NULL)) {
499                 /* config_read prints messages to stderr */
500                 config_delete(cfg);
501                 exit(1);
502         }
503         if(oldwd[0] && chdir(oldwd) == -1)
504                 log_err("cannot chdir(%s): %s", oldwd, strerror(errno));
505         if(opt) {
506                 print_option(cfg, opt, final);
507                 config_delete(cfg);
508                 return;
509         }
510         morechecks(cfg, cfgfile);
511         check_mod(cfg, iter_get_funcblock());
512         check_mod(cfg, val_get_funcblock());
513 #ifdef WITH_PYTHONMODULE
514         if(strstr(cfg->module_conf, "python"))
515                 check_mod(cfg, pythonmod_get_funcblock());
516 #endif
517         check_fwd(cfg);
518         check_hints(cfg);
519         printf("unbound-checkconf: no errors in %s\n", cfgfile);
520         config_delete(cfg);
521 }
522
523 /** getopt global, in case header files fail to declare it. */
524 extern int optind;
525 /** getopt global, in case header files fail to declare it. */
526 extern char* optarg;
527
528 /** Main routine for checkconf */
529 int main(int argc, char* argv[])
530 {
531         int c;
532         int final = 0;
533         const char* f;
534         const char* opt = NULL;
535         const char* cfgfile = CONFIGFILE;
536         log_ident_set("unbound-checkconf");
537         log_init(NULL, 0, NULL);
538         checklock_start();
539 #ifdef USE_WINSOCK
540         /* use registry config file in preference to compiletime location */
541         if(!(cfgfile=w_lookup_reg_str("Software\\Unbound", "ConfigFile")))
542                 cfgfile = CONFIGFILE;
543 #endif /* USE_WINSOCK */
544         /* parse the options */
545         while( (c=getopt(argc, argv, "fho:")) != -1) {
546                 switch(c) {
547                 case 'f':
548                         final = 1;
549                         break;
550                 case 'o':
551                         opt = optarg;
552                         break;
553                 case '?':
554                 case 'h':
555                 default:
556                         usage();
557                 }
558         }
559         argc -= optind;
560         argv += optind;
561         if(argc != 0 && argc != 1)
562                 usage();
563         if(argc == 1)
564                 f = argv[0];
565         else    f = cfgfile;
566         checkconf(f, opt, final);
567         checklock_stop();
568         return 0;
569 }