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