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