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