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