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