1/*
2 * Copyright (C) 2004-2012  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2002, 2003  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* $Id$ */
19
20/*! \file */
21
22#include <config.h>
23
24#include <string.h>
25
26#include <isc/lex.h>
27#include <isc/mem.h>
28#include <isc/result.h>
29#include <isc/string.h>
30#include <isc/util.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 (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 (0)
48
49
50/*%
51 * Forward declarations of static functions.
52 */
53
54static isc_result_t
55parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype,
56		    const cfg_type_t *othertype, cfg_obj_t **ret);
57
58static isc_result_t
59parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
60
61static isc_result_t
62parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
63			cfg_obj_t **ret);
64
65static void
66print_updatepolicy(cfg_printer_t *pctx, const cfg_obj_t *obj);
67
68static isc_result_t
69parse_updatepolicy(cfg_parser_t *pctx, const cfg_type_t *type,
70		   cfg_obj_t **ret);
71static void
72print_updatepolicy(cfg_printer_t *pctx, const cfg_obj_t *obj);
73
74static void
75doc_updatepolicy(cfg_printer_t *pctx, const cfg_type_t *type);
76
77static void
78print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj);
79
80static void
81doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type);
82
83static void
84doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type);
85
86static cfg_type_t cfg_type_addrmatchelt;
87static cfg_type_t cfg_type_bracketed_sockaddrnameportlist;
88static cfg_type_t cfg_type_controls;
89static cfg_type_t cfg_type_controls_sockaddr;
90static cfg_type_t cfg_type_dialuptype;
91static cfg_type_t cfg_type_ixfrdifftype;
92static cfg_type_t cfg_type_key;
93static cfg_type_t cfg_type_logseverity;
94static cfg_type_t cfg_type_lwres;
95static cfg_type_t cfg_type_masterselement;
96static cfg_type_t cfg_type_nameportiplist;
97static cfg_type_t cfg_type_negated;
98static cfg_type_t cfg_type_notifytype;
99static cfg_type_t cfg_type_optional_allow;
100static cfg_type_t cfg_type_optional_class;
101static cfg_type_t cfg_type_optional_facility;
102static cfg_type_t cfg_type_optional_port;
103static cfg_type_t cfg_type_querysource4;
104static cfg_type_t cfg_type_querysource6;
105static cfg_type_t cfg_type_querysource;
106static cfg_type_t cfg_type_server;
107static cfg_type_t cfg_type_server_key_kludge;
108static cfg_type_t cfg_type_size;
109static cfg_type_t cfg_type_sizenodefault;
110static cfg_type_t cfg_type_sockaddr4wild;
111static cfg_type_t cfg_type_sockaddr6wild;
112static cfg_type_t cfg_type_statschannels;
113static cfg_type_t cfg_type_dynamically_loadable_zones;
114static cfg_type_t cfg_type_dynamically_loadable_zones_opts;
115static cfg_type_t cfg_type_v4_aaaa;
116
117/*
118 * Clauses that can be found in a 'dynamically loadable zones' statement
119 */
120static cfg_clausedef_t
121dynamically_loadable_zones_clauses[] = {
122	{ "database", &cfg_type_astring, 0 },
123	{ NULL, NULL, 0 }
124};
125
126/*
127 * A dynamically loadable zones statement.
128 */
129static cfg_tuplefielddef_t dynamically_loadable_zones_fields[] = {
130	{ "name", &cfg_type_astring, 0 },
131	{ "options", &cfg_type_dynamically_loadable_zones_opts, 0 },
132	{ NULL, NULL, 0 }
133};
134
135static cfg_type_t cfg_type_dynamically_loadable_zones = {
136	"dlz", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
137	&cfg_rep_tuple,
138	dynamically_loadable_zones_fields
139	};
140
141
142/*% tkey-dhkey */
143
144static cfg_tuplefielddef_t tkey_dhkey_fields[] = {
145	{ "name", &cfg_type_qstring, 0 },
146	{ "keyid", &cfg_type_uint32, 0 },
147	{ NULL, NULL, 0 }
148};
149
150static cfg_type_t cfg_type_tkey_dhkey = {
151	"tkey-dhkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
152	tkey_dhkey_fields
153};
154
155/*% listen-on */
156
157static cfg_tuplefielddef_t listenon_fields[] = {
158	{ "port", &cfg_type_optional_port, 0 },
159	{ "acl", &cfg_type_bracketed_aml, 0 },
160	{ NULL, NULL, 0 }
161};
162cfg_type_t cfg_type_listenon = {
163	"listenon", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, listenon_fields };
164
165/*% acl */
166
167static cfg_tuplefielddef_t acl_fields[] = {
168	{ "name", &cfg_type_astring, 0 },
169	{ "value", &cfg_type_bracketed_aml, 0 },
170	{ NULL, NULL, 0 }
171};
172
173cfg_type_t cfg_type_acl = {
174	"acl", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, acl_fields };
175
176/*% masters */
177static cfg_tuplefielddef_t masters_fields[] = {
178	{ "name", &cfg_type_astring, 0 },
179	{ "port", &cfg_type_optional_port, 0 },
180	{ "addresses", &cfg_type_bracketed_namesockaddrkeylist, 0 },
181	{ NULL, NULL, 0 }
182};
183
184static cfg_type_t cfg_type_masters = {
185	"masters", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, masters_fields };
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
200cfg_type_t cfg_type_namesockaddrkey = {
201	"namesockaddrkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
202	namesockaddrkey_fields
203};
204
205cfg_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, &cfg_type_namesockaddrkey
208};
209
210static cfg_tuplefielddef_t namesockaddrkeylist_fields[] = {
211	{ "port", &cfg_type_optional_port, 0 },
212	{ "addresses", &cfg_type_bracketed_namesockaddrkeylist, 0 },
213	{ NULL, NULL, 0 }
214};
215cfg_type_t cfg_type_namesockaddrkeylist = {
216	"sockaddrkeylist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
217	namesockaddrkeylist_fields
218};
219
220/*%
221 * A list of socket addresses with an optional default port,
222 * as used in the also-notify option.  E.g.,
223 * "port 1234 { 10.0.0.1; 1::2 port 69; }"
224 */
225static cfg_tuplefielddef_t portiplist_fields[] = {
226	{ "port", &cfg_type_optional_port, 0 },
227	{ "addresses", &cfg_type_bracketed_sockaddrlist, 0 },
228	{ NULL, NULL, 0 }
229};
230cfg_type_t cfg_type_portiplist = {
231	"portiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
232	portiplist_fields
233};
234
235/*%
236 * A public key, as in the "pubkey" statement.
237 */
238static cfg_tuplefielddef_t pubkey_fields[] = {
239	{ "flags", &cfg_type_uint32, 0 },
240	{ "protocol", &cfg_type_uint32, 0 },
241	{ "algorithm", &cfg_type_uint32, 0 },
242	{ "key", &cfg_type_qstring, 0 },
243	{ NULL, NULL, 0 }
244};
245static cfg_type_t cfg_type_pubkey = {
246	"pubkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
247	&cfg_rep_tuple, pubkey_fields };
248
249/*%
250 * A list of RR types, used in grant statements.
251 * Note that the old parser allows quotes around the RR type names.
252 */
253cfg_type_t cfg_type_rrtypelist = {
254	"rrtypelist", cfg_parse_spacelist, cfg_print_spacelist,
255	cfg_doc_terminal, &cfg_rep_list, &cfg_type_astring
256};
257
258static const char *mode_enums[] = { "grant", "deny", NULL };
259cfg_type_t cfg_type_mode = {
260	"mode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
261	&cfg_rep_string, &mode_enums
262};
263
264static isc_result_t
265parse_matchtype(cfg_parser_t *pctx, const cfg_type_t *type,
266		cfg_obj_t **ret) {
267	isc_result_t result;
268
269	CHECK(cfg_peektoken(pctx, 0));
270	if (pctx->token.type == isc_tokentype_string &&
271	    strcasecmp(TOKEN_STRING(pctx), "zonesub") == 0) {
272		pctx->flags |= CFG_PCTX_SKIP;
273	}
274	return (cfg_parse_enum(pctx, type, ret));
275
276 cleanup:
277	return (result);
278}
279
280static isc_result_t
281parse_matchname(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
282	isc_result_t result;
283	cfg_obj_t *obj = NULL;
284
285	if ((pctx->flags & CFG_PCTX_SKIP) != 0) {
286		pctx->flags &= ~CFG_PCTX_SKIP;
287		CHECK(cfg_parse_void(pctx, NULL, &obj));
288	} else
289		result = cfg_parse_astring(pctx, type, &obj);
290
291	*ret = obj;
292 cleanup:
293	return (result);
294}
295
296static void
297doc_matchname(cfg_printer_t *pctx, const cfg_type_t *type) {
298	cfg_print_chars(pctx, "[ ", 2);
299	cfg_doc_obj(pctx, type->of);
300	cfg_print_chars(pctx, " ]", 2);
301}
302
303static const char *matchtype_enums[] = {
304	"name", "subdomain", "wildcard", "self", "selfsub", "selfwild",
305	"krb5-self", "ms-self", "krb5-subdomain", "ms-subdomain",
306	"tcp-self", "6to4-self", "zonesub", "external", NULL };
307
308cfg_type_t cfg_type_matchtype = {
309	"matchtype", parse_matchtype, cfg_print_ustring,
310	cfg_doc_enum, &cfg_rep_string, &matchtype_enums
311};
312
313cfg_type_t cfg_type_matchname = {
314	"optional_matchname", parse_matchname, cfg_print_ustring,
315	&doc_matchname, &cfg_rep_string, &cfg_type_ustring
316};
317
318/*%
319 * A grant statement, used in the update policy.
320 */
321static cfg_tuplefielddef_t grant_fields[] = {
322	{ "mode", &cfg_type_mode, 0 },
323	{ "identity", &cfg_type_astring, 0 }, /* domain name */
324	{ "matchtype", &cfg_type_matchtype, 0 },
325	{ "name", &cfg_type_matchname, 0 }, /* domain name */
326	{ "types", &cfg_type_rrtypelist, 0 },
327	{ NULL, NULL, 0 }
328};
329cfg_type_t cfg_type_grant = {
330	"grant", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
331	 &cfg_rep_tuple, grant_fields
332};
333
334cfg_type_t cfg_type_updatepolicy = {
335	"update_policy", parse_updatepolicy, print_updatepolicy, doc_updatepolicy,
336	&cfg_rep_list, &cfg_type_grant
337};
338
339static isc_result_t
340parse_updatepolicy(cfg_parser_t *pctx, const cfg_type_t *type,
341		   cfg_obj_t **ret) {
342	isc_result_t result;
343	CHECK(cfg_gettoken(pctx, 0));
344	if (pctx->token.type == isc_tokentype_special &&
345	    pctx->token.value.as_char == '{') {
346		cfg_ungettoken(pctx);
347		return (cfg_parse_bracketed_list(pctx, type, ret));
348	}
349
350	if (pctx->token.type == isc_tokentype_string &&
351	    strcasecmp(TOKEN_STRING(pctx), "local") == 0) {
352		cfg_obj_t *obj = NULL;
353		CHECK(cfg_create_obj(pctx, &cfg_type_ustring, &obj));
354		obj->value.string.length = strlen("local");
355		obj->value.string.base	= isc_mem_get(pctx->mctx,
356						obj->value.string.length + 1);
357		if (obj->value.string.base == NULL) {
358			isc_mem_put(pctx->mctx, obj, sizeof(*obj));
359			return (ISC_R_NOMEMORY);
360		}
361		memcpy(obj->value.string.base, "local", 5);
362		obj->value.string.base[5] = '\0';
363		*ret = obj;
364		return (ISC_R_SUCCESS);
365	}
366
367	cfg_ungettoken(pctx);
368	return (ISC_R_UNEXPECTEDTOKEN);
369
370 cleanup:
371	return (result);
372}
373
374static void
375print_updatepolicy(cfg_printer_t *pctx, const cfg_obj_t *obj) {
376	if (cfg_obj_isstring(obj))
377		cfg_print_ustring(pctx, obj);
378	else
379		cfg_print_bracketed_list(pctx, obj);
380}
381
382static void
383doc_updatepolicy(cfg_printer_t *pctx, const cfg_type_t *type) {
384	cfg_print_cstr(pctx, "( local | { ");
385	cfg_doc_obj(pctx, type->of);
386	cfg_print_cstr(pctx, "; ... }");
387}
388
389/*%
390 * A view statement.
391 */
392static cfg_tuplefielddef_t view_fields[] = {
393	{ "name", &cfg_type_astring, 0 },
394	{ "class", &cfg_type_optional_class, 0 },
395	{ "options", &cfg_type_viewopts, 0 },
396	{ NULL, NULL, 0 }
397};
398cfg_type_t cfg_type_view = {
399	"view", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
400	 &cfg_rep_tuple, view_fields
401};
402
403/*%
404 * A zone statement.
405 */
406static cfg_tuplefielddef_t zone_fields[] = {
407	{ "name", &cfg_type_astring, 0 },
408	{ "class", &cfg_type_optional_class, 0 },
409	{ "options", &cfg_type_zoneopts, 0 },
410	{ NULL, NULL, 0 }
411};
412cfg_type_t cfg_type_zone = {
413	"zone", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
414	&cfg_rep_tuple, zone_fields
415};
416
417/*%
418 * A "category" clause in the "logging" statement.
419 */
420static cfg_tuplefielddef_t category_fields[] = {
421	{ "name", &cfg_type_astring, 0 },
422	{ "destinations", &cfg_type_destinationlist,0 },
423	{ NULL, NULL, 0 }
424};
425cfg_type_t cfg_type_category = {
426	"category", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
427	&cfg_rep_tuple, category_fields
428};
429
430
431/*%
432 * A dnssec key, as used in the "trusted-keys" statement.
433 */
434static cfg_tuplefielddef_t dnsseckey_fields[] = {
435	{ "name", &cfg_type_astring, 0 },
436	{ "flags", &cfg_type_uint32, 0 },
437	{ "protocol", &cfg_type_uint32, 0 },
438	{ "algorithm", &cfg_type_uint32, 0 },
439	{ "key", &cfg_type_qstring, 0 },
440	{ NULL, NULL, 0 }
441};
442static cfg_type_t cfg_type_dnsseckey = {
443	"dnsseckey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
444	&cfg_rep_tuple, dnsseckey_fields
445};
446
447/*%
448 * A managed key initialization specifier, as used in the
449 * "managed-keys" statement.
450 */
451static cfg_tuplefielddef_t managedkey_fields[] = {
452	{ "name", &cfg_type_astring, 0 },
453	{ "init", &cfg_type_ustring, 0 },   /* must be literal "initial-key" */
454	{ "flags", &cfg_type_uint32, 0 },
455	{ "protocol", &cfg_type_uint32, 0 },
456	{ "algorithm", &cfg_type_uint32, 0 },
457	{ "key", &cfg_type_qstring, 0 },
458	{ NULL, NULL, 0 }
459};
460static cfg_type_t cfg_type_managedkey = {
461	"managedkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
462	&cfg_rep_tuple, managedkey_fields
463};
464
465static keyword_type_t wild_class_kw = { "class", &cfg_type_ustring };
466
467static cfg_type_t cfg_type_optional_wild_class = {
468	"optional_wild_class", parse_optional_keyvalue, print_keyvalue,
469	doc_optional_keyvalue, &cfg_rep_string, &wild_class_kw
470};
471
472static keyword_type_t wild_type_kw = { "type", &cfg_type_ustring };
473
474static cfg_type_t cfg_type_optional_wild_type = {
475	"optional_wild_type", parse_optional_keyvalue,
476	print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &wild_type_kw
477};
478
479static keyword_type_t wild_name_kw = { "name", &cfg_type_qstring };
480
481static cfg_type_t cfg_type_optional_wild_name = {
482	"optional_wild_name", parse_optional_keyvalue,
483	print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &wild_name_kw
484};
485
486/*%
487 * An rrset ordering element.
488 */
489static cfg_tuplefielddef_t rrsetorderingelement_fields[] = {
490	{ "class", &cfg_type_optional_wild_class, 0 },
491	{ "type", &cfg_type_optional_wild_type, 0 },
492	{ "name", &cfg_type_optional_wild_name, 0 },
493	{ "order", &cfg_type_ustring, 0 }, /* must be literal "order" */
494	{ "ordering", &cfg_type_ustring, 0 },
495	{ NULL, NULL, 0 }
496};
497static cfg_type_t cfg_type_rrsetorderingelement = {
498	"rrsetorderingelement", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
499	rrsetorderingelement_fields
500};
501
502/*%
503 * A global or view "check-names" option.  Note that the zone
504 * "check-names" option has a different syntax.
505 */
506
507static const char *checktype_enums[] = { "master", "slave", "response", NULL };
508static cfg_type_t cfg_type_checktype = {
509	"checktype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
510	&cfg_rep_string, &checktype_enums
511};
512
513static const char *checkmode_enums[] = { "fail", "warn", "ignore", NULL };
514static cfg_type_t cfg_type_checkmode = {
515	"checkmode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
516	&cfg_rep_string, &checkmode_enums
517};
518
519static cfg_tuplefielddef_t checknames_fields[] = {
520	{ "type", &cfg_type_checktype, 0 },
521	{ "mode", &cfg_type_checkmode, 0 },
522	{ NULL, NULL, 0 }
523};
524
525static cfg_type_t cfg_type_checknames = {
526	"checknames", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
527	checknames_fields
528};
529
530cfg_type_t cfg_type_bracketed_sockaddrlist = {
531	"bracketed_sockaddrlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list,
532	&cfg_rep_list, &cfg_type_sockaddr
533};
534
535static const char *autodnssec_enums[] = { "allow", "maintain", "off", NULL };
536static cfg_type_t cfg_type_autodnssec = {
537	"autodnssec", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
538	&cfg_rep_string, &autodnssec_enums
539};
540
541static cfg_type_t cfg_type_rrsetorder = {
542	"rrsetorder", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list,
543	&cfg_rep_list, &cfg_type_rrsetorderingelement
544};
545
546static keyword_type_t port_kw = { "port", &cfg_type_uint32 };
547
548static cfg_type_t cfg_type_optional_port = {
549	"optional_port", parse_optional_keyvalue, print_keyvalue,
550	doc_optional_keyvalue, &cfg_rep_uint32, &port_kw
551};
552
553/*% A list of keys, as in the "key" clause of the controls statement. */
554static cfg_type_t cfg_type_keylist = {
555	"keylist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
556	cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring
557};
558
559/*% A list of dnssec keys, as in "trusted-keys" */
560static cfg_type_t cfg_type_dnsseckeys = {
561	"dnsseckeys", cfg_parse_bracketed_list, cfg_print_bracketed_list,
562	cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_dnsseckey
563};
564
565/*%
566 * A list of managed key entries, as in "trusted-keys".  Currently
567 * (9.7.0) this has a format similar to dnssec keys, except the keyname
568 * is followed by the keyword "initial-key".  In future releases, this
569 * keyword may take other values indicating different methods for the
570 * key to be initialized.
571 */
572
573static cfg_type_t cfg_type_managedkeys = {
574	"managedkeys", cfg_parse_bracketed_list, cfg_print_bracketed_list,
575	cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_managedkey
576};
577
578static const char *forwardtype_enums[] = { "first", "only", NULL };
579static cfg_type_t cfg_type_forwardtype = {
580	"forwardtype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string,
581	&forwardtype_enums
582};
583
584static const char *zonetype_enums[] = {
585	"master", "slave", "stub", "static-stub", "hint", "forward",
586	"delegation-only", NULL };
587static cfg_type_t cfg_type_zonetype = {
588	"zonetype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
589	&cfg_rep_string, &zonetype_enums
590};
591
592static const char *loglevel_enums[] = {
593	"critical", "error", "warning", "notice", "info", "dynamic", NULL };
594static cfg_type_t cfg_type_loglevel = {
595	"loglevel", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string,
596	&loglevel_enums
597};
598
599static const char *transferformat_enums[] = {
600	"many-answers", "one-answer", NULL };
601static cfg_type_t cfg_type_transferformat = {
602	"transferformat", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string,
603	&transferformat_enums
604};
605
606/*%
607 * The special keyword "none", as used in the pid-file option.
608 */
609
610static void
611print_none(cfg_printer_t *pctx, const cfg_obj_t *obj) {
612	UNUSED(obj);
613	cfg_print_cstr(pctx, "none");
614}
615
616static cfg_type_t cfg_type_none = {
617	"none", NULL, print_none, NULL, &cfg_rep_void, NULL
618};
619
620/*%
621 * A quoted string or the special keyword "none".  Used in the pid-file option.
622 */
623static isc_result_t
624parse_qstringornone(cfg_parser_t *pctx, const cfg_type_t *type,
625		    cfg_obj_t **ret)
626{
627	isc_result_t result;
628
629	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
630	if (pctx->token.type == isc_tokentype_string &&
631	    strcasecmp(TOKEN_STRING(pctx), "none") == 0)
632		return (cfg_create_obj(pctx, &cfg_type_none, ret));
633	cfg_ungettoken(pctx);
634	return (cfg_parse_qstring(pctx, type, ret));
635 cleanup:
636	return (result);
637}
638
639static void
640doc_qstringornone(cfg_printer_t *pctx, const cfg_type_t *type) {
641	UNUSED(type);
642	cfg_print_cstr(pctx, "( <quoted_string> | none )");
643}
644
645static cfg_type_t cfg_type_qstringornone = {
646	"qstringornone", parse_qstringornone, NULL, doc_qstringornone,
647	NULL, NULL
648};
649
650/*%
651 * A boolean ("yes" or "no"), or the special keyword "auto".
652 * Used in the dnssec-validation option.
653 */
654static void
655print_auto(cfg_printer_t *pctx, const cfg_obj_t *obj) {
656	UNUSED(obj);
657	cfg_print_cstr(pctx, "auto");
658}
659
660static cfg_type_t cfg_type_auto = {
661	"auto", NULL, print_auto, NULL, &cfg_rep_void, NULL
662};
663
664static isc_result_t
665parse_boolorauto(cfg_parser_t *pctx, const cfg_type_t *type,
666		    cfg_obj_t **ret)
667{
668	isc_result_t result;
669
670	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
671	if (pctx->token.type == isc_tokentype_string &&
672	    strcasecmp(TOKEN_STRING(pctx), "auto") == 0)
673		return (cfg_create_obj(pctx, &cfg_type_auto, ret));
674	cfg_ungettoken(pctx);
675	return (cfg_parse_boolean(pctx, type, ret));
676 cleanup:
677	return (result);
678}
679
680static void
681print_boolorauto(cfg_printer_t *pctx, const cfg_obj_t *obj) {
682	if (obj->type->rep == &cfg_rep_void)
683		cfg_print_chars(pctx, "auto", 4);
684	else if (obj->value.boolean)
685		cfg_print_chars(pctx, "yes", 3);
686	else
687		cfg_print_chars(pctx, "no", 2);
688}
689
690static void
691doc_boolorauto(cfg_printer_t *pctx, const cfg_type_t *type) {
692	UNUSED(type);
693	cfg_print_cstr(pctx, "( yes | no | auto )");
694}
695
696static cfg_type_t cfg_type_boolorauto = {
697	"boolorauto", parse_boolorauto, print_boolorauto,
698	doc_boolorauto, NULL, NULL
699};
700
701/*%
702 * keyword hostname
703 */
704static void
705print_hostname(cfg_printer_t *pctx, const cfg_obj_t *obj) {
706	UNUSED(obj);
707	cfg_print_cstr(pctx, "hostname");
708}
709
710static cfg_type_t cfg_type_hostname = {
711	"hostname", NULL, print_hostname, NULL, &cfg_rep_boolean, NULL
712};
713
714/*%
715 * "server-id" argument.
716 */
717
718static isc_result_t
719parse_serverid(cfg_parser_t *pctx, const cfg_type_t *type,
720		    cfg_obj_t **ret)
721{
722	isc_result_t result;
723	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
724	if (pctx->token.type == isc_tokentype_string &&
725	    strcasecmp(TOKEN_STRING(pctx), "none") == 0)
726		return (cfg_create_obj(pctx, &cfg_type_none, ret));
727	if (pctx->token.type == isc_tokentype_string &&
728	    strcasecmp(TOKEN_STRING(pctx), "hostname") == 0) {
729		return (cfg_create_obj(pctx, &cfg_type_hostname, ret));
730	}
731	cfg_ungettoken(pctx);
732	return (cfg_parse_qstring(pctx, type, ret));
733 cleanup:
734	return (result);
735}
736
737static void
738doc_serverid(cfg_printer_t *pctx, const cfg_type_t *type) {
739	UNUSED(type);
740	cfg_print_cstr(pctx, "( <quoted_string> | none | hostname )");
741}
742
743static cfg_type_t cfg_type_serverid = {
744	"serverid", parse_serverid, NULL, doc_serverid, NULL, NULL };
745
746/*%
747 * Port list.
748 */
749static cfg_tuplefielddef_t porttuple_fields[] = {
750	{ "loport", &cfg_type_uint32, 0 },
751	{ "hiport", &cfg_type_uint32, 0 },
752	{ NULL, NULL, 0 }
753};
754static cfg_type_t cfg_type_porttuple = {
755	"porttuple", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
756	&cfg_rep_tuple, porttuple_fields
757};
758
759static isc_result_t
760parse_port(cfg_parser_t *pctx, cfg_obj_t **ret) {
761	isc_result_t result;
762
763	CHECK(cfg_parse_uint32(pctx, NULL, ret));
764	if ((*ret)->value.uint32 > 0xffff) {
765		cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid port");
766		cfg_obj_destroy(pctx, ret);
767		result = ISC_R_RANGE;
768	}
769
770 cleanup:
771	return (result);
772}
773
774static isc_result_t
775parse_portrange(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
776	isc_result_t result;
777	cfg_obj_t *obj = NULL;
778
779	UNUSED(type);
780
781	CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
782	if (pctx->token.type == isc_tokentype_number)
783		CHECK(parse_port(pctx, ret));
784	else {
785		CHECK(cfg_gettoken(pctx, 0));
786		if (pctx->token.type != isc_tokentype_string ||
787		    strcasecmp(TOKEN_STRING(pctx), "range") != 0) {
788			cfg_parser_error(pctx, CFG_LOG_NEAR,
789					 "expected integer or 'range'");
790			return (ISC_R_UNEXPECTEDTOKEN);
791		}
792		CHECK(cfg_create_tuple(pctx, &cfg_type_porttuple, &obj));
793		CHECK(parse_port(pctx, &obj->value.tuple[0]));
794		CHECK(parse_port(pctx, &obj->value.tuple[1]));
795		if (obj->value.tuple[0]->value.uint32 >
796		    obj->value.tuple[1]->value.uint32) {
797			cfg_parser_error(pctx, CFG_LOG_NOPREP,
798					 "low port '%u' must not be larger "
799					 "than high port",
800					 obj->value.tuple[0]->value.uint32);
801			result = ISC_R_RANGE;
802			goto cleanup;
803		}
804		*ret = obj;
805		obj = NULL;
806	}
807
808 cleanup:
809	if (obj != NULL)
810		cfg_obj_destroy(pctx, &obj);
811	return (result);
812}
813
814static cfg_type_t cfg_type_portrange = {
815	"portrange", parse_portrange, NULL, cfg_doc_terminal,
816	NULL, NULL
817};
818
819static cfg_type_t cfg_type_bracketed_portlist = {
820	"bracketed_sockaddrlist", cfg_parse_bracketed_list,
821	cfg_print_bracketed_list, cfg_doc_bracketed_list,
822	&cfg_rep_list, &cfg_type_portrange
823};
824
825/*%
826 * Clauses that can be found within the top level of the named.conf
827 * file only.
828 */
829static cfg_clausedef_t
830namedconf_clauses[] = {
831	{ "options", &cfg_type_options, 0 },
832	{ "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI },
833	{ "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI },
834	{ "masters", &cfg_type_masters, CFG_CLAUSEFLAG_MULTI },
835	{ "logging", &cfg_type_logging, 0 },
836	{ "view", &cfg_type_view, CFG_CLAUSEFLAG_MULTI },
837	{ "lwres", &cfg_type_lwres, CFG_CLAUSEFLAG_MULTI },
838	{ "statistics-channels", &cfg_type_statschannels,
839	  CFG_CLAUSEFLAG_MULTI },
840	{ NULL, NULL, 0 }
841};
842
843/*%
844 * Clauses that can occur at the top level or in the view
845 * statement, but not in the options block.
846 */
847static cfg_clausedef_t
848namedconf_or_view_clauses[] = {
849	{ "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
850	{ "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI },
851	/* only 1 DLZ per view allowed */
852	{ "dlz", &cfg_type_dynamically_loadable_zones, 0 },
853	{ "server", &cfg_type_server, CFG_CLAUSEFLAG_MULTI },
854	{ "trusted-keys", &cfg_type_dnsseckeys, CFG_CLAUSEFLAG_MULTI },
855	{ "managed-keys", &cfg_type_managedkeys, CFG_CLAUSEFLAG_MULTI },
856	{ NULL, NULL, 0 }
857};
858
859/*%
860 * Clauses that can occur in the bind.keys file.
861 */
862static cfg_clausedef_t
863bindkeys_clauses[] = {
864	{ "trusted-keys", &cfg_type_dnsseckeys, CFG_CLAUSEFLAG_MULTI },
865	{ "managed-keys", &cfg_type_managedkeys, CFG_CLAUSEFLAG_MULTI },
866	{ NULL, NULL, 0 }
867};
868
869/*%
870 * Clauses that can be found within the 'options' statement.
871 */
872static cfg_clausedef_t
873options_clauses[] = {
874	{ "avoid-v4-udp-ports", &cfg_type_bracketed_portlist, 0 },
875	{ "avoid-v6-udp-ports", &cfg_type_bracketed_portlist, 0 },
876	{ "bindkeys-file", &cfg_type_qstring, 0 },
877	{ "blackhole", &cfg_type_bracketed_aml, 0 },
878	{ "coresize", &cfg_type_size, 0 },
879	{ "datasize", &cfg_type_size, 0 },
880	{ "session-keyfile", &cfg_type_qstringornone, 0 },
881	{ "session-keyname", &cfg_type_astring, 0 },
882	{ "session-keyalg", &cfg_type_astring, 0 },
883	{ "deallocate-on-exit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
884	{ "directory", &cfg_type_qstring, CFG_CLAUSEFLAG_CALLBACK },
885	{ "dump-file", &cfg_type_qstring, 0 },
886	{ "fake-iquery", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
887	{ "files", &cfg_type_size, 0 },
888	{ "flush-zones-on-shutdown", &cfg_type_boolean, 0 },
889	{ "has-old-clients", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
890	{ "heartbeat-interval", &cfg_type_uint32, 0 },
891	{ "host-statistics", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTIMP },
892	{ "host-statistics-max", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
893	{ "hostname", &cfg_type_qstringornone, 0 },
894	{ "interface-interval", &cfg_type_uint32, 0 },
895	{ "listen-on", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
896	{ "listen-on-v6", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
897	{ "managed-keys-directory", &cfg_type_qstring, 0 },
898	{ "match-mapped-addresses", &cfg_type_boolean, 0 },
899	{ "memstatistics-file", &cfg_type_qstring, 0 },
900	{ "memstatistics", &cfg_type_boolean, 0 },
901	{ "multiple-cnames", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
902	{ "named-xfer", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
903	{ "pid-file", &cfg_type_qstringornone, 0 },
904	{ "port", &cfg_type_uint32, 0 },
905	{ "querylog", &cfg_type_boolean, 0 },
906	{ "recursing-file", &cfg_type_qstring, 0 },
907	{ "random-device", &cfg_type_qstring, 0 },
908	{ "recursive-clients", &cfg_type_uint32, 0 },
909	{ "reserved-sockets", &cfg_type_uint32, 0 },
910	{ "secroots-file", &cfg_type_qstring, 0 },
911	{ "serial-queries", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
912	{ "serial-query-rate", &cfg_type_uint32, 0 },
913	{ "server-id", &cfg_type_serverid, 0 },
914	{ "stacksize", &cfg_type_size, 0 },
915	{ "statistics-file", &cfg_type_qstring, 0 },
916	{ "statistics-interval", &cfg_type_uint32, CFG_CLAUSEFLAG_NYI },
917	{ "tcp-clients", &cfg_type_uint32, 0 },
918	{ "tcp-listen-queue", &cfg_type_uint32, 0 },
919	{ "tkey-dhkey", &cfg_type_tkey_dhkey, 0 },
920	{ "tkey-gssapi-credential", &cfg_type_qstring, 0 },
921	{ "tkey-gssapi-keytab", &cfg_type_qstring, 0 },
922	{ "tkey-domain", &cfg_type_qstring, 0 },
923	{ "transfers-per-ns", &cfg_type_uint32, 0 },
924	{ "transfers-in", &cfg_type_uint32, 0 },
925	{ "transfers-out", &cfg_type_uint32, 0 },
926	{ "treat-cr-as-space", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
927	{ "use-id-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
928	{ "use-ixfr", &cfg_type_boolean, 0 },
929	{ "use-v4-udp-ports", &cfg_type_bracketed_portlist, 0 },
930	{ "use-v6-udp-ports", &cfg_type_bracketed_portlist, 0 },
931	{ "version", &cfg_type_qstringornone, 0 },
932	{ NULL, NULL, 0 }
933};
934
935static cfg_type_t cfg_type_namelist = {
936	"namelist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
937	cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_qstring };
938
939static keyword_type_t exclude_kw = { "exclude", &cfg_type_namelist };
940
941static cfg_type_t cfg_type_optional_exclude = {
942	"optional_exclude", parse_optional_keyvalue, print_keyvalue,
943	doc_optional_keyvalue, &cfg_rep_list, &exclude_kw };
944
945static keyword_type_t exceptionnames_kw = { "except-from", &cfg_type_namelist };
946
947static cfg_type_t cfg_type_optional_exceptionnames = {
948	"optional_allow", parse_optional_keyvalue, print_keyvalue,
949	doc_optional_keyvalue, &cfg_rep_list, &exceptionnames_kw };
950
951static cfg_tuplefielddef_t denyaddresses_fields[] = {
952	{ "acl", &cfg_type_bracketed_aml, 0 },
953	{ "except-from", &cfg_type_optional_exceptionnames, 0 },
954	{ NULL, NULL, 0 }
955};
956
957static cfg_type_t cfg_type_denyaddresses = {
958	"denyaddresses", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
959	&cfg_rep_tuple, denyaddresses_fields
960};
961
962static cfg_tuplefielddef_t denyaliases_fields[] = {
963	{ "name", &cfg_type_namelist, 0 },
964	{ "except-from", &cfg_type_optional_exceptionnames, 0 },
965	{ NULL, NULL, 0 }
966};
967
968static cfg_type_t cfg_type_denyaliases = {
969	"denyaliases", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
970	&cfg_rep_tuple, denyaliases_fields
971};
972
973static cfg_type_t cfg_type_algorithmlist = {
974	"algorithmlist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
975	cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring };
976
977static cfg_tuplefielddef_t disablealgorithm_fields[] = {
978	{ "name", &cfg_type_astring, 0 },
979	{ "algorithms", &cfg_type_algorithmlist, 0 },
980	{ NULL, NULL, 0 }
981};
982
983static cfg_type_t cfg_type_disablealgorithm = {
984	"disablealgorithm", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
985	&cfg_rep_tuple, disablealgorithm_fields
986};
987
988static cfg_tuplefielddef_t mustbesecure_fields[] = {
989	{ "name", &cfg_type_astring, 0 },
990	{ "value", &cfg_type_boolean, 0 },
991	{ NULL, NULL, 0 }
992};
993
994static cfg_type_t cfg_type_mustbesecure = {
995	"mustbesecure", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
996	&cfg_rep_tuple, mustbesecure_fields
997};
998
999static const char *masterformat_enums[] = { "text", "raw", NULL };
1000static cfg_type_t cfg_type_masterformat = {
1001	"masterformat", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
1002	&cfg_rep_string, &masterformat_enums
1003};
1004
1005
1006
1007/*
1008 *  response-policy {
1009 *	zone <string> [ policy (given|disabled|passthru|
1010 *					nxdomain|nodata|cname <domain> ) ];
1011 *  };
1012 *
1013 * this is a chimera of doc_optional_keyvalue() and cfg_doc_enum()
1014 */
1015static void
1016doc_rpz_policies(cfg_printer_t *pctx, const cfg_type_t *type) {
1017	const keyword_type_t *kw;
1018	const char * const *p;
1019
1020	kw = type->of;
1021	cfg_print_chars(pctx, "[ ", 2);
1022	cfg_print_cstr(pctx, kw->name);
1023	cfg_print_chars(pctx, " ", 1);
1024
1025	cfg_print_chars(pctx, "( ", 2);
1026	for (p = kw->type->of; *p != NULL; p++) {
1027		cfg_print_cstr(pctx, *p);
1028		if (p[1] != NULL)
1029			cfg_print_chars(pctx, " | ", 3);
1030	}
1031}
1032
1033/*
1034 * print_qstring() from parser.c
1035 */
1036static void
1037print_rpz_cname(cfg_printer_t *pctx, const cfg_obj_t *obj)
1038{
1039	cfg_print_chars(pctx, "\"", 1);
1040	cfg_print_ustring(pctx, obj);
1041	cfg_print_chars(pctx, "\"", 1);
1042}
1043
1044static void
1045doc_rpz_cname(cfg_printer_t *pctx, const cfg_type_t *type) {
1046	cfg_doc_terminal(pctx, type);
1047	cfg_print_chars(pctx, " ) ]", 4);
1048}
1049
1050static isc_result_t
1051parse_rpz(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1052	isc_result_t result;
1053	cfg_obj_t *obj = NULL;
1054	const cfg_tuplefielddef_t *fields = type->of;
1055
1056	CHECK(cfg_create_tuple(pctx, type, &obj));
1057	CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
1058	CHECK(cfg_parse_obj(pctx, fields[1].type, &obj->value.tuple[1]));
1059	/*
1060	 * parse cname domain only after "policy cname"
1061	 */
1062	if (cfg_obj_isvoid(obj->value.tuple[1]) ||
1063	    strcasecmp("cname", cfg_obj_asstring(obj->value.tuple[1]))) {
1064		CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2]));
1065	} else {
1066		CHECK(cfg_parse_obj(pctx, fields[2].type, &obj->value.tuple[2]));
1067	}
1068
1069	*ret = obj;
1070	return (ISC_R_SUCCESS);
1071
1072cleanup:
1073	CLEANUP_OBJ(obj);
1074	return (result);
1075}
1076
1077static const char *rpz_policies[] = {
1078	"given", "disabled", "passthru", "no-op", "nxdomain", "nodata",
1079	"cname", NULL
1080};
1081static cfg_type_t cfg_type_rpz_policylist = {
1082	"policies", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
1083	&cfg_rep_string, &rpz_policies
1084};
1085static keyword_type_t rpz_policies_kw = {
1086	"policy", &cfg_type_rpz_policylist
1087};
1088static cfg_type_t cfg_type_rpz_policy = {
1089	"optional_policy", parse_optional_keyvalue, print_keyvalue,
1090	doc_rpz_policies, &cfg_rep_string, &rpz_policies_kw
1091};
1092static cfg_type_t cfg_type_cname = {
1093	"domain", cfg_parse_astring, print_rpz_cname, doc_rpz_cname,
1094	&cfg_rep_string, NULL
1095};
1096static cfg_tuplefielddef_t rpzone_fields[] = {
1097	{ "name", &cfg_type_astring, 0 },
1098	{ "policy", &cfg_type_rpz_policy, 0 },
1099	{ "cname", &cfg_type_cname, 0 },
1100	{ NULL, NULL, 0 }
1101};
1102static cfg_type_t cfg_type_rpzone = {
1103	"rpzone", parse_rpz, cfg_print_tuple, cfg_doc_tuple,
1104	&cfg_rep_tuple, rpzone_fields
1105};
1106static cfg_clausedef_t rpz_clauses[] = {
1107	{ "zone", &cfg_type_rpzone, CFG_CLAUSEFLAG_MULTI },
1108	{ NULL, NULL, 0 }
1109};
1110static cfg_clausedef_t *rpz_clausesets[] = {
1111	rpz_clauses,
1112	NULL
1113};
1114static cfg_type_t cfg_type_rpz = {
1115	"rpz", cfg_parse_map, cfg_print_map, cfg_doc_map,
1116	&cfg_rep_map, rpz_clausesets
1117};
1118
1119
1120
1121/*%
1122 * dnssec-lookaside
1123 */
1124
1125static void
1126print_lookaside(cfg_printer_t *pctx, const cfg_obj_t *obj)
1127{
1128	const cfg_obj_t *domain = obj->value.tuple[0];
1129
1130	if (domain->value.string.length == 4 &&
1131	    strncmp(domain->value.string.base, "auto", 4) == 0)
1132		cfg_print_cstr(pctx, "auto");
1133	else
1134		cfg_print_tuple(pctx, obj);
1135}
1136
1137static void
1138doc_lookaside(cfg_printer_t *pctx, const cfg_type_t *type) {
1139	UNUSED(type);
1140	cfg_print_cstr(pctx, "( <string> trust-anchor <string> | auto | no )");
1141}
1142
1143static keyword_type_t trustanchor_kw = { "trust-anchor", &cfg_type_astring };
1144
1145static cfg_type_t cfg_type_optional_trustanchor = {
1146	"optional_trustanchor", parse_optional_keyvalue, print_keyvalue,
1147	doc_keyvalue, &cfg_rep_string, &trustanchor_kw
1148};
1149
1150static cfg_tuplefielddef_t lookaside_fields[] = {
1151	{ "domain", &cfg_type_astring, 0 },
1152	{ "trust-anchor", &cfg_type_optional_trustanchor, 0 },
1153	{ NULL, NULL, 0 }
1154};
1155
1156static cfg_type_t cfg_type_lookaside = {
1157	"lookaside", cfg_parse_tuple, print_lookaside, doc_lookaside,
1158	&cfg_rep_tuple, lookaside_fields
1159};
1160
1161/*
1162 * DNS64.
1163 */
1164static cfg_clausedef_t
1165dns64_clauses[] = {
1166	{ "clients", &cfg_type_bracketed_aml, 0 },
1167	{ "mapped", &cfg_type_bracketed_aml, 0 },
1168	{ "exclude", &cfg_type_bracketed_aml, 0 },
1169	{ "suffix", &cfg_type_netaddr6, 0 },
1170	{ "recursive-only", &cfg_type_boolean, 0 },
1171	{ "break-dnssec", &cfg_type_boolean, 0 },
1172	{ NULL, NULL, 0 },
1173};
1174
1175static cfg_clausedef_t *
1176dns64_clausesets[] = {
1177	dns64_clauses,
1178	NULL
1179};
1180
1181static cfg_type_t cfg_type_dns64 = {
1182	"dns64", cfg_parse_netprefix_map, cfg_print_map, cfg_doc_map,
1183	&cfg_rep_map, dns64_clausesets
1184};
1185
1186/*%
1187 * Clauses that can be found within the 'view' statement,
1188 * with defaults in the 'options' statement.
1189 */
1190
1191static cfg_clausedef_t
1192view_clauses[] = {
1193	{ "acache-cleaning-interval", &cfg_type_uint32, 0 },
1194	{ "acache-enable", &cfg_type_boolean, 0 },
1195	{ "additional-from-auth", &cfg_type_boolean, 0 },
1196	{ "additional-from-cache", &cfg_type_boolean, 0 },
1197	{ "allow-new-zones", &cfg_type_boolean, 0 },
1198	{ "allow-query-cache", &cfg_type_bracketed_aml, 0 },
1199	{ "allow-query-cache-on", &cfg_type_bracketed_aml, 0 },
1200	{ "allow-recursion", &cfg_type_bracketed_aml, 0 },
1201	{ "allow-recursion-on", &cfg_type_bracketed_aml, 0 },
1202	{ "allow-v6-synthesis", &cfg_type_bracketed_aml,
1203	  CFG_CLAUSEFLAG_OBSOLETE },
1204	{ "attach-cache", &cfg_type_astring, 0 },
1205	{ "auth-nxdomain", &cfg_type_boolean, CFG_CLAUSEFLAG_NEWDEFAULT },
1206	{ "cache-file", &cfg_type_qstring, 0 },
1207	{ "check-names", &cfg_type_checknames, CFG_CLAUSEFLAG_MULTI },
1208	{ "cleaning-interval", &cfg_type_uint32, 0 },
1209	{ "clients-per-query", &cfg_type_uint32, 0 },
1210	{ "deny-answer-addresses", &cfg_type_denyaddresses, 0 },
1211	{ "deny-answer-aliases", &cfg_type_denyaliases, 0 },
1212	{ "disable-algorithms", &cfg_type_disablealgorithm,
1213	  CFG_CLAUSEFLAG_MULTI },
1214	{ "disable-empty-zone", &cfg_type_astring, CFG_CLAUSEFLAG_MULTI },
1215	{ "dns64", &cfg_type_dns64, CFG_CLAUSEFLAG_MULTI },
1216	{ "dns64-server", &cfg_type_astring, 0 },
1217	{ "dns64-contact", &cfg_type_astring, 0 },
1218	{ "dnssec-accept-expired", &cfg_type_boolean, 0 },
1219	{ "dnssec-enable", &cfg_type_boolean, 0 },
1220	{ "dnssec-lookaside", &cfg_type_lookaside, CFG_CLAUSEFLAG_MULTI },
1221	{ "dnssec-must-be-secure",  &cfg_type_mustbesecure,
1222	  CFG_CLAUSEFLAG_MULTI },
1223	{ "dnssec-validation", &cfg_type_boolorauto, 0 },
1224	{ "dual-stack-servers", &cfg_type_nameportiplist, 0 },
1225	{ "edns-udp-size", &cfg_type_uint32, 0 },
1226	{ "empty-contact", &cfg_type_astring, 0 },
1227	{ "empty-server", &cfg_type_astring, 0 },
1228	{ "empty-zones-enable", &cfg_type_boolean, 0 },
1229	{ "fetch-glue", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1230	{ "ixfr-from-differences", &cfg_type_ixfrdifftype, 0 },
1231	{ "lame-ttl", &cfg_type_uint32, 0 },
1232	{ "max-acache-size", &cfg_type_sizenodefault, 0 },
1233	{ "max-cache-size", &cfg_type_sizenodefault, 0 },
1234	{ "max-cache-ttl", &cfg_type_uint32, 0 },
1235	{ "max-clients-per-query", &cfg_type_uint32, 0 },
1236	{ "max-ncache-ttl", &cfg_type_uint32, 0 },
1237	{ "max-udp-size", &cfg_type_uint32, 0 },
1238	{ "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
1239	{ "minimal-responses", &cfg_type_boolean, 0 },
1240	{ "preferred-glue", &cfg_type_astring, 0 },
1241	{ "provide-ixfr", &cfg_type_boolean, 0 },
1242	/*
1243	 * Note that the query-source option syntax is different
1244	 * from the other -source options.
1245	 */
1246	{ "query-source", &cfg_type_querysource4, 0 },
1247	{ "query-source-v6", &cfg_type_querysource6, 0 },
1248	{ "queryport-pool-ports", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
1249	{ "queryport-pool-updateinterval", &cfg_type_uint32,
1250	  CFG_CLAUSEFLAG_OBSOLETE },
1251	{ "recursion", &cfg_type_boolean, 0 },
1252	{ "request-ixfr", &cfg_type_boolean, 0 },
1253	{ "request-nsid", &cfg_type_boolean, 0 },
1254	{ "resolver-query-timeout", &cfg_type_uint32, 0 },
1255	{ "rfc2308-type1", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
1256	{ "root-delegation-only",  &cfg_type_optional_exclude, 0 },
1257	{ "rrset-order", &cfg_type_rrsetorder, 0 },
1258	{ "sortlist", &cfg_type_bracketed_aml, 0 },
1259	{ "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
1260	{ "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_NOTIMP },
1261	{ "transfer-format", &cfg_type_transferformat, 0 },
1262	{ "use-queryport-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1263	{ "zero-no-soa-ttl-cache", &cfg_type_boolean, 0 },
1264#ifdef ALLOW_FILTER_AAAA_ON_V4
1265	{ "filter-aaaa", &cfg_type_bracketed_aml, 0 },
1266	{ "filter-aaaa-on-v4", &cfg_type_v4_aaaa, 0 },
1267#else
1268	{ "filter-aaaa", &cfg_type_bracketed_aml,
1269	   CFG_CLAUSEFLAG_NOTCONFIGURED },
1270	{ "filter-aaaa-on-v4", &cfg_type_v4_aaaa,
1271	   CFG_CLAUSEFLAG_NOTCONFIGURED },
1272#endif
1273	{ "response-policy", &cfg_type_rpz, 0 },
1274	{ NULL, NULL, 0 }
1275};
1276
1277/*%
1278 * Clauses that can be found within the 'view' statement only.
1279 */
1280static cfg_clausedef_t
1281view_only_clauses[] = {
1282	{ "match-clients", &cfg_type_bracketed_aml, 0 },
1283	{ "match-destinations", &cfg_type_bracketed_aml, 0 },
1284	{ "match-recursive-only", &cfg_type_boolean, 0 },
1285	{ NULL, NULL, 0 }
1286};
1287
1288/*%
1289 * Sig-validity-interval.
1290 */
1291static isc_result_t
1292parse_optional_uint32(cfg_parser_t *pctx, const cfg_type_t *type,
1293		      cfg_obj_t **ret)
1294{
1295	isc_result_t result;
1296	UNUSED(type);
1297
1298	CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
1299	if (pctx->token.type == isc_tokentype_number) {
1300		CHECK(cfg_parse_obj(pctx, &cfg_type_uint32, ret));
1301	} else {
1302		CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
1303	}
1304 cleanup:
1305	return (result);
1306}
1307
1308static void
1309doc_optional_uint32(cfg_printer_t *pctx, const cfg_type_t *type) {
1310	UNUSED(type);
1311	cfg_print_cstr(pctx, "[ <integer> ]");
1312}
1313
1314static cfg_type_t cfg_type_optional_uint32 = {
1315	"optional_uint32", parse_optional_uint32, NULL, doc_optional_uint32,
1316	NULL, NULL };
1317
1318static cfg_tuplefielddef_t validityinterval_fields[] = {
1319	{ "validity", &cfg_type_uint32, 0 },
1320	{ "re-sign", &cfg_type_optional_uint32, 0 },
1321	{ NULL, NULL, 0 }
1322};
1323
1324static cfg_type_t cfg_type_validityinterval = {
1325	"validityinterval", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
1326	&cfg_rep_tuple, validityinterval_fields
1327};
1328
1329/*%
1330 * Clauses that can be found in a 'zone' statement,
1331 * with defaults in the 'view' or 'options' statement.
1332 */
1333static cfg_clausedef_t
1334zone_clauses[] = {
1335	{ "allow-notify", &cfg_type_bracketed_aml, 0 },
1336	{ "allow-query", &cfg_type_bracketed_aml, 0 },
1337	{ "allow-query-on", &cfg_type_bracketed_aml, 0 },
1338	{ "allow-transfer", &cfg_type_bracketed_aml, 0 },
1339	{ "allow-update", &cfg_type_bracketed_aml, 0 },
1340	{ "allow-update-forwarding", &cfg_type_bracketed_aml, 0 },
1341	{ "also-notify", &cfg_type_portiplist, 0 },
1342	{ "alt-transfer-source", &cfg_type_sockaddr4wild, 0 },
1343	{ "alt-transfer-source-v6", &cfg_type_sockaddr6wild, 0 },
1344	{ "auto-dnssec", &cfg_type_autodnssec, 0 },
1345	{ "check-dup-records", &cfg_type_checkmode, 0 },
1346	{ "check-integrity", &cfg_type_boolean, 0 },
1347	{ "check-mx", &cfg_type_checkmode, 0 },
1348	{ "check-mx-cname", &cfg_type_checkmode, 0 },
1349	{ "check-sibling", &cfg_type_boolean, 0 },
1350	{ "check-srv-cname", &cfg_type_checkmode, 0 },
1351	{ "check-wildcard", &cfg_type_boolean, 0 },
1352	{ "dialup", &cfg_type_dialuptype, 0 },
1353	{ "dnssec-dnskey-kskonly", &cfg_type_boolean, 0 },
1354	{ "dnssec-secure-to-insecure", &cfg_type_boolean, 0 },
1355	{ "forward", &cfg_type_forwardtype, 0 },
1356	{ "forwarders", &cfg_type_portiplist, 0 },
1357	{ "key-directory", &cfg_type_qstring, 0 },
1358	{ "maintain-ixfr-base", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1359	{ "masterfile-format", &cfg_type_masterformat, 0 },
1360	{ "max-ixfr-log-size", &cfg_type_size, CFG_CLAUSEFLAG_OBSOLETE },
1361	{ "max-journal-size", &cfg_type_sizenodefault, 0 },
1362	{ "max-refresh-time", &cfg_type_uint32, 0 },
1363	{ "max-retry-time", &cfg_type_uint32, 0 },
1364	{ "max-transfer-idle-in", &cfg_type_uint32, 0 },
1365	{ "max-transfer-idle-out", &cfg_type_uint32, 0 },
1366	{ "max-transfer-time-in", &cfg_type_uint32, 0 },
1367	{ "max-transfer-time-out", &cfg_type_uint32, 0 },
1368	{ "min-refresh-time", &cfg_type_uint32, 0 },
1369	{ "min-retry-time", &cfg_type_uint32, 0 },
1370	{ "multi-master", &cfg_type_boolean, 0 },
1371	{ "notify", &cfg_type_notifytype, 0 },
1372	{ "notify-delay", &cfg_type_uint32, 0 },
1373	{ "notify-source", &cfg_type_sockaddr4wild, 0 },
1374	{ "notify-source-v6", &cfg_type_sockaddr6wild, 0 },
1375	{ "notify-to-soa", &cfg_type_boolean, 0 },
1376	{ "nsec3-test-zone", &cfg_type_boolean, CFG_CLAUSEFLAG_TESTONLY },
1377	{ "sig-signing-nodes", &cfg_type_uint32, 0 },
1378	{ "sig-signing-signatures", &cfg_type_uint32, 0 },
1379	{ "sig-signing-type", &cfg_type_uint32, 0 },
1380	{ "sig-validity-interval", &cfg_type_validityinterval, 0 },
1381	{ "transfer-source", &cfg_type_sockaddr4wild, 0 },
1382	{ "transfer-source-v6", &cfg_type_sockaddr6wild, 0 },
1383	{ "try-tcp-refresh", &cfg_type_boolean, 0 },
1384	{ "update-check-ksk", &cfg_type_boolean, 0 },
1385	{ "use-alt-transfer-source", &cfg_type_boolean, 0 },
1386	{ "zero-no-soa-ttl", &cfg_type_boolean, 0 },
1387	{ "zone-statistics", &cfg_type_boolean, 0 },
1388	{ NULL, NULL, 0 }
1389};
1390
1391/*%
1392 * Clauses that can be found in a 'zone' statement
1393 * only.
1394 */
1395static cfg_clausedef_t
1396zone_only_clauses[] = {
1397	{ "type", &cfg_type_zonetype, 0 },
1398	{ "file", &cfg_type_qstring, 0 },
1399	{ "journal", &cfg_type_qstring, 0 },
1400	{ "ixfr-base", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
1401	{ "ixfr-tmp-file", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
1402	{ "masters", &cfg_type_namesockaddrkeylist, 0 },
1403	{ "pubkey", &cfg_type_pubkey,
1404	  CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_OBSOLETE },
1405	{ "update-policy", &cfg_type_updatepolicy, 0 },
1406	{ "database", &cfg_type_astring, 0 },
1407	{ "delegation-only", &cfg_type_boolean, 0 },
1408	/*
1409	 * Note that the format of the check-names option is different between
1410	 * the zone options and the global/view options.  Ugh.
1411	 */
1412	{ "check-names", &cfg_type_checkmode, 0 },
1413	{ "ixfr-from-differences", &cfg_type_boolean, 0 },
1414	{ "server-addresses", &cfg_type_bracketed_sockaddrlist, 0 },
1415	{ "server-names", &cfg_type_namelist, 0 },
1416	{ NULL, NULL, 0 }
1417};
1418
1419
1420/*% The top-level named.conf syntax. */
1421
1422static cfg_clausedef_t *
1423namedconf_clausesets[] = {
1424	namedconf_clauses,
1425	namedconf_or_view_clauses,
1426	NULL
1427};
1428LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_namedconf = {
1429	"namedconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
1430	&cfg_rep_map, namedconf_clausesets
1431};
1432
1433/*% The bind.keys syntax (trusted-keys/managed-keys only). */
1434static cfg_clausedef_t *
1435bindkeys_clausesets[] = {
1436	bindkeys_clauses,
1437	NULL
1438};
1439LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_bindkeys = {
1440	"bindkeys", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
1441	&cfg_rep_map, bindkeys_clausesets
1442};
1443
1444/*% The new-zone-file syntax (for zones added by 'rndc addzone') */
1445static cfg_clausedef_t
1446newzones_clauses[] = {
1447	{ "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI },
1448	{ NULL, NULL, 0 }
1449};
1450
1451static cfg_clausedef_t *
1452newzones_clausesets[] = {
1453	newzones_clauses,
1454	NULL
1455};
1456
1457LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_newzones = {
1458	"newzones", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
1459	&cfg_rep_map, newzones_clausesets
1460};
1461
1462/*% The "options" statement syntax. */
1463
1464static cfg_clausedef_t *
1465options_clausesets[] = {
1466	options_clauses,
1467	view_clauses,
1468	zone_clauses,
1469	NULL
1470};
1471cfg_type_t cfg_type_options = {
1472	"options", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, options_clausesets };
1473
1474/*% The "view" statement syntax. */
1475
1476static cfg_clausedef_t *
1477view_clausesets[] = {
1478	view_only_clauses,
1479	namedconf_or_view_clauses,
1480	view_clauses,
1481	zone_clauses,
1482	dynamically_loadable_zones_clauses,
1483	NULL
1484};
1485cfg_type_t cfg_type_viewopts = {
1486	"view", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, view_clausesets };
1487
1488/*% The "zone" statement syntax. */
1489
1490static cfg_clausedef_t *
1491zone_clausesets[] = {
1492	zone_only_clauses,
1493	zone_clauses,
1494	NULL
1495};
1496cfg_type_t cfg_type_zoneopts = {
1497	"zoneopts", cfg_parse_map, cfg_print_map,
1498	cfg_doc_map, &cfg_rep_map, zone_clausesets };
1499
1500/*% The "dynamically loadable zones" statement syntax. */
1501
1502static cfg_clausedef_t *
1503dynamically_loadable_zones_clausesets[] = {
1504	dynamically_loadable_zones_clauses,
1505	NULL
1506};
1507static cfg_type_t cfg_type_dynamically_loadable_zones_opts = {
1508	"dynamically_loadable_zones_opts", cfg_parse_map,
1509	cfg_print_map, cfg_doc_map, &cfg_rep_map,
1510	dynamically_loadable_zones_clausesets
1511};
1512
1513/*%
1514 * Clauses that can be found within the 'key' statement.
1515 */
1516static cfg_clausedef_t
1517key_clauses[] = {
1518	{ "algorithm", &cfg_type_astring, 0 },
1519	{ "secret", &cfg_type_astring, 0 },
1520	{ NULL, NULL, 0 }
1521};
1522
1523static cfg_clausedef_t *
1524key_clausesets[] = {
1525	key_clauses,
1526	NULL
1527};
1528static cfg_type_t cfg_type_key = {
1529	"key", cfg_parse_named_map, cfg_print_map,
1530	cfg_doc_map, &cfg_rep_map, key_clausesets
1531};
1532
1533
1534/*%
1535 * Clauses that can be found in a 'server' statement.
1536 */
1537static cfg_clausedef_t
1538server_clauses[] = {
1539	{ "bogus", &cfg_type_boolean, 0 },
1540	{ "provide-ixfr", &cfg_type_boolean, 0 },
1541	{ "request-ixfr", &cfg_type_boolean, 0 },
1542	{ "support-ixfr", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1543	{ "transfers", &cfg_type_uint32, 0 },
1544	{ "transfer-format", &cfg_type_transferformat, 0 },
1545	{ "keys", &cfg_type_server_key_kludge, 0 },
1546	{ "edns", &cfg_type_boolean, 0 },
1547	{ "edns-udp-size", &cfg_type_uint32, 0 },
1548	{ "max-udp-size", &cfg_type_uint32, 0 },
1549	{ "notify-source", &cfg_type_sockaddr4wild, 0 },
1550	{ "notify-source-v6", &cfg_type_sockaddr6wild, 0 },
1551	{ "query-source", &cfg_type_querysource4, 0 },
1552	{ "query-source-v6", &cfg_type_querysource6, 0 },
1553	{ "transfer-source", &cfg_type_sockaddr4wild, 0 },
1554	{ "transfer-source-v6", &cfg_type_sockaddr6wild, 0 },
1555	{ NULL, NULL, 0 }
1556};
1557static cfg_clausedef_t *
1558server_clausesets[] = {
1559	server_clauses,
1560	NULL
1561};
1562static cfg_type_t cfg_type_server = {
1563	"server", cfg_parse_netprefix_map, cfg_print_map, cfg_doc_map, &cfg_rep_map,
1564	server_clausesets
1565};
1566
1567
1568/*%
1569 * Clauses that can be found in a 'channel' clause in the
1570 * 'logging' statement.
1571 *
1572 * These have some additional constraints that need to be
1573 * checked after parsing:
1574 *  - There must exactly one of file/syslog/null/stderr
1575 *
1576 */
1577static cfg_clausedef_t
1578channel_clauses[] = {
1579	/* Destinations.  We no longer require these to be first. */
1580	{ "file", &cfg_type_logfile, 0 },
1581	{ "syslog", &cfg_type_optional_facility, 0 },
1582	{ "null", &cfg_type_void, 0 },
1583	{ "stderr", &cfg_type_void, 0 },
1584	/* Options.  We now accept these for the null channel, too. */
1585	{ "severity", &cfg_type_logseverity, 0 },
1586	{ "print-time", &cfg_type_boolean, 0 },
1587	{ "print-severity", &cfg_type_boolean, 0 },
1588	{ "print-category", &cfg_type_boolean, 0 },
1589	{ NULL, NULL, 0 }
1590};
1591static cfg_clausedef_t *
1592channel_clausesets[] = {
1593	channel_clauses,
1594	NULL
1595};
1596cfg_type_t cfg_type_channel = {
1597	"channel", cfg_parse_named_map, cfg_print_map, cfg_doc_map,
1598	&cfg_rep_map, channel_clausesets
1599};
1600
1601/*% A list of log destination, used in the "category" clause. */
1602cfg_type_t cfg_type_destinationlist = {
1603	"destinationlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list,
1604	&cfg_rep_list, &cfg_type_astring };
1605
1606/*%
1607 * Clauses that can be found in a 'logging' statement.
1608 */
1609static cfg_clausedef_t
1610logging_clauses[] = {
1611	{ "channel", &cfg_type_channel, CFG_CLAUSEFLAG_MULTI },
1612	{ "category", &cfg_type_category, CFG_CLAUSEFLAG_MULTI },
1613	{ NULL, NULL, 0 }
1614};
1615static cfg_clausedef_t *
1616logging_clausesets[] = {
1617	logging_clauses,
1618	NULL
1619};
1620cfg_type_t cfg_type_logging = {
1621	"logging", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, logging_clausesets };
1622
1623
1624/*%
1625 * For parsing an 'addzone' statement
1626 */
1627
1628static cfg_tuplefielddef_t addzone_fields[] = {
1629	{ "name", &cfg_type_astring, 0 },
1630	{ "class", &cfg_type_optional_class, 0 },
1631	{ "view", &cfg_type_optional_class, 0 },
1632	{ "options", &cfg_type_zoneopts, 0 },
1633	{ NULL, NULL, 0 }
1634};
1635static cfg_type_t cfg_type_addzone = {
1636	"addzone", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, addzone_fields };
1637
1638static cfg_clausedef_t
1639addzoneconf_clauses[] = {
1640	{ "addzone", &cfg_type_addzone, 0 },
1641	{ NULL, NULL, 0 }
1642};
1643
1644static cfg_clausedef_t *
1645addzoneconf_clausesets[] = {
1646	addzoneconf_clauses,
1647	NULL
1648};
1649
1650LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_addzoneconf = {
1651	"addzoneconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
1652	&cfg_rep_map, addzoneconf_clausesets
1653};
1654
1655
1656static isc_result_t
1657parse_unitstring(char *str, isc_resourcevalue_t *valuep) {
1658	char *endp;
1659	unsigned int len;
1660	isc_uint64_t value;
1661	isc_uint64_t unit;
1662
1663	value = isc_string_touint64(str, &endp, 10);
1664	if (*endp == 0) {
1665		*valuep = value;
1666		return (ISC_R_SUCCESS);
1667	}
1668
1669	len = strlen(str);
1670	if (len < 2 || endp[1] != '\0')
1671		return (ISC_R_FAILURE);
1672
1673	switch (str[len - 1]) {
1674	case 'k':
1675	case 'K':
1676		unit = 1024;
1677		break;
1678	case 'm':
1679	case 'M':
1680		unit = 1024 * 1024;
1681		break;
1682	case 'g':
1683	case 'G':
1684		unit = 1024 * 1024 * 1024;
1685		break;
1686	default:
1687		return (ISC_R_FAILURE);
1688	}
1689	if (value > ISC_UINT64_MAX / unit)
1690		return (ISC_R_FAILURE);
1691	*valuep = value * unit;
1692	return (ISC_R_SUCCESS);
1693}
1694
1695static isc_result_t
1696parse_sizeval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1697	isc_result_t result;
1698	cfg_obj_t *obj = NULL;
1699	isc_uint64_t val;
1700
1701	UNUSED(type);
1702
1703	CHECK(cfg_gettoken(pctx, 0));
1704	if (pctx->token.type != isc_tokentype_string) {
1705		result = ISC_R_UNEXPECTEDTOKEN;
1706		goto cleanup;
1707	}
1708	CHECK(parse_unitstring(TOKEN_STRING(pctx), &val));
1709
1710	CHECK(cfg_create_obj(pctx, &cfg_type_uint64, &obj));
1711	obj->value.uint64 = val;
1712	*ret = obj;
1713	return (ISC_R_SUCCESS);
1714
1715 cleanup:
1716	cfg_parser_error(pctx, CFG_LOG_NEAR, "expected integer and optional unit");
1717	return (result);
1718}
1719
1720/*%
1721 * A size value (number + optional unit).
1722 */
1723static cfg_type_t cfg_type_sizeval = {
1724	"sizeval", parse_sizeval, cfg_print_uint64, cfg_doc_terminal,
1725	&cfg_rep_uint64, NULL };
1726
1727/*%
1728 * A size, "unlimited", or "default".
1729 */
1730
1731static isc_result_t
1732parse_size(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1733	return (parse_enum_or_other(pctx, type, &cfg_type_sizeval, ret));
1734}
1735
1736static const char *size_enums[] = { "unlimited", "default", NULL };
1737static cfg_type_t cfg_type_size = {
1738	"size", parse_size, cfg_print_ustring, cfg_doc_terminal,
1739	&cfg_rep_string, size_enums
1740};
1741
1742/*%
1743 * A size or "unlimited", but not "default".
1744 */
1745static const char *sizenodefault_enums[] = { "unlimited", NULL };
1746static cfg_type_t cfg_type_sizenodefault = {
1747	"size_no_default", parse_size, cfg_print_ustring, cfg_doc_terminal,
1748	&cfg_rep_string, sizenodefault_enums
1749};
1750
1751/*%
1752 * optional_keyvalue
1753 */
1754static isc_result_t
1755parse_maybe_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
1756			      isc_boolean_t optional, cfg_obj_t **ret)
1757{
1758	isc_result_t result;
1759	cfg_obj_t *obj = NULL;
1760	const keyword_type_t *kw = type->of;
1761
1762	CHECK(cfg_peektoken(pctx, 0));
1763	if (pctx->token.type == isc_tokentype_string &&
1764	    strcasecmp(TOKEN_STRING(pctx), kw->name) == 0) {
1765		CHECK(cfg_gettoken(pctx, 0));
1766		CHECK(kw->type->parse(pctx, kw->type, &obj));
1767		obj->type = type; /* XXX kludge */
1768	} else {
1769		if (optional) {
1770			CHECK(cfg_parse_void(pctx, NULL, &obj));
1771		} else {
1772			cfg_parser_error(pctx, CFG_LOG_NEAR, "expected '%s'",
1773				     kw->name);
1774			result = ISC_R_UNEXPECTEDTOKEN;
1775			goto cleanup;
1776		}
1777	}
1778	*ret = obj;
1779 cleanup:
1780	return (result);
1781}
1782
1783static isc_result_t
1784parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype,
1785		    const cfg_type_t *othertype, cfg_obj_t **ret)
1786{
1787	isc_result_t result;
1788	CHECK(cfg_peektoken(pctx, 0));
1789	if (pctx->token.type == isc_tokentype_string &&
1790	    cfg_is_enum(TOKEN_STRING(pctx), enumtype->of)) {
1791		CHECK(cfg_parse_enum(pctx, enumtype, ret));
1792	} else {
1793		CHECK(cfg_parse_obj(pctx, othertype, ret));
1794	}
1795 cleanup:
1796	return (result);
1797}
1798
1799static void
1800doc_enum_or_other(cfg_printer_t *pctx, const cfg_type_t *type) {
1801	cfg_doc_terminal(pctx, type);
1802#if 0 /* XXX */
1803	cfg_print_chars(pctx, "( ", 2);...
1804#endif
1805
1806}
1807
1808static isc_result_t
1809parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1810	return (parse_maybe_optional_keyvalue(pctx, type, ISC_FALSE, ret));
1811}
1812
1813static isc_result_t
1814parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1815	return (parse_maybe_optional_keyvalue(pctx, type, ISC_TRUE, ret));
1816}
1817
1818static void
1819print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1820	const keyword_type_t *kw = obj->type->of;
1821	cfg_print_cstr(pctx, kw->name);
1822	cfg_print_chars(pctx, " ", 1);
1823	kw->type->print(pctx, obj);
1824}
1825
1826static void
1827doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) {
1828	const keyword_type_t *kw = type->of;
1829	cfg_print_cstr(pctx, kw->name);
1830	cfg_print_chars(pctx, " ", 1);
1831	cfg_doc_obj(pctx, kw->type);
1832}
1833
1834static void
1835doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) {
1836	const keyword_type_t *kw = type->of;
1837	cfg_print_chars(pctx, "[ ", 2);
1838	cfg_print_cstr(pctx, kw->name);
1839	cfg_print_chars(pctx, " ", 1);
1840	cfg_doc_obj(pctx, kw->type);
1841	cfg_print_chars(pctx, " ]", 2);
1842}
1843
1844static const char *dialup_enums[] = {
1845	"notify", "notify-passive", "refresh", "passive", NULL };
1846static isc_result_t
1847parse_dialup_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1848	return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
1849}
1850static cfg_type_t cfg_type_dialuptype = {
1851	"dialuptype", parse_dialup_type, cfg_print_ustring, doc_enum_or_other,
1852	&cfg_rep_string, dialup_enums
1853};
1854
1855static const char *notify_enums[] = { "explicit", "master-only", NULL };
1856static isc_result_t
1857parse_notify_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1858	return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
1859}
1860static cfg_type_t cfg_type_notifytype = {
1861	"notifytype", parse_notify_type, cfg_print_ustring, doc_enum_or_other,
1862	&cfg_rep_string, notify_enums,
1863};
1864
1865static const char *ixfrdiff_enums[] = { "master", "slave", NULL };
1866static isc_result_t
1867parse_ixfrdiff_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1868	return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
1869}
1870static cfg_type_t cfg_type_ixfrdifftype = {
1871	"ixfrdiff", parse_ixfrdiff_type, cfg_print_ustring, doc_enum_or_other,
1872	&cfg_rep_string, ixfrdiff_enums,
1873};
1874
1875static const char *v4_aaaa_enums[] = { "break-dnssec", NULL };
1876static isc_result_t
1877parse_v4_aaaa(cfg_parser_t *pctx, const cfg_type_t *type,
1878		     cfg_obj_t **ret) {
1879	return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
1880}
1881static cfg_type_t cfg_type_v4_aaaa = {
1882	"v4_aaaa", parse_v4_aaaa, cfg_print_ustring,
1883	doc_enum_or_other, &cfg_rep_string, v4_aaaa_enums,
1884};
1885
1886static keyword_type_t key_kw = { "key", &cfg_type_astring };
1887
1888LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_keyref = {
1889	"keyref", parse_keyvalue, print_keyvalue, doc_keyvalue,
1890	&cfg_rep_string, &key_kw
1891};
1892
1893cfg_type_t cfg_type_optional_keyref = {
1894	"optional_keyref", parse_optional_keyvalue, print_keyvalue,
1895	doc_optional_keyvalue, &cfg_rep_string, &key_kw
1896};
1897
1898/*%
1899 * A "controls" statement is represented as a map with the multivalued
1900 * "inet" and "unix" clauses.
1901 */
1902
1903static keyword_type_t controls_allow_kw = {
1904	"allow", &cfg_type_bracketed_aml };
1905
1906static cfg_type_t cfg_type_controls_allow = {
1907	"controls_allow", parse_keyvalue,
1908	print_keyvalue, doc_keyvalue,
1909	&cfg_rep_list, &controls_allow_kw
1910};
1911
1912static keyword_type_t controls_keys_kw = {
1913	"keys", &cfg_type_keylist };
1914
1915static cfg_type_t cfg_type_controls_keys = {
1916	"controls_keys", parse_optional_keyvalue,
1917	print_keyvalue, doc_optional_keyvalue,
1918	&cfg_rep_list, &controls_keys_kw
1919};
1920
1921static cfg_tuplefielddef_t inetcontrol_fields[] = {
1922	{ "address", &cfg_type_controls_sockaddr, 0 },
1923	{ "allow", &cfg_type_controls_allow, 0 },
1924	{ "keys", &cfg_type_controls_keys, 0 },
1925	{ NULL, NULL, 0 }
1926};
1927
1928static cfg_type_t cfg_type_inetcontrol = {
1929	"inetcontrol", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
1930	inetcontrol_fields
1931};
1932
1933static keyword_type_t controls_perm_kw = {
1934	"perm", &cfg_type_uint32 };
1935
1936static cfg_type_t cfg_type_controls_perm = {
1937	"controls_perm", parse_keyvalue,
1938	print_keyvalue, doc_keyvalue,
1939	&cfg_rep_uint32, &controls_perm_kw
1940};
1941
1942static keyword_type_t controls_owner_kw = {
1943	"owner", &cfg_type_uint32 };
1944
1945static cfg_type_t cfg_type_controls_owner = {
1946	"controls_owner", parse_keyvalue,
1947	print_keyvalue, doc_keyvalue,
1948	&cfg_rep_uint32, &controls_owner_kw
1949};
1950
1951static keyword_type_t controls_group_kw = {
1952	"group", &cfg_type_uint32 };
1953
1954static cfg_type_t cfg_type_controls_group = {
1955	"controls_allow", parse_keyvalue,
1956	print_keyvalue, doc_keyvalue,
1957	&cfg_rep_uint32, &controls_group_kw
1958};
1959
1960static cfg_tuplefielddef_t unixcontrol_fields[] = {
1961	{ "path", &cfg_type_qstring, 0 },
1962	{ "perm", &cfg_type_controls_perm, 0 },
1963	{ "owner", &cfg_type_controls_owner, 0 },
1964	{ "group", &cfg_type_controls_group, 0 },
1965	{ "keys", &cfg_type_controls_keys, 0 },
1966	{ NULL, NULL, 0 }
1967};
1968
1969static cfg_type_t cfg_type_unixcontrol = {
1970	"unixcontrol", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
1971	unixcontrol_fields
1972};
1973
1974static cfg_clausedef_t
1975controls_clauses[] = {
1976	{ "inet", &cfg_type_inetcontrol, CFG_CLAUSEFLAG_MULTI },
1977	{ "unix", &cfg_type_unixcontrol, CFG_CLAUSEFLAG_MULTI },
1978	{ NULL, NULL, 0 }
1979};
1980
1981static cfg_clausedef_t *
1982controls_clausesets[] = {
1983	controls_clauses,
1984	NULL
1985};
1986static cfg_type_t cfg_type_controls = {
1987	"controls", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map,	&controls_clausesets
1988};
1989
1990/*%
1991 * A "statistics-channels" statement is represented as a map with the
1992 * multivalued "inet" clauses.
1993 */
1994static void
1995doc_optional_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
1996	const keyword_type_t *kw = type->of;
1997	cfg_print_chars(pctx, "[ ", 2);
1998	cfg_print_cstr(pctx, kw->name);
1999	cfg_print_chars(pctx, " ", 1);
2000	cfg_doc_obj(pctx, kw->type);
2001	cfg_print_chars(pctx, " ]", 2);
2002}
2003
2004static cfg_type_t cfg_type_optional_allow = {
2005	"optional_allow", parse_optional_keyvalue, print_keyvalue,
2006	doc_optional_bracketed_list, &cfg_rep_list, &controls_allow_kw
2007};
2008
2009static cfg_tuplefielddef_t statserver_fields[] = {
2010	{ "address", &cfg_type_controls_sockaddr, 0 }, /* reuse controls def */
2011	{ "allow", &cfg_type_optional_allow, 0 },
2012	{ NULL, NULL, 0 }
2013};
2014
2015static cfg_type_t cfg_type_statschannel = {
2016	"statschannel", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
2017	&cfg_rep_tuple, statserver_fields
2018};
2019
2020static cfg_clausedef_t
2021statservers_clauses[] = {
2022	{ "inet", &cfg_type_statschannel, CFG_CLAUSEFLAG_MULTI },
2023	{ NULL, NULL, 0 }
2024};
2025
2026static cfg_clausedef_t *
2027statservers_clausesets[] = {
2028	statservers_clauses,
2029	NULL
2030};
2031
2032static cfg_type_t cfg_type_statschannels = {
2033	"statistics-channels", cfg_parse_map, cfg_print_map, cfg_doc_map,
2034	&cfg_rep_map,	&statservers_clausesets
2035};
2036
2037/*%
2038 * An optional class, as used in view and zone statements.
2039 */
2040static isc_result_t
2041parse_optional_class(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2042	isc_result_t result;
2043	UNUSED(type);
2044	CHECK(cfg_peektoken(pctx, 0));
2045	if (pctx->token.type == isc_tokentype_string)
2046		CHECK(cfg_parse_obj(pctx, &cfg_type_ustring, ret));
2047	else
2048		CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
2049 cleanup:
2050	return (result);
2051}
2052
2053static cfg_type_t cfg_type_optional_class = {
2054	"optional_class", parse_optional_class, NULL, cfg_doc_terminal,
2055	NULL, NULL
2056};
2057
2058static isc_result_t
2059parse_querysource(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2060	isc_result_t result;
2061	cfg_obj_t *obj = NULL;
2062	isc_netaddr_t netaddr;
2063	in_port_t port;
2064	unsigned int have_address = 0;
2065	unsigned int have_port = 0;
2066	const unsigned int *flagp = type->of;
2067
2068	if ((*flagp & CFG_ADDR_V4OK) != 0)
2069		isc_netaddr_any(&netaddr);
2070	else if ((*flagp & CFG_ADDR_V6OK) != 0)
2071		isc_netaddr_any6(&netaddr);
2072	else
2073		INSIST(0);
2074
2075	port = 0;
2076
2077	for (;;) {
2078		CHECK(cfg_peektoken(pctx, 0));
2079		if (pctx->token.type == isc_tokentype_string) {
2080			if (strcasecmp(TOKEN_STRING(pctx),
2081				       "address") == 0)
2082			{
2083				/* read "address" */
2084				CHECK(cfg_gettoken(pctx, 0));
2085				CHECK(cfg_parse_rawaddr(pctx, *flagp,
2086							&netaddr));
2087				have_address++;
2088			} else if (strcasecmp(TOKEN_STRING(pctx), "port") == 0)
2089			{
2090				/* read "port" */
2091				CHECK(cfg_gettoken(pctx, 0));
2092				CHECK(cfg_parse_rawport(pctx,
2093							CFG_ADDR_WILDOK,
2094							&port));
2095				have_port++;
2096			} else if (have_port == 0 && have_address == 0) {
2097				return (cfg_parse_sockaddr(pctx, type, ret));
2098			} else {
2099				cfg_parser_error(pctx, CFG_LOG_NEAR,
2100					     "expected 'address' or 'port'");
2101				return (ISC_R_UNEXPECTEDTOKEN);
2102			}
2103		} else
2104			break;
2105	}
2106	if (have_address > 1 || have_port > 1 ||
2107	    have_address + have_port == 0) {
2108		cfg_parser_error(pctx, 0, "expected one address and/or port");
2109		return (ISC_R_UNEXPECTEDTOKEN);
2110	}
2111
2112	CHECK(cfg_create_obj(pctx, &cfg_type_querysource, &obj));
2113	isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
2114	*ret = obj;
2115	return (ISC_R_SUCCESS);
2116
2117 cleanup:
2118	cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid query source");
2119	CLEANUP_OBJ(obj);
2120	return (result);
2121}
2122
2123static void
2124print_querysource(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2125	isc_netaddr_t na;
2126	isc_netaddr_fromsockaddr(&na, &obj->value.sockaddr);
2127	cfg_print_cstr(pctx, "address ");
2128	cfg_print_rawaddr(pctx, &na);
2129	cfg_print_cstr(pctx, " port ");
2130	cfg_print_rawuint(pctx, isc_sockaddr_getport(&obj->value.sockaddr));
2131}
2132
2133static unsigned int sockaddr4wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V4OK;
2134static unsigned int sockaddr6wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V6OK;
2135
2136static cfg_type_t cfg_type_querysource4 = {
2137	"querysource4", parse_querysource, NULL, cfg_doc_terminal,
2138	NULL, &sockaddr4wild_flags
2139};
2140
2141static cfg_type_t cfg_type_querysource6 = {
2142	"querysource6", parse_querysource, NULL, cfg_doc_terminal,
2143	NULL, &sockaddr6wild_flags
2144};
2145
2146static cfg_type_t cfg_type_querysource = {
2147	"querysource", NULL, print_querysource, NULL, &cfg_rep_sockaddr, NULL
2148};
2149
2150/*% addrmatchelt */
2151
2152static isc_result_t
2153parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2154	isc_result_t result;
2155	UNUSED(type);
2156
2157	CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
2158
2159	if (pctx->token.type == isc_tokentype_string ||
2160	    pctx->token.type == isc_tokentype_qstring) {
2161		if (pctx->token.type == isc_tokentype_string &&
2162		    (strcasecmp(TOKEN_STRING(pctx), "key") == 0)) {
2163			CHECK(cfg_parse_obj(pctx, &cfg_type_keyref, ret));
2164		} else {
2165			if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK |
2166						  CFG_ADDR_V4PREFIXOK |
2167						  CFG_ADDR_V6OK))
2168			{
2169				CHECK(cfg_parse_netprefix(pctx, NULL, ret));
2170			} else {
2171				CHECK(cfg_parse_astring(pctx, NULL, ret));
2172			}
2173		}
2174	} else if (pctx->token.type == isc_tokentype_special) {
2175		if (pctx->token.value.as_char == '{') {
2176			/* Nested match list. */
2177			CHECK(cfg_parse_obj(pctx, &cfg_type_bracketed_aml, ret));
2178		} else if (pctx->token.value.as_char == '!') {
2179			CHECK(cfg_gettoken(pctx, 0)); /* read "!" */
2180			CHECK(cfg_parse_obj(pctx, &cfg_type_negated, ret));
2181		} else {
2182			goto bad;
2183		}
2184	} else {
2185	bad:
2186		cfg_parser_error(pctx, CFG_LOG_NEAR,
2187			     "expected IP match list element");
2188		return (ISC_R_UNEXPECTEDTOKEN);
2189	}
2190 cleanup:
2191	return (result);
2192}
2193
2194/*%
2195 * A negated address match list element (like "! 10.0.0.1").
2196 * Somewhat sneakily, the caller is expected to parse the
2197 * "!", but not to print it.
2198 */
2199
2200static cfg_tuplefielddef_t negated_fields[] = {
2201	{ "value", &cfg_type_addrmatchelt, 0 },
2202	{ NULL, NULL, 0 }
2203};
2204
2205static void
2206print_negated(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2207	cfg_print_chars(pctx, "!", 1);
2208	cfg_print_tuple(pctx, obj);
2209}
2210
2211static cfg_type_t cfg_type_negated = {
2212	"negated", cfg_parse_tuple, print_negated, NULL, &cfg_rep_tuple,
2213	&negated_fields
2214};
2215
2216/*% An address match list element */
2217
2218static cfg_type_t cfg_type_addrmatchelt = {
2219	"address_match_element", parse_addrmatchelt, NULL, cfg_doc_terminal,
2220	NULL, NULL
2221};
2222
2223/*% A bracketed address match list */
2224
2225cfg_type_t cfg_type_bracketed_aml = {
2226	"bracketed_aml", cfg_parse_bracketed_list, cfg_print_bracketed_list,
2227	cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_addrmatchelt
2228};
2229
2230/*%
2231 * The socket address syntax in the "controls" statement is silly.
2232 * It allows both socket address families, but also allows "*",
2233 * whis is gratuitously interpreted as the IPv4 wildcard address.
2234 */
2235static unsigned int controls_sockaddr_flags =
2236	CFG_ADDR_V4OK | CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
2237static cfg_type_t cfg_type_controls_sockaddr = {
2238	"controls_sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr,
2239	cfg_doc_sockaddr, &cfg_rep_sockaddr, &controls_sockaddr_flags
2240};
2241
2242/*%
2243 * Handle the special kludge syntax of the "keys" clause in the "server"
2244 * statement, which takes a single key with or without braces and semicolon.
2245 */
2246static isc_result_t
2247parse_server_key_kludge(cfg_parser_t *pctx, const cfg_type_t *type,
2248			cfg_obj_t **ret)
2249{
2250	isc_result_t result;
2251	isc_boolean_t braces = ISC_FALSE;
2252	UNUSED(type);
2253
2254	/* Allow opening brace. */
2255	CHECK(cfg_peektoken(pctx, 0));
2256	if (pctx->token.type == isc_tokentype_special &&
2257	    pctx->token.value.as_char == '{') {
2258		CHECK(cfg_gettoken(pctx, 0));
2259		braces = ISC_TRUE;
2260	}
2261
2262	CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret));
2263
2264	if (braces) {
2265		/* Skip semicolon if present. */
2266		CHECK(cfg_peektoken(pctx, 0));
2267		if (pctx->token.type == isc_tokentype_special &&
2268		    pctx->token.value.as_char == ';')
2269			CHECK(cfg_gettoken(pctx, 0));
2270
2271		CHECK(cfg_parse_special(pctx, '}'));
2272	}
2273 cleanup:
2274	return (result);
2275}
2276static cfg_type_t cfg_type_server_key_kludge = {
2277	"server_key", parse_server_key_kludge, NULL, cfg_doc_terminal,
2278	NULL, NULL
2279};
2280
2281
2282/*%
2283 * An optional logging facility.
2284 */
2285
2286static isc_result_t
2287parse_optional_facility(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
2288{
2289	isc_result_t result;
2290	UNUSED(type);
2291
2292	CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
2293	if (pctx->token.type == isc_tokentype_string ||
2294	    pctx->token.type == isc_tokentype_qstring) {
2295		CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret));
2296	} else {
2297		CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
2298	}
2299 cleanup:
2300	return (result);
2301}
2302
2303static cfg_type_t cfg_type_optional_facility = {
2304	"optional_facility", parse_optional_facility, NULL, cfg_doc_terminal,
2305	NULL, NULL };
2306
2307
2308/*%
2309 * A log severity.  Return as a string, except "debug N",
2310 * which is returned as a keyword object.
2311 */
2312
2313static keyword_type_t debug_kw = { "debug", &cfg_type_uint32 };
2314cfg_type_t cfg_type_debuglevel = {
2315	"debuglevel", parse_keyvalue,
2316	print_keyvalue, doc_keyvalue,
2317	&cfg_rep_uint32, &debug_kw
2318};
2319
2320static isc_result_t
2321parse_logseverity(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2322	isc_result_t result;
2323	UNUSED(type);
2324
2325	CHECK(cfg_peektoken(pctx, 0));
2326	if (pctx->token.type == isc_tokentype_string &&
2327	    strcasecmp(TOKEN_STRING(pctx), "debug") == 0) {
2328		CHECK(cfg_gettoken(pctx, 0)); /* read "debug" */
2329		CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER));
2330		if (pctx->token.type == isc_tokentype_number) {
2331			CHECK(cfg_parse_uint32(pctx, NULL, ret));
2332		} else {
2333			/*
2334			 * The debug level is optional and defaults to 1.
2335			 * This makes little sense, but we support it for
2336			 * compatibility with BIND 8.
2337			 */
2338			CHECK(cfg_create_obj(pctx, &cfg_type_uint32, ret));
2339			(*ret)->value.uint32 = 1;
2340		}
2341		(*ret)->type = &cfg_type_debuglevel; /* XXX kludge */
2342	} else {
2343		CHECK(cfg_parse_obj(pctx, &cfg_type_loglevel, ret));
2344	}
2345 cleanup:
2346	return (result);
2347}
2348
2349static cfg_type_t cfg_type_logseverity = {
2350	"log_severity", parse_logseverity, NULL, cfg_doc_terminal,
2351	NULL, NULL };
2352
2353/*%
2354 * The "file" clause of the "channel" statement.
2355 * This is yet another special case.
2356 */
2357
2358static const char *logversions_enums[] = { "unlimited", NULL };
2359static isc_result_t
2360parse_logversions(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2361	return (parse_enum_or_other(pctx, type, &cfg_type_uint32, ret));
2362}
2363
2364static cfg_type_t cfg_type_logversions = {
2365	"logversions", parse_logversions, cfg_print_ustring, cfg_doc_terminal,
2366	&cfg_rep_string, logversions_enums
2367};
2368
2369static cfg_tuplefielddef_t logfile_fields[] = {
2370	{ "file", &cfg_type_qstring, 0 },
2371	{ "versions", &cfg_type_logversions, 0 },
2372	{ "size", &cfg_type_size, 0 },
2373	{ NULL, NULL, 0 }
2374};
2375
2376static isc_result_t
2377parse_logfile(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2378	isc_result_t result;
2379	cfg_obj_t *obj = NULL;
2380	const cfg_tuplefielddef_t *fields = type->of;
2381
2382	CHECK(cfg_create_tuple(pctx, type, &obj));
2383
2384	/* Parse the mandatory "file" field */
2385	CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
2386
2387	/* Parse "versions" and "size" fields in any order. */
2388	for (;;) {
2389		CHECK(cfg_peektoken(pctx, 0));
2390		if (pctx->token.type == isc_tokentype_string) {
2391			CHECK(cfg_gettoken(pctx, 0));
2392			if (strcasecmp(TOKEN_STRING(pctx),
2393				       "versions") == 0 &&
2394			    obj->value.tuple[1] == NULL) {
2395				CHECK(cfg_parse_obj(pctx, fields[1].type,
2396					    &obj->value.tuple[1]));
2397			} else if (strcasecmp(TOKEN_STRING(pctx),
2398					      "size") == 0 &&
2399				   obj->value.tuple[2] == NULL) {
2400				CHECK(cfg_parse_obj(pctx, fields[2].type,
2401					    &obj->value.tuple[2]));
2402			} else {
2403				break;
2404			}
2405		} else {
2406			break;
2407		}
2408	}
2409
2410	/* Create void objects for missing optional values. */
2411	if (obj->value.tuple[1] == NULL)
2412		CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1]));
2413	if (obj->value.tuple[2] == NULL)
2414		CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2]));
2415
2416	*ret = obj;
2417	return (ISC_R_SUCCESS);
2418
2419 cleanup:
2420	CLEANUP_OBJ(obj);
2421	return (result);
2422}
2423
2424static void
2425print_logfile(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2426	cfg_print_obj(pctx, obj->value.tuple[0]); /* file */
2427	if (obj->value.tuple[1]->type->print != cfg_print_void) {
2428		cfg_print_cstr(pctx, " versions ");
2429		cfg_print_obj(pctx, obj->value.tuple[1]);
2430	}
2431	if (obj->value.tuple[2]->type->print != cfg_print_void) {
2432		cfg_print_cstr(pctx, " size ");
2433		cfg_print_obj(pctx, obj->value.tuple[2]);
2434	}
2435}
2436
2437
2438static void
2439doc_logfile(cfg_printer_t *pctx, const cfg_type_t *type) {
2440	UNUSED(type);
2441	cfg_print_cstr(pctx, "<quoted_string>");
2442	cfg_print_chars(pctx, " ", 1);
2443	cfg_print_cstr(pctx, "[ versions ( \"unlimited\" | <integer> ) ]");
2444	cfg_print_chars(pctx, " ", 1);
2445	cfg_print_cstr(pctx, "[ size <size> ]");
2446}
2447
2448cfg_type_t cfg_type_logfile = {
2449	"log_file", parse_logfile, print_logfile, doc_logfile,
2450	&cfg_rep_tuple, logfile_fields
2451};
2452
2453/*% An IPv4 address with optional port, "*" accepted as wildcard. */
2454static cfg_type_t cfg_type_sockaddr4wild = {
2455	"sockaddr4wild", cfg_parse_sockaddr, cfg_print_sockaddr,
2456	cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr4wild_flags
2457};
2458
2459/*% An IPv6 address with optional port, "*" accepted as wildcard. */
2460static cfg_type_t cfg_type_sockaddr6wild = {
2461	"v6addrportwild", cfg_parse_sockaddr, cfg_print_sockaddr,
2462	cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr6wild_flags
2463};
2464
2465/*%
2466 * lwres
2467 */
2468
2469static cfg_tuplefielddef_t lwres_view_fields[] = {
2470	{ "name", &cfg_type_astring, 0 },
2471	{ "class", &cfg_type_optional_class, 0 },
2472	{ NULL, NULL, 0 }
2473};
2474static cfg_type_t cfg_type_lwres_view = {
2475	"lwres_view", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
2476	lwres_view_fields
2477};
2478
2479static cfg_type_t cfg_type_lwres_searchlist = {
2480	"lwres_searchlist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
2481	cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring };
2482
2483static cfg_clausedef_t
2484lwres_clauses[] = {
2485	{ "listen-on", &cfg_type_portiplist, 0 },
2486	{ "view", &cfg_type_lwres_view, 0 },
2487	{ "search", &cfg_type_lwres_searchlist, 0 },
2488	{ "ndots", &cfg_type_uint32, 0 },
2489	{ NULL, NULL, 0 }
2490};
2491
2492static cfg_clausedef_t *
2493lwres_clausesets[] = {
2494	lwres_clauses,
2495	NULL
2496};
2497static cfg_type_t cfg_type_lwres = {
2498	"lwres", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map,
2499	lwres_clausesets
2500};
2501
2502/*%
2503 * rndc
2504 */
2505
2506static cfg_clausedef_t
2507rndcconf_options_clauses[] = {
2508	{ "default-key", &cfg_type_astring, 0 },
2509	{ "default-port", &cfg_type_uint32, 0 },
2510	{ "default-server", &cfg_type_astring, 0 },
2511	{ "default-source-address", &cfg_type_netaddr4wild, 0 },
2512	{ "default-source-address-v6", &cfg_type_netaddr6wild, 0 },
2513	{ NULL, NULL, 0 }
2514};
2515
2516static cfg_clausedef_t *
2517rndcconf_options_clausesets[] = {
2518	rndcconf_options_clauses,
2519	NULL
2520};
2521
2522static cfg_type_t cfg_type_rndcconf_options = {
2523	"rndcconf_options", cfg_parse_map, cfg_print_map, cfg_doc_map,
2524	&cfg_rep_map, rndcconf_options_clausesets
2525};
2526
2527static cfg_clausedef_t
2528rndcconf_server_clauses[] = {
2529	{ "key", &cfg_type_astring, 0 },
2530	{ "port", &cfg_type_uint32, 0 },
2531	{ "source-address", &cfg_type_netaddr4wild, 0 },
2532	{ "source-address-v6", &cfg_type_netaddr6wild, 0 },
2533	{ "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 },
2534	{ NULL, NULL, 0 }
2535};
2536
2537static cfg_clausedef_t *
2538rndcconf_server_clausesets[] = {
2539	rndcconf_server_clauses,
2540	NULL
2541};
2542
2543static cfg_type_t cfg_type_rndcconf_server = {
2544	"rndcconf_server", cfg_parse_named_map, cfg_print_map, cfg_doc_map,
2545	&cfg_rep_map, rndcconf_server_clausesets
2546};
2547
2548static cfg_clausedef_t
2549rndcconf_clauses[] = {
2550	{ "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
2551	{ "server", &cfg_type_rndcconf_server, CFG_CLAUSEFLAG_MULTI },
2552	{ "options", &cfg_type_rndcconf_options, 0 },
2553	{ NULL, NULL, 0 }
2554};
2555
2556static cfg_clausedef_t *
2557rndcconf_clausesets[] = {
2558	rndcconf_clauses,
2559	NULL
2560};
2561
2562LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndcconf = {
2563	"rndcconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
2564	&cfg_rep_map, rndcconf_clausesets
2565};
2566
2567static cfg_clausedef_t
2568rndckey_clauses[] = {
2569	{ "key", &cfg_type_key, 0 },
2570	{ NULL, NULL, 0 }
2571};
2572
2573static cfg_clausedef_t *
2574rndckey_clausesets[] = {
2575	rndckey_clauses,
2576	NULL
2577};
2578
2579LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndckey = {
2580	"rndckey", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
2581	&cfg_rep_map, rndckey_clausesets
2582};
2583
2584/*
2585 * session.key has exactly the same syntax as rndc.key, but it's defined
2586 * separately for clarity (and so we can extend it someday, if needed).
2587 */
2588LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_sessionkey = {
2589	"sessionkey", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
2590	&cfg_rep_map, rndckey_clausesets
2591};
2592
2593static cfg_tuplefielddef_t nameport_fields[] = {
2594	{ "name", &cfg_type_astring, 0 },
2595	{ "port", &cfg_type_optional_port, 0 },
2596	{ NULL, NULL, 0 }
2597};
2598static cfg_type_t cfg_type_nameport = {
2599	"nameport", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
2600	&cfg_rep_tuple, nameport_fields
2601};
2602
2603static void
2604doc_sockaddrnameport(cfg_printer_t *pctx, const cfg_type_t *type) {
2605	UNUSED(type);
2606	cfg_print_chars(pctx, "( ", 2);
2607	cfg_print_cstr(pctx, "<quoted_string>");
2608	cfg_print_chars(pctx, " ", 1);
2609	cfg_print_cstr(pctx, "[ port <integer> ]");
2610	cfg_print_chars(pctx, " | ", 3);
2611	cfg_print_cstr(pctx, "<ipv4_address>");
2612	cfg_print_chars(pctx, " ", 1);
2613	cfg_print_cstr(pctx, "[ port <integer> ]");
2614	cfg_print_chars(pctx, " | ", 3);
2615	cfg_print_cstr(pctx, "<ipv6_address>");
2616	cfg_print_chars(pctx, " ", 1);
2617	cfg_print_cstr(pctx, "[ port <integer> ]");
2618	cfg_print_chars(pctx, " )", 2);
2619}
2620
2621static isc_result_t
2622parse_sockaddrnameport(cfg_parser_t *pctx, const cfg_type_t *type,
2623		       cfg_obj_t **ret)
2624{
2625	isc_result_t result;
2626	cfg_obj_t *obj = NULL;
2627	UNUSED(type);
2628
2629	CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
2630	if (pctx->token.type == isc_tokentype_string ||
2631	    pctx->token.type == isc_tokentype_qstring) {
2632		if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK))
2633			CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr, ret));
2634		else {
2635			const cfg_tuplefielddef_t *fields =
2636						   cfg_type_nameport.of;
2637			CHECK(cfg_create_tuple(pctx, &cfg_type_nameport,
2638					       &obj));
2639			CHECK(cfg_parse_obj(pctx, fields[0].type,
2640					    &obj->value.tuple[0]));
2641			CHECK(cfg_parse_obj(pctx, fields[1].type,
2642					    &obj->value.tuple[1]));
2643			*ret = obj;
2644			obj = NULL;
2645		}
2646	} else {
2647		cfg_parser_error(pctx, CFG_LOG_NEAR,
2648			     "expected IP address or hostname");
2649		return (ISC_R_UNEXPECTEDTOKEN);
2650	}
2651 cleanup:
2652	CLEANUP_OBJ(obj);
2653	return (result);
2654}
2655
2656static cfg_type_t cfg_type_sockaddrnameport = {
2657	"sockaddrnameport_element", parse_sockaddrnameport, NULL,
2658	 doc_sockaddrnameport, NULL, NULL
2659};
2660
2661static cfg_type_t cfg_type_bracketed_sockaddrnameportlist = {
2662	"bracketed_sockaddrnameportlist", cfg_parse_bracketed_list,
2663	cfg_print_bracketed_list, cfg_doc_bracketed_list,
2664	&cfg_rep_list, &cfg_type_sockaddrnameport
2665};
2666
2667/*%
2668 * A list of socket addresses or name with an optional default port,
2669 * as used in the dual-stack-servers option.  E.g.,
2670 * "port 1234 { dual-stack-servers.net; 10.0.0.1; 1::2 port 69; }"
2671 */
2672static cfg_tuplefielddef_t nameportiplist_fields[] = {
2673	{ "port", &cfg_type_optional_port, 0 },
2674	{ "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 },
2675	{ NULL, NULL, 0 }
2676};
2677
2678static cfg_type_t cfg_type_nameportiplist = {
2679	"nameportiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
2680	&cfg_rep_tuple, nameportiplist_fields
2681};
2682
2683/*%
2684 * masters element.
2685 */
2686
2687static void
2688doc_masterselement(cfg_printer_t *pctx, const cfg_type_t *type) {
2689	UNUSED(type);
2690	cfg_print_chars(pctx, "( ", 2);
2691	cfg_print_cstr(pctx, "<masters>");
2692	cfg_print_chars(pctx, " | ", 3);
2693	cfg_print_cstr(pctx, "<ipv4_address>");
2694	cfg_print_chars(pctx, " ", 1);
2695	cfg_print_cstr(pctx, "[ port <integer> ]");
2696	cfg_print_chars(pctx, " | ", 3);
2697	cfg_print_cstr(pctx, "<ipv6_address>");
2698	cfg_print_chars(pctx, " ", 1);
2699	cfg_print_cstr(pctx, "[ port <integer> ]");
2700	cfg_print_chars(pctx, " )", 2);
2701}
2702
2703static isc_result_t
2704parse_masterselement(cfg_parser_t *pctx, const cfg_type_t *type,
2705		     cfg_obj_t **ret)
2706{
2707	isc_result_t result;
2708	cfg_obj_t *obj = NULL;
2709	UNUSED(type);
2710
2711	CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
2712	if (pctx->token.type == isc_tokentype_string ||
2713	    pctx->token.type == isc_tokentype_qstring) {
2714		if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK))
2715			CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr, ret));
2716		else
2717			CHECK(cfg_parse_astring(pctx, &cfg_type_astring, ret));
2718	} else {
2719		cfg_parser_error(pctx, CFG_LOG_NEAR,
2720			     "expected IP address or masters name");
2721		return (ISC_R_UNEXPECTEDTOKEN);
2722	}
2723 cleanup:
2724	CLEANUP_OBJ(obj);
2725	return (result);
2726}
2727
2728static cfg_type_t cfg_type_masterselement = {
2729	"masters_element", parse_masterselement, NULL,
2730	 doc_masterselement, NULL, NULL
2731};
2732