1 /*
2  * Copyright (C) 2004-2015  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /*! \file */
19 
20 #include <config.h>
21 
22 #include <stdlib.h>
23 
24 #include <isc/buffer.h>
25 #include <isc/dir.h>
26 #include <isc/formatcheck.h>
27 #include <isc/lex.h>
28 #include <isc/log.h>
29 #include <isc/mem.h>
30 #include <isc/net.h>
31 #include <isc/netaddr.h>
32 #include <isc/netscope.h>
33 #include <isc/print.h>
34 #include <isc/string.h>
35 #include <isc/sockaddr.h>
36 #include <isc/symtab.h>
37 #include <isc/util.h>
38 
39 #include <isccfg/cfg.h>
40 #include <isccfg/grammar.h>
41 #include <isccfg/log.h>
42 
43 /* Shorthand */
44 #define CAT CFG_LOGCATEGORY_CONFIG
45 #define MOD CFG_LOGMODULE_PARSER
46 
47 #define MAP_SYM 1 	/* Unique type for isc_symtab */
48 
49 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
50 
51 /* Check a return value. */
52 #define CHECK(op) 						\
53 	do { result = (op); 					\
54 		if (result != ISC_R_SUCCESS) goto cleanup; 	\
55 	} while (0)
56 
57 /* Clean up a configuration object if non-NULL. */
58 #define CLEANUP_OBJ(obj) \
59 	do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
60 
61 
62 /*
63  * Forward declarations of static functions.
64  */
65 
66 static void
67 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
68 
69 static isc_result_t
70 parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
71 
72 static void
73 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
74 
75 static void
76 free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
77 
78 static isc_result_t
79 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
80 
81 static isc_result_t
82 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
83 	      cfg_obj_t **ret);
84 
85 static void
86 free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
87 
88 static isc_result_t
89 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
90 
91 static void
92 free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
93 
94 static isc_result_t
95 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
96 		 cfg_type_t *elttype, isc_symtab_t *symtab,
97 		 isc_boolean_t callback);
98 
99 static void
100 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
101 
102 static isc_result_t
103 cfg_getstringtoken(cfg_parser_t *pctx);
104 
105 static void
106 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
107 		unsigned int flags, const char *format, va_list args);
108 
109 /*
110  * Data representations.  These correspond to members of the
111  * "value" union in struct cfg_obj (except "void", which does
112  * not need a union member).
113  */
114 
115 cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
116 cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
117 cfg_rep_t cfg_rep_string = { "string", free_string };
118 cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
119 cfg_rep_t cfg_rep_map = { "map", free_map };
120 cfg_rep_t cfg_rep_list = { "list", free_list };
121 cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
122 cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
123 cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
124 cfg_rep_t cfg_rep_void = { "void", free_noop };
125 cfg_rep_t cfg_rep_fixedpoint = { "fixedpoint", free_noop };
126 
127 /*
128  * Configuration type definitions.
129  */
130 
131 /*%
132  * An implicit list.  These are formed by clauses that occur multiple times.
133  */
134 static cfg_type_t cfg_type_implicitlist = {
135 	"implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL };
136 
137 /* Functions. */
138 
139 void
cfg_print_obj(cfg_printer_t * pctx,const cfg_obj_t * obj)140 cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
141 	obj->type->print(pctx, obj);
142 }
143 
144 void
cfg_print_chars(cfg_printer_t * pctx,const char * text,int len)145 cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
146 	pctx->f(pctx->closure, text, len);
147 }
148 
149 static void
print_open(cfg_printer_t * pctx)150 print_open(cfg_printer_t *pctx) {
151 	cfg_print_chars(pctx, "{\n", 2);
152 	pctx->indent++;
153 }
154 
155 static void
print_indent(cfg_printer_t * pctx)156 print_indent(cfg_printer_t *pctx) {
157 	int indent = pctx->indent;
158 	while (indent > 0) {
159 		cfg_print_chars(pctx, "\t", 1);
160 		indent--;
161 	}
162 }
163 
164 static void
print_close(cfg_printer_t * pctx)165 print_close(cfg_printer_t *pctx) {
166 	pctx->indent--;
167 	print_indent(pctx);
168 	cfg_print_chars(pctx, "}", 1);
169 }
170 
171 isc_result_t
cfg_parse_obj(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)172 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
173 	isc_result_t result;
174 	INSIST(ret != NULL && *ret == NULL);
175 	result = type->parse(pctx, type, ret);
176 	if (result != ISC_R_SUCCESS)
177 		return (result);
178 	INSIST(*ret != NULL);
179 	return (ISC_R_SUCCESS);
180 }
181 
182 void
cfg_print(const cfg_obj_t * obj,void (* f)(void * closure,const char * text,int textlen),void * closure)183 cfg_print(const cfg_obj_t *obj,
184 	  void (*f)(void *closure, const char *text, int textlen),
185 	  void *closure)
186 {
187 	cfg_printx(obj, 0, f, closure);
188 }
189 
190 void
cfg_printx(const cfg_obj_t * obj,unsigned int flags,void (* f)(void * closure,const char * text,int textlen),void * closure)191 cfg_printx(const cfg_obj_t *obj, unsigned int flags,
192 	     void (*f)(void *closure, const char *text, int textlen),
193 	     void *closure)
194 {
195 	cfg_printer_t pctx;
196 	pctx.f = f;
197 	pctx.closure = closure;
198 	pctx.indent = 0;
199 	pctx.flags = flags;
200 	obj->type->print(&pctx, obj);
201 }
202 
203 /* Tuples. */
204 
205 isc_result_t
cfg_create_tuple(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)206 cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
207 	isc_result_t result;
208 	const cfg_tuplefielddef_t *fields = type->of;
209 	const cfg_tuplefielddef_t *f;
210 	cfg_obj_t *obj = NULL;
211 	unsigned int nfields = 0;
212 	int i;
213 
214 	for (f = fields; f->name != NULL; f++)
215 		nfields++;
216 
217 	CHECK(cfg_create_obj(pctx, type, &obj));
218 	obj->value.tuple = isc_mem_get(pctx->mctx,
219 				       nfields * sizeof(cfg_obj_t *));
220 	if (obj->value.tuple == NULL) {
221 		result = ISC_R_NOMEMORY;
222 		goto cleanup;
223 	}
224 	for (f = fields, i = 0; f->name != NULL; f++, i++)
225 		obj->value.tuple[i] = NULL;
226 	*ret = obj;
227 	return (ISC_R_SUCCESS);
228 
229  cleanup:
230 	if (obj != NULL)
231 		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
232 	return (result);
233 }
234 
235 isc_result_t
cfg_parse_tuple(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)236 cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
237 {
238 	isc_result_t result;
239 	const cfg_tuplefielddef_t *fields = type->of;
240 	const cfg_tuplefielddef_t *f;
241 	cfg_obj_t *obj = NULL;
242 	unsigned int i;
243 
244 	CHECK(cfg_create_tuple(pctx, type, &obj));
245 	for (f = fields, i = 0; f->name != NULL; f++, i++)
246 		CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
247 
248 	*ret = obj;
249 	return (ISC_R_SUCCESS);
250 
251  cleanup:
252 	CLEANUP_OBJ(obj);
253 	return (result);
254 }
255 
256 void
cfg_print_tuple(cfg_printer_t * pctx,const cfg_obj_t * obj)257 cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
258 	unsigned int i;
259 	const cfg_tuplefielddef_t *fields = obj->type->of;
260 	const cfg_tuplefielddef_t *f;
261 	isc_boolean_t need_space = ISC_FALSE;
262 
263 	for (f = fields, i = 0; f->name != NULL; f++, i++) {
264 		const cfg_obj_t *fieldobj = obj->value.tuple[i];
265 		if (need_space && fieldobj->type->rep != &cfg_rep_void)
266 			cfg_print_cstr(pctx, " ");
267 		cfg_print_obj(pctx, fieldobj);
268 		need_space = ISC_TF(need_space ||
269 				    fieldobj->type->print != cfg_print_void);
270 	}
271 }
272 
273 void
cfg_doc_tuple(cfg_printer_t * pctx,const cfg_type_t * type)274 cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
275 	const cfg_tuplefielddef_t *fields = type->of;
276 	const cfg_tuplefielddef_t *f;
277 	isc_boolean_t need_space = ISC_FALSE;
278 
279 	for (f = fields; f->name != NULL; f++) {
280 		if (need_space)
281 			cfg_print_cstr(pctx, " ");
282 		cfg_doc_obj(pctx, f->type);
283 		need_space = ISC_TF(f->type->print != cfg_print_void);
284 	}
285 }
286 
287 static void
free_tuple(cfg_parser_t * pctx,cfg_obj_t * obj)288 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
289 	unsigned int i;
290 	const cfg_tuplefielddef_t *fields = obj->type->of;
291 	const cfg_tuplefielddef_t *f;
292 	unsigned int nfields = 0;
293 
294 	if (obj->value.tuple == NULL)
295 		return;
296 
297 	for (f = fields, i = 0; f->name != NULL; f++, i++) {
298 		CLEANUP_OBJ(obj->value.tuple[i]);
299 		nfields++;
300 	}
301 	isc_mem_put(pctx->mctx, obj->value.tuple,
302 		    nfields * sizeof(cfg_obj_t *));
303 }
304 
305 isc_boolean_t
cfg_obj_istuple(const cfg_obj_t * obj)306 cfg_obj_istuple(const cfg_obj_t *obj) {
307 	REQUIRE(obj != NULL);
308 	return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
309 }
310 
311 const cfg_obj_t *
cfg_tuple_get(const cfg_obj_t * tupleobj,const char * name)312 cfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) {
313 	unsigned int i;
314 	const cfg_tuplefielddef_t *fields;
315 	const cfg_tuplefielddef_t *f;
316 
317 	REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
318 
319 	fields = tupleobj->type->of;
320 	for (f = fields, i = 0; f->name != NULL; f++, i++) {
321 		if (strcmp(f->name, name) == 0)
322 			return (tupleobj->value.tuple[i]);
323 	}
324 	INSIST(0);
325 	return (NULL);
326 }
327 
328 isc_result_t
cfg_parse_special(cfg_parser_t * pctx,int special)329 cfg_parse_special(cfg_parser_t *pctx, int special) {
330 	isc_result_t result;
331 	CHECK(cfg_gettoken(pctx, 0));
332 	if (pctx->token.type == isc_tokentype_special &&
333 	    pctx->token.value.as_char == special)
334 		return (ISC_R_SUCCESS);
335 
336 	cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
337 	return (ISC_R_UNEXPECTEDTOKEN);
338  cleanup:
339 	return (result);
340 }
341 
342 /*
343  * Parse a required semicolon.  If it is not there, log
344  * an error and increment the error count but continue
345  * parsing.  Since the next token is pushed back,
346  * care must be taken to make sure it is eventually
347  * consumed or an infinite loop may result.
348  */
349 static isc_result_t
parse_semicolon(cfg_parser_t * pctx)350 parse_semicolon(cfg_parser_t *pctx) {
351 	isc_result_t result;
352 	CHECK(cfg_gettoken(pctx, 0));
353 	if (pctx->token.type == isc_tokentype_special &&
354 	    pctx->token.value.as_char == ';')
355 		return (ISC_R_SUCCESS);
356 
357 	cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
358 	cfg_ungettoken(pctx);
359  cleanup:
360 	return (result);
361 }
362 
363 /*
364  * Parse EOF, logging and returning an error if not there.
365  */
366 static isc_result_t
parse_eof(cfg_parser_t * pctx)367 parse_eof(cfg_parser_t *pctx) {
368 	isc_result_t result;
369 	CHECK(cfg_gettoken(pctx, 0));
370 
371 	if (pctx->token.type == isc_tokentype_eof)
372 		return (ISC_R_SUCCESS);
373 
374 	cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
375 	return (ISC_R_UNEXPECTEDTOKEN);
376  cleanup:
377 	return (result);
378 }
379 
380 /* A list of files, used internally for pctx->files. */
381 
382 static cfg_type_t cfg_type_filelist = {
383 	"filelist", NULL, print_list, NULL, &cfg_rep_list,
384 	&cfg_type_qstring
385 };
386 
387 isc_result_t
cfg_parser_create(isc_mem_t * mctx,isc_log_t * lctx,cfg_parser_t ** ret)388 cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
389 	isc_result_t result;
390 	cfg_parser_t *pctx;
391 	isc_lexspecials_t specials;
392 
393 	REQUIRE(mctx != NULL);
394 	REQUIRE(ret != NULL && *ret == NULL);
395 
396 	pctx = isc_mem_get(mctx, sizeof(*pctx));
397 	if (pctx == NULL)
398 		return (ISC_R_NOMEMORY);
399 
400 	pctx->mctx = NULL;
401 	isc_mem_attach(mctx, &pctx->mctx);
402 
403 	result = isc_refcount_init(&pctx->references, 1);
404 	if (result != ISC_R_SUCCESS) {
405 		isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
406 		return (result);
407 	}
408 
409 	pctx->lctx = lctx;
410 	pctx->lexer = NULL;
411 	pctx->seen_eof = ISC_FALSE;
412 	pctx->ungotten = ISC_FALSE;
413 	pctx->errors = 0;
414 	pctx->warnings = 0;
415 	pctx->open_files = NULL;
416 	pctx->closed_files = NULL;
417 	pctx->line = 0;
418 	pctx->callback = NULL;
419 	pctx->callbackarg = NULL;
420 	pctx->token.type = isc_tokentype_unknown;
421 	pctx->flags = 0;
422 
423 	memset(specials, 0, sizeof(specials));
424 	specials['{'] = 1;
425 	specials['}'] = 1;
426 	specials[';'] = 1;
427 	specials['/'] = 1;
428 	specials['"'] = 1;
429 	specials['!'] = 1;
430 
431 	CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer));
432 
433 	isc_lex_setspecials(pctx->lexer, specials);
434 	isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C |
435 					 ISC_LEXCOMMENT_CPLUSPLUS |
436 					 ISC_LEXCOMMENT_SHELL));
437 
438 	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
439 	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
440 
441 	*ret = pctx;
442 	return (ISC_R_SUCCESS);
443 
444  cleanup:
445 	if (pctx->lexer != NULL)
446 		isc_lex_destroy(&pctx->lexer);
447 	CLEANUP_OBJ(pctx->open_files);
448 	CLEANUP_OBJ(pctx->closed_files);
449 	isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
450 	return (result);
451 }
452 
453 static isc_result_t
parser_openfile(cfg_parser_t * pctx,const char * filename)454 parser_openfile(cfg_parser_t *pctx, const char *filename) {
455 	isc_result_t result;
456 	cfg_listelt_t *elt = NULL;
457 	cfg_obj_t *stringobj = NULL;
458 
459 	result = isc_lex_openfile(pctx->lexer, filename);
460 	if (result != ISC_R_SUCCESS) {
461 		cfg_parser_error(pctx, 0, "open: %s: %s",
462 			     filename, isc_result_totext(result));
463 		goto cleanup;
464 	}
465 
466 	CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
467 	CHECK(create_listelt(pctx, &elt));
468 	elt->obj = stringobj;
469 	ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
470 
471 	return (ISC_R_SUCCESS);
472  cleanup:
473 	CLEANUP_OBJ(stringobj);
474 	return (result);
475 }
476 
477 void
cfg_parser_setcallback(cfg_parser_t * pctx,cfg_parsecallback_t callback,void * arg)478 cfg_parser_setcallback(cfg_parser_t *pctx,
479 		       cfg_parsecallback_t callback,
480 		       void *arg)
481 {
482 	pctx->callback = callback;
483 	pctx->callbackarg = arg;
484 }
485 
486 /*
487  * Parse a configuration using a pctx where a lexer has already
488  * been set up with a source.
489  */
490 static isc_result_t
parse2(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)491 parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
492 	isc_result_t result;
493 	cfg_obj_t *obj = NULL;
494 
495 	result = cfg_parse_obj(pctx, type, &obj);
496 
497 	if (pctx->errors != 0) {
498 		/* Errors have been logged. */
499 		if (result == ISC_R_SUCCESS)
500 			result = ISC_R_FAILURE;
501 		goto cleanup;
502 	}
503 
504 	if (result != ISC_R_SUCCESS) {
505 		/* Parsing failed but no errors have been logged. */
506 		cfg_parser_error(pctx, 0, "parsing failed");
507 		goto cleanup;
508 	}
509 
510 	CHECK(parse_eof(pctx));
511 
512 	*ret = obj;
513 	return (ISC_R_SUCCESS);
514 
515  cleanup:
516 	CLEANUP_OBJ(obj);
517 	return (result);
518 }
519 
520 isc_result_t
cfg_parse_file(cfg_parser_t * pctx,const char * filename,const cfg_type_t * type,cfg_obj_t ** ret)521 cfg_parse_file(cfg_parser_t *pctx, const char *filename,
522 	       const cfg_type_t *type, cfg_obj_t **ret)
523 {
524 	isc_result_t result;
525 
526 	REQUIRE(filename != NULL);
527 
528 	CHECK(parser_openfile(pctx, filename));
529 	CHECK(parse2(pctx, type, ret));
530  cleanup:
531 	return (result);
532 }
533 
534 
535 isc_result_t
cfg_parse_buffer(cfg_parser_t * pctx,isc_buffer_t * buffer,const cfg_type_t * type,cfg_obj_t ** ret)536 cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
537 	const cfg_type_t *type, cfg_obj_t **ret)
538 {
539 	isc_result_t result;
540 	REQUIRE(buffer != NULL);
541 	CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
542 	CHECK(parse2(pctx, type, ret));
543  cleanup:
544 	return (result);
545 }
546 
547 void
cfg_parser_attach(cfg_parser_t * src,cfg_parser_t ** dest)548 cfg_parser_attach(cfg_parser_t *src, cfg_parser_t **dest) {
549 	REQUIRE(src != NULL);
550 	REQUIRE(dest != NULL && *dest == NULL);
551 	isc_refcount_increment(&src->references, NULL);
552 	*dest = src;
553 }
554 
555 void
cfg_parser_destroy(cfg_parser_t ** pctxp)556 cfg_parser_destroy(cfg_parser_t **pctxp) {
557 	cfg_parser_t *pctx = *pctxp;
558 	unsigned int refs;
559 
560 	isc_refcount_decrement(&pctx->references, &refs);
561 	if (refs == 0) {
562 		isc_lex_destroy(&pctx->lexer);
563 		/*
564 		 * Cleaning up open_files does not
565 		 * close the files; that was already done
566 		 * by closing the lexer.
567 		 */
568 		CLEANUP_OBJ(pctx->open_files);
569 		CLEANUP_OBJ(pctx->closed_files);
570 		isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
571 	}
572 	*pctxp = NULL;
573 }
574 
575 /*
576  * void
577  */
578 isc_result_t
cfg_parse_void(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)579 cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
580 	UNUSED(type);
581 	return (cfg_create_obj(pctx, &cfg_type_void, ret));
582 }
583 
584 void
cfg_print_void(cfg_printer_t * pctx,const cfg_obj_t * obj)585 cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
586 	UNUSED(pctx);
587 	UNUSED(obj);
588 }
589 
590 void
cfg_doc_void(cfg_printer_t * pctx,const cfg_type_t * type)591 cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
592 	UNUSED(pctx);
593 	UNUSED(type);
594 }
595 
596 isc_boolean_t
cfg_obj_isvoid(const cfg_obj_t * obj)597 cfg_obj_isvoid(const cfg_obj_t *obj) {
598 	REQUIRE(obj != NULL);
599 	return (ISC_TF(obj->type->rep == &cfg_rep_void));
600 }
601 
602 cfg_type_t cfg_type_void = {
603 	"void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
604 	NULL };
605 
606 /*
607  * Fixed point
608  */
609 isc_result_t
cfg_parse_fixedpoint(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)610 cfg_parse_fixedpoint(cfg_parser_t *pctx, const cfg_type_t *type,
611 		     cfg_obj_t **ret)
612 {
613 	isc_result_t result;
614 	cfg_obj_t *obj = NULL;
615 	size_t n1, n2, n3, l;
616 	const char *p;
617 
618 	UNUSED(type);
619 
620 	CHECK(cfg_gettoken(pctx, 0));
621 	if (pctx->token.type != isc_tokentype_string) {
622 		cfg_parser_error(pctx, CFG_LOG_NEAR,
623 				 "expected fixed point number");
624 		return (ISC_R_UNEXPECTEDTOKEN);
625 	}
626 
627 
628 	p = TOKEN_STRING(pctx);
629 	l = strlen(p);
630 	n1 = strspn(p, "0123456789");
631 	n2 = strspn(p + n1, ".");
632 	n3 = strspn(p + n1 + n2, "0123456789");
633 
634 	if ((n1 + n2 + n3 != l) || (n1 + n3 == 0) ||
635 	    n1 > 5 || n2 > 1 || n3 > 2) {
636 		cfg_parser_error(pctx, CFG_LOG_NEAR,
637 				 "expected fixed point number");
638 		return (ISC_R_UNEXPECTEDTOKEN);
639 	}
640 
641 	CHECK(cfg_create_obj(pctx, &cfg_type_fixedpoint, &obj));
642 
643 	obj->value.uint32 = strtoul(p, NULL, 10) * 100;
644 	switch (n3) {
645 	case 2:
646 		obj->value.uint32 += strtoul(p + n1 + n2, NULL, 10);
647 		break;
648 	case 1:
649 		obj->value.uint32 += strtoul(p + n1 + n2, NULL, 10) * 10;
650 		break;
651 	}
652 	*ret = obj;
653 
654  cleanup:
655 	return (result);
656 }
657 
658 void
cfg_print_fixedpoint(cfg_printer_t * pctx,const cfg_obj_t * obj)659 cfg_print_fixedpoint(cfg_printer_t *pctx, const cfg_obj_t *obj) {
660 	char buf[64];
661 	int n;
662 
663 	n = snprintf(buf, sizeof(buf), "%u.%02u",
664 		     obj->value.uint32/100, obj->value.uint32%100);
665 	INSIST(n > 0 && (size_t)n < sizeof(buf));
666 	cfg_print_chars(pctx, buf, strlen(buf));
667 }
668 
669 isc_uint32_t
cfg_obj_asfixedpoint(const cfg_obj_t * obj)670 cfg_obj_asfixedpoint(const cfg_obj_t *obj) {
671 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_fixedpoint);
672 	return (obj->value.uint32);
673 }
674 
675 cfg_type_t cfg_type_fixedpoint = {
676 	"fixedpoint", cfg_parse_fixedpoint, cfg_print_fixedpoint,
677 	cfg_doc_terminal, &cfg_rep_fixedpoint, NULL
678 };
679 
680 /*
681  * uint32
682  */
683 isc_result_t
cfg_parse_uint32(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)684 cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
685 	isc_result_t result;
686 	cfg_obj_t *obj = NULL;
687 	UNUSED(type);
688 
689 	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
690 	if (pctx->token.type != isc_tokentype_number) {
691 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
692 		return (ISC_R_UNEXPECTEDTOKEN);
693 	}
694 
695 	CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
696 
697 	obj->value.uint32 = pctx->token.value.as_ulong;
698 	*ret = obj;
699  cleanup:
700 	return (result);
701 }
702 
703 void
cfg_print_cstr(cfg_printer_t * pctx,const char * s)704 cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
705 	cfg_print_chars(pctx, s, strlen(s));
706 }
707 
708 void
cfg_print_rawuint(cfg_printer_t * pctx,unsigned int u)709 cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
710 	char buf[32];
711 	snprintf(buf, sizeof(buf), "%u", u);
712 	cfg_print_cstr(pctx, buf);
713 }
714 
715 void
cfg_print_uint32(cfg_printer_t * pctx,const cfg_obj_t * obj)716 cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
717 	cfg_print_rawuint(pctx, obj->value.uint32);
718 }
719 
720 isc_boolean_t
cfg_obj_isuint32(const cfg_obj_t * obj)721 cfg_obj_isuint32(const cfg_obj_t *obj) {
722 	REQUIRE(obj != NULL);
723 	return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
724 }
725 
726 isc_uint32_t
cfg_obj_asuint32(const cfg_obj_t * obj)727 cfg_obj_asuint32(const cfg_obj_t *obj) {
728 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
729 	return (obj->value.uint32);
730 }
731 
732 cfg_type_t cfg_type_uint32 = {
733 	"integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
734 	&cfg_rep_uint32, NULL
735 };
736 
737 
738 /*
739  * uint64
740  */
741 isc_boolean_t
cfg_obj_isuint64(const cfg_obj_t * obj)742 cfg_obj_isuint64(const cfg_obj_t *obj) {
743 	REQUIRE(obj != NULL);
744 	return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
745 }
746 
747 isc_uint64_t
cfg_obj_asuint64(const cfg_obj_t * obj)748 cfg_obj_asuint64(const cfg_obj_t *obj) {
749 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
750 	return (obj->value.uint64);
751 }
752 
753 void
cfg_print_uint64(cfg_printer_t * pctx,const cfg_obj_t * obj)754 cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
755 	char buf[32];
756 	snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
757 		 obj->value.uint64);
758 	cfg_print_cstr(pctx, buf);
759 }
760 
761 cfg_type_t cfg_type_uint64 = {
762 	"64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
763 	&cfg_rep_uint64, NULL
764 };
765 
766 /*
767  * qstring (quoted string), ustring (unquoted string), astring
768  * (any string)
769  */
770 
771 /* Create a string object from a null-terminated C string. */
772 static isc_result_t
create_string(cfg_parser_t * pctx,const char * contents,const cfg_type_t * type,cfg_obj_t ** ret)773 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
774 	      cfg_obj_t **ret)
775 {
776 	isc_result_t result;
777 	cfg_obj_t *obj = NULL;
778 	int len;
779 
780 	CHECK(cfg_create_obj(pctx, type, &obj));
781 	len = strlen(contents);
782 	obj->value.string.length = len;
783 	obj->value.string.base = isc_mem_get(pctx->mctx, len + 1);
784 	if (obj->value.string.base == 0) {
785 		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
786 		return (ISC_R_NOMEMORY);
787 	}
788 	memmove(obj->value.string.base, contents, len);
789 	obj->value.string.base[len] = '\0';
790 
791 	*ret = obj;
792  cleanup:
793 	return (result);
794 }
795 
796 isc_result_t
cfg_parse_qstring(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)797 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
798 	isc_result_t result;
799 	UNUSED(type);
800 
801 	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
802 	if (pctx->token.type != isc_tokentype_qstring) {
803 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
804 		return (ISC_R_UNEXPECTEDTOKEN);
805 	}
806 	return (create_string(pctx,
807 			      TOKEN_STRING(pctx),
808 			      &cfg_type_qstring,
809 			      ret));
810  cleanup:
811 	return (result);
812 }
813 
814 static isc_result_t
parse_ustring(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)815 parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
816 	isc_result_t result;
817 	UNUSED(type);
818 
819 	CHECK(cfg_gettoken(pctx, 0));
820 	if (pctx->token.type != isc_tokentype_string) {
821 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
822 		return (ISC_R_UNEXPECTEDTOKEN);
823 	}
824 	return (create_string(pctx,
825 			      TOKEN_STRING(pctx),
826 			      &cfg_type_ustring,
827 			      ret));
828  cleanup:
829 	return (result);
830 }
831 
832 isc_result_t
cfg_parse_astring(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)833 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
834 		  cfg_obj_t **ret)
835 {
836 	isc_result_t result;
837 	UNUSED(type);
838 
839 	CHECK(cfg_getstringtoken(pctx));
840 	return (create_string(pctx,
841 			      TOKEN_STRING(pctx),
842 			      &cfg_type_qstring,
843 			      ret));
844  cleanup:
845 	return (result);
846 }
847 
848 isc_result_t
cfg_parse_sstring(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)849 cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type,
850 		  cfg_obj_t **ret)
851 {
852 	isc_result_t result;
853 	UNUSED(type);
854 
855 	CHECK(cfg_getstringtoken(pctx));
856 	return (create_string(pctx,
857 			      TOKEN_STRING(pctx),
858 			      &cfg_type_sstring,
859 			      ret));
860  cleanup:
861 	return (result);
862 }
863 
864 isc_boolean_t
cfg_is_enum(const char * s,const char * const * enums)865 cfg_is_enum(const char *s, const char *const *enums) {
866 	const char * const *p;
867 	for (p = enums; *p != NULL; p++) {
868 		if (strcasecmp(*p, s) == 0)
869 			return (ISC_TRUE);
870 	}
871 	return (ISC_FALSE);
872 }
873 
874 static isc_result_t
check_enum(cfg_parser_t * pctx,cfg_obj_t * obj,const char * const * enums)875 check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
876 	const char *s = obj->value.string.base;
877 	if (cfg_is_enum(s, enums))
878 		return (ISC_R_SUCCESS);
879 	cfg_parser_error(pctx, 0, "'%s' unexpected", s);
880 	return (ISC_R_UNEXPECTEDTOKEN);
881 }
882 
883 isc_result_t
cfg_parse_enum(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)884 cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
885 	isc_result_t result;
886 	cfg_obj_t *obj = NULL;
887 	CHECK(parse_ustring(pctx, NULL, &obj));
888 	CHECK(check_enum(pctx, obj, type->of));
889 	*ret = obj;
890 	return (ISC_R_SUCCESS);
891  cleanup:
892 	CLEANUP_OBJ(obj);
893 	return (result);
894 }
895 
896 void
cfg_doc_enum(cfg_printer_t * pctx,const cfg_type_t * type)897 cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
898 	const char * const *p;
899 	cfg_print_chars(pctx, "( ", 2);
900 	for (p = type->of; *p != NULL; p++) {
901 		cfg_print_cstr(pctx, *p);
902 		if (p[1] != NULL)
903 			cfg_print_chars(pctx, " | ", 3);
904 	}
905 	cfg_print_chars(pctx, " )", 2);
906 }
907 
908 void
cfg_print_ustring(cfg_printer_t * pctx,const cfg_obj_t * obj)909 cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
910 	cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
911 }
912 
913 static void
print_qstring(cfg_printer_t * pctx,const cfg_obj_t * obj)914 print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
915 	cfg_print_chars(pctx, "\"", 1);
916 	cfg_print_ustring(pctx, obj);
917 	cfg_print_chars(pctx, "\"", 1);
918 }
919 
920 static void
print_sstring(cfg_printer_t * pctx,const cfg_obj_t * obj)921 print_sstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
922 	cfg_print_chars(pctx, "\"", 1);
923 	if ((pctx->flags & CFG_PRINTER_XKEY) != 0) {
924 		unsigned int len = obj->value.string.length;
925 		while (len-- > 0)
926 			cfg_print_chars(pctx, "?", 1);
927 	} else
928 		cfg_print_ustring(pctx, obj);
929 	cfg_print_chars(pctx, "\"", 1);
930 }
931 
932 static void
free_string(cfg_parser_t * pctx,cfg_obj_t * obj)933 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
934 	isc_mem_put(pctx->mctx, obj->value.string.base,
935 		    obj->value.string.length + 1);
936 }
937 
938 isc_boolean_t
cfg_obj_isstring(const cfg_obj_t * obj)939 cfg_obj_isstring(const cfg_obj_t *obj) {
940 	REQUIRE(obj != NULL);
941 	return (ISC_TF(obj->type->rep == &cfg_rep_string));
942 }
943 
944 const char *
cfg_obj_asstring(const cfg_obj_t * obj)945 cfg_obj_asstring(const cfg_obj_t *obj) {
946 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
947 	return (obj->value.string.base);
948 }
949 
950 /* Quoted string only */
951 cfg_type_t cfg_type_qstring = {
952 	"quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal,
953 	&cfg_rep_string, NULL
954 };
955 
956 /* Unquoted string only */
957 cfg_type_t cfg_type_ustring = {
958 	"string", parse_ustring, cfg_print_ustring, cfg_doc_terminal,
959 	&cfg_rep_string, NULL
960 };
961 
962 /* Any string (quoted or unquoted); printed with quotes */
963 cfg_type_t cfg_type_astring = {
964 	"string", cfg_parse_astring, print_qstring, cfg_doc_terminal,
965 	&cfg_rep_string, NULL
966 };
967 
968 /*
969  * Any string (quoted or unquoted); printed with quotes.
970  * If CFG_PRINTER_XKEY is set when printing the string will be '?' out.
971  */
972 cfg_type_t cfg_type_sstring = {
973 	"string", cfg_parse_sstring, print_sstring, cfg_doc_terminal,
974 	&cfg_rep_string, NULL
975 };
976 
977 /*
978  * Booleans
979  */
980 
981 isc_boolean_t
cfg_obj_isboolean(const cfg_obj_t * obj)982 cfg_obj_isboolean(const cfg_obj_t *obj) {
983 	REQUIRE(obj != NULL);
984 	return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
985 }
986 
987 isc_boolean_t
cfg_obj_asboolean(const cfg_obj_t * obj)988 cfg_obj_asboolean(const cfg_obj_t *obj) {
989 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
990 	return (obj->value.boolean);
991 }
992 
993 isc_result_t
cfg_parse_boolean(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)994 cfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
995 {
996 	isc_result_t result;
997 	isc_boolean_t value;
998 	cfg_obj_t *obj = NULL;
999 	UNUSED(type);
1000 
1001 	result = cfg_gettoken(pctx, 0);
1002 	if (result != ISC_R_SUCCESS)
1003 		return (result);
1004 
1005 	if (pctx->token.type != isc_tokentype_string)
1006 		goto bad_boolean;
1007 
1008 	if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
1009 	    (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
1010 	    (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
1011 		value = ISC_TRUE;
1012 	} else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
1013 		   (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
1014 		   (strcmp(TOKEN_STRING(pctx), "0") == 0)) {
1015 		value = ISC_FALSE;
1016 	} else {
1017 		goto bad_boolean;
1018 	}
1019 
1020 	CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
1021 	obj->value.boolean = value;
1022 	*ret = obj;
1023 	return (result);
1024 
1025  bad_boolean:
1026 	cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
1027 	return (ISC_R_UNEXPECTEDTOKEN);
1028 
1029  cleanup:
1030 	return (result);
1031 }
1032 
1033 void
cfg_print_boolean(cfg_printer_t * pctx,const cfg_obj_t * obj)1034 cfg_print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1035 	if (obj->value.boolean)
1036 		cfg_print_chars(pctx, "yes", 3);
1037 	else
1038 		cfg_print_chars(pctx, "no", 2);
1039 }
1040 
1041 cfg_type_t cfg_type_boolean = {
1042 	"boolean", cfg_parse_boolean, cfg_print_boolean, cfg_doc_terminal,
1043 	&cfg_rep_boolean, NULL
1044 };
1045 
1046 /*
1047  * Lists.
1048  */
1049 
1050 isc_result_t
cfg_create_list(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** obj)1051 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
1052 	isc_result_t result;
1053 	CHECK(cfg_create_obj(pctx, type, obj));
1054 	ISC_LIST_INIT((*obj)->value.list);
1055  cleanup:
1056 	return (result);
1057 }
1058 
1059 static isc_result_t
create_listelt(cfg_parser_t * pctx,cfg_listelt_t ** eltp)1060 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
1061 	cfg_listelt_t *elt;
1062 	elt = isc_mem_get(pctx->mctx, sizeof(*elt));
1063 	if (elt == NULL)
1064 		return (ISC_R_NOMEMORY);
1065 	elt->obj = NULL;
1066 	ISC_LINK_INIT(elt, link);
1067 	*eltp = elt;
1068 	return (ISC_R_SUCCESS);
1069 }
1070 
1071 static void
free_list_elt(cfg_parser_t * pctx,cfg_listelt_t * elt)1072 free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
1073 	cfg_obj_destroy(pctx, &elt->obj);
1074 	isc_mem_put(pctx->mctx, elt, sizeof(*elt));
1075 }
1076 
1077 static void
free_list(cfg_parser_t * pctx,cfg_obj_t * obj)1078 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
1079 	cfg_listelt_t *elt, *next;
1080 	for (elt = ISC_LIST_HEAD(obj->value.list);
1081 	     elt != NULL;
1082 	     elt = next)
1083 	{
1084 		next = ISC_LIST_NEXT(elt, link);
1085 		free_list_elt(pctx, elt);
1086 	}
1087 }
1088 
1089 isc_result_t
cfg_parse_listelt(cfg_parser_t * pctx,const cfg_type_t * elttype,cfg_listelt_t ** ret)1090 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
1091 		  cfg_listelt_t **ret)
1092 {
1093 	isc_result_t result;
1094 	cfg_listelt_t *elt = NULL;
1095 	cfg_obj_t *value = NULL;
1096 
1097 	CHECK(create_listelt(pctx, &elt));
1098 
1099 	result = cfg_parse_obj(pctx, elttype, &value);
1100 	if (result != ISC_R_SUCCESS)
1101 		goto cleanup;
1102 
1103 	elt->obj = value;
1104 
1105 	*ret = elt;
1106 	return (ISC_R_SUCCESS);
1107 
1108  cleanup:
1109 	isc_mem_put(pctx->mctx, elt, sizeof(*elt));
1110 	return (result);
1111 }
1112 
1113 /*
1114  * Parse a homogeneous list whose elements are of type 'elttype'
1115  * and where each element is terminated by a semicolon.
1116  */
1117 static isc_result_t
parse_list(cfg_parser_t * pctx,const cfg_type_t * listtype,cfg_obj_t ** ret)1118 parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
1119 {
1120 	cfg_obj_t *listobj = NULL;
1121 	const cfg_type_t *listof = listtype->of;
1122 	isc_result_t result;
1123 	cfg_listelt_t *elt = NULL;
1124 
1125 	CHECK(cfg_create_list(pctx, listtype, &listobj));
1126 
1127 	for (;;) {
1128 		CHECK(cfg_peektoken(pctx, 0));
1129 		if (pctx->token.type == isc_tokentype_special &&
1130 		    pctx->token.value.as_char == /*{*/ '}')
1131 			break;
1132 		CHECK(cfg_parse_listelt(pctx, listof, &elt));
1133 		CHECK(parse_semicolon(pctx));
1134 		ISC_LIST_APPEND(listobj->value.list, elt, link);
1135 		elt = NULL;
1136 	}
1137 	*ret = listobj;
1138 	return (ISC_R_SUCCESS);
1139 
1140  cleanup:
1141 	if (elt != NULL)
1142 		free_list_elt(pctx, elt);
1143 	CLEANUP_OBJ(listobj);
1144 	return (result);
1145 }
1146 
1147 static void
print_list(cfg_printer_t * pctx,const cfg_obj_t * obj)1148 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1149 	const cfg_list_t *list = &obj->value.list;
1150 	const cfg_listelt_t *elt;
1151 
1152 	for (elt = ISC_LIST_HEAD(*list);
1153 	     elt != NULL;
1154 	     elt = ISC_LIST_NEXT(elt, link)) {
1155 		print_indent(pctx);
1156 		cfg_print_obj(pctx, elt->obj);
1157 		cfg_print_chars(pctx, ";\n", 2);
1158 	}
1159 }
1160 
1161 isc_result_t
cfg_parse_bracketed_list(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1162 cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
1163 		     cfg_obj_t **ret)
1164 {
1165 	isc_result_t result;
1166 	CHECK(cfg_parse_special(pctx, '{'));
1167 	CHECK(parse_list(pctx, type, ret));
1168 	CHECK(cfg_parse_special(pctx, '}'));
1169  cleanup:
1170 	return (result);
1171 }
1172 
1173 void
cfg_print_bracketed_list(cfg_printer_t * pctx,const cfg_obj_t * obj)1174 cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1175 	print_open(pctx);
1176 	print_list(pctx, obj);
1177 	print_close(pctx);
1178 }
1179 
1180 void
cfg_doc_bracketed_list(cfg_printer_t * pctx,const cfg_type_t * type)1181 cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
1182 	cfg_print_chars(pctx, "{ ", 2);
1183 	cfg_doc_obj(pctx, type->of);
1184 	cfg_print_chars(pctx, "; ... }", 7);
1185 }
1186 
1187 /*
1188  * Parse a homogeneous list whose elements are of type 'elttype'
1189  * and where elements are separated by space.  The list ends
1190  * before the first semicolon.
1191  */
1192 isc_result_t
cfg_parse_spacelist(cfg_parser_t * pctx,const cfg_type_t * listtype,cfg_obj_t ** ret)1193 cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
1194 		    cfg_obj_t **ret)
1195 {
1196 	cfg_obj_t *listobj = NULL;
1197 	const cfg_type_t *listof = listtype->of;
1198 	isc_result_t result;
1199 
1200 	CHECK(cfg_create_list(pctx, listtype, &listobj));
1201 
1202 	for (;;) {
1203 		cfg_listelt_t *elt = NULL;
1204 
1205 		CHECK(cfg_peektoken(pctx, 0));
1206 		if (pctx->token.type == isc_tokentype_special &&
1207 		    pctx->token.value.as_char == ';')
1208 			break;
1209 		CHECK(cfg_parse_listelt(pctx, listof, &elt));
1210 		ISC_LIST_APPEND(listobj->value.list, elt, link);
1211 	}
1212 	*ret = listobj;
1213 	return (ISC_R_SUCCESS);
1214 
1215  cleanup:
1216 	CLEANUP_OBJ(listobj);
1217 	return (result);
1218 }
1219 
1220 void
cfg_print_spacelist(cfg_printer_t * pctx,const cfg_obj_t * obj)1221 cfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1222 	const cfg_list_t *list = &obj->value.list;
1223 	const cfg_listelt_t *elt;
1224 
1225 	for (elt = ISC_LIST_HEAD(*list);
1226 	     elt != NULL;
1227 	     elt = ISC_LIST_NEXT(elt, link)) {
1228 		cfg_print_obj(pctx, elt->obj);
1229 		if (ISC_LIST_NEXT(elt, link) != NULL)
1230 			cfg_print_chars(pctx, " ", 1);
1231 	}
1232 }
1233 
1234 isc_boolean_t
cfg_obj_islist(const cfg_obj_t * obj)1235 cfg_obj_islist(const cfg_obj_t *obj) {
1236 	REQUIRE(obj != NULL);
1237 	return (ISC_TF(obj->type->rep == &cfg_rep_list));
1238 }
1239 
1240 const cfg_listelt_t *
cfg_list_first(const cfg_obj_t * obj)1241 cfg_list_first(const cfg_obj_t *obj) {
1242 	REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
1243 	if (obj == NULL)
1244 		return (NULL);
1245 	return (ISC_LIST_HEAD(obj->value.list));
1246 }
1247 
1248 const cfg_listelt_t *
cfg_list_next(const cfg_listelt_t * elt)1249 cfg_list_next(const cfg_listelt_t *elt) {
1250 	REQUIRE(elt != NULL);
1251 	return (ISC_LIST_NEXT(elt, link));
1252 }
1253 
1254 /*
1255  * Return the length of a list object.  If obj is NULL or is not
1256  * a list, return 0.
1257  */
1258 unsigned int
cfg_list_length(const cfg_obj_t * obj,isc_boolean_t recurse)1259 cfg_list_length(const cfg_obj_t *obj, isc_boolean_t recurse) {
1260 	const cfg_listelt_t *elt;
1261 	unsigned int count = 0;
1262 
1263 	if (obj == NULL || !cfg_obj_islist(obj))
1264 		return (0U);
1265 	for (elt = cfg_list_first(obj);
1266 	     elt != NULL;
1267 	     elt = cfg_list_next(elt)) {
1268 		if (recurse && cfg_obj_islist(elt->obj)) {
1269 			count += cfg_list_length(elt->obj, recurse);
1270 		} else {
1271 			count++;
1272 		}
1273 	}
1274 	return (count);
1275 }
1276 
1277 cfg_obj_t *
cfg_listelt_value(const cfg_listelt_t * elt)1278 cfg_listelt_value(const cfg_listelt_t *elt) {
1279 	REQUIRE(elt != NULL);
1280 	return (elt->obj);
1281 }
1282 
1283 /*
1284  * Maps.
1285  */
1286 
1287 /*
1288  * Parse a map body.  That's something like
1289  *
1290  *   "foo 1; bar { glub; }; zap true; zap false;"
1291  *
1292  * i.e., a sequence of option names followed by values and
1293  * terminated by semicolons.  Used for the top level of
1294  * the named.conf syntax, as well as for the body of the
1295  * options, view, zone, and other statements.
1296  */
1297 isc_result_t
cfg_parse_mapbody(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1298 cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1299 {
1300 	const cfg_clausedef_t * const *clausesets = type->of;
1301 	isc_result_t result;
1302 	const cfg_clausedef_t * const *clauseset;
1303 	const cfg_clausedef_t *clause;
1304 	cfg_obj_t *value = NULL;
1305 	cfg_obj_t *obj = NULL;
1306 	cfg_obj_t *eltobj = NULL;
1307 	cfg_obj_t *includename = NULL;
1308 	isc_symvalue_t symval;
1309 	cfg_list_t *list = NULL;
1310 
1311 	CHECK(create_map(pctx, type, &obj));
1312 
1313 	obj->value.map.clausesets = clausesets;
1314 
1315 	for (;;) {
1316 		cfg_listelt_t *elt;
1317 
1318 	redo:
1319 		/*
1320 		 * Parse the option name and see if it is known.
1321 		 */
1322 		CHECK(cfg_gettoken(pctx, 0));
1323 
1324 		if (pctx->token.type != isc_tokentype_string) {
1325 			cfg_ungettoken(pctx);
1326 			break;
1327 		}
1328 
1329 		/*
1330 		 * We accept "include" statements wherever a map body
1331 		 * clause can occur.
1332 		 */
1333 		if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
1334 			/*
1335 			 * Turn the file name into a temporary configuration
1336 			 * object just so that it is not overwritten by the
1337 			 * semicolon token.
1338 			 */
1339 			CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
1340 			CHECK(parse_semicolon(pctx));
1341 			CHECK(parser_openfile(pctx, includename->
1342 					      value.string.base));
1343 			 cfg_obj_destroy(pctx, &includename);
1344 			 goto redo;
1345 		}
1346 
1347 		clause = NULL;
1348 		for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
1349 			for (clause = *clauseset;
1350 			     clause->name != NULL;
1351 			     clause++) {
1352 				if (strcasecmp(TOKEN_STRING(pctx),
1353 					   clause->name) == 0)
1354 					goto done;
1355 			}
1356 		}
1357 	done:
1358 		if (clause == NULL || clause->name == NULL) {
1359 			cfg_parser_error(pctx, CFG_LOG_NOPREP, "unknown option");
1360 			/*
1361 			 * Try to recover by parsing this option as an unknown
1362 			 * option and discarding it.
1363 			 */
1364 			CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, &eltobj));
1365 			cfg_obj_destroy(pctx, &eltobj);
1366 			CHECK(parse_semicolon(pctx));
1367 			continue;
1368 		}
1369 
1370 		/* Clause is known. */
1371 
1372 		/* Issue warnings if appropriate */
1373 		if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0)
1374 			cfg_parser_warning(pctx, 0, "option '%s' is obsolete",
1375 				       clause->name);
1376 		if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0)
1377 			cfg_parser_warning(pctx, 0, "option '%s' is "
1378 				       "not implemented", clause->name);
1379 		if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0)
1380 			cfg_parser_warning(pctx, 0, "option '%s' is "
1381 				       "not implemented", clause->name);
1382 
1383 		if ((clause->flags & CFG_CLAUSEFLAG_NOTCONFIGURED) != 0) {
1384 			cfg_parser_warning(pctx, 0, "option '%s' was not "
1385 					   "enabled at compile time",
1386 					   clause->name);
1387 			result = ISC_R_FAILURE;
1388 			goto cleanup;
1389 		}
1390 
1391 		/*
1392 		 * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
1393 		 * set here - we need to log the *lack* of such an option,
1394 		 * not its presence.
1395 		 */
1396 
1397 		/* See if the clause already has a value; if not create one. */
1398 		result = isc_symtab_lookup(obj->value.map.symtab,
1399 					   clause->name, 0, &symval);
1400 
1401 		if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
1402 			/* Multivalued clause */
1403 			cfg_obj_t *listobj = NULL;
1404 			if (result == ISC_R_NOTFOUND) {
1405 				CHECK(cfg_create_list(pctx,
1406 						  &cfg_type_implicitlist,
1407 						  &listobj));
1408 				symval.as_pointer = listobj;
1409 				result = isc_symtab_define(obj->value.
1410 						   map.symtab,
1411 						   clause->name,
1412 						   1, symval,
1413 						   isc_symexists_reject);
1414 				if (result != ISC_R_SUCCESS) {
1415 					cfg_parser_error(pctx, CFG_LOG_NEAR,
1416 						     "isc_symtab_define(%s) "
1417 						     "failed", clause->name);
1418 					isc_mem_put(pctx->mctx, list,
1419 						    sizeof(cfg_list_t));
1420 					goto cleanup;
1421 				}
1422 			} else {
1423 				INSIST(result == ISC_R_SUCCESS);
1424 				listobj = symval.as_pointer;
1425 			}
1426 
1427 			elt = NULL;
1428 			CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
1429 			CHECK(parse_semicolon(pctx));
1430 
1431 			ISC_LIST_APPEND(listobj->value.list, elt, link);
1432 		} else {
1433 			/* Single-valued clause */
1434 			if (result == ISC_R_NOTFOUND) {
1435 				isc_boolean_t callback =
1436 					ISC_TF((clause->flags &
1437 						CFG_CLAUSEFLAG_CALLBACK) != 0);
1438 				CHECK(parse_symtab_elt(pctx, clause->name,
1439 						       clause->type,
1440 						       obj->value.map.symtab,
1441 						       callback));
1442 				CHECK(parse_semicolon(pctx));
1443 			} else if (result == ISC_R_SUCCESS) {
1444 				cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
1445 					     clause->name);
1446 				result = ISC_R_EXISTS;
1447 				goto cleanup;
1448 			} else {
1449 				cfg_parser_error(pctx, CFG_LOG_NEAR,
1450 					     "isc_symtab_define() failed");
1451 				goto cleanup;
1452 			}
1453 		}
1454 	}
1455 
1456 
1457 	*ret = obj;
1458 	return (ISC_R_SUCCESS);
1459 
1460  cleanup:
1461 	CLEANUP_OBJ(value);
1462 	CLEANUP_OBJ(obj);
1463 	CLEANUP_OBJ(eltobj);
1464 	CLEANUP_OBJ(includename);
1465 	return (result);
1466 }
1467 
1468 static isc_result_t
parse_symtab_elt(cfg_parser_t * pctx,const char * name,cfg_type_t * elttype,isc_symtab_t * symtab,isc_boolean_t callback)1469 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
1470 		 cfg_type_t *elttype, isc_symtab_t *symtab,
1471 		 isc_boolean_t callback)
1472 {
1473 	isc_result_t result;
1474 	cfg_obj_t *obj = NULL;
1475 	isc_symvalue_t symval;
1476 
1477 	CHECK(cfg_parse_obj(pctx, elttype, &obj));
1478 
1479 	if (callback && pctx->callback != NULL)
1480 		CHECK(pctx->callback(name, obj, pctx->callbackarg));
1481 
1482 	symval.as_pointer = obj;
1483 	CHECK(isc_symtab_define(symtab, name,
1484 				1, symval,
1485 				isc_symexists_reject));
1486 	return (ISC_R_SUCCESS);
1487 
1488  cleanup:
1489 	CLEANUP_OBJ(obj);
1490 	return (result);
1491 }
1492 
1493 /*
1494  * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
1495  */
1496 isc_result_t
cfg_parse_map(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1497 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1498 	isc_result_t result;
1499 	CHECK(cfg_parse_special(pctx, '{'));
1500 	CHECK(cfg_parse_mapbody(pctx, type, ret));
1501 	CHECK(cfg_parse_special(pctx, '}'));
1502  cleanup:
1503 	return (result);
1504 }
1505 
1506 /*
1507  * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
1508  */
1509 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)1510 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type,
1511 		    cfg_obj_t **ret)
1512 {
1513 	isc_result_t result;
1514 	cfg_obj_t *idobj = NULL;
1515 	cfg_obj_t *mapobj = NULL;
1516 
1517 	CHECK(cfg_parse_obj(pctx, nametype, &idobj));
1518 	CHECK(cfg_parse_map(pctx, type, &mapobj));
1519 	mapobj->value.map.id = idobj;
1520 	*ret = mapobj;
1521 	return (result);
1522  cleanup:
1523 	CLEANUP_OBJ(idobj);
1524 	CLEANUP_OBJ(mapobj);
1525 	return (result);
1526 }
1527 
1528 /*
1529  * Parse a map identified by a string name.  E.g., "name { foo 1; }".
1530  * Used for the "key" and "channel" statements.
1531  */
1532 isc_result_t
cfg_parse_named_map(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1533 cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1534 	return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
1535 }
1536 
1537 /*
1538  * Parse a map identified by a network address.
1539  * Used to be used for the "server" statement.
1540  */
1541 isc_result_t
cfg_parse_addressed_map(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1542 cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1543 	return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
1544 }
1545 
1546 /*
1547  * Parse a map identified by a network prefix.
1548  * Used for the "server" statement.
1549  */
1550 isc_result_t
cfg_parse_netprefix_map(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1551 cfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1552 	return (parse_any_named_map(pctx, &cfg_type_netprefix, type, ret));
1553 }
1554 
1555 void
cfg_print_mapbody(cfg_printer_t * pctx,const cfg_obj_t * obj)1556 cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1557 	isc_result_t result = ISC_R_SUCCESS;
1558 
1559 	const cfg_clausedef_t * const *clauseset;
1560 
1561 	for (clauseset = obj->value.map.clausesets;
1562 	     *clauseset != NULL;
1563 	     clauseset++)
1564 	{
1565 		isc_symvalue_t symval;
1566 		const cfg_clausedef_t *clause;
1567 
1568 		for (clause = *clauseset;
1569 		     clause->name != NULL;
1570 		     clause++) {
1571 			result = isc_symtab_lookup(obj->value.map.symtab,
1572 						   clause->name, 0, &symval);
1573 			if (result == ISC_R_SUCCESS) {
1574 				cfg_obj_t *symobj = symval.as_pointer;
1575 				if (symobj->type == &cfg_type_implicitlist) {
1576 					/* Multivalued. */
1577 					cfg_list_t *list = &symobj->value.list;
1578 					cfg_listelt_t *elt;
1579 					for (elt = ISC_LIST_HEAD(*list);
1580 					     elt != NULL;
1581 					     elt = ISC_LIST_NEXT(elt, link)) {
1582 						print_indent(pctx);
1583 						cfg_print_cstr(pctx, clause->name);
1584 						cfg_print_chars(pctx, " ", 1);
1585 						cfg_print_obj(pctx, elt->obj);
1586 						cfg_print_chars(pctx, ";\n", 2);
1587 					}
1588 				} else {
1589 					/* Single-valued. */
1590 					print_indent(pctx);
1591 					cfg_print_cstr(pctx, clause->name);
1592 					cfg_print_chars(pctx, " ", 1);
1593 					cfg_print_obj(pctx, symobj);
1594 					cfg_print_chars(pctx, ";\n", 2);
1595 				}
1596 			} else if (result == ISC_R_NOTFOUND) {
1597 				; /* do nothing */
1598 			} else {
1599 				INSIST(0);
1600 			}
1601 		}
1602 	}
1603 }
1604 
1605 void
cfg_doc_mapbody(cfg_printer_t * pctx,const cfg_type_t * type)1606 cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
1607 	const cfg_clausedef_t * const *clauseset;
1608 	const cfg_clausedef_t *clause;
1609 
1610 	for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1611 		for (clause = *clauseset;
1612 		     clause->name != NULL;
1613 		     clause++) {
1614 			cfg_print_cstr(pctx, clause->name);
1615 			cfg_print_chars(pctx, " ", 1);
1616 			cfg_doc_obj(pctx, clause->type);
1617 			cfg_print_chars(pctx, ";", 1);
1618 			/* XXX print flags here? */
1619 			cfg_print_chars(pctx, "\n\n", 2);
1620 		}
1621 	}
1622 }
1623 
1624 static struct flagtext {
1625 	unsigned int flag;
1626 	const char *text;
1627 } flagtexts[] = {
1628 	{ CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
1629 	{ CFG_CLAUSEFLAG_NYI, "not yet implemented" },
1630 	{ CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
1631 	{ CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
1632 	{ CFG_CLAUSEFLAG_TESTONLY, "test only" },
1633 	{ CFG_CLAUSEFLAG_NOTCONFIGURED, "not configured" },
1634 	{ 0, NULL }
1635 };
1636 
1637 void
cfg_print_map(cfg_printer_t * pctx,const cfg_obj_t * obj)1638 cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1639 	if (obj->value.map.id != NULL) {
1640 		cfg_print_obj(pctx, obj->value.map.id);
1641 		cfg_print_chars(pctx, " ", 1);
1642 	}
1643 	print_open(pctx);
1644 	cfg_print_mapbody(pctx, obj);
1645 	print_close(pctx);
1646 }
1647 
1648 static void
print_clause_flags(cfg_printer_t * pctx,unsigned int flags)1649 print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
1650 	struct flagtext *p;
1651 	isc_boolean_t first = ISC_TRUE;
1652 	for (p = flagtexts; p->flag != 0; p++) {
1653 		if ((flags & p->flag) != 0) {
1654 			if (first)
1655 				cfg_print_chars(pctx, " // ", 4);
1656 			else
1657 				cfg_print_chars(pctx, ", ", 2);
1658 			cfg_print_cstr(pctx, p->text);
1659 			first = ISC_FALSE;
1660 		}
1661 	}
1662 }
1663 
1664 void
cfg_doc_map(cfg_printer_t * pctx,const cfg_type_t * type)1665 cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
1666 	const cfg_clausedef_t * const *clauseset;
1667 	const cfg_clausedef_t *clause;
1668 
1669 	if (type->parse == cfg_parse_named_map) {
1670 		cfg_doc_obj(pctx, &cfg_type_astring);
1671 		cfg_print_chars(pctx, " ", 1);
1672 	} else if (type->parse == cfg_parse_addressed_map) {
1673 		cfg_doc_obj(pctx, &cfg_type_netaddr);
1674 		cfg_print_chars(pctx, " ", 1);
1675 	} else if (type->parse == cfg_parse_netprefix_map) {
1676 		cfg_doc_obj(pctx, &cfg_type_netprefix);
1677 		cfg_print_chars(pctx, " ", 1);
1678 	}
1679 
1680 	print_open(pctx);
1681 
1682 	for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1683 		for (clause = *clauseset;
1684 		     clause->name != NULL;
1685 		     clause++) {
1686 			print_indent(pctx);
1687 			cfg_print_cstr(pctx, clause->name);
1688 			if (clause->type->print != cfg_print_void)
1689 				cfg_print_chars(pctx, " ", 1);
1690 			cfg_doc_obj(pctx, clause->type);
1691 			cfg_print_chars(pctx, ";", 1);
1692 			print_clause_flags(pctx, clause->flags);
1693 			cfg_print_chars(pctx, "\n", 1);
1694 		}
1695 	}
1696 	print_close(pctx);
1697 }
1698 
1699 isc_boolean_t
cfg_obj_ismap(const cfg_obj_t * obj)1700 cfg_obj_ismap(const cfg_obj_t *obj) {
1701 	REQUIRE(obj != NULL);
1702 	return (ISC_TF(obj->type->rep == &cfg_rep_map));
1703 }
1704 
1705 isc_result_t
cfg_map_get(const cfg_obj_t * mapobj,const char * name,const cfg_obj_t ** obj)1706 cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
1707 	isc_result_t result;
1708 	isc_symvalue_t val;
1709 	const cfg_map_t *map;
1710 
1711 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1712 	REQUIRE(name != NULL);
1713 	REQUIRE(obj != NULL && *obj == NULL);
1714 
1715 	map = &mapobj->value.map;
1716 
1717 	result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
1718 	if (result != ISC_R_SUCCESS)
1719 		return (result);
1720 	*obj = val.as_pointer;
1721 	return (ISC_R_SUCCESS);
1722 }
1723 
1724 const cfg_obj_t *
cfg_map_getname(const cfg_obj_t * mapobj)1725 cfg_map_getname(const cfg_obj_t *mapobj) {
1726 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1727 	return (mapobj->value.map.id);
1728 }
1729 
1730 
1731 /* Parse an arbitrary token, storing its raw text representation. */
1732 static isc_result_t
parse_token(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1733 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1734 	cfg_obj_t *obj = NULL;
1735 	isc_result_t result;
1736 	isc_region_t r;
1737 
1738 	UNUSED(type);
1739 
1740 	CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
1741 	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
1742 	if (pctx->token.type == isc_tokentype_eof) {
1743 		cfg_ungettoken(pctx);
1744 		result = ISC_R_EOF;
1745 		goto cleanup;
1746 	}
1747 
1748 	isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
1749 
1750 	obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
1751 	if (obj->value.string.base == NULL) {
1752 		result = ISC_R_NOMEMORY;
1753 		goto cleanup;
1754 	}
1755 	obj->value.string.length = r.length;
1756 	memmove(obj->value.string.base, r.base, r.length);
1757 	obj->value.string.base[r.length] = '\0';
1758 	*ret = obj;
1759 	return (result);
1760 
1761  cleanup:
1762 	if (obj != NULL)
1763 		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
1764 	return (result);
1765 }
1766 
1767 cfg_type_t cfg_type_token = {
1768 	"token", parse_token, cfg_print_ustring, cfg_doc_terminal,
1769 	&cfg_rep_string, NULL
1770 };
1771 
1772 /*
1773  * An unsupported option.  This is just a list of tokens with balanced braces
1774  * ending in a semicolon.
1775  */
1776 
1777 static isc_result_t
parse_unsupported(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1778 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1779 	cfg_obj_t *listobj = NULL;
1780 	isc_result_t result;
1781 	int braces = 0;
1782 
1783 	CHECK(cfg_create_list(pctx, type, &listobj));
1784 
1785 	for (;;) {
1786 		cfg_listelt_t *elt = NULL;
1787 
1788 		CHECK(cfg_peektoken(pctx, 0));
1789 		if (pctx->token.type == isc_tokentype_special) {
1790 			if (pctx->token.value.as_char == '{')
1791 				braces++;
1792 			else if (pctx->token.value.as_char == '}')
1793 				braces--;
1794 			else if (pctx->token.value.as_char == ';')
1795 				if (braces == 0)
1796 					break;
1797 		}
1798 		if (pctx->token.type == isc_tokentype_eof || braces < 0) {
1799 			cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
1800 			result = ISC_R_UNEXPECTEDTOKEN;
1801 			goto cleanup;
1802 		}
1803 
1804 		CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
1805 		ISC_LIST_APPEND(listobj->value.list, elt, link);
1806 	}
1807 	INSIST(braces == 0);
1808 	*ret = listobj;
1809 	return (ISC_R_SUCCESS);
1810 
1811  cleanup:
1812 	CLEANUP_OBJ(listobj);
1813 	return (result);
1814 }
1815 
1816 cfg_type_t cfg_type_unsupported = {
1817 	"unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal,
1818 	&cfg_rep_list, NULL
1819 };
1820 
1821 /*
1822  * Try interpreting the current token as a network address.
1823  *
1824  * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
1825  * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set.  The
1826  * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
1827  * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
1828  * and the IPv6 wildcard address otherwise.
1829  */
1830 static isc_result_t
token_addr(cfg_parser_t * pctx,unsigned int flags,isc_netaddr_t * na)1831 token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1832 	char *s;
1833 	struct in_addr in4a;
1834 	struct in6_addr in6a;
1835 
1836 	if (pctx->token.type != isc_tokentype_string)
1837 		return (ISC_R_UNEXPECTEDTOKEN);
1838 
1839 	s = TOKEN_STRING(pctx);
1840 	if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
1841 		if ((flags & CFG_ADDR_V4OK) != 0) {
1842 			isc_netaddr_any(na);
1843 			return (ISC_R_SUCCESS);
1844 		} else if ((flags & CFG_ADDR_V6OK) != 0) {
1845 			isc_netaddr_any6(na);
1846 			return (ISC_R_SUCCESS);
1847 		} else {
1848 			INSIST(0);
1849 		}
1850 	} else {
1851 		if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
1852 			if (inet_pton(AF_INET, s, &in4a) == 1) {
1853 				isc_netaddr_fromin(na, &in4a);
1854 				return (ISC_R_SUCCESS);
1855 			}
1856 		}
1857 		if ((flags & CFG_ADDR_V4PREFIXOK) != 0 &&
1858 		    strlen(s) <= 15U) {
1859 			char buf[64];
1860 			int i;
1861 
1862 			strcpy(buf, s);
1863 			for (i = 0; i < 3; i++) {
1864 				strcat(buf, ".0");
1865 				if (inet_pton(AF_INET, buf, &in4a) == 1) {
1866 					isc_netaddr_fromin(na, &in4a);
1867 					return (ISC_R_SUCCESS);
1868 				}
1869 			}
1870 		}
1871 		if ((flags & CFG_ADDR_V6OK) != 0 &&
1872 		    strlen(s) <= 127U) {
1873 			char buf[128]; /* see lib/bind9/getaddresses.c */
1874 			char *d; /* zone delimiter */
1875 			isc_uint32_t zone = 0; /* scope zone ID */
1876 
1877 			strcpy(buf, s);
1878 			d = strchr(buf, '%');
1879 			if (d != NULL)
1880 				*d = '\0';
1881 
1882 			if (inet_pton(AF_INET6, buf, &in6a) == 1) {
1883 				if (d != NULL) {
1884 #ifdef ISC_PLATFORM_HAVESCOPEID
1885 					isc_result_t result;
1886 
1887 					result = isc_netscope_pton(AF_INET6,
1888 								   d + 1,
1889 								   &in6a,
1890 								   &zone);
1891 					if (result != ISC_R_SUCCESS)
1892 						return (result);
1893 #else
1894 				return (ISC_R_BADADDRESSFORM);
1895 #endif
1896 				}
1897 
1898 				isc_netaddr_fromin6(na, &in6a);
1899 				isc_netaddr_setzone(na, zone);
1900 				return (ISC_R_SUCCESS);
1901 			}
1902 		}
1903 	}
1904 	return (ISC_R_UNEXPECTEDTOKEN);
1905 }
1906 
1907 isc_result_t
cfg_parse_rawaddr(cfg_parser_t * pctx,unsigned int flags,isc_netaddr_t * na)1908 cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1909 	isc_result_t result;
1910 	const char *wild = "";
1911 	const char *prefix = "";
1912 
1913 	CHECK(cfg_gettoken(pctx, 0));
1914 	result = token_addr(pctx, flags, na);
1915 	if (result == ISC_R_UNEXPECTEDTOKEN) {
1916 		if ((flags & CFG_ADDR_WILDOK) != 0)
1917 			wild = " or '*'";
1918 		if ((flags & CFG_ADDR_V4PREFIXOK) != 0)
1919 			wild = " or IPv4 prefix";
1920 		if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V4OK)
1921 			cfg_parser_error(pctx, CFG_LOG_NEAR,
1922 					 "expected IPv4 address%s%s",
1923 					 prefix, wild);
1924 		else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK)
1925 			cfg_parser_error(pctx, CFG_LOG_NEAR,
1926 					 "expected IPv6 address%s%s",
1927 					 prefix, wild);
1928 		else
1929 			cfg_parser_error(pctx, CFG_LOG_NEAR,
1930 					 "expected IP address%s%s",
1931 					 prefix, wild);
1932 	}
1933  cleanup:
1934 	return (result);
1935 }
1936 
1937 isc_boolean_t
cfg_lookingat_netaddr(cfg_parser_t * pctx,unsigned int flags)1938 cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
1939 	isc_result_t result;
1940 	isc_netaddr_t na_dummy;
1941 	result = token_addr(pctx, flags, &na_dummy);
1942 	return (ISC_TF(result == ISC_R_SUCCESS));
1943 }
1944 
1945 isc_result_t
cfg_parse_rawport(cfg_parser_t * pctx,unsigned int flags,in_port_t * port)1946 cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
1947 	isc_result_t result;
1948 
1949 	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1950 
1951 	if ((flags & CFG_ADDR_WILDOK) != 0 &&
1952 	    pctx->token.type == isc_tokentype_string &&
1953 	    strcmp(TOKEN_STRING(pctx), "*") == 0) {
1954 		*port = 0;
1955 		return (ISC_R_SUCCESS);
1956 	}
1957 	if (pctx->token.type != isc_tokentype_number) {
1958 		cfg_parser_error(pctx, CFG_LOG_NEAR,
1959 			     "expected port number or '*'");
1960 		return (ISC_R_UNEXPECTEDTOKEN);
1961 	}
1962 	if (pctx->token.value.as_ulong >= 65536U) {
1963 		cfg_parser_error(pctx, CFG_LOG_NEAR,
1964 			     "port number out of range");
1965 		return (ISC_R_UNEXPECTEDTOKEN);
1966 	}
1967 	*port = (in_port_t)(pctx->token.value.as_ulong);
1968 	return (ISC_R_SUCCESS);
1969  cleanup:
1970 	return (result);
1971 }
1972 
1973 void
cfg_print_rawaddr(cfg_printer_t * pctx,const isc_netaddr_t * na)1974 cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
1975 	isc_result_t result;
1976 	char text[128];
1977 	isc_buffer_t buf;
1978 
1979 	isc_buffer_init(&buf, text, sizeof(text));
1980 	result = isc_netaddr_totext(na, &buf);
1981 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
1982 	cfg_print_chars(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf));
1983 }
1984 
1985 /* netaddr */
1986 
1987 static unsigned int netaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
1988 static unsigned int netaddr4_flags = CFG_ADDR_V4OK;
1989 static unsigned int netaddr4wild_flags = CFG_ADDR_V4OK | CFG_ADDR_WILDOK;
1990 static unsigned int netaddr6_flags = CFG_ADDR_V6OK;
1991 static unsigned int netaddr6wild_flags = CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
1992 
1993 static isc_result_t
parse_netaddr(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1994 parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1995 	isc_result_t result;
1996 	cfg_obj_t *obj = NULL;
1997 	isc_netaddr_t netaddr;
1998 	unsigned int flags = *(const unsigned int *)type->of;
1999 
2000 	CHECK(cfg_create_obj(pctx, type, &obj));
2001 	CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
2002 	isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
2003 	*ret = obj;
2004 	return (ISC_R_SUCCESS);
2005  cleanup:
2006 	CLEANUP_OBJ(obj);
2007 	return (result);
2008 }
2009 
2010 static void
cfg_doc_netaddr(cfg_printer_t * pctx,const cfg_type_t * type)2011 cfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
2012 	const unsigned int *flagp = type->of;
2013 	int n = 0;
2014 	if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
2015 		cfg_print_chars(pctx, "( ", 2);
2016 	if (*flagp & CFG_ADDR_V4OK) {
2017 		cfg_print_cstr(pctx, "<ipv4_address>");
2018 		n++;
2019 	}
2020 	if (*flagp & CFG_ADDR_V6OK) {
2021 		if (n != 0)
2022 			cfg_print_chars(pctx, " | ", 3);
2023 		cfg_print_cstr(pctx, "<ipv6_address>");
2024 		n++;
2025 	}
2026 	if (*flagp & CFG_ADDR_WILDOK) {
2027 		if (n != 0)
2028 			cfg_print_chars(pctx, " | ", 3);
2029 		cfg_print_chars(pctx, "*", 1);
2030 		n++;
2031 		POST(n);
2032 	}
2033 	if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
2034 		cfg_print_chars(pctx, " )", 2);
2035 }
2036 
2037 cfg_type_t cfg_type_netaddr = {
2038 	"netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
2039 	&cfg_rep_sockaddr, &netaddr_flags
2040 };
2041 
2042 cfg_type_t cfg_type_netaddr4 = {
2043 	"netaddr4", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
2044 	&cfg_rep_sockaddr, &netaddr4_flags
2045 };
2046 
2047 cfg_type_t cfg_type_netaddr4wild = {
2048 	"netaddr4wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
2049 	&cfg_rep_sockaddr, &netaddr4wild_flags
2050 };
2051 
2052 cfg_type_t cfg_type_netaddr6 = {
2053 	"netaddr6", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
2054 	&cfg_rep_sockaddr, &netaddr6_flags
2055 };
2056 
2057 cfg_type_t cfg_type_netaddr6wild = {
2058 	"netaddr6wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
2059 	&cfg_rep_sockaddr, &netaddr6wild_flags
2060 };
2061 
2062 /* netprefix */
2063 
2064 isc_result_t
cfg_parse_netprefix(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)2065 cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
2066 		    cfg_obj_t **ret)
2067 {
2068 	cfg_obj_t *obj = NULL;
2069 	isc_result_t result;
2070 	isc_netaddr_t netaddr;
2071 	unsigned int addrlen = 0, prefixlen;
2072 	UNUSED(type);
2073 
2074 	CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
2075 				CFG_ADDR_V6OK, &netaddr));
2076 	switch (netaddr.family) {
2077 	case AF_INET:
2078 		addrlen = 32;
2079 		break;
2080 	case AF_INET6:
2081 		addrlen = 128;
2082 		break;
2083 	default:
2084 		INSIST(0);
2085 		break;
2086 	}
2087 	CHECK(cfg_peektoken(pctx, 0));
2088 	if (pctx->token.type == isc_tokentype_special &&
2089 	    pctx->token.value.as_char == '/') {
2090 		CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
2091 		CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
2092 		if (pctx->token.type != isc_tokentype_number) {
2093 			cfg_parser_error(pctx, CFG_LOG_NEAR,
2094 				     "expected prefix length");
2095 			return (ISC_R_UNEXPECTEDTOKEN);
2096 		}
2097 		prefixlen = pctx->token.value.as_ulong;
2098 		if (prefixlen > addrlen) {
2099 			cfg_parser_error(pctx, CFG_LOG_NOPREP,
2100 				     "invalid prefix length");
2101 			return (ISC_R_RANGE);
2102 		}
2103 	} else {
2104 		prefixlen = addrlen;
2105 	}
2106 	CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
2107 	obj->value.netprefix.address = netaddr;
2108 	obj->value.netprefix.prefixlen = prefixlen;
2109 	*ret = obj;
2110 	return (ISC_R_SUCCESS);
2111  cleanup:
2112 	cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
2113 	return (result);
2114 }
2115 
2116 static void
print_netprefix(cfg_printer_t * pctx,const cfg_obj_t * obj)2117 print_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2118 	const cfg_netprefix_t *p = &obj->value.netprefix;
2119 
2120 	cfg_print_rawaddr(pctx, &p->address);
2121 	cfg_print_chars(pctx, "/", 1);
2122 	cfg_print_rawuint(pctx, p->prefixlen);
2123 }
2124 
2125 isc_boolean_t
cfg_obj_isnetprefix(const cfg_obj_t * obj)2126 cfg_obj_isnetprefix(const cfg_obj_t *obj) {
2127 	REQUIRE(obj != NULL);
2128 	return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
2129 }
2130 
2131 void
cfg_obj_asnetprefix(const cfg_obj_t * obj,isc_netaddr_t * netaddr,unsigned int * prefixlen)2132 cfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr,
2133 		    unsigned int *prefixlen)
2134 {
2135 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
2136 	REQUIRE(netaddr != NULL);
2137 	REQUIRE(prefixlen != NULL);
2138 
2139 	*netaddr = obj->value.netprefix.address;
2140 	*prefixlen = obj->value.netprefix.prefixlen;
2141 }
2142 
2143 cfg_type_t cfg_type_netprefix = {
2144 	"netprefix", cfg_parse_netprefix, print_netprefix, cfg_doc_terminal,
2145 	&cfg_rep_netprefix, NULL
2146 };
2147 
2148 static isc_result_t
parse_sockaddrsub(cfg_parser_t * pctx,const cfg_type_t * type,int flags,cfg_obj_t ** ret)2149 parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type,
2150 		  int flags, cfg_obj_t **ret)
2151 {
2152 	isc_result_t result;
2153 	isc_netaddr_t netaddr;
2154 	in_port_t port = 0;
2155 	cfg_obj_t *obj = NULL;
2156 
2157 	CHECK(cfg_create_obj(pctx, type, &obj));
2158 	CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
2159 	CHECK(cfg_peektoken(pctx, 0));
2160 	if (pctx->token.type == isc_tokentype_string &&
2161 	    strcasecmp(TOKEN_STRING(pctx), "port") == 0) {
2162 		CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
2163 		CHECK(cfg_parse_rawport(pctx, flags, &port));
2164 	}
2165 	isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
2166 	*ret = obj;
2167 	return (ISC_R_SUCCESS);
2168 
2169  cleanup:
2170 	CLEANUP_OBJ(obj);
2171 	return (result);
2172 }
2173 
2174 static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
2175 cfg_type_t cfg_type_sockaddr = {
2176 	"sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr,
2177 	&cfg_rep_sockaddr, &sockaddr_flags
2178 };
2179 
2180 isc_result_t
cfg_parse_sockaddr(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)2181 cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2182 	const unsigned int *flagp = type->of;
2183 	return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret));
2184 }
2185 
2186 void
cfg_print_sockaddr(cfg_printer_t * pctx,const cfg_obj_t * obj)2187 cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2188 	isc_netaddr_t netaddr;
2189 	in_port_t port;
2190 	char buf[ISC_NETADDR_FORMATSIZE];
2191 
2192 	isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
2193 	isc_netaddr_format(&netaddr, buf, sizeof(buf));
2194 	cfg_print_cstr(pctx, buf);
2195 	port = isc_sockaddr_getport(&obj->value.sockaddr);
2196 	if (port != 0) {
2197 		cfg_print_chars(pctx, " port ", 6);
2198 		cfg_print_rawuint(pctx, port);
2199 	}
2200 }
2201 
2202 void
cfg_doc_sockaddr(cfg_printer_t * pctx,const cfg_type_t * type)2203 cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
2204 	const unsigned int *flagp = type->of;
2205 	int n = 0;
2206 	cfg_print_chars(pctx, "( ", 2);
2207 	if (*flagp & CFG_ADDR_V4OK) {
2208 		cfg_print_cstr(pctx, "<ipv4_address>");
2209 		n++;
2210 	}
2211 	if (*flagp & CFG_ADDR_V6OK) {
2212 		if (n != 0)
2213 			cfg_print_chars(pctx, " | ", 3);
2214 		cfg_print_cstr(pctx, "<ipv6_address>");
2215 		n++;
2216 	}
2217 	if (*flagp & CFG_ADDR_WILDOK) {
2218 		if (n != 0)
2219 			cfg_print_chars(pctx, " | ", 3);
2220 		cfg_print_chars(pctx, "*", 1);
2221 		n++;
2222 		POST(n);
2223 	}
2224 	cfg_print_chars(pctx, " ) ", 3);
2225 	if (*flagp & CFG_ADDR_WILDOK) {
2226 		cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
2227 	} else {
2228 		cfg_print_cstr(pctx, "[ port <integer> ]");
2229 	}
2230 }
2231 
2232 isc_boolean_t
cfg_obj_issockaddr(const cfg_obj_t * obj)2233 cfg_obj_issockaddr(const cfg_obj_t *obj) {
2234 	REQUIRE(obj != NULL);
2235 	return (ISC_TF(obj->type->rep == &cfg_rep_sockaddr));
2236 }
2237 
2238 const isc_sockaddr_t *
cfg_obj_assockaddr(const cfg_obj_t * obj)2239 cfg_obj_assockaddr(const cfg_obj_t *obj) {
2240 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
2241 	return (&obj->value.sockaddr);
2242 }
2243 
2244 isc_result_t
cfg_gettoken(cfg_parser_t * pctx,int options)2245 cfg_gettoken(cfg_parser_t *pctx, int options) {
2246 	isc_result_t result;
2247 
2248 	if (pctx->seen_eof)
2249 		return (ISC_R_SUCCESS);
2250 
2251 	options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
2252 
2253  redo:
2254 	pctx->token.type = isc_tokentype_unknown;
2255 	result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
2256 	pctx->ungotten = ISC_FALSE;
2257 	pctx->line = isc_lex_getsourceline(pctx->lexer);
2258 
2259 	switch (result) {
2260 	case ISC_R_SUCCESS:
2261 		if (pctx->token.type == isc_tokentype_eof) {
2262 			result = isc_lex_close(pctx->lexer);
2263 			INSIST(result == ISC_R_NOMORE ||
2264 			       result == ISC_R_SUCCESS);
2265 
2266 			if (isc_lex_getsourcename(pctx->lexer) != NULL) {
2267 				/*
2268 				 * Closed an included file, not the main file.
2269 				 */
2270 				cfg_listelt_t *elt;
2271 				elt = ISC_LIST_TAIL(pctx->open_files->
2272 						    value.list);
2273 				INSIST(elt != NULL);
2274 				ISC_LIST_UNLINK(pctx->open_files->
2275 						value.list, elt, link);
2276 				ISC_LIST_APPEND(pctx->closed_files->
2277 						value.list, elt, link);
2278 				goto redo;
2279 			}
2280 			pctx->seen_eof = ISC_TRUE;
2281 		}
2282 		break;
2283 
2284 	case ISC_R_NOSPACE:
2285 		/* More understandable than "ran out of space". */
2286 		cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
2287 		break;
2288 
2289 	case ISC_R_IOERROR:
2290 		cfg_parser_error(pctx, 0, "%s",
2291 				 isc_result_totext(result));
2292 		break;
2293 
2294 	default:
2295 		cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
2296 				 isc_result_totext(result));
2297 		break;
2298 	}
2299 	return (result);
2300 }
2301 
2302 void
cfg_ungettoken(cfg_parser_t * pctx)2303 cfg_ungettoken(cfg_parser_t *pctx) {
2304 	if (pctx->seen_eof)
2305 		return;
2306 	isc_lex_ungettoken(pctx->lexer, &pctx->token);
2307 	pctx->ungotten = ISC_TRUE;
2308 }
2309 
2310 isc_result_t
cfg_peektoken(cfg_parser_t * pctx,int options)2311 cfg_peektoken(cfg_parser_t *pctx, int options) {
2312 	isc_result_t result;
2313 	CHECK(cfg_gettoken(pctx, options));
2314 	cfg_ungettoken(pctx);
2315  cleanup:
2316 	return (result);
2317 }
2318 
2319 /*
2320  * Get a string token, accepting both the quoted and the unquoted form.
2321  * Log an error if the next token is not a string.
2322  */
2323 static isc_result_t
cfg_getstringtoken(cfg_parser_t * pctx)2324 cfg_getstringtoken(cfg_parser_t *pctx) {
2325 	isc_result_t result;
2326 
2327 	result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
2328 	if (result != ISC_R_SUCCESS)
2329 		return (result);
2330 
2331 	if (pctx->token.type != isc_tokentype_string &&
2332 	    pctx->token.type != isc_tokentype_qstring) {
2333 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
2334 		return (ISC_R_UNEXPECTEDTOKEN);
2335 	}
2336 	return (ISC_R_SUCCESS);
2337 }
2338 
2339 void
cfg_parser_error(cfg_parser_t * pctx,unsigned int flags,const char * fmt,...)2340 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2341 	va_list args;
2342 	va_start(args, fmt);
2343 	parser_complain(pctx, ISC_FALSE, flags, fmt, args);
2344 	va_end(args);
2345 	pctx->errors++;
2346 }
2347 
2348 void
cfg_parser_warning(cfg_parser_t * pctx,unsigned int flags,const char * fmt,...)2349 cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2350 	va_list args;
2351 	va_start(args, fmt);
2352 	parser_complain(pctx, ISC_TRUE, flags, fmt, args);
2353 	va_end(args);
2354 	pctx->warnings++;
2355 }
2356 
2357 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
2358 
2359 static isc_boolean_t
have_current_file(cfg_parser_t * pctx)2360 have_current_file(cfg_parser_t *pctx) {
2361 	cfg_listelt_t *elt;
2362 	if (pctx->open_files == NULL)
2363 		return (ISC_FALSE);
2364 
2365 	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
2366 	if (elt == NULL)
2367 	      return (ISC_FALSE);
2368 
2369 	return (ISC_TRUE);
2370 }
2371 
2372 static char *
current_file(cfg_parser_t * pctx)2373 current_file(cfg_parser_t *pctx) {
2374 	static char none[] = "none";
2375 	cfg_listelt_t *elt;
2376 	cfg_obj_t *fileobj;
2377 
2378 	if (!have_current_file(pctx))
2379 		return (none);
2380 
2381 	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
2382 	if (elt == NULL)	/* shouldn't be possible, but... */
2383 	      return (none);
2384 
2385 	fileobj = elt->obj;
2386 	INSIST(fileobj->type == &cfg_type_qstring);
2387 	return (fileobj->value.string.base);
2388 }
2389 
2390 static void
parser_complain(cfg_parser_t * pctx,isc_boolean_t is_warning,unsigned int flags,const char * format,va_list args)2391 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
2392 		unsigned int flags, const char *format,
2393 		va_list args)
2394 {
2395 	char tokenbuf[MAX_LOG_TOKEN + 10];
2396 	static char where[ISC_DIR_PATHMAX + 100];
2397 	static char message[2048];
2398 	int level = ISC_LOG_ERROR;
2399 	const char *prep = "";
2400 	size_t len;
2401 
2402 	if (is_warning)
2403 		level = ISC_LOG_WARNING;
2404 
2405 	where[0] = '\0';
2406 	if (have_current_file(pctx))
2407 		snprintf(where, sizeof(where), "%s:%u: ",
2408 			 current_file(pctx), pctx->line);
2409 
2410 	len = vsnprintf(message, sizeof(message), format, args);
2411 #define ELIPSIS " ... "
2412 	if (len >= sizeof(message))
2413 		strcpy(message + sizeof(message) - sizeof(ELIPSIS) - 1,
2414 		       ELIPSIS);
2415 
2416 	if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) {
2417 		isc_region_t r;
2418 
2419 		if (pctx->ungotten)
2420 			(void)cfg_gettoken(pctx, 0);
2421 
2422 		if (pctx->token.type == isc_tokentype_eof) {
2423 			snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
2424 		} else if (pctx->token.type == isc_tokentype_unknown) {
2425 			flags = 0;
2426 			tokenbuf[0] = '\0';
2427 		} else {
2428 			isc_lex_getlasttokentext(pctx->lexer,
2429 						 &pctx->token, &r);
2430 			if (r.length > MAX_LOG_TOKEN)
2431 				snprintf(tokenbuf, sizeof(tokenbuf),
2432 					 "'%.*s...'", MAX_LOG_TOKEN, r.base);
2433 			else
2434 				snprintf(tokenbuf, sizeof(tokenbuf),
2435 					 "'%.*s'", (int)r.length, r.base);
2436 		}
2437 
2438 		/* Choose a preposition. */
2439 		if (flags & CFG_LOG_NEAR)
2440 			prep = " near ";
2441 		else if (flags & CFG_LOG_BEFORE)
2442 			prep = " before ";
2443 		else
2444 			prep = " ";
2445 	} else {
2446 		tokenbuf[0] = '\0';
2447 	}
2448 	isc_log_write(pctx->lctx, CAT, MOD, level,
2449 		      "%s%s%s%s", where, message, prep, tokenbuf);
2450 }
2451 
2452 void
cfg_obj_log(const cfg_obj_t * obj,isc_log_t * lctx,int level,const char * fmt,...)2453 cfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level,
2454 	    const char *fmt, ...) {
2455 	va_list ap;
2456 	char msgbuf[2048];
2457 
2458 	if (! isc_log_wouldlog(lctx, level))
2459 		return;
2460 
2461 	va_start(ap, fmt);
2462 
2463 	vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
2464 	isc_log_write(lctx, CAT, MOD, level,
2465 		      "%s:%u: %s",
2466 		      obj->file == NULL ? "<unknown file>" : obj->file,
2467 		      obj->line, msgbuf);
2468 	va_end(ap);
2469 }
2470 
2471 const char *
cfg_obj_file(const cfg_obj_t * obj)2472 cfg_obj_file(const cfg_obj_t *obj) {
2473 	return (obj->file);
2474 }
2475 
2476 unsigned int
cfg_obj_line(const cfg_obj_t * obj)2477 cfg_obj_line(const cfg_obj_t *obj) {
2478 	return (obj->line);
2479 }
2480 
2481 isc_result_t
cfg_create_obj(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)2482 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2483 	isc_result_t result;
2484 	cfg_obj_t *obj;
2485 
2486 	obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
2487 	if (obj == NULL)
2488 		return (ISC_R_NOMEMORY);
2489 	obj->type = type;
2490 	obj->file = current_file(pctx);
2491 	obj->line = pctx->line;
2492 	result = isc_refcount_init(&obj->references, 1);
2493 	if (result != ISC_R_SUCCESS) {
2494 		isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
2495 		return (result);
2496 	}
2497 	*ret = obj;
2498 	return (ISC_R_SUCCESS);
2499 }
2500 
2501 
2502 static void
map_symtabitem_destroy(char * key,unsigned int type,isc_symvalue_t symval,void * userarg)2503 map_symtabitem_destroy(char *key, unsigned int type,
2504 		       isc_symvalue_t symval, void *userarg)
2505 {
2506 	cfg_obj_t *obj = symval.as_pointer;
2507 	cfg_parser_t *pctx = (cfg_parser_t *)userarg;
2508 
2509 	UNUSED(key);
2510 	UNUSED(type);
2511 
2512 	cfg_obj_destroy(pctx, &obj);
2513 }
2514 
2515 
2516 static isc_result_t
create_map(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)2517 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2518 	isc_result_t result;
2519 	isc_symtab_t *symtab = NULL;
2520 	cfg_obj_t *obj = NULL;
2521 
2522 	CHECK(cfg_create_obj(pctx, type, &obj));
2523 	CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */
2524 				map_symtabitem_destroy,
2525 				pctx, ISC_FALSE, &symtab));
2526 	obj->value.map.symtab = symtab;
2527 	obj->value.map.id = NULL;
2528 
2529 	*ret = obj;
2530 	return (ISC_R_SUCCESS);
2531 
2532  cleanup:
2533 	if (obj != NULL)
2534 		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
2535 	return (result);
2536 }
2537 
2538 static void
free_map(cfg_parser_t * pctx,cfg_obj_t * obj)2539 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
2540 	CLEANUP_OBJ(obj->value.map.id);
2541 	isc_symtab_destroy(&obj->value.map.symtab);
2542 }
2543 
2544 isc_boolean_t
cfg_obj_istype(const cfg_obj_t * obj,const cfg_type_t * type)2545 cfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type) {
2546 	return (ISC_TF(obj->type == type));
2547 }
2548 
2549 /*
2550  * Destroy 'obj', a configuration object created in 'pctx'.
2551  */
2552 void
cfg_obj_destroy(cfg_parser_t * pctx,cfg_obj_t ** objp)2553 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
2554 	cfg_obj_t *obj;
2555 	unsigned int refs;
2556 
2557 	REQUIRE(objp != NULL && *objp != NULL);
2558 	REQUIRE(pctx != NULL);
2559 
2560 	obj = *objp;
2561 
2562 	isc_refcount_decrement(&obj->references, &refs);
2563 	if (refs == 0) {
2564 		obj->type->rep->free(pctx, obj);
2565 		isc_refcount_destroy(&obj->references);
2566 		isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
2567 	}
2568 	*objp = NULL;
2569 }
2570 
2571 void
cfg_obj_attach(cfg_obj_t * src,cfg_obj_t ** dest)2572 cfg_obj_attach(cfg_obj_t *src, cfg_obj_t **dest) {
2573     REQUIRE(src != NULL);
2574     REQUIRE(dest != NULL && *dest == NULL);
2575     isc_refcount_increment(&src->references, NULL);
2576     *dest = src;
2577 }
2578 
2579 static void
free_noop(cfg_parser_t * pctx,cfg_obj_t * obj)2580 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
2581 	UNUSED(pctx);
2582 	UNUSED(obj);
2583 }
2584 
2585 void
cfg_doc_obj(cfg_printer_t * pctx,const cfg_type_t * type)2586 cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
2587 	type->doc(pctx, type);
2588 }
2589 
2590 void
cfg_doc_terminal(cfg_printer_t * pctx,const cfg_type_t * type)2591 cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) {
2592 	cfg_print_chars(pctx, "<", 1);
2593 	cfg_print_cstr(pctx, type->name);
2594 	cfg_print_chars(pctx, ">", 1);
2595 }
2596 
2597 void
cfg_print_grammar(const cfg_type_t * type,void (* f)(void * closure,const char * text,int textlen),void * closure)2598 cfg_print_grammar(const cfg_type_t *type,
2599 	void (*f)(void *closure, const char *text, int textlen),
2600 	void *closure)
2601 {
2602 	cfg_printer_t pctx;
2603 	pctx.f = f;
2604 	pctx.closure = closure;
2605 	pctx.indent = 0;
2606 	pctx.flags = 0;
2607 	cfg_doc_obj(&pctx, type);
2608 }
2609