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