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