parser.c revision 170222
1264790Sbapt/*
2264790Sbapt * Copyright (C) 2004-2006  Internet Systems Consortium, Inc. ("ISC")
3264790Sbapt * Copyright (C) 2000-2003  Internet Software Consortium.
4264790Sbapt *
5264790Sbapt * Permission to use, copy, modify, and distribute this software for any
6264790Sbapt * purpose with or without fee is hereby granted, provided that the above
7264790Sbapt * copyright notice and this permission notice appear in all copies.
8264790Sbapt *
9264790Sbapt * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10264790Sbapt * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11264790Sbapt * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12264790Sbapt * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13264790Sbapt * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14264790Sbapt * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15264790Sbapt * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* $Id: parser.c,v 1.112.18.11 2006/02/28 03:10:49 marka 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
1112const cfg_obj_t *
1113cfg_listelt_value(const cfg_listelt_t *elt) {
1114	REQUIRE(elt != NULL);
1115	return (elt->obj);
1116}
1117
1118/*
1119 * Maps.
1120 */
1121
1122/*
1123 * Parse a map body.  That's something like
1124 *
1125 *   "foo 1; bar { glub; }; zap true; zap false;"
1126 *
1127 * i.e., a sequence of option names followed by values and
1128 * terminated by semicolons.  Used for the top level of
1129 * the named.conf syntax, as well as for the body of the
1130 * options, view, zone, and other statements.
1131 */
1132isc_result_t
1133cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1134{
1135	const cfg_clausedef_t * const *clausesets = type->of;
1136	isc_result_t result;
1137	const cfg_clausedef_t * const *clauseset;
1138	const cfg_clausedef_t *clause;
1139	cfg_obj_t *value = NULL;
1140	cfg_obj_t *obj = NULL;
1141	cfg_obj_t *eltobj = NULL;
1142	cfg_obj_t *includename = NULL;
1143	isc_symvalue_t symval;
1144	cfg_list_t *list = NULL;
1145
1146	CHECK(create_map(pctx, type, &obj));
1147
1148	obj->value.map.clausesets = clausesets;
1149
1150	for (;;) {
1151		cfg_listelt_t *elt;
1152
1153	redo:
1154		/*
1155		 * Parse the option name and see if it is known.
1156		 */
1157		CHECK(cfg_gettoken(pctx, 0));
1158
1159		if (pctx->token.type != isc_tokentype_string) {
1160			cfg_ungettoken(pctx);
1161			break;
1162		}
1163
1164		/*
1165		 * We accept "include" statements wherever a map body
1166		 * clause can occur.
1167		 */
1168		if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
1169			/*
1170			 * Turn the file name into a temporary configuration
1171			 * object just so that it is not overwritten by the
1172			 * semicolon token.
1173			 */
1174			CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
1175			CHECK(parse_semicolon(pctx));
1176			CHECK(parser_openfile(pctx, includename->
1177					      value.string.base));
1178			 cfg_obj_destroy(pctx, &includename);
1179			 goto redo;
1180		}
1181
1182		clause = NULL;
1183		for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
1184			for (clause = *clauseset;
1185			     clause->name != NULL;
1186			     clause++) {
1187				if (strcasecmp(TOKEN_STRING(pctx),
1188					   clause->name) == 0)
1189					goto done;
1190			}
1191		}
1192	done:
1193		if (clause == NULL || clause->name == NULL) {
1194			cfg_parser_error(pctx, CFG_LOG_NOPREP, "unknown option");
1195			/*
1196			 * Try to recover by parsing this option as an unknown
1197			 * option and discarding it.
1198			 */
1199			CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, &eltobj));
1200			cfg_obj_destroy(pctx, &eltobj);
1201			CHECK(parse_semicolon(pctx));
1202			continue;
1203		}
1204
1205		/* Clause is known. */
1206
1207		/* Issue warnings if appropriate */
1208		if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0)
1209			cfg_parser_warning(pctx, 0, "option '%s' is obsolete",
1210				       clause->name);
1211		if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0)
1212			cfg_parser_warning(pctx, 0, "option '%s' is "
1213				       "not implemented", clause->name);
1214		if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0)
1215			cfg_parser_warning(pctx, 0, "option '%s' is "
1216				       "not implemented", clause->name);
1217		/*
1218		 * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
1219		 * set here - we need to log the *lack* of such an option,
1220		 * not its presence.
1221		 */
1222
1223		/* See if the clause already has a value; if not create one. */
1224		result = isc_symtab_lookup(obj->value.map.symtab,
1225					   clause->name, 0, &symval);
1226
1227		if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
1228			/* Multivalued clause */
1229			cfg_obj_t *listobj = NULL;
1230			if (result == ISC_R_NOTFOUND) {
1231				CHECK(cfg_create_list(pctx,
1232						  &cfg_type_implicitlist,
1233						  &listobj));
1234				symval.as_pointer = listobj;
1235				result = isc_symtab_define(obj->value.
1236						   map.symtab,
1237						   clause->name,
1238						   1, symval,
1239						   isc_symexists_reject);
1240				if (result != ISC_R_SUCCESS) {
1241					cfg_parser_error(pctx, CFG_LOG_NEAR,
1242						     "isc_symtab_define(%s) "
1243						     "failed", clause->name);
1244					isc_mem_put(pctx->mctx, list,
1245						    sizeof(cfg_list_t));
1246					goto cleanup;
1247				}
1248			} else {
1249				INSIST(result == ISC_R_SUCCESS);
1250				listobj = symval.as_pointer;
1251			}
1252
1253			elt = NULL;
1254			CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
1255			CHECK(parse_semicolon(pctx));
1256
1257			ISC_LIST_APPEND(listobj->value.list, elt, link);
1258		} else {
1259			/* Single-valued clause */
1260			if (result == ISC_R_NOTFOUND) {
1261				isc_boolean_t callback =
1262					ISC_TF((clause->flags &
1263						CFG_CLAUSEFLAG_CALLBACK) != 0);
1264				CHECK(parse_symtab_elt(pctx, clause->name,
1265						       clause->type,
1266						       obj->value.map.symtab,
1267						       callback));
1268				CHECK(parse_semicolon(pctx));
1269			} else if (result == ISC_R_SUCCESS) {
1270				cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
1271					     clause->name);
1272				result = ISC_R_EXISTS;
1273				goto cleanup;
1274			} else {
1275				cfg_parser_error(pctx, CFG_LOG_NEAR,
1276					     "isc_symtab_define() failed");
1277				goto cleanup;
1278			}
1279		}
1280	}
1281
1282
1283	*ret = obj;
1284	return (ISC_R_SUCCESS);
1285
1286 cleanup:
1287	CLEANUP_OBJ(value);
1288	CLEANUP_OBJ(obj);
1289	CLEANUP_OBJ(eltobj);
1290	CLEANUP_OBJ(includename);
1291	return (result);
1292}
1293
1294static isc_result_t
1295parse_symtab_elt(cfg_parser_t *pctx, const char *name,
1296		 cfg_type_t *elttype, isc_symtab_t *symtab,
1297		 isc_boolean_t callback)
1298{
1299	isc_result_t result;
1300	cfg_obj_t *obj = NULL;
1301	isc_symvalue_t symval;
1302
1303	CHECK(cfg_parse_obj(pctx, elttype, &obj));
1304
1305	if (callback && pctx->callback != NULL)
1306		CHECK(pctx->callback(name, obj, pctx->callbackarg));
1307
1308	symval.as_pointer = obj;
1309	CHECK(isc_symtab_define(symtab, name,
1310				1, symval,
1311				isc_symexists_reject));
1312	return (ISC_R_SUCCESS);
1313
1314 cleanup:
1315	CLEANUP_OBJ(obj);
1316	return (result);
1317}
1318
1319/*
1320 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
1321 */
1322isc_result_t
1323cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1324	isc_result_t result;
1325	CHECK(cfg_parse_special(pctx, '{'));
1326	CHECK(cfg_parse_mapbody(pctx, type, ret));
1327	CHECK(cfg_parse_special(pctx, '}'));
1328 cleanup:
1329	return (result);
1330}
1331
1332/*
1333 * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
1334 */
1335static isc_result_t
1336parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type,
1337		    cfg_obj_t **ret)
1338{
1339	isc_result_t result;
1340	cfg_obj_t *idobj = NULL;
1341	cfg_obj_t *mapobj = NULL;
1342
1343	CHECK(cfg_parse_obj(pctx, nametype, &idobj));
1344	CHECK(cfg_parse_map(pctx, type, &mapobj));
1345	mapobj->value.map.id = idobj;
1346	idobj = NULL;
1347	*ret = mapobj;
1348 cleanup:
1349	CLEANUP_OBJ(idobj);
1350	return (result);
1351}
1352
1353/*
1354 * Parse a map identified by a string name.  E.g., "name { foo 1; }".
1355 * Used for the "key" and "channel" statements.
1356 */
1357isc_result_t
1358cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1359	return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
1360}
1361
1362/*
1363 * Parse a map identified by a network address.
1364 * Used to be used for the "server" statement.
1365 */
1366isc_result_t
1367cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1368	return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
1369}
1370
1371/*
1372 * Parse a map identified by a network prefix.
1373 * Used for the "server" statement.
1374 */
1375isc_result_t
1376cfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1377	return (parse_any_named_map(pctx, &cfg_type_netprefix, type, ret));
1378}
1379
1380void
1381cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1382	isc_result_t result = ISC_R_SUCCESS;
1383
1384	const cfg_clausedef_t * const *clauseset;
1385
1386	for (clauseset = obj->value.map.clausesets;
1387	     *clauseset != NULL;
1388	     clauseset++)
1389	{
1390		isc_symvalue_t symval;
1391		const cfg_clausedef_t *clause;
1392
1393		for (clause = *clauseset;
1394		     clause->name != NULL;
1395		     clause++) {
1396			result = isc_symtab_lookup(obj->value.map.symtab,
1397						   clause->name, 0, &symval);
1398			if (result == ISC_R_SUCCESS) {
1399				cfg_obj_t *obj = symval.as_pointer;
1400				if (obj->type == &cfg_type_implicitlist) {
1401					/* Multivalued. */
1402					cfg_list_t *list = &obj->value.list;
1403					cfg_listelt_t *elt;
1404					for (elt = ISC_LIST_HEAD(*list);
1405					     elt != NULL;
1406					     elt = ISC_LIST_NEXT(elt, link)) {
1407						print_indent(pctx);
1408						cfg_print_cstr(pctx, clause->name);
1409						cfg_print_chars(pctx, " ", 1);
1410						cfg_print_obj(pctx, elt->obj);
1411						cfg_print_chars(pctx, ";\n", 2);
1412					}
1413				} else {
1414					/* Single-valued. */
1415					print_indent(pctx);
1416					cfg_print_cstr(pctx, clause->name);
1417					cfg_print_chars(pctx, " ", 1);
1418					cfg_print_obj(pctx, obj);
1419					cfg_print_chars(pctx, ";\n", 2);
1420				}
1421			} else if (result == ISC_R_NOTFOUND) {
1422				; /* do nothing */
1423			} else {
1424				INSIST(0);
1425			}
1426		}
1427	}
1428}
1429
1430void
1431cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
1432	const cfg_clausedef_t * const *clauseset;
1433	const cfg_clausedef_t *clause;
1434
1435	for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1436		for (clause = *clauseset;
1437		     clause->name != NULL;
1438		     clause++) {
1439			cfg_print_cstr(pctx, clause->name);
1440			cfg_print_chars(pctx, " ", 1);
1441			cfg_doc_obj(pctx, clause->type);
1442			cfg_print_chars(pctx, ";", 1);
1443			/* XXX print flags here? */
1444			cfg_print_chars(pctx, "\n\n", 2);
1445		}
1446	}
1447}
1448
1449static struct flagtext {
1450	unsigned int flag;
1451	const char *text;
1452} flagtexts[] = {
1453	{ CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
1454	{ CFG_CLAUSEFLAG_NYI, "not yet implemented" },
1455	{ CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
1456	{ CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
1457	{ 0, NULL }
1458};
1459
1460void
1461cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1462	if (obj->value.map.id != NULL) {
1463		cfg_print_obj(pctx, obj->value.map.id);
1464		cfg_print_chars(pctx, " ", 1);
1465	}
1466	print_open(pctx);
1467	cfg_print_mapbody(pctx, obj);
1468	print_close(pctx);
1469}
1470
1471static void
1472print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
1473	struct flagtext *p;
1474	isc_boolean_t first = ISC_TRUE;
1475	for (p = flagtexts; p->flag != 0; p++) {
1476		if ((flags & p->flag) != 0) {
1477			if (first)
1478				cfg_print_chars(pctx, " // ", 4);
1479			else
1480				cfg_print_chars(pctx, ", ", 2);
1481			cfg_print_cstr(pctx, p->text);
1482			first = ISC_FALSE;
1483		}
1484	}
1485}
1486
1487void
1488cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
1489	const cfg_clausedef_t * const *clauseset;
1490	const cfg_clausedef_t *clause;
1491
1492	if (type->parse == cfg_parse_named_map) {
1493		cfg_doc_obj(pctx, &cfg_type_astring);
1494		cfg_print_chars(pctx, " ", 1);
1495	} else if (type->parse == cfg_parse_addressed_map) {
1496		cfg_doc_obj(pctx, &cfg_type_netaddr);
1497		cfg_print_chars(pctx, " ", 1);
1498	} else if (type->parse == cfg_parse_netprefix_map) {
1499		cfg_doc_obj(pctx, &cfg_type_netprefix);
1500		cfg_print_chars(pctx, " ", 1);
1501	}
1502
1503	print_open(pctx);
1504
1505	for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1506		for (clause = *clauseset;
1507		     clause->name != NULL;
1508		     clause++) {
1509			print_indent(pctx);
1510			cfg_print_cstr(pctx, clause->name);
1511			if (clause->type->print != cfg_print_void)
1512				cfg_print_chars(pctx, " ", 1);
1513			cfg_doc_obj(pctx, clause->type);
1514			cfg_print_chars(pctx, ";", 1);
1515			print_clause_flags(pctx, clause->flags);
1516			cfg_print_chars(pctx, "\n", 1);
1517		}
1518	}
1519	print_close(pctx);
1520}
1521
1522isc_boolean_t
1523cfg_obj_ismap(const cfg_obj_t *obj) {
1524	REQUIRE(obj != NULL);
1525	return (ISC_TF(obj->type->rep == &cfg_rep_map));
1526}
1527
1528isc_result_t
1529cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
1530	isc_result_t result;
1531	isc_symvalue_t val;
1532	const cfg_map_t *map;
1533
1534	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1535	REQUIRE(name != NULL);
1536	REQUIRE(obj != NULL && *obj == NULL);
1537
1538	map = &mapobj->value.map;
1539
1540	result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
1541	if (result != ISC_R_SUCCESS)
1542		return (result);
1543	*obj = val.as_pointer;
1544	return (ISC_R_SUCCESS);
1545}
1546
1547const cfg_obj_t *
1548cfg_map_getname(const cfg_obj_t *mapobj) {
1549	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1550	return (mapobj->value.map.id);
1551}
1552
1553
1554/* Parse an arbitrary token, storing its raw text representation. */
1555static isc_result_t
1556parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1557	cfg_obj_t *obj = NULL;
1558        isc_result_t result;
1559	isc_region_t r;
1560
1561	UNUSED(type);
1562
1563	CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
1564	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
1565	if (pctx->token.type == isc_tokentype_eof) {
1566		cfg_ungettoken(pctx);
1567		result = ISC_R_EOF;
1568		goto cleanup;
1569	}
1570
1571	isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
1572
1573	obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
1574	if (obj->value.string.base == NULL) {
1575		result = ISC_R_NOMEMORY;
1576		goto cleanup;
1577	}
1578	obj->value.string.length = r.length;
1579	memcpy(obj->value.string.base, r.base, r.length);
1580	obj->value.string.base[r.length] = '\0';
1581	*ret = obj;
1582	return (result);
1583
1584 cleanup:
1585	if (obj != NULL)
1586		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
1587	return (result);
1588}
1589
1590cfg_type_t cfg_type_token = {
1591	"token", parse_token, cfg_print_ustring, cfg_doc_terminal,
1592	&cfg_rep_string, NULL
1593};
1594
1595/*
1596 * An unsupported option.  This is just a list of tokens with balanced braces
1597 * ending in a semicolon.
1598 */
1599
1600static isc_result_t
1601parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1602	cfg_obj_t *listobj = NULL;
1603	isc_result_t result;
1604	int braces = 0;
1605
1606	CHECK(cfg_create_list(pctx, type, &listobj));
1607
1608	for (;;) {
1609		cfg_listelt_t *elt = NULL;
1610
1611		CHECK(cfg_peektoken(pctx, 0));
1612		if (pctx->token.type == isc_tokentype_special) {
1613			if (pctx->token.value.as_char == '{')
1614				braces++;
1615			else if (pctx->token.value.as_char == '}')
1616				braces--;
1617			else if (pctx->token.value.as_char == ';')
1618				if (braces == 0)
1619					break;
1620		}
1621		if (pctx->token.type == isc_tokentype_eof || braces < 0) {
1622			cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
1623			result = ISC_R_UNEXPECTEDTOKEN;
1624			goto cleanup;
1625		}
1626
1627		CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
1628		ISC_LIST_APPEND(listobj->value.list, elt, link);
1629	}
1630	INSIST(braces == 0);
1631	*ret = listobj;
1632	return (ISC_R_SUCCESS);
1633
1634 cleanup:
1635	CLEANUP_OBJ(listobj);
1636	return (result);
1637}
1638
1639cfg_type_t cfg_type_unsupported = {
1640	"unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal,
1641	&cfg_rep_list, NULL
1642};
1643
1644/*
1645 * Try interpreting the current token as a network address.
1646 *
1647 * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
1648 * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set.  The
1649 * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
1650 * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
1651 * and the IPv6 wildcard address otherwise.
1652 */
1653static isc_result_t
1654token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1655	char *s;
1656	struct in_addr in4a;
1657	struct in6_addr in6a;
1658
1659	if (pctx->token.type != isc_tokentype_string)
1660		return (ISC_R_UNEXPECTEDTOKEN);
1661
1662	s = TOKEN_STRING(pctx);
1663	if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
1664		if ((flags & CFG_ADDR_V4OK) != 0) {
1665			isc_netaddr_any(na);
1666			return (ISC_R_SUCCESS);
1667		} else if ((flags & CFG_ADDR_V6OK) != 0) {
1668			isc_netaddr_any6(na);
1669			return (ISC_R_SUCCESS);
1670		} else {
1671			INSIST(0);
1672		}
1673	} else {
1674		if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
1675			if (inet_pton(AF_INET, s, &in4a) == 1) {
1676				isc_netaddr_fromin(na, &in4a);
1677				return (ISC_R_SUCCESS);
1678			}
1679		}
1680		if ((flags & CFG_ADDR_V4PREFIXOK) != 0 &&
1681		    strlen(s) <= 15U) {
1682			char buf[64];
1683			int i;
1684
1685			strcpy(buf, s);
1686			for (i = 0; i < 3; i++) {
1687				strcat(buf, ".0");
1688				if (inet_pton(AF_INET, buf, &in4a) == 1) {
1689					isc_netaddr_fromin(na, &in4a);
1690					return (ISC_R_SUCCESS);
1691				}
1692			}
1693		}
1694		if ((flags & CFG_ADDR_V6OK) != 0 &&
1695		    strlen(s) <= 127U) {
1696			char buf[128]; /* see lib/bind9/getaddresses.c */
1697			char *d; /* zone delimiter */
1698			isc_uint32_t zone = 0; /* scope zone ID */
1699
1700			strcpy(buf, s);
1701			d = strchr(buf, '%');
1702			if (d != NULL)
1703				*d = '\0';
1704
1705			if (inet_pton(AF_INET6, buf, &in6a) == 1) {
1706				if (d != NULL) {
1707#ifdef ISC_PLATFORM_HAVESCOPEID
1708					isc_result_t result;
1709
1710					result = isc_netscope_pton(AF_INET6,
1711								   d + 1,
1712								   &in6a,
1713								   &zone);
1714					if (result != ISC_R_SUCCESS)
1715						return (result);
1716#else
1717				return (ISC_R_BADADDRESSFORM);
1718#endif
1719				}
1720
1721				isc_netaddr_fromin6(na, &in6a);
1722				isc_netaddr_setzone(na, zone);
1723				return (ISC_R_SUCCESS);
1724			}
1725		}
1726	}
1727	return (ISC_R_UNEXPECTEDTOKEN);
1728}
1729
1730isc_result_t
1731cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1732	isc_result_t result;
1733	const char *wild = "";
1734	const char *prefix = "";
1735
1736	CHECK(cfg_gettoken(pctx, 0));
1737	result = token_addr(pctx, flags, na);
1738	if (result == ISC_R_UNEXPECTEDTOKEN) {
1739		if ((flags & CFG_ADDR_WILDOK) != 0)
1740			wild = " or '*'";
1741		if ((flags & CFG_ADDR_V4PREFIXOK) != 0)
1742			wild = " or IPv4 prefix";
1743		if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V4OK)
1744			cfg_parser_error(pctx, CFG_LOG_NEAR,
1745					 "expected IPv4 address%s%s",
1746					 prefix, wild);
1747		else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK)
1748			cfg_parser_error(pctx, CFG_LOG_NEAR,
1749					 "expected IPv6 address%s%s",
1750					 prefix, wild);
1751		else
1752			cfg_parser_error(pctx, CFG_LOG_NEAR,
1753					 "expected IP address%s%s",
1754					 prefix, wild);
1755	}
1756 cleanup:
1757	return (result);
1758}
1759
1760isc_boolean_t
1761cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
1762	isc_result_t result;
1763	isc_netaddr_t na_dummy;
1764	result = token_addr(pctx, flags, &na_dummy);
1765	return (ISC_TF(result == ISC_R_SUCCESS));
1766}
1767
1768isc_result_t
1769cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
1770	isc_result_t result;
1771
1772	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1773
1774	if ((flags & CFG_ADDR_WILDOK) != 0 &&
1775	    pctx->token.type == isc_tokentype_string &&
1776	    strcmp(TOKEN_STRING(pctx), "*") == 0) {
1777		*port = 0;
1778		return (ISC_R_SUCCESS);
1779	}
1780	if (pctx->token.type != isc_tokentype_number) {
1781		cfg_parser_error(pctx, CFG_LOG_NEAR,
1782			     "expected port number or '*'");
1783		return (ISC_R_UNEXPECTEDTOKEN);
1784	}
1785	if (pctx->token.value.as_ulong >= 65536U) {
1786		cfg_parser_error(pctx, CFG_LOG_NEAR,
1787			     "port number out of range");
1788		return (ISC_R_UNEXPECTEDTOKEN);
1789	}
1790	*port = (in_port_t)(pctx->token.value.as_ulong);
1791	return (ISC_R_SUCCESS);
1792 cleanup:
1793	return (result);
1794}
1795
1796void
1797cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
1798	isc_result_t result;
1799	char text[128];
1800	isc_buffer_t buf;
1801
1802	isc_buffer_init(&buf, text, sizeof(text));
1803	result = isc_netaddr_totext(na, &buf);
1804	RUNTIME_CHECK(result == ISC_R_SUCCESS);
1805	cfg_print_chars(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf));
1806}
1807
1808/* netaddr */
1809
1810static unsigned int netaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
1811static unsigned int netaddr4_flags = CFG_ADDR_V4OK;
1812static unsigned int netaddr4wild_flags = CFG_ADDR_V4OK | CFG_ADDR_WILDOK;
1813static unsigned int netaddr6_flags = CFG_ADDR_V6OK;
1814static unsigned int netaddr6wild_flags = CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
1815
1816static isc_result_t
1817parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1818	isc_result_t result;
1819	cfg_obj_t *obj = NULL;
1820	isc_netaddr_t netaddr;
1821	unsigned int flags = *(const unsigned int *)type->of;
1822
1823	CHECK(cfg_create_obj(pctx, type, &obj));
1824	CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
1825	isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
1826	*ret = obj;
1827	return (ISC_R_SUCCESS);
1828 cleanup:
1829	CLEANUP_OBJ(obj);
1830	return (result);
1831}
1832
1833static void
1834cfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
1835	const unsigned int *flagp = type->of;
1836	int n = 0;
1837	if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
1838		cfg_print_chars(pctx, "( ", 2);
1839	if (*flagp & CFG_ADDR_V4OK) {
1840		cfg_print_cstr(pctx, "<ipv4_address>");
1841		n++;
1842	}
1843	if (*flagp & CFG_ADDR_V6OK) {
1844		if (n != 0)
1845			cfg_print_chars(pctx, " | ", 3);
1846		cfg_print_cstr(pctx, "<ipv6_address>");
1847		n++;
1848	}
1849	if (*flagp & CFG_ADDR_WILDOK) {
1850		if (n != 0)
1851			cfg_print_chars(pctx, " | ", 3);
1852		cfg_print_chars(pctx, "*", 1);
1853		n++;
1854	}
1855	if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
1856		cfg_print_chars(pctx, " )", 2);
1857}
1858
1859cfg_type_t cfg_type_netaddr = {
1860	"netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1861	&cfg_rep_sockaddr, &netaddr_flags
1862};
1863
1864cfg_type_t cfg_type_netaddr4 = {
1865	"netaddr4", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1866	&cfg_rep_sockaddr, &netaddr4_flags
1867};
1868
1869cfg_type_t cfg_type_netaddr4wild = {
1870	"netaddr4wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1871	&cfg_rep_sockaddr, &netaddr4wild_flags
1872};
1873
1874cfg_type_t cfg_type_netaddr6 = {
1875	"netaddr6", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1876	&cfg_rep_sockaddr, &netaddr6_flags
1877};
1878
1879cfg_type_t cfg_type_netaddr6wild = {
1880	"netaddr6wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1881	&cfg_rep_sockaddr, &netaddr6wild_flags
1882};
1883
1884/* netprefix */
1885
1886isc_result_t
1887cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
1888		    cfg_obj_t **ret)
1889{
1890	cfg_obj_t *obj = NULL;
1891	isc_result_t result;
1892	isc_netaddr_t netaddr;
1893	unsigned int addrlen, prefixlen;
1894	UNUSED(type);
1895
1896	CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
1897				CFG_ADDR_V6OK, &netaddr));
1898	switch (netaddr.family) {
1899	case AF_INET:
1900		addrlen = 32;
1901		break;
1902	case AF_INET6:
1903		addrlen = 128;
1904		break;
1905	default:
1906		addrlen = 0;
1907		INSIST(0);
1908		break;
1909	}
1910	CHECK(cfg_peektoken(pctx, 0));
1911	if (pctx->token.type == isc_tokentype_special &&
1912	    pctx->token.value.as_char == '/') {
1913		CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
1914		CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1915		if (pctx->token.type != isc_tokentype_number) {
1916			cfg_parser_error(pctx, CFG_LOG_NEAR,
1917				     "expected prefix length");
1918			return (ISC_R_UNEXPECTEDTOKEN);
1919		}
1920		prefixlen = pctx->token.value.as_ulong;
1921		if (prefixlen > addrlen) {
1922			cfg_parser_error(pctx, CFG_LOG_NOPREP,
1923				     "invalid prefix length");
1924			return (ISC_R_RANGE);
1925		}
1926	} else {
1927		prefixlen = addrlen;
1928	}
1929	CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
1930	obj->value.netprefix.address = netaddr;
1931	obj->value.netprefix.prefixlen = prefixlen;
1932	*ret = obj;
1933	return (ISC_R_SUCCESS);
1934 cleanup:
1935	cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
1936	return (result);
1937}
1938
1939static void
1940print_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1941	const cfg_netprefix_t *p = &obj->value.netprefix;
1942
1943	cfg_print_rawaddr(pctx, &p->address);
1944	cfg_print_chars(pctx, "/", 1);
1945	cfg_print_rawuint(pctx, p->prefixlen);
1946}
1947
1948isc_boolean_t
1949cfg_obj_isnetprefix(const cfg_obj_t *obj) {
1950	REQUIRE(obj != NULL);
1951	return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
1952}
1953
1954void
1955cfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr,
1956		    unsigned int *prefixlen) {
1957	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
1958	*netaddr = obj->value.netprefix.address;
1959	*prefixlen = obj->value.netprefix.prefixlen;
1960}
1961
1962cfg_type_t cfg_type_netprefix = {
1963	"netprefix", cfg_parse_netprefix, print_netprefix, cfg_doc_terminal,
1964	&cfg_rep_netprefix, NULL
1965};
1966
1967static isc_result_t
1968parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type,
1969		  int flags, cfg_obj_t **ret)
1970{
1971	isc_result_t result;
1972	isc_netaddr_t netaddr;
1973	in_port_t port = 0;
1974	cfg_obj_t *obj = NULL;
1975
1976	CHECK(cfg_create_obj(pctx, type, &obj));
1977	CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
1978	CHECK(cfg_peektoken(pctx, 0));
1979	if (pctx->token.type == isc_tokentype_string &&
1980	    strcasecmp(TOKEN_STRING(pctx), "port") == 0) {
1981		CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
1982		CHECK(cfg_parse_rawport(pctx, flags, &port));
1983	}
1984	isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
1985	*ret = obj;
1986	return (ISC_R_SUCCESS);
1987
1988 cleanup:
1989	CLEANUP_OBJ(obj);
1990	return (result);
1991}
1992
1993static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
1994cfg_type_t cfg_type_sockaddr = {
1995	"sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr,
1996	&cfg_rep_sockaddr, &sockaddr_flags
1997};
1998
1999isc_result_t
2000cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2001	const unsigned int *flagp = type->of;
2002	return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret));
2003}
2004
2005void
2006cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2007	isc_netaddr_t netaddr;
2008	in_port_t port;
2009	char buf[ISC_NETADDR_FORMATSIZE];
2010
2011	isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
2012	isc_netaddr_format(&netaddr, buf, sizeof(buf));
2013	cfg_print_cstr(pctx, buf);
2014	port = isc_sockaddr_getport(&obj->value.sockaddr);
2015	if (port != 0) {
2016		cfg_print_chars(pctx, " port ", 6);
2017		cfg_print_rawuint(pctx, port);
2018	}
2019}
2020
2021void
2022cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
2023	const unsigned int *flagp = type->of;
2024	int n = 0;
2025	cfg_print_chars(pctx, "( ", 2);
2026	if (*flagp & CFG_ADDR_V4OK) {
2027		cfg_print_cstr(pctx, "<ipv4_address>");
2028		n++;
2029	}
2030	if (*flagp & CFG_ADDR_V6OK) {
2031		if (n != 0)
2032			cfg_print_chars(pctx, " | ", 3);
2033		cfg_print_cstr(pctx, "<ipv6_address>");
2034		n++;
2035	}
2036	if (*flagp & CFG_ADDR_WILDOK) {
2037		if (n != 0)
2038			cfg_print_chars(pctx, " | ", 3);
2039		cfg_print_chars(pctx, "*", 1);
2040		n++;
2041	}
2042	cfg_print_chars(pctx, " ) ", 3);
2043	if (*flagp & CFG_ADDR_WILDOK) {
2044		cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
2045	} else {
2046		cfg_print_cstr(pctx, "[ port <integer> ]");
2047	}
2048}
2049
2050isc_boolean_t
2051cfg_obj_issockaddr(const cfg_obj_t *obj) {
2052	REQUIRE(obj != NULL);
2053	return (ISC_TF(obj->type->rep == &cfg_rep_sockaddr));
2054}
2055
2056const isc_sockaddr_t *
2057cfg_obj_assockaddr(const cfg_obj_t *obj) {
2058	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
2059	return (&obj->value.sockaddr);
2060}
2061
2062isc_result_t
2063cfg_gettoken(cfg_parser_t *pctx, int options) {
2064	isc_result_t result;
2065
2066	if (pctx->seen_eof)
2067		return (ISC_R_SUCCESS);
2068
2069	options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
2070
2071 redo:
2072	pctx->token.type = isc_tokentype_unknown;
2073	result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
2074	pctx->ungotten = ISC_FALSE;
2075	pctx->line = isc_lex_getsourceline(pctx->lexer);
2076
2077	switch (result) {
2078	case ISC_R_SUCCESS:
2079		if (pctx->token.type == isc_tokentype_eof) {
2080			result = isc_lex_close(pctx->lexer);
2081			INSIST(result == ISC_R_NOMORE ||
2082			       result == ISC_R_SUCCESS);
2083
2084			if (isc_lex_getsourcename(pctx->lexer) != NULL) {
2085				/*
2086				 * Closed an included file, not the main file.
2087				 */
2088				cfg_listelt_t *elt;
2089				elt = ISC_LIST_TAIL(pctx->open_files->
2090						    value.list);
2091				INSIST(elt != NULL);
2092				ISC_LIST_UNLINK(pctx->open_files->
2093						value.list, elt, link);
2094				ISC_LIST_APPEND(pctx->closed_files->
2095						value.list, elt, link);
2096				goto redo;
2097			}
2098			pctx->seen_eof = ISC_TRUE;
2099		}
2100		break;
2101
2102	case ISC_R_NOSPACE:
2103		/* More understandable than "ran out of space". */
2104		cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
2105		break;
2106
2107	case ISC_R_IOERROR:
2108		cfg_parser_error(pctx, 0, "%s",
2109				 isc_result_totext(result));
2110		break;
2111
2112	default:
2113		cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
2114				 isc_result_totext(result));
2115		break;
2116	}
2117	return (result);
2118}
2119
2120void
2121cfg_ungettoken(cfg_parser_t *pctx) {
2122	if (pctx->seen_eof)
2123		return;
2124	isc_lex_ungettoken(pctx->lexer, &pctx->token);
2125	pctx->ungotten = ISC_TRUE;
2126}
2127
2128isc_result_t
2129cfg_peektoken(cfg_parser_t *pctx, int options) {
2130	isc_result_t result;
2131	CHECK(cfg_gettoken(pctx, options));
2132	cfg_ungettoken(pctx);
2133 cleanup:
2134	return (result);
2135}
2136
2137/*
2138 * Get a string token, accepting both the quoted and the unquoted form.
2139 * Log an error if the next token is not a string.
2140 */
2141static isc_result_t
2142cfg_getstringtoken(cfg_parser_t *pctx) {
2143	isc_result_t result;
2144
2145	result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
2146	if (result != ISC_R_SUCCESS)
2147		return (result);
2148
2149	if (pctx->token.type != isc_tokentype_string &&
2150	    pctx->token.type != isc_tokentype_qstring) {
2151		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
2152		return (ISC_R_UNEXPECTEDTOKEN);
2153	}
2154	return (ISC_R_SUCCESS);
2155}
2156
2157void
2158cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2159	va_list args;
2160	va_start(args, fmt);
2161	parser_complain(pctx, ISC_FALSE, flags, fmt, args);
2162	va_end(args);
2163	pctx->errors++;
2164}
2165
2166void
2167cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2168	va_list args;
2169	va_start(args, fmt);
2170	parser_complain(pctx, ISC_TRUE, flags, fmt, args);
2171	va_end(args);
2172	pctx->warnings++;
2173}
2174
2175#define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
2176
2177static char *
2178current_file(cfg_parser_t *pctx) {
2179	static char none[] = "none";
2180	cfg_listelt_t *elt;
2181	cfg_obj_t *fileobj;
2182
2183	if (pctx->open_files == NULL)
2184		return (none);
2185	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
2186	if (elt == NULL)
2187	      return (none);
2188
2189	fileobj = elt->obj;
2190	INSIST(fileobj->type == &cfg_type_qstring);
2191	return (fileobj->value.string.base);
2192}
2193
2194static void
2195parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
2196		unsigned int flags, const char *format,
2197		va_list args)
2198{
2199	char tokenbuf[MAX_LOG_TOKEN + 10];
2200	static char where[ISC_DIR_PATHMAX + 100];
2201	static char message[2048];
2202	int level = ISC_LOG_ERROR;
2203	const char *prep = "";
2204	size_t len;
2205
2206	if (is_warning)
2207		level = ISC_LOG_WARNING;
2208
2209	snprintf(where, sizeof(where), "%s:%u: ",
2210		 current_file(pctx), pctx->line);
2211
2212	len = vsnprintf(message, sizeof(message), format, args);
2213	if (len >= sizeof(message))
2214		FATAL_ERROR(__FILE__, __LINE__,
2215			    "error message would overflow");
2216
2217	if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) {
2218		isc_region_t r;
2219
2220		if (pctx->ungotten)
2221			(void)cfg_gettoken(pctx, 0);
2222
2223		if (pctx->token.type == isc_tokentype_eof) {
2224			snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
2225		} else if (pctx->token.type == isc_tokentype_unknown) {
2226			flags = 0;
2227			tokenbuf[0] = '\0';
2228		} else {
2229			isc_lex_getlasttokentext(pctx->lexer,
2230						 &pctx->token, &r);
2231			if (r.length > MAX_LOG_TOKEN)
2232				snprintf(tokenbuf, sizeof(tokenbuf),
2233					 "'%.*s...'", MAX_LOG_TOKEN, r.base);
2234			else
2235				snprintf(tokenbuf, sizeof(tokenbuf),
2236					 "'%.*s'", (int)r.length, r.base);
2237		}
2238
2239		/* Choose a preposition. */
2240		if (flags & CFG_LOG_NEAR)
2241			prep = " near ";
2242		else if (flags & CFG_LOG_BEFORE)
2243			prep = " before ";
2244		else
2245			prep = " ";
2246	} else {
2247		tokenbuf[0] = '\0';
2248	}
2249	isc_log_write(pctx->lctx, CAT, MOD, level,
2250		      "%s%s%s%s", where, message, prep, tokenbuf);
2251}
2252
2253void
2254cfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level,
2255	    const char *fmt, ...) {
2256	va_list ap;
2257	char msgbuf[2048];
2258
2259	if (! isc_log_wouldlog(lctx, level))
2260		return;
2261
2262	va_start(ap, fmt);
2263
2264	vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
2265	isc_log_write(lctx, CAT, MOD, level,
2266		      "%s:%u: %s",
2267		      obj->file == NULL ? "<unknown file>" : obj->file,
2268		      obj->line, msgbuf);
2269	va_end(ap);
2270}
2271
2272const char *
2273cfg_obj_file(const cfg_obj_t *obj) {
2274	return (obj->file);
2275}
2276
2277unsigned int
2278cfg_obj_line(const cfg_obj_t *obj) {
2279	return (obj->line);
2280}
2281
2282isc_result_t
2283cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2284	cfg_obj_t *obj;
2285
2286	obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
2287	if (obj == NULL)
2288		return (ISC_R_NOMEMORY);
2289	obj->type = type;
2290	obj->file = current_file(pctx);
2291	obj->line = pctx->line;
2292	*ret = obj;
2293	return (ISC_R_SUCCESS);
2294}
2295
2296static void
2297map_symtabitem_destroy(char *key, unsigned int type,
2298		       isc_symvalue_t symval, void *userarg)
2299{
2300	cfg_obj_t *obj = symval.as_pointer;
2301	cfg_parser_t *pctx = (cfg_parser_t *)userarg;
2302
2303	UNUSED(key);
2304	UNUSED(type);
2305
2306	cfg_obj_destroy(pctx, &obj);
2307}
2308
2309
2310static isc_result_t
2311create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2312	isc_result_t result;
2313	isc_symtab_t *symtab = NULL;
2314	cfg_obj_t *obj = NULL;
2315
2316	CHECK(cfg_create_obj(pctx, type, &obj));
2317	CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */
2318				map_symtabitem_destroy,
2319				pctx, ISC_FALSE, &symtab));
2320	obj->value.map.symtab = symtab;
2321	obj->value.map.id = NULL;
2322
2323	*ret = obj;
2324	return (ISC_R_SUCCESS);
2325
2326 cleanup:
2327	if (obj != NULL)
2328		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
2329	return (result);
2330}
2331
2332static void
2333free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
2334	CLEANUP_OBJ(obj->value.map.id);
2335	isc_symtab_destroy(&obj->value.map.symtab);
2336}
2337
2338isc_boolean_t
2339cfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type) {
2340	return (ISC_TF(obj->type == type));
2341}
2342
2343/*
2344 * Destroy 'obj', a configuration object created in 'pctx'.
2345 */
2346void
2347cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
2348	cfg_obj_t *obj = *objp;
2349	obj->type->rep->free(pctx, obj);
2350	isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
2351	*objp = NULL;
2352}
2353
2354static void
2355free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
2356	UNUSED(pctx);
2357	UNUSED(obj);
2358}
2359
2360void
2361cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
2362	type->doc(pctx, type);
2363}
2364
2365void
2366cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) {
2367	cfg_print_chars(pctx, "<", 1);
2368	cfg_print_cstr(pctx, type->name);
2369	cfg_print_chars(pctx, ">", 1);
2370}
2371
2372void
2373cfg_print_grammar(const cfg_type_t *type,
2374	void (*f)(void *closure, const char *text, int textlen),
2375	void *closure)
2376{
2377	cfg_printer_t pctx;
2378	pctx.f = f;
2379	pctx.closure = closure;
2380	pctx.indent = 0;
2381	cfg_doc_obj(&pctx, type);
2382}
2383