xref: /netbsd-src/external/mpl/bind/dist/lib/isccfg/parser.c (revision 2d8e86c2f207da6fbbd50f11b6f33765ebdfa0e9)
1 /*	$NetBSD: parser.c,v 1.4 2019/02/24 20:01:32 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * See the COPYRIGHT file distributed with this work for additional
11  * information regarding copyright ownership.
12  */
13 
14 /*! \file */
15 
16 #include <config.h>
17 
18 #include <inttypes.h>
19 #include <stdbool.h>
20 #include <stdlib.h>
21 
22 #include <isc/buffer.h>
23 #include <isc/dir.h>
24 #include <isc/formatcheck.h>
25 #include <isc/lex.h>
26 #include <isc/log.h>
27 #include <isc/mem.h>
28 #include <isc/net.h>
29 #include <isc/netaddr.h>
30 #include <isc/netscope.h>
31 #include <isc/print.h>
32 #include <isc/string.h>
33 #include <isc/sockaddr.h>
34 #include <isc/symtab.h>
35 #include <isc/util.h>
36 
37 #include <isccfg/cfg.h>
38 #include <isccfg/grammar.h>
39 #include <isccfg/log.h>
40 
41 /* Shorthand */
42 #define CAT CFG_LOGCATEGORY_CONFIG
43 #define MOD CFG_LOGMODULE_PARSER
44 
45 #define MAP_SYM 1 	/* Unique type for isc_symtab */
46 
47 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
48 
49 /* Check a return value. */
50 #define CHECK(op) 						\
51 	do { result = (op); 					\
52 		if (result != ISC_R_SUCCESS) goto cleanup; 	\
53 	} while (/*CONSTCOND*/0)
54 
55 /* Clean up a configuration object if non-NULL. */
56 #define CLEANUP_OBJ(obj) \
57 	do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (/*CONSTCOND*/0)
58 
59 
60 /*
61  * Forward declarations of static functions.
62  */
63 
64 static void
65 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
66 
67 static isc_result_t
68 parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
69 
70 static void
71 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
72 
73 static void
74 free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
75 
76 static isc_result_t
77 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
78 
79 static isc_result_t
80 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
81 	      cfg_obj_t **ret);
82 
83 static void
84 free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
85 
86 static isc_result_t
87 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
88 
89 static void
90 free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
91 
92 static isc_result_t
93 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
94 		 cfg_type_t *elttype, isc_symtab_t *symtab,
95 		 bool callback);
96 
97 static void
98 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
99 
100 static isc_result_t
101 cfg_getstringtoken(cfg_parser_t *pctx);
102 
103 static void
104 parser_complain(cfg_parser_t *pctx, bool is_warning,
105 		unsigned int flags, const char *format, va_list args);
106 
107 #ifdef HAVE_GEOIP
108 static isc_result_t
109 parse_geoip(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
110 
111 static void
112 print_geoip(cfg_printer_t *pctx, const cfg_obj_t *obj);
113 
114 static void
115 doc_geoip(cfg_printer_t *pctx, const cfg_type_t *type);
116 #endif /* HAVE_GEOIP */
117 
118 /*
119  * Data representations.  These correspond to members of the
120  * "value" union in struct cfg_obj (except "void", which does
121  * not need a union member).
122  */
123 
124 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
125 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
126 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_string = { "string", free_string };
127 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
128 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_map = { "map", free_map };
129 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_list = { "list", free_list };
130 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
131 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
132 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_netprefix =
133 	{ "netprefix", free_noop };
134 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_void = { "void", free_noop };
135 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_fixedpoint =
136 	{ "fixedpoint", free_noop };
137 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_percentage =
138 	{ "percentage", free_noop };
139 
140 /*
141  * Configuration type definitions.
142  */
143 
144 /*%
145  * An implicit list.  These are formed by clauses that occur multiple times.
146  */
147 static cfg_type_t cfg_type_implicitlist = {
148 	"implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL };
149 
150 /* Functions. */
151 
152 void
153 cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
154 	REQUIRE(pctx != NULL);
155 	REQUIRE(obj != NULL);
156 
157 	obj->type->print(pctx, obj);
158 }
159 
160 void
161 cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
162 	REQUIRE(pctx != NULL);
163 	REQUIRE(text != NULL);
164 
165 	pctx->f(pctx->closure, text, len);
166 }
167 
168 static void
169 print_open(cfg_printer_t *pctx) {
170 	if ((pctx->flags & CFG_PRINTER_ONELINE) != 0)
171 		cfg_print_cstr(pctx, "{ ");
172 	else {
173 		cfg_print_cstr(pctx, "{\n");
174 		pctx->indent++;
175 	}
176 }
177 
178 void
179 cfg_print_indent(cfg_printer_t *pctx) {
180 	int indent = pctx->indent;
181 	if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) {
182 		cfg_print_cstr(pctx, " ");
183 		return;
184 	}
185 	while (indent > 0) {
186 		cfg_print_cstr(pctx, "\t");
187 		indent--;
188 	}
189 }
190 
191 static void
192 print_close(cfg_printer_t *pctx) {
193 	if ((pctx->flags & CFG_PRINTER_ONELINE) == 0) {
194 		pctx->indent--;
195 		cfg_print_indent(pctx);
196 	}
197 	cfg_print_cstr(pctx, "}");
198 }
199 
200 isc_result_t
201 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
202 	isc_result_t result;
203 
204 	REQUIRE(pctx != NULL);
205 	REQUIRE(type != NULL);
206 	REQUIRE(ret != NULL && *ret == NULL);
207 
208 	result = type->parse(pctx, type, ret);
209 	if (result != ISC_R_SUCCESS)
210 		return (result);
211 	ENSURE(*ret != NULL);
212 	return (ISC_R_SUCCESS);
213 }
214 
215 void
216 cfg_print(const cfg_obj_t *obj,
217 	  void (*f)(void *closure, const char *text, int textlen),
218 	  void *closure)
219 {
220 	REQUIRE(obj != NULL);
221 	REQUIRE(f != NULL);
222 
223 	cfg_printx(obj, 0, f, closure);
224 }
225 
226 void
227 cfg_printx(const cfg_obj_t *obj, unsigned int flags,
228 	     void (*f)(void *closure, const char *text, int textlen),
229 	     void *closure)
230 {
231 	cfg_printer_t pctx;
232 
233 	REQUIRE(obj != NULL);
234 	REQUIRE(f != NULL);
235 
236 	pctx.f = f;
237 	pctx.closure = closure;
238 	pctx.indent = 0;
239 	pctx.flags = flags;
240 	obj->type->print(&pctx, obj);
241 }
242 
243 /* Tuples. */
244 
245 isc_result_t
246 cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
247 	isc_result_t result;
248 	const cfg_tuplefielddef_t *fields = type->of;
249 	const cfg_tuplefielddef_t *f;
250 	cfg_obj_t *obj = NULL;
251 	unsigned int nfields = 0;
252 	int i;
253 
254 	REQUIRE(pctx != NULL);
255 	REQUIRE(type != NULL);
256 	REQUIRE(ret != NULL && *ret == NULL);
257 
258 	for (f = fields; f->name != NULL; f++)
259 		nfields++;
260 
261 	CHECK(cfg_create_obj(pctx, type, &obj));
262 	obj->value.tuple = isc_mem_get(pctx->mctx,
263 				       nfields * sizeof(cfg_obj_t *));
264 	if (obj->value.tuple == NULL) {
265 		result = ISC_R_NOMEMORY;
266 		goto cleanup;
267 	}
268 	for (f = fields, i = 0; f->name != NULL; f++, i++)
269 		obj->value.tuple[i] = NULL;
270 	*ret = obj;
271 	return (ISC_R_SUCCESS);
272 
273  cleanup:
274 	if (obj != NULL)
275 		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
276 	return (result);
277 }
278 
279 isc_result_t
280 cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
281 {
282 	isc_result_t result;
283 	const cfg_tuplefielddef_t *fields = type->of;
284 	const cfg_tuplefielddef_t *f;
285 	cfg_obj_t *obj = NULL;
286 	unsigned int i;
287 
288 	REQUIRE(pctx != NULL);
289 	REQUIRE(type != NULL);
290 	REQUIRE(ret != NULL && *ret == NULL);
291 
292 	CHECK(cfg_create_tuple(pctx, type, &obj));
293 	for (f = fields, i = 0; f->name != NULL; f++, i++)
294 		CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
295 
296 	*ret = obj;
297 	return (ISC_R_SUCCESS);
298 
299  cleanup:
300 	CLEANUP_OBJ(obj);
301 	return (result);
302 }
303 
304 void
305 cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
306 	unsigned int i;
307 	const cfg_tuplefielddef_t *fields;
308 	const cfg_tuplefielddef_t *f;
309 	bool need_space = false;
310 
311 	REQUIRE(pctx != NULL);
312 	REQUIRE(obj != NULL);
313 
314 	fields = obj->type->of;
315 
316 	for (f = fields, i = 0; f->name != NULL; f++, i++) {
317 		const cfg_obj_t *fieldobj = obj->value.tuple[i];
318 		if (need_space && fieldobj->type->rep != &cfg_rep_void)
319 			cfg_print_cstr(pctx, " ");
320 		cfg_print_obj(pctx, fieldobj);
321 		need_space = (need_space ||
322 			      fieldobj->type->print != cfg_print_void);
323 	}
324 }
325 
326 void
327 cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
328 	const cfg_tuplefielddef_t *fields;
329 	const cfg_tuplefielddef_t *f;
330 	bool need_space = false;
331 
332 	REQUIRE(pctx != NULL);
333 	REQUIRE(type != NULL);
334 
335 	fields = type->of;
336 
337 	for (f = fields; f->name != NULL; f++) {
338 		if (need_space)
339 			cfg_print_cstr(pctx, " ");
340 		cfg_doc_obj(pctx, f->type);
341 		need_space = (f->type->print != cfg_print_void);
342 	}
343 }
344 
345 static void
346 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
347 	unsigned int i;
348 	const cfg_tuplefielddef_t *fields = obj->type->of;
349 	const cfg_tuplefielddef_t *f;
350 	unsigned int nfields = 0;
351 
352 	if (obj->value.tuple == NULL)
353 		return;
354 
355 	for (f = fields, i = 0; f->name != NULL; f++, i++) {
356 		CLEANUP_OBJ(obj->value.tuple[i]);
357 		nfields++;
358 	}
359 	isc_mem_put(pctx->mctx, obj->value.tuple,
360 		    nfields * sizeof(cfg_obj_t *));
361 }
362 
363 bool
364 cfg_obj_istuple(const cfg_obj_t *obj) {
365 	REQUIRE(obj != NULL);
366 	return (obj->type->rep == &cfg_rep_tuple);
367 }
368 
369 const cfg_obj_t *
370 cfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) {
371 	unsigned int i;
372 	const cfg_tuplefielddef_t *fields;
373 	const cfg_tuplefielddef_t *f;
374 
375 	REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
376 	REQUIRE(name != NULL);
377 
378 	fields = tupleobj->type->of;
379 	for (f = fields, i = 0; f->name != NULL; f++, i++) {
380 		if (strcmp(f->name, name) == 0)
381 			return (tupleobj->value.tuple[i]);
382 	}
383 	INSIST(0);
384 	ISC_UNREACHABLE();
385 }
386 
387 isc_result_t
388 cfg_parse_special(cfg_parser_t *pctx, int special) {
389 	isc_result_t result;
390 
391 	REQUIRE(pctx != NULL);
392 
393 	CHECK(cfg_gettoken(pctx, 0));
394 	if (pctx->token.type == isc_tokentype_special &&
395 	    pctx->token.value.as_char == special)
396 		return (ISC_R_SUCCESS);
397 
398 	cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
399 	return (ISC_R_UNEXPECTEDTOKEN);
400  cleanup:
401 	return (result);
402 }
403 
404 /*
405  * Parse a required semicolon.  If it is not there, log
406  * an error and increment the error count but continue
407  * parsing.  Since the next token is pushed back,
408  * care must be taken to make sure it is eventually
409  * consumed or an infinite loop may result.
410  */
411 static isc_result_t
412 parse_semicolon(cfg_parser_t *pctx) {
413 	isc_result_t result;
414 
415 	CHECK(cfg_gettoken(pctx, 0));
416 	if (pctx->token.type == isc_tokentype_special &&
417 	    pctx->token.value.as_char == ';')
418 		return (ISC_R_SUCCESS);
419 
420 	cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
421 	cfg_ungettoken(pctx);
422  cleanup:
423 	return (result);
424 }
425 
426 /*
427  * Parse EOF, logging and returning an error if not there.
428  */
429 static isc_result_t
430 parse_eof(cfg_parser_t *pctx) {
431 	isc_result_t result;
432 
433 	CHECK(cfg_gettoken(pctx, 0));
434 
435 	if (pctx->token.type == isc_tokentype_eof)
436 		return (ISC_R_SUCCESS);
437 
438 	cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
439 	return (ISC_R_UNEXPECTEDTOKEN);
440  cleanup:
441 	return (result);
442 }
443 
444 /* A list of files, used internally for pctx->files. */
445 
446 static cfg_type_t cfg_type_filelist = {
447 	"filelist", NULL, print_list, NULL, &cfg_rep_list,
448 	&cfg_type_qstring
449 };
450 
451 isc_result_t
452 cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
453 	isc_result_t result;
454 	cfg_parser_t *pctx;
455 	isc_lexspecials_t specials;
456 
457 	REQUIRE(mctx != NULL);
458 	REQUIRE(ret != NULL && *ret == NULL);
459 
460 	pctx = isc_mem_get(mctx, sizeof(*pctx));
461 	if (pctx == NULL)
462 		return (ISC_R_NOMEMORY);
463 
464 	pctx->mctx = NULL;
465 	isc_mem_attach(mctx, &pctx->mctx);
466 
467 	isc_refcount_init(&pctx->references, 1);
468 
469 	pctx->lctx = lctx;
470 	pctx->lexer = NULL;
471 	pctx->seen_eof = false;
472 	pctx->ungotten = false;
473 	pctx->errors = 0;
474 	pctx->warnings = 0;
475 	pctx->open_files = NULL;
476 	pctx->closed_files = NULL;
477 	pctx->line = 0;
478 	pctx->callback = NULL;
479 	pctx->callbackarg = NULL;
480 	pctx->token.type = isc_tokentype_unknown;
481 	pctx->flags = 0;
482 	pctx->buf_name = NULL;
483 
484 	memset(specials, 0, sizeof(specials));
485 	specials['{'] = 1;
486 	specials['}'] = 1;
487 	specials[';'] = 1;
488 	specials['/'] = 1;
489 	specials['"'] = 1;
490 	specials['!'] = 1;
491 
492 	CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer));
493 
494 	isc_lex_setspecials(pctx->lexer, specials);
495 	isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C |
496 					 ISC_LEXCOMMENT_CPLUSPLUS |
497 					 ISC_LEXCOMMENT_SHELL));
498 
499 	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
500 	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
501 
502 	*ret = pctx;
503 	return (ISC_R_SUCCESS);
504 
505  cleanup:
506 	if (pctx->lexer != NULL)
507 		isc_lex_destroy(&pctx->lexer);
508 	CLEANUP_OBJ(pctx->open_files);
509 	CLEANUP_OBJ(pctx->closed_files);
510 	isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
511 	return (result);
512 }
513 
514 static isc_result_t
515 parser_openfile(cfg_parser_t *pctx, const char *filename) {
516 	isc_result_t result;
517 	cfg_listelt_t *elt = NULL;
518 	cfg_obj_t *stringobj = NULL;
519 
520 	result = isc_lex_openfile(pctx->lexer, filename);
521 	if (result != ISC_R_SUCCESS) {
522 		cfg_parser_error(pctx, 0, "open: %s: %s",
523 			     filename, isc_result_totext(result));
524 		goto cleanup;
525 	}
526 
527 	CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
528 	CHECK(create_listelt(pctx, &elt));
529 	elt->obj = stringobj;
530 	ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
531 
532 	return (ISC_R_SUCCESS);
533  cleanup:
534 	CLEANUP_OBJ(stringobj);
535 	return (result);
536 }
537 
538 void
539 cfg_parser_setcallback(cfg_parser_t *pctx,
540 		       cfg_parsecallback_t callback,
541 		       void *arg)
542 {
543 	REQUIRE(pctx != NULL);
544 
545 	pctx->callback = callback;
546 	pctx->callbackarg = arg;
547 }
548 
549 void
550 cfg_parser_reset(cfg_parser_t *pctx) {
551 	REQUIRE(pctx != NULL);
552 
553 	if (pctx->lexer != NULL)
554 		isc_lex_close(pctx->lexer);
555 
556 	pctx->seen_eof = false;
557 	pctx->ungotten = false;
558 	pctx->errors = 0;
559 	pctx->warnings = 0;
560 	pctx->line = 0;
561 }
562 
563 /*
564  * Parse a configuration using a pctx where a lexer has already
565  * been set up with a source.
566  */
567 static isc_result_t
568 parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
569 	isc_result_t result;
570 	cfg_obj_t *obj = NULL;
571 
572 	result = cfg_parse_obj(pctx, type, &obj);
573 
574 	if (pctx->errors != 0) {
575 		/* Errors have been logged. */
576 		if (result == ISC_R_SUCCESS)
577 			result = ISC_R_FAILURE;
578 		goto cleanup;
579 	}
580 
581 	if (result != ISC_R_SUCCESS) {
582 		/* Parsing failed but no errors have been logged. */
583 		cfg_parser_error(pctx, 0, "parsing failed: %s",
584 				 isc_result_totext(result));
585 		goto cleanup;
586 	}
587 
588 	CHECK(parse_eof(pctx));
589 
590 	*ret = obj;
591 	return (ISC_R_SUCCESS);
592 
593  cleanup:
594 	CLEANUP_OBJ(obj);
595 	return (result);
596 }
597 
598 isc_result_t
599 cfg_parse_file(cfg_parser_t *pctx, const char *filename,
600 	       const cfg_type_t *type, cfg_obj_t **ret)
601 {
602 	isc_result_t result;
603 	cfg_listelt_t *elt;
604 
605 	REQUIRE(pctx != NULL);
606 	REQUIRE(filename != NULL);
607 	REQUIRE(type != NULL);
608 	REQUIRE(ret != NULL && *ret == NULL);
609 
610 	CHECK(parser_openfile(pctx, filename));
611 
612 	result = parse2(pctx, type, ret);
613 
614 	/* Clean up the opened file */
615 	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
616 	INSIST(elt != NULL);
617 	ISC_LIST_UNLINK(pctx->open_files->value.list, elt, link);
618 	ISC_LIST_APPEND(pctx->closed_files->value.list, elt, link);
619 
620  cleanup:
621 	return (result);
622 }
623 
624 
625 isc_result_t
626 cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
627 		 const char *file, unsigned int line,
628 		 const cfg_type_t *type, unsigned int flags,
629 		 cfg_obj_t **ret)
630 {
631 	isc_result_t result;
632 
633 	REQUIRE(pctx != NULL);
634 	REQUIRE(type != NULL);
635 	REQUIRE(buffer != NULL);
636 	REQUIRE(ret != NULL && *ret == NULL);
637 	REQUIRE((flags & ~(CFG_PCTX_NODEPRECATED)) == 0);
638 
639 	CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
640 
641 	pctx->buf_name = file;
642 	pctx->flags = flags;
643 
644 	if (line != 0U)
645 		CHECK(isc_lex_setsourceline(pctx->lexer, line));
646 
647 	CHECK(parse2(pctx, type, ret));
648 	pctx->buf_name = NULL;
649 
650  cleanup:
651 	return (result);
652 }
653 
654 void
655 cfg_parser_attach(cfg_parser_t *src, cfg_parser_t **dest) {
656 	REQUIRE(src != NULL);
657 	REQUIRE(dest != NULL && *dest == NULL);
658 
659 	isc_refcount_increment(&src->references);
660 	*dest = src;
661 }
662 
663 void
664 cfg_parser_destroy(cfg_parser_t **pctxp) {
665 	cfg_parser_t *pctx;
666 
667 	REQUIRE(pctxp != NULL && *pctxp != NULL);
668 	pctx = *pctxp;
669 	*pctxp = NULL;
670 
671 	if (isc_refcount_decrement(&pctx->references) == 1) {
672 		isc_lex_destroy(&pctx->lexer);
673 		/*
674 		 * Cleaning up open_files does not
675 		 * close the files; that was already done
676 		 * by closing the lexer.
677 		 */
678 		CLEANUP_OBJ(pctx->open_files);
679 		CLEANUP_OBJ(pctx->closed_files);
680 		isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
681 	}
682 }
683 
684 /*
685  * void
686  */
687 isc_result_t
688 cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
689 	REQUIRE(pctx != NULL);
690 	REQUIRE(ret != NULL && *ret == NULL);
691 
692 	UNUSED(type);
693 
694 	return (cfg_create_obj(pctx, &cfg_type_void, ret));
695 }
696 
697 void
698 cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
699 
700 	REQUIRE(pctx != NULL);
701 	REQUIRE(obj != NULL);
702 
703 	UNUSED(pctx);
704 	UNUSED(obj);
705 }
706 
707 void
708 cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
709 
710 	REQUIRE(pctx != NULL);
711 	REQUIRE(type != NULL);
712 
713 	UNUSED(pctx);
714 	UNUSED(type);
715 }
716 
717 bool
718 cfg_obj_isvoid(const cfg_obj_t *obj) {
719 	REQUIRE(obj != NULL);
720 	return (obj->type->rep == &cfg_rep_void);
721 }
722 
723 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_void = {
724 	"void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
725 	NULL };
726 
727 /*
728  * percentage
729  */
730 isc_result_t
731 cfg_parse_percentage(cfg_parser_t *pctx, const cfg_type_t *type,
732 		     cfg_obj_t **ret)
733 {
734 	char *endp;
735 	isc_result_t result;
736 	cfg_obj_t *obj = NULL;
737 	uint64_t percent;
738 
739 	REQUIRE(pctx != NULL);
740 	REQUIRE(ret != NULL && *ret == NULL);
741 
742 	UNUSED(type);
743 
744 	CHECK(cfg_gettoken(pctx, 0));
745 	if (pctx->token.type != isc_tokentype_string) {
746 		cfg_parser_error(pctx, CFG_LOG_NEAR,
747 				 "expected percentage");
748 		return (ISC_R_UNEXPECTEDTOKEN);
749 	}
750 
751 	percent = strtoull(TOKEN_STRING(pctx), &endp, 10);
752 	if (*endp != '%' || *(endp+1) != 0) {
753 		cfg_parser_error(pctx, CFG_LOG_NEAR,
754 				 "expected percentage");
755 		return (ISC_R_UNEXPECTEDTOKEN);
756 	}
757 
758 	CHECK(cfg_create_obj(pctx, &cfg_type_percentage, &obj));
759 	obj->value.uint32 = (uint32_t)percent;
760 	*ret = obj;
761 
762  cleanup:
763 	return (result);
764 }
765 
766 void
767 cfg_print_percentage(cfg_printer_t *pctx, const cfg_obj_t *obj) {
768 	char buf[64];
769 	int n;
770 
771 	REQUIRE(pctx != NULL);
772 	REQUIRE(obj != NULL);
773 
774 	n = snprintf(buf, sizeof(buf), "%u%%", obj->value.uint32);
775 	INSIST(n > 0 && (size_t)n < sizeof(buf));
776 	cfg_print_chars(pctx, buf, strlen(buf));
777 }
778 
779 uint32_t
780 cfg_obj_aspercentage(const cfg_obj_t *obj) {
781 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_percentage);
782 	return (obj->value.uint32);
783 }
784 
785 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_percentage = {
786 	"percentage", cfg_parse_percentage, cfg_print_percentage,
787 	cfg_doc_terminal, &cfg_rep_percentage, NULL
788 };
789 
790 bool
791 cfg_obj_ispercentage(const cfg_obj_t *obj) {
792 	REQUIRE(obj != NULL);
793 	return (obj->type->rep == &cfg_rep_percentage);
794 }
795 
796 /*
797  * Fixed point
798  */
799 isc_result_t
800 cfg_parse_fixedpoint(cfg_parser_t *pctx, const cfg_type_t *type,
801 		     cfg_obj_t **ret)
802 {
803 	isc_result_t result;
804 	cfg_obj_t *obj = NULL;
805 	size_t n1, n2, n3, l;
806 	const char *p;
807 
808 	REQUIRE(pctx != NULL);
809 	REQUIRE(ret != NULL && *ret == NULL);
810 
811 	UNUSED(type);
812 
813 	CHECK(cfg_gettoken(pctx, 0));
814 	if (pctx->token.type != isc_tokentype_string) {
815 		cfg_parser_error(pctx, CFG_LOG_NEAR,
816 				 "expected fixed point number");
817 		return (ISC_R_UNEXPECTEDTOKEN);
818 	}
819 
820 
821 	p = TOKEN_STRING(pctx);
822 	l = strlen(p);
823 	n1 = strspn(p, "0123456789");
824 	n2 = strspn(p + n1, ".");
825 	n3 = strspn(p + n1 + n2, "0123456789");
826 
827 	if ((n1 + n2 + n3 != l) || (n1 + n3 == 0) ||
828 	    n1 > 5 || n2 > 1 || n3 > 2) {
829 		cfg_parser_error(pctx, CFG_LOG_NEAR,
830 				 "expected fixed point number");
831 		return (ISC_R_UNEXPECTEDTOKEN);
832 	}
833 
834 	CHECK(cfg_create_obj(pctx, &cfg_type_fixedpoint, &obj));
835 
836 	obj->value.uint32 = strtoul(p, NULL, 10) * 100;
837 	switch (n3) {
838 	case 2:
839 		obj->value.uint32 += strtoul(p + n1 + n2, NULL, 10);
840 		break;
841 	case 1:
842 		obj->value.uint32 += strtoul(p + n1 + n2, NULL, 10) * 10;
843 		break;
844 	}
845 	*ret = obj;
846 
847  cleanup:
848 	return (result);
849 }
850 
851 void
852 cfg_print_fixedpoint(cfg_printer_t *pctx, const cfg_obj_t *obj) {
853 	char buf[64];
854 	int n;
855 
856 	REQUIRE(pctx != NULL);
857 	REQUIRE(obj != NULL);
858 
859 	n = snprintf(buf, sizeof(buf), "%u.%02u",
860 		     obj->value.uint32/100, obj->value.uint32%100);
861 	INSIST(n > 0 && (size_t)n < sizeof(buf));
862 	cfg_print_chars(pctx, buf, strlen(buf));
863 }
864 
865 uint32_t
866 cfg_obj_asfixedpoint(const cfg_obj_t *obj) {
867 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_fixedpoint);
868 	return (obj->value.uint32);
869 }
870 
871 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_fixedpoint = {
872 	"fixedpoint", cfg_parse_fixedpoint, cfg_print_fixedpoint,
873 	cfg_doc_terminal, &cfg_rep_fixedpoint, NULL
874 };
875 
876 bool
877 cfg_obj_isfixedpoint(const cfg_obj_t *obj) {
878 	REQUIRE(obj != NULL);
879 	return (obj->type->rep == &cfg_rep_fixedpoint);
880 }
881 
882 /*
883  * uint32
884  */
885 isc_result_t
886 cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
887 	isc_result_t result;
888 	cfg_obj_t *obj = NULL;
889 
890 	REQUIRE(pctx != NULL);
891 	REQUIRE(ret != NULL && *ret == NULL);
892 
893 	UNUSED(type);
894 
895 	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
896 	if (pctx->token.type != isc_tokentype_number) {
897 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
898 		return (ISC_R_UNEXPECTEDTOKEN);
899 	}
900 
901 	CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
902 
903 	obj->value.uint32 = pctx->token.value.as_ulong;
904 	*ret = obj;
905  cleanup:
906 	return (result);
907 }
908 
909 void
910 cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
911 	cfg_print_chars(pctx, s, strlen(s));
912 }
913 
914 void
915 cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
916 	char buf[32];
917 
918 	snprintf(buf, sizeof(buf), "%u", u);
919 	cfg_print_cstr(pctx, buf);
920 }
921 
922 void
923 cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
924 	cfg_print_rawuint(pctx, obj->value.uint32);
925 }
926 
927 bool
928 cfg_obj_isuint32(const cfg_obj_t *obj) {
929 	REQUIRE(obj != NULL);
930 	return (obj->type->rep == &cfg_rep_uint32);
931 }
932 
933 uint32_t
934 cfg_obj_asuint32(const cfg_obj_t *obj) {
935 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
936 	return (obj->value.uint32);
937 }
938 
939 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_uint32 = {
940 	"integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
941 	&cfg_rep_uint32, NULL
942 };
943 
944 
945 /*
946  * uint64
947  */
948 bool
949 cfg_obj_isuint64(const cfg_obj_t *obj) {
950 	REQUIRE(obj != NULL);
951 	return (obj->type->rep == &cfg_rep_uint64);
952 }
953 
954 uint64_t
955 cfg_obj_asuint64(const cfg_obj_t *obj) {
956 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
957 	return (obj->value.uint64);
958 }
959 
960 void
961 cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
962 	char buf[32];
963 
964 	snprintf(buf, sizeof(buf), "%" PRIu64,
965 		 obj->value.uint64);
966 	cfg_print_cstr(pctx, buf);
967 }
968 
969 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_uint64 = {
970 	"64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
971 	&cfg_rep_uint64, NULL
972 };
973 
974 /*
975  * qstring (quoted string), ustring (unquoted string), astring
976  * (any string)
977  */
978 
979 /* Create a string object from a null-terminated C string. */
980 static isc_result_t
981 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
982 	      cfg_obj_t **ret)
983 {
984 	isc_result_t result;
985 	cfg_obj_t *obj = NULL;
986 	int len;
987 
988 	CHECK(cfg_create_obj(pctx, type, &obj));
989 	len = strlen(contents);
990 	obj->value.string.length = len;
991 	obj->value.string.base = isc_mem_get(pctx->mctx, len + 1);
992 	if (obj->value.string.base == 0) {
993 		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
994 		return (ISC_R_NOMEMORY);
995 	}
996 	memmove(obj->value.string.base, contents, len);
997 	obj->value.string.base[len] = '\0';
998 
999 	*ret = obj;
1000  cleanup:
1001 	return (result);
1002 }
1003 
1004 isc_result_t
1005 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1006 	isc_result_t result;
1007 
1008 	REQUIRE(pctx != NULL);
1009 	REQUIRE(ret != NULL && *ret == NULL);
1010 
1011 	UNUSED(type);
1012 
1013 	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
1014 	if (pctx->token.type != isc_tokentype_qstring) {
1015 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
1016 		return (ISC_R_UNEXPECTEDTOKEN);
1017 	}
1018 	return (create_string(pctx, TOKEN_STRING(pctx),
1019 			      &cfg_type_qstring, ret));
1020  cleanup:
1021 	return (result);
1022 }
1023 
1024 static isc_result_t
1025 parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1026 	isc_result_t result;
1027 
1028 	UNUSED(type);
1029 
1030 	CHECK(cfg_gettoken(pctx, 0));
1031 	if (pctx->token.type != isc_tokentype_string) {
1032 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
1033 		return (ISC_R_UNEXPECTEDTOKEN);
1034 	}
1035 	return (create_string(pctx,
1036 			      TOKEN_STRING(pctx),
1037 			      &cfg_type_ustring,
1038 			      ret));
1039  cleanup:
1040 	return (result);
1041 }
1042 
1043 isc_result_t
1044 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
1045 		  cfg_obj_t **ret)
1046 {
1047 	isc_result_t result;
1048 
1049 	REQUIRE(pctx != NULL);
1050 	REQUIRE(ret != NULL && *ret == NULL);
1051 
1052 	UNUSED(type);
1053 
1054 	CHECK(cfg_getstringtoken(pctx));
1055 	return (create_string(pctx,
1056 			      TOKEN_STRING(pctx),
1057 			      &cfg_type_qstring,
1058 			      ret));
1059  cleanup:
1060 	return (result);
1061 }
1062 
1063 isc_result_t
1064 cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type,
1065 		  cfg_obj_t **ret)
1066 {
1067 	isc_result_t result;
1068 
1069 	REQUIRE(pctx != NULL);
1070 	REQUIRE(ret != NULL && *ret == NULL);
1071 
1072 	UNUSED(type);
1073 
1074 	CHECK(cfg_getstringtoken(pctx));
1075 	return (create_string(pctx,
1076 			      TOKEN_STRING(pctx),
1077 			      &cfg_type_sstring,
1078 			      ret));
1079  cleanup:
1080 	return (result);
1081 }
1082 
1083 static isc_result_t
1084 parse_btext(cfg_parser_t *pctx, const cfg_type_t *type,
1085 	    cfg_obj_t **ret)
1086 {
1087 	isc_result_t result;
1088 
1089 	UNUSED(type);
1090 
1091 	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_BTEXT));
1092 	if (pctx->token.type != isc_tokentype_btext) {
1093 		cfg_parser_error(pctx, CFG_LOG_NEAR,
1094 				 "expected bracketed text");
1095 		return (ISC_R_UNEXPECTEDTOKEN);
1096 	}
1097 	return (create_string(pctx,
1098 			      TOKEN_STRING(pctx),
1099 			      &cfg_type_bracketed_text,
1100 			      ret));
1101  cleanup:
1102 	return (result);
1103 }
1104 
1105 static void
1106 print_btext(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1107 	/*
1108 	 * We need to print "{" instead of running print_open()
1109 	 * in order to preserve the exact original formatting
1110 	 * of the bracketed text. But we increment the indent value
1111 	 * so that print_close() will leave us back in our original
1112 	 * state.
1113 	 */
1114 	pctx->indent++;
1115 	cfg_print_cstr(pctx, "{");
1116 	cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
1117 	print_close(pctx);
1118 }
1119 
1120 static void
1121 doc_btext(cfg_printer_t *pctx, const cfg_type_t *type) {
1122 	UNUSED(type);
1123 
1124 	cfg_print_cstr(pctx, "{ <unspecified-text> }");
1125 }
1126 
1127 bool
1128 cfg_is_enum(const char *s, const char *const *enums) {
1129 	const char * const *p;
1130 
1131 	REQUIRE(s != NULL);
1132 	REQUIRE(enums != NULL);
1133 
1134 	for (p = enums; *p != NULL; p++) {
1135 		if (strcasecmp(*p, s) == 0)
1136 			return (true);
1137 	}
1138 	return (false);
1139 }
1140 
1141 static isc_result_t
1142 check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
1143 	const char *s = obj->value.string.base;
1144 
1145 	if (cfg_is_enum(s, enums))
1146 		return (ISC_R_SUCCESS);
1147 	cfg_parser_error(pctx, 0, "'%s' unexpected", s);
1148 	return (ISC_R_UNEXPECTEDTOKEN);
1149 }
1150 
1151 isc_result_t
1152 cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1153 	isc_result_t result;
1154 	cfg_obj_t *obj = NULL;
1155 
1156 	REQUIRE(pctx != NULL);
1157 	REQUIRE(type != NULL);
1158 	REQUIRE(ret != NULL && *ret == NULL);
1159 
1160 	CHECK(parse_ustring(pctx, NULL, &obj));
1161 	CHECK(check_enum(pctx, obj, type->of));
1162 	*ret = obj;
1163 	return (ISC_R_SUCCESS);
1164  cleanup:
1165 	CLEANUP_OBJ(obj);
1166 	return (result);
1167 }
1168 
1169 void
1170 cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
1171 	const char * const *p;
1172 
1173 	REQUIRE(pctx != NULL);
1174 	REQUIRE(type != NULL);
1175 
1176 	cfg_print_cstr(pctx, "( ");
1177 	for (p = type->of; *p != NULL; p++) {
1178 		cfg_print_cstr(pctx, *p);
1179 		if (p[1] != NULL)
1180 			cfg_print_cstr(pctx, " | ");
1181 	}
1182 	cfg_print_cstr(pctx, " )");
1183 }
1184 
1185 isc_result_t
1186 cfg_parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype,
1187 			const cfg_type_t *othertype, cfg_obj_t **ret)
1188 {
1189 	isc_result_t result;
1190 	CHECK(cfg_peektoken(pctx, 0));
1191 	if (pctx->token.type == isc_tokentype_string &&
1192 	    cfg_is_enum(TOKEN_STRING(pctx), enumtype->of))
1193 	{
1194 		CHECK(cfg_parse_enum(pctx, enumtype, ret));
1195 	} else {
1196 		CHECK(cfg_parse_obj(pctx, othertype, ret));
1197 	}
1198  cleanup:
1199 	return (result);
1200 }
1201 
1202 void
1203 cfg_doc_enum_or_other(cfg_printer_t *pctx, const cfg_type_t *enumtype,
1204 		      const cfg_type_t *othertype)
1205 {
1206 	const char * const *p;
1207 	bool first = true;
1208 
1209 	/*
1210 	 * If othertype is cfg_type_void, it means that enumtype is
1211 	 * optional.
1212 	 */
1213 
1214 	if (othertype == &cfg_type_void) {
1215 		cfg_print_cstr(pctx, "[ ");
1216 	}
1217 	cfg_print_cstr(pctx, "( ");
1218 	for (p = enumtype->of; *p != NULL; p++) {
1219 		if (!first) {
1220 			cfg_print_cstr(pctx, " | ");
1221 		}
1222 		first = false;
1223 		cfg_print_cstr(pctx, *p);
1224 	}
1225 	if (othertype != &cfg_type_void) {
1226 		if (!first) {
1227 			cfg_print_cstr(pctx, " | ");
1228 		}
1229 		cfg_doc_terminal(pctx, othertype);
1230 	}
1231 	cfg_print_cstr(pctx, " )");
1232 	if (othertype == &cfg_type_void) {
1233 		cfg_print_cstr(pctx, " ]");
1234 	}
1235 }
1236 
1237 void
1238 cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1239 	REQUIRE(pctx != NULL);
1240 	REQUIRE(obj != NULL);
1241 
1242 	cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
1243 }
1244 
1245 static void
1246 print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1247 	cfg_print_cstr(pctx, "\"");
1248 	cfg_print_ustring(pctx, obj);
1249 	cfg_print_cstr(pctx, "\"");
1250 }
1251 
1252 static void
1253 print_sstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1254 	cfg_print_cstr(pctx, "\"");
1255 	if ((pctx->flags & CFG_PRINTER_XKEY) != 0) {
1256 		unsigned int len = obj->value.string.length;
1257 		while (len-- > 0)
1258 			cfg_print_cstr(pctx, "?");
1259 	} else
1260 		cfg_print_ustring(pctx, obj);
1261 	cfg_print_cstr(pctx, "\"");
1262 }
1263 
1264 static void
1265 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
1266 	isc_mem_put(pctx->mctx, obj->value.string.base,
1267 		    obj->value.string.length + 1);
1268 }
1269 
1270 bool
1271 cfg_obj_isstring(const cfg_obj_t *obj) {
1272 	REQUIRE(obj != NULL);
1273 	return (obj->type->rep == &cfg_rep_string);
1274 }
1275 
1276 const char *
1277 cfg_obj_asstring(const cfg_obj_t *obj) {
1278 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
1279 	return (obj->value.string.base);
1280 }
1281 
1282 /* Quoted string only */
1283 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_qstring = {
1284 	"quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal,
1285 	&cfg_rep_string, NULL
1286 };
1287 
1288 /* Unquoted string only */
1289 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_ustring = {
1290 	"string", parse_ustring, cfg_print_ustring, cfg_doc_terminal,
1291 	&cfg_rep_string, NULL
1292 };
1293 
1294 /* Any string (quoted or unquoted); printed with quotes */
1295 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_astring = {
1296 	"string", cfg_parse_astring, print_qstring, cfg_doc_terminal,
1297 	&cfg_rep_string, NULL
1298 };
1299 
1300 /*
1301  * Any string (quoted or unquoted); printed with quotes.
1302  * If CFG_PRINTER_XKEY is set when printing the string will be '?' out.
1303  */
1304 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_sstring = {
1305 	"string", cfg_parse_sstring, print_sstring, cfg_doc_terminal,
1306 	&cfg_rep_string, NULL
1307 };
1308 
1309 /*
1310  * Text enclosed in brackets. Used to pass a block of configuration
1311  * text to dynamic library or external application. Checked for
1312  * bracket balance, but not otherwise parsed.
1313  */
1314 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_bracketed_text = {
1315 	"bracketed_text", parse_btext, print_btext, doc_btext,
1316 	&cfg_rep_string, NULL
1317 };
1318 
1319 #ifdef HAVE_GEOIP
1320 /*
1321  * "geoip" ACL element:
1322  * geoip [ db <database> ] search-type <string>
1323  */
1324 static const char *geoiptype_enums[] = {
1325 	"area", "areacode", "asnum", "city", "continent", "country",
1326 	"country3", "countryname", "domain", "isp", "metro", "metrocode",
1327 	"netspeed", "org", "postal", "postalcode", "region", "regionname",
1328 	"timezone", "tz", NULL
1329 };
1330 static cfg_type_t cfg_type_geoiptype = {
1331 	"geoiptype", cfg_parse_enum, cfg_print_ustring,
1332 	cfg_doc_enum, &cfg_rep_string, &geoiptype_enums
1333 };
1334 
1335 static const char *geoipdb_enums[] = {
1336 	"asnum", "city", "country", "domain", "isp", "netspeed",
1337 	"org", "region", NULL
1338 };
1339 static cfg_type_t cfg_type_geoipdb = {
1340 	"geoipdb", cfg_parse_enum, cfg_print_ustring,
1341 	cfg_doc_enum, &cfg_rep_string, &geoipdb_enums
1342 };
1343 
1344 static cfg_tuplefielddef_t geoip_fields[] = {
1345 	{ "negated", &cfg_type_void, 0 },
1346 	{ "db", &cfg_type_geoipdb, 0 },
1347 	{ "subtype", &cfg_type_geoiptype, 0 },
1348 	{ "search", &cfg_type_astring, 0 },
1349 	{ NULL, NULL, 0 }
1350 };
1351 
1352 static cfg_type_t cfg_type_geoip = {
1353 	"geoip", parse_geoip, print_geoip, doc_geoip,
1354 	&cfg_rep_tuple, geoip_fields
1355 };
1356 
1357 static isc_result_t
1358 parse_geoip(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1359 	isc_result_t result;
1360 	cfg_obj_t *obj = NULL;
1361 	const cfg_tuplefielddef_t *fields = type->of;
1362 
1363 	CHECK(cfg_create_tuple(pctx, type, &obj));
1364 	CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[0]));
1365 
1366 	/* Parse the optional "db" field. */
1367 	CHECK(cfg_peektoken(pctx, 0));
1368 	if (pctx->token.type == isc_tokentype_string) {
1369 		CHECK(cfg_gettoken(pctx, 0));
1370 		if (strcasecmp(TOKEN_STRING(pctx), "db") == 0 &&
1371 		    obj->value.tuple[1] == NULL) {
1372 			CHECK(cfg_parse_obj(pctx, fields[1].type,
1373 				    &obj->value.tuple[1]));
1374 		} else {
1375 			CHECK(cfg_parse_void(pctx, NULL,
1376 					     &obj->value.tuple[1]));
1377 			cfg_ungettoken(pctx);
1378 		}
1379 	}
1380 
1381 	CHECK(cfg_parse_obj(pctx, fields[2].type, &obj->value.tuple[2]));
1382 	CHECK(cfg_parse_obj(pctx, fields[3].type, &obj->value.tuple[3]));
1383 
1384 	*ret = obj;
1385 	return (ISC_R_SUCCESS);
1386 
1387  cleanup:
1388 	CLEANUP_OBJ(obj);
1389 	return (result);
1390 }
1391 
1392 static void
1393 print_geoip(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1394 	if (obj->value.tuple[1]->type->print != cfg_print_void) {
1395 		cfg_print_cstr(pctx, " db ");
1396 		cfg_print_obj(pctx, obj->value.tuple[1]);
1397 	}
1398 	cfg_print_obj(pctx, obj->value.tuple[2]);
1399 	cfg_print_obj(pctx, obj->value.tuple[3]);
1400 }
1401 
1402 static void
1403 doc_geoip(cfg_printer_t *pctx, const cfg_type_t *type) {
1404 	UNUSED(type);
1405 	cfg_print_cstr(pctx, "[ db ");
1406 	cfg_doc_enum(pctx, &cfg_type_geoipdb);
1407 	cfg_print_cstr(pctx, " ]");
1408 	cfg_print_cstr(pctx, " ");
1409 	cfg_doc_enum(pctx, &cfg_type_geoiptype);
1410 	cfg_print_cstr(pctx, " ");
1411 	cfg_print_cstr(pctx, "<quoted_string>");
1412 }
1413 #endif /* HAVE_GEOIP */
1414 
1415 static cfg_type_t cfg_type_addrmatchelt;
1416 static cfg_type_t cfg_type_negated;
1417 
1418 static isc_result_t
1419 parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type,
1420 		   cfg_obj_t **ret)
1421 {
1422 	isc_result_t result;
1423 	UNUSED(type);
1424 
1425 	CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
1426 
1427 	if (pctx->token.type == isc_tokentype_string ||
1428 	    pctx->token.type == isc_tokentype_qstring) {
1429 		if (pctx->token.type == isc_tokentype_string &&
1430 		    (strcasecmp(TOKEN_STRING(pctx), "key") == 0)) {
1431 			CHECK(cfg_parse_obj(pctx, &cfg_type_keyref, ret));
1432 		} else if (pctx->token.type == isc_tokentype_string &&
1433 			   (strcasecmp(TOKEN_STRING(pctx), "geoip") == 0)) {
1434 #ifdef HAVE_GEOIP
1435 			CHECK(cfg_gettoken(pctx, 0));
1436 			CHECK(cfg_parse_obj(pctx, &cfg_type_geoip, ret));
1437 #else
1438 			cfg_parser_error(pctx, CFG_LOG_NEAR, "'geoip' "
1439 					 "not supported in this build");
1440 			return (ISC_R_UNEXPECTEDTOKEN);
1441 #endif
1442 		} else {
1443 			if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK |
1444 						  CFG_ADDR_V4PREFIXOK |
1445 						  CFG_ADDR_V6OK))
1446 			{
1447 				CHECK(cfg_parse_netprefix(pctx, NULL, ret));
1448 			} else {
1449 				CHECK(cfg_parse_astring(pctx, NULL, ret));
1450 			}
1451 		}
1452 	} else if (pctx->token.type == isc_tokentype_special) {
1453 		if (pctx->token.value.as_char == '{') {
1454 			/* Nested match list. */
1455 			CHECK(cfg_parse_obj(pctx,
1456 					    &cfg_type_bracketed_aml, ret));
1457 		} else if (pctx->token.value.as_char == '!') {
1458 			CHECK(cfg_gettoken(pctx, 0)); /* read "!" */
1459 			CHECK(cfg_parse_obj(pctx, &cfg_type_negated, ret));
1460 		} else {
1461 			goto bad;
1462 		}
1463 	} else {
1464 	bad:
1465 		cfg_parser_error(pctx, CFG_LOG_NEAR,
1466 			     "expected IP match list element");
1467 		return (ISC_R_UNEXPECTEDTOKEN);
1468 	}
1469  cleanup:
1470 	return (result);
1471 }
1472 
1473 /*%
1474  * A negated address match list element (like "! 10.0.0.1").
1475  * Somewhat sneakily, the caller is expected to parse the
1476  * "!", but not to print it.
1477  */
1478 static cfg_tuplefielddef_t negated_fields[] = {
1479 	{ "negated", &cfg_type_addrmatchelt, 0 },
1480 	{ NULL, NULL, 0 }
1481 };
1482 
1483 static void
1484 print_negated(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1485 	cfg_print_cstr(pctx, "!");
1486 	cfg_print_tuple(pctx, obj);
1487 }
1488 
1489 static cfg_type_t cfg_type_negated = {
1490 	"negated", cfg_parse_tuple, print_negated, NULL, &cfg_rep_tuple,
1491 	&negated_fields
1492 };
1493 
1494 /*% An address match list element */
1495 
1496 static cfg_type_t cfg_type_addrmatchelt = {
1497 	"address_match_element", parse_addrmatchelt, NULL, cfg_doc_terminal,
1498 	NULL, NULL
1499 };
1500 
1501 /*%
1502  * A bracketed address match list
1503  */
1504 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_bracketed_aml = {
1505 	"bracketed_aml", cfg_parse_bracketed_list, cfg_print_bracketed_list,
1506 	cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_addrmatchelt
1507 };
1508 
1509 /*
1510  * Optional bracketed text
1511  */
1512 static isc_result_t
1513 parse_optional_btext(cfg_parser_t *pctx, const cfg_type_t *type,
1514 		     cfg_obj_t **ret)
1515 {
1516 	isc_result_t result;
1517 
1518 	UNUSED(type);
1519 
1520 	CHECK(cfg_peektoken(pctx, ISC_LEXOPT_BTEXT));
1521 	if (pctx->token.type == isc_tokentype_btext) {
1522 		CHECK(cfg_parse_obj(pctx, &cfg_type_bracketed_text, ret));
1523 	} else {
1524 		CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
1525 	}
1526  cleanup:
1527 	return (result);
1528 }
1529 
1530 static void
1531 print_optional_btext(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1532 	if (obj->type == &cfg_type_void) {
1533 		return;
1534 	}
1535 
1536 	pctx->indent++;
1537 	cfg_print_cstr(pctx, "{");
1538 	cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
1539 	print_close(pctx);
1540 }
1541 
1542 static void
1543 doc_optional_btext(cfg_printer_t *pctx, const cfg_type_t *type) {
1544 	UNUSED(type);
1545 
1546 	cfg_print_cstr(pctx, "[ { <unspecified-text> } ]");
1547 }
1548 
1549 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_optional_bracketed_text = {
1550 	"optional_btext", parse_optional_btext, print_optional_btext,
1551 	doc_optional_btext, NULL, NULL
1552 };
1553 
1554 /*
1555  * Booleans
1556  */
1557 
1558 bool
1559 cfg_obj_isboolean(const cfg_obj_t *obj) {
1560 	REQUIRE(obj != NULL);
1561 	return (obj->type->rep == &cfg_rep_boolean);
1562 }
1563 
1564 bool
1565 cfg_obj_asboolean(const cfg_obj_t *obj) {
1566 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
1567 	return (obj->value.boolean);
1568 }
1569 
1570 isc_result_t
1571 cfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1572 {
1573 	isc_result_t result;
1574 	bool value;
1575 	cfg_obj_t *obj = NULL;
1576 
1577 	REQUIRE(pctx != NULL);
1578 	REQUIRE(ret != NULL && *ret == NULL);
1579 
1580 	UNUSED(type);
1581 
1582 	result = cfg_gettoken(pctx, 0);
1583 	if (result != ISC_R_SUCCESS)
1584 		return (result);
1585 
1586 	if (pctx->token.type != isc_tokentype_string)
1587 		goto bad_boolean;
1588 
1589 	if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
1590 	    (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
1591 	    (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
1592 		value = true;
1593 	} else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
1594 		   (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
1595 		   (strcmp(TOKEN_STRING(pctx), "0") == 0)) {
1596 		value = false;
1597 	} else {
1598 		goto bad_boolean;
1599 	}
1600 
1601 	CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
1602 	obj->value.boolean = value;
1603 	*ret = obj;
1604 	return (result);
1605 
1606  bad_boolean:
1607 	cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
1608 	return (ISC_R_UNEXPECTEDTOKEN);
1609 
1610  cleanup:
1611 	return (result);
1612 }
1613 
1614 void
1615 cfg_print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1616 	REQUIRE(pctx != NULL);
1617 	REQUIRE(obj != NULL);
1618 
1619 	if (obj->value.boolean)
1620 		cfg_print_cstr(pctx, "yes");
1621 	else
1622 		cfg_print_cstr(pctx, "no");
1623 }
1624 
1625 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_boolean = {
1626 	"boolean", cfg_parse_boolean, cfg_print_boolean, cfg_doc_terminal,
1627 	&cfg_rep_boolean, NULL
1628 };
1629 
1630 /*
1631  * Lists.
1632  */
1633 
1634 isc_result_t
1635 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
1636 	isc_result_t result;
1637 
1638 	REQUIRE(pctx != NULL);
1639 	REQUIRE(type != NULL);
1640 	REQUIRE(obj != NULL && *obj == NULL);
1641 
1642 	CHECK(cfg_create_obj(pctx, type, obj));
1643 	ISC_LIST_INIT((*obj)->value.list);
1644  cleanup:
1645 	return (result);
1646 }
1647 
1648 static isc_result_t
1649 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
1650 	cfg_listelt_t *elt;
1651 
1652 	elt = isc_mem_get(pctx->mctx, sizeof(*elt));
1653 	if (elt == NULL)
1654 		return (ISC_R_NOMEMORY);
1655 	elt->obj = NULL;
1656 	ISC_LINK_INIT(elt, link);
1657 	*eltp = elt;
1658 	return (ISC_R_SUCCESS);
1659 }
1660 
1661 static void
1662 free_listelt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
1663 	if (elt->obj != NULL)
1664 		cfg_obj_destroy(pctx, &elt->obj);
1665 	isc_mem_put(pctx->mctx, elt, sizeof(*elt));
1666 }
1667 
1668 static void
1669 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
1670 	cfg_listelt_t *elt, *next;
1671 	for (elt = ISC_LIST_HEAD(obj->value.list);
1672 	     elt != NULL;
1673 	     elt = next)
1674 	{
1675 		next = ISC_LIST_NEXT(elt, link);
1676 		free_listelt(pctx, elt);
1677 	}
1678 }
1679 
1680 isc_result_t
1681 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
1682 		  cfg_listelt_t **ret)
1683 {
1684 	isc_result_t result;
1685 	cfg_listelt_t *elt = NULL;
1686 	cfg_obj_t *value = NULL;
1687 
1688 	REQUIRE(pctx != NULL);
1689 	REQUIRE(elttype != NULL);
1690 	REQUIRE(ret != NULL && *ret == NULL);
1691 
1692 	CHECK(create_listelt(pctx, &elt));
1693 
1694 	result = cfg_parse_obj(pctx, elttype, &value);
1695 	if (result != ISC_R_SUCCESS)
1696 		goto cleanup;
1697 
1698 	elt->obj = value;
1699 
1700 	*ret = elt;
1701 	return (ISC_R_SUCCESS);
1702 
1703  cleanup:
1704 	isc_mem_put(pctx->mctx, elt, sizeof(*elt));
1705 	return (result);
1706 }
1707 
1708 /*
1709  * Parse a homogeneous list whose elements are of type 'elttype'
1710  * and where each element is terminated by a semicolon.
1711  */
1712 static isc_result_t
1713 parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
1714 {
1715 	cfg_obj_t *listobj = NULL;
1716 	const cfg_type_t *listof = listtype->of;
1717 	isc_result_t result;
1718 	cfg_listelt_t *elt = NULL;
1719 
1720 	CHECK(cfg_create_list(pctx, listtype, &listobj));
1721 
1722 	for (;;) {
1723 		CHECK(cfg_peektoken(pctx, 0));
1724 		if (pctx->token.type == isc_tokentype_special &&
1725 		    pctx->token.value.as_char == /*{*/ '}')
1726 			break;
1727 		CHECK(cfg_parse_listelt(pctx, listof, &elt));
1728 		CHECK(parse_semicolon(pctx));
1729 		ISC_LIST_APPEND(listobj->value.list, elt, link);
1730 		elt = NULL;
1731 	}
1732 	*ret = listobj;
1733 	return (ISC_R_SUCCESS);
1734 
1735  cleanup:
1736 	if (elt != NULL)
1737 		free_listelt(pctx, elt);
1738 	CLEANUP_OBJ(listobj);
1739 	return (result);
1740 }
1741 
1742 static void
1743 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1744 	const cfg_list_t *list = &obj->value.list;
1745 	const cfg_listelt_t *elt;
1746 
1747 	for (elt = ISC_LIST_HEAD(*list);
1748 	     elt != NULL;
1749 	     elt = ISC_LIST_NEXT(elt, link))
1750 	{
1751 		if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) {
1752 			cfg_print_obj(pctx, elt->obj);
1753 			cfg_print_cstr(pctx, "; ");
1754 		} else {
1755 			cfg_print_indent(pctx);
1756 			cfg_print_obj(pctx, elt->obj);
1757 			cfg_print_cstr(pctx, ";\n");
1758 		}
1759 	}
1760 }
1761 
1762 isc_result_t
1763 cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
1764 			 cfg_obj_t **ret)
1765 {
1766 	isc_result_t result;
1767 
1768 	REQUIRE(pctx != NULL);
1769 	REQUIRE(type != NULL);
1770 	REQUIRE(ret != NULL && *ret == NULL);
1771 
1772 	CHECK(cfg_parse_special(pctx, '{'));
1773 	CHECK(parse_list(pctx, type, ret));
1774 	CHECK(cfg_parse_special(pctx, '}'));
1775  cleanup:
1776 	return (result);
1777 }
1778 
1779 void
1780 cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1781 	REQUIRE(pctx != NULL);
1782 	REQUIRE(obj != NULL);
1783 
1784 	print_open(pctx);
1785 	print_list(pctx, obj);
1786 	print_close(pctx);
1787 }
1788 
1789 void
1790 cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
1791 	REQUIRE(pctx != NULL);
1792 	REQUIRE(type != NULL);
1793 
1794 	cfg_print_cstr(pctx, "{ ");
1795 	cfg_doc_obj(pctx, type->of);
1796 	cfg_print_cstr(pctx, "; ... }");
1797 }
1798 
1799 /*
1800  * Parse a homogeneous list whose elements are of type 'elttype'
1801  * and where elements are separated by space.  The list ends
1802  * before the first semicolon.
1803  */
1804 isc_result_t
1805 cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
1806 		    cfg_obj_t **ret)
1807 {
1808 	cfg_obj_t *listobj = NULL;
1809 	const cfg_type_t *listof = listtype->of;
1810 	isc_result_t result;
1811 
1812 	REQUIRE(pctx != NULL);
1813 	REQUIRE(listtype != NULL);
1814 	REQUIRE(ret != NULL && *ret == NULL);
1815 
1816 	CHECK(cfg_create_list(pctx, listtype, &listobj));
1817 
1818 	for (;;) {
1819 		cfg_listelt_t *elt = NULL;
1820 
1821 		CHECK(cfg_peektoken(pctx, 0));
1822 		if (pctx->token.type == isc_tokentype_special &&
1823 		    pctx->token.value.as_char == ';')
1824 			break;
1825 		CHECK(cfg_parse_listelt(pctx, listof, &elt));
1826 		ISC_LIST_APPEND(listobj->value.list, elt, link);
1827 	}
1828 	*ret = listobj;
1829 	return (ISC_R_SUCCESS);
1830 
1831  cleanup:
1832 	CLEANUP_OBJ(listobj);
1833 	return (result);
1834 }
1835 
1836 void
1837 cfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1838 	const cfg_list_t *list = &obj->value.list;
1839 	const cfg_listelt_t *elt;
1840 
1841 	REQUIRE(pctx != NULL);
1842 	REQUIRE(obj != NULL);
1843 
1844 	for (elt = ISC_LIST_HEAD(*list);
1845 	     elt != NULL;
1846 	     elt = ISC_LIST_NEXT(elt, link)) {
1847 		cfg_print_obj(pctx, elt->obj);
1848 		if (ISC_LIST_NEXT(elt, link) != NULL)
1849 			cfg_print_cstr(pctx, " ");
1850 	}
1851 }
1852 
1853 bool
1854 cfg_obj_islist(const cfg_obj_t *obj) {
1855 	REQUIRE(obj != NULL);
1856 	return (obj->type->rep == &cfg_rep_list);
1857 }
1858 
1859 const cfg_listelt_t *
1860 cfg_list_first(const cfg_obj_t *obj) {
1861 	REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
1862 	if (obj == NULL)
1863 		return (NULL);
1864 	return (ISC_LIST_HEAD(obj->value.list));
1865 }
1866 
1867 const cfg_listelt_t *
1868 cfg_list_next(const cfg_listelt_t *elt) {
1869 	REQUIRE(elt != NULL);
1870 	return (ISC_LIST_NEXT(elt, link));
1871 }
1872 
1873 /*
1874  * Return the length of a list object.  If obj is NULL or is not
1875  * a list, return 0.
1876  */
1877 unsigned int
1878 cfg_list_length(const cfg_obj_t *obj, bool recurse) {
1879 	const cfg_listelt_t *elt;
1880 	unsigned int count = 0;
1881 
1882 	if (obj == NULL || !cfg_obj_islist(obj))
1883 		return (0U);
1884 	for (elt = cfg_list_first(obj);
1885 	     elt != NULL;
1886 	     elt = cfg_list_next(elt)) {
1887 		if (recurse && cfg_obj_islist(elt->obj)) {
1888 			count += cfg_list_length(elt->obj, recurse);
1889 		} else {
1890 			count++;
1891 		}
1892 	}
1893 	return (count);
1894 }
1895 
1896 cfg_obj_t *
1897 cfg_listelt_value(const cfg_listelt_t *elt) {
1898 	REQUIRE(elt != NULL);
1899 	return (elt->obj);
1900 }
1901 
1902 /*
1903  * Maps.
1904  */
1905 
1906 /*
1907  * Parse a map body.  That's something like
1908  *
1909  *   "foo 1; bar { glub; }; zap true; zap false;"
1910  *
1911  * i.e., a sequence of option names followed by values and
1912  * terminated by semicolons.  Used for the top level of
1913  * the named.conf syntax, as well as for the body of the
1914  * options, view, zone, and other statements.
1915  */
1916 isc_result_t
1917 cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1918 {
1919 	const cfg_clausedef_t * const *clausesets = type->of;
1920 	isc_result_t result;
1921 	const cfg_clausedef_t * const *clauseset;
1922 	const cfg_clausedef_t *clause;
1923 	cfg_obj_t *value = NULL;
1924 	cfg_obj_t *obj = NULL;
1925 	cfg_obj_t *eltobj = NULL;
1926 	cfg_obj_t *includename = NULL;
1927 	isc_symvalue_t symval;
1928 	cfg_list_t *list = NULL;
1929 
1930 	REQUIRE(pctx != NULL);
1931 	REQUIRE(type != NULL);
1932 	REQUIRE(ret != NULL && *ret == NULL);
1933 
1934 	CHECK(create_map(pctx, type, &obj));
1935 
1936 	obj->value.map.clausesets = clausesets;
1937 
1938 	for (;;) {
1939 		cfg_listelt_t *elt;
1940 
1941 	redo:
1942 		/*
1943 		 * Parse the option name and see if it is known.
1944 		 */
1945 		CHECK(cfg_gettoken(pctx, 0));
1946 
1947 		if (pctx->token.type != isc_tokentype_string) {
1948 			cfg_ungettoken(pctx);
1949 			break;
1950 		}
1951 
1952 		/*
1953 		 * We accept "include" statements wherever a map body
1954 		 * clause can occur.
1955 		 */
1956 		if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
1957 			/*
1958 			 * Turn the file name into a temporary configuration
1959 			 * object just so that it is not overwritten by the
1960 			 * semicolon token.
1961 			 */
1962 			CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
1963 			CHECK(parse_semicolon(pctx));
1964 			CHECK(parser_openfile(pctx, includename->
1965 					      value.string.base));
1966 			 cfg_obj_destroy(pctx, &includename);
1967 			 goto redo;
1968 		}
1969 
1970 		clause = NULL;
1971 		for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
1972 			for (clause = *clauseset;
1973 			     clause->name != NULL;
1974 			     clause++) {
1975 				if (strcasecmp(TOKEN_STRING(pctx),
1976 					   clause->name) == 0)
1977 					goto done;
1978 			}
1979 		}
1980 	done:
1981 		if (clause == NULL || clause->name == NULL) {
1982 			cfg_parser_error(pctx, CFG_LOG_NOPREP,
1983 					 "unknown option");
1984 			/*
1985 			 * Try to recover by parsing this option as an unknown
1986 			 * option and discarding it.
1987 			 */
1988 			CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported,
1989 					    &eltobj));
1990 			cfg_obj_destroy(pctx, &eltobj);
1991 			CHECK(parse_semicolon(pctx));
1992 			continue;
1993 		}
1994 
1995 		/* Clause is known. */
1996 
1997 		/* Issue fatal errors if appropriate */
1998 		if ((clause->flags & CFG_CLAUSEFLAG_ANCIENT) != 0) {
1999 			cfg_parser_error(pctx, 0,
2000 					 "option '%s' no longer exists",
2001 					 clause->name);
2002 			CHECK(ISC_R_FAILURE);
2003 		}
2004 
2005 		/* Issue warnings if appropriate */
2006 		if ((pctx->flags & CFG_PCTX_NODEPRECATED) == 0 &&
2007 		    (clause->flags & CFG_CLAUSEFLAG_DEPRECATED) != 0)
2008 		{
2009 			cfg_parser_warning(pctx, 0,
2010 					   "option '%s' is deprecated",
2011 					   clause->name);
2012 		}
2013 		if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0) {
2014 			cfg_parser_warning(pctx, 0,
2015 					   "option '%s' is obsolete and "
2016 					   "should be removed ",
2017 					   clause->name);
2018 		}
2019 		if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0) {
2020 			cfg_parser_warning(pctx, 0,
2021 					   "option '%s' is not implemented",
2022 					   clause->name);
2023 		}
2024 		if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0) {
2025 			cfg_parser_warning(pctx, 0,
2026 					   "option '%s' is not implemented",
2027 					   clause->name);
2028 		}
2029 		if ((clause->flags & CFG_CLAUSEFLAG_NOOP) != 0) {
2030 			cfg_parser_warning(pctx, 0, "option '%s' was not "
2031 					   "enabled at compile time "
2032 					   "(ignored)", clause->name);
2033 		}
2034 
2035 		if ((clause->flags & CFG_CLAUSEFLAG_NOTCONFIGURED) != 0) {
2036 			cfg_parser_error(pctx, 0, "option '%s' was not "
2037 					   "enabled at compile time",
2038 					   clause->name);
2039 			CHECK(ISC_R_FAILURE);
2040 		}
2041 
2042 		/*
2043 		 * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
2044 		 * set here - we need to log the *lack* of such an option,
2045 		 * not its presence.
2046 		 */
2047 
2048 		/* See if the clause already has a value; if not create one. */
2049 		result = isc_symtab_lookup(obj->value.map.symtab,
2050 					   clause->name, 0, &symval);
2051 
2052 		if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
2053 			/* Multivalued clause */
2054 			cfg_obj_t *listobj = NULL;
2055 			if (result == ISC_R_NOTFOUND) {
2056 				CHECK(cfg_create_list(pctx,
2057 						  &cfg_type_implicitlist,
2058 						  &listobj));
2059 				symval.as_pointer = listobj;
2060 				result = isc_symtab_define(obj->value.
2061 						   map.symtab,
2062 						   clause->name,
2063 						   1, symval,
2064 						   isc_symexists_reject);
2065 				if (result != ISC_R_SUCCESS) {
2066 					cfg_parser_error(pctx, CFG_LOG_NEAR,
2067 						     "isc_symtab_define(%s) "
2068 						     "failed", clause->name);
2069 					isc_mem_put(pctx->mctx, list,
2070 						    sizeof(cfg_list_t));
2071 					goto cleanup;
2072 				}
2073 			} else {
2074 				INSIST(result == ISC_R_SUCCESS);
2075 				listobj = symval.as_pointer;
2076 			}
2077 
2078 			elt = NULL;
2079 			CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
2080 			CHECK(parse_semicolon(pctx));
2081 
2082 			ISC_LIST_APPEND(listobj->value.list, elt, link);
2083 		} else {
2084 			/* Single-valued clause */
2085 			if (result == ISC_R_NOTFOUND) {
2086 				bool callback =
2087 					((clause->flags &
2088 					  CFG_CLAUSEFLAG_CALLBACK) != 0);
2089 				CHECK(parse_symtab_elt(pctx, clause->name,
2090 						       clause->type,
2091 						       obj->value.map.symtab,
2092 						       callback));
2093 				CHECK(parse_semicolon(pctx));
2094 			} else if (result == ISC_R_SUCCESS) {
2095 				cfg_parser_error(pctx, CFG_LOG_NEAR,
2096 						 "'%s' redefined",
2097 						 clause->name);
2098 				result = ISC_R_EXISTS;
2099 				goto cleanup;
2100 			} else {
2101 				cfg_parser_error(pctx, CFG_LOG_NEAR,
2102 					     "isc_symtab_define() failed");
2103 				goto cleanup;
2104 			}
2105 		}
2106 	}
2107 
2108 
2109 	*ret = obj;
2110 	return (ISC_R_SUCCESS);
2111 
2112  cleanup:
2113 	CLEANUP_OBJ(value);
2114 	CLEANUP_OBJ(obj);
2115 	CLEANUP_OBJ(eltobj);
2116 	CLEANUP_OBJ(includename);
2117 	return (result);
2118 }
2119 
2120 static isc_result_t
2121 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
2122 		 cfg_type_t *elttype, isc_symtab_t *symtab,
2123 		 bool callback)
2124 {
2125 	isc_result_t result;
2126 	cfg_obj_t *obj = NULL;
2127 	isc_symvalue_t symval;
2128 
2129 	CHECK(cfg_parse_obj(pctx, elttype, &obj));
2130 
2131 	if (callback && pctx->callback != NULL)
2132 		CHECK(pctx->callback(name, obj, pctx->callbackarg));
2133 
2134 	symval.as_pointer = obj;
2135 	CHECK(isc_symtab_define(symtab, name,
2136 				1, symval,
2137 				isc_symexists_reject));
2138 	return (ISC_R_SUCCESS);
2139 
2140  cleanup:
2141 	CLEANUP_OBJ(obj);
2142 	return (result);
2143 }
2144 
2145 /*
2146  * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
2147  */
2148 isc_result_t
2149 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2150 	isc_result_t result;
2151 
2152 	REQUIRE(pctx != NULL);
2153 	REQUIRE(type != NULL);
2154 	REQUIRE(ret != NULL && *ret == NULL);
2155 
2156 	CHECK(cfg_parse_special(pctx, '{'));
2157 	CHECK(cfg_parse_mapbody(pctx, type, ret));
2158 	CHECK(cfg_parse_special(pctx, '}'));
2159  cleanup:
2160 	return (result);
2161 }
2162 
2163 /*
2164  * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
2165  */
2166 static isc_result_t
2167 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype,
2168 		    const cfg_type_t *type, cfg_obj_t **ret)
2169 {
2170 	isc_result_t result;
2171 	cfg_obj_t *idobj = NULL;
2172 	cfg_obj_t *mapobj = NULL;
2173 
2174 	REQUIRE(pctx != NULL);
2175 	REQUIRE(nametype != NULL);
2176 	REQUIRE(type != NULL);
2177 	REQUIRE(ret != NULL && *ret == NULL);
2178 
2179 	CHECK(cfg_parse_obj(pctx, nametype, &idobj));
2180 	CHECK(cfg_parse_map(pctx, type, &mapobj));
2181 	mapobj->value.map.id = idobj;
2182 	*ret = mapobj;
2183 	return (result);
2184  cleanup:
2185 	CLEANUP_OBJ(idobj);
2186 	CLEANUP_OBJ(mapobj);
2187 	return (result);
2188 }
2189 
2190 /*
2191  * Parse a map identified by a string name.  E.g., "name { foo 1; }".
2192  * Used for the "key" and "channel" statements.
2193  */
2194 isc_result_t
2195 cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2196 	return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
2197 }
2198 
2199 /*
2200  * Parse a map identified by a network address.
2201  * Used to be used for the "server" statement.
2202  */
2203 isc_result_t
2204 cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2205 	return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
2206 }
2207 
2208 /*
2209  * Parse a map identified by a network prefix.
2210  * Used for the "server" statement.
2211  */
2212 isc_result_t
2213 cfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2214 	return (parse_any_named_map(pctx, &cfg_type_netprefix, type, ret));
2215 }
2216 
2217 static void
2218 print_symval(cfg_printer_t *pctx, const char *name, cfg_obj_t *obj) {
2219 	if ((pctx->flags & CFG_PRINTER_ONELINE) == 0)
2220 		cfg_print_indent(pctx);
2221 
2222 	cfg_print_cstr(pctx, name);
2223 	cfg_print_cstr(pctx, " ");
2224 	cfg_print_obj(pctx, obj);
2225 
2226 	if ((pctx->flags & CFG_PRINTER_ONELINE) == 0)
2227 		cfg_print_cstr(pctx, ";\n");
2228 	else
2229 		cfg_print_cstr(pctx, "; ");
2230 }
2231 
2232 void
2233 cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2234 	isc_result_t result = ISC_R_SUCCESS;
2235 	const cfg_clausedef_t * const *clauseset;
2236 
2237 	REQUIRE(pctx != NULL);
2238 	REQUIRE(obj != NULL);
2239 
2240 	for (clauseset = obj->value.map.clausesets;
2241 	     *clauseset != NULL;
2242 	     clauseset++)
2243 	{
2244 		isc_symvalue_t symval;
2245 		const cfg_clausedef_t *clause;
2246 
2247 		for (clause = *clauseset;
2248 		     clause->name != NULL;
2249 		     clause++) {
2250 			result = isc_symtab_lookup(obj->value.map.symtab,
2251 						   clause->name, 0, &symval);
2252 			if (result == ISC_R_SUCCESS) {
2253 				cfg_obj_t *symobj = symval.as_pointer;
2254 				if (symobj->type == &cfg_type_implicitlist) {
2255 					/* Multivalued. */
2256 					cfg_list_t *list = &symobj->value.list;
2257 					cfg_listelt_t *elt;
2258 					for (elt = ISC_LIST_HEAD(*list);
2259 					     elt != NULL;
2260 					     elt = ISC_LIST_NEXT(elt, link)) {
2261 						print_symval(pctx,
2262 							     clause->name,
2263 							     elt->obj);
2264 					}
2265 				} else {
2266 					/* Single-valued. */
2267 					print_symval(pctx, clause->name,
2268 						     symobj);
2269 				}
2270 			} else if (result == ISC_R_NOTFOUND) {
2271 				; /* do nothing */
2272 			} else {
2273 				INSIST(0);
2274 				ISC_UNREACHABLE();
2275 			}
2276 		}
2277 	}
2278 }
2279 
2280 static struct flagtext {
2281 	unsigned int flag;
2282 	const char *text;
2283 } flagtexts[] = {
2284 	{ CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
2285 	{ CFG_CLAUSEFLAG_NYI, "not yet implemented" },
2286 	{ CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
2287 	{ CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
2288 	{ CFG_CLAUSEFLAG_TESTONLY, "test only" },
2289 	{ CFG_CLAUSEFLAG_NOTCONFIGURED, "not configured" },
2290 	{ CFG_CLAUSEFLAG_MULTI, "may occur multiple times" },
2291 	{ CFG_CLAUSEFLAG_EXPERIMENTAL, "experimental" },
2292 	{ CFG_CLAUSEFLAG_NOOP, "non-operational" },
2293 	{ CFG_CLAUSEFLAG_DEPRECATED, "deprecated" },
2294 	{ CFG_CLAUSEFLAG_ANCIENT, "ancient" },
2295 	{ 0, NULL }
2296 };
2297 
2298 void
2299 cfg_print_clauseflags(cfg_printer_t *pctx, unsigned int flags) {
2300 	struct flagtext *p;
2301 	bool first = true;
2302 	for (p = flagtexts; p->flag != 0; p++) {
2303 		if ((flags & p->flag) != 0) {
2304 			if (first)
2305 				cfg_print_cstr(pctx, " // ");
2306 			else
2307 				cfg_print_cstr(pctx, ", ");
2308 			cfg_print_cstr(pctx, p->text);
2309 			first = false;
2310 		}
2311 	}
2312 }
2313 
2314 void
2315 cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
2316 	const cfg_clausedef_t * const *clauseset;
2317 	const cfg_clausedef_t *clause;
2318 
2319 	REQUIRE(pctx != NULL);
2320 	REQUIRE(type != NULL);
2321 
2322 	for (clauseset = type->of; *clauseset != NULL; clauseset++) {
2323 		for (clause = *clauseset; clause->name != NULL; clause++) {
2324 			cfg_print_cstr(pctx, clause->name);
2325 			cfg_print_cstr(pctx, " ");
2326 			cfg_doc_obj(pctx, clause->type);
2327 			cfg_print_cstr(pctx, ";");
2328 			cfg_print_clauseflags(pctx, clause->flags);
2329 			cfg_print_cstr(pctx, "\n\n");
2330 		}
2331 	}
2332 }
2333 
2334 void
2335 cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2336 	REQUIRE(pctx != NULL);
2337 	REQUIRE(obj != NULL);
2338 
2339 	if (obj->value.map.id != NULL) {
2340 		cfg_print_obj(pctx, obj->value.map.id);
2341 		cfg_print_cstr(pctx, " ");
2342 	}
2343 	print_open(pctx);
2344 	cfg_print_mapbody(pctx, obj);
2345 	print_close(pctx);
2346 }
2347 
2348 void
2349 cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
2350 	const cfg_clausedef_t * const *clauseset;
2351 	const cfg_clausedef_t *clause;
2352 
2353 	REQUIRE(pctx != NULL);
2354 	REQUIRE(type != NULL);
2355 
2356 	if (type->parse == cfg_parse_named_map) {
2357 		cfg_doc_obj(pctx, &cfg_type_astring);
2358 		cfg_print_cstr(pctx, " ");
2359 	} else if (type->parse == cfg_parse_addressed_map) {
2360 		cfg_doc_obj(pctx, &cfg_type_netaddr);
2361 		cfg_print_cstr(pctx, " ");
2362 	} else if (type->parse == cfg_parse_netprefix_map) {
2363 		cfg_doc_obj(pctx, &cfg_type_netprefix);
2364 		cfg_print_cstr(pctx, " ");
2365 	}
2366 
2367 	print_open(pctx);
2368 
2369 	for (clauseset = type->of; *clauseset != NULL; clauseset++) {
2370 		for (clause = *clauseset; clause->name != NULL; clause++) {
2371 			cfg_print_indent(pctx);
2372 			cfg_print_cstr(pctx, clause->name);
2373 			if (clause->type->print != cfg_print_void)
2374 				cfg_print_cstr(pctx, " ");
2375 			cfg_doc_obj(pctx, clause->type);
2376 			cfg_print_cstr(pctx, ";");
2377 			cfg_print_clauseflags(pctx, clause->flags);
2378 			cfg_print_cstr(pctx, "\n");
2379 		}
2380 	}
2381 	print_close(pctx);
2382 }
2383 
2384 bool
2385 cfg_obj_ismap(const cfg_obj_t *obj) {
2386 	REQUIRE(obj != NULL);
2387 	return (obj->type->rep == &cfg_rep_map);
2388 }
2389 
2390 isc_result_t
2391 cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
2392 	isc_result_t result;
2393 	isc_symvalue_t val;
2394 	const cfg_map_t *map;
2395 
2396 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
2397 	REQUIRE(name != NULL);
2398 	REQUIRE(obj != NULL && *obj == NULL);
2399 
2400 	map = &mapobj->value.map;
2401 
2402 	result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
2403 	if (result != ISC_R_SUCCESS)
2404 		return (result);
2405 	*obj = val.as_pointer;
2406 	return (ISC_R_SUCCESS);
2407 }
2408 
2409 const cfg_obj_t *
2410 cfg_map_getname(const cfg_obj_t *mapobj) {
2411 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
2412 	return (mapobj->value.map.id);
2413 }
2414 
2415 unsigned int
2416 cfg_map_count(const cfg_obj_t *mapobj) {
2417 	const cfg_map_t *map;
2418 
2419 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
2420 
2421 	map = &mapobj->value.map;
2422 	return (isc_symtab_count(map->symtab));
2423 }
2424 
2425 const char *
2426 cfg_map_firstclause(const cfg_type_t *map, const void **clauses,
2427 		    unsigned int *idx)
2428 {
2429 	cfg_clausedef_t * const * clauseset;
2430 
2431 	REQUIRE(map != NULL && map->rep == &cfg_rep_map);
2432 	REQUIRE(idx != NULL);
2433 	REQUIRE(clauses != NULL && *clauses == NULL);
2434 
2435 	clauseset = map->of;
2436 	if (*clauseset == NULL) {
2437 		return (NULL);
2438 	}
2439 	*clauses = *clauseset;
2440 	*idx = 0;
2441 	while ((*clauseset)[*idx].name == NULL) {
2442 		*clauses = (*++clauseset);
2443 		if (*clauses == NULL)
2444 			return (NULL);
2445 	}
2446 	return ((*clauseset)[*idx].name);
2447 }
2448 
2449 const char *
2450 cfg_map_nextclause(const cfg_type_t *map, const void **clauses,
2451 		   unsigned int *idx)
2452 {
2453 	cfg_clausedef_t * const * clauseset;
2454 
2455 	REQUIRE(map != NULL && map->rep == &cfg_rep_map);
2456 	REQUIRE(idx != NULL);
2457 	REQUIRE(clauses != NULL && *clauses != NULL);
2458 
2459 	clauseset = map->of;
2460 	while (*clauseset != NULL && *clauseset != *clauses) {
2461 		clauseset++;
2462 	}
2463 	INSIST(*clauseset == *clauses);
2464 	(*idx)++;
2465 	while ((*clauseset)[*idx].name == NULL) {
2466 		*idx = 0;
2467 		*clauses = (*++clauseset);
2468 		if (*clauses == NULL)
2469 			return (NULL);
2470 	}
2471 	return ((*clauseset)[*idx].name);
2472 }
2473 
2474 /* Parse an arbitrary token, storing its raw text representation. */
2475 static isc_result_t
2476 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2477 	cfg_obj_t *obj = NULL;
2478 	isc_result_t result;
2479 	isc_region_t r;
2480 
2481 	UNUSED(type);
2482 
2483 	CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
2484 	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
2485 	if (pctx->token.type == isc_tokentype_eof) {
2486 		cfg_ungettoken(pctx);
2487 		result = ISC_R_EOF;
2488 		goto cleanup;
2489 	}
2490 
2491 	isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
2492 
2493 	obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
2494 	if (obj->value.string.base == NULL) {
2495 		result = ISC_R_NOMEMORY;
2496 		goto cleanup;
2497 	}
2498 	obj->value.string.length = r.length;
2499 	memmove(obj->value.string.base, r.base, r.length);
2500 	obj->value.string.base[r.length] = '\0';
2501 	*ret = obj;
2502 	return (result);
2503 
2504  cleanup:
2505 	if (obj != NULL)
2506 		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
2507 	return (result);
2508 }
2509 
2510 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_token = {
2511 	"token", parse_token, cfg_print_ustring, cfg_doc_terminal,
2512 	&cfg_rep_string, NULL
2513 };
2514 
2515 /*
2516  * An unsupported option.  This is just a list of tokens with balanced braces
2517  * ending in a semicolon.
2518  */
2519 
2520 static isc_result_t
2521 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2522 	cfg_obj_t *listobj = NULL;
2523 	isc_result_t result;
2524 	int braces = 0;
2525 
2526 	CHECK(cfg_create_list(pctx, type, &listobj));
2527 
2528 	for (;;) {
2529 		cfg_listelt_t *elt = NULL;
2530 
2531 		CHECK(cfg_peektoken(pctx, 0));
2532 		if (pctx->token.type == isc_tokentype_special) {
2533 			if (pctx->token.value.as_char == '{')
2534 				braces++;
2535 			else if (pctx->token.value.as_char == '}')
2536 				braces--;
2537 			else if (pctx->token.value.as_char == ';')
2538 				if (braces == 0)
2539 					break;
2540 		}
2541 		if (pctx->token.type == isc_tokentype_eof || braces < 0) {
2542 			cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
2543 			result = ISC_R_UNEXPECTEDTOKEN;
2544 			goto cleanup;
2545 		}
2546 
2547 		CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
2548 		ISC_LIST_APPEND(listobj->value.list, elt, link);
2549 	}
2550 	INSIST(braces == 0);
2551 	*ret = listobj;
2552 	return (ISC_R_SUCCESS);
2553 
2554  cleanup:
2555 	CLEANUP_OBJ(listobj);
2556 	return (result);
2557 }
2558 
2559 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_unsupported = {
2560 	"unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal,
2561 	&cfg_rep_list, NULL
2562 };
2563 
2564 /*
2565  * Try interpreting the current token as a network address.
2566  *
2567  * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
2568  * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set.  The
2569  * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
2570  * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
2571  * and the IPv6 wildcard address otherwise.
2572  */
2573 static isc_result_t
2574 token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
2575 	char *s;
2576 	struct in_addr in4a;
2577 	struct in6_addr in6a;
2578 
2579 	if (pctx->token.type != isc_tokentype_string)
2580 		return (ISC_R_UNEXPECTEDTOKEN);
2581 
2582 	s = TOKEN_STRING(pctx);
2583 	if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
2584 		if ((flags & CFG_ADDR_V4OK) != 0) {
2585 			isc_netaddr_any(na);
2586 			return (ISC_R_SUCCESS);
2587 		} else if ((flags & CFG_ADDR_V6OK) != 0) {
2588 			isc_netaddr_any6(na);
2589 			return (ISC_R_SUCCESS);
2590 		} else {
2591 			INSIST(0);
2592 			ISC_UNREACHABLE();
2593 		}
2594 	} else {
2595 		if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
2596 			if (inet_pton(AF_INET, s, &in4a) == 1) {
2597 				isc_netaddr_fromin(na, &in4a);
2598 				return (ISC_R_SUCCESS);
2599 			}
2600 		}
2601 		if ((flags & CFG_ADDR_V4PREFIXOK) != 0 && strlen(s) <= 15U) {
2602 			char buf[64];
2603 			int i;
2604 
2605 			strlcpy(buf, s, sizeof(buf));
2606 			for (i = 0; i < 3; i++) {
2607 				strlcat(buf, ".0", sizeof(buf));
2608 				if (inet_pton(AF_INET, buf, &in4a) == 1) {
2609 					isc_netaddr_fromin(na, &in4a);
2610 					return (ISC_R_SUCCESS);
2611 				}
2612 			}
2613 		}
2614 		if ((flags & CFG_ADDR_V6OK) != 0 && strlen(s) <= 127U) {
2615 			char buf[128]; /* see lib/bind9/getaddresses.c */
2616 			char *d; /* zone delimiter */
2617 			uint32_t zone = 0; /* scope zone ID */
2618 
2619 			strlcpy(buf, s, sizeof(buf));
2620 			d = strchr(buf, '%');
2621 			if (d != NULL)
2622 				*d = '\0';
2623 
2624 			if (inet_pton(AF_INET6, buf, &in6a) == 1) {
2625 				if (d != NULL) {
2626 					isc_result_t result;
2627 
2628 					result = isc_netscope_pton(AF_INET6,
2629 								   d + 1,
2630 								   &in6a,
2631 								   &zone);
2632 					if (result != ISC_R_SUCCESS)
2633 						return (result);
2634 				}
2635 
2636 				isc_netaddr_fromin6(na, &in6a);
2637 				isc_netaddr_setzone(na, zone);
2638 				return (ISC_R_SUCCESS);
2639 			}
2640 		}
2641 	}
2642 	return (ISC_R_UNEXPECTEDTOKEN);
2643 }
2644 
2645 isc_result_t
2646 cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
2647 	isc_result_t result;
2648 	const char *wild = "";
2649 	const char *prefix = "";
2650 
2651 	REQUIRE(pctx != NULL);
2652 	REQUIRE(na != NULL);
2653 
2654 	CHECK(cfg_gettoken(pctx, 0));
2655 	result = token_addr(pctx, flags, na);
2656 	if (result == ISC_R_UNEXPECTEDTOKEN) {
2657 		if ((flags & CFG_ADDR_WILDOK) != 0)
2658 			wild = " or '*'";
2659 		if ((flags & CFG_ADDR_V4PREFIXOK) != 0)
2660 			wild = " or IPv4 prefix";
2661 		if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V4OK)
2662 			cfg_parser_error(pctx, CFG_LOG_NEAR,
2663 					 "expected IPv4 address%s%s",
2664 					 prefix, wild);
2665 		else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK)
2666 			cfg_parser_error(pctx, CFG_LOG_NEAR,
2667 					 "expected IPv6 address%s%s",
2668 					 prefix, wild);
2669 		else
2670 			cfg_parser_error(pctx, CFG_LOG_NEAR,
2671 					 "expected IP address%s%s",
2672 					 prefix, wild);
2673 	}
2674  cleanup:
2675 	return (result);
2676 }
2677 
2678 bool
2679 cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
2680 	isc_result_t result;
2681 	isc_netaddr_t na_dummy;
2682 
2683 	REQUIRE(pctx != NULL);
2684 
2685 	result = token_addr(pctx, flags, &na_dummy);
2686 	return (result == ISC_R_SUCCESS);
2687 }
2688 
2689 isc_result_t
2690 cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
2691 	isc_result_t result;
2692 
2693 	REQUIRE(pctx != NULL);
2694 	REQUIRE(port != NULL);
2695 
2696 	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
2697 
2698 	if ((flags & CFG_ADDR_WILDOK) != 0 &&
2699 	    pctx->token.type == isc_tokentype_string &&
2700 	    strcmp(TOKEN_STRING(pctx), "*") == 0) {
2701 		*port = 0;
2702 		return (ISC_R_SUCCESS);
2703 	}
2704 	if (pctx->token.type != isc_tokentype_number) {
2705 		cfg_parser_error(pctx, CFG_LOG_NEAR,
2706 			     "expected port number or '*'");
2707 		return (ISC_R_UNEXPECTEDTOKEN);
2708 	}
2709 	if (pctx->token.value.as_ulong >= 65536U) {
2710 		cfg_parser_error(pctx, CFG_LOG_NEAR,
2711 			     "port number out of range");
2712 		return (ISC_R_UNEXPECTEDTOKEN);
2713 	}
2714 	*port = (in_port_t)(pctx->token.value.as_ulong);
2715 	return (ISC_R_SUCCESS);
2716  cleanup:
2717 	return (result);
2718 }
2719 
2720 void
2721 cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
2722 	isc_result_t result;
2723 	char text[128];
2724 	isc_buffer_t buf;
2725 
2726 	REQUIRE(pctx != NULL);
2727 	REQUIRE(na != NULL);
2728 
2729 	isc_buffer_init(&buf, text, sizeof(text));
2730 	result = isc_netaddr_totext(na, &buf);
2731 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
2732 	cfg_print_chars(pctx, isc_buffer_base(&buf),
2733 			isc_buffer_usedlength(&buf));
2734 }
2735 
2736 isc_result_t
2737 cfg_parse_dscp(cfg_parser_t *pctx, isc_dscp_t *dscp) {
2738 	isc_result_t result;
2739 
2740 	REQUIRE(pctx != NULL);
2741 	REQUIRE(dscp != NULL);
2742 
2743 	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
2744 
2745 	if (pctx->token.type != isc_tokentype_number) {
2746 		cfg_parser_error(pctx, CFG_LOG_NEAR,
2747 			     "expected number");
2748 		return (ISC_R_UNEXPECTEDTOKEN);
2749 	}
2750 	if (pctx->token.value.as_ulong > 63U) {
2751 		cfg_parser_error(pctx, CFG_LOG_NEAR,
2752 			     "dscp out of range");
2753 		return (ISC_R_RANGE);
2754 	}
2755 	*dscp = (isc_dscp_t)(pctx->token.value.as_ulong);
2756 	return (ISC_R_SUCCESS);
2757  cleanup:
2758 	return (result);
2759 }
2760 
2761 /* netaddr */
2762 
2763 static unsigned int netaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
2764 static unsigned int netaddr4_flags = CFG_ADDR_V4OK;
2765 static unsigned int netaddr4wild_flags = CFG_ADDR_V4OK | CFG_ADDR_WILDOK;
2766 static unsigned int netaddr6_flags = CFG_ADDR_V6OK;
2767 static unsigned int netaddr6wild_flags = CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
2768 
2769 static isc_result_t
2770 parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2771 	isc_result_t result;
2772 	cfg_obj_t *obj = NULL;
2773 	isc_netaddr_t netaddr;
2774 	unsigned int flags = *(const unsigned int *)type->of;
2775 
2776 	CHECK(cfg_create_obj(pctx, type, &obj));
2777 	CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
2778 	isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
2779 	*ret = obj;
2780 	return (ISC_R_SUCCESS);
2781  cleanup:
2782 	CLEANUP_OBJ(obj);
2783 	return (result);
2784 }
2785 
2786 static void
2787 cfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
2788 	const unsigned int *flagp = type->of;
2789 	int n = 0;
2790 	if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK) {
2791 		cfg_print_cstr(pctx, "( ");
2792 	}
2793 	if ((*flagp & CFG_ADDR_V4OK) != 0) {
2794 		cfg_print_cstr(pctx, "<ipv4_address>");
2795 		n++;
2796 	}
2797 	if ((*flagp & CFG_ADDR_V6OK) != 0) {
2798 		if (n != 0)
2799 			cfg_print_cstr(pctx, " | ");
2800 		cfg_print_cstr(pctx, "<ipv6_address>");
2801 		n++;
2802 	}
2803 	if ((*flagp & CFG_ADDR_WILDOK) != 0) {
2804 		if (n != 0)
2805 			cfg_print_cstr(pctx, " | ");
2806 		cfg_print_cstr(pctx, "*");
2807 		n++;
2808 		POST(n);
2809 	}
2810 	if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK) {
2811 		cfg_print_cstr(pctx, " )");
2812 	}
2813 }
2814 
2815 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_netaddr = {
2816 	"netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
2817 	&cfg_rep_sockaddr, &netaddr_flags
2818 };
2819 
2820 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_netaddr4 = {
2821 	"netaddr4", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
2822 	&cfg_rep_sockaddr, &netaddr4_flags
2823 };
2824 
2825 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_netaddr4wild = {
2826 	"netaddr4wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
2827 	&cfg_rep_sockaddr, &netaddr4wild_flags
2828 };
2829 
2830 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_netaddr6 = {
2831 	"netaddr6", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
2832 	&cfg_rep_sockaddr, &netaddr6_flags
2833 };
2834 
2835 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_netaddr6wild = {
2836 	"netaddr6wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
2837 	&cfg_rep_sockaddr, &netaddr6wild_flags
2838 };
2839 
2840 /* netprefix */
2841 
2842 isc_result_t
2843 cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
2844 		    cfg_obj_t **ret)
2845 {
2846 	cfg_obj_t *obj = NULL;
2847 	isc_result_t result;
2848 	isc_netaddr_t netaddr;
2849 	unsigned int addrlen = 0, prefixlen;
2850 
2851 	REQUIRE(pctx != NULL);
2852 	REQUIRE(ret != NULL && *ret == NULL);
2853 
2854 	UNUSED(type);
2855 
2856 	CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
2857 				CFG_ADDR_V6OK, &netaddr));
2858 	switch (netaddr.family) {
2859 	case AF_INET:
2860 		addrlen = 32;
2861 		break;
2862 	case AF_INET6:
2863 		addrlen = 128;
2864 		break;
2865 	default:
2866 		INSIST(0);
2867 		ISC_UNREACHABLE();
2868 	}
2869 	CHECK(cfg_peektoken(pctx, 0));
2870 	if (pctx->token.type == isc_tokentype_special &&
2871 	    pctx->token.value.as_char == '/') {
2872 		CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
2873 		CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
2874 		if (pctx->token.type != isc_tokentype_number) {
2875 			cfg_parser_error(pctx, CFG_LOG_NEAR,
2876 				     "expected prefix length");
2877 			return (ISC_R_UNEXPECTEDTOKEN);
2878 		}
2879 		prefixlen = pctx->token.value.as_ulong;
2880 		if (prefixlen > addrlen) {
2881 			cfg_parser_error(pctx, CFG_LOG_NOPREP,
2882 				     "invalid prefix length");
2883 			return (ISC_R_RANGE);
2884 		}
2885 	} else {
2886 		prefixlen = addrlen;
2887 	}
2888 	CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
2889 	obj->value.netprefix.address = netaddr;
2890 	obj->value.netprefix.prefixlen = prefixlen;
2891 	*ret = obj;
2892 	return (ISC_R_SUCCESS);
2893  cleanup:
2894 	cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
2895 	return (result);
2896 }
2897 
2898 static void
2899 print_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2900 	const cfg_netprefix_t *p = &obj->value.netprefix;
2901 
2902 	cfg_print_rawaddr(pctx, &p->address);
2903 	cfg_print_cstr(pctx, "/");
2904 	cfg_print_rawuint(pctx, p->prefixlen);
2905 }
2906 
2907 bool
2908 cfg_obj_isnetprefix(const cfg_obj_t *obj) {
2909 	REQUIRE(obj != NULL);
2910 	return (obj->type->rep == &cfg_rep_netprefix);
2911 }
2912 
2913 void
2914 cfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr,
2915 		    unsigned int *prefixlen)
2916 {
2917 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
2918 	REQUIRE(netaddr != NULL);
2919 	REQUIRE(prefixlen != NULL);
2920 
2921 	*netaddr = obj->value.netprefix.address;
2922 	*prefixlen = obj->value.netprefix.prefixlen;
2923 }
2924 
2925 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_netprefix = {
2926 	"netprefix", cfg_parse_netprefix, print_netprefix, cfg_doc_terminal,
2927 	&cfg_rep_netprefix, NULL
2928 };
2929 
2930 static isc_result_t
2931 parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type,
2932 		  int flags, cfg_obj_t **ret)
2933 {
2934 	isc_result_t result;
2935 	isc_netaddr_t netaddr;
2936 	in_port_t port = 0;
2937 	isc_dscp_t dscp = -1;
2938 	cfg_obj_t *obj = NULL;
2939 	int have_port = 0, have_dscp = 0;
2940 
2941 	CHECK(cfg_create_obj(pctx, type, &obj));
2942 	CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
2943 	for (;;) {
2944 		CHECK(cfg_peektoken(pctx, 0));
2945 		if (pctx->token.type == isc_tokentype_string) {
2946 			if (strcasecmp(TOKEN_STRING(pctx), "port") == 0) {
2947 				CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
2948 				CHECK(cfg_parse_rawport(pctx, flags, &port));
2949 				++have_port;
2950 			} else if ((flags & CFG_ADDR_DSCPOK) != 0 &&
2951 				   strcasecmp(TOKEN_STRING(pctx), "dscp") == 0)
2952 			{
2953 				CHECK(cfg_gettoken(pctx, 0)); /* read "dscp" */
2954 				CHECK(cfg_parse_dscp(pctx, &dscp));
2955 				++have_dscp;
2956 			} else
2957 				break;
2958 		} else
2959 			break;
2960 	}
2961 	if (have_port > 1) {
2962 		cfg_parser_error(pctx, 0, "expected at most one port");
2963 		result = ISC_R_UNEXPECTEDTOKEN;
2964 		goto cleanup;
2965 	}
2966 
2967 	if (have_dscp > 1) {
2968 		cfg_parser_error(pctx, 0, "expected at most one dscp");
2969 		result = ISC_R_UNEXPECTEDTOKEN;
2970 		goto cleanup;
2971 	}
2972 	isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
2973 	obj->value.sockaddrdscp.dscp = dscp;
2974 	*ret = obj;
2975 	return (ISC_R_SUCCESS);
2976 
2977  cleanup:
2978 	CLEANUP_OBJ(obj);
2979 	return (result);
2980 }
2981 
2982 static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
2983 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_sockaddr = {
2984 	"sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr,
2985 	&cfg_rep_sockaddr, &sockaddr_flags
2986 };
2987 
2988 static unsigned int sockaddrdscp_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK |
2989 					 CFG_ADDR_DSCPOK;
2990 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_sockaddrdscp = {
2991 	"sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr,
2992 	&cfg_rep_sockaddr, &sockaddrdscp_flags
2993 };
2994 
2995 isc_result_t
2996 cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2997 	const unsigned int *flagp;
2998 
2999 	REQUIRE(pctx != NULL);
3000 	REQUIRE(type != NULL);
3001 	REQUIRE(ret != NULL && *ret == NULL);
3002 
3003 	flagp = type->of;
3004 
3005 	return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret));
3006 }
3007 
3008 void
3009 cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) {
3010 	isc_netaddr_t netaddr;
3011 	in_port_t port;
3012 	char buf[ISC_NETADDR_FORMATSIZE];
3013 
3014 	REQUIRE(pctx != NULL);
3015 	REQUIRE(obj != NULL);
3016 
3017 	isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
3018 	isc_netaddr_format(&netaddr, buf, sizeof(buf));
3019 	cfg_print_cstr(pctx, buf);
3020 	port = isc_sockaddr_getport(&obj->value.sockaddr);
3021 	if (port != 0) {
3022 		cfg_print_cstr(pctx, " port ");
3023 		cfg_print_rawuint(pctx, port);
3024 	}
3025 	if (obj->value.sockaddrdscp.dscp != -1) {
3026 		cfg_print_cstr(pctx, " dscp ");
3027 		cfg_print_rawuint(pctx, obj->value.sockaddrdscp.dscp);
3028 	}
3029 }
3030 
3031 void
3032 cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
3033 	const unsigned int *flagp = type->of;
3034 	int n = 0;
3035 
3036 	REQUIRE(pctx != NULL);
3037 	REQUIRE(type != NULL);
3038 
3039 	cfg_print_cstr(pctx, "( ");
3040 	if ((*flagp & CFG_ADDR_V4OK) != 0) {
3041 		cfg_print_cstr(pctx, "<ipv4_address>");
3042 		n++;
3043 	}
3044 	if ((*flagp & CFG_ADDR_V6OK) != 0) {
3045 		if (n != 0)
3046 			cfg_print_cstr(pctx, " | ");
3047 		cfg_print_cstr(pctx, "<ipv6_address>");
3048 		n++;
3049 	}
3050 	if ((*flagp & CFG_ADDR_WILDOK) != 0) {
3051 		if (n != 0)
3052 			cfg_print_cstr(pctx, " | ");
3053 		cfg_print_cstr(pctx, "*");
3054 		n++;
3055 		POST(n);
3056 	}
3057 	cfg_print_cstr(pctx, " ) ");
3058 	if ((*flagp & CFG_ADDR_WILDOK) != 0) {
3059 		cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
3060 	} else {
3061 		cfg_print_cstr(pctx, "[ port <integer> ]");
3062 	}
3063 	if ((*flagp & CFG_ADDR_DSCPOK) != 0) {
3064 		cfg_print_cstr(pctx, " [ dscp <integer> ]");
3065 	}
3066 }
3067 
3068 bool
3069 cfg_obj_issockaddr(const cfg_obj_t *obj) {
3070 	REQUIRE(obj != NULL);
3071 	return (obj->type->rep == &cfg_rep_sockaddr);
3072 }
3073 
3074 const isc_sockaddr_t *
3075 cfg_obj_assockaddr(const cfg_obj_t *obj) {
3076 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
3077 	return (&obj->value.sockaddr);
3078 }
3079 
3080 isc_dscp_t
3081 cfg_obj_getdscp(const cfg_obj_t *obj) {
3082 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
3083 	return (obj->value.sockaddrdscp.dscp);
3084 }
3085 
3086 isc_result_t
3087 cfg_gettoken(cfg_parser_t *pctx, int options) {
3088 	isc_result_t result;
3089 
3090 	REQUIRE(pctx != NULL);
3091 
3092 	if (pctx->seen_eof)
3093 		return (ISC_R_SUCCESS);
3094 
3095 	options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
3096 
3097  redo:
3098 	pctx->token.type = isc_tokentype_unknown;
3099 	result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
3100 	pctx->ungotten = false;
3101 	pctx->line = isc_lex_getsourceline(pctx->lexer);
3102 
3103 	switch (result) {
3104 	case ISC_R_SUCCESS:
3105 		if (pctx->token.type == isc_tokentype_eof) {
3106 			result = isc_lex_close(pctx->lexer);
3107 			INSIST(result == ISC_R_NOMORE ||
3108 			       result == ISC_R_SUCCESS);
3109 
3110 			if (isc_lex_getsourcename(pctx->lexer) != NULL) {
3111 				/*
3112 				 * Closed an included file, not the main file.
3113 				 */
3114 				cfg_listelt_t *elt;
3115 				elt = ISC_LIST_TAIL(pctx->open_files->
3116 						    value.list);
3117 				INSIST(elt != NULL);
3118 				ISC_LIST_UNLINK(pctx->open_files->
3119 						value.list, elt, link);
3120 				ISC_LIST_APPEND(pctx->closed_files->
3121 						value.list, elt, link);
3122 				goto redo;
3123 			}
3124 			pctx->seen_eof = true;
3125 		}
3126 		break;
3127 
3128 	case ISC_R_NOSPACE:
3129 		/* More understandable than "ran out of space". */
3130 		cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
3131 		break;
3132 
3133 	case ISC_R_IOERROR:
3134 		cfg_parser_error(pctx, 0, "%s",
3135 				 isc_result_totext(result));
3136 		break;
3137 
3138 	default:
3139 		cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
3140 				 isc_result_totext(result));
3141 		break;
3142 	}
3143 	return (result);
3144 }
3145 
3146 void
3147 cfg_ungettoken(cfg_parser_t *pctx) {
3148 	REQUIRE(pctx != NULL);
3149 
3150 	if (pctx->seen_eof)
3151 		return;
3152 	isc_lex_ungettoken(pctx->lexer, &pctx->token);
3153 	pctx->ungotten = true;
3154 }
3155 
3156 isc_result_t
3157 cfg_peektoken(cfg_parser_t *pctx, int options) {
3158 	isc_result_t result;
3159 
3160 	REQUIRE(pctx != NULL);
3161 
3162 	CHECK(cfg_gettoken(pctx, options));
3163 	cfg_ungettoken(pctx);
3164  cleanup:
3165 	return (result);
3166 }
3167 
3168 /*
3169  * Get a string token, accepting both the quoted and the unquoted form.
3170  * Log an error if the next token is not a string.
3171  */
3172 static isc_result_t
3173 cfg_getstringtoken(cfg_parser_t *pctx) {
3174 	isc_result_t result;
3175 
3176 	result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
3177 	if (result != ISC_R_SUCCESS)
3178 		return (result);
3179 
3180 	if (pctx->token.type != isc_tokentype_string &&
3181 	    pctx->token.type != isc_tokentype_qstring) {
3182 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
3183 		return (ISC_R_UNEXPECTEDTOKEN);
3184 	}
3185 	return (ISC_R_SUCCESS);
3186 }
3187 
3188 void
3189 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
3190 	va_list args;
3191 
3192 	REQUIRE(pctx != NULL);
3193 	REQUIRE(fmt != NULL);
3194 
3195 	va_start(args, fmt);
3196 	parser_complain(pctx, false, flags, fmt, args);
3197 	va_end(args);
3198 	pctx->errors++;
3199 }
3200 
3201 void
3202 cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
3203 	va_list args;
3204 
3205 	REQUIRE(pctx != NULL);
3206 	REQUIRE(fmt != NULL);
3207 
3208 	va_start(args, fmt);
3209 	parser_complain(pctx, true, flags, fmt, args);
3210 	va_end(args);
3211 	pctx->warnings++;
3212 }
3213 
3214 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
3215 
3216 static bool
3217 have_current_file(cfg_parser_t *pctx) {
3218 	cfg_listelt_t *elt;
3219 	if (pctx->open_files == NULL)
3220 		return (false);
3221 
3222 	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
3223 	if (elt == NULL)
3224 	      return (false);
3225 
3226 	return (true);
3227 }
3228 
3229 static char *
3230 current_file(cfg_parser_t *pctx) {
3231 	static char none[] = "none";
3232 	cfg_listelt_t *elt;
3233 	cfg_obj_t *fileobj;
3234 
3235 	if (!have_current_file(pctx))
3236 		return (none);
3237 
3238 	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
3239 	if (elt == NULL)	/* shouldn't be possible, but... */
3240 	      return (none);
3241 
3242 	fileobj = elt->obj;
3243 	INSIST(fileobj->type == &cfg_type_qstring);
3244 	return (fileobj->value.string.base);
3245 }
3246 
3247 static void
3248 parser_complain(cfg_parser_t *pctx, bool is_warning,
3249 		unsigned int flags, const char *format,
3250 		va_list args)
3251 {
3252 	char tokenbuf[MAX_LOG_TOKEN + 10];
3253 	static char where[PATH_MAX + 100];
3254 	static char message[2048];
3255 	int level = ISC_LOG_ERROR;
3256 	const char *prep = "";
3257 	size_t len;
3258 
3259 	if (is_warning)
3260 		level = ISC_LOG_WARNING;
3261 
3262 	where[0] = '\0';
3263 	if (have_current_file(pctx))
3264 		snprintf(where, sizeof(where), "%s:%u: ",
3265 			 current_file(pctx), pctx->line);
3266 	else if (pctx->buf_name != NULL)
3267 		snprintf(where, sizeof(where), "%s: ", pctx->buf_name);
3268 
3269 	len = vsnprintf(message, sizeof(message), format, args);
3270 #define ELIPSIS " ... "
3271 	if (len >= sizeof(message)) {
3272 		message[sizeof(message) - sizeof(ELIPSIS)] = 0;
3273 		strlcat(message, ELIPSIS, sizeof(message));
3274 	}
3275 
3276 	if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) {
3277 		isc_region_t r;
3278 
3279 		if (pctx->ungotten)
3280 			(void)cfg_gettoken(pctx, 0);
3281 
3282 		if (pctx->token.type == isc_tokentype_eof) {
3283 			snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
3284 		} else if (pctx->token.type == isc_tokentype_unknown) {
3285 			flags = 0;
3286 			tokenbuf[0] = '\0';
3287 		} else {
3288 			isc_lex_getlasttokentext(pctx->lexer,
3289 						 &pctx->token, &r);
3290 			if (r.length > MAX_LOG_TOKEN)
3291 				snprintf(tokenbuf, sizeof(tokenbuf),
3292 					 "'%.*s...'", MAX_LOG_TOKEN, r.base);
3293 			else
3294 				snprintf(tokenbuf, sizeof(tokenbuf),
3295 					 "'%.*s'", (int)r.length, r.base);
3296 		}
3297 
3298 		/* Choose a preposition. */
3299 		if ((flags & CFG_LOG_NEAR) != 0) {
3300 			prep = " near ";
3301 		} else if ((flags & CFG_LOG_BEFORE) != 0) {
3302 			prep = " before ";
3303 		} else {
3304 			prep = " ";
3305 		}
3306 	} else {
3307 		tokenbuf[0] = '\0';
3308 	}
3309 	isc_log_write(pctx->lctx, CAT, MOD, level,
3310 		      "%s%s%s%s", where, message, prep, tokenbuf);
3311 }
3312 
3313 void
3314 cfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level,
3315 	    const char *fmt, ...) {
3316 	va_list ap;
3317 	char msgbuf[2048];
3318 
3319 	REQUIRE(obj != NULL);
3320 	REQUIRE(fmt != NULL);
3321 
3322 	if (! isc_log_wouldlog(lctx, level))
3323 		return;
3324 
3325 	va_start(ap, fmt);
3326 	vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
3327 	va_end(ap);
3328 
3329 	if (obj->file != NULL) {
3330 		isc_log_write(lctx, CAT, MOD, level,
3331 			      "%s:%u: %s", obj->file, obj->line, msgbuf);
3332 	} else {
3333 		isc_log_write(lctx, CAT, MOD, level, "%s", msgbuf);
3334 	}
3335 }
3336 
3337 const char *
3338 cfg_obj_file(const cfg_obj_t *obj) {
3339 	REQUIRE(obj != NULL);
3340 
3341 	return (obj->file);
3342 }
3343 
3344 unsigned int
3345 cfg_obj_line(const cfg_obj_t *obj) {
3346 	REQUIRE(obj != NULL);
3347 
3348 	return (obj->line);
3349 }
3350 
3351 isc_result_t
3352 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3353 	cfg_obj_t *obj;
3354 
3355 	REQUIRE(pctx != NULL);
3356 	REQUIRE(type != NULL);
3357 	REQUIRE(ret != NULL && *ret == NULL);
3358 
3359 	obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
3360 	if (obj == NULL)
3361 		return (ISC_R_NOMEMORY);
3362 
3363 	obj->type = type;
3364 	obj->file = current_file(pctx);
3365 	obj->line = pctx->line;
3366 	obj->pctx = pctx;
3367 
3368 	isc_refcount_init(&obj->references, 1);
3369 
3370 	*ret = obj;
3371 
3372 	return (ISC_R_SUCCESS);
3373 }
3374 
3375 
3376 static void
3377 map_symtabitem_destroy(char *key, unsigned int type,
3378 		       isc_symvalue_t symval, void *userarg)
3379 {
3380 	cfg_obj_t *obj = symval.as_pointer;
3381 	cfg_parser_t *pctx = (cfg_parser_t *)userarg;
3382 
3383 	UNUSED(key);
3384 	UNUSED(type);
3385 
3386 	cfg_obj_destroy(pctx, &obj);
3387 }
3388 
3389 static isc_result_t
3390 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3391 	isc_result_t result;
3392 	isc_symtab_t *symtab = NULL;
3393 	cfg_obj_t *obj = NULL;
3394 
3395 	CHECK(cfg_create_obj(pctx, type, &obj));
3396 	CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */
3397 				map_symtabitem_destroy,
3398 				pctx, false, &symtab));
3399 	obj->value.map.symtab = symtab;
3400 	obj->value.map.id = NULL;
3401 
3402 	*ret = obj;
3403 	return (ISC_R_SUCCESS);
3404 
3405  cleanup:
3406 	if (obj != NULL)
3407 		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
3408 	return (result);
3409 }
3410 
3411 static void
3412 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
3413 	CLEANUP_OBJ(obj->value.map.id);
3414 	isc_symtab_destroy(&obj->value.map.symtab);
3415 }
3416 
3417 bool
3418 cfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type) {
3419 
3420 	REQUIRE(obj != NULL);
3421 	REQUIRE(type != NULL);
3422 
3423 	return (obj->type == type);
3424 }
3425 
3426 /*
3427  * Destroy 'obj', a configuration object created in 'pctx'.
3428  */
3429 void
3430 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
3431 	REQUIRE(objp != NULL && *objp != NULL);
3432 	REQUIRE(pctx != NULL);
3433 
3434 	cfg_obj_t *obj = *objp;
3435 	*objp = NULL;
3436 
3437 	if (isc_refcount_decrement(&obj->references) == 1) {
3438 		obj->type->rep->free(pctx, obj);
3439 		isc_refcount_destroy(&obj->references);
3440 		isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
3441 	}
3442 }
3443 
3444 void
3445 cfg_obj_attach(cfg_obj_t *src, cfg_obj_t **dest) {
3446     REQUIRE(src != NULL);
3447     REQUIRE(dest != NULL && *dest == NULL);
3448 
3449     isc_refcount_increment(&src->references);
3450     *dest = src;
3451 }
3452 
3453 static void
3454 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
3455 	UNUSED(pctx);
3456 	UNUSED(obj);
3457 }
3458 
3459 void
3460 cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
3461 	REQUIRE(pctx != NULL);
3462 	REQUIRE(type != NULL);
3463 
3464 	type->doc(pctx, type);
3465 }
3466 
3467 void
3468 cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) {
3469 	REQUIRE(pctx != NULL);
3470 	REQUIRE(type != NULL);
3471 
3472 	cfg_print_cstr(pctx, "<");
3473 	cfg_print_cstr(pctx, type->name);
3474 	cfg_print_cstr(pctx, ">");
3475 }
3476 
3477 void
3478 cfg_print_grammar(const cfg_type_t *type,
3479 	void (*f)(void *closure, const char *text, int textlen),
3480 	void *closure)
3481 {
3482 	cfg_printer_t pctx;
3483 
3484 	pctx.f = f;
3485 	pctx.closure = closure;
3486 	pctx.indent = 0;
3487 	pctx.flags = 0;
3488 	cfg_doc_obj(&pctx, type);
3489 }
3490 
3491 isc_result_t
3492 cfg_parser_mapadd(cfg_parser_t *pctx, cfg_obj_t *mapobj,
3493 		  cfg_obj_t *obj, const char *clausename)
3494 {
3495 	isc_result_t result = ISC_R_SUCCESS;
3496 	const cfg_map_t *map;
3497 	isc_symvalue_t symval;
3498 	cfg_obj_t *destobj = NULL;
3499 	cfg_listelt_t *elt = NULL;
3500 	const cfg_clausedef_t * const *clauseset;
3501 	const cfg_clausedef_t *clause;
3502 
3503 	REQUIRE(pctx != NULL);
3504 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
3505 	REQUIRE(obj != NULL);
3506 	REQUIRE(clausename != NULL);
3507 
3508 	map = &mapobj->value.map;
3509 
3510 	clause = NULL;
3511 	for (clauseset = map->clausesets; *clauseset != NULL; clauseset++) {
3512 		for (clause = *clauseset; clause->name != NULL; clause++) {
3513 			if (strcasecmp(clause->name, clausename) == 0) {
3514 				goto breakout;
3515 			}
3516 		}
3517 	}
3518 
3519  breakout:
3520 	if (clause == NULL || clause->name == NULL)
3521 		return (ISC_R_FAILURE);
3522 
3523 	result = isc_symtab_lookup(map->symtab, clausename, 0, &symval);
3524 	if (result == ISC_R_NOTFOUND) {
3525 		if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
3526 			CHECK(cfg_create_list(pctx, &cfg_type_implicitlist,
3527 					      &destobj));
3528 			CHECK(create_listelt(pctx, &elt));
3529 			cfg_obj_attach(obj, &elt->obj);
3530 			ISC_LIST_APPEND(destobj->value.list, elt, link);
3531 			symval.as_pointer = destobj;
3532 		} else
3533 			symval.as_pointer = obj;
3534 
3535 		CHECK(isc_symtab_define(map->symtab, clausename, 1, symval,
3536 					isc_symexists_reject));
3537 	} else {
3538 		cfg_obj_t *destobj2 = symval.as_pointer;
3539 
3540 		INSIST(result == ISC_R_SUCCESS);
3541 
3542 		if (destobj2->type == &cfg_type_implicitlist) {
3543 			CHECK(create_listelt(pctx, &elt));
3544 			cfg_obj_attach(obj, &elt->obj);
3545 			ISC_LIST_APPEND(destobj2->value.list, elt, link);
3546 		} else
3547 			result = ISC_R_EXISTS;
3548 	}
3549 
3550 	destobj = NULL;
3551 	elt = NULL;
3552 
3553  cleanup:
3554 	if (elt != NULL)
3555 		free_listelt(pctx, elt);
3556 	CLEANUP_OBJ(destobj);
3557 
3558 	return (result);
3559 }
3560 
3561 isc_result_t
3562 cfg_pluginlist_foreach(const cfg_obj_t *config, const cfg_obj_t *list,
3563 		       isc_log_t *lctx, pluginlist_cb_t *callback,
3564 		       void *callback_data)
3565 {
3566 	isc_result_t result = ISC_R_SUCCESS;
3567 	const cfg_listelt_t *element;
3568 
3569 	REQUIRE(config != NULL);
3570 	REQUIRE(callback != NULL);
3571 
3572 	for (element = cfg_list_first(list);
3573 	     element != NULL;
3574 	     element = cfg_list_next(element))
3575 	{
3576 		const cfg_obj_t *plugin = cfg_listelt_value(element);
3577 		const cfg_obj_t *obj;
3578 		const char *type, *library;
3579 		const char *parameters = NULL;
3580 
3581 		/* Get the path to the plugin module. */
3582 		obj = cfg_tuple_get(plugin, "type");
3583 		type = cfg_obj_asstring(obj);
3584 
3585 		/* Only query plugins are supported currently. */
3586 		if (strcasecmp(type, "query") != 0) {
3587 			cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
3588 				    "unsupported plugin type");
3589 			return (ISC_R_FAILURE);
3590 		}
3591 
3592 		library = cfg_obj_asstring(cfg_tuple_get(plugin, "library"));
3593 
3594 		obj = cfg_tuple_get(plugin, "parameters");
3595 		if (obj != NULL && cfg_obj_isstring(obj)) {
3596 			parameters = cfg_obj_asstring(obj);
3597 		}
3598 
3599 		result = callback(config, obj, library, parameters,
3600 				  callback_data);
3601 		if (result != ISC_R_SUCCESS) {
3602 			break;
3603 		}
3604 	}
3605 
3606 	return (result);
3607 }
3608