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