xref: /openbsd/usr.bin/dig/lib/isccfg/parser.c (revision 3cab2bb3)
1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14  * PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 /*! \file */
18 
19 #include <limits.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include <isc/lex.h>
24 #include <isc/log.h>
25 #include <isc/symtab.h>
26 #include <isc/util.h>
27 
28 #include <isccfg/cfg.h>
29 #include <isccfg/grammar.h>
30 
31 /*!
32  * Pass one of these flags to cfg_parser_error() to include the
33  * token text in log message.
34  */
35 #define CFG_LOG_NEAR    0x00000001	/*%< Say "near <token>" */
36 #define CFG_LOG_BEFORE  0x00000002	/*%< Say "before <token>" */
37 #define CFG_LOG_NOPREP  0x00000004	/*%< Say just "<token>" */
38 
39 isc_logcategory_t cfg_category = { "config",     0 };
40 isc_logmodule_t cfg_module = { "isccfg/parser",      0 };
41 
42 /* Shorthand */
43 #define CAT &cfg_category
44 #define MOD &cfg_module
45 
46 #define MAP_SYM 1 	/* Unique type for isc_symtab */
47 
48 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
49 
50 #define CFG_LEXOPT_QSTRING (ISC_LEXOPT_QSTRING | ISC_LEXOPT_QSTRINGMULTILINE)
51 
52 /* Check a return value. */
53 #define CHECK(op) 						\
54 	do { result = (op); 					\
55 		if (result != ISC_R_SUCCESS) goto cleanup; 	\
56 	} while (0)
57 
58 /* Clean up a configuration object if non-NULL. */
59 #define CLEANUP_OBJ(obj) \
60 	do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
61 
62 /* Forward declarations of variables */
63 cfg_rep_t cfg_rep_string;
64 cfg_rep_t cfg_rep_list;
65 
66 cfg_type_t cfg_type_qstring;
67 cfg_type_t cfg_type_sstring;
68 cfg_type_t cfg_type_token;
69 cfg_type_t cfg_type_unsupported;
70 
71 /*
72  * Forward declarations of static functions.
73  */
74 
75 static isc_result_t
76 cfg_gettoken(cfg_parser_t *pctx, int options);
77 
78 static isc_result_t
79 cfg_peektoken(cfg_parser_t *pctx, int options);
80 
81 static void
82 cfg_ungettoken(cfg_parser_t *pctx);
83 
84 static isc_result_t
85 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
86 
87 static isc_result_t
88 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
89 
90 static isc_result_t
91 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
92 
93 static isc_result_t
94 cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
95 
96 static isc_result_t
97 cfg_parse_special(cfg_parser_t *pctx, int special);
98 /*%< Parse a required special character 'special'. */
99 
100 static isc_result_t
101 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
102 
103 static isc_result_t
104 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
105 		  cfg_listelt_t **ret);
106 
107 static isc_result_t
108 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
109 
110 static isc_result_t
111 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
112 
113 static void
114 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags,
115 		 const char *fmt, ...) __attribute__((__format__(__printf__, 3, 4)));
116 
117 static void
118 free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
119 
120 static isc_result_t
121 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
122 
123 static isc_result_t
124 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
125 	      cfg_obj_t **ret);
126 
127 static void
128 free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
129 
130 static isc_result_t
131 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
132 
133 static void
134 free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
135 
136 static isc_result_t
137 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
138 		 cfg_type_t *elttype, isc_symtab_t *symtab);
139 
140 static isc_result_t
141 cfg_getstringtoken(cfg_parser_t *pctx);
142 
143 static void
144 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
145 		unsigned int flags, const char *format, va_list args);
146 
147 /*
148  * Data representations.  These correspond to members of the
149  * "value" union in struct cfg_obj (except "void", which does
150  * not need a union member).
151  */
152 
153 cfg_rep_t cfg_rep_string = { "string", free_string };
154 cfg_rep_t cfg_rep_map = { "map", free_map };
155 cfg_rep_t cfg_rep_list = { "list", free_list };
156 
157 /*
158  * Configuration type definitions.
159  */
160 
161 /* Functions. */
162 
163 static isc_result_t
164 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
165 	isc_result_t result;
166 
167 	REQUIRE(pctx != NULL);
168 	REQUIRE(type != NULL);
169 	REQUIRE(ret != NULL && *ret == NULL);
170 
171 	result = type->parse(pctx, type, ret);
172 	if (result != ISC_R_SUCCESS)
173 		return (result);
174 	ENSURE(*ret != NULL);
175 	return (ISC_R_SUCCESS);
176 }
177 
178 static isc_result_t
179 cfg_parse_special(cfg_parser_t *pctx, int special) {
180 	isc_result_t result;
181 
182 	REQUIRE(pctx != NULL);
183 
184 	CHECK(cfg_gettoken(pctx, 0));
185 	if (pctx->token.type == isc_tokentype_special &&
186 	    pctx->token.value.as_char == special)
187 		return (ISC_R_SUCCESS);
188 
189 	cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
190 	return (ISC_R_UNEXPECTEDTOKEN);
191  cleanup:
192 	return (result);
193 }
194 
195 /*
196  * Parse a required semicolon.  If it is not there, log
197  * an error and increment the error count but continue
198  * parsing.  Since the next token is pushed back,
199  * care must be taken to make sure it is eventually
200  * consumed or an infinite loop may result.
201  */
202 static isc_result_t
203 parse_semicolon(cfg_parser_t *pctx) {
204 	isc_result_t result;
205 
206 	CHECK(cfg_gettoken(pctx, 0));
207 	if (pctx->token.type == isc_tokentype_special &&
208 	    pctx->token.value.as_char == ';')
209 		return (ISC_R_SUCCESS);
210 
211 	cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
212 	cfg_ungettoken(pctx);
213  cleanup:
214 	return (result);
215 }
216 
217 /*
218  * Parse EOF, logging and returning an error if not there.
219  */
220 static isc_result_t
221 parse_eof(cfg_parser_t *pctx) {
222 	isc_result_t result;
223 
224 	CHECK(cfg_gettoken(pctx, 0));
225 
226 	if (pctx->token.type == isc_tokentype_eof)
227 		return (ISC_R_SUCCESS);
228 
229 	cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
230 	return (ISC_R_UNEXPECTEDTOKEN);
231  cleanup:
232 	return (result);
233 }
234 
235 /* A list of files, used internally for pctx->files. */
236 
237 static cfg_type_t cfg_type_filelist = {
238 	"filelist", NULL, &cfg_rep_list,
239 	&cfg_type_qstring
240 };
241 
242 isc_result_t
243 cfg_parser_create(isc_log_t *lctx, cfg_parser_t **ret) {
244 	isc_result_t result;
245 	cfg_parser_t *pctx;
246 	isc_lexspecials_t specials;
247 
248 	REQUIRE(ret != NULL && *ret == NULL);
249 
250 	pctx = malloc(sizeof(*pctx));
251 	if (pctx == NULL)
252 		return (ISC_R_NOMEMORY);
253 
254 	result = isc_refcount_init(&pctx->references, 1);
255 	if (result != ISC_R_SUCCESS) {
256 		free(pctx);
257 		return (result);
258 	}
259 
260 	pctx->lctx = lctx;
261 	pctx->lexer = NULL;
262 	pctx->seen_eof = ISC_FALSE;
263 	pctx->ungotten = ISC_FALSE;
264 	pctx->errors = 0;
265 	pctx->open_files = NULL;
266 	pctx->closed_files = NULL;
267 	pctx->line = 0;
268 	pctx->token.type = isc_tokentype_unknown;
269 	pctx->flags = 0;
270 
271 	memset(specials, 0, sizeof(specials));
272 	specials['{'] = 1;
273 	specials['}'] = 1;
274 	specials[';'] = 1;
275 	specials['/'] = 1;
276 	specials['"'] = 1;
277 	specials['!'] = 1;
278 
279 	CHECK(isc_lex_create(1024, &pctx->lexer));
280 
281 	isc_lex_setspecials(pctx->lexer, specials);
282 	isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C |
283 					 ISC_LEXCOMMENT_CPLUSPLUS |
284 					 ISC_LEXCOMMENT_SHELL));
285 
286 	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
287 	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
288 
289 	*ret = pctx;
290 	return (ISC_R_SUCCESS);
291 
292  cleanup:
293 	if (pctx->lexer != NULL)
294 		isc_lex_destroy(&pctx->lexer);
295 	CLEANUP_OBJ(pctx->open_files);
296 	CLEANUP_OBJ(pctx->closed_files);
297 	free(pctx);
298 	return (result);
299 }
300 
301 static isc_result_t
302 parser_openfile(cfg_parser_t *pctx, const char *filename) {
303 	isc_result_t result;
304 	cfg_listelt_t *elt = NULL;
305 	cfg_obj_t *stringobj = NULL;
306 
307 	result = isc_lex_openfile(pctx->lexer, filename);
308 	if (result != ISC_R_SUCCESS) {
309 		cfg_parser_error(pctx, 0, "open: %s: %s",
310 			     filename, isc_result_totext(result));
311 		goto cleanup;
312 	}
313 
314 	CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
315 	CHECK(create_listelt(pctx, &elt));
316 	elt->obj = stringobj;
317 	ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
318 
319 	return (ISC_R_SUCCESS);
320  cleanup:
321 	CLEANUP_OBJ(stringobj);
322 	return (result);
323 }
324 
325 /*
326  * Parse a configuration using a pctx where a lexer has already
327  * been set up with a source.
328  */
329 static isc_result_t
330 parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
331 	isc_result_t result;
332 	cfg_obj_t *obj = NULL;
333 
334 	result = cfg_parse_obj(pctx, type, &obj);
335 
336 	if (pctx->errors != 0) {
337 		/* Errors have been logged. */
338 		if (result == ISC_R_SUCCESS)
339 			result = ISC_R_FAILURE;
340 		goto cleanup;
341 	}
342 
343 	if (result != ISC_R_SUCCESS) {
344 		/* Parsing failed but no errors have been logged. */
345 		cfg_parser_error(pctx, 0, "parsing failed: %s",
346 				 isc_result_totext(result));
347 		goto cleanup;
348 	}
349 
350 	CHECK(parse_eof(pctx));
351 
352 	*ret = obj;
353 	return (ISC_R_SUCCESS);
354 
355  cleanup:
356 	CLEANUP_OBJ(obj);
357 	return (result);
358 }
359 
360 isc_result_t
361 cfg_parse_file(cfg_parser_t *pctx, const char *filename,
362 	       const cfg_type_t *type, cfg_obj_t **ret)
363 {
364 	isc_result_t result;
365 
366 	REQUIRE(pctx != NULL);
367 	REQUIRE(filename != NULL);
368 	REQUIRE(type != NULL);
369 	REQUIRE(ret != NULL && *ret == NULL);
370 
371 	CHECK(parser_openfile(pctx, filename));
372 	CHECK(parse2(pctx, type, ret));
373  cleanup:
374 	return (result);
375 }
376 
377 void
378 cfg_parser_destroy(cfg_parser_t **pctxp) {
379 	cfg_parser_t *pctx;
380 	unsigned int refs;
381 
382 	REQUIRE(pctxp != NULL && *pctxp != NULL);
383 
384 	pctx = *pctxp;
385 	*pctxp = NULL;
386 
387 	isc_refcount_decrement(&pctx->references, &refs);
388 	if (refs == 0) {
389 		isc_lex_destroy(&pctx->lexer);
390 		/*
391 		 * Cleaning up open_files does not
392 		 * close the files; that was already done
393 		 * by closing the lexer.
394 		 */
395 		CLEANUP_OBJ(pctx->open_files);
396 		CLEANUP_OBJ(pctx->closed_files);
397 		free(pctx);
398 	}
399 }
400 
401 /*
402  * qstring (quoted string), ustring (unquoted string), astring
403  * (any string)
404  */
405 
406 /* Create a string object from a null-terminated C string. */
407 static isc_result_t
408 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
409 	      cfg_obj_t **ret)
410 {
411 	isc_result_t result;
412 	cfg_obj_t *obj = NULL;
413 	int len;
414 
415 	CHECK(cfg_create_obj(pctx, type, &obj));
416 	len = strlen(contents);
417 	obj->value.string.length = len;
418 	obj->value.string.base = malloc(len + 1);
419 	if (obj->value.string.base == NULL) {
420 		free(obj);
421 		return (ISC_R_NOMEMORY);
422 	}
423 	memmove(obj->value.string.base, contents, len);
424 	obj->value.string.base[len] = '\0';
425 
426 	*ret = obj;
427  cleanup:
428 	return (result);
429 }
430 
431 static isc_result_t
432 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
433 	isc_result_t result;
434 
435 	REQUIRE(pctx != NULL);
436 	REQUIRE(ret != NULL && *ret == NULL);
437 
438 	UNUSED(type);
439 
440 	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
441 	if (pctx->token.type != isc_tokentype_qstring) {
442 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
443 		return (ISC_R_UNEXPECTEDTOKEN);
444 	}
445 	return (create_string(pctx, TOKEN_STRING(pctx),
446 			      &cfg_type_qstring, ret));
447  cleanup:
448 	return (result);
449 }
450 
451 static isc_result_t
452 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
453 		  cfg_obj_t **ret)
454 {
455 	isc_result_t result;
456 
457 	REQUIRE(pctx != NULL);
458 	REQUIRE(ret != NULL && *ret == NULL);
459 
460 	UNUSED(type);
461 
462 	CHECK(cfg_getstringtoken(pctx));
463 	return (create_string(pctx,
464 			      TOKEN_STRING(pctx),
465 			      &cfg_type_qstring,
466 			      ret));
467  cleanup:
468 	return (result);
469 }
470 
471 static isc_result_t
472 cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type,
473 		  cfg_obj_t **ret)
474 {
475 	isc_result_t result;
476 
477 	REQUIRE(pctx != NULL);
478 	REQUIRE(ret != NULL && *ret == NULL);
479 
480 	UNUSED(type);
481 
482 	CHECK(cfg_getstringtoken(pctx));
483 	return (create_string(pctx,
484 			      TOKEN_STRING(pctx),
485 			      &cfg_type_sstring,
486 			      ret));
487  cleanup:
488 	return (result);
489 }
490 
491 static void
492 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
493 	UNUSED(pctx);
494 	free(obj->value.string.base);
495 }
496 
497 const char *
498 cfg_obj_asstring(const cfg_obj_t *obj) {
499 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
500 	return (obj->value.string.base);
501 }
502 
503 /* Quoted string only */
504 cfg_type_t cfg_type_qstring = {
505 	"quoted_string", cfg_parse_qstring, &cfg_rep_string, NULL
506 };
507 
508 /* Any string (quoted or unquoted); printed with quotes */
509 cfg_type_t cfg_type_astring = {
510 	"string", cfg_parse_astring, &cfg_rep_string, NULL
511 };
512 
513 /*
514  * Any string (quoted or unquoted); printed with quotes.
515  * If CFG_PRINTER_XKEY is set when printing the string will be '?' out.
516  */
517 cfg_type_t cfg_type_sstring = {
518 	"string", cfg_parse_sstring, &cfg_rep_string, NULL
519 };
520 
521 /*
522  * Booleans
523  */
524 
525 /*
526  * Lists.
527  */
528 
529 static isc_result_t
530 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
531 	isc_result_t result;
532 
533 	REQUIRE(pctx != NULL);
534 	REQUIRE(type != NULL);
535 	REQUIRE(obj != NULL && *obj == NULL);
536 
537 	CHECK(cfg_create_obj(pctx, type, obj));
538 	ISC_LIST_INIT((*obj)->value.list);
539  cleanup:
540 	return (result);
541 }
542 
543 static isc_result_t
544 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
545 	UNUSED(pctx);
546 	cfg_listelt_t *elt;
547 
548 	elt = malloc(sizeof(*elt));
549 	if (elt == NULL)
550 		return (ISC_R_NOMEMORY);
551 	elt->obj = NULL;
552 	ISC_LINK_INIT(elt, link);
553 	*eltp = elt;
554 	return (ISC_R_SUCCESS);
555 }
556 
557 static void
558 free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
559 	cfg_obj_destroy(pctx, &elt->obj);
560 	free(elt);
561 }
562 
563 static void
564 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
565 	cfg_listelt_t *elt, *next;
566 	for (elt = ISC_LIST_HEAD(obj->value.list);
567 	     elt != NULL;
568 	     elt = next)
569 	{
570 		next = ISC_LIST_NEXT(elt, link);
571 		free_list_elt(pctx, elt);
572 	}
573 }
574 
575 static isc_result_t
576 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
577 		  cfg_listelt_t **ret)
578 {
579 	isc_result_t result;
580 	cfg_listelt_t *elt = NULL;
581 	cfg_obj_t *value = NULL;
582 
583 	REQUIRE(pctx != NULL);
584 	REQUIRE(elttype != NULL);
585 	REQUIRE(ret != NULL && *ret == NULL);
586 
587 	CHECK(create_listelt(pctx, &elt));
588 
589 	result = cfg_parse_obj(pctx, elttype, &value);
590 	if (result != ISC_R_SUCCESS)
591 		goto cleanup;
592 
593 	elt->obj = value;
594 
595 	*ret = elt;
596 	return (ISC_R_SUCCESS);
597 
598  cleanup:
599 	free(elt);
600 	return (result);
601 }
602 
603 isc_boolean_t
604 cfg_obj_islist(const cfg_obj_t *obj) {
605 	REQUIRE(obj != NULL);
606 	return (ISC_TF(obj->type->rep == &cfg_rep_list));
607 }
608 
609 const cfg_listelt_t *
610 cfg_list_first(const cfg_obj_t *obj) {
611 	REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
612 	if (obj == NULL)
613 		return (NULL);
614 	return (ISC_LIST_HEAD(obj->value.list));
615 }
616 
617 const cfg_listelt_t *
618 cfg_list_next(const cfg_listelt_t *elt) {
619 	REQUIRE(elt != NULL);
620 	return (ISC_LIST_NEXT(elt, link));
621 }
622 
623 /*
624  * Return the length of a list object.  If obj is NULL or is not
625  * a list, return 0.
626  */
627 unsigned int
628 cfg_list_length(const cfg_obj_t *obj, isc_boolean_t recurse) {
629 	const cfg_listelt_t *elt;
630 	unsigned int count = 0;
631 
632 	if (obj == NULL || !cfg_obj_islist(obj))
633 		return (0U);
634 	for (elt = cfg_list_first(obj);
635 	     elt != NULL;
636 	     elt = cfg_list_next(elt)) {
637 		if (recurse && cfg_obj_islist(elt->obj)) {
638 			count += cfg_list_length(elt->obj, recurse);
639 		} else {
640 			count++;
641 		}
642 	}
643 	return (count);
644 }
645 
646 /*
647  * Maps.
648  */
649 
650 /*
651  * Parse a map body.  That's something like
652  *
653  *   "foo 1; bar { glub; }; zap true; zap false;"
654  *
655  * i.e., a sequence of option names followed by values and
656  * terminated by semicolons.  Used for the top level of
657  * the named.conf syntax, as well as for the body of the
658  * options, view, zone, and other statements.
659  */
660 isc_result_t
661 cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
662 {
663 	const cfg_clausedef_t * const *clausesets = type->of;
664 	isc_result_t result;
665 	const cfg_clausedef_t * const *clauseset;
666 	const cfg_clausedef_t *clause;
667 	cfg_obj_t *value = NULL;
668 	cfg_obj_t *obj = NULL;
669 	cfg_obj_t *eltobj = NULL;
670 	cfg_obj_t *includename = NULL;
671 	isc_symvalue_t symval;
672 
673 	REQUIRE(pctx != NULL);
674 	REQUIRE(type != NULL);
675 	REQUIRE(ret != NULL && *ret == NULL);
676 
677 	CHECK(create_map(pctx, type, &obj));
678 
679 	obj->value.map.clausesets = clausesets;
680 
681 	for (;;) {
682 	redo:
683 		/*
684 		 * Parse the option name and see if it is known.
685 		 */
686 		CHECK(cfg_gettoken(pctx, 0));
687 
688 		if (pctx->token.type != isc_tokentype_string) {
689 			cfg_ungettoken(pctx);
690 			break;
691 		}
692 
693 		/*
694 		 * We accept "include" statements wherever a map body
695 		 * clause can occur.
696 		 */
697 		if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
698 			/*
699 			 * Turn the file name into a temporary configuration
700 			 * object just so that it is not overwritten by the
701 			 * semicolon token.
702 			 */
703 			CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
704 			CHECK(parse_semicolon(pctx));
705 			CHECK(parser_openfile(pctx, includename->
706 					      value.string.base));
707 			 cfg_obj_destroy(pctx, &includename);
708 			 goto redo;
709 		}
710 
711 		clause = NULL;
712 		for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
713 			for (clause = *clauseset;
714 			     clause->name != NULL;
715 			     clause++) {
716 				if (strcasecmp(TOKEN_STRING(pctx),
717 					   clause->name) == 0)
718 					goto done;
719 			}
720 		}
721 	done:
722 		if (clause == NULL || clause->name == NULL) {
723 			cfg_parser_error(pctx, CFG_LOG_NOPREP,
724 					 "unknown option");
725 			/*
726 			 * Try to recover by parsing this option as an unknown
727 			 * option and discarding it.
728 			 */
729 			CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported,
730 					    &eltobj));
731 			cfg_obj_destroy(pctx, &eltobj);
732 			CHECK(parse_semicolon(pctx));
733 			continue;
734 		}
735 
736 		/* Clause is known. */
737 
738 		/* See if the clause already has a value; if not create one. */
739 		result = isc_symtab_lookup(obj->value.map.symtab,
740 					   clause->name, 0, &symval);
741 
742 		/* Single-valued clause */
743 		if (result == ISC_R_NOTFOUND) {
744 			CHECK(parse_symtab_elt(pctx, clause->name,
745 					       clause->type,
746 					       obj->value.map.symtab));
747 			CHECK(parse_semicolon(pctx));
748 		} else if (result == ISC_R_SUCCESS) {
749 			cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
750 				     clause->name);
751 			result = ISC_R_EXISTS;
752 			goto cleanup;
753 		} else {
754 			cfg_parser_error(pctx, CFG_LOG_NEAR,
755 				     "isc_symtab_define() failed");
756 			goto cleanup;
757 		}
758 	}
759 
760 	*ret = obj;
761 	return (ISC_R_SUCCESS);
762 
763  cleanup:
764 	CLEANUP_OBJ(value);
765 	CLEANUP_OBJ(obj);
766 	CLEANUP_OBJ(eltobj);
767 	CLEANUP_OBJ(includename);
768 	return (result);
769 }
770 
771 static isc_result_t
772 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
773 		 cfg_type_t *elttype, isc_symtab_t *symtab)
774 {
775 	isc_result_t result;
776 	cfg_obj_t *obj = NULL;
777 	isc_symvalue_t symval;
778 
779 	CHECK(cfg_parse_obj(pctx, elttype, &obj));
780 
781 	symval.as_pointer = obj;
782 	CHECK(isc_symtab_define(symtab, name,
783 				1, symval,
784 				isc_symexists_reject));
785 	return (ISC_R_SUCCESS);
786 
787  cleanup:
788 	CLEANUP_OBJ(obj);
789 	return (result);
790 }
791 
792 /*
793  * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
794  */
795 static isc_result_t
796 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
797 	isc_result_t result;
798 
799 	REQUIRE(pctx != NULL);
800 	REQUIRE(type != NULL);
801 	REQUIRE(ret != NULL && *ret == NULL);
802 
803 	CHECK(cfg_parse_special(pctx, '{'));
804 	CHECK(cfg_parse_mapbody(pctx, type, ret));
805 	CHECK(cfg_parse_special(pctx, '}'));
806  cleanup:
807 	return (result);
808 }
809 
810 /*
811  * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
812  */
813 static isc_result_t
814 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype,
815 		    const cfg_type_t *type, cfg_obj_t **ret)
816 {
817 	isc_result_t result;
818 	cfg_obj_t *idobj = NULL;
819 	cfg_obj_t *mapobj = NULL;
820 
821 	REQUIRE(pctx != NULL);
822 	REQUIRE(nametype != NULL);
823 	REQUIRE(type != NULL);
824 	REQUIRE(ret != NULL && *ret == NULL);
825 
826 	CHECK(cfg_parse_obj(pctx, nametype, &idobj));
827 	CHECK(cfg_parse_map(pctx, type, &mapobj));
828 	mapobj->value.map.id = idobj;
829 	*ret = mapobj;
830 	return (result);
831  cleanup:
832 	CLEANUP_OBJ(idobj);
833 	CLEANUP_OBJ(mapobj);
834 	return (result);
835 }
836 
837 /*
838  * Parse a map identified by a string name.  E.g., "name { foo 1; }".
839  * Used for the "key" and "channel" statements.
840  */
841 isc_result_t
842 cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
843 	return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
844 }
845 
846 isc_result_t
847 cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
848 	isc_result_t result;
849 	isc_symvalue_t val;
850 	const cfg_map_t *map;
851 
852 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
853 	REQUIRE(name != NULL);
854 	REQUIRE(obj != NULL && *obj == NULL);
855 
856 	map = &mapobj->value.map;
857 
858 	result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
859 	if (result != ISC_R_SUCCESS)
860 		return (result);
861 	*obj = val.as_pointer;
862 	return (ISC_R_SUCCESS);
863 }
864 
865 const cfg_obj_t *
866 cfg_map_getname(const cfg_obj_t *mapobj) {
867 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
868 	return (mapobj->value.map.id);
869 }
870 
871 /* Parse an arbitrary token, storing its raw text representation. */
872 static isc_result_t
873 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
874 	cfg_obj_t *obj = NULL;
875 	isc_result_t result;
876 	isc_region_t r;
877 
878 	UNUSED(type);
879 
880 	CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
881 	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
882 	if (pctx->token.type == isc_tokentype_eof) {
883 		cfg_ungettoken(pctx);
884 		result = ISC_R_EOF;
885 		goto cleanup;
886 	}
887 
888 	isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
889 
890 	obj->value.string.base = malloc(r.length + 1);
891 	if (obj->value.string.base == NULL) {
892 		result = ISC_R_NOMEMORY;
893 		goto cleanup;
894 	}
895 	obj->value.string.length = r.length;
896 	memmove(obj->value.string.base, r.base, r.length);
897 	obj->value.string.base[r.length] = '\0';
898 	*ret = obj;
899 	return (result);
900 
901  cleanup:
902 	if (obj != NULL)
903 		free(obj);
904 	return (result);
905 }
906 
907 cfg_type_t cfg_type_token = {
908 	"token", parse_token, &cfg_rep_string, NULL
909 };
910 
911 /*
912  * An unsupported option.  This is just a list of tokens with balanced braces
913  * ending in a semicolon.
914  */
915 
916 static isc_result_t
917 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
918 	cfg_obj_t *listobj = NULL;
919 	isc_result_t result;
920 	int braces = 0;
921 
922 	CHECK(cfg_create_list(pctx, type, &listobj));
923 
924 	for (;;) {
925 		cfg_listelt_t *elt = NULL;
926 
927 		CHECK(cfg_peektoken(pctx, 0));
928 		if (pctx->token.type == isc_tokentype_special) {
929 			if (pctx->token.value.as_char == '{')
930 				braces++;
931 			else if (pctx->token.value.as_char == '}')
932 				braces--;
933 			else if (pctx->token.value.as_char == ';')
934 				if (braces == 0)
935 					break;
936 		}
937 		if (pctx->token.type == isc_tokentype_eof || braces < 0) {
938 			cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
939 			result = ISC_R_UNEXPECTEDTOKEN;
940 			goto cleanup;
941 		}
942 
943 		CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
944 		ISC_LIST_APPEND(listobj->value.list, elt, link);
945 	}
946 	INSIST(braces == 0);
947 	*ret = listobj;
948 	return (ISC_R_SUCCESS);
949 
950  cleanup:
951 	CLEANUP_OBJ(listobj);
952 	return (result);
953 }
954 
955 cfg_type_t cfg_type_unsupported = {
956 	"unsupported", parse_unsupported, &cfg_rep_list, NULL
957 };
958 
959 static isc_result_t
960 cfg_gettoken(cfg_parser_t *pctx, int options) {
961 	isc_result_t result;
962 
963 	REQUIRE(pctx != NULL);
964 
965 	if (pctx->seen_eof)
966 		return (ISC_R_SUCCESS);
967 
968 	options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
969 
970  redo:
971 	pctx->token.type = isc_tokentype_unknown;
972 	result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
973 	pctx->ungotten = ISC_FALSE;
974 	pctx->line = isc_lex_getsourceline(pctx->lexer);
975 
976 	switch (result) {
977 	case ISC_R_SUCCESS:
978 		if (pctx->token.type == isc_tokentype_eof) {
979 			result = isc_lex_close(pctx->lexer);
980 			INSIST(result == ISC_R_NOMORE ||
981 			       result == ISC_R_SUCCESS);
982 
983 			if (isc_lex_getsourcename(pctx->lexer) != NULL) {
984 				/*
985 				 * Closed an included file, not the main file.
986 				 */
987 				cfg_listelt_t *elt;
988 				elt = ISC_LIST_TAIL(pctx->open_files->
989 						    value.list);
990 				INSIST(elt != NULL);
991 				ISC_LIST_UNLINK(pctx->open_files->
992 						value.list, elt, link);
993 				ISC_LIST_APPEND(pctx->closed_files->
994 						value.list, elt, link);
995 				goto redo;
996 			}
997 			pctx->seen_eof = ISC_TRUE;
998 		}
999 		break;
1000 
1001 	case ISC_R_NOSPACE:
1002 		/* More understandable than "ran out of space". */
1003 		cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
1004 		break;
1005 
1006 	case ISC_R_IOERROR:
1007 		cfg_parser_error(pctx, 0, "%s",
1008 				 isc_result_totext(result));
1009 		break;
1010 
1011 	default:
1012 		cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
1013 				 isc_result_totext(result));
1014 		break;
1015 	}
1016 	return (result);
1017 }
1018 
1019 static void
1020 cfg_ungettoken(cfg_parser_t *pctx) {
1021 	REQUIRE(pctx != NULL);
1022 
1023 	if (pctx->seen_eof)
1024 		return;
1025 	isc_lex_ungettoken(pctx->lexer, &pctx->token);
1026 	pctx->ungotten = ISC_TRUE;
1027 }
1028 
1029 static isc_result_t
1030 cfg_peektoken(cfg_parser_t *pctx, int options) {
1031 	isc_result_t result;
1032 
1033 	REQUIRE(pctx != NULL);
1034 
1035 	CHECK(cfg_gettoken(pctx, options));
1036 	cfg_ungettoken(pctx);
1037  cleanup:
1038 	return (result);
1039 }
1040 
1041 /*
1042  * Get a string token, accepting both the quoted and the unquoted form.
1043  * Log an error if the next token is not a string.
1044  */
1045 static isc_result_t
1046 cfg_getstringtoken(cfg_parser_t *pctx) {
1047 	isc_result_t result;
1048 
1049 	result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
1050 	if (result != ISC_R_SUCCESS)
1051 		return (result);
1052 
1053 	if (pctx->token.type != isc_tokentype_string &&
1054 	    pctx->token.type != isc_tokentype_qstring) {
1055 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
1056 		return (ISC_R_UNEXPECTEDTOKEN);
1057 	}
1058 	return (ISC_R_SUCCESS);
1059 }
1060 
1061 static void
1062 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
1063 	va_list args;
1064 
1065 	REQUIRE(pctx != NULL);
1066 	REQUIRE(fmt != NULL);
1067 
1068 	va_start(args, fmt);
1069 	parser_complain(pctx, ISC_FALSE, flags, fmt, args);
1070 	va_end(args);
1071 	pctx->errors++;
1072 }
1073 
1074 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
1075 
1076 static isc_boolean_t
1077 have_current_file(cfg_parser_t *pctx) {
1078 	cfg_listelt_t *elt;
1079 	if (pctx->open_files == NULL)
1080 		return (ISC_FALSE);
1081 
1082 	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
1083 	if (elt == NULL)
1084 	      return (ISC_FALSE);
1085 
1086 	return (ISC_TRUE);
1087 }
1088 
1089 static char *
1090 current_file(cfg_parser_t *pctx) {
1091 	static char none[] = "none";
1092 	cfg_listelt_t *elt;
1093 	cfg_obj_t *fileobj;
1094 
1095 	if (!have_current_file(pctx))
1096 		return (none);
1097 
1098 	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
1099 	if (elt == NULL)	/* shouldn't be possible, but... */
1100 	      return (none);
1101 
1102 	fileobj = elt->obj;
1103 	INSIST(fileobj->type == &cfg_type_qstring);
1104 	return (fileobj->value.string.base);
1105 }
1106 
1107 static void
1108 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
1109 		unsigned int flags, const char *format,
1110 		va_list args)
1111 {
1112 	char tokenbuf[MAX_LOG_TOKEN + 10];
1113 	static char where[PATH_MAX + 100];
1114 	static char message[2048];
1115 	int level = ISC_LOG_ERROR;
1116 	const char *prep = "";
1117 	size_t len;
1118 
1119 	if (is_warning)
1120 		level = ISC_LOG_WARNING;
1121 
1122 	where[0] = '\0';
1123 	if (have_current_file(pctx))
1124 		snprintf(where, sizeof(where), "%s:%u: ",
1125 			 current_file(pctx), pctx->line);
1126 
1127 	len = vsnprintf(message, sizeof(message), format, args);
1128 #define ELIPSIS " ... "
1129 	if (len >= sizeof(message)) {
1130 		message[sizeof(message) - sizeof(ELIPSIS)] = 0;
1131 		strlcat(message, ELIPSIS, sizeof(message));
1132 	}
1133 
1134 	if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) {
1135 		isc_region_t r;
1136 
1137 		if (pctx->ungotten)
1138 			(void)cfg_gettoken(pctx, 0);
1139 
1140 		if (pctx->token.type == isc_tokentype_eof) {
1141 			snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
1142 		} else if (pctx->token.type == isc_tokentype_unknown) {
1143 			flags = 0;
1144 			tokenbuf[0] = '\0';
1145 		} else {
1146 			isc_lex_getlasttokentext(pctx->lexer,
1147 						 &pctx->token, &r);
1148 			if (r.length > MAX_LOG_TOKEN)
1149 				snprintf(tokenbuf, sizeof(tokenbuf),
1150 					 "'%.*s...'", MAX_LOG_TOKEN, r.base);
1151 			else
1152 				snprintf(tokenbuf, sizeof(tokenbuf),
1153 					 "'%.*s'", (int)r.length, r.base);
1154 		}
1155 
1156 		/* Choose a preposition. */
1157 		if (flags & CFG_LOG_NEAR)
1158 			prep = " near ";
1159 		else if (flags & CFG_LOG_BEFORE)
1160 			prep = " before ";
1161 		else
1162 			prep = " ";
1163 	} else {
1164 		tokenbuf[0] = '\0';
1165 	}
1166 	isc_log_write(pctx->lctx, CAT, MOD, level,
1167 		      "%s%s%s%s", where, message, prep, tokenbuf);
1168 }
1169 
1170 static isc_result_t
1171 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1172 	isc_result_t result;
1173 	cfg_obj_t *obj;
1174 
1175 	REQUIRE(pctx != NULL);
1176 	REQUIRE(type != NULL);
1177 	REQUIRE(ret != NULL && *ret == NULL);
1178 
1179 	obj = malloc(sizeof(cfg_obj_t));
1180 	if (obj == NULL)
1181 		return (ISC_R_NOMEMORY);
1182 	obj->type = type;
1183 	obj->file = current_file(pctx);
1184 	obj->line = pctx->line;
1185 	result = isc_refcount_init(&obj->references, 1);
1186 	if (result != ISC_R_SUCCESS) {
1187 		free(obj);
1188 		return (result);
1189 	}
1190 	*ret = obj;
1191 	return (ISC_R_SUCCESS);
1192 }
1193 
1194 static void
1195 map_symtabitem_destroy(char *key, unsigned int type,
1196 		       isc_symvalue_t symval, void *userarg)
1197 {
1198 	cfg_obj_t *obj = symval.as_pointer;
1199 	cfg_parser_t *pctx = (cfg_parser_t *)userarg;
1200 
1201 	UNUSED(key);
1202 	UNUSED(type);
1203 
1204 	cfg_obj_destroy(pctx, &obj);
1205 }
1206 
1207 static isc_result_t
1208 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1209 	isc_result_t result;
1210 	isc_symtab_t *symtab = NULL;
1211 	cfg_obj_t *obj = NULL;
1212 
1213 	CHECK(cfg_create_obj(pctx, type, &obj));
1214 	CHECK(isc_symtab_create(5, /* XXX */
1215 				map_symtabitem_destroy,
1216 				pctx, ISC_FALSE, &symtab));
1217 	obj->value.map.symtab = symtab;
1218 	obj->value.map.id = NULL;
1219 
1220 	*ret = obj;
1221 	return (ISC_R_SUCCESS);
1222 
1223  cleanup:
1224 	if (obj != NULL)
1225 		free(obj);
1226 	return (result);
1227 }
1228 
1229 static void
1230 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
1231 	CLEANUP_OBJ(obj->value.map.id);
1232 	isc_symtab_destroy(&obj->value.map.symtab);
1233 }
1234 
1235 /*
1236  * Destroy 'obj', a configuration object created in 'pctx'.
1237  */
1238 void
1239 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
1240 	cfg_obj_t *obj;
1241 	unsigned int refs;
1242 
1243 	REQUIRE(objp != NULL && *objp != NULL);
1244 	REQUIRE(pctx != NULL);
1245 
1246 	obj = *objp;
1247 
1248 	isc_refcount_decrement(&obj->references, &refs);
1249 	if (refs == 0) {
1250 		obj->type->rep->free(pctx, obj);
1251 		isc_refcount_destroy(&obj->references);
1252 		free(obj);
1253 	}
1254 	*objp = NULL;
1255 }
1256