2 * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2001-2003 Internet Software Consortium.
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
18 /* $Id: check.c,v 1.37.6.34 2006/03/02 00:37:20 marka Exp $ */
25 #include <isc/buffer.h>
28 #include <isc/netaddr.h>
29 #include <isc/parseint.h>
30 #include <isc/region.h>
31 #include <isc/result.h>
32 #include <isc/sockaddr.h>
33 #include <isc/symtab.h>
36 #include <dns/fixedname.h>
37 #include <dns/rdataclass.h>
38 #include <dns/rdatatype.h>
39 #include <dns/secalg.h>
41 #include <isccfg/cfg.h>
43 #include <bind9/check.h>
46 freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) {
49 isc_mem_free(userarg, key);
53 check_orderent(const cfg_obj_t *ent, isc_log_t *logctx) {
54 isc_result_t result = ISC_R_SUCCESS;
57 dns_fixedname_t fixed;
59 dns_rdataclass_t rdclass;
60 dns_rdatatype_t rdtype;
64 dns_fixedname_init(&fixed);
65 obj = cfg_tuple_get(ent, "class");
66 if (cfg_obj_isstring(obj)) {
68 DE_CONST(cfg_obj_asstring(obj), r.base);
69 r.length = strlen(r.base);
70 tresult = dns_rdataclass_fromtext(&rdclass, &r);
71 if (tresult != ISC_R_SUCCESS) {
72 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
73 "rrset-order: invalid class '%s'",
75 result = ISC_R_FAILURE;
79 obj = cfg_tuple_get(ent, "type");
80 if (cfg_obj_isstring(obj)) {
82 DE_CONST(cfg_obj_asstring(obj), r.base);
83 r.length = strlen(r.base);
84 tresult = dns_rdatatype_fromtext(&rdtype, &r);
85 if (tresult != ISC_R_SUCCESS) {
86 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
87 "rrset-order: invalid type '%s'",
89 result = ISC_R_FAILURE;
93 obj = cfg_tuple_get(ent, "name");
94 if (cfg_obj_isstring(obj)) {
95 str = cfg_obj_asstring(obj);
96 isc_buffer_init(&b, str, strlen(str));
97 isc_buffer_add(&b, strlen(str));
98 tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
99 dns_rootname, ISC_FALSE, NULL);
100 if (tresult != ISC_R_SUCCESS) {
101 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
102 "rrset-order: invalid name '%s'", str);
103 result = ISC_R_FAILURE;
107 obj = cfg_tuple_get(ent, "order");
108 if (!cfg_obj_isstring(obj) ||
109 strcasecmp("order", cfg_obj_asstring(obj)) != 0) {
110 cfg_obj_log(ent, logctx, ISC_LOG_ERROR,
111 "rrset-order: keyword 'order' missing");
112 result = ISC_R_FAILURE;
115 obj = cfg_tuple_get(ent, "ordering");
116 if (!cfg_obj_isstring(obj)) {
117 cfg_obj_log(ent, logctx, ISC_LOG_ERROR,
118 "rrset-order: missing ordering");
119 result = ISC_R_FAILURE;
120 } else if (strcasecmp(cfg_obj_asstring(obj), "fixed") == 0) {
121 cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
122 "rrset-order: order 'fixed' not fully implemented");
123 } else if (/* strcasecmp(cfg_obj_asstring(obj), "fixed") != 0 && */
124 strcasecmp(cfg_obj_asstring(obj), "random") != 0 &&
125 strcasecmp(cfg_obj_asstring(obj), "cyclic") != 0) {
126 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
127 "rrset-order: invalid order '%s'",
128 cfg_obj_asstring(obj));
129 result = ISC_R_FAILURE;
135 check_order(const cfg_obj_t *options, isc_log_t *logctx) {
136 isc_result_t result = ISC_R_SUCCESS;
137 isc_result_t tresult;
138 const cfg_listelt_t *element;
139 const cfg_obj_t *obj = NULL;
141 if (cfg_map_get(options, "rrset-order", &obj) != ISC_R_SUCCESS)
144 for (element = cfg_list_first(obj);
146 element = cfg_list_next(element))
148 tresult = check_orderent(cfg_listelt_value(element), logctx);
149 if (tresult != ISC_R_SUCCESS)
156 check_dual_stack(const cfg_obj_t *options, isc_log_t *logctx) {
157 const cfg_listelt_t *element;
158 const cfg_obj_t *alternates = NULL;
159 const cfg_obj_t *value;
160 const cfg_obj_t *obj;
162 dns_fixedname_t fixed;
165 isc_result_t result = ISC_R_SUCCESS;
166 isc_result_t tresult;
168 (void)cfg_map_get(options, "dual-stack-servers", &alternates);
170 if (alternates == NULL)
171 return (ISC_R_SUCCESS);
173 obj = cfg_tuple_get(alternates, "port");
174 if (cfg_obj_isuint32(obj)) {
175 isc_uint32_t val = cfg_obj_asuint32(obj);
176 if (val > ISC_UINT16_MAX) {
177 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
178 "port '%u' out of range", val);
179 result = ISC_R_FAILURE;
182 obj = cfg_tuple_get(alternates, "addresses");
183 for (element = cfg_list_first(obj);
185 element = cfg_list_next(element)) {
186 value = cfg_listelt_value(element);
187 if (cfg_obj_issockaddr(value))
189 obj = cfg_tuple_get(value, "name");
190 str = cfg_obj_asstring(obj);
191 isc_buffer_init(&buffer, str, strlen(str));
192 isc_buffer_add(&buffer, strlen(str));
193 dns_fixedname_init(&fixed);
194 name = dns_fixedname_name(&fixed);
195 tresult = dns_name_fromtext(name, &buffer, dns_rootname,
197 if (tresult != ISC_R_SUCCESS) {
198 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
199 "bad name '%s'", str);
200 result = ISC_R_FAILURE;
202 obj = cfg_tuple_get(value, "port");
203 if (cfg_obj_isuint32(obj)) {
204 isc_uint32_t val = cfg_obj_asuint32(obj);
205 if (val > ISC_UINT16_MAX) {
206 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
207 "port '%u' out of range", val);
208 result = ISC_R_FAILURE;
216 check_forward(const cfg_obj_t *options, isc_log_t *logctx) {
217 const cfg_obj_t *forward = NULL;
218 const cfg_obj_t *forwarders = NULL;
220 (void)cfg_map_get(options, "forward", &forward);
221 (void)cfg_map_get(options, "forwarders", &forwarders);
223 if (forward != NULL && forwarders == NULL) {
224 cfg_obj_log(forward, logctx, ISC_LOG_ERROR,
225 "no matching 'forwarders' statement");
226 return (ISC_R_FAILURE);
228 return (ISC_R_SUCCESS);
232 disabled_algorithms(const cfg_obj_t *disabled, isc_log_t *logctx) {
233 isc_result_t result = ISC_R_SUCCESS;
234 isc_result_t tresult;
235 const cfg_listelt_t *element;
238 dns_fixedname_t fixed;
240 const cfg_obj_t *obj;
242 dns_fixedname_init(&fixed);
243 name = dns_fixedname_name(&fixed);
244 obj = cfg_tuple_get(disabled, "name");
245 str = cfg_obj_asstring(obj);
246 isc_buffer_init(&b, str, strlen(str));
247 isc_buffer_add(&b, strlen(str));
248 tresult = dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL);
249 if (tresult != ISC_R_SUCCESS) {
250 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
251 "bad domain name '%s'", str);
255 obj = cfg_tuple_get(disabled, "algorithms");
257 for (element = cfg_list_first(obj);
259 element = cfg_list_next(element))
263 isc_result_t tresult;
265 DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base);
266 r.length = strlen(r.base);
268 tresult = dns_secalg_fromtext(&alg, &r);
269 if (tresult != ISC_R_SUCCESS) {
271 result = isc_parse_uint8(&ui, r.base, 10);
273 if (tresult != ISC_R_SUCCESS) {
274 cfg_obj_log(cfg_listelt_value(element), logctx,
275 ISC_LOG_ERROR, "invalid algorithm");
283 nameexist(const cfg_obj_t *obj, const char *name, int value,
284 isc_symtab_t *symtab, const char *fmt, isc_log_t *logctx,
291 isc_symvalue_t symvalue;
293 key = isc_mem_strdup(mctx, name);
295 return (ISC_R_NOMEMORY);
296 symvalue.as_cpointer = obj;
297 result = isc_symtab_define(symtab, key, value, symvalue,
298 isc_symexists_reject);
299 if (result == ISC_R_EXISTS) {
300 RUNTIME_CHECK(isc_symtab_lookup(symtab, key, value,
301 &symvalue) == ISC_R_SUCCESS);
302 file = cfg_obj_file(symvalue.as_cpointer);
303 line = cfg_obj_line(symvalue.as_cpointer);
306 file = "<unknown file>";
307 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, fmt, key, file, line);
308 isc_mem_free(mctx, key);
309 result = ISC_R_EXISTS;
310 } else if (result != ISC_R_SUCCESS) {
311 isc_mem_free(mctx, key);
317 mustbesecure(const cfg_obj_t *secure, isc_symtab_t *symtab, isc_log_t *logctx,
320 const cfg_obj_t *obj;
321 char namebuf[DNS_NAME_FORMATSIZE];
323 dns_fixedname_t fixed;
326 isc_result_t result = ISC_R_SUCCESS;
328 dns_fixedname_init(&fixed);
329 name = dns_fixedname_name(&fixed);
330 obj = cfg_tuple_get(secure, "name");
331 str = cfg_obj_asstring(obj);
332 isc_buffer_init(&b, str, strlen(str));
333 isc_buffer_add(&b, strlen(str));
334 result = dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL);
335 if (result != ISC_R_SUCCESS) {
336 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
337 "bad domain name '%s'", str);
339 dns_name_format(name, namebuf, sizeof(namebuf));
340 result = nameexist(secure, namebuf, 1, symtab,
341 "dnssec-must-be-secure '%s': already "
342 "exists previous definition: %s:%u",
355 check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx) {
356 isc_result_t result = ISC_R_SUCCESS;
357 isc_result_t tresult;
359 const cfg_obj_t *obj = NULL;
360 const cfg_listelt_t *element;
361 isc_symtab_t *symtab = NULL;
363 static intervaltable intervals[] = {
364 { "cleaning-interval", 60, 28 * 24 * 60 }, /* 28 days */
365 { "heartbeat-interval", 60, 28 * 24 * 60 }, /* 28 days */
366 { "interface-interval", 60, 28 * 24 * 60 }, /* 28 days */
367 { "max-transfer-idle-in", 60, 28 * 24 * 60 }, /* 28 days */
368 { "max-transfer-idle-out", 60, 28 * 24 * 60 }, /* 28 days */
369 { "max-transfer-time-in", 60, 28 * 24 * 60 }, /* 28 days */
370 { "max-transfer-time-out", 60, 28 * 24 * 60 }, /* 28 days */
371 { "sig-validity-interval", 86400, 10 * 366 }, /* 10 years */
372 { "statistics-interval", 60, 28 * 24 * 60 }, /* 28 days */
376 * Check that fields specified in units of time other than seconds
377 * have reasonable values.
379 for (i = 0; i < sizeof(intervals) / sizeof(intervals[0]); i++) {
382 (void)cfg_map_get(options, intervals[i].name, &obj);
385 val = cfg_obj_asuint32(obj);
386 if (val > intervals[i].max) {
387 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
388 "%s '%u' is out of range (0..%u)",
389 intervals[i].name, val,
391 result = ISC_R_RANGE;
392 } else if (val > (ISC_UINT32_MAX / intervals[i].scale)) {
393 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
394 "%s '%d' is out of range",
395 intervals[i].name, val);
396 result = ISC_R_RANGE;
400 (void)cfg_map_get(options, "preferred-glue", &obj);
403 str = cfg_obj_asstring(obj);
404 if (strcasecmp(str, "a") != 0 &&
405 strcasecmp(str, "aaaa") != 0 &&
406 strcasecmp(str, "none") != 0)
407 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
408 "preferred-glue unexpected value '%s'",
412 (void)cfg_map_get(options, "root-delegation-only", &obj);
414 if (!cfg_obj_isvoid(obj)) {
415 const cfg_listelt_t *element;
416 const cfg_obj_t *exclude;
418 dns_fixedname_t fixed;
422 dns_fixedname_init(&fixed);
423 name = dns_fixedname_name(&fixed);
424 for (element = cfg_list_first(obj);
426 element = cfg_list_next(element)) {
427 exclude = cfg_listelt_value(element);
428 str = cfg_obj_asstring(exclude);
429 isc_buffer_init(&b, str, strlen(str));
430 isc_buffer_add(&b, strlen(str));
431 tresult = dns_name_fromtext(name, &b,
434 if (tresult != ISC_R_SUCCESS) {
435 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
436 "bad domain name '%s'",
445 * Set supported DNSSEC algorithms.
448 (void)cfg_map_get(options, "disable-algorithms", &obj);
450 for (element = cfg_list_first(obj);
452 element = cfg_list_next(element))
454 obj = cfg_listelt_value(element);
455 tresult = disabled_algorithms(obj, logctx);
456 if (tresult != ISC_R_SUCCESS)
462 * Check the DLV zone name.
465 (void)cfg_map_get(options, "dnssec-lookaside", &obj);
467 tresult = isc_symtab_create(mctx, 100, freekey, mctx,
469 if (tresult != ISC_R_SUCCESS)
471 for (element = cfg_list_first(obj);
473 element = cfg_list_next(element))
475 dns_fixedname_t fixedname;
480 obj = cfg_listelt_value(element);
482 dlv = cfg_obj_asstring(cfg_tuple_get(obj, "domain"));
483 dns_fixedname_init(&fixedname);
484 name = dns_fixedname_name(&fixedname);
485 isc_buffer_init(&b, dlv, strlen(dlv));
486 isc_buffer_add(&b, strlen(dlv));
487 tresult = dns_name_fromtext(name, &b, dns_rootname,
489 if (tresult != ISC_R_SUCCESS) {
490 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
491 "bad domain name '%s'", dlv);
494 if (symtab != NULL) {
495 tresult = nameexist(obj, dlv, 1, symtab,
496 "dnssec-lookaside '%s': "
497 "already exists previous "
500 if (tresult != ISC_R_SUCCESS &&
501 result == ISC_R_SUCCESS)
505 * XXXMPA to be removed when multiple lookaside
506 * namespaces are supported.
508 if (!dns_name_equal(dns_rootname, name)) {
509 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
510 "dnssec-lookaside '%s': "
511 "non-root not yet supported", dlv);
512 if (result == ISC_R_SUCCESS)
513 result = ISC_R_FAILURE;
515 dlv = cfg_obj_asstring(cfg_tuple_get(obj,
517 dns_fixedname_init(&fixedname);
518 isc_buffer_init(&b, dlv, strlen(dlv));
519 isc_buffer_add(&b, strlen(dlv));
520 tresult = dns_name_fromtext(name, &b, dns_rootname,
522 if (tresult != ISC_R_SUCCESS) {
523 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
524 "bad domain name '%s'", dlv);
525 if (result == ISC_R_SUCCESS)
530 isc_symtab_destroy(&symtab);
534 * Check dnssec-must-be-secure.
537 (void)cfg_map_get(options, "dnssec-must-be-secure", &obj);
539 isc_symtab_t *symtab = NULL;
540 tresult = isc_symtab_create(mctx, 100, freekey, mctx,
542 if (tresult != ISC_R_SUCCESS)
544 for (element = cfg_list_first(obj);
546 element = cfg_list_next(element))
548 obj = cfg_listelt_value(element);
549 tresult = mustbesecure(obj, symtab, logctx, mctx);
550 if (tresult != ISC_R_SUCCESS)
554 isc_symtab_destroy(&symtab);
561 get_masters_def(const cfg_obj_t *cctx, const char *name, const cfg_obj_t **ret) {
563 const cfg_obj_t *masters = NULL;
564 const cfg_listelt_t *elt;
566 result = cfg_map_get(cctx, "masters", &masters);
567 if (result != ISC_R_SUCCESS)
569 for (elt = cfg_list_first(masters);
571 elt = cfg_list_next(elt)) {
572 const cfg_obj_t *list;
573 const char *listname;
575 list = cfg_listelt_value(elt);
576 listname = cfg_obj_asstring(cfg_tuple_get(list, "name"));
578 if (strcasecmp(listname, name) == 0) {
580 return (ISC_R_SUCCESS);
583 return (ISC_R_NOTFOUND);
587 validate_masters(const cfg_obj_t *obj, const cfg_obj_t *config,
588 isc_uint32_t *countp, isc_log_t *logctx, isc_mem_t *mctx)
590 isc_result_t result = ISC_R_SUCCESS;
591 isc_result_t tresult;
592 isc_uint32_t count = 0;
593 isc_symtab_t *symtab = NULL;
594 isc_symvalue_t symvalue;
595 const cfg_listelt_t *element;
596 const cfg_listelt_t **stack = NULL;
597 isc_uint32_t stackcount = 0, pushed = 0;
598 const cfg_obj_t *list;
600 REQUIRE(countp != NULL);
601 result = isc_symtab_create(mctx, 100, NULL, NULL, ISC_FALSE, &symtab);
602 if (result != ISC_R_SUCCESS) {
608 list = cfg_tuple_get(obj, "addresses");
609 element = cfg_list_first(list);
613 element = cfg_list_next(element))
615 const char *listname;
616 const cfg_obj_t *addr;
617 const cfg_obj_t *key;
619 addr = cfg_tuple_get(cfg_listelt_value(element),
621 key = cfg_tuple_get(cfg_listelt_value(element), "key");
623 if (cfg_obj_issockaddr(addr)) {
627 if (!cfg_obj_isvoid(key)) {
628 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
629 "unexpected token '%s'",
630 cfg_obj_asstring(key));
631 if (result == ISC_R_SUCCESS)
632 result = ISC_R_FAILURE;
634 listname = cfg_obj_asstring(addr);
635 symvalue.as_cpointer = addr;
636 tresult = isc_symtab_define(symtab, listname, 1, symvalue,
637 isc_symexists_reject);
638 if (tresult == ISC_R_EXISTS)
640 tresult = get_masters_def(config, listname, &obj);
641 if (tresult != ISC_R_SUCCESS) {
642 if (result == ISC_R_SUCCESS)
644 cfg_obj_log(addr, logctx, ISC_LOG_ERROR,
645 "unable to find masters list '%s'",
650 if (stackcount == pushed) {
652 isc_uint32_t newlen = stackcount + 16;
653 size_t newsize, oldsize;
655 newsize = newlen * sizeof(*stack);
656 oldsize = stackcount * sizeof(*stack);
657 new = isc_mem_get(mctx, newsize);
660 if (stackcount != 0) {
661 memcpy(new, stack, oldsize);
662 isc_mem_put(mctx, stack, oldsize);
667 stack[pushed++] = cfg_list_next(element);
671 element = stack[--pushed];
676 isc_mem_put(mctx, stack, stackcount * sizeof(*stack));
677 isc_symtab_destroy(&symtab);
686 #define FORWARDZONE 16
687 #define DELEGATIONZONE 32
695 check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *config,
696 isc_symtab_t *symtab, dns_rdataclass_t defclass,
697 isc_log_t *logctx, isc_mem_t *mctx)
702 const cfg_obj_t *zoptions;
703 const cfg_obj_t *obj = NULL;
704 isc_result_t result = ISC_R_SUCCESS;
705 isc_result_t tresult;
707 dns_rdataclass_t zclass;
708 dns_fixedname_t fixedname;
711 static optionstable options[] = {
712 { "allow-query", MASTERZONE | SLAVEZONE | STUBZONE },
713 { "allow-notify", SLAVEZONE },
714 { "allow-transfer", MASTERZONE | SLAVEZONE },
715 { "notify", MASTERZONE | SLAVEZONE },
716 { "also-notify", MASTERZONE | SLAVEZONE },
717 { "dialup", MASTERZONE | SLAVEZONE | STUBZONE },
718 { "delegation-only", HINTZONE | STUBZONE },
719 { "forward", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE},
720 { "forwarders", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE},
721 { "maintain-ixfr-base", MASTERZONE | SLAVEZONE },
722 { "max-ixfr-log-size", MASTERZONE | SLAVEZONE },
723 { "notify-source", MASTERZONE | SLAVEZONE },
724 { "notify-source-v6", MASTERZONE | SLAVEZONE },
725 { "transfer-source", SLAVEZONE | STUBZONE },
726 { "transfer-source-v6", SLAVEZONE | STUBZONE },
727 { "max-transfer-time-in", SLAVEZONE | STUBZONE },
728 { "max-transfer-time-out", MASTERZONE | SLAVEZONE },
729 { "max-transfer-idle-in", SLAVEZONE | STUBZONE },
730 { "max-transfer-idle-out", MASTERZONE | SLAVEZONE },
731 { "max-retry-time", SLAVEZONE | STUBZONE },
732 { "min-retry-time", SLAVEZONE | STUBZONE },
733 { "max-refresh-time", SLAVEZONE | STUBZONE },
734 { "min-refresh-time", SLAVEZONE | STUBZONE },
735 { "sig-validity-interval", MASTERZONE },
736 { "zone-statistics", MASTERZONE | SLAVEZONE | STUBZONE },
737 { "allow-update", MASTERZONE },
738 { "allow-update-forwarding", SLAVEZONE },
739 { "file", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE },
740 { "ixfr-base", MASTERZONE | SLAVEZONE },
741 { "ixfr-tmp-file", MASTERZONE | SLAVEZONE },
742 { "masters", SLAVEZONE | STUBZONE },
743 { "pubkey", MASTERZONE | SLAVEZONE | STUBZONE },
744 { "update-policy", MASTERZONE },
745 { "database", MASTERZONE | SLAVEZONE | STUBZONE },
746 { "key-directory", MASTERZONE },
749 static optionstable dialups[] = {
750 { "notify", MASTERZONE | SLAVEZONE },
751 { "notify-passive", SLAVEZONE },
752 { "refresh", SLAVEZONE | STUBZONE },
753 { "passive", SLAVEZONE | STUBZONE },
756 zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
758 zoptions = cfg_tuple_get(zconfig, "options");
761 (void)cfg_map_get(zoptions, "type", &obj);
763 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
764 "zone '%s': type not present", zname);
765 return (ISC_R_FAILURE);
768 typestr = cfg_obj_asstring(obj);
769 if (strcasecmp(typestr, "master") == 0)
771 else if (strcasecmp(typestr, "slave") == 0)
773 else if (strcasecmp(typestr, "stub") == 0)
775 else if (strcasecmp(typestr, "forward") == 0)
777 else if (strcasecmp(typestr, "hint") == 0)
779 else if (strcasecmp(typestr, "delegation-only") == 0)
780 ztype = DELEGATIONZONE;
782 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
783 "zone '%s': invalid type %s",
785 return (ISC_R_FAILURE);
788 obj = cfg_tuple_get(zconfig, "class");
789 if (cfg_obj_isstring(obj)) {
792 DE_CONST(cfg_obj_asstring(obj), r.base);
793 r.length = strlen(r.base);
794 result = dns_rdataclass_fromtext(&zclass, &r);
795 if (result != ISC_R_SUCCESS) {
796 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
797 "zone '%s': invalid class %s",
799 return (ISC_R_FAILURE);
801 if (zclass != defclass) {
802 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
803 "zone '%s': class '%s' does not "
804 "match view/default class",
806 return (ISC_R_FAILURE);
811 * Look for an already existing zone.
812 * We need to make this cannonical as isc_symtab_define()
813 * deals with strings.
815 dns_fixedname_init(&fixedname);
816 isc_buffer_init(&b, zname, strlen(zname));
817 isc_buffer_add(&b, strlen(zname));
818 tresult = dns_name_fromtext(dns_fixedname_name(&fixedname), &b,
819 dns_rootname, ISC_TRUE, NULL);
820 if (result != ISC_R_SUCCESS) {
821 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
822 "zone '%s': is not a valid name", zname);
823 tresult = ISC_R_FAILURE;
825 char namebuf[DNS_NAME_FORMATSIZE];
827 dns_name_format(dns_fixedname_name(&fixedname),
828 namebuf, sizeof(namebuf));
829 tresult = nameexist(zconfig, namebuf, ztype == HINTZONE ? 1 : 2,
830 symtab, "zone '%s': already exists "
831 "previous definition: %s:%u", logctx, mctx);
832 if (tresult != ISC_R_SUCCESS)
837 * Look for inappropriate options for the given zone type.
839 for (i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
841 if ((options[i].allowed & ztype) == 0 &&
842 cfg_map_get(zoptions, options[i].name, &obj) ==
845 if (strcmp(options[i].name, "allow-update") != 0 ||
846 ztype != SLAVEZONE) {
847 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
848 "option '%s' is not allowed "
850 options[i].name, typestr, zname);
851 result = ISC_R_FAILURE;
853 cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
854 "option '%s' is not allowed "
856 options[i].name, typestr, zname);
861 * Slave & stub zones must have a "masters" field.
863 if (ztype == SLAVEZONE || ztype == STUBZONE) {
865 if (cfg_map_get(zoptions, "masters", &obj) != ISC_R_SUCCESS) {
866 cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
867 "zone '%s': missing 'masters' entry",
869 result = ISC_R_FAILURE;
872 tresult = validate_masters(obj, config, &count,
874 if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS)
876 if (tresult == ISC_R_SUCCESS && count == 0) {
877 cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
878 "zone '%s': empty 'masters' entry",
880 result = ISC_R_FAILURE;
886 * Master zones can't have both "allow-update" and "update-policy".
888 if (ztype == MASTERZONE) {
889 isc_result_t res1, res2;
891 res1 = cfg_map_get(zoptions, "allow-update", &obj);
893 res2 = cfg_map_get(zoptions, "update-policy", &obj);
894 if (res1 == ISC_R_SUCCESS && res2 == ISC_R_SUCCESS) {
895 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
896 "zone '%s': 'allow-update' is ignored "
897 "when 'update-policy' is present",
899 result = ISC_R_FAILURE;
904 * Check the excessively complicated "dialup" option.
906 if (ztype == MASTERZONE || ztype == SLAVEZONE || ztype == STUBZONE) {
907 const cfg_obj_t *dialup = NULL;
908 (void)cfg_map_get(zoptions, "dialup", &dialup);
909 if (dialup != NULL && cfg_obj_isstring(dialup)) {
910 const char *str = cfg_obj_asstring(dialup);
912 i < sizeof(dialups) / sizeof(dialups[0]);
915 if (strcasecmp(dialups[i].name, str) != 0)
917 if ((dialups[i].allowed & ztype) == 0) {
918 cfg_obj_log(obj, logctx,
920 "dialup type '%s' is not "
923 str, typestr, zname);
924 result = ISC_R_FAILURE;
928 if (i == sizeof(dialups) / sizeof(dialups[0])) {
929 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
930 "invalid dialup type '%s' in zone "
932 result = ISC_R_FAILURE;
938 * Check that forwarding is reasonable.
940 if (check_forward(zoptions, logctx) != ISC_R_SUCCESS)
941 result = ISC_R_FAILURE;
944 * Check various options.
946 tresult = check_options(zoptions, logctx, mctx);
947 if (tresult != ISC_R_SUCCESS)
951 * If the zone type is rbt/rbt64 then master/hint zones
952 * require file clauses.
955 tresult = cfg_map_get(zoptions, "database", &obj);
956 if (tresult == ISC_R_NOTFOUND ||
957 (tresult == ISC_R_SUCCESS &&
958 (strcmp("rbt", cfg_obj_asstring(obj)) == 0 ||
959 strcmp("rbt64", cfg_obj_asstring(obj)) == 0))) {
961 tresult = cfg_map_get(zoptions, "file", &obj);
962 if (tresult != ISC_R_SUCCESS &&
963 (ztype == MASTERZONE || ztype == HINTZONE)) {
964 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
965 "zone '%s': missing 'file' entry",
975 bind9_check_key(const cfg_obj_t *key, isc_log_t *logctx) {
976 const cfg_obj_t *algobj = NULL;
977 const cfg_obj_t *secretobj = NULL;
978 const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
980 (void)cfg_map_get(key, "algorithm", &algobj);
981 (void)cfg_map_get(key, "secret", &secretobj);
982 if (secretobj == NULL || algobj == NULL) {
983 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
984 "key '%s' must have both 'secret' and "
985 "'algorithm' defined",
987 return (ISC_R_FAILURE);
989 return (ISC_R_SUCCESS);
993 check_keylist(const cfg_obj_t *keys, isc_symtab_t *symtab, isc_log_t *logctx) {
994 isc_result_t result = ISC_R_SUCCESS;
995 isc_result_t tresult;
996 const cfg_listelt_t *element;
998 for (element = cfg_list_first(keys);
1000 element = cfg_list_next(element))
1002 const cfg_obj_t *key = cfg_listelt_value(element);
1003 const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
1004 isc_symvalue_t symvalue;
1006 symvalue.as_cpointer = key;
1007 tresult = isc_symtab_define(symtab, keyname, 1,
1008 symvalue, isc_symexists_reject);
1009 if (tresult == ISC_R_EXISTS) {
1013 RUNTIME_CHECK(isc_symtab_lookup(symtab, keyname,
1014 1, &symvalue) == ISC_R_SUCCESS);
1015 file = cfg_obj_file(symvalue.as_cpointer);
1016 line = cfg_obj_line(symvalue.as_cpointer);
1019 file = "<unknown file>";
1020 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
1021 "key '%s': already exists "
1022 "previous definition: %s:%u",
1023 keyname, file, line);
1025 } else if (tresult != ISC_R_SUCCESS)
1028 tresult = bind9_check_key(key, logctx);
1029 if (tresult != ISC_R_SUCCESS)
1036 check_servers(const cfg_obj_t *servers, isc_log_t *logctx) {
1037 isc_result_t result = ISC_R_SUCCESS;
1038 const cfg_listelt_t *e1;
1039 const cfg_listelt_t *e2;
1040 const cfg_obj_t *v1;
1041 const cfg_obj_t *v2;
1042 const isc_sockaddr_t *s1;
1043 const isc_sockaddr_t *s2;
1045 const cfg_obj_t *ts;
1048 isc_buffer_t target;
1050 for (e1 = cfg_list_first(servers); e1 != NULL; e1 = cfg_list_next(e1)) {
1051 v1 = cfg_listelt_value(e1);
1052 s1 = cfg_obj_assockaddr(cfg_map_getname(v1));
1054 if (isc_sockaddr_pf(s1) == AF_INET)
1055 xfr = "transfer-source-v6";
1057 xfr = "transfer-source";
1058 (void)cfg_map_get(v1, xfr, &ts);
1060 isc_netaddr_fromsockaddr(&na, s1);
1061 isc_buffer_init(&target, buf, sizeof(buf) - 1);
1062 RUNTIME_CHECK(isc_netaddr_totext(&na, &target)
1064 buf[isc_buffer_usedlength(&target)] = '\0';
1065 cfg_obj_log(v1, logctx, ISC_LOG_ERROR,
1066 "server '%s': %s not valid", buf, xfr);
1067 result = ISC_R_FAILURE;
1070 while ((e2 = cfg_list_next(e2)) != NULL) {
1071 v2 = cfg_listelt_value(e2);
1072 s2 = cfg_obj_assockaddr(cfg_map_getname(v2));
1073 if (isc_sockaddr_eqaddr(s1, s2)) {
1074 const char *file = cfg_obj_file(v1);
1075 unsigned int line = cfg_obj_line(v1);
1078 file = "<unknown file>";
1080 isc_netaddr_fromsockaddr(&na, s2);
1081 isc_buffer_init(&target, buf, sizeof(buf) - 1);
1082 RUNTIME_CHECK(isc_netaddr_totext(&na, &target)
1084 buf[isc_buffer_usedlength(&target)] = '\0';
1086 cfg_obj_log(v2, logctx, ISC_LOG_ERROR,
1087 "server '%s': already exists "
1088 "previous definition: %s:%u",
1090 result = ISC_R_FAILURE;
1098 check_viewconf(const cfg_obj_t *config, const cfg_obj_t *vconfig,
1099 dns_rdataclass_t vclass, isc_log_t *logctx, isc_mem_t *mctx)
1101 const cfg_obj_t *servers = NULL;
1102 const cfg_obj_t *zones = NULL;
1103 const cfg_obj_t *keys = NULL;
1104 const cfg_listelt_t *element;
1105 isc_symtab_t *symtab = NULL;
1106 isc_result_t result = ISC_R_SUCCESS;
1107 isc_result_t tresult = ISC_R_SUCCESS;
1110 * Check that all zone statements are syntactically correct and
1111 * there are no duplicate zones.
1113 tresult = isc_symtab_create(mctx, 100, freekey, mctx,
1114 ISC_FALSE, &symtab);
1115 if (tresult != ISC_R_SUCCESS)
1116 return (ISC_R_NOMEMORY);
1118 if (vconfig != NULL)
1119 (void)cfg_map_get(vconfig, "zone", &zones);
1121 (void)cfg_map_get(config, "zone", &zones);
1123 for (element = cfg_list_first(zones);
1125 element = cfg_list_next(element))
1127 isc_result_t tresult;
1128 const cfg_obj_t *zone = cfg_listelt_value(element);
1130 tresult = check_zoneconf(zone, config, symtab, vclass,
1132 if (tresult != ISC_R_SUCCESS)
1133 result = ISC_R_FAILURE;
1136 isc_symtab_destroy(&symtab);
1139 * Check that all key statements are syntactically correct and
1140 * there are no duplicate keys.
1142 tresult = isc_symtab_create(mctx, 100, NULL, NULL, ISC_TRUE, &symtab);
1143 if (tresult != ISC_R_SUCCESS)
1144 return (ISC_R_NOMEMORY);
1146 (void)cfg_map_get(config, "key", &keys);
1147 tresult = check_keylist(keys, symtab, logctx);
1148 if (tresult == ISC_R_EXISTS)
1149 result = ISC_R_FAILURE;
1150 else if (tresult != ISC_R_SUCCESS) {
1151 isc_symtab_destroy(&symtab);
1155 if (vconfig != NULL) {
1157 (void)cfg_map_get(vconfig, "key", &keys);
1158 tresult = check_keylist(keys, symtab, logctx);
1159 if (tresult == ISC_R_EXISTS)
1160 result = ISC_R_FAILURE;
1161 else if (tresult != ISC_R_SUCCESS) {
1162 isc_symtab_destroy(&symtab);
1167 isc_symtab_destroy(&symtab);
1170 * Check that forwarding is reasonable.
1172 if (vconfig == NULL) {
1173 const cfg_obj_t *options = NULL;
1174 (void)cfg_map_get(config, "options", &options);
1175 if (options != NULL)
1176 if (check_forward(options, logctx) != ISC_R_SUCCESS)
1177 result = ISC_R_FAILURE;
1179 if (check_forward(vconfig, logctx) != ISC_R_SUCCESS)
1180 result = ISC_R_FAILURE;
1183 * Check that dual-stack-servers is reasonable.
1185 if (vconfig == NULL) {
1186 const cfg_obj_t *options = NULL;
1187 (void)cfg_map_get(config, "options", &options);
1188 if (options != NULL)
1189 if (check_dual_stack(options, logctx) != ISC_R_SUCCESS)
1190 result = ISC_R_FAILURE;
1192 if (check_dual_stack(vconfig, logctx) != ISC_R_SUCCESS)
1193 result = ISC_R_FAILURE;
1197 * Check that rrset-order is reasonable.
1199 if (vconfig != NULL) {
1200 if (check_order(vconfig, logctx) != ISC_R_SUCCESS)
1201 result = ISC_R_FAILURE;
1204 if (vconfig != NULL) {
1205 (void)cfg_map_get(vconfig, "server", &servers);
1206 if (servers != NULL &&
1207 check_servers(servers, logctx) != ISC_R_SUCCESS)
1208 result = ISC_R_FAILURE;
1211 if (vconfig != NULL)
1212 tresult = check_options(vconfig, logctx, mctx);
1214 tresult = check_options(config, logctx, mctx);
1215 if (tresult != ISC_R_SUCCESS)
1223 bind9_check_namedconf(const cfg_obj_t *config, isc_log_t *logctx,
1226 const cfg_obj_t *options = NULL;
1227 const cfg_obj_t *servers = NULL;
1228 const cfg_obj_t *views = NULL;
1229 const cfg_obj_t *acls = NULL;
1230 const cfg_obj_t *kals = NULL;
1231 const cfg_obj_t *obj;
1232 const cfg_listelt_t *velement;
1233 isc_result_t result = ISC_R_SUCCESS;
1234 isc_result_t tresult;
1235 isc_symtab_t *symtab = NULL;
1237 static const char *builtin[] = { "localhost", "localnets",
1240 (void)cfg_map_get(config, "options", &options);
1242 if (options != NULL &&
1243 check_options(options, logctx, mctx) != ISC_R_SUCCESS)
1244 result = ISC_R_FAILURE;
1246 (void)cfg_map_get(config, "server", &servers);
1247 if (servers != NULL &&
1248 check_servers(servers, logctx) != ISC_R_SUCCESS)
1249 result = ISC_R_FAILURE;
1251 if (options != NULL &&
1252 check_order(options, logctx) != ISC_R_SUCCESS)
1253 result = ISC_R_FAILURE;
1255 (void)cfg_map_get(config, "view", &views);
1257 if (views != NULL && options != NULL)
1258 if (check_dual_stack(options, logctx) != ISC_R_SUCCESS)
1259 result = ISC_R_FAILURE;
1261 if (views == NULL) {
1262 if (check_viewconf(config, NULL, dns_rdataclass_in,
1263 logctx, mctx) != ISC_R_SUCCESS)
1264 result = ISC_R_FAILURE;
1266 const cfg_obj_t *zones = NULL;
1268 (void)cfg_map_get(config, "zone", &zones);
1269 if (zones != NULL) {
1270 cfg_obj_log(zones, logctx, ISC_LOG_ERROR,
1271 "when using 'view' statements, "
1272 "all zones must be in views");
1273 result = ISC_R_FAILURE;
1277 tresult = isc_symtab_create(mctx, 100, NULL, NULL, ISC_TRUE, &symtab);
1278 if (tresult != ISC_R_SUCCESS)
1280 for (velement = cfg_list_first(views);
1282 velement = cfg_list_next(velement))
1284 const cfg_obj_t *view = cfg_listelt_value(velement);
1285 const cfg_obj_t *vname = cfg_tuple_get(view, "name");
1286 const cfg_obj_t *voptions = cfg_tuple_get(view, "options");
1287 const cfg_obj_t *vclassobj = cfg_tuple_get(view, "class");
1288 dns_rdataclass_t vclass = dns_rdataclass_in;
1289 isc_result_t tresult = ISC_R_SUCCESS;
1290 const char *key = cfg_obj_asstring(vname);
1291 isc_symvalue_t symvalue;
1293 if (cfg_obj_isstring(vclassobj)) {
1296 DE_CONST(cfg_obj_asstring(vclassobj), r.base);
1297 r.length = strlen(r.base);
1298 tresult = dns_rdataclass_fromtext(&vclass, &r);
1299 if (tresult != ISC_R_SUCCESS)
1300 cfg_obj_log(vclassobj, logctx, ISC_LOG_ERROR,
1301 "view '%s': invalid class %s",
1302 cfg_obj_asstring(vname), r.base);
1304 if (tresult == ISC_R_SUCCESS && symtab != NULL) {
1305 symvalue.as_cpointer = view;
1306 tresult = isc_symtab_define(symtab, key, vclass,
1308 isc_symexists_reject);
1309 if (tresult == ISC_R_EXISTS) {
1312 RUNTIME_CHECK(isc_symtab_lookup(symtab, key,
1313 vclass, &symvalue) == ISC_R_SUCCESS);
1314 file = cfg_obj_file(symvalue.as_cpointer);
1315 line = cfg_obj_line(symvalue.as_cpointer);
1316 cfg_obj_log(view, logctx, ISC_LOG_ERROR,
1317 "view '%s': already exists "
1318 "previous definition: %s:%u",
1321 } else if (result != ISC_R_SUCCESS) {
1323 } else if ((strcasecmp(key, "_bind") == 0 &&
1324 vclass == dns_rdataclass_ch) ||
1325 (strcasecmp(key, "_default") == 0 &&
1326 vclass == dns_rdataclass_in)) {
1327 cfg_obj_log(view, logctx, ISC_LOG_ERROR,
1328 "attempt to redefine builtin view "
1330 result = ISC_R_EXISTS;
1333 if (tresult == ISC_R_SUCCESS)
1334 tresult = check_viewconf(config, voptions,
1335 vclass, logctx, mctx);
1336 if (tresult != ISC_R_SUCCESS)
1337 result = ISC_R_FAILURE;
1340 isc_symtab_destroy(&symtab);
1342 if (views != NULL && options != NULL) {
1344 tresult = cfg_map_get(options, "cache-file", &obj);
1345 if (tresult == ISC_R_SUCCESS) {
1346 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1347 "'cache-file' cannot be a global "
1348 "option if views are present");
1349 result = ISC_R_FAILURE;
1353 tresult = cfg_map_get(config, "acl", &acls);
1354 if (tresult == ISC_R_SUCCESS) {
1355 const cfg_listelt_t *elt;
1356 const cfg_listelt_t *elt2;
1357 const char *aclname;
1359 for (elt = cfg_list_first(acls);
1361 elt = cfg_list_next(elt)) {
1362 const cfg_obj_t *acl = cfg_listelt_value(elt);
1365 aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
1367 i < sizeof(builtin) / sizeof(builtin[0]);
1369 if (strcasecmp(aclname, builtin[i]) == 0) {
1370 cfg_obj_log(acl, logctx, ISC_LOG_ERROR,
1371 "attempt to redefine "
1374 result = ISC_R_FAILURE;
1378 for (elt2 = cfg_list_next(elt);
1380 elt2 = cfg_list_next(elt2)) {
1381 const cfg_obj_t *acl2 = cfg_listelt_value(elt2);
1383 name = cfg_obj_asstring(cfg_tuple_get(acl2,
1385 if (strcasecmp(aclname, name) == 0) {
1386 const char *file = cfg_obj_file(acl);
1387 unsigned int line = cfg_obj_line(acl);
1390 file = "<unknown file>";
1392 cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
1393 "attempt to redefine "
1394 "acl '%s' previous "
1395 "definition: %s:%u",
1397 result = ISC_R_FAILURE;
1403 tresult = cfg_map_get(config, "kal", &kals);
1404 if (tresult == ISC_R_SUCCESS) {
1405 const cfg_listelt_t *elt;
1406 const cfg_listelt_t *elt2;
1407 const char *aclname;
1409 for (elt = cfg_list_first(kals);
1411 elt = cfg_list_next(elt)) {
1412 const cfg_obj_t *acl = cfg_listelt_value(elt);
1414 aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
1416 for (elt2 = cfg_list_next(elt);
1418 elt2 = cfg_list_next(elt2)) {
1419 const cfg_obj_t *acl2 = cfg_listelt_value(elt2);
1421 name = cfg_obj_asstring(cfg_tuple_get(acl2,
1423 if (strcasecmp(aclname, name) == 0) {
1424 const char *file = cfg_obj_file(acl);
1425 unsigned int line = cfg_obj_line(acl);
1428 file = "<unknown file>";
1430 cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
1431 "attempt to redefine "
1432 "kal '%s' previous "
1433 "definition: %s:%u",
1435 result = ISC_R_FAILURE;