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