parser.c revision 193149
1/*
2 * Copyright (C) 2004-2008  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000-2003  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* $Id: parser.c,v 1.129 2008/09/25 04:02:39 tbox Exp $ */
19
20/*! \file */
21
22#include <config.h>
23
24#include <isc/buffer.h>
25#include <isc/dir.h>
26#include <isc/formatcheck.h>
27#include <isc/lex.h>
28#include <isc/log.h>
29#include <isc/mem.h>
30#include <isc/net.h>
31#include <isc/netaddr.h>
32#include <isc/print.h>
33#include <isc/string.h>
34#include <isc/sockaddr.h>
35#include <isc/netscope.h>
36#include <isc/util.h>
37#include <isc/symtab.h>
38
39#include <isccfg/cfg.h>
40#include <isccfg/grammar.h>
41#include <isccfg/log.h>
42
43/* Shorthand */
44#define CAT CFG_LOGCATEGORY_CONFIG
45#define MOD CFG_LOGMODULE_PARSER
46
47#define MAP_SYM 1 	/* Unique type for isc_symtab */
48
49#define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
50
51/* Check a return value. */
52#define CHECK(op) 						\
53	do { result = (op); 					\
54		if (result != ISC_R_SUCCESS) goto cleanup; 	\
55	} while (0)
56
57/* Clean up a configuration object if non-NULL. */
58#define CLEANUP_OBJ(obj) \
59	do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
60
61
62/*
63 * Forward declarations of static functions.
64 */
65
66static void
67free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
68
69static isc_result_t
70parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
71
72static void
73print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
74
75static void
76free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
77
78static isc_result_t
79create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
80
81static isc_result_t
82create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
83	      cfg_obj_t **ret);
84
85static void
86free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
87
88static isc_result_t
89create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
90
91static void
92free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
93
94static isc_result_t
95parse_symtab_elt(cfg_parser_t *pctx, const char *name,
96		 cfg_type_t *elttype, isc_symtab_t *symtab,
97		 isc_boolean_t callback);
98
99static void
100free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
101
102static isc_result_t
103cfg_getstringtoken(cfg_parser_t *pctx);
104
105static void
106parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
107		unsigned int flags, const char *format, va_list args);
108
109/*
110 * Data representations.  These correspond to members of the
111 * "value" union in struct cfg_obj (except "void", which does
112 * not need a union member).
113 */
114
115cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
116cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
117cfg_rep_t cfg_rep_string = { "string", free_string };
118cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
119cfg_rep_t cfg_rep_map = { "map", free_map };
120cfg_rep_t cfg_rep_list = { "list", free_list };
121cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
122cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
123cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
124cfg_rep_t cfg_rep_void = { "void", free_noop };
125
126/*
127 * Configuration type definitions.
128 */
129
130/*%
131 * An implicit list.  These are formed by clauses that occur multiple times.
132 */
133static cfg_type_t cfg_type_implicitlist = {
134	"implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL };
135
136/* Functions. */
137
138void
139cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
140	obj->type->print(pctx, obj);
141}
142
143void
144cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
145	pctx->f(pctx->closure, text, len);
146}
147
148static void
149print_open(cfg_printer_t *pctx) {
150	cfg_print_chars(pctx, "{\n", 2);
151	pctx->indent++;
152}
153
154static void
155print_indent(cfg_printer_t *pctx) {
156	int indent = pctx->indent;
157	while (indent > 0) {
158		cfg_print_chars(pctx, "\t", 1);
159		indent--;
160	}
161}
162
163static void
164print_close(cfg_printer_t *pctx) {
165	pctx->indent--;
166	print_indent(pctx);
167	cfg_print_chars(pctx, "}", 1);
168}
169
170isc_result_t
171cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
172	isc_result_t result;
173	INSIST(ret != NULL && *ret == NULL);
174	result = type->parse(pctx, type, ret);
175	if (result != ISC_R_SUCCESS)
176		return (result);
177	INSIST(*ret != NULL);
178	return (ISC_R_SUCCESS);
179}
180
181void
182cfg_print(const cfg_obj_t *obj,
183	  void (*f)(void *closure, const char *text, int textlen),
184	  void *closure)
185{
186	cfg_printer_t pctx;
187	pctx.f = f;
188	pctx.closure = closure;
189	pctx.indent = 0;
190	obj->type->print(&pctx, obj);
191}
192
193
194/* Tuples. */
195
196isc_result_t
197cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
198	isc_result_t result;
199	const cfg_tuplefielddef_t *fields = type->of;
200	const cfg_tuplefielddef_t *f;
201	cfg_obj_t *obj = NULL;
202	unsigned int nfields = 0;
203	int i;
204
205	for (f = fields; f->name != NULL; f++)
206		nfields++;
207
208	CHECK(cfg_create_obj(pctx, type, &obj));
209	obj->value.tuple = isc_mem_get(pctx->mctx,
210				       nfields * sizeof(cfg_obj_t *));
211	if (obj->value.tuple == NULL) {
212		result = ISC_R_NOMEMORY;
213		goto cleanup;
214	}
215	for (f = fields, i = 0; f->name != NULL; f++, i++)
216		obj->value.tuple[i] = NULL;
217	*ret = obj;
218	return (ISC_R_SUCCESS);
219
220 cleanup:
221	if (obj != NULL)
222		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
223	return (result);
224}
225
226isc_result_t
227cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
228{
229	isc_result_t result;
230	const cfg_tuplefielddef_t *fields = type->of;
231	const cfg_tuplefielddef_t *f;
232	cfg_obj_t *obj = NULL;
233	unsigned int i;
234
235	CHECK(cfg_create_tuple(pctx, type, &obj));
236	for (f = fields, i = 0; f->name != NULL; f++, i++)
237		CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
238
239	*ret = obj;
240	return (ISC_R_SUCCESS);
241
242 cleanup:
243	CLEANUP_OBJ(obj);
244	return (result);
245}
246
247void
248cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
249	unsigned int i;
250	const cfg_tuplefielddef_t *fields = obj->type->of;
251	const cfg_tuplefielddef_t *f;
252	isc_boolean_t need_space = ISC_FALSE;
253
254	for (f = fields, i = 0; f->name != NULL; f++, i++) {
255		const cfg_obj_t *fieldobj = obj->value.tuple[i];
256		if (need_space)
257			cfg_print_chars(pctx, " ", 1);
258		cfg_print_obj(pctx, fieldobj);
259		need_space = ISC_TF(fieldobj->type->print != cfg_print_void);
260	}
261}
262
263void
264cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
265	const cfg_tuplefielddef_t *fields = type->of;
266	const cfg_tuplefielddef_t *f;
267	isc_boolean_t need_space = ISC_FALSE;
268
269	for (f = fields; f->name != NULL; f++) {
270		if (need_space)
271			cfg_print_chars(pctx, " ", 1);
272		cfg_doc_obj(pctx, f->type);
273		need_space = ISC_TF(f->type->print != cfg_print_void);
274	}
275}
276
277static void
278free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
279	unsigned int i;
280	const cfg_tuplefielddef_t *fields = obj->type->of;
281	const cfg_tuplefielddef_t *f;
282	unsigned int nfields = 0;
283
284	if (obj->value.tuple == NULL)
285		return;
286
287	for (f = fields, i = 0; f->name != NULL; f++, i++) {
288		CLEANUP_OBJ(obj->value.tuple[i]);
289		nfields++;
290	}
291	isc_mem_put(pctx->mctx, obj->value.tuple,
292		    nfields * sizeof(cfg_obj_t *));
293}
294
295isc_boolean_t
296cfg_obj_istuple(const cfg_obj_t *obj) {
297	REQUIRE(obj != NULL);
298	return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
299}
300
301const cfg_obj_t *
302cfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) {
303	unsigned int i;
304	const cfg_tuplefielddef_t *fields;
305	const cfg_tuplefielddef_t *f;
306
307	REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
308
309	fields = tupleobj->type->of;
310	for (f = fields, i = 0; f->name != NULL; f++, i++) {
311		if (strcmp(f->name, name) == 0)
312			return (tupleobj->value.tuple[i]);
313	}
314	INSIST(0);
315	return (NULL);
316}
317
318isc_result_t
319cfg_parse_special(cfg_parser_t *pctx, int special) {
320	isc_result_t result;
321	CHECK(cfg_gettoken(pctx, 0));
322	if (pctx->token.type == isc_tokentype_special &&
323	    pctx->token.value.as_char == special)
324		return (ISC_R_SUCCESS);
325
326	cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
327	return (ISC_R_UNEXPECTEDTOKEN);
328 cleanup:
329	return (result);
330}
331
332/*
333 * Parse a required semicolon.  If it is not there, log
334 * an error and increment the error count but continue
335 * parsing.  Since the next token is pushed back,
336 * care must be taken to make sure it is eventually
337 * consumed or an infinite loop may result.
338 */
339static isc_result_t
340parse_semicolon(cfg_parser_t *pctx) {
341	isc_result_t result;
342	CHECK(cfg_gettoken(pctx, 0));
343	if (pctx->token.type == isc_tokentype_special &&
344	    pctx->token.value.as_char == ';')
345		return (ISC_R_SUCCESS);
346
347	cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
348	cfg_ungettoken(pctx);
349 cleanup:
350	return (result);
351}
352
353/*
354 * Parse EOF, logging and returning an error if not there.
355 */
356static isc_result_t
357parse_eof(cfg_parser_t *pctx) {
358	isc_result_t result;
359	CHECK(cfg_gettoken(pctx, 0));
360
361	if (pctx->token.type == isc_tokentype_eof)
362		return (ISC_R_SUCCESS);
363
364	cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
365	return (ISC_R_UNEXPECTEDTOKEN);
366 cleanup:
367	return (result);
368}
369
370/* A list of files, used internally for pctx->files. */
371
372static cfg_type_t cfg_type_filelist = {
373	"filelist", NULL, print_list, NULL, &cfg_rep_list,
374	&cfg_type_qstring
375};
376
377isc_result_t
378cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
379	isc_result_t result;
380	cfg_parser_t *pctx;
381	isc_lexspecials_t specials;
382
383	REQUIRE(mctx != NULL);
384	REQUIRE(ret != NULL && *ret == NULL);
385
386	pctx = isc_mem_get(mctx, sizeof(*pctx));
387	if (pctx == NULL)
388		return (ISC_R_NOMEMORY);
389
390	pctx->mctx = mctx;
391	pctx->lctx = lctx;
392	pctx->lexer = NULL;
393	pctx->seen_eof = ISC_FALSE;
394	pctx->ungotten = ISC_FALSE;
395	pctx->errors = 0;
396	pctx->warnings = 0;
397	pctx->open_files = NULL;
398	pctx->closed_files = NULL;
399	pctx->line = 0;
400	pctx->callback = NULL;
401	pctx->callbackarg = NULL;
402	pctx->token.type = isc_tokentype_unknown;
403
404	memset(specials, 0, sizeof(specials));
405	specials['{'] = 1;
406	specials['}'] = 1;
407	specials[';'] = 1;
408	specials['/'] = 1;
409	specials['"'] = 1;
410	specials['!'] = 1;
411
412	CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer));
413
414	isc_lex_setspecials(pctx->lexer, specials);
415	isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C |
416					 ISC_LEXCOMMENT_CPLUSPLUS |
417					 ISC_LEXCOMMENT_SHELL));
418
419	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
420	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
421
422	*ret = pctx;
423	return (ISC_R_SUCCESS);
424
425 cleanup:
426	if (pctx->lexer != NULL)
427		isc_lex_destroy(&pctx->lexer);
428	CLEANUP_OBJ(pctx->open_files);
429	CLEANUP_OBJ(pctx->closed_files);
430	isc_mem_put(mctx, pctx, sizeof(*pctx));
431	return (result);
432}
433
434static isc_result_t
435parser_openfile(cfg_parser_t *pctx, const char *filename) {
436	isc_result_t result;
437	cfg_listelt_t *elt = NULL;
438	cfg_obj_t *stringobj = NULL;
439
440	result = isc_lex_openfile(pctx->lexer, filename);
441	if (result != ISC_R_SUCCESS) {
442		cfg_parser_error(pctx, 0, "open: %s: %s",
443			     filename, isc_result_totext(result));
444		goto cleanup;
445	}
446
447	CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
448	CHECK(create_listelt(pctx, &elt));
449	elt->obj = stringobj;
450	ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
451
452	return (ISC_R_SUCCESS);
453 cleanup:
454	CLEANUP_OBJ(stringobj);
455	return (result);
456}
457
458void
459cfg_parser_setcallback(cfg_parser_t *pctx,
460		       cfg_parsecallback_t callback,
461		       void *arg)
462{
463	pctx->callback = callback;
464	pctx->callbackarg = arg;
465}
466
467/*
468 * Parse a configuration using a pctx where a lexer has already
469 * been set up with a source.
470 */
471static isc_result_t
472parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
473	isc_result_t result;
474	cfg_obj_t *obj = NULL;
475
476	result = cfg_parse_obj(pctx, type, &obj);
477
478	if (pctx->errors != 0) {
479		/* Errors have been logged. */
480		if (result == ISC_R_SUCCESS)
481			result = ISC_R_FAILURE;
482		goto cleanup;
483	}
484
485	if (result != ISC_R_SUCCESS) {
486		/* Parsing failed but no errors have been logged. */
487		cfg_parser_error(pctx, 0, "parsing failed");
488		goto cleanup;
489	}
490
491	CHECK(parse_eof(pctx));
492
493	*ret = obj;
494	return (ISC_R_SUCCESS);
495
496 cleanup:
497	CLEANUP_OBJ(obj);
498	return (result);
499}
500
501isc_result_t
502cfg_parse_file(cfg_parser_t *pctx, const char *filename,
503	       const cfg_type_t *type, cfg_obj_t **ret)
504{
505	isc_result_t result;
506
507	REQUIRE(filename != NULL);
508
509	CHECK(parser_openfile(pctx, filename));
510	CHECK(parse2(pctx, type, ret));
511 cleanup:
512	return (result);
513}
514
515
516isc_result_t
517cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
518	const cfg_type_t *type, cfg_obj_t **ret)
519{
520	isc_result_t result;
521	REQUIRE(buffer != NULL);
522	CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
523	CHECK(parse2(pctx, type, ret));
524 cleanup:
525	return (result);
526}
527
528void
529cfg_parser_destroy(cfg_parser_t **pctxp) {
530	cfg_parser_t *pctx = *pctxp;
531	isc_lex_destroy(&pctx->lexer);
532	/*
533	 * Cleaning up open_files does not
534	 * close the files; that was already done
535	 * by closing the lexer.
536	 */
537	CLEANUP_OBJ(pctx->open_files);
538	CLEANUP_OBJ(pctx->closed_files);
539	isc_mem_put(pctx->mctx, pctx, sizeof(*pctx));
540	*pctxp = NULL;
541}
542
543/*
544 * void
545 */
546isc_result_t
547cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
548	UNUSED(type);
549	return (cfg_create_obj(pctx, &cfg_type_void, ret));
550}
551
552void
553cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
554	UNUSED(pctx);
555	UNUSED(obj);
556}
557
558void
559cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
560	UNUSED(pctx);
561	UNUSED(type);
562}
563
564isc_boolean_t
565cfg_obj_isvoid(const cfg_obj_t *obj) {
566	REQUIRE(obj != NULL);
567	return (ISC_TF(obj->type->rep == &cfg_rep_void));
568}
569
570cfg_type_t cfg_type_void = {
571	"void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
572	NULL };
573
574
575/*
576 * uint32
577 */
578isc_result_t
579cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
580	isc_result_t result;
581	cfg_obj_t *obj = NULL;
582	UNUSED(type);
583
584	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
585	if (pctx->token.type != isc_tokentype_number) {
586		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
587		return (ISC_R_UNEXPECTEDTOKEN);
588	}
589
590	CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
591
592	obj->value.uint32 = pctx->token.value.as_ulong;
593	*ret = obj;
594 cleanup:
595	return (result);
596}
597
598void
599cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
600	cfg_print_chars(pctx, s, strlen(s));
601}
602
603void
604cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
605	char buf[32];
606	snprintf(buf, sizeof(buf), "%u", u);
607	cfg_print_cstr(pctx, buf);
608}
609
610void
611cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
612	cfg_print_rawuint(pctx, obj->value.uint32);
613}
614
615isc_boolean_t
616cfg_obj_isuint32(const cfg_obj_t *obj) {
617	REQUIRE(obj != NULL);
618	return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
619}
620
621isc_uint32_t
622cfg_obj_asuint32(const cfg_obj_t *obj) {
623	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
624	return (obj->value.uint32);
625}
626
627cfg_type_t cfg_type_uint32 = {
628	"integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
629	&cfg_rep_uint32, NULL
630};
631
632
633/*
634 * uint64
635 */
636isc_boolean_t
637cfg_obj_isuint64(const cfg_obj_t *obj) {
638	REQUIRE(obj != NULL);
639	return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
640}
641
642isc_uint64_t
643cfg_obj_asuint64(const cfg_obj_t *obj) {
644	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
645	return (obj->value.uint64);
646}
647
648void
649cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
650	char buf[32];
651	snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
652		 obj->value.uint64);
653	cfg_print_cstr(pctx, buf);
654}
655
656cfg_type_t cfg_type_uint64 = {
657	"64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
658	&cfg_rep_uint64, NULL
659};
660
661/*
662 * qstring (quoted string), ustring (unquoted string), astring
663 * (any string)
664 */
665
666/* Create a string object from a null-terminated C string. */
667static isc_result_t
668create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
669	      cfg_obj_t **ret)
670{
671	isc_result_t result;
672	cfg_obj_t *obj = NULL;
673	int len;
674
675	CHECK(cfg_create_obj(pctx, type, &obj));
676	len = strlen(contents);
677	obj->value.string.length = len;
678	obj->value.string.base = isc_mem_get(pctx->mctx, len + 1);
679	if (obj->value.string.base == 0) {
680		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
681		return (ISC_R_NOMEMORY);
682	}
683	memcpy(obj->value.string.base, contents, len);
684	obj->value.string.base[len] = '\0';
685
686	*ret = obj;
687 cleanup:
688	return (result);
689}
690
691isc_result_t
692cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
693	isc_result_t result;
694	UNUSED(type);
695
696	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
697	if (pctx->token.type != isc_tokentype_qstring) {
698		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
699		return (ISC_R_UNEXPECTEDTOKEN);
700	}
701	return (create_string(pctx,
702			      TOKEN_STRING(pctx),
703			      &cfg_type_qstring,
704			      ret));
705 cleanup:
706	return (result);
707}
708
709static isc_result_t
710parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
711	isc_result_t result;
712	UNUSED(type);
713
714	CHECK(cfg_gettoken(pctx, 0));
715	if (pctx->token.type != isc_tokentype_string) {
716		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
717		return (ISC_R_UNEXPECTEDTOKEN);
718	}
719	return (create_string(pctx,
720			      TOKEN_STRING(pctx),
721			      &cfg_type_ustring,
722			      ret));
723 cleanup:
724	return (result);
725}
726
727isc_result_t
728cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
729		  cfg_obj_t **ret)
730{
731	isc_result_t result;
732	UNUSED(type);
733
734	CHECK(cfg_getstringtoken(pctx));
735	return (create_string(pctx,
736			      TOKEN_STRING(pctx),
737			      &cfg_type_qstring,
738			      ret));
739 cleanup:
740	return (result);
741}
742
743isc_boolean_t
744cfg_is_enum(const char *s, const char *const *enums) {
745	const char * const *p;
746	for (p = enums; *p != NULL; p++) {
747		if (strcasecmp(*p, s) == 0)
748			return (ISC_TRUE);
749	}
750	return (ISC_FALSE);
751}
752
753static isc_result_t
754check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
755	const char *s = obj->value.string.base;
756	if (cfg_is_enum(s, enums))
757		return (ISC_R_SUCCESS);
758	cfg_parser_error(pctx, 0, "'%s' unexpected", s);
759	return (ISC_R_UNEXPECTEDTOKEN);
760}
761
762isc_result_t
763cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
764	isc_result_t result;
765	cfg_obj_t *obj = NULL;
766	CHECK(parse_ustring(pctx, NULL, &obj));
767	CHECK(check_enum(pctx, obj, type->of));
768	*ret = obj;
769	return (ISC_R_SUCCESS);
770 cleanup:
771	CLEANUP_OBJ(obj);
772	return (result);
773}
774
775void
776cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
777	const char * const *p;
778	cfg_print_chars(pctx, "( ", 2);
779	for (p = type->of; *p != NULL; p++) {
780		cfg_print_cstr(pctx, *p);
781		if (p[1] != NULL)
782			cfg_print_chars(pctx, " | ", 3);
783	}
784	cfg_print_chars(pctx, " )", 2);
785}
786
787void
788cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
789	cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
790}
791
792static void
793print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
794	cfg_print_chars(pctx, "\"", 1);
795	cfg_print_ustring(pctx, obj);
796	cfg_print_chars(pctx, "\"", 1);
797}
798
799static void
800free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
801	isc_mem_put(pctx->mctx, obj->value.string.base,
802		    obj->value.string.length + 1);
803}
804
805isc_boolean_t
806cfg_obj_isstring(const cfg_obj_t *obj) {
807	REQUIRE(obj != NULL);
808	return (ISC_TF(obj->type->rep == &cfg_rep_string));
809}
810
811const char *
812cfg_obj_asstring(const cfg_obj_t *obj) {
813	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
814	return (obj->value.string.base);
815}
816
817/* Quoted string only */
818cfg_type_t cfg_type_qstring = {
819	"quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal,
820	&cfg_rep_string, NULL
821};
822
823/* Unquoted string only */
824cfg_type_t cfg_type_ustring = {
825	"string", parse_ustring, cfg_print_ustring, cfg_doc_terminal,
826	&cfg_rep_string, NULL
827};
828
829/* Any string (quoted or unquoted); printed with quotes */
830cfg_type_t cfg_type_astring = {
831	"string", cfg_parse_astring, print_qstring, cfg_doc_terminal,
832	&cfg_rep_string, NULL
833};
834
835/*
836 * Booleans
837 */
838
839isc_boolean_t
840cfg_obj_isboolean(const cfg_obj_t *obj) {
841	REQUIRE(obj != NULL);
842	return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
843}
844
845isc_boolean_t
846cfg_obj_asboolean(const cfg_obj_t *obj) {
847	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
848	return (obj->value.boolean);
849}
850
851static isc_result_t
852parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
853{
854	isc_result_t result;
855	isc_boolean_t value;
856	cfg_obj_t *obj = NULL;
857	UNUSED(type);
858
859	result = cfg_gettoken(pctx, 0);
860	if (result != ISC_R_SUCCESS)
861		return (result);
862
863	if (pctx->token.type != isc_tokentype_string)
864		goto bad_boolean;
865
866	if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
867	    (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
868	    (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
869		value = ISC_TRUE;
870	} else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
871		   (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
872		   (strcmp(TOKEN_STRING(pctx), "0") == 0)) {
873		value = ISC_FALSE;
874	} else {
875		goto bad_boolean;
876	}
877
878	CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
879	obj->value.boolean = value;
880	*ret = obj;
881	return (result);
882
883 bad_boolean:
884	cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
885	return (ISC_R_UNEXPECTEDTOKEN);
886
887 cleanup:
888	return (result);
889}
890
891static void
892print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
893	if (obj->value.boolean)
894		cfg_print_chars(pctx, "yes", 3);
895	else
896		cfg_print_chars(pctx, "no", 2);
897}
898
899cfg_type_t cfg_type_boolean = {
900	"boolean", parse_boolean, print_boolean, cfg_doc_terminal,
901	&cfg_rep_boolean, NULL
902};
903
904/*
905 * Lists.
906 */
907
908isc_result_t
909cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
910	isc_result_t result;
911	CHECK(cfg_create_obj(pctx, type, obj));
912	ISC_LIST_INIT((*obj)->value.list);
913 cleanup:
914	return (result);
915}
916
917static isc_result_t
918create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
919	cfg_listelt_t *elt;
920	elt = isc_mem_get(pctx->mctx, sizeof(*elt));
921	if (elt == NULL)
922		return (ISC_R_NOMEMORY);
923	elt->obj = NULL;
924	ISC_LINK_INIT(elt, link);
925	*eltp = elt;
926	return (ISC_R_SUCCESS);
927}
928
929static void
930free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
931	cfg_obj_destroy(pctx, &elt->obj);
932	isc_mem_put(pctx->mctx, elt, sizeof(*elt));
933}
934
935static void
936free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
937	cfg_listelt_t *elt, *next;
938	for (elt = ISC_LIST_HEAD(obj->value.list);
939	     elt != NULL;
940	     elt = next)
941	{
942		next = ISC_LIST_NEXT(elt, link);
943		free_list_elt(pctx, elt);
944	}
945}
946
947isc_result_t
948cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
949		  cfg_listelt_t **ret)
950{
951	isc_result_t result;
952	cfg_listelt_t *elt = NULL;
953	cfg_obj_t *value = NULL;
954
955	CHECK(create_listelt(pctx, &elt));
956
957	result = cfg_parse_obj(pctx, elttype, &value);
958	if (result != ISC_R_SUCCESS)
959		goto cleanup;
960
961	elt->obj = value;
962
963	*ret = elt;
964	return (ISC_R_SUCCESS);
965
966 cleanup:
967	isc_mem_put(pctx->mctx, elt, sizeof(*elt));
968	return (result);
969}
970
971/*
972 * Parse a homogeneous list whose elements are of type 'elttype'
973 * and where each element is terminated by a semicolon.
974 */
975static isc_result_t
976parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
977{
978	cfg_obj_t *listobj = NULL;
979	const cfg_type_t *listof = listtype->of;
980	isc_result_t result;
981	cfg_listelt_t *elt = NULL;
982
983	CHECK(cfg_create_list(pctx, listtype, &listobj));
984
985	for (;;) {
986		CHECK(cfg_peektoken(pctx, 0));
987		if (pctx->token.type == isc_tokentype_special &&
988		    pctx->token.value.as_char == /*{*/ '}')
989			break;
990		CHECK(cfg_parse_listelt(pctx, listof, &elt));
991		CHECK(parse_semicolon(pctx));
992		ISC_LIST_APPEND(listobj->value.list, elt, link);
993		elt = NULL;
994	}
995	*ret = listobj;
996	return (ISC_R_SUCCESS);
997
998 cleanup:
999	if (elt != NULL)
1000		free_list_elt(pctx, elt);
1001	CLEANUP_OBJ(listobj);
1002	return (result);
1003}
1004
1005static void
1006print_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1007	const cfg_list_t *list = &obj->value.list;
1008	const cfg_listelt_t *elt;
1009
1010	for (elt = ISC_LIST_HEAD(*list);
1011	     elt != NULL;
1012	     elt = ISC_LIST_NEXT(elt, link)) {
1013		print_indent(pctx);
1014		cfg_print_obj(pctx, elt->obj);
1015		cfg_print_chars(pctx, ";\n", 2);
1016	}
1017}
1018
1019isc_result_t
1020cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
1021		     cfg_obj_t **ret)
1022{
1023	isc_result_t result;
1024	CHECK(cfg_parse_special(pctx, '{'));
1025	CHECK(parse_list(pctx, type, ret));
1026	CHECK(cfg_parse_special(pctx, '}'));
1027 cleanup:
1028	return (result);
1029}
1030
1031void
1032cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1033	print_open(pctx);
1034	print_list(pctx, obj);
1035	print_close(pctx);
1036}
1037
1038void
1039cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
1040	cfg_print_chars(pctx, "{ ", 2);
1041	cfg_doc_obj(pctx, type->of);
1042	cfg_print_chars(pctx, "; ... }", 7);
1043}
1044
1045/*
1046 * Parse a homogeneous list whose elements are of type 'elttype'
1047 * and where elements are separated by space.  The list ends
1048 * before the first semicolon.
1049 */
1050isc_result_t
1051cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
1052		    cfg_obj_t **ret)
1053{
1054	cfg_obj_t *listobj = NULL;
1055	const cfg_type_t *listof = listtype->of;
1056	isc_result_t result;
1057
1058	CHECK(cfg_create_list(pctx, listtype, &listobj));
1059
1060	for (;;) {
1061		cfg_listelt_t *elt = NULL;
1062
1063		CHECK(cfg_peektoken(pctx, 0));
1064		if (pctx->token.type == isc_tokentype_special &&
1065		    pctx->token.value.as_char == ';')
1066			break;
1067		CHECK(cfg_parse_listelt(pctx, listof, &elt));
1068		ISC_LIST_APPEND(listobj->value.list, elt, link);
1069	}
1070	*ret = listobj;
1071	return (ISC_R_SUCCESS);
1072
1073 cleanup:
1074	CLEANUP_OBJ(listobj);
1075	return (result);
1076}
1077
1078void
1079cfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1080	const cfg_list_t *list = &obj->value.list;
1081	const cfg_listelt_t *elt;
1082
1083	for (elt = ISC_LIST_HEAD(*list);
1084	     elt != NULL;
1085	     elt = ISC_LIST_NEXT(elt, link)) {
1086		cfg_print_obj(pctx, elt->obj);
1087		if (ISC_LIST_NEXT(elt, link) != NULL)
1088			cfg_print_chars(pctx, " ", 1);
1089	}
1090}
1091
1092isc_boolean_t
1093cfg_obj_islist(const cfg_obj_t *obj) {
1094	REQUIRE(obj != NULL);
1095	return (ISC_TF(obj->type->rep == &cfg_rep_list));
1096}
1097
1098const cfg_listelt_t *
1099cfg_list_first(const cfg_obj_t *obj) {
1100	REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
1101	if (obj == NULL)
1102		return (NULL);
1103	return (ISC_LIST_HEAD(obj->value.list));
1104}
1105
1106const cfg_listelt_t *
1107cfg_list_next(const cfg_listelt_t *elt) {
1108	REQUIRE(elt != NULL);
1109	return (ISC_LIST_NEXT(elt, link));
1110}
1111
1112/*
1113 * Return the length of a list object.  If obj is NULL or is not
1114 * a list, return 0.
1115 */
1116unsigned int
1117cfg_list_length(const cfg_obj_t *obj, isc_boolean_t recurse) {
1118	const cfg_listelt_t *elt;
1119	unsigned int count = 0;
1120
1121	if (obj == NULL || !cfg_obj_islist(obj))
1122		return (0U);
1123	for (elt = cfg_list_first(obj);
1124	     elt != NULL;
1125	     elt = cfg_list_next(elt)) {
1126		if (recurse && cfg_obj_islist(elt->obj)) {
1127			count += cfg_list_length(elt->obj, recurse);
1128		} else {
1129			count++;
1130		}
1131	}
1132	return (count);
1133}
1134
1135const cfg_obj_t *
1136cfg_listelt_value(const cfg_listelt_t *elt) {
1137	REQUIRE(elt != NULL);
1138	return (elt->obj);
1139}
1140
1141/*
1142 * Maps.
1143 */
1144
1145/*
1146 * Parse a map body.  That's something like
1147 *
1148 *   "foo 1; bar { glub; }; zap true; zap false;"
1149 *
1150 * i.e., a sequence of option names followed by values and
1151 * terminated by semicolons.  Used for the top level of
1152 * the named.conf syntax, as well as for the body of the
1153 * options, view, zone, and other statements.
1154 */
1155isc_result_t
1156cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1157{
1158	const cfg_clausedef_t * const *clausesets = type->of;
1159	isc_result_t result;
1160	const cfg_clausedef_t * const *clauseset;
1161	const cfg_clausedef_t *clause;
1162	cfg_obj_t *value = NULL;
1163	cfg_obj_t *obj = NULL;
1164	cfg_obj_t *eltobj = NULL;
1165	cfg_obj_t *includename = NULL;
1166	isc_symvalue_t symval;
1167	cfg_list_t *list = NULL;
1168
1169	CHECK(create_map(pctx, type, &obj));
1170
1171	obj->value.map.clausesets = clausesets;
1172
1173	for (;;) {
1174		cfg_listelt_t *elt;
1175
1176	redo:
1177		/*
1178		 * Parse the option name and see if it is known.
1179		 */
1180		CHECK(cfg_gettoken(pctx, 0));
1181
1182		if (pctx->token.type != isc_tokentype_string) {
1183			cfg_ungettoken(pctx);
1184			break;
1185		}
1186
1187		/*
1188		 * We accept "include" statements wherever a map body
1189		 * clause can occur.
1190		 */
1191		if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
1192			/*
1193			 * Turn the file name into a temporary configuration
1194			 * object just so that it is not overwritten by the
1195			 * semicolon token.
1196			 */
1197			CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
1198			CHECK(parse_semicolon(pctx));
1199			CHECK(parser_openfile(pctx, includename->
1200					      value.string.base));
1201			 cfg_obj_destroy(pctx, &includename);
1202			 goto redo;
1203		}
1204
1205		clause = NULL;
1206		for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
1207			for (clause = *clauseset;
1208			     clause->name != NULL;
1209			     clause++) {
1210				if (strcasecmp(TOKEN_STRING(pctx),
1211					   clause->name) == 0)
1212					goto done;
1213			}
1214		}
1215	done:
1216		if (clause == NULL || clause->name == NULL) {
1217			cfg_parser_error(pctx, CFG_LOG_NOPREP, "unknown option");
1218			/*
1219			 * Try to recover by parsing this option as an unknown
1220			 * option and discarding it.
1221			 */
1222			CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, &eltobj));
1223			cfg_obj_destroy(pctx, &eltobj);
1224			CHECK(parse_semicolon(pctx));
1225			continue;
1226		}
1227
1228		/* Clause is known. */
1229
1230		/* Issue warnings if appropriate */
1231		if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0)
1232			cfg_parser_warning(pctx, 0, "option '%s' is obsolete",
1233				       clause->name);
1234		if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0)
1235			cfg_parser_warning(pctx, 0, "option '%s' is "
1236				       "not implemented", clause->name);
1237		if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0)
1238			cfg_parser_warning(pctx, 0, "option '%s' is "
1239				       "not implemented", clause->name);
1240		/*
1241		 * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
1242		 * set here - we need to log the *lack* of such an option,
1243		 * not its presence.
1244		 */
1245
1246		/* See if the clause already has a value; if not create one. */
1247		result = isc_symtab_lookup(obj->value.map.symtab,
1248					   clause->name, 0, &symval);
1249
1250		if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
1251			/* Multivalued clause */
1252			cfg_obj_t *listobj = NULL;
1253			if (result == ISC_R_NOTFOUND) {
1254				CHECK(cfg_create_list(pctx,
1255						  &cfg_type_implicitlist,
1256						  &listobj));
1257				symval.as_pointer = listobj;
1258				result = isc_symtab_define(obj->value.
1259						   map.symtab,
1260						   clause->name,
1261						   1, symval,
1262						   isc_symexists_reject);
1263				if (result != ISC_R_SUCCESS) {
1264					cfg_parser_error(pctx, CFG_LOG_NEAR,
1265						     "isc_symtab_define(%s) "
1266						     "failed", clause->name);
1267					isc_mem_put(pctx->mctx, list,
1268						    sizeof(cfg_list_t));
1269					goto cleanup;
1270				}
1271			} else {
1272				INSIST(result == ISC_R_SUCCESS);
1273				listobj = symval.as_pointer;
1274			}
1275
1276			elt = NULL;
1277			CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
1278			CHECK(parse_semicolon(pctx));
1279
1280			ISC_LIST_APPEND(listobj->value.list, elt, link);
1281		} else {
1282			/* Single-valued clause */
1283			if (result == ISC_R_NOTFOUND) {
1284				isc_boolean_t callback =
1285					ISC_TF((clause->flags &
1286						CFG_CLAUSEFLAG_CALLBACK) != 0);
1287				CHECK(parse_symtab_elt(pctx, clause->name,
1288						       clause->type,
1289						       obj->value.map.symtab,
1290						       callback));
1291				CHECK(parse_semicolon(pctx));
1292			} else if (result == ISC_R_SUCCESS) {
1293				cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
1294					     clause->name);
1295				result = ISC_R_EXISTS;
1296				goto cleanup;
1297			} else {
1298				cfg_parser_error(pctx, CFG_LOG_NEAR,
1299					     "isc_symtab_define() failed");
1300				goto cleanup;
1301			}
1302		}
1303	}
1304
1305
1306	*ret = obj;
1307	return (ISC_R_SUCCESS);
1308
1309 cleanup:
1310	CLEANUP_OBJ(value);
1311	CLEANUP_OBJ(obj);
1312	CLEANUP_OBJ(eltobj);
1313	CLEANUP_OBJ(includename);
1314	return (result);
1315}
1316
1317static isc_result_t
1318parse_symtab_elt(cfg_parser_t *pctx, const char *name,
1319		 cfg_type_t *elttype, isc_symtab_t *symtab,
1320		 isc_boolean_t callback)
1321{
1322	isc_result_t result;
1323	cfg_obj_t *obj = NULL;
1324	isc_symvalue_t symval;
1325
1326	CHECK(cfg_parse_obj(pctx, elttype, &obj));
1327
1328	if (callback && pctx->callback != NULL)
1329		CHECK(pctx->callback(name, obj, pctx->callbackarg));
1330
1331	symval.as_pointer = obj;
1332	CHECK(isc_symtab_define(symtab, name,
1333				1, symval,
1334				isc_symexists_reject));
1335	return (ISC_R_SUCCESS);
1336
1337 cleanup:
1338	CLEANUP_OBJ(obj);
1339	return (result);
1340}
1341
1342/*
1343 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
1344 */
1345isc_result_t
1346cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1347	isc_result_t result;
1348	CHECK(cfg_parse_special(pctx, '{'));
1349	CHECK(cfg_parse_mapbody(pctx, type, ret));
1350	CHECK(cfg_parse_special(pctx, '}'));
1351 cleanup:
1352	return (result);
1353}
1354
1355/*
1356 * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
1357 */
1358static isc_result_t
1359parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type,
1360		    cfg_obj_t **ret)
1361{
1362	isc_result_t result;
1363	cfg_obj_t *idobj = NULL;
1364	cfg_obj_t *mapobj = NULL;
1365
1366	CHECK(cfg_parse_obj(pctx, nametype, &idobj));
1367	CHECK(cfg_parse_map(pctx, type, &mapobj));
1368	mapobj->value.map.id = idobj;
1369	idobj = NULL;
1370	*ret = mapobj;
1371 cleanup:
1372	CLEANUP_OBJ(idobj);
1373	return (result);
1374}
1375
1376/*
1377 * Parse a map identified by a string name.  E.g., "name { foo 1; }".
1378 * Used for the "key" and "channel" statements.
1379 */
1380isc_result_t
1381cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1382	return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
1383}
1384
1385/*
1386 * Parse a map identified by a network address.
1387 * Used to be used for the "server" statement.
1388 */
1389isc_result_t
1390cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1391	return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
1392}
1393
1394/*
1395 * Parse a map identified by a network prefix.
1396 * Used for the "server" statement.
1397 */
1398isc_result_t
1399cfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1400	return (parse_any_named_map(pctx, &cfg_type_netprefix, type, ret));
1401}
1402
1403void
1404cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1405	isc_result_t result = ISC_R_SUCCESS;
1406
1407	const cfg_clausedef_t * const *clauseset;
1408
1409	for (clauseset = obj->value.map.clausesets;
1410	     *clauseset != NULL;
1411	     clauseset++)
1412	{
1413		isc_symvalue_t symval;
1414		const cfg_clausedef_t *clause;
1415
1416		for (clause = *clauseset;
1417		     clause->name != NULL;
1418		     clause++) {
1419			result = isc_symtab_lookup(obj->value.map.symtab,
1420						   clause->name, 0, &symval);
1421			if (result == ISC_R_SUCCESS) {
1422				cfg_obj_t *obj = symval.as_pointer;
1423				if (obj->type == &cfg_type_implicitlist) {
1424					/* Multivalued. */
1425					cfg_list_t *list = &obj->value.list;
1426					cfg_listelt_t *elt;
1427					for (elt = ISC_LIST_HEAD(*list);
1428					     elt != NULL;
1429					     elt = ISC_LIST_NEXT(elt, link)) {
1430						print_indent(pctx);
1431						cfg_print_cstr(pctx, clause->name);
1432						cfg_print_chars(pctx, " ", 1);
1433						cfg_print_obj(pctx, elt->obj);
1434						cfg_print_chars(pctx, ";\n", 2);
1435					}
1436				} else {
1437					/* Single-valued. */
1438					print_indent(pctx);
1439					cfg_print_cstr(pctx, clause->name);
1440					cfg_print_chars(pctx, " ", 1);
1441					cfg_print_obj(pctx, obj);
1442					cfg_print_chars(pctx, ";\n", 2);
1443				}
1444			} else if (result == ISC_R_NOTFOUND) {
1445				; /* do nothing */
1446			} else {
1447				INSIST(0);
1448			}
1449		}
1450	}
1451}
1452
1453void
1454cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
1455	const cfg_clausedef_t * const *clauseset;
1456	const cfg_clausedef_t *clause;
1457
1458	for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1459		for (clause = *clauseset;
1460		     clause->name != NULL;
1461		     clause++) {
1462			cfg_print_cstr(pctx, clause->name);
1463			cfg_print_chars(pctx, " ", 1);
1464			cfg_doc_obj(pctx, clause->type);
1465			cfg_print_chars(pctx, ";", 1);
1466			/* XXX print flags here? */
1467			cfg_print_chars(pctx, "\n\n", 2);
1468		}
1469	}
1470}
1471
1472static struct flagtext {
1473	unsigned int flag;
1474	const char *text;
1475} flagtexts[] = {
1476	{ CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
1477	{ CFG_CLAUSEFLAG_NYI, "not yet implemented" },
1478	{ CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
1479	{ CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
1480	{ CFG_CLAUSEFLAG_TESTONLY, "test only" },
1481	{ 0, NULL }
1482};
1483
1484void
1485cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1486	if (obj->value.map.id != NULL) {
1487		cfg_print_obj(pctx, obj->value.map.id);
1488		cfg_print_chars(pctx, " ", 1);
1489	}
1490	print_open(pctx);
1491	cfg_print_mapbody(pctx, obj);
1492	print_close(pctx);
1493}
1494
1495static void
1496print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
1497	struct flagtext *p;
1498	isc_boolean_t first = ISC_TRUE;
1499	for (p = flagtexts; p->flag != 0; p++) {
1500		if ((flags & p->flag) != 0) {
1501			if (first)
1502				cfg_print_chars(pctx, " // ", 4);
1503			else
1504				cfg_print_chars(pctx, ", ", 2);
1505			cfg_print_cstr(pctx, p->text);
1506			first = ISC_FALSE;
1507		}
1508	}
1509}
1510
1511void
1512cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
1513	const cfg_clausedef_t * const *clauseset;
1514	const cfg_clausedef_t *clause;
1515
1516	if (type->parse == cfg_parse_named_map) {
1517		cfg_doc_obj(pctx, &cfg_type_astring);
1518		cfg_print_chars(pctx, " ", 1);
1519	} else if (type->parse == cfg_parse_addressed_map) {
1520		cfg_doc_obj(pctx, &cfg_type_netaddr);
1521		cfg_print_chars(pctx, " ", 1);
1522	} else if (type->parse == cfg_parse_netprefix_map) {
1523		cfg_doc_obj(pctx, &cfg_type_netprefix);
1524		cfg_print_chars(pctx, " ", 1);
1525	}
1526
1527	print_open(pctx);
1528
1529	for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1530		for (clause = *clauseset;
1531		     clause->name != NULL;
1532		     clause++) {
1533			print_indent(pctx);
1534			cfg_print_cstr(pctx, clause->name);
1535			if (clause->type->print != cfg_print_void)
1536				cfg_print_chars(pctx, " ", 1);
1537			cfg_doc_obj(pctx, clause->type);
1538			cfg_print_chars(pctx, ";", 1);
1539			print_clause_flags(pctx, clause->flags);
1540			cfg_print_chars(pctx, "\n", 1);
1541		}
1542	}
1543	print_close(pctx);
1544}
1545
1546isc_boolean_t
1547cfg_obj_ismap(const cfg_obj_t *obj) {
1548	REQUIRE(obj != NULL);
1549	return (ISC_TF(obj->type->rep == &cfg_rep_map));
1550}
1551
1552isc_result_t
1553cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
1554	isc_result_t result;
1555	isc_symvalue_t val;
1556	const cfg_map_t *map;
1557
1558	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1559	REQUIRE(name != NULL);
1560	REQUIRE(obj != NULL && *obj == NULL);
1561
1562	map = &mapobj->value.map;
1563
1564	result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
1565	if (result != ISC_R_SUCCESS)
1566		return (result);
1567	*obj = val.as_pointer;
1568	return (ISC_R_SUCCESS);
1569}
1570
1571const cfg_obj_t *
1572cfg_map_getname(const cfg_obj_t *mapobj) {
1573	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1574	return (mapobj->value.map.id);
1575}
1576
1577
1578/* Parse an arbitrary token, storing its raw text representation. */
1579static isc_result_t
1580parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1581	cfg_obj_t *obj = NULL;
1582	isc_result_t result;
1583	isc_region_t r;
1584
1585	UNUSED(type);
1586
1587	CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
1588	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
1589	if (pctx->token.type == isc_tokentype_eof) {
1590		cfg_ungettoken(pctx);
1591		result = ISC_R_EOF;
1592		goto cleanup;
1593	}
1594
1595	isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
1596
1597	obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
1598	if (obj->value.string.base == NULL) {
1599		result = ISC_R_NOMEMORY;
1600		goto cleanup;
1601	}
1602	obj->value.string.length = r.length;
1603	memcpy(obj->value.string.base, r.base, r.length);
1604	obj->value.string.base[r.length] = '\0';
1605	*ret = obj;
1606	return (result);
1607
1608 cleanup:
1609	if (obj != NULL)
1610		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
1611	return (result);
1612}
1613
1614cfg_type_t cfg_type_token = {
1615	"token", parse_token, cfg_print_ustring, cfg_doc_terminal,
1616	&cfg_rep_string, NULL
1617};
1618
1619/*
1620 * An unsupported option.  This is just a list of tokens with balanced braces
1621 * ending in a semicolon.
1622 */
1623
1624static isc_result_t
1625parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1626	cfg_obj_t *listobj = NULL;
1627	isc_result_t result;
1628	int braces = 0;
1629
1630	CHECK(cfg_create_list(pctx, type, &listobj));
1631
1632	for (;;) {
1633		cfg_listelt_t *elt = NULL;
1634
1635		CHECK(cfg_peektoken(pctx, 0));
1636		if (pctx->token.type == isc_tokentype_special) {
1637			if (pctx->token.value.as_char == '{')
1638				braces++;
1639			else if (pctx->token.value.as_char == '}')
1640				braces--;
1641			else if (pctx->token.value.as_char == ';')
1642				if (braces == 0)
1643					break;
1644		}
1645		if (pctx->token.type == isc_tokentype_eof || braces < 0) {
1646			cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
1647			result = ISC_R_UNEXPECTEDTOKEN;
1648			goto cleanup;
1649		}
1650
1651		CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
1652		ISC_LIST_APPEND(listobj->value.list, elt, link);
1653	}
1654	INSIST(braces == 0);
1655	*ret = listobj;
1656	return (ISC_R_SUCCESS);
1657
1658 cleanup:
1659	CLEANUP_OBJ(listobj);
1660	return (result);
1661}
1662
1663cfg_type_t cfg_type_unsupported = {
1664	"unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal,
1665	&cfg_rep_list, NULL
1666};
1667
1668/*
1669 * Try interpreting the current token as a network address.
1670 *
1671 * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
1672 * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set.  The
1673 * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
1674 * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
1675 * and the IPv6 wildcard address otherwise.
1676 */
1677static isc_result_t
1678token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1679	char *s;
1680	struct in_addr in4a;
1681	struct in6_addr in6a;
1682
1683	if (pctx->token.type != isc_tokentype_string)
1684		return (ISC_R_UNEXPECTEDTOKEN);
1685
1686	s = TOKEN_STRING(pctx);
1687	if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
1688		if ((flags & CFG_ADDR_V4OK) != 0) {
1689			isc_netaddr_any(na);
1690			return (ISC_R_SUCCESS);
1691		} else if ((flags & CFG_ADDR_V6OK) != 0) {
1692			isc_netaddr_any6(na);
1693			return (ISC_R_SUCCESS);
1694		} else {
1695			INSIST(0);
1696		}
1697	} else {
1698		if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
1699			if (inet_pton(AF_INET, s, &in4a) == 1) {
1700				isc_netaddr_fromin(na, &in4a);
1701				return (ISC_R_SUCCESS);
1702			}
1703		}
1704		if ((flags & CFG_ADDR_V4PREFIXOK) != 0 &&
1705		    strlen(s) <= 15U) {
1706			char buf[64];
1707			int i;
1708
1709			strcpy(buf, s);
1710			for (i = 0; i < 3; i++) {
1711				strcat(buf, ".0");
1712				if (inet_pton(AF_INET, buf, &in4a) == 1) {
1713					isc_netaddr_fromin(na, &in4a);
1714					return (ISC_R_SUCCESS);
1715				}
1716			}
1717		}
1718		if ((flags & CFG_ADDR_V6OK) != 0 &&
1719		    strlen(s) <= 127U) {
1720			char buf[128]; /* see lib/bind9/getaddresses.c */
1721			char *d; /* zone delimiter */
1722			isc_uint32_t zone = 0; /* scope zone ID */
1723
1724			strcpy(buf, s);
1725			d = strchr(buf, '%');
1726			if (d != NULL)
1727				*d = '\0';
1728
1729			if (inet_pton(AF_INET6, buf, &in6a) == 1) {
1730				if (d != NULL) {
1731#ifdef ISC_PLATFORM_HAVESCOPEID
1732					isc_result_t result;
1733
1734					result = isc_netscope_pton(AF_INET6,
1735								   d + 1,
1736								   &in6a,
1737								   &zone);
1738					if (result != ISC_R_SUCCESS)
1739						return (result);
1740#else
1741				return (ISC_R_BADADDRESSFORM);
1742#endif
1743				}
1744
1745				isc_netaddr_fromin6(na, &in6a);
1746				isc_netaddr_setzone(na, zone);
1747				return (ISC_R_SUCCESS);
1748			}
1749		}
1750	}
1751	return (ISC_R_UNEXPECTEDTOKEN);
1752}
1753
1754isc_result_t
1755cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1756	isc_result_t result;
1757	const char *wild = "";
1758	const char *prefix = "";
1759
1760	CHECK(cfg_gettoken(pctx, 0));
1761	result = token_addr(pctx, flags, na);
1762	if (result == ISC_R_UNEXPECTEDTOKEN) {
1763		if ((flags & CFG_ADDR_WILDOK) != 0)
1764			wild = " or '*'";
1765		if ((flags & CFG_ADDR_V4PREFIXOK) != 0)
1766			wild = " or IPv4 prefix";
1767		if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V4OK)
1768			cfg_parser_error(pctx, CFG_LOG_NEAR,
1769					 "expected IPv4 address%s%s",
1770					 prefix, wild);
1771		else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK)
1772			cfg_parser_error(pctx, CFG_LOG_NEAR,
1773					 "expected IPv6 address%s%s",
1774					 prefix, wild);
1775		else
1776			cfg_parser_error(pctx, CFG_LOG_NEAR,
1777					 "expected IP address%s%s",
1778					 prefix, wild);
1779	}
1780 cleanup:
1781	return (result);
1782}
1783
1784isc_boolean_t
1785cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
1786	isc_result_t result;
1787	isc_netaddr_t na_dummy;
1788	result = token_addr(pctx, flags, &na_dummy);
1789	return (ISC_TF(result == ISC_R_SUCCESS));
1790}
1791
1792isc_result_t
1793cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
1794	isc_result_t result;
1795
1796	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1797
1798	if ((flags & CFG_ADDR_WILDOK) != 0 &&
1799	    pctx->token.type == isc_tokentype_string &&
1800	    strcmp(TOKEN_STRING(pctx), "*") == 0) {
1801		*port = 0;
1802		return (ISC_R_SUCCESS);
1803	}
1804	if (pctx->token.type != isc_tokentype_number) {
1805		cfg_parser_error(pctx, CFG_LOG_NEAR,
1806			     "expected port number or '*'");
1807		return (ISC_R_UNEXPECTEDTOKEN);
1808	}
1809	if (pctx->token.value.as_ulong >= 65536U) {
1810		cfg_parser_error(pctx, CFG_LOG_NEAR,
1811			     "port number out of range");
1812		return (ISC_R_UNEXPECTEDTOKEN);
1813	}
1814	*port = (in_port_t)(pctx->token.value.as_ulong);
1815	return (ISC_R_SUCCESS);
1816 cleanup:
1817	return (result);
1818}
1819
1820void
1821cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
1822	isc_result_t result;
1823	char text[128];
1824	isc_buffer_t buf;
1825
1826	isc_buffer_init(&buf, text, sizeof(text));
1827	result = isc_netaddr_totext(na, &buf);
1828	RUNTIME_CHECK(result == ISC_R_SUCCESS);
1829	cfg_print_chars(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf));
1830}
1831
1832/* netaddr */
1833
1834static unsigned int netaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
1835static unsigned int netaddr4_flags = CFG_ADDR_V4OK;
1836static unsigned int netaddr4wild_flags = CFG_ADDR_V4OK | CFG_ADDR_WILDOK;
1837static unsigned int netaddr6_flags = CFG_ADDR_V6OK;
1838static unsigned int netaddr6wild_flags = CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
1839
1840static isc_result_t
1841parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1842	isc_result_t result;
1843	cfg_obj_t *obj = NULL;
1844	isc_netaddr_t netaddr;
1845	unsigned int flags = *(const unsigned int *)type->of;
1846
1847	CHECK(cfg_create_obj(pctx, type, &obj));
1848	CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
1849	isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
1850	*ret = obj;
1851	return (ISC_R_SUCCESS);
1852 cleanup:
1853	CLEANUP_OBJ(obj);
1854	return (result);
1855}
1856
1857static void
1858cfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
1859	const unsigned int *flagp = type->of;
1860	int n = 0;
1861	if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
1862		cfg_print_chars(pctx, "( ", 2);
1863	if (*flagp & CFG_ADDR_V4OK) {
1864		cfg_print_cstr(pctx, "<ipv4_address>");
1865		n++;
1866	}
1867	if (*flagp & CFG_ADDR_V6OK) {
1868		if (n != 0)
1869			cfg_print_chars(pctx, " | ", 3);
1870		cfg_print_cstr(pctx, "<ipv6_address>");
1871		n++;
1872	}
1873	if (*flagp & CFG_ADDR_WILDOK) {
1874		if (n != 0)
1875			cfg_print_chars(pctx, " | ", 3);
1876		cfg_print_chars(pctx, "*", 1);
1877		n++;
1878	}
1879	if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
1880		cfg_print_chars(pctx, " )", 2);
1881}
1882
1883cfg_type_t cfg_type_netaddr = {
1884	"netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1885	&cfg_rep_sockaddr, &netaddr_flags
1886};
1887
1888cfg_type_t cfg_type_netaddr4 = {
1889	"netaddr4", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1890	&cfg_rep_sockaddr, &netaddr4_flags
1891};
1892
1893cfg_type_t cfg_type_netaddr4wild = {
1894	"netaddr4wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1895	&cfg_rep_sockaddr, &netaddr4wild_flags
1896};
1897
1898cfg_type_t cfg_type_netaddr6 = {
1899	"netaddr6", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1900	&cfg_rep_sockaddr, &netaddr6_flags
1901};
1902
1903cfg_type_t cfg_type_netaddr6wild = {
1904	"netaddr6wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1905	&cfg_rep_sockaddr, &netaddr6wild_flags
1906};
1907
1908/* netprefix */
1909
1910isc_result_t
1911cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
1912		    cfg_obj_t **ret)
1913{
1914	cfg_obj_t *obj = NULL;
1915	isc_result_t result;
1916	isc_netaddr_t netaddr;
1917	unsigned int addrlen, prefixlen;
1918	UNUSED(type);
1919
1920	CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
1921				CFG_ADDR_V6OK, &netaddr));
1922	switch (netaddr.family) {
1923	case AF_INET:
1924		addrlen = 32;
1925		break;
1926	case AF_INET6:
1927		addrlen = 128;
1928		break;
1929	default:
1930		addrlen = 0;
1931		INSIST(0);
1932		break;
1933	}
1934	CHECK(cfg_peektoken(pctx, 0));
1935	if (pctx->token.type == isc_tokentype_special &&
1936	    pctx->token.value.as_char == '/') {
1937		CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
1938		CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1939		if (pctx->token.type != isc_tokentype_number) {
1940			cfg_parser_error(pctx, CFG_LOG_NEAR,
1941				     "expected prefix length");
1942			return (ISC_R_UNEXPECTEDTOKEN);
1943		}
1944		prefixlen = pctx->token.value.as_ulong;
1945		if (prefixlen > addrlen) {
1946			cfg_parser_error(pctx, CFG_LOG_NOPREP,
1947				     "invalid prefix length");
1948			return (ISC_R_RANGE);
1949		}
1950	} else {
1951		prefixlen = addrlen;
1952	}
1953	CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
1954	obj->value.netprefix.address = netaddr;
1955	obj->value.netprefix.prefixlen = prefixlen;
1956	*ret = obj;
1957	return (ISC_R_SUCCESS);
1958 cleanup:
1959	cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
1960	return (result);
1961}
1962
1963static void
1964print_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1965	const cfg_netprefix_t *p = &obj->value.netprefix;
1966
1967	cfg_print_rawaddr(pctx, &p->address);
1968	cfg_print_chars(pctx, "/", 1);
1969	cfg_print_rawuint(pctx, p->prefixlen);
1970}
1971
1972isc_boolean_t
1973cfg_obj_isnetprefix(const cfg_obj_t *obj) {
1974	REQUIRE(obj != NULL);
1975	return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
1976}
1977
1978void
1979cfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr,
1980		    unsigned int *prefixlen) {
1981	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
1982	*netaddr = obj->value.netprefix.address;
1983	*prefixlen = obj->value.netprefix.prefixlen;
1984}
1985
1986cfg_type_t cfg_type_netprefix = {
1987	"netprefix", cfg_parse_netprefix, print_netprefix, cfg_doc_terminal,
1988	&cfg_rep_netprefix, NULL
1989};
1990
1991static isc_result_t
1992parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type,
1993		  int flags, cfg_obj_t **ret)
1994{
1995	isc_result_t result;
1996	isc_netaddr_t netaddr;
1997	in_port_t port = 0;
1998	cfg_obj_t *obj = NULL;
1999
2000	CHECK(cfg_create_obj(pctx, type, &obj));
2001	CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
2002	CHECK(cfg_peektoken(pctx, 0));
2003	if (pctx->token.type == isc_tokentype_string &&
2004	    strcasecmp(TOKEN_STRING(pctx), "port") == 0) {
2005		CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
2006		CHECK(cfg_parse_rawport(pctx, flags, &port));
2007	}
2008	isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
2009	*ret = obj;
2010	return (ISC_R_SUCCESS);
2011
2012 cleanup:
2013	CLEANUP_OBJ(obj);
2014	return (result);
2015}
2016
2017static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
2018cfg_type_t cfg_type_sockaddr = {
2019	"sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr,
2020	&cfg_rep_sockaddr, &sockaddr_flags
2021};
2022
2023isc_result_t
2024cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2025	const unsigned int *flagp = type->of;
2026	return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret));
2027}
2028
2029void
2030cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2031	isc_netaddr_t netaddr;
2032	in_port_t port;
2033	char buf[ISC_NETADDR_FORMATSIZE];
2034
2035	isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
2036	isc_netaddr_format(&netaddr, buf, sizeof(buf));
2037	cfg_print_cstr(pctx, buf);
2038	port = isc_sockaddr_getport(&obj->value.sockaddr);
2039	if (port != 0) {
2040		cfg_print_chars(pctx, " port ", 6);
2041		cfg_print_rawuint(pctx, port);
2042	}
2043}
2044
2045void
2046cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
2047	const unsigned int *flagp = type->of;
2048	int n = 0;
2049	cfg_print_chars(pctx, "( ", 2);
2050	if (*flagp & CFG_ADDR_V4OK) {
2051		cfg_print_cstr(pctx, "<ipv4_address>");
2052		n++;
2053	}
2054	if (*flagp & CFG_ADDR_V6OK) {
2055		if (n != 0)
2056			cfg_print_chars(pctx, " | ", 3);
2057		cfg_print_cstr(pctx, "<ipv6_address>");
2058		n++;
2059	}
2060	if (*flagp & CFG_ADDR_WILDOK) {
2061		if (n != 0)
2062			cfg_print_chars(pctx, " | ", 3);
2063		cfg_print_chars(pctx, "*", 1);
2064		n++;
2065	}
2066	cfg_print_chars(pctx, " ) ", 3);
2067	if (*flagp & CFG_ADDR_WILDOK) {
2068		cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
2069	} else {
2070		cfg_print_cstr(pctx, "[ port <integer> ]");
2071	}
2072}
2073
2074isc_boolean_t
2075cfg_obj_issockaddr(const cfg_obj_t *obj) {
2076	REQUIRE(obj != NULL);
2077	return (ISC_TF(obj->type->rep == &cfg_rep_sockaddr));
2078}
2079
2080const isc_sockaddr_t *
2081cfg_obj_assockaddr(const cfg_obj_t *obj) {
2082	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
2083	return (&obj->value.sockaddr);
2084}
2085
2086isc_result_t
2087cfg_gettoken(cfg_parser_t *pctx, int options) {
2088	isc_result_t result;
2089
2090	if (pctx->seen_eof)
2091		return (ISC_R_SUCCESS);
2092
2093	options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
2094
2095 redo:
2096	pctx->token.type = isc_tokentype_unknown;
2097	result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
2098	pctx->ungotten = ISC_FALSE;
2099	pctx->line = isc_lex_getsourceline(pctx->lexer);
2100
2101	switch (result) {
2102	case ISC_R_SUCCESS:
2103		if (pctx->token.type == isc_tokentype_eof) {
2104			result = isc_lex_close(pctx->lexer);
2105			INSIST(result == ISC_R_NOMORE ||
2106			       result == ISC_R_SUCCESS);
2107
2108			if (isc_lex_getsourcename(pctx->lexer) != NULL) {
2109				/*
2110				 * Closed an included file, not the main file.
2111				 */
2112				cfg_listelt_t *elt;
2113				elt = ISC_LIST_TAIL(pctx->open_files->
2114						    value.list);
2115				INSIST(elt != NULL);
2116				ISC_LIST_UNLINK(pctx->open_files->
2117						value.list, elt, link);
2118				ISC_LIST_APPEND(pctx->closed_files->
2119						value.list, elt, link);
2120				goto redo;
2121			}
2122			pctx->seen_eof = ISC_TRUE;
2123		}
2124		break;
2125
2126	case ISC_R_NOSPACE:
2127		/* More understandable than "ran out of space". */
2128		cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
2129		break;
2130
2131	case ISC_R_IOERROR:
2132		cfg_parser_error(pctx, 0, "%s",
2133				 isc_result_totext(result));
2134		break;
2135
2136	default:
2137		cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
2138				 isc_result_totext(result));
2139		break;
2140	}
2141	return (result);
2142}
2143
2144void
2145cfg_ungettoken(cfg_parser_t *pctx) {
2146	if (pctx->seen_eof)
2147		return;
2148	isc_lex_ungettoken(pctx->lexer, &pctx->token);
2149	pctx->ungotten = ISC_TRUE;
2150}
2151
2152isc_result_t
2153cfg_peektoken(cfg_parser_t *pctx, int options) {
2154	isc_result_t result;
2155	CHECK(cfg_gettoken(pctx, options));
2156	cfg_ungettoken(pctx);
2157 cleanup:
2158	return (result);
2159}
2160
2161/*
2162 * Get a string token, accepting both the quoted and the unquoted form.
2163 * Log an error if the next token is not a string.
2164 */
2165static isc_result_t
2166cfg_getstringtoken(cfg_parser_t *pctx) {
2167	isc_result_t result;
2168
2169	result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
2170	if (result != ISC_R_SUCCESS)
2171		return (result);
2172
2173	if (pctx->token.type != isc_tokentype_string &&
2174	    pctx->token.type != isc_tokentype_qstring) {
2175		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
2176		return (ISC_R_UNEXPECTEDTOKEN);
2177	}
2178	return (ISC_R_SUCCESS);
2179}
2180
2181void
2182cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2183	va_list args;
2184	va_start(args, fmt);
2185	parser_complain(pctx, ISC_FALSE, flags, fmt, args);
2186	va_end(args);
2187	pctx->errors++;
2188}
2189
2190void
2191cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2192	va_list args;
2193	va_start(args, fmt);
2194	parser_complain(pctx, ISC_TRUE, flags, fmt, args);
2195	va_end(args);
2196	pctx->warnings++;
2197}
2198
2199#define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
2200
2201static char *
2202current_file(cfg_parser_t *pctx) {
2203	static char none[] = "none";
2204	cfg_listelt_t *elt;
2205	cfg_obj_t *fileobj;
2206
2207	if (pctx->open_files == NULL)
2208		return (none);
2209	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
2210	if (elt == NULL)
2211	      return (none);
2212
2213	fileobj = elt->obj;
2214	INSIST(fileobj->type == &cfg_type_qstring);
2215	return (fileobj->value.string.base);
2216}
2217
2218static void
2219parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
2220		unsigned int flags, const char *format,
2221		va_list args)
2222{
2223	char tokenbuf[MAX_LOG_TOKEN + 10];
2224	static char where[ISC_DIR_PATHMAX + 100];
2225	static char message[2048];
2226	int level = ISC_LOG_ERROR;
2227	const char *prep = "";
2228	size_t len;
2229
2230	if (is_warning)
2231		level = ISC_LOG_WARNING;
2232
2233	snprintf(where, sizeof(where), "%s:%u: ",
2234		 current_file(pctx), pctx->line);
2235
2236	len = vsnprintf(message, sizeof(message), format, args);
2237	if (len >= sizeof(message))
2238		FATAL_ERROR(__FILE__, __LINE__,
2239			    "error message would overflow");
2240
2241	if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) {
2242		isc_region_t r;
2243
2244		if (pctx->ungotten)
2245			(void)cfg_gettoken(pctx, 0);
2246
2247		if (pctx->token.type == isc_tokentype_eof) {
2248			snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
2249		} else if (pctx->token.type == isc_tokentype_unknown) {
2250			flags = 0;
2251			tokenbuf[0] = '\0';
2252		} else {
2253			isc_lex_getlasttokentext(pctx->lexer,
2254						 &pctx->token, &r);
2255			if (r.length > MAX_LOG_TOKEN)
2256				snprintf(tokenbuf, sizeof(tokenbuf),
2257					 "'%.*s...'", MAX_LOG_TOKEN, r.base);
2258			else
2259				snprintf(tokenbuf, sizeof(tokenbuf),
2260					 "'%.*s'", (int)r.length, r.base);
2261		}
2262
2263		/* Choose a preposition. */
2264		if (flags & CFG_LOG_NEAR)
2265			prep = " near ";
2266		else if (flags & CFG_LOG_BEFORE)
2267			prep = " before ";
2268		else
2269			prep = " ";
2270	} else {
2271		tokenbuf[0] = '\0';
2272	}
2273	isc_log_write(pctx->lctx, CAT, MOD, level,
2274		      "%s%s%s%s", where, message, prep, tokenbuf);
2275}
2276
2277void
2278cfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level,
2279	    const char *fmt, ...) {
2280	va_list ap;
2281	char msgbuf[2048];
2282
2283	if (! isc_log_wouldlog(lctx, level))
2284		return;
2285
2286	va_start(ap, fmt);
2287
2288	vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
2289	isc_log_write(lctx, CAT, MOD, level,
2290		      "%s:%u: %s",
2291		      obj->file == NULL ? "<unknown file>" : obj->file,
2292		      obj->line, msgbuf);
2293	va_end(ap);
2294}
2295
2296const char *
2297cfg_obj_file(const cfg_obj_t *obj) {
2298	return (obj->file);
2299}
2300
2301unsigned int
2302cfg_obj_line(const cfg_obj_t *obj) {
2303	return (obj->line);
2304}
2305
2306isc_result_t
2307cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2308	cfg_obj_t *obj;
2309
2310	obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
2311	if (obj == NULL)
2312		return (ISC_R_NOMEMORY);
2313	obj->type = type;
2314	obj->file = current_file(pctx);
2315	obj->line = pctx->line;
2316	*ret = obj;
2317	return (ISC_R_SUCCESS);
2318}
2319
2320static void
2321map_symtabitem_destroy(char *key, unsigned int type,
2322		       isc_symvalue_t symval, void *userarg)
2323{
2324	cfg_obj_t *obj = symval.as_pointer;
2325	cfg_parser_t *pctx = (cfg_parser_t *)userarg;
2326
2327	UNUSED(key);
2328	UNUSED(type);
2329
2330	cfg_obj_destroy(pctx, &obj);
2331}
2332
2333
2334static isc_result_t
2335create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2336	isc_result_t result;
2337	isc_symtab_t *symtab = NULL;
2338	cfg_obj_t *obj = NULL;
2339
2340	CHECK(cfg_create_obj(pctx, type, &obj));
2341	CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */
2342				map_symtabitem_destroy,
2343				pctx, ISC_FALSE, &symtab));
2344	obj->value.map.symtab = symtab;
2345	obj->value.map.id = NULL;
2346
2347	*ret = obj;
2348	return (ISC_R_SUCCESS);
2349
2350 cleanup:
2351	if (obj != NULL)
2352		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
2353	return (result);
2354}
2355
2356static void
2357free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
2358	CLEANUP_OBJ(obj->value.map.id);
2359	isc_symtab_destroy(&obj->value.map.symtab);
2360}
2361
2362isc_boolean_t
2363cfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type) {
2364	return (ISC_TF(obj->type == type));
2365}
2366
2367/*
2368 * Destroy 'obj', a configuration object created in 'pctx'.
2369 */
2370void
2371cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
2372	cfg_obj_t *obj = *objp;
2373	obj->type->rep->free(pctx, obj);
2374	isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
2375	*objp = NULL;
2376}
2377
2378static void
2379free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
2380	UNUSED(pctx);
2381	UNUSED(obj);
2382}
2383
2384void
2385cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
2386	type->doc(pctx, type);
2387}
2388
2389void
2390cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) {
2391	cfg_print_chars(pctx, "<", 1);
2392	cfg_print_cstr(pctx, type->name);
2393	cfg_print_chars(pctx, ">", 1);
2394}
2395
2396void
2397cfg_print_grammar(const cfg_type_t *type,
2398	void (*f)(void *closure, const char *text, int textlen),
2399	void *closure)
2400{
2401	cfg_printer_t pctx;
2402	pctx.f = f;
2403	pctx.closure = closure;
2404	pctx.indent = 0;
2405	cfg_doc_obj(&pctx, type);
2406}
2407