Add BIND 9.2.4rc7.
[dragonfly.git] / contrib / bind-9.2.4rc7 / lib / isccfg / parser.c
blobdf4ed7a93090680cc19716572689fb44eef1d779
1 /*
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.24 2004/03/28 23:09:55 marka Exp $ */
20 #include <config.h>
22 #include <isc/buffer.h>
23 #include <isc/dir.h>
24 #include <isc/formatcheck.h>
25 #include <isc/lex.h>
26 #include <isc/log.h>
27 #include <isc/mem.h>
28 #include <isc/net.h>
29 #include <isc/netaddr.h>
30 #include <isc/print.h>
31 #include <isc/string.h>
32 #include <isc/sockaddr.h>
33 #include <isc/util.h>
34 #include <isc/symtab.h>
36 #include <isccfg/cfg.h>
37 #include <isccfg/log.h>
39 /* Shorthand */
40 #define CAT CFG_LOGCATEGORY_CONFIG
41 #define MOD CFG_LOGMODULE_PARSER
43 #define QSTRING (ISC_LEXOPT_QSTRING | ISC_LEXOPT_QSTRINGMULTILINE)
46 * Pass one of these flags to parser_error() to include the
47 * token text in log message.
49 #define LOG_NEAR 0x00000001 /* Say "near <token>" */
50 #define LOG_BEFORE 0x00000002 /* Say "before <token>" */
51 #define LOG_NOPREP 0x00000004 /* Say just "<token>" */
53 #define MAP_SYM 1 /* Unique type for isc_symtab */
55 /* Clause may occur multiple times (e.g., "zone") */
56 #define CFG_CLAUSEFLAG_MULTI 0x00000001
57 /* Clause is obsolete */
58 #define CFG_CLAUSEFLAG_OBSOLETE 0x00000002
59 /* Clause is not implemented, and may never be */
60 #define CFG_CLAUSEFLAG_NOTIMP 0x00000004
61 /* Clause is not implemented yet */
62 #define CFG_CLAUSEFLAG_NYI 0x00000008
63 /* Default value has changed since earlier release */
64 #define CFG_CLAUSEFLAG_NEWDEFAULT 0x00000010
66 * Clause needs to be interpreted during parsing
67 * by calling a callback function, like the
68 * "directory" option.
70 #define CFG_CLAUSEFLAG_CALLBACK 0x00000020
73 * Flags defining whether to accept certain types of network addresses.
75 #define V4OK 0x00000001
76 #define V4PREFIXOK 0x00000002
77 #define V6OK 0x00000004
78 #define WILDOK 0x00000008
80 /* Check a return value. */
81 #define CHECK(op) \
82 do { result = (op); \
83 if (result != ISC_R_SUCCESS) goto cleanup; \
84 } while (0)
86 /* Clean up a configuration object if non-NULL. */
87 #define CLEANUP_OBJ(obj) \
88 do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
91 typedef struct cfg_clausedef cfg_clausedef_t;
92 typedef struct cfg_tuplefielddef cfg_tuplefielddef_t;
93 typedef struct cfg_printer cfg_printer_t;
94 typedef ISC_LIST(cfg_listelt_t) cfg_list_t;
95 typedef struct cfg_map cfg_map_t;
96 typedef struct cfg_rep cfg_rep_t;
99 * Function types for configuration object methods
102 typedef isc_result_t (*cfg_parsefunc_t)(cfg_parser_t *, const cfg_type_t *type,
103 cfg_obj_t **);
104 typedef void (*cfg_printfunc_t)(cfg_printer_t *, cfg_obj_t *);
105 typedef void (*cfg_freefunc_t)(cfg_parser_t *, cfg_obj_t *);
109 * Structure definitions
112 /* The parser object. */
113 struct cfg_parser {
114 isc_mem_t * mctx;
115 isc_log_t * lctx;
116 isc_lex_t * lexer;
117 unsigned int errors;
118 unsigned int warnings;
119 isc_token_t token;
121 /* We are at the end of all input. */
122 isc_boolean_t seen_eof;
124 /* The current token has been pushed back. */
125 isc_boolean_t ungotten;
128 * The stack of currently active files, represented
129 * as a configuration list of configuration strings.
130 * The head is the top-level file, subsequent elements
131 * (if any) are the nested include files, and the
132 * last element is the file currently being parsed.
134 cfg_obj_t * open_files;
137 * Names of files that we have parsed and closed
138 * and were previously on the open_file list.
139 * We keep these objects around after closing
140 * the files because the file names may still be
141 * referenced from other configuration objects
142 * for use in reporting semantic errors after
143 * parsing is complete.
145 cfg_obj_t * closed_files;
148 * Current line number. We maintain our own
149 * copy of this so that it is available even
150 * when a file has just been closed.
152 unsigned int line;
154 cfg_parsecallback_t callback;
155 void *callbackarg;
159 * A configuration printer object. This is an abstract
160 * interface to a destination to which text can be printed
161 * by calling the function 'f'.
163 struct cfg_printer {
164 void (*f)(void *closure, const char *text, int textlen);
165 void *closure;
166 int indent;
169 /* A clause definition. */
171 struct cfg_clausedef {
172 const char *name;
173 cfg_type_t *type;
174 unsigned int flags;
177 /* A tuple field definition. */
179 struct cfg_tuplefielddef {
180 const char *name;
181 cfg_type_t *type;
182 unsigned int flags;
185 /* A configuration object type definition. */
186 struct cfg_type {
187 const char *name; /* For debugging purposes only */
188 cfg_parsefunc_t parse;
189 cfg_printfunc_t print;
190 cfg_rep_t * rep; /* Data representation */
191 const void * of; /* For meta-types */
194 /* A keyword-type definition, for things like "port <integer>". */
196 typedef struct {
197 const char *name;
198 const cfg_type_t *type;
199 } keyword_type_t;
201 struct cfg_map {
202 cfg_obj_t *id; /* Used for 'named maps' like keys, zones, &c */
203 const cfg_clausedef_t * const *clausesets; /* The clauses that
204 can occur in this map;
205 used for printing */
206 isc_symtab_t *symtab;
209 typedef struct cfg_netprefix cfg_netprefix_t;
211 struct cfg_netprefix {
212 isc_netaddr_t address; /* IP4/IP6 */
213 unsigned int prefixlen;
217 * A configuration data representation.
219 struct cfg_rep {
220 const char * name; /* For debugging only */
221 cfg_freefunc_t free; /* How to free this kind of data. */
225 * A configuration object. This is the main building block
226 * of the configuration parse tree.
229 struct cfg_obj {
230 const cfg_type_t *type;
231 union {
232 isc_uint32_t uint32;
233 isc_uint64_t uint64;
234 isc_textregion_t string; /* null terminated, too */
235 isc_boolean_t boolean;
236 cfg_map_t map;
237 cfg_list_t list;
238 cfg_obj_t ** tuple;
239 isc_sockaddr_t sockaddr;
240 cfg_netprefix_t netprefix;
241 } value;
242 char * file;
243 unsigned int line;
247 /* A list element. */
249 struct cfg_listelt {
250 cfg_obj_t *obj;
251 ISC_LINK(cfg_listelt_t) link;
255 * Forward declarations of static functions.
258 static isc_result_t
259 create_cfgobj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
261 static isc_result_t
262 create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
264 static isc_result_t
265 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
267 static void
268 free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
270 static isc_result_t
271 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
272 cfg_obj_t **ret);
274 static void
275 free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
277 static isc_result_t
278 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
280 static isc_result_t
281 create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
283 static void
284 free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
286 static isc_result_t
287 get_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na);
289 static void
290 print(cfg_printer_t *pctx, const char *text, int len);
292 static void
293 print_void(cfg_printer_t *pctx, cfg_obj_t *obj);
295 static isc_result_t
296 parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype,
297 const cfg_type_t *othertype, cfg_obj_t **ret);
299 static isc_result_t
300 parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
302 static void
303 print_mapbody(cfg_printer_t *pctx, cfg_obj_t *obj);
305 static isc_result_t
306 parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
308 static void
309 print_map(cfg_printer_t *pctx, cfg_obj_t *obj);
311 static isc_result_t
312 parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
314 static isc_result_t
315 parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
317 static isc_result_t
318 parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
320 static void
321 print_list(cfg_printer_t *pctx, cfg_obj_t *obj);
323 static isc_result_t
324 parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
326 static void
327 print_tuple(cfg_printer_t *pctx, cfg_obj_t *obj);
329 static void
330 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
332 static isc_result_t
333 parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
335 static void
336 print_spacelist(cfg_printer_t *pctx, cfg_obj_t *obj);
338 static void
339 print_sockaddr(cfg_printer_t *pctx, cfg_obj_t *obj);
341 static isc_result_t
342 parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
344 static isc_result_t
345 parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
347 static void
348 print_bracketed_list(cfg_printer_t *pctx, cfg_obj_t *obj);
350 static isc_result_t
351 parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
353 static isc_result_t
354 parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
356 static void
357 print_keyvalue(cfg_printer_t *pctx, cfg_obj_t *obj);
359 static isc_result_t
360 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
361 cfg_type_t *elttype, isc_symtab_t *symtab,
362 isc_boolean_t callback);
364 static void
365 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
367 static isc_result_t
368 cfg_gettoken(cfg_parser_t *pctx, int options);
370 static void
371 cfg_ungettoken(cfg_parser_t *pctx);
373 static isc_result_t
374 cfg_peektoken(cfg_parser_t *pctx, int options);
376 static isc_result_t
377 cfg_getstringtoken(cfg_parser_t *pctx);
379 static void
380 parser_error(cfg_parser_t *pctx, unsigned int flags,
381 const char *fmt, ...) ISC_FORMAT_PRINTF(3, 4);
383 static void
384 parser_warning(cfg_parser_t *pctx, unsigned int flags,
385 const char *fmt, ...) ISC_FORMAT_PRINTF(3, 4);
387 static void
388 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
389 unsigned int flags, const char *format, va_list args);
391 static void
392 print_uint32(cfg_printer_t *pctx, cfg_obj_t *obj);
394 static void
395 print_ustring(cfg_printer_t *pctx, cfg_obj_t *obj);
397 static isc_result_t
398 parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
401 * Data representations. These correspond to members of the
402 * "value" union in struct cfg_obj (except "void", which does
403 * not need a union member).
406 cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
407 cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
408 cfg_rep_t cfg_rep_string = { "string", free_string };
409 cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
410 cfg_rep_t cfg_rep_map = { "map", free_map };
411 cfg_rep_t cfg_rep_list = { "list", free_list };
412 cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
413 cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
414 cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
415 cfg_rep_t cfg_rep_void = { "void", free_noop };
418 * Forward declarations of configuration type definitions.
419 * Additional types are declared publicly in cfg.h.
422 static cfg_type_t cfg_type_boolean;
423 static cfg_type_t cfg_type_uint32;
424 static cfg_type_t cfg_type_qstring;
425 static cfg_type_t cfg_type_astring;
426 static cfg_type_t cfg_type_ustring;
427 static cfg_type_t cfg_type_optional_port;
428 static cfg_type_t cfg_type_bracketed_aml;
429 static cfg_type_t cfg_type_acl;
430 static cfg_type_t cfg_type_portiplist;
431 static cfg_type_t cfg_type_bracketed_sockaddrlist;
432 static cfg_type_t cfg_type_sockaddr;
433 static cfg_type_t cfg_type_netaddr;
434 static cfg_type_t cfg_type_optional_keyref;
435 static cfg_type_t cfg_type_options;
436 static cfg_type_t cfg_type_view;
437 static cfg_type_t cfg_type_viewopts;
438 static cfg_type_t cfg_type_key;
439 static cfg_type_t cfg_type_server;
440 static cfg_type_t cfg_type_controls;
441 static cfg_type_t cfg_type_bracketed_sockaddrkeylist;
442 static cfg_type_t cfg_type_querysource4;
443 static cfg_type_t cfg_type_querysource6;
444 static cfg_type_t cfg_type_querysource;
445 static cfg_type_t cfg_type_sockaddr4wild;
446 static cfg_type_t cfg_type_sockaddr6wild;
447 static cfg_type_t cfg_type_sockaddr;
448 static cfg_type_t cfg_type_netprefix;
449 static cfg_type_t cfg_type_zone;
450 static cfg_type_t cfg_type_zoneopts;
451 static cfg_type_t cfg_type_logging;
452 static cfg_type_t cfg_type_optional_facility;
453 static cfg_type_t cfg_type_void;
454 static cfg_type_t cfg_type_optional_class;
455 static cfg_type_t cfg_type_destinationlist;
456 static cfg_type_t cfg_type_size;
457 static cfg_type_t cfg_type_sizenodefault;
458 static cfg_type_t cfg_type_negated;
459 static cfg_type_t cfg_type_addrmatchelt;
460 static cfg_type_t cfg_type_unsupported;
461 static cfg_type_t cfg_type_token;
462 static cfg_type_t cfg_type_server_key_kludge;
463 static cfg_type_t cfg_type_optional_facility;
464 static cfg_type_t cfg_type_logseverity;
465 static cfg_type_t cfg_type_logfile;
466 static cfg_type_t cfg_type_lwres;
467 static cfg_type_t cfg_type_controls_sockaddr;
468 static cfg_type_t cfg_type_notifytype;
469 static cfg_type_t cfg_type_dialuptype;
472 * Configuration type definitions.
475 /* tkey-dhkey */
477 static cfg_tuplefielddef_t tkey_dhkey_fields[] = {
478 { "name", &cfg_type_qstring, 0 },
479 { "keyid", &cfg_type_uint32, 0 },
480 { NULL, NULL, 0 }
483 static cfg_type_t cfg_type_tkey_dhkey = {
484 "tkey-dhkey", parse_tuple, print_tuple, &cfg_rep_tuple,
485 tkey_dhkey_fields
488 /* listen-on */
490 static cfg_tuplefielddef_t listenon_fields[] = {
491 { "port", &cfg_type_optional_port, 0 },
492 { "acl", &cfg_type_bracketed_aml, 0 },
493 { NULL, NULL, 0 }
495 static cfg_type_t cfg_type_listenon = {
496 "listenon", parse_tuple, print_tuple, &cfg_rep_tuple, listenon_fields };
498 /* acl */
500 static cfg_tuplefielddef_t acl_fields[] = {
501 { "name", &cfg_type_astring, 0 },
502 { "value", &cfg_type_bracketed_aml, 0 },
503 { NULL, NULL, 0 }
506 static cfg_type_t cfg_type_acl = {
507 "acl", parse_tuple, print_tuple, &cfg_rep_tuple, acl_fields };
511 * "sockaddrkeylist", a list of socket addresses with optional keys
512 * and an optional default port, as used in the masters option.
513 * E.g.,
514 * "port 1234 { 10.0.0.1 key foo; 1::2 port 69; }"
517 static cfg_tuplefielddef_t sockaddrkey_fields[] = {
518 { "sockaddr", &cfg_type_sockaddr, 0 },
519 { "key", &cfg_type_optional_keyref, 0 },
520 { NULL, NULL, 0 },
523 static cfg_type_t cfg_type_sockaddrkey = {
524 "sockaddrkey", parse_tuple, print_tuple, &cfg_rep_tuple,
525 sockaddrkey_fields
528 static cfg_type_t cfg_type_bracketed_sockaddrkeylist = {
529 "bracketed_sockaddrkeylist", parse_bracketed_list,
530 print_bracketed_list, &cfg_rep_list, &cfg_type_sockaddrkey
533 static cfg_tuplefielddef_t sockaddrkeylist_fields[] = {
534 { "port", &cfg_type_optional_port, 0 },
535 { "addresses", &cfg_type_bracketed_sockaddrkeylist, 0 },
536 { NULL, NULL, 0 }
538 static cfg_type_t cfg_type_sockaddrkeylist = {
539 "sockaddrkeylist", parse_tuple, print_tuple, &cfg_rep_tuple,
540 sockaddrkeylist_fields
544 * A list of socket addresses with an optional default port,
545 * as used in the also-notify option. E.g.,
546 * "port 1234 { 10.0.0.1; 1::2 port 69; }"
548 static cfg_tuplefielddef_t portiplist_fields[] = {
549 { "port", &cfg_type_optional_port, 0 },
550 { "addresses", &cfg_type_bracketed_sockaddrlist, 0 },
551 { NULL, NULL, 0 }
553 static cfg_type_t cfg_type_portiplist = {
554 "portiplist", parse_tuple, print_tuple, &cfg_rep_tuple,
555 portiplist_fields
559 * A public key, as in the "pubkey" statement.
561 static cfg_tuplefielddef_t pubkey_fields[] = {
562 { "flags", &cfg_type_uint32, 0 },
563 { "protocol", &cfg_type_uint32, 0 },
564 { "algorithm", &cfg_type_uint32, 0 },
565 { "key", &cfg_type_qstring, 0 },
566 { NULL, NULL, 0 }
568 static cfg_type_t cfg_type_pubkey = {
569 "pubkey", parse_tuple, print_tuple, &cfg_rep_tuple, pubkey_fields };
573 * A list of RR types, used in grant statements.
574 * Note that the old parser allows quotes around the RR type names.
576 static cfg_type_t cfg_type_rrtypelist = {
577 "rrtypelist", parse_spacelist, print_spacelist, &cfg_rep_list,
578 &cfg_type_astring
581 static const char *mode_enums[] = { "grant", "deny", NULL };
582 static cfg_type_t cfg_type_mode = {
583 "mode", parse_enum, print_ustring, &cfg_rep_string,
584 &mode_enums
587 static const char *matchtype_enums[] = {
588 "name", "subdomain", "wildcard", "self", NULL };
589 static cfg_type_t cfg_type_matchtype = {
590 "matchtype", parse_enum, print_ustring, &cfg_rep_string,
591 &matchtype_enums
595 * A grant statement, used in the update policy.
597 static cfg_tuplefielddef_t grant_fields[] = {
598 { "mode", &cfg_type_mode, 0 },
599 { "identity", &cfg_type_astring, 0 }, /* domain name */
600 { "matchtype", &cfg_type_matchtype, 0 },
601 { "name", &cfg_type_astring, 0 }, /* domain name */
602 { "types", &cfg_type_rrtypelist, 0 },
603 { NULL, NULL, 0 }
605 static cfg_type_t cfg_type_grant = {
606 "grant", parse_tuple, print_tuple, &cfg_rep_tuple, grant_fields };
608 static cfg_type_t cfg_type_updatepolicy = {
609 "update_policy", parse_bracketed_list, print_bracketed_list,
610 &cfg_rep_list, &cfg_type_grant
614 * A view statement.
616 static cfg_tuplefielddef_t view_fields[] = {
617 { "name", &cfg_type_astring, 0 },
618 { "class", &cfg_type_optional_class, 0 },
619 { "options", &cfg_type_viewopts, 0 },
620 { NULL, NULL, 0 }
622 static cfg_type_t cfg_type_view = {
623 "view", parse_tuple, print_tuple, &cfg_rep_tuple, view_fields };
626 * A zone statement.
628 static cfg_tuplefielddef_t zone_fields[] = {
629 { "name", &cfg_type_astring, 0 },
630 { "class", &cfg_type_optional_class, 0 },
631 { "options", &cfg_type_zoneopts, 0 },
632 { NULL, NULL, 0 }
634 static cfg_type_t cfg_type_zone = {
635 "zone", parse_tuple, print_tuple, &cfg_rep_tuple, zone_fields };
638 * A "category" clause in the "logging" statement.
640 static cfg_tuplefielddef_t category_fields[] = {
641 { "name", &cfg_type_astring, 0 },
642 { "destinations", &cfg_type_destinationlist,0 },
643 { NULL, NULL, 0 }
645 static cfg_type_t cfg_type_category = {
646 "category", parse_tuple, print_tuple, &cfg_rep_tuple, category_fields };
650 * A trusted key, as used in the "trusted-keys" statement.
652 static cfg_tuplefielddef_t trustedkey_fields[] = {
653 { "name", &cfg_type_astring, 0 },
654 { "flags", &cfg_type_uint32, 0 },
655 { "protocol", &cfg_type_uint32, 0 },
656 { "algorithm", &cfg_type_uint32, 0 },
657 { "key", &cfg_type_qstring, 0 },
658 { NULL, NULL, 0 }
660 static cfg_type_t cfg_type_trustedkey = {
661 "trustedkey", parse_tuple, print_tuple, &cfg_rep_tuple,
662 trustedkey_fields
666 static keyword_type_t wild_class_kw = { "class", &cfg_type_ustring };
668 static cfg_type_t cfg_type_optional_wild_class = {
669 "optional_wild_class", parse_optional_keyvalue,
670 print_keyvalue, &cfg_rep_string, &wild_class_kw
673 static keyword_type_t wild_type_kw = { "type", &cfg_type_ustring };
675 static cfg_type_t cfg_type_optional_wild_type = {
676 "optional_wild_type", parse_optional_keyvalue,
677 print_keyvalue, &cfg_rep_string, &wild_type_kw
680 static keyword_type_t wild_name_kw = { "name", &cfg_type_qstring };
682 static cfg_type_t cfg_type_optional_wild_name = {
683 "optional_wild_name", parse_optional_keyvalue,
684 print_keyvalue, &cfg_rep_string, &wild_name_kw
688 * An rrset ordering element.
690 static cfg_tuplefielddef_t rrsetorderingelement_fields[] = {
691 { "class", &cfg_type_optional_wild_class, 0 },
692 { "type", &cfg_type_optional_wild_type, 0 },
693 { "name", &cfg_type_optional_wild_name, 0 },
694 { "order", &cfg_type_ustring, 0 }, /* must be literal "order" */
695 { "ordering", &cfg_type_ustring, 0 },
696 { NULL, NULL, 0 }
698 static cfg_type_t cfg_type_rrsetorderingelement = {
699 "rrsetorderingelement", parse_tuple, print_tuple, &cfg_rep_tuple,
700 rrsetorderingelement_fields
704 * A global or view "check-names" option. Note that the zone
705 * "check-names" option has a different syntax.
707 static cfg_tuplefielddef_t checknames_fields[] = {
708 { "type", &cfg_type_ustring, 0 },
709 { "mode", &cfg_type_ustring, 0 },
710 { NULL, NULL, 0 }
712 static cfg_type_t cfg_type_checknames = {
713 "checknames", parse_tuple, print_tuple, &cfg_rep_tuple,
714 checknames_fields
717 static cfg_type_t cfg_type_bracketed_sockaddrlist = {
718 "bracketed_sockaddrlist", parse_bracketed_list, print_bracketed_list,
719 &cfg_rep_list, &cfg_type_sockaddr
722 static cfg_type_t cfg_type_rrsetorder = {
723 "rrsetorder", parse_bracketed_list, print_bracketed_list,
724 &cfg_rep_list, &cfg_type_rrsetorderingelement
727 static keyword_type_t port_kw = { "port", &cfg_type_uint32 };
729 static cfg_type_t cfg_type_optional_port = {
730 "optional_port", parse_optional_keyvalue, print_keyvalue,
731 &cfg_rep_uint32, &port_kw
734 /* A list of keys, as in the "key" clause of the controls statement. */
735 static cfg_type_t cfg_type_keylist = {
736 "keylist", parse_bracketed_list, print_bracketed_list, &cfg_rep_list,
737 &cfg_type_astring
740 static cfg_type_t cfg_type_trustedkeys = {
741 "trusted-keys", parse_bracketed_list, print_bracketed_list, &cfg_rep_list,
742 &cfg_type_trustedkey
746 * An implicit list. These are formed by clauses that occur multiple times.
748 static cfg_type_t cfg_type_implicitlist = {
749 "implicitlist", NULL, print_list, &cfg_rep_list, NULL };
751 static const char *forwardtype_enums[] = { "first", "only", NULL };
752 static cfg_type_t cfg_type_forwardtype = {
753 "forwardtype", parse_enum, print_ustring, &cfg_rep_string,
754 &forwardtype_enums
757 static const char *zonetype_enums[] = {
758 "master", "slave", "stub", "hint", "forward", "delegation-only", NULL };
759 static cfg_type_t cfg_type_zonetype = {
760 "zonetype", parse_enum, print_ustring, &cfg_rep_string,
761 &zonetype_enums
764 static const char *loglevel_enums[] = {
765 "critical", "error", "warning", "notice", "info", "dynamic", NULL };
766 static cfg_type_t cfg_type_loglevel = {
767 "loglevel", parse_enum, print_ustring, &cfg_rep_string,
768 &loglevel_enums
771 static const char *transferformat_enums[] = {
772 "many-answers", "one-answer", NULL };
773 static cfg_type_t cfg_type_transferformat = {
774 "transferformat", parse_enum, print_ustring, &cfg_rep_string,
775 &transferformat_enums
779 * Clauses that can be found within the top level of the named.conf
780 * file only.
782 static cfg_clausedef_t
783 namedconf_clauses[] = {
784 { "options", &cfg_type_options, 0 },
785 { "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI },
786 { "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI },
787 { "logging", &cfg_type_logging, 0 },
788 { "view", &cfg_type_view, CFG_CLAUSEFLAG_MULTI },
789 { "lwres", &cfg_type_lwres, CFG_CLAUSEFLAG_MULTI },
790 { NULL, NULL, 0 }
794 * Clauses that can occur at the top level or in the view
795 * statement, but not in the options block.
797 static cfg_clausedef_t
798 namedconf_or_view_clauses[] = {
799 { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
800 { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI },
801 { "server", &cfg_type_server, CFG_CLAUSEFLAG_MULTI },
802 #ifdef ISC_RFC2535
803 { "trusted-keys", &cfg_type_trustedkeys, CFG_CLAUSEFLAG_MULTI },
804 #else
805 { "trusted-keys", &cfg_type_trustedkeys,
806 CFG_CLAUSEFLAG_MULTI|CFG_CLAUSEFLAG_OBSOLETE },
807 #endif
808 { NULL, NULL, 0 }
812 * Clauses that can be found within the 'options' statement.
814 static cfg_clausedef_t
815 options_clauses[] = {
816 { "blackhole", &cfg_type_bracketed_aml, 0 },
817 { "coresize", &cfg_type_size, 0 },
818 { "datasize", &cfg_type_size, 0 },
819 { "deallocate-on-exit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
820 { "directory", &cfg_type_qstring, CFG_CLAUSEFLAG_CALLBACK },
821 { "dump-file", &cfg_type_qstring, 0 },
822 { "fake-iquery", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
823 { "files", &cfg_type_size, 0 },
824 { "has-old-clients", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
825 { "heartbeat-interval", &cfg_type_uint32, 0 },
826 { "host-statistics", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTIMP },
827 { "interface-interval", &cfg_type_uint32, 0 },
828 { "listen-on", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
829 { "listen-on-v6", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
830 { "match-mapped-addresses", &cfg_type_boolean, 0 },
831 { "memstatistics-file", &cfg_type_qstring, CFG_CLAUSEFLAG_NOTIMP },
832 { "multiple-cnames", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
833 { "named-xfer", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
834 { "pid-file", &cfg_type_qstring, 0 },
835 { "port", &cfg_type_uint32, 0 },
836 { "random-device", &cfg_type_qstring, 0 },
837 { "recursive-clients", &cfg_type_uint32, 0 },
838 { "rrset-order", &cfg_type_rrsetorder, CFG_CLAUSEFLAG_NOTIMP },
839 { "serial-queries", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
840 { "serial-query-rate", &cfg_type_uint32, 0 },
841 { "stacksize", &cfg_type_size, 0 },
842 { "statistics-file", &cfg_type_qstring, 0 },
843 { "statistics-interval", &cfg_type_uint32, CFG_CLAUSEFLAG_NYI },
844 { "tcp-clients", &cfg_type_uint32, 0 },
845 { "tkey-dhkey", &cfg_type_tkey_dhkey, 0 },
846 { "tkey-gssapi-credential", &cfg_type_qstring, 0 },
847 { "tkey-domain", &cfg_type_qstring, 0 },
848 { "transfers-per-ns", &cfg_type_uint32, 0 },
849 { "transfers-in", &cfg_type_uint32, 0 },
850 { "transfers-out", &cfg_type_uint32, 0 },
851 { "treat-cr-as-space", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
852 { "use-id-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
853 { "use-ixfr", &cfg_type_boolean, 0 },
854 { "version", &cfg_type_qstring, 0 },
855 { NULL, NULL, 0 }
859 static cfg_type_t cfg_type_namelist = {
860 "namelist", parse_bracketed_list, print_bracketed_list,
861 &cfg_rep_list, &cfg_type_qstring };
863 static keyword_type_t exclude_kw = { "exclude", &cfg_type_namelist };
865 static cfg_type_t cfg_type_optional_exclude = {
866 "optional_exclude", parse_optional_keyvalue, print_keyvalue,
867 &cfg_rep_list, &exclude_kw };
870 * Clauses that can be found within the 'view' statement,
871 * with defaults in the 'options' statement.
874 static cfg_clausedef_t
875 view_clauses[] = {
876 { "allow-recursion", &cfg_type_bracketed_aml, 0 },
877 { "allow-v6-synthesis", &cfg_type_bracketed_aml, 0 },
878 { "sortlist", &cfg_type_bracketed_aml, 0 },
879 { "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_NOTIMP },
880 { "auth-nxdomain", &cfg_type_boolean, CFG_CLAUSEFLAG_NEWDEFAULT },
881 { "minimal-responses", &cfg_type_boolean, 0 },
882 { "recursion", &cfg_type_boolean, 0 },
883 { "provide-ixfr", &cfg_type_boolean, 0 },
884 { "request-ixfr", &cfg_type_boolean, 0 },
885 { "fetch-glue", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
886 { "rfc2308-type1", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
887 { "additional-from-auth", &cfg_type_boolean, 0 },
888 { "additional-from-cache", &cfg_type_boolean, 0 },
890 * Note that the query-source option syntax is different
891 * from the other -source options.
893 { "query-source", &cfg_type_querysource4, 0 },
894 { "query-source-v6", &cfg_type_querysource6, 0 },
895 { "cleaning-interval", &cfg_type_uint32, 0 },
896 { "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
897 { "lame-ttl", &cfg_type_uint32, 0 },
898 { "max-ncache-ttl", &cfg_type_uint32, 0 },
899 { "max-cache-ttl", &cfg_type_uint32, 0 },
900 { "transfer-format", &cfg_type_transferformat, 0 },
901 { "max-cache-size", &cfg_type_sizenodefault, 0 },
902 { "check-names", &cfg_type_checknames,
903 CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_NOTIMP },
904 { "cache-file", &cfg_type_qstring, 0 },
905 { "root-delegation-only", &cfg_type_optional_exclude, 0 },
906 { NULL, NULL, 0 }
910 * Clauses that can be found within the 'view' statement only.
912 static cfg_clausedef_t
913 view_only_clauses[] = {
914 { "match-clients", &cfg_type_bracketed_aml, 0 },
915 { "match-destinations", &cfg_type_bracketed_aml, 0 },
916 { "match-recursive-only", &cfg_type_boolean, 0 },
917 { NULL, NULL, 0 }
921 * Clauses that can be found in a 'zone' statement,
922 * with defaults in the 'view' or 'options' statement.
924 static cfg_clausedef_t
925 zone_clauses[] = {
926 { "allow-query", &cfg_type_bracketed_aml, 0 },
927 { "allow-transfer", &cfg_type_bracketed_aml, 0 },
928 { "allow-update-forwarding", &cfg_type_bracketed_aml, 0 },
929 { "allow-notify", &cfg_type_bracketed_aml, 0 },
930 { "notify", &cfg_type_notifytype, 0 },
931 { "notify-source", &cfg_type_sockaddr4wild, 0 },
932 { "notify-source-v6", &cfg_type_sockaddr6wild, 0 },
933 { "also-notify", &cfg_type_portiplist, 0 },
934 { "dialup", &cfg_type_dialuptype, 0 },
935 { "forward", &cfg_type_forwardtype, 0 },
936 { "forwarders", &cfg_type_portiplist, 0 },
937 { "maintain-ixfr-base", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
938 { "max-ixfr-log-size", &cfg_type_size, CFG_CLAUSEFLAG_OBSOLETE },
939 { "transfer-source", &cfg_type_sockaddr4wild, 0 },
940 { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 },
941 { "max-transfer-time-in", &cfg_type_uint32, 0 },
942 { "max-transfer-time-out", &cfg_type_uint32, 0 },
943 { "max-transfer-idle-in", &cfg_type_uint32, 0 },
944 { "max-transfer-idle-out", &cfg_type_uint32, 0 },
945 { "max-retry-time", &cfg_type_uint32, 0 },
946 { "min-retry-time", &cfg_type_uint32, 0 },
947 { "max-refresh-time", &cfg_type_uint32, 0 },
948 { "min-refresh-time", &cfg_type_uint32, 0 },
949 { "sig-validity-interval", &cfg_type_uint32, 0 },
950 { "zone-statistics", &cfg_type_boolean, 0 },
951 { NULL, NULL, 0 }
955 * Clauses that can be found in a 'zone' statement
956 * only.
958 static cfg_clausedef_t
959 zone_only_clauses[] = {
960 { "type", &cfg_type_zonetype, 0 },
961 { "allow-update", &cfg_type_bracketed_aml, 0 },
962 { "file", &cfg_type_qstring, 0 },
963 { "ixfr-base", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
964 { "ixfr-tmp-file", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
965 { "masters", &cfg_type_sockaddrkeylist, 0 },
966 { "pubkey", &cfg_type_pubkey,
967 CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_OBSOLETE },
968 { "update-policy", &cfg_type_updatepolicy, 0 },
969 { "database", &cfg_type_astring, 0 },
970 { "delegation-only", &cfg_type_boolean, 0 },
972 * Note that the format of the check-names option is different between
973 * the zone options and the global/view options. Ugh.
975 { "check-names", &cfg_type_ustring, CFG_CLAUSEFLAG_NOTIMP },
976 { NULL, NULL, 0 }
980 /* The top-level named.conf syntax. */
982 static cfg_clausedef_t *
983 namedconf_clausesets[] = {
984 namedconf_clauses,
985 namedconf_or_view_clauses,
986 NULL
989 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_namedconf = {
990 "namedconf", parse_mapbody, print_mapbody, &cfg_rep_map,
991 namedconf_clausesets
994 /* The "options" statement syntax. */
996 static cfg_clausedef_t *
997 options_clausesets[] = {
998 options_clauses,
999 view_clauses,
1000 zone_clauses,
1001 NULL
1003 static cfg_type_t cfg_type_options = {
1004 "options", parse_map, print_map, &cfg_rep_map, options_clausesets };
1006 /* The "view" statement syntax. */
1008 static cfg_clausedef_t *
1009 view_clausesets[] = {
1010 view_only_clauses,
1011 namedconf_or_view_clauses,
1012 view_clauses,
1013 zone_clauses,
1014 NULL
1016 static cfg_type_t cfg_type_viewopts = {
1017 "view", parse_map, print_map, &cfg_rep_map, view_clausesets };
1019 /* The "zone" statement syntax. */
1021 static cfg_clausedef_t *
1022 zone_clausesets[] = {
1023 zone_only_clauses,
1024 zone_clauses,
1025 NULL
1027 static cfg_type_t cfg_type_zoneopts = {
1028 "zoneopts", parse_map, print_map, &cfg_rep_map, zone_clausesets };
1031 * Clauses that can be found within the 'key' statement.
1033 static cfg_clausedef_t
1034 key_clauses[] = {
1035 { "algorithm", &cfg_type_astring, 0 },
1036 { "secret", &cfg_type_astring, 0 },
1037 { NULL, NULL, 0 }
1040 static cfg_clausedef_t *
1041 key_clausesets[] = {
1042 key_clauses,
1043 NULL
1045 static cfg_type_t cfg_type_key = {
1046 "key", parse_named_map, print_map, &cfg_rep_map, key_clausesets };
1050 * Clauses that can be found in a 'server' statement.
1052 static cfg_clausedef_t
1053 server_clauses[] = {
1054 { "bogus", &cfg_type_boolean, 0 },
1055 { "provide-ixfr", &cfg_type_boolean, 0 },
1056 { "request-ixfr", &cfg_type_boolean, 0 },
1057 { "support-ixfr", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1058 { "transfers", &cfg_type_uint32, 0 },
1059 { "transfer-format", &cfg_type_transferformat, 0 },
1060 { "keys", &cfg_type_server_key_kludge, 0 },
1061 { "edns", &cfg_type_boolean, 0 },
1062 { NULL, NULL, 0 }
1064 static cfg_clausedef_t *
1065 server_clausesets[] = {
1066 server_clauses,
1067 NULL
1069 static cfg_type_t cfg_type_server = {
1070 "server", parse_addressed_map, print_map, &cfg_rep_map,
1071 server_clausesets
1076 * Clauses that can be found in a 'channel' clause in the
1077 * 'logging' statement.
1079 * These have some additional constraints that need to be
1080 * checked after parsing:
1081 * - There must exactly one of file/syslog/null/stderr
1084 static cfg_clausedef_t
1085 channel_clauses[] = {
1086 /* Destinations. We no longer require these to be first. */
1087 { "file", &cfg_type_logfile, 0 },
1088 { "syslog", &cfg_type_optional_facility, 0 },
1089 { "null", &cfg_type_void, 0 },
1090 { "stderr", &cfg_type_void, 0 },
1091 /* Options. We now accept these for the null channel, too. */
1092 { "severity", &cfg_type_logseverity, 0 },
1093 { "print-time", &cfg_type_boolean, 0 },
1094 { "print-severity", &cfg_type_boolean, 0 },
1095 { "print-category", &cfg_type_boolean, 0 },
1096 { NULL, NULL, 0 }
1098 static cfg_clausedef_t *
1099 channel_clausesets[] = {
1100 channel_clauses,
1101 NULL
1103 static cfg_type_t cfg_type_channel = {
1104 "channel", parse_named_map, print_map,
1105 &cfg_rep_map, channel_clausesets
1108 /* A list of log destination, used in the "category" clause. */
1109 static cfg_type_t cfg_type_destinationlist = {
1110 "destinationlist", parse_bracketed_list, print_bracketed_list,
1111 &cfg_rep_list, &cfg_type_astring };
1114 * Clauses that can be found in a 'logging' statement.
1116 static cfg_clausedef_t
1117 logging_clauses[] = {
1118 { "channel", &cfg_type_channel, CFG_CLAUSEFLAG_MULTI },
1119 { "category", &cfg_type_category, CFG_CLAUSEFLAG_MULTI },
1120 { NULL, NULL, 0 }
1122 static cfg_clausedef_t *
1123 logging_clausesets[] = {
1124 logging_clauses,
1125 NULL
1127 static cfg_type_t cfg_type_logging = {
1128 "logging", parse_map, print_map, &cfg_rep_map, logging_clausesets };
1131 /* Functions. */
1133 static void
1134 print_obj(cfg_printer_t *pctx, cfg_obj_t *obj) {
1135 obj->type->print(pctx, obj);
1138 static void
1139 print(cfg_printer_t *pctx, const char *text, int len) {
1140 pctx->f(pctx->closure, text, len);
1143 static void
1144 print_open(cfg_printer_t *pctx) {
1145 print(pctx, "{\n", 2);
1146 pctx->indent++;
1149 static void
1150 print_indent(cfg_printer_t *pctx) {
1151 int indent = pctx->indent;
1152 while (indent > 0) {
1153 print(pctx, "\t", 1);
1154 indent--;
1158 static void
1159 print_close(cfg_printer_t *pctx) {
1160 pctx->indent--;
1161 print_indent(pctx);
1162 print(pctx, "}", 1);
1165 static isc_result_t
1166 parse(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1167 isc_result_t result;
1168 INSIST(ret != NULL && *ret == NULL);
1169 result = type->parse(pctx, type, ret);
1170 if (result != ISC_R_SUCCESS)
1171 return (result);
1172 INSIST(*ret != NULL);
1173 return (ISC_R_SUCCESS);
1176 void
1177 cfg_print(cfg_obj_t *obj,
1178 void (*f)(void *closure, const char *text, int textlen),
1179 void *closure)
1181 cfg_printer_t pctx;
1182 pctx.f = f;
1183 pctx.closure = closure;
1184 pctx.indent = 0;
1185 obj->type->print(&pctx, obj);
1189 /* Tuples. */
1191 static isc_result_t
1192 create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1193 isc_result_t result;
1194 const cfg_tuplefielddef_t *fields = type->of;
1195 const cfg_tuplefielddef_t *f;
1196 cfg_obj_t *obj = NULL;
1197 unsigned int nfields = 0;
1198 int i;
1200 for (f = fields; f->name != NULL; f++)
1201 nfields++;
1203 CHECK(create_cfgobj(pctx, type, &obj));
1204 obj->value.tuple = isc_mem_get(pctx->mctx,
1205 nfields * sizeof(cfg_obj_t *));
1206 if (obj->value.tuple == NULL) {
1207 result = ISC_R_NOMEMORY;
1208 goto cleanup;
1210 for (f = fields, i = 0; f->name != NULL; f++, i++)
1211 obj->value.tuple[i] = NULL;
1212 *ret = obj;
1213 return (ISC_R_SUCCESS);
1215 cleanup:
1216 if (obj != NULL)
1217 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
1218 return (result);
1221 static isc_result_t
1222 parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1224 isc_result_t result;
1225 const cfg_tuplefielddef_t *fields = type->of;
1226 const cfg_tuplefielddef_t *f;
1227 cfg_obj_t *obj = NULL;
1228 unsigned int i;
1230 CHECK(create_tuple(pctx, type, &obj));
1231 for (f = fields, i = 0; f->name != NULL; f++, i++)
1232 CHECK(parse(pctx, f->type, &obj->value.tuple[i]));
1234 *ret = obj;
1235 return (ISC_R_SUCCESS);
1237 cleanup:
1238 CLEANUP_OBJ(obj);
1239 return (result);
1242 static void
1243 print_tuple(cfg_printer_t *pctx, cfg_obj_t *obj) {
1244 unsigned int i;
1245 const cfg_tuplefielddef_t *fields = obj->type->of;
1246 const cfg_tuplefielddef_t *f;
1247 isc_boolean_t need_space = ISC_FALSE;
1249 for (f = fields, i = 0; f->name != NULL; f++, i++) {
1250 cfg_obj_t *fieldobj = obj->value.tuple[i];
1251 if (need_space)
1252 print(pctx, " ", 1);
1253 print_obj(pctx, fieldobj);
1254 need_space = ISC_TF(fieldobj->type->print != print_void);
1258 static void
1259 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
1260 unsigned int i;
1261 const cfg_tuplefielddef_t *fields = obj->type->of;
1262 const cfg_tuplefielddef_t *f;
1263 unsigned int nfields = 0;
1265 if (obj->value.tuple == NULL)
1266 return;
1268 for (f = fields, i = 0; f->name != NULL; f++, i++) {
1269 CLEANUP_OBJ(obj->value.tuple[i]);
1270 nfields++;
1272 isc_mem_put(pctx->mctx, obj->value.tuple,
1273 nfields * sizeof(cfg_obj_t *));
1276 isc_boolean_t
1277 cfg_obj_istuple(cfg_obj_t *obj) {
1278 REQUIRE(obj != NULL);
1279 return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
1282 cfg_obj_t *
1283 cfg_tuple_get(cfg_obj_t *tupleobj, const char* name) {
1284 unsigned int i;
1285 const cfg_tuplefielddef_t *fields;
1286 const cfg_tuplefielddef_t *f;
1288 REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
1290 fields = tupleobj->type->of;
1291 for (f = fields, i = 0; f->name != NULL; f++, i++) {
1292 if (strcmp(f->name, name) == 0)
1293 return (tupleobj->value.tuple[i]);
1295 INSIST(0);
1296 return (NULL);
1300 * Parse a required special character.
1302 static isc_result_t
1303 parse_special(cfg_parser_t *pctx, int special) {
1304 isc_result_t result;
1305 CHECK(cfg_gettoken(pctx, 0));
1306 if (pctx->token.type == isc_tokentype_special &&
1307 pctx->token.value.as_char == special)
1308 return (ISC_R_SUCCESS);
1310 parser_error(pctx, LOG_NEAR, "'%c' expected", special);
1311 return (ISC_R_UNEXPECTEDTOKEN);
1312 cleanup:
1313 return (result);
1317 * Parse a required semicolon. If it is not there, log
1318 * an error and increment the error count but continue
1319 * parsing. Since the next token is pushed back,
1320 * care must be taken to make sure it is eventually
1321 * consumed or an infinite loop may result.
1323 static isc_result_t
1324 parse_semicolon(cfg_parser_t *pctx) {
1325 isc_result_t result;
1326 CHECK(cfg_gettoken(pctx, 0));
1327 if (pctx->token.type == isc_tokentype_special &&
1328 pctx->token.value.as_char == ';')
1329 return (ISC_R_SUCCESS);
1331 parser_error(pctx, LOG_BEFORE, "missing ';'");
1332 cfg_ungettoken(pctx);
1333 cleanup:
1334 return (result);
1338 * Parse EOF, logging and returning an error if not there.
1340 static isc_result_t
1341 parse_eof(cfg_parser_t *pctx) {
1342 isc_result_t result;
1343 CHECK(cfg_gettoken(pctx, 0));
1345 if (pctx->token.type == isc_tokentype_eof)
1346 return (ISC_R_SUCCESS);
1348 parser_error(pctx, LOG_NEAR, "syntax error");
1349 return (ISC_R_UNEXPECTEDTOKEN);
1350 cleanup:
1351 return(result);
1354 /* A list of files, used internally for pctx->files. */
1356 static cfg_type_t cfg_type_filelist = {
1357 "filelist", NULL, print_list, &cfg_rep_list,
1358 &cfg_type_qstring
1361 isc_result_t
1362 cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret)
1364 isc_result_t result;
1365 cfg_parser_t *pctx;
1366 isc_lexspecials_t specials;
1368 REQUIRE(mctx != NULL);
1369 REQUIRE(ret != NULL && *ret == NULL);
1371 pctx = isc_mem_get(mctx, sizeof(*pctx));
1372 if (pctx == NULL)
1373 return (ISC_R_NOMEMORY);
1375 pctx->mctx = mctx;
1376 pctx->lctx = lctx;
1377 pctx->lexer = NULL;
1378 pctx->seen_eof = ISC_FALSE;
1379 pctx->ungotten = ISC_FALSE;
1380 pctx->errors = 0;
1381 pctx->warnings = 0;
1382 pctx->open_files = NULL;
1383 pctx->closed_files = NULL;
1384 pctx->line = 0;
1385 pctx->callback = NULL;
1386 pctx->callbackarg = NULL;
1387 pctx->token.type = isc_tokentype_unknown;
1389 memset(specials, 0, sizeof(specials));
1390 specials['{'] = 1;
1391 specials['}'] = 1;
1392 specials[';'] = 1;
1393 specials['/'] = 1;
1394 specials['"'] = 1;
1395 specials['!'] = 1;
1397 CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer));
1399 isc_lex_setspecials(pctx->lexer, specials);
1400 isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C |
1401 ISC_LEXCOMMENT_CPLUSPLUS |
1402 ISC_LEXCOMMENT_SHELL));
1404 CHECK(create_list(pctx, &cfg_type_filelist, &pctx->open_files));
1405 CHECK(create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
1407 *ret = pctx;
1408 return (ISC_R_SUCCESS);
1410 cleanup:
1411 if (pctx->lexer != NULL)
1412 isc_lex_destroy(&pctx->lexer);
1413 CLEANUP_OBJ(pctx->open_files);
1414 CLEANUP_OBJ(pctx->closed_files);
1415 isc_mem_put(mctx, pctx, sizeof(*pctx));
1416 return (result);
1419 static isc_result_t
1420 parser_openfile(cfg_parser_t *pctx, const char *filename) {
1421 isc_result_t result;
1422 cfg_listelt_t *elt = NULL;
1423 cfg_obj_t *stringobj = NULL;
1425 result = isc_lex_openfile(pctx->lexer, filename);
1426 if (result != ISC_R_SUCCESS) {
1427 parser_error(pctx, 0, "open: %s: %s",
1428 filename, isc_result_totext(result));
1429 goto cleanup;
1432 CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
1433 CHECK(create_listelt(pctx, &elt));
1434 elt->obj = stringobj;
1435 ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
1437 return (ISC_R_SUCCESS);
1438 cleanup:
1439 CLEANUP_OBJ(stringobj);
1440 return (result);
1443 void
1444 cfg_parser_setcallback(cfg_parser_t *pctx,
1445 cfg_parsecallback_t callback,
1446 void *arg)
1448 pctx->callback = callback;
1449 pctx->callbackarg = arg;
1453 * Parse a configuration using a pctx where a lexer has already
1454 * been set up with a source.
1456 static isc_result_t
1457 parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1458 isc_result_t result;
1459 cfg_obj_t *obj = NULL;
1461 result = parse(pctx, type, &obj);
1463 if (pctx->errors != 0) {
1464 /* Errors have been logged. */
1465 if (result == ISC_R_SUCCESS)
1466 result = ISC_R_FAILURE;
1467 goto cleanup;
1470 if (result != ISC_R_SUCCESS) {
1471 /* Parsing failed but no errors have been logged. */
1472 parser_error(pctx, 0, "parsing failed");
1473 goto cleanup;
1476 CHECK(parse_eof(pctx));
1478 *ret = obj;
1479 return (ISC_R_SUCCESS);
1481 cleanup:
1482 CLEANUP_OBJ(obj);
1483 return (result);
1486 isc_result_t
1487 cfg_parse_file(cfg_parser_t *pctx, const char *filename,
1488 const cfg_type_t *type, cfg_obj_t **ret)
1490 isc_result_t result;
1492 REQUIRE(filename != NULL);
1494 CHECK(parser_openfile(pctx, filename));
1495 CHECK(parse2(pctx, type, ret));
1496 cleanup:
1497 return (result);
1501 isc_result_t
1502 cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
1503 const cfg_type_t *type, cfg_obj_t **ret)
1505 isc_result_t result;
1506 REQUIRE(buffer != NULL);
1507 CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
1508 CHECK(parse2(pctx, type, ret));
1509 cleanup:
1510 return (result);
1513 void
1514 cfg_parser_destroy(cfg_parser_t **pctxp) {
1515 cfg_parser_t *pctx = *pctxp;
1516 isc_lex_destroy(&pctx->lexer);
1518 * Cleaning up open_files does not
1519 * close the files; that was already done
1520 * by closing the lexer.
1522 CLEANUP_OBJ(pctx->open_files);
1523 CLEANUP_OBJ(pctx->closed_files);
1524 isc_mem_put(pctx->mctx, pctx, sizeof(*pctx));
1525 *pctxp = NULL;
1529 * void
1531 static isc_result_t
1532 parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1533 UNUSED(type);
1534 return (create_cfgobj(pctx, &cfg_type_void, ret));
1537 static void
1538 print_void(cfg_printer_t *pctx, cfg_obj_t *obj) {
1539 UNUSED(pctx);
1540 UNUSED(obj);
1543 isc_boolean_t
1544 cfg_obj_isvoid(cfg_obj_t *obj) {
1545 REQUIRE(obj != NULL);
1546 return (ISC_TF(obj->type->rep == &cfg_rep_void));
1549 static cfg_type_t cfg_type_void = {
1550 "void", parse_void, print_void, &cfg_rep_void, NULL };
1554 * uint32
1556 static isc_result_t
1557 parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1558 isc_result_t result;
1559 cfg_obj_t *obj = NULL;
1560 UNUSED(type);
1562 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
1563 if (pctx->token.type != isc_tokentype_number) {
1564 parser_error(pctx, LOG_NEAR, "expected number");
1565 return (ISC_R_UNEXPECTEDTOKEN);
1568 CHECK(create_cfgobj(pctx, &cfg_type_uint32, &obj));
1570 obj->value.uint32 = pctx->token.value.as_ulong;
1571 *ret = obj;
1572 cleanup:
1573 return (result);
1576 static void
1577 print_cstr(cfg_printer_t *pctx, const char *s) {
1578 print(pctx, s, strlen(s));
1581 static void
1582 print_uint(cfg_printer_t *pctx, unsigned int u) {
1583 char buf[32];
1584 snprintf(buf, sizeof(buf), "%u", u);
1585 print_cstr(pctx, buf);
1588 static void
1589 print_uint32(cfg_printer_t *pctx, cfg_obj_t *obj) {
1590 print_uint(pctx, obj->value.uint32);
1593 isc_boolean_t
1594 cfg_obj_isuint32(cfg_obj_t *obj) {
1595 REQUIRE(obj != NULL);
1596 return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
1599 isc_uint32_t
1600 cfg_obj_asuint32(cfg_obj_t *obj) {
1601 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
1602 return (obj->value.uint32);
1605 static cfg_type_t cfg_type_uint32 = {
1606 "integer", parse_uint32, print_uint32, &cfg_rep_uint32, NULL };
1610 * uint64
1612 isc_boolean_t
1613 cfg_obj_isuint64(cfg_obj_t *obj) {
1614 REQUIRE(obj != NULL);
1615 return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
1618 isc_uint64_t
1619 cfg_obj_asuint64(cfg_obj_t *obj) {
1620 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
1621 return (obj->value.uint64);
1624 static isc_result_t
1625 parse_unitstring(char *str, isc_resourcevalue_t *valuep) {
1626 char *endp;
1627 unsigned int len;
1628 isc_uint64_t value;
1629 isc_uint64_t unit;
1631 value = isc_string_touint64(str, &endp, 10);
1632 if (*endp == 0) {
1633 *valuep = value;
1634 return (ISC_R_SUCCESS);
1637 len = strlen(str);
1638 if (len < 2 || endp[1] != '\0')
1639 return (ISC_R_FAILURE);
1641 switch (str[len - 1]) {
1642 case 'k':
1643 case 'K':
1644 unit = 1024;
1645 break;
1646 case 'm':
1647 case 'M':
1648 unit = 1024 * 1024;
1649 break;
1650 case 'g':
1651 case 'G':
1652 unit = 1024 * 1024 * 1024;
1653 break;
1654 default:
1655 return (ISC_R_FAILURE);
1657 if (value > ISC_UINT64_MAX / unit)
1658 return (ISC_R_FAILURE);
1659 *valuep = value * unit;
1660 return (ISC_R_SUCCESS);
1663 static void
1664 print_uint64(cfg_printer_t *pctx, cfg_obj_t *obj) {
1665 char buf[32];
1666 sprintf(buf, "%" ISC_PRINT_QUADFORMAT "u", obj->value.uint64);
1667 print_cstr(pctx, buf);
1670 static cfg_type_t cfg_type_uint64 = {
1671 "64_bit_integer", NULL, print_uint64, &cfg_rep_uint64, NULL };
1673 static isc_result_t
1674 parse_sizeval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1675 isc_result_t result;
1676 cfg_obj_t *obj = NULL;
1677 isc_uint64_t val;
1679 UNUSED(type);
1681 CHECK(cfg_gettoken(pctx, 0));
1682 if (pctx->token.type != isc_tokentype_string) {
1683 result = ISC_R_UNEXPECTEDTOKEN;
1684 goto cleanup;
1686 CHECK(parse_unitstring(pctx->token.value.as_pointer, &val));
1688 CHECK(create_cfgobj(pctx, &cfg_type_uint64, &obj));
1689 obj->value.uint64 = val;
1690 *ret = obj;
1691 return (ISC_R_SUCCESS);
1693 cleanup:
1694 parser_error(pctx, LOG_NEAR, "expected integer and optional unit");
1695 return (result);
1699 * A size value (number + optional unit).
1701 static cfg_type_t cfg_type_sizeval = {
1702 "sizeval", parse_sizeval, print_uint64, &cfg_rep_uint64, NULL };
1705 * A size, "unlimited", or "default".
1708 static isc_result_t
1709 parse_size(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1710 return (parse_enum_or_other(pctx, type, &cfg_type_sizeval, ret));
1713 static const char *size_enums[] = { "unlimited", "default", NULL };
1714 static cfg_type_t cfg_type_size = {
1715 "size", parse_size, print_ustring, &cfg_rep_string, size_enums
1719 * A size or "unlimited", but not "default".
1721 static const char *sizenodefault_enums[] = { "unlimited", NULL };
1722 static cfg_type_t cfg_type_sizenodefault = {
1723 "size_no_default", parse_size, print_ustring, &cfg_rep_string,
1724 sizenodefault_enums
1728 * optional_keyvalue
1730 static isc_result_t
1731 parse_maybe_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
1732 isc_boolean_t optional, cfg_obj_t **ret)
1734 isc_result_t result;
1735 cfg_obj_t *obj = NULL;
1736 const keyword_type_t *kw = type->of;
1738 CHECK(cfg_peektoken(pctx, 0));
1739 if (pctx->token.type == isc_tokentype_string &&
1740 strcasecmp(pctx->token.value.as_pointer, kw->name) == 0) {
1741 CHECK(cfg_gettoken(pctx, 0));
1742 CHECK(kw->type->parse(pctx, kw->type, &obj));
1743 obj->type = type; /* XXX kludge */
1744 } else {
1745 if (optional) {
1746 CHECK(parse_void(pctx, NULL, &obj));
1747 } else {
1748 parser_error(pctx, LOG_NEAR, "expected '%s'",
1749 kw->name);
1750 result = ISC_R_UNEXPECTEDTOKEN;
1751 goto cleanup;
1754 *ret = obj;
1755 cleanup:
1756 return (result);
1759 static isc_result_t
1760 parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1761 return (parse_maybe_optional_keyvalue(pctx, type, ISC_FALSE, ret));
1764 static isc_result_t
1765 parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1766 return (parse_maybe_optional_keyvalue(pctx, type, ISC_TRUE, ret));
1769 static void
1770 print_keyvalue(cfg_printer_t *pctx, cfg_obj_t *obj) {
1771 const keyword_type_t *kw = obj->type->of;
1772 print_cstr(pctx, kw->name);
1773 print(pctx, " ", 1);
1774 kw->type->print(pctx, obj);
1778 * qstring, ustring, astring
1781 /* Create a string object from a null-terminated C string. */
1782 static isc_result_t
1783 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
1784 cfg_obj_t **ret)
1786 isc_result_t result;
1787 cfg_obj_t *obj = NULL;
1788 int len;
1790 CHECK(create_cfgobj(pctx, type, &obj));
1791 len = strlen(contents);
1792 obj->value.string.length = len;
1793 obj->value.string.base = isc_mem_get(pctx->mctx, len + 1);
1794 if (obj->value.string.base == 0) {
1795 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
1796 return (ISC_R_NOMEMORY);
1798 memcpy(obj->value.string.base, contents, len);
1799 obj->value.string.base[len] = '\0';
1801 *ret = obj;
1802 cleanup:
1803 return (result);
1806 static isc_result_t
1807 parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1808 isc_result_t result;
1809 UNUSED(type);
1811 CHECK(cfg_gettoken(pctx, QSTRING));
1812 if (pctx->token.type != isc_tokentype_qstring) {
1813 parser_error(pctx, LOG_NEAR, "expected quoted string");
1814 return (ISC_R_UNEXPECTEDTOKEN);
1816 return (create_string(pctx,
1817 pctx->token.value.as_pointer,
1818 &cfg_type_qstring,
1819 ret));
1820 cleanup:
1821 return (result);
1824 static isc_result_t
1825 parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1826 isc_result_t result;
1827 UNUSED(type);
1829 CHECK(cfg_gettoken(pctx, 0));
1830 if (pctx->token.type != isc_tokentype_string) {
1831 parser_error(pctx, LOG_NEAR, "expected unquoted string");
1832 return (ISC_R_UNEXPECTEDTOKEN);
1834 return (create_string(pctx,
1835 pctx->token.value.as_pointer,
1836 &cfg_type_ustring,
1837 ret));
1838 cleanup:
1839 return (result);
1842 static isc_result_t
1843 parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1844 isc_result_t result;
1845 UNUSED(type);
1847 CHECK(cfg_getstringtoken(pctx));
1848 return (create_string(pctx,
1849 pctx->token.value.as_pointer,
1850 &cfg_type_qstring,
1851 ret));
1852 cleanup:
1853 return (result);
1856 static isc_boolean_t
1857 is_enum(const char *s, const char *const *enums) {
1858 const char * const *p;
1859 for (p = enums; *p != NULL; p++) {
1860 if (strcasecmp(*p, s) == 0)
1861 return (ISC_TRUE);
1863 return (ISC_FALSE);
1866 static isc_result_t
1867 check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
1868 const char *s = obj->value.string.base;
1869 if (is_enum(s, enums))
1870 return (ISC_R_SUCCESS);
1871 parser_error(pctx, 0, "'%s' unexpected", s);
1872 return (ISC_R_UNEXPECTEDTOKEN);
1875 static isc_result_t
1876 parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1877 isc_result_t result;
1878 cfg_obj_t *obj = NULL;
1879 CHECK(parse_ustring(pctx, NULL, &obj));
1880 CHECK(check_enum(pctx, obj, type->of));
1881 *ret = obj;
1882 return (ISC_R_SUCCESS);
1883 cleanup:
1884 CLEANUP_OBJ(obj);
1885 return (result);
1888 static isc_result_t
1889 parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype,
1890 const cfg_type_t *othertype, cfg_obj_t **ret)
1892 isc_result_t result;
1893 CHECK(cfg_peektoken(pctx, 0));
1894 if (pctx->token.type == isc_tokentype_string &&
1895 is_enum(pctx->token.value.as_pointer, enumtype->of)) {
1896 CHECK(parse_enum(pctx, enumtype, ret));
1897 } else {
1898 CHECK(parse(pctx, othertype, ret));
1900 cleanup:
1901 return (result);
1906 * Print a string object.
1908 static void
1909 print_ustring(cfg_printer_t *pctx, cfg_obj_t *obj) {
1910 print(pctx, obj->value.string.base, obj->value.string.length);
1913 static void
1914 print_qstring(cfg_printer_t *pctx, cfg_obj_t *obj) {
1915 print(pctx, "\"", 1);
1916 print_ustring(pctx, obj);
1917 print(pctx, "\"", 1);
1920 static void
1921 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
1922 isc_mem_put(pctx->mctx, obj->value.string.base,
1923 obj->value.string.length + 1);
1926 isc_boolean_t
1927 cfg_obj_isstring(cfg_obj_t *obj) {
1928 REQUIRE(obj != NULL);
1929 return (ISC_TF(obj->type->rep == &cfg_rep_string));
1932 char *
1933 cfg_obj_asstring(cfg_obj_t *obj) {
1934 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
1935 return (obj->value.string.base);
1938 isc_boolean_t
1939 cfg_obj_isboolean(cfg_obj_t *obj) {
1940 REQUIRE(obj != NULL);
1941 return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
1944 isc_boolean_t
1945 cfg_obj_asboolean(cfg_obj_t *obj) {
1946 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
1947 return (obj->value.boolean);
1950 /* Quoted string only */
1951 static cfg_type_t cfg_type_qstring = {
1952 "quoted_string", parse_qstring, print_qstring, &cfg_rep_string, NULL };
1954 /* Unquoted string only */
1955 static cfg_type_t cfg_type_ustring = {
1956 "string", parse_ustring, print_ustring, &cfg_rep_string, NULL };
1958 /* Any string (quoted or unquoted); printed with quotes */
1959 static cfg_type_t cfg_type_astring = {
1960 "string", parse_astring, print_qstring, &cfg_rep_string, NULL };
1964 * boolean
1966 static isc_result_t
1967 parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1969 isc_result_t result;
1970 isc_boolean_t value;
1971 cfg_obj_t *obj = NULL;
1972 UNUSED(type);
1974 result = cfg_gettoken(pctx, 0);
1975 if (result != ISC_R_SUCCESS)
1976 return (result);
1978 if (pctx->token.type != isc_tokentype_string)
1979 goto bad_boolean;
1981 if ((strcasecmp(pctx->token.value.as_pointer, "true") == 0) ||
1982 (strcasecmp(pctx->token.value.as_pointer, "yes") == 0) ||
1983 (strcmp(pctx->token.value.as_pointer, "1") == 0)) {
1984 value = ISC_TRUE;
1985 } else if ((strcasecmp(pctx->token.value.as_pointer, "false") == 0) ||
1986 (strcasecmp(pctx->token.value.as_pointer, "no") == 0) ||
1987 (strcmp(pctx->token.value.as_pointer, "0") == 0)) {
1988 value = ISC_FALSE;
1989 } else {
1990 goto bad_boolean;
1993 CHECK(create_cfgobj(pctx, &cfg_type_boolean, &obj));
1994 obj->value.boolean = value;
1995 *ret = obj;
1996 return (result);
1998 bad_boolean:
1999 parser_error(pctx, LOG_NEAR, "boolean expected");
2000 return (ISC_R_UNEXPECTEDTOKEN);
2002 cleanup:
2003 return (result);
2006 static void
2007 print_boolean(cfg_printer_t *pctx, cfg_obj_t *obj) {
2008 if (obj->value.boolean)
2009 print(pctx, "yes", 3);
2010 else
2011 print(pctx, "no", 2);
2014 static cfg_type_t cfg_type_boolean = {
2015 "boolean", parse_boolean, print_boolean, &cfg_rep_boolean, NULL };
2017 static const char *dialup_enums[] = {
2018 "notify", "notify-passive", "refresh", "passive", NULL };
2019 static isc_result_t
2020 parse_dialup_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2021 return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2023 static cfg_type_t cfg_type_dialuptype = {
2024 "dialuptype", parse_dialup_type, print_ustring,
2025 &cfg_rep_string, dialup_enums
2028 static const char *notify_enums[] = { "explicit", NULL };
2029 static isc_result_t
2030 parse_notify_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2031 return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2033 static cfg_type_t cfg_type_notifytype = {
2034 "notifytype", parse_notify_type, print_ustring,
2035 &cfg_rep_string, notify_enums,
2038 static keyword_type_t key_kw = { "key", &cfg_type_astring };
2040 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_keyref = {
2041 "keyref", parse_keyvalue, print_keyvalue,
2042 &cfg_rep_string, &key_kw
2045 static cfg_type_t cfg_type_optional_keyref = {
2046 "optional_keyref", parse_optional_keyvalue, print_keyvalue,
2047 &cfg_rep_string, &key_kw
2052 * Lists.
2055 static isc_result_t
2056 create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
2057 isc_result_t result;
2058 CHECK(create_cfgobj(pctx, type, obj));
2059 ISC_LIST_INIT((*obj)->value.list);
2060 cleanup:
2061 return (result);
2064 static isc_result_t
2065 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
2066 cfg_listelt_t *elt;
2067 elt = isc_mem_get(pctx->mctx, sizeof(*elt));
2068 if (elt == NULL)
2069 return (ISC_R_NOMEMORY);
2070 elt->obj = NULL;
2071 ISC_LINK_INIT(elt, link);
2072 *eltp = elt;
2073 return (ISC_R_SUCCESS);
2076 static void
2077 free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
2078 cfg_obj_destroy(pctx, &elt->obj);
2079 isc_mem_put(pctx->mctx, elt, sizeof(*elt));
2082 static void
2083 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
2084 cfg_listelt_t *elt, *next;
2085 for (elt = ISC_LIST_HEAD(obj->value.list);
2086 elt != NULL;
2087 elt = next)
2089 next = ISC_LIST_NEXT(elt, link);
2090 free_list_elt(pctx, elt);
2094 static isc_result_t
2095 parse_list_elt(cfg_parser_t *pctx, const cfg_type_t *elttype,
2096 cfg_listelt_t **ret)
2098 isc_result_t result;
2099 cfg_listelt_t *elt = NULL;
2100 cfg_obj_t *value = NULL;
2102 CHECK(create_listelt(pctx, &elt));
2104 result = parse(pctx, elttype, &value);
2105 if (result != ISC_R_SUCCESS)
2106 goto cleanup;
2108 elt->obj = value;
2110 *ret = elt;
2111 return (ISC_R_SUCCESS);
2113 cleanup:
2114 isc_mem_put(pctx->mctx, elt, sizeof(*elt));
2115 return (result);
2119 * Parse a homogeneous list whose elements are of type 'elttype'
2120 * and where each element is terminated by a semicolon.
2122 static isc_result_t
2123 parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
2125 cfg_obj_t *listobj = NULL;
2126 const cfg_type_t *listof = listtype->of;
2127 isc_result_t result;
2128 cfg_listelt_t *elt = NULL;
2130 CHECK(create_list(pctx, listtype, &listobj));
2132 for (;;) {
2133 CHECK(cfg_peektoken(pctx, 0));
2134 if (pctx->token.type == isc_tokentype_special &&
2135 pctx->token.value.as_char == /*{*/ '}')
2136 break;
2137 CHECK(parse_list_elt(pctx, listof, &elt));
2138 CHECK(parse_semicolon(pctx));
2139 ISC_LIST_APPEND(listobj->value.list, elt, link);
2140 elt = NULL;
2142 *ret = listobj;
2143 return (ISC_R_SUCCESS);
2145 cleanup:
2146 if (elt != NULL)
2147 free_list_elt(pctx, elt);
2148 CLEANUP_OBJ(listobj);
2149 return (result);
2152 static void
2153 print_list(cfg_printer_t *pctx, cfg_obj_t *obj) {
2154 cfg_list_t *list = &obj->value.list;
2155 cfg_listelt_t *elt;
2157 for (elt = ISC_LIST_HEAD(*list);
2158 elt != NULL;
2159 elt = ISC_LIST_NEXT(elt, link)) {
2160 print_indent(pctx);
2161 print_obj(pctx, elt->obj);
2162 print(pctx, ";\n", 2);
2166 static isc_result_t
2167 parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
2169 isc_result_t result;
2170 CHECK(parse_special(pctx, '{'));
2171 CHECK(parse_list(pctx, type, ret));
2172 CHECK(parse_special(pctx, '}'));
2173 cleanup:
2174 return (result);
2177 static void
2178 print_bracketed_list(cfg_printer_t *pctx, cfg_obj_t *obj) {
2179 print_open(pctx);
2180 print_list(pctx, obj);
2181 print_close(pctx);
2185 * Parse a homogeneous list whose elements are of type 'elttype'
2186 * and where elements are separated by space. The list ends
2187 * before the first semicolon.
2189 static isc_result_t
2190 parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
2192 cfg_obj_t *listobj = NULL;
2193 const cfg_type_t *listof = listtype->of;
2194 isc_result_t result;
2196 CHECK(create_list(pctx, listtype, &listobj));
2198 for (;;) {
2199 cfg_listelt_t *elt = NULL;
2201 CHECK(cfg_peektoken(pctx, 0));
2202 if (pctx->token.type == isc_tokentype_special &&
2203 pctx->token.value.as_char == ';')
2204 break;
2205 CHECK(parse_list_elt(pctx, listof, &elt));
2206 ISC_LIST_APPEND(listobj->value.list, elt, link);
2208 *ret = listobj;
2209 return (ISC_R_SUCCESS);
2211 cleanup:
2212 CLEANUP_OBJ(listobj);
2213 return (result);
2216 static void
2217 print_spacelist(cfg_printer_t *pctx, cfg_obj_t *obj) {
2218 cfg_list_t *list = &obj->value.list;
2219 cfg_listelt_t *elt;
2221 for (elt = ISC_LIST_HEAD(*list);
2222 elt != NULL;
2223 elt = ISC_LIST_NEXT(elt, link)) {
2224 print_obj(pctx, elt->obj);
2225 if (ISC_LIST_NEXT(elt, link) != NULL)
2226 print(pctx, " ", 1);
2230 isc_boolean_t
2231 cfg_obj_islist(cfg_obj_t *obj) {
2232 REQUIRE(obj != NULL);
2233 return (ISC_TF(obj->type->rep == &cfg_rep_list));
2236 cfg_listelt_t *
2237 cfg_list_first(cfg_obj_t *obj) {
2238 REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
2239 if (obj == NULL)
2240 return (NULL);
2241 return (ISC_LIST_HEAD(obj->value.list));
2244 cfg_listelt_t *
2245 cfg_list_next(cfg_listelt_t *elt) {
2246 REQUIRE(elt != NULL);
2247 return (ISC_LIST_NEXT(elt, link));
2250 cfg_obj_t *
2251 cfg_listelt_value(cfg_listelt_t *elt) {
2252 REQUIRE(elt != NULL);
2253 return (elt->obj);
2257 * Maps.
2261 * Parse a map body. That's something like
2263 * "foo 1; bar { glub; }; zap true; zap false;"
2265 * i.e., a sequence of option names followed by values and
2266 * terminated by semicolons. Used for the top level of
2267 * the named.conf syntax, as well as for the body of the
2268 * options, view, zone, and other statements.
2270 static isc_result_t
2271 parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
2273 const cfg_clausedef_t * const *clausesets = type->of;
2274 isc_result_t result;
2275 const cfg_clausedef_t * const *clauseset;
2276 const cfg_clausedef_t *clause;
2277 cfg_obj_t *value = NULL;
2278 cfg_obj_t *obj = NULL;
2279 cfg_obj_t *eltobj = NULL;
2280 cfg_obj_t *includename = NULL;
2281 isc_symvalue_t symval;
2282 cfg_list_t *list = NULL;
2284 CHECK(create_map(pctx, type, &obj));
2286 obj->value.map.clausesets = clausesets;
2288 for (;;) {
2289 cfg_listelt_t *elt;
2291 redo:
2293 * Parse the option name and see if it is known.
2295 CHECK(cfg_gettoken(pctx, 0));
2297 if (pctx->token.type != isc_tokentype_string) {
2298 cfg_ungettoken(pctx);
2299 break;
2303 * We accept "include" statements wherever a map body
2304 * clause can occur.
2306 if (strcasecmp(pctx->token.value.as_pointer, "include") == 0) {
2308 * Turn the file name into a temporary configuration
2309 * object just so that it is not overwritten by the
2310 * semicolon token.
2312 CHECK(parse(pctx, &cfg_type_qstring, &includename));
2313 CHECK(parse_semicolon(pctx));
2314 CHECK(parser_openfile(pctx, includename->
2315 value.string.base));
2316 cfg_obj_destroy(pctx, &includename);
2317 goto redo;
2320 clause = NULL;
2321 for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
2322 for (clause = *clauseset;
2323 clause->name != NULL;
2324 clause++) {
2325 if (strcasecmp(pctx->token.value.as_pointer,
2326 clause->name) == 0)
2327 goto done;
2330 done:
2331 if (clause == NULL || clause->name == NULL) {
2332 parser_error(pctx, LOG_NOPREP, "unknown option");
2334 * Try to recover by parsing this option as an unknown
2335 * option and discarding it.
2337 CHECK(parse(pctx, &cfg_type_unsupported, &eltobj));
2338 cfg_obj_destroy(pctx, &eltobj);
2339 CHECK(parse_semicolon(pctx));
2340 continue;
2343 /* Clause is known. */
2345 /* Issue warnings if appropriate */
2346 if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0)
2347 parser_warning(pctx, 0, "option '%s' is obsolete",
2348 clause->name);
2349 if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0)
2350 parser_warning(pctx, 0, "option '%s' is "
2351 "not implemented", clause->name);
2352 if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0)
2353 parser_warning(pctx, 0, "option '%s' is "
2354 "not implemented", clause->name);
2356 * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
2357 * set here - we need to log the *lack* of such an option,
2358 * not its presence.
2361 /* See if the clause already has a value; if not create one. */
2362 result = isc_symtab_lookup(obj->value.map.symtab,
2363 clause->name, 0, &symval);
2365 if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
2366 /* Multivalued clause */
2367 cfg_obj_t *listobj = NULL;
2368 if (result == ISC_R_NOTFOUND) {
2369 CHECK(create_list(pctx,
2370 &cfg_type_implicitlist,
2371 &listobj));
2372 symval.as_pointer = listobj;
2373 result = isc_symtab_define(obj->value.
2374 map.symtab,
2375 clause->name,
2376 1, symval,
2377 isc_symexists_reject);
2378 if (result != ISC_R_SUCCESS) {
2379 parser_error(pctx, LOG_NEAR,
2380 "isc_symtab_define(%s) "
2381 "failed", clause->name);
2382 isc_mem_put(pctx->mctx, list,
2383 sizeof(cfg_list_t));
2384 goto cleanup;
2386 } else {
2387 INSIST(result == ISC_R_SUCCESS);
2388 listobj = symval.as_pointer;
2391 elt = NULL;
2392 CHECK(parse_list_elt(pctx, clause->type, &elt));
2393 CHECK(parse_semicolon(pctx));
2395 ISC_LIST_APPEND(listobj->value.list, elt, link);
2396 } else {
2397 /* Single-valued clause */
2398 if (result == ISC_R_NOTFOUND) {
2399 isc_boolean_t callback =
2400 ISC_TF((clause->flags &
2401 CFG_CLAUSEFLAG_CALLBACK) != 0);
2402 CHECK(parse_symtab_elt(pctx, clause->name,
2403 clause->type,
2404 obj->value.map.symtab,
2405 callback));
2406 CHECK(parse_semicolon(pctx));
2407 } else if (result == ISC_R_SUCCESS) {
2408 parser_error(pctx, LOG_NEAR, "'%s' redefined",
2409 clause->name);
2410 result = ISC_R_EXISTS;
2411 goto cleanup;
2412 } else {
2413 parser_error(pctx, LOG_NEAR,
2414 "isc_symtab_define() failed");
2415 goto cleanup;
2421 *ret = obj;
2422 return (ISC_R_SUCCESS);
2424 cleanup:
2425 CLEANUP_OBJ(value);
2426 CLEANUP_OBJ(obj);
2427 CLEANUP_OBJ(eltobj);
2428 CLEANUP_OBJ(includename);
2429 return (result);
2432 static isc_result_t
2433 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
2434 cfg_type_t *elttype, isc_symtab_t *symtab,
2435 isc_boolean_t callback)
2437 isc_result_t result;
2438 cfg_obj_t *obj = NULL;
2439 isc_symvalue_t symval;
2441 CHECK(parse(pctx, elttype, &obj));
2443 if (callback && pctx->callback != NULL)
2444 CHECK(pctx->callback(name, obj, pctx->callbackarg));
2446 symval.as_pointer = obj;
2447 CHECK(isc_symtab_define(symtab, name,
2448 1, symval,
2449 isc_symexists_reject));
2450 return (ISC_R_SUCCESS);
2452 cleanup:
2453 CLEANUP_OBJ(obj);
2454 return (result);
2458 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
2460 static isc_result_t
2461 parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
2463 isc_result_t result;
2464 CHECK(parse_special(pctx, '{'));
2465 CHECK(parse_mapbody(pctx, type, ret));
2466 CHECK(parse_special(pctx, '}'));
2467 cleanup:
2468 return (result);
2472 * Subroutine for parse_named_map() and parse_addressed_map().
2474 static isc_result_t
2475 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type,
2476 cfg_obj_t **ret)
2478 isc_result_t result;
2479 cfg_obj_t *idobj = NULL;
2480 cfg_obj_t *mapobj = NULL;
2482 CHECK(parse(pctx, nametype, &idobj));
2483 CHECK(parse_map(pctx, type, &mapobj));
2484 mapobj->value.map.id = idobj;
2485 idobj = NULL;
2486 *ret = mapobj;
2487 cleanup:
2488 CLEANUP_OBJ(idobj);
2489 return (result);
2493 * Parse a map identified by a string name. E.g., "name { foo 1; }".
2494 * Used for the "key" and "channel" statements.
2496 static isc_result_t
2497 parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2498 return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
2502 * Parse a map identified by a network address.
2503 * Used for the "server" statement.
2505 static isc_result_t
2506 parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2507 return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
2510 static void
2511 print_mapbody(cfg_printer_t *pctx, cfg_obj_t *obj) {
2512 isc_result_t result = ISC_R_SUCCESS;
2514 const cfg_clausedef_t * const *clauseset;
2516 for (clauseset = obj->value.map.clausesets;
2517 *clauseset != NULL;
2518 clauseset++)
2520 isc_symvalue_t symval;
2521 const cfg_clausedef_t *clause;
2523 for (clause = *clauseset;
2524 clause->name != NULL;
2525 clause++) {
2526 result = isc_symtab_lookup(obj->value.map.symtab,
2527 clause->name, 0, &symval);
2528 if (result == ISC_R_SUCCESS) {
2529 cfg_obj_t *obj = symval.as_pointer;
2530 if (obj->type == &cfg_type_implicitlist) {
2531 /* Multivalued. */
2532 cfg_list_t *list = &obj->value.list;
2533 cfg_listelt_t *elt;
2534 for (elt = ISC_LIST_HEAD(*list);
2535 elt != NULL;
2536 elt = ISC_LIST_NEXT(elt, link)) {
2537 print_indent(pctx);
2538 print_cstr(pctx, clause->name);
2539 print(pctx, " ", 1);
2540 print_obj(pctx, elt->obj);
2541 print(pctx, ";\n", 2);
2543 } else {
2544 /* Single-valued. */
2545 print_indent(pctx);
2546 print_cstr(pctx, clause->name);
2547 print(pctx, " ", 1);
2548 print_obj(pctx, obj);
2549 print(pctx, ";\n", 2);
2551 } else if (result == ISC_R_NOTFOUND) {
2552 ; /* do nothing */
2553 } else {
2554 INSIST(0);
2560 static void
2561 print_map(cfg_printer_t *pctx, cfg_obj_t *obj) {
2562 if (obj->value.map.id != NULL) {
2563 print_obj(pctx, obj->value.map.id);
2564 print(pctx, " ", 1);
2566 print_open(pctx);
2567 print_mapbody(pctx, obj);
2568 print_close(pctx);
2571 isc_boolean_t
2572 cfg_obj_ismap(cfg_obj_t *obj) {
2573 REQUIRE(obj != NULL);
2574 return (ISC_TF(obj->type->rep == &cfg_rep_map));
2577 isc_result_t
2578 cfg_map_get(cfg_obj_t *mapobj, const char* name, cfg_obj_t **obj) {
2579 isc_result_t result;
2580 isc_symvalue_t val;
2581 cfg_map_t *map;
2583 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
2584 REQUIRE(name != NULL);
2585 REQUIRE(obj != NULL && *obj == NULL);
2587 map = &mapobj->value.map;
2589 result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
2590 if (result != ISC_R_SUCCESS)
2591 return (result);
2592 *obj = val.as_pointer;
2593 return (ISC_R_SUCCESS);
2596 cfg_obj_t *
2597 cfg_map_getname(cfg_obj_t *mapobj) {
2598 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
2599 return (mapobj->value.map.id);
2603 /* Parse an arbitrary token, storing its raw text representation. */
2604 static isc_result_t
2605 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2606 cfg_obj_t *obj = NULL;
2607 isc_result_t result;
2608 isc_region_t r;
2610 UNUSED(type);
2612 CHECK(create_cfgobj(pctx, &cfg_type_token, &obj));
2613 CHECK(cfg_gettoken(pctx, QSTRING));
2614 if (pctx->token.type == isc_tokentype_eof) {
2615 cfg_ungettoken(pctx);
2616 result = ISC_R_EOF;
2617 goto cleanup;
2620 isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
2622 obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
2623 obj->value.string.length = r.length;
2624 memcpy(obj->value.string.base, r.base, r.length);
2625 obj->value.string.base[r.length] = '\0';
2626 *ret = obj;
2628 cleanup:
2629 return (result);
2632 static cfg_type_t cfg_type_token = {
2633 "token", parse_token, print_ustring, &cfg_rep_string, NULL };
2636 * An unsupported option. This is just a list of tokens with balanced braces
2637 * ending in a semicolon.
2640 static isc_result_t
2641 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2642 cfg_obj_t *listobj = NULL;
2643 isc_result_t result;
2644 int braces = 0;
2646 CHECK(create_list(pctx, type, &listobj));
2648 for (;;) {
2649 cfg_listelt_t *elt = NULL;
2651 CHECK(cfg_peektoken(pctx, 0));
2652 if (pctx->token.type == isc_tokentype_special) {
2653 if (pctx->token.value.as_char == '{')
2654 braces++;
2655 else if (pctx->token.value.as_char == '}')
2656 braces--;
2657 else if (pctx->token.value.as_char == ';')
2658 if (braces == 0)
2659 break;
2661 if (pctx->token.type == isc_tokentype_eof || braces < 0) {
2662 parser_error(pctx, LOG_NEAR, "unexpected token");
2663 result = ISC_R_UNEXPECTEDTOKEN;
2664 goto cleanup;
2667 CHECK(parse_list_elt(pctx, &cfg_type_token, &elt));
2668 ISC_LIST_APPEND(listobj->value.list, elt, link);
2670 INSIST(braces == 0);
2671 *ret = listobj;
2672 return (ISC_R_SUCCESS);
2674 cleanup:
2675 CLEANUP_OBJ(listobj);
2676 return (result);
2679 static cfg_type_t cfg_type_unsupported = {
2680 "unsupported", parse_unsupported, print_spacelist,
2681 &cfg_rep_list, NULL
2685 * A "controls" statement is represented as a map with the multivalued
2686 * "inet" and "unix" clauses. Inet controls are tuples; unix controls
2687 * are cfg_unsupported_t objects.
2690 static keyword_type_t controls_allow_kw = {
2691 "allow", &cfg_type_bracketed_aml };
2692 static cfg_type_t cfg_type_controls_allow = {
2693 "controls_allow", parse_keyvalue,
2694 print_keyvalue, &cfg_rep_list, &controls_allow_kw
2697 static keyword_type_t controls_keys_kw = {
2698 "keys", &cfg_type_keylist };
2699 static cfg_type_t cfg_type_controls_keys = {
2700 "controls_keys", parse_optional_keyvalue,
2701 print_keyvalue, &cfg_rep_list, &controls_keys_kw
2704 static cfg_tuplefielddef_t inetcontrol_fields[] = {
2705 { "address", &cfg_type_controls_sockaddr, 0 },
2706 { "allow", &cfg_type_controls_allow, 0 },
2707 { "keys", &cfg_type_controls_keys, 0 },
2708 { NULL, NULL, 0 }
2710 static cfg_type_t cfg_type_inetcontrol = {
2711 "inetcontrol", parse_tuple, print_tuple, &cfg_rep_tuple,
2712 inetcontrol_fields
2715 static cfg_clausedef_t
2716 controls_clauses[] = {
2717 { "inet", &cfg_type_inetcontrol, CFG_CLAUSEFLAG_MULTI },
2718 { "unix", &cfg_type_unsupported,
2719 CFG_CLAUSEFLAG_MULTI|CFG_CLAUSEFLAG_NOTIMP },
2720 { NULL, NULL, 0 }
2722 static cfg_clausedef_t *
2723 controls_clausesets[] = {
2724 controls_clauses,
2725 NULL
2727 static cfg_type_t cfg_type_controls = {
2728 "controls", parse_map, print_map, &cfg_rep_map, &controls_clausesets
2732 * An optional class, as used in view and zone statements.
2734 static isc_result_t
2735 parse_optional_class(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2736 isc_result_t result;
2737 UNUSED(type);
2738 CHECK(cfg_peektoken(pctx, 0));
2739 if (pctx->token.type == isc_tokentype_string)
2740 CHECK(parse(pctx, &cfg_type_ustring, ret));
2741 else
2742 CHECK(parse(pctx, &cfg_type_void, ret));
2743 cleanup:
2744 return (result);
2747 static cfg_type_t cfg_type_optional_class = {
2748 "optional_class", parse_optional_class, NULL, NULL, NULL };
2752 * Try interpreting the current token as a network address.
2754 * If WILDOK is set in flags, "*" can be used as a wildcard
2755 * and at least one of V4OK and V6OK must also be set. The
2756 * "*" is interpreted as the IPv4 wildcard address if V4OK is
2757 * set (including the case where V4OK and V6OK are both set),
2758 * and the IPv6 wildcard address otherwise.
2760 static isc_result_t
2761 token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
2762 char *s;
2763 struct in_addr in4a;
2764 struct in6_addr in6a;
2766 if (pctx->token.type != isc_tokentype_string)
2767 return (ISC_R_UNEXPECTEDTOKEN);
2769 s = pctx->token.value.as_pointer;
2770 if ((flags & WILDOK) != 0 && strcmp(s, "*") == 0) {
2771 if ((flags & V4OK) != 0) {
2772 isc_netaddr_any(na);
2773 return (ISC_R_SUCCESS);
2774 } else if ((flags & V6OK) != 0) {
2775 isc_netaddr_any6(na);
2776 return (ISC_R_SUCCESS);
2777 } else {
2778 INSIST(0);
2780 } else {
2781 if ((flags & (V4OK | V4PREFIXOK)) != 0) {
2782 if (inet_pton(AF_INET, s, &in4a) == 1) {
2783 isc_netaddr_fromin(na, &in4a);
2784 return (ISC_R_SUCCESS);
2787 if ((flags & V4PREFIXOK) != 0 &&
2788 strlen(s) <= 15U) {
2789 char buf[64];
2790 int i;
2792 strcpy(buf, s);
2793 for (i = 0; i < 3; i++) {
2794 strcat(buf, ".0");
2795 if (inet_pton(AF_INET, buf, &in4a) == 1) {
2796 isc_netaddr_fromin(na, &in4a);
2797 return (ISC_R_SUCCESS);
2801 if (flags & V6OK) {
2802 if (inet_pton(AF_INET6, s, &in6a) == 1) {
2803 isc_netaddr_fromin6(na, &in6a);
2804 return (ISC_R_SUCCESS);
2808 return (ISC_R_UNEXPECTEDTOKEN);
2811 static isc_result_t
2812 get_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
2813 isc_result_t result;
2814 CHECK(cfg_gettoken(pctx, 0));
2815 result = token_addr(pctx, flags, na);
2816 if (result == ISC_R_UNEXPECTEDTOKEN)
2817 parser_error(pctx, LOG_NEAR, "expected IP address");
2818 cleanup:
2819 return (result);
2822 static isc_boolean_t
2823 looking_at_netaddr(cfg_parser_t *pctx, unsigned int flags) {
2824 isc_result_t result;
2825 isc_netaddr_t na_dummy;
2826 result = token_addr(pctx, flags, &na_dummy);
2827 return (ISC_TF(result == ISC_R_SUCCESS));
2830 static isc_result_t
2831 get_port(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
2832 isc_result_t result;
2834 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
2836 if ((flags & WILDOK) != 0 &&
2837 pctx->token.type == isc_tokentype_string &&
2838 strcmp(pctx->token.value.as_pointer, "*") == 0) {
2839 *port = 0;
2840 return (ISC_R_SUCCESS);
2842 if (pctx->token.type != isc_tokentype_number) {
2843 parser_error(pctx, LOG_NEAR,
2844 "expected port number or '*'");
2845 return (ISC_R_UNEXPECTEDTOKEN);
2847 if (pctx->token.value.as_ulong >= 65536U) {
2848 parser_error(pctx, LOG_NEAR,
2849 "port number out of range");
2850 return (ISC_R_UNEXPECTEDTOKEN);
2852 *port = (in_port_t)(pctx->token.value.as_ulong);
2853 return (ISC_R_SUCCESS);
2854 cleanup:
2855 return (result);
2858 static isc_result_t
2859 parse_querysource(cfg_parser_t *pctx, int flags, cfg_obj_t **ret) {
2860 isc_result_t result;
2861 cfg_obj_t *obj = NULL;
2862 isc_netaddr_t netaddr;
2863 in_port_t port;
2864 unsigned int have_address = 0;
2865 unsigned int have_port = 0;
2867 if ((flags & V4OK) != 0)
2868 isc_netaddr_any(&netaddr);
2869 else if ((flags & V6OK) != 0)
2870 isc_netaddr_any6(&netaddr);
2871 else
2872 INSIST(0);
2874 port = 0;
2876 CHECK(create_cfgobj(pctx, &cfg_type_querysource, &obj));
2877 for (;;) {
2878 CHECK(cfg_peektoken(pctx, 0));
2879 if (pctx->token.type == isc_tokentype_string) {
2880 if (strcasecmp(pctx->token.value.as_pointer,
2881 "address") == 0)
2883 /* read "address" */
2884 CHECK(cfg_gettoken(pctx, 0));
2885 CHECK(get_addr(pctx, flags|WILDOK, &netaddr));
2886 have_address++;
2887 } else if (strcasecmp(pctx->token.value.as_pointer,
2888 "port") == 0)
2890 /* read "port" */
2891 CHECK(cfg_gettoken(pctx, 0));
2892 CHECK(get_port(pctx, WILDOK, &port));
2893 have_port++;
2894 } else {
2895 parser_error(pctx, LOG_NEAR,
2896 "expected 'address' or 'port'");
2897 return (ISC_R_UNEXPECTEDTOKEN);
2899 } else
2900 break;
2902 if (have_address > 1 || have_port > 1 ||
2903 have_address + have_port == 0) {
2904 parser_error(pctx, 0, "expected one address and/or port");
2905 return (ISC_R_UNEXPECTEDTOKEN);
2908 isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
2909 *ret = obj;
2910 return (ISC_R_SUCCESS);
2912 cleanup:
2913 parser_error(pctx, LOG_NEAR, "invalid query source");
2914 CLEANUP_OBJ(obj);
2915 return (result);
2918 static isc_result_t
2919 parse_querysource4(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2920 UNUSED(type);
2921 return (parse_querysource(pctx, V4OK, ret));
2924 static isc_result_t
2925 parse_querysource6(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2926 UNUSED(type);
2927 return (parse_querysource(pctx, V6OK, ret));
2930 static void
2931 print_isc_netaddr(cfg_printer_t *pctx, isc_netaddr_t *na) {
2932 isc_result_t result;
2933 char text[128];
2934 isc_buffer_t buf;
2936 isc_buffer_init(&buf, text, sizeof(text));
2937 result = isc_netaddr_totext(na, &buf);
2938 RUNTIME_CHECK(result == ISC_R_SUCCESS);
2939 print(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf));
2942 static void
2943 print_querysource(cfg_printer_t *pctx, cfg_obj_t *obj) {
2944 isc_netaddr_t na;
2945 isc_netaddr_fromsockaddr(&na, &obj->value.sockaddr);
2946 print(pctx, "address ", 8);
2947 print_isc_netaddr(pctx, &na);
2948 print(pctx, " port ", 6);
2949 print_uint(pctx, isc_sockaddr_getport(&obj->value.sockaddr));
2952 static cfg_type_t cfg_type_querysource4 = {
2953 "querysource4", parse_querysource4, NULL, NULL, NULL };
2954 static cfg_type_t cfg_type_querysource6 = {
2955 "querysource6", parse_querysource6, NULL, NULL, NULL };
2956 static cfg_type_t cfg_type_querysource = {
2957 "querysource", NULL, print_querysource, &cfg_rep_sockaddr, NULL };
2959 /* netaddr */
2961 static isc_result_t
2962 parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2963 isc_result_t result;
2964 cfg_obj_t *obj = NULL;
2965 isc_netaddr_t netaddr;
2966 UNUSED(type);
2967 CHECK(create_cfgobj(pctx, type, &obj));
2968 CHECK(get_addr(pctx, V4OK|V6OK, &netaddr));
2969 isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
2970 *ret = obj;
2971 return (ISC_R_SUCCESS);
2972 cleanup:
2973 CLEANUP_OBJ(obj);
2974 return (result);
2977 static cfg_type_t cfg_type_netaddr = {
2978 "netaddr", parse_netaddr, print_sockaddr, &cfg_rep_sockaddr, NULL };
2980 /* netprefix */
2982 static isc_result_t
2983 parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2984 cfg_obj_t *obj = NULL;
2985 isc_result_t result;
2986 isc_netaddr_t netaddr;
2987 unsigned int addrlen, prefixlen;
2988 UNUSED(type);
2990 CHECK(get_addr(pctx, V4OK|V4PREFIXOK|V6OK, &netaddr));
2991 switch (netaddr.family) {
2992 case AF_INET:
2993 addrlen = 32;
2994 break;
2995 case AF_INET6:
2996 addrlen = 128;
2997 break;
2998 default:
2999 addrlen = 0;
3000 INSIST(0);
3001 break;
3003 CHECK(cfg_peektoken(pctx, 0));
3004 if (pctx->token.type == isc_tokentype_special &&
3005 pctx->token.value.as_char == '/') {
3006 CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
3007 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
3008 if (pctx->token.type != isc_tokentype_number) {
3009 parser_error(pctx, LOG_NEAR,
3010 "expected prefix length");
3011 return (ISC_R_UNEXPECTEDTOKEN);
3013 prefixlen = pctx->token.value.as_ulong;
3014 if (prefixlen > addrlen) {
3015 parser_error(pctx, LOG_NOPREP,
3016 "invalid prefix length");
3017 return (ISC_R_RANGE);
3019 } else {
3020 prefixlen = addrlen;
3022 CHECK(create_cfgobj(pctx, &cfg_type_netprefix, &obj));
3023 obj->value.netprefix.address = netaddr;
3024 obj->value.netprefix.prefixlen = prefixlen;
3025 *ret = obj;
3026 return (ISC_R_SUCCESS);
3027 cleanup:
3028 parser_error(pctx, LOG_NEAR, "expected network prefix");
3029 return (result);
3032 static void
3033 print_netprefix(cfg_printer_t *pctx, cfg_obj_t *obj) {
3034 cfg_netprefix_t *p = &obj->value.netprefix;
3035 print_isc_netaddr(pctx, &p->address);
3036 print(pctx, "/", 1);
3037 print_uint(pctx, p->prefixlen);
3040 isc_boolean_t
3041 cfg_obj_isnetprefix(cfg_obj_t *obj) {
3042 REQUIRE(obj != NULL);
3043 return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
3046 void
3047 cfg_obj_asnetprefix(cfg_obj_t *obj, isc_netaddr_t *netaddr,
3048 unsigned int *prefixlen) {
3049 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
3050 *netaddr = obj->value.netprefix.address;
3051 *prefixlen = obj->value.netprefix.prefixlen;
3054 static cfg_type_t cfg_type_netprefix = {
3055 "netprefix", parse_netprefix, print_netprefix, &cfg_rep_netprefix, NULL };
3057 /* addrmatchelt */
3059 static isc_result_t
3060 parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3061 isc_result_t result;
3062 UNUSED(type);
3064 CHECK(cfg_peektoken(pctx, QSTRING));
3066 if (pctx->token.type == isc_tokentype_string ||
3067 pctx->token.type == isc_tokentype_qstring) {
3068 if (pctx->token.type == isc_tokentype_string &&
3069 (strcasecmp(pctx->token.value.as_pointer, "key") == 0)) {
3070 CHECK(parse(pctx, &cfg_type_keyref, ret));
3071 } else {
3072 if (looking_at_netaddr(pctx, V4OK|V4PREFIXOK|V6OK)) {
3073 CHECK(parse_netprefix(pctx, NULL, ret));
3074 } else {
3075 CHECK(parse_astring(pctx, NULL, ret));
3078 } else if (pctx->token.type == isc_tokentype_special) {
3079 if (pctx->token.value.as_char == '{') {
3080 /* Nested match list. */
3081 CHECK(parse(pctx, &cfg_type_bracketed_aml, ret));
3082 } else if (pctx->token.value.as_char == '!') {
3083 CHECK(cfg_gettoken(pctx, 0)); /* read "!" */
3084 CHECK(parse(pctx, &cfg_type_negated, ret));
3085 } else {
3086 goto bad;
3088 } else {
3089 bad:
3090 parser_error(pctx, LOG_NEAR,
3091 "expected IP match list element");
3092 return (ISC_R_UNEXPECTEDTOKEN);
3094 cleanup:
3095 return (result);
3099 * A negated address match list element (like "! 10.0.0.1").
3100 * Somewhat sneakily, the caller is expected to parse the
3101 * "!", but not to print it.
3104 static cfg_tuplefielddef_t negated_fields[] = {
3105 { "value", &cfg_type_addrmatchelt, 0 },
3106 { NULL, NULL, 0 }
3109 static void
3110 print_negated(cfg_printer_t *pctx, cfg_obj_t *obj) {
3111 print(pctx, "!", 1);
3112 print_tuple(pctx, obj);
3115 static cfg_type_t cfg_type_negated = {
3116 "negated", parse_tuple, print_negated, &cfg_rep_tuple,
3117 &negated_fields
3120 /* an address match list element */
3122 static cfg_type_t cfg_type_addrmatchelt = {
3123 "address_match_element", parse_addrmatchelt, NULL, NULL, NULL };
3124 static cfg_type_t cfg_type_bracketed_aml = {
3125 "bracketed_aml", parse_bracketed_list, print_bracketed_list,
3126 &cfg_rep_list, &cfg_type_addrmatchelt
3129 static isc_result_t
3130 parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type,
3131 int flags, cfg_obj_t **ret)
3133 isc_result_t result;
3134 isc_netaddr_t netaddr;
3135 in_port_t port = 0;
3136 cfg_obj_t *obj = NULL;
3138 CHECK(create_cfgobj(pctx, type, &obj));
3139 CHECK(get_addr(pctx, flags, &netaddr));
3140 CHECK(cfg_peektoken(pctx, 0));
3141 if (pctx->token.type == isc_tokentype_string &&
3142 strcasecmp(pctx->token.value.as_pointer, "port") == 0) {
3143 CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
3144 CHECK(get_port(pctx, flags, &port));
3146 isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
3147 *ret = obj;
3148 return (ISC_R_SUCCESS);
3150 cleanup:
3151 CLEANUP_OBJ(obj);
3152 return (result);
3155 static isc_result_t
3156 parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3157 const unsigned int *flagp = type->of;
3158 return (parse_sockaddrsub(pctx, &cfg_type_sockaddr4wild, *flagp, ret));
3161 static void
3162 print_sockaddr(cfg_printer_t *pctx, cfg_obj_t *obj) {
3163 isc_netaddr_t netaddr;
3164 in_port_t port;
3165 char buf[ISC_NETADDR_FORMATSIZE];
3167 isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
3168 isc_netaddr_format(&netaddr, buf, sizeof(buf));
3169 print_cstr(pctx, buf);
3170 port = isc_sockaddr_getport(&obj->value.sockaddr);
3171 if (port != 0) {
3172 print(pctx, " port ", 6);
3173 print_uint(pctx, port);
3177 isc_boolean_t
3178 cfg_obj_issockaddr(cfg_obj_t *obj) {
3179 REQUIRE(obj != NULL);
3180 return (ISC_TF(obj->type->rep == &cfg_rep_sockaddr));
3183 isc_sockaddr_t *
3184 cfg_obj_assockaddr(cfg_obj_t *obj) {
3185 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
3186 return (&obj->value.sockaddr);
3189 /* An IPv4/IPv6 address with optional port, "*" accepted as wildcard. */
3190 static unsigned int sockaddr4wild_flags = WILDOK|V4OK;
3191 static cfg_type_t cfg_type_sockaddr4wild = {
3192 "sockaddr4wild", parse_sockaddr, print_sockaddr,
3193 &cfg_rep_sockaddr, &sockaddr4wild_flags
3196 static unsigned int sockaddr6wild_flags = WILDOK|V6OK;
3197 static cfg_type_t cfg_type_sockaddr6wild = {
3198 "v6addrportwild", parse_sockaddr, print_sockaddr,
3199 &cfg_rep_sockaddr, &sockaddr6wild_flags
3202 static unsigned int sockaddr_flags = V4OK|V6OK;
3203 static cfg_type_t cfg_type_sockaddr = {
3204 "sockaddr", parse_sockaddr, print_sockaddr,
3205 &cfg_rep_sockaddr, &sockaddr_flags
3209 * The socket address syntax in the "controls" statement is silly.
3210 * It allows both socket address families, but also allows "*",
3211 * whis is gratuitously interpreted as the IPv4 wildcard address.
3213 static unsigned int controls_sockaddr_flags = V4OK|V6OK|WILDOK;
3214 static cfg_type_t cfg_type_controls_sockaddr = {
3215 "controls_sockaddr", parse_sockaddr, print_sockaddr,
3216 &cfg_rep_sockaddr, &controls_sockaddr_flags };
3220 * Handle the special kludge syntax of the "keys" clause in the "server"
3221 * statement, which takes a single key with our without braces and semicolon.
3223 static isc_result_t
3224 parse_server_key_kludge(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
3226 isc_result_t result;
3227 isc_boolean_t braces = ISC_FALSE;
3228 UNUSED(type);
3230 /* Allow opening brace. */
3231 CHECK(cfg_peektoken(pctx, 0));
3232 if (pctx->token.type == isc_tokentype_special &&
3233 pctx->token.value.as_char == '{') {
3234 result = cfg_gettoken(pctx, 0);
3235 braces = ISC_TRUE;
3238 CHECK(parse(pctx, &cfg_type_astring, ret));
3240 if (braces) {
3241 /* Skip semicolon if present. */
3242 CHECK(cfg_peektoken(pctx, 0));
3243 if (pctx->token.type == isc_tokentype_special &&
3244 pctx->token.value.as_char == ';')
3245 CHECK(cfg_gettoken(pctx, 0));
3247 CHECK(parse_special(pctx, '}'));
3249 cleanup:
3250 return (result);
3252 static cfg_type_t cfg_type_server_key_kludge = {
3253 "server_key", parse_server_key_kludge, NULL, NULL, NULL };
3257 * An optional logging facility.
3260 static isc_result_t
3261 parse_optional_facility(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
3263 isc_result_t result;
3264 UNUSED(type);
3266 CHECK(cfg_peektoken(pctx, QSTRING));
3267 if (pctx->token.type == isc_tokentype_string ||
3268 pctx->token.type == isc_tokentype_qstring) {
3269 CHECK(parse(pctx, &cfg_type_astring, ret));
3270 } else {
3271 CHECK(parse(pctx, &cfg_type_void, ret));
3273 cleanup:
3274 return (result);
3277 static cfg_type_t cfg_type_optional_facility = {
3278 "optional_facility", parse_optional_facility, NULL, NULL, NULL };
3282 * A log severity. Return as a string, except "debug N",
3283 * which is returned as a keyword object.
3286 static keyword_type_t debug_kw = { "debug", &cfg_type_uint32 };
3287 static cfg_type_t cfg_type_debuglevel = {
3288 "debuglevel", parse_keyvalue,
3289 print_keyvalue, &cfg_rep_uint32, &debug_kw
3292 static isc_result_t
3293 parse_logseverity(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3294 isc_result_t result;
3295 UNUSED(type);
3297 CHECK(cfg_peektoken(pctx, 0));
3298 if (pctx->token.type == isc_tokentype_string &&
3299 strcasecmp(pctx->token.value.as_pointer, "debug") == 0) {
3300 CHECK(cfg_gettoken(pctx, 0)); /* read "debug" */
3301 CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER));
3302 if (pctx->token.type == isc_tokentype_number) {
3303 CHECK(parse_uint32(pctx, NULL, ret));
3304 } else {
3306 * The debug level is optional and defaults to 1.
3307 * This makes little sense, but we support it for
3308 * compatibility with BIND 8.
3310 CHECK(create_cfgobj(pctx, &cfg_type_uint32, ret));
3311 (*ret)->value.uint32 = 1;
3313 (*ret)->type = &cfg_type_debuglevel; /* XXX kludge */
3314 } else {
3315 CHECK(parse(pctx, &cfg_type_loglevel, ret));
3317 cleanup:
3318 return (result);
3321 static cfg_type_t cfg_type_logseverity = {
3322 "logseverity", parse_logseverity, NULL, NULL, NULL };
3325 * The "file" clause of the "channel" statement.
3326 * This is yet another special case.
3329 static const char *logversions_enums[] = { "unlimited", NULL };
3330 static isc_result_t
3331 parse_logversions(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3332 return (parse_enum_or_other(pctx, type, &cfg_type_uint32, ret));
3334 static cfg_type_t cfg_type_logversions = {
3335 "logversions", parse_logversions, print_ustring,
3336 &cfg_rep_string, logversions_enums
3339 static cfg_tuplefielddef_t logfile_fields[] = {
3340 { "file", &cfg_type_qstring, 0 },
3341 { "versions", &cfg_type_logversions, 0 },
3342 { "size", &cfg_type_size, 0 },
3343 { NULL, NULL, 0 }
3346 static isc_result_t
3347 parse_logfile(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3348 isc_result_t result;
3349 cfg_obj_t *obj = NULL;
3350 const cfg_tuplefielddef_t *fields = type->of;
3352 CHECK(create_tuple(pctx, type, &obj));
3354 /* Parse the mandatory "file" field */
3355 CHECK(parse(pctx, fields[0].type, &obj->value.tuple[0]));
3357 /* Parse "versions" and "size" fields in any order. */
3358 for (;;) {
3359 CHECK(cfg_peektoken(pctx, 0));
3360 if (pctx->token.type == isc_tokentype_string) {
3361 CHECK(cfg_gettoken(pctx, 0));
3362 if (strcasecmp(pctx->token.value.as_pointer,
3363 "versions") == 0 &&
3364 obj->value.tuple[1] == NULL) {
3365 CHECK(parse(pctx, fields[1].type,
3366 &obj->value.tuple[1]));
3367 } else if (strcasecmp(pctx->token.value.as_pointer,
3368 "size") == 0 &&
3369 obj->value.tuple[2] == NULL) {
3370 CHECK(parse(pctx, fields[2].type,
3371 &obj->value.tuple[2]));
3372 } else {
3373 break;
3375 } else {
3376 break;
3380 /* Create void objects for missing optional values. */
3381 if (obj->value.tuple[1] == NULL)
3382 CHECK(parse_void(pctx, NULL, &obj->value.tuple[1]));
3383 if (obj->value.tuple[2] == NULL)
3384 CHECK(parse_void(pctx, NULL, &obj->value.tuple[2]));
3386 *ret = obj;
3387 return (ISC_R_SUCCESS);
3389 cleanup:
3390 CLEANUP_OBJ(obj);
3391 return (result);
3394 static void
3395 print_logfile(cfg_printer_t *pctx, cfg_obj_t *obj) {
3396 print_obj(pctx, obj->value.tuple[0]); /* file */
3397 if (obj->value.tuple[1]->type->print != print_void) {
3398 print(pctx, " versions ", 10);
3399 print_obj(pctx, obj->value.tuple[1]);
3401 if (obj->value.tuple[2]->type->print != print_void) {
3402 print(pctx, " size ", 6);
3403 print_obj(pctx, obj->value.tuple[2]);
3407 static cfg_type_t cfg_type_logfile = {
3408 "logfile", parse_logfile, print_logfile, &cfg_rep_tuple,
3409 logfile_fields
3414 * lwres
3417 static cfg_tuplefielddef_t lwres_view_fields[] = {
3418 { "name", &cfg_type_astring, 0 },
3419 { "class", &cfg_type_optional_class, 0 },
3420 { NULL, NULL, 0 }
3422 static cfg_type_t cfg_type_lwres_view = {
3423 "lwres_view", parse_tuple, print_tuple, &cfg_rep_tuple,
3424 lwres_view_fields
3427 static cfg_type_t cfg_type_lwres_searchlist = {
3428 "lwres_searchlist", parse_bracketed_list, print_bracketed_list,
3429 &cfg_rep_list, &cfg_type_astring };
3431 static cfg_clausedef_t
3432 lwres_clauses[] = {
3433 { "listen-on", &cfg_type_portiplist, 0 },
3434 { "view", &cfg_type_lwres_view, 0 },
3435 { "search", &cfg_type_lwres_searchlist, 0 },
3436 { "ndots", &cfg_type_uint32, 0 },
3437 { NULL, NULL, 0 }
3440 static cfg_clausedef_t *
3441 lwres_clausesets[] = {
3442 lwres_clauses,
3443 NULL
3445 static cfg_type_t cfg_type_lwres = {
3446 "lwres", parse_map, print_map, &cfg_rep_map, lwres_clausesets };
3449 * rndc
3452 static cfg_clausedef_t
3453 rndcconf_options_clauses[] = {
3454 { "default-server", &cfg_type_astring, 0 },
3455 { "default-key", &cfg_type_astring, 0 },
3456 { "default-port", &cfg_type_uint32, 0 },
3457 { NULL, NULL, 0 }
3460 static cfg_clausedef_t *
3461 rndcconf_options_clausesets[] = {
3462 rndcconf_options_clauses,
3463 NULL
3466 static cfg_type_t cfg_type_rndcconf_options = {
3467 "rndcconf_options", parse_map, print_map, &cfg_rep_map,
3468 rndcconf_options_clausesets
3471 static cfg_clausedef_t
3472 rndcconf_server_clauses[] = {
3473 { "key", &cfg_type_astring, 0 },
3474 { "port", &cfg_type_uint32, 0 },
3475 { NULL, NULL, 0 }
3478 static cfg_clausedef_t *
3479 rndcconf_server_clausesets[] = {
3480 rndcconf_server_clauses,
3481 NULL
3484 static cfg_type_t cfg_type_rndcconf_server = {
3485 "rndcconf_server", parse_named_map, print_map, &cfg_rep_map,
3486 rndcconf_server_clausesets
3489 static cfg_clausedef_t
3490 rndcconf_clauses[] = {
3491 { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
3492 { "server", &cfg_type_rndcconf_server, CFG_CLAUSEFLAG_MULTI },
3493 { "options", &cfg_type_rndcconf_options, 0 },
3494 { NULL, NULL, 0 }
3497 static cfg_clausedef_t *
3498 rndcconf_clausesets[] = {
3499 rndcconf_clauses,
3500 NULL
3503 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndcconf = {
3504 "rndcconf", parse_mapbody, print_mapbody, &cfg_rep_map,
3505 rndcconf_clausesets
3508 static cfg_clausedef_t
3509 rndckey_clauses[] = {
3510 { "key", &cfg_type_key, 0 },
3511 { NULL, NULL, 0 }
3514 static cfg_clausedef_t *
3515 rndckey_clausesets[] = {
3516 rndckey_clauses,
3517 NULL
3520 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndckey = {
3521 "rndckey", parse_mapbody, print_mapbody, &cfg_rep_map,
3522 rndckey_clausesets
3526 static isc_result_t
3527 cfg_gettoken(cfg_parser_t *pctx, int options) {
3528 isc_result_t result;
3530 if (pctx->seen_eof)
3531 return (ISC_R_SUCCESS);
3533 options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
3535 redo:
3536 pctx->token.type = isc_tokentype_unknown;
3537 result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
3538 pctx->ungotten = ISC_FALSE;
3539 pctx->line = isc_lex_getsourceline(pctx->lexer);
3541 switch (result) {
3542 case ISC_R_SUCCESS:
3543 if (pctx->token.type == isc_tokentype_eof) {
3544 result = isc_lex_close(pctx->lexer);
3545 INSIST(result == ISC_R_NOMORE ||
3546 result == ISC_R_SUCCESS);
3548 if (isc_lex_getsourcename(pctx->lexer) != NULL) {
3550 * Closed an included file, not the main file.
3552 cfg_listelt_t *elt;
3553 elt = ISC_LIST_TAIL(pctx->open_files->
3554 value.list);
3555 INSIST(elt != NULL);
3556 ISC_LIST_UNLINK(pctx->open_files->
3557 value.list, elt, link);
3558 ISC_LIST_APPEND(pctx->closed_files->
3559 value.list, elt, link);
3560 goto redo;
3562 pctx->seen_eof = ISC_TRUE;
3564 break;
3566 case ISC_R_NOSPACE:
3567 /* More understandable than "ran out of space". */
3568 parser_error(pctx, LOG_NEAR, "token too big");
3569 break;
3571 case ISC_R_IOERROR:
3572 parser_error(pctx, 0, "%s",
3573 isc_result_totext(result));
3574 break;
3576 default:
3577 parser_error(pctx, LOG_NEAR, "%s",
3578 isc_result_totext(result));
3579 break;
3581 return (result);
3584 static void
3585 cfg_ungettoken(cfg_parser_t *pctx) {
3586 if (pctx->seen_eof)
3587 return;
3588 isc_lex_ungettoken(pctx->lexer, &pctx->token);
3589 pctx->ungotten = ISC_TRUE;
3592 static isc_result_t
3593 cfg_peektoken(cfg_parser_t *pctx, int options) {
3594 isc_result_t result;
3595 CHECK(cfg_gettoken(pctx, options));
3596 cfg_ungettoken(pctx);
3597 cleanup:
3598 return (result);
3602 * Get a string token, accepting both the quoted and the unquoted form.
3603 * Log an error if the next token is not a string.
3605 static isc_result_t
3606 cfg_getstringtoken(cfg_parser_t *pctx) {
3607 isc_result_t result;
3609 result = cfg_gettoken(pctx, QSTRING);
3610 if (result != ISC_R_SUCCESS)
3611 return (result);
3613 if (pctx->token.type != isc_tokentype_string &&
3614 pctx->token.type != isc_tokentype_qstring) {
3615 parser_error(pctx, LOG_NEAR, "expected string");
3616 return (ISC_R_UNEXPECTEDTOKEN);
3618 return (ISC_R_SUCCESS);
3621 static void
3622 parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
3623 va_list args;
3624 va_start(args, fmt);
3625 parser_complain(pctx, ISC_FALSE, flags, fmt, args);
3626 va_end(args);
3627 pctx->errors++;
3630 static void
3631 parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
3632 va_list args;
3633 va_start(args, fmt);
3634 parser_complain(pctx, ISC_TRUE, flags, fmt, args);
3635 va_end(args);
3636 pctx->warnings++;
3639 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
3641 static char *
3642 current_file(cfg_parser_t *pctx) {
3643 static char none[] = "none";
3644 cfg_listelt_t *elt;
3645 cfg_obj_t *fileobj;
3647 if (pctx->open_files == NULL)
3648 return (none);
3649 elt = ISC_LIST_TAIL(pctx->open_files->value.list);
3650 if (elt == NULL)
3651 return (none);
3653 fileobj = elt->obj;
3654 INSIST(fileobj->type == &cfg_type_qstring);
3655 return (fileobj->value.string.base);
3658 static void
3659 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
3660 unsigned int flags, const char *format,
3661 va_list args)
3663 char tokenbuf[MAX_LOG_TOKEN + 10];
3664 static char where[ISC_DIR_PATHMAX + 100];
3665 static char message[2048];
3666 int level = ISC_LOG_ERROR;
3667 const char *prep = "";
3669 if (is_warning)
3670 level = ISC_LOG_WARNING;
3672 sprintf(where, "%s:%u: ", current_file(pctx), pctx->line);
3674 if ((unsigned int)vsprintf(message, format, args) >= sizeof message)
3675 FATAL_ERROR(__FILE__, __LINE__,
3676 "error message would overflow");
3678 if ((flags & (LOG_NEAR|LOG_BEFORE|LOG_NOPREP)) != 0) {
3679 isc_region_t r;
3681 if (pctx->ungotten)
3682 (void)cfg_gettoken(pctx, 0);
3684 if (pctx->token.type == isc_tokentype_eof) {
3685 snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
3686 } else if (pctx->token.type == isc_tokentype_unknown) {
3687 flags = 0;
3688 tokenbuf[0] = '\0';
3689 } else {
3690 isc_lex_getlasttokentext(pctx->lexer,
3691 &pctx->token, &r);
3692 if (r.length > MAX_LOG_TOKEN)
3693 snprintf(tokenbuf, sizeof(tokenbuf),
3694 "'%.*s...'", MAX_LOG_TOKEN, r.base);
3695 else
3696 snprintf(tokenbuf, sizeof(tokenbuf),
3697 "'%.*s'", (int)r.length, r.base);
3700 /* Choose a preposition. */
3701 if (flags & LOG_NEAR)
3702 prep = " near ";
3703 else if (flags & LOG_BEFORE)
3704 prep = " before ";
3705 else
3706 prep = " ";
3707 } else {
3708 tokenbuf[0] = '\0';
3710 isc_log_write(pctx->lctx, CAT, MOD, level,
3711 "%s%s%s%s", where, message, prep, tokenbuf);
3714 void
3715 cfg_obj_log(cfg_obj_t *obj, isc_log_t *lctx, int level, const char *fmt, ...) {
3716 va_list ap;
3717 char msgbuf[2048];
3719 if (! isc_log_wouldlog(lctx, level))
3720 return;
3722 va_start(ap, fmt);
3724 vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
3725 isc_log_write(lctx, CAT, MOD, level,
3726 "%s:%u: %s",
3727 obj->file == NULL ? "<unknown file>" : obj->file,
3728 obj->line, msgbuf);
3729 va_end(ap);
3732 static isc_result_t
3733 create_cfgobj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3734 cfg_obj_t *obj;
3736 obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
3737 if (obj == NULL)
3738 return (ISC_R_NOMEMORY);
3739 obj->type = type;
3740 obj->file = current_file(pctx);
3741 obj->line = pctx->line;
3742 *ret = obj;
3743 return (ISC_R_SUCCESS);
3746 static void
3747 map_symtabitem_destroy(char *key, unsigned int type,
3748 isc_symvalue_t symval, void *userarg)
3750 cfg_obj_t *obj = symval.as_pointer;
3751 cfg_parser_t *pctx = (cfg_parser_t *)userarg;
3753 UNUSED(key);
3754 UNUSED(type);
3756 cfg_obj_destroy(pctx, &obj);
3760 static isc_result_t
3761 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3762 isc_result_t result;
3763 isc_symtab_t *symtab = NULL;
3764 cfg_obj_t *obj = NULL;
3766 CHECK(create_cfgobj(pctx, type, &obj));
3767 CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */
3768 map_symtabitem_destroy,
3769 pctx, ISC_FALSE, &symtab));
3771 obj->value.map.symtab = symtab;
3772 obj->value.map.id = NULL;
3774 *ret = obj;
3775 return (ISC_R_SUCCESS);
3777 cleanup:
3778 if (obj != NULL)
3779 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
3780 return (result);
3783 static void
3784 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
3785 CLEANUP_OBJ(obj->value.map.id);
3786 isc_symtab_destroy(&obj->value.map.symtab);
3789 isc_boolean_t
3790 cfg_obj_istype(cfg_obj_t *obj, const cfg_type_t *type) {
3791 return (ISC_TF(obj->type == type));
3795 * Destroy 'obj', a configuration object created in 'pctx'.
3797 void
3798 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
3799 cfg_obj_t *obj = *objp;
3800 obj->type->rep->free(pctx, obj);
3801 isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
3802 *objp = NULL;
3805 static void
3806 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
3807 UNUSED(pctx);
3808 UNUSED(obj);
3812 * Data and functions for printing grammar summaries.
3814 static struct flagtext {
3815 unsigned int flag;
3816 const char *text;
3817 } flagtexts[] = {
3818 { CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
3819 { CFG_CLAUSEFLAG_NYI, "not yet implemented" },
3820 { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
3821 { CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
3822 { 0, NULL }
3825 static void
3826 print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
3827 struct flagtext *p;
3828 isc_boolean_t first = ISC_TRUE;
3829 for (p = flagtexts; p->flag != 0; p++) {
3830 if ((flags & p->flag) != 0) {
3831 if (first)
3832 print(pctx, " // ", 4);
3833 else
3834 print(pctx, ", ", 2);
3835 print_cstr(pctx, p->text);
3836 first = ISC_FALSE;
3841 static void
3842 print_grammar(cfg_printer_t *pctx, const cfg_type_t *type) {
3843 if (type->print == print_mapbody) {
3844 const cfg_clausedef_t * const *clauseset;
3845 const cfg_clausedef_t *clause;
3847 for (clauseset = type->of; *clauseset != NULL; clauseset++) {
3848 for (clause = *clauseset;
3849 clause->name != NULL;
3850 clause++) {
3851 print_cstr(pctx, clause->name);
3852 print(pctx, " ", 1);
3853 print_grammar(pctx, clause->type);
3854 print(pctx, ";", 1);
3855 /* XXX print flags here? */
3856 print(pctx, "\n\n", 2);
3859 } else if (type->print == print_map) {
3860 const cfg_clausedef_t * const *clauseset;
3861 const cfg_clausedef_t *clause;
3863 if (type->parse == parse_named_map) {
3864 print_grammar(pctx, &cfg_type_astring);
3865 print(pctx, " ", 1);
3866 } else if (type->parse == parse_addressed_map) {
3867 print_grammar(pctx, &cfg_type_netaddr);
3868 print(pctx, " ", 1);
3871 print_open(pctx);
3873 for (clauseset = type->of; *clauseset != NULL; clauseset++) {
3874 for (clause = *clauseset;
3875 clause->name != NULL;
3876 clause++) {
3877 print_indent(pctx);
3878 print_cstr(pctx, clause->name);
3879 if (clause->type->print != print_void)
3880 print(pctx, " ", 1);
3881 print_grammar(pctx, clause->type);
3882 print(pctx, ";", 1);
3883 print_clause_flags(pctx, clause->flags);
3884 print(pctx, "\n", 1);
3887 print_close(pctx);
3888 } else if (type->print == print_tuple) {
3889 const cfg_tuplefielddef_t *fields = type->of;
3890 const cfg_tuplefielddef_t *f;
3891 isc_boolean_t need_space = ISC_FALSE;
3893 for (f = fields; f->name != NULL; f++) {
3894 if (need_space)
3895 print(pctx, " ", 1);
3896 print_grammar(pctx, f->type);
3897 need_space = ISC_TF(f->type->print != print_void);
3899 } else if (type->parse == parse_enum) {
3900 const char * const *p;
3901 print(pctx, "( ", 2);
3902 for (p = type->of; *p != NULL; p++) {
3903 print_cstr(pctx, *p);
3904 if (p[1] != NULL)
3905 print(pctx, " | ", 3);
3907 print(pctx, " )", 2);
3908 } else if (type->print == print_bracketed_list) {
3909 print(pctx, "{ ", 2);
3910 print_grammar(pctx, type->of);
3911 print(pctx, "; ... }", 7);
3912 } else if (type->parse == parse_keyvalue) {
3913 const keyword_type_t *kw = type->of;
3914 print_cstr(pctx, kw->name);
3915 print(pctx, " ", 1);
3916 print_grammar(pctx, kw->type);
3917 } else if (type->parse == parse_optional_keyvalue) {
3918 const keyword_type_t *kw = type->of;
3919 print(pctx, "[ ", 2);
3920 print_cstr(pctx, kw->name);
3921 print(pctx, " ", 1);
3922 print_grammar(pctx, kw->type);
3923 print(pctx, " ]", 2);
3924 } else if (type->parse == parse_sockaddr) {
3925 const unsigned int *flagp = type->of;
3926 int n = 0;
3927 print(pctx, "( ", 2);
3928 if (*flagp & V4OK) {
3929 if (n != 0)
3930 print(pctx, " | ", 3);
3931 print_cstr(pctx, "<ipv4_address>");
3932 n++;
3934 if (*flagp & V6OK) {
3935 if (n != 0)
3936 print(pctx, " | ", 3);
3937 print_cstr(pctx, "<ipv6_address>");
3938 n++;
3940 if (*flagp & WILDOK) {
3941 if (n != 0)
3942 print(pctx, " | ", 3);
3943 print(pctx, "*", 1);
3944 n++;
3946 print(pctx, " ) ", 3);
3947 if (*flagp & WILDOK) {
3948 print_cstr(pctx, "[ port ( <integer> | * ) ]");
3949 } else {
3950 print_cstr(pctx, "[ port <integer> ]");
3952 } else if (type->print == print_void) {
3953 /* Print nothing. */
3954 } else {
3955 print(pctx, "<", 1);
3956 print_cstr(pctx, type->name);
3957 print(pctx, ">", 1);
3961 void
3962 cfg_print_grammar(const cfg_type_t *type,
3963 void (*f)(void *closure, const char *text, int textlen),
3964 void *closure)
3966 cfg_printer_t pctx;
3967 pctx.f = f;
3968 pctx.closure = closure;
3969 pctx.indent = 0;
3970 print_grammar(&pctx, type);