Merge from vendor branch TCPDUMP:
[dragonfly.git] / contrib / bind-9.3 / lib / bind9 / check.c
1 /*
2  * Copyright (C) 2004, 2005  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.32 2005/11/03 23:08: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 fully implemented");
123         } else if (/* strcasecmp(cfg_obj_asstring(obj), "fixed") != 0 && */
124                    strcasecmp(cfg_obj_asstring(obj), "random") != 0 &&
125                    strcasecmp(cfg_obj_asstring(obj), "cyclic") != 0) {
126                 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
127                             "rrset-order: invalid order '%s'",
128                             cfg_obj_asstring(obj));
129                 result = ISC_R_FAILURE;
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                 *countp = count;
603                 return (result);
604         }
605
606  newlist:
607         list = cfg_tuple_get(obj, "addresses");
608         element = cfg_list_first(list);
609  resume:        
610         for ( ;
611              element != NULL;
612              element = cfg_list_next(element))
613         {
614                 char *listname;
615                 cfg_obj_t *addr;
616                 cfg_obj_t *key;
617
618                 addr = cfg_tuple_get(cfg_listelt_value(element),
619                                      "masterselement");
620                 key = cfg_tuple_get(cfg_listelt_value(element), "key");
621
622                 if (cfg_obj_issockaddr(addr)) {
623                         count++;
624                         continue;
625                 }
626                 if (!cfg_obj_isvoid(key)) {
627                         cfg_obj_log(key, logctx, ISC_LOG_ERROR,
628                                     "unexpected token '%s'",
629                                     cfg_obj_asstring(key));
630                         if (result == ISC_R_SUCCESS)
631                                 result = ISC_R_FAILURE;
632                 }
633                 listname = cfg_obj_asstring(addr);
634                 symvalue.as_pointer = addr;
635                 tresult = isc_symtab_define(symtab, listname, 1, symvalue,
636                                             isc_symexists_reject);
637                 if (tresult == ISC_R_EXISTS)
638                         continue;
639                 tresult = get_masters_def(config, listname, &obj);
640                 if (tresult != ISC_R_SUCCESS) {
641                         if (result == ISC_R_SUCCESS)
642                                 result = tresult;
643                         cfg_obj_log(addr, logctx, ISC_LOG_ERROR,
644                                     "unable to find masters list '%s'",
645                                     listname);
646                         continue;
647                 }
648                 /* Grow stack? */
649                 if (stackcount == pushed) {
650                         void * new;
651                         isc_uint32_t newlen = stackcount + 16;
652                         size_t newsize, oldsize;
653
654                         newsize = newlen * sizeof(*stack);
655                         oldsize = stackcount * sizeof(*stack);
656                         new = isc_mem_get(mctx, newsize);
657                         if (new == NULL)
658                                 goto cleanup;
659                         if (stackcount != 0) {
660                                 memcpy(new, stack, oldsize);
661                                 isc_mem_put(mctx, stack, oldsize);
662                         }
663                         stack = new;
664                         stackcount = newlen;
665                 }
666                 stack[pushed++] = cfg_list_next(element);
667                 goto newlist;
668         }
669         if (pushed != 0) {
670                 element = stack[--pushed];
671                 goto resume;
672         }
673  cleanup:
674         if (stack != NULL)
675                 isc_mem_put(mctx, stack, stackcount * sizeof(*stack));
676         isc_symtab_destroy(&symtab);
677         *countp = count;
678         return (result);
679 }
680
681 #define MASTERZONE      1
682 #define SLAVEZONE       2
683 #define STUBZONE        4
684 #define HINTZONE        8
685 #define FORWARDZONE     16
686 #define DELEGATIONZONE  32
687
688 typedef struct {
689         const char *name;
690         int allowed;
691 } optionstable;
692
693 static isc_result_t
694 check_zoneconf(cfg_obj_t *zconfig, cfg_obj_t *config, isc_symtab_t *symtab,
695                dns_rdataclass_t defclass, isc_log_t *logctx, isc_mem_t *mctx)
696 {
697         const char *zname;
698         const char *typestr;
699         unsigned int ztype;
700         cfg_obj_t *zoptions;
701         cfg_obj_t *obj = NULL;
702         isc_result_t result = ISC_R_SUCCESS;
703         isc_result_t tresult;
704         unsigned int i;
705         dns_rdataclass_t zclass;
706         dns_fixedname_t fixedname;
707         isc_buffer_t b;
708
709         static optionstable options[] = {
710         { "allow-query", MASTERZONE | SLAVEZONE | STUBZONE },
711         { "allow-notify", SLAVEZONE },
712         { "allow-transfer", MASTERZONE | SLAVEZONE },
713         { "notify", MASTERZONE | SLAVEZONE },
714         { "also-notify", MASTERZONE | SLAVEZONE },
715         { "dialup", MASTERZONE | SLAVEZONE | STUBZONE },
716         { "delegation-only", HINTZONE | STUBZONE },
717         { "forward", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE},
718         { "forwarders", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE},
719         { "maintain-ixfr-base", MASTERZONE | SLAVEZONE },
720         { "max-ixfr-log-size", MASTERZONE | SLAVEZONE },
721         { "notify-source", MASTERZONE | SLAVEZONE },
722         { "notify-source-v6", MASTERZONE | SLAVEZONE },
723         { "transfer-source", SLAVEZONE | STUBZONE },
724         { "transfer-source-v6", SLAVEZONE | STUBZONE },
725         { "max-transfer-time-in", SLAVEZONE | STUBZONE },
726         { "max-transfer-time-out", MASTERZONE | SLAVEZONE },
727         { "max-transfer-idle-in", SLAVEZONE | STUBZONE },
728         { "max-transfer-idle-out", MASTERZONE | SLAVEZONE },
729         { "max-retry-time", SLAVEZONE | STUBZONE },
730         { "min-retry-time", SLAVEZONE | STUBZONE },
731         { "max-refresh-time", SLAVEZONE | STUBZONE },
732         { "min-refresh-time", SLAVEZONE | STUBZONE },
733         { "sig-validity-interval", MASTERZONE },
734         { "zone-statistics", MASTERZONE | SLAVEZONE | STUBZONE },
735         { "allow-update", MASTERZONE },
736         { "allow-update-forwarding", SLAVEZONE },
737         { "file", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE },
738         { "ixfr-base", MASTERZONE | SLAVEZONE },
739         { "ixfr-tmp-file", MASTERZONE | SLAVEZONE },
740         { "masters", SLAVEZONE | STUBZONE },
741         { "pubkey", MASTERZONE | SLAVEZONE | STUBZONE },
742         { "update-policy", MASTERZONE },
743         { "database", MASTERZONE | SLAVEZONE | STUBZONE },
744         { "key-directory", MASTERZONE },
745         };
746
747         static optionstable dialups[] = {
748         { "notify", MASTERZONE | SLAVEZONE },
749         { "notify-passive", SLAVEZONE },
750         { "refresh", SLAVEZONE | STUBZONE },
751         { "passive", SLAVEZONE | STUBZONE },
752         };
753
754         zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
755
756         zoptions = cfg_tuple_get(zconfig, "options");
757
758         obj = NULL;
759         (void)cfg_map_get(zoptions, "type", &obj);
760         if (obj == NULL) {
761                 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
762                             "zone '%s': type not present", zname);
763                 return (ISC_R_FAILURE);
764         }
765
766         typestr = cfg_obj_asstring(obj);
767         if (strcasecmp(typestr, "master") == 0)
768                 ztype = MASTERZONE;
769         else if (strcasecmp(typestr, "slave") == 0)
770                 ztype = SLAVEZONE;
771         else if (strcasecmp(typestr, "stub") == 0)
772                 ztype = STUBZONE;
773         else if (strcasecmp(typestr, "forward") == 0)
774                 ztype = FORWARDZONE;
775         else if (strcasecmp(typestr, "hint") == 0)
776                 ztype = HINTZONE;
777         else if (strcasecmp(typestr, "delegation-only") == 0)
778                 ztype = DELEGATIONZONE;
779         else {
780                 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
781                             "zone '%s': invalid type %s",
782                             zname, typestr);
783                 return (ISC_R_FAILURE);
784         }
785
786         obj = cfg_tuple_get(zconfig, "class");
787         if (cfg_obj_isstring(obj)) {
788                 isc_textregion_t r;
789
790                 DE_CONST(cfg_obj_asstring(obj), r.base);
791                 r.length = strlen(r.base);
792                 result = dns_rdataclass_fromtext(&zclass, &r);
793                 if (result != ISC_R_SUCCESS) {
794                         cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
795                                     "zone '%s': invalid class %s",
796                                     zname, r.base);
797                         return (ISC_R_FAILURE);
798                 }
799                 if (zclass != defclass) {
800                         cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
801                                     "zone '%s': class '%s' does not "
802                                     "match view/default class",
803                                     zname, r.base);
804                         return (ISC_R_FAILURE);
805                 }
806         }
807
808         /*
809          * Look for an already existing zone.
810          * We need to make this cannonical as isc_symtab_define()
811          * deals with strings.
812          */
813         dns_fixedname_init(&fixedname);
814         isc_buffer_init(&b, zname, strlen(zname));
815         isc_buffer_add(&b, strlen(zname));
816         tresult = dns_name_fromtext(dns_fixedname_name(&fixedname), &b,
817                                    dns_rootname, ISC_TRUE, NULL);
818         if (result != ISC_R_SUCCESS) {
819                 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
820                             "zone '%s': is not a valid name", zname);
821                 tresult = ISC_R_FAILURE;
822         } else {
823                 char namebuf[DNS_NAME_FORMATSIZE];
824
825                 dns_name_format(dns_fixedname_name(&fixedname),
826                                 namebuf, sizeof(namebuf));
827                 tresult = nameexist(zconfig, namebuf, ztype == HINTZONE ? 1 : 2,
828                                    symtab, "zone '%s': already exists "
829                                    "previous definition: %s:%u", logctx, mctx);
830                 if (tresult != ISC_R_SUCCESS)
831                         result = tresult;
832         }
833
834         /*
835          * Look for inappropriate options for the given zone type.
836          */
837         for (i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
838                 obj = NULL;
839                 if ((options[i].allowed & ztype) == 0 &&
840                     cfg_map_get(zoptions, options[i].name, &obj) ==
841                     ISC_R_SUCCESS)
842                 {
843                         if (strcmp(options[i].name, "allow-update") != 0 ||
844                             ztype != SLAVEZONE) {
845                                 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
846                                             "option '%s' is not allowed "
847                                             "in '%s' zone '%s'",
848                                             options[i].name, typestr, zname);
849                                         result = ISC_R_FAILURE;
850                         } else
851                                 cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
852                                             "option '%s' is not allowed "
853                                             "in '%s' zone '%s'",
854                                             options[i].name, typestr, zname);
855                 }
856         }
857
858         /*
859          * Slave & stub zones must have a "masters" field.
860          */
861         if (ztype == SLAVEZONE || ztype == STUBZONE) {
862                 obj = NULL;
863                 if (cfg_map_get(zoptions, "masters", &obj) != ISC_R_SUCCESS) {
864                         cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
865                                     "zone '%s': missing 'masters' entry",
866                                     zname);
867                         result = ISC_R_FAILURE;
868                 } else {
869                         isc_uint32_t count;
870                         tresult = validate_masters(obj, config, &count,
871                                                    logctx, mctx);
872                         if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS)
873                                 result = tresult;
874                         if (tresult == ISC_R_SUCCESS && count == 0) {
875                                 cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
876                                             "zone '%s': empty 'masters' entry",
877                                             zname);
878                                 result = ISC_R_FAILURE;
879                         }
880                 }
881         }
882
883         /*
884          * Master zones can't have both "allow-update" and "update-policy".
885          */
886         if (ztype == MASTERZONE) {
887                 isc_result_t res1, res2;
888                 obj = NULL;
889                 res1 = cfg_map_get(zoptions, "allow-update", &obj);
890                 obj = NULL;
891                 res2 = cfg_map_get(zoptions, "update-policy", &obj);
892                 if (res1 == ISC_R_SUCCESS && res2 == ISC_R_SUCCESS) {
893                         cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
894                                     "zone '%s': 'allow-update' is ignored "
895                                     "when 'update-policy' is present",
896                                     zname);
897                         result = ISC_R_FAILURE;
898                 }
899         }
900
901         /*
902          * Check the excessively complicated "dialup" option.
903          */
904         if (ztype == MASTERZONE || ztype == SLAVEZONE || ztype == STUBZONE) {
905                 cfg_obj_t *dialup = NULL;
906                 (void)cfg_map_get(zoptions, "dialup", &dialup);
907                 if (dialup != NULL && cfg_obj_isstring(dialup)) {
908                         char *str = cfg_obj_asstring(dialup);
909                         for (i = 0;
910                              i < sizeof(dialups) / sizeof(dialups[0]);
911                              i++)
912                         {
913                                 if (strcasecmp(dialups[i].name, str) != 0)
914                                         continue;
915                                 if ((dialups[i].allowed & ztype) == 0) {
916                                         cfg_obj_log(obj, logctx,
917                                                     ISC_LOG_ERROR,
918                                                     "dialup type '%s' is not "
919                                                     "allowed in '%s' "
920                                                     "zone '%s'",
921                                                     str, typestr, zname);
922                                         result = ISC_R_FAILURE;
923                                 }
924                                 break;
925                         }
926                         if (i == sizeof(dialups) / sizeof(dialups[0])) {
927                                 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
928                                             "invalid dialup type '%s' in zone "
929                                             "'%s'", str, zname);
930                                 result = ISC_R_FAILURE;
931                         }
932                 }
933         }
934
935         /*
936          * Check that forwarding is reasonable.
937          */
938         if (check_forward(zoptions, logctx) != ISC_R_SUCCESS)
939                 result = ISC_R_FAILURE;
940
941         /*
942          * Check various options.
943          */
944         tresult = check_options(zoptions, logctx, mctx);
945         if (tresult != ISC_R_SUCCESS)
946                 result = tresult;
947
948         /*
949          * If the zone type is rbt/rbt64 then master/hint zones
950          * require file clauses.
951          */
952         obj = NULL;
953         tresult = cfg_map_get(zoptions, "database", &obj);
954         if (tresult == ISC_R_NOTFOUND ||
955             (tresult == ISC_R_SUCCESS &&
956              (strcmp("rbt", cfg_obj_asstring(obj)) == 0 ||
957               strcmp("rbt64", cfg_obj_asstring(obj)) == 0))) {
958                 obj = NULL;
959                 tresult = cfg_map_get(zoptions, "file", &obj);
960                 if (tresult != ISC_R_SUCCESS &&
961                     (ztype == MASTERZONE || ztype == HINTZONE)) {
962                         cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
963                                     "zone '%s': missing 'file' entry",
964                                     zname);
965                         result = tresult;
966                 }
967         }
968         
969         return (result);
970 }
971
972 isc_result_t
973 bind9_check_key(cfg_obj_t *key, isc_log_t *logctx) {
974         cfg_obj_t *algobj = NULL;
975         cfg_obj_t *secretobj = NULL;
976         const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
977         
978         (void)cfg_map_get(key, "algorithm", &algobj);
979         (void)cfg_map_get(key, "secret", &secretobj);
980         if (secretobj == NULL || algobj == NULL) {
981                 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
982                             "key '%s' must have both 'secret' and "
983                             "'algorithm' defined",
984                             keyname);
985                 return (ISC_R_FAILURE);
986         }
987         return (ISC_R_SUCCESS);
988 }
989
990 static isc_result_t
991 check_keylist(cfg_obj_t *keys, isc_symtab_t *symtab, isc_log_t *logctx) {
992         isc_result_t result = ISC_R_SUCCESS;
993         isc_result_t tresult;
994         cfg_listelt_t *element;
995
996         for (element = cfg_list_first(keys);
997              element != NULL;
998              element = cfg_list_next(element))
999         {
1000                 cfg_obj_t *key = cfg_listelt_value(element);
1001                 const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
1002                 isc_symvalue_t symvalue;
1003
1004                 symvalue.as_pointer = key;
1005                 tresult = isc_symtab_define(symtab, keyname, 1,
1006                                             symvalue, isc_symexists_reject);
1007                 if (tresult == ISC_R_EXISTS) {
1008                         const char *file;
1009                         unsigned int line;
1010
1011                         RUNTIME_CHECK(isc_symtab_lookup(symtab, keyname,
1012                                             1, &symvalue) == ISC_R_SUCCESS);
1013                         file = cfg_obj_file(symvalue.as_pointer);
1014                         line = cfg_obj_line(symvalue.as_pointer);
1015
1016                         if (file == NULL)
1017                                 file = "<unknown file>";
1018                         cfg_obj_log(key, logctx, ISC_LOG_ERROR,
1019                                     "key '%s': already exists "
1020                                     "previous definition: %s:%u",
1021                                     keyname, file, line);
1022                         result = tresult;
1023                 } else if (tresult != ISC_R_SUCCESS)
1024                         return (tresult);
1025
1026                 tresult = bind9_check_key(key, logctx);
1027                 if (tresult != ISC_R_SUCCESS)
1028                         return (tresult);
1029         }
1030         return (result);
1031 }
1032
1033 static isc_result_t
1034 check_servers(cfg_obj_t *servers, isc_log_t *logctx) {
1035         isc_result_t result = ISC_R_SUCCESS;
1036         cfg_listelt_t *e1, *e2;
1037         cfg_obj_t *v1, *v2;
1038         isc_sockaddr_t *s1, *s2;
1039         isc_netaddr_t na;
1040         cfg_obj_t *ts;
1041         char buf[128];
1042         const char *xfr;
1043         isc_buffer_t target;
1044
1045         for (e1 = cfg_list_first(servers); e1 != NULL; e1 = cfg_list_next(e1)) {
1046                 v1 = cfg_listelt_value(e1);
1047                 s1 = cfg_obj_assockaddr(cfg_map_getname(v1));
1048                 ts = NULL;
1049                 if (isc_sockaddr_pf(s1) == AF_INET)
1050                         xfr = "transfer-source-v6";
1051                 else
1052                         xfr = "transfer-source";
1053                 (void)cfg_map_get(v1, xfr, &ts);
1054                 if (ts != NULL) {
1055                         isc_netaddr_fromsockaddr(&na, s1);
1056                         isc_buffer_init(&target, buf, sizeof(buf) - 1);
1057                         RUNTIME_CHECK(isc_netaddr_totext(&na, &target)
1058                                       == ISC_R_SUCCESS);
1059                         buf[isc_buffer_usedlength(&target)] = '\0';
1060                         cfg_obj_log(v1, logctx, ISC_LOG_ERROR,
1061                                     "server '%s': %s not valid", buf, xfr);
1062                         result = ISC_R_FAILURE;
1063                 }
1064                 e2 = e1;
1065                 while ((e2 = cfg_list_next(e2)) != NULL) {
1066                         v2 = cfg_listelt_value(e2);
1067                         s2 = cfg_obj_assockaddr(cfg_map_getname(v2));
1068                         if (isc_sockaddr_eqaddr(s1, s2)) {
1069                                 const char *file = cfg_obj_file(v1);
1070                                 unsigned int line = cfg_obj_line(v1);
1071
1072                                 if (file == NULL)
1073                                         file = "<unknown file>";
1074
1075                                 isc_netaddr_fromsockaddr(&na, s2);
1076                                 isc_buffer_init(&target, buf, sizeof(buf) - 1);
1077                                 RUNTIME_CHECK(isc_netaddr_totext(&na, &target)
1078                                               == ISC_R_SUCCESS);
1079                                 buf[isc_buffer_usedlength(&target)] = '\0';
1080
1081                                 cfg_obj_log(v2, logctx, ISC_LOG_ERROR,
1082                                             "server '%s': already exists "
1083                                             "previous definition: %s:%u",
1084                                             buf, file, line);
1085                                 result = ISC_R_FAILURE;
1086                         }
1087                 }
1088         }
1089         return (result);
1090 }
1091                 
1092 static isc_result_t
1093 check_viewconf(cfg_obj_t *config, cfg_obj_t *vconfig, dns_rdataclass_t vclass,
1094                isc_log_t *logctx, isc_mem_t *mctx)
1095 {
1096         cfg_obj_t *servers = NULL;
1097         cfg_obj_t *zones = NULL;
1098         cfg_obj_t *keys = NULL;
1099         cfg_listelt_t *element;
1100         isc_symtab_t *symtab = NULL;
1101         isc_result_t result = ISC_R_SUCCESS;
1102         isc_result_t tresult = ISC_R_SUCCESS;
1103
1104         /*
1105          * Check that all zone statements are syntactically correct and
1106          * there are no duplicate zones.
1107          */
1108         tresult = isc_symtab_create(mctx, 100, freekey, mctx,
1109                                     ISC_FALSE, &symtab);
1110         if (tresult != ISC_R_SUCCESS)
1111                 return (ISC_R_NOMEMORY);
1112
1113         if (vconfig != NULL)
1114                 (void)cfg_map_get(vconfig, "zone", &zones);
1115         else
1116                 (void)cfg_map_get(config, "zone", &zones);
1117
1118         for (element = cfg_list_first(zones);
1119              element != NULL;
1120              element = cfg_list_next(element))
1121         {
1122                 isc_result_t tresult;
1123                 cfg_obj_t *zone = cfg_listelt_value(element);
1124
1125                 tresult = check_zoneconf(zone, config, symtab, vclass,
1126                                          logctx, mctx);
1127                 if (tresult != ISC_R_SUCCESS)
1128                         result = ISC_R_FAILURE;
1129         }
1130
1131         isc_symtab_destroy(&symtab);
1132
1133         /*
1134          * Check that all key statements are syntactically correct and
1135          * there are no duplicate keys.
1136          */
1137         tresult = isc_symtab_create(mctx, 100, NULL, NULL, ISC_TRUE, &symtab);
1138         if (tresult != ISC_R_SUCCESS)
1139                 return (ISC_R_NOMEMORY);
1140
1141         (void)cfg_map_get(config, "key", &keys);
1142         tresult = check_keylist(keys, symtab, logctx);
1143         if (tresult == ISC_R_EXISTS)
1144                 result = ISC_R_FAILURE;
1145         else if (tresult != ISC_R_SUCCESS) {
1146                 isc_symtab_destroy(&symtab);
1147                 return (tresult);
1148         }
1149         
1150         if (vconfig != NULL) {
1151                 keys = NULL;
1152                 (void)cfg_map_get(vconfig, "key", &keys);
1153                 tresult = check_keylist(keys, symtab, logctx);
1154                 if (tresult == ISC_R_EXISTS)
1155                         result = ISC_R_FAILURE;
1156                 else if (tresult != ISC_R_SUCCESS) {
1157                         isc_symtab_destroy(&symtab);
1158                         return (tresult);
1159                 }
1160         }
1161
1162         isc_symtab_destroy(&symtab);
1163
1164         /*
1165          * Check that forwarding is reasonable.
1166          */
1167         if (vconfig == NULL) {
1168                 cfg_obj_t *options = NULL;
1169                 (void)cfg_map_get(config, "options", &options);
1170                 if (options != NULL)
1171                         if (check_forward(options, logctx) != ISC_R_SUCCESS)
1172                                 result = ISC_R_FAILURE;
1173         } else {
1174                 if (check_forward(vconfig, logctx) != ISC_R_SUCCESS)
1175                         result = ISC_R_FAILURE;
1176         }
1177         /*
1178          * Check that dual-stack-servers is reasonable.
1179          */
1180         if (vconfig == NULL) {
1181                 cfg_obj_t *options = NULL;
1182                 (void)cfg_map_get(config, "options", &options);
1183                 if (options != NULL)
1184                         if (check_dual_stack(options, logctx) != ISC_R_SUCCESS)
1185                                 result = ISC_R_FAILURE;
1186         } else {
1187                 if (check_dual_stack(vconfig, logctx) != ISC_R_SUCCESS)
1188                         result = ISC_R_FAILURE;
1189         }
1190
1191         /*
1192          * Check that rrset-order is reasonable.
1193          */
1194         if (vconfig != NULL) {
1195                 if (check_order(vconfig, logctx) != ISC_R_SUCCESS)
1196                         result = ISC_R_FAILURE;
1197         }
1198
1199         if (vconfig != NULL) {
1200                 (void)cfg_map_get(vconfig, "server", &servers);
1201                 if (servers != NULL &&
1202                     check_servers(servers, logctx) != ISC_R_SUCCESS)
1203                         result = ISC_R_FAILURE;
1204         }
1205
1206         if (vconfig != NULL)
1207                 tresult = check_options(vconfig, logctx, mctx);
1208         else
1209                 tresult = check_options(config, logctx, mctx);
1210         if (tresult != ISC_R_SUCCESS)
1211                 result = tresult;
1212
1213         return (result);
1214 }
1215
1216
1217 isc_result_t
1218 bind9_check_namedconf(cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx) {
1219         cfg_obj_t *options = NULL;
1220         cfg_obj_t *servers = NULL;
1221         cfg_obj_t *views = NULL;
1222         cfg_obj_t *acls = NULL;
1223         cfg_obj_t *kals = NULL;
1224         cfg_obj_t *obj;
1225         cfg_listelt_t *velement;
1226         isc_result_t result = ISC_R_SUCCESS;
1227         isc_result_t tresult;
1228         isc_symtab_t *symtab = NULL;
1229
1230         static const char *builtin[] = { "localhost", "localnets",
1231                                          "any", "none"};
1232
1233         (void)cfg_map_get(config, "options", &options);
1234
1235         if (options != NULL &&
1236             check_options(options, logctx, mctx) != ISC_R_SUCCESS)
1237                 result = ISC_R_FAILURE;
1238
1239         (void)cfg_map_get(config, "server", &servers);
1240         if (servers != NULL &&
1241             check_servers(servers, logctx) != ISC_R_SUCCESS)
1242                 result = ISC_R_FAILURE;
1243
1244         if (options != NULL && 
1245             check_order(options, logctx) != ISC_R_SUCCESS)
1246                 result = ISC_R_FAILURE;
1247
1248         (void)cfg_map_get(config, "view", &views);
1249
1250         if (views != NULL && options != NULL)
1251                 if (check_dual_stack(options, logctx) != ISC_R_SUCCESS)
1252                         result = ISC_R_FAILURE;
1253
1254         if (views == NULL) {
1255                 if (check_viewconf(config, NULL, dns_rdataclass_in,
1256                                    logctx, mctx) != ISC_R_SUCCESS)
1257                         result = ISC_R_FAILURE;
1258         } else {
1259                 cfg_obj_t *zones = NULL;
1260
1261                 (void)cfg_map_get(config, "zone", &zones);
1262                 if (zones != NULL) {
1263                         cfg_obj_log(zones, logctx, ISC_LOG_ERROR,
1264                                     "when using 'view' statements, "
1265                                     "all zones must be in views");
1266                         result = ISC_R_FAILURE;
1267                 }
1268         }
1269
1270         tresult = isc_symtab_create(mctx, 100, NULL, NULL, ISC_TRUE, &symtab);
1271         if (tresult != ISC_R_SUCCESS)
1272                 result = tresult;
1273         for (velement = cfg_list_first(views);
1274              velement != NULL;
1275              velement = cfg_list_next(velement))
1276         {
1277                 cfg_obj_t *view = cfg_listelt_value(velement);
1278                 cfg_obj_t *vname = cfg_tuple_get(view, "name");
1279                 cfg_obj_t *voptions = cfg_tuple_get(view, "options");
1280                 cfg_obj_t *vclassobj = cfg_tuple_get(view, "class");
1281                 dns_rdataclass_t vclass = dns_rdataclass_in;
1282                 isc_result_t tresult = ISC_R_SUCCESS;
1283                 const char *key = cfg_obj_asstring(vname);
1284                 isc_symvalue_t symvalue;
1285
1286                 if (cfg_obj_isstring(vclassobj)) {
1287                         isc_textregion_t r;
1288
1289                         DE_CONST(cfg_obj_asstring(vclassobj), r.base);
1290                         r.length = strlen(r.base);
1291                         tresult = dns_rdataclass_fromtext(&vclass, &r);
1292                         if (tresult != ISC_R_SUCCESS)
1293                                 cfg_obj_log(vclassobj, logctx, ISC_LOG_ERROR,
1294                                             "view '%s': invalid class %s",
1295                                             cfg_obj_asstring(vname), r.base);
1296                 }
1297                 if (tresult == ISC_R_SUCCESS && symtab != NULL) {
1298                         symvalue.as_pointer = view;
1299                         tresult = isc_symtab_define(symtab, key, vclass,
1300                                                     symvalue,
1301                                                     isc_symexists_reject);
1302                         if (tresult == ISC_R_EXISTS) {
1303                                 const char *file;
1304                                 unsigned int line;
1305                                 RUNTIME_CHECK(isc_symtab_lookup(symtab, key,
1306                                            vclass, &symvalue) == ISC_R_SUCCESS);
1307                                 file = cfg_obj_file(symvalue.as_pointer);
1308                                 line = cfg_obj_line(symvalue.as_pointer);
1309                                 cfg_obj_log(view, logctx, ISC_LOG_ERROR,
1310                                             "view '%s': already exists "
1311                                             "previous definition: %s:%u",
1312                                             key, file, line);
1313                                 result = tresult;
1314                         } else if (result != ISC_R_SUCCESS) {
1315                                 result = tresult;
1316                         } else if ((strcasecmp(key, "_bind") == 0 &&
1317                                     vclass == dns_rdataclass_ch) ||
1318                                    (strcasecmp(key, "_default") == 0 &&
1319                                     vclass == dns_rdataclass_in)) {
1320                                 cfg_obj_log(view, logctx, ISC_LOG_ERROR,
1321                                             "attempt to redefine builtin view "
1322                                             "'%s'", key);
1323                                 result = ISC_R_EXISTS;
1324                         }
1325                 }
1326                 if (tresult == ISC_R_SUCCESS)
1327                         tresult = check_viewconf(config, voptions,
1328                                                  vclass, logctx, mctx);
1329                 if (tresult != ISC_R_SUCCESS)
1330                         result = ISC_R_FAILURE;
1331         }
1332         if (symtab != NULL)
1333                 isc_symtab_destroy(&symtab);
1334
1335         if (views != NULL && options != NULL) {
1336                 obj = NULL;
1337                 tresult = cfg_map_get(options, "cache-file", &obj);
1338                 if (tresult == ISC_R_SUCCESS) {
1339                         cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1340                                     "'cache-file' cannot be a global "
1341                                     "option if views are present");
1342                         result = ISC_R_FAILURE;
1343                 }
1344         }
1345
1346         tresult = cfg_map_get(config, "acl", &acls);
1347         if (tresult == ISC_R_SUCCESS) {
1348                 cfg_listelt_t *elt;
1349                 cfg_listelt_t *elt2;
1350                 const char *aclname;
1351
1352                 for (elt = cfg_list_first(acls);
1353                      elt != NULL;
1354                      elt = cfg_list_next(elt)) {
1355                         cfg_obj_t *acl = cfg_listelt_value(elt);
1356                         unsigned int i;
1357
1358                         aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
1359                         for (i = 0;
1360                              i < sizeof(builtin) / sizeof(builtin[0]);
1361                              i++)
1362                                 if (strcasecmp(aclname, builtin[i]) == 0) {
1363                                         cfg_obj_log(acl, logctx, ISC_LOG_ERROR,
1364                                                     "attempt to redefine "
1365                                                     "builtin acl '%s'",
1366                                                     aclname);
1367                                         result = ISC_R_FAILURE;
1368                                         break;
1369                                 }
1370
1371                         for (elt2 = cfg_list_next(elt);
1372                              elt2 != NULL;
1373                              elt2 = cfg_list_next(elt2)) {
1374                                 cfg_obj_t *acl2 = cfg_listelt_value(elt2);
1375                                 const char *name;
1376                                 name = cfg_obj_asstring(cfg_tuple_get(acl2,
1377                                                                       "name"));
1378                                 if (strcasecmp(aclname, name) == 0) {
1379                                         const char *file = cfg_obj_file(acl);
1380                                         unsigned int line = cfg_obj_line(acl);
1381
1382                                         if (file == NULL)
1383                                                 file = "<unknown file>";
1384
1385                                         cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
1386                                                     "attempt to redefine "
1387                                                     "acl '%s' previous "
1388                                                     "definition: %s:%u",
1389                                                      name, file, line);
1390                                         result = ISC_R_FAILURE;
1391                                 }
1392                         }
1393                 }
1394         }
1395
1396         tresult = cfg_map_get(config, "kal", &kals);
1397         if (tresult == ISC_R_SUCCESS) {
1398                 cfg_listelt_t *elt;
1399                 cfg_listelt_t *elt2;
1400                 const char *aclname;
1401
1402                 for (elt = cfg_list_first(kals);
1403                      elt != NULL;
1404                      elt = cfg_list_next(elt)) {
1405                         cfg_obj_t *acl = cfg_listelt_value(elt);
1406
1407                         aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
1408
1409                         for (elt2 = cfg_list_next(elt);
1410                              elt2 != NULL;
1411                              elt2 = cfg_list_next(elt2)) {
1412                                 cfg_obj_t *acl2 = cfg_listelt_value(elt2);
1413                                 const char *name;
1414                                 name = cfg_obj_asstring(cfg_tuple_get(acl2,
1415                                                                       "name"));
1416                                 if (strcasecmp(aclname, name) == 0) {
1417                                         const char *file = cfg_obj_file(acl);
1418                                         unsigned int line = cfg_obj_line(acl);
1419
1420                                         if (file == NULL)
1421                                                 file = "<unknown file>";
1422
1423                                         cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
1424                                                     "attempt to redefine "
1425                                                     "kal '%s' previous "
1426                                                     "definition: %s:%u",
1427                                                      name, file, line);
1428                                         result = ISC_R_FAILURE;
1429                                 }
1430                         }
1431                 }
1432         }
1433
1434         return (result);
1435 }