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