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