]> CyberLeo.Net >> Repos - FreeBSD/releng/9.1.git/blob - contrib/bind9/lib/isccfg/namedconf.c
Fix multiple vulnerabilities in file(1) and libmagic(3).
[FreeBSD/releng/9.1.git] / contrib / bind9 / lib / isccfg / namedconf.c
1 /*
2  * Copyright (C) 2004-2012  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2002, 2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and/or 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 /*! \file */
19
20 #include <config.h>
21
22 #include <string.h>
23
24 #include <isc/lex.h>
25 #include <isc/mem.h>
26 #include <isc/result.h>
27 #include <isc/string.h>
28 #include <isc/util.h>
29
30 #include <isccfg/cfg.h>
31 #include <isccfg/grammar.h>
32 #include <isccfg/log.h>
33
34 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
35
36 /*% Check a return value. */
37 #define CHECK(op)                                               \
38         do { result = (op);                                     \
39                 if (result != ISC_R_SUCCESS) goto cleanup;      \
40         } while (0)
41
42 /*% Clean up a configuration object if non-NULL. */
43 #define CLEANUP_OBJ(obj) \
44         do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
45
46
47 /*%
48  * Forward declarations of static functions.
49  */
50
51 static isc_result_t
52 parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype,
53                     const cfg_type_t *othertype, cfg_obj_t **ret);
54
55 static isc_result_t
56 parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
57
58 static isc_result_t
59 parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
60                         cfg_obj_t **ret);
61
62 static isc_result_t
63 parse_updatepolicy(cfg_parser_t *pctx, const cfg_type_t *type,
64                    cfg_obj_t **ret);
65 static void
66 print_updatepolicy(cfg_printer_t *pctx, const cfg_obj_t *obj);
67
68 static void
69 doc_updatepolicy(cfg_printer_t *pctx, const cfg_type_t *type);
70
71 static void
72 print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj);
73
74 static void
75 doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type);
76
77 static void
78 doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type);
79
80 static cfg_type_t cfg_type_acl;
81 static cfg_type_t cfg_type_addrmatchelt;
82 static cfg_type_t cfg_type_bracketed_aml;
83 static cfg_type_t cfg_type_bracketed_namesockaddrkeylist;
84 static cfg_type_t cfg_type_bracketed_sockaddrlist;
85 static cfg_type_t cfg_type_bracketed_sockaddrnameportlist;
86 static cfg_type_t cfg_type_controls;
87 static cfg_type_t cfg_type_controls_sockaddr;
88 static cfg_type_t cfg_type_destinationlist;
89 static cfg_type_t cfg_type_dialuptype;
90 static cfg_type_t cfg_type_ixfrdifftype;
91 static cfg_type_t cfg_type_key;
92 static cfg_type_t cfg_type_logfile;
93 static cfg_type_t cfg_type_logging;
94 static cfg_type_t cfg_type_logseverity;
95 static cfg_type_t cfg_type_lwres;
96 static cfg_type_t cfg_type_masterselement;
97 static cfg_type_t cfg_type_nameportiplist;
98 static cfg_type_t cfg_type_negated;
99 static cfg_type_t cfg_type_notifytype;
100 static cfg_type_t cfg_type_optional_allow;
101 static cfg_type_t cfg_type_optional_class;
102 static cfg_type_t cfg_type_optional_facility;
103 static cfg_type_t cfg_type_optional_keyref;
104 static cfg_type_t cfg_type_optional_port;
105 static cfg_type_t cfg_type_options;
106 static cfg_type_t cfg_type_portiplist;
107 static cfg_type_t cfg_type_querysource4;
108 static cfg_type_t cfg_type_querysource6;
109 static cfg_type_t cfg_type_querysource;
110 static cfg_type_t cfg_type_server;
111 static cfg_type_t cfg_type_server_key_kludge;
112 static cfg_type_t cfg_type_size;
113 static cfg_type_t cfg_type_sizenodefault;
114 static cfg_type_t cfg_type_sockaddr4wild;
115 static cfg_type_t cfg_type_sockaddr6wild;
116 static cfg_type_t cfg_type_statschannels;
117 static cfg_type_t cfg_type_view;
118 static cfg_type_t cfg_type_viewopts;
119 static cfg_type_t cfg_type_zone;
120 static cfg_type_t cfg_type_zoneopts;
121 static cfg_type_t cfg_type_dynamically_loadable_zones;
122 static cfg_type_t cfg_type_dynamically_loadable_zones_opts;
123 static cfg_type_t cfg_type_v4_aaaa;
124
125 /*
126  * Clauses that can be found in a 'dynamically loadable zones' statement
127  */
128 static cfg_clausedef_t
129 dynamically_loadable_zones_clauses[] = {
130         { "database", &cfg_type_astring, 0 },
131         { NULL, NULL, 0 }
132 };
133
134 /*
135  * A dynamically loadable zones statement.
136  */
137 static cfg_tuplefielddef_t dynamically_loadable_zones_fields[] = {
138         { "name", &cfg_type_astring, 0 },
139         { "options", &cfg_type_dynamically_loadable_zones_opts, 0 },
140         { NULL, NULL, 0 }
141 };
142
143 static cfg_type_t cfg_type_dynamically_loadable_zones = {
144         "dlz", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
145         &cfg_rep_tuple,
146         dynamically_loadable_zones_fields
147         };
148
149
150 /*% tkey-dhkey */
151
152 static cfg_tuplefielddef_t tkey_dhkey_fields[] = {
153         { "name", &cfg_type_qstring, 0 },
154         { "keyid", &cfg_type_uint32, 0 },
155         { NULL, NULL, 0 }
156 };
157
158 static cfg_type_t cfg_type_tkey_dhkey = {
159         "tkey-dhkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
160         tkey_dhkey_fields
161 };
162
163 /*% listen-on */
164
165 static cfg_tuplefielddef_t listenon_fields[] = {
166         { "port", &cfg_type_optional_port, 0 },
167         { "acl", &cfg_type_bracketed_aml, 0 },
168         { NULL, NULL, 0 }
169 };
170 static cfg_type_t cfg_type_listenon = {
171         "listenon", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, listenon_fields };
172
173 /*% acl */
174
175 static cfg_tuplefielddef_t acl_fields[] = {
176         { "name", &cfg_type_astring, 0 },
177         { "value", &cfg_type_bracketed_aml, 0 },
178         { NULL, NULL, 0 }
179 };
180
181 static cfg_type_t cfg_type_acl = {
182         "acl", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, acl_fields };
183
184 /*% masters */
185 static cfg_tuplefielddef_t masters_fields[] = {
186         { "name", &cfg_type_astring, 0 },
187         { "port", &cfg_type_optional_port, 0 },
188         { "addresses", &cfg_type_bracketed_namesockaddrkeylist, 0 },
189         { NULL, NULL, 0 }
190 };
191
192 static cfg_type_t cfg_type_masters = {
193         "masters", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, masters_fields };
194
195 /*%
196  * "sockaddrkeylist", a list of socket addresses with optional keys
197  * and an optional default port, as used in the masters option.
198  * E.g.,
199  *   "port 1234 { mymasters; 10.0.0.1 key foo; 1::2 port 69; }"
200  */
201
202 static cfg_tuplefielddef_t namesockaddrkey_fields[] = {
203         { "masterselement", &cfg_type_masterselement, 0 },
204         { "key", &cfg_type_optional_keyref, 0 },
205         { NULL, NULL, 0 },
206 };
207
208 static cfg_type_t cfg_type_namesockaddrkey = {
209         "namesockaddrkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
210         namesockaddrkey_fields
211 };
212
213 static cfg_type_t cfg_type_bracketed_namesockaddrkeylist = {
214         "bracketed_namesockaddrkeylist", cfg_parse_bracketed_list,
215         cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_namesockaddrkey
216 };
217
218 static cfg_tuplefielddef_t namesockaddrkeylist_fields[] = {
219         { "port", &cfg_type_optional_port, 0 },
220         { "addresses", &cfg_type_bracketed_namesockaddrkeylist, 0 },
221         { NULL, NULL, 0 }
222 };
223 static cfg_type_t cfg_type_namesockaddrkeylist = {
224         "sockaddrkeylist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
225         namesockaddrkeylist_fields
226 };
227
228 /*%
229  * A list of socket addresses with an optional default port,
230  * as used in the also-notify option.  E.g.,
231  * "port 1234 { 10.0.0.1; 1::2 port 69; }"
232  */
233 static cfg_tuplefielddef_t portiplist_fields[] = {
234         { "port", &cfg_type_optional_port, 0 },
235         { "addresses", &cfg_type_bracketed_sockaddrlist, 0 },
236         { NULL, NULL, 0 }
237 };
238 static cfg_type_t cfg_type_portiplist = {
239         "portiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
240         portiplist_fields
241 };
242
243 /*%
244  * A public key, as in the "pubkey" statement.
245  */
246 static cfg_tuplefielddef_t pubkey_fields[] = {
247         { "flags", &cfg_type_uint32, 0 },
248         { "protocol", &cfg_type_uint32, 0 },
249         { "algorithm", &cfg_type_uint32, 0 },
250         { "key", &cfg_type_qstring, 0 },
251         { NULL, NULL, 0 }
252 };
253 static cfg_type_t cfg_type_pubkey = {
254         "pubkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
255         &cfg_rep_tuple, pubkey_fields };
256
257 /*%
258  * A list of RR types, used in grant statements.
259  * Note that the old parser allows quotes around the RR type names.
260  */
261 static cfg_type_t cfg_type_rrtypelist = {
262         "rrtypelist", cfg_parse_spacelist, cfg_print_spacelist,
263         cfg_doc_terminal, &cfg_rep_list, &cfg_type_astring
264 };
265
266 static const char *mode_enums[] = { "grant", "deny", NULL };
267 static cfg_type_t cfg_type_mode = {
268         "mode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
269         &cfg_rep_string, &mode_enums
270 };
271
272 static isc_result_t
273 parse_matchtype(cfg_parser_t *pctx, const cfg_type_t *type,
274                 cfg_obj_t **ret) {
275         isc_result_t result;
276
277         CHECK(cfg_peektoken(pctx, 0));
278         if (pctx->token.type == isc_tokentype_string &&
279             strcasecmp(TOKEN_STRING(pctx), "zonesub") == 0) {
280                 pctx->flags |= CFG_PCTX_SKIP;
281         }
282         return (cfg_parse_enum(pctx, type, ret));
283
284  cleanup:
285         return (result);
286 }
287
288 static isc_result_t
289 parse_matchname(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
290         isc_result_t result;
291         cfg_obj_t *obj = NULL;
292
293         if ((pctx->flags & CFG_PCTX_SKIP) != 0) {
294                 pctx->flags &= ~CFG_PCTX_SKIP;
295                 CHECK(cfg_parse_void(pctx, NULL, &obj));
296         } else
297                 result = cfg_parse_astring(pctx, type, &obj);
298
299         *ret = obj;
300  cleanup:
301         return (result);
302 }
303
304 static void
305 doc_matchname(cfg_printer_t *pctx, const cfg_type_t *type) {
306         cfg_print_chars(pctx, "[ ", 2);
307         cfg_doc_obj(pctx, type->of);
308         cfg_print_chars(pctx, " ]", 2);
309 }
310
311 static const char *matchtype_enums[] = {
312         "name", "subdomain", "wildcard", "self", "selfsub", "selfwild",
313         "krb5-self", "ms-self", "krb5-subdomain", "ms-subdomain",
314         "tcp-self", "6to4-self", "zonesub", "external", NULL };
315
316 static cfg_type_t cfg_type_matchtype = {
317         "matchtype", parse_matchtype, cfg_print_ustring,
318         cfg_doc_enum, &cfg_rep_string, &matchtype_enums
319 };
320
321 static cfg_type_t cfg_type_matchname = {
322         "optional_matchname", parse_matchname, cfg_print_ustring,
323         &doc_matchname, &cfg_rep_tuple, &cfg_type_ustring
324 };
325
326 /*%
327  * A grant statement, used in the update policy.
328  */
329 static cfg_tuplefielddef_t grant_fields[] = {
330         { "mode", &cfg_type_mode, 0 },
331         { "identity", &cfg_type_astring, 0 }, /* domain name */
332         { "matchtype", &cfg_type_matchtype, 0 },
333         { "name", &cfg_type_matchname, 0 }, /* domain name */
334         { "types", &cfg_type_rrtypelist, 0 },
335         { NULL, NULL, 0 }
336 };
337 static cfg_type_t cfg_type_grant = {
338         "grant", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
339          &cfg_rep_tuple, grant_fields
340 };
341
342 static cfg_type_t cfg_type_updatepolicy = {
343         "update_policy", parse_updatepolicy, print_updatepolicy,
344         doc_updatepolicy, &cfg_rep_list, &cfg_type_grant
345 };
346
347 static isc_result_t
348 parse_updatepolicy(cfg_parser_t *pctx, const cfg_type_t *type,
349                    cfg_obj_t **ret) {
350         isc_result_t result;
351         CHECK(cfg_gettoken(pctx, 0));
352         if (pctx->token.type == isc_tokentype_special &&
353             pctx->token.value.as_char == '{') {
354                 cfg_ungettoken(pctx);
355                 return (cfg_parse_bracketed_list(pctx, type, ret));
356         }
357
358         if (pctx->token.type == isc_tokentype_string &&
359             strcasecmp(TOKEN_STRING(pctx), "local") == 0) {
360                 cfg_obj_t *obj = NULL;
361                 CHECK(cfg_create_obj(pctx, &cfg_type_ustring, &obj));
362                 obj->value.string.length = strlen("local");
363                 obj->value.string.base  = isc_mem_get(pctx->mctx,
364                                                 obj->value.string.length + 1);
365                 if (obj->value.string.base == NULL) {
366                         isc_mem_put(pctx->mctx, obj, sizeof(*obj));
367                         return (ISC_R_NOMEMORY);
368                 }
369                 memcpy(obj->value.string.base, "local", 5);
370                 obj->value.string.base[5] = '\0';
371                 *ret = obj;
372                 return (ISC_R_SUCCESS);
373         }
374
375         cfg_ungettoken(pctx);
376         return (ISC_R_UNEXPECTEDTOKEN);
377
378  cleanup:
379         return (result);
380 }
381
382 static void
383 print_updatepolicy(cfg_printer_t *pctx, const cfg_obj_t *obj) {
384         if (cfg_obj_isstring(obj))
385                 cfg_print_ustring(pctx, obj);
386         else
387                 cfg_print_bracketed_list(pctx, obj);
388 }
389
390 static void
391 doc_updatepolicy(cfg_printer_t *pctx, const cfg_type_t *type) {
392         cfg_print_cstr(pctx, "( local | { ");
393         cfg_doc_obj(pctx, type->of);
394         cfg_print_cstr(pctx, "; ... }");
395 }
396
397 /*%
398  * A view statement.
399  */
400 static cfg_tuplefielddef_t view_fields[] = {
401         { "name", &cfg_type_astring, 0 },
402         { "class", &cfg_type_optional_class, 0 },
403         { "options", &cfg_type_viewopts, 0 },
404         { NULL, NULL, 0 }
405 };
406 static cfg_type_t cfg_type_view = {
407         "view", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
408          &cfg_rep_tuple, view_fields
409 };
410
411 /*%
412  * A zone statement.
413  */
414 static cfg_tuplefielddef_t zone_fields[] = {
415         { "name", &cfg_type_astring, 0 },
416         { "class", &cfg_type_optional_class, 0 },
417         { "options", &cfg_type_zoneopts, 0 },
418         { NULL, NULL, 0 }
419 };
420 static cfg_type_t cfg_type_zone = {
421         "zone", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
422         &cfg_rep_tuple, zone_fields
423 };
424
425 /*%
426  * A "category" clause in the "logging" statement.
427  */
428 static cfg_tuplefielddef_t category_fields[] = {
429         { "name", &cfg_type_astring, 0 },
430         { "destinations", &cfg_type_destinationlist,0 },
431         { NULL, NULL, 0 }
432 };
433 static cfg_type_t cfg_type_category = {
434         "category", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
435         &cfg_rep_tuple, category_fields
436 };
437
438
439 /*%
440  * A dnssec key, as used in the "trusted-keys" statement.
441  */
442 static cfg_tuplefielddef_t dnsseckey_fields[] = {
443         { "name", &cfg_type_astring, 0 },
444         { "flags", &cfg_type_uint32, 0 },
445         { "protocol", &cfg_type_uint32, 0 },
446         { "algorithm", &cfg_type_uint32, 0 },
447         { "key", &cfg_type_qstring, 0 },
448         { NULL, NULL, 0 }
449 };
450 static cfg_type_t cfg_type_dnsseckey = {
451         "dnsseckey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
452         &cfg_rep_tuple, dnsseckey_fields
453 };
454
455 /*%
456  * A managed key initialization specifier, as used in the
457  * "managed-keys" statement.
458  */
459 static cfg_tuplefielddef_t managedkey_fields[] = {
460         { "name", &cfg_type_astring, 0 },
461         { "init", &cfg_type_ustring, 0 },   /* must be literal "initial-key" */
462         { "flags", &cfg_type_uint32, 0 },
463         { "protocol", &cfg_type_uint32, 0 },
464         { "algorithm", &cfg_type_uint32, 0 },
465         { "key", &cfg_type_qstring, 0 },
466         { NULL, NULL, 0 }
467 };
468 static cfg_type_t cfg_type_managedkey = {
469         "managedkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
470         &cfg_rep_tuple, managedkey_fields
471 };
472
473 static keyword_type_t wild_class_kw = { "class", &cfg_type_ustring };
474
475 static cfg_type_t cfg_type_optional_wild_class = {
476         "optional_wild_class", parse_optional_keyvalue, print_keyvalue,
477         doc_optional_keyvalue, &cfg_rep_string, &wild_class_kw
478 };
479
480 static keyword_type_t wild_type_kw = { "type", &cfg_type_ustring };
481
482 static cfg_type_t cfg_type_optional_wild_type = {
483         "optional_wild_type", parse_optional_keyvalue,
484         print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &wild_type_kw
485 };
486
487 static keyword_type_t wild_name_kw = { "name", &cfg_type_qstring };
488
489 static cfg_type_t cfg_type_optional_wild_name = {
490         "optional_wild_name", parse_optional_keyvalue,
491         print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &wild_name_kw
492 };
493
494 /*%
495  * An rrset ordering element.
496  */
497 static cfg_tuplefielddef_t rrsetorderingelement_fields[] = {
498         { "class", &cfg_type_optional_wild_class, 0 },
499         { "type", &cfg_type_optional_wild_type, 0 },
500         { "name", &cfg_type_optional_wild_name, 0 },
501         { "order", &cfg_type_ustring, 0 }, /* must be literal "order" */
502         { "ordering", &cfg_type_ustring, 0 },
503         { NULL, NULL, 0 }
504 };
505 static cfg_type_t cfg_type_rrsetorderingelement = {
506         "rrsetorderingelement", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
507         rrsetorderingelement_fields
508 };
509
510 /*%
511  * A global or view "check-names" option.  Note that the zone
512  * "check-names" option has a different syntax.
513  */
514
515 static const char *checktype_enums[] = { "master", "slave", "response", NULL };
516 static cfg_type_t cfg_type_checktype = {
517         "checktype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
518         &cfg_rep_string, &checktype_enums
519 };
520
521 static const char *checkmode_enums[] = { "fail", "warn", "ignore", NULL };
522 static cfg_type_t cfg_type_checkmode = {
523         "checkmode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
524         &cfg_rep_string, &checkmode_enums
525 };
526
527 static cfg_tuplefielddef_t checknames_fields[] = {
528         { "type", &cfg_type_checktype, 0 },
529         { "mode", &cfg_type_checkmode, 0 },
530         { NULL, NULL, 0 }
531 };
532
533 static cfg_type_t cfg_type_checknames = {
534         "checknames", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
535         checknames_fields
536 };
537
538 static cfg_type_t cfg_type_bracketed_sockaddrlist = {
539         "bracketed_sockaddrlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list,
540         &cfg_rep_list, &cfg_type_sockaddr
541 };
542
543 static const char *autodnssec_enums[] = { "allow", "maintain", "off", NULL };
544 static cfg_type_t cfg_type_autodnssec = {
545         "autodnssec", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
546         &cfg_rep_string, &autodnssec_enums
547 };
548
549 static cfg_type_t cfg_type_rrsetorder = {
550         "rrsetorder", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list,
551         &cfg_rep_list, &cfg_type_rrsetorderingelement
552 };
553
554 static keyword_type_t port_kw = { "port", &cfg_type_uint32 };
555
556 static cfg_type_t cfg_type_optional_port = {
557         "optional_port", parse_optional_keyvalue, print_keyvalue,
558         doc_optional_keyvalue, &cfg_rep_uint32, &port_kw
559 };
560
561 /*% A list of keys, as in the "key" clause of the controls statement. */
562 static cfg_type_t cfg_type_keylist = {
563         "keylist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
564         cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring
565 };
566
567 /*% A list of dnssec keys, as in "trusted-keys" */
568 static cfg_type_t cfg_type_dnsseckeys = {
569         "dnsseckeys", cfg_parse_bracketed_list, cfg_print_bracketed_list,
570         cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_dnsseckey
571 };
572
573 /*%
574  * A list of managed key entries, as in "trusted-keys".  Currently
575  * (9.7.0) this has a format similar to dnssec keys, except the keyname
576  * is followed by the keyword "initial-key".  In future releases, this
577  * keyword may take other values indicating different methods for the
578  * key to be initialized.
579  */
580
581 static cfg_type_t cfg_type_managedkeys = {
582         "managedkeys", cfg_parse_bracketed_list, cfg_print_bracketed_list,
583         cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_managedkey
584 };
585
586 static const char *forwardtype_enums[] = { "first", "only", NULL };
587 static cfg_type_t cfg_type_forwardtype = {
588         "forwardtype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string,
589         &forwardtype_enums
590 };
591
592 static const char *zonetype_enums[] = {
593         "master", "slave", "stub", "static-stub", "hint", "forward",
594         "delegation-only", NULL };
595 static cfg_type_t cfg_type_zonetype = {
596         "zonetype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
597         &cfg_rep_string, &zonetype_enums
598 };
599
600 static const char *loglevel_enums[] = {
601         "critical", "error", "warning", "notice", "info", "dynamic", NULL };
602 static cfg_type_t cfg_type_loglevel = {
603         "loglevel", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string,
604         &loglevel_enums
605 };
606
607 static const char *transferformat_enums[] = {
608         "many-answers", "one-answer", NULL };
609 static cfg_type_t cfg_type_transferformat = {
610         "transferformat", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string,
611         &transferformat_enums
612 };
613
614 /*%
615  * The special keyword "none", as used in the pid-file option.
616  */
617
618 static void
619 print_none(cfg_printer_t *pctx, const cfg_obj_t *obj) {
620         UNUSED(obj);
621         cfg_print_cstr(pctx, "none");
622 }
623
624 static cfg_type_t cfg_type_none = {
625         "none", NULL, print_none, NULL, &cfg_rep_void, NULL
626 };
627
628 /*%
629  * A quoted string or the special keyword "none".  Used in the pid-file option.
630  */
631 static isc_result_t
632 parse_qstringornone(cfg_parser_t *pctx, const cfg_type_t *type,
633                     cfg_obj_t **ret)
634 {
635         isc_result_t result;
636
637         CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
638         if (pctx->token.type == isc_tokentype_string &&
639             strcasecmp(TOKEN_STRING(pctx), "none") == 0)
640                 return (cfg_create_obj(pctx, &cfg_type_none, ret));
641         cfg_ungettoken(pctx);
642         return (cfg_parse_qstring(pctx, type, ret));
643  cleanup:
644         return (result);
645 }
646
647 static void
648 doc_qstringornone(cfg_printer_t *pctx, const cfg_type_t *type) {
649         UNUSED(type);
650         cfg_print_cstr(pctx, "( <quoted_string> | none )");
651 }
652
653 static cfg_type_t cfg_type_qstringornone = {
654         "qstringornone", parse_qstringornone, NULL, doc_qstringornone,
655         NULL, NULL
656 };
657
658 /*%
659  * A boolean ("yes" or "no"), or the special keyword "auto".
660  * Used in the dnssec-validation option.
661  */
662 static void
663 print_auto(cfg_printer_t *pctx, const cfg_obj_t *obj) {
664         UNUSED(obj);
665         cfg_print_cstr(pctx, "auto");
666 }
667
668 static cfg_type_t cfg_type_auto = {
669         "auto", NULL, print_auto, NULL, &cfg_rep_void, NULL
670 };
671
672 static isc_result_t
673 parse_boolorauto(cfg_parser_t *pctx, const cfg_type_t *type,
674                     cfg_obj_t **ret)
675 {
676         isc_result_t result;
677
678         CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
679         if (pctx->token.type == isc_tokentype_string &&
680             strcasecmp(TOKEN_STRING(pctx), "auto") == 0)
681                 return (cfg_create_obj(pctx, &cfg_type_auto, ret));
682         cfg_ungettoken(pctx);
683         return (cfg_parse_boolean(pctx, type, ret));
684  cleanup:
685         return (result);
686 }
687
688 static void
689 print_boolorauto(cfg_printer_t *pctx, const cfg_obj_t *obj) {
690         if (obj->type->rep == &cfg_rep_void)
691                 cfg_print_chars(pctx, "auto", 4);
692         else if (obj->value.boolean)
693                 cfg_print_chars(pctx, "yes", 3);
694         else
695                 cfg_print_chars(pctx, "no", 2);
696 }
697
698 static void
699 doc_boolorauto(cfg_printer_t *pctx, const cfg_type_t *type) {
700         UNUSED(type);
701         cfg_print_cstr(pctx, "( yes | no | auto )");
702 }
703
704 static cfg_type_t cfg_type_boolorauto = {
705         "boolorauto", parse_boolorauto, print_boolorauto,
706         doc_boolorauto, NULL, NULL
707 };
708
709 /*%
710  * keyword hostname
711  */
712 static void
713 print_hostname(cfg_printer_t *pctx, const cfg_obj_t *obj) {
714         UNUSED(obj);
715         cfg_print_cstr(pctx, "hostname");
716 }
717
718 static cfg_type_t cfg_type_hostname = {
719         "hostname", NULL, print_hostname, NULL, &cfg_rep_boolean, NULL
720 };
721
722 /*%
723  * "server-id" argument.
724  */
725
726 static isc_result_t
727 parse_serverid(cfg_parser_t *pctx, const cfg_type_t *type,
728                     cfg_obj_t **ret)
729 {
730         isc_result_t result;
731         CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
732         if (pctx->token.type == isc_tokentype_string &&
733             strcasecmp(TOKEN_STRING(pctx), "none") == 0)
734                 return (cfg_create_obj(pctx, &cfg_type_none, ret));
735         if (pctx->token.type == isc_tokentype_string &&
736             strcasecmp(TOKEN_STRING(pctx), "hostname") == 0) {
737                 return (cfg_create_obj(pctx, &cfg_type_hostname, ret));
738         }
739         cfg_ungettoken(pctx);
740         return (cfg_parse_qstring(pctx, type, ret));
741  cleanup:
742         return (result);
743 }
744
745 static void
746 doc_serverid(cfg_printer_t *pctx, const cfg_type_t *type) {
747         UNUSED(type);
748         cfg_print_cstr(pctx, "( <quoted_string> | none | hostname )");
749 }
750
751 static cfg_type_t cfg_type_serverid = {
752         "serverid", parse_serverid, NULL, doc_serverid, NULL, NULL };
753
754 /*%
755  * Port list.
756  */
757 static cfg_tuplefielddef_t porttuple_fields[] = {
758         { "loport", &cfg_type_uint32, 0 },
759         { "hiport", &cfg_type_uint32, 0 },
760         { NULL, NULL, 0 }
761 };
762 static cfg_type_t cfg_type_porttuple = {
763         "porttuple", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
764         &cfg_rep_tuple, porttuple_fields
765 };
766
767 static isc_result_t
768 parse_port(cfg_parser_t *pctx, cfg_obj_t **ret) {
769         isc_result_t result;
770
771         CHECK(cfg_parse_uint32(pctx, NULL, ret));
772         if ((*ret)->value.uint32 > 0xffff) {
773                 cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid port");
774                 cfg_obj_destroy(pctx, ret);
775                 result = ISC_R_RANGE;
776         }
777
778  cleanup:
779         return (result);
780 }
781
782 static isc_result_t
783 parse_portrange(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
784         isc_result_t result;
785         cfg_obj_t *obj = NULL;
786
787         UNUSED(type);
788
789         CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
790         if (pctx->token.type == isc_tokentype_number)
791                 CHECK(parse_port(pctx, ret));
792         else {
793                 CHECK(cfg_gettoken(pctx, 0));
794                 if (pctx->token.type != isc_tokentype_string ||
795                     strcasecmp(TOKEN_STRING(pctx), "range") != 0) {
796                         cfg_parser_error(pctx, CFG_LOG_NEAR,
797                                          "expected integer or 'range'");
798                         return (ISC_R_UNEXPECTEDTOKEN);
799                 }
800                 CHECK(cfg_create_tuple(pctx, &cfg_type_porttuple, &obj));
801                 CHECK(parse_port(pctx, &obj->value.tuple[0]));
802                 CHECK(parse_port(pctx, &obj->value.tuple[1]));
803                 if (obj->value.tuple[0]->value.uint32 >
804                     obj->value.tuple[1]->value.uint32) {
805                         cfg_parser_error(pctx, CFG_LOG_NOPREP,
806                                          "low port '%u' must not be larger "
807                                          "than high port",
808                                          obj->value.tuple[0]->value.uint32);
809                         result = ISC_R_RANGE;
810                         goto cleanup;
811                 }
812                 *ret = obj;
813                 obj = NULL;
814         }
815
816  cleanup:
817         if (obj != NULL)
818                 cfg_obj_destroy(pctx, &obj);
819         return (result);
820 }
821
822 static cfg_type_t cfg_type_portrange = {
823         "portrange", parse_portrange, NULL, cfg_doc_terminal,
824         NULL, NULL
825 };
826
827 static cfg_type_t cfg_type_bracketed_portlist = {
828         "bracketed_sockaddrlist", cfg_parse_bracketed_list,
829         cfg_print_bracketed_list, cfg_doc_bracketed_list,
830         &cfg_rep_list, &cfg_type_portrange
831 };
832
833 /*%
834  * Clauses that can be found within the top level of the named.conf
835  * file only.
836  */
837 static cfg_clausedef_t
838 namedconf_clauses[] = {
839         { "options", &cfg_type_options, 0 },
840         { "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI },
841         { "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI },
842         { "masters", &cfg_type_masters, CFG_CLAUSEFLAG_MULTI },
843         { "logging", &cfg_type_logging, 0 },
844         { "view", &cfg_type_view, CFG_CLAUSEFLAG_MULTI },
845         { "lwres", &cfg_type_lwres, CFG_CLAUSEFLAG_MULTI },
846         { "statistics-channels", &cfg_type_statschannels,
847           CFG_CLAUSEFLAG_MULTI },
848         { NULL, NULL, 0 }
849 };
850
851 /*%
852  * Clauses that can occur at the top level or in the view
853  * statement, but not in the options block.
854  */
855 static cfg_clausedef_t
856 namedconf_or_view_clauses[] = {
857         { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
858         { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI },
859         /* only 1 DLZ per view allowed */
860         { "dlz", &cfg_type_dynamically_loadable_zones, 0 },
861         { "server", &cfg_type_server, CFG_CLAUSEFLAG_MULTI },
862         { "trusted-keys", &cfg_type_dnsseckeys, CFG_CLAUSEFLAG_MULTI },
863         { "managed-keys", &cfg_type_managedkeys, CFG_CLAUSEFLAG_MULTI },
864         { NULL, NULL, 0 }
865 };
866
867 /*%
868  * Clauses that can occur in the bind.keys file.
869  */
870 static cfg_clausedef_t
871 bindkeys_clauses[] = {
872         { "trusted-keys", &cfg_type_dnsseckeys, CFG_CLAUSEFLAG_MULTI },
873         { "managed-keys", &cfg_type_managedkeys, CFG_CLAUSEFLAG_MULTI },
874         { NULL, NULL, 0 }
875 };
876
877 /*%
878  * Clauses that can be found within the 'options' statement.
879  */
880 static cfg_clausedef_t
881 options_clauses[] = {
882         { "avoid-v4-udp-ports", &cfg_type_bracketed_portlist, 0 },
883         { "avoid-v6-udp-ports", &cfg_type_bracketed_portlist, 0 },
884         { "bindkeys-file", &cfg_type_qstring, 0 },
885         { "blackhole", &cfg_type_bracketed_aml, 0 },
886         { "coresize", &cfg_type_size, 0 },
887         { "datasize", &cfg_type_size, 0 },
888         { "session-keyfile", &cfg_type_qstringornone, 0 },
889         { "session-keyname", &cfg_type_astring, 0 },
890         { "session-keyalg", &cfg_type_astring, 0 },
891         { "deallocate-on-exit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
892         { "directory", &cfg_type_qstring, CFG_CLAUSEFLAG_CALLBACK },
893         { "dump-file", &cfg_type_qstring, 0 },
894         { "fake-iquery", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
895         { "files", &cfg_type_size, 0 },
896         { "flush-zones-on-shutdown", &cfg_type_boolean, 0 },
897         { "has-old-clients", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
898         { "heartbeat-interval", &cfg_type_uint32, 0 },
899         { "host-statistics", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTIMP },
900         { "host-statistics-max", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
901         { "hostname", &cfg_type_qstringornone, 0 },
902         { "interface-interval", &cfg_type_uint32, 0 },
903         { "listen-on", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
904         { "listen-on-v6", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
905         { "managed-keys-directory", &cfg_type_qstring, 0 },
906         { "match-mapped-addresses", &cfg_type_boolean, 0 },
907         { "memstatistics-file", &cfg_type_qstring, 0 },
908         { "memstatistics", &cfg_type_boolean, 0 },
909         { "multiple-cnames", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
910         { "named-xfer", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
911         { "pid-file", &cfg_type_qstringornone, 0 },
912         { "port", &cfg_type_uint32, 0 },
913         { "querylog", &cfg_type_boolean, 0 },
914         { "recursing-file", &cfg_type_qstring, 0 },
915         { "random-device", &cfg_type_qstring, 0 },
916         { "recursive-clients", &cfg_type_uint32, 0 },
917         { "reserved-sockets", &cfg_type_uint32, 0 },
918         { "secroots-file", &cfg_type_qstring, 0 },
919         { "serial-queries", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
920         { "serial-query-rate", &cfg_type_uint32, 0 },
921         { "server-id", &cfg_type_serverid, 0 },
922         { "stacksize", &cfg_type_size, 0 },
923         { "statistics-file", &cfg_type_qstring, 0 },
924         { "statistics-interval", &cfg_type_uint32, CFG_CLAUSEFLAG_NYI },
925         { "tcp-clients", &cfg_type_uint32, 0 },
926         { "tcp-listen-queue", &cfg_type_uint32, 0 },
927         { "tkey-dhkey", &cfg_type_tkey_dhkey, 0 },
928         { "tkey-gssapi-credential", &cfg_type_qstring, 0 },
929         { "tkey-gssapi-keytab", &cfg_type_qstring, 0 },
930         { "tkey-domain", &cfg_type_qstring, 0 },
931         { "transfers-per-ns", &cfg_type_uint32, 0 },
932         { "transfers-in", &cfg_type_uint32, 0 },
933         { "transfers-out", &cfg_type_uint32, 0 },
934         { "treat-cr-as-space", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
935         { "use-id-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
936         { "use-ixfr", &cfg_type_boolean, 0 },
937         { "use-v4-udp-ports", &cfg_type_bracketed_portlist, 0 },
938         { "use-v6-udp-ports", &cfg_type_bracketed_portlist, 0 },
939         { "version", &cfg_type_qstringornone, 0 },
940         { NULL, NULL, 0 }
941 };
942
943 static cfg_type_t cfg_type_namelist = {
944         "namelist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
945         cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_qstring };
946
947 static keyword_type_t exclude_kw = { "exclude", &cfg_type_namelist };
948
949 static cfg_type_t cfg_type_optional_exclude = {
950         "optional_exclude", parse_optional_keyvalue, print_keyvalue,
951         doc_optional_keyvalue, &cfg_rep_list, &exclude_kw };
952
953 static keyword_type_t exceptionnames_kw = { "except-from", &cfg_type_namelist };
954
955 static cfg_type_t cfg_type_optional_exceptionnames = {
956         "optional_allow", parse_optional_keyvalue, print_keyvalue,
957         doc_optional_keyvalue, &cfg_rep_list, &exceptionnames_kw };
958
959 static cfg_tuplefielddef_t denyaddresses_fields[] = {
960         { "acl", &cfg_type_bracketed_aml, 0 },
961         { "except-from", &cfg_type_optional_exceptionnames, 0 },
962         { NULL, NULL, 0 }
963 };
964
965 static cfg_type_t cfg_type_denyaddresses = {
966         "denyaddresses", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
967         &cfg_rep_tuple, denyaddresses_fields
968 };
969
970 static cfg_tuplefielddef_t denyaliases_fields[] = {
971         { "name", &cfg_type_namelist, 0 },
972         { "except-from", &cfg_type_optional_exceptionnames, 0 },
973         { NULL, NULL, 0 }
974 };
975
976 static cfg_type_t cfg_type_denyaliases = {
977         "denyaliases", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
978         &cfg_rep_tuple, denyaliases_fields
979 };
980
981 static cfg_type_t cfg_type_algorithmlist = {
982         "algorithmlist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
983         cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring };
984
985 static cfg_tuplefielddef_t disablealgorithm_fields[] = {
986         { "name", &cfg_type_astring, 0 },
987         { "algorithms", &cfg_type_algorithmlist, 0 },
988         { NULL, NULL, 0 }
989 };
990
991 static cfg_type_t cfg_type_disablealgorithm = {
992         "disablealgorithm", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
993         &cfg_rep_tuple, disablealgorithm_fields
994 };
995
996 static cfg_tuplefielddef_t mustbesecure_fields[] = {
997         { "name", &cfg_type_astring, 0 },
998         { "value", &cfg_type_boolean, 0 },
999         { NULL, NULL, 0 }
1000 };
1001
1002 static cfg_type_t cfg_type_mustbesecure = {
1003         "mustbesecure", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
1004         &cfg_rep_tuple, mustbesecure_fields
1005 };
1006
1007 static const char *masterformat_enums[] = { "text", "raw", NULL };
1008 static cfg_type_t cfg_type_masterformat = {
1009         "masterformat", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
1010         &cfg_rep_string, &masterformat_enums
1011 };
1012
1013
1014
1015 /*
1016  *  response-policy {
1017  *      zone <string> [ policy (given|disabled|passthru|
1018  *                                      nxdomain|nodata|cname <domain> ) ];
1019  *  };
1020  *
1021  * this is a chimera of doc_optional_keyvalue() and cfg_doc_enum()
1022  */
1023 static void
1024 doc_rpz_policies(cfg_printer_t *pctx, const cfg_type_t *type) {
1025         const keyword_type_t *kw;
1026         const char * const *p;
1027
1028         kw = type->of;
1029         cfg_print_chars(pctx, "[ ", 2);
1030         cfg_print_cstr(pctx, kw->name);
1031         cfg_print_chars(pctx, " ", 1);
1032
1033         cfg_print_chars(pctx, "( ", 2);
1034         for (p = kw->type->of; *p != NULL; p++) {
1035                 cfg_print_cstr(pctx, *p);
1036                 if (p[1] != NULL)
1037                         cfg_print_chars(pctx, " | ", 3);
1038         }
1039 }
1040
1041 /*
1042  * print_qstring() from parser.c
1043  */
1044 static void
1045 print_rpz_cname(cfg_printer_t *pctx, const cfg_obj_t *obj)
1046 {
1047         cfg_print_chars(pctx, "\"", 1);
1048         cfg_print_ustring(pctx, obj);
1049         cfg_print_chars(pctx, "\"", 1);
1050 }
1051
1052 static void
1053 doc_rpz_cname(cfg_printer_t *pctx, const cfg_type_t *type) {
1054         cfg_doc_terminal(pctx, type);
1055         cfg_print_chars(pctx, " ) ]", 4);
1056 }
1057
1058 static isc_result_t
1059 parse_rpz(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1060         isc_result_t result;
1061         cfg_obj_t *obj = NULL;
1062         const cfg_tuplefielddef_t *fields = type->of;
1063
1064         CHECK(cfg_create_tuple(pctx, type, &obj));
1065         CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
1066         CHECK(cfg_parse_obj(pctx, fields[1].type, &obj->value.tuple[1]));
1067         /*
1068          * parse cname domain only after "policy cname"
1069          */
1070         if (cfg_obj_isvoid(obj->value.tuple[1]) ||
1071             strcasecmp("cname", cfg_obj_asstring(obj->value.tuple[1]))) {
1072                 CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2]));
1073         } else {
1074                 CHECK(cfg_parse_obj(pctx, fields[2].type, &obj->value.tuple[2]));
1075         }
1076
1077         *ret = obj;
1078         return (ISC_R_SUCCESS);
1079
1080 cleanup:
1081         CLEANUP_OBJ(obj);
1082         return (result);
1083 }
1084
1085 static const char *rpz_policies[] = {
1086         "given", "disabled", "passthru", "no-op", "nxdomain", "nodata",
1087         "cname", NULL
1088 };
1089 static cfg_type_t cfg_type_rpz_policylist = {
1090         "policies", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
1091         &cfg_rep_string, &rpz_policies
1092 };
1093 static keyword_type_t rpz_policies_kw = {
1094         "policy", &cfg_type_rpz_policylist
1095 };
1096 static cfg_type_t cfg_type_rpz_policy = {
1097         "optional_policy", parse_optional_keyvalue, print_keyvalue,
1098         doc_rpz_policies, &cfg_rep_string, &rpz_policies_kw
1099 };
1100 static cfg_type_t cfg_type_cname = {
1101         "domain", cfg_parse_astring, print_rpz_cname, doc_rpz_cname,
1102         &cfg_rep_string, NULL
1103 };
1104 static cfg_tuplefielddef_t rpzone_fields[] = {
1105         { "name", &cfg_type_astring, 0 },
1106         { "policy", &cfg_type_rpz_policy, 0 },
1107         { "cname", &cfg_type_cname, 0 },
1108         { NULL, NULL, 0 }
1109 };
1110 static cfg_type_t cfg_type_rpzone = {
1111         "rpzone", parse_rpz, cfg_print_tuple, cfg_doc_tuple,
1112         &cfg_rep_tuple, rpzone_fields
1113 };
1114 static cfg_clausedef_t rpz_clauses[] = {
1115         { "zone", &cfg_type_rpzone, CFG_CLAUSEFLAG_MULTI },
1116         { NULL, NULL, 0 }
1117 };
1118 static cfg_clausedef_t *rpz_clausesets[] = {
1119         rpz_clauses,
1120         NULL
1121 };
1122 static cfg_type_t cfg_type_rpz = {
1123         "rpz", cfg_parse_map, cfg_print_map, cfg_doc_map,
1124         &cfg_rep_map, rpz_clausesets
1125 };
1126
1127
1128
1129 /*%
1130  * dnssec-lookaside
1131  */
1132
1133 static void
1134 print_lookaside(cfg_printer_t *pctx, const cfg_obj_t *obj)
1135 {
1136         const cfg_obj_t *domain = obj->value.tuple[0];
1137
1138         if (domain->value.string.length == 4 &&
1139             strncmp(domain->value.string.base, "auto", 4) == 0)
1140                 cfg_print_cstr(pctx, "auto");
1141         else
1142                 cfg_print_tuple(pctx, obj);
1143 }
1144
1145 static void
1146 doc_lookaside(cfg_printer_t *pctx, const cfg_type_t *type) {
1147         UNUSED(type);
1148         cfg_print_cstr(pctx, "( <string> trust-anchor <string> | auto | no )");
1149 }
1150
1151 static keyword_type_t trustanchor_kw = { "trust-anchor", &cfg_type_astring };
1152
1153 static cfg_type_t cfg_type_optional_trustanchor = {
1154         "optional_trustanchor", parse_optional_keyvalue, print_keyvalue,
1155         doc_keyvalue, &cfg_rep_string, &trustanchor_kw
1156 };
1157
1158 static cfg_tuplefielddef_t lookaside_fields[] = {
1159         { "domain", &cfg_type_astring, 0 },
1160         { "trust-anchor", &cfg_type_optional_trustanchor, 0 },
1161         { NULL, NULL, 0 }
1162 };
1163
1164 static cfg_type_t cfg_type_lookaside = {
1165         "lookaside", cfg_parse_tuple, print_lookaside, doc_lookaside,
1166         &cfg_rep_tuple, lookaside_fields
1167 };
1168
1169 /*
1170  * DNS64.
1171  */
1172 static cfg_clausedef_t
1173 dns64_clauses[] = {
1174         { "clients", &cfg_type_bracketed_aml, 0 },
1175         { "mapped", &cfg_type_bracketed_aml, 0 },
1176         { "exclude", &cfg_type_bracketed_aml, 0 },
1177         { "suffix", &cfg_type_netaddr6, 0 },
1178         { "recursive-only", &cfg_type_boolean, 0 },
1179         { "break-dnssec", &cfg_type_boolean, 0 },
1180         { NULL, NULL, 0 },
1181 };
1182
1183 static cfg_clausedef_t *
1184 dns64_clausesets[] = {
1185         dns64_clauses,
1186         NULL
1187 };
1188
1189 static cfg_type_t cfg_type_dns64 = {
1190         "dns64", cfg_parse_netprefix_map, cfg_print_map, cfg_doc_map,
1191         &cfg_rep_map, dns64_clausesets
1192 };
1193
1194 /*%
1195  * Clauses that can be found within the 'view' statement,
1196  * with defaults in the 'options' statement.
1197  */
1198
1199 static cfg_clausedef_t
1200 view_clauses[] = {
1201         { "acache-cleaning-interval", &cfg_type_uint32, 0 },
1202         { "acache-enable", &cfg_type_boolean, 0 },
1203         { "additional-from-auth", &cfg_type_boolean, 0 },
1204         { "additional-from-cache", &cfg_type_boolean, 0 },
1205         { "allow-new-zones", &cfg_type_boolean, 0 },
1206         { "allow-query-cache", &cfg_type_bracketed_aml, 0 },
1207         { "allow-query-cache-on", &cfg_type_bracketed_aml, 0 },
1208         { "allow-recursion", &cfg_type_bracketed_aml, 0 },
1209         { "allow-recursion-on", &cfg_type_bracketed_aml, 0 },
1210         { "allow-v6-synthesis", &cfg_type_bracketed_aml,
1211           CFG_CLAUSEFLAG_OBSOLETE },
1212         { "attach-cache", &cfg_type_astring, 0 },
1213         { "auth-nxdomain", &cfg_type_boolean, CFG_CLAUSEFLAG_NEWDEFAULT },
1214         { "cache-file", &cfg_type_qstring, 0 },
1215         { "check-names", &cfg_type_checknames, CFG_CLAUSEFLAG_MULTI },
1216         { "cleaning-interval", &cfg_type_uint32, 0 },
1217         { "clients-per-query", &cfg_type_uint32, 0 },
1218         { "deny-answer-addresses", &cfg_type_denyaddresses, 0 },
1219         { "deny-answer-aliases", &cfg_type_denyaliases, 0 },
1220         { "disable-algorithms", &cfg_type_disablealgorithm,
1221           CFG_CLAUSEFLAG_MULTI },
1222         { "disable-empty-zone", &cfg_type_astring, CFG_CLAUSEFLAG_MULTI },
1223         { "dns64", &cfg_type_dns64, CFG_CLAUSEFLAG_MULTI },
1224         { "dns64-server", &cfg_type_astring, 0 },
1225         { "dns64-contact", &cfg_type_astring, 0 },
1226         { "dnssec-accept-expired", &cfg_type_boolean, 0 },
1227         { "dnssec-enable", &cfg_type_boolean, 0 },
1228         { "dnssec-lookaside", &cfg_type_lookaside, CFG_CLAUSEFLAG_MULTI },
1229         { "dnssec-must-be-secure",  &cfg_type_mustbesecure,
1230           CFG_CLAUSEFLAG_MULTI },
1231         { "dnssec-validation", &cfg_type_boolorauto, 0 },
1232         { "dual-stack-servers", &cfg_type_nameportiplist, 0 },
1233         { "edns-udp-size", &cfg_type_uint32, 0 },
1234         { "empty-contact", &cfg_type_astring, 0 },
1235         { "empty-server", &cfg_type_astring, 0 },
1236         { "empty-zones-enable", &cfg_type_boolean, 0 },
1237         { "fetch-glue", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1238         { "ixfr-from-differences", &cfg_type_ixfrdifftype, 0 },
1239         { "lame-ttl", &cfg_type_uint32, 0 },
1240         { "max-acache-size", &cfg_type_sizenodefault, 0 },
1241         { "max-cache-size", &cfg_type_sizenodefault, 0 },
1242         { "max-cache-ttl", &cfg_type_uint32, 0 },
1243         { "max-clients-per-query", &cfg_type_uint32, 0 },
1244         { "max-ncache-ttl", &cfg_type_uint32, 0 },
1245         { "max-recursion-depth", &cfg_type_uint32, 0 },
1246         { "max-recursion-queries", &cfg_type_uint32, 0 },
1247         { "max-udp-size", &cfg_type_uint32, 0 },
1248         { "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
1249         { "minimal-responses", &cfg_type_boolean, 0 },
1250         { "preferred-glue", &cfg_type_astring, 0 },
1251         { "provide-ixfr", &cfg_type_boolean, 0 },
1252         /*
1253          * Note that the query-source option syntax is different
1254          * from the other -source options.
1255          */
1256         { "query-source", &cfg_type_querysource4, 0 },
1257         { "query-source-v6", &cfg_type_querysource6, 0 },
1258         { "queryport-pool-ports", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
1259         { "queryport-pool-updateinterval", &cfg_type_uint32,
1260           CFG_CLAUSEFLAG_OBSOLETE },
1261         { "recursion", &cfg_type_boolean, 0 },
1262         { "request-ixfr", &cfg_type_boolean, 0 },
1263         { "request-nsid", &cfg_type_boolean, 0 },
1264         { "resolver-query-timeout", &cfg_type_uint32, 0 },
1265         { "rfc2308-type1", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
1266         { "root-delegation-only",  &cfg_type_optional_exclude, 0 },
1267         { "rrset-order", &cfg_type_rrsetorder, 0 },
1268         { "sortlist", &cfg_type_bracketed_aml, 0 },
1269         { "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
1270         { "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_NOTIMP },
1271         { "transfer-format", &cfg_type_transferformat, 0 },
1272         { "use-queryport-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1273         { "zero-no-soa-ttl-cache", &cfg_type_boolean, 0 },
1274 #ifdef ALLOW_FILTER_AAAA_ON_V4
1275         { "filter-aaaa", &cfg_type_bracketed_aml, 0 },
1276         { "filter-aaaa-on-v4", &cfg_type_v4_aaaa, 0 },
1277 #else
1278         { "filter-aaaa", &cfg_type_bracketed_aml,
1279            CFG_CLAUSEFLAG_NOTCONFIGURED },
1280         { "filter-aaaa-on-v4", &cfg_type_v4_aaaa,
1281            CFG_CLAUSEFLAG_NOTCONFIGURED },
1282 #endif
1283         { "response-policy", &cfg_type_rpz, 0 },
1284         { NULL, NULL, 0 }
1285 };
1286
1287 /*%
1288  * Clauses that can be found within the 'view' statement only.
1289  */
1290 static cfg_clausedef_t
1291 view_only_clauses[] = {
1292         { "match-clients", &cfg_type_bracketed_aml, 0 },
1293         { "match-destinations", &cfg_type_bracketed_aml, 0 },
1294         { "match-recursive-only", &cfg_type_boolean, 0 },
1295         { NULL, NULL, 0 }
1296 };
1297
1298 /*%
1299  * Sig-validity-interval.
1300  */
1301 static isc_result_t
1302 parse_optional_uint32(cfg_parser_t *pctx, const cfg_type_t *type,
1303                       cfg_obj_t **ret)
1304 {
1305         isc_result_t result;
1306         UNUSED(type);
1307
1308         CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
1309         if (pctx->token.type == isc_tokentype_number) {
1310                 CHECK(cfg_parse_obj(pctx, &cfg_type_uint32, ret));
1311         } else {
1312                 CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
1313         }
1314  cleanup:
1315         return (result);
1316 }
1317
1318 static void
1319 doc_optional_uint32(cfg_printer_t *pctx, const cfg_type_t *type) {
1320         UNUSED(type);
1321         cfg_print_cstr(pctx, "[ <integer> ]");
1322 }
1323
1324 static cfg_type_t cfg_type_optional_uint32 = {
1325         "optional_uint32", parse_optional_uint32, NULL, doc_optional_uint32,
1326         NULL, NULL };
1327
1328 static cfg_tuplefielddef_t validityinterval_fields[] = {
1329         { "validity", &cfg_type_uint32, 0 },
1330         { "re-sign", &cfg_type_optional_uint32, 0 },
1331         { NULL, NULL, 0 }
1332 };
1333
1334 static cfg_type_t cfg_type_validityinterval = {
1335         "validityinterval", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
1336         &cfg_rep_tuple, validityinterval_fields
1337 };
1338
1339 /*%
1340  * Clauses that can be found in a 'zone' statement,
1341  * with defaults in the 'view' or 'options' statement.
1342  */
1343 static cfg_clausedef_t
1344 zone_clauses[] = {
1345         { "allow-notify", &cfg_type_bracketed_aml, 0 },
1346         { "allow-query", &cfg_type_bracketed_aml, 0 },
1347         { "allow-query-on", &cfg_type_bracketed_aml, 0 },
1348         { "allow-transfer", &cfg_type_bracketed_aml, 0 },
1349         { "allow-update", &cfg_type_bracketed_aml, 0 },
1350         { "allow-update-forwarding", &cfg_type_bracketed_aml, 0 },
1351         { "also-notify", &cfg_type_portiplist, 0 },
1352         { "alt-transfer-source", &cfg_type_sockaddr4wild, 0 },
1353         { "alt-transfer-source-v6", &cfg_type_sockaddr6wild, 0 },
1354         { "auto-dnssec", &cfg_type_autodnssec, 0 },
1355         { "check-dup-records", &cfg_type_checkmode, 0 },
1356         { "check-integrity", &cfg_type_boolean, 0 },
1357         { "check-mx", &cfg_type_checkmode, 0 },
1358         { "check-mx-cname", &cfg_type_checkmode, 0 },
1359         { "check-sibling", &cfg_type_boolean, 0 },
1360         { "check-srv-cname", &cfg_type_checkmode, 0 },
1361         { "check-wildcard", &cfg_type_boolean, 0 },
1362         { "dialup", &cfg_type_dialuptype, 0 },
1363         { "dnssec-dnskey-kskonly", &cfg_type_boolean, 0 },
1364         { "dnssec-secure-to-insecure", &cfg_type_boolean, 0 },
1365         { "forward", &cfg_type_forwardtype, 0 },
1366         { "forwarders", &cfg_type_portiplist, 0 },
1367         { "key-directory", &cfg_type_qstring, 0 },
1368         { "maintain-ixfr-base", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1369         { "masterfile-format", &cfg_type_masterformat, 0 },
1370         { "max-ixfr-log-size", &cfg_type_size, CFG_CLAUSEFLAG_OBSOLETE },
1371         { "max-journal-size", &cfg_type_sizenodefault, 0 },
1372         { "max-refresh-time", &cfg_type_uint32, 0 },
1373         { "max-retry-time", &cfg_type_uint32, 0 },
1374         { "max-transfer-idle-in", &cfg_type_uint32, 0 },
1375         { "max-transfer-idle-out", &cfg_type_uint32, 0 },
1376         { "max-transfer-time-in", &cfg_type_uint32, 0 },
1377         { "max-transfer-time-out", &cfg_type_uint32, 0 },
1378         { "min-refresh-time", &cfg_type_uint32, 0 },
1379         { "min-retry-time", &cfg_type_uint32, 0 },
1380         { "multi-master", &cfg_type_boolean, 0 },
1381         { "notify", &cfg_type_notifytype, 0 },
1382         { "notify-delay", &cfg_type_uint32, 0 },
1383         { "notify-source", &cfg_type_sockaddr4wild, 0 },
1384         { "notify-source-v6", &cfg_type_sockaddr6wild, 0 },
1385         { "notify-to-soa", &cfg_type_boolean, 0 },
1386         { "nsec3-test-zone", &cfg_type_boolean, CFG_CLAUSEFLAG_TESTONLY },
1387         { "sig-signing-nodes", &cfg_type_uint32, 0 },
1388         { "sig-signing-signatures", &cfg_type_uint32, 0 },
1389         { "sig-signing-type", &cfg_type_uint32, 0 },
1390         { "sig-validity-interval", &cfg_type_validityinterval, 0 },
1391         { "transfer-source", &cfg_type_sockaddr4wild, 0 },
1392         { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 },
1393         { "try-tcp-refresh", &cfg_type_boolean, 0 },
1394         { "update-check-ksk", &cfg_type_boolean, 0 },
1395         { "use-alt-transfer-source", &cfg_type_boolean, 0 },
1396         { "zero-no-soa-ttl", &cfg_type_boolean, 0 },
1397         { "zone-statistics", &cfg_type_boolean, 0 },
1398         { NULL, NULL, 0 }
1399 };
1400
1401 /*%
1402  * Clauses that can be found in a 'zone' statement
1403  * only.
1404  */
1405 static cfg_clausedef_t
1406 zone_only_clauses[] = {
1407         { "type", &cfg_type_zonetype, 0 },
1408         { "file", &cfg_type_qstring, 0 },
1409         { "journal", &cfg_type_qstring, 0 },
1410         { "ixfr-base", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
1411         { "ixfr-tmp-file", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
1412         { "masters", &cfg_type_namesockaddrkeylist, 0 },
1413         { "pubkey", &cfg_type_pubkey,
1414           CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_OBSOLETE },
1415         { "update-policy", &cfg_type_updatepolicy, 0 },
1416         { "database", &cfg_type_astring, 0 },
1417         { "delegation-only", &cfg_type_boolean, 0 },
1418         /*
1419          * Note that the format of the check-names option is different between
1420          * the zone options and the global/view options.  Ugh.
1421          */
1422         { "check-names", &cfg_type_checkmode, 0 },
1423         { "ixfr-from-differences", &cfg_type_boolean, 0 },
1424         { "server-addresses", &cfg_type_bracketed_sockaddrlist, 0 },
1425         { "server-names", &cfg_type_namelist, 0 },
1426         { NULL, NULL, 0 }
1427 };
1428
1429
1430 /*% The top-level named.conf syntax. */
1431
1432 static cfg_clausedef_t *
1433 namedconf_clausesets[] = {
1434         namedconf_clauses,
1435         namedconf_or_view_clauses,
1436         NULL
1437 };
1438 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_namedconf = {
1439         "namedconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
1440         &cfg_rep_map, namedconf_clausesets
1441 };
1442
1443 /*% The bind.keys syntax (trusted-keys/managed-keys only). */
1444 static cfg_clausedef_t *
1445 bindkeys_clausesets[] = {
1446         bindkeys_clauses,
1447         NULL
1448 };
1449 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_bindkeys = {
1450         "bindkeys", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
1451         &cfg_rep_map, bindkeys_clausesets
1452 };
1453
1454 /*% The new-zone-file syntax (for zones added by 'rndc addzone') */
1455 static cfg_clausedef_t
1456 newzones_clauses[] = {
1457         { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI },
1458         { NULL, NULL, 0 }
1459 };
1460
1461 static cfg_clausedef_t *
1462 newzones_clausesets[] = {
1463         newzones_clauses,
1464         NULL
1465 };
1466
1467 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_newzones = {
1468         "newzones", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
1469         &cfg_rep_map, newzones_clausesets
1470 };
1471
1472 /*% The "options" statement syntax. */
1473
1474 static cfg_clausedef_t *
1475 options_clausesets[] = {
1476         options_clauses,
1477         view_clauses,
1478         zone_clauses,
1479         NULL
1480 };
1481 static cfg_type_t cfg_type_options = {
1482         "options", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, options_clausesets };
1483
1484 /*% The "view" statement syntax. */
1485
1486 static cfg_clausedef_t *
1487 view_clausesets[] = {
1488         view_only_clauses,
1489         namedconf_or_view_clauses,
1490         view_clauses,
1491         zone_clauses,
1492         dynamically_loadable_zones_clauses,
1493         NULL
1494 };
1495 static cfg_type_t cfg_type_viewopts = {
1496         "view", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, view_clausesets };
1497
1498 /*% The "zone" statement syntax. */
1499
1500 static cfg_clausedef_t *
1501 zone_clausesets[] = {
1502         zone_only_clauses,
1503         zone_clauses,
1504         NULL
1505 };
1506 static cfg_type_t cfg_type_zoneopts = {
1507         "zoneopts", cfg_parse_map, cfg_print_map,
1508         cfg_doc_map, &cfg_rep_map, zone_clausesets };
1509
1510 /*% The "dynamically loadable zones" statement syntax. */
1511
1512 static cfg_clausedef_t *
1513 dynamically_loadable_zones_clausesets[] = {
1514         dynamically_loadable_zones_clauses,
1515         NULL
1516 };
1517 static cfg_type_t cfg_type_dynamically_loadable_zones_opts = {
1518         "dynamically_loadable_zones_opts", cfg_parse_map,
1519         cfg_print_map, cfg_doc_map, &cfg_rep_map,
1520         dynamically_loadable_zones_clausesets
1521 };
1522
1523 /*%
1524  * Clauses that can be found within the 'key' statement.
1525  */
1526 static cfg_clausedef_t
1527 key_clauses[] = {
1528         { "algorithm", &cfg_type_astring, 0 },
1529         { "secret", &cfg_type_astring, 0 },
1530         { NULL, NULL, 0 }
1531 };
1532
1533 static cfg_clausedef_t *
1534 key_clausesets[] = {
1535         key_clauses,
1536         NULL
1537 };
1538 static cfg_type_t cfg_type_key = {
1539         "key", cfg_parse_named_map, cfg_print_map,
1540         cfg_doc_map, &cfg_rep_map, key_clausesets
1541 };
1542
1543
1544 /*%
1545  * Clauses that can be found in a 'server' statement.
1546  */
1547 static cfg_clausedef_t
1548 server_clauses[] = {
1549         { "bogus", &cfg_type_boolean, 0 },
1550         { "provide-ixfr", &cfg_type_boolean, 0 },
1551         { "request-ixfr", &cfg_type_boolean, 0 },
1552         { "support-ixfr", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1553         { "transfers", &cfg_type_uint32, 0 },
1554         { "transfer-format", &cfg_type_transferformat, 0 },
1555         { "keys", &cfg_type_server_key_kludge, 0 },
1556         { "edns", &cfg_type_boolean, 0 },
1557         { "edns-udp-size", &cfg_type_uint32, 0 },
1558         { "max-udp-size", &cfg_type_uint32, 0 },
1559         { "notify-source", &cfg_type_sockaddr4wild, 0 },
1560         { "notify-source-v6", &cfg_type_sockaddr6wild, 0 },
1561         { "query-source", &cfg_type_querysource4, 0 },
1562         { "query-source-v6", &cfg_type_querysource6, 0 },
1563         { "transfer-source", &cfg_type_sockaddr4wild, 0 },
1564         { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 },
1565         { NULL, NULL, 0 }
1566 };
1567 static cfg_clausedef_t *
1568 server_clausesets[] = {
1569         server_clauses,
1570         NULL
1571 };
1572 static cfg_type_t cfg_type_server = {
1573         "server", cfg_parse_netprefix_map, cfg_print_map, cfg_doc_map, &cfg_rep_map,
1574         server_clausesets
1575 };
1576
1577
1578 /*%
1579  * Clauses that can be found in a 'channel' clause in the
1580  * 'logging' statement.
1581  *
1582  * These have some additional constraints that need to be
1583  * checked after parsing:
1584  *  - There must exactly one of file/syslog/null/stderr
1585  *
1586  */
1587 static cfg_clausedef_t
1588 channel_clauses[] = {
1589         /* Destinations.  We no longer require these to be first. */
1590         { "file", &cfg_type_logfile, 0 },
1591         { "syslog", &cfg_type_optional_facility, 0 },
1592         { "null", &cfg_type_void, 0 },
1593         { "stderr", &cfg_type_void, 0 },
1594         /* Options.  We now accept these for the null channel, too. */
1595         { "severity", &cfg_type_logseverity, 0 },
1596         { "print-time", &cfg_type_boolean, 0 },
1597         { "print-severity", &cfg_type_boolean, 0 },
1598         { "print-category", &cfg_type_boolean, 0 },
1599         { NULL, NULL, 0 }
1600 };
1601 static cfg_clausedef_t *
1602 channel_clausesets[] = {
1603         channel_clauses,
1604         NULL
1605 };
1606 static cfg_type_t cfg_type_channel = {
1607         "channel", cfg_parse_named_map, cfg_print_map, cfg_doc_map,
1608         &cfg_rep_map, channel_clausesets
1609 };
1610
1611 /*% A list of log destination, used in the "category" clause. */
1612 static cfg_type_t cfg_type_destinationlist = {
1613         "destinationlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list,
1614         &cfg_rep_list, &cfg_type_astring };
1615
1616 /*%
1617  * Clauses that can be found in a 'logging' statement.
1618  */
1619 static cfg_clausedef_t
1620 logging_clauses[] = {
1621         { "channel", &cfg_type_channel, CFG_CLAUSEFLAG_MULTI },
1622         { "category", &cfg_type_category, CFG_CLAUSEFLAG_MULTI },
1623         { NULL, NULL, 0 }
1624 };
1625 static cfg_clausedef_t *
1626 logging_clausesets[] = {
1627         logging_clauses,
1628         NULL
1629 };
1630 static cfg_type_t cfg_type_logging = {
1631         "logging", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, logging_clausesets };
1632
1633
1634 /*%
1635  * For parsing an 'addzone' statement
1636  */
1637
1638 static cfg_tuplefielddef_t addzone_fields[] = {
1639         { "name", &cfg_type_astring, 0 },
1640         { "class", &cfg_type_optional_class, 0 },
1641         { "view", &cfg_type_optional_class, 0 },
1642         { "options", &cfg_type_zoneopts, 0 },
1643         { NULL, NULL, 0 }
1644 };
1645 static cfg_type_t cfg_type_addzone = {
1646         "addzone", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, addzone_fields };
1647
1648 static cfg_clausedef_t
1649 addzoneconf_clauses[] = {
1650         { "addzone", &cfg_type_addzone, 0 },
1651         { NULL, NULL, 0 }
1652 };
1653
1654 static cfg_clausedef_t *
1655 addzoneconf_clausesets[] = {
1656         addzoneconf_clauses,
1657         NULL
1658 };
1659
1660 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_addzoneconf = {
1661         "addzoneconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
1662         &cfg_rep_map, addzoneconf_clausesets
1663 };
1664
1665
1666 static isc_result_t
1667 parse_unitstring(char *str, isc_resourcevalue_t *valuep) {
1668         char *endp;
1669         unsigned int len;
1670         isc_uint64_t value;
1671         isc_uint64_t unit;
1672
1673         value = isc_string_touint64(str, &endp, 10);
1674         if (*endp == 0) {
1675                 *valuep = value;
1676                 return (ISC_R_SUCCESS);
1677         }
1678
1679         len = strlen(str);
1680         if (len < 2 || endp[1] != '\0')
1681                 return (ISC_R_FAILURE);
1682
1683         switch (str[len - 1]) {
1684         case 'k':
1685         case 'K':
1686                 unit = 1024;
1687                 break;
1688         case 'm':
1689         case 'M':
1690                 unit = 1024 * 1024;
1691                 break;
1692         case 'g':
1693         case 'G':
1694                 unit = 1024 * 1024 * 1024;
1695                 break;
1696         default:
1697                 return (ISC_R_FAILURE);
1698         }
1699         if (value > ISC_UINT64_MAX / unit)
1700                 return (ISC_R_FAILURE);
1701         *valuep = value * unit;
1702         return (ISC_R_SUCCESS);
1703 }
1704
1705 static isc_result_t
1706 parse_sizeval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1707         isc_result_t result;
1708         cfg_obj_t *obj = NULL;
1709         isc_uint64_t val;
1710
1711         UNUSED(type);
1712
1713         CHECK(cfg_gettoken(pctx, 0));
1714         if (pctx->token.type != isc_tokentype_string) {
1715                 result = ISC_R_UNEXPECTEDTOKEN;
1716                 goto cleanup;
1717         }
1718         CHECK(parse_unitstring(TOKEN_STRING(pctx), &val));
1719
1720         CHECK(cfg_create_obj(pctx, &cfg_type_uint64, &obj));
1721         obj->value.uint64 = val;
1722         *ret = obj;
1723         return (ISC_R_SUCCESS);
1724
1725  cleanup:
1726         cfg_parser_error(pctx, CFG_LOG_NEAR, "expected integer and optional unit");
1727         return (result);
1728 }
1729
1730 /*%
1731  * A size value (number + optional unit).
1732  */
1733 static cfg_type_t cfg_type_sizeval = {
1734         "sizeval", parse_sizeval, cfg_print_uint64, cfg_doc_terminal,
1735         &cfg_rep_uint64, NULL };
1736
1737 /*%
1738  * A size, "unlimited", or "default".
1739  */
1740
1741 static isc_result_t
1742 parse_size(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1743         return (parse_enum_or_other(pctx, type, &cfg_type_sizeval, ret));
1744 }
1745
1746 static const char *size_enums[] = { "unlimited", "default", NULL };
1747 static cfg_type_t cfg_type_size = {
1748         "size", parse_size, cfg_print_ustring, cfg_doc_terminal,
1749         &cfg_rep_string, size_enums
1750 };
1751
1752 /*%
1753  * A size or "unlimited", but not "default".
1754  */
1755 static const char *sizenodefault_enums[] = { "unlimited", NULL };
1756 static cfg_type_t cfg_type_sizenodefault = {
1757         "size_no_default", parse_size, cfg_print_ustring, cfg_doc_terminal,
1758         &cfg_rep_string, sizenodefault_enums
1759 };
1760
1761 /*%
1762  * optional_keyvalue
1763  */
1764 static isc_result_t
1765 parse_maybe_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
1766                               isc_boolean_t optional, cfg_obj_t **ret)
1767 {
1768         isc_result_t result;
1769         cfg_obj_t *obj = NULL;
1770         const keyword_type_t *kw = type->of;
1771
1772         CHECK(cfg_peektoken(pctx, 0));
1773         if (pctx->token.type == isc_tokentype_string &&
1774             strcasecmp(TOKEN_STRING(pctx), kw->name) == 0) {
1775                 CHECK(cfg_gettoken(pctx, 0));
1776                 CHECK(kw->type->parse(pctx, kw->type, &obj));
1777                 obj->type = type; /* XXX kludge */
1778         } else {
1779                 if (optional) {
1780                         CHECK(cfg_parse_void(pctx, NULL, &obj));
1781                 } else {
1782                         cfg_parser_error(pctx, CFG_LOG_NEAR, "expected '%s'",
1783                                      kw->name);
1784                         result = ISC_R_UNEXPECTEDTOKEN;
1785                         goto cleanup;
1786                 }
1787         }
1788         *ret = obj;
1789  cleanup:
1790         return (result);
1791 }
1792
1793 static isc_result_t
1794 parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype,
1795                     const cfg_type_t *othertype, cfg_obj_t **ret)
1796 {
1797         isc_result_t result;
1798         CHECK(cfg_peektoken(pctx, 0));
1799         if (pctx->token.type == isc_tokentype_string &&
1800             cfg_is_enum(TOKEN_STRING(pctx), enumtype->of)) {
1801                 CHECK(cfg_parse_enum(pctx, enumtype, ret));
1802         } else {
1803                 CHECK(cfg_parse_obj(pctx, othertype, ret));
1804         }
1805  cleanup:
1806         return (result);
1807 }
1808
1809 static void
1810 doc_enum_or_other(cfg_printer_t *pctx, const cfg_type_t *type) {
1811         cfg_doc_terminal(pctx, type);
1812 #if 0 /* XXX */
1813         cfg_print_chars(pctx, "( ", 2);...
1814 #endif
1815
1816 }
1817
1818 static isc_result_t
1819 parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1820         return (parse_maybe_optional_keyvalue(pctx, type, ISC_FALSE, ret));
1821 }
1822
1823 static isc_result_t
1824 parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1825         return (parse_maybe_optional_keyvalue(pctx, type, ISC_TRUE, ret));
1826 }
1827
1828 static void
1829 print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1830         const keyword_type_t *kw = obj->type->of;
1831         cfg_print_cstr(pctx, kw->name);
1832         cfg_print_chars(pctx, " ", 1);
1833         kw->type->print(pctx, obj);
1834 }
1835
1836 static void
1837 doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) {
1838         const keyword_type_t *kw = type->of;
1839         cfg_print_cstr(pctx, kw->name);
1840         cfg_print_chars(pctx, " ", 1);
1841         cfg_doc_obj(pctx, kw->type);
1842 }
1843
1844 static void
1845 doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) {
1846         const keyword_type_t *kw = type->of;
1847         cfg_print_chars(pctx, "[ ", 2);
1848         cfg_print_cstr(pctx, kw->name);
1849         cfg_print_chars(pctx, " ", 1);
1850         cfg_doc_obj(pctx, kw->type);
1851         cfg_print_chars(pctx, " ]", 2);
1852 }
1853
1854 static const char *dialup_enums[] = {
1855         "notify", "notify-passive", "refresh", "passive", NULL };
1856 static isc_result_t
1857 parse_dialup_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1858         return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
1859 }
1860 static cfg_type_t cfg_type_dialuptype = {
1861         "dialuptype", parse_dialup_type, cfg_print_ustring, doc_enum_or_other,
1862         &cfg_rep_string, dialup_enums
1863 };
1864
1865 static const char *notify_enums[] = { "explicit", "master-only", NULL };
1866 static isc_result_t
1867 parse_notify_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1868         return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
1869 }
1870 static cfg_type_t cfg_type_notifytype = {
1871         "notifytype", parse_notify_type, cfg_print_ustring, doc_enum_or_other,
1872         &cfg_rep_string, notify_enums,
1873 };
1874
1875 static const char *ixfrdiff_enums[] = { "master", "slave", NULL };
1876 static isc_result_t
1877 parse_ixfrdiff_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1878         return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
1879 }
1880 static cfg_type_t cfg_type_ixfrdifftype = {
1881         "ixfrdiff", parse_ixfrdiff_type, cfg_print_ustring, doc_enum_or_other,
1882         &cfg_rep_string, ixfrdiff_enums,
1883 };
1884
1885 static const char *v4_aaaa_enums[] = { "break-dnssec", NULL };
1886 static isc_result_t
1887 parse_v4_aaaa(cfg_parser_t *pctx, const cfg_type_t *type,
1888                      cfg_obj_t **ret) {
1889         return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
1890 }
1891 static cfg_type_t cfg_type_v4_aaaa = {
1892         "v4_aaaa", parse_v4_aaaa, cfg_print_ustring,
1893         doc_enum_or_other, &cfg_rep_string, v4_aaaa_enums,
1894 };
1895
1896 static keyword_type_t key_kw = { "key", &cfg_type_astring };
1897
1898 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_keyref = {
1899         "keyref", parse_keyvalue, print_keyvalue, doc_keyvalue,
1900         &cfg_rep_string, &key_kw
1901 };
1902
1903 static cfg_type_t cfg_type_optional_keyref = {
1904         "optional_keyref", parse_optional_keyvalue, print_keyvalue,
1905         doc_optional_keyvalue, &cfg_rep_string, &key_kw
1906 };
1907
1908 /*%
1909  * A "controls" statement is represented as a map with the multivalued
1910  * "inet" and "unix" clauses.
1911  */
1912
1913 static keyword_type_t controls_allow_kw = {
1914         "allow", &cfg_type_bracketed_aml };
1915
1916 static cfg_type_t cfg_type_controls_allow = {
1917         "controls_allow", parse_keyvalue,
1918         print_keyvalue, doc_keyvalue,
1919         &cfg_rep_list, &controls_allow_kw
1920 };
1921
1922 static keyword_type_t controls_keys_kw = {
1923         "keys", &cfg_type_keylist };
1924
1925 static cfg_type_t cfg_type_controls_keys = {
1926         "controls_keys", parse_optional_keyvalue,
1927         print_keyvalue, doc_optional_keyvalue,
1928         &cfg_rep_list, &controls_keys_kw
1929 };
1930
1931 static cfg_tuplefielddef_t inetcontrol_fields[] = {
1932         { "address", &cfg_type_controls_sockaddr, 0 },
1933         { "allow", &cfg_type_controls_allow, 0 },
1934         { "keys", &cfg_type_controls_keys, 0 },
1935         { NULL, NULL, 0 }
1936 };
1937
1938 static cfg_type_t cfg_type_inetcontrol = {
1939         "inetcontrol", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
1940         inetcontrol_fields
1941 };
1942
1943 static keyword_type_t controls_perm_kw = {
1944         "perm", &cfg_type_uint32 };
1945
1946 static cfg_type_t cfg_type_controls_perm = {
1947         "controls_perm", parse_keyvalue,
1948         print_keyvalue, doc_keyvalue,
1949         &cfg_rep_uint32, &controls_perm_kw
1950 };
1951
1952 static keyword_type_t controls_owner_kw = {
1953         "owner", &cfg_type_uint32 };
1954
1955 static cfg_type_t cfg_type_controls_owner = {
1956         "controls_owner", parse_keyvalue,
1957         print_keyvalue, doc_keyvalue,
1958         &cfg_rep_uint32, &controls_owner_kw
1959 };
1960
1961 static keyword_type_t controls_group_kw = {
1962         "group", &cfg_type_uint32 };
1963
1964 static cfg_type_t cfg_type_controls_group = {
1965         "controls_allow", parse_keyvalue,
1966         print_keyvalue, doc_keyvalue,
1967         &cfg_rep_uint32, &controls_group_kw
1968 };
1969
1970 static cfg_tuplefielddef_t unixcontrol_fields[] = {
1971         { "path", &cfg_type_qstring, 0 },
1972         { "perm", &cfg_type_controls_perm, 0 },
1973         { "owner", &cfg_type_controls_owner, 0 },
1974         { "group", &cfg_type_controls_group, 0 },
1975         { "keys", &cfg_type_controls_keys, 0 },
1976         { NULL, NULL, 0 }
1977 };
1978
1979 static cfg_type_t cfg_type_unixcontrol = {
1980         "unixcontrol", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
1981         unixcontrol_fields
1982 };
1983
1984 static cfg_clausedef_t
1985 controls_clauses[] = {
1986         { "inet", &cfg_type_inetcontrol, CFG_CLAUSEFLAG_MULTI },
1987         { "unix", &cfg_type_unixcontrol, CFG_CLAUSEFLAG_MULTI },
1988         { NULL, NULL, 0 }
1989 };
1990
1991 static cfg_clausedef_t *
1992 controls_clausesets[] = {
1993         controls_clauses,
1994         NULL
1995 };
1996 static cfg_type_t cfg_type_controls = {
1997         "controls", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map,    &controls_clausesets
1998 };
1999
2000 /*%
2001  * A "statistics-channels" statement is represented as a map with the
2002  * multivalued "inet" clauses.
2003  */
2004 static void
2005 doc_optional_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
2006         const keyword_type_t *kw = type->of;
2007         cfg_print_chars(pctx, "[ ", 2);
2008         cfg_print_cstr(pctx, kw->name);
2009         cfg_print_chars(pctx, " ", 1);
2010         cfg_doc_obj(pctx, kw->type);
2011         cfg_print_chars(pctx, " ]", 2);
2012 }
2013
2014 static cfg_type_t cfg_type_optional_allow = {
2015         "optional_allow", parse_optional_keyvalue, print_keyvalue,
2016         doc_optional_bracketed_list, &cfg_rep_list, &controls_allow_kw
2017 };
2018
2019 static cfg_tuplefielddef_t statserver_fields[] = {
2020         { "address", &cfg_type_controls_sockaddr, 0 }, /* reuse controls def */
2021         { "allow", &cfg_type_optional_allow, 0 },
2022         { NULL, NULL, 0 }
2023 };
2024
2025 static cfg_type_t cfg_type_statschannel = {
2026         "statschannel", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
2027         &cfg_rep_tuple, statserver_fields
2028 };
2029
2030 static cfg_clausedef_t
2031 statservers_clauses[] = {
2032         { "inet", &cfg_type_statschannel, CFG_CLAUSEFLAG_MULTI },
2033         { NULL, NULL, 0 }
2034 };
2035
2036 static cfg_clausedef_t *
2037 statservers_clausesets[] = {
2038         statservers_clauses,
2039         NULL
2040 };
2041
2042 static cfg_type_t cfg_type_statschannels = {
2043         "statistics-channels", cfg_parse_map, cfg_print_map, cfg_doc_map,
2044         &cfg_rep_map,   &statservers_clausesets
2045 };
2046
2047 /*%
2048  * An optional class, as used in view and zone statements.
2049  */
2050 static isc_result_t
2051 parse_optional_class(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2052         isc_result_t result;
2053         UNUSED(type);
2054         CHECK(cfg_peektoken(pctx, 0));
2055         if (pctx->token.type == isc_tokentype_string)
2056                 CHECK(cfg_parse_obj(pctx, &cfg_type_ustring, ret));
2057         else
2058                 CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
2059  cleanup:
2060         return (result);
2061 }
2062
2063 static cfg_type_t cfg_type_optional_class = {
2064         "optional_class", parse_optional_class, NULL, cfg_doc_terminal,
2065         NULL, NULL
2066 };
2067
2068 static isc_result_t
2069 parse_querysource(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2070         isc_result_t result;
2071         cfg_obj_t *obj = NULL;
2072         isc_netaddr_t netaddr;
2073         in_port_t port;
2074         unsigned int have_address = 0;
2075         unsigned int have_port = 0;
2076         const unsigned int *flagp = type->of;
2077
2078         if ((*flagp & CFG_ADDR_V4OK) != 0)
2079                 isc_netaddr_any(&netaddr);
2080         else if ((*flagp & CFG_ADDR_V6OK) != 0)
2081                 isc_netaddr_any6(&netaddr);
2082         else
2083                 INSIST(0);
2084
2085         port = 0;
2086
2087         for (;;) {
2088                 CHECK(cfg_peektoken(pctx, 0));
2089                 if (pctx->token.type == isc_tokentype_string) {
2090                         if (strcasecmp(TOKEN_STRING(pctx),
2091                                        "address") == 0)
2092                         {
2093                                 /* read "address" */
2094                                 CHECK(cfg_gettoken(pctx, 0));
2095                                 CHECK(cfg_parse_rawaddr(pctx, *flagp,
2096                                                         &netaddr));
2097                                 have_address++;
2098                         } else if (strcasecmp(TOKEN_STRING(pctx), "port") == 0)
2099                         {
2100                                 /* read "port" */
2101                                 CHECK(cfg_gettoken(pctx, 0));
2102                                 CHECK(cfg_parse_rawport(pctx,
2103                                                         CFG_ADDR_WILDOK,
2104                                                         &port));
2105                                 have_port++;
2106                         } else if (have_port == 0 && have_address == 0) {
2107                                 return (cfg_parse_sockaddr(pctx, type, ret));
2108                         } else {
2109                                 cfg_parser_error(pctx, CFG_LOG_NEAR,
2110                                              "expected 'address' or 'port'");
2111                                 return (ISC_R_UNEXPECTEDTOKEN);
2112                         }
2113                 } else
2114                         break;
2115         }
2116         if (have_address > 1 || have_port > 1 ||
2117             have_address + have_port == 0) {
2118                 cfg_parser_error(pctx, 0, "expected one address and/or port");
2119                 return (ISC_R_UNEXPECTEDTOKEN);
2120         }
2121
2122         CHECK(cfg_create_obj(pctx, &cfg_type_querysource, &obj));
2123         isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
2124         *ret = obj;
2125         return (ISC_R_SUCCESS);
2126
2127  cleanup:
2128         cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid query source");
2129         CLEANUP_OBJ(obj);
2130         return (result);
2131 }
2132
2133 static void
2134 print_querysource(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2135         isc_netaddr_t na;
2136         isc_netaddr_fromsockaddr(&na, &obj->value.sockaddr);
2137         cfg_print_cstr(pctx, "address ");
2138         cfg_print_rawaddr(pctx, &na);
2139         cfg_print_cstr(pctx, " port ");
2140         cfg_print_rawuint(pctx, isc_sockaddr_getport(&obj->value.sockaddr));
2141 }
2142
2143 static unsigned int sockaddr4wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V4OK;
2144 static unsigned int sockaddr6wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V6OK;
2145
2146 static cfg_type_t cfg_type_querysource4 = {
2147         "querysource4", parse_querysource, NULL, cfg_doc_terminal,
2148         NULL, &sockaddr4wild_flags
2149 };
2150
2151 static cfg_type_t cfg_type_querysource6 = {
2152         "querysource6", parse_querysource, NULL, cfg_doc_terminal,
2153         NULL, &sockaddr6wild_flags
2154 };
2155
2156 static cfg_type_t cfg_type_querysource = {
2157         "querysource", NULL, print_querysource, NULL, &cfg_rep_sockaddr, NULL
2158 };
2159
2160 /*% addrmatchelt */
2161
2162 static isc_result_t
2163 parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2164         isc_result_t result;
2165         UNUSED(type);
2166
2167         CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
2168
2169         if (pctx->token.type == isc_tokentype_string ||
2170             pctx->token.type == isc_tokentype_qstring) {
2171                 if (pctx->token.type == isc_tokentype_string &&
2172                     (strcasecmp(TOKEN_STRING(pctx), "key") == 0)) {
2173                         CHECK(cfg_parse_obj(pctx, &cfg_type_keyref, ret));
2174                 } else {
2175                         if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK |
2176                                                   CFG_ADDR_V4PREFIXOK |
2177                                                   CFG_ADDR_V6OK))
2178                         {
2179                                 CHECK(cfg_parse_netprefix(pctx, NULL, ret));
2180                         } else {
2181                                 CHECK(cfg_parse_astring(pctx, NULL, ret));
2182                         }
2183                 }
2184         } else if (pctx->token.type == isc_tokentype_special) {
2185                 if (pctx->token.value.as_char == '{') {
2186                         /* Nested match list. */
2187                         CHECK(cfg_parse_obj(pctx, &cfg_type_bracketed_aml, ret));
2188                 } else if (pctx->token.value.as_char == '!') {
2189                         CHECK(cfg_gettoken(pctx, 0)); /* read "!" */
2190                         CHECK(cfg_parse_obj(pctx, &cfg_type_negated, ret));
2191                 } else {
2192                         goto bad;
2193                 }
2194         } else {
2195         bad:
2196                 cfg_parser_error(pctx, CFG_LOG_NEAR,
2197                              "expected IP match list element");
2198                 return (ISC_R_UNEXPECTEDTOKEN);
2199         }
2200  cleanup:
2201         return (result);
2202 }
2203
2204 /*%
2205  * A negated address match list element (like "! 10.0.0.1").
2206  * Somewhat sneakily, the caller is expected to parse the
2207  * "!", but not to print it.
2208  */
2209
2210 static cfg_tuplefielddef_t negated_fields[] = {
2211         { "value", &cfg_type_addrmatchelt, 0 },
2212         { NULL, NULL, 0 }
2213 };
2214
2215 static void
2216 print_negated(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2217         cfg_print_chars(pctx, "!", 1);
2218         cfg_print_tuple(pctx, obj);
2219 }
2220
2221 static cfg_type_t cfg_type_negated = {
2222         "negated", cfg_parse_tuple, print_negated, NULL, &cfg_rep_tuple,
2223         &negated_fields
2224 };
2225
2226 /*% An address match list element */
2227
2228 static cfg_type_t cfg_type_addrmatchelt = {
2229         "address_match_element", parse_addrmatchelt, NULL, cfg_doc_terminal,
2230         NULL, NULL
2231 };
2232
2233 /*% A bracketed address match list */
2234
2235 static cfg_type_t cfg_type_bracketed_aml = {
2236         "bracketed_aml", cfg_parse_bracketed_list, cfg_print_bracketed_list,
2237         cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_addrmatchelt
2238 };
2239
2240 /*%
2241  * The socket address syntax in the "controls" statement is silly.
2242  * It allows both socket address families, but also allows "*",
2243  * whis is gratuitously interpreted as the IPv4 wildcard address.
2244  */
2245 static unsigned int controls_sockaddr_flags =
2246         CFG_ADDR_V4OK | CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
2247 static cfg_type_t cfg_type_controls_sockaddr = {
2248         "controls_sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr,
2249         cfg_doc_sockaddr, &cfg_rep_sockaddr, &controls_sockaddr_flags
2250 };
2251
2252 /*%
2253  * Handle the special kludge syntax of the "keys" clause in the "server"
2254  * statement, which takes a single key with or without braces and semicolon.
2255  */
2256 static isc_result_t
2257 parse_server_key_kludge(cfg_parser_t *pctx, const cfg_type_t *type,
2258                         cfg_obj_t **ret)
2259 {
2260         isc_result_t result;
2261         isc_boolean_t braces = ISC_FALSE;
2262         UNUSED(type);
2263
2264         /* Allow opening brace. */
2265         CHECK(cfg_peektoken(pctx, 0));
2266         if (pctx->token.type == isc_tokentype_special &&
2267             pctx->token.value.as_char == '{') {
2268                 CHECK(cfg_gettoken(pctx, 0));
2269                 braces = ISC_TRUE;
2270         }
2271
2272         CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret));
2273
2274         if (braces) {
2275                 /* Skip semicolon if present. */
2276                 CHECK(cfg_peektoken(pctx, 0));
2277                 if (pctx->token.type == isc_tokentype_special &&
2278                     pctx->token.value.as_char == ';')
2279                         CHECK(cfg_gettoken(pctx, 0));
2280
2281                 CHECK(cfg_parse_special(pctx, '}'));
2282         }
2283  cleanup:
2284         return (result);
2285 }
2286 static cfg_type_t cfg_type_server_key_kludge = {
2287         "server_key", parse_server_key_kludge, NULL, cfg_doc_terminal,
2288         NULL, NULL
2289 };
2290
2291
2292 /*%
2293  * An optional logging facility.
2294  */
2295
2296 static isc_result_t
2297 parse_optional_facility(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
2298 {
2299         isc_result_t result;
2300         UNUSED(type);
2301
2302         CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
2303         if (pctx->token.type == isc_tokentype_string ||
2304             pctx->token.type == isc_tokentype_qstring) {
2305                 CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret));
2306         } else {
2307                 CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
2308         }
2309  cleanup:
2310         return (result);
2311 }
2312
2313 static cfg_type_t cfg_type_optional_facility = {
2314         "optional_facility", parse_optional_facility, NULL, cfg_doc_terminal,
2315         NULL, NULL };
2316
2317
2318 /*%
2319  * A log severity.  Return as a string, except "debug N",
2320  * which is returned as a keyword object.
2321  */
2322
2323 static keyword_type_t debug_kw = { "debug", &cfg_type_uint32 };
2324 static cfg_type_t cfg_type_debuglevel = {
2325         "debuglevel", parse_keyvalue,
2326         print_keyvalue, doc_keyvalue,
2327         &cfg_rep_uint32, &debug_kw
2328 };
2329
2330 static isc_result_t
2331 parse_logseverity(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2332         isc_result_t result;
2333         UNUSED(type);
2334
2335         CHECK(cfg_peektoken(pctx, 0));
2336         if (pctx->token.type == isc_tokentype_string &&
2337             strcasecmp(TOKEN_STRING(pctx), "debug") == 0) {
2338                 CHECK(cfg_gettoken(pctx, 0)); /* read "debug" */
2339                 CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER));
2340                 if (pctx->token.type == isc_tokentype_number) {
2341                         CHECK(cfg_parse_uint32(pctx, NULL, ret));
2342                 } else {
2343                         /*
2344                          * The debug level is optional and defaults to 1.
2345                          * This makes little sense, but we support it for
2346                          * compatibility with BIND 8.
2347                          */
2348                         CHECK(cfg_create_obj(pctx, &cfg_type_uint32, ret));
2349                         (*ret)->value.uint32 = 1;
2350                 }
2351                 (*ret)->type = &cfg_type_debuglevel; /* XXX kludge */
2352         } else {
2353                 CHECK(cfg_parse_obj(pctx, &cfg_type_loglevel, ret));
2354         }
2355  cleanup:
2356         return (result);
2357 }
2358
2359 static cfg_type_t cfg_type_logseverity = {
2360         "log_severity", parse_logseverity, NULL, cfg_doc_terminal,
2361         NULL, NULL };
2362
2363 /*%
2364  * The "file" clause of the "channel" statement.
2365  * This is yet another special case.
2366  */
2367
2368 static const char *logversions_enums[] = { "unlimited", NULL };
2369 static isc_result_t
2370 parse_logversions(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2371         return (parse_enum_or_other(pctx, type, &cfg_type_uint32, ret));
2372 }
2373
2374 static cfg_type_t cfg_type_logversions = {
2375         "logversions", parse_logversions, cfg_print_ustring, cfg_doc_terminal,
2376         &cfg_rep_string, logversions_enums
2377 };
2378
2379 static cfg_tuplefielddef_t logfile_fields[] = {
2380         { "file", &cfg_type_qstring, 0 },
2381         { "versions", &cfg_type_logversions, 0 },
2382         { "size", &cfg_type_size, 0 },
2383         { NULL, NULL, 0 }
2384 };
2385
2386 static isc_result_t
2387 parse_logfile(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2388         isc_result_t result;
2389         cfg_obj_t *obj = NULL;
2390         const cfg_tuplefielddef_t *fields = type->of;
2391
2392         CHECK(cfg_create_tuple(pctx, type, &obj));
2393
2394         /* Parse the mandatory "file" field */
2395         CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
2396
2397         /* Parse "versions" and "size" fields in any order. */
2398         for (;;) {
2399                 CHECK(cfg_peektoken(pctx, 0));
2400                 if (pctx->token.type == isc_tokentype_string) {
2401                         CHECK(cfg_gettoken(pctx, 0));
2402                         if (strcasecmp(TOKEN_STRING(pctx),
2403                                        "versions") == 0 &&
2404                             obj->value.tuple[1] == NULL) {
2405                                 CHECK(cfg_parse_obj(pctx, fields[1].type,
2406                                             &obj->value.tuple[1]));
2407                         } else if (strcasecmp(TOKEN_STRING(pctx),
2408                                               "size") == 0 &&
2409                                    obj->value.tuple[2] == NULL) {
2410                                 CHECK(cfg_parse_obj(pctx, fields[2].type,
2411                                             &obj->value.tuple[2]));
2412                         } else {
2413                                 break;
2414                         }
2415                 } else {
2416                         break;
2417                 }
2418         }
2419
2420         /* Create void objects for missing optional values. */
2421         if (obj->value.tuple[1] == NULL)
2422                 CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1]));
2423         if (obj->value.tuple[2] == NULL)
2424                 CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2]));
2425
2426         *ret = obj;
2427         return (ISC_R_SUCCESS);
2428
2429  cleanup:
2430         CLEANUP_OBJ(obj);
2431         return (result);
2432 }
2433
2434 static void
2435 print_logfile(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2436         cfg_print_obj(pctx, obj->value.tuple[0]); /* file */
2437         if (obj->value.tuple[1]->type->print != cfg_print_void) {
2438                 cfg_print_cstr(pctx, " versions ");
2439                 cfg_print_obj(pctx, obj->value.tuple[1]);
2440         }
2441         if (obj->value.tuple[2]->type->print != cfg_print_void) {
2442                 cfg_print_cstr(pctx, " size ");
2443                 cfg_print_obj(pctx, obj->value.tuple[2]);
2444         }
2445 }
2446
2447
2448 static void
2449 doc_logfile(cfg_printer_t *pctx, const cfg_type_t *type) {
2450         UNUSED(type);
2451         cfg_print_cstr(pctx, "<quoted_string>");
2452         cfg_print_chars(pctx, " ", 1);
2453         cfg_print_cstr(pctx, "[ versions ( \"unlimited\" | <integer> ) ]");
2454         cfg_print_chars(pctx, " ", 1);
2455         cfg_print_cstr(pctx, "[ size <size> ]");
2456 }
2457
2458 static cfg_type_t cfg_type_logfile = {
2459         "log_file", parse_logfile, print_logfile, doc_logfile,
2460         &cfg_rep_tuple, logfile_fields
2461 };
2462
2463 /*% An IPv4 address with optional port, "*" accepted as wildcard. */
2464 static cfg_type_t cfg_type_sockaddr4wild = {
2465         "sockaddr4wild", cfg_parse_sockaddr, cfg_print_sockaddr,
2466         cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr4wild_flags
2467 };
2468
2469 /*% An IPv6 address with optional port, "*" accepted as wildcard. */
2470 static cfg_type_t cfg_type_sockaddr6wild = {
2471         "v6addrportwild", cfg_parse_sockaddr, cfg_print_sockaddr,
2472         cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr6wild_flags
2473 };
2474
2475 /*%
2476  * lwres
2477  */
2478
2479 static cfg_tuplefielddef_t lwres_view_fields[] = {
2480         { "name", &cfg_type_astring, 0 },
2481         { "class", &cfg_type_optional_class, 0 },
2482         { NULL, NULL, 0 }
2483 };
2484 static cfg_type_t cfg_type_lwres_view = {
2485         "lwres_view", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
2486         lwres_view_fields
2487 };
2488
2489 static cfg_type_t cfg_type_lwres_searchlist = {
2490         "lwres_searchlist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
2491         cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring };
2492
2493 static cfg_clausedef_t
2494 lwres_clauses[] = {
2495         { "listen-on", &cfg_type_portiplist, 0 },
2496         { "view", &cfg_type_lwres_view, 0 },
2497         { "search", &cfg_type_lwres_searchlist, 0 },
2498         { "ndots", &cfg_type_uint32, 0 },
2499         { NULL, NULL, 0 }
2500 };
2501
2502 static cfg_clausedef_t *
2503 lwres_clausesets[] = {
2504         lwres_clauses,
2505         NULL
2506 };
2507 static cfg_type_t cfg_type_lwres = {
2508         "lwres", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map,
2509         lwres_clausesets
2510 };
2511
2512 /*%
2513  * rndc
2514  */
2515
2516 static cfg_clausedef_t
2517 rndcconf_options_clauses[] = {
2518         { "default-key", &cfg_type_astring, 0 },
2519         { "default-port", &cfg_type_uint32, 0 },
2520         { "default-server", &cfg_type_astring, 0 },
2521         { "default-source-address", &cfg_type_netaddr4wild, 0 },
2522         { "default-source-address-v6", &cfg_type_netaddr6wild, 0 },
2523         { NULL, NULL, 0 }
2524 };
2525
2526 static cfg_clausedef_t *
2527 rndcconf_options_clausesets[] = {
2528         rndcconf_options_clauses,
2529         NULL
2530 };
2531
2532 static cfg_type_t cfg_type_rndcconf_options = {
2533         "rndcconf_options", cfg_parse_map, cfg_print_map, cfg_doc_map,
2534         &cfg_rep_map, rndcconf_options_clausesets
2535 };
2536
2537 static cfg_clausedef_t
2538 rndcconf_server_clauses[] = {
2539         { "key", &cfg_type_astring, 0 },
2540         { "port", &cfg_type_uint32, 0 },
2541         { "source-address", &cfg_type_netaddr4wild, 0 },
2542         { "source-address-v6", &cfg_type_netaddr6wild, 0 },
2543         { "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 },
2544         { NULL, NULL, 0 }
2545 };
2546
2547 static cfg_clausedef_t *
2548 rndcconf_server_clausesets[] = {
2549         rndcconf_server_clauses,
2550         NULL
2551 };
2552
2553 static cfg_type_t cfg_type_rndcconf_server = {
2554         "rndcconf_server", cfg_parse_named_map, cfg_print_map, cfg_doc_map,
2555         &cfg_rep_map, rndcconf_server_clausesets
2556 };
2557
2558 static cfg_clausedef_t
2559 rndcconf_clauses[] = {
2560         { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
2561         { "server", &cfg_type_rndcconf_server, CFG_CLAUSEFLAG_MULTI },
2562         { "options", &cfg_type_rndcconf_options, 0 },
2563         { NULL, NULL, 0 }
2564 };
2565
2566 static cfg_clausedef_t *
2567 rndcconf_clausesets[] = {
2568         rndcconf_clauses,
2569         NULL
2570 };
2571
2572 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndcconf = {
2573         "rndcconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
2574         &cfg_rep_map, rndcconf_clausesets
2575 };
2576
2577 static cfg_clausedef_t
2578 rndckey_clauses[] = {
2579         { "key", &cfg_type_key, 0 },
2580         { NULL, NULL, 0 }
2581 };
2582
2583 static cfg_clausedef_t *
2584 rndckey_clausesets[] = {
2585         rndckey_clauses,
2586         NULL
2587 };
2588
2589 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndckey = {
2590         "rndckey", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
2591         &cfg_rep_map, rndckey_clausesets
2592 };
2593
2594 /*
2595  * session.key has exactly the same syntax as rndc.key, but it's defined
2596  * separately for clarity (and so we can extend it someday, if needed).
2597  */
2598 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_sessionkey = {
2599         "sessionkey", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
2600         &cfg_rep_map, rndckey_clausesets
2601 };
2602
2603 static cfg_tuplefielddef_t nameport_fields[] = {
2604         { "name", &cfg_type_astring, 0 },
2605         { "port", &cfg_type_optional_port, 0 },
2606         { NULL, NULL, 0 }
2607 };
2608 static cfg_type_t cfg_type_nameport = {
2609         "nameport", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
2610         &cfg_rep_tuple, nameport_fields
2611 };
2612
2613 static void
2614 doc_sockaddrnameport(cfg_printer_t *pctx, const cfg_type_t *type) {
2615         UNUSED(type);
2616         cfg_print_chars(pctx, "( ", 2);
2617         cfg_print_cstr(pctx, "<quoted_string>");
2618         cfg_print_chars(pctx, " ", 1);
2619         cfg_print_cstr(pctx, "[ port <integer> ]");
2620         cfg_print_chars(pctx, " | ", 3);
2621         cfg_print_cstr(pctx, "<ipv4_address>");
2622         cfg_print_chars(pctx, " ", 1);
2623         cfg_print_cstr(pctx, "[ port <integer> ]");
2624         cfg_print_chars(pctx, " | ", 3);
2625         cfg_print_cstr(pctx, "<ipv6_address>");
2626         cfg_print_chars(pctx, " ", 1);
2627         cfg_print_cstr(pctx, "[ port <integer> ]");
2628         cfg_print_chars(pctx, " )", 2);
2629 }
2630
2631 static isc_result_t
2632 parse_sockaddrnameport(cfg_parser_t *pctx, const cfg_type_t *type,
2633                        cfg_obj_t **ret)
2634 {
2635         isc_result_t result;
2636         cfg_obj_t *obj = NULL;
2637         UNUSED(type);
2638
2639         CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
2640         if (pctx->token.type == isc_tokentype_string ||
2641             pctx->token.type == isc_tokentype_qstring) {
2642                 if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK))
2643                         CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr, ret));
2644                 else {
2645                         const cfg_tuplefielddef_t *fields =
2646                                                    cfg_type_nameport.of;
2647                         CHECK(cfg_create_tuple(pctx, &cfg_type_nameport,
2648                                                &obj));
2649                         CHECK(cfg_parse_obj(pctx, fields[0].type,
2650                                             &obj->value.tuple[0]));
2651                         CHECK(cfg_parse_obj(pctx, fields[1].type,
2652                                             &obj->value.tuple[1]));
2653                         *ret = obj;
2654                         obj = NULL;
2655                 }
2656         } else {
2657                 cfg_parser_error(pctx, CFG_LOG_NEAR,
2658                              "expected IP address or hostname");
2659                 return (ISC_R_UNEXPECTEDTOKEN);
2660         }
2661  cleanup:
2662         CLEANUP_OBJ(obj);
2663         return (result);
2664 }
2665
2666 static cfg_type_t cfg_type_sockaddrnameport = {
2667         "sockaddrnameport_element", parse_sockaddrnameport, NULL,
2668          doc_sockaddrnameport, NULL, NULL
2669 };
2670
2671 static cfg_type_t cfg_type_bracketed_sockaddrnameportlist = {
2672         "bracketed_sockaddrnameportlist", cfg_parse_bracketed_list,
2673         cfg_print_bracketed_list, cfg_doc_bracketed_list,
2674         &cfg_rep_list, &cfg_type_sockaddrnameport
2675 };
2676
2677 /*%
2678  * A list of socket addresses or name with an optional default port,
2679  * as used in the dual-stack-servers option.  E.g.,
2680  * "port 1234 { dual-stack-servers.net; 10.0.0.1; 1::2 port 69; }"
2681  */
2682 static cfg_tuplefielddef_t nameportiplist_fields[] = {
2683         { "port", &cfg_type_optional_port, 0 },
2684         { "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 },
2685         { NULL, NULL, 0 }
2686 };
2687
2688 static cfg_type_t cfg_type_nameportiplist = {
2689         "nameportiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
2690         &cfg_rep_tuple, nameportiplist_fields
2691 };
2692
2693 /*%
2694  * masters element.
2695  */
2696
2697 static void
2698 doc_masterselement(cfg_printer_t *pctx, const cfg_type_t *type) {
2699         UNUSED(type);
2700         cfg_print_chars(pctx, "( ", 2);
2701         cfg_print_cstr(pctx, "<masters>");
2702         cfg_print_chars(pctx, " | ", 3);
2703         cfg_print_cstr(pctx, "<ipv4_address>");
2704         cfg_print_chars(pctx, " ", 1);
2705         cfg_print_cstr(pctx, "[ port <integer> ]");
2706         cfg_print_chars(pctx, " | ", 3);
2707         cfg_print_cstr(pctx, "<ipv6_address>");
2708         cfg_print_chars(pctx, " ", 1);
2709         cfg_print_cstr(pctx, "[ port <integer> ]");
2710         cfg_print_chars(pctx, " )", 2);
2711 }
2712
2713 static isc_result_t
2714 parse_masterselement(cfg_parser_t *pctx, const cfg_type_t *type,
2715                      cfg_obj_t **ret)
2716 {
2717         isc_result_t result;
2718         cfg_obj_t *obj = NULL;
2719         UNUSED(type);
2720
2721         CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
2722         if (pctx->token.type == isc_tokentype_string ||
2723             pctx->token.type == isc_tokentype_qstring) {
2724                 if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK))
2725                         CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr, ret));
2726                 else
2727                         CHECK(cfg_parse_astring(pctx, &cfg_type_astring, ret));
2728         } else {
2729                 cfg_parser_error(pctx, CFG_LOG_NEAR,
2730                              "expected IP address or masters name");
2731                 return (ISC_R_UNEXPECTEDTOKEN);
2732         }
2733  cleanup:
2734         CLEANUP_OBJ(obj);
2735         return (result);
2736 }
2737
2738 static cfg_type_t cfg_type_masterselement = {
2739         "masters_element", parse_masterselement, NULL,
2740          doc_masterselement, NULL, NULL
2741 };