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