]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bind9/lib/bind9/check.c
This commit was generated by cvs2svn to compensate for changes in r146901,
[FreeBSD/FreeBSD.git] / contrib / bind9 / lib / bind9 / check.c
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2001-2003  Internet Software Consortium.
4  *
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.
8  *
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.
16  */
17
18 /* $Id: check.c,v 1.37.6.29 2004/11/22 05:02:41 marka Exp $ */
19
20 #include <config.h>
21
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <isc/buffer.h>
26 #include <isc/log.h>
27 #include <isc/mem.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>
34 #include <isc/util.h>
35
36 #include <dns/fixedname.h>
37 #include <dns/rdataclass.h>
38 #include <dns/rdatatype.h>
39 #include <dns/secalg.h>
40
41 #include <isccfg/cfg.h>
42
43 #include <bind9/check.h>
44
45 static void
46 freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) {
47         UNUSED(type);
48         UNUSED(value);
49         isc_mem_free(userarg, key);
50 }
51
52 static isc_result_t
53 check_orderent(cfg_obj_t *ent, isc_log_t *logctx) {
54         isc_result_t result = ISC_R_SUCCESS;
55         isc_result_t tresult;
56         isc_textregion_t r;
57         dns_fixedname_t fixed;
58         cfg_obj_t *obj;
59         dns_rdataclass_t rdclass;
60         dns_rdatatype_t rdtype;
61         isc_buffer_t b;
62         const char *str;
63
64         dns_fixedname_init(&fixed);
65         obj = cfg_tuple_get(ent, "class");
66         if (cfg_obj_isstring(obj)) {
67
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'",
74                                     r.base);
75                         result = ISC_R_FAILURE;
76                 }
77         }
78
79         obj = cfg_tuple_get(ent, "type");
80         if (cfg_obj_isstring(obj)) {
81
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'",
88                                     r.base);
89                         result = ISC_R_FAILURE;
90                 }
91         }
92
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;
104                 }
105         }
106
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;
113         }
114
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;
130         }
131         return (result);
132 }
133
134 static isc_result_t
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;
140
141         if (cfg_map_get(options, "rrset-order", &obj) != ISC_R_SUCCESS)
142                 return (result);
143
144         for (element = cfg_list_first(obj);
145              element != NULL;
146              element = cfg_list_next(element))
147         {
148                 tresult = check_orderent(cfg_listelt_value(element), logctx);
149                 if (tresult != ISC_R_SUCCESS)
150                         result = tresult;
151         }
152         return (result);
153 }
154
155 static isc_result_t
156 check_dual_stack(cfg_obj_t *options, isc_log_t *logctx) {
157         cfg_listelt_t *element;
158         cfg_obj_t *alternates = NULL;
159         cfg_obj_t *value;
160         cfg_obj_t *obj;
161         char *str;
162         dns_fixedname_t fixed;
163         dns_name_t *name;
164         isc_buffer_t buffer;
165         isc_result_t result = ISC_R_SUCCESS;
166         isc_result_t tresult;
167
168         (void)cfg_map_get(options, "dual-stack-servers", &alternates);
169
170         if (alternates == NULL)
171                 return (ISC_R_SUCCESS);
172
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;
180                 }
181         }
182         obj = cfg_tuple_get(alternates, "addresses");
183         for (element = cfg_list_first(obj);
184              element != NULL;
185              element = cfg_list_next(element)) {
186                 value = cfg_listelt_value(element);
187                 if (cfg_obj_issockaddr(value))
188                         continue;
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,
196                                            ISC_FALSE, NULL);
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;
201                 }
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;
209                         }
210                 }
211         }
212         return (result);
213 }
214
215 static isc_result_t
216 check_forward(cfg_obj_t *options, isc_log_t *logctx) {
217         cfg_obj_t *forward = NULL;
218         cfg_obj_t *forwarders = NULL;
219
220         (void)cfg_map_get(options, "forward", &forward);
221         (void)cfg_map_get(options, "forwarders", &forwarders);
222
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);
227         }
228         return (ISC_R_SUCCESS);
229 }
230
231 static isc_result_t
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;
236         const char *str;
237         isc_buffer_t b;
238         dns_fixedname_t fixed;
239         dns_name_t *name;
240         cfg_obj_t *obj;
241
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);
252                 result = tresult;
253         }
254
255         obj = cfg_tuple_get(disabled, "algorithms");
256
257         for (element = cfg_list_first(obj);
258              element != NULL;
259              element = cfg_list_next(element))
260         {
261                 isc_textregion_t r;
262                 dns_secalg_t alg;
263                 isc_result_t tresult;
264
265                 r.base = cfg_obj_asstring(cfg_listelt_value(element));
266                 r.length = strlen(r.base);
267
268                 tresult = dns_secalg_fromtext(&alg, &r);
269                 if (tresult != ISC_R_SUCCESS) {
270                         isc_uint8_t ui;
271                         result = isc_parse_uint8(&ui, r.base, 10);
272                 }
273                 if (tresult != ISC_R_SUCCESS) {
274                         cfg_obj_log(cfg_listelt_value(element), logctx,
275                                     ISC_LOG_ERROR, "invalid algorithm");
276                         result = tresult;
277                 }
278         }
279         return (result);
280 }
281
282 static isc_result_t
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)
285 {
286         char *key;
287         const char *file;
288         unsigned int line;
289         isc_result_t result;
290         isc_symvalue_t symvalue;
291
292         key = isc_mem_strdup(mctx, name);
293         if (key == NULL)
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);
303
304                 if (file == NULL)
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);
311         }
312         return (result);
313 }
314
315 static isc_result_t
316 mustbesecure(cfg_obj_t *secure, isc_symtab_t *symtab, isc_log_t *logctx,
317              isc_mem_t *mctx)
318 {
319         cfg_obj_t *obj;
320         char namebuf[DNS_NAME_FORMATSIZE];
321         const char *str;
322         dns_fixedname_t fixed;
323         dns_name_t *name;
324         isc_buffer_t b;
325         isc_result_t result = ISC_R_SUCCESS;
326
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);
337         } else {
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",
342                                    logctx, mctx);
343         }
344         return (result);
345 }
346
347 typedef struct {
348         const char *name;
349         unsigned int scale;
350         unsigned int max;
351 } intervaltable;
352
353 static isc_result_t
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;
357         unsigned int i;
358         cfg_obj_t *obj = NULL;
359         cfg_listelt_t *element;
360         isc_symtab_t *symtab = NULL;
361
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 */
372         };
373
374         /*
375          * Check that fields specified in units of time other than seconds
376          * have reasonable values.
377          */
378         for (i = 0; i < sizeof(intervals) / sizeof(intervals[0]); i++) {
379                 isc_uint32_t val;
380                 obj = NULL;
381                 (void)cfg_map_get(options, intervals[i].name, &obj);
382                 if (obj == NULL)
383                         continue;
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,
389                                     intervals[i].max);
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;
396                 }
397         }
398         obj = NULL;
399         (void)cfg_map_get(options, "preferred-glue", &obj);
400         if (obj != NULL) {
401                 const char *str;
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'",
408                                     str);
409         }
410         obj = NULL;
411         (void)cfg_map_get(options, "root-delegation-only", &obj);
412         if (obj != NULL) {
413                 if (!cfg_obj_isvoid(obj)) {
414                         cfg_listelt_t *element;
415                         cfg_obj_t *exclude;
416                         char *str;
417                         dns_fixedname_t fixed;
418                         dns_name_t *name;
419                         isc_buffer_t b;
420
421                         dns_fixedname_init(&fixed);
422                         name = dns_fixedname_name(&fixed);
423                         for (element = cfg_list_first(obj);
424                              element != NULL;
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,
431                                                            dns_rootname,
432                                                            ISC_FALSE, NULL);
433                                 if (tresult != ISC_R_SUCCESS) {
434                                         cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
435                                                     "bad domain name '%s'",
436                                                     str);
437                                         result = tresult;
438                                 }
439                         }
440                 }
441         }
442        
443         /*
444          * Set supported DNSSEC algorithms.
445          */
446         obj = NULL;
447         (void)cfg_map_get(options, "disable-algorithms", &obj);
448         if (obj != NULL) {
449                 for (element = cfg_list_first(obj);
450                      element != NULL;
451                      element = cfg_list_next(element))
452                 {
453                         obj = cfg_listelt_value(element);
454                         tresult = disabled_algorithms(obj, logctx);
455                         if (tresult != ISC_R_SUCCESS)
456                                 result = tresult;
457                 }
458         }
459
460         /*
461          * Check the DLV zone name.
462          */
463         obj = NULL;
464         (void)cfg_map_get(options, "dnssec-lookaside", &obj);
465         if (obj != NULL) {
466                 tresult = isc_symtab_create(mctx, 100, freekey, mctx,
467                                             ISC_TRUE, &symtab);
468                 if (tresult != ISC_R_SUCCESS)
469                         result = tresult;
470                 for (element = cfg_list_first(obj);
471                      element != NULL;
472                      element = cfg_list_next(element))
473                 {
474                         dns_fixedname_t fixedname;
475                         dns_name_t *name;
476                         const char *dlv;
477                         isc_buffer_t b;
478
479                         obj = cfg_listelt_value(element);
480
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,
487                                                     ISC_TRUE, NULL);
488                         if (tresult != ISC_R_SUCCESS) {
489                                 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
490                                             "bad domain name '%s'", dlv);
491                                 result = tresult;
492                         }
493                         if (symtab != NULL) {
494                                 tresult = nameexist(obj, dlv, 1, symtab,
495                                                     "dnssec-lookaside '%s': "
496                                                     "already exists previous "
497                                                     "definition: %s:%u",
498                                                     logctx, mctx);
499                                 if (tresult != ISC_R_SUCCESS &&
500                                     result == ISC_R_SUCCESS)
501                                         result = tresult;
502                         }
503                         /*
504                          * XXXMPA to be removed when multiple lookaside
505                          * namespaces are supported.
506                          */
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;
513                         }
514                         dlv = cfg_obj_asstring(cfg_tuple_get(obj,
515                                                "trust-anchor"));
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,
520                                                     ISC_TRUE, NULL);
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)
525                                         result = tresult;
526                         }
527                 }
528                 if (symtab != NULL)
529                         isc_symtab_destroy(&symtab);
530         }
531
532         /*
533          * Check dnssec-must-be-secure.
534          */
535         obj = NULL;
536         (void)cfg_map_get(options, "dnssec-must-be-secure", &obj);
537         if (obj != NULL) {
538                 isc_symtab_t *symtab = NULL;
539                 tresult = isc_symtab_create(mctx, 100, freekey, mctx,
540                                             ISC_FALSE, &symtab);
541                 if (tresult != ISC_R_SUCCESS)
542                         result = tresult;
543                 for (element = cfg_list_first(obj);
544                      element != NULL;
545                      element = cfg_list_next(element))
546                 {
547                         obj = cfg_listelt_value(element);
548                         tresult = mustbesecure(obj, symtab, logctx, mctx);
549                         if (tresult != ISC_R_SUCCESS)
550                                 result = tresult;
551                 }
552                 if (symtab != NULL)
553                         isc_symtab_destroy(&symtab);
554         }
555
556         return (result);
557 }
558
559 static isc_result_t
560 get_masters_def(cfg_obj_t *cctx, char *name, cfg_obj_t **ret) {
561         isc_result_t result;
562         cfg_obj_t *masters = NULL;
563         cfg_listelt_t *elt;
564
565         result = cfg_map_get(cctx, "masters", &masters);
566         if (result != ISC_R_SUCCESS)
567                 return (result);
568         for (elt = cfg_list_first(masters);
569              elt != NULL;
570              elt = cfg_list_next(elt)) {
571                 cfg_obj_t *list;
572                 const char *listname;
573
574                 list = cfg_listelt_value(elt);
575                 listname = cfg_obj_asstring(cfg_tuple_get(list, "name"));
576
577                 if (strcasecmp(listname, name) == 0) {
578                         *ret = list;
579                         return (ISC_R_SUCCESS);
580                 }
581         }
582         return (ISC_R_NOTFOUND);
583 }
584
585 static isc_result_t
586 validate_masters(cfg_obj_t *obj, cfg_obj_t *config, isc_uint32_t *countp,
587                  isc_log_t *logctx, isc_mem_t *mctx)
588 {
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;
597         cfg_obj_t *list;
598
599         REQUIRE(countp != NULL);
600         result = isc_symtab_create(mctx, 100, NULL, NULL, ISC_FALSE, &symtab);
601         if (result != ISC_R_SUCCESS)
602                 return (result);
603
604  newlist:
605         list = cfg_tuple_get(obj, "addresses");
606         element = cfg_list_first(list);
607  resume:        
608         for ( ;
609              element != NULL;
610              element = cfg_list_next(element))
611         {
612                 char *listname;
613                 cfg_obj_t *addr;
614                 cfg_obj_t *key;
615
616                 addr = cfg_tuple_get(cfg_listelt_value(element),
617                                      "masterselement");
618                 key = cfg_tuple_get(cfg_listelt_value(element), "key");
619
620                 if (cfg_obj_issockaddr(addr)) {
621                         count++;
622                         continue;
623                 }
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;
630                 }
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)
636                         continue;
637                 tresult = get_masters_def(config, listname, &obj);
638                 if (tresult != ISC_R_SUCCESS) {
639                         if (result == ISC_R_SUCCESS)
640                                 result = tresult;
641                         cfg_obj_log(addr, logctx, ISC_LOG_ERROR,
642                                     "unable to find masters list '%s'",
643                                     listname);
644                         continue;
645                 }
646                 /* Grow stack? */
647                 if (stackcount == pushed) {
648                         void * new;
649                         isc_uint32_t newlen = stackcount + 16;
650                         size_t newsize, oldsize;
651
652                         newsize = newlen * sizeof(*stack);
653                         oldsize = stackcount * sizeof(*stack);
654                         new = isc_mem_get(mctx, newsize);
655                         if (new == NULL)
656                                 goto cleanup;
657                         if (stackcount != 0) {
658                                 memcpy(new, stack, oldsize);
659                                 isc_mem_put(mctx, stack, oldsize);
660                         }
661                         stack = new;
662                         stackcount = newlen;
663                 }
664                 stack[pushed++] = cfg_list_next(element);
665                 goto newlist;
666         }
667         if (pushed != 0) {
668                 element = stack[--pushed];
669                 goto resume;
670         }
671  cleanup:
672         if (stack != NULL)
673                 isc_mem_put(mctx, stack, stackcount * sizeof(*stack));
674         isc_symtab_destroy(&symtab);
675         *countp = count;
676         return (result);
677 }
678
679 #define MASTERZONE      1
680 #define SLAVEZONE       2
681 #define STUBZONE        4
682 #define HINTZONE        8
683 #define FORWARDZONE     16
684 #define DELEGATIONZONE  32
685
686 typedef struct {
687         const char *name;
688         int allowed;
689 } optionstable;
690
691 static isc_result_t
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)
694 {
695         const char *zname;
696         const char *typestr;
697         unsigned int ztype;
698         cfg_obj_t *zoptions;
699         cfg_obj_t *obj = NULL;
700         isc_result_t result = ISC_R_SUCCESS;
701         isc_result_t tresult;
702         unsigned int i;
703         dns_rdataclass_t zclass;
704         dns_fixedname_t fixedname;
705         isc_buffer_t b;
706
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 },
743         };
744
745         static optionstable dialups[] = {
746         { "notify", MASTERZONE | SLAVEZONE },
747         { "notify-passive", SLAVEZONE },
748         { "refresh", SLAVEZONE | STUBZONE },
749         { "passive", SLAVEZONE | STUBZONE },
750         };
751
752         zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
753
754         zoptions = cfg_tuple_get(zconfig, "options");
755
756         obj = NULL;
757         (void)cfg_map_get(zoptions, "type", &obj);
758         if (obj == NULL) {
759                 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
760                             "zone '%s': type not present", zname);
761                 return (ISC_R_FAILURE);
762         }
763
764         typestr = cfg_obj_asstring(obj);
765         if (strcasecmp(typestr, "master") == 0)
766                 ztype = MASTERZONE;
767         else if (strcasecmp(typestr, "slave") == 0)
768                 ztype = SLAVEZONE;
769         else if (strcasecmp(typestr, "stub") == 0)
770                 ztype = STUBZONE;
771         else if (strcasecmp(typestr, "forward") == 0)
772                 ztype = FORWARDZONE;
773         else if (strcasecmp(typestr, "hint") == 0)
774                 ztype = HINTZONE;
775         else if (strcasecmp(typestr, "delegation-only") == 0)
776                 ztype = DELEGATIONZONE;
777         else {
778                 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
779                             "zone '%s': invalid type %s",
780                             zname, typestr);
781                 return (ISC_R_FAILURE);
782         }
783
784         obj = cfg_tuple_get(zconfig, "class");
785         if (cfg_obj_isstring(obj)) {
786                 isc_textregion_t r;
787
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",
794                                     zname, r.base);
795                         return (ISC_R_FAILURE);
796                 }
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",
801                                     zname, r.base);
802                         return (ISC_R_FAILURE);
803                 }
804         }
805
806         /*
807          * Look for an already existing zone.
808          * We need to make this cannonical as isc_symtab_define()
809          * deals with strings.
810          */
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;
820         } else {
821                 char namebuf[DNS_NAME_FORMATSIZE];
822
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)
829                         result = tresult;
830         }
831
832         /*
833          * Look for inappropriate options for the given zone type.
834          */
835         for (i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
836                 obj = NULL;
837                 if ((options[i].allowed & ztype) == 0 &&
838                     cfg_map_get(zoptions, options[i].name, &obj) ==
839                     ISC_R_SUCCESS)
840                 {
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 "
845                                             "in '%s' zone '%s'",
846                                             options[i].name, typestr, zname);
847                                         result = ISC_R_FAILURE;
848                         } else
849                                 cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
850                                             "option '%s' is not allowed "
851                                             "in '%s' zone '%s'",
852                                             options[i].name, typestr, zname);
853                 }
854         }
855
856         /*
857          * Slave & stub zones must have a "masters" field.
858          */
859         if (ztype == SLAVEZONE || ztype == STUBZONE) {
860                 obj = NULL;
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",
864                                     zname);
865                         result = ISC_R_FAILURE;
866                 } else {
867                         isc_uint32_t count;
868                         tresult = validate_masters(obj, config, &count,
869                                                    logctx, mctx);
870                         if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS)
871                                 result = tresult;
872                         if (tresult == ISC_R_SUCCESS && count == 0) {
873                                 cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
874                                             "zone '%s': empty 'masters' entry",
875                                             zname);
876                                 result = ISC_R_FAILURE;
877                         }
878                 }
879         }
880
881         /*
882          * Master zones can't have both "allow-update" and "update-policy".
883          */
884         if (ztype == MASTERZONE) {
885                 isc_result_t res1, res2;
886                 obj = NULL;
887                 res1 = cfg_map_get(zoptions, "allow-update", &obj);
888                 obj = NULL;
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",
894                                     zname);
895                         result = ISC_R_FAILURE;
896                 }
897         }
898
899         /*
900          * Check the excessively complicated "dialup" option.
901          */
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);
907                         for (i = 0;
908                              i < sizeof(dialups) / sizeof(dialups[0]);
909                              i++)
910                         {
911                                 if (strcasecmp(dialups[i].name, str) != 0)
912                                         continue;
913                                 if ((dialups[i].allowed & ztype) == 0) {
914                                         cfg_obj_log(obj, logctx,
915                                                     ISC_LOG_ERROR,
916                                                     "dialup type '%s' is not "
917                                                     "allowed in '%s' "
918                                                     "zone '%s'",
919                                                     str, typestr, zname);
920                                         result = ISC_R_FAILURE;
921                                 }
922                                 break;
923                         }
924                         if (i == sizeof(dialups) / sizeof(dialups[0])) {
925                                 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
926                                             "invalid dialup type '%s' in zone "
927                                             "'%s'", str, zname);
928                                 result = ISC_R_FAILURE;
929                         }
930                 }
931         }
932
933         /*
934          * Check that forwarding is reasonable.
935          */
936         if (check_forward(zoptions, logctx) != ISC_R_SUCCESS)
937                 result = ISC_R_FAILURE;
938
939         /*
940          * Check various options.
941          */
942         tresult = check_options(zoptions, logctx, mctx);
943         if (tresult != ISC_R_SUCCESS)
944                 result = tresult;
945
946         /*
947          * If the zone type is rbt/rbt64 then master/hint zones
948          * require file clauses.
949          */
950         obj = NULL;
951         tresult = cfg_map_get(zoptions, "database", &obj);
952         if (tresult == ISC_R_NOTFOUND ||
953             (tresult == ISC_R_SUCCESS &&
954              (strcmp("rbt", cfg_obj_asstring(obj)) == 0 ||
955               strcmp("rbt64", cfg_obj_asstring(obj)) == 0))) {
956                 obj = NULL;
957                 tresult = cfg_map_get(zoptions, "file", &obj);
958                 if (tresult != ISC_R_SUCCESS &&
959                     (ztype == MASTERZONE || ztype == HINTZONE)) {
960                         cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
961                                     "zone '%s': missing 'file' entry",
962                                     zname);
963                         result = tresult;
964                 }
965         }
966         
967         return (result);
968 }
969
970 isc_result_t
971 bind9_check_key(cfg_obj_t *key, isc_log_t *logctx) {
972         cfg_obj_t *algobj = NULL;
973         cfg_obj_t *secretobj = NULL;
974         const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
975         
976         (void)cfg_map_get(key, "algorithm", &algobj);
977         (void)cfg_map_get(key, "secret", &secretobj);
978         if (secretobj == NULL || algobj == NULL) {
979                 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
980                             "key '%s' must have both 'secret' and "
981                             "'algorithm' defined",
982                             keyname);
983                 return (ISC_R_FAILURE);
984         }
985         return (ISC_R_SUCCESS);
986 }
987
988 static isc_result_t
989 check_keylist(cfg_obj_t *keys, isc_symtab_t *symtab, isc_log_t *logctx) {
990         isc_result_t result = ISC_R_SUCCESS;
991         isc_result_t tresult;
992         cfg_listelt_t *element;
993
994         for (element = cfg_list_first(keys);
995              element != NULL;
996              element = cfg_list_next(element))
997         {
998                 cfg_obj_t *key = cfg_listelt_value(element);
999                 const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
1000                 isc_symvalue_t symvalue;
1001
1002                 symvalue.as_pointer = key;
1003                 tresult = isc_symtab_define(symtab, keyname, 1,
1004                                             symvalue, isc_symexists_reject);
1005                 if (tresult == ISC_R_EXISTS) {
1006                         const char *file;
1007                         unsigned int line;
1008
1009                         RUNTIME_CHECK(isc_symtab_lookup(symtab, keyname,
1010                                             1, &symvalue) == ISC_R_SUCCESS);
1011                         file = cfg_obj_file(symvalue.as_pointer);
1012                         line = cfg_obj_line(symvalue.as_pointer);
1013
1014                         if (file == NULL)
1015                                 file = "<unknown file>";
1016                         cfg_obj_log(key, logctx, ISC_LOG_ERROR,
1017                                     "key '%s': already exists "
1018                                     "previous definition: %s:%u",
1019                                     keyname, file, line);
1020                         result = tresult;
1021                 } else if (tresult != ISC_R_SUCCESS)
1022                         return (tresult);
1023
1024                 tresult = bind9_check_key(key, logctx);
1025                 if (tresult != ISC_R_SUCCESS)
1026                         return (tresult);
1027         }
1028         return (result);
1029 }
1030
1031 static isc_result_t
1032 check_servers(cfg_obj_t *servers, isc_log_t *logctx) {
1033         isc_result_t result = ISC_R_SUCCESS;
1034         cfg_listelt_t *e1, *e2;
1035         cfg_obj_t *v1, *v2;
1036         isc_sockaddr_t *s1, *s2;
1037         isc_netaddr_t na;
1038         cfg_obj_t *ts;
1039         char buf[128];
1040         const char *xfr;
1041         isc_buffer_t target;
1042
1043         for (e1 = cfg_list_first(servers); e1 != NULL; e1 = cfg_list_next(e1)) {
1044                 v1 = cfg_listelt_value(e1);
1045                 s1 = cfg_obj_assockaddr(cfg_map_getname(v1));
1046                 ts = NULL;
1047                 if (isc_sockaddr_pf(s1) == AF_INET)
1048                         xfr = "transfer-source-v6";
1049                 else
1050                         xfr = "transfer-source";
1051                 (void)cfg_map_get(v1, xfr, &ts);
1052                 if (ts != NULL) {
1053                         isc_netaddr_fromsockaddr(&na, s1);
1054                         isc_buffer_init(&target, buf, sizeof(buf) - 1);
1055                         RUNTIME_CHECK(isc_netaddr_totext(&na, &target)
1056                                       == ISC_R_SUCCESS);
1057                         buf[isc_buffer_usedlength(&target)] = '\0';
1058                         cfg_obj_log(v1, logctx, ISC_LOG_ERROR,
1059                                     "server '%s': %s not valid", buf, xfr);
1060                         result = ISC_R_FAILURE;
1061                 }
1062                 e2 = e1;
1063                 while ((e2 = cfg_list_next(e2)) != NULL) {
1064                         v2 = cfg_listelt_value(e2);
1065                         s2 = cfg_obj_assockaddr(cfg_map_getname(v2));
1066                         if (isc_sockaddr_eqaddr(s1, s2)) {
1067                                 const char *file = cfg_obj_file(v1);
1068                                 unsigned int line = cfg_obj_line(v1);
1069
1070                                 if (file == NULL)
1071                                         file = "<unknown file>";
1072
1073                                 isc_netaddr_fromsockaddr(&na, s2);
1074                                 isc_buffer_init(&target, buf, sizeof(buf) - 1);
1075                                 RUNTIME_CHECK(isc_netaddr_totext(&na, &target)
1076                                               == ISC_R_SUCCESS);
1077                                 buf[isc_buffer_usedlength(&target)] = '\0';
1078
1079                                 cfg_obj_log(v2, logctx, ISC_LOG_ERROR,
1080                                             "server '%s': already exists "
1081                                             "previous definition: %s:%u",
1082                                             buf, file, line);
1083                                 result = ISC_R_FAILURE;
1084                         }
1085                 }
1086         }
1087         return (result);
1088 }
1089                 
1090 static isc_result_t
1091 check_viewconf(cfg_obj_t *config, cfg_obj_t *vconfig, dns_rdataclass_t vclass,
1092                isc_log_t *logctx, isc_mem_t *mctx)
1093 {
1094         cfg_obj_t *servers = NULL;
1095         cfg_obj_t *zones = NULL;
1096         cfg_obj_t *keys = NULL;
1097         cfg_listelt_t *element;
1098         isc_symtab_t *symtab = NULL;
1099         isc_result_t result = ISC_R_SUCCESS;
1100         isc_result_t tresult = ISC_R_SUCCESS;
1101
1102         /*
1103          * Check that all zone statements are syntactically correct and
1104          * there are no duplicate zones.
1105          */
1106         tresult = isc_symtab_create(mctx, 100, freekey, mctx,
1107                                     ISC_FALSE, &symtab);
1108         if (tresult != ISC_R_SUCCESS)
1109                 return (ISC_R_NOMEMORY);
1110
1111         if (vconfig != NULL)
1112                 (void)cfg_map_get(vconfig, "zone", &zones);
1113         else
1114                 (void)cfg_map_get(config, "zone", &zones);
1115
1116         for (element = cfg_list_first(zones);
1117              element != NULL;
1118              element = cfg_list_next(element))
1119         {
1120                 isc_result_t tresult;
1121                 cfg_obj_t *zone = cfg_listelt_value(element);
1122
1123                 tresult = check_zoneconf(zone, config, symtab, vclass,
1124                                          logctx, mctx);
1125                 if (tresult != ISC_R_SUCCESS)
1126                         result = ISC_R_FAILURE;
1127         }
1128
1129         isc_symtab_destroy(&symtab);
1130
1131         /*
1132          * Check that all key statements are syntactically correct and
1133          * there are no duplicate keys.
1134          */
1135         tresult = isc_symtab_create(mctx, 100, NULL, NULL, ISC_TRUE, &symtab);
1136         if (tresult != ISC_R_SUCCESS)
1137                 return (ISC_R_NOMEMORY);
1138
1139         (void)cfg_map_get(config, "key", &keys);
1140         tresult = check_keylist(keys, symtab, logctx);
1141         if (tresult == ISC_R_EXISTS)
1142                 result = ISC_R_FAILURE;
1143         else if (tresult != ISC_R_SUCCESS) {
1144                 isc_symtab_destroy(&symtab);
1145                 return (tresult);
1146         }
1147         
1148         if (vconfig != NULL) {
1149                 keys = NULL;
1150                 (void)cfg_map_get(vconfig, "key", &keys);
1151                 tresult = check_keylist(keys, symtab, logctx);
1152                 if (tresult == ISC_R_EXISTS)
1153                         result = ISC_R_FAILURE;
1154                 else if (tresult != ISC_R_SUCCESS) {
1155                         isc_symtab_destroy(&symtab);
1156                         return (tresult);
1157                 }
1158         }
1159
1160         isc_symtab_destroy(&symtab);
1161
1162         /*
1163          * Check that forwarding is reasonable.
1164          */
1165         if (vconfig == NULL) {
1166                 cfg_obj_t *options = NULL;
1167                 (void)cfg_map_get(config, "options", &options);
1168                 if (options != NULL)
1169                         if (check_forward(options, logctx) != ISC_R_SUCCESS)
1170                                 result = ISC_R_FAILURE;
1171         } else {
1172                 if (check_forward(vconfig, logctx) != ISC_R_SUCCESS)
1173                         result = ISC_R_FAILURE;
1174         }
1175         /*
1176          * Check that dual-stack-servers is reasonable.
1177          */
1178         if (vconfig == NULL) {
1179                 cfg_obj_t *options = NULL;
1180                 (void)cfg_map_get(config, "options", &options);
1181                 if (options != NULL)
1182                         if (check_dual_stack(options, logctx) != ISC_R_SUCCESS)
1183                                 result = ISC_R_FAILURE;
1184         } else {
1185                 if (check_dual_stack(vconfig, logctx) != ISC_R_SUCCESS)
1186                         result = ISC_R_FAILURE;
1187         }
1188
1189         /*
1190          * Check that rrset-order is reasonable.
1191          */
1192         if (vconfig != NULL) {
1193                 if (check_order(vconfig, logctx) != ISC_R_SUCCESS)
1194                         result = ISC_R_FAILURE;
1195         }
1196
1197         if (vconfig != NULL) {
1198                 (void)cfg_map_get(vconfig, "server", &servers);
1199                 if (servers != NULL &&
1200                     check_servers(servers, logctx) != ISC_R_SUCCESS)
1201                         result = ISC_R_FAILURE;
1202         }
1203
1204         if (vconfig != NULL)
1205                 tresult = check_options(vconfig, logctx, mctx);
1206         else
1207                 tresult = check_options(config, logctx, mctx);
1208         if (tresult != ISC_R_SUCCESS)
1209                 result = tresult;
1210
1211         return (result);
1212 }
1213
1214
1215 isc_result_t
1216 bind9_check_namedconf(cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx) {
1217         cfg_obj_t *options = NULL;
1218         cfg_obj_t *servers = NULL;
1219         cfg_obj_t *views = NULL;
1220         cfg_obj_t *acls = NULL;
1221         cfg_obj_t *kals = NULL;
1222         cfg_obj_t *obj;
1223         cfg_listelt_t *velement;
1224         isc_result_t result = ISC_R_SUCCESS;
1225         isc_result_t tresult;
1226         isc_symtab_t *symtab = NULL;
1227
1228         static const char *builtin[] = { "localhost", "localnets",
1229                                          "any", "none"};
1230
1231         (void)cfg_map_get(config, "options", &options);
1232
1233         if (options != NULL &&
1234             check_options(options, logctx, mctx) != ISC_R_SUCCESS)
1235                 result = ISC_R_FAILURE;
1236
1237         (void)cfg_map_get(config, "server", &servers);
1238         if (servers != NULL &&
1239             check_servers(servers, logctx) != ISC_R_SUCCESS)
1240                 result = ISC_R_FAILURE;
1241
1242         if (options != NULL && 
1243             check_order(options, logctx) != ISC_R_SUCCESS)
1244                 result = ISC_R_FAILURE;
1245
1246         (void)cfg_map_get(config, "view", &views);
1247
1248         if (views != NULL && options != NULL)
1249                 if (check_dual_stack(options, logctx) != ISC_R_SUCCESS)
1250                         result = ISC_R_FAILURE;
1251
1252         if (views == NULL) {
1253                 if (check_viewconf(config, NULL, dns_rdataclass_in,
1254                                    logctx, mctx) != ISC_R_SUCCESS)
1255                         result = ISC_R_FAILURE;
1256         } else {
1257                 cfg_obj_t *zones = NULL;
1258
1259                 (void)cfg_map_get(config, "zone", &zones);
1260                 if (zones != NULL) {
1261                         cfg_obj_log(zones, logctx, ISC_LOG_ERROR,
1262                                     "when using 'view' statements, "
1263                                     "all zones must be in views");
1264                         result = ISC_R_FAILURE;
1265                 }
1266         }
1267
1268         tresult = isc_symtab_create(mctx, 100, NULL, NULL, ISC_TRUE, &symtab);
1269         if (tresult != ISC_R_SUCCESS)
1270                 result = tresult;
1271         for (velement = cfg_list_first(views);
1272              velement != NULL;
1273              velement = cfg_list_next(velement))
1274         {
1275                 cfg_obj_t *view = cfg_listelt_value(velement);
1276                 cfg_obj_t *vname = cfg_tuple_get(view, "name");
1277                 cfg_obj_t *voptions = cfg_tuple_get(view, "options");
1278                 cfg_obj_t *vclassobj = cfg_tuple_get(view, "class");
1279                 dns_rdataclass_t vclass = dns_rdataclass_in;
1280                 isc_result_t tresult = ISC_R_SUCCESS;
1281                 const char *key = cfg_obj_asstring(vname);
1282                 isc_symvalue_t symvalue;
1283
1284                 if (cfg_obj_isstring(vclassobj)) {
1285                         isc_textregion_t r;
1286
1287                         DE_CONST(cfg_obj_asstring(vclassobj), r.base);
1288                         r.length = strlen(r.base);
1289                         tresult = dns_rdataclass_fromtext(&vclass, &r);
1290                         if (tresult != ISC_R_SUCCESS)
1291                                 cfg_obj_log(vclassobj, logctx, ISC_LOG_ERROR,
1292                                             "view '%s': invalid class %s",
1293                                             cfg_obj_asstring(vname), r.base);
1294                 }
1295                 if (tresult == ISC_R_SUCCESS && symtab != NULL) {
1296                         symvalue.as_pointer = view;
1297                         tresult = isc_symtab_define(symtab, key, vclass,
1298                                                     symvalue,
1299                                                     isc_symexists_reject);
1300                         if (tresult == ISC_R_EXISTS) {
1301                                 const char *file;
1302                                 unsigned int line;
1303                                 RUNTIME_CHECK(isc_symtab_lookup(symtab, key,
1304                                            vclass, &symvalue) == ISC_R_SUCCESS);
1305                                 file = cfg_obj_file(symvalue.as_pointer);
1306                                 line = cfg_obj_line(symvalue.as_pointer);
1307                                 cfg_obj_log(view, logctx, ISC_LOG_ERROR,
1308                                             "view '%s': already exists "
1309                                             "previous definition: %s:%u",
1310                                             key, file, line);
1311                                 result = tresult;
1312                         } else if (result != ISC_R_SUCCESS) {
1313                                 result = tresult;
1314                         } else if ((strcasecmp(key, "_bind") == 0 &&
1315                                     vclass == dns_rdataclass_ch) ||
1316                                    (strcasecmp(key, "_default") == 0 &&
1317                                     vclass == dns_rdataclass_in)) {
1318                                 cfg_obj_log(view, logctx, ISC_LOG_ERROR,
1319                                             "attempt to redefine builtin view "
1320                                             "'%s'", key);
1321                                 result = ISC_R_EXISTS;
1322                         }
1323                 }
1324                 if (tresult == ISC_R_SUCCESS)
1325                         tresult = check_viewconf(config, voptions,
1326                                                  vclass, logctx, mctx);
1327                 if (tresult != ISC_R_SUCCESS)
1328                         result = ISC_R_FAILURE;
1329         }
1330         if (symtab != NULL)
1331                 isc_symtab_destroy(&symtab);
1332
1333         if (views != NULL && options != NULL) {
1334                 obj = NULL;
1335                 tresult = cfg_map_get(options, "cache-file", &obj);
1336                 if (tresult == ISC_R_SUCCESS) {
1337                         cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1338                                     "'cache-file' cannot be a global "
1339                                     "option if views are present");
1340                         result = ISC_R_FAILURE;
1341                 }
1342         }
1343
1344         tresult = cfg_map_get(config, "acl", &acls);
1345         if (tresult == ISC_R_SUCCESS) {
1346                 cfg_listelt_t *elt;
1347                 cfg_listelt_t *elt2;
1348                 const char *aclname;
1349
1350                 for (elt = cfg_list_first(acls);
1351                      elt != NULL;
1352                      elt = cfg_list_next(elt)) {
1353                         cfg_obj_t *acl = cfg_listelt_value(elt);
1354                         unsigned int i;
1355
1356                         aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
1357                         for (i = 0;
1358                              i < sizeof(builtin) / sizeof(builtin[0]);
1359                              i++)
1360                                 if (strcasecmp(aclname, builtin[i]) == 0) {
1361                                         cfg_obj_log(acl, logctx, ISC_LOG_ERROR,
1362                                                     "attempt to redefine "
1363                                                     "builtin acl '%s'",
1364                                                     aclname);
1365                                         result = ISC_R_FAILURE;
1366                                         break;
1367                                 }
1368
1369                         for (elt2 = cfg_list_next(elt);
1370                              elt2 != NULL;
1371                              elt2 = cfg_list_next(elt2)) {
1372                                 cfg_obj_t *acl2 = cfg_listelt_value(elt2);
1373                                 const char *name;
1374                                 name = cfg_obj_asstring(cfg_tuple_get(acl2,
1375                                                                       "name"));
1376                                 if (strcasecmp(aclname, name) == 0) {
1377                                         const char *file = cfg_obj_file(acl);
1378                                         unsigned int line = cfg_obj_line(acl);
1379
1380                                         if (file == NULL)
1381                                                 file = "<unknown file>";
1382
1383                                         cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
1384                                                     "attempt to redefine "
1385                                                     "acl '%s' previous "
1386                                                     "definition: %s:%u",
1387                                                      name, file, line);
1388                                         result = ISC_R_FAILURE;
1389                                 }
1390                         }
1391                 }
1392         }
1393
1394         tresult = cfg_map_get(config, "kal", &kals);
1395         if (tresult == ISC_R_SUCCESS) {
1396                 cfg_listelt_t *elt;
1397                 cfg_listelt_t *elt2;
1398                 const char *aclname;
1399
1400                 for (elt = cfg_list_first(kals);
1401                      elt != NULL;
1402                      elt = cfg_list_next(elt)) {
1403                         cfg_obj_t *acl = cfg_listelt_value(elt);
1404
1405                         aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
1406
1407                         for (elt2 = cfg_list_next(elt);
1408                              elt2 != NULL;
1409                              elt2 = cfg_list_next(elt2)) {
1410                                 cfg_obj_t *acl2 = cfg_listelt_value(elt2);
1411                                 const char *name;
1412                                 name = cfg_obj_asstring(cfg_tuple_get(acl2,
1413                                                                       "name"));
1414                                 if (strcasecmp(aclname, name) == 0) {
1415                                         const char *file = cfg_obj_file(acl);
1416                                         unsigned int line = cfg_obj_line(acl);
1417
1418                                         if (file == NULL)
1419                                                 file = "<unknown file>";
1420
1421                                         cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
1422                                                     "attempt to redefine "
1423                                                     "kal '%s' previous "
1424                                                     "definition: %s:%u",
1425                                                      name, file, line);
1426                                         result = ISC_R_FAILURE;
1427                                 }
1428                         }
1429                 }
1430         }
1431
1432         return (result);
1433 }