xref: /netbsd-src/external/mpl/bind/dist/lib/isccfg/namedconf.c (revision fb5eed702691094bd687fbf1ded189c87457cd35)
1 /*	$NetBSD: namedconf.c,v 1.12 2021/08/19 11:50:19 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9  *
10  * See the COPYRIGHT file distributed with this work for additional
11  * information regarding copyright ownership.
12  */
13 
14 /*! \file */
15 
16 #include <inttypes.h>
17 #include <stdbool.h>
18 #include <stdlib.h>
19 #include <string.h>
20 
21 #include <isc/lex.h>
22 #include <isc/mem.h>
23 #include <isc/print.h>
24 #include <isc/result.h>
25 #include <isc/string.h>
26 #include <isc/util.h>
27 
28 #include <dns/result.h>
29 #include <dns/ttl.h>
30 
31 #include <isccfg/cfg.h>
32 #include <isccfg/grammar.h>
33 #include <isccfg/log.h>
34 #include <isccfg/namedconf.h>
35 
36 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
37 
38 /*% Check a return value. */
39 #define CHECK(op)                            \
40 	do {                                 \
41 		result = (op);               \
42 		if (result != ISC_R_SUCCESS) \
43 			goto cleanup;        \
44 	} while (0)
45 
46 /*% Clean up a configuration object if non-NULL. */
47 #define CLEANUP_OBJ(obj)                               \
48 	do {                                           \
49 		if ((obj) != NULL)                     \
50 			cfg_obj_destroy(pctx, &(obj)); \
51 	} while (0)
52 
53 /*%
54  * Forward declarations of static functions.
55  */
56 
57 static isc_result_t
58 parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
59 
60 static isc_result_t
61 parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
62 			cfg_obj_t **ret);
63 
64 static isc_result_t
65 parse_updatepolicy(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
66 static void
67 print_updatepolicy(cfg_printer_t *pctx, const cfg_obj_t *obj);
68 
69 static void
70 doc_updatepolicy(cfg_printer_t *pctx, const cfg_type_t *type);
71 
72 static void
73 print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj);
74 
75 static void
76 doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type);
77 
78 static void
79 doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type);
80 
81 static cfg_type_t cfg_type_acl;
82 static cfg_type_t cfg_type_bracketed_dscpsockaddrlist;
83 static cfg_type_t cfg_type_bracketed_namesockaddrkeylist;
84 static cfg_type_t cfg_type_bracketed_netaddrlist;
85 static cfg_type_t cfg_type_bracketed_sockaddrnameportlist;
86 static cfg_type_t cfg_type_controls;
87 static cfg_type_t cfg_type_controls_sockaddr;
88 static cfg_type_t cfg_type_destinationlist;
89 static cfg_type_t cfg_type_dialuptype;
90 static cfg_type_t cfg_type_dlz;
91 static cfg_type_t cfg_type_dnssecpolicy;
92 static cfg_type_t cfg_type_dnstap;
93 static cfg_type_t cfg_type_dnstapoutput;
94 static cfg_type_t cfg_type_dyndb;
95 static cfg_type_t cfg_type_plugin;
96 static cfg_type_t cfg_type_ixfrdifftype;
97 static cfg_type_t cfg_type_ixfrratio;
98 static cfg_type_t cfg_type_key;
99 static cfg_type_t cfg_type_logfile;
100 static cfg_type_t cfg_type_logging;
101 static cfg_type_t cfg_type_logseverity;
102 static cfg_type_t cfg_type_logsuffix;
103 static cfg_type_t cfg_type_logversions;
104 static cfg_type_t cfg_type_remoteselement;
105 static cfg_type_t cfg_type_maxduration;
106 static cfg_type_t cfg_type_minimal;
107 static cfg_type_t cfg_type_nameportiplist;
108 static cfg_type_t cfg_type_notifytype;
109 static cfg_type_t cfg_type_optional_allow;
110 static cfg_type_t cfg_type_optional_class;
111 static cfg_type_t cfg_type_optional_dscp;
112 static cfg_type_t cfg_type_optional_facility;
113 static cfg_type_t cfg_type_optional_keyref;
114 static cfg_type_t cfg_type_optional_port;
115 static cfg_type_t cfg_type_optional_uint32;
116 static cfg_type_t cfg_type_options;
117 static cfg_type_t cfg_type_portiplist;
118 static cfg_type_t cfg_type_printtime;
119 static cfg_type_t cfg_type_qminmethod;
120 static cfg_type_t cfg_type_querysource4;
121 static cfg_type_t cfg_type_querysource6;
122 static cfg_type_t cfg_type_querysource;
123 static cfg_type_t cfg_type_server;
124 static cfg_type_t cfg_type_server_key_kludge;
125 static cfg_type_t cfg_type_size;
126 static cfg_type_t cfg_type_sizenodefault;
127 static cfg_type_t cfg_type_sizeorpercent;
128 static cfg_type_t cfg_type_sizeval;
129 static cfg_type_t cfg_type_sockaddr4wild;
130 static cfg_type_t cfg_type_sockaddr6wild;
131 static cfg_type_t cfg_type_statschannels;
132 static cfg_type_t cfg_type_view;
133 static cfg_type_t cfg_type_viewopts;
134 static cfg_type_t cfg_type_zone;
135 
136 /*% tkey-dhkey */
137 
138 static cfg_tuplefielddef_t tkey_dhkey_fields[] = {
139 	{ "name", &cfg_type_qstring, 0 },
140 	{ "keyid", &cfg_type_uint32, 0 },
141 	{ NULL, NULL, 0 }
142 };
143 
144 static cfg_type_t cfg_type_tkey_dhkey = { "tkey-dhkey",	   cfg_parse_tuple,
145 					  cfg_print_tuple, cfg_doc_tuple,
146 					  &cfg_rep_tuple,  tkey_dhkey_fields };
147 
148 /*% listen-on */
149 
150 static cfg_tuplefielddef_t listenon_fields[] = {
151 	{ "port", &cfg_type_optional_port, 0 },
152 	{ "dscp", &cfg_type_optional_dscp, 0 },
153 	{ "acl", &cfg_type_bracketed_aml, 0 },
154 	{ NULL, NULL, 0 }
155 };
156 
157 static cfg_type_t cfg_type_listenon = { "listenon",	 cfg_parse_tuple,
158 					cfg_print_tuple, cfg_doc_tuple,
159 					&cfg_rep_tuple,	 listenon_fields };
160 
161 /*% acl */
162 
163 static cfg_tuplefielddef_t acl_fields[] = { { "name", &cfg_type_astring, 0 },
164 					    { "value", &cfg_type_bracketed_aml,
165 					      0 },
166 					    { NULL, NULL, 0 } };
167 
168 static cfg_type_t cfg_type_acl = { "acl",	    cfg_parse_tuple,
169 				   cfg_print_tuple, cfg_doc_tuple,
170 				   &cfg_rep_tuple,  acl_fields };
171 
172 /*% remote servers, used for primaries and parental agents */
173 static cfg_tuplefielddef_t remotes_fields[] = {
174 	{ "name", &cfg_type_astring, 0 },
175 	{ "port", &cfg_type_optional_port, 0 },
176 	{ "dscp", &cfg_type_optional_dscp, 0 },
177 	{ "addresses", &cfg_type_bracketed_namesockaddrkeylist, 0 },
178 	{ NULL, NULL, 0 }
179 };
180 
181 static cfg_type_t cfg_type_remoteservers = { "remote-servers", cfg_parse_tuple,
182 					     cfg_print_tuple,  cfg_doc_tuple,
183 					     &cfg_rep_tuple,   remotes_fields };
184 
185 /*%
186  * "sockaddrkeylist", a list of socket addresses with optional keys
187  * and an optional default port, as used in the remote-servers option.
188  * E.g.,
189  *   "port 1234 { myservers; 10.0.0.1 key foo; 1::2 port 69; }"
190  */
191 
192 static cfg_tuplefielddef_t namesockaddrkey_fields[] = {
193 	{ "remoteselement", &cfg_type_remoteselement, 0 },
194 	{ "key", &cfg_type_optional_keyref, 0 },
195 	{ NULL, NULL, 0 },
196 };
197 
198 static cfg_type_t cfg_type_namesockaddrkey = {
199 	"namesockaddrkey", cfg_parse_tuple, cfg_print_tuple,
200 	cfg_doc_tuple,	   &cfg_rep_tuple,  namesockaddrkey_fields
201 };
202 
203 static cfg_type_t cfg_type_bracketed_namesockaddrkeylist = {
204 	"bracketed_namesockaddrkeylist",
205 	cfg_parse_bracketed_list,
206 	cfg_print_bracketed_list,
207 	cfg_doc_bracketed_list,
208 	&cfg_rep_list,
209 	&cfg_type_namesockaddrkey
210 };
211 
212 static cfg_tuplefielddef_t namesockaddrkeylist_fields[] = {
213 	{ "port", &cfg_type_optional_port, 0 },
214 	{ "dscp", &cfg_type_optional_dscp, 0 },
215 	{ "addresses", &cfg_type_bracketed_namesockaddrkeylist, 0 },
216 	{ NULL, NULL, 0 }
217 };
218 static cfg_type_t cfg_type_namesockaddrkeylist = {
219 	"sockaddrkeylist", cfg_parse_tuple, cfg_print_tuple,
220 	cfg_doc_tuple,	   &cfg_rep_tuple,  namesockaddrkeylist_fields
221 };
222 
223 /*%
224  * A list of socket addresses with an optional default port, as used
225  * in the 'listen-on' option.  E.g., "{ 10.0.0.1; 1::2 port 69; }"
226  */
227 static cfg_tuplefielddef_t portiplist_fields[] = {
228 	{ "port", &cfg_type_optional_port, 0 },
229 	{ "dscp", &cfg_type_optional_dscp, 0 },
230 	{ "addresses", &cfg_type_bracketed_dscpsockaddrlist, 0 },
231 	{ NULL, NULL, 0 }
232 };
233 static cfg_type_t cfg_type_portiplist = { "portiplist",	   cfg_parse_tuple,
234 					  cfg_print_tuple, cfg_doc_tuple,
235 					  &cfg_rep_tuple,  portiplist_fields };
236 
237 /*
238  * Obsolete format for the "pubkey" statement.
239  */
240 static cfg_tuplefielddef_t pubkey_fields[] = {
241 	{ "flags", &cfg_type_uint32, 0 },
242 	{ "protocol", &cfg_type_uint32, 0 },
243 	{ "algorithm", &cfg_type_uint32, 0 },
244 	{ "key", &cfg_type_qstring, 0 },
245 	{ NULL, NULL, 0 }
246 };
247 static cfg_type_t cfg_type_pubkey = { "pubkey",	       cfg_parse_tuple,
248 				      cfg_print_tuple, cfg_doc_tuple,
249 				      &cfg_rep_tuple,  pubkey_fields };
250 
251 /*%
252  * A list of RR types, used in grant statements.
253  * Note that the old parser allows quotes around the RR type names.
254  */
255 static cfg_type_t cfg_type_rrtypelist = {
256 	"rrtypelist",	  cfg_parse_spacelist, cfg_print_spacelist,
257 	cfg_doc_terminal, &cfg_rep_list,       &cfg_type_astring
258 };
259 
260 static const char *mode_enums[] = { "deny", "grant", NULL };
261 static cfg_type_t cfg_type_mode = {
262 	"mode",	      cfg_parse_enum,  cfg_print_ustring,
263 	cfg_doc_enum, &cfg_rep_string, &mode_enums
264 };
265 
266 static isc_result_t
267 parse_matchtype(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
268 	isc_result_t result;
269 
270 	CHECK(cfg_peektoken(pctx, 0));
271 	if (pctx->token.type == isc_tokentype_string &&
272 	    strcasecmp(TOKEN_STRING(pctx), "zonesub") == 0)
273 	{
274 		pctx->flags |= CFG_PCTX_SKIP;
275 	}
276 	return (cfg_parse_enum(pctx, type, ret));
277 
278 cleanup:
279 	return (result);
280 }
281 
282 static isc_result_t
283 parse_matchname(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
284 	isc_result_t result;
285 	cfg_obj_t *obj = NULL;
286 
287 	if ((pctx->flags & CFG_PCTX_SKIP) != 0) {
288 		pctx->flags &= ~CFG_PCTX_SKIP;
289 		CHECK(cfg_parse_void(pctx, NULL, &obj));
290 	} else {
291 		result = cfg_parse_astring(pctx, type, &obj);
292 	}
293 
294 	*ret = obj;
295 cleanup:
296 	return (result);
297 }
298 
299 static void
300 doc_matchname(cfg_printer_t *pctx, const cfg_type_t *type) {
301 	cfg_print_cstr(pctx, "[ ");
302 	cfg_doc_obj(pctx, type->of);
303 	cfg_print_cstr(pctx, " ]");
304 }
305 
306 static const char *matchtype_enums[] = { "6to4-self",
307 					 "external",
308 					 "krb5-self",
309 					 "krb5-selfsub",
310 					 "krb5-subdomain",
311 					 "ms-self",
312 					 "ms-selfsub",
313 					 "ms-subdomain",
314 					 "name",
315 					 "self",
316 					 "selfsub",
317 					 "selfwild",
318 					 "subdomain",
319 					 "tcp-self",
320 					 "wildcard",
321 					 "zonesub",
322 					 NULL };
323 
324 static cfg_type_t cfg_type_matchtype = { "matchtype",	    parse_matchtype,
325 					 cfg_print_ustring, cfg_doc_enum,
326 					 &cfg_rep_string,   &matchtype_enums };
327 
328 static cfg_type_t cfg_type_matchname = {
329 	"optional_matchname", parse_matchname, cfg_print_ustring,
330 	doc_matchname,	      &cfg_rep_tuple,  &cfg_type_ustring
331 };
332 
333 /*%
334  * A grant statement, used in the update policy.
335  */
336 static cfg_tuplefielddef_t grant_fields[] = {
337 	{ "mode", &cfg_type_mode, 0 },
338 	{ "identity", &cfg_type_astring, 0 }, /* domain name */
339 	{ "matchtype", &cfg_type_matchtype, 0 },
340 	{ "name", &cfg_type_matchname, 0 }, /* domain name */
341 	{ "types", &cfg_type_rrtypelist, 0 },
342 	{ NULL, NULL, 0 }
343 };
344 static cfg_type_t cfg_type_grant = { "grant",	      cfg_parse_tuple,
345 				     cfg_print_tuple, cfg_doc_tuple,
346 				     &cfg_rep_tuple,  grant_fields };
347 
348 static cfg_type_t cfg_type_updatepolicy = {
349 	"update_policy",  parse_updatepolicy, print_updatepolicy,
350 	doc_updatepolicy, &cfg_rep_list,      &cfg_type_grant
351 };
352 
353 static isc_result_t
354 parse_updatepolicy(cfg_parser_t *pctx, const cfg_type_t *type,
355 		   cfg_obj_t **ret) {
356 	isc_result_t result;
357 	CHECK(cfg_gettoken(pctx, 0));
358 	if (pctx->token.type == isc_tokentype_special &&
359 	    pctx->token.value.as_char == '{')
360 	{
361 		cfg_ungettoken(pctx);
362 		return (cfg_parse_bracketed_list(pctx, type, ret));
363 	}
364 
365 	if (pctx->token.type == isc_tokentype_string &&
366 	    strcasecmp(TOKEN_STRING(pctx), "local") == 0)
367 	{
368 		cfg_obj_t *obj = NULL;
369 		CHECK(cfg_create_obj(pctx, &cfg_type_ustring, &obj));
370 		obj->value.string.length = strlen("local");
371 		obj->value.string.base =
372 			isc_mem_get(pctx->mctx, obj->value.string.length + 1);
373 		memmove(obj->value.string.base, "local", 5);
374 		obj->value.string.base[5] = '\0';
375 		*ret = obj;
376 		return (ISC_R_SUCCESS);
377 	}
378 
379 	cfg_ungettoken(pctx);
380 	return (ISC_R_UNEXPECTEDTOKEN);
381 
382 cleanup:
383 	return (result);
384 }
385 
386 static void
387 print_updatepolicy(cfg_printer_t *pctx, const cfg_obj_t *obj) {
388 	if (cfg_obj_isstring(obj)) {
389 		cfg_print_ustring(pctx, obj);
390 	} else {
391 		cfg_print_bracketed_list(pctx, obj);
392 	}
393 }
394 
395 static void
396 doc_updatepolicy(cfg_printer_t *pctx, const cfg_type_t *type) {
397 	cfg_print_cstr(pctx, "( local | { ");
398 	cfg_doc_obj(pctx, type->of);
399 	cfg_print_cstr(pctx, "; ... }");
400 }
401 
402 /*%
403  * A view statement.
404  */
405 static cfg_tuplefielddef_t view_fields[] = {
406 	{ "name", &cfg_type_astring, 0 },
407 	{ "class", &cfg_type_optional_class, 0 },
408 	{ "options", &cfg_type_viewopts, 0 },
409 	{ NULL, NULL, 0 }
410 };
411 static cfg_type_t cfg_type_view = { "view",	     cfg_parse_tuple,
412 				    cfg_print_tuple, cfg_doc_tuple,
413 				    &cfg_rep_tuple,  view_fields };
414 
415 /*%
416  * A zone statement.
417  */
418 static cfg_tuplefielddef_t zone_fields[] = {
419 	{ "name", &cfg_type_astring, 0 },
420 	{ "class", &cfg_type_optional_class, 0 },
421 	{ "options", &cfg_type_zoneopts, 0 },
422 	{ NULL, NULL, 0 }
423 };
424 static cfg_type_t cfg_type_zone = { "zone",	     cfg_parse_tuple,
425 				    cfg_print_tuple, cfg_doc_tuple,
426 				    &cfg_rep_tuple,  zone_fields };
427 
428 /*%
429  * A dnssec-policy statement.
430  */
431 static cfg_tuplefielddef_t dnssecpolicy_fields[] = {
432 	{ "name", &cfg_type_astring, 0 },
433 	{ "options", &cfg_type_dnssecpolicyopts, 0 },
434 	{ NULL, NULL, 0 }
435 };
436 
437 static cfg_type_t cfg_type_dnssecpolicy = {
438 	"dnssec-policy", cfg_parse_tuple, cfg_print_tuple,
439 	cfg_doc_tuple,	 &cfg_rep_tuple,  dnssecpolicy_fields
440 };
441 
442 /*%
443  * A "category" clause in the "logging" statement.
444  */
445 static cfg_tuplefielddef_t category_fields[] = {
446 	{ "name", &cfg_type_astring, 0 },
447 	{ "destinations", &cfg_type_destinationlist, 0 },
448 	{ NULL, NULL, 0 }
449 };
450 static cfg_type_t cfg_type_category = { "category",	 cfg_parse_tuple,
451 					cfg_print_tuple, cfg_doc_tuple,
452 					&cfg_rep_tuple,	 category_fields };
453 
454 static isc_result_t
455 parse_maxduration(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
456 	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_duration, ret));
457 }
458 
459 static void
460 doc_maxduration(cfg_printer_t *pctx, const cfg_type_t *type) {
461 	cfg_doc_enum_or_other(pctx, type, &cfg_type_duration);
462 }
463 
464 /*%
465  * A duration or "unlimited", but not "default".
466  */
467 static const char *maxduration_enums[] = { "unlimited", NULL };
468 static cfg_type_t cfg_type_maxduration = {
469 	"maxduration_no_default", parse_maxduration, cfg_print_ustring,
470 	doc_maxduration,	  &cfg_rep_duration, maxduration_enums
471 };
472 
473 /*%
474  * A dnssec key, as used in the "trusted-keys" statement.
475  */
476 static cfg_tuplefielddef_t dnsseckey_fields[] = {
477 	{ "name", &cfg_type_astring, 0 },
478 	{ "anchortype", &cfg_type_void, 0 },
479 	{ "rdata1", &cfg_type_uint32, 0 },
480 	{ "rdata2", &cfg_type_uint32, 0 },
481 	{ "rdata3", &cfg_type_uint32, 0 },
482 	{ "data", &cfg_type_qstring, 0 },
483 	{ NULL, NULL, 0 }
484 };
485 static cfg_type_t cfg_type_dnsseckey = { "dnsseckey",	  cfg_parse_tuple,
486 					 cfg_print_tuple, cfg_doc_tuple,
487 					 &cfg_rep_tuple,  dnsseckey_fields };
488 
489 /*%
490  * Optional enums.
491  *
492  */
493 static isc_result_t
494 parse_optional_enum(cfg_parser_t *pctx, const cfg_type_t *type,
495 		    cfg_obj_t **ret) {
496 	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_void, ret));
497 }
498 
499 static void
500 doc_optional_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
501 	UNUSED(type);
502 	cfg_print_cstr(pctx, "[ ");
503 	cfg_doc_enum(pctx, type);
504 	cfg_print_cstr(pctx, " ]");
505 }
506 
507 /*%
508  * A key initialization specifier, as used in the
509  * "trust-anchors" (or synonymous "managed-keys") statement.
510  */
511 static const char *anchortype_enums[] = { "static-key", "initial-key",
512 					  "static-ds", "initial-ds", NULL };
513 static cfg_type_t cfg_type_anchortype = { "anchortype",	     cfg_parse_enum,
514 					  cfg_print_ustring, cfg_doc_enum,
515 					  &cfg_rep_string,   anchortype_enums };
516 static cfg_tuplefielddef_t managedkey_fields[] = {
517 	{ "name", &cfg_type_astring, 0 },
518 	{ "anchortype", &cfg_type_anchortype, 0 },
519 	{ "rdata1", &cfg_type_uint32, 0 },
520 	{ "rdata2", &cfg_type_uint32, 0 },
521 	{ "rdata3", &cfg_type_uint32, 0 },
522 	{ "data", &cfg_type_qstring, 0 },
523 	{ NULL, NULL, 0 }
524 };
525 static cfg_type_t cfg_type_managedkey = { "managedkey",	   cfg_parse_tuple,
526 					  cfg_print_tuple, cfg_doc_tuple,
527 					  &cfg_rep_tuple,  managedkey_fields };
528 
529 /*%
530  * DNSSEC key roles.
531  */
532 static const char *dnsseckeyrole_enums[] = { "csk", "ksk", "zsk", NULL };
533 static cfg_type_t cfg_type_dnsseckeyrole = {
534 	"dnssec-key-role", cfg_parse_enum,  cfg_print_ustring,
535 	cfg_doc_enum,	   &cfg_rep_string, &dnsseckeyrole_enums
536 };
537 
538 /*%
539  * DNSSEC key storage types.
540  */
541 static const char *dnsseckeystore_enums[] = { "key-directory", NULL };
542 static cfg_type_t cfg_type_dnsseckeystore = {
543 	"dnssec-key-storage", parse_optional_enum, cfg_print_ustring,
544 	doc_optional_enum,    &cfg_rep_string,	   dnsseckeystore_enums
545 };
546 
547 /*%
548  * A dnssec key, as used in the "keys" statement in a "dnssec-policy".
549  */
550 static keyword_type_t algorithm_kw = { "algorithm", &cfg_type_ustring };
551 static cfg_type_t cfg_type_algorithm = { "algorithm",	  parse_keyvalue,
552 					 print_keyvalue,  doc_keyvalue,
553 					 &cfg_rep_string, &algorithm_kw };
554 
555 static keyword_type_t lifetime_kw = { "lifetime",
556 				      &cfg_type_duration_or_unlimited };
557 static cfg_type_t cfg_type_lifetime = { "lifetime",	   parse_keyvalue,
558 					print_keyvalue,	   doc_keyvalue,
559 					&cfg_rep_duration, &lifetime_kw };
560 
561 static cfg_tuplefielddef_t kaspkey_fields[] = {
562 	{ "role", &cfg_type_dnsseckeyrole, 0 },
563 	{ "keystore-type", &cfg_type_dnsseckeystore, 0 },
564 	{ "lifetime", &cfg_type_lifetime, 0 },
565 	{ "algorithm", &cfg_type_algorithm, 0 },
566 	{ "length", &cfg_type_optional_uint32, 0 },
567 	{ NULL, NULL, 0 }
568 };
569 static cfg_type_t cfg_type_kaspkey = { "kaspkey",	cfg_parse_tuple,
570 				       cfg_print_tuple, cfg_doc_tuple,
571 				       &cfg_rep_tuple,	kaspkey_fields };
572 
573 /*%
574  * NSEC3 parameters.
575  */
576 static keyword_type_t nsec3iter_kw = { "iterations", &cfg_type_uint32 };
577 static cfg_type_t cfg_type_nsec3iter = {
578 	"iterations",	       parse_optional_keyvalue, print_keyvalue,
579 	doc_optional_keyvalue, &cfg_rep_uint32,		&nsec3iter_kw
580 };
581 
582 static keyword_type_t nsec3optout_kw = { "optout", &cfg_type_boolean };
583 static cfg_type_t cfg_type_nsec3optout = {
584 	"optout",	  parse_optional_keyvalue,
585 	print_keyvalue,	  doc_optional_keyvalue,
586 	&cfg_rep_boolean, &nsec3optout_kw
587 };
588 
589 static keyword_type_t nsec3salt_kw = { "salt-length", &cfg_type_uint32 };
590 static cfg_type_t cfg_type_nsec3salt = {
591 	"salt-length",	       parse_optional_keyvalue, print_keyvalue,
592 	doc_optional_keyvalue, &cfg_rep_uint32,		&nsec3salt_kw
593 };
594 
595 static cfg_tuplefielddef_t nsec3param_fields[] = {
596 	{ "iterations", &cfg_type_nsec3iter, 0 },
597 	{ "optout", &cfg_type_nsec3optout, 0 },
598 	{ "salt-length", &cfg_type_nsec3salt, 0 },
599 	{ NULL, NULL, 0 }
600 };
601 
602 static cfg_type_t cfg_type_nsec3 = { "nsec3param",    cfg_parse_tuple,
603 				     cfg_print_tuple, cfg_doc_tuple,
604 				     &cfg_rep_tuple,  nsec3param_fields };
605 
606 /*%
607  * Wild class, type, name.
608  */
609 static keyword_type_t wild_class_kw = { "class", &cfg_type_ustring };
610 
611 static cfg_type_t cfg_type_optional_wild_class = {
612 	"optional_wild_class", parse_optional_keyvalue, print_keyvalue,
613 	doc_optional_keyvalue, &cfg_rep_string,		&wild_class_kw
614 };
615 
616 static keyword_type_t wild_type_kw = { "type", &cfg_type_ustring };
617 
618 static cfg_type_t cfg_type_optional_wild_type = {
619 	"optional_wild_type",  parse_optional_keyvalue, print_keyvalue,
620 	doc_optional_keyvalue, &cfg_rep_string,		&wild_type_kw
621 };
622 
623 static keyword_type_t wild_name_kw = { "name", &cfg_type_qstring };
624 
625 static cfg_type_t cfg_type_optional_wild_name = {
626 	"optional_wild_name",  parse_optional_keyvalue, print_keyvalue,
627 	doc_optional_keyvalue, &cfg_rep_string,		&wild_name_kw
628 };
629 
630 /*%
631  * An rrset ordering element.
632  */
633 static cfg_tuplefielddef_t rrsetorderingelement_fields[] = {
634 	{ "class", &cfg_type_optional_wild_class, 0 },
635 	{ "type", &cfg_type_optional_wild_type, 0 },
636 	{ "name", &cfg_type_optional_wild_name, 0 },
637 	{ "order", &cfg_type_ustring, 0 }, /* must be literal "order" */
638 	{ "ordering", &cfg_type_ustring, 0 },
639 	{ NULL, NULL, 0 }
640 };
641 static cfg_type_t cfg_type_rrsetorderingelement = {
642 	"rrsetorderingelement", cfg_parse_tuple, cfg_print_tuple,
643 	cfg_doc_tuple,		&cfg_rep_tuple,	 rrsetorderingelement_fields
644 };
645 
646 /*%
647  * A global or view "check-names" option.  Note that the zone
648  * "check-names" option has a different syntax.
649  */
650 
651 static const char *checktype_enums[] = { "primary", "master",	"secondary",
652 					 "slave",   "response", NULL };
653 static cfg_type_t cfg_type_checktype = { "checktype",	    cfg_parse_enum,
654 					 cfg_print_ustring, cfg_doc_enum,
655 					 &cfg_rep_string,   &checktype_enums };
656 
657 static const char *checkmode_enums[] = { "fail", "warn", "ignore", NULL };
658 static cfg_type_t cfg_type_checkmode = { "checkmode",	    cfg_parse_enum,
659 					 cfg_print_ustring, cfg_doc_enum,
660 					 &cfg_rep_string,   &checkmode_enums };
661 
662 static const char *warn_enums[] = { "warn", "ignore", NULL };
663 static cfg_type_t cfg_type_warn = {
664 	"warn",	      cfg_parse_enum,  cfg_print_ustring,
665 	cfg_doc_enum, &cfg_rep_string, &warn_enums
666 };
667 
668 static cfg_tuplefielddef_t checknames_fields[] = {
669 	{ "type", &cfg_type_checktype, 0 },
670 	{ "mode", &cfg_type_checkmode, 0 },
671 	{ NULL, NULL, 0 }
672 };
673 
674 static cfg_type_t cfg_type_checknames = { "checknames",	   cfg_parse_tuple,
675 					  cfg_print_tuple, cfg_doc_tuple,
676 					  &cfg_rep_tuple,  checknames_fields };
677 
678 static cfg_type_t cfg_type_bracketed_dscpsockaddrlist = {
679 	"bracketed_sockaddrlist",
680 	cfg_parse_bracketed_list,
681 	cfg_print_bracketed_list,
682 	cfg_doc_bracketed_list,
683 	&cfg_rep_list,
684 	&cfg_type_sockaddrdscp
685 };
686 
687 static cfg_type_t cfg_type_bracketed_netaddrlist = { "bracketed_netaddrlist",
688 						     cfg_parse_bracketed_list,
689 						     cfg_print_bracketed_list,
690 						     cfg_doc_bracketed_list,
691 						     &cfg_rep_list,
692 						     &cfg_type_netaddr };
693 
694 static const char *autodnssec_enums[] = { "allow", "maintain", "off", NULL };
695 static cfg_type_t cfg_type_autodnssec = {
696 	"autodnssec", cfg_parse_enum,  cfg_print_ustring,
697 	cfg_doc_enum, &cfg_rep_string, &autodnssec_enums
698 };
699 
700 static const char *dnssecupdatemode_enums[] = { "maintain", "no-resign", NULL };
701 static cfg_type_t cfg_type_dnssecupdatemode = {
702 	"dnssecupdatemode", cfg_parse_enum,  cfg_print_ustring,
703 	cfg_doc_enum,	    &cfg_rep_string, &dnssecupdatemode_enums
704 };
705 
706 static const char *updatemethods_enums[] = { "date", "increment", "unixtime",
707 					     NULL };
708 static cfg_type_t cfg_type_updatemethod = {
709 	"updatemethod", cfg_parse_enum,	 cfg_print_ustring,
710 	cfg_doc_enum,	&cfg_rep_string, &updatemethods_enums
711 };
712 
713 /*
714  * zone-statistics: full, terse, or none.
715  *
716  * for backward compatibility, we also support boolean values.
717  * yes represents "full", no represents "terse". in the future we
718  * may change no to mean "none".
719  */
720 static const char *zonestat_enums[] = { "full", "terse", "none", NULL };
721 static isc_result_t
722 parse_zonestat(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
723 	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
724 }
725 static void
726 doc_zonestat(cfg_printer_t *pctx, const cfg_type_t *type) {
727 	cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
728 }
729 static cfg_type_t cfg_type_zonestat = { "zonestat",	   parse_zonestat,
730 					cfg_print_ustring, doc_zonestat,
731 					&cfg_rep_string,   zonestat_enums };
732 
733 static cfg_type_t cfg_type_rrsetorder = { "rrsetorder",
734 					  cfg_parse_bracketed_list,
735 					  cfg_print_bracketed_list,
736 					  cfg_doc_bracketed_list,
737 					  &cfg_rep_list,
738 					  &cfg_type_rrsetorderingelement };
739 
740 static keyword_type_t dscp_kw = { "dscp", &cfg_type_uint32 };
741 
742 static cfg_type_t cfg_type_optional_dscp = {
743 	"optional_dscp",       parse_optional_keyvalue, print_keyvalue,
744 	doc_optional_keyvalue, &cfg_rep_uint32,		&dscp_kw
745 };
746 
747 static keyword_type_t port_kw = { "port", &cfg_type_uint32 };
748 
749 static cfg_type_t cfg_type_optional_port = {
750 	"optional_port",       parse_optional_keyvalue, print_keyvalue,
751 	doc_optional_keyvalue, &cfg_rep_uint32,		&port_kw
752 };
753 
754 /*% A list of keys, as in the "key" clause of the controls statement. */
755 static cfg_type_t cfg_type_keylist = { "keylist",
756 				       cfg_parse_bracketed_list,
757 				       cfg_print_bracketed_list,
758 				       cfg_doc_bracketed_list,
759 				       &cfg_rep_list,
760 				       &cfg_type_astring };
761 
762 /*% A list of dnssec keys, as in "trusted-keys". Deprecated. */
763 static cfg_type_t cfg_type_trustedkeys = { "trustedkeys",
764 					   cfg_parse_bracketed_list,
765 					   cfg_print_bracketed_list,
766 					   cfg_doc_bracketed_list,
767 					   &cfg_rep_list,
768 					   &cfg_type_dnsseckey };
769 
770 /*%
771  * A list of managed trust anchors.  Each entry contains a name, a keyword
772  * ("static-key", initial-key", "static-ds" or "initial-ds"), and the
773  * fields associated with either a DNSKEY or a DS record.
774  */
775 static cfg_type_t cfg_type_dnsseckeys = { "dnsseckeys",
776 					  cfg_parse_bracketed_list,
777 					  cfg_print_bracketed_list,
778 					  cfg_doc_bracketed_list,
779 					  &cfg_rep_list,
780 					  &cfg_type_managedkey };
781 
782 /*%
783  * A list of key entries, used in a DNSSEC Key and Signing Policy.
784  */
785 static cfg_type_t cfg_type_kaspkeys = { "kaspkeys",
786 					cfg_parse_bracketed_list,
787 					cfg_print_bracketed_list,
788 					cfg_doc_bracketed_list,
789 					&cfg_rep_list,
790 					&cfg_type_kaspkey };
791 
792 static const char *forwardtype_enums[] = { "first", "only", NULL };
793 static cfg_type_t cfg_type_forwardtype = {
794 	"forwardtype", cfg_parse_enum,	cfg_print_ustring,
795 	cfg_doc_enum,  &cfg_rep_string, &forwardtype_enums
796 };
797 
798 static const char *zonetype_enums[] = {
799 	"primary",  "master",	       "secondary", "slave",
800 	"mirror",   "delegation-only", "forward",   "hint",
801 	"redirect", "static-stub",     "stub",	    NULL
802 };
803 static cfg_type_t cfg_type_zonetype = { "zonetype",	   cfg_parse_enum,
804 					cfg_print_ustring, cfg_doc_enum,
805 					&cfg_rep_string,   &zonetype_enums };
806 
807 static const char *loglevel_enums[] = { "critical", "error", "warning",
808 					"notice",   "info",  "dynamic",
809 					NULL };
810 static cfg_type_t cfg_type_loglevel = { "loglevel",	   cfg_parse_enum,
811 					cfg_print_ustring, cfg_doc_enum,
812 					&cfg_rep_string,   &loglevel_enums };
813 
814 static const char *transferformat_enums[] = { "many-answers", "one-answer",
815 					      NULL };
816 static cfg_type_t cfg_type_transferformat = {
817 	"transferformat", cfg_parse_enum,  cfg_print_ustring,
818 	cfg_doc_enum,	  &cfg_rep_string, &transferformat_enums
819 };
820 
821 /*%
822  * The special keyword "none", as used in the pid-file option.
823  */
824 
825 static void
826 print_none(cfg_printer_t *pctx, const cfg_obj_t *obj) {
827 	UNUSED(obj);
828 	cfg_print_cstr(pctx, "none");
829 }
830 
831 static cfg_type_t cfg_type_none = { "none", NULL,	   print_none,
832 				    NULL,   &cfg_rep_void, NULL };
833 
834 /*%
835  * A quoted string or the special keyword "none".  Used in the pid-file option.
836  */
837 static isc_result_t
838 parse_qstringornone(cfg_parser_t *pctx, const cfg_type_t *type,
839 		    cfg_obj_t **ret) {
840 	isc_result_t result;
841 
842 	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
843 	if (pctx->token.type == isc_tokentype_string &&
844 	    strcasecmp(TOKEN_STRING(pctx), "none") == 0)
845 	{
846 		return (cfg_create_obj(pctx, &cfg_type_none, ret));
847 	}
848 	cfg_ungettoken(pctx);
849 	return (cfg_parse_qstring(pctx, type, ret));
850 cleanup:
851 	return (result);
852 }
853 
854 static void
855 doc_qstringornone(cfg_printer_t *pctx, const cfg_type_t *type) {
856 	UNUSED(type);
857 	cfg_print_cstr(pctx, "( <quoted_string> | none )");
858 }
859 
860 static cfg_type_t cfg_type_qstringornone = { "qstringornone",
861 					     parse_qstringornone,
862 					     NULL,
863 					     doc_qstringornone,
864 					     NULL,
865 					     NULL };
866 
867 /*%
868  * A boolean ("yes" or "no"), or the special keyword "auto".
869  * Used in the dnssec-validation option.
870  */
871 static void
872 print_auto(cfg_printer_t *pctx, const cfg_obj_t *obj) {
873 	UNUSED(obj);
874 	cfg_print_cstr(pctx, "auto");
875 }
876 
877 static cfg_type_t cfg_type_auto = { "auto", NULL,	   print_auto,
878 				    NULL,   &cfg_rep_void, NULL };
879 
880 static isc_result_t
881 parse_boolorauto(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
882 	isc_result_t result;
883 
884 	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
885 	if (pctx->token.type == isc_tokentype_string &&
886 	    strcasecmp(TOKEN_STRING(pctx), "auto") == 0)
887 	{
888 		return (cfg_create_obj(pctx, &cfg_type_auto, ret));
889 	}
890 	cfg_ungettoken(pctx);
891 	return (cfg_parse_boolean(pctx, type, ret));
892 cleanup:
893 	return (result);
894 }
895 
896 static void
897 print_boolorauto(cfg_printer_t *pctx, const cfg_obj_t *obj) {
898 	if (obj->type->rep == &cfg_rep_void) {
899 		cfg_print_cstr(pctx, "auto");
900 	} else if (obj->value.boolean) {
901 		cfg_print_cstr(pctx, "yes");
902 	} else {
903 		cfg_print_cstr(pctx, "no");
904 	}
905 }
906 
907 static void
908 doc_boolorauto(cfg_printer_t *pctx, const cfg_type_t *type) {
909 	UNUSED(type);
910 	cfg_print_cstr(pctx, "( yes | no | auto )");
911 }
912 
913 static cfg_type_t cfg_type_boolorauto = {
914 	"boolorauto", parse_boolorauto, print_boolorauto, doc_boolorauto, NULL,
915 	NULL
916 };
917 
918 /*%
919  * keyword hostname
920  */
921 static void
922 print_hostname(cfg_printer_t *pctx, const cfg_obj_t *obj) {
923 	UNUSED(obj);
924 	cfg_print_cstr(pctx, "hostname");
925 }
926 
927 static cfg_type_t cfg_type_hostname = { "hostname",	  NULL,
928 					print_hostname,	  NULL,
929 					&cfg_rep_boolean, NULL };
930 
931 /*%
932  * "server-id" argument.
933  */
934 
935 static isc_result_t
936 parse_serverid(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
937 	isc_result_t result;
938 	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
939 	if (pctx->token.type == isc_tokentype_string &&
940 	    strcasecmp(TOKEN_STRING(pctx), "none") == 0)
941 	{
942 		return (cfg_create_obj(pctx, &cfg_type_none, ret));
943 	}
944 	if (pctx->token.type == isc_tokentype_string &&
945 	    strcasecmp(TOKEN_STRING(pctx), "hostname") == 0)
946 	{
947 		result = cfg_create_obj(pctx, &cfg_type_hostname, ret);
948 		if (result == ISC_R_SUCCESS) {
949 			(*ret)->value.boolean = true;
950 		}
951 		return (result);
952 	}
953 	cfg_ungettoken(pctx);
954 	return (cfg_parse_qstring(pctx, type, ret));
955 cleanup:
956 	return (result);
957 }
958 
959 static void
960 doc_serverid(cfg_printer_t *pctx, const cfg_type_t *type) {
961 	UNUSED(type);
962 	cfg_print_cstr(pctx, "( <quoted_string> | none | hostname )");
963 }
964 
965 static cfg_type_t cfg_type_serverid = { "serverid",   parse_serverid, NULL,
966 					doc_serverid, NULL,	      NULL };
967 
968 /*%
969  * Port list.
970  */
971 static void
972 print_porttuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
973 	cfg_print_cstr(pctx, "range ");
974 	cfg_print_tuple(pctx, obj);
975 }
976 static cfg_tuplefielddef_t porttuple_fields[] = {
977 	{ "loport", &cfg_type_uint32, 0 },
978 	{ "hiport", &cfg_type_uint32, 0 },
979 	{ NULL, NULL, 0 }
980 };
981 static cfg_type_t cfg_type_porttuple = { "porttuple",	  cfg_parse_tuple,
982 					 print_porttuple, cfg_doc_tuple,
983 					 &cfg_rep_tuple,  porttuple_fields };
984 
985 static isc_result_t
986 parse_port(cfg_parser_t *pctx, cfg_obj_t **ret) {
987 	isc_result_t result;
988 
989 	CHECK(cfg_parse_uint32(pctx, NULL, ret));
990 	if ((*ret)->value.uint32 > 0xffff) {
991 		cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid port");
992 		cfg_obj_destroy(pctx, ret);
993 		result = ISC_R_RANGE;
994 	}
995 
996 cleanup:
997 	return (result);
998 }
999 
1000 static isc_result_t
1001 parse_portrange(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1002 	isc_result_t result;
1003 	cfg_obj_t *obj = NULL;
1004 
1005 	UNUSED(type);
1006 
1007 	CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
1008 	if (pctx->token.type == isc_tokentype_number) {
1009 		CHECK(parse_port(pctx, ret));
1010 	} else {
1011 		CHECK(cfg_gettoken(pctx, 0));
1012 		if (pctx->token.type != isc_tokentype_string ||
1013 		    strcasecmp(TOKEN_STRING(pctx), "range") != 0)
1014 		{
1015 			cfg_parser_error(pctx, CFG_LOG_NEAR,
1016 					 "expected integer or 'range'");
1017 			return (ISC_R_UNEXPECTEDTOKEN);
1018 		}
1019 		CHECK(cfg_create_tuple(pctx, &cfg_type_porttuple, &obj));
1020 		CHECK(parse_port(pctx, &obj->value.tuple[0]));
1021 		CHECK(parse_port(pctx, &obj->value.tuple[1]));
1022 		if (obj->value.tuple[0]->value.uint32 >
1023 		    obj->value.tuple[1]->value.uint32) {
1024 			cfg_parser_error(pctx, CFG_LOG_NOPREP,
1025 					 "low port '%u' must not be larger "
1026 					 "than high port",
1027 					 obj->value.tuple[0]->value.uint32);
1028 			result = ISC_R_RANGE;
1029 			goto cleanup;
1030 		}
1031 		*ret = obj;
1032 		obj = NULL;
1033 	}
1034 
1035 cleanup:
1036 	if (obj != NULL) {
1037 		cfg_obj_destroy(pctx, &obj);
1038 	}
1039 	return (result);
1040 }
1041 
1042 static cfg_type_t cfg_type_portrange = { "portrange", parse_portrange,
1043 					 NULL,	      cfg_doc_terminal,
1044 					 NULL,	      NULL };
1045 
1046 static cfg_type_t cfg_type_bracketed_portlist = { "bracketed_sockaddrlist",
1047 						  cfg_parse_bracketed_list,
1048 						  cfg_print_bracketed_list,
1049 						  cfg_doc_bracketed_list,
1050 						  &cfg_rep_list,
1051 						  &cfg_type_portrange };
1052 
1053 static const char *cookiealg_enums[] = { "aes", "siphash24", NULL };
1054 static cfg_type_t cfg_type_cookiealg = { "cookiealg",	    cfg_parse_enum,
1055 					 cfg_print_ustring, cfg_doc_enum,
1056 					 &cfg_rep_string,   &cookiealg_enums };
1057 
1058 /*%
1059  * fetch-quota-params
1060  */
1061 
1062 static cfg_tuplefielddef_t fetchquota_fields[] = {
1063 	{ "frequency", &cfg_type_uint32, 0 },
1064 	{ "low", &cfg_type_fixedpoint, 0 },
1065 	{ "high", &cfg_type_fixedpoint, 0 },
1066 	{ "discount", &cfg_type_fixedpoint, 0 },
1067 	{ NULL, NULL, 0 }
1068 };
1069 
1070 static cfg_type_t cfg_type_fetchquota = { "fetchquota",	   cfg_parse_tuple,
1071 					  cfg_print_tuple, cfg_doc_tuple,
1072 					  &cfg_rep_tuple,  fetchquota_fields };
1073 
1074 /*%
1075  * fetches-per-server or fetches-per-zone
1076  */
1077 
1078 static const char *response_enums[] = { "drop", "fail", NULL };
1079 
1080 static cfg_type_t cfg_type_responsetype = {
1081 	"responsetype",	   parse_optional_enum, cfg_print_ustring,
1082 	doc_optional_enum, &cfg_rep_string,	response_enums
1083 };
1084 
1085 static cfg_tuplefielddef_t fetchesper_fields[] = {
1086 	{ "fetches", &cfg_type_uint32, 0 },
1087 	{ "response", &cfg_type_responsetype, 0 },
1088 	{ NULL, NULL, 0 }
1089 };
1090 
1091 static cfg_type_t cfg_type_fetchesper = { "fetchesper",	   cfg_parse_tuple,
1092 					  cfg_print_tuple, cfg_doc_tuple,
1093 					  &cfg_rep_tuple,  fetchesper_fields };
1094 
1095 /*%
1096  * Clauses that can be found within the top level of the named.conf
1097  * file only.
1098  */
1099 static cfg_clausedef_t namedconf_clauses[] = {
1100 	{ "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI },
1101 	{ "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI },
1102 	{ "dnssec-policy", &cfg_type_dnssecpolicy, CFG_CLAUSEFLAG_MULTI },
1103 	{ "logging", &cfg_type_logging, 0 },
1104 	{ "lwres", &cfg_type_bracketed_text,
1105 	  CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_OBSOLETE },
1106 	{ "masters", &cfg_type_remoteservers, CFG_CLAUSEFLAG_MULTI },
1107 	{ "options", &cfg_type_options, 0 },
1108 	{ "parental-agents", &cfg_type_remoteservers, CFG_CLAUSEFLAG_MULTI },
1109 	{ "primaries", &cfg_type_remoteservers, CFG_CLAUSEFLAG_MULTI },
1110 	{ "statistics-channels", &cfg_type_statschannels,
1111 	  CFG_CLAUSEFLAG_MULTI },
1112 	{ "view", &cfg_type_view, CFG_CLAUSEFLAG_MULTI },
1113 	{ NULL, NULL, 0 }
1114 };
1115 
1116 /*%
1117  * Clauses that can occur at the top level or in the view
1118  * statement, but not in the options block.
1119  */
1120 static cfg_clausedef_t namedconf_or_view_clauses[] = {
1121 	{ "dlz", &cfg_type_dlz, CFG_CLAUSEFLAG_MULTI },
1122 	{ "dyndb", &cfg_type_dyndb, CFG_CLAUSEFLAG_MULTI },
1123 	{ "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
1124 	{ "managed-keys", &cfg_type_dnsseckeys,
1125 	  CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_DEPRECATED },
1126 	{ "plugin", &cfg_type_plugin, CFG_CLAUSEFLAG_MULTI },
1127 	{ "server", &cfg_type_server, CFG_CLAUSEFLAG_MULTI },
1128 	{ "trust-anchors", &cfg_type_dnsseckeys, CFG_CLAUSEFLAG_MULTI },
1129 	{ "trusted-keys", &cfg_type_trustedkeys,
1130 	  CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_DEPRECATED },
1131 	{ "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI },
1132 	{ NULL, NULL, 0 }
1133 };
1134 
1135 /*%
1136  * Clauses that can occur in the bind.keys file.
1137  */
1138 static cfg_clausedef_t bindkeys_clauses[] = {
1139 	{ "managed-keys", &cfg_type_dnsseckeys,
1140 	  CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_DEPRECATED },
1141 	{ "trust-anchors", &cfg_type_dnsseckeys, CFG_CLAUSEFLAG_MULTI },
1142 	{ "trusted-keys", &cfg_type_trustedkeys,
1143 	  CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_DEPRECATED },
1144 	{ NULL, NULL, 0 }
1145 };
1146 
1147 static const char *fstrm_model_enums[] = { "mpsc", "spsc", NULL };
1148 static cfg_type_t cfg_type_fstrm_model = {
1149 	"model",      cfg_parse_enum,  cfg_print_ustring,
1150 	cfg_doc_enum, &cfg_rep_string, &fstrm_model_enums
1151 };
1152 
1153 /*%
1154  * Clauses that can be found within the 'options' statement.
1155  */
1156 static cfg_clausedef_t options_clauses[] = {
1157 	{ "answer-cookie", &cfg_type_boolean, 0 },
1158 	{ "automatic-interface-scan", &cfg_type_boolean, 0 },
1159 	{ "avoid-v4-udp-ports", &cfg_type_bracketed_portlist, 0 },
1160 	{ "avoid-v6-udp-ports", &cfg_type_bracketed_portlist, 0 },
1161 	{ "bindkeys-file", &cfg_type_qstring, 0 },
1162 	{ "blackhole", &cfg_type_bracketed_aml, 0 },
1163 	{ "cookie-algorithm", &cfg_type_cookiealg, 0 },
1164 	{ "cookie-secret", &cfg_type_sstring, CFG_CLAUSEFLAG_MULTI },
1165 	{ "coresize", &cfg_type_size, 0 },
1166 	{ "datasize", &cfg_type_size, 0 },
1167 	{ "deallocate-on-exit", &cfg_type_boolean, CFG_CLAUSEFLAG_ANCIENT },
1168 	{ "directory", &cfg_type_qstring, CFG_CLAUSEFLAG_CALLBACK },
1169 #ifdef HAVE_DNSTAP
1170 	{ "dnstap-output", &cfg_type_dnstapoutput, 0 },
1171 	{ "dnstap-identity", &cfg_type_serverid, 0 },
1172 	{ "dnstap-version", &cfg_type_qstringornone, 0 },
1173 #else  /* ifdef HAVE_DNSTAP */
1174 	{ "dnstap-output", &cfg_type_dnstapoutput,
1175 	  CFG_CLAUSEFLAG_NOTCONFIGURED },
1176 	{ "dnstap-identity", &cfg_type_serverid, CFG_CLAUSEFLAG_NOTCONFIGURED },
1177 	{ "dnstap-version", &cfg_type_qstringornone,
1178 	  CFG_CLAUSEFLAG_NOTCONFIGURED },
1179 #endif /* ifdef HAVE_DNSTAP */
1180 	{ "dscp", &cfg_type_uint32, 0 },
1181 	{ "dump-file", &cfg_type_qstring, 0 },
1182 	{ "fake-iquery", &cfg_type_boolean, CFG_CLAUSEFLAG_ANCIENT },
1183 	{ "files", &cfg_type_size, 0 },
1184 	{ "flush-zones-on-shutdown", &cfg_type_boolean, 0 },
1185 #ifdef HAVE_DNSTAP
1186 	{ "fstrm-set-buffer-hint", &cfg_type_uint32, 0 },
1187 	{ "fstrm-set-flush-timeout", &cfg_type_uint32, 0 },
1188 	{ "fstrm-set-input-queue-size", &cfg_type_uint32, 0 },
1189 	{ "fstrm-set-output-notify-threshold", &cfg_type_uint32, 0 },
1190 	{ "fstrm-set-output-queue-model", &cfg_type_fstrm_model, 0 },
1191 	{ "fstrm-set-output-queue-size", &cfg_type_uint32, 0 },
1192 	{ "fstrm-set-reopen-interval", &cfg_type_duration, 0 },
1193 #else  /* ifdef HAVE_DNSTAP */
1194 	{ "fstrm-set-buffer-hint", &cfg_type_uint32,
1195 	  CFG_CLAUSEFLAG_NOTCONFIGURED },
1196 	{ "fstrm-set-flush-timeout", &cfg_type_uint32,
1197 	  CFG_CLAUSEFLAG_NOTCONFIGURED },
1198 	{ "fstrm-set-input-queue-size", &cfg_type_uint32,
1199 	  CFG_CLAUSEFLAG_NOTCONFIGURED },
1200 	{ "fstrm-set-output-notify-threshold", &cfg_type_uint32,
1201 	  CFG_CLAUSEFLAG_NOTCONFIGURED },
1202 	{ "fstrm-set-output-queue-model", &cfg_type_fstrm_model,
1203 	  CFG_CLAUSEFLAG_NOTCONFIGURED },
1204 	{ "fstrm-set-output-queue-size", &cfg_type_uint32,
1205 	  CFG_CLAUSEFLAG_NOTCONFIGURED },
1206 	{ "fstrm-set-reopen-interval", &cfg_type_duration,
1207 	  CFG_CLAUSEFLAG_NOTCONFIGURED },
1208 #endif /* HAVE_DNSTAP */
1209 #if defined(HAVE_GEOIP2)
1210 	{ "geoip-directory", &cfg_type_qstringornone, 0 },
1211 #else  /* if defined(HAVE_GEOIP2) */
1212 	{ "geoip-directory", &cfg_type_qstringornone,
1213 	  CFG_CLAUSEFLAG_NOTCONFIGURED },
1214 #endif /* HAVE_GEOIP2 */
1215 	{ "geoip-use-ecs", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1216 	{ "has-old-clients", &cfg_type_boolean, CFG_CLAUSEFLAG_ANCIENT },
1217 	{ "heartbeat-interval", &cfg_type_uint32, 0 },
1218 	{ "host-statistics", &cfg_type_boolean, CFG_CLAUSEFLAG_ANCIENT },
1219 	{ "host-statistics-max", &cfg_type_uint32, CFG_CLAUSEFLAG_ANCIENT },
1220 	{ "hostname", &cfg_type_qstringornone, 0 },
1221 	{ "interface-interval", &cfg_type_duration, 0 },
1222 	{ "keep-response-order", &cfg_type_bracketed_aml, 0 },
1223 	{ "listen-on", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
1224 	{ "listen-on-v6", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
1225 	{ "lock-file", &cfg_type_qstringornone, 0 },
1226 	{ "managed-keys-directory", &cfg_type_qstring, 0 },
1227 	{ "match-mapped-addresses", &cfg_type_boolean, 0 },
1228 	{ "max-rsa-exponent-size", &cfg_type_uint32, 0 },
1229 	{ "memstatistics", &cfg_type_boolean, 0 },
1230 	{ "memstatistics-file", &cfg_type_qstring, 0 },
1231 	{ "multiple-cnames", &cfg_type_boolean, CFG_CLAUSEFLAG_ANCIENT },
1232 	{ "named-xfer", &cfg_type_qstring, CFG_CLAUSEFLAG_ANCIENT },
1233 	{ "notify-rate", &cfg_type_uint32, 0 },
1234 	{ "pid-file", &cfg_type_qstringornone, 0 },
1235 	{ "port", &cfg_type_uint32, 0 },
1236 	{ "querylog", &cfg_type_boolean, 0 },
1237 	{ "random-device", &cfg_type_qstringornone, 0 },
1238 	{ "recursing-file", &cfg_type_qstring, 0 },
1239 	{ "recursive-clients", &cfg_type_uint32, 0 },
1240 	{ "reserved-sockets", &cfg_type_uint32, 0 },
1241 	{ "secroots-file", &cfg_type_qstring, 0 },
1242 	{ "serial-queries", &cfg_type_uint32, CFG_CLAUSEFLAG_ANCIENT },
1243 	{ "serial-query-rate", &cfg_type_uint32, 0 },
1244 	{ "server-id", &cfg_type_serverid, 0 },
1245 	{ "session-keyalg", &cfg_type_astring, 0 },
1246 	{ "session-keyfile", &cfg_type_qstringornone, 0 },
1247 	{ "session-keyname", &cfg_type_astring, 0 },
1248 	{ "sit-secret", &cfg_type_sstring, CFG_CLAUSEFLAG_OBSOLETE },
1249 	{ "stacksize", &cfg_type_size, 0 },
1250 	{ "startup-notify-rate", &cfg_type_uint32, 0 },
1251 	{ "statistics-file", &cfg_type_qstring, 0 },
1252 	{ "statistics-interval", &cfg_type_uint32, CFG_CLAUSEFLAG_ANCIENT },
1253 	{ "tcp-advertised-timeout", &cfg_type_uint32, 0 },
1254 	{ "tcp-clients", &cfg_type_uint32, 0 },
1255 	{ "tcp-idle-timeout", &cfg_type_uint32, 0 },
1256 	{ "tcp-initial-timeout", &cfg_type_uint32, 0 },
1257 	{ "tcp-keepalive-timeout", &cfg_type_uint32, 0 },
1258 	{ "tcp-listen-queue", &cfg_type_uint32, 0 },
1259 	{ "tkey-dhkey", &cfg_type_tkey_dhkey, 0 },
1260 	{ "tkey-domain", &cfg_type_qstring, 0 },
1261 	{ "tkey-gssapi-credential", &cfg_type_qstring, 0 },
1262 	{ "tkey-gssapi-keytab", &cfg_type_qstring, 0 },
1263 	{ "transfer-message-size", &cfg_type_uint32, 0 },
1264 	{ "transfers-in", &cfg_type_uint32, 0 },
1265 	{ "transfers-out", &cfg_type_uint32, 0 },
1266 	{ "transfers-per-ns", &cfg_type_uint32, 0 },
1267 	{ "treat-cr-as-space", &cfg_type_boolean, CFG_CLAUSEFLAG_ANCIENT },
1268 	{ "use-id-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_ANCIENT },
1269 	{ "use-ixfr", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1270 	{ "use-v4-udp-ports", &cfg_type_bracketed_portlist, 0 },
1271 	{ "use-v6-udp-ports", &cfg_type_bracketed_portlist, 0 },
1272 	{ "version", &cfg_type_qstringornone, 0 },
1273 	{ NULL, NULL, 0 }
1274 };
1275 
1276 static cfg_type_t cfg_type_namelist = { "namelist",
1277 					cfg_parse_bracketed_list,
1278 					cfg_print_bracketed_list,
1279 					cfg_doc_bracketed_list,
1280 					&cfg_rep_list,
1281 					&cfg_type_astring };
1282 
1283 static keyword_type_t exclude_kw = { "exclude", &cfg_type_namelist };
1284 
1285 static cfg_type_t cfg_type_optional_exclude = {
1286 	"optional_exclude",    parse_optional_keyvalue, print_keyvalue,
1287 	doc_optional_keyvalue, &cfg_rep_list,		&exclude_kw
1288 };
1289 
1290 static keyword_type_t exceptionnames_kw = { "except-from", &cfg_type_namelist };
1291 
1292 static cfg_type_t cfg_type_optional_exceptionnames = {
1293 	"optional_allow",      parse_optional_keyvalue, print_keyvalue,
1294 	doc_optional_keyvalue, &cfg_rep_list,		&exceptionnames_kw
1295 };
1296 
1297 static cfg_tuplefielddef_t denyaddresses_fields[] = {
1298 	{ "acl", &cfg_type_bracketed_aml, 0 },
1299 	{ "except-from", &cfg_type_optional_exceptionnames, 0 },
1300 	{ NULL, NULL, 0 }
1301 };
1302 
1303 static cfg_type_t cfg_type_denyaddresses = {
1304 	"denyaddresses", cfg_parse_tuple, cfg_print_tuple,
1305 	cfg_doc_tuple,	 &cfg_rep_tuple,  denyaddresses_fields
1306 };
1307 
1308 static cfg_tuplefielddef_t denyaliases_fields[] = {
1309 	{ "name", &cfg_type_namelist, 0 },
1310 	{ "except-from", &cfg_type_optional_exceptionnames, 0 },
1311 	{ NULL, NULL, 0 }
1312 };
1313 
1314 static cfg_type_t cfg_type_denyaliases = {
1315 	"denyaliases", cfg_parse_tuple, cfg_print_tuple,
1316 	cfg_doc_tuple, &cfg_rep_tuple,	denyaliases_fields
1317 };
1318 
1319 static cfg_type_t cfg_type_algorithmlist = { "algorithmlist",
1320 					     cfg_parse_bracketed_list,
1321 					     cfg_print_bracketed_list,
1322 					     cfg_doc_bracketed_list,
1323 					     &cfg_rep_list,
1324 					     &cfg_type_astring };
1325 
1326 static cfg_tuplefielddef_t disablealgorithm_fields[] = {
1327 	{ "name", &cfg_type_astring, 0 },
1328 	{ "algorithms", &cfg_type_algorithmlist, 0 },
1329 	{ NULL, NULL, 0 }
1330 };
1331 
1332 static cfg_type_t cfg_type_disablealgorithm = {
1333 	"disablealgorithm", cfg_parse_tuple, cfg_print_tuple,
1334 	cfg_doc_tuple,	    &cfg_rep_tuple,  disablealgorithm_fields
1335 };
1336 
1337 static cfg_type_t cfg_type_dsdigestlist = { "dsdigestlist",
1338 					    cfg_parse_bracketed_list,
1339 					    cfg_print_bracketed_list,
1340 					    cfg_doc_bracketed_list,
1341 					    &cfg_rep_list,
1342 					    &cfg_type_astring };
1343 
1344 static cfg_tuplefielddef_t disabledsdigest_fields[] = {
1345 	{ "name", &cfg_type_astring, 0 },
1346 	{ "digests", &cfg_type_dsdigestlist, 0 },
1347 	{ NULL, NULL, 0 }
1348 };
1349 
1350 static cfg_type_t cfg_type_disabledsdigest = {
1351 	"disabledsdigest", cfg_parse_tuple, cfg_print_tuple,
1352 	cfg_doc_tuple,	   &cfg_rep_tuple,  disabledsdigest_fields
1353 };
1354 
1355 static cfg_tuplefielddef_t mustbesecure_fields[] = {
1356 	{ "name", &cfg_type_astring, 0 },
1357 	{ "value", &cfg_type_boolean, 0 },
1358 	{ NULL, NULL, 0 }
1359 };
1360 
1361 static cfg_type_t cfg_type_mustbesecure = {
1362 	"mustbesecure", cfg_parse_tuple, cfg_print_tuple,
1363 	cfg_doc_tuple,	&cfg_rep_tuple,	 mustbesecure_fields
1364 };
1365 
1366 static const char *masterformat_enums[] = { "map", "raw", "text", NULL };
1367 static cfg_type_t cfg_type_masterformat = {
1368 	"masterformat", cfg_parse_enum,	 cfg_print_ustring,
1369 	cfg_doc_enum,	&cfg_rep_string, &masterformat_enums
1370 };
1371 
1372 static const char *masterstyle_enums[] = { "full", "relative", NULL };
1373 static cfg_type_t cfg_type_masterstyle = {
1374 	"masterstyle", cfg_parse_enum,	cfg_print_ustring,
1375 	cfg_doc_enum,  &cfg_rep_string, &masterstyle_enums
1376 };
1377 
1378 static keyword_type_t blocksize_kw = { "block-size", &cfg_type_uint32 };
1379 
1380 static cfg_type_t cfg_type_blocksize = { "blocksize",	  parse_keyvalue,
1381 					 print_keyvalue,  doc_keyvalue,
1382 					 &cfg_rep_uint32, &blocksize_kw };
1383 
1384 static cfg_tuplefielddef_t resppadding_fields[] = {
1385 	{ "acl", &cfg_type_bracketed_aml, 0 },
1386 	{ "block-size", &cfg_type_blocksize, 0 },
1387 	{ NULL, NULL, 0 }
1388 };
1389 
1390 static cfg_type_t cfg_type_resppadding = {
1391 	"resppadding", cfg_parse_tuple, cfg_print_tuple,
1392 	cfg_doc_tuple, &cfg_rep_tuple,	resppadding_fields
1393 };
1394 
1395 /*%
1396  *  dnstap {
1397  *      &lt;message type&gt; [query | response] ;
1398  *      ...
1399  *  }
1400  *
1401  *  ... where message type is one of: client, resolver, auth, forwarder,
1402  *                                    update, all
1403  */
1404 static const char *dnstap_types[] = { "all",	   "auth",     "client",
1405 				      "forwarder", "resolver", "update",
1406 				      NULL };
1407 
1408 static const char *dnstap_modes[] = { "query", "response", NULL };
1409 
1410 static cfg_type_t cfg_type_dnstap_type = { "dnstap_type",     cfg_parse_enum,
1411 					   cfg_print_ustring, cfg_doc_enum,
1412 					   &cfg_rep_string,   dnstap_types };
1413 
1414 static cfg_type_t cfg_type_dnstap_mode = {
1415 	"dnstap_mode",	   parse_optional_enum, cfg_print_ustring,
1416 	doc_optional_enum, &cfg_rep_string,	dnstap_modes
1417 };
1418 
1419 static cfg_tuplefielddef_t dnstap_fields[] = {
1420 	{ "type", &cfg_type_dnstap_type, 0 },
1421 	{ "mode", &cfg_type_dnstap_mode, 0 },
1422 	{ NULL, NULL, 0 }
1423 };
1424 
1425 static cfg_type_t cfg_type_dnstap_entry = { "dnstap_value",  cfg_parse_tuple,
1426 					    cfg_print_tuple, cfg_doc_tuple,
1427 					    &cfg_rep_tuple,  dnstap_fields };
1428 
1429 static cfg_type_t cfg_type_dnstap = { "dnstap",
1430 				      cfg_parse_bracketed_list,
1431 				      cfg_print_bracketed_list,
1432 				      cfg_doc_bracketed_list,
1433 				      &cfg_rep_list,
1434 				      &cfg_type_dnstap_entry };
1435 
1436 /*%
1437  * dnstap-output
1438  */
1439 static isc_result_t
1440 parse_dtout(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1441 	isc_result_t result;
1442 	cfg_obj_t *obj = NULL;
1443 	const cfg_tuplefielddef_t *fields = type->of;
1444 
1445 	CHECK(cfg_create_tuple(pctx, type, &obj));
1446 
1447 	/* Parse the mandatory "mode" and "path" fields */
1448 	CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
1449 	CHECK(cfg_parse_obj(pctx, fields[1].type, &obj->value.tuple[1]));
1450 
1451 	/* Parse "versions" and "size" fields in any order. */
1452 	for (;;) {
1453 		CHECK(cfg_peektoken(pctx, 0));
1454 		if (pctx->token.type == isc_tokentype_string) {
1455 			CHECK(cfg_gettoken(pctx, 0));
1456 			if (strcasecmp(TOKEN_STRING(pctx), "size") == 0 &&
1457 			    obj->value.tuple[2] == NULL) {
1458 				CHECK(cfg_parse_obj(pctx, fields[2].type,
1459 						    &obj->value.tuple[2]));
1460 			} else if (strcasecmp(TOKEN_STRING(pctx), "versions") ==
1461 					   0 &&
1462 				   obj->value.tuple[3] == NULL)
1463 			{
1464 				CHECK(cfg_parse_obj(pctx, fields[3].type,
1465 						    &obj->value.tuple[3]));
1466 			} else if (strcasecmp(TOKEN_STRING(pctx), "suffix") ==
1467 					   0 &&
1468 				   obj->value.tuple[4] == NULL)
1469 			{
1470 				CHECK(cfg_parse_obj(pctx, fields[4].type,
1471 						    &obj->value.tuple[4]));
1472 			} else {
1473 				cfg_parser_error(pctx, CFG_LOG_NEAR,
1474 						 "unexpected token");
1475 				result = ISC_R_UNEXPECTEDTOKEN;
1476 				goto cleanup;
1477 			}
1478 		} else {
1479 			break;
1480 		}
1481 	}
1482 
1483 	/* Create void objects for missing optional values. */
1484 	if (obj->value.tuple[2] == NULL) {
1485 		CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2]));
1486 	}
1487 	if (obj->value.tuple[3] == NULL) {
1488 		CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[3]));
1489 	}
1490 	if (obj->value.tuple[4] == NULL) {
1491 		CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[4]));
1492 	}
1493 
1494 	*ret = obj;
1495 	return (ISC_R_SUCCESS);
1496 
1497 cleanup:
1498 	CLEANUP_OBJ(obj);
1499 	return (result);
1500 }
1501 
1502 static void
1503 print_dtout(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1504 	cfg_print_obj(pctx, obj->value.tuple[0]); /* mode */
1505 	cfg_print_obj(pctx, obj->value.tuple[1]); /* file */
1506 	if (obj->value.tuple[2]->type->print != cfg_print_void) {
1507 		cfg_print_cstr(pctx, " size ");
1508 		cfg_print_obj(pctx, obj->value.tuple[2]);
1509 	}
1510 	if (obj->value.tuple[3]->type->print != cfg_print_void) {
1511 		cfg_print_cstr(pctx, " versions ");
1512 		cfg_print_obj(pctx, obj->value.tuple[3]);
1513 	}
1514 	if (obj->value.tuple[4]->type->print != cfg_print_void) {
1515 		cfg_print_cstr(pctx, " suffix ");
1516 		cfg_print_obj(pctx, obj->value.tuple[4]);
1517 	}
1518 }
1519 
1520 static void
1521 doc_dtout(cfg_printer_t *pctx, const cfg_type_t *type) {
1522 	UNUSED(type);
1523 	cfg_print_cstr(pctx, "( file | unix ) <quoted_string>");
1524 	cfg_print_cstr(pctx, " ");
1525 	cfg_print_cstr(pctx, "[ size ( unlimited | <size> ) ]");
1526 	cfg_print_cstr(pctx, " ");
1527 	cfg_print_cstr(pctx, "[ versions ( unlimited | <integer> ) ]");
1528 	cfg_print_cstr(pctx, " ");
1529 	cfg_print_cstr(pctx, "[ suffix ( increment | timestamp ) ]");
1530 }
1531 
1532 static const char *dtoutmode_enums[] = { "file", "unix", NULL };
1533 static cfg_type_t cfg_type_dtmode = { "dtmode",		 cfg_parse_enum,
1534 				      cfg_print_ustring, cfg_doc_enum,
1535 				      &cfg_rep_string,	 &dtoutmode_enums };
1536 
1537 static cfg_tuplefielddef_t dtout_fields[] = {
1538 	{ "mode", &cfg_type_dtmode, 0 },
1539 	{ "path", &cfg_type_qstring, 0 },
1540 	{ "size", &cfg_type_sizenodefault, 0 },
1541 	{ "versions", &cfg_type_logversions, 0 },
1542 	{ "suffix", &cfg_type_logsuffix, 0 },
1543 	{ NULL, NULL, 0 }
1544 };
1545 
1546 static cfg_type_t cfg_type_dnstapoutput = { "dnstapoutput", parse_dtout,
1547 					    print_dtout,    doc_dtout,
1548 					    &cfg_rep_tuple, dtout_fields };
1549 
1550 /*%
1551  *  response-policy {
1552  *	zone &lt;string&gt; [ policy (given|disabled|passthru|drop|tcp-only|
1553  *					nxdomain|nodata|cname &lt;domain&gt; ) ]
1554  *		      [ recursive-only yes|no ] [ log yes|no ]
1555  *		      [ max-policy-ttl number ]
1556  *		      [ nsip-enable yes|no ] [ nsdname-enable yes|no ];
1557  *  } [ recursive-only yes|no ] [ max-policy-ttl number ]
1558  *	 [ min-update-interval number ]
1559  *	 [ break-dnssec yes|no ] [ min-ns-dots number ]
1560  *	 [ qname-wait-recurse yes|no ]
1561  *	 [ nsip-enable yes|no ] [ nsdname-enable yes|no ]
1562  *	 [ dnsrps-enable yes|no ]
1563  *	 [ dnsrps-options { DNSRPS configuration string } ];
1564  */
1565 
1566 static void
1567 doc_rpz_policy(cfg_printer_t *pctx, const cfg_type_t *type) {
1568 	const char *const *p;
1569 	/*
1570 	 * This is cfg_doc_enum() without the trailing " )".
1571 	 */
1572 	cfg_print_cstr(pctx, "( ");
1573 	for (p = type->of; *p != NULL; p++) {
1574 		cfg_print_cstr(pctx, *p);
1575 		if (p[1] != NULL) {
1576 			cfg_print_cstr(pctx, " | ");
1577 		}
1578 	}
1579 }
1580 
1581 static void
1582 doc_rpz_cname(cfg_printer_t *pctx, const cfg_type_t *type) {
1583 	cfg_doc_terminal(pctx, type);
1584 	cfg_print_cstr(pctx, " )");
1585 }
1586 
1587 /*
1588  * Parse
1589  *	given|disabled|passthru|drop|tcp-only|nxdomain|nodata|cname <domain>
1590  */
1591 static isc_result_t
1592 cfg_parse_rpz_policy(cfg_parser_t *pctx, const cfg_type_t *type,
1593 		     cfg_obj_t **ret) {
1594 	isc_result_t result;
1595 	cfg_obj_t *obj = NULL;
1596 	const cfg_tuplefielddef_t *fields;
1597 
1598 	CHECK(cfg_create_tuple(pctx, type, &obj));
1599 
1600 	fields = type->of;
1601 	CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
1602 	/*
1603 	 * parse cname domain only after "policy cname"
1604 	 */
1605 	if (strcasecmp("cname", cfg_obj_asstring(obj->value.tuple[0])) != 0) {
1606 		CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1]));
1607 	} else {
1608 		CHECK(cfg_parse_obj(pctx, fields[1].type,
1609 				    &obj->value.tuple[1]));
1610 	}
1611 
1612 	*ret = obj;
1613 	return (ISC_R_SUCCESS);
1614 
1615 cleanup:
1616 	CLEANUP_OBJ(obj);
1617 	return (result);
1618 }
1619 
1620 /*
1621  * Parse a tuple consisting of any kind of required field followed
1622  * by 2 or more optional keyvalues that can be in any order.
1623  */
1624 static isc_result_t
1625 cfg_parse_kv_tuple(cfg_parser_t *pctx, const cfg_type_t *type,
1626 		   cfg_obj_t **ret) {
1627 	const cfg_tuplefielddef_t *fields, *f;
1628 	cfg_obj_t *obj = NULL;
1629 	int fn;
1630 	isc_result_t result;
1631 
1632 	CHECK(cfg_create_tuple(pctx, type, &obj));
1633 
1634 	/*
1635 	 * The zone first field is required and always first.
1636 	 */
1637 	fields = type->of;
1638 	CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
1639 
1640 	for (;;) {
1641 		CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
1642 		if (pctx->token.type != isc_tokentype_string) {
1643 			break;
1644 		}
1645 
1646 		for (fn = 1, f = &fields[1];; ++fn, ++f) {
1647 			if (f->name == NULL) {
1648 				cfg_parser_error(pctx, 0, "unexpected '%s'",
1649 						 TOKEN_STRING(pctx));
1650 				result = ISC_R_UNEXPECTEDTOKEN;
1651 				goto cleanup;
1652 			}
1653 			if (obj->value.tuple[fn] == NULL &&
1654 			    strcasecmp(f->name, TOKEN_STRING(pctx)) == 0)
1655 			{
1656 				break;
1657 			}
1658 		}
1659 
1660 		CHECK(cfg_gettoken(pctx, 0));
1661 		CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[fn]));
1662 	}
1663 
1664 	for (fn = 1, f = &fields[1]; f->name != NULL; ++fn, ++f) {
1665 		if (obj->value.tuple[fn] == NULL) {
1666 			CHECK(cfg_parse_void(pctx, NULL,
1667 					     &obj->value.tuple[fn]));
1668 		}
1669 	}
1670 
1671 	*ret = obj;
1672 	return (ISC_R_SUCCESS);
1673 
1674 cleanup:
1675 	CLEANUP_OBJ(obj);
1676 	return (result);
1677 }
1678 
1679 static void
1680 cfg_print_kv_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1681 	unsigned int i;
1682 	const cfg_tuplefielddef_t *fields, *f;
1683 	const cfg_obj_t *fieldobj;
1684 
1685 	fields = obj->type->of;
1686 	for (f = fields, i = 0; f->name != NULL; f++, i++) {
1687 		fieldobj = obj->value.tuple[i];
1688 		if (fieldobj->type->print == cfg_print_void) {
1689 			continue;
1690 		}
1691 		if (i != 0) {
1692 			cfg_print_cstr(pctx, " ");
1693 			cfg_print_cstr(pctx, f->name);
1694 			cfg_print_cstr(pctx, " ");
1695 		}
1696 		cfg_print_obj(pctx, fieldobj);
1697 	}
1698 }
1699 
1700 static void
1701 cfg_doc_kv_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
1702 	const cfg_tuplefielddef_t *fields, *f;
1703 
1704 	fields = type->of;
1705 	for (f = fields; f->name != NULL; f++) {
1706 		if (f != fields) {
1707 			cfg_print_cstr(pctx, " [ ");
1708 			cfg_print_cstr(pctx, f->name);
1709 			if (f->type->doc != cfg_doc_void) {
1710 				cfg_print_cstr(pctx, " ");
1711 			}
1712 		}
1713 		cfg_doc_obj(pctx, f->type);
1714 		if (f != fields) {
1715 			cfg_print_cstr(pctx, " ]");
1716 		}
1717 	}
1718 }
1719 
1720 static keyword_type_t zone_kw = { "zone", &cfg_type_astring };
1721 static cfg_type_t cfg_type_rpz_zone = { "zone",		 parse_keyvalue,
1722 					print_keyvalue,	 doc_keyvalue,
1723 					&cfg_rep_string, &zone_kw };
1724 /*
1725  * "no-op" is an obsolete equivalent of "passthru".
1726  */
1727 static const char *rpz_policies[] = { "cname",	  "disabled", "drop",
1728 				      "given",	  "no-op",    "nodata",
1729 				      "nxdomain", "passthru", "tcp-only",
1730 				      NULL };
1731 static cfg_type_t cfg_type_rpz_policy_name = {
1732 	"policy name",	cfg_parse_enum,	 cfg_print_ustring,
1733 	doc_rpz_policy, &cfg_rep_string, &rpz_policies
1734 };
1735 static cfg_type_t cfg_type_rpz_cname = {
1736 	"quoted_string", cfg_parse_astring, NULL,
1737 	doc_rpz_cname,	 &cfg_rep_string,   NULL
1738 };
1739 static cfg_tuplefielddef_t rpz_policy_fields[] = {
1740 	{ "policy name", &cfg_type_rpz_policy_name, 0 },
1741 	{ "cname", &cfg_type_rpz_cname, 0 },
1742 	{ NULL, NULL, 0 }
1743 };
1744 static cfg_type_t cfg_type_rpz_policy = { "policy tuple",  cfg_parse_rpz_policy,
1745 					  cfg_print_tuple, cfg_doc_tuple,
1746 					  &cfg_rep_tuple,  rpz_policy_fields };
1747 static cfg_tuplefielddef_t rpz_zone_fields[] = {
1748 	{ "zone name", &cfg_type_rpz_zone, 0 },
1749 	{ "add-soa", &cfg_type_boolean, 0 },
1750 	{ "log", &cfg_type_boolean, 0 },
1751 	{ "max-policy-ttl", &cfg_type_duration, 0 },
1752 	{ "min-update-interval", &cfg_type_duration, 0 },
1753 	{ "policy", &cfg_type_rpz_policy, 0 },
1754 	{ "recursive-only", &cfg_type_boolean, 0 },
1755 	{ "nsip-enable", &cfg_type_boolean, 0 },
1756 	{ "nsdname-enable", &cfg_type_boolean, 0 },
1757 	{ NULL, NULL, 0 }
1758 };
1759 static cfg_type_t cfg_type_rpz_tuple = { "rpz tuple",	     cfg_parse_kv_tuple,
1760 					 cfg_print_kv_tuple, cfg_doc_kv_tuple,
1761 					 &cfg_rep_tuple,     rpz_zone_fields };
1762 static cfg_type_t cfg_type_rpz_list = { "zone list",
1763 					cfg_parse_bracketed_list,
1764 					cfg_print_bracketed_list,
1765 					cfg_doc_bracketed_list,
1766 					&cfg_rep_list,
1767 					&cfg_type_rpz_tuple };
1768 static cfg_tuplefielddef_t rpz_fields[] = {
1769 	{ "zone list", &cfg_type_rpz_list, 0 },
1770 	{ "add-soa", &cfg_type_boolean, 0 },
1771 	{ "break-dnssec", &cfg_type_boolean, 0 },
1772 	{ "max-policy-ttl", &cfg_type_duration, 0 },
1773 	{ "min-update-interval", &cfg_type_duration, 0 },
1774 	{ "min-ns-dots", &cfg_type_uint32, 0 },
1775 	{ "nsip-wait-recurse", &cfg_type_boolean, 0 },
1776 	{ "qname-wait-recurse", &cfg_type_boolean, 0 },
1777 	{ "recursive-only", &cfg_type_boolean, 0 },
1778 	{ "nsip-enable", &cfg_type_boolean, 0 },
1779 	{ "nsdname-enable", &cfg_type_boolean, 0 },
1780 #ifdef USE_DNSRPS
1781 	{ "dnsrps-enable", &cfg_type_boolean, 0 },
1782 	{ "dnsrps-options", &cfg_type_bracketed_text, 0 },
1783 #else  /* ifdef USE_DNSRPS */
1784 	{ "dnsrps-enable", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTCONFIGURED },
1785 	{ "dnsrps-options", &cfg_type_bracketed_text,
1786 	  CFG_CLAUSEFLAG_NOTCONFIGURED },
1787 #endif /* ifdef USE_DNSRPS */
1788 	{ NULL, NULL, 0 }
1789 };
1790 static cfg_type_t cfg_type_rpz = { "rpz",
1791 				   cfg_parse_kv_tuple,
1792 				   cfg_print_kv_tuple,
1793 				   cfg_doc_kv_tuple,
1794 				   &cfg_rep_tuple,
1795 				   rpz_fields };
1796 
1797 /*
1798  * Catalog zones
1799  */
1800 static cfg_type_t cfg_type_catz_zone = { "zone",	  parse_keyvalue,
1801 					 print_keyvalue,  doc_keyvalue,
1802 					 &cfg_rep_string, &zone_kw };
1803 
1804 static cfg_tuplefielddef_t catz_zone_fields[] = {
1805 	{ "zone name", &cfg_type_catz_zone, 0 },
1806 	{ "default-masters", &cfg_type_namesockaddrkeylist, 0 },
1807 	{ "zone-directory", &cfg_type_qstring, 0 },
1808 	{ "in-memory", &cfg_type_boolean, 0 },
1809 	{ "min-update-interval", &cfg_type_duration, 0 },
1810 	{ NULL, NULL, 0 }
1811 };
1812 static cfg_type_t cfg_type_catz_tuple = {
1813 	"catz tuple",	  cfg_parse_kv_tuple, cfg_print_kv_tuple,
1814 	cfg_doc_kv_tuple, &cfg_rep_tuple,     catz_zone_fields
1815 };
1816 static cfg_type_t cfg_type_catz_list = { "zone list",
1817 					 cfg_parse_bracketed_list,
1818 					 cfg_print_bracketed_list,
1819 					 cfg_doc_bracketed_list,
1820 					 &cfg_rep_list,
1821 					 &cfg_type_catz_tuple };
1822 static cfg_tuplefielddef_t catz_fields[] = {
1823 	{ "zone list", &cfg_type_catz_list, 0 }, { NULL, NULL, 0 }
1824 };
1825 static cfg_type_t cfg_type_catz = {
1826 	"catz",		  cfg_parse_kv_tuple, cfg_print_kv_tuple,
1827 	cfg_doc_kv_tuple, &cfg_rep_tuple,     catz_fields
1828 };
1829 
1830 /*
1831  * rate-limit
1832  */
1833 static cfg_clausedef_t rrl_clauses[] = {
1834 	{ "all-per-second", &cfg_type_uint32, 0 },
1835 	{ "errors-per-second", &cfg_type_uint32, 0 },
1836 	{ "exempt-clients", &cfg_type_bracketed_aml, 0 },
1837 	{ "ipv4-prefix-length", &cfg_type_uint32, 0 },
1838 	{ "ipv6-prefix-length", &cfg_type_uint32, 0 },
1839 	{ "log-only", &cfg_type_boolean, 0 },
1840 	{ "max-table-size", &cfg_type_uint32, 0 },
1841 	{ "min-table-size", &cfg_type_uint32, 0 },
1842 	{ "nodata-per-second", &cfg_type_uint32, 0 },
1843 	{ "nxdomains-per-second", &cfg_type_uint32, 0 },
1844 	{ "qps-scale", &cfg_type_uint32, 0 },
1845 	{ "referrals-per-second", &cfg_type_uint32, 0 },
1846 	{ "responses-per-second", &cfg_type_uint32, 0 },
1847 	{ "slip", &cfg_type_uint32, 0 },
1848 	{ "window", &cfg_type_uint32, 0 },
1849 	{ NULL, NULL, 0 }
1850 };
1851 
1852 static cfg_clausedef_t *rrl_clausesets[] = { rrl_clauses, NULL };
1853 
1854 static cfg_type_t cfg_type_rrl = { "rate-limit", cfg_parse_map, cfg_print_map,
1855 				   cfg_doc_map,	 &cfg_rep_map,	rrl_clausesets };
1856 
1857 /*%
1858  * dnssec-lookaside
1859  */
1860 
1861 static void
1862 print_lookaside(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1863 	const cfg_obj_t *domain = obj->value.tuple[0];
1864 
1865 	if (domain->value.string.length == 4 &&
1866 	    strncmp(domain->value.string.base, "auto", 4) == 0)
1867 	{
1868 		cfg_print_cstr(pctx, "auto");
1869 	} else {
1870 		cfg_print_tuple(pctx, obj);
1871 	}
1872 }
1873 
1874 static void
1875 doc_lookaside(cfg_printer_t *pctx, const cfg_type_t *type) {
1876 	UNUSED(type);
1877 	cfg_print_cstr(pctx, "( <string> trust-anchor <string> | auto | no )");
1878 }
1879 
1880 static keyword_type_t trustanchor_kw = { "trust-anchor", &cfg_type_astring };
1881 
1882 static cfg_type_t cfg_type_optional_trustanchor = {
1883 	"optional_trustanchor", parse_optional_keyvalue, print_keyvalue,
1884 	doc_keyvalue,		&cfg_rep_string,	 &trustanchor_kw
1885 };
1886 
1887 static cfg_tuplefielddef_t lookaside_fields[] = {
1888 	{ "domain", &cfg_type_astring, 0 },
1889 	{ "trust-anchor", &cfg_type_optional_trustanchor, 0 },
1890 	{ NULL, NULL, 0 }
1891 };
1892 
1893 static cfg_type_t cfg_type_lookaside = { "lookaside",	  cfg_parse_tuple,
1894 					 print_lookaside, doc_lookaside,
1895 					 &cfg_rep_tuple,  lookaside_fields };
1896 
1897 static isc_result_t
1898 parse_optional_uint32(cfg_parser_t *pctx, const cfg_type_t *type,
1899 		      cfg_obj_t **ret) {
1900 	isc_result_t result;
1901 	UNUSED(type);
1902 
1903 	CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
1904 	if (pctx->token.type == isc_tokentype_number) {
1905 		CHECK(cfg_parse_obj(pctx, &cfg_type_uint32, ret));
1906 	} else {
1907 		CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
1908 	}
1909 cleanup:
1910 	return (result);
1911 }
1912 
1913 static void
1914 doc_optional_uint32(cfg_printer_t *pctx, const cfg_type_t *type) {
1915 	UNUSED(type);
1916 	cfg_print_cstr(pctx, "[ <integer> ]");
1917 }
1918 
1919 static cfg_type_t cfg_type_optional_uint32 = { "optional_uint32",
1920 					       parse_optional_uint32,
1921 					       NULL,
1922 					       doc_optional_uint32,
1923 					       NULL,
1924 					       NULL };
1925 
1926 static cfg_tuplefielddef_t prefetch_fields[] = {
1927 	{ "trigger", &cfg_type_uint32, 0 },
1928 	{ "eligible", &cfg_type_optional_uint32, 0 },
1929 	{ NULL, NULL, 0 }
1930 };
1931 
1932 static cfg_type_t cfg_type_prefetch = { "prefetch",	 cfg_parse_tuple,
1933 					cfg_print_tuple, cfg_doc_tuple,
1934 					&cfg_rep_tuple,	 prefetch_fields };
1935 /*
1936  * DNS64.
1937  */
1938 static cfg_clausedef_t dns64_clauses[] = {
1939 	{ "break-dnssec", &cfg_type_boolean, 0 },
1940 	{ "clients", &cfg_type_bracketed_aml, 0 },
1941 	{ "exclude", &cfg_type_bracketed_aml, 0 },
1942 	{ "mapped", &cfg_type_bracketed_aml, 0 },
1943 	{ "recursive-only", &cfg_type_boolean, 0 },
1944 	{ "suffix", &cfg_type_netaddr6, 0 },
1945 	{ NULL, NULL, 0 },
1946 };
1947 
1948 static cfg_clausedef_t *dns64_clausesets[] = { dns64_clauses, NULL };
1949 
1950 static cfg_type_t cfg_type_dns64 = { "dns64",	    cfg_parse_netprefix_map,
1951 				     cfg_print_map, cfg_doc_map,
1952 				     &cfg_rep_map,  dns64_clausesets };
1953 
1954 static const char *staleanswerclienttimeout_enums[] = { "disabled", "off",
1955 							NULL };
1956 static isc_result_t
1957 parse_staleanswerclienttimeout(cfg_parser_t *pctx, const cfg_type_t *type,
1958 			       cfg_obj_t **ret) {
1959 	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_uint32, ret));
1960 }
1961 
1962 static void
1963 doc_staleanswerclienttimeout(cfg_printer_t *pctx, const cfg_type_t *type) {
1964 	cfg_doc_enum_or_other(pctx, type, &cfg_type_uint32);
1965 }
1966 
1967 static cfg_type_t cfg_type_staleanswerclienttimeout = {
1968 	"staleanswerclienttimeout",
1969 	parse_staleanswerclienttimeout,
1970 	cfg_print_ustring,
1971 	doc_staleanswerclienttimeout,
1972 	&cfg_rep_string,
1973 	staleanswerclienttimeout_enums
1974 };
1975 
1976 /*%
1977  * Clauses that can be found within the 'view' statement,
1978  * with defaults in the 'options' statement.
1979  */
1980 
1981 static cfg_clausedef_t view_clauses[] = {
1982 	{ "acache-cleaning-interval", &cfg_type_uint32,
1983 	  CFG_CLAUSEFLAG_OBSOLETE },
1984 	{ "acache-enable", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1985 	{ "additional-from-auth", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1986 	{ "additional-from-cache", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1987 	{ "allow-new-zones", &cfg_type_boolean, 0 },
1988 	{ "allow-query-cache", &cfg_type_bracketed_aml, 0 },
1989 	{ "allow-query-cache-on", &cfg_type_bracketed_aml, 0 },
1990 	{ "allow-recursion", &cfg_type_bracketed_aml, 0 },
1991 	{ "allow-recursion-on", &cfg_type_bracketed_aml, 0 },
1992 	{ "allow-v6-synthesis", &cfg_type_bracketed_aml,
1993 	  CFG_CLAUSEFLAG_OBSOLETE },
1994 	{ "attach-cache", &cfg_type_astring, 0 },
1995 	{ "auth-nxdomain", &cfg_type_boolean, CFG_CLAUSEFLAG_NEWDEFAULT },
1996 	{ "cache-file", &cfg_type_qstring, 0 },
1997 	{ "catalog-zones", &cfg_type_catz, 0 },
1998 	{ "check-names", &cfg_type_checknames, CFG_CLAUSEFLAG_MULTI },
1999 	{ "cleaning-interval", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
2000 	{ "clients-per-query", &cfg_type_uint32, 0 },
2001 	{ "deny-answer-addresses", &cfg_type_denyaddresses, 0 },
2002 	{ "deny-answer-aliases", &cfg_type_denyaliases, 0 },
2003 	{ "disable-algorithms", &cfg_type_disablealgorithm,
2004 	  CFG_CLAUSEFLAG_MULTI },
2005 	{ "disable-ds-digests", &cfg_type_disabledsdigest,
2006 	  CFG_CLAUSEFLAG_MULTI },
2007 	{ "disable-empty-zone", &cfg_type_astring, CFG_CLAUSEFLAG_MULTI },
2008 	{ "dns64", &cfg_type_dns64, CFG_CLAUSEFLAG_MULTI },
2009 	{ "dns64-contact", &cfg_type_astring, 0 },
2010 	{ "dns64-server", &cfg_type_astring, 0 },
2011 #ifdef USE_DNSRPS
2012 	{ "dnsrps-enable", &cfg_type_boolean, 0 },
2013 	{ "dnsrps-options", &cfg_type_bracketed_text, 0 },
2014 #else  /* ifdef USE_DNSRPS */
2015 	{ "dnsrps-enable", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTCONFIGURED },
2016 	{ "dnsrps-options", &cfg_type_bracketed_text,
2017 	  CFG_CLAUSEFLAG_NOTCONFIGURED },
2018 #endif /* ifdef USE_DNSRPS */
2019 	{ "dnssec-accept-expired", &cfg_type_boolean, 0 },
2020 	{ "dnssec-enable", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
2021 	{ "dnssec-lookaside", &cfg_type_lookaside,
2022 	  CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_OBSOLETE },
2023 	{ "dnssec-must-be-secure", &cfg_type_mustbesecure,
2024 	  CFG_CLAUSEFLAG_MULTI },
2025 	{ "dnssec-validation", &cfg_type_boolorauto, 0 },
2026 #ifdef HAVE_DNSTAP
2027 	{ "dnstap", &cfg_type_dnstap, 0 },
2028 #else  /* ifdef HAVE_DNSTAP */
2029 	{ "dnstap", &cfg_type_dnstap, CFG_CLAUSEFLAG_NOTCONFIGURED },
2030 #endif /* HAVE_DNSTAP */
2031 	{ "dual-stack-servers", &cfg_type_nameportiplist, 0 },
2032 	{ "edns-udp-size", &cfg_type_uint32, 0 },
2033 	{ "empty-contact", &cfg_type_astring, 0 },
2034 	{ "empty-server", &cfg_type_astring, 0 },
2035 	{ "empty-zones-enable", &cfg_type_boolean, 0 },
2036 	{ "fetch-glue", &cfg_type_boolean, CFG_CLAUSEFLAG_ANCIENT },
2037 	{ "fetch-quota-params", &cfg_type_fetchquota, 0 },
2038 	{ "fetches-per-server", &cfg_type_fetchesper, 0 },
2039 	{ "fetches-per-zone", &cfg_type_fetchesper, 0 },
2040 	{ "filter-aaaa", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_OBSOLETE },
2041 	{ "filter-aaaa-on-v4", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
2042 	{ "filter-aaaa-on-v6", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
2043 	{ "glue-cache", &cfg_type_boolean, 0 },
2044 	{ "ixfr-from-differences", &cfg_type_ixfrdifftype, 0 },
2045 	{ "lame-ttl", &cfg_type_duration, 0 },
2046 #ifdef HAVE_LMDB
2047 	{ "lmdb-mapsize", &cfg_type_sizeval, 0 },
2048 #else  /* ifdef HAVE_LMDB */
2049 	{ "lmdb-mapsize", &cfg_type_sizeval, CFG_CLAUSEFLAG_NOOP },
2050 #endif /* ifdef HAVE_LMDB */
2051 	{ "max-acache-size", &cfg_type_sizenodefault, CFG_CLAUSEFLAG_OBSOLETE },
2052 	{ "max-cache-size", &cfg_type_sizeorpercent, 0 },
2053 	{ "max-cache-ttl", &cfg_type_duration, 0 },
2054 	{ "max-clients-per-query", &cfg_type_uint32, 0 },
2055 	{ "max-ncache-ttl", &cfg_type_duration, 0 },
2056 	{ "max-recursion-depth", &cfg_type_uint32, 0 },
2057 	{ "max-recursion-queries", &cfg_type_uint32, 0 },
2058 	{ "max-stale-ttl", &cfg_type_duration, 0 },
2059 	{ "max-udp-size", &cfg_type_uint32, 0 },
2060 	{ "message-compression", &cfg_type_boolean, 0 },
2061 	{ "min-cache-ttl", &cfg_type_duration, 0 },
2062 	{ "min-ncache-ttl", &cfg_type_duration, 0 },
2063 	{ "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_ANCIENT },
2064 	{ "minimal-any", &cfg_type_boolean, 0 },
2065 	{ "minimal-responses", &cfg_type_minimal, 0 },
2066 	{ "new-zones-directory", &cfg_type_qstring, 0 },
2067 	{ "no-case-compress", &cfg_type_bracketed_aml, 0 },
2068 	{ "nocookie-udp-size", &cfg_type_uint32, 0 },
2069 	{ "nosit-udp-size", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
2070 	{ "nta-lifetime", &cfg_type_duration, 0 },
2071 	{ "nta-recheck", &cfg_type_duration, 0 },
2072 	{ "nxdomain-redirect", &cfg_type_astring, 0 },
2073 	{ "preferred-glue", &cfg_type_astring, 0 },
2074 	{ "prefetch", &cfg_type_prefetch, 0 },
2075 	{ "provide-ixfr", &cfg_type_boolean, 0 },
2076 	{ "qname-minimization", &cfg_type_qminmethod, 0 },
2077 	/*
2078 	 * Note that the query-source option syntax is different
2079 	 * from the other -source options.
2080 	 */
2081 	{ "query-source", &cfg_type_querysource4, 0 },
2082 	{ "query-source-v6", &cfg_type_querysource6, 0 },
2083 	{ "queryport-pool-ports", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
2084 	{ "queryport-pool-updateinterval", &cfg_type_uint32,
2085 	  CFG_CLAUSEFLAG_OBSOLETE },
2086 	{ "rate-limit", &cfg_type_rrl, 0 },
2087 	{ "recursion", &cfg_type_boolean, 0 },
2088 	{ "request-nsid", &cfg_type_boolean, 0 },
2089 	{ "request-sit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
2090 	{ "require-server-cookie", &cfg_type_boolean, 0 },
2091 	{ "resolver-nonbackoff-tries", &cfg_type_uint32, 0 },
2092 	{ "resolver-query-timeout", &cfg_type_uint32, 0 },
2093 	{ "resolver-retry-interval", &cfg_type_uint32, 0 },
2094 	{ "response-padding", &cfg_type_resppadding, 0 },
2095 	{ "response-policy", &cfg_type_rpz, 0 },
2096 	{ "rfc2308-type1", &cfg_type_boolean, CFG_CLAUSEFLAG_ANCIENT },
2097 	{ "root-delegation-only", &cfg_type_optional_exclude, 0 },
2098 	{ "root-key-sentinel", &cfg_type_boolean, 0 },
2099 	{ "rrset-order", &cfg_type_rrsetorder, 0 },
2100 	{ "send-cookie", &cfg_type_boolean, 0 },
2101 	{ "servfail-ttl", &cfg_type_duration, 0 },
2102 	{ "sortlist", &cfg_type_bracketed_aml, 0 },
2103 	{ "stale-answer-enable", &cfg_type_boolean, 0 },
2104 	{ "stale-answer-client-timeout", &cfg_type_staleanswerclienttimeout,
2105 	  0 },
2106 	{ "stale-answer-ttl", &cfg_type_duration, 0 },
2107 	{ "stale-cache-enable", &cfg_type_boolean, 0 },
2108 	{ "stale-refresh-time", &cfg_type_duration, 0 },
2109 	{ "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
2110 	{ "synth-from-dnssec", &cfg_type_boolean, 0 },
2111 	{ "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_ANCIENT },
2112 	{ "transfer-format", &cfg_type_transferformat, 0 },
2113 	{ "trust-anchor-telemetry", &cfg_type_boolean,
2114 	  CFG_CLAUSEFLAG_EXPERIMENTAL },
2115 	{ "use-queryport-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
2116 	{ "validate-except", &cfg_type_namelist, 0 },
2117 	{ "v6-bias", &cfg_type_uint32, 0 },
2118 	{ "zero-no-soa-ttl-cache", &cfg_type_boolean, 0 },
2119 	{ NULL, NULL, 0 }
2120 };
2121 
2122 /*%
2123  * Clauses that can be found within the 'view' statement only.
2124  */
2125 static cfg_clausedef_t view_only_clauses[] = {
2126 	{ "match-clients", &cfg_type_bracketed_aml, 0 },
2127 	{ "match-destinations", &cfg_type_bracketed_aml, 0 },
2128 	{ "match-recursive-only", &cfg_type_boolean, 0 },
2129 	{ NULL, NULL, 0 }
2130 };
2131 
2132 /*%
2133  * Sig-validity-interval.
2134  */
2135 
2136 static cfg_tuplefielddef_t validityinterval_fields[] = {
2137 	{ "validity", &cfg_type_uint32, 0 },
2138 	{ "re-sign", &cfg_type_optional_uint32, 0 },
2139 	{ NULL, NULL, 0 }
2140 };
2141 
2142 static cfg_type_t cfg_type_validityinterval = {
2143 	"validityinterval", cfg_parse_tuple, cfg_print_tuple,
2144 	cfg_doc_tuple,	    &cfg_rep_tuple,  validityinterval_fields
2145 };
2146 
2147 /*%
2148  * Clauses that can be found in a 'dnssec-policy' statement.
2149  */
2150 static cfg_clausedef_t dnssecpolicy_clauses[] = {
2151 	{ "dnskey-ttl", &cfg_type_duration, 0 },
2152 	{ "keys", &cfg_type_kaspkeys, 0 },
2153 	{ "max-zone-ttl", &cfg_type_duration, 0 },
2154 	{ "nsec3param", &cfg_type_nsec3, 0 },
2155 	{ "parent-ds-ttl", &cfg_type_duration, 0 },
2156 	{ "parent-propagation-delay", &cfg_type_duration, 0 },
2157 	{ "parent-registration-delay", &cfg_type_duration,
2158 	  CFG_CLAUSEFLAG_OBSOLETE },
2159 	{ "publish-safety", &cfg_type_duration, 0 },
2160 	{ "purge-keys", &cfg_type_duration, 0 },
2161 	{ "retire-safety", &cfg_type_duration, 0 },
2162 	{ "signatures-refresh", &cfg_type_duration, 0 },
2163 	{ "signatures-validity", &cfg_type_duration, 0 },
2164 	{ "signatures-validity-dnskey", &cfg_type_duration, 0 },
2165 	{ "zone-propagation-delay", &cfg_type_duration, 0 },
2166 	{ NULL, NULL, 0 }
2167 };
2168 
2169 /*%
2170  * Clauses that can be found in a 'zone' statement,
2171  * with defaults in the 'view' or 'options' statement.
2172  *
2173  * Note: CFG_ZONE_* options indicate in which zone types this clause is
2174  * legal.
2175  */
2176 static cfg_clausedef_t zone_clauses[] = {
2177 	{ "allow-notify", &cfg_type_bracketed_aml,
2178 	  CFG_ZONE_SLAVE | CFG_ZONE_MIRROR },
2179 	{ "allow-query", &cfg_type_bracketed_aml,
2180 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB |
2181 		  CFG_ZONE_REDIRECT | CFG_ZONE_STATICSTUB },
2182 	{ "allow-query-on", &cfg_type_bracketed_aml,
2183 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB |
2184 		  CFG_ZONE_REDIRECT | CFG_ZONE_STATICSTUB },
2185 	{ "allow-transfer", &cfg_type_bracketed_aml,
2186 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR },
2187 	{ "allow-update", &cfg_type_bracketed_aml, CFG_ZONE_MASTER },
2188 	{ "allow-update-forwarding", &cfg_type_bracketed_aml,
2189 	  CFG_ZONE_SLAVE | CFG_ZONE_MIRROR },
2190 	{ "also-notify", &cfg_type_namesockaddrkeylist,
2191 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR },
2192 	{ "alt-transfer-source", &cfg_type_sockaddr4wild,
2193 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR },
2194 	{ "alt-transfer-source-v6", &cfg_type_sockaddr6wild,
2195 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR },
2196 	{ "auto-dnssec", &cfg_type_autodnssec,
2197 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE },
2198 	{ "check-dup-records", &cfg_type_checkmode, CFG_ZONE_MASTER },
2199 	{ "check-integrity", &cfg_type_boolean, CFG_ZONE_MASTER },
2200 	{ "check-mx", &cfg_type_checkmode, CFG_ZONE_MASTER },
2201 	{ "check-mx-cname", &cfg_type_checkmode, CFG_ZONE_MASTER },
2202 	{ "check-sibling", &cfg_type_boolean, CFG_ZONE_MASTER },
2203 	{ "check-spf", &cfg_type_warn, CFG_ZONE_MASTER },
2204 	{ "check-srv-cname", &cfg_type_checkmode, CFG_ZONE_MASTER },
2205 	{ "check-wildcard", &cfg_type_boolean, CFG_ZONE_MASTER },
2206 	{ "dialup", &cfg_type_dialuptype,
2207 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_STUB },
2208 	{ "dnssec-dnskey-kskonly", &cfg_type_boolean,
2209 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE },
2210 	{ "dnssec-loadkeys-interval", &cfg_type_uint32,
2211 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE },
2212 	{ "dnssec-policy", &cfg_type_astring,
2213 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE },
2214 	{ "dnssec-secure-to-insecure", &cfg_type_boolean, CFG_ZONE_MASTER },
2215 	{ "dnssec-update-mode", &cfg_type_dnssecupdatemode,
2216 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE },
2217 	{ "forward", &cfg_type_forwardtype,
2218 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_STUB |
2219 		  CFG_ZONE_STATICSTUB | CFG_ZONE_FORWARD },
2220 	{ "forwarders", &cfg_type_portiplist,
2221 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_STUB |
2222 		  CFG_ZONE_STATICSTUB | CFG_ZONE_FORWARD },
2223 	{ "key-directory", &cfg_type_qstring,
2224 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE },
2225 	{ "maintain-ixfr-base", &cfg_type_boolean, CFG_CLAUSEFLAG_ANCIENT },
2226 	{ "masterfile-format", &cfg_type_masterformat,
2227 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB |
2228 		  CFG_ZONE_REDIRECT },
2229 	{ "masterfile-style", &cfg_type_masterstyle,
2230 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB |
2231 		  CFG_ZONE_REDIRECT },
2232 	{ "max-ixfr-log-size", &cfg_type_size, CFG_CLAUSEFLAG_ANCIENT },
2233 	{ "max-ixfr-ratio", &cfg_type_ixfrratio,
2234 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR },
2235 	{ "max-journal-size", &cfg_type_size,
2236 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR },
2237 	{ "max-records", &cfg_type_uint32,
2238 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB |
2239 		  CFG_ZONE_STATICSTUB | CFG_ZONE_REDIRECT },
2240 	{ "max-refresh-time", &cfg_type_uint32,
2241 	  CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
2242 	{ "max-retry-time", &cfg_type_uint32,
2243 	  CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
2244 	{ "max-transfer-idle-in", &cfg_type_uint32,
2245 	  CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
2246 	{ "max-transfer-idle-out", &cfg_type_uint32,
2247 	  CFG_ZONE_MASTER | CFG_ZONE_MIRROR | CFG_ZONE_SLAVE },
2248 	{ "max-transfer-time-in", &cfg_type_uint32,
2249 	  CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
2250 	{ "max-transfer-time-out", &cfg_type_uint32,
2251 	  CFG_ZONE_MASTER | CFG_ZONE_MIRROR | CFG_ZONE_SLAVE },
2252 	{ "max-zone-ttl", &cfg_type_maxduration,
2253 	  CFG_ZONE_MASTER | CFG_ZONE_REDIRECT },
2254 	{ "min-refresh-time", &cfg_type_uint32,
2255 	  CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
2256 	{ "min-retry-time", &cfg_type_uint32,
2257 	  CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
2258 	{ "multi-master", &cfg_type_boolean,
2259 	  CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
2260 	{ "notify", &cfg_type_notifytype,
2261 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR },
2262 	{ "notify-delay", &cfg_type_uint32,
2263 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR },
2264 	{ "notify-source", &cfg_type_sockaddr4wild,
2265 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR },
2266 	{ "notify-source-v6", &cfg_type_sockaddr6wild,
2267 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR },
2268 	{ "notify-to-soa", &cfg_type_boolean,
2269 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE },
2270 	{ "nsec3-test-zone", &cfg_type_boolean,
2271 	  CFG_CLAUSEFLAG_TESTONLY | CFG_ZONE_MASTER | CFG_ZONE_SLAVE },
2272 	{ "parental-source", &cfg_type_sockaddr4wild,
2273 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE },
2274 	{ "parental-source-v6", &cfg_type_sockaddr6wild,
2275 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE },
2276 	{ "request-expire", &cfg_type_boolean,
2277 	  CFG_ZONE_SLAVE | CFG_ZONE_MIRROR },
2278 	{ "request-ixfr", &cfg_type_boolean, CFG_ZONE_SLAVE | CFG_ZONE_MIRROR },
2279 	{ "serial-update-method", &cfg_type_updatemethod, CFG_ZONE_MASTER },
2280 	{ "sig-signing-nodes", &cfg_type_uint32,
2281 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE },
2282 	{ "sig-signing-signatures", &cfg_type_uint32,
2283 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE },
2284 	{ "sig-signing-type", &cfg_type_uint32,
2285 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE },
2286 	{ "sig-validity-interval", &cfg_type_validityinterval,
2287 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE },
2288 	{ "dnskey-sig-validity", &cfg_type_uint32,
2289 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE },
2290 	{ "transfer-source", &cfg_type_sockaddr4wild,
2291 	  CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
2292 	{ "transfer-source-v6", &cfg_type_sockaddr6wild,
2293 	  CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
2294 	{ "try-tcp-refresh", &cfg_type_boolean,
2295 	  CFG_ZONE_SLAVE | CFG_ZONE_MIRROR },
2296 	{ "update-check-ksk", &cfg_type_boolean,
2297 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE },
2298 	{ "use-alt-transfer-source", &cfg_type_boolean,
2299 	  CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
2300 	{ "zero-no-soa-ttl", &cfg_type_boolean,
2301 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR },
2302 	{ "zone-statistics", &cfg_type_zonestat,
2303 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB |
2304 		  CFG_ZONE_STATICSTUB | CFG_ZONE_REDIRECT },
2305 	{ NULL, NULL, 0 }
2306 };
2307 
2308 /*%
2309  * Clauses that can be found in a 'zone' statement only.
2310  *
2311  * Note: CFG_ZONE_* options indicate in which zone types this clause is
2312  * legal.
2313  */
2314 static cfg_clausedef_t zone_only_clauses[] = {
2315 	/*
2316 	 * Note that the format of the check-names option is different between
2317 	 * the zone options and the global/view options.  Ugh.
2318 	 */
2319 	{ "type", &cfg_type_zonetype,
2320 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB |
2321 		  CFG_ZONE_STATICSTUB | CFG_ZONE_DELEGATION | CFG_ZONE_HINT |
2322 		  CFG_ZONE_REDIRECT | CFG_ZONE_FORWARD },
2323 	{ "check-names", &cfg_type_checkmode,
2324 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_HINT |
2325 		  CFG_ZONE_STUB },
2326 	{ "database", &cfg_type_astring,
2327 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
2328 	{ "delegation-only", &cfg_type_boolean,
2329 	  CFG_ZONE_HINT | CFG_ZONE_STUB | CFG_ZONE_FORWARD },
2330 	{ "dlz", &cfg_type_astring,
2331 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_REDIRECT },
2332 	{ "file", &cfg_type_qstring,
2333 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB |
2334 		  CFG_ZONE_HINT | CFG_ZONE_REDIRECT },
2335 	{ "in-view", &cfg_type_astring, CFG_ZONE_INVIEW },
2336 	{ "inline-signing", &cfg_type_boolean,
2337 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE },
2338 	{ "ixfr-base", &cfg_type_qstring, CFG_CLAUSEFLAG_ANCIENT },
2339 	{ "ixfr-from-differences", &cfg_type_boolean,
2340 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR },
2341 	{ "ixfr-tmp-file", &cfg_type_qstring, CFG_CLAUSEFLAG_ANCIENT },
2342 	{ "journal", &cfg_type_qstring,
2343 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR },
2344 	{ "masters", &cfg_type_namesockaddrkeylist,
2345 	  CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB |
2346 		  CFG_ZONE_REDIRECT },
2347 	{ "parental-agents", &cfg_type_namesockaddrkeylist,
2348 	  CFG_ZONE_MASTER | CFG_ZONE_SLAVE },
2349 	{ "primaries", &cfg_type_namesockaddrkeylist,
2350 	  CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB |
2351 		  CFG_ZONE_REDIRECT },
2352 	{ "pubkey", &cfg_type_pubkey, CFG_CLAUSEFLAG_ANCIENT },
2353 	{ "server-addresses", &cfg_type_bracketed_netaddrlist,
2354 	  CFG_ZONE_STATICSTUB },
2355 	{ "server-names", &cfg_type_namelist, CFG_ZONE_STATICSTUB },
2356 	{ "update-policy", &cfg_type_updatepolicy, CFG_ZONE_MASTER },
2357 	{ NULL, NULL, 0 }
2358 };
2359 
2360 /*% The top-level named.conf syntax. */
2361 
2362 static cfg_clausedef_t *namedconf_clausesets[] = { namedconf_clauses,
2363 						   namedconf_or_view_clauses,
2364 						   NULL };
2365 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_namedconf = {
2366 	"namedconf",	 cfg_parse_mapbody, cfg_print_mapbody,
2367 	cfg_doc_mapbody, &cfg_rep_map,	    namedconf_clausesets
2368 };
2369 
2370 /*% The bind.keys syntax (trust-anchors/managed-keys/trusted-keys only). */
2371 static cfg_clausedef_t *bindkeys_clausesets[] = { bindkeys_clauses, NULL };
2372 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_bindkeys = {
2373 	"bindkeys",	 cfg_parse_mapbody, cfg_print_mapbody,
2374 	cfg_doc_mapbody, &cfg_rep_map,	    bindkeys_clausesets
2375 };
2376 
2377 /*% The "options" statement syntax. */
2378 
2379 static cfg_clausedef_t *options_clausesets[] = { options_clauses, view_clauses,
2380 						 zone_clauses, NULL };
2381 static cfg_type_t cfg_type_options = { "options",     cfg_parse_map,
2382 				       cfg_print_map, cfg_doc_map,
2383 				       &cfg_rep_map,  options_clausesets };
2384 
2385 /*% The "view" statement syntax. */
2386 
2387 static cfg_clausedef_t *view_clausesets[] = { view_only_clauses,
2388 					      namedconf_or_view_clauses,
2389 					      view_clauses, zone_clauses,
2390 					      NULL };
2391 
2392 static cfg_type_t cfg_type_viewopts = { "view",	       cfg_parse_map,
2393 					cfg_print_map, cfg_doc_map,
2394 					&cfg_rep_map,  view_clausesets };
2395 
2396 /*% The "zone" statement syntax. */
2397 
2398 static cfg_clausedef_t *zone_clausesets[] = { zone_only_clauses, zone_clauses,
2399 					      NULL };
2400 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_zoneopts = {
2401 	"zoneopts",  cfg_parse_map, cfg_print_map,
2402 	cfg_doc_map, &cfg_rep_map,  zone_clausesets
2403 };
2404 
2405 /*% The "dnssec-policy" statement syntax. */
2406 static cfg_clausedef_t *dnssecpolicy_clausesets[] = { dnssecpolicy_clauses,
2407 						      NULL };
2408 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_dnssecpolicyopts = {
2409 	"dnssecpolicyopts", cfg_parse_map, cfg_print_map,
2410 	cfg_doc_map,	    &cfg_rep_map,  dnssecpolicy_clausesets
2411 };
2412 
2413 /*% The "dynamically loadable zones" statement syntax. */
2414 
2415 static cfg_clausedef_t dlz_clauses[] = { { "database", &cfg_type_astring, 0 },
2416 					 { "search", &cfg_type_boolean, 0 },
2417 					 { NULL, NULL, 0 } };
2418 static cfg_clausedef_t *dlz_clausesets[] = { dlz_clauses, NULL };
2419 static cfg_type_t cfg_type_dlz = { "dlz",	  cfg_parse_named_map,
2420 				   cfg_print_map, cfg_doc_map,
2421 				   &cfg_rep_map,  dlz_clausesets };
2422 
2423 /*%
2424  * The "dyndb" statement syntax.
2425  */
2426 
2427 static cfg_tuplefielddef_t dyndb_fields[] = {
2428 	{ "name", &cfg_type_astring, 0 },
2429 	{ "library", &cfg_type_qstring, 0 },
2430 	{ "parameters", &cfg_type_bracketed_text, 0 },
2431 	{ NULL, NULL, 0 }
2432 };
2433 
2434 static cfg_type_t cfg_type_dyndb = { "dyndb",	      cfg_parse_tuple,
2435 				     cfg_print_tuple, cfg_doc_tuple,
2436 				     &cfg_rep_tuple,  dyndb_fields };
2437 
2438 /*%
2439  * The "plugin" statement syntax.
2440  * Currently only one plugin type is supported: query.
2441  */
2442 
2443 static const char *plugin_enums[] = { "query", NULL };
2444 static cfg_type_t cfg_type_plugintype = { "plugintype",	     cfg_parse_enum,
2445 					  cfg_print_ustring, cfg_doc_enum,
2446 					  &cfg_rep_string,   plugin_enums };
2447 static cfg_tuplefielddef_t plugin_fields[] = {
2448 	{ "type", &cfg_type_plugintype, 0 },
2449 	{ "library", &cfg_type_astring, 0 },
2450 	{ "parameters", &cfg_type_optional_bracketed_text, 0 },
2451 	{ NULL, NULL, 0 }
2452 };
2453 static cfg_type_t cfg_type_plugin = { "plugin",	       cfg_parse_tuple,
2454 				      cfg_print_tuple, cfg_doc_tuple,
2455 				      &cfg_rep_tuple,  plugin_fields };
2456 
2457 /*%
2458  * Clauses that can be found within the 'key' statement.
2459  */
2460 static cfg_clausedef_t key_clauses[] = { { "algorithm", &cfg_type_astring, 0 },
2461 					 { "secret", &cfg_type_sstring, 0 },
2462 					 { NULL, NULL, 0 } };
2463 
2464 static cfg_clausedef_t *key_clausesets[] = { key_clauses, NULL };
2465 static cfg_type_t cfg_type_key = { "key",	  cfg_parse_named_map,
2466 				   cfg_print_map, cfg_doc_map,
2467 				   &cfg_rep_map,  key_clausesets };
2468 
2469 /*%
2470  * Clauses that can be found in a 'server' statement.
2471  */
2472 static cfg_clausedef_t server_clauses[] = {
2473 	{ "bogus", &cfg_type_boolean, 0 },
2474 	{ "edns", &cfg_type_boolean, 0 },
2475 	{ "edns-udp-size", &cfg_type_uint32, 0 },
2476 	{ "edns-version", &cfg_type_uint32, 0 },
2477 	{ "keys", &cfg_type_server_key_kludge, 0 },
2478 	{ "max-udp-size", &cfg_type_uint32, 0 },
2479 	{ "notify-source", &cfg_type_sockaddr4wild, 0 },
2480 	{ "notify-source-v6", &cfg_type_sockaddr6wild, 0 },
2481 	{ "padding", &cfg_type_uint32, 0 },
2482 	{ "provide-ixfr", &cfg_type_boolean, 0 },
2483 	{ "query-source", &cfg_type_querysource4, 0 },
2484 	{ "query-source-v6", &cfg_type_querysource6, 0 },
2485 	{ "request-expire", &cfg_type_boolean, 0 },
2486 	{ "request-ixfr", &cfg_type_boolean, 0 },
2487 	{ "request-nsid", &cfg_type_boolean, 0 },
2488 	{ "request-sit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
2489 	{ "send-cookie", &cfg_type_boolean, 0 },
2490 	{ "support-ixfr", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
2491 	{ "tcp-keepalive", &cfg_type_boolean, 0 },
2492 	{ "tcp-only", &cfg_type_boolean, 0 },
2493 	{ "transfer-format", &cfg_type_transferformat, 0 },
2494 	{ "transfer-source", &cfg_type_sockaddr4wild, 0 },
2495 	{ "transfer-source-v6", &cfg_type_sockaddr6wild, 0 },
2496 	{ "transfers", &cfg_type_uint32, 0 },
2497 	{ NULL, NULL, 0 }
2498 };
2499 static cfg_clausedef_t *server_clausesets[] = { server_clauses, NULL };
2500 static cfg_type_t cfg_type_server = { "server",	     cfg_parse_netprefix_map,
2501 				      cfg_print_map, cfg_doc_map,
2502 				      &cfg_rep_map,  server_clausesets };
2503 
2504 /*%
2505  * Clauses that can be found in a 'channel' clause in the
2506  * 'logging' statement.
2507  *
2508  * These have some additional constraints that need to be
2509  * checked after parsing:
2510  *  - There must exactly one of file/syslog/null/stderr
2511  */
2512 
2513 static const char *printtime_enums[] = { "iso8601", "iso8601-utc", "local",
2514 					 NULL };
2515 static isc_result_t
2516 parse_printtime(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2517 	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2518 }
2519 static void
2520 doc_printtime(cfg_printer_t *pctx, const cfg_type_t *type) {
2521 	cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
2522 }
2523 static cfg_type_t cfg_type_printtime = { "printtime",	    parse_printtime,
2524 					 cfg_print_ustring, doc_printtime,
2525 					 &cfg_rep_string,   printtime_enums };
2526 
2527 static cfg_clausedef_t channel_clauses[] = {
2528 	/* Destinations.  We no longer require these to be first. */
2529 	{ "file", &cfg_type_logfile, 0 },
2530 	{ "syslog", &cfg_type_optional_facility, 0 },
2531 	{ "null", &cfg_type_void, 0 },
2532 	{ "stderr", &cfg_type_void, 0 },
2533 	/* Options.  We now accept these for the null channel, too. */
2534 	{ "severity", &cfg_type_logseverity, 0 },
2535 	{ "print-time", &cfg_type_printtime, 0 },
2536 	{ "print-severity", &cfg_type_boolean, 0 },
2537 	{ "print-category", &cfg_type_boolean, 0 },
2538 	{ "buffered", &cfg_type_boolean, 0 },
2539 	{ NULL, NULL, 0 }
2540 };
2541 static cfg_clausedef_t *channel_clausesets[] = { channel_clauses, NULL };
2542 static cfg_type_t cfg_type_channel = { "channel",     cfg_parse_named_map,
2543 				       cfg_print_map, cfg_doc_map,
2544 				       &cfg_rep_map,  channel_clausesets };
2545 
2546 /*% A list of log destination, used in the "category" clause. */
2547 static cfg_type_t cfg_type_destinationlist = { "destinationlist",
2548 					       cfg_parse_bracketed_list,
2549 					       cfg_print_bracketed_list,
2550 					       cfg_doc_bracketed_list,
2551 					       &cfg_rep_list,
2552 					       &cfg_type_astring };
2553 
2554 /*%
2555  * Clauses that can be found in a 'logging' statement.
2556  */
2557 static cfg_clausedef_t logging_clauses[] = {
2558 	{ "channel", &cfg_type_channel, CFG_CLAUSEFLAG_MULTI },
2559 	{ "category", &cfg_type_category, CFG_CLAUSEFLAG_MULTI },
2560 	{ NULL, NULL, 0 }
2561 };
2562 static cfg_clausedef_t *logging_clausesets[] = { logging_clauses, NULL };
2563 static cfg_type_t cfg_type_logging = { "logging",     cfg_parse_map,
2564 				       cfg_print_map, cfg_doc_map,
2565 				       &cfg_rep_map,  logging_clausesets };
2566 
2567 /*%
2568  * For parsing an 'addzone' statement
2569  */
2570 static cfg_tuplefielddef_t addzone_fields[] = {
2571 	{ "name", &cfg_type_astring, 0 },
2572 	{ "class", &cfg_type_optional_class, 0 },
2573 	{ "view", &cfg_type_optional_class, 0 },
2574 	{ "options", &cfg_type_zoneopts, 0 },
2575 	{ NULL, NULL, 0 }
2576 };
2577 static cfg_type_t cfg_type_addzone = { "zone",		cfg_parse_tuple,
2578 				       cfg_print_tuple, cfg_doc_tuple,
2579 				       &cfg_rep_tuple,	addzone_fields };
2580 
2581 static cfg_clausedef_t addzoneconf_clauses[] = {
2582 	{ "zone", &cfg_type_addzone, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 }
2583 };
2584 
2585 static cfg_clausedef_t *addzoneconf_clausesets[] = { addzoneconf_clauses,
2586 						     NULL };
2587 
2588 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_addzoneconf = {
2589 	"addzoneconf",	 cfg_parse_mapbody, cfg_print_mapbody,
2590 	cfg_doc_mapbody, &cfg_rep_map,	    addzoneconf_clausesets
2591 };
2592 
2593 static isc_result_t
2594 parse_unitstring(char *str, isc_resourcevalue_t *valuep) {
2595 	char *endp;
2596 	unsigned int len;
2597 	uint64_t value;
2598 	uint64_t unit;
2599 
2600 	value = strtoull(str, &endp, 10);
2601 	if (*endp == 0) {
2602 		*valuep = value;
2603 		return (ISC_R_SUCCESS);
2604 	}
2605 
2606 	len = strlen(str);
2607 	if (len < 2 || endp[1] != '\0') {
2608 		return (ISC_R_FAILURE);
2609 	}
2610 
2611 	switch (str[len - 1]) {
2612 	case 'k':
2613 	case 'K':
2614 		unit = 1024;
2615 		break;
2616 	case 'm':
2617 	case 'M':
2618 		unit = 1024 * 1024;
2619 		break;
2620 	case 'g':
2621 	case 'G':
2622 		unit = 1024 * 1024 * 1024;
2623 		break;
2624 	default:
2625 		return (ISC_R_FAILURE);
2626 	}
2627 	if (value > ((uint64_t)UINT64_MAX / unit)) {
2628 		return (ISC_R_FAILURE);
2629 	}
2630 	*valuep = value * unit;
2631 	return (ISC_R_SUCCESS);
2632 }
2633 
2634 static isc_result_t
2635 parse_sizeval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2636 	isc_result_t result;
2637 	cfg_obj_t *obj = NULL;
2638 	uint64_t val;
2639 
2640 	UNUSED(type);
2641 
2642 	CHECK(cfg_gettoken(pctx, 0));
2643 	if (pctx->token.type != isc_tokentype_string) {
2644 		result = ISC_R_UNEXPECTEDTOKEN;
2645 		goto cleanup;
2646 	}
2647 	CHECK(parse_unitstring(TOKEN_STRING(pctx), &val));
2648 
2649 	CHECK(cfg_create_obj(pctx, &cfg_type_uint64, &obj));
2650 	obj->value.uint64 = val;
2651 	*ret = obj;
2652 	return (ISC_R_SUCCESS);
2653 
2654 cleanup:
2655 	cfg_parser_error(pctx, CFG_LOG_NEAR,
2656 			 "expected integer and optional unit");
2657 	return (result);
2658 }
2659 
2660 static isc_result_t
2661 parse_sizeval_percent(cfg_parser_t *pctx, const cfg_type_t *type,
2662 		      cfg_obj_t **ret) {
2663 	char *endp;
2664 	isc_result_t result;
2665 	cfg_obj_t *obj = NULL;
2666 	uint64_t val;
2667 	uint64_t percent;
2668 
2669 	UNUSED(type);
2670 
2671 	CHECK(cfg_gettoken(pctx, 0));
2672 	if (pctx->token.type != isc_tokentype_string) {
2673 		result = ISC_R_UNEXPECTEDTOKEN;
2674 		goto cleanup;
2675 	}
2676 
2677 	percent = strtoull(TOKEN_STRING(pctx), &endp, 10);
2678 
2679 	if (*endp == '%' && *(endp + 1) == 0) {
2680 		CHECK(cfg_create_obj(pctx, &cfg_type_percentage, &obj));
2681 		obj->value.uint32 = (uint32_t)percent;
2682 		*ret = obj;
2683 		return (ISC_R_SUCCESS);
2684 	} else {
2685 		CHECK(parse_unitstring(TOKEN_STRING(pctx), &val));
2686 		CHECK(cfg_create_obj(pctx, &cfg_type_uint64, &obj));
2687 		obj->value.uint64 = val;
2688 		*ret = obj;
2689 		return (ISC_R_SUCCESS);
2690 	}
2691 
2692 cleanup:
2693 	cfg_parser_error(pctx, CFG_LOG_NEAR,
2694 			 "expected integer and optional unit or percent");
2695 	return (result);
2696 }
2697 
2698 static void
2699 doc_sizeval_percent(cfg_printer_t *pctx, const cfg_type_t *type) {
2700 	UNUSED(type);
2701 
2702 	cfg_print_cstr(pctx, "( ");
2703 	cfg_doc_terminal(pctx, &cfg_type_size);
2704 	cfg_print_cstr(pctx, " | ");
2705 	cfg_doc_terminal(pctx, &cfg_type_percentage);
2706 	cfg_print_cstr(pctx, " )");
2707 }
2708 
2709 /*%
2710  * A size value (number + optional unit).
2711  */
2712 static cfg_type_t cfg_type_sizeval = { "sizeval",	 parse_sizeval,
2713 				       cfg_print_uint64, cfg_doc_terminal,
2714 				       &cfg_rep_uint64,	 NULL };
2715 
2716 /*%
2717  * A size, "unlimited", or "default".
2718  */
2719 
2720 static isc_result_t
2721 parse_size(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2722 	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_sizeval, ret));
2723 }
2724 
2725 static void
2726 doc_size(cfg_printer_t *pctx, const cfg_type_t *type) {
2727 	cfg_doc_enum_or_other(pctx, type, &cfg_type_sizeval);
2728 }
2729 
2730 static const char *size_enums[] = { "default", "unlimited", NULL };
2731 static cfg_type_t cfg_type_size = {
2732 	"size",	  parse_size,	   cfg_print_ustring,
2733 	doc_size, &cfg_rep_string, size_enums
2734 };
2735 
2736 /*%
2737  * A size or "unlimited", but not "default".
2738  */
2739 static const char *sizenodefault_enums[] = { "unlimited", NULL };
2740 static cfg_type_t cfg_type_sizenodefault = {
2741 	"size_no_default", parse_size,	    cfg_print_ustring,
2742 	doc_size,	   &cfg_rep_string, sizenodefault_enums
2743 };
2744 
2745 /*%
2746  * A size in absolute values or percents.
2747  */
2748 static cfg_type_t cfg_type_sizeval_percent = {
2749 	"sizeval_percent",   parse_sizeval_percent, cfg_print_ustring,
2750 	doc_sizeval_percent, &cfg_rep_string,	    NULL
2751 };
2752 
2753 /*%
2754  * A size in absolute values or percents, or "unlimited", or "default"
2755  */
2756 
2757 static isc_result_t
2758 parse_size_or_percent(cfg_parser_t *pctx, const cfg_type_t *type,
2759 		      cfg_obj_t **ret) {
2760 	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_sizeval_percent,
2761 					ret));
2762 }
2763 
2764 static void
2765 doc_parse_size_or_percent(cfg_printer_t *pctx, const cfg_type_t *type) {
2766 	UNUSED(type);
2767 	cfg_print_cstr(pctx, "( default | unlimited | ");
2768 	cfg_doc_terminal(pctx, &cfg_type_sizeval);
2769 	cfg_print_cstr(pctx, " | ");
2770 	cfg_doc_terminal(pctx, &cfg_type_percentage);
2771 	cfg_print_cstr(pctx, " )");
2772 }
2773 
2774 static const char *sizeorpercent_enums[] = { "default", "unlimited", NULL };
2775 static cfg_type_t cfg_type_sizeorpercent = {
2776 	"size_or_percent",	   parse_size_or_percent, cfg_print_ustring,
2777 	doc_parse_size_or_percent, &cfg_rep_string,	  sizeorpercent_enums
2778 };
2779 
2780 /*%
2781  * An IXFR size ratio: percentage, or "unlimited".
2782  */
2783 
2784 static isc_result_t
2785 parse_ixfrratio(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2786 	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_percentage, ret));
2787 }
2788 
2789 static void
2790 doc_ixfrratio(cfg_printer_t *pctx, const cfg_type_t *type) {
2791 	UNUSED(type);
2792 	cfg_print_cstr(pctx, "( unlimited | ");
2793 	cfg_doc_terminal(pctx, &cfg_type_percentage);
2794 	cfg_print_cstr(pctx, " )");
2795 }
2796 
2797 static const char *ixfrratio_enums[] = { "unlimited", NULL };
2798 static cfg_type_t cfg_type_ixfrratio = { "ixfr_ratio", parse_ixfrratio,
2799 					 NULL,	       doc_ixfrratio,
2800 					 NULL,	       ixfrratio_enums };
2801 
2802 /*%
2803  * optional_keyvalue
2804  */
2805 static isc_result_t
2806 parse_maybe_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
2807 			      bool optional, cfg_obj_t **ret) {
2808 	isc_result_t result;
2809 	cfg_obj_t *obj = NULL;
2810 	const keyword_type_t *kw = type->of;
2811 
2812 	CHECK(cfg_peektoken(pctx, 0));
2813 	if (pctx->token.type == isc_tokentype_string &&
2814 	    strcasecmp(TOKEN_STRING(pctx), kw->name) == 0)
2815 	{
2816 		CHECK(cfg_gettoken(pctx, 0));
2817 		CHECK(kw->type->parse(pctx, kw->type, &obj));
2818 		obj->type = type; /* XXX kludge */
2819 	} else {
2820 		if (optional) {
2821 			CHECK(cfg_parse_void(pctx, NULL, &obj));
2822 		} else {
2823 			cfg_parser_error(pctx, CFG_LOG_NEAR, "expected '%s'",
2824 					 kw->name);
2825 			result = ISC_R_UNEXPECTEDTOKEN;
2826 			goto cleanup;
2827 		}
2828 	}
2829 	*ret = obj;
2830 cleanup:
2831 	return (result);
2832 }
2833 
2834 static isc_result_t
2835 parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2836 	return (parse_maybe_optional_keyvalue(pctx, type, false, ret));
2837 }
2838 
2839 static isc_result_t
2840 parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
2841 			cfg_obj_t **ret) {
2842 	return (parse_maybe_optional_keyvalue(pctx, type, true, ret));
2843 }
2844 
2845 static void
2846 print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2847 	const keyword_type_t *kw = obj->type->of;
2848 	cfg_print_cstr(pctx, kw->name);
2849 	cfg_print_cstr(pctx, " ");
2850 	kw->type->print(pctx, obj);
2851 }
2852 
2853 static void
2854 doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) {
2855 	const keyword_type_t *kw = type->of;
2856 	cfg_print_cstr(pctx, kw->name);
2857 	cfg_print_cstr(pctx, " ");
2858 	cfg_doc_obj(pctx, kw->type);
2859 }
2860 
2861 static void
2862 doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) {
2863 	const keyword_type_t *kw = type->of;
2864 	cfg_print_cstr(pctx, "[ ");
2865 	cfg_print_cstr(pctx, kw->name);
2866 	cfg_print_cstr(pctx, " ");
2867 	cfg_doc_obj(pctx, kw->type);
2868 	cfg_print_cstr(pctx, " ]");
2869 }
2870 
2871 static const char *dialup_enums[] = { "notify", "notify-passive", "passive",
2872 				      "refresh", NULL };
2873 static isc_result_t
2874 parse_dialup_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2875 	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2876 }
2877 static void
2878 doc_dialup_type(cfg_printer_t *pctx, const cfg_type_t *type) {
2879 	cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
2880 }
2881 static cfg_type_t cfg_type_dialuptype = { "dialuptype",	     parse_dialup_type,
2882 					  cfg_print_ustring, doc_dialup_type,
2883 					  &cfg_rep_string,   dialup_enums };
2884 
2885 static const char *notify_enums[] = { "explicit", "master-only", "primary-only",
2886 				      NULL };
2887 static isc_result_t
2888 parse_notify_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2889 	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2890 }
2891 static void
2892 doc_notify_type(cfg_printer_t *pctx, const cfg_type_t *type) {
2893 	cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
2894 }
2895 static cfg_type_t cfg_type_notifytype = {
2896 	"notifytype",	 parse_notify_type, cfg_print_ustring,
2897 	doc_notify_type, &cfg_rep_string,   notify_enums,
2898 };
2899 
2900 static const char *minimal_enums[] = { "no-auth", "no-auth-recursive", NULL };
2901 static isc_result_t
2902 parse_minimal(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2903 	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2904 }
2905 static void
2906 doc_minimal(cfg_printer_t *pctx, const cfg_type_t *type) {
2907 	cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
2908 }
2909 static cfg_type_t cfg_type_minimal = {
2910 	"minimal",   parse_minimal,   cfg_print_ustring,
2911 	doc_minimal, &cfg_rep_string, minimal_enums,
2912 };
2913 
2914 static const char *ixfrdiff_enums[] = { "primary", "master", "secondary",
2915 					"slave", NULL };
2916 static isc_result_t
2917 parse_ixfrdiff_type(cfg_parser_t *pctx, const cfg_type_t *type,
2918 		    cfg_obj_t **ret) {
2919 	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2920 }
2921 static void
2922 doc_ixfrdiff_type(cfg_printer_t *pctx, const cfg_type_t *type) {
2923 	cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
2924 }
2925 static cfg_type_t cfg_type_ixfrdifftype = {
2926 	"ixfrdiff",	   parse_ixfrdiff_type, cfg_print_ustring,
2927 	doc_ixfrdiff_type, &cfg_rep_string,	ixfrdiff_enums,
2928 };
2929 
2930 static keyword_type_t key_kw = { "key", &cfg_type_astring };
2931 
2932 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_keyref = {
2933 	"keyref",     parse_keyvalue,  print_keyvalue,
2934 	doc_keyvalue, &cfg_rep_string, &key_kw
2935 };
2936 
2937 static cfg_type_t cfg_type_optional_keyref = {
2938 	"optional_keyref",     parse_optional_keyvalue, print_keyvalue,
2939 	doc_optional_keyvalue, &cfg_rep_string,		&key_kw
2940 };
2941 
2942 static const char *qminmethod_enums[] = { "strict", "relaxed", "disabled",
2943 					  "off", NULL };
2944 
2945 static cfg_type_t cfg_type_qminmethod = { "qminmethod",	     cfg_parse_enum,
2946 					  cfg_print_ustring, cfg_doc_enum,
2947 					  &cfg_rep_string,   qminmethod_enums };
2948 
2949 /*%
2950  * A "controls" statement is represented as a map with the multivalued
2951  * "inet" and "unix" clauses.
2952  */
2953 
2954 static keyword_type_t controls_allow_kw = { "allow", &cfg_type_bracketed_aml };
2955 
2956 static cfg_type_t cfg_type_controls_allow = {
2957 	"controls_allow", parse_keyvalue, print_keyvalue,
2958 	doc_keyvalue,	  &cfg_rep_list,  &controls_allow_kw
2959 };
2960 
2961 static keyword_type_t controls_keys_kw = { "keys", &cfg_type_keylist };
2962 
2963 static cfg_type_t cfg_type_controls_keys = {
2964 	"controls_keys",       parse_optional_keyvalue, print_keyvalue,
2965 	doc_optional_keyvalue, &cfg_rep_list,		&controls_keys_kw
2966 };
2967 
2968 static keyword_type_t controls_readonly_kw = { "read-only", &cfg_type_boolean };
2969 
2970 static cfg_type_t cfg_type_controls_readonly = {
2971 	"controls_readonly",   parse_optional_keyvalue, print_keyvalue,
2972 	doc_optional_keyvalue, &cfg_rep_boolean,	&controls_readonly_kw
2973 };
2974 
2975 static cfg_tuplefielddef_t inetcontrol_fields[] = {
2976 	{ "address", &cfg_type_controls_sockaddr, 0 },
2977 	{ "allow", &cfg_type_controls_allow, 0 },
2978 	{ "keys", &cfg_type_controls_keys, 0 },
2979 	{ "read-only", &cfg_type_controls_readonly, 0 },
2980 	{ NULL, NULL, 0 }
2981 };
2982 
2983 static cfg_type_t cfg_type_inetcontrol = {
2984 	"inetcontrol", cfg_parse_tuple, cfg_print_tuple,
2985 	cfg_doc_tuple, &cfg_rep_tuple,	inetcontrol_fields
2986 };
2987 
2988 static keyword_type_t controls_perm_kw = { "perm", &cfg_type_uint32 };
2989 
2990 static cfg_type_t cfg_type_controls_perm = {
2991 	"controls_perm", parse_keyvalue,  print_keyvalue,
2992 	doc_keyvalue,	 &cfg_rep_uint32, &controls_perm_kw
2993 };
2994 
2995 static keyword_type_t controls_owner_kw = { "owner", &cfg_type_uint32 };
2996 
2997 static cfg_type_t cfg_type_controls_owner = {
2998 	"controls_owner", parse_keyvalue,  print_keyvalue,
2999 	doc_keyvalue,	  &cfg_rep_uint32, &controls_owner_kw
3000 };
3001 
3002 static keyword_type_t controls_group_kw = { "group", &cfg_type_uint32 };
3003 
3004 static cfg_type_t cfg_type_controls_group = {
3005 	"controls_allow", parse_keyvalue,  print_keyvalue,
3006 	doc_keyvalue,	  &cfg_rep_uint32, &controls_group_kw
3007 };
3008 
3009 static cfg_tuplefielddef_t unixcontrol_fields[] = {
3010 	{ "path", &cfg_type_qstring, 0 },
3011 	{ "perm", &cfg_type_controls_perm, 0 },
3012 	{ "owner", &cfg_type_controls_owner, 0 },
3013 	{ "group", &cfg_type_controls_group, 0 },
3014 	{ "keys", &cfg_type_controls_keys, 0 },
3015 	{ "read-only", &cfg_type_controls_readonly, 0 },
3016 	{ NULL, NULL, 0 }
3017 };
3018 
3019 static cfg_type_t cfg_type_unixcontrol = {
3020 	"unixcontrol", cfg_parse_tuple, cfg_print_tuple,
3021 	cfg_doc_tuple, &cfg_rep_tuple,	unixcontrol_fields
3022 };
3023 
3024 static cfg_clausedef_t controls_clauses[] = {
3025 	{ "inet", &cfg_type_inetcontrol, CFG_CLAUSEFLAG_MULTI },
3026 	{ "unix", &cfg_type_unixcontrol, CFG_CLAUSEFLAG_MULTI },
3027 	{ NULL, NULL, 0 }
3028 };
3029 
3030 static cfg_clausedef_t *controls_clausesets[] = { controls_clauses, NULL };
3031 static cfg_type_t cfg_type_controls = { "controls",    cfg_parse_map,
3032 					cfg_print_map, cfg_doc_map,
3033 					&cfg_rep_map,  &controls_clausesets };
3034 
3035 /*%
3036  * A "statistics-channels" statement is represented as a map with the
3037  * multivalued "inet" clauses.
3038  */
3039 static void
3040 doc_optional_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
3041 	const keyword_type_t *kw = type->of;
3042 	cfg_print_cstr(pctx, "[ ");
3043 	cfg_print_cstr(pctx, kw->name);
3044 	cfg_print_cstr(pctx, " ");
3045 	cfg_doc_obj(pctx, kw->type);
3046 	cfg_print_cstr(pctx, " ]");
3047 }
3048 
3049 static cfg_type_t cfg_type_optional_allow = {
3050 	"optional_allow", parse_optional_keyvalue,
3051 	print_keyvalue,	  doc_optional_bracketed_list,
3052 	&cfg_rep_list,	  &controls_allow_kw
3053 };
3054 
3055 static cfg_tuplefielddef_t statserver_fields[] = {
3056 	{ "address", &cfg_type_controls_sockaddr, 0 }, /* reuse controls def */
3057 	{ "allow", &cfg_type_optional_allow, 0 },
3058 	{ NULL, NULL, 0 }
3059 };
3060 
3061 static cfg_type_t cfg_type_statschannel = {
3062 	"statschannel", cfg_parse_tuple, cfg_print_tuple,
3063 	cfg_doc_tuple,	&cfg_rep_tuple,	 statserver_fields
3064 };
3065 
3066 static cfg_clausedef_t statservers_clauses[] = {
3067 	{ "inet", &cfg_type_statschannel, CFG_CLAUSEFLAG_MULTI },
3068 	{ NULL, NULL, 0 }
3069 };
3070 
3071 static cfg_clausedef_t *statservers_clausesets[] = { statservers_clauses,
3072 						     NULL };
3073 
3074 static cfg_type_t cfg_type_statschannels = {
3075 	"statistics-channels", cfg_parse_map, cfg_print_map,
3076 	cfg_doc_map,	       &cfg_rep_map,  &statservers_clausesets
3077 };
3078 
3079 /*%
3080  * An optional class, as used in view and zone statements.
3081  */
3082 static isc_result_t
3083 parse_optional_class(cfg_parser_t *pctx, const cfg_type_t *type,
3084 		     cfg_obj_t **ret) {
3085 	isc_result_t result;
3086 	UNUSED(type);
3087 	CHECK(cfg_peektoken(pctx, 0));
3088 	if (pctx->token.type == isc_tokentype_string) {
3089 		CHECK(cfg_parse_obj(pctx, &cfg_type_ustring, ret));
3090 	} else {
3091 		CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
3092 	}
3093 cleanup:
3094 	return (result);
3095 }
3096 
3097 static void
3098 doc_optional_class(cfg_printer_t *pctx, const cfg_type_t *type) {
3099 	UNUSED(type);
3100 	cfg_print_cstr(pctx, "[ <class> ]");
3101 }
3102 
3103 static cfg_type_t cfg_type_optional_class = { "optional_class",
3104 					      parse_optional_class,
3105 					      NULL,
3106 					      doc_optional_class,
3107 					      NULL,
3108 					      NULL };
3109 
3110 static isc_result_t
3111 parse_querysource(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3112 	isc_result_t result;
3113 	cfg_obj_t *obj = NULL;
3114 	isc_netaddr_t netaddr;
3115 	in_port_t port = 0;
3116 	isc_dscp_t dscp = -1;
3117 	unsigned int have_address = 0;
3118 	unsigned int have_port = 0;
3119 	unsigned int have_dscp = 0;
3120 	const unsigned int *flagp = type->of;
3121 
3122 	if ((*flagp & CFG_ADDR_V4OK) != 0) {
3123 		isc_netaddr_any(&netaddr);
3124 	} else if ((*flagp & CFG_ADDR_V6OK) != 0) {
3125 		isc_netaddr_any6(&netaddr);
3126 	} else {
3127 		INSIST(0);
3128 		ISC_UNREACHABLE();
3129 	}
3130 
3131 	for (;;) {
3132 		CHECK(cfg_peektoken(pctx, 0));
3133 		if (pctx->token.type == isc_tokentype_string) {
3134 			if (strcasecmp(TOKEN_STRING(pctx), "address") == 0) {
3135 				/* read "address" */
3136 				CHECK(cfg_gettoken(pctx, 0));
3137 				CHECK(cfg_parse_rawaddr(pctx, *flagp,
3138 							&netaddr));
3139 				have_address++;
3140 			} else if (strcasecmp(TOKEN_STRING(pctx), "port") == 0)
3141 			{
3142 				/* read "port" */
3143 				CHECK(cfg_gettoken(pctx, 0));
3144 				CHECK(cfg_parse_rawport(pctx, CFG_ADDR_WILDOK,
3145 							&port));
3146 				have_port++;
3147 			} else if (strcasecmp(TOKEN_STRING(pctx), "dscp") == 0)
3148 			{
3149 				/* read "dscp" */
3150 				CHECK(cfg_gettoken(pctx, 0));
3151 				CHECK(cfg_parse_dscp(pctx, &dscp));
3152 				have_dscp++;
3153 			} else if (have_port == 0 && have_dscp == 0 &&
3154 				   have_address == 0) {
3155 				return (cfg_parse_sockaddr(pctx, type, ret));
3156 			} else {
3157 				cfg_parser_error(pctx, CFG_LOG_NEAR,
3158 						 "expected 'address', 'port', "
3159 						 "or 'dscp'");
3160 				return (ISC_R_UNEXPECTEDTOKEN);
3161 			}
3162 		} else {
3163 			break;
3164 		}
3165 	}
3166 	if (have_address > 1 || have_port > 1 || have_address + have_port == 0)
3167 	{
3168 		cfg_parser_error(pctx, 0, "expected one address and/or port");
3169 		return (ISC_R_UNEXPECTEDTOKEN);
3170 	}
3171 
3172 	if (have_dscp > 1) {
3173 		cfg_parser_error(pctx, 0, "expected at most one dscp");
3174 		return (ISC_R_UNEXPECTEDTOKEN);
3175 	}
3176 
3177 	CHECK(cfg_create_obj(pctx, &cfg_type_querysource, &obj));
3178 	isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
3179 	obj->value.sockaddrdscp.dscp = dscp;
3180 	*ret = obj;
3181 	return (ISC_R_SUCCESS);
3182 
3183 cleanup:
3184 	cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid query source");
3185 	CLEANUP_OBJ(obj);
3186 	return (result);
3187 }
3188 
3189 static void
3190 print_querysource(cfg_printer_t *pctx, const cfg_obj_t *obj) {
3191 	isc_netaddr_t na;
3192 	isc_netaddr_fromsockaddr(&na, &obj->value.sockaddr);
3193 	cfg_print_cstr(pctx, "address ");
3194 	cfg_print_rawaddr(pctx, &na);
3195 	cfg_print_cstr(pctx, " port ");
3196 	cfg_print_rawuint(pctx, isc_sockaddr_getport(&obj->value.sockaddr));
3197 	if (obj->value.sockaddrdscp.dscp != -1) {
3198 		cfg_print_cstr(pctx, " dscp ");
3199 		cfg_print_rawuint(pctx, obj->value.sockaddrdscp.dscp);
3200 	}
3201 }
3202 
3203 static void
3204 doc_querysource(cfg_printer_t *pctx, const cfg_type_t *type) {
3205 	const unsigned int *flagp = type->of;
3206 
3207 	cfg_print_cstr(pctx, "( ( [ address ] ( ");
3208 	if ((*flagp & CFG_ADDR_V4OK) != 0) {
3209 		cfg_print_cstr(pctx, "<ipv4_address>");
3210 	} else if ((*flagp & CFG_ADDR_V6OK) != 0) {
3211 		cfg_print_cstr(pctx, "<ipv6_address>");
3212 	} else {
3213 		INSIST(0);
3214 		ISC_UNREACHABLE();
3215 	}
3216 	cfg_print_cstr(pctx, " | * ) [ port ( <integer> | * ) ] ) | "
3217 			     "( [ [ address ] ( ");
3218 	if ((*flagp & CFG_ADDR_V4OK) != 0) {
3219 		cfg_print_cstr(pctx, "<ipv4_address>");
3220 	} else if ((*flagp & CFG_ADDR_V6OK) != 0) {
3221 		cfg_print_cstr(pctx, "<ipv6_address>");
3222 	} else {
3223 		INSIST(0);
3224 		ISC_UNREACHABLE();
3225 	}
3226 	cfg_print_cstr(pctx, " | * ) ] port ( <integer> | * ) ) )"
3227 			     " [ dscp <integer> ]");
3228 }
3229 
3230 static unsigned int sockaddr4wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V4OK |
3231 					  CFG_ADDR_DSCPOK;
3232 static unsigned int sockaddr6wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V6OK |
3233 					  CFG_ADDR_DSCPOK;
3234 
3235 static cfg_type_t cfg_type_querysource4 = {
3236 	"querysource4", parse_querysource,   NULL, doc_querysource,
3237 	NULL,		&sockaddr4wild_flags
3238 };
3239 
3240 static cfg_type_t cfg_type_querysource6 = {
3241 	"querysource6", parse_querysource,   NULL, doc_querysource,
3242 	NULL,		&sockaddr6wild_flags
3243 };
3244 
3245 static cfg_type_t cfg_type_querysource = { "querysource",     NULL,
3246 					   print_querysource, NULL,
3247 					   &cfg_rep_sockaddr, NULL };
3248 
3249 /*%
3250  * The socket address syntax in the "controls" statement is silly.
3251  * It allows both socket address families, but also allows "*",
3252  * which is gratuitously interpreted as the IPv4 wildcard address.
3253  */
3254 static unsigned int controls_sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK |
3255 					      CFG_ADDR_WILDOK;
3256 static cfg_type_t cfg_type_controls_sockaddr = {
3257 	"controls_sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr,
3258 	cfg_doc_sockaddr,    &cfg_rep_sockaddr,	 &controls_sockaddr_flags
3259 };
3260 
3261 /*%
3262  * Handle the special kludge syntax of the "keys" clause in the "server"
3263  * statement, which takes a single key with or without braces and semicolon.
3264  */
3265 static isc_result_t
3266 parse_server_key_kludge(cfg_parser_t *pctx, const cfg_type_t *type,
3267 			cfg_obj_t **ret) {
3268 	isc_result_t result;
3269 	bool braces = false;
3270 	UNUSED(type);
3271 
3272 	/* Allow opening brace. */
3273 	CHECK(cfg_peektoken(pctx, 0));
3274 	if (pctx->token.type == isc_tokentype_special &&
3275 	    pctx->token.value.as_char == '{')
3276 	{
3277 		CHECK(cfg_gettoken(pctx, 0));
3278 		braces = true;
3279 	}
3280 
3281 	CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret));
3282 
3283 	if (braces) {
3284 		/* Skip semicolon if present. */
3285 		CHECK(cfg_peektoken(pctx, 0));
3286 		if (pctx->token.type == isc_tokentype_special &&
3287 		    pctx->token.value.as_char == ';')
3288 		{
3289 			CHECK(cfg_gettoken(pctx, 0));
3290 		}
3291 
3292 		CHECK(cfg_parse_special(pctx, '}'));
3293 	}
3294 cleanup:
3295 	return (result);
3296 }
3297 static cfg_type_t cfg_type_server_key_kludge = {
3298 	"server_key", parse_server_key_kludge, NULL, cfg_doc_terminal, NULL,
3299 	NULL
3300 };
3301 
3302 /*%
3303  * An optional logging facility.
3304  */
3305 
3306 static isc_result_t
3307 parse_optional_facility(cfg_parser_t *pctx, const cfg_type_t *type,
3308 			cfg_obj_t **ret) {
3309 	isc_result_t result;
3310 	UNUSED(type);
3311 
3312 	CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
3313 	if (pctx->token.type == isc_tokentype_string ||
3314 	    pctx->token.type == isc_tokentype_qstring)
3315 	{
3316 		CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret));
3317 	} else {
3318 		CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
3319 	}
3320 cleanup:
3321 	return (result);
3322 }
3323 
3324 static void
3325 doc_optional_facility(cfg_printer_t *pctx, const cfg_type_t *type) {
3326 	UNUSED(type);
3327 	cfg_print_cstr(pctx, "[ <syslog_facility> ]");
3328 }
3329 
3330 static cfg_type_t cfg_type_optional_facility = { "optional_facility",
3331 						 parse_optional_facility,
3332 						 NULL,
3333 						 doc_optional_facility,
3334 						 NULL,
3335 						 NULL };
3336 
3337 /*%
3338  * A log severity.  Return as a string, except "debug N",
3339  * which is returned as a keyword object.
3340  */
3341 
3342 static keyword_type_t debug_kw = { "debug", &cfg_type_uint32 };
3343 static cfg_type_t cfg_type_debuglevel = { "debuglevel",	   parse_keyvalue,
3344 					  print_keyvalue,  doc_keyvalue,
3345 					  &cfg_rep_uint32, &debug_kw };
3346 
3347 static isc_result_t
3348 parse_logseverity(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3349 	isc_result_t result;
3350 	UNUSED(type);
3351 
3352 	CHECK(cfg_peektoken(pctx, 0));
3353 	if (pctx->token.type == isc_tokentype_string &&
3354 	    strcasecmp(TOKEN_STRING(pctx), "debug") == 0)
3355 	{
3356 		CHECK(cfg_gettoken(pctx, 0)); /* read "debug" */
3357 		CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER));
3358 		if (pctx->token.type == isc_tokentype_number) {
3359 			CHECK(cfg_parse_uint32(pctx, NULL, ret));
3360 		} else {
3361 			/*
3362 			 * The debug level is optional and defaults to 1.
3363 			 * This makes little sense, but we support it for
3364 			 * compatibility with BIND 8.
3365 			 */
3366 			CHECK(cfg_create_obj(pctx, &cfg_type_uint32, ret));
3367 			(*ret)->value.uint32 = 1;
3368 		}
3369 		(*ret)->type = &cfg_type_debuglevel; /* XXX kludge */
3370 	} else {
3371 		CHECK(cfg_parse_obj(pctx, &cfg_type_loglevel, ret));
3372 	}
3373 cleanup:
3374 	return (result);
3375 }
3376 
3377 static cfg_type_t cfg_type_logseverity = { "log_severity", parse_logseverity,
3378 					   NULL,	   cfg_doc_terminal,
3379 					   NULL,	   NULL };
3380 
3381 /*%
3382  * The "file" clause of the "channel" statement.
3383  * This is yet another special case.
3384  */
3385 
3386 static const char *logversions_enums[] = { "unlimited", NULL };
3387 static isc_result_t
3388 parse_logversions(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3389 	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_uint32, ret));
3390 }
3391 
3392 static void
3393 doc_logversions(cfg_printer_t *pctx, const cfg_type_t *type) {
3394 	cfg_doc_enum_or_other(pctx, type, &cfg_type_uint32);
3395 }
3396 
3397 static cfg_type_t cfg_type_logversions = {
3398 	"logversions",	 parse_logversions, cfg_print_ustring,
3399 	doc_logversions, &cfg_rep_string,   logversions_enums
3400 };
3401 
3402 static const char *logsuffix_enums[] = { "increment", "timestamp", NULL };
3403 static cfg_type_t cfg_type_logsuffix = { "logsuffix",	    cfg_parse_enum,
3404 					 cfg_print_ustring, cfg_doc_enum,
3405 					 &cfg_rep_string,   &logsuffix_enums };
3406 
3407 static cfg_tuplefielddef_t logfile_fields[] = {
3408 	{ "file", &cfg_type_qstring, 0 },
3409 	{ "versions", &cfg_type_logversions, 0 },
3410 	{ "size", &cfg_type_size, 0 },
3411 	{ "suffix", &cfg_type_logsuffix, 0 },
3412 	{ NULL, NULL, 0 }
3413 };
3414 
3415 static isc_result_t
3416 parse_logfile(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3417 	isc_result_t result;
3418 	cfg_obj_t *obj = NULL;
3419 	const cfg_tuplefielddef_t *fields = type->of;
3420 
3421 	CHECK(cfg_create_tuple(pctx, type, &obj));
3422 
3423 	/* Parse the mandatory "file" field */
3424 	CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
3425 
3426 	/* Parse "versions" and "size" fields in any order. */
3427 	for (;;) {
3428 		CHECK(cfg_peektoken(pctx, 0));
3429 		if (pctx->token.type == isc_tokentype_string) {
3430 			CHECK(cfg_gettoken(pctx, 0));
3431 			if (strcasecmp(TOKEN_STRING(pctx), "versions") == 0 &&
3432 			    obj->value.tuple[1] == NULL)
3433 			{
3434 				CHECK(cfg_parse_obj(pctx, fields[1].type,
3435 						    &obj->value.tuple[1]));
3436 			} else if (strcasecmp(TOKEN_STRING(pctx), "size") ==
3437 					   0 &&
3438 				   obj->value.tuple[2] == NULL)
3439 			{
3440 				CHECK(cfg_parse_obj(pctx, fields[2].type,
3441 						    &obj->value.tuple[2]));
3442 			} else if (strcasecmp(TOKEN_STRING(pctx), "suffix") ==
3443 					   0 &&
3444 				   obj->value.tuple[3] == NULL)
3445 			{
3446 				CHECK(cfg_parse_obj(pctx, fields[3].type,
3447 						    &obj->value.tuple[3]));
3448 			} else {
3449 				break;
3450 			}
3451 		} else {
3452 			break;
3453 		}
3454 	}
3455 
3456 	/* Create void objects for missing optional values. */
3457 	if (obj->value.tuple[1] == NULL) {
3458 		CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1]));
3459 	}
3460 	if (obj->value.tuple[2] == NULL) {
3461 		CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2]));
3462 	}
3463 	if (obj->value.tuple[3] == NULL) {
3464 		CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[3]));
3465 	}
3466 
3467 	*ret = obj;
3468 	return (ISC_R_SUCCESS);
3469 
3470 cleanup:
3471 	CLEANUP_OBJ(obj);
3472 	return (result);
3473 }
3474 
3475 static void
3476 print_logfile(cfg_printer_t *pctx, const cfg_obj_t *obj) {
3477 	cfg_print_obj(pctx, obj->value.tuple[0]); /* file */
3478 	if (obj->value.tuple[1]->type->print != cfg_print_void) {
3479 		cfg_print_cstr(pctx, " versions ");
3480 		cfg_print_obj(pctx, obj->value.tuple[1]);
3481 	}
3482 	if (obj->value.tuple[2]->type->print != cfg_print_void) {
3483 		cfg_print_cstr(pctx, " size ");
3484 		cfg_print_obj(pctx, obj->value.tuple[2]);
3485 	}
3486 	if (obj->value.tuple[3]->type->print != cfg_print_void) {
3487 		cfg_print_cstr(pctx, " suffix ");
3488 		cfg_print_obj(pctx, obj->value.tuple[3]);
3489 	}
3490 }
3491 
3492 static void
3493 doc_logfile(cfg_printer_t *pctx, const cfg_type_t *type) {
3494 	UNUSED(type);
3495 	cfg_print_cstr(pctx, "<quoted_string>");
3496 	cfg_print_cstr(pctx, " ");
3497 	cfg_print_cstr(pctx, "[ versions ( unlimited | <integer> ) ]");
3498 	cfg_print_cstr(pctx, " ");
3499 	cfg_print_cstr(pctx, "[ size <size> ]");
3500 	cfg_print_cstr(pctx, " ");
3501 	cfg_print_cstr(pctx, "[ suffix ( increment | timestamp ) ]");
3502 }
3503 
3504 static cfg_type_t cfg_type_logfile = { "log_file",     parse_logfile,
3505 				       print_logfile,  doc_logfile,
3506 				       &cfg_rep_tuple, logfile_fields };
3507 
3508 /*% An IPv4 address with optional dscp and port, "*" accepted as wildcard. */
3509 static cfg_type_t cfg_type_sockaddr4wild = {
3510 	"sockaddr4wild",  cfg_parse_sockaddr, cfg_print_sockaddr,
3511 	cfg_doc_sockaddr, &cfg_rep_sockaddr,  &sockaddr4wild_flags
3512 };
3513 
3514 /*% An IPv6 address with optional port, "*" accepted as wildcard. */
3515 static cfg_type_t cfg_type_sockaddr6wild = {
3516 	"v6addrportwild", cfg_parse_sockaddr, cfg_print_sockaddr,
3517 	cfg_doc_sockaddr, &cfg_rep_sockaddr,  &sockaddr6wild_flags
3518 };
3519 
3520 /*%
3521  * rndc
3522  */
3523 
3524 static cfg_clausedef_t rndcconf_options_clauses[] = {
3525 	{ "default-key", &cfg_type_astring, 0 },
3526 	{ "default-port", &cfg_type_uint32, 0 },
3527 	{ "default-server", &cfg_type_astring, 0 },
3528 	{ "default-source-address", &cfg_type_netaddr4wild, 0 },
3529 	{ "default-source-address-v6", &cfg_type_netaddr6wild, 0 },
3530 	{ NULL, NULL, 0 }
3531 };
3532 
3533 static cfg_clausedef_t *rndcconf_options_clausesets[] = {
3534 	rndcconf_options_clauses, NULL
3535 };
3536 
3537 static cfg_type_t cfg_type_rndcconf_options = {
3538 	"rndcconf_options", cfg_parse_map, cfg_print_map,
3539 	cfg_doc_map,	    &cfg_rep_map,  rndcconf_options_clausesets
3540 };
3541 
3542 static cfg_clausedef_t rndcconf_server_clauses[] = {
3543 	{ "key", &cfg_type_astring, 0 },
3544 	{ "port", &cfg_type_uint32, 0 },
3545 	{ "source-address", &cfg_type_netaddr4wild, 0 },
3546 	{ "source-address-v6", &cfg_type_netaddr6wild, 0 },
3547 	{ "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 },
3548 	{ NULL, NULL, 0 }
3549 };
3550 
3551 static cfg_clausedef_t *rndcconf_server_clausesets[] = {
3552 	rndcconf_server_clauses, NULL
3553 };
3554 
3555 static cfg_type_t cfg_type_rndcconf_server = {
3556 	"rndcconf_server", cfg_parse_named_map, cfg_print_map,
3557 	cfg_doc_map,	   &cfg_rep_map,	rndcconf_server_clausesets
3558 };
3559 
3560 static cfg_clausedef_t rndcconf_clauses[] = {
3561 	{ "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
3562 	{ "server", &cfg_type_rndcconf_server, CFG_CLAUSEFLAG_MULTI },
3563 	{ "options", &cfg_type_rndcconf_options, 0 },
3564 	{ NULL, NULL, 0 }
3565 };
3566 
3567 static cfg_clausedef_t *rndcconf_clausesets[] = { rndcconf_clauses, NULL };
3568 
3569 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndcconf = {
3570 	"rndcconf",	 cfg_parse_mapbody, cfg_print_mapbody,
3571 	cfg_doc_mapbody, &cfg_rep_map,	    rndcconf_clausesets
3572 };
3573 
3574 static cfg_clausedef_t rndckey_clauses[] = { { "key", &cfg_type_key, 0 },
3575 					     { NULL, NULL, 0 } };
3576 
3577 static cfg_clausedef_t *rndckey_clausesets[] = { rndckey_clauses, NULL };
3578 
3579 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndckey = {
3580 	"rndckey",	 cfg_parse_mapbody, cfg_print_mapbody,
3581 	cfg_doc_mapbody, &cfg_rep_map,	    rndckey_clausesets
3582 };
3583 
3584 /*
3585  * session.key has exactly the same syntax as rndc.key, but it's defined
3586  * separately for clarity (and so we can extend it someday, if needed).
3587  */
3588 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_sessionkey = {
3589 	"sessionkey",	 cfg_parse_mapbody, cfg_print_mapbody,
3590 	cfg_doc_mapbody, &cfg_rep_map,	    rndckey_clausesets
3591 };
3592 
3593 static cfg_tuplefielddef_t nameport_fields[] = {
3594 	{ "name", &cfg_type_astring, 0 },
3595 	{ "port", &cfg_type_optional_port, 0 },
3596 	{ "dscp", &cfg_type_optional_dscp, 0 },
3597 	{ NULL, NULL, 0 }
3598 };
3599 
3600 static cfg_type_t cfg_type_nameport = { "nameport",	 cfg_parse_tuple,
3601 					cfg_print_tuple, cfg_doc_tuple,
3602 					&cfg_rep_tuple,	 nameport_fields };
3603 
3604 static void
3605 doc_sockaddrnameport(cfg_printer_t *pctx, const cfg_type_t *type) {
3606 	UNUSED(type);
3607 	cfg_print_cstr(pctx, "( ");
3608 	cfg_print_cstr(pctx, "<quoted_string>");
3609 	cfg_print_cstr(pctx, " ");
3610 	cfg_print_cstr(pctx, "[ port <integer> ]");
3611 	cfg_print_cstr(pctx, " ");
3612 	cfg_print_cstr(pctx, "[ dscp <integer> ]");
3613 	cfg_print_cstr(pctx, " | ");
3614 	cfg_print_cstr(pctx, "<ipv4_address>");
3615 	cfg_print_cstr(pctx, " ");
3616 	cfg_print_cstr(pctx, "[ port <integer> ]");
3617 	cfg_print_cstr(pctx, " ");
3618 	cfg_print_cstr(pctx, "[ dscp <integer> ]");
3619 	cfg_print_cstr(pctx, " | ");
3620 	cfg_print_cstr(pctx, "<ipv6_address>");
3621 	cfg_print_cstr(pctx, " ");
3622 	cfg_print_cstr(pctx, "[ port <integer> ]");
3623 	cfg_print_cstr(pctx, " ");
3624 	cfg_print_cstr(pctx, "[ dscp <integer> ]");
3625 	cfg_print_cstr(pctx, " )");
3626 }
3627 
3628 static isc_result_t
3629 parse_sockaddrnameport(cfg_parser_t *pctx, const cfg_type_t *type,
3630 		       cfg_obj_t **ret) {
3631 	isc_result_t result;
3632 	cfg_obj_t *obj = NULL;
3633 	UNUSED(type);
3634 
3635 	CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
3636 	if (pctx->token.type == isc_tokentype_string ||
3637 	    pctx->token.type == isc_tokentype_qstring)
3638 	{
3639 		if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK))
3640 		{
3641 			CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr,
3642 						 ret));
3643 		} else {
3644 			const cfg_tuplefielddef_t *fields =
3645 				cfg_type_nameport.of;
3646 			CHECK(cfg_create_tuple(pctx, &cfg_type_nameport, &obj));
3647 			CHECK(cfg_parse_obj(pctx, fields[0].type,
3648 					    &obj->value.tuple[0]));
3649 			CHECK(cfg_parse_obj(pctx, fields[1].type,
3650 					    &obj->value.tuple[1]));
3651 			CHECK(cfg_parse_obj(pctx, fields[2].type,
3652 					    &obj->value.tuple[2]));
3653 			*ret = obj;
3654 			obj = NULL;
3655 		}
3656 	} else {
3657 		cfg_parser_error(pctx, CFG_LOG_NEAR,
3658 				 "expected IP address or hostname");
3659 		return (ISC_R_UNEXPECTEDTOKEN);
3660 	}
3661 cleanup:
3662 	CLEANUP_OBJ(obj);
3663 	return (result);
3664 }
3665 
3666 static cfg_type_t cfg_type_sockaddrnameport = { "sockaddrnameport_element",
3667 						parse_sockaddrnameport,
3668 						NULL,
3669 						doc_sockaddrnameport,
3670 						NULL,
3671 						NULL };
3672 
3673 static cfg_type_t cfg_type_bracketed_sockaddrnameportlist = {
3674 	"bracketed_sockaddrnameportlist",
3675 	cfg_parse_bracketed_list,
3676 	cfg_print_bracketed_list,
3677 	cfg_doc_bracketed_list,
3678 	&cfg_rep_list,
3679 	&cfg_type_sockaddrnameport
3680 };
3681 
3682 /*%
3683  * A list of socket addresses or name with an optional default port,
3684  * as used in the dual-stack-servers option.  E.g.,
3685  * "port 1234 { dual-stack-servers.net; 10.0.0.1; 1::2 port 69; }"
3686  */
3687 static cfg_tuplefielddef_t nameportiplist_fields[] = {
3688 	{ "port", &cfg_type_optional_port, 0 },
3689 	{ "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 },
3690 	{ NULL, NULL, 0 }
3691 };
3692 
3693 static cfg_type_t cfg_type_nameportiplist = {
3694 	"nameportiplist", cfg_parse_tuple, cfg_print_tuple,
3695 	cfg_doc_tuple,	  &cfg_rep_tuple,  nameportiplist_fields
3696 };
3697 
3698 /*%
3699  * primaries element.
3700  */
3701 
3702 static void
3703 doc_remoteselement(cfg_printer_t *pctx, const cfg_type_t *type) {
3704 	UNUSED(type);
3705 	cfg_print_cstr(pctx, "( ");
3706 	cfg_print_cstr(pctx, "<remote-servers>");
3707 	cfg_print_cstr(pctx, " | ");
3708 	cfg_print_cstr(pctx, "<ipv4_address>");
3709 	cfg_print_cstr(pctx, " ");
3710 	cfg_print_cstr(pctx, "[ port <integer> ]");
3711 	cfg_print_cstr(pctx, " | ");
3712 	cfg_print_cstr(pctx, "<ipv6_address>");
3713 	cfg_print_cstr(pctx, " ");
3714 	cfg_print_cstr(pctx, "[ port <integer> ]");
3715 	cfg_print_cstr(pctx, " )");
3716 }
3717 
3718 static isc_result_t
3719 parse_remoteselement(cfg_parser_t *pctx, const cfg_type_t *type,
3720 		     cfg_obj_t **ret) {
3721 	isc_result_t result;
3722 	cfg_obj_t *obj = NULL;
3723 	UNUSED(type);
3724 
3725 	CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
3726 	if (pctx->token.type == isc_tokentype_string ||
3727 	    pctx->token.type == isc_tokentype_qstring)
3728 	{
3729 		if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK))
3730 		{
3731 			CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr,
3732 						 ret));
3733 		} else {
3734 			CHECK(cfg_parse_astring(pctx, &cfg_type_astring, ret));
3735 		}
3736 	} else {
3737 		cfg_parser_error(pctx, CFG_LOG_NEAR,
3738 				 "expected IP address or remote servers list "
3739 				 "name");
3740 		return (ISC_R_UNEXPECTEDTOKEN);
3741 	}
3742 cleanup:
3743 	CLEANUP_OBJ(obj);
3744 	return (result);
3745 }
3746 
3747 static cfg_type_t cfg_type_remoteselement = { "remotes_element",
3748 					      parse_remoteselement,
3749 					      NULL,
3750 					      doc_remoteselement,
3751 					      NULL,
3752 					      NULL };
3753 
3754 static int
3755 cmp_clause(const void *ap, const void *bp) {
3756 	const cfg_clausedef_t *a = (const cfg_clausedef_t *)ap;
3757 	const cfg_clausedef_t *b = (const cfg_clausedef_t *)bp;
3758 	return (strcmp(a->name, b->name));
3759 }
3760 
3761 bool
3762 cfg_clause_validforzone(const char *name, unsigned int ztype) {
3763 	const cfg_clausedef_t *clause;
3764 	bool valid = false;
3765 
3766 	for (clause = zone_clauses; clause->name != NULL; clause++) {
3767 		if ((clause->flags & ztype) == 0 ||
3768 		    strcmp(clause->name, name) != 0) {
3769 			continue;
3770 		}
3771 		valid = true;
3772 	}
3773 	for (clause = zone_only_clauses; clause->name != NULL; clause++) {
3774 		if ((clause->flags & ztype) == 0 ||
3775 		    strcmp(clause->name, name) != 0) {
3776 			continue;
3777 		}
3778 		valid = true;
3779 	}
3780 
3781 	return (valid);
3782 }
3783 
3784 void
3785 cfg_print_zonegrammar(const unsigned int zonetype, unsigned int flags,
3786 		      void (*f)(void *closure, const char *text, int textlen),
3787 		      void *closure) {
3788 #define NCLAUSES                                               \
3789 	(((sizeof(zone_clauses) + sizeof(zone_only_clauses)) / \
3790 	  sizeof(clause[0])) -                                 \
3791 	 1)
3792 
3793 	cfg_printer_t pctx;
3794 	cfg_clausedef_t *clause = NULL;
3795 	cfg_clausedef_t clauses[NCLAUSES];
3796 
3797 	pctx.f = f;
3798 	pctx.closure = closure;
3799 	pctx.indent = 0;
3800 	pctx.flags = flags;
3801 
3802 	memmove(clauses, zone_clauses, sizeof(zone_clauses));
3803 	memmove(clauses + sizeof(zone_clauses) / sizeof(zone_clauses[0]) - 1,
3804 		zone_only_clauses, sizeof(zone_only_clauses));
3805 	qsort(clauses, NCLAUSES - 1, sizeof(clause[0]), cmp_clause);
3806 
3807 	cfg_print_cstr(&pctx, "zone <string> [ <class> ] {\n");
3808 	pctx.indent++;
3809 
3810 	switch (zonetype) {
3811 	case CFG_ZONE_MASTER:
3812 		cfg_print_indent(&pctx);
3813 		cfg_print_cstr(&pctx, "type ( master | primary );\n");
3814 		break;
3815 	case CFG_ZONE_SLAVE:
3816 		cfg_print_indent(&pctx);
3817 		cfg_print_cstr(&pctx, "type ( slave | secondary );\n");
3818 		break;
3819 	case CFG_ZONE_MIRROR:
3820 		cfg_print_indent(&pctx);
3821 		cfg_print_cstr(&pctx, "type mirror;\n");
3822 		break;
3823 	case CFG_ZONE_STUB:
3824 		cfg_print_indent(&pctx);
3825 		cfg_print_cstr(&pctx, "type stub;\n");
3826 		break;
3827 	case CFG_ZONE_HINT:
3828 		cfg_print_indent(&pctx);
3829 		cfg_print_cstr(&pctx, "type hint;\n");
3830 		break;
3831 	case CFG_ZONE_FORWARD:
3832 		cfg_print_indent(&pctx);
3833 		cfg_print_cstr(&pctx, "type forward;\n");
3834 		break;
3835 	case CFG_ZONE_STATICSTUB:
3836 		cfg_print_indent(&pctx);
3837 		cfg_print_cstr(&pctx, "type static-stub;\n");
3838 		break;
3839 	case CFG_ZONE_REDIRECT:
3840 		cfg_print_indent(&pctx);
3841 		cfg_print_cstr(&pctx, "type redirect;\n");
3842 		break;
3843 	case CFG_ZONE_DELEGATION:
3844 		cfg_print_indent(&pctx);
3845 		cfg_print_cstr(&pctx, "type delegation-only;\n");
3846 		break;
3847 	case CFG_ZONE_INVIEW:
3848 		/* no zone type is specified for these */
3849 		break;
3850 	default:
3851 		INSIST(0);
3852 		ISC_UNREACHABLE();
3853 	}
3854 
3855 	for (clause = clauses; clause->name != NULL; clause++) {
3856 		if (((pctx.flags & CFG_PRINTER_ACTIVEONLY) != 0) &&
3857 		    (((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0) ||
3858 		     ((clause->flags & CFG_CLAUSEFLAG_ANCIENT) != 0) ||
3859 		     ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0) ||
3860 		     ((clause->flags & CFG_CLAUSEFLAG_TESTONLY) != 0)))
3861 		{
3862 			continue;
3863 		}
3864 		if ((clause->flags & zonetype) == 0 ||
3865 		    strcasecmp(clause->name, "type") == 0) {
3866 			continue;
3867 		}
3868 		cfg_print_indent(&pctx);
3869 		cfg_print_cstr(&pctx, clause->name);
3870 		cfg_print_cstr(&pctx, " ");
3871 		cfg_doc_obj(&pctx, clause->type);
3872 		cfg_print_cstr(&pctx, ";");
3873 		cfg_print_clauseflags(&pctx, clause->flags);
3874 		cfg_print_cstr(&pctx, "\n");
3875 	}
3876 
3877 	pctx.indent--;
3878 	cfg_print_cstr(&pctx, "};\n");
3879 }
3880