2 * Copyright (C) 2004 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.28 2004/07/29 00:08:08 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(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 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(cfg_obj_t *options, isc_log_t *logctx) {
136 isc_result_t result = ISC_R_SUCCESS;
137 isc_result_t tresult;
138 cfg_listelt_t *element;
139 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(cfg_obj_t *options, isc_log_t *logctx) {
157 cfg_listelt_t *element;
158 cfg_obj_t *alternates = NULL;
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(cfg_obj_t *options, isc_log_t *logctx) {
217 cfg_obj_t *forward = NULL;
218 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(cfg_obj_t *disabled, isc_log_t *logctx) {
233 isc_result_t result = ISC_R_SUCCESS;
234 isc_result_t tresult;
235 cfg_listelt_t *element;
238 dns_fixedname_t fixed;
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 r.base = cfg_obj_asstring(cfg_listelt_value(element));
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(cfg_obj_t *obj, const char *name, int value, isc_symtab_t *symtab,
284 const char *fmt, isc_log_t *logctx, isc_mem_t *mctx)
290 isc_symvalue_t symvalue;
292 key = isc_mem_strdup(mctx, name);
294 return (ISC_R_NOMEMORY);
295 symvalue.as_pointer = obj;
296 result = isc_symtab_define(symtab, key, value, symvalue,
297 isc_symexists_reject);
298 if (result == ISC_R_EXISTS) {
299 RUNTIME_CHECK(isc_symtab_lookup(symtab, key, value,
300 &symvalue) == ISC_R_SUCCESS);
301 file = cfg_obj_file(symvalue.as_pointer);
302 line = cfg_obj_line(symvalue.as_pointer);
305 file = "<unknown file>";
306 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, fmt, key, file, line);
307 isc_mem_free(mctx, key);
308 result = ISC_R_EXISTS;
309 } else if (result != ISC_R_SUCCESS) {
310 isc_mem_free(mctx, key);
316 mustbesecure(cfg_obj_t *secure, isc_symtab_t *symtab, isc_log_t *logctx,
320 char namebuf[DNS_NAME_FORMATSIZE];
322 dns_fixedname_t fixed;
325 isc_result_t result = ISC_R_SUCCESS;
327 dns_fixedname_init(&fixed);
328 name = dns_fixedname_name(&fixed);
329 obj = cfg_tuple_get(secure, "name");
330 str = cfg_obj_asstring(obj);
331 isc_buffer_init(&b, str, strlen(str));
332 isc_buffer_add(&b, strlen(str));
333 result = dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL);
334 if (result != ISC_R_SUCCESS) {
335 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
336 "bad domain name '%s'", str);
338 dns_name_format(name, namebuf, sizeof(namebuf));
339 result = nameexist(secure, namebuf, 1, symtab,
340 "dnssec-must-be-secure '%s': already "
341 "exists previous definition: %s:%u",
354 check_options(cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx) {
355 isc_result_t result = ISC_R_SUCCESS;
356 isc_result_t tresult;
358 cfg_obj_t *obj = NULL;
359 cfg_listelt_t *element;
360 isc_symtab_t *symtab = NULL;
362 static intervaltable intervals[] = {
363 { "cleaning-interval", 60, 28 * 24 * 60 }, /* 28 days */
364 { "heartbeat-interval", 60, 28 * 24 * 60 }, /* 28 days */
365 { "interface-interval", 60, 28 * 24 * 60 }, /* 28 days */
366 { "max-transfer-idle-in", 60, 28 * 24 * 60 }, /* 28 days */
367 { "max-transfer-idle-out", 60, 28 * 24 * 60 }, /* 28 days */
368 { "max-transfer-time-in", 60, 28 * 24 * 60 }, /* 28 days */
369 { "max-transfer-time-out", 60, 28 * 24 * 60 }, /* 28 days */
370 { "sig-validity-interval", 86400, 10 * 366 }, /* 10 years */
371 { "statistics-interval", 60, 28 * 24 * 60 }, /* 28 days */
375 * Check that fields specified in units of time other than seconds
376 * have reasonable values.
378 for (i = 0; i < sizeof(intervals) / sizeof(intervals[0]); i++) {
381 (void)cfg_map_get(options, intervals[i].name, &obj);
384 val = cfg_obj_asuint32(obj);
385 if (val > intervals[i].max) {
386 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
387 "%s '%u' is out of range (0..%u)",
388 intervals[i].name, val,
390 result = ISC_R_RANGE;
391 } else if (val > (ISC_UINT32_MAX / intervals[i].scale)) {
392 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
393 "%s '%d' is out of range",
394 intervals[i].name, val);
395 result = ISC_R_RANGE;
399 (void)cfg_map_get(options, "preferred-glue", &obj);
402 str = cfg_obj_asstring(obj);
403 if (strcasecmp(str, "a") != 0 &&
404 strcasecmp(str, "aaaa") != 0 &&
405 strcasecmp(str, "none") != 0)
406 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
407 "preferred-glue unexpected value '%s'",
411 (void)cfg_map_get(options, "root-delegation-only", &obj);
413 if (!cfg_obj_isvoid(obj)) {
414 cfg_listelt_t *element;
417 dns_fixedname_t fixed;
421 dns_fixedname_init(&fixed);
422 name = dns_fixedname_name(&fixed);
423 for (element = cfg_list_first(obj);
425 element = cfg_list_next(element)) {
426 exclude = cfg_listelt_value(element);
427 str = cfg_obj_asstring(exclude);
428 isc_buffer_init(&b, str, strlen(str));
429 isc_buffer_add(&b, strlen(str));
430 tresult = dns_name_fromtext(name, &b,
433 if (tresult != ISC_R_SUCCESS) {
434 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
435 "bad domain name '%s'",
444 * Set supported DNSSEC algorithms.
447 (void)cfg_map_get(options, "disable-algorithms", &obj);
449 for (element = cfg_list_first(obj);
451 element = cfg_list_next(element))
453 obj = cfg_listelt_value(element);
454 tresult = disabled_algorithms(obj, logctx);
455 if (tresult != ISC_R_SUCCESS)
461 * Check the DLV zone name.
464 (void)cfg_map_get(options, "dnssec-lookaside", &obj);
466 tresult = isc_symtab_create(mctx, 100, freekey, mctx,
468 if (tresult != ISC_R_SUCCESS)
470 for (element = cfg_list_first(obj);
472 element = cfg_list_next(element))
474 dns_fixedname_t fixedname;
479 obj = cfg_listelt_value(element);
481 dlv = cfg_obj_asstring(cfg_tuple_get(obj, "domain"));
482 dns_fixedname_init(&fixedname);
483 name = dns_fixedname_name(&fixedname);
484 isc_buffer_init(&b, dlv, strlen(dlv));
485 isc_buffer_add(&b, strlen(dlv));
486 tresult = dns_name_fromtext(name, &b, dns_rootname,
488 if (tresult != ISC_R_SUCCESS) {
489 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
490 "bad domain name '%s'", dlv);
493 if (symtab != NULL) {
494 tresult = nameexist(obj, dlv, 1, symtab,
495 "dnssec-lookaside '%s': "
496 "already exists previous "
499 if (tresult != ISC_R_SUCCESS &&
500 result == ISC_R_SUCCESS)
504 * XXXMPA to be removed when multiple lookaside
505 * namespaces are supported.
507 if (!dns_name_equal(dns_rootname, name)) {
508 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
509 "dnssec-lookaside '%s': "
510 "non-root not yet supported", dlv);
511 if (result == ISC_R_SUCCESS)
512 result = ISC_R_FAILURE;
514 dlv = cfg_obj_asstring(cfg_tuple_get(obj,
516 dns_fixedname_init(&fixedname);
517 isc_buffer_init(&b, dlv, strlen(dlv));
518 isc_buffer_add(&b, strlen(dlv));
519 tresult = dns_name_fromtext(name, &b, dns_rootname,
521 if (tresult != ISC_R_SUCCESS) {
522 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
523 "bad domain name '%s'", dlv);
524 if (result == ISC_R_SUCCESS)
529 isc_symtab_destroy(&symtab);
533 * Check dnssec-must-be-secure.
536 (void)cfg_map_get(options, "dnssec-must-be-secure", &obj);
538 isc_symtab_t *symtab = NULL;
539 tresult = isc_symtab_create(mctx, 100, freekey, mctx,
541 if (tresult != ISC_R_SUCCESS)
543 for (element = cfg_list_first(obj);
545 element = cfg_list_next(element))
547 obj = cfg_listelt_value(element);
548 tresult = mustbesecure(obj, symtab, logctx, mctx);
549 if (tresult != ISC_R_SUCCESS)
553 isc_symtab_destroy(&symtab);
560 get_masters_def(cfg_obj_t *cctx, char *name, cfg_obj_t **ret) {
562 cfg_obj_t *masters = NULL;
565 result = cfg_map_get(cctx, "masters", &masters);
566 if (result != ISC_R_SUCCESS)
568 for (elt = cfg_list_first(masters);
570 elt = cfg_list_next(elt)) {
572 const char *listname;
574 list = cfg_listelt_value(elt);
575 listname = cfg_obj_asstring(cfg_tuple_get(list, "name"));
577 if (strcasecmp(listname, name) == 0) {
579 return (ISC_R_SUCCESS);
582 return (ISC_R_NOTFOUND);
586 validate_masters(cfg_obj_t *obj, cfg_obj_t *config, isc_uint32_t *countp,
587 isc_log_t *logctx, isc_mem_t *mctx)
589 isc_result_t result = ISC_R_SUCCESS;
590 isc_result_t tresult;
591 isc_uint32_t count = 0;
592 isc_symtab_t *symtab = NULL;
593 isc_symvalue_t symvalue;
594 cfg_listelt_t *element;
595 cfg_listelt_t **stack = NULL;
596 isc_uint32_t stackcount = 0, pushed = 0;
599 REQUIRE(countp != NULL);
600 result = isc_symtab_create(mctx, 100, NULL, NULL, ISC_FALSE, &symtab);
601 if (result != ISC_R_SUCCESS)
605 list = cfg_tuple_get(obj, "addresses");
606 element = cfg_list_first(list);
610 element = cfg_list_next(element))
616 addr = cfg_tuple_get(cfg_listelt_value(element),
618 key = cfg_tuple_get(cfg_listelt_value(element), "key");
620 if (cfg_obj_issockaddr(addr)) {
624 if (!cfg_obj_isvoid(key)) {
625 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
626 "unexpected token '%s'",
627 cfg_obj_asstring(key));
628 if (result == ISC_R_SUCCESS)
629 result = ISC_R_FAILURE;
631 listname = cfg_obj_asstring(addr);
632 symvalue.as_pointer = addr;
633 tresult = isc_symtab_define(symtab, listname, 1, symvalue,
634 isc_symexists_reject);
635 if (tresult == ISC_R_EXISTS)
637 tresult = get_masters_def(config, listname, &obj);
638 if (tresult != ISC_R_SUCCESS) {
639 if (result == ISC_R_SUCCESS)
641 cfg_obj_log(addr, logctx, ISC_LOG_ERROR,
642 "unable to find masters list '%s'",
647 if (stackcount == pushed) {
649 isc_uint32_t newlen = stackcount + 16;
650 size_t newsize, oldsize;
652 newsize = newlen * sizeof(*stack);
653 oldsize = stackcount * sizeof(*stack);
654 new = isc_mem_get(mctx, newsize);
657 if (stackcount != 0) {
658 memcpy(new, stack, oldsize);
659 isc_mem_put(mctx, stack, oldsize);
664 stack[pushed++] = cfg_list_next(element);
668 element = stack[--pushed];
673 isc_mem_put(mctx, stack, stackcount * sizeof(*stack));
674 isc_symtab_destroy(&symtab);
683 #define FORWARDZONE 16
684 #define DELEGATIONZONE 32
692 check_zoneconf(cfg_obj_t *zconfig, cfg_obj_t *config, isc_symtab_t *symtab,
693 dns_rdataclass_t defclass, isc_log_t *logctx, isc_mem_t *mctx)
699 cfg_obj_t *obj = NULL;
700 isc_result_t result = ISC_R_SUCCESS;
701 isc_result_t tresult;
703 dns_rdataclass_t zclass;
704 dns_fixedname_t fixedname;
707 static optionstable options[] = {
708 { "allow-query", MASTERZONE | SLAVEZONE | STUBZONE },
709 { "allow-notify", SLAVEZONE },
710 { "allow-transfer", MASTERZONE | SLAVEZONE },
711 { "notify", MASTERZONE | SLAVEZONE },
712 { "also-notify", MASTERZONE | SLAVEZONE },
713 { "dialup", MASTERZONE | SLAVEZONE | STUBZONE },
714 { "delegation-only", HINTZONE | STUBZONE },
715 { "forward", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE},
716 { "forwarders", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE},
717 { "maintain-ixfr-base", MASTERZONE | SLAVEZONE },
718 { "max-ixfr-log-size", MASTERZONE | SLAVEZONE },
719 { "notify-source", MASTERZONE | SLAVEZONE },
720 { "notify-source-v6", MASTERZONE | SLAVEZONE },
721 { "transfer-source", SLAVEZONE | STUBZONE },
722 { "transfer-source-v6", SLAVEZONE | STUBZONE },
723 { "max-transfer-time-in", SLAVEZONE | STUBZONE },
724 { "max-transfer-time-out", MASTERZONE | SLAVEZONE },
725 { "max-transfer-idle-in", SLAVEZONE | STUBZONE },
726 { "max-transfer-idle-out", MASTERZONE | SLAVEZONE },
727 { "max-retry-time", SLAVEZONE | STUBZONE },
728 { "min-retry-time", SLAVEZONE | STUBZONE },
729 { "max-refresh-time", SLAVEZONE | STUBZONE },
730 { "min-refresh-time", SLAVEZONE | STUBZONE },
731 { "sig-validity-interval", MASTERZONE },
732 { "zone-statistics", MASTERZONE | SLAVEZONE | STUBZONE },
733 { "allow-update", MASTERZONE },
734 { "allow-update-forwarding", SLAVEZONE },
735 { "file", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE},
736 { "ixfr-base", MASTERZONE | SLAVEZONE },
737 { "ixfr-tmp-file", MASTERZONE | SLAVEZONE },
738 { "masters", SLAVEZONE | STUBZONE },
739 { "pubkey", MASTERZONE | SLAVEZONE | STUBZONE },
740 { "update-policy", MASTERZONE },
741 { "database", MASTERZONE | SLAVEZONE | STUBZONE },
742 { "key-directory", MASTERZONE },
745 static optionstable dialups[] = {
746 { "notify", MASTERZONE | SLAVEZONE },
747 { "notify-passive", SLAVEZONE },
748 { "refresh", SLAVEZONE | STUBZONE },
749 { "passive", SLAVEZONE | STUBZONE },
752 zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
754 zoptions = cfg_tuple_get(zconfig, "options");
757 (void)cfg_map_get(zoptions, "type", &obj);
759 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
760 "zone '%s': type not present", zname);
761 return (ISC_R_FAILURE);
764 typestr = cfg_obj_asstring(obj);
765 if (strcasecmp(typestr, "master") == 0)
767 else if (strcasecmp(typestr, "slave") == 0)
769 else if (strcasecmp(typestr, "stub") == 0)
771 else if (strcasecmp(typestr, "forward") == 0)
773 else if (strcasecmp(typestr, "hint") == 0)
775 else if (strcasecmp(typestr, "delegation-only") == 0)
776 ztype = DELEGATIONZONE;
778 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
779 "zone '%s': invalid type %s",
781 return (ISC_R_FAILURE);
784 obj = cfg_tuple_get(zconfig, "class");
785 if (cfg_obj_isstring(obj)) {
788 DE_CONST(cfg_obj_asstring(obj), r.base);
789 r.length = strlen(r.base);
790 result = dns_rdataclass_fromtext(&zclass, &r);
791 if (result != ISC_R_SUCCESS) {
792 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
793 "zone '%s': invalid class %s",
795 return (ISC_R_FAILURE);
797 if (zclass != defclass) {
798 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
799 "zone '%s': class '%s' does not "
800 "match view/default class",
802 return (ISC_R_FAILURE);
807 * Look for an already existing zone.
808 * We need to make this cannonical as isc_symtab_define()
809 * deals with strings.
811 dns_fixedname_init(&fixedname);
812 isc_buffer_init(&b, zname, strlen(zname));
813 isc_buffer_add(&b, strlen(zname));
814 tresult = dns_name_fromtext(dns_fixedname_name(&fixedname), &b,
815 dns_rootname, ISC_TRUE, NULL);
816 if (result != ISC_R_SUCCESS) {
817 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
818 "zone '%s': is not a valid name", zname);
819 tresult = ISC_R_FAILURE;
821 char namebuf[DNS_NAME_FORMATSIZE];
823 dns_name_format(dns_fixedname_name(&fixedname),
824 namebuf, sizeof(namebuf));
825 tresult = nameexist(zconfig, namebuf, ztype == HINTZONE ? 1 : 2,
826 symtab, "zone '%s': already exists "
827 "previous definition: %s:%u", logctx, mctx);
828 if (tresult != ISC_R_SUCCESS)
833 * Look for inappropriate options for the given zone type.
835 for (i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
837 if ((options[i].allowed & ztype) == 0 &&
838 cfg_map_get(zoptions, options[i].name, &obj) ==
841 if (strcmp(options[i].name, "allow-update") != 0 ||
842 ztype != SLAVEZONE) {
843 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
844 "option '%s' is not allowed "
846 options[i].name, typestr, zname);
847 result = ISC_R_FAILURE;
849 cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
850 "option '%s' is not allowed "
852 options[i].name, typestr, zname);
857 * Slave & stub zones must have a "masters" field.
859 if (ztype == SLAVEZONE || ztype == STUBZONE) {
861 if (cfg_map_get(zoptions, "masters", &obj) != ISC_R_SUCCESS) {
862 cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
863 "zone '%s': missing 'masters' entry",
865 result = ISC_R_FAILURE;
868 tresult = validate_masters(obj, config, &count,
870 if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS)
872 if (tresult == ISC_R_SUCCESS && count == 0) {
873 cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
874 "zone '%s': empty 'masters' entry",
876 result = ISC_R_FAILURE;
882 * Master zones can't have both "allow-update" and "update-policy".
884 if (ztype == MASTERZONE) {
885 isc_result_t res1, res2;
887 res1 = cfg_map_get(zoptions, "allow-update", &obj);
889 res2 = cfg_map_get(zoptions, "update-policy", &obj);
890 if (res1 == ISC_R_SUCCESS && res2 == ISC_R_SUCCESS) {
891 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
892 "zone '%s': 'allow-update' is ignored "
893 "when 'update-policy' is present",
895 result = ISC_R_FAILURE;
900 * Check the excessively complicated "dialup" option.
902 if (ztype == MASTERZONE || ztype == SLAVEZONE || ztype == STUBZONE) {
903 cfg_obj_t *dialup = NULL;
904 (void)cfg_map_get(zoptions, "dialup", &dialup);
905 if (dialup != NULL && cfg_obj_isstring(dialup)) {
906 char *str = cfg_obj_asstring(dialup);
908 i < sizeof(dialups) / sizeof(dialups[0]);
911 if (strcasecmp(dialups[i].name, str) != 0)
913 if ((dialups[i].allowed & ztype) == 0) {
914 cfg_obj_log(obj, logctx,
916 "dialup type '%s' is not "
919 str, typestr, zname);
920 result = ISC_R_FAILURE;
924 if (i == sizeof(dialups) / sizeof(dialups[0])) {
925 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
926 "invalid dialup type '%s' in zone "
928 result = ISC_R_FAILURE;
934 * Check that forwarding is reasonable.
936 if (check_forward(zoptions, logctx) != ISC_R_SUCCESS)
937 result = ISC_R_FAILURE;
940 * Check various options.
942 tresult = check_options(zoptions, logctx, mctx);
943 if (tresult != ISC_R_SUCCESS)
950 bind9_check_key(cfg_obj_t *key, isc_log_t *logctx) {
951 cfg_obj_t *algobj = NULL;
952 cfg_obj_t *secretobj = NULL;
953 const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
955 (void)cfg_map_get(key, "algorithm", &algobj);
956 (void)cfg_map_get(key, "secret", &secretobj);
957 if (secretobj == NULL || algobj == NULL) {
958 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
959 "key '%s' must have both 'secret' and "
960 "'algorithm' defined",
962 return (ISC_R_FAILURE);
964 return (ISC_R_SUCCESS);
968 check_keylist(cfg_obj_t *keys, isc_symtab_t *symtab, isc_log_t *logctx) {
969 isc_result_t result = ISC_R_SUCCESS;
970 isc_result_t tresult;
971 cfg_listelt_t *element;
973 for (element = cfg_list_first(keys);
975 element = cfg_list_next(element))
977 cfg_obj_t *key = cfg_listelt_value(element);
978 const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
979 isc_symvalue_t symvalue;
981 symvalue.as_pointer = key;
982 tresult = isc_symtab_define(symtab, keyname, 1,
983 symvalue, isc_symexists_reject);
984 if (tresult == ISC_R_EXISTS) {
988 RUNTIME_CHECK(isc_symtab_lookup(symtab, keyname,
989 1, &symvalue) == ISC_R_SUCCESS);
990 file = cfg_obj_file(symvalue.as_pointer);
991 line = cfg_obj_line(symvalue.as_pointer);
994 file = "<unknown file>";
995 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
996 "key '%s': already exists "
997 "previous definition: %s:%u",
998 keyname, file, line);
1000 } else if (tresult != ISC_R_SUCCESS)
1003 tresult = bind9_check_key(key, logctx);
1004 if (tresult != ISC_R_SUCCESS)
1011 check_servers(cfg_obj_t *servers, isc_log_t *logctx) {
1012 isc_result_t result = ISC_R_SUCCESS;
1013 cfg_listelt_t *e1, *e2;
1015 isc_sockaddr_t *s1, *s2;
1020 isc_buffer_t target;
1022 for (e1 = cfg_list_first(servers); e1 != NULL; e1 = cfg_list_next(e1)) {
1023 v1 = cfg_listelt_value(e1);
1024 s1 = cfg_obj_assockaddr(cfg_map_getname(v1));
1026 if (isc_sockaddr_pf(s1) == AF_INET)
1027 xfr = "transfer-source-v6";
1029 xfr = "transfer-source";
1030 (void)cfg_map_get(v1, xfr, &ts);
1032 isc_netaddr_fromsockaddr(&na, s1);
1033 isc_buffer_init(&target, buf, sizeof(buf) - 1);
1034 RUNTIME_CHECK(isc_netaddr_totext(&na, &target)
1036 buf[isc_buffer_usedlength(&target)] = '\0';
1037 cfg_obj_log(v1, logctx, ISC_LOG_ERROR,
1038 "server '%s': %s not valid", buf, xfr);
1039 result = ISC_R_FAILURE;
1042 while ((e2 = cfg_list_next(e2)) != NULL) {
1043 v2 = cfg_listelt_value(e2);
1044 s2 = cfg_obj_assockaddr(cfg_map_getname(v2));
1045 if (isc_sockaddr_eqaddr(s1, s2)) {
1046 const char *file = cfg_obj_file(v1);
1047 unsigned int line = cfg_obj_line(v1);
1050 file = "<unknown file>";
1052 isc_netaddr_fromsockaddr(&na, s2);
1053 isc_buffer_init(&target, buf, sizeof(buf) - 1);
1054 RUNTIME_CHECK(isc_netaddr_totext(&na, &target)
1056 buf[isc_buffer_usedlength(&target)] = '\0';
1058 cfg_obj_log(v2, logctx, ISC_LOG_ERROR,
1059 "server '%s': already exists "
1060 "previous definition: %s:%u",
1062 result = ISC_R_FAILURE;
1070 check_viewconf(cfg_obj_t *config, cfg_obj_t *vconfig, dns_rdataclass_t vclass,
1071 isc_log_t *logctx, isc_mem_t *mctx)
1073 cfg_obj_t *servers = NULL;
1074 cfg_obj_t *zones = NULL;
1075 cfg_obj_t *keys = NULL;
1076 cfg_listelt_t *element;
1077 isc_symtab_t *symtab = NULL;
1078 isc_result_t result = ISC_R_SUCCESS;
1079 isc_result_t tresult = ISC_R_SUCCESS;
1082 * Check that all zone statements are syntactically correct and
1083 * there are no duplicate zones.
1085 tresult = isc_symtab_create(mctx, 100, freekey, mctx,
1086 ISC_FALSE, &symtab);
1087 if (tresult != ISC_R_SUCCESS)
1088 return (ISC_R_NOMEMORY);
1090 if (vconfig != NULL)
1091 (void)cfg_map_get(vconfig, "zone", &zones);
1093 (void)cfg_map_get(config, "zone", &zones);
1095 for (element = cfg_list_first(zones);
1097 element = cfg_list_next(element))
1099 isc_result_t tresult;
1100 cfg_obj_t *zone = cfg_listelt_value(element);
1102 tresult = check_zoneconf(zone, config, symtab, vclass,
1104 if (tresult != ISC_R_SUCCESS)
1105 result = ISC_R_FAILURE;
1108 isc_symtab_destroy(&symtab);
1111 * Check that all key statements are syntactically correct and
1112 * there are no duplicate keys.
1114 tresult = isc_symtab_create(mctx, 100, NULL, NULL, ISC_TRUE, &symtab);
1115 if (tresult != ISC_R_SUCCESS)
1116 return (ISC_R_NOMEMORY);
1118 (void)cfg_map_get(config, "key", &keys);
1119 tresult = check_keylist(keys, symtab, logctx);
1120 if (tresult == ISC_R_EXISTS)
1121 result = ISC_R_FAILURE;
1122 else if (tresult != ISC_R_SUCCESS) {
1123 isc_symtab_destroy(&symtab);
1127 if (vconfig != NULL) {
1129 (void)cfg_map_get(vconfig, "key", &keys);
1130 tresult = check_keylist(keys, symtab, logctx);
1131 if (tresult == ISC_R_EXISTS)
1132 result = ISC_R_FAILURE;
1133 else if (tresult != ISC_R_SUCCESS) {
1134 isc_symtab_destroy(&symtab);
1139 isc_symtab_destroy(&symtab);
1142 * Check that forwarding is reasonable.
1144 if (vconfig == NULL) {
1145 cfg_obj_t *options = NULL;
1146 (void)cfg_map_get(config, "options", &options);
1147 if (options != NULL)
1148 if (check_forward(options, logctx) != ISC_R_SUCCESS)
1149 result = ISC_R_FAILURE;
1151 if (check_forward(vconfig, logctx) != ISC_R_SUCCESS)
1152 result = ISC_R_FAILURE;
1155 * Check that dual-stack-servers is reasonable.
1157 if (vconfig == NULL) {
1158 cfg_obj_t *options = NULL;
1159 (void)cfg_map_get(config, "options", &options);
1160 if (options != NULL)
1161 if (check_dual_stack(options, logctx) != ISC_R_SUCCESS)
1162 result = ISC_R_FAILURE;
1164 if (check_dual_stack(vconfig, logctx) != ISC_R_SUCCESS)
1165 result = ISC_R_FAILURE;
1169 * Check that rrset-order is reasonable.
1171 if (vconfig != NULL) {
1172 if (check_order(vconfig, logctx) != ISC_R_SUCCESS)
1173 result = ISC_R_FAILURE;
1176 if (vconfig != NULL) {
1177 (void)cfg_map_get(vconfig, "server", &servers);
1178 if (servers != NULL &&
1179 check_servers(servers, logctx) != ISC_R_SUCCESS)
1180 result = ISC_R_FAILURE;
1183 if (vconfig != NULL)
1184 tresult = check_options(vconfig, logctx, mctx);
1186 tresult = check_options(config, logctx, mctx);
1187 if (tresult != ISC_R_SUCCESS)
1195 bind9_check_namedconf(cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx) {
1196 cfg_obj_t *options = NULL;
1197 cfg_obj_t *servers = NULL;
1198 cfg_obj_t *views = NULL;
1199 cfg_obj_t *acls = NULL;
1200 cfg_obj_t *kals = NULL;
1202 cfg_listelt_t *velement;
1203 isc_result_t result = ISC_R_SUCCESS;
1204 isc_result_t tresult;
1205 isc_symtab_t *symtab = NULL;
1207 static const char *builtin[] = { "localhost", "localnets",
1210 (void)cfg_map_get(config, "options", &options);
1212 if (options != NULL &&
1213 check_options(options, logctx, mctx) != ISC_R_SUCCESS)
1214 result = ISC_R_FAILURE;
1216 (void)cfg_map_get(config, "server", &servers);
1217 if (servers != NULL &&
1218 check_servers(servers, logctx) != ISC_R_SUCCESS)
1219 result = ISC_R_FAILURE;
1221 if (options != NULL &&
1222 check_order(options, logctx) != ISC_R_SUCCESS)
1223 result = ISC_R_FAILURE;
1225 (void)cfg_map_get(config, "view", &views);
1227 if (views != NULL && options != NULL)
1228 if (check_dual_stack(options, logctx) != ISC_R_SUCCESS)
1229 result = ISC_R_FAILURE;
1231 if (views == NULL) {
1232 if (check_viewconf(config, NULL, dns_rdataclass_in,
1233 logctx, mctx) != ISC_R_SUCCESS)
1234 result = ISC_R_FAILURE;
1236 cfg_obj_t *zones = NULL;
1238 (void)cfg_map_get(config, "zone", &zones);
1239 if (zones != NULL) {
1240 cfg_obj_log(zones, logctx, ISC_LOG_ERROR,
1241 "when using 'view' statements, "
1242 "all zones must be in views");
1243 result = ISC_R_FAILURE;
1247 tresult = isc_symtab_create(mctx, 100, NULL, NULL, ISC_TRUE, &symtab);
1248 if (tresult != ISC_R_SUCCESS)
1250 for (velement = cfg_list_first(views);
1252 velement = cfg_list_next(velement))
1254 cfg_obj_t *view = cfg_listelt_value(velement);
1255 cfg_obj_t *vname = cfg_tuple_get(view, "name");
1256 cfg_obj_t *voptions = cfg_tuple_get(view, "options");
1257 cfg_obj_t *vclassobj = cfg_tuple_get(view, "class");
1258 dns_rdataclass_t vclass = dns_rdataclass_in;
1259 isc_result_t tresult = ISC_R_SUCCESS;
1260 const char *key = cfg_obj_asstring(vname);
1261 isc_symvalue_t symvalue;
1263 if (cfg_obj_isstring(vclassobj)) {
1266 DE_CONST(cfg_obj_asstring(vclassobj), r.base);
1267 r.length = strlen(r.base);
1268 tresult = dns_rdataclass_fromtext(&vclass, &r);
1269 if (tresult != ISC_R_SUCCESS)
1270 cfg_obj_log(vclassobj, logctx, ISC_LOG_ERROR,
1271 "view '%s': invalid class %s",
1272 cfg_obj_asstring(vname), r.base);
1274 if (tresult == ISC_R_SUCCESS && symtab != NULL) {
1275 symvalue.as_pointer = view;
1276 tresult = isc_symtab_define(symtab, key, vclass,
1278 isc_symexists_reject);
1279 if (tresult == ISC_R_EXISTS) {
1282 RUNTIME_CHECK(isc_symtab_lookup(symtab, key,
1283 vclass, &symvalue) == ISC_R_SUCCESS);
1284 file = cfg_obj_file(symvalue.as_pointer);
1285 line = cfg_obj_line(symvalue.as_pointer);
1286 cfg_obj_log(view, logctx, ISC_LOG_ERROR,
1287 "view '%s': already exists "
1288 "previous definition: %s:%u",
1291 } else if (result != ISC_R_SUCCESS) {
1293 } else if ((strcasecmp(key, "_bind") == 0 &&
1294 vclass == dns_rdataclass_ch) ||
1295 (strcasecmp(key, "_default") == 0 &&
1296 vclass == dns_rdataclass_in)) {
1297 cfg_obj_log(view, logctx, ISC_LOG_ERROR,
1298 "attempt to redefine builtin view "
1300 result = ISC_R_EXISTS;
1303 if (tresult == ISC_R_SUCCESS)
1304 tresult = check_viewconf(config, voptions,
1305 vclass, logctx, mctx);
1306 if (tresult != ISC_R_SUCCESS)
1307 result = ISC_R_FAILURE;
1310 isc_symtab_destroy(&symtab);
1312 if (views != NULL && options != NULL) {
1314 tresult = cfg_map_get(options, "cache-file", &obj);
1315 if (tresult == ISC_R_SUCCESS) {
1316 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1317 "'cache-file' cannot be a global "
1318 "option if views are present");
1319 result = ISC_R_FAILURE;
1323 tresult = cfg_map_get(config, "acl", &acls);
1324 if (tresult == ISC_R_SUCCESS) {
1326 cfg_listelt_t *elt2;
1327 const char *aclname;
1329 for (elt = cfg_list_first(acls);
1331 elt = cfg_list_next(elt)) {
1332 cfg_obj_t *acl = cfg_listelt_value(elt);
1335 aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
1337 i < sizeof(builtin) / sizeof(builtin[0]);
1339 if (strcasecmp(aclname, builtin[i]) == 0) {
1340 cfg_obj_log(acl, logctx, ISC_LOG_ERROR,
1341 "attempt to redefine "
1344 result = ISC_R_FAILURE;
1348 for (elt2 = cfg_list_next(elt);
1350 elt2 = cfg_list_next(elt2)) {
1351 cfg_obj_t *acl2 = cfg_listelt_value(elt2);
1353 name = cfg_obj_asstring(cfg_tuple_get(acl2,
1355 if (strcasecmp(aclname, name) == 0) {
1356 const char *file = cfg_obj_file(acl);
1357 unsigned int line = cfg_obj_line(acl);
1360 file = "<unknown file>";
1362 cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
1363 "attempt to redefine "
1364 "acl '%s' previous "
1365 "definition: %s:%u",
1367 result = ISC_R_FAILURE;
1373 tresult = cfg_map_get(config, "kal", &kals);
1374 if (tresult == ISC_R_SUCCESS) {
1376 cfg_listelt_t *elt2;
1377 const char *aclname;
1379 for (elt = cfg_list_first(kals);
1381 elt = cfg_list_next(elt)) {
1382 cfg_obj_t *acl = cfg_listelt_value(elt);
1384 aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
1386 for (elt2 = cfg_list_next(elt);
1388 elt2 = cfg_list_next(elt2)) {
1389 cfg_obj_t *acl2 = cfg_listelt_value(elt2);
1391 name = cfg_obj_asstring(cfg_tuple_get(acl2,
1393 if (strcasecmp(aclname, name) == 0) {
1394 const char *file = cfg_obj_file(acl);
1395 unsigned int line = cfg_obj_line(acl);
1398 file = "<unknown file>";
1400 cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
1401 "attempt to redefine "
1402 "kal '%s' previous "
1403 "definition: %s:%u",
1405 result = ISC_R_FAILURE;