Remove building with NOCRYPTO option
[minix.git] / external / bsd / bind / dist / lib / isccfg / parser.c
blob1130d4e9875c378500ecab341d56cd34f55105ed
1 /* $NetBSD: parser.c,v 1.9 2015/07/08 17:29:00 christos Exp $ */
3 /*
4 * Copyright (C) 2004-2015 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 2000-2003 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
20 /* Id */
22 /*! \file */
24 #include <config.h>
26 #include <isc/buffer.h>
27 #include <isc/dir.h>
28 #include <isc/formatcheck.h>
29 #include <isc/lex.h>
30 #include <isc/log.h>
31 #include <isc/mem.h>
32 #include <isc/net.h>
33 #include <isc/netaddr.h>
34 #include <isc/netscope.h>
35 #include <isc/print.h>
36 #include <isc/string.h>
37 #include <isc/sockaddr.h>
38 #include <isc/symtab.h>
39 #include <isc/util.h>
41 #include <isccfg/cfg.h>
42 #include <isccfg/grammar.h>
43 #include <isccfg/log.h>
45 /* Shorthand */
46 #define CAT CFG_LOGCATEGORY_CONFIG
47 #define MOD CFG_LOGMODULE_PARSER
49 #define MAP_SYM 1 /* Unique type for isc_symtab */
51 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
53 /* Check a return value. */
54 #define CHECK(op) \
55 do { result = (op); \
56 if (result != ISC_R_SUCCESS) goto cleanup; \
57 } while (/*CONSTCOND*/0)
59 /* Clean up a configuration object if non-NULL. */
60 #define CLEANUP_OBJ(obj) \
61 do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (/*CONSTCOND*/0)
65 * Forward declarations of static functions.
68 static void
69 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
71 static isc_result_t
72 parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
74 static void
75 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
77 static void
78 free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
80 static isc_result_t
81 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
83 static isc_result_t
84 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
85 cfg_obj_t **ret);
87 static void
88 free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
90 static isc_result_t
91 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
93 static void
94 free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
96 static isc_result_t
97 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
98 cfg_type_t *elttype, isc_symtab_t *symtab,
99 isc_boolean_t callback);
101 static void
102 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
104 static isc_result_t
105 cfg_getstringtoken(cfg_parser_t *pctx);
107 static void
108 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
109 unsigned int flags, const char *format, va_list args);
112 * Data representations. These correspond to members of the
113 * "value" union in struct cfg_obj (except "void", which does
114 * not need a union member).
117 cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
118 cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
119 cfg_rep_t cfg_rep_string = { "string", free_string };
120 cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
121 cfg_rep_t cfg_rep_map = { "map", free_map };
122 cfg_rep_t cfg_rep_list = { "list", free_list };
123 cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
124 cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
125 cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
126 cfg_rep_t cfg_rep_void = { "void", free_noop };
129 * Configuration type definitions.
133 * An implicit list. These are formed by clauses that occur multiple times.
135 static cfg_type_t cfg_type_implicitlist = {
136 "implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL };
138 /* Functions. */
140 void
141 cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
142 obj->type->print(pctx, obj);
145 void
146 cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
147 pctx->f(pctx->closure, text, len);
150 static void
151 print_open(cfg_printer_t *pctx) {
152 cfg_print_chars(pctx, "{\n", 2);
153 pctx->indent++;
156 static void
157 print_indent(cfg_printer_t *pctx) {
158 int indent = pctx->indent;
159 while (indent > 0) {
160 cfg_print_chars(pctx, "\t", 1);
161 indent--;
165 static void
166 print_close(cfg_printer_t *pctx) {
167 pctx->indent--;
168 print_indent(pctx);
169 cfg_print_chars(pctx, "}", 1);
172 isc_result_t
173 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
174 isc_result_t result;
175 INSIST(ret != NULL && *ret == NULL);
176 result = type->parse(pctx, type, ret);
177 if (result != ISC_R_SUCCESS)
178 return (result);
179 INSIST(*ret != NULL);
180 return (ISC_R_SUCCESS);
183 void
184 cfg_print(const cfg_obj_t *obj,
185 void (*f)(void *closure, const char *text, int textlen),
186 void *closure)
188 cfg_printx(obj, 0, f, closure);
191 void
192 cfg_printx(const cfg_obj_t *obj, unsigned int flags,
193 void (*f)(void *closure, const char *text, int textlen),
194 void *closure)
196 cfg_printer_t pctx;
197 pctx.f = f;
198 pctx.closure = closure;
199 pctx.indent = 0;
200 pctx.flags = flags;
201 obj->type->print(&pctx, obj);
204 /* Tuples. */
206 isc_result_t
207 cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
208 isc_result_t result;
209 const cfg_tuplefielddef_t *fields = type->of;
210 const cfg_tuplefielddef_t *f;
211 cfg_obj_t *obj = NULL;
212 unsigned int nfields = 0;
213 int i;
215 for (f = fields; f->name != NULL; f++)
216 nfields++;
218 CHECK(cfg_create_obj(pctx, type, &obj));
219 obj->value.tuple = isc_mem_get(pctx->mctx,
220 nfields * sizeof(cfg_obj_t *));
221 if (obj->value.tuple == NULL) {
222 result = ISC_R_NOMEMORY;
223 goto cleanup;
225 for (f = fields, i = 0; f->name != NULL; f++, i++)
226 obj->value.tuple[i] = NULL;
227 *ret = obj;
228 return (ISC_R_SUCCESS);
230 cleanup:
231 if (obj != NULL)
232 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
233 return (result);
236 isc_result_t
237 cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
239 isc_result_t result;
240 const cfg_tuplefielddef_t *fields = type->of;
241 const cfg_tuplefielddef_t *f;
242 cfg_obj_t *obj = NULL;
243 unsigned int i;
245 CHECK(cfg_create_tuple(pctx, type, &obj));
246 for (f = fields, i = 0; f->name != NULL; f++, i++)
247 CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
249 *ret = obj;
250 return (ISC_R_SUCCESS);
252 cleanup:
253 CLEANUP_OBJ(obj);
254 return (result);
257 void
258 cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
259 unsigned int i;
260 const cfg_tuplefielddef_t *fields = obj->type->of;
261 const cfg_tuplefielddef_t *f;
262 isc_boolean_t need_space = ISC_FALSE;
264 for (f = fields, i = 0; f->name != NULL; f++, i++) {
265 const cfg_obj_t *fieldobj = obj->value.tuple[i];
266 if (need_space)
267 cfg_print_chars(pctx, " ", 1);
268 cfg_print_obj(pctx, fieldobj);
269 need_space = ISC_TF(fieldobj->type->print != cfg_print_void);
273 void
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;
279 for (f = fields; f->name != NULL; f++) {
280 if (need_space)
281 cfg_print_chars(pctx, " ", 1);
282 cfg_doc_obj(pctx, f->type);
283 need_space = ISC_TF(f->type->print != cfg_print_void);
287 static void
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;
294 if (obj->value.tuple == NULL)
295 return;
297 for (f = fields, i = 0; f->name != NULL; f++, i++) {
298 CLEANUP_OBJ(obj->value.tuple[i]);
299 nfields++;
301 isc_mem_put(pctx->mctx, obj->value.tuple,
302 nfields * sizeof(cfg_obj_t *));
305 isc_boolean_t
306 cfg_obj_istuple(const cfg_obj_t *obj) {
307 REQUIRE(obj != NULL);
308 return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
311 const cfg_obj_t *
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;
317 REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
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]);
324 INSIST(0);
325 return (NULL);
328 isc_result_t
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);
336 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
337 return (ISC_R_UNEXPECTEDTOKEN);
338 cleanup:
339 return (result);
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.
349 static isc_result_t
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);
357 cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
358 cfg_ungettoken(pctx);
359 cleanup:
360 return (result);
364 * Parse EOF, logging and returning an error if not there.
366 static isc_result_t
367 parse_eof(cfg_parser_t *pctx) {
368 isc_result_t result;
369 CHECK(cfg_gettoken(pctx, 0));
371 if (pctx->token.type == isc_tokentype_eof)
372 return (ISC_R_SUCCESS);
374 cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
375 return (ISC_R_UNEXPECTEDTOKEN);
376 cleanup:
377 return (result);
380 /* A list of files, used internally for pctx->files. */
382 static cfg_type_t cfg_type_filelist = {
383 "filelist", NULL, print_list, NULL, &cfg_rep_list,
384 &cfg_type_qstring
387 isc_result_t
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;
393 REQUIRE(mctx != NULL);
394 REQUIRE(ret != NULL && *ret == NULL);
396 pctx = isc_mem_get(mctx, sizeof(*pctx));
397 if (pctx == NULL)
398 return (ISC_R_NOMEMORY);
400 pctx->mctx = NULL;
401 isc_mem_attach(mctx, &pctx->mctx);
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);
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;
423 memset(specials, 0, sizeof(specials));
424 specials['{'] = 1;
425 specials['}'] = 1;
426 specials[';'] = 1;
427 specials['/'] = 1;
428 specials['"'] = 1;
429 specials['!'] = 1;
431 CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer));
433 isc_lex_setspecials(pctx->lexer, specials);
434 isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C |
435 ISC_LEXCOMMENT_CPLUSPLUS |
436 ISC_LEXCOMMENT_SHELL));
438 CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
439 CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
441 *ret = pctx;
442 return (ISC_R_SUCCESS);
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);
453 static isc_result_t
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;
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;
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);
471 return (ISC_R_SUCCESS);
472 cleanup:
473 CLEANUP_OBJ(stringobj);
474 return (result);
477 void
478 cfg_parser_setcallback(cfg_parser_t *pctx,
479 cfg_parsecallback_t callback,
480 void *arg)
482 pctx->callback = callback;
483 pctx->callbackarg = arg;
487 * Parse a configuration using a pctx where a lexer has already
488 * been set up with a source.
490 static isc_result_t
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;
495 result = cfg_parse_obj(pctx, type, &obj);
497 if (pctx->errors != 0) {
498 /* Errors have been logged. */
499 if (result == ISC_R_SUCCESS)
500 result = ISC_R_FAILURE;
501 goto cleanup;
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;
510 CHECK(parse_eof(pctx));
512 *ret = obj;
513 return (ISC_R_SUCCESS);
515 cleanup:
516 CLEANUP_OBJ(obj);
517 return (result);
520 isc_result_t
521 cfg_parse_file(cfg_parser_t *pctx, const char *filename,
522 const cfg_type_t *type, cfg_obj_t **ret)
524 isc_result_t result;
526 REQUIRE(filename != NULL);
528 CHECK(parser_openfile(pctx, filename));
529 CHECK(parse2(pctx, type, ret));
530 cleanup:
531 return (result);
535 isc_result_t
536 cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
537 const cfg_type_t *type, cfg_obj_t **ret)
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);
547 void
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;
555 void
556 cfg_parser_destroy(cfg_parser_t **pctxp) {
557 cfg_parser_t *pctx = *pctxp;
558 unsigned int refs;
560 isc_refcount_decrement(&pctx->references, &refs);
561 if (refs == 0) {
562 isc_lex_destroy(&pctx->lexer);
564 * Cleaning up open_files does not
565 * close the files; that was already done
566 * by closing the lexer.
568 CLEANUP_OBJ(pctx->open_files);
569 CLEANUP_OBJ(pctx->closed_files);
570 isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
572 *pctxp = NULL;
576 * void
578 isc_result_t
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));
584 void
585 cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
586 UNUSED(pctx);
587 UNUSED(obj);
590 void
591 cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
592 UNUSED(pctx);
593 UNUSED(type);
596 isc_boolean_t
597 cfg_obj_isvoid(const cfg_obj_t *obj) {
598 REQUIRE(obj != NULL);
599 return (ISC_TF(obj->type->rep == &cfg_rep_void));
602 cfg_type_t cfg_type_void = {
603 "void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
604 NULL };
608 * uint32
610 isc_result_t
611 cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
612 isc_result_t result;
613 cfg_obj_t *obj = NULL;
614 UNUSED(type);
616 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
617 if (pctx->token.type != isc_tokentype_number) {
618 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
619 return (ISC_R_UNEXPECTEDTOKEN);
622 CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
624 obj->value.uint32 = pctx->token.value.as_ulong;
625 *ret = obj;
626 cleanup:
627 return (result);
630 void
631 cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
632 cfg_print_chars(pctx, s, strlen(s));
635 void
636 cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
637 char buf[32];
638 snprintf(buf, sizeof(buf), "%u", u);
639 cfg_print_cstr(pctx, buf);
642 void
643 cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
644 cfg_print_rawuint(pctx, obj->value.uint32);
647 isc_boolean_t
648 cfg_obj_isuint32(const cfg_obj_t *obj) {
649 REQUIRE(obj != NULL);
650 return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
653 isc_uint32_t
654 cfg_obj_asuint32(const cfg_obj_t *obj) {
655 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
656 return (obj->value.uint32);
659 cfg_type_t cfg_type_uint32 = {
660 "integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
661 &cfg_rep_uint32, NULL
666 * uint64
668 isc_boolean_t
669 cfg_obj_isuint64(const cfg_obj_t *obj) {
670 REQUIRE(obj != NULL);
671 return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
674 isc_uint64_t
675 cfg_obj_asuint64(const cfg_obj_t *obj) {
676 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
677 return (obj->value.uint64);
680 void
681 cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
682 char buf[32];
683 snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
684 obj->value.uint64);
685 cfg_print_cstr(pctx, buf);
688 cfg_type_t cfg_type_uint64 = {
689 "64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
690 &cfg_rep_uint64, NULL
694 * qstring (quoted string), ustring (unquoted string), astring
695 * (any string)
698 /* Create a string object from a null-terminated C string. */
699 static isc_result_t
700 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
701 cfg_obj_t **ret)
703 isc_result_t result;
704 cfg_obj_t *obj = NULL;
705 int len;
707 CHECK(cfg_create_obj(pctx, type, &obj));
708 len = strlen(contents);
709 obj->value.string.length = len;
710 obj->value.string.base = isc_mem_get(pctx->mctx, len + 1);
711 if (obj->value.string.base == 0) {
712 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
713 return (ISC_R_NOMEMORY);
715 memmove(obj->value.string.base, contents, len);
716 obj->value.string.base[len] = '\0';
718 *ret = obj;
719 cleanup:
720 return (result);
723 isc_result_t
724 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
725 isc_result_t result;
726 UNUSED(type);
728 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
729 if (pctx->token.type != isc_tokentype_qstring) {
730 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
731 return (ISC_R_UNEXPECTEDTOKEN);
733 return (create_string(pctx,
734 TOKEN_STRING(pctx),
735 &cfg_type_qstring,
736 ret));
737 cleanup:
738 return (result);
741 static isc_result_t
742 parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
743 isc_result_t result;
744 UNUSED(type);
746 CHECK(cfg_gettoken(pctx, 0));
747 if (pctx->token.type != isc_tokentype_string) {
748 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
749 return (ISC_R_UNEXPECTEDTOKEN);
751 return (create_string(pctx,
752 TOKEN_STRING(pctx),
753 &cfg_type_ustring,
754 ret));
755 cleanup:
756 return (result);
759 isc_result_t
760 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
761 cfg_obj_t **ret)
763 isc_result_t result;
764 UNUSED(type);
766 CHECK(cfg_getstringtoken(pctx));
767 return (create_string(pctx,
768 TOKEN_STRING(pctx),
769 &cfg_type_qstring,
770 ret));
771 cleanup:
772 return (result);
775 isc_result_t
776 cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type,
777 cfg_obj_t **ret)
779 isc_result_t result;
780 UNUSED(type);
782 CHECK(cfg_getstringtoken(pctx));
783 return (create_string(pctx,
784 TOKEN_STRING(pctx),
785 &cfg_type_sstring,
786 ret));
787 cleanup:
788 return (result);
791 isc_boolean_t
792 cfg_is_enum(const char *s, const char *const *enums) {
793 const char * const *p;
794 for (p = enums; *p != NULL; p++) {
795 if (strcasecmp(*p, s) == 0)
796 return (ISC_TRUE);
798 return (ISC_FALSE);
801 static isc_result_t
802 check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
803 const char *s = obj->value.string.base;
804 if (cfg_is_enum(s, enums))
805 return (ISC_R_SUCCESS);
806 cfg_parser_error(pctx, 0, "'%s' unexpected", s);
807 return (ISC_R_UNEXPECTEDTOKEN);
810 isc_result_t
811 cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
812 isc_result_t result;
813 cfg_obj_t *obj = NULL;
814 CHECK(parse_ustring(pctx, NULL, &obj));
815 CHECK(check_enum(pctx, obj, type->of));
816 *ret = obj;
817 return (ISC_R_SUCCESS);
818 cleanup:
819 CLEANUP_OBJ(obj);
820 return (result);
823 void
824 cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
825 const char * const *p;
826 cfg_print_chars(pctx, "( ", 2);
827 for (p = type->of; *p != NULL; p++) {
828 cfg_print_cstr(pctx, *p);
829 if (p[1] != NULL)
830 cfg_print_chars(pctx, " | ", 3);
832 cfg_print_chars(pctx, " )", 2);
835 void
836 cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
837 cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
840 static void
841 print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
842 cfg_print_chars(pctx, "\"", 1);
843 cfg_print_ustring(pctx, obj);
844 cfg_print_chars(pctx, "\"", 1);
847 static void
848 print_sstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
849 cfg_print_chars(pctx, "\"", 1);
850 if ((pctx->flags & CFG_PRINTER_XKEY) != 0) {
851 unsigned int len = obj->value.string.length;
852 while (len-- > 0)
853 cfg_print_chars(pctx, "?", 1);
854 } else
855 cfg_print_ustring(pctx, obj);
856 cfg_print_chars(pctx, "\"", 1);
859 static void
860 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
861 isc_mem_put(pctx->mctx, obj->value.string.base,
862 obj->value.string.length + 1);
865 isc_boolean_t
866 cfg_obj_isstring(const cfg_obj_t *obj) {
867 REQUIRE(obj != NULL);
868 return (ISC_TF(obj->type->rep == &cfg_rep_string));
871 const char *
872 cfg_obj_asstring(const cfg_obj_t *obj) {
873 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
874 return (obj->value.string.base);
877 /* Quoted string only */
878 cfg_type_t cfg_type_qstring = {
879 "quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal,
880 &cfg_rep_string, NULL
883 /* Unquoted string only */
884 cfg_type_t cfg_type_ustring = {
885 "string", parse_ustring, cfg_print_ustring, cfg_doc_terminal,
886 &cfg_rep_string, NULL
889 /* Any string (quoted or unquoted); printed with quotes */
890 cfg_type_t cfg_type_astring = {
891 "string", cfg_parse_astring, print_qstring, cfg_doc_terminal,
892 &cfg_rep_string, NULL
896 * Any string (quoted or unquoted); printed with quotes.
897 * If CFG_PRINTER_XKEY is set when printing the string will be '?' out.
899 cfg_type_t cfg_type_sstring = {
900 "string", cfg_parse_sstring, print_sstring, cfg_doc_terminal,
901 &cfg_rep_string, NULL
905 * Booleans
908 isc_boolean_t
909 cfg_obj_isboolean(const cfg_obj_t *obj) {
910 REQUIRE(obj != NULL);
911 return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
914 isc_boolean_t
915 cfg_obj_asboolean(const cfg_obj_t *obj) {
916 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
917 return (obj->value.boolean);
920 isc_result_t
921 cfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
923 isc_result_t result;
924 isc_boolean_t value;
925 cfg_obj_t *obj = NULL;
926 UNUSED(type);
928 result = cfg_gettoken(pctx, 0);
929 if (result != ISC_R_SUCCESS)
930 return (result);
932 if (pctx->token.type != isc_tokentype_string)
933 goto bad_boolean;
935 if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
936 (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
937 (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
938 value = ISC_TRUE;
939 } else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
940 (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
941 (strcmp(TOKEN_STRING(pctx), "0") == 0)) {
942 value = ISC_FALSE;
943 } else {
944 goto bad_boolean;
947 CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
948 obj->value.boolean = value;
949 *ret = obj;
950 return (result);
952 bad_boolean:
953 cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
954 return (ISC_R_UNEXPECTEDTOKEN);
956 cleanup:
957 return (result);
960 void
961 cfg_print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
962 if (obj->value.boolean)
963 cfg_print_chars(pctx, "yes", 3);
964 else
965 cfg_print_chars(pctx, "no", 2);
968 cfg_type_t cfg_type_boolean = {
969 "boolean", cfg_parse_boolean, cfg_print_boolean, cfg_doc_terminal,
970 &cfg_rep_boolean, NULL
974 * Lists.
977 isc_result_t
978 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
979 isc_result_t result;
980 CHECK(cfg_create_obj(pctx, type, obj));
981 ISC_LIST_INIT((*obj)->value.list);
982 cleanup:
983 return (result);
986 static isc_result_t
987 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
988 cfg_listelt_t *elt;
989 elt = isc_mem_get(pctx->mctx, sizeof(*elt));
990 if (elt == NULL)
991 return (ISC_R_NOMEMORY);
992 elt->obj = NULL;
993 ISC_LINK_INIT(elt, link);
994 *eltp = elt;
995 return (ISC_R_SUCCESS);
998 static void
999 free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
1000 cfg_obj_destroy(pctx, &elt->obj);
1001 isc_mem_put(pctx->mctx, elt, sizeof(*elt));
1004 static void
1005 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
1006 cfg_listelt_t *elt, *next;
1007 for (elt = ISC_LIST_HEAD(obj->value.list);
1008 elt != NULL;
1009 elt = next)
1011 next = ISC_LIST_NEXT(elt, link);
1012 free_list_elt(pctx, elt);
1016 isc_result_t
1017 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
1018 cfg_listelt_t **ret)
1020 isc_result_t result;
1021 cfg_listelt_t *elt = NULL;
1022 cfg_obj_t *value = NULL;
1024 CHECK(create_listelt(pctx, &elt));
1026 result = cfg_parse_obj(pctx, elttype, &value);
1027 if (result != ISC_R_SUCCESS)
1028 goto cleanup;
1030 elt->obj = value;
1032 *ret = elt;
1033 return (ISC_R_SUCCESS);
1035 cleanup:
1036 isc_mem_put(pctx->mctx, elt, sizeof(*elt));
1037 return (result);
1041 * Parse a homogeneous list whose elements are of type 'elttype'
1042 * and where each element is terminated by a semicolon.
1044 static isc_result_t
1045 parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
1047 cfg_obj_t *listobj = NULL;
1048 const cfg_type_t *listof = listtype->of;
1049 isc_result_t result;
1050 cfg_listelt_t *elt = NULL;
1052 CHECK(cfg_create_list(pctx, listtype, &listobj));
1054 for (;;) {
1055 CHECK(cfg_peektoken(pctx, 0));
1056 if (pctx->token.type == isc_tokentype_special &&
1057 pctx->token.value.as_char == /*{*/ '}')
1058 break;
1059 CHECK(cfg_parse_listelt(pctx, listof, &elt));
1060 CHECK(parse_semicolon(pctx));
1061 ISC_LIST_APPEND(listobj->value.list, elt, link);
1062 elt = NULL;
1064 *ret = listobj;
1065 return (ISC_R_SUCCESS);
1067 cleanup:
1068 if (elt != NULL)
1069 free_list_elt(pctx, elt);
1070 CLEANUP_OBJ(listobj);
1071 return (result);
1074 static void
1075 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1076 const cfg_list_t *list = &obj->value.list;
1077 const cfg_listelt_t *elt;
1079 for (elt = ISC_LIST_HEAD(*list);
1080 elt != NULL;
1081 elt = ISC_LIST_NEXT(elt, link)) {
1082 print_indent(pctx);
1083 cfg_print_obj(pctx, elt->obj);
1084 cfg_print_chars(pctx, ";\n", 2);
1088 isc_result_t
1089 cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
1090 cfg_obj_t **ret)
1092 isc_result_t result;
1093 CHECK(cfg_parse_special(pctx, '{'));
1094 CHECK(parse_list(pctx, type, ret));
1095 CHECK(cfg_parse_special(pctx, '}'));
1096 cleanup:
1097 return (result);
1100 void
1101 cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1102 print_open(pctx);
1103 print_list(pctx, obj);
1104 print_close(pctx);
1107 void
1108 cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
1109 cfg_print_chars(pctx, "{ ", 2);
1110 cfg_doc_obj(pctx, type->of);
1111 cfg_print_chars(pctx, "; ... }", 7);
1115 * Parse a homogeneous list whose elements are of type 'elttype'
1116 * and where elements are separated by space. The list ends
1117 * before the first semicolon.
1119 isc_result_t
1120 cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
1121 cfg_obj_t **ret)
1123 cfg_obj_t *listobj = NULL;
1124 const cfg_type_t *listof = listtype->of;
1125 isc_result_t result;
1127 CHECK(cfg_create_list(pctx, listtype, &listobj));
1129 for (;;) {
1130 cfg_listelt_t *elt = NULL;
1132 CHECK(cfg_peektoken(pctx, 0));
1133 if (pctx->token.type == isc_tokentype_special &&
1134 pctx->token.value.as_char == ';')
1135 break;
1136 CHECK(cfg_parse_listelt(pctx, listof, &elt));
1137 ISC_LIST_APPEND(listobj->value.list, elt, link);
1139 *ret = listobj;
1140 return (ISC_R_SUCCESS);
1142 cleanup:
1143 CLEANUP_OBJ(listobj);
1144 return (result);
1147 void
1148 cfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1149 const cfg_list_t *list = &obj->value.list;
1150 const cfg_listelt_t *elt;
1152 for (elt = ISC_LIST_HEAD(*list);
1153 elt != NULL;
1154 elt = ISC_LIST_NEXT(elt, link)) {
1155 cfg_print_obj(pctx, elt->obj);
1156 if (ISC_LIST_NEXT(elt, link) != NULL)
1157 cfg_print_chars(pctx, " ", 1);
1161 isc_boolean_t
1162 cfg_obj_islist(const cfg_obj_t *obj) {
1163 REQUIRE(obj != NULL);
1164 return (ISC_TF(obj->type->rep == &cfg_rep_list));
1167 const cfg_listelt_t *
1168 cfg_list_first(const cfg_obj_t *obj) {
1169 REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
1170 if (obj == NULL)
1171 return (NULL);
1172 return (ISC_LIST_HEAD(obj->value.list));
1175 const cfg_listelt_t *
1176 cfg_list_next(const cfg_listelt_t *elt) {
1177 REQUIRE(elt != NULL);
1178 return (ISC_LIST_NEXT(elt, link));
1182 * Return the length of a list object. If obj is NULL or is not
1183 * a list, return 0.
1185 unsigned int
1186 cfg_list_length(const cfg_obj_t *obj, isc_boolean_t recurse) {
1187 const cfg_listelt_t *elt;
1188 unsigned int count = 0;
1190 if (obj == NULL || !cfg_obj_islist(obj))
1191 return (0U);
1192 for (elt = cfg_list_first(obj);
1193 elt != NULL;
1194 elt = cfg_list_next(elt)) {
1195 if (recurse && cfg_obj_islist(elt->obj)) {
1196 count += cfg_list_length(elt->obj, recurse);
1197 } else {
1198 count++;
1201 return (count);
1204 cfg_obj_t *
1205 cfg_listelt_value(const cfg_listelt_t *elt) {
1206 REQUIRE(elt != NULL);
1207 return (elt->obj);
1211 * Maps.
1215 * Parse a map body. That's something like
1217 * "foo 1; bar { glub; }; zap true; zap false;"
1219 * i.e., a sequence of option names followed by values and
1220 * terminated by semicolons. Used for the top level of
1221 * the named.conf syntax, as well as for the body of the
1222 * options, view, zone, and other statements.
1224 isc_result_t
1225 cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1227 const cfg_clausedef_t * const *clausesets = type->of;
1228 isc_result_t result;
1229 const cfg_clausedef_t * const *clauseset;
1230 const cfg_clausedef_t *clause;
1231 cfg_obj_t *value = NULL;
1232 cfg_obj_t *obj = NULL;
1233 cfg_obj_t *eltobj = NULL;
1234 cfg_obj_t *includename = NULL;
1235 isc_symvalue_t symval;
1236 cfg_list_t *list = NULL;
1238 CHECK(create_map(pctx, type, &obj));
1240 obj->value.map.clausesets = clausesets;
1242 for (;;) {
1243 cfg_listelt_t *elt;
1245 redo:
1247 * Parse the option name and see if it is known.
1249 CHECK(cfg_gettoken(pctx, 0));
1251 if (pctx->token.type != isc_tokentype_string) {
1252 cfg_ungettoken(pctx);
1253 break;
1257 * We accept "include" statements wherever a map body
1258 * clause can occur.
1260 if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
1262 * Turn the file name into a temporary configuration
1263 * object just so that it is not overwritten by the
1264 * semicolon token.
1266 CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
1267 CHECK(parse_semicolon(pctx));
1268 CHECK(parser_openfile(pctx, includename->
1269 value.string.base));
1270 cfg_obj_destroy(pctx, &includename);
1271 goto redo;
1274 clause = NULL;
1275 for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
1276 for (clause = *clauseset;
1277 clause->name != NULL;
1278 clause++) {
1279 if (strcasecmp(TOKEN_STRING(pctx),
1280 clause->name) == 0)
1281 goto done;
1284 done:
1285 if (clause == NULL || clause->name == NULL) {
1286 cfg_parser_error(pctx, CFG_LOG_NOPREP, "unknown option");
1288 * Try to recover by parsing this option as an unknown
1289 * option and discarding it.
1291 CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, &eltobj));
1292 cfg_obj_destroy(pctx, &eltobj);
1293 CHECK(parse_semicolon(pctx));
1294 continue;
1297 /* Clause is known. */
1299 /* Issue warnings if appropriate */
1300 if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0)
1301 cfg_parser_warning(pctx, 0, "option '%s' is obsolete",
1302 clause->name);
1303 if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0)
1304 cfg_parser_warning(pctx, 0, "option '%s' is "
1305 "not implemented", clause->name);
1306 if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0)
1307 cfg_parser_warning(pctx, 0, "option '%s' is "
1308 "not implemented", clause->name);
1310 if ((clause->flags & CFG_CLAUSEFLAG_NOTCONFIGURED) != 0) {
1311 cfg_parser_warning(pctx, 0, "option '%s' was not "
1312 "enabled at compile time",
1313 clause->name);
1314 result = ISC_R_FAILURE;
1315 goto cleanup;
1319 * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
1320 * set here - we need to log the *lack* of such an option,
1321 * not its presence.
1324 /* See if the clause already has a value; if not create one. */
1325 result = isc_symtab_lookup(obj->value.map.symtab,
1326 clause->name, 0, &symval);
1328 if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
1329 /* Multivalued clause */
1330 cfg_obj_t *listobj = NULL;
1331 if (result == ISC_R_NOTFOUND) {
1332 CHECK(cfg_create_list(pctx,
1333 &cfg_type_implicitlist,
1334 &listobj));
1335 symval.as_pointer = listobj;
1336 result = isc_symtab_define(obj->value.
1337 map.symtab,
1338 clause->name,
1339 1, symval,
1340 isc_symexists_reject);
1341 if (result != ISC_R_SUCCESS) {
1342 cfg_parser_error(pctx, CFG_LOG_NEAR,
1343 "isc_symtab_define(%s) "
1344 "failed", clause->name);
1345 isc_mem_put(pctx->mctx, list,
1346 sizeof(cfg_list_t));
1347 goto cleanup;
1349 } else {
1350 INSIST(result == ISC_R_SUCCESS);
1351 listobj = symval.as_pointer;
1354 elt = NULL;
1355 CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
1356 CHECK(parse_semicolon(pctx));
1358 ISC_LIST_APPEND(listobj->value.list, elt, link);
1359 } else {
1360 /* Single-valued clause */
1361 if (result == ISC_R_NOTFOUND) {
1362 isc_boolean_t callback =
1363 ISC_TF((clause->flags &
1364 CFG_CLAUSEFLAG_CALLBACK) != 0);
1365 CHECK(parse_symtab_elt(pctx, clause->name,
1366 clause->type,
1367 obj->value.map.symtab,
1368 callback));
1369 CHECK(parse_semicolon(pctx));
1370 } else if (result == ISC_R_SUCCESS) {
1371 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
1372 clause->name);
1373 result = ISC_R_EXISTS;
1374 goto cleanup;
1375 } else {
1376 cfg_parser_error(pctx, CFG_LOG_NEAR,
1377 "isc_symtab_define() failed");
1378 goto cleanup;
1384 *ret = obj;
1385 return (ISC_R_SUCCESS);
1387 cleanup:
1388 CLEANUP_OBJ(value);
1389 CLEANUP_OBJ(obj);
1390 CLEANUP_OBJ(eltobj);
1391 CLEANUP_OBJ(includename);
1392 return (result);
1395 static isc_result_t
1396 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
1397 cfg_type_t *elttype, isc_symtab_t *symtab,
1398 isc_boolean_t callback)
1400 isc_result_t result;
1401 cfg_obj_t *obj = NULL;
1402 isc_symvalue_t symval;
1404 CHECK(cfg_parse_obj(pctx, elttype, &obj));
1406 if (callback && pctx->callback != NULL)
1407 CHECK(pctx->callback(name, obj, pctx->callbackarg));
1409 symval.as_pointer = obj;
1410 CHECK(isc_symtab_define(symtab, name,
1411 1, symval,
1412 isc_symexists_reject));
1413 return (ISC_R_SUCCESS);
1415 cleanup:
1416 CLEANUP_OBJ(obj);
1417 return (result);
1421 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
1423 isc_result_t
1424 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1425 isc_result_t result;
1426 CHECK(cfg_parse_special(pctx, '{'));
1427 CHECK(cfg_parse_mapbody(pctx, type, ret));
1428 CHECK(cfg_parse_special(pctx, '}'));
1429 cleanup:
1430 return (result);
1434 * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
1436 static isc_result_t
1437 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type,
1438 cfg_obj_t **ret)
1440 isc_result_t result;
1441 cfg_obj_t *idobj = NULL;
1442 cfg_obj_t *mapobj = NULL;
1444 CHECK(cfg_parse_obj(pctx, nametype, &idobj));
1445 CHECK(cfg_parse_map(pctx, type, &mapobj));
1446 mapobj->value.map.id = idobj;
1447 idobj = NULL;
1448 *ret = mapobj;
1449 cleanup:
1450 CLEANUP_OBJ(idobj);
1451 return (result);
1455 * Parse a map identified by a string name. E.g., "name { foo 1; }".
1456 * Used for the "key" and "channel" statements.
1458 isc_result_t
1459 cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1460 return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
1464 * Parse a map identified by a network address.
1465 * Used to be used for the "server" statement.
1467 isc_result_t
1468 cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1469 return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
1473 * Parse a map identified by a network prefix.
1474 * Used for the "server" statement.
1476 isc_result_t
1477 cfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1478 return (parse_any_named_map(pctx, &cfg_type_netprefix, type, ret));
1481 void
1482 cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1483 isc_result_t result = ISC_R_SUCCESS;
1485 const cfg_clausedef_t * const *clauseset;
1487 for (clauseset = obj->value.map.clausesets;
1488 *clauseset != NULL;
1489 clauseset++)
1491 isc_symvalue_t symval;
1492 const cfg_clausedef_t *clause;
1494 for (clause = *clauseset;
1495 clause->name != NULL;
1496 clause++) {
1497 result = isc_symtab_lookup(obj->value.map.symtab,
1498 clause->name, 0, &symval);
1499 if (result == ISC_R_SUCCESS) {
1500 cfg_obj_t *symobj = symval.as_pointer;
1501 if (symobj->type == &cfg_type_implicitlist) {
1502 /* Multivalued. */
1503 cfg_list_t *list = &symobj->value.list;
1504 cfg_listelt_t *elt;
1505 for (elt = ISC_LIST_HEAD(*list);
1506 elt != NULL;
1507 elt = ISC_LIST_NEXT(elt, link)) {
1508 print_indent(pctx);
1509 cfg_print_cstr(pctx, clause->name);
1510 cfg_print_chars(pctx, " ", 1);
1511 cfg_print_obj(pctx, elt->obj);
1512 cfg_print_chars(pctx, ";\n", 2);
1514 } else {
1515 /* Single-valued. */
1516 print_indent(pctx);
1517 cfg_print_cstr(pctx, clause->name);
1518 cfg_print_chars(pctx, " ", 1);
1519 cfg_print_obj(pctx, symobj);
1520 cfg_print_chars(pctx, ";\n", 2);
1522 } else if (result == ISC_R_NOTFOUND) {
1523 ; /* do nothing */
1524 } else {
1525 INSIST(0);
1531 void
1532 cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
1533 const cfg_clausedef_t * const *clauseset;
1534 const cfg_clausedef_t *clause;
1536 for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1537 for (clause = *clauseset;
1538 clause->name != NULL;
1539 clause++) {
1540 cfg_print_cstr(pctx, clause->name);
1541 cfg_print_chars(pctx, " ", 1);
1542 cfg_doc_obj(pctx, clause->type);
1543 cfg_print_chars(pctx, ";", 1);
1544 /* XXX print flags here? */
1545 cfg_print_chars(pctx, "\n\n", 2);
1550 static struct flagtext {
1551 unsigned int flag;
1552 const char *text;
1553 } flagtexts[] = {
1554 { CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
1555 { CFG_CLAUSEFLAG_NYI, "not yet implemented" },
1556 { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
1557 { CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
1558 { CFG_CLAUSEFLAG_TESTONLY, "test only" },
1559 { CFG_CLAUSEFLAG_NOTCONFIGURED, "not configured" },
1560 { 0, NULL }
1563 void
1564 cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1565 if (obj->value.map.id != NULL) {
1566 cfg_print_obj(pctx, obj->value.map.id);
1567 cfg_print_chars(pctx, " ", 1);
1569 print_open(pctx);
1570 cfg_print_mapbody(pctx, obj);
1571 print_close(pctx);
1574 static void
1575 print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
1576 struct flagtext *p;
1577 isc_boolean_t first = ISC_TRUE;
1578 for (p = flagtexts; p->flag != 0; p++) {
1579 if ((flags & p->flag) != 0) {
1580 if (first)
1581 cfg_print_chars(pctx, " // ", 4);
1582 else
1583 cfg_print_chars(pctx, ", ", 2);
1584 cfg_print_cstr(pctx, p->text);
1585 first = ISC_FALSE;
1590 void
1591 cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
1592 const cfg_clausedef_t * const *clauseset;
1593 const cfg_clausedef_t *clause;
1595 if (type->parse == cfg_parse_named_map) {
1596 cfg_doc_obj(pctx, &cfg_type_astring);
1597 cfg_print_chars(pctx, " ", 1);
1598 } else if (type->parse == cfg_parse_addressed_map) {
1599 cfg_doc_obj(pctx, &cfg_type_netaddr);
1600 cfg_print_chars(pctx, " ", 1);
1601 } else if (type->parse == cfg_parse_netprefix_map) {
1602 cfg_doc_obj(pctx, &cfg_type_netprefix);
1603 cfg_print_chars(pctx, " ", 1);
1606 print_open(pctx);
1608 for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1609 for (clause = *clauseset;
1610 clause->name != NULL;
1611 clause++) {
1612 print_indent(pctx);
1613 cfg_print_cstr(pctx, clause->name);
1614 if (clause->type->print != cfg_print_void)
1615 cfg_print_chars(pctx, " ", 1);
1616 cfg_doc_obj(pctx, clause->type);
1617 cfg_print_chars(pctx, ";", 1);
1618 print_clause_flags(pctx, clause->flags);
1619 cfg_print_chars(pctx, "\n", 1);
1622 print_close(pctx);
1625 isc_boolean_t
1626 cfg_obj_ismap(const cfg_obj_t *obj) {
1627 REQUIRE(obj != NULL);
1628 return (ISC_TF(obj->type->rep == &cfg_rep_map));
1631 isc_result_t
1632 cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
1633 isc_result_t result;
1634 isc_symvalue_t val;
1635 const cfg_map_t *map;
1637 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1638 REQUIRE(name != NULL);
1639 REQUIRE(obj != NULL && *obj == NULL);
1641 map = &mapobj->value.map;
1643 result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
1644 if (result != ISC_R_SUCCESS)
1645 return (result);
1646 *obj = val.as_pointer;
1647 return (ISC_R_SUCCESS);
1650 const cfg_obj_t *
1651 cfg_map_getname(const cfg_obj_t *mapobj) {
1652 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1653 return (mapobj->value.map.id);
1656 unsigned int
1657 cfg_map_count(const cfg_obj_t *mapobj) {
1658 const cfg_map_t *map;
1659 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1660 map = &mapobj->value.map;
1661 return (isc_symtab_count(map->symtab));
1664 /* Parse an arbitrary token, storing its raw text representation. */
1665 static isc_result_t
1666 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1667 cfg_obj_t *obj = NULL;
1668 isc_result_t result;
1669 isc_region_t r;
1671 UNUSED(type);
1673 CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
1674 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
1675 if (pctx->token.type == isc_tokentype_eof) {
1676 cfg_ungettoken(pctx);
1677 result = ISC_R_EOF;
1678 goto cleanup;
1681 isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
1683 obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
1684 if (obj->value.string.base == NULL) {
1685 result = ISC_R_NOMEMORY;
1686 goto cleanup;
1688 obj->value.string.length = r.length;
1689 memmove(obj->value.string.base, r.base, r.length);
1690 obj->value.string.base[r.length] = '\0';
1691 *ret = obj;
1692 return (result);
1694 cleanup:
1695 if (obj != NULL)
1696 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
1697 return (result);
1700 cfg_type_t cfg_type_token = {
1701 "token", parse_token, cfg_print_ustring, cfg_doc_terminal,
1702 &cfg_rep_string, NULL
1706 * An unsupported option. This is just a list of tokens with balanced braces
1707 * ending in a semicolon.
1710 static isc_result_t
1711 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1712 cfg_obj_t *listobj = NULL;
1713 isc_result_t result;
1714 int braces = 0;
1716 CHECK(cfg_create_list(pctx, type, &listobj));
1718 for (;;) {
1719 cfg_listelt_t *elt = NULL;
1721 CHECK(cfg_peektoken(pctx, 0));
1722 if (pctx->token.type == isc_tokentype_special) {
1723 if (pctx->token.value.as_char == '{')
1724 braces++;
1725 else if (pctx->token.value.as_char == '}')
1726 braces--;
1727 else if (pctx->token.value.as_char == ';')
1728 if (braces == 0)
1729 break;
1731 if (pctx->token.type == isc_tokentype_eof || braces < 0) {
1732 cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
1733 result = ISC_R_UNEXPECTEDTOKEN;
1734 goto cleanup;
1737 CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
1738 ISC_LIST_APPEND(listobj->value.list, elt, link);
1740 INSIST(braces == 0);
1741 *ret = listobj;
1742 return (ISC_R_SUCCESS);
1744 cleanup:
1745 CLEANUP_OBJ(listobj);
1746 return (result);
1749 cfg_type_t cfg_type_unsupported = {
1750 "unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal,
1751 &cfg_rep_list, NULL
1755 * Try interpreting the current token as a network address.
1757 * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
1758 * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set. The
1759 * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
1760 * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
1761 * and the IPv6 wildcard address otherwise.
1763 static isc_result_t
1764 token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1765 char *s;
1766 struct in_addr in4a;
1767 struct in6_addr in6a;
1769 if (pctx->token.type != isc_tokentype_string)
1770 return (ISC_R_UNEXPECTEDTOKEN);
1772 s = TOKEN_STRING(pctx);
1773 if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
1774 if ((flags & CFG_ADDR_V4OK) != 0) {
1775 isc_netaddr_any(na);
1776 return (ISC_R_SUCCESS);
1777 } else if ((flags & CFG_ADDR_V6OK) != 0) {
1778 isc_netaddr_any6(na);
1779 return (ISC_R_SUCCESS);
1780 } else {
1781 INSIST(0);
1783 } else {
1784 if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
1785 if (inet_pton(AF_INET, s, &in4a) == 1) {
1786 isc_netaddr_fromin(na, &in4a);
1787 return (ISC_R_SUCCESS);
1790 if ((flags & CFG_ADDR_V4PREFIXOK) != 0 &&
1791 strlen(s) <= 15U) {
1792 char buf[64];
1793 int i;
1795 strcpy(buf, s);
1796 for (i = 0; i < 3; i++) {
1797 strcat(buf, ".0");
1798 if (inet_pton(AF_INET, buf, &in4a) == 1) {
1799 isc_netaddr_fromin(na, &in4a);
1800 return (ISC_R_SUCCESS);
1804 if ((flags & CFG_ADDR_V6OK) != 0 &&
1805 strlen(s) <= 127U) {
1806 char buf[128]; /* see lib/bind9/getaddresses.c */
1807 char *d; /* zone delimiter */
1808 isc_uint32_t zone = 0; /* scope zone ID */
1810 strcpy(buf, s);
1811 d = strchr(buf, '%');
1812 if (d != NULL)
1813 *d = '\0';
1815 if (inet_pton(AF_INET6, buf, &in6a) == 1) {
1816 if (d != NULL) {
1817 #ifdef ISC_PLATFORM_HAVESCOPEID
1818 isc_result_t result;
1820 result = isc_netscope_pton(AF_INET6,
1821 d + 1,
1822 &in6a,
1823 &zone);
1824 if (result != ISC_R_SUCCESS)
1825 return (result);
1826 #else
1827 return (ISC_R_BADADDRESSFORM);
1828 #endif
1831 isc_netaddr_fromin6(na, &in6a);
1832 isc_netaddr_setzone(na, zone);
1833 return (ISC_R_SUCCESS);
1837 return (ISC_R_UNEXPECTEDTOKEN);
1840 isc_result_t
1841 cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1842 isc_result_t result;
1843 const char *wild = "";
1844 const char *prefix = "";
1846 CHECK(cfg_gettoken(pctx, 0));
1847 result = token_addr(pctx, flags, na);
1848 if (result == ISC_R_UNEXPECTEDTOKEN) {
1849 if ((flags & CFG_ADDR_WILDOK) != 0)
1850 wild = " or '*'";
1851 if ((flags & CFG_ADDR_V4PREFIXOK) != 0)
1852 wild = " or IPv4 prefix";
1853 if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V4OK)
1854 cfg_parser_error(pctx, CFG_LOG_NEAR,
1855 "expected IPv4 address%s%s",
1856 prefix, wild);
1857 else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK)
1858 cfg_parser_error(pctx, CFG_LOG_NEAR,
1859 "expected IPv6 address%s%s",
1860 prefix, wild);
1861 else
1862 cfg_parser_error(pctx, CFG_LOG_NEAR,
1863 "expected IP address%s%s",
1864 prefix, wild);
1866 cleanup:
1867 return (result);
1870 isc_boolean_t
1871 cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
1872 isc_result_t result;
1873 isc_netaddr_t na_dummy;
1874 result = token_addr(pctx, flags, &na_dummy);
1875 return (ISC_TF(result == ISC_R_SUCCESS));
1878 isc_result_t
1879 cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
1880 isc_result_t result;
1882 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1884 if ((flags & CFG_ADDR_WILDOK) != 0 &&
1885 pctx->token.type == isc_tokentype_string &&
1886 strcmp(TOKEN_STRING(pctx), "*") == 0) {
1887 *port = 0;
1888 return (ISC_R_SUCCESS);
1890 if (pctx->token.type != isc_tokentype_number) {
1891 cfg_parser_error(pctx, CFG_LOG_NEAR,
1892 "expected port number or '*'");
1893 return (ISC_R_UNEXPECTEDTOKEN);
1895 if (pctx->token.value.as_ulong >= 65536U) {
1896 cfg_parser_error(pctx, CFG_LOG_NEAR,
1897 "port number out of range");
1898 return (ISC_R_UNEXPECTEDTOKEN);
1900 *port = (in_port_t)(pctx->token.value.as_ulong);
1901 return (ISC_R_SUCCESS);
1902 cleanup:
1903 return (result);
1906 void
1907 cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
1908 isc_result_t result;
1909 char text[128];
1910 isc_buffer_t buf;
1912 isc_buffer_init(&buf, text, sizeof(text));
1913 result = isc_netaddr_totext(na, &buf);
1914 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1915 cfg_print_chars(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf));
1918 isc_result_t
1919 cfg_parse_dscp(cfg_parser_t *pctx, isc_dscp_t *dscp) {
1920 isc_result_t result;
1922 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
1924 if (pctx->token.type != isc_tokentype_number) {
1925 cfg_parser_error(pctx, CFG_LOG_NEAR,
1926 "expected number");
1927 return (ISC_R_UNEXPECTEDTOKEN);
1929 if (pctx->token.value.as_ulong > 63U) {
1930 cfg_parser_error(pctx, CFG_LOG_NEAR,
1931 "dscp out of range");
1932 return (ISC_R_RANGE);
1934 *dscp = (isc_dscp_t)(pctx->token.value.as_ulong);
1935 return (ISC_R_SUCCESS);
1936 cleanup:
1937 return (result);
1940 /* netaddr */
1942 static unsigned int netaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
1943 static unsigned int netaddr4_flags = CFG_ADDR_V4OK;
1944 static unsigned int netaddr4wild_flags = CFG_ADDR_V4OK | CFG_ADDR_WILDOK;
1945 static unsigned int netaddr6_flags = CFG_ADDR_V6OK;
1946 static unsigned int netaddr6wild_flags = CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
1948 static isc_result_t
1949 parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1950 isc_result_t result;
1951 cfg_obj_t *obj = NULL;
1952 isc_netaddr_t netaddr;
1953 unsigned int flags = *(const unsigned int *)type->of;
1955 CHECK(cfg_create_obj(pctx, type, &obj));
1956 CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
1957 isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
1958 *ret = obj;
1959 return (ISC_R_SUCCESS);
1960 cleanup:
1961 CLEANUP_OBJ(obj);
1962 return (result);
1965 static void
1966 cfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
1967 const unsigned int *flagp = type->of;
1968 int n = 0;
1969 if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
1970 cfg_print_chars(pctx, "( ", 2);
1971 if (*flagp & CFG_ADDR_V4OK) {
1972 cfg_print_cstr(pctx, "<ipv4_address>");
1973 n++;
1975 if (*flagp & CFG_ADDR_V6OK) {
1976 if (n != 0)
1977 cfg_print_chars(pctx, " | ", 3);
1978 cfg_print_cstr(pctx, "<ipv6_address>");
1979 n++;
1981 if (*flagp & CFG_ADDR_WILDOK) {
1982 if (n != 0)
1983 cfg_print_chars(pctx, " | ", 3);
1984 cfg_print_chars(pctx, "*", 1);
1985 n++;
1986 POST(n);
1988 if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
1989 cfg_print_chars(pctx, " )", 2);
1992 cfg_type_t cfg_type_netaddr = {
1993 "netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1994 &cfg_rep_sockaddr, &netaddr_flags
1997 cfg_type_t cfg_type_netaddr4 = {
1998 "netaddr4", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1999 &cfg_rep_sockaddr, &netaddr4_flags
2002 cfg_type_t cfg_type_netaddr4wild = {
2003 "netaddr4wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
2004 &cfg_rep_sockaddr, &netaddr4wild_flags
2007 cfg_type_t cfg_type_netaddr6 = {
2008 "netaddr6", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
2009 &cfg_rep_sockaddr, &netaddr6_flags
2012 cfg_type_t cfg_type_netaddr6wild = {
2013 "netaddr6wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
2014 &cfg_rep_sockaddr, &netaddr6wild_flags
2017 /* netprefix */
2019 isc_result_t
2020 cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
2021 cfg_obj_t **ret)
2023 cfg_obj_t *obj = NULL;
2024 isc_result_t result;
2025 isc_netaddr_t netaddr;
2026 unsigned int addrlen = 0, prefixlen;
2027 UNUSED(type);
2029 CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
2030 CFG_ADDR_V6OK, &netaddr));
2031 switch (netaddr.family) {
2032 case AF_INET:
2033 addrlen = 32;
2034 break;
2035 case AF_INET6:
2036 addrlen = 128;
2037 break;
2038 default:
2039 INSIST(0);
2040 break;
2042 CHECK(cfg_peektoken(pctx, 0));
2043 if (pctx->token.type == isc_tokentype_special &&
2044 pctx->token.value.as_char == '/') {
2045 CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
2046 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
2047 if (pctx->token.type != isc_tokentype_number) {
2048 cfg_parser_error(pctx, CFG_LOG_NEAR,
2049 "expected prefix length");
2050 return (ISC_R_UNEXPECTEDTOKEN);
2052 prefixlen = pctx->token.value.as_ulong;
2053 if (prefixlen > addrlen) {
2054 cfg_parser_error(pctx, CFG_LOG_NOPREP,
2055 "invalid prefix length");
2056 return (ISC_R_RANGE);
2058 } else {
2059 prefixlen = addrlen;
2061 CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
2062 obj->value.netprefix.address = netaddr;
2063 obj->value.netprefix.prefixlen = prefixlen;
2064 *ret = obj;
2065 return (ISC_R_SUCCESS);
2066 cleanup:
2067 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
2068 return (result);
2071 static void
2072 print_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2073 const cfg_netprefix_t *p = &obj->value.netprefix;
2075 cfg_print_rawaddr(pctx, &p->address);
2076 cfg_print_chars(pctx, "/", 1);
2077 cfg_print_rawuint(pctx, p->prefixlen);
2080 isc_boolean_t
2081 cfg_obj_isnetprefix(const cfg_obj_t *obj) {
2082 REQUIRE(obj != NULL);
2083 return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
2086 void
2087 cfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr,
2088 unsigned int *prefixlen)
2090 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
2091 REQUIRE(netaddr != NULL);
2092 REQUIRE(prefixlen != NULL);
2094 *netaddr = obj->value.netprefix.address;
2095 *prefixlen = obj->value.netprefix.prefixlen;
2098 cfg_type_t cfg_type_netprefix = {
2099 "netprefix", cfg_parse_netprefix, print_netprefix, cfg_doc_terminal,
2100 &cfg_rep_netprefix, NULL
2103 static isc_result_t
2104 parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type,
2105 int flags, cfg_obj_t **ret)
2107 isc_result_t result;
2108 isc_netaddr_t netaddr;
2109 in_port_t port = 0;
2110 isc_dscp_t dscp = -1;
2111 cfg_obj_t *obj = NULL;
2112 int have_port = 0, have_dscp = 0;
2114 CHECK(cfg_create_obj(pctx, type, &obj));
2115 CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
2116 for (;;) {
2117 CHECK(cfg_peektoken(pctx, 0));
2118 if (pctx->token.type == isc_tokentype_string) {
2119 if (strcasecmp(TOKEN_STRING(pctx), "port") == 0) {
2120 CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
2121 CHECK(cfg_parse_rawport(pctx, flags, &port));
2122 ++have_port;
2123 } else if ((flags & CFG_ADDR_DSCPOK) != 0 &&
2124 strcasecmp(TOKEN_STRING(pctx), "dscp") == 0)
2126 CHECK(cfg_gettoken(pctx, 0)); /* read "dscp" */
2127 CHECK(cfg_parse_dscp(pctx, &dscp));
2128 ++have_dscp;
2129 } else
2130 break;
2131 } else
2132 break;
2134 if (have_port > 1) {
2135 cfg_parser_error(pctx, 0, "expected at most one port");
2136 result = ISC_R_UNEXPECTEDTOKEN;
2137 goto cleanup;
2140 if (have_dscp > 1) {
2141 cfg_parser_error(pctx, 0, "expected at most one dscp");
2142 result = ISC_R_UNEXPECTEDTOKEN;
2143 goto cleanup;
2145 isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
2146 obj->value.sockaddrdscp.dscp = dscp;
2147 *ret = obj;
2148 return (ISC_R_SUCCESS);
2150 cleanup:
2151 CLEANUP_OBJ(obj);
2152 return (result);
2155 static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
2156 cfg_type_t cfg_type_sockaddr = {
2157 "sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr,
2158 &cfg_rep_sockaddr, &sockaddr_flags
2161 static unsigned int sockaddrdscp_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK |
2162 CFG_ADDR_DSCPOK;
2163 cfg_type_t cfg_type_sockaddrdscp = {
2164 "sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr,
2165 &cfg_rep_sockaddr, &sockaddrdscp_flags
2168 isc_result_t
2169 cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2170 const unsigned int *flagp = type->of;
2171 return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret));
2174 void
2175 cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2176 isc_netaddr_t netaddr;
2177 in_port_t port;
2178 char buf[ISC_NETADDR_FORMATSIZE];
2180 isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
2181 isc_netaddr_format(&netaddr, buf, sizeof(buf));
2182 cfg_print_cstr(pctx, buf);
2183 port = isc_sockaddr_getport(&obj->value.sockaddr);
2184 if (port != 0) {
2185 cfg_print_chars(pctx, " port ", 6);
2186 cfg_print_rawuint(pctx, port);
2188 if (obj->value.sockaddrdscp.dscp != -1) {
2189 cfg_print_chars(pctx, " dscp ", 6);
2190 cfg_print_rawuint(pctx, obj->value.sockaddrdscp.dscp);
2194 void
2195 cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
2196 const unsigned int *flagp = type->of;
2197 int n = 0;
2198 cfg_print_chars(pctx, "( ", 2);
2199 if (*flagp & CFG_ADDR_V4OK) {
2200 cfg_print_cstr(pctx, "<ipv4_address>");
2201 n++;
2203 if (*flagp & CFG_ADDR_V6OK) {
2204 if (n != 0)
2205 cfg_print_chars(pctx, " | ", 3);
2206 cfg_print_cstr(pctx, "<ipv6_address>");
2207 n++;
2209 if (*flagp & CFG_ADDR_WILDOK) {
2210 if (n != 0)
2211 cfg_print_chars(pctx, " | ", 3);
2212 cfg_print_chars(pctx, "*", 1);
2213 n++;
2214 POST(n);
2216 cfg_print_chars(pctx, " ) ", 3);
2217 if (*flagp & CFG_ADDR_WILDOK) {
2218 cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
2219 } else {
2220 cfg_print_cstr(pctx, "[ port <integer> ]");
2222 if ((*flagp & CFG_ADDR_DSCPOK) != 0) {
2223 cfg_print_cstr(pctx, " [ dscp <integer> ]");
2227 isc_boolean_t
2228 cfg_obj_issockaddr(const cfg_obj_t *obj) {
2229 REQUIRE(obj != NULL);
2230 return (ISC_TF(obj->type->rep == &cfg_rep_sockaddr));
2233 const isc_sockaddr_t *
2234 cfg_obj_assockaddr(const cfg_obj_t *obj) {
2235 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
2236 return (&obj->value.sockaddr);
2239 isc_dscp_t
2240 cfg_obj_getdscp(const cfg_obj_t *obj) {
2241 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
2242 return (obj->value.sockaddrdscp.dscp);
2245 isc_result_t
2246 cfg_gettoken(cfg_parser_t *pctx, int options) {
2247 isc_result_t result;
2249 if (pctx->seen_eof)
2250 return (ISC_R_SUCCESS);
2252 options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
2254 redo:
2255 pctx->token.type = isc_tokentype_unknown;
2256 result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
2257 pctx->ungotten = ISC_FALSE;
2258 pctx->line = isc_lex_getsourceline(pctx->lexer);
2260 switch (result) {
2261 case ISC_R_SUCCESS:
2262 if (pctx->token.type == isc_tokentype_eof) {
2263 result = isc_lex_close(pctx->lexer);
2264 INSIST(result == ISC_R_NOMORE ||
2265 result == ISC_R_SUCCESS);
2267 if (isc_lex_getsourcename(pctx->lexer) != NULL) {
2269 * Closed an included file, not the main file.
2271 cfg_listelt_t *elt;
2272 elt = ISC_LIST_TAIL(pctx->open_files->
2273 value.list);
2274 INSIST(elt != NULL);
2275 ISC_LIST_UNLINK(pctx->open_files->
2276 value.list, elt, link);
2277 ISC_LIST_APPEND(pctx->closed_files->
2278 value.list, elt, link);
2279 goto redo;
2281 pctx->seen_eof = ISC_TRUE;
2283 break;
2285 case ISC_R_NOSPACE:
2286 /* More understandable than "ran out of space". */
2287 cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
2288 break;
2290 case ISC_R_IOERROR:
2291 cfg_parser_error(pctx, 0, "%s",
2292 isc_result_totext(result));
2293 break;
2295 default:
2296 cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
2297 isc_result_totext(result));
2298 break;
2300 return (result);
2303 void
2304 cfg_ungettoken(cfg_parser_t *pctx) {
2305 if (pctx->seen_eof)
2306 return;
2307 isc_lex_ungettoken(pctx->lexer, &pctx->token);
2308 pctx->ungotten = ISC_TRUE;
2311 isc_result_t
2312 cfg_peektoken(cfg_parser_t *pctx, int options) {
2313 isc_result_t result;
2314 CHECK(cfg_gettoken(pctx, options));
2315 cfg_ungettoken(pctx);
2316 cleanup:
2317 return (result);
2321 * Get a string token, accepting both the quoted and the unquoted form.
2322 * Log an error if the next token is not a string.
2324 static isc_result_t
2325 cfg_getstringtoken(cfg_parser_t *pctx) {
2326 isc_result_t result;
2328 result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
2329 if (result != ISC_R_SUCCESS)
2330 return (result);
2332 if (pctx->token.type != isc_tokentype_string &&
2333 pctx->token.type != isc_tokentype_qstring) {
2334 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
2335 return (ISC_R_UNEXPECTEDTOKEN);
2337 return (ISC_R_SUCCESS);
2340 void
2341 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2342 va_list args;
2343 va_start(args, fmt);
2344 parser_complain(pctx, ISC_FALSE, flags, fmt, args);
2345 va_end(args);
2346 pctx->errors++;
2349 void
2350 cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2351 va_list args;
2352 va_start(args, fmt);
2353 parser_complain(pctx, ISC_TRUE, flags, fmt, args);
2354 va_end(args);
2355 pctx->warnings++;
2358 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
2360 static isc_boolean_t
2361 have_current_file(cfg_parser_t *pctx) {
2362 cfg_listelt_t *elt;
2363 if (pctx->open_files == NULL)
2364 return (ISC_FALSE);
2366 elt = ISC_LIST_TAIL(pctx->open_files->value.list);
2367 if (elt == NULL)
2368 return (ISC_FALSE);
2370 return (ISC_TRUE);
2373 static char *
2374 current_file(cfg_parser_t *pctx) {
2375 static char none[] = "none";
2376 cfg_listelt_t *elt;
2377 cfg_obj_t *fileobj;
2379 if (!have_current_file(pctx))
2380 return (none);
2382 elt = ISC_LIST_TAIL(pctx->open_files->value.list);
2383 if (elt == NULL) /* shouldn't be possible, but... */
2384 return (none);
2386 fileobj = elt->obj;
2387 INSIST(fileobj->type == &cfg_type_qstring);
2388 return (fileobj->value.string.base);
2391 static void
2392 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
2393 unsigned int flags, const char *format,
2394 va_list args)
2396 char tokenbuf[MAX_LOG_TOKEN + 10];
2397 static char where[ISC_DIR_PATHMAX + 100];
2398 static char message[2048];
2399 int level = ISC_LOG_ERROR;
2400 const char *prep = "";
2401 size_t len;
2403 if (is_warning)
2404 level = ISC_LOG_WARNING;
2406 where[0] = '\0';
2407 if (have_current_file(pctx))
2408 snprintf(where, sizeof(where), "%s:%u: ",
2409 current_file(pctx), pctx->line);
2411 len = vsnprintf(message, sizeof(message), format, args);
2412 if (len >= sizeof(message))
2413 FATAL_ERROR(__FILE__, __LINE__,
2414 "error message would overflow");
2416 if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) {
2417 isc_region_t r;
2419 if (pctx->ungotten)
2420 (void)cfg_gettoken(pctx, 0);
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);
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';
2448 isc_log_write(pctx->lctx, CAT, MOD, level,
2449 "%s%s%s%s", where, message, prep, tokenbuf);
2452 void
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];
2458 if (! isc_log_wouldlog(lctx, level))
2459 return;
2461 va_start(ap, fmt);
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);
2471 const char *
2472 cfg_obj_file(const cfg_obj_t *obj) {
2473 return (obj->file);
2476 unsigned int
2477 cfg_obj_line(const cfg_obj_t *obj) {
2478 return (obj->line);
2481 isc_result_t
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;
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);
2497 *ret = obj;
2498 return (ISC_R_SUCCESS);
2502 static void
2503 map_symtabitem_destroy(char *key, unsigned int type,
2504 isc_symvalue_t symval, void *userarg)
2506 cfg_obj_t *obj = symval.as_pointer;
2507 cfg_parser_t *pctx = (cfg_parser_t *)userarg;
2509 UNUSED(key);
2510 UNUSED(type);
2512 cfg_obj_destroy(pctx, &obj);
2516 static isc_result_t
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;
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;
2529 *ret = obj;
2530 return (ISC_R_SUCCESS);
2532 cleanup:
2533 if (obj != NULL)
2534 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
2535 return (result);
2538 static void
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);
2544 isc_boolean_t
2545 cfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type) {
2546 return (ISC_TF(obj->type == type));
2550 * Destroy 'obj', a configuration object created in 'pctx'.
2552 void
2553 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
2554 cfg_obj_t *obj;
2555 unsigned int refs;
2557 REQUIRE(objp != NULL && *objp != NULL);
2558 REQUIRE(pctx != NULL);
2560 obj = *objp;
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));
2568 *objp = NULL;
2571 void
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;
2579 static void
2580 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
2581 UNUSED(pctx);
2582 UNUSED(obj);
2585 void
2586 cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
2587 type->doc(pctx, type);
2590 void
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);
2597 void
2598 cfg_print_grammar(const cfg_type_t *type,
2599 void (*f)(void *closure, const char *text, int textlen),
2600 void *closure)
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);