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