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