2 * Copyright (C) 2004 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.18 2004/05/15 03:46:13 jinmei 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, 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, 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(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, 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 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(cfg_obj_t *obj) {
295 REQUIRE(obj != NULL);
296 return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
300 cfg_tuple_get(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, cfg_obj_t *obj) {
557 cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
563 cfg_obj_isvoid(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, cfg_obj_t *obj) {
610 cfg_print_rawuint(pctx, obj->value.uint32);
614 cfg_obj_isuint32(cfg_obj_t *obj) {
615 REQUIRE(obj != NULL);
616 return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
620 cfg_obj_asuint32(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(cfg_obj_t *obj) {
636 REQUIRE(obj != NULL);
637 return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
641 cfg_obj_asuint64(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, 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, cfg_obj_t **ret) {
730 CHECK(cfg_getstringtoken(pctx));
731 return (create_string(pctx,
740 cfg_is_enum(const char *s, const char *const *enums) {
741 const char * const *p;
742 for (p = enums; *p != NULL; p++) {
743 if (strcasecmp(*p, s) == 0)
750 check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
751 const char *s = obj->value.string.base;
752 if (cfg_is_enum(s, enums))
753 return (ISC_R_SUCCESS);
754 cfg_parser_error(pctx, 0, "'%s' unexpected", s);
755 return (ISC_R_UNEXPECTEDTOKEN);
759 cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
761 cfg_obj_t *obj = NULL;
762 CHECK(parse_ustring(pctx, NULL, &obj));
763 CHECK(check_enum(pctx, obj, type->of));
765 return (ISC_R_SUCCESS);
772 cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
773 const char * const *p;
774 cfg_print_chars(pctx, "( ", 2);
775 for (p = type->of; *p != NULL; p++) {
776 cfg_print_cstr(pctx, *p);
778 cfg_print_chars(pctx, " | ", 3);
780 cfg_print_chars(pctx, " )", 2);
784 cfg_print_ustring(cfg_printer_t *pctx, cfg_obj_t *obj) {
785 cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
789 print_qstring(cfg_printer_t *pctx, cfg_obj_t *obj) {
790 cfg_print_chars(pctx, "\"", 1);
791 cfg_print_ustring(pctx, obj);
792 cfg_print_chars(pctx, "\"", 1);
796 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
797 isc_mem_put(pctx->mctx, obj->value.string.base,
798 obj->value.string.length + 1);
802 cfg_obj_isstring(cfg_obj_t *obj) {
803 REQUIRE(obj != NULL);
804 return (ISC_TF(obj->type->rep == &cfg_rep_string));
808 cfg_obj_asstring(cfg_obj_t *obj) {
809 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
810 return (obj->value.string.base);
813 /* Quoted string only */
814 cfg_type_t cfg_type_qstring = {
815 "quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal,
816 &cfg_rep_string, NULL
819 /* Unquoted string only */
820 cfg_type_t cfg_type_ustring = {
821 "string", parse_ustring, cfg_print_ustring, cfg_doc_terminal,
822 &cfg_rep_string, NULL
825 /* Any string (quoted or unquoted); printed with quotes */
826 cfg_type_t cfg_type_astring = {
827 "string", cfg_parse_astring, print_qstring, cfg_doc_terminal,
828 &cfg_rep_string, NULL
836 cfg_obj_isboolean(cfg_obj_t *obj) {
837 REQUIRE(obj != NULL);
838 return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
842 cfg_obj_asboolean(cfg_obj_t *obj) {
843 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
844 return (obj->value.boolean);
848 parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
852 cfg_obj_t *obj = NULL;
855 result = cfg_gettoken(pctx, 0);
856 if (result != ISC_R_SUCCESS)
859 if (pctx->token.type != isc_tokentype_string)
862 if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
863 (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
864 (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
866 } else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
867 (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
868 (strcmp(TOKEN_STRING(pctx), "0") == 0)) {
874 CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
875 obj->value.boolean = value;
880 cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
881 return (ISC_R_UNEXPECTEDTOKEN);
888 print_boolean(cfg_printer_t *pctx, cfg_obj_t *obj) {
889 if (obj->value.boolean)
890 cfg_print_chars(pctx, "yes", 3);
892 cfg_print_chars(pctx, "no", 2);
895 cfg_type_t cfg_type_boolean = {
896 "boolean", parse_boolean, print_boolean, cfg_doc_terminal,
897 &cfg_rep_boolean, NULL
905 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
907 CHECK(cfg_create_obj(pctx, type, obj));
908 ISC_LIST_INIT((*obj)->value.list);
914 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
916 elt = isc_mem_get(pctx->mctx, sizeof(*elt));
918 return (ISC_R_NOMEMORY);
920 ISC_LINK_INIT(elt, link);
922 return (ISC_R_SUCCESS);
926 free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
927 cfg_obj_destroy(pctx, &elt->obj);
928 isc_mem_put(pctx->mctx, elt, sizeof(*elt));
932 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
933 cfg_listelt_t *elt, *next;
934 for (elt = ISC_LIST_HEAD(obj->value.list);
938 next = ISC_LIST_NEXT(elt, link);
939 free_list_elt(pctx, elt);
944 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
948 cfg_listelt_t *elt = NULL;
949 cfg_obj_t *value = NULL;
951 CHECK(create_listelt(pctx, &elt));
953 result = cfg_parse_obj(pctx, elttype, &value);
954 if (result != ISC_R_SUCCESS)
960 return (ISC_R_SUCCESS);
963 isc_mem_put(pctx->mctx, elt, sizeof(*elt));
968 * Parse a homogeneous list whose elements are of type 'elttype'
969 * and where each element is terminated by a semicolon.
972 parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
974 cfg_obj_t *listobj = NULL;
975 const cfg_type_t *listof = listtype->of;
977 cfg_listelt_t *elt = NULL;
979 CHECK(cfg_create_list(pctx, listtype, &listobj));
982 CHECK(cfg_peektoken(pctx, 0));
983 if (pctx->token.type == isc_tokentype_special &&
984 pctx->token.value.as_char == /*{*/ '}')
986 CHECK(cfg_parse_listelt(pctx, listof, &elt));
987 CHECK(parse_semicolon(pctx));
988 ISC_LIST_APPEND(listobj->value.list, elt, link);
992 return (ISC_R_SUCCESS);
996 free_list_elt(pctx, elt);
997 CLEANUP_OBJ(listobj);
1002 print_list(cfg_printer_t *pctx, cfg_obj_t *obj) {
1003 cfg_list_t *list = &obj->value.list;
1006 for (elt = ISC_LIST_HEAD(*list);
1008 elt = ISC_LIST_NEXT(elt, link)) {
1010 cfg_print_obj(pctx, elt->obj);
1011 cfg_print_chars(pctx, ";\n", 2);
1016 cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
1019 isc_result_t result;
1020 CHECK(cfg_parse_special(pctx, '{'));
1021 CHECK(parse_list(pctx, type, ret));
1022 CHECK(cfg_parse_special(pctx, '}'));
1028 cfg_print_bracketed_list(cfg_printer_t *pctx, cfg_obj_t *obj) {
1030 print_list(pctx, obj);
1035 cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
1036 cfg_print_chars(pctx, "{ ", 2);
1037 cfg_doc_obj(pctx, type->of);
1038 cfg_print_chars(pctx, "; ... }", 7);
1042 * Parse a homogeneous list whose elements are of type 'elttype'
1043 * and where elements are separated by space. The list ends
1044 * before the first semicolon.
1047 cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
1050 cfg_obj_t *listobj = NULL;
1051 const cfg_type_t *listof = listtype->of;
1052 isc_result_t result;
1054 CHECK(cfg_create_list(pctx, listtype, &listobj));
1057 cfg_listelt_t *elt = NULL;
1059 CHECK(cfg_peektoken(pctx, 0));
1060 if (pctx->token.type == isc_tokentype_special &&
1061 pctx->token.value.as_char == ';')
1063 CHECK(cfg_parse_listelt(pctx, listof, &elt));
1064 ISC_LIST_APPEND(listobj->value.list, elt, link);
1067 return (ISC_R_SUCCESS);
1070 CLEANUP_OBJ(listobj);
1075 cfg_print_spacelist(cfg_printer_t *pctx, cfg_obj_t *obj) {
1076 cfg_list_t *list = &obj->value.list;
1079 for (elt = ISC_LIST_HEAD(*list);
1081 elt = ISC_LIST_NEXT(elt, link)) {
1082 cfg_print_obj(pctx, elt->obj);
1083 if (ISC_LIST_NEXT(elt, link) != NULL)
1084 cfg_print_chars(pctx, " ", 1);
1090 cfg_obj_islist(cfg_obj_t *obj) {
1091 REQUIRE(obj != NULL);
1092 return (ISC_TF(obj->type->rep == &cfg_rep_list));
1096 cfg_list_first(cfg_obj_t *obj) {
1097 REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
1100 return (ISC_LIST_HEAD(obj->value.list));
1104 cfg_list_next(cfg_listelt_t *elt) {
1105 REQUIRE(elt != NULL);
1106 return (ISC_LIST_NEXT(elt, link));
1110 cfg_listelt_value(cfg_listelt_t *elt) {
1111 REQUIRE(elt != NULL);
1120 * Parse a map body. That's something like
1122 * "foo 1; bar { glub; }; zap true; zap false;"
1124 * i.e., a sequence of option names followed by values and
1125 * terminated by semicolons. Used for the top level of
1126 * the named.conf syntax, as well as for the body of the
1127 * options, view, zone, and other statements.
1130 cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1132 const cfg_clausedef_t * const *clausesets = type->of;
1133 isc_result_t result;
1134 const cfg_clausedef_t * const *clauseset;
1135 const cfg_clausedef_t *clause;
1136 cfg_obj_t *value = NULL;
1137 cfg_obj_t *obj = NULL;
1138 cfg_obj_t *eltobj = NULL;
1139 cfg_obj_t *includename = NULL;
1140 isc_symvalue_t symval;
1141 cfg_list_t *list = NULL;
1143 CHECK(create_map(pctx, type, &obj));
1145 obj->value.map.clausesets = clausesets;
1152 * Parse the option name and see if it is known.
1154 CHECK(cfg_gettoken(pctx, 0));
1156 if (pctx->token.type != isc_tokentype_string) {
1157 cfg_ungettoken(pctx);
1162 * We accept "include" statements wherever a map body
1165 if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
1167 * Turn the file name into a temporary configuration
1168 * object just so that it is not overwritten by the
1171 CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
1172 CHECK(parse_semicolon(pctx));
1173 CHECK(parser_openfile(pctx, includename->
1174 value.string.base));
1175 cfg_obj_destroy(pctx, &includename);
1180 for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
1181 for (clause = *clauseset;
1182 clause->name != NULL;
1184 if (strcasecmp(TOKEN_STRING(pctx),
1190 if (clause == NULL || clause->name == NULL) {
1191 cfg_parser_error(pctx, CFG_LOG_NOPREP, "unknown option");
1193 * Try to recover by parsing this option as an unknown
1194 * option and discarding it.
1196 CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, &eltobj));
1197 cfg_obj_destroy(pctx, &eltobj);
1198 CHECK(parse_semicolon(pctx));
1202 /* Clause is known. */
1204 /* Issue warnings if appropriate */
1205 if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0)
1206 cfg_parser_warning(pctx, 0, "option '%s' is obsolete",
1208 if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0)
1209 cfg_parser_warning(pctx, 0, "option '%s' is "
1210 "not implemented", clause->name);
1211 if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0)
1212 cfg_parser_warning(pctx, 0, "option '%s' is "
1213 "not implemented", clause->name);
1215 * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
1216 * set here - we need to log the *lack* of such an option,
1220 /* See if the clause already has a value; if not create one. */
1221 result = isc_symtab_lookup(obj->value.map.symtab,
1222 clause->name, 0, &symval);
1224 if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
1225 /* Multivalued clause */
1226 cfg_obj_t *listobj = NULL;
1227 if (result == ISC_R_NOTFOUND) {
1228 CHECK(cfg_create_list(pctx,
1229 &cfg_type_implicitlist,
1231 symval.as_pointer = listobj;
1232 result = isc_symtab_define(obj->value.
1236 isc_symexists_reject);
1237 if (result != ISC_R_SUCCESS) {
1238 cfg_parser_error(pctx, CFG_LOG_NEAR,
1239 "isc_symtab_define(%s) "
1240 "failed", clause->name);
1241 isc_mem_put(pctx->mctx, list,
1242 sizeof(cfg_list_t));
1246 INSIST(result == ISC_R_SUCCESS);
1247 listobj = symval.as_pointer;
1251 CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
1252 CHECK(parse_semicolon(pctx));
1254 ISC_LIST_APPEND(listobj->value.list, elt, link);
1256 /* Single-valued clause */
1257 if (result == ISC_R_NOTFOUND) {
1258 isc_boolean_t callback =
1259 ISC_TF((clause->flags &
1260 CFG_CLAUSEFLAG_CALLBACK) != 0);
1261 CHECK(parse_symtab_elt(pctx, clause->name,
1263 obj->value.map.symtab,
1265 CHECK(parse_semicolon(pctx));
1266 } else if (result == ISC_R_SUCCESS) {
1267 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
1269 result = ISC_R_EXISTS;
1272 cfg_parser_error(pctx, CFG_LOG_NEAR,
1273 "isc_symtab_define() failed");
1281 return (ISC_R_SUCCESS);
1286 CLEANUP_OBJ(eltobj);
1287 CLEANUP_OBJ(includename);
1292 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
1293 cfg_type_t *elttype, isc_symtab_t *symtab,
1294 isc_boolean_t callback)
1296 isc_result_t result;
1297 cfg_obj_t *obj = NULL;
1298 isc_symvalue_t symval;
1300 CHECK(cfg_parse_obj(pctx, elttype, &obj));
1302 if (callback && pctx->callback != NULL)
1303 CHECK(pctx->callback(name, obj, pctx->callbackarg));
1305 symval.as_pointer = obj;
1306 CHECK(isc_symtab_define(symtab, name,
1308 isc_symexists_reject));
1309 return (ISC_R_SUCCESS);
1317 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
1320 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1321 isc_result_t result;
1322 CHECK(cfg_parse_special(pctx, '{'));
1323 CHECK(cfg_parse_mapbody(pctx, type, ret));
1324 CHECK(cfg_parse_special(pctx, '}'));
1330 * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
1333 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type,
1336 isc_result_t result;
1337 cfg_obj_t *idobj = NULL;
1338 cfg_obj_t *mapobj = NULL;
1340 CHECK(cfg_parse_obj(pctx, nametype, &idobj));
1341 CHECK(cfg_parse_map(pctx, type, &mapobj));
1342 mapobj->value.map.id = idobj;
1351 * Parse a map identified by a string name. E.g., "name { foo 1; }".
1352 * Used for the "key" and "channel" statements.
1355 cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1356 return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
1360 * Parse a map identified by a network address.
1361 * Used for the "server" statement.
1364 cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1365 return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
1369 cfg_print_mapbody(cfg_printer_t *pctx, cfg_obj_t *obj) {
1370 isc_result_t result = ISC_R_SUCCESS;
1372 const cfg_clausedef_t * const *clauseset;
1374 for (clauseset = obj->value.map.clausesets;
1378 isc_symvalue_t symval;
1379 const cfg_clausedef_t *clause;
1381 for (clause = *clauseset;
1382 clause->name != NULL;
1384 result = isc_symtab_lookup(obj->value.map.symtab,
1385 clause->name, 0, &symval);
1386 if (result == ISC_R_SUCCESS) {
1387 cfg_obj_t *obj = symval.as_pointer;
1388 if (obj->type == &cfg_type_implicitlist) {
1390 cfg_list_t *list = &obj->value.list;
1392 for (elt = ISC_LIST_HEAD(*list);
1394 elt = ISC_LIST_NEXT(elt, link)) {
1396 cfg_print_cstr(pctx, clause->name);
1397 cfg_print_chars(pctx, " ", 1);
1398 cfg_print_obj(pctx, elt->obj);
1399 cfg_print_chars(pctx, ";\n", 2);
1402 /* Single-valued. */
1404 cfg_print_cstr(pctx, clause->name);
1405 cfg_print_chars(pctx, " ", 1);
1406 cfg_print_obj(pctx, obj);
1407 cfg_print_chars(pctx, ";\n", 2);
1409 } else if (result == ISC_R_NOTFOUND) {
1419 cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
1420 const cfg_clausedef_t * const *clauseset;
1421 const cfg_clausedef_t *clause;
1423 for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1424 for (clause = *clauseset;
1425 clause->name != NULL;
1427 cfg_print_cstr(pctx, clause->name);
1428 cfg_print_chars(pctx, " ", 1);
1429 cfg_doc_obj(pctx, clause->type);
1430 cfg_print_chars(pctx, ";", 1);
1431 /* XXX print flags here? */
1432 cfg_print_chars(pctx, "\n\n", 2);
1437 static struct flagtext {
1441 { CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
1442 { CFG_CLAUSEFLAG_NYI, "not yet implemented" },
1443 { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
1444 { CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
1449 cfg_print_map(cfg_printer_t *pctx, cfg_obj_t *obj) {
1450 if (obj->value.map.id != NULL) {
1451 cfg_print_obj(pctx, obj->value.map.id);
1452 cfg_print_chars(pctx, " ", 1);
1455 cfg_print_mapbody(pctx, obj);
1460 print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
1462 isc_boolean_t first = ISC_TRUE;
1463 for (p = flagtexts; p->flag != 0; p++) {
1464 if ((flags & p->flag) != 0) {
1466 cfg_print_chars(pctx, " // ", 4);
1468 cfg_print_chars(pctx, ", ", 2);
1469 cfg_print_cstr(pctx, p->text);
1476 cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
1477 const cfg_clausedef_t * const *clauseset;
1478 const cfg_clausedef_t *clause;
1480 if (type->parse == cfg_parse_named_map) {
1481 cfg_doc_obj(pctx, &cfg_type_astring);
1482 cfg_print_chars(pctx, " ", 1);
1483 } else if (type->parse == cfg_parse_addressed_map) {
1484 cfg_doc_obj(pctx, &cfg_type_netaddr);
1485 cfg_print_chars(pctx, " ", 1);
1490 for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1491 for (clause = *clauseset;
1492 clause->name != NULL;
1495 cfg_print_cstr(pctx, clause->name);
1496 if (clause->type->print != cfg_print_void)
1497 cfg_print_chars(pctx, " ", 1);
1498 cfg_doc_obj(pctx, clause->type);
1499 cfg_print_chars(pctx, ";", 1);
1500 print_clause_flags(pctx, clause->flags);
1501 cfg_print_chars(pctx, "\n", 1);
1508 cfg_obj_ismap(cfg_obj_t *obj) {
1509 REQUIRE(obj != NULL);
1510 return (ISC_TF(obj->type->rep == &cfg_rep_map));
1514 cfg_map_get(cfg_obj_t *mapobj, const char* name, cfg_obj_t **obj) {
1515 isc_result_t result;
1519 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1520 REQUIRE(name != NULL);
1521 REQUIRE(obj != NULL && *obj == NULL);
1523 map = &mapobj->value.map;
1525 result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
1526 if (result != ISC_R_SUCCESS)
1528 *obj = val.as_pointer;
1529 return (ISC_R_SUCCESS);
1533 cfg_map_getname(cfg_obj_t *mapobj) {
1534 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1535 return (mapobj->value.map.id);
1539 /* Parse an arbitrary token, storing its raw text representation. */
1541 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1542 cfg_obj_t *obj = NULL;
1543 isc_result_t result;
1548 CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
1549 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
1550 if (pctx->token.type == isc_tokentype_eof) {
1551 cfg_ungettoken(pctx);
1556 isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
1558 obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
1559 obj->value.string.length = r.length;
1560 memcpy(obj->value.string.base, r.base, r.length);
1561 obj->value.string.base[r.length] = '\0';
1568 cfg_type_t cfg_type_token = {
1569 "token", parse_token, cfg_print_ustring, cfg_doc_terminal,
1570 &cfg_rep_string, NULL
1574 * An unsupported option. This is just a list of tokens with balanced braces
1575 * ending in a semicolon.
1579 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1580 cfg_obj_t *listobj = NULL;
1581 isc_result_t result;
1584 CHECK(cfg_create_list(pctx, type, &listobj));
1587 cfg_listelt_t *elt = NULL;
1589 CHECK(cfg_peektoken(pctx, 0));
1590 if (pctx->token.type == isc_tokentype_special) {
1591 if (pctx->token.value.as_char == '{')
1593 else if (pctx->token.value.as_char == '}')
1595 else if (pctx->token.value.as_char == ';')
1599 if (pctx->token.type == isc_tokentype_eof || braces < 0) {
1600 cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
1601 result = ISC_R_UNEXPECTEDTOKEN;
1605 CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
1606 ISC_LIST_APPEND(listobj->value.list, elt, link);
1608 INSIST(braces == 0);
1610 return (ISC_R_SUCCESS);
1613 CLEANUP_OBJ(listobj);
1617 cfg_type_t cfg_type_unsupported = {
1618 "unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal,
1623 * Try interpreting the current token as a network address.
1625 * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
1626 * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set. The
1627 * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
1628 * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
1629 * and the IPv6 wildcard address otherwise.
1632 token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1634 struct in_addr in4a;
1635 struct in6_addr in6a;
1637 if (pctx->token.type != isc_tokentype_string)
1638 return (ISC_R_UNEXPECTEDTOKEN);
1640 s = TOKEN_STRING(pctx);
1641 if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
1642 if ((flags & CFG_ADDR_V4OK) != 0) {
1643 isc_netaddr_any(na);
1644 return (ISC_R_SUCCESS);
1645 } else if ((flags & CFG_ADDR_V6OK) != 0) {
1646 isc_netaddr_any6(na);
1647 return (ISC_R_SUCCESS);
1652 if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
1653 if (inet_pton(AF_INET, s, &in4a) == 1) {
1654 isc_netaddr_fromin(na, &in4a);
1655 return (ISC_R_SUCCESS);
1658 if ((flags & CFG_ADDR_V4PREFIXOK) != 0 &&
1664 for (i = 0; i < 3; i++) {
1666 if (inet_pton(AF_INET, buf, &in4a) == 1) {
1667 isc_netaddr_fromin(na, &in4a);
1668 return (ISC_R_SUCCESS);
1672 if ((flags & CFG_ADDR_V6OK) != 0 &&
1673 strlen(s) <= 127U) {
1674 char buf[128]; /* see lib/bind9/getaddresses.c */
1675 char *d; /* zone delimiter */
1676 isc_uint32_t zone = 0; /* scope zone ID */
1679 d = strchr(buf, '%');
1683 if (inet_pton(AF_INET6, buf, &in6a) == 1) {
1685 #ifdef ISC_PLATFORM_HAVESCOPEID
1686 isc_result_t result;
1688 result = isc_netscope_pton(AF_INET6,
1692 if (result != ISC_R_SUCCESS)
1695 return (ISC_R_BADADDRESSFORM);
1699 isc_netaddr_fromin6(na, &in6a);
1700 isc_netaddr_setzone(na, zone);
1701 return (ISC_R_SUCCESS);
1705 return (ISC_R_UNEXPECTEDTOKEN);
1709 cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1710 isc_result_t result;
1711 CHECK(cfg_gettoken(pctx, 0));
1712 result = token_addr(pctx, flags, na);
1713 if (result == ISC_R_UNEXPECTEDTOKEN)
1714 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected IP address");
1720 cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
1721 isc_result_t result;
1722 isc_netaddr_t na_dummy;
1723 result = token_addr(pctx, flags, &na_dummy);
1724 return (ISC_TF(result == ISC_R_SUCCESS));
1728 cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
1729 isc_result_t result;
1731 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1733 if ((flags & CFG_ADDR_WILDOK) != 0 &&
1734 pctx->token.type == isc_tokentype_string &&
1735 strcmp(TOKEN_STRING(pctx), "*") == 0) {
1737 return (ISC_R_SUCCESS);
1739 if (pctx->token.type != isc_tokentype_number) {
1740 cfg_parser_error(pctx, CFG_LOG_NEAR,
1741 "expected port number or '*'");
1742 return (ISC_R_UNEXPECTEDTOKEN);
1744 if (pctx->token.value.as_ulong >= 65536U) {
1745 cfg_parser_error(pctx, CFG_LOG_NEAR,
1746 "port number out of range");
1747 return (ISC_R_UNEXPECTEDTOKEN);
1749 *port = (in_port_t)(pctx->token.value.as_ulong);
1750 return (ISC_R_SUCCESS);
1756 cfg_print_rawaddr(cfg_printer_t *pctx, isc_netaddr_t *na) {
1757 isc_result_t result;
1761 isc_buffer_init(&buf, text, sizeof(text));
1762 result = isc_netaddr_totext(na, &buf);
1763 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1764 cfg_print_chars(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf));
1770 parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1771 isc_result_t result;
1772 cfg_obj_t *obj = NULL;
1773 isc_netaddr_t netaddr;
1775 CHECK(cfg_create_obj(pctx, type, &obj));
1776 CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK, &netaddr));
1777 isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
1779 return (ISC_R_SUCCESS);
1785 cfg_type_t cfg_type_netaddr = {
1786 "netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_terminal,
1787 &cfg_rep_sockaddr, NULL
1793 cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
1796 cfg_obj_t *obj = NULL;
1797 isc_result_t result;
1798 isc_netaddr_t netaddr;
1799 unsigned int addrlen, prefixlen;
1802 CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
1803 CFG_ADDR_V6OK, &netaddr));
1804 switch (netaddr.family) {
1816 CHECK(cfg_peektoken(pctx, 0));
1817 if (pctx->token.type == isc_tokentype_special &&
1818 pctx->token.value.as_char == '/') {
1819 CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
1820 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1821 if (pctx->token.type != isc_tokentype_number) {
1822 cfg_parser_error(pctx, CFG_LOG_NEAR,
1823 "expected prefix length");
1824 return (ISC_R_UNEXPECTEDTOKEN);
1826 prefixlen = pctx->token.value.as_ulong;
1827 if (prefixlen > addrlen) {
1828 cfg_parser_error(pctx, CFG_LOG_NOPREP,
1829 "invalid prefix length");
1830 return (ISC_R_RANGE);
1833 prefixlen = addrlen;
1835 CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
1836 obj->value.netprefix.address = netaddr;
1837 obj->value.netprefix.prefixlen = prefixlen;
1839 return (ISC_R_SUCCESS);
1841 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
1846 print_netprefix(cfg_printer_t *pctx, cfg_obj_t *obj) {
1847 cfg_netprefix_t *p = &obj->value.netprefix;
1848 cfg_print_rawaddr(pctx, &p->address);
1849 cfg_print_chars(pctx, "/", 1);
1850 cfg_print_rawuint(pctx, p->prefixlen);
1854 cfg_obj_isnetprefix(cfg_obj_t *obj) {
1855 REQUIRE(obj != NULL);
1856 return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
1860 cfg_obj_asnetprefix(cfg_obj_t *obj, isc_netaddr_t *netaddr,
1861 unsigned int *prefixlen) {
1862 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
1863 *netaddr = obj->value.netprefix.address;
1864 *prefixlen = obj->value.netprefix.prefixlen;
1867 cfg_type_t cfg_type_netprefix = {
1868 "netprefix", cfg_parse_netprefix, print_netprefix, cfg_doc_terminal,
1869 &cfg_rep_netprefix, NULL
1873 parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type,
1874 int flags, cfg_obj_t **ret)
1876 isc_result_t result;
1877 isc_netaddr_t netaddr;
1879 cfg_obj_t *obj = NULL;
1881 CHECK(cfg_create_obj(pctx, type, &obj));
1882 CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
1883 CHECK(cfg_peektoken(pctx, 0));
1884 if (pctx->token.type == isc_tokentype_string &&
1885 strcasecmp(TOKEN_STRING(pctx), "port") == 0) {
1886 CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
1887 CHECK(cfg_parse_rawport(pctx, flags, &port));
1889 isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
1891 return (ISC_R_SUCCESS);
1898 static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
1899 cfg_type_t cfg_type_sockaddr = {
1900 "sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr,
1901 &cfg_rep_sockaddr, &sockaddr_flags
1905 cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1906 const unsigned int *flagp = type->of;
1907 return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret));
1911 cfg_print_sockaddr(cfg_printer_t *pctx, cfg_obj_t *obj) {
1912 isc_netaddr_t netaddr;
1914 char buf[ISC_NETADDR_FORMATSIZE];
1916 isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
1917 isc_netaddr_format(&netaddr, buf, sizeof(buf));
1918 cfg_print_cstr(pctx, buf);
1919 port = isc_sockaddr_getport(&obj->value.sockaddr);
1921 cfg_print_chars(pctx, " port ", 6);
1922 cfg_print_rawuint(pctx, port);
1927 cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
1928 const unsigned int *flagp = type->of;
1930 cfg_print_chars(pctx, "( ", 2);
1931 if (*flagp & CFG_ADDR_V4OK) {
1933 cfg_print_chars(pctx, " | ", 3);
1934 cfg_print_cstr(pctx, "<ipv4_address>");
1937 if (*flagp & CFG_ADDR_V6OK) {
1939 cfg_print_chars(pctx, " | ", 3);
1940 cfg_print_cstr(pctx, "<ipv6_address>");
1943 if (*flagp & CFG_ADDR_WILDOK) {
1945 cfg_print_chars(pctx, " | ", 3);
1946 cfg_print_chars(pctx, "*", 1);
1949 cfg_print_chars(pctx, " ) ", 3);
1950 if (*flagp & CFG_ADDR_WILDOK) {
1951 cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
1953 cfg_print_cstr(pctx, "[ port <integer> ]");
1958 cfg_obj_issockaddr(cfg_obj_t *obj) {
1959 REQUIRE(obj != NULL);
1960 return (ISC_TF(obj->type->rep == &cfg_rep_sockaddr));
1964 cfg_obj_assockaddr(cfg_obj_t *obj) {
1965 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
1966 return (&obj->value.sockaddr);
1970 cfg_gettoken(cfg_parser_t *pctx, int options) {
1971 isc_result_t result;
1974 return (ISC_R_SUCCESS);
1976 options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
1979 pctx->token.type = isc_tokentype_unknown;
1980 result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
1981 pctx->ungotten = ISC_FALSE;
1982 pctx->line = isc_lex_getsourceline(pctx->lexer);
1986 if (pctx->token.type == isc_tokentype_eof) {
1987 result = isc_lex_close(pctx->lexer);
1988 INSIST(result == ISC_R_NOMORE ||
1989 result == ISC_R_SUCCESS);
1991 if (isc_lex_getsourcename(pctx->lexer) != NULL) {
1993 * Closed an included file, not the main file.
1996 elt = ISC_LIST_TAIL(pctx->open_files->
1998 INSIST(elt != NULL);
1999 ISC_LIST_UNLINK(pctx->open_files->
2000 value.list, elt, link);
2001 ISC_LIST_APPEND(pctx->closed_files->
2002 value.list, elt, link);
2005 pctx->seen_eof = ISC_TRUE;
2010 /* More understandable than "ran out of space". */
2011 cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
2015 cfg_parser_error(pctx, 0, "%s",
2016 isc_result_totext(result));
2020 cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
2021 isc_result_totext(result));
2028 cfg_ungettoken(cfg_parser_t *pctx) {
2031 isc_lex_ungettoken(pctx->lexer, &pctx->token);
2032 pctx->ungotten = ISC_TRUE;
2036 cfg_peektoken(cfg_parser_t *pctx, int options) {
2037 isc_result_t result;
2038 CHECK(cfg_gettoken(pctx, options));
2039 cfg_ungettoken(pctx);
2045 * Get a string token, accepting both the quoted and the unquoted form.
2046 * Log an error if the next token is not a string.
2049 cfg_getstringtoken(cfg_parser_t *pctx) {
2050 isc_result_t result;
2052 result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
2053 if (result != ISC_R_SUCCESS)
2056 if (pctx->token.type != isc_tokentype_string &&
2057 pctx->token.type != isc_tokentype_qstring) {
2058 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
2059 return (ISC_R_UNEXPECTEDTOKEN);
2061 return (ISC_R_SUCCESS);
2065 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2067 va_start(args, fmt);
2068 parser_complain(pctx, ISC_FALSE, flags, fmt, args);
2074 cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2076 va_start(args, fmt);
2077 parser_complain(pctx, ISC_TRUE, flags, fmt, args);
2082 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
2085 current_file(cfg_parser_t *pctx) {
2086 static char none[] = "none";
2090 if (pctx->open_files == NULL)
2092 elt = ISC_LIST_TAIL(pctx->open_files->value.list);
2097 INSIST(fileobj->type == &cfg_type_qstring);
2098 return (fileobj->value.string.base);
2102 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
2103 unsigned int flags, const char *format,
2106 char tokenbuf[MAX_LOG_TOKEN + 10];
2107 static char where[ISC_DIR_PATHMAX + 100];
2108 static char message[2048];
2109 int level = ISC_LOG_ERROR;
2110 const char *prep = "";
2114 level = ISC_LOG_WARNING;
2116 snprintf(where, sizeof(where), "%s:%u: ",
2117 current_file(pctx), pctx->line);
2119 len = vsnprintf(message, sizeof(message), format, args);
2120 if (len >= sizeof(message))
2121 FATAL_ERROR(__FILE__, __LINE__,
2122 "error message would overflow");
2124 if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) {
2128 (void)cfg_gettoken(pctx, 0);
2130 if (pctx->token.type == isc_tokentype_eof) {
2131 snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
2132 } else if (pctx->token.type == isc_tokentype_unknown) {
2136 isc_lex_getlasttokentext(pctx->lexer,
2138 if (r.length > MAX_LOG_TOKEN)
2139 snprintf(tokenbuf, sizeof(tokenbuf),
2140 "'%.*s...'", MAX_LOG_TOKEN, r.base);
2142 snprintf(tokenbuf, sizeof(tokenbuf),
2143 "'%.*s'", (int)r.length, r.base);
2146 /* Choose a preposition. */
2147 if (flags & CFG_LOG_NEAR)
2149 else if (flags & CFG_LOG_BEFORE)
2156 isc_log_write(pctx->lctx, CAT, MOD, level,
2157 "%s%s%s%s", where, message, prep, tokenbuf);
2161 cfg_obj_log(cfg_obj_t *obj, isc_log_t *lctx, int level, const char *fmt, ...) {
2165 if (! isc_log_wouldlog(lctx, level))
2170 vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
2171 isc_log_write(lctx, CAT, MOD, level,
2173 obj->file == NULL ? "<unknown file>" : obj->file,
2179 cfg_obj_file(cfg_obj_t *obj) {
2184 cfg_obj_line(cfg_obj_t *obj) {
2189 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2192 obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
2194 return (ISC_R_NOMEMORY);
2196 obj->file = current_file(pctx);
2197 obj->line = pctx->line;
2199 return (ISC_R_SUCCESS);
2203 map_symtabitem_destroy(char *key, unsigned int type,
2204 isc_symvalue_t symval, void *userarg)
2206 cfg_obj_t *obj = symval.as_pointer;
2207 cfg_parser_t *pctx = (cfg_parser_t *)userarg;
2212 cfg_obj_destroy(pctx, &obj);
2217 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2218 isc_result_t result;
2219 isc_symtab_t *symtab = NULL;
2220 cfg_obj_t *obj = NULL;
2222 CHECK(cfg_create_obj(pctx, type, &obj));
2223 CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */
2224 map_symtabitem_destroy,
2225 pctx, ISC_FALSE, &symtab));
2227 obj->value.map.symtab = symtab;
2228 obj->value.map.id = NULL;
2231 return (ISC_R_SUCCESS);
2235 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
2240 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
2241 CLEANUP_OBJ(obj->value.map.id);
2242 isc_symtab_destroy(&obj->value.map.symtab);
2246 cfg_obj_istype(cfg_obj_t *obj, const cfg_type_t *type) {
2247 return (ISC_TF(obj->type == type));
2251 * Destroy 'obj', a configuration object created in 'pctx'.
2254 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
2255 cfg_obj_t *obj = *objp;
2256 obj->type->rep->free(pctx, obj);
2257 isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
2262 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
2268 cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
2269 type->doc(pctx, type);
2273 cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) {
2274 cfg_print_chars(pctx, "<", 1);
2275 cfg_print_cstr(pctx, type->name);
2276 cfg_print_chars(pctx, ">", 1);
2280 cfg_print_grammar(const cfg_type_t *type,
2281 void (*f)(void *closure, const char *text, int textlen),
2286 pctx.closure = closure;
2288 cfg_doc_obj(&pctx, type);