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