Home | History | Annotate | Line # | Download | only in isccfg
      1 /*	$NetBSD: parser.c,v 1.17 2026/01/29 18:37:55 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0 AND BSD-2-Clause
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 /*
     17  * Copyright (c) 2009-2018 NLNet Labs.
     18  * All rights reserved.
     19  *
     20  * Redistribution and use in source and binary forms, with or without
     21  * modification, are permitted provided that the following conditions
     22  * are met:
     23  * 1. Redistributions of source code must retain the above copyright
     24  *    notice, this list of conditions and the following disclaimer.
     25  * 2. Redistributions in binary form must reproduce the above copyright
     26  *    notice, this list of conditions and the following disclaimer in the
     27  *    documentation and/or other materials provided with the distribution.
     28  *
     29  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     30  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     31  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     32  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     33  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     35  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     36  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
     37  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     38  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
     39  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     40  */
     41 
     42 /*! \file */
     43 
     44 #include <ctype.h>
     45 #include <errno.h>
     46 #include <glob.h>
     47 #include <inttypes.h>
     48 #include <stdbool.h>
     49 #include <stdlib.h>
     50 #include <string.h>
     51 
     52 #include <isc/buffer.h>
     53 #include <isc/dir.h>
     54 #include <isc/errno.h>
     55 #include <isc/formatcheck.h>
     56 #include <isc/lex.h>
     57 #include <isc/log.h>
     58 #include <isc/mem.h>
     59 #include <isc/net.h>
     60 #include <isc/netaddr.h>
     61 #include <isc/netmgr.h>
     62 #include <isc/netscope.h>
     63 #include <isc/sockaddr.h>
     64 #include <isc/string.h>
     65 #include <isc/symtab.h>
     66 #include <isc/util.h>
     67 
     68 #include <dns/ttl.h>
     69 
     70 #include <isccfg/cfg.h>
     71 #include <isccfg/grammar.h>
     72 #include <isccfg/log.h>
     73 
     74 /* Shorthand */
     75 #define CAT CFG_LOGCATEGORY_CONFIG
     76 #define MOD CFG_LOGMODULE_PARSER
     77 
     78 #define MAP_SYM 1 /* Unique type for isc_symtab */
     79 
     80 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
     81 
     82 /* Clean up a configuration object if non-NULL. */
     83 #define CLEANUP_OBJ(obj)                               \
     84 	do {                                           \
     85 		if ((obj) != NULL)                     \
     86 			cfg_obj_destroy(pctx, &(obj)); \
     87 	} while (0)
     88 
     89 /*
     90  * Forward declarations of static functions.
     91  */
     92 
     93 static void
     94 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
     95 
     96 static isc_result_t
     97 parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
     98 
     99 static void
    100 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
    101 
    102 static void
    103 free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
    104 
    105 static isc_result_t
    106 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
    107 
    108 static isc_result_t
    109 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
    110 	      cfg_obj_t **ret);
    111 
    112 static void
    113 free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
    114 
    115 static void
    116 copy_string(cfg_parser_t *pctx, const cfg_obj_t *obj, isc_textregion_t *dst);
    117 
    118 static void
    119 free_sockaddrtls(cfg_parser_t *pctx, cfg_obj_t *obj);
    120 
    121 static isc_result_t
    122 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
    123 
    124 static void
    125 free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
    126 
    127 static isc_result_t
    128 parse_symtab_elt(cfg_parser_t *pctx, const char *name, cfg_type_t *elttype,
    129 		 isc_symtab_t *symtab, bool callback);
    130 
    131 static void
    132 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
    133 
    134 static isc_result_t
    135 cfg_getstringtoken(cfg_parser_t *pctx);
    136 
    137 static void
    138 parser_complain(cfg_parser_t *pctx, bool is_warning, unsigned int flags,
    139 		const char *format, va_list args);
    140 
    141 #if defined(HAVE_GEOIP2)
    142 static isc_result_t
    143 parse_geoip(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
    144 
    145 static void
    146 print_geoip(cfg_printer_t *pctx, const cfg_obj_t *obj);
    147 
    148 static void
    149 doc_geoip(cfg_printer_t *pctx, const cfg_type_t *type);
    150 #endif /* HAVE_GEOIP2 */
    151 
    152 /*
    153  * Data representations.  These correspond to members of the
    154  * "value" union in struct cfg_obj (except "void", which does
    155  * not need a union member).
    156  */
    157 
    158 cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
    159 cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
    160 cfg_rep_t cfg_rep_string = { "string", free_string };
    161 cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
    162 cfg_rep_t cfg_rep_map = { "map", free_map };
    163 cfg_rep_t cfg_rep_list = { "list", free_list };
    164 cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
    165 cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
    166 cfg_rep_t cfg_rep_sockaddrtls = { "sockaddrtls", free_sockaddrtls };
    167 cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
    168 cfg_rep_t cfg_rep_void = { "void", free_noop };
    169 cfg_rep_t cfg_rep_fixedpoint = { "fixedpoint", free_noop };
    170 cfg_rep_t cfg_rep_percentage = { "percentage", free_noop };
    171 cfg_rep_t cfg_rep_duration = { "duration", free_noop };
    172 
    173 /*
    174  * Configuration type definitions.
    175  */
    176 
    177 /*%
    178  * An implicit list.  These are formed by clauses that occur multiple times.
    179  */
    180 static cfg_type_t cfg_type_implicitlist = { "implicitlist", NULL,
    181 					    print_list,	    NULL,
    182 					    &cfg_rep_list,  NULL };
    183 
    184 /* Functions. */
    185 
    186 void
    187 cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
    188 	REQUIRE(pctx != NULL);
    189 	REQUIRE(obj != NULL);
    190 
    191 	obj->type->print(pctx, obj);
    192 }
    193 
    194 void
    195 cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
    196 	REQUIRE(pctx != NULL);
    197 	REQUIRE(text != NULL);
    198 
    199 	pctx->f(pctx->closure, text, len);
    200 }
    201 
    202 static void
    203 print_open(cfg_printer_t *pctx) {
    204 	if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) {
    205 		cfg_print_cstr(pctx, "{ ");
    206 	} else {
    207 		cfg_print_cstr(pctx, "{\n");
    208 		pctx->indent++;
    209 	}
    210 }
    211 
    212 void
    213 cfg_print_indent(cfg_printer_t *pctx) {
    214 	int indent = pctx->indent;
    215 	if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) {
    216 		cfg_print_cstr(pctx, " ");
    217 		return;
    218 	}
    219 	while (indent > 0) {
    220 		cfg_print_cstr(pctx, "\t");
    221 		indent--;
    222 	}
    223 }
    224 
    225 static void
    226 print_close(cfg_printer_t *pctx) {
    227 	if ((pctx->flags & CFG_PRINTER_ONELINE) == 0) {
    228 		pctx->indent--;
    229 		cfg_print_indent(pctx);
    230 	}
    231 	cfg_print_cstr(pctx, "}");
    232 }
    233 
    234 isc_result_t
    235 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
    236 	isc_result_t result;
    237 
    238 	REQUIRE(pctx != NULL);
    239 	REQUIRE(type != NULL);
    240 	REQUIRE(ret != NULL && *ret == NULL);
    241 
    242 	result = type->parse(pctx, type, ret);
    243 	if (result != ISC_R_SUCCESS) {
    244 		return result;
    245 	}
    246 	ENSURE(*ret != NULL);
    247 	return ISC_R_SUCCESS;
    248 }
    249 
    250 void
    251 cfg_print(const cfg_obj_t *obj,
    252 	  void (*f)(void *closure, const char *text, int textlen),
    253 	  void *closure) {
    254 	REQUIRE(obj != NULL);
    255 	REQUIRE(f != NULL);
    256 
    257 	cfg_printx(obj, 0, f, closure);
    258 }
    259 
    260 void
    261 cfg_printx(const cfg_obj_t *obj, unsigned int flags,
    262 	   void (*f)(void *closure, const char *text, int textlen),
    263 	   void *closure) {
    264 	cfg_printer_t pctx;
    265 
    266 	REQUIRE(obj != NULL);
    267 	REQUIRE(f != NULL);
    268 
    269 	pctx.f = f;
    270 	pctx.closure = closure;
    271 	pctx.indent = 0;
    272 	pctx.flags = flags;
    273 	obj->type->print(&pctx, obj);
    274 }
    275 
    276 /* Tuples. */
    277 
    278 isc_result_t
    279 cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
    280 	isc_result_t result;
    281 	const cfg_tuplefielddef_t *fields;
    282 	const cfg_tuplefielddef_t *f;
    283 	cfg_obj_t *obj = NULL;
    284 	unsigned int nfields = 0;
    285 	int i;
    286 
    287 	REQUIRE(pctx != NULL);
    288 	REQUIRE(type != NULL);
    289 	REQUIRE(ret != NULL && *ret == NULL);
    290 
    291 	fields = type->of;
    292 
    293 	for (f = fields; f->name != NULL; f++) {
    294 		nfields++;
    295 	}
    296 
    297 	CHECK(cfg_create_obj(pctx, type, &obj));
    298 	obj->value.tuple = isc_mem_cget(pctx->mctx, nfields,
    299 					sizeof(cfg_obj_t *));
    300 	for (f = fields, i = 0; f->name != NULL; f++, i++) {
    301 		obj->value.tuple[i] = NULL;
    302 	}
    303 	*ret = obj;
    304 	return ISC_R_SUCCESS;
    305 
    306 cleanup:
    307 	if (obj != NULL) {
    308 		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
    309 	}
    310 	return result;
    311 }
    312 
    313 isc_result_t
    314 cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
    315 	isc_result_t result;
    316 	const cfg_tuplefielddef_t *fields;
    317 	const cfg_tuplefielddef_t *f;
    318 	cfg_obj_t *obj = NULL;
    319 	unsigned int i;
    320 
    321 	REQUIRE(pctx != NULL);
    322 	REQUIRE(type != NULL);
    323 	REQUIRE(ret != NULL && *ret == NULL);
    324 
    325 	fields = type->of;
    326 
    327 	CHECK(cfg_create_tuple(pctx, type, &obj));
    328 	for (f = fields, i = 0; f->name != NULL; f++, i++) {
    329 		CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
    330 	}
    331 
    332 	*ret = obj;
    333 	return ISC_R_SUCCESS;
    334 
    335 cleanup:
    336 	CLEANUP_OBJ(obj);
    337 	return result;
    338 }
    339 
    340 void
    341 cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
    342 	unsigned int i;
    343 	const cfg_tuplefielddef_t *fields;
    344 	const cfg_tuplefielddef_t *f;
    345 	bool need_space = false;
    346 
    347 	REQUIRE(pctx != NULL);
    348 	REQUIRE(obj != NULL);
    349 
    350 	fields = obj->type->of;
    351 
    352 	for (f = fields, i = 0; f->name != NULL; f++, i++) {
    353 		const cfg_obj_t *fieldobj = obj->value.tuple[i];
    354 		if (need_space && fieldobj->type->rep != &cfg_rep_void) {
    355 			cfg_print_cstr(pctx, " ");
    356 		}
    357 		cfg_print_obj(pctx, fieldobj);
    358 		need_space = (need_space ||
    359 			      fieldobj->type->print != cfg_print_void);
    360 	}
    361 }
    362 
    363 void
    364 cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
    365 	const cfg_tuplefielddef_t *fields;
    366 	const cfg_tuplefielddef_t *f;
    367 	bool need_space = false;
    368 
    369 	REQUIRE(pctx != NULL);
    370 	REQUIRE(type != NULL);
    371 
    372 	fields = type->of;
    373 
    374 	for (f = fields; f->name != NULL; f++) {
    375 		if (need_space) {
    376 			cfg_print_cstr(pctx, " ");
    377 		}
    378 		cfg_doc_obj(pctx, f->type);
    379 		need_space = (f->type->print != cfg_print_void);
    380 	}
    381 }
    382 
    383 static void
    384 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
    385 	unsigned int i;
    386 	const cfg_tuplefielddef_t *fields = obj->type->of;
    387 	const cfg_tuplefielddef_t *f;
    388 	unsigned int nfields = 0;
    389 
    390 	if (obj->value.tuple == NULL) {
    391 		return;
    392 	}
    393 
    394 	for (f = fields, i = 0; f->name != NULL; f++, i++) {
    395 		CLEANUP_OBJ(obj->value.tuple[i]);
    396 		nfields++;
    397 	}
    398 	isc_mem_cput(pctx->mctx, obj->value.tuple, nfields,
    399 		     sizeof(cfg_obj_t *));
    400 }
    401 
    402 bool
    403 cfg_obj_istuple(const cfg_obj_t *obj) {
    404 	REQUIRE(obj != NULL);
    405 	return obj->type->rep == &cfg_rep_tuple;
    406 }
    407 
    408 const cfg_obj_t *
    409 cfg_tuple_get(const cfg_obj_t *tupleobj, const char *name) {
    410 	unsigned int i;
    411 	const cfg_tuplefielddef_t *fields;
    412 	const cfg_tuplefielddef_t *f;
    413 
    414 	REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
    415 	REQUIRE(name != NULL);
    416 
    417 	fields = tupleobj->type->of;
    418 	for (f = fields, i = 0; f->name != NULL; f++, i++) {
    419 		if (strcmp(f->name, name) == 0) {
    420 			return tupleobj->value.tuple[i];
    421 		}
    422 	}
    423 	UNREACHABLE();
    424 }
    425 
    426 isc_result_t
    427 cfg_parse_special(cfg_parser_t *pctx, int special) {
    428 	isc_result_t result;
    429 
    430 	REQUIRE(pctx != NULL);
    431 
    432 	CHECK(cfg_gettoken(pctx, 0));
    433 	if (pctx->token.type == isc_tokentype_special &&
    434 	    pctx->token.value.as_char == special)
    435 	{
    436 		return ISC_R_SUCCESS;
    437 	}
    438 
    439 	cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
    440 	return ISC_R_UNEXPECTEDTOKEN;
    441 cleanup:
    442 	return result;
    443 }
    444 
    445 /*
    446  * Parse a required semicolon.  If it is not there, log
    447  * an error and increment the error count but continue
    448  * parsing.  Since the next token is pushed back,
    449  * care must be taken to make sure it is eventually
    450  * consumed or an infinite loop may result.
    451  */
    452 static isc_result_t
    453 parse_semicolon(cfg_parser_t *pctx) {
    454 	isc_result_t result;
    455 
    456 	CHECK(cfg_gettoken(pctx, 0));
    457 	if (pctx->token.type == isc_tokentype_special &&
    458 	    pctx->token.value.as_char == ';')
    459 	{
    460 		return ISC_R_SUCCESS;
    461 	}
    462 
    463 	cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
    464 	cfg_ungettoken(pctx);
    465 cleanup:
    466 	return result;
    467 }
    468 
    469 /*
    470  * Parse EOF, logging and returning an error if not there.
    471  */
    472 static isc_result_t
    473 parse_eof(cfg_parser_t *pctx) {
    474 	isc_result_t result;
    475 
    476 	CHECK(cfg_gettoken(pctx, 0));
    477 
    478 	if (pctx->token.type == isc_tokentype_eof) {
    479 		return ISC_R_SUCCESS;
    480 	}
    481 
    482 	cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
    483 	return ISC_R_UNEXPECTEDTOKEN;
    484 cleanup:
    485 	return result;
    486 }
    487 
    488 /* A list of files, used internally for pctx->files. */
    489 
    490 static cfg_type_t cfg_type_filelist = { "filelist",    NULL,
    491 					print_list,    NULL,
    492 					&cfg_rep_list, &cfg_type_qstring };
    493 
    494 isc_result_t
    495 cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
    496 	isc_result_t result;
    497 	cfg_parser_t *pctx;
    498 	isc_lexspecials_t specials;
    499 
    500 	REQUIRE(mctx != NULL);
    501 	REQUIRE(ret != NULL && *ret == NULL);
    502 
    503 	pctx = isc_mem_get(mctx, sizeof(*pctx));
    504 
    505 	pctx->mctx = NULL;
    506 	isc_mem_attach(mctx, &pctx->mctx);
    507 
    508 	isc_refcount_init(&pctx->references, 1);
    509 
    510 	pctx->lctx = lctx;
    511 	pctx->lexer = NULL;
    512 	pctx->seen_eof = false;
    513 	pctx->ungotten = false;
    514 	pctx->errors = 0;
    515 	pctx->warnings = 0;
    516 	pctx->open_files = NULL;
    517 	pctx->closed_files = NULL;
    518 	pctx->line = 0;
    519 	pctx->callback = NULL;
    520 	pctx->callbackarg = NULL;
    521 	pctx->token.type = isc_tokentype_unknown;
    522 	pctx->flags = 0;
    523 	pctx->buf_name = NULL;
    524 
    525 	memset(specials, 0, sizeof(specials));
    526 	specials['{'] = 1;
    527 	specials['}'] = 1;
    528 	specials[';'] = 1;
    529 	specials['/'] = 1;
    530 	specials['"'] = 1;
    531 	specials['!'] = 1;
    532 
    533 	isc_lex_create(pctx->mctx, 1024, &pctx->lexer);
    534 
    535 	isc_lex_setspecials(pctx->lexer, specials);
    536 	isc_lex_setcomments(pctx->lexer, ISC_LEXCOMMENT_C |
    537 						 ISC_LEXCOMMENT_CPLUSPLUS |
    538 						 ISC_LEXCOMMENT_SHELL);
    539 
    540 	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
    541 	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
    542 
    543 	*ret = pctx;
    544 	return ISC_R_SUCCESS;
    545 
    546 cleanup:
    547 	if (pctx->lexer != NULL) {
    548 		isc_lex_destroy(&pctx->lexer);
    549 	}
    550 	CLEANUP_OBJ(pctx->open_files);
    551 	CLEANUP_OBJ(pctx->closed_files);
    552 	isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
    553 	return result;
    554 }
    555 
    556 void
    557 cfg_parser_setflags(cfg_parser_t *pctx, unsigned int flags, bool turn_on) {
    558 	REQUIRE(pctx != NULL);
    559 
    560 	if (turn_on) {
    561 		pctx->flags |= flags;
    562 	} else {
    563 		pctx->flags &= ~flags;
    564 	}
    565 }
    566 
    567 static isc_result_t
    568 parser_openfile(cfg_parser_t *pctx, const char *filename) {
    569 	isc_result_t result;
    570 	cfg_listelt_t *elt = NULL;
    571 	cfg_obj_t *stringobj = NULL;
    572 
    573 	result = isc_lex_openfile(pctx->lexer, filename);
    574 	if (result != ISC_R_SUCCESS) {
    575 		cfg_parser_error(pctx, 0, "open: %s: %s", filename,
    576 				 isc_result_totext(result));
    577 		goto cleanup;
    578 	}
    579 
    580 	CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
    581 	CHECK(create_listelt(pctx, &elt));
    582 	elt->obj = stringobj;
    583 	ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
    584 
    585 	return ISC_R_SUCCESS;
    586 cleanup:
    587 	CLEANUP_OBJ(stringobj);
    588 	return result;
    589 }
    590 
    591 void
    592 cfg_parser_setcallback(cfg_parser_t *pctx, cfg_parsecallback_t callback,
    593 		       void *arg) {
    594 	REQUIRE(pctx != NULL);
    595 
    596 	pctx->callback = callback;
    597 	pctx->callbackarg = arg;
    598 }
    599 
    600 void
    601 cfg_parser_reset(cfg_parser_t *pctx) {
    602 	REQUIRE(pctx != NULL);
    603 
    604 	if (pctx->lexer != NULL) {
    605 		isc_lex_close(pctx->lexer);
    606 	}
    607 
    608 	pctx->seen_eof = false;
    609 	pctx->ungotten = false;
    610 	pctx->errors = 0;
    611 	pctx->warnings = 0;
    612 	pctx->line = 0;
    613 }
    614 
    615 /*
    616  * Parse a configuration using a pctx where a lexer has already
    617  * been set up with a source.
    618  */
    619 static isc_result_t
    620 parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
    621 	isc_result_t result;
    622 	cfg_obj_t *obj = NULL;
    623 
    624 	result = cfg_parse_obj(pctx, type, &obj);
    625 
    626 	if (pctx->errors != 0) {
    627 		/* Errors have been logged. */
    628 		if (result == ISC_R_SUCCESS) {
    629 			result = ISC_R_FAILURE;
    630 		}
    631 		goto cleanup;
    632 	}
    633 
    634 	if (result != ISC_R_SUCCESS) {
    635 		/* Parsing failed but no errors have been logged. */
    636 		cfg_parser_error(pctx, 0, "parsing failed: %s",
    637 				 isc_result_totext(result));
    638 		goto cleanup;
    639 	}
    640 
    641 	CHECK(parse_eof(pctx));
    642 
    643 	*ret = obj;
    644 	return ISC_R_SUCCESS;
    645 
    646 cleanup:
    647 	CLEANUP_OBJ(obj);
    648 	return result;
    649 }
    650 
    651 isc_result_t
    652 cfg_parse_file(cfg_parser_t *pctx, const char *filename, const cfg_type_t *type,
    653 	       cfg_obj_t **ret) {
    654 	isc_result_t result;
    655 	cfg_listelt_t *elt;
    656 
    657 	REQUIRE(pctx != NULL);
    658 	REQUIRE(filename != NULL);
    659 	REQUIRE(type != NULL);
    660 	REQUIRE(ret != NULL && *ret == NULL);
    661 
    662 	CHECK(parser_openfile(pctx, filename));
    663 
    664 	result = parse2(pctx, type, ret);
    665 
    666 	/* Clean up the opened file */
    667 	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
    668 	INSIST(elt != NULL);
    669 	ISC_LIST_UNLINK(pctx->open_files->value.list, elt, link);
    670 	ISC_LIST_APPEND(pctx->closed_files->value.list, elt, link);
    671 
    672 cleanup:
    673 	return result;
    674 }
    675 
    676 isc_result_t
    677 cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer, const char *file,
    678 		 unsigned int line, const cfg_type_t *type, unsigned int flags,
    679 		 cfg_obj_t **ret) {
    680 	isc_result_t result;
    681 
    682 	REQUIRE(pctx != NULL);
    683 	REQUIRE(type != NULL);
    684 	REQUIRE(buffer != NULL);
    685 	REQUIRE(ret != NULL && *ret == NULL);
    686 	REQUIRE((flags & ~(CFG_PCTX_NODEPRECATED | CFG_PCTX_NOOBSOLETE |
    687 			   CFG_PCTX_NOEXPERIMENTAL)) == 0);
    688 
    689 	CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
    690 
    691 	pctx->buf_name = file;
    692 	pctx->flags = flags;
    693 
    694 	if (line != 0U) {
    695 		CHECK(isc_lex_setsourceline(pctx->lexer, line));
    696 	}
    697 
    698 	CHECK(parse2(pctx, type, ret));
    699 	pctx->buf_name = NULL;
    700 
    701 cleanup:
    702 	return result;
    703 }
    704 
    705 void
    706 cfg_parser_attach(cfg_parser_t *src, cfg_parser_t **dest) {
    707 	REQUIRE(src != NULL);
    708 	REQUIRE(dest != NULL && *dest == NULL);
    709 
    710 	isc_refcount_increment(&src->references);
    711 	*dest = src;
    712 }
    713 
    714 void
    715 cfg_parser_destroy(cfg_parser_t **pctxp) {
    716 	cfg_parser_t *pctx;
    717 
    718 	REQUIRE(pctxp != NULL && *pctxp != NULL);
    719 	pctx = *pctxp;
    720 	*pctxp = NULL;
    721 
    722 	if (isc_refcount_decrement(&pctx->references) == 1) {
    723 		isc_lex_destroy(&pctx->lexer);
    724 		/*
    725 		 * Cleaning up open_files does not
    726 		 * close the files; that was already done
    727 		 * by closing the lexer.
    728 		 */
    729 		CLEANUP_OBJ(pctx->open_files);
    730 		CLEANUP_OBJ(pctx->closed_files);
    731 		isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
    732 	}
    733 }
    734 
    735 /*
    736  * void
    737  */
    738 isc_result_t
    739 cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
    740 	REQUIRE(pctx != NULL);
    741 	REQUIRE(ret != NULL && *ret == NULL);
    742 
    743 	UNUSED(type);
    744 
    745 	return cfg_create_obj(pctx, &cfg_type_void, ret);
    746 }
    747 
    748 void
    749 cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
    750 	REQUIRE(pctx != NULL);
    751 	REQUIRE(obj != NULL);
    752 
    753 	UNUSED(pctx);
    754 	UNUSED(obj);
    755 }
    756 
    757 void
    758 cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
    759 	REQUIRE(pctx != NULL);
    760 	REQUIRE(type != NULL);
    761 
    762 	UNUSED(pctx);
    763 	UNUSED(type);
    764 }
    765 
    766 bool
    767 cfg_obj_isvoid(const cfg_obj_t *obj) {
    768 	REQUIRE(obj != NULL);
    769 	return obj->type->rep == &cfg_rep_void;
    770 }
    771 
    772 cfg_type_t cfg_type_void = { "void",	   cfg_parse_void, cfg_print_void,
    773 			     cfg_doc_void, &cfg_rep_void,  NULL };
    774 
    775 /*
    776  * percentage
    777  */
    778 isc_result_t
    779 cfg_parse_percentage(cfg_parser_t *pctx, const cfg_type_t *type,
    780 		     cfg_obj_t **ret) {
    781 	char *endp;
    782 	isc_result_t result;
    783 	cfg_obj_t *obj = NULL;
    784 	uint64_t percent;
    785 
    786 	REQUIRE(pctx != NULL);
    787 	REQUIRE(ret != NULL && *ret == NULL);
    788 
    789 	UNUSED(type);
    790 
    791 	CHECK(cfg_gettoken(pctx, 0));
    792 	if (pctx->token.type != isc_tokentype_string) {
    793 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected percentage");
    794 		return ISC_R_UNEXPECTEDTOKEN;
    795 	}
    796 
    797 	percent = strtoull(TOKEN_STRING(pctx), &endp, 10);
    798 	if (*endp != '%' || *(endp + 1) != 0) {
    799 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected percentage");
    800 		return ISC_R_UNEXPECTEDTOKEN;
    801 	}
    802 
    803 	CHECK(cfg_create_obj(pctx, &cfg_type_percentage, &obj));
    804 	obj->value.uint32 = (uint32_t)percent;
    805 	*ret = obj;
    806 
    807 cleanup:
    808 	return result;
    809 }
    810 
    811 void
    812 cfg_print_percentage(cfg_printer_t *pctx, const cfg_obj_t *obj) {
    813 	char buf[64];
    814 	int n;
    815 
    816 	REQUIRE(pctx != NULL);
    817 	REQUIRE(obj != NULL);
    818 
    819 	n = snprintf(buf, sizeof(buf), "%u%%", obj->value.uint32);
    820 	INSIST(n > 0 && (size_t)n < sizeof(buf));
    821 	cfg_print_chars(pctx, buf, strlen(buf));
    822 }
    823 
    824 uint32_t
    825 cfg_obj_aspercentage(const cfg_obj_t *obj) {
    826 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_percentage);
    827 	return obj->value.uint32;
    828 }
    829 
    830 cfg_type_t cfg_type_percentage = { "percentage",	 cfg_parse_percentage,
    831 				   cfg_print_percentage, cfg_doc_terminal,
    832 				   &cfg_rep_percentage,	 NULL };
    833 
    834 bool
    835 cfg_obj_ispercentage(const cfg_obj_t *obj) {
    836 	REQUIRE(obj != NULL);
    837 	return obj->type->rep == &cfg_rep_percentage;
    838 }
    839 
    840 /*
    841  * Fixed point
    842  */
    843 isc_result_t
    844 cfg_parse_fixedpoint(cfg_parser_t *pctx, const cfg_type_t *type,
    845 		     cfg_obj_t **ret) {
    846 	isc_result_t result;
    847 	cfg_obj_t *obj = NULL;
    848 	size_t n1, n2, n3, l;
    849 	const char *p;
    850 
    851 	REQUIRE(pctx != NULL);
    852 	REQUIRE(ret != NULL && *ret == NULL);
    853 
    854 	UNUSED(type);
    855 
    856 	CHECK(cfg_gettoken(pctx, 0));
    857 	if (pctx->token.type != isc_tokentype_string) {
    858 		cfg_parser_error(pctx, CFG_LOG_NEAR,
    859 				 "expected fixed point number");
    860 		return ISC_R_UNEXPECTEDTOKEN;
    861 	}
    862 
    863 	p = TOKEN_STRING(pctx);
    864 	l = strlen(p);
    865 	n1 = strspn(p, "0123456789");
    866 	n2 = strspn(p + n1, ".");
    867 	n3 = strspn(p + n1 + n2, "0123456789");
    868 
    869 	if ((n1 + n2 + n3 != l) || (n1 + n3 == 0) || n1 > 5 || n2 > 1 || n3 > 2)
    870 	{
    871 		cfg_parser_error(pctx, CFG_LOG_NEAR,
    872 				 "expected fixed point number");
    873 		return ISC_R_UNEXPECTEDTOKEN;
    874 	}
    875 
    876 	CHECK(cfg_create_obj(pctx, &cfg_type_fixedpoint, &obj));
    877 
    878 	obj->value.uint32 = strtoul(p, NULL, 10) * 100;
    879 	switch (n3) {
    880 	case 2:
    881 		obj->value.uint32 += strtoul(p + n1 + n2, NULL, 10);
    882 		break;
    883 	case 1:
    884 		obj->value.uint32 += strtoul(p + n1 + n2, NULL, 10) * 10;
    885 		break;
    886 	}
    887 	*ret = obj;
    888 
    889 cleanup:
    890 	return result;
    891 }
    892 
    893 void
    894 cfg_print_fixedpoint(cfg_printer_t *pctx, const cfg_obj_t *obj) {
    895 	char buf[64];
    896 	int n;
    897 
    898 	REQUIRE(pctx != NULL);
    899 	REQUIRE(obj != NULL);
    900 
    901 	n = snprintf(buf, sizeof(buf), "%u.%02u", obj->value.uint32 / 100,
    902 		     obj->value.uint32 % 100);
    903 	INSIST(n > 0 && (size_t)n < sizeof(buf));
    904 	cfg_print_chars(pctx, buf, strlen(buf));
    905 }
    906 
    907 uint32_t
    908 cfg_obj_asfixedpoint(const cfg_obj_t *obj) {
    909 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_fixedpoint);
    910 	return obj->value.uint32;
    911 }
    912 
    913 cfg_type_t cfg_type_fixedpoint = { "fixedpoint",	 cfg_parse_fixedpoint,
    914 				   cfg_print_fixedpoint, cfg_doc_terminal,
    915 				   &cfg_rep_fixedpoint,	 NULL };
    916 
    917 bool
    918 cfg_obj_isfixedpoint(const cfg_obj_t *obj) {
    919 	REQUIRE(obj != NULL);
    920 	return obj->type->rep == &cfg_rep_fixedpoint;
    921 }
    922 
    923 /*
    924  * uint32
    925  */
    926 isc_result_t
    927 cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
    928 	isc_result_t result;
    929 	cfg_obj_t *obj = NULL;
    930 
    931 	REQUIRE(pctx != NULL);
    932 	REQUIRE(ret != NULL && *ret == NULL);
    933 
    934 	UNUSED(type);
    935 
    936 	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
    937 	if (pctx->token.type != isc_tokentype_number) {
    938 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
    939 		return ISC_R_UNEXPECTEDTOKEN;
    940 	}
    941 
    942 	CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
    943 
    944 	obj->value.uint32 = pctx->token.value.as_ulong;
    945 	*ret = obj;
    946 cleanup:
    947 	return result;
    948 }
    949 
    950 void
    951 cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
    952 	cfg_print_chars(pctx, s, strlen(s));
    953 }
    954 
    955 void
    956 cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
    957 	char buf[32];
    958 
    959 	snprintf(buf, sizeof(buf), "%u", u);
    960 	cfg_print_cstr(pctx, buf);
    961 }
    962 
    963 void
    964 cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
    965 	cfg_print_rawuint(pctx, obj->value.uint32);
    966 }
    967 
    968 bool
    969 cfg_obj_isuint32(const cfg_obj_t *obj) {
    970 	REQUIRE(obj != NULL);
    971 	return obj->type->rep == &cfg_rep_uint32;
    972 }
    973 
    974 uint32_t
    975 cfg_obj_asuint32(const cfg_obj_t *obj) {
    976 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
    977 	return obj->value.uint32;
    978 }
    979 
    980 cfg_type_t cfg_type_uint32 = { "integer",	 cfg_parse_uint32,
    981 			       cfg_print_uint32, cfg_doc_terminal,
    982 			       &cfg_rep_uint32,	 NULL };
    983 
    984 /*
    985  * uint64
    986  */
    987 bool
    988 cfg_obj_isuint64(const cfg_obj_t *obj) {
    989 	REQUIRE(obj != NULL);
    990 	return obj->type->rep == &cfg_rep_uint64;
    991 }
    992 
    993 uint64_t
    994 cfg_obj_asuint64(const cfg_obj_t *obj) {
    995 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
    996 	return obj->value.uint64;
    997 }
    998 
    999 void
   1000 cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
   1001 	char buf[32];
   1002 
   1003 	snprintf(buf, sizeof(buf), "%" PRIu64, obj->value.uint64);
   1004 	cfg_print_cstr(pctx, buf);
   1005 }
   1006 
   1007 cfg_type_t cfg_type_uint64 = { "64_bit_integer", NULL,
   1008 			       cfg_print_uint64, cfg_doc_terminal,
   1009 			       &cfg_rep_uint64,	 NULL };
   1010 
   1011 /*
   1012  * Get the number of digits in a number.
   1013  */
   1014 static size_t
   1015 numlen(uint32_t num) {
   1016 	uint32_t period = num;
   1017 	size_t count = 0;
   1018 
   1019 	if (period == 0) {
   1020 		return 1;
   1021 	}
   1022 	while (period > 0) {
   1023 		count++;
   1024 		period /= 10;
   1025 	}
   1026 	return count;
   1027 }
   1028 
   1029 /*
   1030  * duration
   1031  */
   1032 void
   1033 cfg_print_duration(cfg_printer_t *pctx, const cfg_obj_t *obj) {
   1034 	char buf[CFG_DURATION_MAXLEN];
   1035 	char *str;
   1036 	const char *indicators = "YMWDHMS";
   1037 	int count, i;
   1038 	int durationlen[7] = { 0 };
   1039 	isccfg_duration_t duration;
   1040 	/*
   1041 	 * D ? The duration has a date part.
   1042 	 * T ? The duration has a time part.
   1043 	 */
   1044 	bool D = false, T = false;
   1045 
   1046 	REQUIRE(pctx != NULL);
   1047 	REQUIRE(obj != NULL);
   1048 
   1049 	duration = obj->value.duration;
   1050 
   1051 	/* If this is not an ISO 8601 duration, just print it as a number. */
   1052 	if (!duration.iso8601) {
   1053 		cfg_print_rawuint(pctx, duration.parts[6]);
   1054 		return;
   1055 	}
   1056 
   1057 	/* Calculate length of string. */
   1058 	buf[0] = 'P';
   1059 	buf[1] = '\0';
   1060 	str = &buf[1];
   1061 	count = 2;
   1062 	for (i = 0; i < 6; i++) {
   1063 		if (duration.parts[i] > 0) {
   1064 			durationlen[i] = 1 + numlen(duration.parts[i]);
   1065 			if (i < 4) {
   1066 				D = true;
   1067 			} else {
   1068 				T = true;
   1069 			}
   1070 			count += durationlen[i];
   1071 		}
   1072 	}
   1073 	/*
   1074 	 * Special case for seconds which is not taken into account in the
   1075 	 * above for loop: Count the length of the seconds part if it is
   1076 	 * non-zero, or if all the other parts are also zero.  In the latter
   1077 	 * case this function will print "PT0S".
   1078 	 */
   1079 	if (duration.parts[6] > 0 ||
   1080 	    (!D && !duration.parts[4] && !duration.parts[5]))
   1081 	{
   1082 		durationlen[6] = 1 + numlen(duration.parts[6]);
   1083 		T = true;
   1084 		count += durationlen[6];
   1085 	}
   1086 	/* Add one character for the time indicator. */
   1087 	if (T) {
   1088 		count++;
   1089 	}
   1090 	INSIST(count < CFG_DURATION_MAXLEN);
   1091 
   1092 	/* Now print the duration. */
   1093 	for (i = 0; i < 6; i++) {
   1094 		/*
   1095 		 * We don't check here if weeks and other time indicator are
   1096 		 * used mutually exclusively.
   1097 		 */
   1098 		if (duration.parts[i] > 0) {
   1099 			snprintf(str, durationlen[i] + 2, "%u%c",
   1100 				 (uint32_t)duration.parts[i], indicators[i]);
   1101 			str += durationlen[i];
   1102 		}
   1103 		if (i == 3 && T) {
   1104 			snprintf(str, 2, "T");
   1105 			str += 1;
   1106 		}
   1107 	}
   1108 	/* Special case for seconds. */
   1109 	if (duration.parts[6] > 0 ||
   1110 	    (!D && !duration.parts[4] && !duration.parts[5]))
   1111 	{
   1112 		snprintf(str, durationlen[6] + 2, "%u%c",
   1113 			 (uint32_t)duration.parts[6], indicators[6]);
   1114 	}
   1115 	cfg_print_chars(pctx, buf, strlen(buf));
   1116 }
   1117 
   1118 void
   1119 cfg_print_duration_or_unlimited(cfg_printer_t *pctx, const cfg_obj_t *obj) {
   1120 	isccfg_duration_t duration;
   1121 
   1122 	REQUIRE(pctx != NULL);
   1123 	REQUIRE(obj != NULL);
   1124 
   1125 	duration = obj->value.duration;
   1126 
   1127 	if (duration.unlimited) {
   1128 		cfg_print_cstr(pctx, "unlimited");
   1129 	} else {
   1130 		cfg_print_duration(pctx, obj);
   1131 	}
   1132 }
   1133 
   1134 bool
   1135 cfg_obj_isduration(const cfg_obj_t *obj) {
   1136 	REQUIRE(obj != NULL);
   1137 	return obj->type->rep == &cfg_rep_duration;
   1138 }
   1139 
   1140 uint32_t
   1141 cfg_obj_asduration(const cfg_obj_t *obj) {
   1142 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_duration);
   1143 	return isccfg_duration_toseconds(&(obj->value.duration));
   1144 }
   1145 
   1146 static isc_result_t
   1147 parse_duration(cfg_parser_t *pctx, cfg_obj_t **ret) {
   1148 	isc_result_t result;
   1149 	cfg_obj_t *obj = NULL;
   1150 	isccfg_duration_t duration;
   1151 
   1152 	result = isccfg_parse_duration(&pctx->token.value.as_textregion,
   1153 				       &duration);
   1154 
   1155 	if (result == ISC_R_RANGE) {
   1156 		cfg_parser_error(pctx, CFG_LOG_NEAR,
   1157 				 "duration or TTL out of range");
   1158 		return result;
   1159 	} else if (result != ISC_R_SUCCESS) {
   1160 		goto cleanup;
   1161 	}
   1162 
   1163 	CHECK(cfg_create_obj(pctx, &cfg_type_duration, &obj));
   1164 	obj->value.duration = duration;
   1165 	*ret = obj;
   1166 
   1167 	return ISC_R_SUCCESS;
   1168 
   1169 cleanup:
   1170 	cfg_parser_error(pctx, CFG_LOG_NEAR,
   1171 			 "expected ISO 8601 duration or TTL value");
   1172 	return result;
   1173 }
   1174 
   1175 isc_result_t
   1176 cfg_parse_duration(cfg_parser_t *pctx, const cfg_type_t *type,
   1177 		   cfg_obj_t **ret) {
   1178 	isc_result_t result;
   1179 
   1180 	UNUSED(type);
   1181 
   1182 	CHECK(cfg_gettoken(pctx, 0));
   1183 	if (pctx->token.type != isc_tokentype_string) {
   1184 		result = ISC_R_UNEXPECTEDTOKEN;
   1185 		goto cleanup;
   1186 	}
   1187 
   1188 	return parse_duration(pctx, ret);
   1189 
   1190 cleanup:
   1191 	cfg_parser_error(pctx, CFG_LOG_NEAR,
   1192 			 "expected ISO 8601 duration or TTL value");
   1193 	return result;
   1194 }
   1195 
   1196 isc_result_t
   1197 cfg_parse_duration_or_unlimited(cfg_parser_t *pctx, const cfg_type_t *type,
   1198 				cfg_obj_t **ret) {
   1199 	isc_result_t result;
   1200 	cfg_obj_t *obj = NULL;
   1201 	isccfg_duration_t duration;
   1202 
   1203 	UNUSED(type);
   1204 
   1205 	CHECK(cfg_gettoken(pctx, 0));
   1206 	if (pctx->token.type != isc_tokentype_string) {
   1207 		result = ISC_R_UNEXPECTEDTOKEN;
   1208 		goto cleanup;
   1209 	}
   1210 
   1211 	if (strcmp(TOKEN_STRING(pctx), "unlimited") == 0) {
   1212 		for (int i = 0; i < 7; i++) {
   1213 			duration.parts[i] = 0;
   1214 		}
   1215 		duration.iso8601 = false;
   1216 		duration.unlimited = true;
   1217 
   1218 		CHECK(cfg_create_obj(pctx, &cfg_type_duration, &obj));
   1219 		obj->value.duration = duration;
   1220 		*ret = obj;
   1221 		return ISC_R_SUCCESS;
   1222 	}
   1223 
   1224 	return parse_duration(pctx, ret);
   1225 
   1226 cleanup:
   1227 	cfg_parser_error(pctx, CFG_LOG_NEAR,
   1228 			 "expected ISO 8601 duration, TTL value, or unlimited");
   1229 	return result;
   1230 }
   1231 
   1232 /*%
   1233  * A duration as defined by ISO 8601 (P[n]Y[n]M[n]DT[n]H[n]M[n]S).
   1234  * - P is the duration indicator ("period") placed at the start.
   1235  * - Y is the year indicator that follows the value for the number of years.
   1236  * - M is the month indicator that follows the value for the number of months.
   1237  * - D is the day indicator that follows the value for the number of days.
   1238  * - T is the time indicator that precedes the time components.
   1239  * - H is the hour indicator that follows the value for the number of hours.
   1240  * - M is the minute indicator that follows the value for the number of
   1241  *   minutes.
   1242  * - S is the second indicator that follows the value for the number of
   1243  *   seconds.
   1244  *
   1245  * A duration can also be a TTL value (number + optional unit).
   1246  */
   1247 cfg_type_t cfg_type_duration = { "duration",	     cfg_parse_duration,
   1248 				 cfg_print_duration, cfg_doc_terminal,
   1249 				 &cfg_rep_duration,  NULL };
   1250 cfg_type_t cfg_type_duration_or_unlimited = { "duration_or_unlimited",
   1251 					      cfg_parse_duration_or_unlimited,
   1252 					      cfg_print_duration_or_unlimited,
   1253 					      cfg_doc_terminal,
   1254 					      &cfg_rep_duration,
   1255 					      NULL };
   1256 
   1257 /*
   1258  * qstring (quoted string), ustring (unquoted string), astring
   1259  * (any string), sstring (secret string)
   1260  */
   1261 
   1262 /* Create a string object from a null-terminated C string. */
   1263 static isc_result_t
   1264 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
   1265 	      cfg_obj_t **ret) {
   1266 	isc_result_t result;
   1267 	cfg_obj_t *obj = NULL;
   1268 	int len;
   1269 
   1270 	CHECK(cfg_create_obj(pctx, type, &obj));
   1271 	len = strlen(contents);
   1272 	obj->value.string.length = len;
   1273 	obj->value.string.base = isc_mem_get(pctx->mctx, len + 1);
   1274 	if (obj->value.string.base == 0) {
   1275 		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
   1276 		return ISC_R_NOMEMORY;
   1277 	}
   1278 	memmove(obj->value.string.base, contents, len);
   1279 	obj->value.string.base[len] = '\0';
   1280 
   1281 	*ret = obj;
   1282 cleanup:
   1283 	return result;
   1284 }
   1285 
   1286 isc_result_t
   1287 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
   1288 	isc_result_t result;
   1289 
   1290 	REQUIRE(pctx != NULL);
   1291 	REQUIRE(ret != NULL && *ret == NULL);
   1292 
   1293 	UNUSED(type);
   1294 
   1295 	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
   1296 	if (pctx->token.type != isc_tokentype_qstring) {
   1297 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
   1298 		return ISC_R_UNEXPECTEDTOKEN;
   1299 	}
   1300 	return create_string(pctx, TOKEN_STRING(pctx), &cfg_type_qstring, ret);
   1301 cleanup:
   1302 	return result;
   1303 }
   1304 
   1305 static isc_result_t
   1306 parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
   1307 	isc_result_t result;
   1308 
   1309 	UNUSED(type);
   1310 
   1311 	CHECK(cfg_gettoken(pctx, 0));
   1312 	if (pctx->token.type != isc_tokentype_string) {
   1313 		cfg_parser_error(pctx, CFG_LOG_NEAR,
   1314 				 "expected unquoted string");
   1315 		return ISC_R_UNEXPECTEDTOKEN;
   1316 	}
   1317 	return create_string(pctx, TOKEN_STRING(pctx), &cfg_type_ustring, ret);
   1318 cleanup:
   1319 	return result;
   1320 }
   1321 
   1322 isc_result_t
   1323 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
   1324 	isc_result_t result;
   1325 
   1326 	REQUIRE(pctx != NULL);
   1327 	REQUIRE(ret != NULL && *ret == NULL);
   1328 
   1329 	UNUSED(type);
   1330 
   1331 	CHECK(cfg_getstringtoken(pctx));
   1332 	return create_string(pctx, TOKEN_STRING(pctx), &cfg_type_qstring, ret);
   1333 cleanup:
   1334 	return result;
   1335 }
   1336 
   1337 isc_result_t
   1338 cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
   1339 	isc_result_t result;
   1340 
   1341 	REQUIRE(pctx != NULL);
   1342 	REQUIRE(ret != NULL && *ret == NULL);
   1343 
   1344 	UNUSED(type);
   1345 
   1346 	CHECK(cfg_getstringtoken(pctx));
   1347 	return create_string(pctx, TOKEN_STRING(pctx), &cfg_type_sstring, ret);
   1348 cleanup:
   1349 	return result;
   1350 }
   1351 
   1352 static isc_result_t
   1353 parse_btext(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
   1354 	isc_result_t result;
   1355 
   1356 	UNUSED(type);
   1357 
   1358 	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_BTEXT));
   1359 	if (pctx->token.type != isc_tokentype_btext) {
   1360 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected bracketed text");
   1361 		return ISC_R_UNEXPECTEDTOKEN;
   1362 	}
   1363 	return create_string(pctx, TOKEN_STRING(pctx), &cfg_type_bracketed_text,
   1364 			     ret);
   1365 cleanup:
   1366 	return result;
   1367 }
   1368 
   1369 static void
   1370 print_btext(cfg_printer_t *pctx, const cfg_obj_t *obj) {
   1371 	/*
   1372 	 * We need to print "{" instead of running print_open()
   1373 	 * in order to preserve the exact original formatting
   1374 	 * of the bracketed text. But we increment the indent value
   1375 	 * so that print_close() will leave us back in our original
   1376 	 * state.
   1377 	 */
   1378 	pctx->indent++;
   1379 	cfg_print_cstr(pctx, "{");
   1380 	cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
   1381 	print_close(pctx);
   1382 }
   1383 
   1384 static void
   1385 doc_btext(cfg_printer_t *pctx, const cfg_type_t *type) {
   1386 	UNUSED(type);
   1387 
   1388 	cfg_print_cstr(pctx, "{ <unspecified-text> }");
   1389 }
   1390 
   1391 bool
   1392 cfg_is_enum(const char *s, const char *const *enums) {
   1393 	const char *const *p;
   1394 
   1395 	REQUIRE(s != NULL);
   1396 	REQUIRE(enums != NULL);
   1397 
   1398 	for (p = enums; *p != NULL; p++) {
   1399 		if (strcasecmp(*p, s) == 0) {
   1400 			return true;
   1401 		}
   1402 	}
   1403 	return false;
   1404 }
   1405 
   1406 static isc_result_t
   1407 check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
   1408 	const char *s = obj->value.string.base;
   1409 
   1410 	if (cfg_is_enum(s, enums)) {
   1411 		return ISC_R_SUCCESS;
   1412 	}
   1413 	cfg_parser_error(pctx, 0, "'%s' unexpected", s);
   1414 	return ISC_R_UNEXPECTEDTOKEN;
   1415 }
   1416 
   1417 isc_result_t
   1418 cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
   1419 	isc_result_t result;
   1420 	cfg_obj_t *obj = NULL;
   1421 
   1422 	REQUIRE(pctx != NULL);
   1423 	REQUIRE(type != NULL);
   1424 	REQUIRE(ret != NULL && *ret == NULL);
   1425 
   1426 	CHECK(parse_ustring(pctx, NULL, &obj));
   1427 	CHECK(check_enum(pctx, obj, type->of));
   1428 	*ret = obj;
   1429 	return ISC_R_SUCCESS;
   1430 cleanup:
   1431 	CLEANUP_OBJ(obj);
   1432 	return result;
   1433 }
   1434 
   1435 void
   1436 cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
   1437 	const char *const *p;
   1438 
   1439 	REQUIRE(pctx != NULL);
   1440 	REQUIRE(type != NULL);
   1441 
   1442 	cfg_print_cstr(pctx, "( ");
   1443 	for (p = type->of; *p != NULL; p++) {
   1444 		cfg_print_cstr(pctx, *p);
   1445 		if (p[1] != NULL) {
   1446 			cfg_print_cstr(pctx, " | ");
   1447 		}
   1448 	}
   1449 	cfg_print_cstr(pctx, " )");
   1450 }
   1451 
   1452 isc_result_t
   1453 cfg_parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype,
   1454 			const cfg_type_t *othertype, cfg_obj_t **ret) {
   1455 	isc_result_t result;
   1456 	CHECK(cfg_peektoken(pctx, 0));
   1457 	if (pctx->token.type == isc_tokentype_string &&
   1458 	    cfg_is_enum(TOKEN_STRING(pctx), enumtype->of))
   1459 	{
   1460 		CHECK(cfg_parse_enum(pctx, enumtype, ret));
   1461 	} else {
   1462 		CHECK(cfg_parse_obj(pctx, othertype, ret));
   1463 	}
   1464 cleanup:
   1465 	return result;
   1466 }
   1467 
   1468 void
   1469 cfg_doc_enum_or_other(cfg_printer_t *pctx, const cfg_type_t *enumtype,
   1470 		      const cfg_type_t *othertype) {
   1471 	const char *const *p;
   1472 	bool first = true;
   1473 
   1474 	/*
   1475 	 * If othertype is cfg_type_void, it means that enumtype is
   1476 	 * optional.
   1477 	 */
   1478 
   1479 	if (othertype == &cfg_type_void) {
   1480 		cfg_print_cstr(pctx, "[ ");
   1481 	}
   1482 	cfg_print_cstr(pctx, "( ");
   1483 	for (p = enumtype->of; *p != NULL; p++) {
   1484 		if (!first) {
   1485 			cfg_print_cstr(pctx, " | ");
   1486 		}
   1487 		first = false;
   1488 		cfg_print_cstr(pctx, *p);
   1489 	}
   1490 	if (othertype != &cfg_type_void) {
   1491 		if (!first) {
   1492 			cfg_print_cstr(pctx, " | ");
   1493 		}
   1494 		cfg_doc_terminal(pctx, othertype);
   1495 	}
   1496 	cfg_print_cstr(pctx, " )");
   1497 	if (othertype == &cfg_type_void) {
   1498 		cfg_print_cstr(pctx, " ]");
   1499 	}
   1500 }
   1501 
   1502 void
   1503 cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
   1504 	REQUIRE(pctx != NULL);
   1505 	REQUIRE(obj != NULL);
   1506 
   1507 	cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
   1508 }
   1509 
   1510 static void
   1511 print_rawqstring(cfg_printer_t *pctx, const isc_textregion_t string) {
   1512 	cfg_print_cstr(pctx, "\"");
   1513 	for (size_t i = 0; i < string.length; i++) {
   1514 		if (string.base[i] == '"') {
   1515 			cfg_print_cstr(pctx, "\\");
   1516 		}
   1517 		cfg_print_chars(pctx, (const char *)&string.base[i], 1);
   1518 	}
   1519 	cfg_print_cstr(pctx, "\"");
   1520 }
   1521 
   1522 static void
   1523 print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
   1524 	print_rawqstring(pctx, obj->value.string);
   1525 }
   1526 
   1527 static void
   1528 print_sstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
   1529 	cfg_print_cstr(pctx, "\"");
   1530 	if ((pctx->flags & CFG_PRINTER_XKEY) != 0) {
   1531 		unsigned int len = obj->value.string.length;
   1532 		while (len-- > 0) {
   1533 			cfg_print_cstr(pctx, "?");
   1534 		}
   1535 	} else {
   1536 		cfg_print_ustring(pctx, obj);
   1537 	}
   1538 	cfg_print_cstr(pctx, "\"");
   1539 }
   1540 
   1541 static void
   1542 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
   1543 	isc_mem_put(pctx->mctx, obj->value.string.base,
   1544 		    obj->value.string.length + 1);
   1545 }
   1546 
   1547 static void
   1548 copy_string(cfg_parser_t *pctx, const cfg_obj_t *obj, isc_textregion_t *dst) {
   1549 	if (dst->base != NULL) {
   1550 		INSIST(dst->length != 0);
   1551 		isc_mem_put(pctx->mctx, dst->base, dst->length + 1);
   1552 	}
   1553 	dst->length = obj->value.string.length;
   1554 	dst->base = isc_mem_get(pctx->mctx, dst->length + 1);
   1555 	memmove(dst->base, obj->value.string.base, dst->length);
   1556 	dst->base[dst->length] = '\0';
   1557 }
   1558 
   1559 static void
   1560 free_sockaddrtls(cfg_parser_t *pctx, cfg_obj_t *obj) {
   1561 	if (obj->value.sockaddrtls.tls.base != NULL) {
   1562 		INSIST(obj->value.sockaddrtls.tls.length != 0);
   1563 		isc_mem_put(pctx->mctx, obj->value.sockaddrtls.tls.base,
   1564 			    obj->value.sockaddrtls.tls.length + 1);
   1565 	}
   1566 }
   1567 
   1568 bool
   1569 cfg_obj_isstring(const cfg_obj_t *obj) {
   1570 	REQUIRE(obj != NULL);
   1571 	return obj->type->rep == &cfg_rep_string;
   1572 }
   1573 
   1574 const char *
   1575 cfg_obj_asstring(const cfg_obj_t *obj) {
   1576 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
   1577 	return obj->value.string.base;
   1578 }
   1579 
   1580 /* Quoted string only */
   1581 cfg_type_t cfg_type_qstring = { "quoted_string", cfg_parse_qstring,
   1582 				print_qstring,	 cfg_doc_terminal,
   1583 				&cfg_rep_string, NULL };
   1584 
   1585 /* Unquoted string only */
   1586 cfg_type_t cfg_type_ustring = { "string",	   parse_ustring,
   1587 				cfg_print_ustring, cfg_doc_terminal,
   1588 				&cfg_rep_string,   NULL };
   1589 
   1590 /* Any string (quoted or unquoted); printed with quotes */
   1591 cfg_type_t cfg_type_astring = { "string",	 cfg_parse_astring,
   1592 				print_qstring,	 cfg_doc_terminal,
   1593 				&cfg_rep_string, NULL };
   1594 
   1595 /*
   1596  * Any string (quoted or unquoted); printed with quotes.
   1597  * If CFG_PRINTER_XKEY is set when printing the string will be '?' out.
   1598  */
   1599 cfg_type_t cfg_type_sstring = { "string",	 cfg_parse_sstring,
   1600 				print_sstring,	 cfg_doc_terminal,
   1601 				&cfg_rep_string, NULL };
   1602 
   1603 /*
   1604  * Text enclosed in brackets. Used to pass a block of configuration
   1605  * text to dynamic library or external application. Checked for
   1606  * bracket balance, but not otherwise parsed.
   1607  */
   1608 cfg_type_t cfg_type_bracketed_text = { "bracketed_text", parse_btext,
   1609 				       print_btext,	 doc_btext,
   1610 				       &cfg_rep_string,	 NULL };
   1611 
   1612 #if defined(HAVE_GEOIP2)
   1613 /*
   1614  * "geoip" ACL element:
   1615  * geoip [ db <database> ] search-type <string>
   1616  */
   1617 static const char *geoiptype_enums[] = {
   1618 	"area",	      "areacode",  "asnum",	  "city",     "continent",
   1619 	"country",    "country3",  "countryname", "domain",   "isp",
   1620 	"metro",      "metrocode", "netspeed",	  "org",      "postal",
   1621 	"postalcode", "region",	   "regionname",  "timezone", "tz",
   1622 	NULL
   1623 };
   1624 static cfg_type_t cfg_type_geoiptype = { "geoiptype",	    cfg_parse_enum,
   1625 					 cfg_print_ustring, cfg_doc_enum,
   1626 					 &cfg_rep_string,   &geoiptype_enums };
   1627 
   1628 static cfg_tuplefielddef_t geoip_fields[] = {
   1629 	{ "negated", &cfg_type_void, 0 },
   1630 	{ "db", &cfg_type_astring, 0 },
   1631 	{ "subtype", &cfg_type_geoiptype, 0 },
   1632 	{ "search", &cfg_type_astring, 0 },
   1633 	{ NULL, NULL, 0 }
   1634 };
   1635 
   1636 static cfg_type_t cfg_type_geoip = { "geoip",	parse_geoip,	print_geoip,
   1637 				     doc_geoip, &cfg_rep_tuple, geoip_fields };
   1638 
   1639 static isc_result_t
   1640 parse_geoip(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
   1641 	isc_result_t result;
   1642 	cfg_obj_t *obj = NULL;
   1643 	const cfg_tuplefielddef_t *fields = type->of;
   1644 
   1645 	CHECK(cfg_create_tuple(pctx, type, &obj));
   1646 	CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[0]));
   1647 
   1648 	/* Parse the optional "db" field. */
   1649 	CHECK(cfg_peektoken(pctx, 0));
   1650 	if (pctx->token.type == isc_tokentype_string) {
   1651 		CHECK(cfg_gettoken(pctx, 0));
   1652 		if (strcasecmp(TOKEN_STRING(pctx), "db") == 0 &&
   1653 		    obj->value.tuple[1] == NULL)
   1654 		{
   1655 			CHECK(cfg_parse_obj(pctx, fields[1].type,
   1656 					    &obj->value.tuple[1]));
   1657 		} else {
   1658 			CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1]));
   1659 			cfg_ungettoken(pctx);
   1660 		}
   1661 	}
   1662 
   1663 	CHECK(cfg_parse_obj(pctx, fields[2].type, &obj->value.tuple[2]));
   1664 	CHECK(cfg_parse_obj(pctx, fields[3].type, &obj->value.tuple[3]));
   1665 
   1666 	*ret = obj;
   1667 	return ISC_R_SUCCESS;
   1668 
   1669 cleanup:
   1670 	CLEANUP_OBJ(obj);
   1671 	return result;
   1672 }
   1673 
   1674 static void
   1675 print_geoip(cfg_printer_t *pctx, const cfg_obj_t *obj) {
   1676 	if (obj->value.tuple[1]->type->print != cfg_print_void) {
   1677 		cfg_print_cstr(pctx, " db ");
   1678 		cfg_print_obj(pctx, obj->value.tuple[1]);
   1679 	}
   1680 	cfg_print_obj(pctx, obj->value.tuple[2]);
   1681 	cfg_print_obj(pctx, obj->value.tuple[3]);
   1682 }
   1683 
   1684 static void
   1685 doc_geoip(cfg_printer_t *pctx, const cfg_type_t *type) {
   1686 	UNUSED(type);
   1687 	cfg_print_cstr(pctx, "[ db ");
   1688 	cfg_doc_obj(pctx, &cfg_type_astring);
   1689 	cfg_print_cstr(pctx, " ]");
   1690 	cfg_print_cstr(pctx, " ");
   1691 	cfg_doc_enum(pctx, &cfg_type_geoiptype);
   1692 	cfg_print_cstr(pctx, " ");
   1693 	cfg_doc_obj(pctx, &cfg_type_astring);
   1694 }
   1695 #endif /* HAVE_GEOIP2 */
   1696 
   1697 static cfg_type_t cfg_type_addrmatchelt;
   1698 static cfg_type_t cfg_type_negated;
   1699 
   1700 static isc_result_t
   1701 parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type,
   1702 		   cfg_obj_t **ret) {
   1703 	isc_result_t result;
   1704 	UNUSED(type);
   1705 
   1706 	CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
   1707 
   1708 	if (pctx->token.type == isc_tokentype_string ||
   1709 	    pctx->token.type == isc_tokentype_qstring)
   1710 	{
   1711 		if (pctx->token.type == isc_tokentype_string &&
   1712 		    (strcasecmp(TOKEN_STRING(pctx), "key") == 0))
   1713 		{
   1714 			CHECK(cfg_parse_obj(pctx, &cfg_type_keyref, ret));
   1715 		} else if (pctx->token.type == isc_tokentype_string &&
   1716 			   (strcasecmp(TOKEN_STRING(pctx), "geoip") == 0))
   1717 		{
   1718 #if defined(HAVE_GEOIP2)
   1719 			CHECK(cfg_gettoken(pctx, 0));
   1720 			CHECK(cfg_parse_obj(pctx, &cfg_type_geoip, ret));
   1721 #else  /* if defined(HAVE_GEOIP2) */
   1722 			cfg_parser_error(pctx, CFG_LOG_NEAR,
   1723 					 "'geoip' "
   1724 					 "not supported in this build");
   1725 			return ISC_R_UNEXPECTEDTOKEN;
   1726 #endif /* if defined(HAVE_GEOIP2) */
   1727 		} else {
   1728 			if (cfg_lookingat_netaddr(
   1729 				    pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
   1730 						  CFG_ADDR_V6OK))
   1731 			{
   1732 				CHECK(cfg_parse_netprefix(pctx, NULL, ret));
   1733 			} else {
   1734 				CHECK(cfg_parse_astring(pctx, NULL, ret));
   1735 			}
   1736 		}
   1737 	} else if (pctx->token.type == isc_tokentype_special) {
   1738 		if (pctx->token.value.as_char == '{') {
   1739 			/* Nested match list. */
   1740 			CHECK(cfg_parse_obj(pctx, &cfg_type_bracketed_aml,
   1741 					    ret));
   1742 		} else if (pctx->token.value.as_char == '!') {
   1743 			CHECK(cfg_gettoken(pctx, 0)); /* read "!" */
   1744 			CHECK(cfg_parse_obj(pctx, &cfg_type_negated, ret));
   1745 		} else {
   1746 			goto bad;
   1747 		}
   1748 	} else {
   1749 	bad:
   1750 		cfg_parser_error(pctx, CFG_LOG_NEAR,
   1751 				 "expected IP match list element");
   1752 		return ISC_R_UNEXPECTEDTOKEN;
   1753 	}
   1754 cleanup:
   1755 	return result;
   1756 }
   1757 
   1758 /*%
   1759  * A negated address match list element (like "! 10.0.0.1").
   1760  * Somewhat sneakily, the caller is expected to parse the
   1761  * "!", but not to print it.
   1762  */
   1763 static cfg_tuplefielddef_t negated_fields[] = {
   1764 	{ "negated", &cfg_type_addrmatchelt, 0 }, { NULL, NULL, 0 }
   1765 };
   1766 
   1767 static void
   1768 print_negated(cfg_printer_t *pctx, const cfg_obj_t *obj) {
   1769 	cfg_print_cstr(pctx, "!");
   1770 	cfg_print_tuple(pctx, obj);
   1771 }
   1772 
   1773 static cfg_type_t cfg_type_negated = { "negated",      cfg_parse_tuple,
   1774 				       print_negated,  NULL,
   1775 				       &cfg_rep_tuple, &negated_fields };
   1776 
   1777 /*% An address match list element */
   1778 
   1779 static cfg_type_t cfg_type_addrmatchelt = { "address_match_element",
   1780 					    parse_addrmatchelt,
   1781 					    NULL,
   1782 					    cfg_doc_terminal,
   1783 					    NULL,
   1784 					    NULL };
   1785 
   1786 /*%
   1787  * A bracketed address match list
   1788  */
   1789 cfg_type_t cfg_type_bracketed_aml = { "bracketed_aml",
   1790 				      cfg_parse_bracketed_list,
   1791 				      cfg_print_bracketed_list,
   1792 				      cfg_doc_bracketed_list,
   1793 				      &cfg_rep_list,
   1794 				      &cfg_type_addrmatchelt };
   1795 
   1796 /*
   1797  * Optional bracketed text
   1798  */
   1799 static isc_result_t
   1800 parse_optional_btext(cfg_parser_t *pctx, const cfg_type_t *type,
   1801 		     cfg_obj_t **ret) {
   1802 	isc_result_t result;
   1803 
   1804 	UNUSED(type);
   1805 
   1806 	CHECK(cfg_peektoken(pctx, ISC_LEXOPT_BTEXT));
   1807 	if (pctx->token.type == isc_tokentype_btext) {
   1808 		CHECK(cfg_parse_obj(pctx, &cfg_type_bracketed_text, ret));
   1809 	} else {
   1810 		CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
   1811 	}
   1812 cleanup:
   1813 	return result;
   1814 }
   1815 
   1816 static void
   1817 print_optional_btext(cfg_printer_t *pctx, const cfg_obj_t *obj) {
   1818 	if (obj->type == &cfg_type_void) {
   1819 		return;
   1820 	}
   1821 
   1822 	pctx->indent++;
   1823 	cfg_print_cstr(pctx, "{");
   1824 	cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
   1825 	print_close(pctx);
   1826 }
   1827 
   1828 static void
   1829 doc_optional_btext(cfg_printer_t *pctx, const cfg_type_t *type) {
   1830 	UNUSED(type);
   1831 
   1832 	cfg_print_cstr(pctx, "[ { <unspecified-text> } ]");
   1833 }
   1834 
   1835 cfg_type_t cfg_type_optional_bracketed_text = { "optional_btext",
   1836 						parse_optional_btext,
   1837 						print_optional_btext,
   1838 						doc_optional_btext,
   1839 						NULL,
   1840 						NULL };
   1841 
   1842 /*
   1843  * Booleans
   1844  */
   1845 
   1846 bool
   1847 cfg_obj_isboolean(const cfg_obj_t *obj) {
   1848 	REQUIRE(obj != NULL);
   1849 	return obj->type->rep == &cfg_rep_boolean;
   1850 }
   1851 
   1852 bool
   1853 cfg_obj_asboolean(const cfg_obj_t *obj) {
   1854 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
   1855 	return obj->value.boolean;
   1856 }
   1857 
   1858 isc_result_t
   1859 cfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
   1860 	isc_result_t result;
   1861 	bool value;
   1862 	cfg_obj_t *obj = NULL;
   1863 
   1864 	REQUIRE(pctx != NULL);
   1865 	REQUIRE(ret != NULL && *ret == NULL);
   1866 
   1867 	UNUSED(type);
   1868 
   1869 	result = cfg_gettoken(pctx, 0);
   1870 	if (result != ISC_R_SUCCESS) {
   1871 		return result;
   1872 	}
   1873 
   1874 	if (pctx->token.type != isc_tokentype_string) {
   1875 		goto bad_boolean;
   1876 	}
   1877 
   1878 	if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
   1879 	    (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
   1880 	    (strcmp(TOKEN_STRING(pctx), "1") == 0))
   1881 	{
   1882 		value = true;
   1883 	} else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
   1884 		   (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
   1885 		   (strcmp(TOKEN_STRING(pctx), "0") == 0))
   1886 	{
   1887 		value = false;
   1888 	} else {
   1889 		goto bad_boolean;
   1890 	}
   1891 
   1892 	CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
   1893 	obj->value.boolean = value;
   1894 	*ret = obj;
   1895 	return result;
   1896 
   1897 bad_boolean:
   1898 	cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
   1899 	return ISC_R_UNEXPECTEDTOKEN;
   1900 
   1901 cleanup:
   1902 	return result;
   1903 }
   1904 
   1905 void
   1906 cfg_print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
   1907 	REQUIRE(pctx != NULL);
   1908 	REQUIRE(obj != NULL);
   1909 
   1910 	if (obj->value.boolean) {
   1911 		cfg_print_cstr(pctx, "yes");
   1912 	} else {
   1913 		cfg_print_cstr(pctx, "no");
   1914 	}
   1915 }
   1916 
   1917 cfg_type_t cfg_type_boolean = { "boolean",	   cfg_parse_boolean,
   1918 				cfg_print_boolean, cfg_doc_terminal,
   1919 				&cfg_rep_boolean,  NULL };
   1920 
   1921 /*
   1922  * Lists.
   1923  */
   1924 
   1925 isc_result_t
   1926 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
   1927 	isc_result_t result;
   1928 
   1929 	REQUIRE(pctx != NULL);
   1930 	REQUIRE(type != NULL);
   1931 	REQUIRE(obj != NULL && *obj == NULL);
   1932 
   1933 	CHECK(cfg_create_obj(pctx, type, obj));
   1934 	ISC_LIST_INIT((*obj)->value.list);
   1935 cleanup:
   1936 	return result;
   1937 }
   1938 
   1939 static isc_result_t
   1940 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
   1941 	cfg_listelt_t *elt;
   1942 
   1943 	elt = isc_mem_get(pctx->mctx, sizeof(*elt));
   1944 	elt->obj = NULL;
   1945 	ISC_LINK_INIT(elt, link);
   1946 	*eltp = elt;
   1947 	return ISC_R_SUCCESS;
   1948 }
   1949 
   1950 static void
   1951 free_listelt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
   1952 	if (elt->obj != NULL) {
   1953 		cfg_obj_destroy(pctx, &elt->obj);
   1954 	}
   1955 	isc_mem_put(pctx->mctx, elt, sizeof(*elt));
   1956 }
   1957 
   1958 static void
   1959 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
   1960 	cfg_listelt_t *elt, *next;
   1961 	for (elt = ISC_LIST_HEAD(obj->value.list); elt != NULL; elt = next) {
   1962 		next = ISC_LIST_NEXT(elt, link);
   1963 		free_listelt(pctx, elt);
   1964 	}
   1965 }
   1966 
   1967 isc_result_t
   1968 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
   1969 		  cfg_listelt_t **ret) {
   1970 	isc_result_t result;
   1971 	cfg_listelt_t *elt = NULL;
   1972 	cfg_obj_t *value = NULL;
   1973 
   1974 	REQUIRE(pctx != NULL);
   1975 	REQUIRE(elttype != NULL);
   1976 	REQUIRE(ret != NULL && *ret == NULL);
   1977 
   1978 	CHECK(create_listelt(pctx, &elt));
   1979 
   1980 	result = cfg_parse_obj(pctx, elttype, &value);
   1981 	if (result != ISC_R_SUCCESS) {
   1982 		goto cleanup;
   1983 	}
   1984 
   1985 	elt->obj = value;
   1986 
   1987 	*ret = elt;
   1988 	return ISC_R_SUCCESS;
   1989 
   1990 cleanup:
   1991 	isc_mem_put(pctx->mctx, elt, sizeof(*elt));
   1992 	return result;
   1993 }
   1994 
   1995 /*
   1996  * Parse a homogeneous list whose elements are of type 'elttype'
   1997  * and where each element is terminated by a semicolon.
   1998  */
   1999 static isc_result_t
   2000 parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret) {
   2001 	cfg_obj_t *listobj = NULL;
   2002 	const cfg_type_t *listof = listtype->of;
   2003 	isc_result_t result;
   2004 	cfg_listelt_t *elt = NULL;
   2005 
   2006 	CHECK(cfg_create_list(pctx, listtype, &listobj));
   2007 
   2008 	for (;;) {
   2009 		CHECK(cfg_peektoken(pctx, 0));
   2010 		if (pctx->token.type == isc_tokentype_special &&
   2011 		    pctx->token.value.as_char == /*{*/ '}')
   2012 		{
   2013 			break;
   2014 		}
   2015 		CHECK(cfg_parse_listelt(pctx, listof, &elt));
   2016 		CHECK(parse_semicolon(pctx));
   2017 		ISC_LIST_APPEND(listobj->value.list, elt, link);
   2018 		elt = NULL;
   2019 	}
   2020 	*ret = listobj;
   2021 	return ISC_R_SUCCESS;
   2022 
   2023 cleanup:
   2024 	if (elt != NULL) {
   2025 		free_listelt(pctx, elt);
   2026 	}
   2027 	CLEANUP_OBJ(listobj);
   2028 	return result;
   2029 }
   2030 
   2031 static void
   2032 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
   2033 	const cfg_list_t *list = &obj->value.list;
   2034 	const cfg_listelt_t *elt;
   2035 
   2036 	for (elt = ISC_LIST_HEAD(*list); elt != NULL;
   2037 	     elt = ISC_LIST_NEXT(elt, link))
   2038 	{
   2039 		if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) {
   2040 			cfg_print_obj(pctx, elt->obj);
   2041 			cfg_print_cstr(pctx, "; ");
   2042 		} else {
   2043 			cfg_print_indent(pctx);
   2044 			cfg_print_obj(pctx, elt->obj);
   2045 			cfg_print_cstr(pctx, ";\n");
   2046 		}
   2047 	}
   2048 }
   2049 
   2050 isc_result_t
   2051 cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
   2052 			 cfg_obj_t **ret) {
   2053 	isc_result_t result;
   2054 
   2055 	REQUIRE(pctx != NULL);
   2056 	REQUIRE(type != NULL);
   2057 	REQUIRE(ret != NULL && *ret == NULL);
   2058 
   2059 	CHECK(cfg_parse_special(pctx, '{'));
   2060 	CHECK(parse_list(pctx, type, ret));
   2061 	CHECK(cfg_parse_special(pctx, '}'));
   2062 cleanup:
   2063 	return result;
   2064 }
   2065 
   2066 void
   2067 cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
   2068 	REQUIRE(pctx != NULL);
   2069 	REQUIRE(obj != NULL);
   2070 
   2071 	print_open(pctx);
   2072 	print_list(pctx, obj);
   2073 	print_close(pctx);
   2074 }
   2075 
   2076 void
   2077 cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
   2078 	REQUIRE(pctx != NULL);
   2079 	REQUIRE(type != NULL);
   2080 
   2081 	cfg_print_cstr(pctx, "{ ");
   2082 	cfg_doc_obj(pctx, type->of);
   2083 	cfg_print_cstr(pctx, "; ... }");
   2084 }
   2085 
   2086 /*
   2087  * Parse a homogeneous list whose elements are of type 'elttype'
   2088  * and where elements are separated by space.  The list ends
   2089  * before the first semicolon.
   2090  */
   2091 isc_result_t
   2092 cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
   2093 		    cfg_obj_t **ret) {
   2094 	cfg_obj_t *listobj = NULL;
   2095 	const cfg_type_t *listof;
   2096 	isc_result_t result;
   2097 
   2098 	REQUIRE(pctx != NULL);
   2099 	REQUIRE(listtype != NULL);
   2100 	REQUIRE(ret != NULL && *ret == NULL);
   2101 
   2102 	listof = listtype->of;
   2103 
   2104 	CHECK(cfg_create_list(pctx, listtype, &listobj));
   2105 
   2106 	for (;;) {
   2107 		cfg_listelt_t *elt = NULL;
   2108 
   2109 		CHECK(cfg_peektoken(pctx, 0));
   2110 		if (pctx->token.type == isc_tokentype_special &&
   2111 		    pctx->token.value.as_char == ';')
   2112 		{
   2113 			break;
   2114 		}
   2115 		CHECK(cfg_parse_listelt(pctx, listof, &elt));
   2116 		ISC_LIST_APPEND(listobj->value.list, elt, link);
   2117 	}
   2118 	*ret = listobj;
   2119 	return ISC_R_SUCCESS;
   2120 
   2121 cleanup:
   2122 	CLEANUP_OBJ(listobj);
   2123 	return result;
   2124 }
   2125 
   2126 void
   2127 cfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) {
   2128 	const cfg_list_t *list = NULL;
   2129 	const cfg_listelt_t *elt = NULL;
   2130 
   2131 	REQUIRE(pctx != NULL);
   2132 	REQUIRE(obj != NULL);
   2133 
   2134 	list = &obj->value.list;
   2135 
   2136 	for (elt = ISC_LIST_HEAD(*list); elt != NULL;
   2137 	     elt = ISC_LIST_NEXT(elt, link))
   2138 	{
   2139 		cfg_print_obj(pctx, elt->obj);
   2140 		if (ISC_LIST_NEXT(elt, link) != NULL) {
   2141 			cfg_print_cstr(pctx, " ");
   2142 		}
   2143 	}
   2144 }
   2145 
   2146 bool
   2147 cfg_obj_islist(const cfg_obj_t *obj) {
   2148 	REQUIRE(obj != NULL);
   2149 	return obj->type->rep == &cfg_rep_list;
   2150 }
   2151 
   2152 const cfg_listelt_t *
   2153 cfg_list_first(const cfg_obj_t *obj) {
   2154 	REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
   2155 	if (obj == NULL) {
   2156 		return NULL;
   2157 	}
   2158 	return ISC_LIST_HEAD(obj->value.list);
   2159 }
   2160 
   2161 const cfg_listelt_t *
   2162 cfg_list_next(const cfg_listelt_t *elt) {
   2163 	REQUIRE(elt != NULL);
   2164 	return ISC_LIST_NEXT(elt, link);
   2165 }
   2166 
   2167 /*
   2168  * Return the length of a list object.  If obj is NULL or is not
   2169  * a list, return 0.
   2170  */
   2171 unsigned int
   2172 cfg_list_length(const cfg_obj_t *obj, bool recurse) {
   2173 	const cfg_listelt_t *elt;
   2174 	unsigned int count = 0;
   2175 
   2176 	if (obj == NULL || !cfg_obj_islist(obj)) {
   2177 		return 0U;
   2178 	}
   2179 	for (elt = cfg_list_first(obj); elt != NULL; elt = cfg_list_next(elt)) {
   2180 		if (recurse && cfg_obj_islist(elt->obj)) {
   2181 			count += cfg_list_length(elt->obj, recurse);
   2182 		} else {
   2183 			count++;
   2184 		}
   2185 	}
   2186 	return count;
   2187 }
   2188 
   2189 cfg_obj_t *
   2190 cfg_listelt_value(const cfg_listelt_t *elt) {
   2191 	REQUIRE(elt != NULL);
   2192 	return elt->obj;
   2193 }
   2194 
   2195 /*
   2196  * Maps.
   2197  */
   2198 
   2199 /*
   2200  * Parse a map body.  That's something like
   2201  *
   2202  *   "foo 1; bar { glub; }; zap true; zap false;"
   2203  *
   2204  * i.e., a sequence of option names followed by values and
   2205  * terminated by semicolons.  Used for the top level of
   2206  * the named.conf syntax, as well as for the body of the
   2207  * options, view, zone, and other statements.
   2208  */
   2209 isc_result_t
   2210 cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
   2211 	const cfg_clausedef_t *const *clausesets;
   2212 	isc_result_t result;
   2213 	const cfg_clausedef_t *const *clauseset;
   2214 	const cfg_clausedef_t *clause;
   2215 	cfg_obj_t *value = NULL;
   2216 	cfg_obj_t *obj = NULL;
   2217 	cfg_obj_t *eltobj = NULL;
   2218 	cfg_obj_t *includename = NULL;
   2219 	isc_symvalue_t symval;
   2220 	cfg_list_t *list = NULL;
   2221 
   2222 	REQUIRE(pctx != NULL);
   2223 	REQUIRE(type != NULL);
   2224 	REQUIRE(ret != NULL && *ret == NULL);
   2225 
   2226 	clausesets = type->of;
   2227 
   2228 	CHECK(create_map(pctx, type, &obj));
   2229 
   2230 	obj->value.map.clausesets = clausesets;
   2231 
   2232 	for (;;) {
   2233 		cfg_listelt_t *elt;
   2234 
   2235 	redo:
   2236 		/*
   2237 		 * Parse the option name and see if it is known.
   2238 		 */
   2239 		CHECK(cfg_gettoken(pctx, 0));
   2240 
   2241 		if (pctx->token.type != isc_tokentype_string) {
   2242 			cfg_ungettoken(pctx);
   2243 			break;
   2244 		}
   2245 
   2246 		/*
   2247 		 * We accept "include" statements wherever a map body
   2248 		 * clause can occur.
   2249 		 */
   2250 		if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
   2251 			glob_t g;
   2252 			int rc;
   2253 
   2254 			/*
   2255 			 * Turn the file name into a temporary configuration
   2256 			 * object just so that it is not overwritten by the
   2257 			 * semicolon token.
   2258 			 */
   2259 			CHECK(cfg_parse_obj(pctx, &cfg_type_qstring,
   2260 					    &includename));
   2261 			CHECK(parse_semicolon(pctx));
   2262 
   2263 			if (includename->value.string.length == 0) {
   2264 				CHECK(ISC_R_FILENOTFOUND);
   2265 			}
   2266 
   2267 			/*
   2268 			 * Allow include to specify a pattern that follows
   2269 			 * the same rules as the shell e.g "/path/zone*.conf"
   2270 			 */
   2271 			rc = glob(cfg_obj_asstring(includename), GLOB_ERR, NULL,
   2272 				  &g);
   2273 
   2274 			switch (rc) {
   2275 			case 0:
   2276 				break;
   2277 			case GLOB_NOMATCH:
   2278 				CHECK(ISC_R_FILENOTFOUND);
   2279 				break;
   2280 			case GLOB_NOSPACE:
   2281 				CHECK(ISC_R_NOMEMORY);
   2282 				break;
   2283 			default:
   2284 				if (errno == 0) {
   2285 					CHECK(ISC_R_IOERROR);
   2286 				}
   2287 				CHECK(isc_errno_toresult(errno));
   2288 			}
   2289 
   2290 			for (size_t i = 0; i < g.gl_pathc; ++i) {
   2291 				CHECK(parser_openfile(pctx, g.gl_pathv[i]));
   2292 			}
   2293 
   2294 			cfg_obj_destroy(pctx, &includename);
   2295 			globfree(&g);
   2296 
   2297 			goto redo;
   2298 		}
   2299 
   2300 		clause = NULL;
   2301 		for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
   2302 			for (clause = *clauseset; clause->name != NULL;
   2303 			     clause++)
   2304 			{
   2305 				if (strcasecmp(TOKEN_STRING(pctx),
   2306 					       clause->name) == 0)
   2307 				{
   2308 					goto done;
   2309 				}
   2310 			}
   2311 		}
   2312 	done:
   2313 		if (clause == NULL || clause->name == NULL) {
   2314 			cfg_parser_error(pctx, CFG_LOG_NOPREP,
   2315 					 "unknown option");
   2316 			/*
   2317 			 * Try to recover by parsing this option as an unknown
   2318 			 * option and discarding it.
   2319 			 */
   2320 			CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported,
   2321 					    &eltobj));
   2322 			cfg_obj_destroy(pctx, &eltobj);
   2323 			CHECK(parse_semicolon(pctx));
   2324 			continue;
   2325 		}
   2326 
   2327 		/* Clause is known. */
   2328 
   2329 		/* Issue fatal errors if appropriate */
   2330 		if ((clause->flags & CFG_CLAUSEFLAG_ANCIENT) != 0) {
   2331 			cfg_parser_error(pctx, 0,
   2332 					 "option '%s' no longer exists",
   2333 					 clause->name);
   2334 			CHECK(ISC_R_FAILURE);
   2335 		}
   2336 		if ((clause->flags & CFG_CLAUSEFLAG_NOTCONFIGURED) != 0) {
   2337 			cfg_parser_error(pctx, 0,
   2338 					 "option '%s' was not "
   2339 					 "enabled at compile time",
   2340 					 clause->name);
   2341 			CHECK(ISC_R_FAILURE);
   2342 		}
   2343 
   2344 		/* Issue warnings if appropriate */
   2345 		if ((pctx->flags & CFG_PCTX_NODEPRECATED) == 0 &&
   2346 		    (clause->flags & CFG_CLAUSEFLAG_DEPRECATED) != 0)
   2347 		{
   2348 			cfg_parser_warning(pctx, 0, "option '%s' is deprecated",
   2349 					   clause->name);
   2350 		}
   2351 		if ((pctx->flags & CFG_PCTX_NOOBSOLETE) == 0 &&
   2352 		    (clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0)
   2353 		{
   2354 			cfg_parser_warning(pctx, 0,
   2355 					   "option '%s' is obsolete and "
   2356 					   "should be removed ",
   2357 					   clause->name);
   2358 		}
   2359 		if ((pctx->flags & CFG_PCTX_NOEXPERIMENTAL) == 0 &&
   2360 		    (clause->flags & CFG_CLAUSEFLAG_EXPERIMENTAL) != 0)
   2361 		{
   2362 			cfg_parser_warning(pctx, 0,
   2363 					   "option '%s' is experimental and "
   2364 					   "subject to change in the future",
   2365 					   clause->name);
   2366 		}
   2367 
   2368 		/* See if the clause already has a value; if not create one. */
   2369 		result = isc_symtab_lookup(obj->value.map.symtab, clause->name,
   2370 					   0, &symval);
   2371 
   2372 		if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
   2373 			/* Multivalued clause */
   2374 			cfg_obj_t *listobj = NULL;
   2375 			if (result == ISC_R_NOTFOUND) {
   2376 				CHECK(cfg_create_list(pctx,
   2377 						      &cfg_type_implicitlist,
   2378 						      &listobj));
   2379 				symval.as_pointer = listobj;
   2380 				result = isc_symtab_define(
   2381 					obj->value.map.symtab, clause->name, 1,
   2382 					symval, isc_symexists_reject);
   2383 				if (result != ISC_R_SUCCESS) {
   2384 					cfg_parser_error(pctx, CFG_LOG_NEAR,
   2385 							 "isc_symtab_define(%s)"
   2386 							 " "
   2387 							 "failed",
   2388 							 clause->name);
   2389 					isc_mem_put(pctx->mctx, list,
   2390 						    sizeof(cfg_list_t));
   2391 					goto cleanup;
   2392 				}
   2393 			} else {
   2394 				INSIST(result == ISC_R_SUCCESS);
   2395 				listobj = symval.as_pointer;
   2396 			}
   2397 
   2398 			elt = NULL;
   2399 			CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
   2400 			CHECK(parse_semicolon(pctx));
   2401 
   2402 			ISC_LIST_APPEND(listobj->value.list, elt, link);
   2403 		} else {
   2404 			/* Single-valued clause */
   2405 			if (result == ISC_R_NOTFOUND) {
   2406 				bool callback = ((clause->flags &
   2407 						  CFG_CLAUSEFLAG_CALLBACK) !=
   2408 						 0);
   2409 				CHECK(parse_symtab_elt(
   2410 					pctx, clause->name, clause->type,
   2411 					obj->value.map.symtab, callback));
   2412 				CHECK(parse_semicolon(pctx));
   2413 			} else if (result == ISC_R_SUCCESS) {
   2414 				cfg_parser_error(pctx, CFG_LOG_NEAR,
   2415 						 "'%s' redefined",
   2416 						 clause->name);
   2417 				result = ISC_R_EXISTS;
   2418 				goto cleanup;
   2419 			} else {
   2420 				cfg_parser_error(pctx, CFG_LOG_NEAR,
   2421 						 "isc_symtab_define() failed");
   2422 				goto cleanup;
   2423 			}
   2424 		}
   2425 	}
   2426 
   2427 	*ret = obj;
   2428 	return ISC_R_SUCCESS;
   2429 
   2430 cleanup:
   2431 	CLEANUP_OBJ(value);
   2432 	CLEANUP_OBJ(obj);
   2433 	CLEANUP_OBJ(eltobj);
   2434 	CLEANUP_OBJ(includename);
   2435 	return result;
   2436 }
   2437 
   2438 static isc_result_t
   2439 parse_symtab_elt(cfg_parser_t *pctx, const char *name, cfg_type_t *elttype,
   2440 		 isc_symtab_t *symtab, bool callback) {
   2441 	isc_result_t result;
   2442 	cfg_obj_t *obj = NULL;
   2443 	isc_symvalue_t symval;
   2444 
   2445 	CHECK(cfg_parse_obj(pctx, elttype, &obj));
   2446 
   2447 	if (callback && pctx->callback != NULL) {
   2448 		CHECK(pctx->callback(name, obj, pctx->callbackarg));
   2449 	}
   2450 
   2451 	symval.as_pointer = obj;
   2452 	CHECK(isc_symtab_define(symtab, name, 1, symval, isc_symexists_reject));
   2453 	return ISC_R_SUCCESS;
   2454 
   2455 cleanup:
   2456 	CLEANUP_OBJ(obj);
   2457 	return result;
   2458 }
   2459 
   2460 /*
   2461  * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
   2462  */
   2463 isc_result_t
   2464 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
   2465 	isc_result_t result;
   2466 
   2467 	REQUIRE(pctx != NULL);
   2468 	REQUIRE(type != NULL);
   2469 	REQUIRE(ret != NULL && *ret == NULL);
   2470 
   2471 	CHECK(cfg_parse_special(pctx, '{'));
   2472 	CHECK(cfg_parse_mapbody(pctx, type, ret));
   2473 	CHECK(cfg_parse_special(pctx, '}'));
   2474 cleanup:
   2475 	return result;
   2476 }
   2477 
   2478 /*
   2479  * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
   2480  */
   2481 static isc_result_t
   2482 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype,
   2483 		    const cfg_type_t *type, cfg_obj_t **ret) {
   2484 	isc_result_t result;
   2485 	cfg_obj_t *idobj = NULL;
   2486 	cfg_obj_t *mapobj = NULL;
   2487 
   2488 	REQUIRE(pctx != NULL);
   2489 	REQUIRE(nametype != NULL);
   2490 	REQUIRE(type != NULL);
   2491 	REQUIRE(ret != NULL && *ret == NULL);
   2492 
   2493 	CHECK(cfg_parse_obj(pctx, nametype, &idobj));
   2494 	CHECK(cfg_parse_map(pctx, type, &mapobj));
   2495 	mapobj->value.map.id = idobj;
   2496 	*ret = mapobj;
   2497 	return result;
   2498 cleanup:
   2499 	CLEANUP_OBJ(idobj);
   2500 	CLEANUP_OBJ(mapobj);
   2501 	return result;
   2502 }
   2503 
   2504 /*
   2505  * Parse a map identified by a string name.  E.g., "name { foo 1; }".
   2506  * Used for the "key" and "channel" statements.
   2507  */
   2508 isc_result_t
   2509 cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type,
   2510 		    cfg_obj_t **ret) {
   2511 	return parse_any_named_map(pctx, &cfg_type_astring, type, ret);
   2512 }
   2513 
   2514 /*
   2515  * Parse a map identified by a network address.
   2516  * Used to be used for the "server" statement.
   2517  */
   2518 isc_result_t
   2519 cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type,
   2520 			cfg_obj_t **ret) {
   2521 	return parse_any_named_map(pctx, &cfg_type_netaddr, type, ret);
   2522 }
   2523 
   2524 /*
   2525  * Parse a map identified by a network prefix.
   2526  * Used for the "server" statement.
   2527  */
   2528 isc_result_t
   2529 cfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type,
   2530 			cfg_obj_t **ret) {
   2531 	return parse_any_named_map(pctx, &cfg_type_netprefix, type, ret);
   2532 }
   2533 
   2534 static void
   2535 print_symval(cfg_printer_t *pctx, const char *name, cfg_obj_t *obj) {
   2536 	if ((pctx->flags & CFG_PRINTER_ONELINE) == 0) {
   2537 		cfg_print_indent(pctx);
   2538 	}
   2539 
   2540 	cfg_print_cstr(pctx, name);
   2541 	cfg_print_cstr(pctx, " ");
   2542 	cfg_print_obj(pctx, obj);
   2543 
   2544 	if ((pctx->flags & CFG_PRINTER_ONELINE) == 0) {
   2545 		cfg_print_cstr(pctx, ";\n");
   2546 	} else {
   2547 		cfg_print_cstr(pctx, "; ");
   2548 	}
   2549 }
   2550 
   2551 void
   2552 cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) {
   2553 	const cfg_clausedef_t *const *clauseset;
   2554 
   2555 	REQUIRE(pctx != NULL);
   2556 	REQUIRE(obj != NULL);
   2557 
   2558 	for (clauseset = obj->value.map.clausesets; *clauseset != NULL;
   2559 	     clauseset++)
   2560 	{
   2561 		isc_symvalue_t symval;
   2562 		const cfg_clausedef_t *clause;
   2563 
   2564 		for (clause = *clauseset; clause->name != NULL; clause++) {
   2565 			isc_result_t result;
   2566 			result = isc_symtab_lookup(obj->value.map.symtab,
   2567 						   clause->name, 0, &symval);
   2568 			if (result == ISC_R_SUCCESS) {
   2569 				cfg_obj_t *symobj = symval.as_pointer;
   2570 				if (symobj->type == &cfg_type_implicitlist) {
   2571 					/* Multivalued. */
   2572 					cfg_list_t *list = &symobj->value.list;
   2573 					cfg_listelt_t *elt;
   2574 					for (elt = ISC_LIST_HEAD(*list);
   2575 					     elt != NULL;
   2576 					     elt = ISC_LIST_NEXT(elt, link))
   2577 					{
   2578 						print_symval(pctx, clause->name,
   2579 							     elt->obj);
   2580 					}
   2581 				} else {
   2582 					/* Single-valued. */
   2583 					print_symval(pctx, clause->name,
   2584 						     symobj);
   2585 				}
   2586 			} else if (result == ISC_R_NOTFOUND) {
   2587 				/* do nothing */
   2588 			} else {
   2589 				UNREACHABLE();
   2590 			}
   2591 		}
   2592 	}
   2593 }
   2594 
   2595 static struct flagtext {
   2596 	unsigned int flag;
   2597 	const char *text;
   2598 } flagtexts[] = { { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
   2599 		  { CFG_CLAUSEFLAG_TESTONLY, "test only" },
   2600 		  { CFG_CLAUSEFLAG_NOTCONFIGURED, "not configured" },
   2601 		  { CFG_CLAUSEFLAG_MULTI, "may occur multiple times" },
   2602 		  { CFG_CLAUSEFLAG_EXPERIMENTAL, "experimental" },
   2603 		  { CFG_CLAUSEFLAG_DEPRECATED, "deprecated" },
   2604 		  { CFG_CLAUSEFLAG_ANCIENT, "ancient" },
   2605 		  { 0, NULL } };
   2606 
   2607 void
   2608 cfg_print_clauseflags(cfg_printer_t *pctx, unsigned int flags) {
   2609 	struct flagtext *p;
   2610 	bool first = true;
   2611 	for (p = flagtexts; p->flag != 0; p++) {
   2612 		if ((flags & p->flag) != 0) {
   2613 			if (first) {
   2614 				cfg_print_cstr(pctx, " // ");
   2615 			} else {
   2616 				cfg_print_cstr(pctx, ", ");
   2617 			}
   2618 			cfg_print_cstr(pctx, p->text);
   2619 			first = false;
   2620 		}
   2621 	}
   2622 }
   2623 
   2624 void
   2625 cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
   2626 	const cfg_clausedef_t *const *clauseset;
   2627 	const cfg_clausedef_t *clause;
   2628 
   2629 	REQUIRE(pctx != NULL);
   2630 	REQUIRE(type != NULL);
   2631 
   2632 	for (clauseset = type->of; *clauseset != NULL; clauseset++) {
   2633 		for (clause = *clauseset; clause->name != NULL; clause++) {
   2634 			if (((pctx->flags & CFG_PRINTER_ACTIVEONLY) != 0) &&
   2635 			    (((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0) ||
   2636 			     ((clause->flags & CFG_CLAUSEFLAG_TESTONLY) != 0)))
   2637 			{
   2638 				continue;
   2639 			}
   2640 			if ((clause->flags & CFG_CLAUSEFLAG_ANCIENT) != 0 ||
   2641 			    (clause->flags & CFG_CLAUSEFLAG_NODOC) != 0)
   2642 			{
   2643 				continue;
   2644 			}
   2645 			cfg_print_cstr(pctx, clause->name);
   2646 			cfg_print_cstr(pctx, " ");
   2647 			cfg_doc_obj(pctx, clause->type);
   2648 			cfg_print_cstr(pctx, ";");
   2649 			cfg_print_clauseflags(pctx, clause->flags);
   2650 			cfg_print_cstr(pctx, "\n\n");
   2651 		}
   2652 	}
   2653 }
   2654 
   2655 void
   2656 cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) {
   2657 	REQUIRE(pctx != NULL);
   2658 	REQUIRE(obj != NULL);
   2659 
   2660 	if (obj->value.map.id != NULL) {
   2661 		cfg_print_obj(pctx, obj->value.map.id);
   2662 		cfg_print_cstr(pctx, " ");
   2663 	}
   2664 	print_open(pctx);
   2665 	cfg_print_mapbody(pctx, obj);
   2666 	print_close(pctx);
   2667 }
   2668 
   2669 void
   2670 cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
   2671 	const cfg_clausedef_t *const *clauseset;
   2672 	const cfg_clausedef_t *clause;
   2673 
   2674 	REQUIRE(pctx != NULL);
   2675 	REQUIRE(type != NULL);
   2676 
   2677 	if (type->parse == cfg_parse_named_map) {
   2678 		cfg_doc_obj(pctx, &cfg_type_astring);
   2679 		cfg_print_cstr(pctx, " ");
   2680 	} else if (type->parse == cfg_parse_addressed_map) {
   2681 		cfg_doc_obj(pctx, &cfg_type_netaddr);
   2682 		cfg_print_cstr(pctx, " ");
   2683 	} else if (type->parse == cfg_parse_netprefix_map) {
   2684 		cfg_doc_obj(pctx, &cfg_type_netprefix);
   2685 		cfg_print_cstr(pctx, " ");
   2686 	}
   2687 
   2688 	print_open(pctx);
   2689 
   2690 	for (clauseset = type->of; *clauseset != NULL; clauseset++) {
   2691 		for (clause = *clauseset; clause->name != NULL; clause++) {
   2692 			if (((pctx->flags & CFG_PRINTER_ACTIVEONLY) != 0) &&
   2693 			    (((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0) ||
   2694 			     ((clause->flags & CFG_CLAUSEFLAG_TESTONLY) != 0)))
   2695 			{
   2696 				continue;
   2697 			}
   2698 			if ((clause->flags & CFG_CLAUSEFLAG_ANCIENT) != 0 ||
   2699 			    (clause->flags & CFG_CLAUSEFLAG_NODOC) != 0)
   2700 			{
   2701 				continue;
   2702 			}
   2703 			cfg_print_indent(pctx);
   2704 			cfg_print_cstr(pctx, clause->name);
   2705 			if (clause->type->print != cfg_print_void) {
   2706 				cfg_print_cstr(pctx, " ");
   2707 			}
   2708 			cfg_doc_obj(pctx, clause->type);
   2709 			cfg_print_cstr(pctx, ";");
   2710 			cfg_print_clauseflags(pctx, clause->flags);
   2711 			cfg_print_cstr(pctx, "\n");
   2712 		}
   2713 	}
   2714 	print_close(pctx);
   2715 }
   2716 
   2717 bool
   2718 cfg_obj_ismap(const cfg_obj_t *obj) {
   2719 	REQUIRE(obj != NULL);
   2720 	return obj->type->rep == &cfg_rep_map;
   2721 }
   2722 
   2723 isc_result_t
   2724 cfg_map_get(const cfg_obj_t *mapobj, const char *name, const cfg_obj_t **obj) {
   2725 	isc_result_t result;
   2726 	isc_symvalue_t val;
   2727 	const cfg_map_t *map;
   2728 
   2729 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
   2730 	REQUIRE(name != NULL);
   2731 	REQUIRE(obj != NULL && *obj == NULL);
   2732 
   2733 	map = &mapobj->value.map;
   2734 
   2735 	result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
   2736 	if (result != ISC_R_SUCCESS) {
   2737 		return result;
   2738 	}
   2739 	*obj = val.as_pointer;
   2740 	return ISC_R_SUCCESS;
   2741 }
   2742 
   2743 const cfg_obj_t *
   2744 cfg_map_getname(const cfg_obj_t *mapobj) {
   2745 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
   2746 	return mapobj->value.map.id;
   2747 }
   2748 
   2749 unsigned int
   2750 cfg_map_count(const cfg_obj_t *mapobj) {
   2751 	const cfg_map_t *map;
   2752 
   2753 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
   2754 
   2755 	map = &mapobj->value.map;
   2756 	return isc_symtab_count(map->symtab);
   2757 }
   2758 
   2759 const char *
   2760 cfg_map_firstclause(const cfg_type_t *map, const void **clauses,
   2761 		    unsigned int *idx) {
   2762 	cfg_clausedef_t *const *clauseset;
   2763 
   2764 	REQUIRE(map != NULL && map->rep == &cfg_rep_map);
   2765 	REQUIRE(idx != NULL);
   2766 	REQUIRE(clauses != NULL && *clauses == NULL);
   2767 
   2768 	clauseset = map->of;
   2769 	if (*clauseset == NULL) {
   2770 		return NULL;
   2771 	}
   2772 	*clauses = *clauseset;
   2773 	*idx = 0;
   2774 	while ((*clauseset)[*idx].name == NULL) {
   2775 		*clauses = (*++clauseset);
   2776 		if (*clauses == NULL) {
   2777 			return NULL;
   2778 		}
   2779 	}
   2780 	return (*clauseset)[*idx].name;
   2781 }
   2782 
   2783 const char *
   2784 cfg_map_nextclause(const cfg_type_t *map, const void **clauses,
   2785 		   unsigned int *idx) {
   2786 	cfg_clausedef_t *const *clauseset;
   2787 
   2788 	REQUIRE(map != NULL && map->rep == &cfg_rep_map);
   2789 	REQUIRE(idx != NULL);
   2790 	REQUIRE(clauses != NULL && *clauses != NULL);
   2791 
   2792 	clauseset = map->of;
   2793 	while (*clauseset != NULL && *clauseset != *clauses) {
   2794 		clauseset++;
   2795 	}
   2796 	INSIST(*clauseset == *clauses);
   2797 	(*idx)++;
   2798 	while ((*clauseset)[*idx].name == NULL) {
   2799 		*idx = 0;
   2800 		*clauses = (*++clauseset);
   2801 		if (*clauses == NULL) {
   2802 			return NULL;
   2803 		}
   2804 	}
   2805 	return (*clauseset)[*idx].name;
   2806 }
   2807 
   2808 /* Parse an arbitrary token, storing its raw text representation. */
   2809 static isc_result_t
   2810 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
   2811 	cfg_obj_t *obj = NULL;
   2812 	isc_result_t result;
   2813 	isc_region_t r;
   2814 
   2815 	UNUSED(type);
   2816 
   2817 	CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
   2818 	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
   2819 	if (pctx->token.type == isc_tokentype_eof) {
   2820 		cfg_ungettoken(pctx);
   2821 		result = ISC_R_EOF;
   2822 		goto cleanup;
   2823 	}
   2824 
   2825 	isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
   2826 
   2827 	obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
   2828 	obj->value.string.length = r.length;
   2829 	memmove(obj->value.string.base, r.base, r.length);
   2830 	obj->value.string.base[r.length] = '\0';
   2831 	*ret = obj;
   2832 	return result;
   2833 
   2834 cleanup:
   2835 	if (obj != NULL) {
   2836 		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
   2837 	}
   2838 	return result;
   2839 }
   2840 
   2841 cfg_type_t cfg_type_token = { "token",		 parse_token,
   2842 			      cfg_print_ustring, cfg_doc_terminal,
   2843 			      &cfg_rep_string,	 NULL };
   2844 
   2845 /*
   2846  * An unsupported option.  This is just a list of tokens with balanced braces
   2847  * ending in a semicolon.
   2848  */
   2849 
   2850 static isc_result_t
   2851 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
   2852 	cfg_obj_t *listobj = NULL;
   2853 	isc_result_t result;
   2854 	int braces = 0;
   2855 
   2856 	CHECK(cfg_create_list(pctx, type, &listobj));
   2857 
   2858 	for (;;) {
   2859 		cfg_listelt_t *elt = NULL;
   2860 
   2861 		CHECK(cfg_peektoken(pctx, 0));
   2862 		if (pctx->token.type == isc_tokentype_special) {
   2863 			if (pctx->token.value.as_char == '{') {
   2864 				braces++;
   2865 			} else if (pctx->token.value.as_char == '}') {
   2866 				braces--;
   2867 			} else if (pctx->token.value.as_char == ';') {
   2868 				if (braces == 0) {
   2869 					break;
   2870 				}
   2871 			}
   2872 		}
   2873 		if (pctx->token.type == isc_tokentype_eof || braces < 0) {
   2874 			cfg_parser_error(pctx, CFG_LOG_NEAR,
   2875 					 "unexpected token");
   2876 			result = ISC_R_UNEXPECTEDTOKEN;
   2877 			goto cleanup;
   2878 		}
   2879 
   2880 		CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
   2881 		ISC_LIST_APPEND(listobj->value.list, elt, link);
   2882 	}
   2883 	INSIST(braces == 0);
   2884 	*ret = listobj;
   2885 	return ISC_R_SUCCESS;
   2886 
   2887 cleanup:
   2888 	CLEANUP_OBJ(listobj);
   2889 	return result;
   2890 }
   2891 
   2892 cfg_type_t cfg_type_unsupported = { "unsupported",	 parse_unsupported,
   2893 				    cfg_print_spacelist, cfg_doc_terminal,
   2894 				    &cfg_rep_list,	 NULL };
   2895 
   2896 /*
   2897  * Try interpreting the current token as a network address.
   2898  *
   2899  * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
   2900  * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set.  The
   2901  * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
   2902  * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
   2903  * and the IPv6 wildcard address otherwise.
   2904  */
   2905 static isc_result_t
   2906 token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
   2907 	char *s;
   2908 	struct in_addr in4a;
   2909 	struct in6_addr in6a;
   2910 
   2911 	if (pctx->token.type != isc_tokentype_string) {
   2912 		return ISC_R_UNEXPECTEDTOKEN;
   2913 	}
   2914 
   2915 	s = TOKEN_STRING(pctx);
   2916 	if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
   2917 		if ((flags & CFG_ADDR_V4OK) != 0) {
   2918 			isc_netaddr_any(na);
   2919 			return ISC_R_SUCCESS;
   2920 		} else if ((flags & CFG_ADDR_V6OK) != 0) {
   2921 			isc_netaddr_any6(na);
   2922 			return ISC_R_SUCCESS;
   2923 		} else {
   2924 			UNREACHABLE();
   2925 		}
   2926 	} else {
   2927 		if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
   2928 			if (inet_pton(AF_INET, s, &in4a) == 1) {
   2929 				isc_netaddr_fromin(na, &in4a);
   2930 				return ISC_R_SUCCESS;
   2931 			}
   2932 		}
   2933 		if ((flags & CFG_ADDR_V4PREFIXOK) != 0 && strlen(s) <= 15U) {
   2934 			char buf[64];
   2935 			int i;
   2936 
   2937 			strlcpy(buf, s, sizeof(buf));
   2938 			for (i = 0; i < 3; i++) {
   2939 				strlcat(buf, ".0", sizeof(buf));
   2940 				if (inet_pton(AF_INET, buf, &in4a) == 1) {
   2941 					isc_netaddr_fromin(na, &in4a);
   2942 					return ISC_R_IPV4PREFIX;
   2943 				}
   2944 			}
   2945 		}
   2946 		if ((flags & CFG_ADDR_V6OK) != 0 && strlen(s) <= 127U) {
   2947 			char buf[128];	   /* see isc_getaddresses() */
   2948 			char *d;	   /* zone delimiter */
   2949 			uint32_t zone = 0; /* scope zone ID */
   2950 
   2951 			strlcpy(buf, s, sizeof(buf));
   2952 			d = strchr(buf, '%');
   2953 			if (d != NULL) {
   2954 				*d = '\0';
   2955 			}
   2956 
   2957 			if (inet_pton(AF_INET6, buf, &in6a) == 1) {
   2958 				if (d != NULL) {
   2959 					isc_result_t result;
   2960 
   2961 					result = isc_netscope_pton(
   2962 						AF_INET6, d + 1, &in6a, &zone);
   2963 					if (result != ISC_R_SUCCESS) {
   2964 						return result;
   2965 					}
   2966 				}
   2967 
   2968 				isc_netaddr_fromin6(na, &in6a);
   2969 				isc_netaddr_setzone(na, zone);
   2970 				return ISC_R_SUCCESS;
   2971 			}
   2972 		}
   2973 	}
   2974 	return ISC_R_UNEXPECTEDTOKEN;
   2975 }
   2976 
   2977 isc_result_t
   2978 cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
   2979 	isc_result_t result;
   2980 	const char *wild = "";
   2981 	const char *prefix = "";
   2982 
   2983 	REQUIRE(pctx != NULL);
   2984 	REQUIRE(na != NULL);
   2985 
   2986 	CHECK(cfg_gettoken(pctx, 0));
   2987 	result = token_addr(pctx, flags, na);
   2988 	if (result == ISC_R_UNEXPECTEDTOKEN) {
   2989 		if ((flags & CFG_ADDR_WILDOK) != 0) {
   2990 			wild = " or '*'";
   2991 		}
   2992 		if ((flags & CFG_ADDR_V4PREFIXOK) != 0) {
   2993 			wild = " or IPv4 prefix";
   2994 		}
   2995 		if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V4OK) {
   2996 			cfg_parser_error(pctx, CFG_LOG_NEAR,
   2997 					 "expected IPv4 address%s%s", prefix,
   2998 					 wild);
   2999 		} else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK) {
   3000 			cfg_parser_error(pctx, CFG_LOG_NEAR,
   3001 					 "expected IPv6 address%s%s", prefix,
   3002 					 wild);
   3003 		} else {
   3004 			cfg_parser_error(pctx, CFG_LOG_NEAR,
   3005 					 "expected IP address%s%s", prefix,
   3006 					 wild);
   3007 		}
   3008 	}
   3009 cleanup:
   3010 	return result;
   3011 }
   3012 
   3013 bool
   3014 cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
   3015 	isc_result_t result;
   3016 	isc_netaddr_t na_dummy;
   3017 
   3018 	REQUIRE(pctx != NULL);
   3019 
   3020 	result = token_addr(pctx, flags, &na_dummy);
   3021 	return result == ISC_R_SUCCESS || result == ISC_R_IPV4PREFIX;
   3022 }
   3023 
   3024 isc_result_t
   3025 cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
   3026 	isc_result_t result;
   3027 
   3028 	REQUIRE(pctx != NULL);
   3029 	REQUIRE(port != NULL);
   3030 
   3031 	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
   3032 
   3033 	if ((flags & CFG_ADDR_WILDOK) != 0 &&
   3034 	    pctx->token.type == isc_tokentype_string &&
   3035 	    strcmp(TOKEN_STRING(pctx), "*") == 0)
   3036 	{
   3037 		*port = 0;
   3038 		return ISC_R_SUCCESS;
   3039 	}
   3040 	if (pctx->token.type != isc_tokentype_number) {
   3041 		cfg_parser_error(pctx, CFG_LOG_NEAR,
   3042 				 "expected port number or '*'");
   3043 		return ISC_R_UNEXPECTEDTOKEN;
   3044 	}
   3045 	if (pctx->token.value.as_ulong >= 65536U) {
   3046 		cfg_parser_error(pctx, CFG_LOG_NEAR,
   3047 				 "port number out of range");
   3048 		return ISC_R_UNEXPECTEDTOKEN;
   3049 	}
   3050 	*port = (in_port_t)(pctx->token.value.as_ulong);
   3051 	return ISC_R_SUCCESS;
   3052 cleanup:
   3053 	return result;
   3054 }
   3055 
   3056 void
   3057 cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
   3058 	isc_result_t result;
   3059 	char text[128];
   3060 	isc_buffer_t buf;
   3061 
   3062 	REQUIRE(pctx != NULL);
   3063 	REQUIRE(na != NULL);
   3064 
   3065 	isc_buffer_init(&buf, text, sizeof(text));
   3066 	result = isc_netaddr_totext(na, &buf);
   3067 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
   3068 	cfg_print_chars(pctx, isc_buffer_base(&buf),
   3069 			isc_buffer_usedlength(&buf));
   3070 }
   3071 
   3072 /* netaddr */
   3073 
   3074 static unsigned int netaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
   3075 static unsigned int netaddr4_flags = CFG_ADDR_V4OK;
   3076 static unsigned int netaddr4wild_flags = CFG_ADDR_V4OK | CFG_ADDR_WILDOK;
   3077 static unsigned int netaddr6_flags = CFG_ADDR_V6OK;
   3078 static unsigned int netaddr6wild_flags = CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
   3079 
   3080 static isc_result_t
   3081 parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
   3082 	isc_result_t result;
   3083 	cfg_obj_t *obj = NULL;
   3084 	isc_netaddr_t netaddr;
   3085 	unsigned int flags = *(const unsigned int *)type->of;
   3086 
   3087 	CHECK(cfg_create_obj(pctx, type, &obj));
   3088 	CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
   3089 	isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
   3090 	*ret = obj;
   3091 	return ISC_R_SUCCESS;
   3092 cleanup:
   3093 	CLEANUP_OBJ(obj);
   3094 	return result;
   3095 }
   3096 
   3097 static void
   3098 cfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
   3099 	const unsigned int *flagp = type->of;
   3100 	int n = 0;
   3101 	if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK) {
   3102 		cfg_print_cstr(pctx, "( ");
   3103 	}
   3104 	if ((*flagp & CFG_ADDR_V4OK) != 0) {
   3105 		cfg_print_cstr(pctx, "<ipv4_address>");
   3106 		n++;
   3107 	}
   3108 	if ((*flagp & CFG_ADDR_V6OK) != 0) {
   3109 		if (n != 0) {
   3110 			cfg_print_cstr(pctx, " | ");
   3111 		}
   3112 		cfg_print_cstr(pctx, "<ipv6_address>");
   3113 		n++;
   3114 	}
   3115 	if ((*flagp & CFG_ADDR_WILDOK) != 0) {
   3116 		if (n != 0) {
   3117 			cfg_print_cstr(pctx, " | ");
   3118 		}
   3119 		cfg_print_cstr(pctx, "*");
   3120 		n++;
   3121 		POST(n);
   3122 	}
   3123 	if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK) {
   3124 		cfg_print_cstr(pctx, " )");
   3125 	}
   3126 }
   3127 
   3128 cfg_type_t cfg_type_netaddr = { "netaddr",	    parse_netaddr,
   3129 				cfg_print_sockaddr, cfg_doc_netaddr,
   3130 				&cfg_rep_sockaddr,  &netaddr_flags };
   3131 
   3132 cfg_type_t cfg_type_netaddr4 = { "netaddr4",	     parse_netaddr,
   3133 				 cfg_print_sockaddr, cfg_doc_netaddr,
   3134 				 &cfg_rep_sockaddr,  &netaddr4_flags };
   3135 
   3136 cfg_type_t cfg_type_netaddr4wild = { "netaddr4wild",	 parse_netaddr,
   3137 				     cfg_print_sockaddr, cfg_doc_netaddr,
   3138 				     &cfg_rep_sockaddr,	 &netaddr4wild_flags };
   3139 
   3140 cfg_type_t cfg_type_netaddr6 = { "netaddr6",	     parse_netaddr,
   3141 				 cfg_print_sockaddr, cfg_doc_netaddr,
   3142 				 &cfg_rep_sockaddr,  &netaddr6_flags };
   3143 
   3144 cfg_type_t cfg_type_netaddr6wild = { "netaddr6wild",	 parse_netaddr,
   3145 				     cfg_print_sockaddr, cfg_doc_netaddr,
   3146 				     &cfg_rep_sockaddr,	 &netaddr6wild_flags };
   3147 
   3148 /* netprefix */
   3149 
   3150 isc_result_t
   3151 cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
   3152 		    cfg_obj_t **ret) {
   3153 	cfg_obj_t *obj = NULL;
   3154 	isc_result_t result;
   3155 	isc_netaddr_t netaddr;
   3156 	unsigned int addrlen = 0, prefixlen;
   3157 	bool expectprefix;
   3158 
   3159 	REQUIRE(pctx != NULL);
   3160 	REQUIRE(ret != NULL && *ret == NULL);
   3161 
   3162 	UNUSED(type);
   3163 
   3164 	result = cfg_parse_rawaddr(
   3165 		pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK | CFG_ADDR_V6OK,
   3166 		&netaddr);
   3167 	if (result != ISC_R_SUCCESS && result != ISC_R_IPV4PREFIX) {
   3168 		CHECK(result);
   3169 	}
   3170 	switch (netaddr.family) {
   3171 	case AF_INET:
   3172 		addrlen = 32;
   3173 		break;
   3174 	case AF_INET6:
   3175 		addrlen = 128;
   3176 		break;
   3177 	default:
   3178 		UNREACHABLE();
   3179 	}
   3180 	expectprefix = (result == ISC_R_IPV4PREFIX);
   3181 	CHECK(cfg_peektoken(pctx, 0));
   3182 	if (pctx->token.type == isc_tokentype_special &&
   3183 	    pctx->token.value.as_char == '/')
   3184 	{
   3185 		CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
   3186 		CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
   3187 		if (pctx->token.type != isc_tokentype_number) {
   3188 			cfg_parser_error(pctx, CFG_LOG_NEAR,
   3189 					 "expected prefix length");
   3190 			return ISC_R_UNEXPECTEDTOKEN;
   3191 		}
   3192 		prefixlen = pctx->token.value.as_ulong;
   3193 		if (prefixlen > addrlen) {
   3194 			cfg_parser_error(pctx, CFG_LOG_NOPREP,
   3195 					 "invalid prefix length");
   3196 			return ISC_R_RANGE;
   3197 		}
   3198 		result = isc_netaddr_prefixok(&netaddr, prefixlen);
   3199 		if (result != ISC_R_SUCCESS) {
   3200 			char buf[ISC_NETADDR_FORMATSIZE + 1];
   3201 			isc_netaddr_format(&netaddr, buf, sizeof(buf));
   3202 			cfg_parser_error(pctx, CFG_LOG_NOPREP,
   3203 					 "'%s/%u': address/prefix length "
   3204 					 "mismatch",
   3205 					 buf, prefixlen);
   3206 			return ISC_R_FAILURE;
   3207 		}
   3208 	} else {
   3209 		if (expectprefix) {
   3210 			cfg_parser_error(pctx, CFG_LOG_NEAR,
   3211 					 "incomplete IPv4 address or prefix");
   3212 			return ISC_R_FAILURE;
   3213 		}
   3214 		prefixlen = addrlen;
   3215 	}
   3216 	CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
   3217 	obj->value.netprefix.address = netaddr;
   3218 	obj->value.netprefix.prefixlen = prefixlen;
   3219 	*ret = obj;
   3220 	return ISC_R_SUCCESS;
   3221 cleanup:
   3222 	cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
   3223 	return result;
   3224 }
   3225 
   3226 static void
   3227 print_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) {
   3228 	const cfg_netprefix_t *p = &obj->value.netprefix;
   3229 
   3230 	cfg_print_rawaddr(pctx, &p->address);
   3231 	cfg_print_cstr(pctx, "/");
   3232 	cfg_print_rawuint(pctx, p->prefixlen);
   3233 }
   3234 
   3235 bool
   3236 cfg_obj_isnetprefix(const cfg_obj_t *obj) {
   3237 	REQUIRE(obj != NULL);
   3238 	return obj->type->rep == &cfg_rep_netprefix;
   3239 }
   3240 
   3241 void
   3242 cfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr,
   3243 		    unsigned int *prefixlen) {
   3244 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
   3245 	REQUIRE(netaddr != NULL);
   3246 	REQUIRE(prefixlen != NULL);
   3247 
   3248 	*netaddr = obj->value.netprefix.address;
   3249 	*prefixlen = obj->value.netprefix.prefixlen;
   3250 }
   3251 
   3252 cfg_type_t cfg_type_netprefix = { "netprefix",	      cfg_parse_netprefix,
   3253 				  print_netprefix,    cfg_doc_terminal,
   3254 				  &cfg_rep_netprefix, NULL };
   3255 
   3256 static isc_result_t
   3257 parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type, int flags,
   3258 		  cfg_obj_t **ret) {
   3259 	isc_result_t result;
   3260 	isc_netaddr_t netaddr;
   3261 	in_port_t port = 0;
   3262 	cfg_obj_t *obj = NULL;
   3263 	int have_port = 0;
   3264 	int have_tls = 0;
   3265 
   3266 	CHECK(cfg_create_obj(pctx, type, &obj));
   3267 	CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
   3268 
   3269 	for (;;) {
   3270 		CHECK(cfg_peektoken(pctx, 0));
   3271 		if (pctx->token.type == isc_tokentype_string) {
   3272 			if (strcasecmp(TOKEN_STRING(pctx), "port") == 0) {
   3273 				if ((pctx->flags & CFG_PCTX_NODEPRECATED) ==
   3274 					    0 &&
   3275 				    (flags & CFG_ADDR_PORTOK) == 0)
   3276 				{
   3277 					cfg_parser_warning(
   3278 						pctx, 0,
   3279 						"token 'port' is deprecated");
   3280 				}
   3281 				CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
   3282 				CHECK(cfg_parse_rawport(pctx, flags, &port));
   3283 				++have_port;
   3284 			} else if ((flags & CFG_ADDR_TLSOK) != 0 &&
   3285 				   strcasecmp(TOKEN_STRING(pctx), "tls") == 0)
   3286 			{
   3287 				cfg_obj_t *tls = NULL;
   3288 
   3289 				CHECK(cfg_gettoken(pctx, 0)); /* read "tls" */
   3290 				CHECK(cfg_parse_astring(pctx, NULL, &tls));
   3291 				copy_string(pctx, tls,
   3292 					    &obj->value.sockaddrtls.tls);
   3293 				CLEANUP_OBJ(tls);
   3294 				++have_tls;
   3295 			} else {
   3296 				break;
   3297 			}
   3298 		} else {
   3299 			break;
   3300 		}
   3301 	}
   3302 
   3303 	if (have_port > 1) {
   3304 		cfg_parser_error(pctx, 0, "expected at most one port");
   3305 		result = ISC_R_UNEXPECTEDTOKEN;
   3306 		goto cleanup;
   3307 	}
   3308 	if (have_tls > 1) {
   3309 		cfg_parser_error(pctx, 0, "expected at most one tls");
   3310 		result = ISC_R_UNEXPECTEDTOKEN;
   3311 		goto cleanup;
   3312 	}
   3313 
   3314 	isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
   3315 	*ret = obj;
   3316 	return ISC_R_SUCCESS;
   3317 
   3318 cleanup:
   3319 	CLEANUP_OBJ(obj);
   3320 	return result;
   3321 }
   3322 
   3323 static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK |
   3324 				     CFG_ADDR_PORTOK;
   3325 cfg_type_t cfg_type_sockaddr = { "sockaddr",	     cfg_parse_sockaddr,
   3326 				 cfg_print_sockaddr, cfg_doc_sockaddr,
   3327 				 &cfg_rep_sockaddr,  &sockaddr_flags };
   3328 
   3329 static unsigned int sockaddrtls_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK |
   3330 					CFG_ADDR_PORTOK | CFG_ADDR_TLSOK;
   3331 cfg_type_t cfg_type_sockaddrtls = { "sockaddrtls",	  cfg_parse_sockaddrtls,
   3332 				    cfg_print_sockaddr,	  cfg_doc_sockaddr,
   3333 				    &cfg_rep_sockaddrtls, &sockaddrtls_flags };
   3334 
   3335 isc_result_t
   3336 cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type,
   3337 		   cfg_obj_t **ret) {
   3338 	const unsigned int *flagp;
   3339 
   3340 	REQUIRE(pctx != NULL);
   3341 	REQUIRE(type != NULL);
   3342 	REQUIRE(ret != NULL && *ret == NULL);
   3343 
   3344 	flagp = type->of;
   3345 
   3346 	return parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret);
   3347 }
   3348 
   3349 isc_result_t
   3350 cfg_parse_sockaddrtls(cfg_parser_t *pctx, const cfg_type_t *type,
   3351 		      cfg_obj_t **ret) {
   3352 	const unsigned int *flagp;
   3353 
   3354 	REQUIRE(pctx != NULL);
   3355 	REQUIRE(type != NULL);
   3356 	REQUIRE(ret != NULL && *ret == NULL);
   3357 
   3358 	flagp = type->of;
   3359 
   3360 	return parse_sockaddrsub(pctx, &cfg_type_sockaddrtls, *flagp, ret);
   3361 }
   3362 
   3363 void
   3364 cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) {
   3365 	isc_netaddr_t netaddr;
   3366 	in_port_t port;
   3367 	char buf[ISC_NETADDR_FORMATSIZE];
   3368 
   3369 	REQUIRE(pctx != NULL);
   3370 	REQUIRE(obj != NULL);
   3371 
   3372 	isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
   3373 	isc_netaddr_format(&netaddr, buf, sizeof(buf));
   3374 	cfg_print_cstr(pctx, buf);
   3375 	port = isc_sockaddr_getport(&obj->value.sockaddr);
   3376 	if (port != 0) {
   3377 		cfg_print_cstr(pctx, " port ");
   3378 		cfg_print_rawuint(pctx, port);
   3379 	}
   3380 	if (obj->value.sockaddrtls.tls.base != NULL) {
   3381 		cfg_print_cstr(pctx, " tls ");
   3382 		print_rawqstring(pctx, obj->value.sockaddrtls.tls);
   3383 	}
   3384 }
   3385 
   3386 void
   3387 cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
   3388 	const unsigned int *flagp;
   3389 	int n = 0;
   3390 
   3391 	REQUIRE(pctx != NULL);
   3392 	REQUIRE(type != NULL);
   3393 
   3394 	flagp = type->of;
   3395 
   3396 	cfg_print_cstr(pctx, "( ");
   3397 	if ((*flagp & CFG_ADDR_V4OK) != 0) {
   3398 		cfg_print_cstr(pctx, "<ipv4_address>");
   3399 		n++;
   3400 	}
   3401 	if ((*flagp & CFG_ADDR_V6OK) != 0) {
   3402 		if (n != 0) {
   3403 			cfg_print_cstr(pctx, " | ");
   3404 		}
   3405 		cfg_print_cstr(pctx, "<ipv6_address>");
   3406 		n++;
   3407 	}
   3408 	if ((*flagp & CFG_ADDR_WILDOK) != 0) {
   3409 		if (n != 0) {
   3410 			cfg_print_cstr(pctx, " | ");
   3411 		}
   3412 		cfg_print_cstr(pctx, "*");
   3413 		n++;
   3414 		POST(n);
   3415 	}
   3416 	cfg_print_cstr(pctx, " )");
   3417 	if ((*flagp & CFG_ADDR_PORTOK) != 0) {
   3418 		if ((*flagp & CFG_ADDR_WILDOK) != 0) {
   3419 			cfg_print_cstr(pctx, " [ port ( <integer> | * ) ]");
   3420 		} else {
   3421 			cfg_print_cstr(pctx, " [ port <integer> ]");
   3422 		}
   3423 	}
   3424 	if ((*flagp & CFG_ADDR_TLSOK) != 0) {
   3425 		cfg_print_cstr(pctx, " [ tls <string> ]");
   3426 	}
   3427 }
   3428 
   3429 bool
   3430 cfg_obj_issockaddr(const cfg_obj_t *obj) {
   3431 	REQUIRE(obj != NULL);
   3432 	return obj->type->rep == &cfg_rep_sockaddr;
   3433 }
   3434 
   3435 bool
   3436 cfg_obj_issockaddrtls(const cfg_obj_t *obj) {
   3437 	REQUIRE(obj != NULL);
   3438 	return obj->type->rep == &cfg_rep_sockaddrtls;
   3439 }
   3440 
   3441 const isc_sockaddr_t *
   3442 cfg_obj_assockaddr(const cfg_obj_t *obj) {
   3443 	REQUIRE(obj != NULL);
   3444 	REQUIRE(obj->type->rep == &cfg_rep_sockaddr ||
   3445 		obj->type->rep == &cfg_rep_sockaddrtls);
   3446 	return &obj->value.sockaddr;
   3447 }
   3448 
   3449 const char *
   3450 cfg_obj_getsockaddrtls(const cfg_obj_t *obj) {
   3451 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddrtls);
   3452 	return obj->value.sockaddrtls.tls.base;
   3453 }
   3454 
   3455 isc_result_t
   3456 cfg_gettoken(cfg_parser_t *pctx, int options) {
   3457 	isc_result_t result;
   3458 
   3459 	REQUIRE(pctx != NULL);
   3460 
   3461 	if (pctx->seen_eof) {
   3462 		return ISC_R_SUCCESS;
   3463 	}
   3464 
   3465 	options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
   3466 
   3467 redo:
   3468 	pctx->token.type = isc_tokentype_unknown;
   3469 	result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
   3470 	pctx->ungotten = false;
   3471 	pctx->line = isc_lex_getsourceline(pctx->lexer);
   3472 
   3473 	switch (result) {
   3474 	case ISC_R_SUCCESS:
   3475 		if (pctx->token.type == isc_tokentype_eof) {
   3476 			result = isc_lex_close(pctx->lexer);
   3477 			INSIST(result == ISC_R_NOMORE ||
   3478 			       result == ISC_R_SUCCESS);
   3479 
   3480 			if (isc_lex_getsourcename(pctx->lexer) != NULL) {
   3481 				/*
   3482 				 * Closed an included file, not the main file.
   3483 				 */
   3484 				cfg_listelt_t *elt;
   3485 				elt = ISC_LIST_TAIL(
   3486 					pctx->open_files->value.list);
   3487 				INSIST(elt != NULL);
   3488 				ISC_LIST_UNLINK(pctx->open_files->value.list,
   3489 						elt, link);
   3490 				ISC_LIST_APPEND(pctx->closed_files->value.list,
   3491 						elt, link);
   3492 				goto redo;
   3493 			}
   3494 			pctx->seen_eof = true;
   3495 		}
   3496 		break;
   3497 
   3498 	case ISC_R_NOSPACE:
   3499 		/* More understandable than "ran out of space". */
   3500 		cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
   3501 		break;
   3502 
   3503 	case ISC_R_IOERROR:
   3504 		cfg_parser_error(pctx, 0, "%s", isc_result_totext(result));
   3505 		break;
   3506 
   3507 	default:
   3508 		cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
   3509 				 isc_result_totext(result));
   3510 		break;
   3511 	}
   3512 	return result;
   3513 }
   3514 
   3515 void
   3516 cfg_ungettoken(cfg_parser_t *pctx) {
   3517 	REQUIRE(pctx != NULL);
   3518 
   3519 	if (pctx->seen_eof) {
   3520 		return;
   3521 	}
   3522 	isc_lex_ungettoken(pctx->lexer, &pctx->token);
   3523 	pctx->ungotten = true;
   3524 }
   3525 
   3526 isc_result_t
   3527 cfg_peektoken(cfg_parser_t *pctx, int options) {
   3528 	isc_result_t result;
   3529 
   3530 	REQUIRE(pctx != NULL);
   3531 
   3532 	CHECK(cfg_gettoken(pctx, options));
   3533 	cfg_ungettoken(pctx);
   3534 cleanup:
   3535 	return result;
   3536 }
   3537 
   3538 /*
   3539  * Get a string token, accepting both the quoted and the unquoted form.
   3540  * Log an error if the next token is not a string.
   3541  */
   3542 static isc_result_t
   3543 cfg_getstringtoken(cfg_parser_t *pctx) {
   3544 	isc_result_t result;
   3545 
   3546 	result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
   3547 	if (result != ISC_R_SUCCESS) {
   3548 		return result;
   3549 	}
   3550 
   3551 	if (pctx->token.type != isc_tokentype_string &&
   3552 	    pctx->token.type != isc_tokentype_qstring)
   3553 	{
   3554 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
   3555 		return ISC_R_UNEXPECTEDTOKEN;
   3556 	}
   3557 	return ISC_R_SUCCESS;
   3558 }
   3559 
   3560 void
   3561 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
   3562 	va_list args;
   3563 
   3564 	REQUIRE(pctx != NULL);
   3565 	REQUIRE(fmt != NULL);
   3566 
   3567 	va_start(args, fmt);
   3568 	parser_complain(pctx, false, flags, fmt, args);
   3569 	va_end(args);
   3570 	pctx->errors++;
   3571 }
   3572 
   3573 void
   3574 cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt,
   3575 		   ...) {
   3576 	va_list args;
   3577 
   3578 	REQUIRE(pctx != NULL);
   3579 	REQUIRE(fmt != NULL);
   3580 
   3581 	va_start(args, fmt);
   3582 	parser_complain(pctx, true, flags, fmt, args);
   3583 	va_end(args);
   3584 	pctx->warnings++;
   3585 }
   3586 
   3587 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
   3588 
   3589 static bool
   3590 have_current_file(cfg_parser_t *pctx) {
   3591 	cfg_listelt_t *elt;
   3592 	if (pctx->open_files == NULL) {
   3593 		return false;
   3594 	}
   3595 
   3596 	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
   3597 	if (elt == NULL) {
   3598 		return false;
   3599 	}
   3600 
   3601 	return true;
   3602 }
   3603 
   3604 static char *
   3605 current_file(cfg_parser_t *pctx) {
   3606 	static char none[] = "none";
   3607 	cfg_listelt_t *elt;
   3608 	cfg_obj_t *fileobj;
   3609 
   3610 	if (!have_current_file(pctx)) {
   3611 		return none;
   3612 	}
   3613 
   3614 	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
   3615 	if (elt == NULL) { /* shouldn't be possible, but... */
   3616 		return none;
   3617 	}
   3618 
   3619 	fileobj = elt->obj;
   3620 	INSIST(fileobj->type == &cfg_type_qstring);
   3621 	return fileobj->value.string.base;
   3622 }
   3623 
   3624 static void
   3625 parser_complain(cfg_parser_t *pctx, bool is_warning, unsigned int flags,
   3626 		const char *format, va_list args) {
   3627 	char tokenbuf[MAX_LOG_TOKEN + 10];
   3628 	static char where[PATH_MAX + 100];
   3629 	static char message[2048];
   3630 	int level = ISC_LOG_ERROR;
   3631 	const char *prep = "";
   3632 	size_t len;
   3633 
   3634 	if (is_warning) {
   3635 		level = ISC_LOG_WARNING;
   3636 	}
   3637 
   3638 	where[0] = '\0';
   3639 	if (have_current_file(pctx)) {
   3640 		snprintf(where, sizeof(where), "%s:%u: ", current_file(pctx),
   3641 			 pctx->line);
   3642 	} else if (pctx->buf_name != NULL) {
   3643 		snprintf(where, sizeof(where), "%s: ", pctx->buf_name);
   3644 	}
   3645 
   3646 	len = vsnprintf(message, sizeof(message), format, args);
   3647 #define ELLIPSIS " ... "
   3648 	if (len >= sizeof(message)) {
   3649 		message[sizeof(message) - sizeof(ELLIPSIS)] = 0;
   3650 		strlcat(message, ELLIPSIS, sizeof(message));
   3651 	}
   3652 
   3653 	if ((flags & (CFG_LOG_NEAR | CFG_LOG_BEFORE | CFG_LOG_NOPREP)) != 0) {
   3654 		isc_region_t r;
   3655 
   3656 		if (pctx->ungotten) {
   3657 			(void)cfg_gettoken(pctx, 0);
   3658 		}
   3659 
   3660 		if (pctx->token.type == isc_tokentype_eof) {
   3661 			snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
   3662 		} else if (pctx->token.type == isc_tokentype_unknown) {
   3663 			flags = 0;
   3664 			tokenbuf[0] = '\0';
   3665 		} else {
   3666 			isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
   3667 			if (r.length > MAX_LOG_TOKEN) {
   3668 				snprintf(tokenbuf, sizeof(tokenbuf),
   3669 					 "'%.*s...'", MAX_LOG_TOKEN, r.base);
   3670 			} else {
   3671 				snprintf(tokenbuf, sizeof(tokenbuf), "'%.*s'",
   3672 					 (int)r.length, r.base);
   3673 			}
   3674 		}
   3675 
   3676 		/* Choose a preposition. */
   3677 		if ((flags & CFG_LOG_NEAR) != 0) {
   3678 			prep = " near ";
   3679 		} else if ((flags & CFG_LOG_BEFORE) != 0) {
   3680 			prep = " before ";
   3681 		} else {
   3682 			prep = " ";
   3683 		}
   3684 	} else {
   3685 		tokenbuf[0] = '\0';
   3686 	}
   3687 	isc_log_write(pctx->lctx, CAT, MOD, level, "%s%s%s%s", where, message,
   3688 		      prep, tokenbuf);
   3689 }
   3690 
   3691 void
   3692 cfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level, const char *fmt,
   3693 	    ...) {
   3694 	va_list ap;
   3695 	char msgbuf[2048];
   3696 
   3697 	REQUIRE(obj != NULL);
   3698 	REQUIRE(fmt != NULL);
   3699 
   3700 	if (!isc_log_wouldlog(lctx, level)) {
   3701 		return;
   3702 	}
   3703 
   3704 	va_start(ap, fmt);
   3705 	vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
   3706 	va_end(ap);
   3707 
   3708 	if (obj->file != NULL) {
   3709 		isc_log_write(lctx, CAT, MOD, level, "%s:%u: %s", obj->file,
   3710 			      obj->line, msgbuf);
   3711 	} else {
   3712 		isc_log_write(lctx, CAT, MOD, level, "%s", msgbuf);
   3713 	}
   3714 }
   3715 
   3716 const char *
   3717 cfg_obj_file(const cfg_obj_t *obj) {
   3718 	REQUIRE(obj != NULL);
   3719 
   3720 	return obj->file;
   3721 }
   3722 
   3723 unsigned int
   3724 cfg_obj_line(const cfg_obj_t *obj) {
   3725 	REQUIRE(obj != NULL);
   3726 
   3727 	return obj->line;
   3728 }
   3729 
   3730 isc_result_t
   3731 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
   3732 	cfg_obj_t *obj;
   3733 
   3734 	REQUIRE(pctx != NULL);
   3735 	REQUIRE(type != NULL);
   3736 	REQUIRE(ret != NULL && *ret == NULL);
   3737 
   3738 	obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
   3739 	*obj = (cfg_obj_t){ .type = type,
   3740 			    .file = current_file(pctx),
   3741 			    .line = pctx->line,
   3742 			    .pctx = pctx };
   3743 	isc_refcount_init(&obj->references, 1);
   3744 
   3745 	*ret = obj;
   3746 
   3747 	return ISC_R_SUCCESS;
   3748 }
   3749 
   3750 static void
   3751 map_symtabitem_destroy(char *key, unsigned int type, isc_symvalue_t symval,
   3752 		       void *userarg) {
   3753 	cfg_obj_t *obj = symval.as_pointer;
   3754 	cfg_parser_t *pctx = (cfg_parser_t *)userarg;
   3755 
   3756 	UNUSED(key);
   3757 	UNUSED(type);
   3758 
   3759 	cfg_obj_destroy(pctx, &obj);
   3760 }
   3761 
   3762 static isc_result_t
   3763 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
   3764 	isc_result_t result;
   3765 	isc_symtab_t *symtab = NULL;
   3766 	cfg_obj_t *obj = NULL;
   3767 
   3768 	CHECK(cfg_create_obj(pctx, type, &obj));
   3769 	CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */
   3770 				map_symtabitem_destroy, pctx, false, &symtab));
   3771 	obj->value.map.symtab = symtab;
   3772 	obj->value.map.id = NULL;
   3773 
   3774 	*ret = obj;
   3775 	return ISC_R_SUCCESS;
   3776 
   3777 cleanup:
   3778 	if (obj != NULL) {
   3779 		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
   3780 	}
   3781 	return result;
   3782 }
   3783 
   3784 static void
   3785 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
   3786 	CLEANUP_OBJ(obj->value.map.id);
   3787 	isc_symtab_destroy(&obj->value.map.symtab);
   3788 }
   3789 
   3790 bool
   3791 cfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type) {
   3792 	REQUIRE(obj != NULL);
   3793 	REQUIRE(type != NULL);
   3794 
   3795 	return obj->type == type;
   3796 }
   3797 
   3798 /*
   3799  * Destroy 'obj', a configuration object created in 'pctx'.
   3800  */
   3801 void
   3802 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
   3803 	REQUIRE(objp != NULL && *objp != NULL);
   3804 	REQUIRE(pctx != NULL);
   3805 
   3806 	cfg_obj_t *obj = *objp;
   3807 	*objp = NULL;
   3808 
   3809 	if (isc_refcount_decrement(&obj->references) == 1) {
   3810 		obj->type->rep->free(pctx, obj);
   3811 		isc_refcount_destroy(&obj->references);
   3812 		isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
   3813 	}
   3814 }
   3815 
   3816 void
   3817 cfg_obj_attach(cfg_obj_t *src, cfg_obj_t **dest) {
   3818 	REQUIRE(src != NULL);
   3819 	REQUIRE(dest != NULL && *dest == NULL);
   3820 
   3821 	isc_refcount_increment(&src->references);
   3822 	*dest = src;
   3823 }
   3824 
   3825 static void
   3826 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
   3827 	UNUSED(pctx);
   3828 	UNUSED(obj);
   3829 }
   3830 
   3831 void
   3832 cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
   3833 	REQUIRE(pctx != NULL);
   3834 	REQUIRE(type != NULL);
   3835 
   3836 	type->doc(pctx, type);
   3837 }
   3838 
   3839 void
   3840 cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) {
   3841 	REQUIRE(pctx != NULL);
   3842 	REQUIRE(type != NULL);
   3843 
   3844 	cfg_print_cstr(pctx, "<");
   3845 	cfg_print_cstr(pctx, type->name);
   3846 	cfg_print_cstr(pctx, ">");
   3847 }
   3848 
   3849 void
   3850 cfg_print_grammar(const cfg_type_t *type, unsigned int flags,
   3851 		  void (*f)(void *closure, const char *text, int textlen),
   3852 		  void *closure) {
   3853 	cfg_printer_t pctx;
   3854 
   3855 	pctx.f = f;
   3856 	pctx.closure = closure;
   3857 	pctx.indent = 0;
   3858 	pctx.flags = flags;
   3859 	cfg_doc_obj(&pctx, type);
   3860 }
   3861 
   3862 isc_result_t
   3863 cfg_parser_mapadd(cfg_parser_t *pctx, cfg_obj_t *mapobj, cfg_obj_t *obj,
   3864 		  const char *clausename) {
   3865 	isc_result_t result = ISC_R_SUCCESS;
   3866 	const cfg_map_t *map;
   3867 	isc_symvalue_t symval;
   3868 	cfg_obj_t *destobj = NULL;
   3869 	cfg_listelt_t *elt = NULL;
   3870 	const cfg_clausedef_t *const *clauseset;
   3871 	const cfg_clausedef_t *clause;
   3872 
   3873 	REQUIRE(pctx != NULL);
   3874 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
   3875 	REQUIRE(obj != NULL);
   3876 	REQUIRE(clausename != NULL);
   3877 
   3878 	map = &mapobj->value.map;
   3879 
   3880 	clause = NULL;
   3881 	for (clauseset = map->clausesets; *clauseset != NULL; clauseset++) {
   3882 		for (clause = *clauseset; clause->name != NULL; clause++) {
   3883 			if (strcasecmp(clause->name, clausename) == 0) {
   3884 				goto breakout;
   3885 			}
   3886 		}
   3887 	}
   3888 
   3889 breakout:
   3890 	if (clause == NULL || clause->name == NULL) {
   3891 		return ISC_R_FAILURE;
   3892 	}
   3893 
   3894 	result = isc_symtab_lookup(map->symtab, clausename, 0, &symval);
   3895 	if (result == ISC_R_NOTFOUND) {
   3896 		if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
   3897 			CHECK(cfg_create_list(pctx, &cfg_type_implicitlist,
   3898 					      &destobj));
   3899 			CHECK(create_listelt(pctx, &elt));
   3900 			cfg_obj_attach(obj, &elt->obj);
   3901 			ISC_LIST_APPEND(destobj->value.list, elt, link);
   3902 			symval.as_pointer = destobj;
   3903 		} else {
   3904 			symval.as_pointer = obj;
   3905 		}
   3906 
   3907 		CHECK(isc_symtab_define(map->symtab, clausename, 1, symval,
   3908 					isc_symexists_reject));
   3909 	} else {
   3910 		cfg_obj_t *destobj2 = symval.as_pointer;
   3911 
   3912 		INSIST(result == ISC_R_SUCCESS);
   3913 
   3914 		if (destobj2->type == &cfg_type_implicitlist) {
   3915 			CHECK(create_listelt(pctx, &elt));
   3916 			cfg_obj_attach(obj, &elt->obj);
   3917 			ISC_LIST_APPEND(destobj2->value.list, elt, link);
   3918 		} else {
   3919 			result = ISC_R_EXISTS;
   3920 		}
   3921 	}
   3922 
   3923 	destobj = NULL;
   3924 	elt = NULL;
   3925 
   3926 cleanup:
   3927 	if (elt != NULL) {
   3928 		free_listelt(pctx, elt);
   3929 	}
   3930 	CLEANUP_OBJ(destobj);
   3931 
   3932 	return result;
   3933 }
   3934 
   3935 isc_result_t
   3936 cfg_pluginlist_foreach(const cfg_obj_t *config, const cfg_obj_t *list,
   3937 		       isc_log_t *lctx, pluginlist_cb_t *callback,
   3938 		       void *callback_data) {
   3939 	isc_result_t result = ISC_R_SUCCESS;
   3940 	const cfg_listelt_t *element;
   3941 
   3942 	REQUIRE(config != NULL);
   3943 	REQUIRE(callback != NULL);
   3944 
   3945 	for (element = cfg_list_first(list); element != NULL;
   3946 	     element = cfg_list_next(element))
   3947 	{
   3948 		const cfg_obj_t *plugin = cfg_listelt_value(element);
   3949 		const cfg_obj_t *obj;
   3950 		const char *type, *library;
   3951 		const char *parameters = NULL;
   3952 
   3953 		/* Get the path to the plugin module. */
   3954 		obj = cfg_tuple_get(plugin, "type");
   3955 		type = cfg_obj_asstring(obj);
   3956 
   3957 		/* Only query plugins are supported currently. */
   3958 		if (strcasecmp(type, "query") != 0) {
   3959 			cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
   3960 				    "unsupported plugin type");
   3961 			return ISC_R_FAILURE;
   3962 		}
   3963 
   3964 		library = cfg_obj_asstring(cfg_tuple_get(plugin, "library"));
   3965 
   3966 		obj = cfg_tuple_get(plugin, "parameters");
   3967 		if (obj != NULL && cfg_obj_isstring(obj)) {
   3968 			parameters = cfg_obj_asstring(obj);
   3969 		}
   3970 
   3971 		result = callback(config, obj, library, parameters,
   3972 				  callback_data);
   3973 		if (result != ISC_R_SUCCESS) {
   3974 			break;
   3975 		}
   3976 	}
   3977 
   3978 	return result;
   3979 }
   3980