namedconf.c revision 1.3
1/*	$NetBSD: namedconf.c,v 1.3 2019/01/09 16:55:18 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 * A public key, as in 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", 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_OBSOLETE },
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_OBSOLETE },
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#ifdef HAVE_GEOIP
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_OBSOLETE },
1079	{ "heartbeat-interval", &cfg_type_uint32, 0 },
1080	{ "host-statistics", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTIMP },
1081	{ "host-statistics-max", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
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_OBSOLETE },
1094	{ "named-xfer", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
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_OBSOLETE },
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_NYI },
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_OBSOLETE },
1130	{ "use-id-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
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	{ "log", &cfg_type_boolean, 0 },
1610	{ "max-policy-ttl", &cfg_type_ttlval, 0 },
1611	{ "min-update-interval", &cfg_type_ttlval, 0 },
1612	{ "policy", &cfg_type_rpz_policy, 0 },
1613	{ "recursive-only", &cfg_type_boolean, 0 },
1614	{ "nsip-enable", &cfg_type_boolean, 0 },
1615	{ "nsdname-enable", &cfg_type_boolean, 0 },
1616	{ NULL, NULL, 0 }
1617};
1618static cfg_type_t cfg_type_rpz_tuple = {
1619	"rpz tuple", cfg_parse_kv_tuple,
1620	cfg_print_kv_tuple, cfg_doc_kv_tuple, &cfg_rep_tuple,
1621	rpz_zone_fields
1622};
1623static cfg_type_t cfg_type_rpz_list = {
1624	"zone list", cfg_parse_bracketed_list, cfg_print_bracketed_list,
1625	cfg_doc_bracketed_list, &cfg_rep_list,
1626	&cfg_type_rpz_tuple
1627};
1628static cfg_tuplefielddef_t rpz_fields[] = {
1629	{ "zone list", &cfg_type_rpz_list, 0 },
1630	{ "break-dnssec", &cfg_type_boolean, 0 },
1631	{ "max-policy-ttl", &cfg_type_ttlval, 0 },
1632	{ "min-update-interval", &cfg_type_ttlval, 0 },
1633	{ "min-ns-dots", &cfg_type_uint32, 0 },
1634	{ "nsip-wait-recurse", &cfg_type_boolean, 0 },
1635	{ "qname-wait-recurse", &cfg_type_boolean, 0 },
1636	{ "recursive-only", &cfg_type_boolean, 0 },
1637	{ "nsip-enable", &cfg_type_boolean, 0 },
1638	{ "nsdname-enable", &cfg_type_boolean, 0 },
1639#ifdef USE_DNSRPS
1640	{ "dnsrps-enable", &cfg_type_boolean, 0 },
1641	{ "dnsrps-options", &cfg_type_bracketed_text, 0 },
1642#else
1643	{ "dnsrps-enable", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTCONFIGURED },
1644	{ "dnsrps-options", &cfg_type_bracketed_text,
1645		CFG_CLAUSEFLAG_NOTCONFIGURED },
1646#endif
1647	{ NULL, NULL, 0 }
1648};
1649static cfg_type_t cfg_type_rpz = {
1650	"rpz", cfg_parse_kv_tuple,
1651	cfg_print_kv_tuple, cfg_doc_kv_tuple, &cfg_rep_tuple,
1652	rpz_fields
1653};
1654
1655/*
1656 * Catalog zones
1657 */
1658static cfg_type_t cfg_type_catz_zone = {
1659	"zone", parse_keyvalue, print_keyvalue,
1660	doc_keyvalue, &cfg_rep_string,
1661	&zone_kw
1662};
1663
1664static cfg_tuplefielddef_t catz_zone_fields[] = {
1665	{ "zone name", &cfg_type_catz_zone, 0 },
1666	{ "default-masters", &cfg_type_namesockaddrkeylist, 0 },
1667	{ "zone-directory", &cfg_type_qstring, 0 },
1668	{ "in-memory", &cfg_type_boolean, 0 },
1669	{ "min-update-interval", &cfg_type_ttlval, 0 },
1670	{ NULL, NULL, 0 }
1671};
1672static cfg_type_t cfg_type_catz_tuple = {
1673	"catz tuple", cfg_parse_kv_tuple,
1674	cfg_print_kv_tuple, cfg_doc_kv_tuple, &cfg_rep_tuple,
1675	catz_zone_fields
1676};
1677static cfg_type_t cfg_type_catz_list = {
1678	"zone list", cfg_parse_bracketed_list, cfg_print_bracketed_list,
1679	cfg_doc_bracketed_list, &cfg_rep_list,
1680	&cfg_type_catz_tuple
1681};
1682static cfg_tuplefielddef_t catz_fields[] = {
1683	{ "zone list", &cfg_type_catz_list, 0 },
1684	{ NULL, NULL, 0 }
1685};
1686static cfg_type_t cfg_type_catz = {
1687	"catz", cfg_parse_kv_tuple, cfg_print_kv_tuple,
1688	cfg_doc_kv_tuple, &cfg_rep_tuple, catz_fields
1689};
1690
1691/*
1692 * rate-limit
1693 */
1694static cfg_clausedef_t rrl_clauses[] = {
1695	{ "all-per-second", &cfg_type_uint32, 0 },
1696	{ "errors-per-second", &cfg_type_uint32, 0 },
1697	{ "exempt-clients", &cfg_type_bracketed_aml, 0 },
1698	{ "ipv4-prefix-length", &cfg_type_uint32, 0 },
1699	{ "ipv6-prefix-length", &cfg_type_uint32, 0 },
1700	{ "log-only", &cfg_type_boolean, 0 },
1701	{ "max-table-size", &cfg_type_uint32, 0 },
1702	{ "min-table-size", &cfg_type_uint32, 0 },
1703	{ "nodata-per-second", &cfg_type_uint32, 0 },
1704	{ "nxdomains-per-second", &cfg_type_uint32, 0 },
1705	{ "qps-scale", &cfg_type_uint32, 0 },
1706	{ "referrals-per-second", &cfg_type_uint32, 0 },
1707	{ "responses-per-second", &cfg_type_uint32, 0 },
1708	{ "slip", &cfg_type_uint32, 0 },
1709	{ "window", &cfg_type_uint32, 0 },
1710	{ NULL, NULL, 0 }
1711};
1712
1713static cfg_clausedef_t *rrl_clausesets[] = {
1714	rrl_clauses, NULL
1715};
1716
1717static cfg_type_t cfg_type_rrl = {
1718	"rate-limit", cfg_parse_map, cfg_print_map, cfg_doc_map,
1719	&cfg_rep_map, rrl_clausesets
1720};
1721
1722/*%
1723 * dnssec-lookaside
1724 */
1725
1726static void
1727print_lookaside(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1728	const cfg_obj_t *domain = obj->value.tuple[0];
1729
1730	if (domain->value.string.length == 4 &&
1731	    strncmp(domain->value.string.base, "auto", 4) == 0)
1732		cfg_print_cstr(pctx, "auto");
1733	else
1734		cfg_print_tuple(pctx, obj);
1735}
1736
1737static void
1738doc_lookaside(cfg_printer_t *pctx, const cfg_type_t *type) {
1739	UNUSED(type);
1740	cfg_print_cstr(pctx, "( <string> trust-anchor <string> | auto | no )");
1741}
1742
1743static keyword_type_t trustanchor_kw = { "trust-anchor", &cfg_type_astring };
1744
1745static cfg_type_t cfg_type_optional_trustanchor = {
1746	"optional_trustanchor", parse_optional_keyvalue, print_keyvalue,
1747	doc_keyvalue, &cfg_rep_string, &trustanchor_kw
1748};
1749
1750static cfg_tuplefielddef_t lookaside_fields[] = {
1751	{ "domain", &cfg_type_astring, 0 },
1752	{ "trust-anchor", &cfg_type_optional_trustanchor, 0 },
1753	{ NULL, NULL, 0 }
1754};
1755
1756static cfg_type_t cfg_type_lookaside = {
1757	"lookaside", cfg_parse_tuple, print_lookaside, doc_lookaside,
1758	&cfg_rep_tuple, lookaside_fields
1759};
1760
1761static isc_result_t
1762parse_optional_uint32(cfg_parser_t *pctx, const cfg_type_t *type,
1763		      cfg_obj_t **ret)
1764{
1765	isc_result_t result;
1766	UNUSED(type);
1767
1768	CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
1769	if (pctx->token.type == isc_tokentype_number) {
1770		CHECK(cfg_parse_obj(pctx, &cfg_type_uint32, ret));
1771	} else {
1772		CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
1773	}
1774 cleanup:
1775	return (result);
1776}
1777
1778static void
1779doc_optional_uint32(cfg_printer_t *pctx, const cfg_type_t *type) {
1780	UNUSED(type);
1781	cfg_print_cstr(pctx, "[ <integer> ]");
1782}
1783
1784static cfg_type_t cfg_type_optional_uint32 = {
1785	"optional_uint32", parse_optional_uint32, NULL, doc_optional_uint32,
1786	NULL, NULL
1787};
1788
1789static cfg_tuplefielddef_t prefetch_fields[] = {
1790	{ "trigger", &cfg_type_uint32, 0 },
1791	{ "eligible", &cfg_type_optional_uint32, 0 },
1792	{ NULL, NULL, 0 }
1793};
1794
1795static cfg_type_t cfg_type_prefetch = {
1796	"prefetch", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
1797	&cfg_rep_tuple, prefetch_fields
1798};
1799/*
1800 * DNS64.
1801 */
1802static cfg_clausedef_t
1803dns64_clauses[] = {
1804	{ "break-dnssec", &cfg_type_boolean, 0 },
1805	{ "clients", &cfg_type_bracketed_aml, 0 },
1806	{ "exclude", &cfg_type_bracketed_aml, 0 },
1807	{ "mapped", &cfg_type_bracketed_aml, 0 },
1808	{ "recursive-only", &cfg_type_boolean, 0 },
1809	{ "suffix", &cfg_type_netaddr6, 0 },
1810	{ NULL, NULL, 0 },
1811};
1812
1813static cfg_clausedef_t *
1814dns64_clausesets[] = {
1815	dns64_clauses,
1816	NULL
1817};
1818
1819static cfg_type_t cfg_type_dns64 = {
1820	"dns64", cfg_parse_netprefix_map, cfg_print_map, cfg_doc_map,
1821	&cfg_rep_map, dns64_clausesets
1822};
1823
1824/*%
1825 * Clauses that can be found within the 'view' statement,
1826 * with defaults in the 'options' statement.
1827 */
1828
1829static cfg_clausedef_t
1830view_clauses[] = {
1831	{ "acache-cleaning-interval", &cfg_type_uint32,
1832	  CFG_CLAUSEFLAG_OBSOLETE },
1833	{ "acache-enable", &cfg_type_boolean,
1834	  CFG_CLAUSEFLAG_OBSOLETE },
1835	{ "additional-from-auth", &cfg_type_boolean,
1836	  CFG_CLAUSEFLAG_OBSOLETE },
1837	{ "additional-from-cache", &cfg_type_boolean,
1838	  CFG_CLAUSEFLAG_OBSOLETE },
1839	{ "allow-new-zones", &cfg_type_boolean, 0 },
1840	{ "allow-query-cache", &cfg_type_bracketed_aml, 0 },
1841	{ "allow-query-cache-on", &cfg_type_bracketed_aml, 0 },
1842	{ "allow-recursion", &cfg_type_bracketed_aml, 0 },
1843	{ "allow-recursion-on", &cfg_type_bracketed_aml, 0 },
1844	{ "allow-v6-synthesis", &cfg_type_bracketed_aml,
1845	  CFG_CLAUSEFLAG_OBSOLETE },
1846	{ "attach-cache", &cfg_type_astring, 0 },
1847	{ "auth-nxdomain", &cfg_type_boolean, CFG_CLAUSEFLAG_NEWDEFAULT },
1848	{ "cache-file", &cfg_type_qstring, 0 },
1849	{ "catalog-zones", &cfg_type_catz, 0 },
1850	{ "check-names", &cfg_type_checknames, CFG_CLAUSEFLAG_MULTI },
1851	{ "cleaning-interval", &cfg_type_uint32, 0 },
1852	{ "clients-per-query", &cfg_type_uint32, 0 },
1853	{ "deny-answer-addresses", &cfg_type_denyaddresses, 0 },
1854	{ "deny-answer-aliases", &cfg_type_denyaliases, 0 },
1855	{ "disable-algorithms", &cfg_type_disablealgorithm,
1856	  CFG_CLAUSEFLAG_MULTI },
1857	{ "disable-ds-digests", &cfg_type_disabledsdigest,
1858	  CFG_CLAUSEFLAG_MULTI },
1859	{ "disable-empty-zone", &cfg_type_astring, CFG_CLAUSEFLAG_MULTI },
1860	{ "dns64", &cfg_type_dns64, CFG_CLAUSEFLAG_MULTI },
1861	{ "dns64-contact", &cfg_type_astring, 0 },
1862	{ "dns64-server", &cfg_type_astring, 0 },
1863#ifdef USE_DNSRPS
1864	{ "dnsrps-enable", &cfg_type_boolean, 0 },
1865	{ "dnsrps-options", &cfg_type_bracketed_text, 0 },
1866#else
1867	{ "dnsrps-enable", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTCONFIGURED },
1868	{ "dnsrps-options", &cfg_type_bracketed_text,
1869		CFG_CLAUSEFLAG_NOTCONFIGURED },
1870#endif
1871	{ "dnssec-accept-expired", &cfg_type_boolean, 0 },
1872	{ "dnssec-enable", &cfg_type_boolean, 0 },
1873	{ "dnssec-lookaside", &cfg_type_lookaside, CFG_CLAUSEFLAG_MULTI },
1874	{ "dnssec-must-be-secure",  &cfg_type_mustbesecure,
1875	  CFG_CLAUSEFLAG_MULTI },
1876	{ "dnssec-validation", &cfg_type_boolorauto, 0 },
1877#ifdef HAVE_DNSTAP
1878	{ "dnstap", &cfg_type_dnstap, 0 },
1879#else
1880	{ "dnstap", &cfg_type_dnstap, CFG_CLAUSEFLAG_NOTCONFIGURED },
1881#endif /* HAVE_DNSTAP */
1882	{ "dual-stack-servers", &cfg_type_nameportiplist, 0 },
1883	{ "edns-udp-size", &cfg_type_uint32, 0 },
1884	{ "empty-contact", &cfg_type_astring, 0 },
1885	{ "empty-server", &cfg_type_astring, 0 },
1886	{ "empty-zones-enable", &cfg_type_boolean, 0 },
1887	{ "fetch-glue", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1888	{ "fetch-quota-params", &cfg_type_fetchquota, 0 },
1889	{ "fetches-per-server", &cfg_type_fetchesper, 0 },
1890	{ "fetches-per-zone", &cfg_type_fetchesper, 0 },
1891	{ "filter-aaaa", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_OBSOLETE },
1892	{ "filter-aaaa-on-v4", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE  },
1893	{ "filter-aaaa-on-v6", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE  },
1894	{ "glue-cache", &cfg_type_boolean, 0 },
1895	{ "ixfr-from-differences", &cfg_type_ixfrdifftype, 0 },
1896	{ "lame-ttl", &cfg_type_ttlval, 0 },
1897#ifdef HAVE_LMDB
1898	{ "lmdb-mapsize", &cfg_type_sizeval, 0 },
1899#else
1900	{ "lmdb-mapsize", &cfg_type_sizeval, CFG_CLAUSEFLAG_NOOP },
1901#endif
1902	{ "max-acache-size", &cfg_type_sizenodefault,
1903	  CFG_CLAUSEFLAG_OBSOLETE },
1904	{ "max-cache-size", &cfg_type_sizeorpercent, 0 },
1905	{ "max-cache-ttl", &cfg_type_ttlval, 0 },
1906	{ "max-clients-per-query", &cfg_type_uint32, 0 },
1907	{ "max-ncache-ttl", &cfg_type_ttlval, 0 },
1908	{ "max-recursion-depth", &cfg_type_uint32, 0 },
1909	{ "max-recursion-queries", &cfg_type_uint32, 0 },
1910	{ "max-stale-ttl", &cfg_type_ttlval, 0 },
1911	{ "max-udp-size", &cfg_type_uint32, 0 },
1912	{ "message-compression", &cfg_type_boolean, 0 },
1913	{ "min-cache-ttl", &cfg_type_ttlval, 0 },
1914	{ "min-ncache-ttl", &cfg_type_ttlval, 0 },
1915	{ "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
1916	{ "minimal-any", &cfg_type_boolean, 0 },
1917	{ "minimal-responses", &cfg_type_minimal, 0 },
1918	{ "new-zones-directory", &cfg_type_qstring, 0 },
1919	{ "no-case-compress", &cfg_type_bracketed_aml, 0 },
1920	{ "nocookie-udp-size", &cfg_type_uint32, 0 },
1921	{ "nosit-udp-size", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
1922	{ "nta-lifetime", &cfg_type_ttlval, 0 },
1923	{ "nta-recheck", &cfg_type_ttlval, 0 },
1924	{ "nxdomain-redirect", &cfg_type_astring, 0 },
1925	{ "preferred-glue", &cfg_type_astring, 0 },
1926	{ "prefetch", &cfg_type_prefetch, 0 },
1927	{ "provide-ixfr", &cfg_type_boolean, 0 },
1928	{ "qname-minimization", &cfg_type_qminmethod, 0 },
1929	/*
1930	 * Note that the query-source option syntax is different
1931	 * from the other -source options.
1932	 */
1933	{ "query-source", &cfg_type_querysource4, 0 },
1934	{ "query-source-v6", &cfg_type_querysource6, 0 },
1935	{ "queryport-pool-ports", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
1936	{ "queryport-pool-updateinterval", &cfg_type_uint32,
1937	  CFG_CLAUSEFLAG_OBSOLETE },
1938	{ "rate-limit", &cfg_type_rrl, 0 },
1939	{ "recursion", &cfg_type_boolean, 0 },
1940	{ "request-nsid", &cfg_type_boolean, 0 },
1941	{ "request-sit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1942	{ "require-server-cookie", &cfg_type_boolean, 0 },
1943	{ "resolver-nonbackoff-tries", &cfg_type_uint32, 0 },
1944	{ "resolver-query-timeout", &cfg_type_uint32, 0 },
1945	{ "resolver-retry-interval", &cfg_type_uint32, 0 },
1946	{ "response-padding", &cfg_type_resppadding, 0 },
1947	{ "response-policy", &cfg_type_rpz, 0 },
1948	{ "rfc2308-type1", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
1949	{ "root-delegation-only",  &cfg_type_optional_exclude, 0 },
1950	{ "root-key-sentinel", &cfg_type_boolean, 0 },
1951	{ "rrset-order", &cfg_type_rrsetorder, 0 },
1952	{ "send-cookie", &cfg_type_boolean, 0 },
1953	{ "servfail-ttl", &cfg_type_ttlval, 0 },
1954	{ "sortlist", &cfg_type_bracketed_aml, 0 },
1955	{ "stale-answer-enable", &cfg_type_boolean, 0 },
1956	{ "stale-answer-ttl", &cfg_type_ttlval, 0 },
1957	{ "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
1958	{ "synth-from-dnssec", &cfg_type_boolean, 0 },
1959	{ "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_NOTIMP },
1960	{ "transfer-format", &cfg_type_transferformat, 0 },
1961	{ "trust-anchor-telemetry", &cfg_type_boolean,
1962	  CFG_CLAUSEFLAG_EXPERIMENTAL },
1963	{ "use-queryport-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1964	{ "validate-except", &cfg_type_namelist, 0 },
1965	{ "v6-bias", &cfg_type_uint32, 0 },
1966	{ "zero-no-soa-ttl-cache", &cfg_type_boolean, 0 },
1967	{ NULL, NULL, 0 }
1968};
1969
1970/*%
1971 * Clauses that can be found within the 'view' statement only.
1972 */
1973static cfg_clausedef_t
1974view_only_clauses[] = {
1975	{ "match-clients", &cfg_type_bracketed_aml, 0 },
1976	{ "match-destinations", &cfg_type_bracketed_aml, 0 },
1977	{ "match-recursive-only", &cfg_type_boolean, 0 },
1978	{ NULL, NULL, 0 }
1979};
1980
1981/*%
1982 * Sig-validity-interval.
1983 */
1984
1985static cfg_tuplefielddef_t validityinterval_fields[] = {
1986	{ "validity", &cfg_type_uint32, 0 },
1987	{ "re-sign", &cfg_type_optional_uint32, 0 },
1988	{ NULL, NULL, 0 }
1989};
1990
1991static cfg_type_t cfg_type_validityinterval = {
1992	"validityinterval", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
1993	&cfg_rep_tuple, validityinterval_fields
1994};
1995
1996/*%
1997 * Clauses that can be found in a 'zone' statement,
1998 * with defaults in the 'view' or 'options' statement.
1999 *
2000 * Note: CFG_ZONE_* options indicate in which zone types this clause is
2001 * legal.
2002 */
2003static cfg_clausedef_t
2004zone_clauses[] = {
2005	{ "allow-notify", &cfg_type_bracketed_aml,
2006		CFG_ZONE_SLAVE | CFG_ZONE_MIRROR
2007	},
2008	{ "allow-query", &cfg_type_bracketed_aml,
2009		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR |
2010		CFG_ZONE_STUB | CFG_ZONE_REDIRECT | CFG_ZONE_STATICSTUB
2011	},
2012	{ "allow-query-on", &cfg_type_bracketed_aml,
2013		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR |
2014		CFG_ZONE_STUB | CFG_ZONE_REDIRECT | CFG_ZONE_STATICSTUB
2015	},
2016	{ "allow-transfer", &cfg_type_bracketed_aml,
2017		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR
2018	},
2019	{ "allow-update", &cfg_type_bracketed_aml,
2020		CFG_ZONE_MASTER
2021	},
2022	{ "allow-update-forwarding", &cfg_type_bracketed_aml,
2023		CFG_ZONE_SLAVE | CFG_ZONE_MIRROR
2024	},
2025	{ "also-notify", &cfg_type_namesockaddrkeylist,
2026		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR
2027	},
2028	{ "alt-transfer-source", &cfg_type_sockaddr4wild,
2029		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR
2030	},
2031	{ "alt-transfer-source-v6", &cfg_type_sockaddr6wild,
2032		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR
2033	},
2034	{ "auto-dnssec", &cfg_type_autodnssec,
2035		CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2036	},
2037	{ "check-dup-records", &cfg_type_checkmode,
2038		CFG_ZONE_MASTER
2039	},
2040	{ "check-integrity", &cfg_type_boolean,
2041		CFG_ZONE_MASTER
2042	},
2043	{ "check-mx", &cfg_type_checkmode,
2044		CFG_ZONE_MASTER
2045	},
2046	{ "check-mx-cname", &cfg_type_checkmode,
2047		CFG_ZONE_MASTER
2048	},
2049	{ "check-sibling", &cfg_type_boolean,
2050		CFG_ZONE_MASTER
2051	},
2052	{ "check-spf", &cfg_type_warn,
2053		CFG_ZONE_MASTER
2054	},
2055	{ "check-srv-cname", &cfg_type_checkmode,
2056		CFG_ZONE_MASTER
2057	},
2058	{ "check-wildcard", &cfg_type_boolean,
2059		CFG_ZONE_MASTER
2060	},
2061	{ "dialup", &cfg_type_dialuptype,
2062		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_STUB
2063	},
2064	{ "dnssec-dnskey-kskonly", &cfg_type_boolean,
2065		CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2066	},
2067	{ "dnssec-loadkeys-interval", &cfg_type_uint32,
2068		CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2069	},
2070	{ "dnssec-secure-to-insecure", &cfg_type_boolean,
2071		CFG_ZONE_MASTER
2072	},
2073	{ "dnssec-update-mode", &cfg_type_dnssecupdatemode,
2074		CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2075	},
2076	{ "forward", &cfg_type_forwardtype,
2077		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_STUB |
2078		CFG_ZONE_STATICSTUB | CFG_ZONE_FORWARD
2079	},
2080	{ "forwarders", &cfg_type_portiplist,
2081		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_STUB |
2082		CFG_ZONE_STATICSTUB | CFG_ZONE_FORWARD
2083	},
2084	{ "inline-signing", &cfg_type_boolean,
2085		CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2086	},
2087	{ "key-directory", &cfg_type_qstring,
2088		CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2089	},
2090	{ "maintain-ixfr-base", &cfg_type_boolean,
2091		CFG_CLAUSEFLAG_OBSOLETE
2092	},
2093	{ "masterfile-format", &cfg_type_masterformat,
2094		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR |
2095		CFG_ZONE_STUB | CFG_ZONE_REDIRECT
2096	},
2097	{ "masterfile-style", &cfg_type_masterstyle,
2098		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR |
2099		CFG_ZONE_STUB | CFG_ZONE_REDIRECT
2100	},
2101	{ "max-ixfr-log-size", &cfg_type_size,
2102		CFG_CLAUSEFLAG_OBSOLETE
2103	},
2104	{ "max-journal-size", &cfg_type_size,
2105		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR
2106	},
2107	{ "max-records", &cfg_type_uint32,
2108		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR |
2109		CFG_ZONE_STUB | CFG_ZONE_STATICSTUB | CFG_ZONE_REDIRECT
2110	},
2111	{ "max-refresh-time", &cfg_type_uint32,
2112		CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB
2113	},
2114	{ "max-retry-time", &cfg_type_uint32,
2115		CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB
2116	},
2117	{ "max-transfer-idle-in", &cfg_type_uint32,
2118		CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB
2119	},
2120	{ "max-transfer-idle-out", &cfg_type_uint32,
2121		CFG_ZONE_MASTER | CFG_ZONE_MIRROR | CFG_ZONE_SLAVE
2122	},
2123	{ "max-transfer-time-in", &cfg_type_uint32,
2124		CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB
2125	},
2126	{ "max-transfer-time-out", &cfg_type_uint32,
2127		CFG_ZONE_MASTER | CFG_ZONE_MIRROR | CFG_ZONE_SLAVE
2128	},
2129	{ "max-zone-ttl", &cfg_type_maxttl,
2130		CFG_ZONE_MASTER | CFG_ZONE_REDIRECT
2131	},
2132	{ "min-refresh-time", &cfg_type_uint32,
2133		CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB
2134	},
2135	{ "min-retry-time", &cfg_type_uint32,
2136		CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB
2137	},
2138	{ "multi-master", &cfg_type_boolean,
2139		CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB
2140	},
2141	{ "notify", &cfg_type_notifytype,
2142		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR
2143	},
2144	{ "notify-delay", &cfg_type_uint32,
2145		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR
2146	},
2147	{ "notify-source", &cfg_type_sockaddr4wild,
2148		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR
2149	},
2150	{ "notify-source-v6", &cfg_type_sockaddr6wild,
2151		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR
2152	},
2153	{ "notify-to-soa", &cfg_type_boolean,
2154		CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2155	},
2156	{ "nsec3-test-zone", &cfg_type_boolean,
2157		CFG_CLAUSEFLAG_TESTONLY |
2158		CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2159	},
2160	{ "request-expire", &cfg_type_boolean,
2161		CFG_ZONE_SLAVE | CFG_ZONE_MIRROR
2162	},
2163	{ "request-ixfr", &cfg_type_boolean,
2164		CFG_ZONE_SLAVE | CFG_ZONE_MIRROR
2165	},
2166	{ "serial-update-method", &cfg_type_updatemethod,
2167		CFG_ZONE_MASTER
2168	},
2169	{ "sig-signing-nodes", &cfg_type_uint32,
2170		CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2171	},
2172	{ "sig-signing-signatures", &cfg_type_uint32,
2173		CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2174	},
2175	{ "sig-signing-type", &cfg_type_uint32,
2176		CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2177	},
2178	{ "sig-validity-interval", &cfg_type_validityinterval,
2179		CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2180	},
2181	{ "dnskey-sig-validity", &cfg_type_uint32,
2182		CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2183	},
2184	{ "transfer-source", &cfg_type_sockaddr4wild,
2185		CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB
2186	},
2187	{ "transfer-source-v6", &cfg_type_sockaddr6wild,
2188		CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB
2189	},
2190	{ "try-tcp-refresh", &cfg_type_boolean,
2191		CFG_ZONE_SLAVE | CFG_ZONE_MIRROR
2192	},
2193	{ "update-check-ksk", &cfg_type_boolean,
2194		CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2195	},
2196	{ "use-alt-transfer-source", &cfg_type_boolean,
2197		CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB
2198	},
2199	{ "zero-no-soa-ttl", &cfg_type_boolean,
2200		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR
2201	},
2202	{ "zone-statistics", &cfg_type_zonestat,
2203		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR |
2204		CFG_ZONE_STUB | CFG_ZONE_STATICSTUB | CFG_ZONE_REDIRECT
2205	},
2206	{ NULL, NULL, 0 }
2207};
2208
2209/*%
2210 * Clauses that can be found in a 'zone' statement only.
2211 *
2212 * Note: CFG_ZONE_* options indicate in which zone types this clause is
2213 * legal.
2214 */
2215static cfg_clausedef_t
2216zone_only_clauses[] = {
2217	/*
2218	 * Note that the format of the check-names option is different between
2219	 * the zone options and the global/view options.  Ugh.
2220	 */
2221	{ "type", &cfg_type_zonetype,
2222		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR |
2223		CFG_ZONE_STUB | CFG_ZONE_STATICSTUB | CFG_ZONE_DELEGATION |
2224		CFG_ZONE_HINT | CFG_ZONE_REDIRECT | CFG_ZONE_FORWARD
2225	},
2226	{ "check-names", &cfg_type_checkmode,
2227		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR |
2228		CFG_ZONE_HINT | CFG_ZONE_STUB
2229	},
2230	{ "database", &cfg_type_astring,
2231		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR |
2232		CFG_ZONE_STUB
2233	},
2234	{ "delegation-only", &cfg_type_boolean,
2235		CFG_ZONE_HINT | CFG_ZONE_STUB | CFG_ZONE_FORWARD
2236	},
2237	{ "dlz", &cfg_type_astring,
2238		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_REDIRECT
2239	},
2240	{ "file", &cfg_type_qstring,
2241		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR |
2242		CFG_ZONE_STUB | CFG_ZONE_HINT | CFG_ZONE_REDIRECT
2243	},
2244	{ "in-view", &cfg_type_astring,
2245		CFG_ZONE_INVIEW
2246	},
2247	{ "ixfr-base", &cfg_type_qstring,
2248		CFG_CLAUSEFLAG_OBSOLETE
2249	},
2250	{ "ixfr-from-differences", &cfg_type_boolean,
2251		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR
2252	},
2253	{ "ixfr-tmp-file", &cfg_type_qstring,
2254		CFG_CLAUSEFLAG_OBSOLETE
2255	},
2256	{ "journal", &cfg_type_qstring,
2257		CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR
2258	},
2259	{ "masters", &cfg_type_namesockaddrkeylist,
2260		CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB |
2261		CFG_ZONE_REDIRECT
2262	},
2263	{ "pubkey", &cfg_type_pubkey,
2264		CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_OBSOLETE
2265	},
2266	{ "server-addresses", &cfg_type_bracketed_netaddrlist,
2267		CFG_ZONE_STATICSTUB
2268	},
2269	{ "server-names", &cfg_type_namelist,
2270		CFG_ZONE_STATICSTUB
2271	},
2272	{ "update-policy", &cfg_type_updatepolicy,
2273		CFG_ZONE_MASTER
2274	},
2275	{ NULL, NULL, 0 }
2276};
2277
2278/*% The top-level named.conf syntax. */
2279
2280static cfg_clausedef_t *
2281namedconf_clausesets[] = {
2282	namedconf_clauses,
2283	namedconf_or_view_clauses,
2284	NULL
2285};
2286LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_namedconf = {
2287	"namedconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
2288	&cfg_rep_map, namedconf_clausesets
2289};
2290
2291/*% The bind.keys syntax (trusted-keys/managed-keys only). */
2292static cfg_clausedef_t *
2293bindkeys_clausesets[] = {
2294	bindkeys_clauses,
2295	NULL
2296};
2297LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_bindkeys = {
2298	"bindkeys", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
2299	&cfg_rep_map, bindkeys_clausesets
2300};
2301
2302/*% The "options" statement syntax. */
2303
2304static cfg_clausedef_t *
2305options_clausesets[] = {
2306	options_clauses,
2307	view_clauses,
2308	zone_clauses,
2309	NULL
2310};
2311static cfg_type_t cfg_type_options = {
2312	"options", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map,
2313	options_clausesets
2314};
2315
2316/*% The "view" statement syntax. */
2317
2318static cfg_clausedef_t *
2319view_clausesets[] = {
2320	view_only_clauses,
2321	namedconf_or_view_clauses,
2322	view_clauses,
2323	zone_clauses,
2324	NULL
2325};
2326
2327static cfg_type_t cfg_type_viewopts = {
2328	"view", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map,
2329	view_clausesets
2330};
2331
2332/*% The "zone" statement syntax. */
2333
2334static cfg_clausedef_t *
2335zone_clausesets[] = {
2336	zone_only_clauses,
2337	zone_clauses,
2338	NULL
2339};
2340LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_zoneopts = {
2341	"zoneopts", cfg_parse_map, cfg_print_map,
2342	cfg_doc_map, &cfg_rep_map, zone_clausesets };
2343
2344/*% The "dynamically loadable zones" statement syntax. */
2345
2346static cfg_clausedef_t
2347dlz_clauses[] = {
2348	{ "database", &cfg_type_astring, 0 },
2349	{ "search", &cfg_type_boolean, 0 },
2350	{ NULL, NULL, 0 }
2351};
2352static cfg_clausedef_t *
2353dlz_clausesets[] = {
2354	dlz_clauses,
2355	NULL
2356};
2357static cfg_type_t cfg_type_dlz = {
2358	"dlz", cfg_parse_named_map, cfg_print_map, cfg_doc_map,
2359	 &cfg_rep_map, dlz_clausesets
2360};
2361
2362/*%
2363 * The "dyndb" statement syntax.
2364 */
2365
2366static cfg_tuplefielddef_t dyndb_fields[] = {
2367	{ "name", &cfg_type_astring, 0 },
2368	{ "library", &cfg_type_qstring, 0 },
2369	{ "parameters", &cfg_type_bracketed_text, 0 },
2370	{ NULL, NULL, 0 }
2371};
2372
2373static cfg_type_t cfg_type_dyndb = {
2374	"dyndb", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
2375	 &cfg_rep_tuple, dyndb_fields
2376};
2377
2378/*%
2379 * The "plugin" statement syntax.
2380 * Currently only one plugin type is supported: query.
2381 */
2382
2383static const char *plugin_enums[] = {
2384	"query", NULL
2385};
2386static cfg_type_t cfg_type_plugintype = {
2387	"plugintype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
2388	&cfg_rep_string, plugin_enums
2389};
2390static cfg_tuplefielddef_t plugin_fields[] = {
2391	{ "type", &cfg_type_plugintype, 0 },
2392	{ "library", &cfg_type_astring, 0 },
2393	{ "parameters", &cfg_type_optional_bracketed_text, 0 },
2394	{ NULL, NULL, 0 }
2395};
2396static cfg_type_t cfg_type_plugin = {
2397	"plugin", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
2398	 &cfg_rep_tuple, plugin_fields
2399};
2400
2401/*%
2402 * Clauses that can be found within the 'key' statement.
2403 */
2404static cfg_clausedef_t
2405key_clauses[] = {
2406	{ "algorithm", &cfg_type_astring, 0 },
2407	{ "secret", &cfg_type_sstring, 0 },
2408	{ NULL, NULL, 0 }
2409};
2410
2411static cfg_clausedef_t *
2412key_clausesets[] = {
2413	key_clauses,
2414	NULL
2415};
2416static cfg_type_t cfg_type_key = {
2417	"key", cfg_parse_named_map, cfg_print_map,
2418	cfg_doc_map, &cfg_rep_map, key_clausesets
2419};
2420
2421
2422/*%
2423 * Clauses that can be found in a 'server' statement.
2424 */
2425static cfg_clausedef_t
2426server_clauses[] = {
2427	{ "bogus", &cfg_type_boolean, 0 },
2428	{ "edns", &cfg_type_boolean, 0 },
2429	{ "edns-udp-size", &cfg_type_uint32, 0 },
2430	{ "edns-version", &cfg_type_uint32, 0 },
2431	{ "keys", &cfg_type_server_key_kludge, 0 },
2432	{ "max-udp-size", &cfg_type_uint32, 0 },
2433	{ "notify-source", &cfg_type_sockaddr4wild, 0 },
2434	{ "notify-source-v6", &cfg_type_sockaddr6wild, 0 },
2435	{ "padding", &cfg_type_uint32, 0 },
2436	{ "provide-ixfr", &cfg_type_boolean, 0 },
2437	{ "query-source", &cfg_type_querysource4, 0 },
2438	{ "query-source-v6", &cfg_type_querysource6, 0 },
2439	{ "request-expire", &cfg_type_boolean, 0 },
2440	{ "request-ixfr", &cfg_type_boolean, 0 },
2441	{ "request-nsid", &cfg_type_boolean, 0 },
2442	{ "request-sit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
2443	{ "send-cookie", &cfg_type_boolean, 0 },
2444	{ "support-ixfr", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
2445	{ "tcp-keepalive", &cfg_type_boolean, 0 },
2446	{ "tcp-only", &cfg_type_boolean, 0 },
2447	{ "transfer-format", &cfg_type_transferformat, 0 },
2448	{ "transfer-source", &cfg_type_sockaddr4wild, 0 },
2449	{ "transfer-source-v6", &cfg_type_sockaddr6wild, 0 },
2450	{ "transfers", &cfg_type_uint32, 0 },
2451	{ NULL, NULL, 0 }
2452};
2453static cfg_clausedef_t *
2454server_clausesets[] = {
2455	server_clauses,
2456	NULL
2457};
2458static cfg_type_t cfg_type_server = {
2459	"server", cfg_parse_netprefix_map, cfg_print_map, cfg_doc_map,
2460	&cfg_rep_map, server_clausesets
2461};
2462
2463/*%
2464 * Clauses that can be found in a 'channel' clause in the
2465 * 'logging' statement.
2466 *
2467 * These have some additional constraints that need to be
2468 * checked after parsing:
2469 *  - There must exactly one of file/syslog/null/stderr
2470 */
2471
2472static const char *printtime_enums[] = {
2473	"iso8601", "iso8601-utc", "local", NULL
2474};
2475static isc_result_t
2476parse_printtime(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2477	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2478}
2479static void
2480doc_printtime(cfg_printer_t *pctx, const cfg_type_t *type) {
2481	cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
2482}
2483static cfg_type_t cfg_type_printtime = {
2484	"printtime", parse_printtime, cfg_print_ustring, doc_printtime,
2485	&cfg_rep_string, printtime_enums
2486};
2487
2488static cfg_clausedef_t
2489channel_clauses[] = {
2490	/* Destinations.  We no longer require these to be first. */
2491	{ "file", &cfg_type_logfile, 0 },
2492	{ "syslog", &cfg_type_optional_facility, 0 },
2493	{ "null", &cfg_type_void, 0 },
2494	{ "stderr", &cfg_type_void, 0 },
2495	/* Options.  We now accept these for the null channel, too. */
2496	{ "severity", &cfg_type_logseverity, 0 },
2497	{ "print-time", &cfg_type_printtime, 0 },
2498	{ "print-severity", &cfg_type_boolean, 0 },
2499	{ "print-category", &cfg_type_boolean, 0 },
2500	{ "buffered", &cfg_type_boolean, 0 },
2501	{ NULL, NULL, 0 }
2502};
2503static cfg_clausedef_t *
2504channel_clausesets[] = {
2505	channel_clauses,
2506	NULL
2507};
2508static cfg_type_t cfg_type_channel = {
2509	"channel", cfg_parse_named_map, cfg_print_map, cfg_doc_map,
2510	&cfg_rep_map, channel_clausesets
2511};
2512
2513/*% A list of log destination, used in the "category" clause. */
2514static cfg_type_t cfg_type_destinationlist = {
2515	"destinationlist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
2516	cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring
2517};
2518
2519/*%
2520 * Clauses that can be found in a 'logging' statement.
2521 */
2522static cfg_clausedef_t logging_clauses[] = {
2523	{ "channel", &cfg_type_channel, CFG_CLAUSEFLAG_MULTI },
2524	{ "category", &cfg_type_category, CFG_CLAUSEFLAG_MULTI },
2525	{ NULL, NULL, 0 }
2526};
2527static cfg_clausedef_t * logging_clausesets[] = {
2528	logging_clauses, NULL
2529};
2530static cfg_type_t cfg_type_logging = {
2531	"logging", cfg_parse_map, cfg_print_map, cfg_doc_map,
2532	&cfg_rep_map, logging_clausesets
2533};
2534
2535/*%
2536 * For parsing an 'addzone' statement
2537 */
2538static cfg_tuplefielddef_t addzone_fields[] = {
2539	{ "name", &cfg_type_astring, 0 },
2540	{ "class", &cfg_type_optional_class, 0 },
2541	{ "view", &cfg_type_optional_class, 0 },
2542	{ "options", &cfg_type_zoneopts, 0 },
2543	{ NULL, NULL, 0 }
2544};
2545static cfg_type_t cfg_type_addzone = {
2546	"zone", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
2547	&cfg_rep_tuple, addzone_fields
2548};
2549
2550static cfg_clausedef_t
2551addzoneconf_clauses[] = {
2552	{ "zone", &cfg_type_addzone, CFG_CLAUSEFLAG_MULTI },
2553	{ NULL, NULL, 0 }
2554};
2555
2556static cfg_clausedef_t *
2557addzoneconf_clausesets[] = {
2558	addzoneconf_clauses,
2559	NULL
2560};
2561
2562LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_addzoneconf = {
2563	"addzoneconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
2564	&cfg_rep_map, addzoneconf_clausesets
2565};
2566
2567static isc_result_t
2568parse_unitstring(char *str, isc_resourcevalue_t *valuep) {
2569	char *endp;
2570	unsigned int len;
2571	uint64_t value;
2572	uint64_t unit;
2573
2574	value = strtoull(str, &endp, 10);
2575	if (*endp == 0) {
2576		*valuep = value;
2577		return (ISC_R_SUCCESS);
2578	}
2579
2580	len = strlen(str);
2581	if (len < 2 || endp[1] != '\0')
2582		return (ISC_R_FAILURE);
2583
2584	switch (str[len - 1]) {
2585	case 'k':
2586	case 'K':
2587		unit = 1024;
2588		break;
2589	case 'm':
2590	case 'M':
2591		unit = 1024 * 1024;
2592		break;
2593	case 'g':
2594	case 'G':
2595		unit = 1024 * 1024 * 1024;
2596		break;
2597	default:
2598		return (ISC_R_FAILURE);
2599	}
2600	if (value > UINT64_MAX / unit)
2601		return (ISC_R_FAILURE);
2602	*valuep = value * unit;
2603	return (ISC_R_SUCCESS);
2604}
2605
2606static isc_result_t
2607parse_sizeval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2608	isc_result_t result;
2609	cfg_obj_t *obj = NULL;
2610	uint64_t val;
2611
2612	UNUSED(type);
2613
2614	CHECK(cfg_gettoken(pctx, 0));
2615	if (pctx->token.type != isc_tokentype_string) {
2616		result = ISC_R_UNEXPECTEDTOKEN;
2617		goto cleanup;
2618	}
2619	CHECK(parse_unitstring(TOKEN_STRING(pctx), &val));
2620
2621	CHECK(cfg_create_obj(pctx, &cfg_type_uint64, &obj));
2622	obj->value.uint64 = val;
2623	*ret = obj;
2624	return (ISC_R_SUCCESS);
2625
2626 cleanup:
2627	cfg_parser_error(pctx, CFG_LOG_NEAR,
2628			 "expected integer and optional unit");
2629	return (result);
2630}
2631
2632static isc_result_t
2633parse_sizeval_percent(cfg_parser_t *pctx, const cfg_type_t *type,
2634		      cfg_obj_t **ret)
2635{
2636	char *endp;
2637	isc_result_t  result;
2638	cfg_obj_t *obj = NULL;
2639	uint64_t val;
2640	uint64_t percent;
2641
2642	UNUSED(type);
2643
2644	CHECK(cfg_gettoken(pctx, 0));
2645	if (pctx->token.type != isc_tokentype_string) {
2646		result = ISC_R_UNEXPECTEDTOKEN;
2647		goto cleanup;
2648	}
2649
2650	percent = strtoull(TOKEN_STRING(pctx), &endp, 10);
2651
2652	if (*endp == '%' && *(endp+1) == 0) {
2653		CHECK(cfg_create_obj(pctx, &cfg_type_percentage, &obj));
2654		obj->value.uint32 = (uint32_t)percent;
2655		*ret = obj;
2656		return (ISC_R_SUCCESS);
2657	} else {
2658		CHECK(parse_unitstring(TOKEN_STRING(pctx), &val));
2659		CHECK(cfg_create_obj(pctx, &cfg_type_uint64, &obj));
2660		obj->value.uint64 = val;
2661		*ret = obj;
2662		return (ISC_R_SUCCESS);
2663	}
2664
2665 cleanup:
2666	cfg_parser_error(pctx, CFG_LOG_NEAR,
2667			 "expected integer and optional unit or percent");
2668	return (result);
2669}
2670
2671static void
2672doc_sizeval_percent(cfg_printer_t *pctx, const cfg_type_t *type) {
2673
2674	UNUSED(type);
2675
2676	cfg_print_cstr(pctx, "( ");
2677	cfg_doc_terminal(pctx, &cfg_type_size);
2678	cfg_print_cstr(pctx, " | ");
2679	cfg_doc_terminal(pctx, &cfg_type_percentage);
2680	cfg_print_cstr(pctx, " )");
2681}
2682
2683/*%
2684 * A size value (number + optional unit).
2685 */
2686static cfg_type_t cfg_type_sizeval = {
2687	"sizeval", parse_sizeval, cfg_print_uint64, cfg_doc_terminal,
2688	&cfg_rep_uint64, NULL
2689};
2690
2691/*%
2692 * A size, "unlimited", or "default".
2693 */
2694
2695static isc_result_t
2696parse_size(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2697	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_sizeval, ret));
2698}
2699
2700static void
2701doc_size(cfg_printer_t *pctx, const cfg_type_t *type) {
2702	cfg_doc_enum_or_other(pctx, type, &cfg_type_sizeval);
2703}
2704
2705static const char *size_enums[] = { "default", "unlimited", NULL };
2706static cfg_type_t cfg_type_size = {
2707	"size", parse_size, cfg_print_ustring, doc_size,
2708	&cfg_rep_string, size_enums
2709};
2710
2711/*%
2712 * A size or "unlimited", but not "default".
2713 */
2714static const char *sizenodefault_enums[] = { "unlimited", NULL };
2715static cfg_type_t cfg_type_sizenodefault = {
2716	"size_no_default", parse_size, cfg_print_ustring, doc_size,
2717	&cfg_rep_string, sizenodefault_enums
2718};
2719
2720/*%
2721 * A size in absolute values or percents.
2722 */
2723static cfg_type_t cfg_type_sizeval_percent = {
2724	"sizeval_percent", parse_sizeval_percent, cfg_print_ustring,
2725	doc_sizeval_percent, &cfg_rep_string, NULL
2726};
2727
2728/*%
2729 * A size in absolute values or percents, or "unlimited", or "default"
2730 */
2731
2732static isc_result_t
2733parse_size_or_percent(cfg_parser_t *pctx, const cfg_type_t *type,
2734		      cfg_obj_t **ret)
2735{
2736	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_sizeval_percent,
2737				    ret));
2738}
2739
2740static void
2741doc_parse_size_or_percent(cfg_printer_t *pctx, const cfg_type_t *type) {
2742	UNUSED(type);
2743	cfg_print_cstr(pctx, "( default | unlimited | ");
2744	cfg_doc_terminal(pctx, &cfg_type_sizeval);
2745	cfg_print_cstr(pctx, " | ");
2746	cfg_doc_terminal(pctx, &cfg_type_percentage);
2747	cfg_print_cstr(pctx, " )");
2748}
2749
2750static const char *sizeorpercent_enums[] = { "default", "unlimited", NULL };
2751static cfg_type_t cfg_type_sizeorpercent = {
2752	"size_or_percent", parse_size_or_percent, cfg_print_ustring,
2753	doc_parse_size_or_percent, &cfg_rep_string, sizeorpercent_enums
2754};
2755
2756/*%
2757 * optional_keyvalue
2758 */
2759static isc_result_t
2760parse_maybe_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
2761			      bool optional, cfg_obj_t **ret)
2762{
2763	isc_result_t result;
2764	cfg_obj_t *obj = NULL;
2765	const keyword_type_t *kw = type->of;
2766
2767	CHECK(cfg_peektoken(pctx, 0));
2768	if (pctx->token.type == isc_tokentype_string &&
2769	    strcasecmp(TOKEN_STRING(pctx), kw->name) == 0) {
2770		CHECK(cfg_gettoken(pctx, 0));
2771		CHECK(kw->type->parse(pctx, kw->type, &obj));
2772		obj->type = type; /* XXX kludge */
2773	} else {
2774		if (optional) {
2775			CHECK(cfg_parse_void(pctx, NULL, &obj));
2776		} else {
2777			cfg_parser_error(pctx, CFG_LOG_NEAR, "expected '%s'",
2778				     kw->name);
2779			result = ISC_R_UNEXPECTEDTOKEN;
2780			goto cleanup;
2781		}
2782	}
2783	*ret = obj;
2784 cleanup:
2785	return (result);
2786}
2787
2788static isc_result_t
2789parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2790	return (parse_maybe_optional_keyvalue(pctx, type, false, ret));
2791}
2792
2793static isc_result_t
2794parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
2795			cfg_obj_t **ret)
2796{
2797	return (parse_maybe_optional_keyvalue(pctx, type, true, ret));
2798}
2799
2800static void
2801print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2802	const keyword_type_t *kw = obj->type->of;
2803	cfg_print_cstr(pctx, kw->name);
2804	cfg_print_cstr(pctx, " ");
2805	kw->type->print(pctx, obj);
2806}
2807
2808static void
2809doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) {
2810	const keyword_type_t *kw = type->of;
2811	cfg_print_cstr(pctx, kw->name);
2812	cfg_print_cstr(pctx, " ");
2813	cfg_doc_obj(pctx, kw->type);
2814}
2815
2816static void
2817doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) {
2818	const keyword_type_t *kw = type->of;
2819	cfg_print_cstr(pctx, "[ ");
2820	cfg_print_cstr(pctx, kw->name);
2821	cfg_print_cstr(pctx, " ");
2822	cfg_doc_obj(pctx, kw->type);
2823	cfg_print_cstr(pctx, " ]");
2824}
2825
2826static const char *dialup_enums[] = {
2827	"notify", "notify-passive", "passive", "refresh", NULL
2828};
2829static isc_result_t
2830parse_dialup_type(cfg_parser_t *pctx, const cfg_type_t *type,
2831		  cfg_obj_t **ret)
2832{
2833	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2834}
2835static void
2836doc_dialup_type(cfg_printer_t *pctx, const cfg_type_t *type) {
2837	cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
2838}
2839static cfg_type_t cfg_type_dialuptype = {
2840	"dialuptype", parse_dialup_type, cfg_print_ustring, doc_dialup_type,
2841	&cfg_rep_string, dialup_enums
2842};
2843
2844static const char *notify_enums[] = { "explicit", "master-only", NULL };
2845static isc_result_t
2846parse_notify_type(cfg_parser_t *pctx, const cfg_type_t *type,
2847		  cfg_obj_t **ret)
2848{
2849	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2850}
2851static void
2852doc_notify_type(cfg_printer_t *pctx, const cfg_type_t *type) {
2853	cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
2854}
2855static cfg_type_t cfg_type_notifytype = {
2856	"notifytype", parse_notify_type, cfg_print_ustring, doc_notify_type,
2857	&cfg_rep_string, notify_enums,
2858};
2859
2860static const char *minimal_enums[] = { "no-auth", "no-auth-recursive", NULL };
2861static isc_result_t
2862parse_minimal(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2863	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2864}
2865static void
2866doc_minimal(cfg_printer_t *pctx, const cfg_type_t *type) {
2867	cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
2868}
2869static cfg_type_t cfg_type_minimal = {
2870	"mimimal", parse_minimal, cfg_print_ustring, doc_minimal,
2871	&cfg_rep_string, minimal_enums,
2872};
2873
2874static const char *ixfrdiff_enums[] = {
2875	"primary", "master", "secondary", "slave", NULL
2876};
2877static isc_result_t
2878parse_ixfrdiff_type(cfg_parser_t *pctx, const cfg_type_t *type,
2879		    cfg_obj_t **ret)
2880{
2881	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2882}
2883static void
2884doc_ixfrdiff_type(cfg_printer_t *pctx, const cfg_type_t *type) {
2885	cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
2886}
2887static cfg_type_t cfg_type_ixfrdifftype = {
2888	"ixfrdiff", parse_ixfrdiff_type, cfg_print_ustring, doc_ixfrdiff_type,
2889	&cfg_rep_string, ixfrdiff_enums,
2890};
2891
2892static keyword_type_t key_kw = { "key", &cfg_type_astring };
2893
2894LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_keyref = {
2895	"keyref", parse_keyvalue, print_keyvalue, doc_keyvalue,
2896	&cfg_rep_string, &key_kw
2897};
2898
2899static cfg_type_t cfg_type_optional_keyref = {
2900	"optional_keyref", parse_optional_keyvalue, print_keyvalue,
2901	doc_optional_keyvalue, &cfg_rep_string, &key_kw
2902};
2903
2904static const char *qminmethod_enums[] = {
2905	"strict", "relaxed", "disabled", "off", NULL
2906};
2907
2908static cfg_type_t cfg_type_qminmethod = {
2909	"qminmethod", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
2910	&cfg_rep_string, qminmethod_enums
2911};
2912
2913/*%
2914 * A "controls" statement is represented as a map with the multivalued
2915 * "inet" and "unix" clauses.
2916 */
2917
2918static keyword_type_t controls_allow_kw = {
2919	"allow", &cfg_type_bracketed_aml };
2920
2921static cfg_type_t cfg_type_controls_allow = {
2922	"controls_allow", parse_keyvalue,
2923	print_keyvalue, doc_keyvalue,
2924	&cfg_rep_list, &controls_allow_kw
2925};
2926
2927static keyword_type_t controls_keys_kw = {
2928	"keys", &cfg_type_keylist
2929};
2930
2931static cfg_type_t cfg_type_controls_keys = {
2932	"controls_keys", parse_optional_keyvalue,
2933	print_keyvalue, doc_optional_keyvalue,
2934	&cfg_rep_list, &controls_keys_kw
2935};
2936
2937static keyword_type_t controls_readonly_kw = {
2938	"read-only", &cfg_type_boolean
2939};
2940
2941static cfg_type_t cfg_type_controls_readonly = {
2942	"controls_readonly", parse_optional_keyvalue,
2943	print_keyvalue, doc_optional_keyvalue,
2944	&cfg_rep_boolean, &controls_readonly_kw
2945};
2946
2947static cfg_tuplefielddef_t inetcontrol_fields[] = {
2948	{ "address", &cfg_type_controls_sockaddr, 0 },
2949	{ "allow", &cfg_type_controls_allow, 0 },
2950	{ "keys", &cfg_type_controls_keys, 0 },
2951	{ "read-only", &cfg_type_controls_readonly, 0 },
2952	{ NULL, NULL, 0 }
2953};
2954
2955static cfg_type_t cfg_type_inetcontrol = {
2956	"inetcontrol", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
2957	&cfg_rep_tuple, inetcontrol_fields
2958};
2959
2960static keyword_type_t controls_perm_kw = {
2961	"perm", &cfg_type_uint32
2962};
2963
2964static cfg_type_t cfg_type_controls_perm = {
2965	"controls_perm", parse_keyvalue,
2966	print_keyvalue, doc_keyvalue,
2967	&cfg_rep_uint32, &controls_perm_kw
2968};
2969
2970static keyword_type_t controls_owner_kw = {
2971	"owner", &cfg_type_uint32
2972};
2973
2974static cfg_type_t cfg_type_controls_owner = {
2975	"controls_owner", parse_keyvalue,
2976	print_keyvalue, doc_keyvalue,
2977	&cfg_rep_uint32, &controls_owner_kw
2978};
2979
2980static keyword_type_t controls_group_kw = {
2981	"group", &cfg_type_uint32
2982};
2983
2984static cfg_type_t cfg_type_controls_group = {
2985	"controls_allow", parse_keyvalue,
2986	print_keyvalue, doc_keyvalue,
2987	&cfg_rep_uint32, &controls_group_kw
2988};
2989
2990static cfg_tuplefielddef_t unixcontrol_fields[] = {
2991	{ "path", &cfg_type_qstring, 0 },
2992	{ "perm", &cfg_type_controls_perm, 0 },
2993	{ "owner", &cfg_type_controls_owner, 0 },
2994	{ "group", &cfg_type_controls_group, 0 },
2995	{ "keys", &cfg_type_controls_keys, 0 },
2996	{ "read-only", &cfg_type_controls_readonly, 0 },
2997	{ NULL, NULL, 0 }
2998};
2999
3000static cfg_type_t cfg_type_unixcontrol = {
3001	"unixcontrol", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
3002	&cfg_rep_tuple, unixcontrol_fields
3003};
3004
3005static cfg_clausedef_t
3006controls_clauses[] = {
3007	{ "inet", &cfg_type_inetcontrol, CFG_CLAUSEFLAG_MULTI },
3008	{ "unix", &cfg_type_unixcontrol, CFG_CLAUSEFLAG_MULTI },
3009	{ NULL, NULL, 0 }
3010};
3011
3012static cfg_clausedef_t *
3013controls_clausesets[] = {
3014	controls_clauses,
3015	NULL
3016};
3017static cfg_type_t cfg_type_controls = {
3018	"controls", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map,
3019	&controls_clausesets
3020};
3021
3022/*%
3023 * A "statistics-channels" statement is represented as a map with the
3024 * multivalued "inet" clauses.
3025 */
3026static void
3027doc_optional_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
3028	const keyword_type_t *kw = type->of;
3029	cfg_print_cstr(pctx, "[ ");
3030	cfg_print_cstr(pctx, kw->name);
3031	cfg_print_cstr(pctx, " ");
3032	cfg_doc_obj(pctx, kw->type);
3033	cfg_print_cstr(pctx, " ]");
3034}
3035
3036static cfg_type_t cfg_type_optional_allow = {
3037	"optional_allow", parse_optional_keyvalue, print_keyvalue,
3038	doc_optional_bracketed_list, &cfg_rep_list, &controls_allow_kw
3039};
3040
3041static cfg_tuplefielddef_t statserver_fields[] = {
3042	{ "address", &cfg_type_controls_sockaddr, 0 }, /* reuse controls def */
3043	{ "allow", &cfg_type_optional_allow, 0 },
3044	{ NULL, NULL, 0 }
3045};
3046
3047static cfg_type_t cfg_type_statschannel = {
3048	"statschannel", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
3049	&cfg_rep_tuple, statserver_fields
3050};
3051
3052static cfg_clausedef_t
3053statservers_clauses[] = {
3054	{ "inet", &cfg_type_statschannel, CFG_CLAUSEFLAG_MULTI },
3055	{ NULL, NULL, 0 }
3056};
3057
3058static cfg_clausedef_t *
3059statservers_clausesets[] = {
3060	statservers_clauses,
3061	NULL
3062};
3063
3064static cfg_type_t cfg_type_statschannels = {
3065	"statistics-channels", cfg_parse_map, cfg_print_map, cfg_doc_map,
3066	&cfg_rep_map,	&statservers_clausesets
3067};
3068
3069/*%
3070 * An optional class, as used in view and zone statements.
3071 */
3072static isc_result_t
3073parse_optional_class(cfg_parser_t *pctx, const cfg_type_t *type,
3074		     cfg_obj_t **ret)
3075{
3076	isc_result_t result;
3077	UNUSED(type);
3078	CHECK(cfg_peektoken(pctx, 0));
3079	if (pctx->token.type == isc_tokentype_string)
3080		CHECK(cfg_parse_obj(pctx, &cfg_type_ustring, ret));
3081	else
3082		CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
3083 cleanup:
3084	return (result);
3085}
3086
3087static void
3088doc_optional_class(cfg_printer_t *pctx, const cfg_type_t *type) {
3089	UNUSED(type);
3090	cfg_print_cstr(pctx, "[ <class> ]");
3091}
3092
3093static cfg_type_t cfg_type_optional_class = {
3094	"optional_class", parse_optional_class, NULL, doc_optional_class,
3095	NULL, NULL
3096};
3097
3098static isc_result_t
3099parse_querysource(cfg_parser_t *pctx, const cfg_type_t *type,
3100		  cfg_obj_t **ret)
3101{
3102	isc_result_t result;
3103	cfg_obj_t *obj = NULL;
3104	isc_netaddr_t netaddr;
3105	in_port_t port = 0;
3106	isc_dscp_t dscp = -1;
3107	unsigned int have_address = 0;
3108	unsigned int have_port = 0;
3109	unsigned int have_dscp = 0;
3110	const unsigned int *flagp = type->of;
3111
3112	if ((*flagp & CFG_ADDR_V4OK) != 0) {
3113		isc_netaddr_any(&netaddr);
3114	} else if ((*flagp & CFG_ADDR_V6OK) != 0) {
3115		isc_netaddr_any6(&netaddr);
3116	} else {
3117		INSIST(0);
3118		ISC_UNREACHABLE();
3119	}
3120
3121	for (;;) {
3122		CHECK(cfg_peektoken(pctx, 0));
3123		if (pctx->token.type == isc_tokentype_string) {
3124			if (strcasecmp(TOKEN_STRING(pctx),
3125				       "address") == 0)
3126			{
3127				/* read "address" */
3128				CHECK(cfg_gettoken(pctx, 0));
3129				CHECK(cfg_parse_rawaddr(pctx, *flagp,
3130							&netaddr));
3131				have_address++;
3132			} else if (strcasecmp(TOKEN_STRING(pctx), "port") == 0)
3133			{
3134				/* read "port" */
3135				CHECK(cfg_gettoken(pctx, 0));
3136				CHECK(cfg_parse_rawport(pctx,
3137							CFG_ADDR_WILDOK,
3138							&port));
3139				have_port++;
3140			} else if (strcasecmp(TOKEN_STRING(pctx), "dscp") == 0)
3141			{
3142				/* read "dscp" */
3143				CHECK(cfg_gettoken(pctx, 0));
3144				CHECK(cfg_parse_dscp(pctx, &dscp));
3145				have_dscp++;
3146			} else if (have_port == 0 && have_dscp == 0 &&
3147				   have_address == 0)
3148			{
3149				return (cfg_parse_sockaddr(pctx, type, ret));
3150			} else {
3151				cfg_parser_error(pctx, CFG_LOG_NEAR,
3152					     "expected 'address', 'port', "
3153					     "or 'dscp'");
3154				return (ISC_R_UNEXPECTEDTOKEN);
3155			}
3156		} else
3157			break;
3158	}
3159	if (have_address > 1 || have_port > 1 ||
3160	    have_address + have_port == 0) {
3161		cfg_parser_error(pctx, 0, "expected one address and/or port");
3162		return (ISC_R_UNEXPECTEDTOKEN);
3163	}
3164
3165	if (have_dscp > 1) {
3166		cfg_parser_error(pctx, 0, "expected at most one dscp");
3167		return (ISC_R_UNEXPECTEDTOKEN);
3168	}
3169
3170	CHECK(cfg_create_obj(pctx, &cfg_type_querysource, &obj));
3171	isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
3172	obj->value.sockaddrdscp.dscp = dscp;
3173	*ret = obj;
3174	return (ISC_R_SUCCESS);
3175
3176 cleanup:
3177	cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid query source");
3178	CLEANUP_OBJ(obj);
3179	return (result);
3180}
3181
3182static void
3183print_querysource(cfg_printer_t *pctx, const cfg_obj_t *obj) {
3184	isc_netaddr_t na;
3185	isc_netaddr_fromsockaddr(&na, &obj->value.sockaddr);
3186	cfg_print_cstr(pctx, "address ");
3187	cfg_print_rawaddr(pctx, &na);
3188	cfg_print_cstr(pctx, " port ");
3189	cfg_print_rawuint(pctx, isc_sockaddr_getport(&obj->value.sockaddr));
3190	if (obj->value.sockaddrdscp.dscp != -1) {
3191		cfg_print_cstr(pctx, " dscp ");
3192		cfg_print_rawuint(pctx, obj->value.sockaddrdscp.dscp);
3193	}
3194}
3195
3196static void
3197doc_querysource(cfg_printer_t *pctx, const cfg_type_t *type) {
3198	const unsigned int *flagp = type->of;
3199
3200	cfg_print_cstr(pctx, "( ( [ address ] ( ");
3201	if ((*flagp & CFG_ADDR_V4OK) != 0) {
3202		cfg_print_cstr(pctx, "<ipv4_address>");
3203	} else if ((*flagp & CFG_ADDR_V6OK) != 0) {
3204		cfg_print_cstr(pctx, "<ipv6_address>");
3205	} else {
3206		INSIST(0);
3207		ISC_UNREACHABLE();
3208	}
3209	cfg_print_cstr(pctx, " | * ) [ port ( <integer> | * ) ] ) | "
3210		       "( [ [ address ] ( ");
3211	if ((*flagp & CFG_ADDR_V4OK) != 0) {
3212		cfg_print_cstr(pctx, "<ipv4_address>");
3213	} else if ((*flagp & CFG_ADDR_V6OK) != 0) {
3214		cfg_print_cstr(pctx, "<ipv6_address>");
3215	} else {
3216		INSIST(0);
3217		ISC_UNREACHABLE();
3218	}
3219	cfg_print_cstr(pctx, " | * ) ] port ( <integer> | * ) ) )"
3220		       " [ dscp <integer> ]");
3221}
3222
3223static unsigned int sockaddr4wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V4OK |
3224					  CFG_ADDR_DSCPOK;
3225static unsigned int sockaddr6wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V6OK |
3226					  CFG_ADDR_DSCPOK;
3227
3228static cfg_type_t cfg_type_querysource4 = {
3229	"querysource4", parse_querysource, NULL, doc_querysource,
3230	NULL, &sockaddr4wild_flags
3231};
3232
3233static cfg_type_t cfg_type_querysource6 = {
3234	"querysource6", parse_querysource, NULL, doc_querysource,
3235	NULL, &sockaddr6wild_flags
3236};
3237
3238static cfg_type_t cfg_type_querysource = {
3239	"querysource", NULL, print_querysource, NULL, &cfg_rep_sockaddr, NULL
3240};
3241
3242/*%
3243 * The socket address syntax in the "controls" statement is silly.
3244 * It allows both socket address families, but also allows "*",
3245 * whis is gratuitously interpreted as the IPv4 wildcard address.
3246 */
3247static unsigned int controls_sockaddr_flags =
3248	CFG_ADDR_V4OK | CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
3249static cfg_type_t cfg_type_controls_sockaddr = {
3250	"controls_sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr,
3251	cfg_doc_sockaddr, &cfg_rep_sockaddr, &controls_sockaddr_flags
3252};
3253
3254/*%
3255 * Handle the special kludge syntax of the "keys" clause in the "server"
3256 * statement, which takes a single key with or without braces and semicolon.
3257 */
3258static isc_result_t
3259parse_server_key_kludge(cfg_parser_t *pctx, const cfg_type_t *type,
3260			cfg_obj_t **ret)
3261{
3262	isc_result_t result;
3263	bool braces = false;
3264	UNUSED(type);
3265
3266	/* Allow opening brace. */
3267	CHECK(cfg_peektoken(pctx, 0));
3268	if (pctx->token.type == isc_tokentype_special &&
3269	    pctx->token.value.as_char == '{') {
3270		CHECK(cfg_gettoken(pctx, 0));
3271		braces = true;
3272	}
3273
3274	CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret));
3275
3276	if (braces) {
3277		/* Skip semicolon if present. */
3278		CHECK(cfg_peektoken(pctx, 0));
3279		if (pctx->token.type == isc_tokentype_special &&
3280		    pctx->token.value.as_char == ';')
3281			CHECK(cfg_gettoken(pctx, 0));
3282
3283		CHECK(cfg_parse_special(pctx, '}'));
3284	}
3285 cleanup:
3286	return (result);
3287}
3288static cfg_type_t cfg_type_server_key_kludge = {
3289	"server_key", parse_server_key_kludge, NULL, cfg_doc_terminal,
3290	NULL, NULL
3291};
3292
3293
3294/*%
3295 * An optional logging facility.
3296 */
3297
3298static isc_result_t
3299parse_optional_facility(cfg_parser_t *pctx, const cfg_type_t *type,
3300			cfg_obj_t **ret)
3301{
3302	isc_result_t result;
3303	UNUSED(type);
3304
3305	CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
3306	if (pctx->token.type == isc_tokentype_string ||
3307	    pctx->token.type == isc_tokentype_qstring) {
3308		CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret));
3309	} else {
3310		CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
3311	}
3312 cleanup:
3313	return (result);
3314}
3315
3316static void
3317doc_optional_facility(cfg_printer_t *pctx, const cfg_type_t *type) {
3318	UNUSED(type);
3319	cfg_print_cstr(pctx, "[ <syslog_facility> ]");
3320}
3321
3322static cfg_type_t cfg_type_optional_facility = {
3323	"optional_facility", parse_optional_facility, NULL,
3324	doc_optional_facility, NULL, NULL
3325};
3326
3327
3328/*%
3329 * A log severity.  Return as a string, except "debug N",
3330 * which is returned as a keyword object.
3331 */
3332
3333static keyword_type_t debug_kw = { "debug", &cfg_type_uint32 };
3334static cfg_type_t cfg_type_debuglevel = {
3335	"debuglevel", parse_keyvalue,
3336	print_keyvalue, doc_keyvalue,
3337	&cfg_rep_uint32, &debug_kw
3338};
3339
3340static isc_result_t
3341parse_logseverity(cfg_parser_t *pctx, const cfg_type_t *type,
3342		  cfg_obj_t **ret)
3343{
3344	isc_result_t result;
3345	UNUSED(type);
3346
3347	CHECK(cfg_peektoken(pctx, 0));
3348	if (pctx->token.type == isc_tokentype_string &&
3349	    strcasecmp(TOKEN_STRING(pctx), "debug") == 0) {
3350		CHECK(cfg_gettoken(pctx, 0)); /* read "debug" */
3351		CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER));
3352		if (pctx->token.type == isc_tokentype_number) {
3353			CHECK(cfg_parse_uint32(pctx, NULL, ret));
3354		} else {
3355			/*
3356			 * The debug level is optional and defaults to 1.
3357			 * This makes little sense, but we support it for
3358			 * compatibility with BIND 8.
3359			 */
3360			CHECK(cfg_create_obj(pctx, &cfg_type_uint32, ret));
3361			(*ret)->value.uint32 = 1;
3362		}
3363		(*ret)->type = &cfg_type_debuglevel; /* XXX kludge */
3364	} else {
3365		CHECK(cfg_parse_obj(pctx, &cfg_type_loglevel, ret));
3366	}
3367 cleanup:
3368	return (result);
3369}
3370
3371static cfg_type_t cfg_type_logseverity = {
3372	"log_severity", parse_logseverity, NULL, cfg_doc_terminal,
3373	NULL, NULL
3374};
3375
3376/*%
3377 * The "file" clause of the "channel" statement.
3378 * This is yet another special case.
3379 */
3380
3381static const char *logversions_enums[] = { "unlimited", NULL };
3382static isc_result_t
3383parse_logversions(cfg_parser_t *pctx, const cfg_type_t *type,
3384		  cfg_obj_t **ret)
3385{
3386	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_uint32, ret));
3387}
3388
3389static void
3390doc_logversions(cfg_printer_t *pctx, const cfg_type_t *type) {
3391	cfg_doc_enum_or_other(pctx, type, &cfg_type_uint32);
3392}
3393
3394static cfg_type_t cfg_type_logversions = {
3395	"logversions", parse_logversions, cfg_print_ustring, doc_logversions,
3396	&cfg_rep_string, logversions_enums
3397};
3398
3399static const char *logsuffix_enums[] = { "increment", "timestamp", NULL };
3400static cfg_type_t cfg_type_logsuffix = {
3401	"logsuffix", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
3402	&cfg_rep_string, &logsuffix_enums
3403};
3404
3405static cfg_tuplefielddef_t logfile_fields[] = {
3406	{ "file", &cfg_type_qstring, 0 },
3407	{ "versions", &cfg_type_logversions, 0 },
3408	{ "size", &cfg_type_size, 0 },
3409	{ "suffix", &cfg_type_logsuffix, 0 },
3410	{ NULL, NULL, 0 }
3411};
3412
3413static isc_result_t
3414parse_logfile(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3415	isc_result_t result;
3416	cfg_obj_t *obj = NULL;
3417	const cfg_tuplefielddef_t *fields = type->of;
3418
3419	CHECK(cfg_create_tuple(pctx, type, &obj));
3420
3421	/* Parse the mandatory "file" field */
3422	CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
3423
3424	/* Parse "versions" and "size" fields in any order. */
3425	for (;;) {
3426		CHECK(cfg_peektoken(pctx, 0));
3427		if (pctx->token.type == isc_tokentype_string) {
3428			CHECK(cfg_gettoken(pctx, 0));
3429			if (strcasecmp(TOKEN_STRING(pctx),
3430				       "versions") == 0 &&
3431			    obj->value.tuple[1] == NULL) {
3432				CHECK(cfg_parse_obj(pctx, fields[1].type,
3433					    &obj->value.tuple[1]));
3434			} else if (strcasecmp(TOKEN_STRING(pctx),
3435					      "size") == 0 &&
3436				   obj->value.tuple[2] == NULL) {
3437				CHECK(cfg_parse_obj(pctx, fields[2].type,
3438					    &obj->value.tuple[2]));
3439			} else if (strcasecmp(TOKEN_STRING(pctx),
3440					      "suffix") == 0 &&
3441				   obj->value.tuple[3] == NULL) {
3442				CHECK(cfg_parse_obj(pctx, fields[3].type,
3443					    &obj->value.tuple[3]));
3444			} else {
3445				break;
3446			}
3447		} else {
3448			break;
3449		}
3450	}
3451
3452	/* Create void objects for missing optional values. */
3453	if (obj->value.tuple[1] == NULL)
3454		CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1]));
3455	if (obj->value.tuple[2] == NULL)
3456		CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2]));
3457	if (obj->value.tuple[3] == NULL)
3458		CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[3]));
3459
3460	*ret = obj;
3461	return (ISC_R_SUCCESS);
3462
3463 cleanup:
3464	CLEANUP_OBJ(obj);
3465	return (result);
3466}
3467
3468static void
3469print_logfile(cfg_printer_t *pctx, const cfg_obj_t *obj) {
3470	cfg_print_obj(pctx, obj->value.tuple[0]); /* file */
3471	if (obj->value.tuple[1]->type->print != cfg_print_void) {
3472		cfg_print_cstr(pctx, " versions ");
3473		cfg_print_obj(pctx, obj->value.tuple[1]);
3474	}
3475	if (obj->value.tuple[2]->type->print != cfg_print_void) {
3476		cfg_print_cstr(pctx, " size ");
3477		cfg_print_obj(pctx, obj->value.tuple[2]);
3478	}
3479	if (obj->value.tuple[3]->type->print != cfg_print_void) {
3480		cfg_print_cstr(pctx, " suffix ");
3481		cfg_print_obj(pctx, obj->value.tuple[3]);
3482	}
3483}
3484
3485
3486static void
3487doc_logfile(cfg_printer_t *pctx, const cfg_type_t *type) {
3488	UNUSED(type);
3489	cfg_print_cstr(pctx, "<quoted_string>");
3490	cfg_print_cstr(pctx, " ");
3491	cfg_print_cstr(pctx, "[ versions ( unlimited | <integer> ) ]");
3492	cfg_print_cstr(pctx, " ");
3493	cfg_print_cstr(pctx, "[ size <size> ]");
3494	cfg_print_cstr(pctx, " ");
3495	cfg_print_cstr(pctx, "[ suffix ( increment | timestamp ) ]");
3496}
3497
3498static cfg_type_t cfg_type_logfile = {
3499	"log_file", parse_logfile, print_logfile, doc_logfile,
3500	&cfg_rep_tuple, logfile_fields
3501};
3502
3503/*% An IPv4 address with optional dscp and port, "*" accepted as wildcard. */
3504static cfg_type_t cfg_type_sockaddr4wild = {
3505	"sockaddr4wild", cfg_parse_sockaddr, cfg_print_sockaddr,
3506	cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr4wild_flags
3507};
3508
3509/*% An IPv6 address with optional port, "*" accepted as wildcard. */
3510static cfg_type_t cfg_type_sockaddr6wild = {
3511	"v6addrportwild", cfg_parse_sockaddr, cfg_print_sockaddr,
3512	cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr6wild_flags
3513};
3514
3515/*%
3516 * rndc
3517 */
3518
3519static cfg_clausedef_t
3520rndcconf_options_clauses[] = {
3521	{ "default-key", &cfg_type_astring, 0 },
3522	{ "default-port", &cfg_type_uint32, 0 },
3523	{ "default-server", &cfg_type_astring, 0 },
3524	{ "default-source-address", &cfg_type_netaddr4wild, 0 },
3525	{ "default-source-address-v6", &cfg_type_netaddr6wild, 0 },
3526	{ NULL, NULL, 0 }
3527};
3528
3529static cfg_clausedef_t *
3530rndcconf_options_clausesets[] = {
3531	rndcconf_options_clauses,
3532	NULL
3533};
3534
3535static cfg_type_t cfg_type_rndcconf_options = {
3536	"rndcconf_options", cfg_parse_map, cfg_print_map, cfg_doc_map,
3537	&cfg_rep_map, rndcconf_options_clausesets
3538};
3539
3540static cfg_clausedef_t
3541rndcconf_server_clauses[] = {
3542	{ "key", &cfg_type_astring, 0 },
3543	{ "port", &cfg_type_uint32, 0 },
3544	{ "source-address", &cfg_type_netaddr4wild, 0 },
3545	{ "source-address-v6", &cfg_type_netaddr6wild, 0 },
3546	{ "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 },
3547	{ NULL, NULL, 0 }
3548};
3549
3550static cfg_clausedef_t *
3551rndcconf_server_clausesets[] = {
3552	rndcconf_server_clauses,
3553	NULL
3554};
3555
3556static cfg_type_t cfg_type_rndcconf_server = {
3557	"rndcconf_server", cfg_parse_named_map, cfg_print_map, cfg_doc_map,
3558	&cfg_rep_map, rndcconf_server_clausesets
3559};
3560
3561static cfg_clausedef_t
3562rndcconf_clauses[] = {
3563	{ "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
3564	{ "server", &cfg_type_rndcconf_server, CFG_CLAUSEFLAG_MULTI },
3565	{ "options", &cfg_type_rndcconf_options, 0 },
3566	{ NULL, NULL, 0 }
3567};
3568
3569static cfg_clausedef_t *
3570rndcconf_clausesets[] = {
3571	rndcconf_clauses,
3572	NULL
3573};
3574
3575LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndcconf = {
3576	"rndcconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
3577	&cfg_rep_map, rndcconf_clausesets
3578};
3579
3580static cfg_clausedef_t
3581rndckey_clauses[] = {
3582	{ "key", &cfg_type_key, 0 },
3583	{ NULL, NULL, 0 }
3584};
3585
3586static cfg_clausedef_t *
3587rndckey_clausesets[] = {
3588	rndckey_clauses,
3589	NULL
3590};
3591
3592LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndckey = {
3593	"rndckey", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
3594	&cfg_rep_map, rndckey_clausesets
3595};
3596
3597/*
3598 * session.key has exactly the same syntax as rndc.key, but it's defined
3599 * separately for clarity (and so we can extend it someday, if needed).
3600 */
3601LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_sessionkey = {
3602	"sessionkey", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
3603	&cfg_rep_map, rndckey_clausesets
3604};
3605
3606static cfg_tuplefielddef_t nameport_fields[] = {
3607	{ "name", &cfg_type_astring, 0 },
3608	{ "port", &cfg_type_optional_port, 0 },
3609	{ "dscp", &cfg_type_optional_dscp, 0 },
3610	{ NULL, NULL, 0 }
3611};
3612
3613static cfg_type_t cfg_type_nameport = {
3614	"nameport", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
3615	&cfg_rep_tuple, nameport_fields
3616};
3617
3618static void
3619doc_sockaddrnameport(cfg_printer_t *pctx, const cfg_type_t *type) {
3620	UNUSED(type);
3621	cfg_print_cstr(pctx, "( ");
3622	cfg_print_cstr(pctx, "<quoted_string>");
3623	cfg_print_cstr(pctx, " ");
3624	cfg_print_cstr(pctx, "[ port <integer> ]");
3625	cfg_print_cstr(pctx, " ");
3626	cfg_print_cstr(pctx, "[ dscp <integer> ]");
3627	cfg_print_cstr(pctx, " | ");
3628	cfg_print_cstr(pctx, "<ipv4_address>");
3629	cfg_print_cstr(pctx, " ");
3630	cfg_print_cstr(pctx, "[ port <integer> ]");
3631	cfg_print_cstr(pctx, " ");
3632	cfg_print_cstr(pctx, "[ dscp <integer> ]");
3633	cfg_print_cstr(pctx, " | ");
3634	cfg_print_cstr(pctx, "<ipv6_address>");
3635	cfg_print_cstr(pctx, " ");
3636	cfg_print_cstr(pctx, "[ port <integer> ]");
3637	cfg_print_cstr(pctx, " ");
3638	cfg_print_cstr(pctx, "[ dscp <integer> ]");
3639	cfg_print_cstr(pctx, " )");
3640}
3641
3642static isc_result_t
3643parse_sockaddrnameport(cfg_parser_t *pctx, const cfg_type_t *type,
3644		       cfg_obj_t **ret)
3645{
3646	isc_result_t result;
3647	cfg_obj_t *obj = NULL;
3648	UNUSED(type);
3649
3650	CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
3651	if (pctx->token.type == isc_tokentype_string ||
3652	    pctx->token.type == isc_tokentype_qstring) {
3653		if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK))
3654			CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr,
3655						 ret));
3656		else {
3657			const cfg_tuplefielddef_t *fields =
3658						   cfg_type_nameport.of;
3659			CHECK(cfg_create_tuple(pctx, &cfg_type_nameport,
3660					       &obj));
3661			CHECK(cfg_parse_obj(pctx, fields[0].type,
3662					    &obj->value.tuple[0]));
3663			CHECK(cfg_parse_obj(pctx, fields[1].type,
3664					    &obj->value.tuple[1]));
3665			CHECK(cfg_parse_obj(pctx, fields[2].type,
3666					    &obj->value.tuple[2]));
3667			*ret = obj;
3668			obj = NULL;
3669		}
3670	} else {
3671		cfg_parser_error(pctx, CFG_LOG_NEAR,
3672			     "expected IP address or hostname");
3673		return (ISC_R_UNEXPECTEDTOKEN);
3674	}
3675 cleanup:
3676	CLEANUP_OBJ(obj);
3677	return (result);
3678}
3679
3680static cfg_type_t cfg_type_sockaddrnameport = {
3681	"sockaddrnameport_element", parse_sockaddrnameport, NULL,
3682	 doc_sockaddrnameport, NULL, NULL
3683};
3684
3685static cfg_type_t cfg_type_bracketed_sockaddrnameportlist = {
3686	"bracketed_sockaddrnameportlist", cfg_parse_bracketed_list,
3687	cfg_print_bracketed_list, cfg_doc_bracketed_list,
3688	&cfg_rep_list, &cfg_type_sockaddrnameport
3689};
3690
3691/*%
3692 * A list of socket addresses or name with an optional default port,
3693 * as used in the dual-stack-servers option.  E.g.,
3694 * "port 1234 { dual-stack-servers.net; 10.0.0.1; 1::2 port 69; }"
3695 */
3696static cfg_tuplefielddef_t nameportiplist_fields[] = {
3697	{ "port", &cfg_type_optional_port, 0 },
3698	{ "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 },
3699	{ NULL, NULL, 0 }
3700};
3701
3702static cfg_type_t cfg_type_nameportiplist = {
3703	"nameportiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
3704	&cfg_rep_tuple, nameportiplist_fields
3705};
3706
3707/*%
3708 * masters element.
3709 */
3710
3711static void
3712doc_masterselement(cfg_printer_t *pctx, const cfg_type_t *type) {
3713	UNUSED(type);
3714	cfg_print_cstr(pctx, "( ");
3715	cfg_print_cstr(pctx, "<masters>");
3716	cfg_print_cstr(pctx, " | ");
3717	cfg_print_cstr(pctx, "<ipv4_address>");
3718	cfg_print_cstr(pctx, " ");
3719	cfg_print_cstr(pctx, "[ port <integer> ]");
3720	cfg_print_cstr(pctx, " | ");
3721	cfg_print_cstr(pctx, "<ipv6_address>");
3722	cfg_print_cstr(pctx, " ");
3723	cfg_print_cstr(pctx, "[ port <integer> ]");
3724	cfg_print_cstr(pctx, " )");
3725}
3726
3727static isc_result_t
3728parse_masterselement(cfg_parser_t *pctx, const cfg_type_t *type,
3729		     cfg_obj_t **ret)
3730{
3731	isc_result_t result;
3732	cfg_obj_t *obj = NULL;
3733	UNUSED(type);
3734
3735	CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
3736	if (pctx->token.type == isc_tokentype_string ||
3737	    pctx->token.type == isc_tokentype_qstring) {
3738		if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK))
3739			CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr,
3740			      ret));
3741		else
3742			CHECK(cfg_parse_astring(pctx, &cfg_type_astring, ret));
3743	} else {
3744		cfg_parser_error(pctx, CFG_LOG_NEAR,
3745			     "expected IP address or masters name");
3746		return (ISC_R_UNEXPECTEDTOKEN);
3747	}
3748 cleanup:
3749	CLEANUP_OBJ(obj);
3750	return (result);
3751}
3752
3753static cfg_type_t cfg_type_masterselement = {
3754	"masters_element", parse_masterselement, NULL,
3755	 doc_masterselement, NULL, NULL
3756};
3757
3758static isc_result_t
3759parse_ttlval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3760	isc_result_t result;
3761	cfg_obj_t *obj = NULL;
3762	uint32_t ttl;
3763
3764	UNUSED(type);
3765
3766	CHECK(cfg_gettoken(pctx, 0));
3767	if (pctx->token.type != isc_tokentype_string) {
3768		result = ISC_R_UNEXPECTEDTOKEN;
3769		goto cleanup;
3770	}
3771
3772	result = dns_ttl_fromtext(&pctx->token.value.as_textregion, &ttl);
3773	if (result == ISC_R_RANGE ) {
3774		cfg_parser_error(pctx, CFG_LOG_NEAR, "TTL out of range ");
3775		return (result);
3776	} else if (result != ISC_R_SUCCESS)
3777		goto cleanup;
3778
3779	CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
3780	obj->value.uint32 = ttl;
3781	*ret = obj;
3782	return (ISC_R_SUCCESS);
3783
3784 cleanup:
3785	cfg_parser_error(pctx, CFG_LOG_NEAR,
3786			 "expected integer and optional unit");
3787	return (result);
3788}
3789
3790/*%
3791 * A TTL value (number + optional unit).
3792 */
3793static cfg_type_t cfg_type_ttlval = {
3794	"ttlval", parse_ttlval, cfg_print_uint64, cfg_doc_terminal,
3795	&cfg_rep_uint64, NULL
3796};
3797
3798static isc_result_t
3799parse_maxttl(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3800	return (cfg_parse_enum_or_other(pctx, type, &cfg_type_ttlval, ret));
3801}
3802
3803static void
3804doc_maxttl(cfg_printer_t *pctx, const cfg_type_t *type) {
3805	cfg_doc_enum_or_other(pctx, type, &cfg_type_ttlval);
3806}
3807
3808/*%
3809 * A size or "unlimited", but not "default".
3810 */
3811static const char *maxttl_enums[] = { "unlimited", NULL };
3812static cfg_type_t cfg_type_maxttl = {
3813	"maxttl_no_default", parse_maxttl, cfg_print_ustring, doc_maxttl,
3814	&cfg_rep_string, maxttl_enums
3815};
3816
3817static int cmp_clause(const void *ap, const void *bp) {
3818	const cfg_clausedef_t *a = (const cfg_clausedef_t *)ap;
3819	const cfg_clausedef_t *b = (const cfg_clausedef_t *)bp;
3820	return (strcmp(a->name, b->name));
3821}
3822
3823bool
3824cfg_clause_validforzone(const char *name, unsigned int ztype) {
3825	const cfg_clausedef_t *clause;
3826	bool valid = false;
3827
3828	for (clause = zone_clauses; clause->name != NULL; clause++) {
3829		if ((clause->flags & ztype) == 0 ||
3830		    strcmp(clause->name, name) != 0)
3831		{
3832			continue;
3833		}
3834		valid = true;
3835	}
3836	for (clause = zone_only_clauses; clause->name != NULL; clause++) {
3837		if ((clause->flags & ztype) == 0 ||
3838		    strcmp(clause->name, name) != 0)
3839		{
3840			continue;
3841		}
3842		valid = true;
3843	}
3844
3845	return (valid);
3846}
3847
3848void
3849cfg_print_zonegrammar(const unsigned int zonetype,
3850		      void (*f)(void *closure, const char *text, int textlen),
3851		      void *closure)
3852{
3853#define NCLAUSES \
3854	(((sizeof(zone_clauses) + sizeof(zone_only_clauses)) / \
3855	  sizeof(clause[0])) - 1)
3856
3857	cfg_printer_t pctx;
3858	cfg_clausedef_t *clause = NULL;
3859	cfg_clausedef_t clauses[NCLAUSES];
3860
3861	pctx.f = f;
3862	pctx.closure = closure;
3863	pctx.indent = 0;
3864	pctx.flags = 0;
3865
3866	memmove(clauses, zone_clauses, sizeof(zone_clauses));
3867	memmove(clauses + sizeof(zone_clauses)/sizeof(zone_clauses[0]) - 1,
3868		zone_only_clauses, sizeof(zone_only_clauses));
3869	qsort(clauses, NCLAUSES - 1, sizeof(clause[0]), cmp_clause);
3870
3871	cfg_print_cstr(&pctx, "zone <string> [ <class> ] {\n");
3872	pctx.indent++;
3873
3874	switch (zonetype) {
3875	case CFG_ZONE_MASTER:
3876		cfg_print_indent(&pctx);
3877		cfg_print_cstr(&pctx, "type ( master | primary );\n");
3878		break;
3879	case CFG_ZONE_SLAVE:
3880		cfg_print_indent(&pctx);
3881		cfg_print_cstr(&pctx, "type ( slave | secondary );\n");
3882		break;
3883	case CFG_ZONE_MIRROR:
3884		cfg_print_indent(&pctx);
3885		cfg_print_cstr(&pctx, "type mirror;\n");
3886		break;
3887	case CFG_ZONE_STUB:
3888		cfg_print_indent(&pctx);
3889		cfg_print_cstr(&pctx, "type stub;\n");
3890		break;
3891	case CFG_ZONE_HINT:
3892		cfg_print_indent(&pctx);
3893		cfg_print_cstr(&pctx, "type hint;\n");
3894		break;
3895	case CFG_ZONE_FORWARD:
3896		cfg_print_indent(&pctx);
3897		cfg_print_cstr(&pctx, "type forward;\n");
3898		break;
3899	case CFG_ZONE_STATICSTUB:
3900		cfg_print_indent(&pctx);
3901		cfg_print_cstr(&pctx, "type static-stub;\n");
3902		break;
3903	case CFG_ZONE_REDIRECT:
3904		cfg_print_indent(&pctx);
3905		cfg_print_cstr(&pctx, "type redirect;\n");
3906		break;
3907	case CFG_ZONE_DELEGATION:
3908		cfg_print_indent(&pctx);
3909		cfg_print_cstr(&pctx, "type delegation-only;\n");
3910		break;
3911	case CFG_ZONE_INVIEW:
3912		/* no zone type is specified for these */
3913		break;
3914	default:
3915		INSIST(0);
3916		ISC_UNREACHABLE();
3917	}
3918
3919	for (clause = clauses; clause->name != NULL; clause++) {
3920		if ((clause->flags & zonetype) == 0 ||
3921		    strcasecmp(clause->name, "type") == 0) {
3922			continue;
3923		}
3924		cfg_print_indent(&pctx);
3925		cfg_print_cstr(&pctx, clause->name);
3926		cfg_print_cstr(&pctx, " ");
3927		cfg_doc_obj(&pctx, clause->type);
3928		cfg_print_cstr(&pctx, ";");
3929		cfg_print_clauseflags(&pctx, clause->flags);
3930		cfg_print_cstr(&pctx, "\n");
3931	}
3932
3933	pctx.indent--;
3934	cfg_print_cstr(&pctx, "};\n");
3935}
3936