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