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