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