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