2 * Copyright (C) 2004, 2006 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000-2003 Internet Software Consortium.
5 * Permission to use, copy, modify, and 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.
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.
18 /* $Id: parser.c,v 1.70.2.20.2.21 2006/02/28 06:32:54 marka Exp $ */
22 #include <isc/buffer.h>
24 #include <isc/formatcheck.h>
29 #include <isc/netaddr.h>
30 #include <isc/print.h>
31 #include <isc/string.h>
32 #include <isc/sockaddr.h>
33 #include <isc/netscope.h>
35 #include <isc/symtab.h>
37 #include <isccfg/cfg.h>
38 #include <isccfg/grammar.h>
39 #include <isccfg/log.h>
42 #define CAT CFG_LOGCATEGORY_CONFIG
43 #define MOD CFG_LOGMODULE_PARSER
45 #define MAP_SYM 1 /* Unique type for isc_symtab */
47 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
49 /* Check a return value. */
52 if (result != ISC_R_SUCCESS) goto cleanup; \
55 /* Clean up a configuration object if non-NULL. */
56 #define CLEANUP_OBJ(obj) \
57 do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
61 * Forward declarations of static functions.
65 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
68 parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
71 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
74 free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
77 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
80 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
84 free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
87 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
90 free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
93 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
94 cfg_type_t *elttype, isc_symtab_t *symtab,
95 isc_boolean_t callback);
98 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
101 cfg_getstringtoken(cfg_parser_t *pctx);
104 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
105 unsigned int flags, const char *format, va_list args);
108 * Data representations. These correspond to members of the
109 * "value" union in struct cfg_obj (except "void", which does
110 * not need a union member).
113 cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
114 cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
115 cfg_rep_t cfg_rep_string = { "string", free_string };
116 cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
117 cfg_rep_t cfg_rep_map = { "map", free_map };
118 cfg_rep_t cfg_rep_list = { "list", free_list };
119 cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
120 cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
121 cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
122 cfg_rep_t cfg_rep_void = { "void", free_noop };
125 * Configuration type definitions.
129 * An implicit list. These are formed by clauses that occur multiple times.
131 static cfg_type_t cfg_type_implicitlist = {
132 "implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL };
137 cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
138 obj->type->print(pctx, obj);
142 cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
143 pctx->f(pctx->closure, text, len);
147 print_open(cfg_printer_t *pctx) {
148 cfg_print_chars(pctx, "{\n", 2);
153 print_indent(cfg_printer_t *pctx) {
154 int indent = pctx->indent;
156 cfg_print_chars(pctx, "\t", 1);
162 print_close(cfg_printer_t *pctx) {
165 cfg_print_chars(pctx, "}", 1);
169 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
171 INSIST(ret != NULL && *ret == NULL);
172 result = type->parse(pctx, type, ret);
173 if (result != ISC_R_SUCCESS)
175 INSIST(*ret != NULL);
176 return (ISC_R_SUCCESS);
180 cfg_print(const cfg_obj_t *obj,
181 void (*f)(void *closure, const char *text, int textlen),
186 pctx.closure = closure;
188 obj->type->print(&pctx, obj);
195 cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
197 const cfg_tuplefielddef_t *fields = type->of;
198 const cfg_tuplefielddef_t *f;
199 cfg_obj_t *obj = NULL;
200 unsigned int nfields = 0;
203 for (f = fields; f->name != NULL; f++)
206 CHECK(cfg_create_obj(pctx, type, &obj));
207 obj->value.tuple = isc_mem_get(pctx->mctx,
208 nfields * sizeof(cfg_obj_t *));
209 if (obj->value.tuple == NULL) {
210 result = ISC_R_NOMEMORY;
213 for (f = fields, i = 0; f->name != NULL; f++, i++)
214 obj->value.tuple[i] = NULL;
216 return (ISC_R_SUCCESS);
220 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
225 cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
228 const cfg_tuplefielddef_t *fields = type->of;
229 const cfg_tuplefielddef_t *f;
230 cfg_obj_t *obj = NULL;
233 CHECK(cfg_create_tuple(pctx, type, &obj));
234 for (f = fields, i = 0; f->name != NULL; f++, i++)
235 CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
238 return (ISC_R_SUCCESS);
246 cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
248 const cfg_tuplefielddef_t *fields = obj->type->of;
249 const cfg_tuplefielddef_t *f;
250 isc_boolean_t need_space = ISC_FALSE;
252 for (f = fields, i = 0; f->name != NULL; f++, i++) {
253 const cfg_obj_t *fieldobj = obj->value.tuple[i];
255 cfg_print_chars(pctx, " ", 1);
256 cfg_print_obj(pctx, fieldobj);
257 need_space = ISC_TF(fieldobj->type->print != cfg_print_void);
262 cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
263 const cfg_tuplefielddef_t *fields = type->of;
264 const cfg_tuplefielddef_t *f;
265 isc_boolean_t need_space = ISC_FALSE;
267 for (f = fields; f->name != NULL; f++) {
269 cfg_print_chars(pctx, " ", 1);
270 cfg_doc_obj(pctx, f->type);
271 need_space = ISC_TF(f->type->print != cfg_print_void);
276 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
278 const cfg_tuplefielddef_t *fields = obj->type->of;
279 const cfg_tuplefielddef_t *f;
280 unsigned int nfields = 0;
282 if (obj->value.tuple == NULL)
285 for (f = fields, i = 0; f->name != NULL; f++, i++) {
286 CLEANUP_OBJ(obj->value.tuple[i]);
289 isc_mem_put(pctx->mctx, obj->value.tuple,
290 nfields * sizeof(cfg_obj_t *));
294 cfg_obj_istuple(const cfg_obj_t *obj) {
295 REQUIRE(obj != NULL);
296 return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
300 cfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) {
302 const cfg_tuplefielddef_t *fields;
303 const cfg_tuplefielddef_t *f;
305 REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
307 fields = tupleobj->type->of;
308 for (f = fields, i = 0; f->name != NULL; f++, i++) {
309 if (strcmp(f->name, name) == 0)
310 return (tupleobj->value.tuple[i]);
317 cfg_parse_special(cfg_parser_t *pctx, int special) {
319 CHECK(cfg_gettoken(pctx, 0));
320 if (pctx->token.type == isc_tokentype_special &&
321 pctx->token.value.as_char == special)
322 return (ISC_R_SUCCESS);
324 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
325 return (ISC_R_UNEXPECTEDTOKEN);
331 * Parse a required semicolon. If it is not there, log
332 * an error and increment the error count but continue
333 * parsing. Since the next token is pushed back,
334 * care must be taken to make sure it is eventually
335 * consumed or an infinite loop may result.
338 parse_semicolon(cfg_parser_t *pctx) {
340 CHECK(cfg_gettoken(pctx, 0));
341 if (pctx->token.type == isc_tokentype_special &&
342 pctx->token.value.as_char == ';')
343 return (ISC_R_SUCCESS);
345 cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
346 cfg_ungettoken(pctx);
352 * Parse EOF, logging and returning an error if not there.
355 parse_eof(cfg_parser_t *pctx) {
357 CHECK(cfg_gettoken(pctx, 0));
359 if (pctx->token.type == isc_tokentype_eof)
360 return (ISC_R_SUCCESS);
362 cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
363 return (ISC_R_UNEXPECTEDTOKEN);
368 /* A list of files, used internally for pctx->files. */
370 static cfg_type_t cfg_type_filelist = {
371 "filelist", NULL, print_list, NULL, &cfg_rep_list,
376 cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
379 isc_lexspecials_t specials;
381 REQUIRE(mctx != NULL);
382 REQUIRE(ret != NULL && *ret == NULL);
384 pctx = isc_mem_get(mctx, sizeof(*pctx));
386 return (ISC_R_NOMEMORY);
391 pctx->seen_eof = ISC_FALSE;
392 pctx->ungotten = ISC_FALSE;
395 pctx->open_files = NULL;
396 pctx->closed_files = NULL;
398 pctx->callback = NULL;
399 pctx->callbackarg = NULL;
400 pctx->token.type = isc_tokentype_unknown;
402 memset(specials, 0, sizeof(specials));
410 CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer));
412 isc_lex_setspecials(pctx->lexer, specials);
413 isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C |
414 ISC_LEXCOMMENT_CPLUSPLUS |
415 ISC_LEXCOMMENT_SHELL));
417 CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
418 CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
421 return (ISC_R_SUCCESS);
424 if (pctx->lexer != NULL)
425 isc_lex_destroy(&pctx->lexer);
426 CLEANUP_OBJ(pctx->open_files);
427 CLEANUP_OBJ(pctx->closed_files);
428 isc_mem_put(mctx, pctx, sizeof(*pctx));
433 parser_openfile(cfg_parser_t *pctx, const char *filename) {
435 cfg_listelt_t *elt = NULL;
436 cfg_obj_t *stringobj = NULL;
438 result = isc_lex_openfile(pctx->lexer, filename);
439 if (result != ISC_R_SUCCESS) {
440 cfg_parser_error(pctx, 0, "open: %s: %s",
441 filename, isc_result_totext(result));
445 CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
446 CHECK(create_listelt(pctx, &elt));
447 elt->obj = stringobj;
448 ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
450 return (ISC_R_SUCCESS);
452 CLEANUP_OBJ(stringobj);
457 cfg_parser_setcallback(cfg_parser_t *pctx,
458 cfg_parsecallback_t callback,
461 pctx->callback = callback;
462 pctx->callbackarg = arg;
466 * Parse a configuration using a pctx where a lexer has already
467 * been set up with a source.
470 parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
472 cfg_obj_t *obj = NULL;
474 result = cfg_parse_obj(pctx, type, &obj);
476 if (pctx->errors != 0) {
477 /* Errors have been logged. */
478 if (result == ISC_R_SUCCESS)
479 result = ISC_R_FAILURE;
483 if (result != ISC_R_SUCCESS) {
484 /* Parsing failed but no errors have been logged. */
485 cfg_parser_error(pctx, 0, "parsing failed");
489 CHECK(parse_eof(pctx));
492 return (ISC_R_SUCCESS);
500 cfg_parse_file(cfg_parser_t *pctx, const char *filename,
501 const cfg_type_t *type, cfg_obj_t **ret)
505 REQUIRE(filename != NULL);
507 CHECK(parser_openfile(pctx, filename));
508 CHECK(parse2(pctx, type, ret));
515 cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
516 const cfg_type_t *type, cfg_obj_t **ret)
519 REQUIRE(buffer != NULL);
520 CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
521 CHECK(parse2(pctx, type, ret));
527 cfg_parser_destroy(cfg_parser_t **pctxp) {
528 cfg_parser_t *pctx = *pctxp;
529 isc_lex_destroy(&pctx->lexer);
531 * Cleaning up open_files does not
532 * close the files; that was already done
533 * by closing the lexer.
535 CLEANUP_OBJ(pctx->open_files);
536 CLEANUP_OBJ(pctx->closed_files);
537 isc_mem_put(pctx->mctx, pctx, sizeof(*pctx));
545 cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
547 return (cfg_create_obj(pctx, &cfg_type_void, ret));
551 cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
557 cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
563 cfg_obj_isvoid(const cfg_obj_t *obj) {
564 REQUIRE(obj != NULL);
565 return (ISC_TF(obj->type->rep == &cfg_rep_void));
568 cfg_type_t cfg_type_void = {
569 "void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
577 cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
579 cfg_obj_t *obj = NULL;
582 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
583 if (pctx->token.type != isc_tokentype_number) {
584 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
585 return (ISC_R_UNEXPECTEDTOKEN);
588 CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
590 obj->value.uint32 = pctx->token.value.as_ulong;
597 cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
598 cfg_print_chars(pctx, s, strlen(s));
602 cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
604 snprintf(buf, sizeof(buf), "%u", u);
605 cfg_print_cstr(pctx, buf);
609 cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
610 cfg_print_rawuint(pctx, obj->value.uint32);
614 cfg_obj_isuint32(const cfg_obj_t *obj) {
615 REQUIRE(obj != NULL);
616 return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
620 cfg_obj_asuint32(const cfg_obj_t *obj) {
621 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
622 return (obj->value.uint32);
625 cfg_type_t cfg_type_uint32 = {
626 "integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
627 &cfg_rep_uint32, NULL
635 cfg_obj_isuint64(const cfg_obj_t *obj) {
636 REQUIRE(obj != NULL);
637 return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
641 cfg_obj_asuint64(const cfg_obj_t *obj) {
642 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
643 return (obj->value.uint64);
647 cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
649 snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
651 cfg_print_cstr(pctx, buf);
654 cfg_type_t cfg_type_uint64 = {
655 "64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
656 &cfg_rep_uint64, NULL
660 * qstring (quoted string), ustring (unquoted string), astring
664 /* Create a string object from a null-terminated C string. */
666 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
670 cfg_obj_t *obj = NULL;
673 CHECK(cfg_create_obj(pctx, type, &obj));
674 len = strlen(contents);
675 obj->value.string.length = len;
676 obj->value.string.base = isc_mem_get(pctx->mctx, len + 1);
677 if (obj->value.string.base == 0) {
678 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
679 return (ISC_R_NOMEMORY);
681 memcpy(obj->value.string.base, contents, len);
682 obj->value.string.base[len] = '\0';
690 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
694 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
695 if (pctx->token.type != isc_tokentype_qstring) {
696 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
697 return (ISC_R_UNEXPECTEDTOKEN);
699 return (create_string(pctx,
708 parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
712 CHECK(cfg_gettoken(pctx, 0));
713 if (pctx->token.type != isc_tokentype_string) {
714 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
715 return (ISC_R_UNEXPECTEDTOKEN);
717 return (create_string(pctx,
726 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
732 CHECK(cfg_getstringtoken(pctx));
733 return (create_string(pctx,
742 cfg_is_enum(const char *s, const char *const *enums) {
743 const char * const *p;
744 for (p = enums; *p != NULL; p++) {
745 if (strcasecmp(*p, s) == 0)
752 check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
753 const char *s = obj->value.string.base;
754 if (cfg_is_enum(s, enums))
755 return (ISC_R_SUCCESS);
756 cfg_parser_error(pctx, 0, "'%s' unexpected", s);
757 return (ISC_R_UNEXPECTEDTOKEN);
761 cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
763 cfg_obj_t *obj = NULL;
764 CHECK(parse_ustring(pctx, NULL, &obj));
765 CHECK(check_enum(pctx, obj, type->of));
767 return (ISC_R_SUCCESS);
774 cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
775 const char * const *p;
776 cfg_print_chars(pctx, "( ", 2);
777 for (p = type->of; *p != NULL; p++) {
778 cfg_print_cstr(pctx, *p);
780 cfg_print_chars(pctx, " | ", 3);
782 cfg_print_chars(pctx, " )", 2);
786 cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
787 cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
791 print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
792 cfg_print_chars(pctx, "\"", 1);
793 cfg_print_ustring(pctx, obj);
794 cfg_print_chars(pctx, "\"", 1);
798 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
799 isc_mem_put(pctx->mctx, obj->value.string.base,
800 obj->value.string.length + 1);
804 cfg_obj_isstring(const cfg_obj_t *obj) {
805 REQUIRE(obj != NULL);
806 return (ISC_TF(obj->type->rep == &cfg_rep_string));
810 cfg_obj_asstring(const cfg_obj_t *obj) {
811 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
812 return (obj->value.string.base);
815 /* Quoted string only */
816 cfg_type_t cfg_type_qstring = {
817 "quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal,
818 &cfg_rep_string, NULL
821 /* Unquoted string only */
822 cfg_type_t cfg_type_ustring = {
823 "string", parse_ustring, cfg_print_ustring, cfg_doc_terminal,
824 &cfg_rep_string, NULL
827 /* Any string (quoted or unquoted); printed with quotes */
828 cfg_type_t cfg_type_astring = {
829 "string", cfg_parse_astring, print_qstring, cfg_doc_terminal,
830 &cfg_rep_string, NULL
838 cfg_obj_isboolean(const cfg_obj_t *obj) {
839 REQUIRE(obj != NULL);
840 return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
844 cfg_obj_asboolean(const cfg_obj_t *obj) {
845 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
846 return (obj->value.boolean);
850 parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
854 cfg_obj_t *obj = NULL;
857 result = cfg_gettoken(pctx, 0);
858 if (result != ISC_R_SUCCESS)
861 if (pctx->token.type != isc_tokentype_string)
864 if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
865 (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
866 (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
868 } else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
869 (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
870 (strcmp(TOKEN_STRING(pctx), "0") == 0)) {
876 CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
877 obj->value.boolean = value;
882 cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
883 return (ISC_R_UNEXPECTEDTOKEN);
890 print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
891 if (obj->value.boolean)
892 cfg_print_chars(pctx, "yes", 3);
894 cfg_print_chars(pctx, "no", 2);
897 cfg_type_t cfg_type_boolean = {
898 "boolean", parse_boolean, print_boolean, cfg_doc_terminal,
899 &cfg_rep_boolean, NULL
907 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
909 CHECK(cfg_create_obj(pctx, type, obj));
910 ISC_LIST_INIT((*obj)->value.list);
916 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
918 elt = isc_mem_get(pctx->mctx, sizeof(*elt));
920 return (ISC_R_NOMEMORY);
922 ISC_LINK_INIT(elt, link);
924 return (ISC_R_SUCCESS);
928 free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
929 cfg_obj_destroy(pctx, &elt->obj);
930 isc_mem_put(pctx->mctx, elt, sizeof(*elt));
934 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
935 cfg_listelt_t *elt, *next;
936 for (elt = ISC_LIST_HEAD(obj->value.list);
940 next = ISC_LIST_NEXT(elt, link);
941 free_list_elt(pctx, elt);
946 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
950 cfg_listelt_t *elt = NULL;
951 cfg_obj_t *value = NULL;
953 CHECK(create_listelt(pctx, &elt));
955 result = cfg_parse_obj(pctx, elttype, &value);
956 if (result != ISC_R_SUCCESS)
962 return (ISC_R_SUCCESS);
965 isc_mem_put(pctx->mctx, elt, sizeof(*elt));
970 * Parse a homogeneous list whose elements are of type 'elttype'
971 * and where each element is terminated by a semicolon.
974 parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
976 cfg_obj_t *listobj = NULL;
977 const cfg_type_t *listof = listtype->of;
979 cfg_listelt_t *elt = NULL;
981 CHECK(cfg_create_list(pctx, listtype, &listobj));
984 CHECK(cfg_peektoken(pctx, 0));
985 if (pctx->token.type == isc_tokentype_special &&
986 pctx->token.value.as_char == /*{*/ '}')
988 CHECK(cfg_parse_listelt(pctx, listof, &elt));
989 CHECK(parse_semicolon(pctx));
990 ISC_LIST_APPEND(listobj->value.list, elt, link);
994 return (ISC_R_SUCCESS);
998 free_list_elt(pctx, elt);
999 CLEANUP_OBJ(listobj);
1004 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1005 const cfg_list_t *list = &obj->value.list;
1006 const cfg_listelt_t *elt;
1008 for (elt = ISC_LIST_HEAD(*list);
1010 elt = ISC_LIST_NEXT(elt, link)) {
1012 cfg_print_obj(pctx, elt->obj);
1013 cfg_print_chars(pctx, ";\n", 2);
1018 cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
1021 isc_result_t result;
1022 CHECK(cfg_parse_special(pctx, '{'));
1023 CHECK(parse_list(pctx, type, ret));
1024 CHECK(cfg_parse_special(pctx, '}'));
1030 cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1032 print_list(pctx, obj);
1037 cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
1038 cfg_print_chars(pctx, "{ ", 2);
1039 cfg_doc_obj(pctx, type->of);
1040 cfg_print_chars(pctx, "; ... }", 7);
1044 * Parse a homogeneous list whose elements are of type 'elttype'
1045 * and where elements are separated by space. The list ends
1046 * before the first semicolon.
1049 cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
1052 cfg_obj_t *listobj = NULL;
1053 const cfg_type_t *listof = listtype->of;
1054 isc_result_t result;
1056 CHECK(cfg_create_list(pctx, listtype, &listobj));
1059 cfg_listelt_t *elt = NULL;
1061 CHECK(cfg_peektoken(pctx, 0));
1062 if (pctx->token.type == isc_tokentype_special &&
1063 pctx->token.value.as_char == ';')
1065 CHECK(cfg_parse_listelt(pctx, listof, &elt));
1066 ISC_LIST_APPEND(listobj->value.list, elt, link);
1069 return (ISC_R_SUCCESS);
1072 CLEANUP_OBJ(listobj);
1077 cfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1078 const cfg_list_t *list = &obj->value.list;
1079 const cfg_listelt_t *elt;
1081 for (elt = ISC_LIST_HEAD(*list);
1083 elt = ISC_LIST_NEXT(elt, link)) {
1084 cfg_print_obj(pctx, elt->obj);
1085 if (ISC_LIST_NEXT(elt, link) != NULL)
1086 cfg_print_chars(pctx, " ", 1);
1092 cfg_obj_islist(const cfg_obj_t *obj) {
1093 REQUIRE(obj != NULL);
1094 return (ISC_TF(obj->type->rep == &cfg_rep_list));
1097 const cfg_listelt_t *
1098 cfg_list_first(const cfg_obj_t *obj) {
1099 REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
1102 return (ISC_LIST_HEAD(obj->value.list));
1105 const cfg_listelt_t *
1106 cfg_list_next(const cfg_listelt_t *elt) {
1107 REQUIRE(elt != NULL);
1108 return (ISC_LIST_NEXT(elt, link));
1112 cfg_listelt_value(const cfg_listelt_t *elt) {
1113 REQUIRE(elt != NULL);
1122 * Parse a map body. That's something like
1124 * "foo 1; bar { glub; }; zap true; zap false;"
1126 * i.e., a sequence of option names followed by values and
1127 * terminated by semicolons. Used for the top level of
1128 * the named.conf syntax, as well as for the body of the
1129 * options, view, zone, and other statements.
1132 cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1134 const cfg_clausedef_t * const *clausesets = type->of;
1135 isc_result_t result;
1136 const cfg_clausedef_t * const *clauseset;
1137 const cfg_clausedef_t *clause;
1138 cfg_obj_t *value = NULL;
1139 cfg_obj_t *obj = NULL;
1140 cfg_obj_t *eltobj = NULL;
1141 cfg_obj_t *includename = NULL;
1142 isc_symvalue_t symval;
1143 cfg_list_t *list = NULL;
1145 CHECK(create_map(pctx, type, &obj));
1147 obj->value.map.clausesets = clausesets;
1154 * Parse the option name and see if it is known.
1156 CHECK(cfg_gettoken(pctx, 0));
1158 if (pctx->token.type != isc_tokentype_string) {
1159 cfg_ungettoken(pctx);
1164 * We accept "include" statements wherever a map body
1167 if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
1169 * Turn the file name into a temporary configuration
1170 * object just so that it is not overwritten by the
1173 CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
1174 CHECK(parse_semicolon(pctx));
1175 CHECK(parser_openfile(pctx, includename->
1176 value.string.base));
1177 cfg_obj_destroy(pctx, &includename);
1182 for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
1183 for (clause = *clauseset;
1184 clause->name != NULL;
1186 if (strcasecmp(TOKEN_STRING(pctx),
1192 if (clause == NULL || clause->name == NULL) {
1193 cfg_parser_error(pctx, CFG_LOG_NOPREP, "unknown option");
1195 * Try to recover by parsing this option as an unknown
1196 * option and discarding it.
1198 CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, &eltobj));
1199 cfg_obj_destroy(pctx, &eltobj);
1200 CHECK(parse_semicolon(pctx));
1204 /* Clause is known. */
1206 /* Issue warnings if appropriate */
1207 if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0)
1208 cfg_parser_warning(pctx, 0, "option '%s' is obsolete",
1210 if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0)
1211 cfg_parser_warning(pctx, 0, "option '%s' is "
1212 "not implemented", clause->name);
1213 if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0)
1214 cfg_parser_warning(pctx, 0, "option '%s' is "
1215 "not implemented", clause->name);
1217 * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
1218 * set here - we need to log the *lack* of such an option,
1222 /* See if the clause already has a value; if not create one. */
1223 result = isc_symtab_lookup(obj->value.map.symtab,
1224 clause->name, 0, &symval);
1226 if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
1227 /* Multivalued clause */
1228 cfg_obj_t *listobj = NULL;
1229 if (result == ISC_R_NOTFOUND) {
1230 CHECK(cfg_create_list(pctx,
1231 &cfg_type_implicitlist,
1233 symval.as_pointer = listobj;
1234 result = isc_symtab_define(obj->value.
1238 isc_symexists_reject);
1239 if (result != ISC_R_SUCCESS) {
1240 cfg_parser_error(pctx, CFG_LOG_NEAR,
1241 "isc_symtab_define(%s) "
1242 "failed", clause->name);
1243 isc_mem_put(pctx->mctx, list,
1244 sizeof(cfg_list_t));
1248 INSIST(result == ISC_R_SUCCESS);
1249 listobj = symval.as_pointer;
1253 CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
1254 CHECK(parse_semicolon(pctx));
1256 ISC_LIST_APPEND(listobj->value.list, elt, link);
1258 /* Single-valued clause */
1259 if (result == ISC_R_NOTFOUND) {
1260 isc_boolean_t callback =
1261 ISC_TF((clause->flags &
1262 CFG_CLAUSEFLAG_CALLBACK) != 0);
1263 CHECK(parse_symtab_elt(pctx, clause->name,
1265 obj->value.map.symtab,
1267 CHECK(parse_semicolon(pctx));
1268 } else if (result == ISC_R_SUCCESS) {
1269 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
1271 result = ISC_R_EXISTS;
1274 cfg_parser_error(pctx, CFG_LOG_NEAR,
1275 "isc_symtab_define() failed");
1283 return (ISC_R_SUCCESS);
1288 CLEANUP_OBJ(eltobj);
1289 CLEANUP_OBJ(includename);
1294 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
1295 cfg_type_t *elttype, isc_symtab_t *symtab,
1296 isc_boolean_t callback)
1298 isc_result_t result;
1299 cfg_obj_t *obj = NULL;
1300 isc_symvalue_t symval;
1302 CHECK(cfg_parse_obj(pctx, elttype, &obj));
1304 if (callback && pctx->callback != NULL)
1305 CHECK(pctx->callback(name, obj, pctx->callbackarg));
1307 symval.as_pointer = obj;
1308 CHECK(isc_symtab_define(symtab, name,
1310 isc_symexists_reject));
1311 return (ISC_R_SUCCESS);
1319 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
1322 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1323 isc_result_t result;
1324 CHECK(cfg_parse_special(pctx, '{'));
1325 CHECK(cfg_parse_mapbody(pctx, type, ret));
1326 CHECK(cfg_parse_special(pctx, '}'));
1332 * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
1335 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type,
1338 isc_result_t result;
1339 cfg_obj_t *idobj = NULL;
1340 cfg_obj_t *mapobj = NULL;
1342 CHECK(cfg_parse_obj(pctx, nametype, &idobj));
1343 CHECK(cfg_parse_map(pctx, type, &mapobj));
1344 mapobj->value.map.id = idobj;
1353 * Parse a map identified by a string name. E.g., "name { foo 1; }".
1354 * Used for the "key" and "channel" statements.
1357 cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1358 return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
1362 * Parse a map identified by a network address.
1363 * Used for the "server" statement.
1366 cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1367 return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
1371 cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1372 isc_result_t result = ISC_R_SUCCESS;
1374 const cfg_clausedef_t * const *clauseset;
1376 for (clauseset = obj->value.map.clausesets;
1380 isc_symvalue_t symval;
1381 const cfg_clausedef_t *clause;
1383 for (clause = *clauseset;
1384 clause->name != NULL;
1386 result = isc_symtab_lookup(obj->value.map.symtab,
1387 clause->name, 0, &symval);
1388 if (result == ISC_R_SUCCESS) {
1389 cfg_obj_t *obj = symval.as_pointer;
1390 if (obj->type == &cfg_type_implicitlist) {
1392 cfg_list_t *list = &obj->value.list;
1394 for (elt = ISC_LIST_HEAD(*list);
1396 elt = ISC_LIST_NEXT(elt, link)) {
1398 cfg_print_cstr(pctx, clause->name);
1399 cfg_print_chars(pctx, " ", 1);
1400 cfg_print_obj(pctx, elt->obj);
1401 cfg_print_chars(pctx, ";\n", 2);
1404 /* Single-valued. */
1406 cfg_print_cstr(pctx, clause->name);
1407 cfg_print_chars(pctx, " ", 1);
1408 cfg_print_obj(pctx, obj);
1409 cfg_print_chars(pctx, ";\n", 2);
1411 } else if (result == ISC_R_NOTFOUND) {
1421 cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
1422 const cfg_clausedef_t * const *clauseset;
1423 const cfg_clausedef_t *clause;
1425 for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1426 for (clause = *clauseset;
1427 clause->name != NULL;
1429 cfg_print_cstr(pctx, clause->name);
1430 cfg_print_chars(pctx, " ", 1);
1431 cfg_doc_obj(pctx, clause->type);
1432 cfg_print_chars(pctx, ";", 1);
1433 /* XXX print flags here? */
1434 cfg_print_chars(pctx, "\n\n", 2);
1439 static struct flagtext {
1443 { CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
1444 { CFG_CLAUSEFLAG_NYI, "not yet implemented" },
1445 { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
1446 { CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
1451 cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1452 if (obj->value.map.id != NULL) {
1453 cfg_print_obj(pctx, obj->value.map.id);
1454 cfg_print_chars(pctx, " ", 1);
1457 cfg_print_mapbody(pctx, obj);
1462 print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
1464 isc_boolean_t first = ISC_TRUE;
1465 for (p = flagtexts; p->flag != 0; p++) {
1466 if ((flags & p->flag) != 0) {
1468 cfg_print_chars(pctx, " // ", 4);
1470 cfg_print_chars(pctx, ", ", 2);
1471 cfg_print_cstr(pctx, p->text);
1478 cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
1479 const cfg_clausedef_t * const *clauseset;
1480 const cfg_clausedef_t *clause;
1482 if (type->parse == cfg_parse_named_map) {
1483 cfg_doc_obj(pctx, &cfg_type_astring);
1484 cfg_print_chars(pctx, " ", 1);
1485 } else if (type->parse == cfg_parse_addressed_map) {
1486 cfg_doc_obj(pctx, &cfg_type_netaddr);
1487 cfg_print_chars(pctx, " ", 1);
1492 for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1493 for (clause = *clauseset;
1494 clause->name != NULL;
1497 cfg_print_cstr(pctx, clause->name);
1498 if (clause->type->print != cfg_print_void)
1499 cfg_print_chars(pctx, " ", 1);
1500 cfg_doc_obj(pctx, clause->type);
1501 cfg_print_chars(pctx, ";", 1);
1502 print_clause_flags(pctx, clause->flags);
1503 cfg_print_chars(pctx, "\n", 1);
1510 cfg_obj_ismap(const cfg_obj_t *obj) {
1511 REQUIRE(obj != NULL);
1512 return (ISC_TF(obj->type->rep == &cfg_rep_map));
1516 cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
1517 isc_result_t result;
1519 const cfg_map_t *map;
1521 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1522 REQUIRE(name != NULL);
1523 REQUIRE(obj != NULL && *obj == NULL);
1525 map = &mapobj->value.map;
1527 result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
1528 if (result != ISC_R_SUCCESS)
1530 *obj = val.as_pointer;
1531 return (ISC_R_SUCCESS);
1535 cfg_map_getname(const cfg_obj_t *mapobj) {
1536 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1537 return (mapobj->value.map.id);
1541 /* Parse an arbitrary token, storing its raw text representation. */
1543 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1544 cfg_obj_t *obj = NULL;
1545 isc_result_t result;
1550 CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
1551 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
1552 if (pctx->token.type == isc_tokentype_eof) {
1553 cfg_ungettoken(pctx);
1558 isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
1560 obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
1561 if (obj->value.string.base == NULL) {
1562 result = ISC_R_NOMEMORY;
1565 obj->value.string.length = r.length;
1566 memcpy(obj->value.string.base, r.base, r.length);
1567 obj->value.string.base[r.length] = '\0';
1573 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
1577 cfg_type_t cfg_type_token = {
1578 "token", parse_token, cfg_print_ustring, cfg_doc_terminal,
1579 &cfg_rep_string, NULL
1583 * An unsupported option. This is just a list of tokens with balanced braces
1584 * ending in a semicolon.
1588 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1589 cfg_obj_t *listobj = NULL;
1590 isc_result_t result;
1593 CHECK(cfg_create_list(pctx, type, &listobj));
1596 cfg_listelt_t *elt = NULL;
1598 CHECK(cfg_peektoken(pctx, 0));
1599 if (pctx->token.type == isc_tokentype_special) {
1600 if (pctx->token.value.as_char == '{')
1602 else if (pctx->token.value.as_char == '}')
1604 else if (pctx->token.value.as_char == ';')
1608 if (pctx->token.type == isc_tokentype_eof || braces < 0) {
1609 cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
1610 result = ISC_R_UNEXPECTEDTOKEN;
1614 CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
1615 ISC_LIST_APPEND(listobj->value.list, elt, link);
1617 INSIST(braces == 0);
1619 return (ISC_R_SUCCESS);
1622 CLEANUP_OBJ(listobj);
1626 cfg_type_t cfg_type_unsupported = {
1627 "unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal,
1632 * Try interpreting the current token as a network address.
1634 * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
1635 * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set. The
1636 * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
1637 * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
1638 * and the IPv6 wildcard address otherwise.
1641 token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1643 struct in_addr in4a;
1644 struct in6_addr in6a;
1646 if (pctx->token.type != isc_tokentype_string)
1647 return (ISC_R_UNEXPECTEDTOKEN);
1649 s = TOKEN_STRING(pctx);
1650 if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
1651 if ((flags & CFG_ADDR_V4OK) != 0) {
1652 isc_netaddr_any(na);
1653 return (ISC_R_SUCCESS);
1654 } else if ((flags & CFG_ADDR_V6OK) != 0) {
1655 isc_netaddr_any6(na);
1656 return (ISC_R_SUCCESS);
1661 if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
1662 if (inet_pton(AF_INET, s, &in4a) == 1) {
1663 isc_netaddr_fromin(na, &in4a);
1664 return (ISC_R_SUCCESS);
1667 if ((flags & CFG_ADDR_V4PREFIXOK) != 0 &&
1673 for (i = 0; i < 3; i++) {
1675 if (inet_pton(AF_INET, buf, &in4a) == 1) {
1676 isc_netaddr_fromin(na, &in4a);
1677 return (ISC_R_SUCCESS);
1681 if ((flags & CFG_ADDR_V6OK) != 0 &&
1682 strlen(s) <= 127U) {
1683 char buf[128]; /* see lib/bind9/getaddresses.c */
1684 char *d; /* zone delimiter */
1685 isc_uint32_t zone = 0; /* scope zone ID */
1688 d = strchr(buf, '%');
1692 if (inet_pton(AF_INET6, buf, &in6a) == 1) {
1694 #ifdef ISC_PLATFORM_HAVESCOPEID
1695 isc_result_t result;
1697 result = isc_netscope_pton(AF_INET6,
1701 if (result != ISC_R_SUCCESS)
1704 return (ISC_R_BADADDRESSFORM);
1708 isc_netaddr_fromin6(na, &in6a);
1709 isc_netaddr_setzone(na, zone);
1710 return (ISC_R_SUCCESS);
1714 return (ISC_R_UNEXPECTEDTOKEN);
1718 cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1719 isc_result_t result;
1720 CHECK(cfg_gettoken(pctx, 0));
1721 result = token_addr(pctx, flags, na);
1722 if (result == ISC_R_UNEXPECTEDTOKEN)
1723 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected IP address");
1729 cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
1730 isc_result_t result;
1731 isc_netaddr_t na_dummy;
1732 result = token_addr(pctx, flags, &na_dummy);
1733 return (ISC_TF(result == ISC_R_SUCCESS));
1737 cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
1738 isc_result_t result;
1740 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1742 if ((flags & CFG_ADDR_WILDOK) != 0 &&
1743 pctx->token.type == isc_tokentype_string &&
1744 strcmp(TOKEN_STRING(pctx), "*") == 0) {
1746 return (ISC_R_SUCCESS);
1748 if (pctx->token.type != isc_tokentype_number) {
1749 cfg_parser_error(pctx, CFG_LOG_NEAR,
1750 "expected port number or '*'");
1751 return (ISC_R_UNEXPECTEDTOKEN);
1753 if (pctx->token.value.as_ulong >= 65536U) {
1754 cfg_parser_error(pctx, CFG_LOG_NEAR,
1755 "port number out of range");
1756 return (ISC_R_UNEXPECTEDTOKEN);
1758 *port = (in_port_t)(pctx->token.value.as_ulong);
1759 return (ISC_R_SUCCESS);
1765 cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
1766 isc_result_t result;
1770 isc_buffer_init(&buf, text, sizeof(text));
1771 result = isc_netaddr_totext(na, &buf);
1772 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1773 cfg_print_chars(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf));
1779 parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1780 isc_result_t result;
1781 cfg_obj_t *obj = NULL;
1782 isc_netaddr_t netaddr;
1784 CHECK(cfg_create_obj(pctx, type, &obj));
1785 CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK, &netaddr));
1786 isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
1788 return (ISC_R_SUCCESS);
1794 cfg_type_t cfg_type_netaddr = {
1795 "netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_terminal,
1796 &cfg_rep_sockaddr, NULL
1802 cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
1805 cfg_obj_t *obj = NULL;
1806 isc_result_t result;
1807 isc_netaddr_t netaddr;
1808 unsigned int addrlen, prefixlen;
1811 CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
1812 CFG_ADDR_V6OK, &netaddr));
1813 switch (netaddr.family) {
1825 CHECK(cfg_peektoken(pctx, 0));
1826 if (pctx->token.type == isc_tokentype_special &&
1827 pctx->token.value.as_char == '/') {
1828 CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
1829 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1830 if (pctx->token.type != isc_tokentype_number) {
1831 cfg_parser_error(pctx, CFG_LOG_NEAR,
1832 "expected prefix length");
1833 return (ISC_R_UNEXPECTEDTOKEN);
1835 prefixlen = pctx->token.value.as_ulong;
1836 if (prefixlen > addrlen) {
1837 cfg_parser_error(pctx, CFG_LOG_NOPREP,
1838 "invalid prefix length");
1839 return (ISC_R_RANGE);
1842 prefixlen = addrlen;
1844 CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
1845 obj->value.netprefix.address = netaddr;
1846 obj->value.netprefix.prefixlen = prefixlen;
1848 return (ISC_R_SUCCESS);
1850 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
1855 print_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1856 const cfg_netprefix_t *p = &obj->value.netprefix;
1858 cfg_print_rawaddr(pctx, &p->address);
1859 cfg_print_chars(pctx, "/", 1);
1860 cfg_print_rawuint(pctx, p->prefixlen);
1864 cfg_obj_isnetprefix(const cfg_obj_t *obj) {
1865 REQUIRE(obj != NULL);
1866 return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
1870 cfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr,
1871 unsigned int *prefixlen) {
1872 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
1873 *netaddr = obj->value.netprefix.address;
1874 *prefixlen = obj->value.netprefix.prefixlen;
1877 cfg_type_t cfg_type_netprefix = {
1878 "netprefix", cfg_parse_netprefix, print_netprefix, cfg_doc_terminal,
1879 &cfg_rep_netprefix, NULL
1883 parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type,
1884 int flags, cfg_obj_t **ret)
1886 isc_result_t result;
1887 isc_netaddr_t netaddr;
1889 cfg_obj_t *obj = NULL;
1891 CHECK(cfg_create_obj(pctx, type, &obj));
1892 CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
1893 CHECK(cfg_peektoken(pctx, 0));
1894 if (pctx->token.type == isc_tokentype_string &&
1895 strcasecmp(TOKEN_STRING(pctx), "port") == 0) {
1896 CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
1897 CHECK(cfg_parse_rawport(pctx, flags, &port));
1899 isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
1901 return (ISC_R_SUCCESS);
1908 static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
1909 cfg_type_t cfg_type_sockaddr = {
1910 "sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr,
1911 &cfg_rep_sockaddr, &sockaddr_flags
1915 cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1916 const unsigned int *flagp = type->of;
1917 return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret));
1921 cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1922 isc_netaddr_t netaddr;
1924 char buf[ISC_NETADDR_FORMATSIZE];
1926 isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
1927 isc_netaddr_format(&netaddr, buf, sizeof(buf));
1928 cfg_print_cstr(pctx, buf);
1929 port = isc_sockaddr_getport(&obj->value.sockaddr);
1931 cfg_print_chars(pctx, " port ", 6);
1932 cfg_print_rawuint(pctx, port);
1937 cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
1938 const unsigned int *flagp = type->of;
1940 cfg_print_chars(pctx, "( ", 2);
1941 if (*flagp & CFG_ADDR_V4OK) {
1942 cfg_print_cstr(pctx, "<ipv4_address>");
1945 if (*flagp & CFG_ADDR_V6OK) {
1947 cfg_print_chars(pctx, " | ", 3);
1948 cfg_print_cstr(pctx, "<ipv6_address>");
1951 if (*flagp & CFG_ADDR_WILDOK) {
1953 cfg_print_chars(pctx, " | ", 3);
1954 cfg_print_chars(pctx, "*", 1);
1957 cfg_print_chars(pctx, " ) ", 3);
1958 if (*flagp & CFG_ADDR_WILDOK) {
1959 cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
1961 cfg_print_cstr(pctx, "[ port <integer> ]");
1966 cfg_obj_issockaddr(const cfg_obj_t *obj) {
1967 REQUIRE(obj != NULL);
1968 return (ISC_TF(obj->type->rep == &cfg_rep_sockaddr));
1971 const isc_sockaddr_t *
1972 cfg_obj_assockaddr(const cfg_obj_t *obj) {
1973 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
1974 return (&obj->value.sockaddr);
1978 cfg_gettoken(cfg_parser_t *pctx, int options) {
1979 isc_result_t result;
1982 return (ISC_R_SUCCESS);
1984 options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
1987 pctx->token.type = isc_tokentype_unknown;
1988 result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
1989 pctx->ungotten = ISC_FALSE;
1990 pctx->line = isc_lex_getsourceline(pctx->lexer);
1994 if (pctx->token.type == isc_tokentype_eof) {
1995 result = isc_lex_close(pctx->lexer);
1996 INSIST(result == ISC_R_NOMORE ||
1997 result == ISC_R_SUCCESS);
1999 if (isc_lex_getsourcename(pctx->lexer) != NULL) {
2001 * Closed an included file, not the main file.
2004 elt = ISC_LIST_TAIL(pctx->open_files->
2006 INSIST(elt != NULL);
2007 ISC_LIST_UNLINK(pctx->open_files->
2008 value.list, elt, link);
2009 ISC_LIST_APPEND(pctx->closed_files->
2010 value.list, elt, link);
2013 pctx->seen_eof = ISC_TRUE;
2018 /* More understandable than "ran out of space". */
2019 cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
2023 cfg_parser_error(pctx, 0, "%s",
2024 isc_result_totext(result));
2028 cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
2029 isc_result_totext(result));
2036 cfg_ungettoken(cfg_parser_t *pctx) {
2039 isc_lex_ungettoken(pctx->lexer, &pctx->token);
2040 pctx->ungotten = ISC_TRUE;
2044 cfg_peektoken(cfg_parser_t *pctx, int options) {
2045 isc_result_t result;
2046 CHECK(cfg_gettoken(pctx, options));
2047 cfg_ungettoken(pctx);
2053 * Get a string token, accepting both the quoted and the unquoted form.
2054 * Log an error if the next token is not a string.
2057 cfg_getstringtoken(cfg_parser_t *pctx) {
2058 isc_result_t result;
2060 result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
2061 if (result != ISC_R_SUCCESS)
2064 if (pctx->token.type != isc_tokentype_string &&
2065 pctx->token.type != isc_tokentype_qstring) {
2066 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
2067 return (ISC_R_UNEXPECTEDTOKEN);
2069 return (ISC_R_SUCCESS);
2073 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2075 va_start(args, fmt);
2076 parser_complain(pctx, ISC_FALSE, flags, fmt, args);
2082 cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2084 va_start(args, fmt);
2085 parser_complain(pctx, ISC_TRUE, flags, fmt, args);
2090 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
2093 current_file(cfg_parser_t *pctx) {
2094 static char none[] = "none";
2098 if (pctx->open_files == NULL)
2100 elt = ISC_LIST_TAIL(pctx->open_files->value.list);
2105 INSIST(fileobj->type == &cfg_type_qstring);
2106 return (fileobj->value.string.base);
2110 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
2111 unsigned int flags, const char *format,
2114 char tokenbuf[MAX_LOG_TOKEN + 10];
2115 static char where[ISC_DIR_PATHMAX + 100];
2116 static char message[2048];
2117 int level = ISC_LOG_ERROR;
2118 const char *prep = "";
2122 level = ISC_LOG_WARNING;
2124 snprintf(where, sizeof(where), "%s:%u: ",
2125 current_file(pctx), pctx->line);
2127 len = vsnprintf(message, sizeof(message), format, args);
2128 if (len >= sizeof(message))
2129 FATAL_ERROR(__FILE__, __LINE__,
2130 "error message would overflow");
2132 if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) {
2136 (void)cfg_gettoken(pctx, 0);
2138 if (pctx->token.type == isc_tokentype_eof) {
2139 snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
2140 } else if (pctx->token.type == isc_tokentype_unknown) {
2144 isc_lex_getlasttokentext(pctx->lexer,
2146 if (r.length > MAX_LOG_TOKEN)
2147 snprintf(tokenbuf, sizeof(tokenbuf),
2148 "'%.*s...'", MAX_LOG_TOKEN, r.base);
2150 snprintf(tokenbuf, sizeof(tokenbuf),
2151 "'%.*s'", (int)r.length, r.base);
2154 /* Choose a preposition. */
2155 if (flags & CFG_LOG_NEAR)
2157 else if (flags & CFG_LOG_BEFORE)
2164 isc_log_write(pctx->lctx, CAT, MOD, level,
2165 "%s%s%s%s", where, message, prep, tokenbuf);
2169 cfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level,
2170 const char *fmt, ...) {
2174 if (! isc_log_wouldlog(lctx, level))
2179 vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
2180 isc_log_write(lctx, CAT, MOD, level,
2182 obj->file == NULL ? "<unknown file>" : obj->file,
2188 cfg_obj_file(const cfg_obj_t *obj) {
2193 cfg_obj_line(const cfg_obj_t *obj) {
2198 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2201 obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
2203 return (ISC_R_NOMEMORY);
2205 obj->file = current_file(pctx);
2206 obj->line = pctx->line;
2208 return (ISC_R_SUCCESS);
2212 map_symtabitem_destroy(char *key, unsigned int type,
2213 isc_symvalue_t symval, void *userarg)
2215 cfg_obj_t *obj = symval.as_pointer;
2216 cfg_parser_t *pctx = (cfg_parser_t *)userarg;
2221 cfg_obj_destroy(pctx, &obj);
2226 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2227 isc_result_t result;
2228 isc_symtab_t *symtab = NULL;
2229 cfg_obj_t *obj = NULL;
2231 CHECK(cfg_create_obj(pctx, type, &obj));
2232 CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */
2233 map_symtabitem_destroy,
2234 pctx, ISC_FALSE, &symtab));
2235 obj->value.map.symtab = symtab;
2236 obj->value.map.id = NULL;
2239 return (ISC_R_SUCCESS);
2243 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
2248 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
2249 CLEANUP_OBJ(obj->value.map.id);
2250 isc_symtab_destroy(&obj->value.map.symtab);
2254 cfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type) {
2255 return (ISC_TF(obj->type == type));
2259 * Destroy 'obj', a configuration object created in 'pctx'.
2262 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
2263 cfg_obj_t *obj = *objp;
2264 obj->type->rep->free(pctx, obj);
2265 isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
2270 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
2276 cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
2277 type->doc(pctx, type);
2281 cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) {
2282 cfg_print_chars(pctx, "<", 1);
2283 cfg_print_cstr(pctx, type->name);
2284 cfg_print_chars(pctx, ">", 1);
2288 cfg_print_grammar(const cfg_type_t *type,
2289 void (*f)(void *closure, const char *text, int textlen),
2294 pctx.closure = closure;
2296 cfg_doc_obj(&pctx, type);