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