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