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
);