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 $ */
22 #include <isc/buffer.h>
24 #include <isc/formatcheck.h>
29 #include <isc/netaddr.h>
30 #include <isc/print.h>
31 #include <isc/string.h>
32 #include <isc/sockaddr.h>
34 #include <isc/symtab.h>
36 #include <isccfg/cfg.h>
37 #include <isccfg/log.h>
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
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. */
83 if (result != ISC_R_SUCCESS) goto cleanup; \
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
,
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. */
118 unsigned int warnings
;
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.
154 cfg_parsecallback_t callback
;
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'.
164 void (*f
)(void *closure
, const char *text
, int textlen
);
169 /* A clause definition. */
171 struct cfg_clausedef
{
177 /* A tuple field definition. */
179 struct cfg_tuplefielddef
{
185 /* A configuration object type definition. */
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>". */
198 const cfg_type_t
*type
;
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;
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.
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.
230 const cfg_type_t
*type
;
234 isc_textregion_t string
; /* null terminated, too */
235 isc_boolean_t boolean
;
239 isc_sockaddr_t sockaddr
;
240 cfg_netprefix_t netprefix
;
247 /* A list element. */
251 ISC_LINK(cfg_listelt_t
) link
;
255 * Forward declarations of static functions.
259 create_cfgobj(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**objp
);
262 create_list(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**objp
);
265 create_listelt(cfg_parser_t
*pctx
, cfg_listelt_t
**eltp
);
268 free_list(cfg_parser_t
*pctx
, cfg_obj_t
*obj
);
271 create_string(cfg_parser_t
*pctx
, const char *contents
, const cfg_type_t
*type
,
275 free_string(cfg_parser_t
*pctx
, cfg_obj_t
*obj
);
278 create_map(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**objp
);
281 create_tuple(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**objp
);
284 free_map(cfg_parser_t
*pctx
, cfg_obj_t
*obj
);
287 get_addr(cfg_parser_t
*pctx
, unsigned int flags
, isc_netaddr_t
*na
);
290 print(cfg_printer_t
*pctx
, const char *text
, int len
);
293 print_void(cfg_printer_t
*pctx
, cfg_obj_t
*obj
);
296 parse_enum_or_other(cfg_parser_t
*pctx
, const cfg_type_t
*enumtype
,
297 const cfg_type_t
*othertype
, cfg_obj_t
**ret
);
300 parse_mapbody(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
);
303 print_mapbody(cfg_printer_t
*pctx
, cfg_obj_t
*obj
);
306 parse_map(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
);
309 print_map(cfg_printer_t
*pctx
, cfg_obj_t
*obj
);
312 parse_named_map(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
);
315 parse_addressed_map(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
);
318 parse_list(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
);
321 print_list(cfg_printer_t
*pctx
, cfg_obj_t
*obj
);
324 parse_tuple(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
);
327 print_tuple(cfg_printer_t
*pctx
, cfg_obj_t
*obj
);
330 free_tuple(cfg_parser_t
*pctx
, cfg_obj_t
*obj
);
333 parse_spacelist(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
);
336 print_spacelist(cfg_printer_t
*pctx
, cfg_obj_t
*obj
);
339 print_sockaddr(cfg_printer_t
*pctx
, cfg_obj_t
*obj
);
342 parse_addrmatchelt(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
);
345 parse_bracketed_list(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
);
348 print_bracketed_list(cfg_printer_t
*pctx
, cfg_obj_t
*obj
);
351 parse_keyvalue(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
);
354 parse_optional_keyvalue(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
);
357 print_keyvalue(cfg_printer_t
*pctx
, cfg_obj_t
*obj
);
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
);
365 free_noop(cfg_parser_t
*pctx
, cfg_obj_t
*obj
);
368 cfg_gettoken(cfg_parser_t
*pctx
, int options
);
371 cfg_ungettoken(cfg_parser_t
*pctx
);
374 cfg_peektoken(cfg_parser_t
*pctx
, int options
);
377 cfg_getstringtoken(cfg_parser_t
*pctx
);
380 parser_error(cfg_parser_t
*pctx
, unsigned int flags
,
381 const char *fmt
, ...) ISC_FORMAT_PRINTF(3, 4);
384 parser_warning(cfg_parser_t
*pctx
, unsigned int flags
,
385 const char *fmt
, ...) ISC_FORMAT_PRINTF(3, 4);
388 parser_complain(cfg_parser_t
*pctx
, isc_boolean_t is_warning
,
389 unsigned int flags
, const char *format
, va_list args
);
392 print_uint32(cfg_printer_t
*pctx
, cfg_obj_t
*obj
);
395 print_ustring(cfg_printer_t
*pctx
, cfg_obj_t
*obj
);
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.
477 static cfg_tuplefielddef_t tkey_dhkey_fields
[] = {
478 { "name", &cfg_type_qstring
, 0 },
479 { "keyid", &cfg_type_uint32
, 0 },
483 static cfg_type_t cfg_type_tkey_dhkey
= {
484 "tkey-dhkey", parse_tuple
, print_tuple
, &cfg_rep_tuple
,
490 static cfg_tuplefielddef_t listenon_fields
[] = {
491 { "port", &cfg_type_optional_port
, 0 },
492 { "acl", &cfg_type_bracketed_aml
, 0 },
495 static cfg_type_t cfg_type_listenon
= {
496 "listenon", parse_tuple
, print_tuple
, &cfg_rep_tuple
, listenon_fields
};
500 static cfg_tuplefielddef_t acl_fields
[] = {
501 { "name", &cfg_type_astring
, 0 },
502 { "value", &cfg_type_bracketed_aml
, 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.
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 },
523 static cfg_type_t cfg_type_sockaddrkey
= {
524 "sockaddrkey", parse_tuple
, print_tuple
, &cfg_rep_tuple
,
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 },
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 },
553 static cfg_type_t cfg_type_portiplist
= {
554 "portiplist", parse_tuple
, print_tuple
, &cfg_rep_tuple
,
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 },
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
,
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
,
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
,
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 },
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
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 },
622 static cfg_type_t cfg_type_view
= {
623 "view", parse_tuple
, print_tuple
, &cfg_rep_tuple
, view_fields
};
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 },
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 },
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 },
660 static cfg_type_t cfg_type_trustedkey
= {
661 "trustedkey", parse_tuple
, print_tuple
, &cfg_rep_tuple
,
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 },
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 },
712 static cfg_type_t cfg_type_checknames
= {
713 "checknames", parse_tuple
, print_tuple
, &cfg_rep_tuple
,
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
,
740 static cfg_type_t cfg_type_trustedkeys
= {
741 "trusted-keys", parse_bracketed_list
, print_bracketed_list
, &cfg_rep_list
,
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
,
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
,
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
,
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
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
},
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
},
803 { "trusted-keys", &cfg_type_trustedkeys
, CFG_CLAUSEFLAG_MULTI
},
805 { "trusted-keys", &cfg_type_trustedkeys
,
806 CFG_CLAUSEFLAG_MULTI
|CFG_CLAUSEFLAG_OBSOLETE
},
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 },
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
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 },
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 },
921 * Clauses that can be found in a 'zone' statement,
922 * with defaults in the 'view' or 'options' statement.
924 static cfg_clausedef_t
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 },
955 * Clauses that can be found in a 'zone' statement
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
},
980 /* The top-level named.conf syntax. */
982 static cfg_clausedef_t
*
983 namedconf_clausesets
[] = {
985 namedconf_or_view_clauses
,
989 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_namedconf
= {
990 "namedconf", parse_mapbody
, print_mapbody
, &cfg_rep_map
,
994 /* The "options" statement syntax. */
996 static cfg_clausedef_t
*
997 options_clausesets
[] = {
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
[] = {
1011 namedconf_or_view_clauses
,
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
[] = {
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
1035 { "algorithm", &cfg_type_astring
, 0 },
1036 { "secret", &cfg_type_astring
, 0 },
1040 static cfg_clausedef_t
*
1041 key_clausesets
[] = {
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 },
1064 static cfg_clausedef_t
*
1065 server_clausesets
[] = {
1069 static cfg_type_t cfg_type_server
= {
1070 "server", parse_addressed_map
, print_map
, &cfg_rep_map
,
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 },
1098 static cfg_clausedef_t
*
1099 channel_clausesets
[] = {
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
},
1122 static cfg_clausedef_t
*
1123 logging_clausesets
[] = {
1127 static cfg_type_t cfg_type_logging
= {
1128 "logging", parse_map
, print_map
, &cfg_rep_map
, logging_clausesets
};
1134 print_obj(cfg_printer_t
*pctx
, cfg_obj_t
*obj
) {
1135 obj
->type
->print(pctx
, obj
);
1139 print(cfg_printer_t
*pctx
, const char *text
, int len
) {
1140 pctx
->f(pctx
->closure
, text
, len
);
1144 print_open(cfg_printer_t
*pctx
) {
1145 print(pctx
, "{\n", 2);
1150 print_indent(cfg_printer_t
*pctx
) {
1151 int indent
= pctx
->indent
;
1152 while (indent
> 0) {
1153 print(pctx
, "\t", 1);
1159 print_close(cfg_printer_t
*pctx
) {
1162 print(pctx
, "}", 1);
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
)
1172 INSIST(*ret
!= NULL
);
1173 return (ISC_R_SUCCESS
);
1177 cfg_print(cfg_obj_t
*obj
,
1178 void (*f
)(void *closure
, const char *text
, int textlen
),
1183 pctx
.closure
= closure
;
1185 obj
->type
->print(&pctx
, obj
);
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;
1200 for (f
= fields
; f
->name
!= NULL
; f
++)
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
;
1210 for (f
= fields
, i
= 0; f
->name
!= NULL
; f
++, i
++)
1211 obj
->value
.tuple
[i
] = NULL
;
1213 return (ISC_R_SUCCESS
);
1217 isc_mem_put(pctx
->mctx
, obj
, sizeof(*obj
));
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
;
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
]));
1235 return (ISC_R_SUCCESS
);
1243 print_tuple(cfg_printer_t
*pctx
, cfg_obj_t
*obj
) {
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
];
1252 print(pctx
, " ", 1);
1253 print_obj(pctx
, fieldobj
);
1254 need_space
= ISC_TF(fieldobj
->type
->print
!= print_void
);
1259 free_tuple(cfg_parser_t
*pctx
, cfg_obj_t
*obj
) {
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
)
1268 for (f
= fields
, i
= 0; f
->name
!= NULL
; f
++, i
++) {
1269 CLEANUP_OBJ(obj
->value
.tuple
[i
]);
1272 isc_mem_put(pctx
->mctx
, obj
->value
.tuple
,
1273 nfields
* sizeof(cfg_obj_t
*));
1277 cfg_obj_istuple(cfg_obj_t
*obj
) {
1278 REQUIRE(obj
!= NULL
);
1279 return (ISC_TF(obj
->type
->rep
== &cfg_rep_tuple
));
1283 cfg_tuple_get(cfg_obj_t
*tupleobj
, const char* name
) {
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
]);
1300 * Parse a required special character.
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
);
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.
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
);
1338 * Parse EOF, logging and returning an error if not there.
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
);
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
,
1362 cfg_parser_create(isc_mem_t
*mctx
, isc_log_t
*lctx
, cfg_parser_t
**ret
)
1364 isc_result_t result
;
1366 isc_lexspecials_t specials
;
1368 REQUIRE(mctx
!= NULL
);
1369 REQUIRE(ret
!= NULL
&& *ret
== NULL
);
1371 pctx
= isc_mem_get(mctx
, sizeof(*pctx
));
1373 return (ISC_R_NOMEMORY
);
1378 pctx
->seen_eof
= ISC_FALSE
;
1379 pctx
->ungotten
= ISC_FALSE
;
1382 pctx
->open_files
= NULL
;
1383 pctx
->closed_files
= NULL
;
1385 pctx
->callback
= NULL
;
1386 pctx
->callbackarg
= NULL
;
1387 pctx
->token
.type
= isc_tokentype_unknown
;
1389 memset(specials
, 0, sizeof(specials
));
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
));
1408 return (ISC_R_SUCCESS
);
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
));
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
));
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
);
1439 CLEANUP_OBJ(stringobj
);
1444 cfg_parser_setcallback(cfg_parser_t
*pctx
,
1445 cfg_parsecallback_t callback
,
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.
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
;
1470 if (result
!= ISC_R_SUCCESS
) {
1471 /* Parsing failed but no errors have been logged. */
1472 parser_error(pctx
, 0, "parsing failed");
1476 CHECK(parse_eof(pctx
));
1479 return (ISC_R_SUCCESS
);
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
));
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
));
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
));
1532 parse_void(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
1534 return (create_cfgobj(pctx
, &cfg_type_void
, ret
));
1538 print_void(cfg_printer_t
*pctx
, cfg_obj_t
*obj
) {
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
};
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
;
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
;
1577 print_cstr(cfg_printer_t
*pctx
, const char *s
) {
1578 print(pctx
, s
, strlen(s
));
1582 print_uint(cfg_printer_t
*pctx
, unsigned int u
) {
1584 snprintf(buf
, sizeof(buf
), "%u", u
);
1585 print_cstr(pctx
, buf
);
1589 print_uint32(cfg_printer_t
*pctx
, cfg_obj_t
*obj
) {
1590 print_uint(pctx
, obj
->value
.uint32
);
1594 cfg_obj_isuint32(cfg_obj_t
*obj
) {
1595 REQUIRE(obj
!= NULL
);
1596 return (ISC_TF(obj
->type
->rep
== &cfg_rep_uint32
));
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
};
1613 cfg_obj_isuint64(cfg_obj_t
*obj
) {
1614 REQUIRE(obj
!= NULL
);
1615 return (ISC_TF(obj
->type
->rep
== &cfg_rep_uint64
));
1619 cfg_obj_asuint64(cfg_obj_t
*obj
) {
1620 REQUIRE(obj
!= NULL
&& obj
->type
->rep
== &cfg_rep_uint64
);
1621 return (obj
->value
.uint64
);
1625 parse_unitstring(char *str
, isc_resourcevalue_t
*valuep
) {
1631 value
= isc_string_touint64(str
, &endp
, 10);
1634 return (ISC_R_SUCCESS
);
1638 if (len
< 2 || endp
[1] != '\0')
1639 return (ISC_R_FAILURE
);
1641 switch (str
[len
- 1]) {
1652 unit
= 1024 * 1024 * 1024;
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
);
1664 print_uint64(cfg_printer_t
*pctx
, cfg_obj_t
*obj
) {
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
};
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
;
1681 CHECK(cfg_gettoken(pctx
, 0));
1682 if (pctx
->token
.type
!= isc_tokentype_string
) {
1683 result
= ISC_R_UNEXPECTEDTOKEN
;
1686 CHECK(parse_unitstring(pctx
->token
.value
.as_pointer
, &val
));
1688 CHECK(create_cfgobj(pctx
, &cfg_type_uint64
, &obj
));
1689 obj
->value
.uint64
= val
;
1691 return (ISC_R_SUCCESS
);
1694 parser_error(pctx
, LOG_NEAR
, "expected integer and optional unit");
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".
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
,
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 */
1746 CHECK(parse_void(pctx
, NULL
, &obj
));
1748 parser_error(pctx
, LOG_NEAR
, "expected '%s'",
1750 result
= ISC_R_UNEXPECTEDTOKEN
;
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
));
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
));
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. */
1783 create_string(cfg_parser_t
*pctx
, const char *contents
, const cfg_type_t
*type
,
1786 isc_result_t result
;
1787 cfg_obj_t
*obj
= NULL
;
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';
1807 parse_qstring(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
1808 isc_result_t result
;
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
,
1825 parse_ustring(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
1826 isc_result_t result
;
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
,
1843 parse_astring(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
1844 isc_result_t result
;
1847 CHECK(cfg_getstringtoken(pctx
));
1848 return (create_string(pctx
,
1849 pctx
->token
.value
.as_pointer
,
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)
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
);
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
));
1882 return (ISC_R_SUCCESS
);
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
));
1898 CHECK(parse(pctx
, othertype
, ret
));
1906 * Print a string object.
1909 print_ustring(cfg_printer_t
*pctx
, cfg_obj_t
*obj
) {
1910 print(pctx
, obj
->value
.string
.base
, obj
->value
.string
.length
);
1914 print_qstring(cfg_printer_t
*pctx
, cfg_obj_t
*obj
) {
1915 print(pctx
, "\"", 1);
1916 print_ustring(pctx
, obj
);
1917 print(pctx
, "\"", 1);
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);
1927 cfg_obj_isstring(cfg_obj_t
*obj
) {
1928 REQUIRE(obj
!= NULL
);
1929 return (ISC_TF(obj
->type
->rep
== &cfg_rep_string
));
1933 cfg_obj_asstring(cfg_obj_t
*obj
) {
1934 REQUIRE(obj
!= NULL
&& obj
->type
->rep
== &cfg_rep_string
);
1935 return (obj
->value
.string
.base
);
1939 cfg_obj_isboolean(cfg_obj_t
*obj
) {
1940 REQUIRE(obj
!= NULL
);
1941 return (ISC_TF(obj
->type
->rep
== &cfg_rep_boolean
));
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
};
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
;
1974 result
= cfg_gettoken(pctx
, 0);
1975 if (result
!= ISC_R_SUCCESS
)
1978 if (pctx
->token
.type
!= isc_tokentype_string
)
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)) {
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)) {
1993 CHECK(create_cfgobj(pctx
, &cfg_type_boolean
, &obj
));
1994 obj
->value
.boolean
= value
;
1999 parser_error(pctx
, LOG_NEAR
, "boolean expected");
2000 return (ISC_R_UNEXPECTEDTOKEN
);
2007 print_boolean(cfg_printer_t
*pctx
, cfg_obj_t
*obj
) {
2008 if (obj
->value
.boolean
)
2009 print(pctx
, "yes", 3);
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
};
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
};
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
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
);
2065 create_listelt(cfg_parser_t
*pctx
, cfg_listelt_t
**eltp
) {
2067 elt
= isc_mem_get(pctx
->mctx
, sizeof(*elt
));
2069 return (ISC_R_NOMEMORY
);
2071 ISC_LINK_INIT(elt
, link
);
2073 return (ISC_R_SUCCESS
);
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
));
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
);
2089 next
= ISC_LIST_NEXT(elt
, link
);
2090 free_list_elt(pctx
, elt
);
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
)
2111 return (ISC_R_SUCCESS
);
2114 isc_mem_put(pctx
->mctx
, elt
, sizeof(*elt
));
2119 * Parse a homogeneous list whose elements are of type 'elttype'
2120 * and where each element is terminated by a semicolon.
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
));
2133 CHECK(cfg_peektoken(pctx
, 0));
2134 if (pctx
->token
.type
== isc_tokentype_special
&&
2135 pctx
->token
.value
.as_char
== /*{*/ '}')
2137 CHECK(parse_list_elt(pctx
, listof
, &elt
));
2138 CHECK(parse_semicolon(pctx
));
2139 ISC_LIST_APPEND(listobj
->value
.list
, elt
, link
);
2143 return (ISC_R_SUCCESS
);
2147 free_list_elt(pctx
, elt
);
2148 CLEANUP_OBJ(listobj
);
2153 print_list(cfg_printer_t
*pctx
, cfg_obj_t
*obj
) {
2154 cfg_list_t
*list
= &obj
->value
.list
;
2157 for (elt
= ISC_LIST_HEAD(*list
);
2159 elt
= ISC_LIST_NEXT(elt
, link
)) {
2161 print_obj(pctx
, elt
->obj
);
2162 print(pctx
, ";\n", 2);
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
, '}'));
2178 print_bracketed_list(cfg_printer_t
*pctx
, cfg_obj_t
*obj
) {
2180 print_list(pctx
, obj
);
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.
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
));
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
== ';')
2205 CHECK(parse_list_elt(pctx
, listof
, &elt
));
2206 ISC_LIST_APPEND(listobj
->value
.list
, elt
, link
);
2209 return (ISC_R_SUCCESS
);
2212 CLEANUP_OBJ(listobj
);
2217 print_spacelist(cfg_printer_t
*pctx
, cfg_obj_t
*obj
) {
2218 cfg_list_t
*list
= &obj
->value
.list
;
2221 for (elt
= ISC_LIST_HEAD(*list
);
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);
2231 cfg_obj_islist(cfg_obj_t
*obj
) {
2232 REQUIRE(obj
!= NULL
);
2233 return (ISC_TF(obj
->type
->rep
== &cfg_rep_list
));
2237 cfg_list_first(cfg_obj_t
*obj
) {
2238 REQUIRE(obj
== NULL
|| obj
->type
->rep
== &cfg_rep_list
);
2241 return (ISC_LIST_HEAD(obj
->value
.list
));
2245 cfg_list_next(cfg_listelt_t
*elt
) {
2246 REQUIRE(elt
!= NULL
);
2247 return (ISC_LIST_NEXT(elt
, link
));
2251 cfg_listelt_value(cfg_listelt_t
*elt
) {
2252 REQUIRE(elt
!= NULL
);
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.
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
;
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
);
2303 * We accept "include" statements wherever a map body
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
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
);
2321 for (clauseset
= clausesets
; *clauseset
!= NULL
; clauseset
++) {
2322 for (clause
= *clauseset
;
2323 clause
->name
!= NULL
;
2325 if (strcasecmp(pctx
->token
.value
.as_pointer
,
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
));
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",
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,
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
,
2372 symval
.as_pointer
= listobj
;
2373 result
= isc_symtab_define(obj
->value
.
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
));
2387 INSIST(result
== ISC_R_SUCCESS
);
2388 listobj
= symval
.as_pointer
;
2392 CHECK(parse_list_elt(pctx
, clause
->type
, &elt
));
2393 CHECK(parse_semicolon(pctx
));
2395 ISC_LIST_APPEND(listobj
->value
.list
, elt
, link
);
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
,
2404 obj
->value
.map
.symtab
,
2406 CHECK(parse_semicolon(pctx
));
2407 } else if (result
== ISC_R_SUCCESS
) {
2408 parser_error(pctx
, LOG_NEAR
, "'%s' redefined",
2410 result
= ISC_R_EXISTS
;
2413 parser_error(pctx
, LOG_NEAR
,
2414 "isc_symtab_define() failed");
2422 return (ISC_R_SUCCESS
);
2427 CLEANUP_OBJ(eltobj
);
2428 CLEANUP_OBJ(includename
);
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
,
2449 isc_symexists_reject
));
2450 return (ISC_R_SUCCESS
);
2458 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
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
, '}'));
2472 * Subroutine for parse_named_map() and parse_addressed_map().
2475 parse_any_named_map(cfg_parser_t
*pctx
, cfg_type_t
*nametype
, const cfg_type_t
*type
,
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
;
2493 * Parse a map identified by a string name. E.g., "name { foo 1; }".
2494 * Used for the "key" and "channel" statements.
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.
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
));
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
;
2520 isc_symvalue_t symval
;
2521 const cfg_clausedef_t
*clause
;
2523 for (clause
= *clauseset
;
2524 clause
->name
!= NULL
;
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
) {
2532 cfg_list_t
*list
= &obj
->value
.list
;
2534 for (elt
= ISC_LIST_HEAD(*list
);
2536 elt
= ISC_LIST_NEXT(elt
, link
)) {
2538 print_cstr(pctx
, clause
->name
);
2539 print(pctx
, " ", 1);
2540 print_obj(pctx
, elt
->obj
);
2541 print(pctx
, ";\n", 2);
2544 /* Single-valued. */
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
) {
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);
2567 print_mapbody(pctx
, obj
);
2572 cfg_obj_ismap(cfg_obj_t
*obj
) {
2573 REQUIRE(obj
!= NULL
);
2574 return (ISC_TF(obj
->type
->rep
== &cfg_rep_map
));
2578 cfg_map_get(cfg_obj_t
*mapobj
, const char* name
, cfg_obj_t
**obj
) {
2579 isc_result_t result
;
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
)
2592 *obj
= val
.as_pointer
;
2593 return (ISC_R_SUCCESS
);
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. */
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
;
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
);
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';
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.
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
;
2646 CHECK(create_list(pctx
, type
, &listobj
));
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
== '{')
2655 else if (pctx
->token
.value
.as_char
== '}')
2657 else if (pctx
->token
.value
.as_char
== ';')
2661 if (pctx
->token
.type
== isc_tokentype_eof
|| braces
< 0) {
2662 parser_error(pctx
, LOG_NEAR
, "unexpected token");
2663 result
= ISC_R_UNEXPECTEDTOKEN
;
2667 CHECK(parse_list_elt(pctx
, &cfg_type_token
, &elt
));
2668 ISC_LIST_APPEND(listobj
->value
.list
, elt
, link
);
2670 INSIST(braces
== 0);
2672 return (ISC_R_SUCCESS
);
2675 CLEANUP_OBJ(listobj
);
2679 static cfg_type_t cfg_type_unsupported
= {
2680 "unsupported", parse_unsupported
, print_spacelist
,
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 },
2710 static cfg_type_t cfg_type_inetcontrol
= {
2711 "inetcontrol", parse_tuple
, print_tuple
, &cfg_rep_tuple
,
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
},
2722 static cfg_clausedef_t
*
2723 controls_clausesets
[] = {
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.
2735 parse_optional_class(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
2736 isc_result_t result
;
2738 CHECK(cfg_peektoken(pctx
, 0));
2739 if (pctx
->token
.type
== isc_tokentype_string
)
2740 CHECK(parse(pctx
, &cfg_type_ustring
, ret
));
2742 CHECK(parse(pctx
, &cfg_type_void
, ret
));
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.
2761 token_addr(cfg_parser_t
*pctx
, unsigned int flags
, isc_netaddr_t
*na
) {
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
);
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 &&
2793 for (i
= 0; i
< 3; i
++) {
2795 if (inet_pton(AF_INET
, buf
, &in4a
) == 1) {
2796 isc_netaddr_fromin(na
, &in4a
);
2797 return (ISC_R_SUCCESS
);
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
);
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");
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
));
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) {
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
);
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
;
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
);
2876 CHECK(create_cfgobj(pctx
, &cfg_type_querysource
, &obj
));
2878 CHECK(cfg_peektoken(pctx
, 0));
2879 if (pctx
->token
.type
== isc_tokentype_string
) {
2880 if (strcasecmp(pctx
->token
.value
.as_pointer
,
2883 /* read "address" */
2884 CHECK(cfg_gettoken(pctx
, 0));
2885 CHECK(get_addr(pctx
, flags
|WILDOK
, &netaddr
));
2887 } else if (strcasecmp(pctx
->token
.value
.as_pointer
,
2891 CHECK(cfg_gettoken(pctx
, 0));
2892 CHECK(get_port(pctx
, WILDOK
, &port
));
2895 parser_error(pctx
, LOG_NEAR
,
2896 "expected 'address' or 'port'");
2897 return (ISC_R_UNEXPECTEDTOKEN
);
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
);
2910 return (ISC_R_SUCCESS
);
2913 parser_error(pctx
, LOG_NEAR
, "invalid query source");
2919 parse_querysource4(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
2921 return (parse_querysource(pctx
, V4OK
, ret
));
2925 parse_querysource6(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
2927 return (parse_querysource(pctx
, V6OK
, ret
));
2931 print_isc_netaddr(cfg_printer_t
*pctx
, isc_netaddr_t
*na
) {
2932 isc_result_t result
;
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
));
2943 print_querysource(cfg_printer_t
*pctx
, cfg_obj_t
*obj
) {
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
};
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
;
2967 CHECK(create_cfgobj(pctx
, type
, &obj
));
2968 CHECK(get_addr(pctx
, V4OK
|V6OK
, &netaddr
));
2969 isc_sockaddr_fromnetaddr(&obj
->value
.sockaddr
, &netaddr
, 0);
2971 return (ISC_R_SUCCESS
);
2977 static cfg_type_t cfg_type_netaddr
= {
2978 "netaddr", parse_netaddr
, print_sockaddr
, &cfg_rep_sockaddr
, NULL
};
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
;
2990 CHECK(get_addr(pctx
, V4OK
|V4PREFIXOK
|V6OK
, &netaddr
));
2991 switch (netaddr
.family
) {
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
);
3020 prefixlen
= addrlen
;
3022 CHECK(create_cfgobj(pctx
, &cfg_type_netprefix
, &obj
));
3023 obj
->value
.netprefix
.address
= netaddr
;
3024 obj
->value
.netprefix
.prefixlen
= prefixlen
;
3026 return (ISC_R_SUCCESS
);
3028 parser_error(pctx
, LOG_NEAR
, "expected network prefix");
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
);
3041 cfg_obj_isnetprefix(cfg_obj_t
*obj
) {
3042 REQUIRE(obj
!= NULL
);
3043 return (ISC_TF(obj
->type
->rep
== &cfg_rep_netprefix
));
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
};
3060 parse_addrmatchelt(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
3061 isc_result_t result
;
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
));
3072 if (looking_at_netaddr(pctx
, V4OK
|V4PREFIXOK
|V6OK
)) {
3073 CHECK(parse_netprefix(pctx
, NULL
, ret
));
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
));
3090 parser_error(pctx
, LOG_NEAR
,
3091 "expected IP match list element");
3092 return (ISC_R_UNEXPECTEDTOKEN
);
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 },
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
,
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
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
;
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
);
3148 return (ISC_R_SUCCESS
);
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
));
3162 print_sockaddr(cfg_printer_t
*pctx
, cfg_obj_t
*obj
) {
3163 isc_netaddr_t netaddr
;
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
);
3172 print(pctx
, " port ", 6);
3173 print_uint(pctx
, port
);
3178 cfg_obj_issockaddr(cfg_obj_t
*obj
) {
3179 REQUIRE(obj
!= NULL
);
3180 return (ISC_TF(obj
->type
->rep
== &cfg_rep_sockaddr
));
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.
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
;
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);
3238 CHECK(parse(pctx
, &cfg_type_astring
, ret
));
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
, '}'));
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.
3261 parse_optional_facility(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
)
3263 isc_result_t result
;
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
));
3271 CHECK(parse(pctx
, &cfg_type_void
, ret
));
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
3293 parse_logseverity(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
3294 isc_result_t result
;
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
));
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 */
3315 CHECK(parse(pctx
, &cfg_type_loglevel
, ret
));
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
};
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 },
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. */
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
,
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
,
3369 obj
->value
.tuple
[2] == NULL
) {
3370 CHECK(parse(pctx
, fields
[2].type
,
3371 &obj
->value
.tuple
[2]));
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]));
3387 return (ISC_R_SUCCESS
);
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
,
3417 static cfg_tuplefielddef_t lwres_view_fields
[] = {
3418 { "name", &cfg_type_astring
, 0 },
3419 { "class", &cfg_type_optional_class
, 0 },
3422 static cfg_type_t cfg_type_lwres_view
= {
3423 "lwres_view", parse_tuple
, print_tuple
, &cfg_rep_tuple
,
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
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 },
3440 static cfg_clausedef_t
*
3441 lwres_clausesets
[] = {
3445 static cfg_type_t cfg_type_lwres
= {
3446 "lwres", parse_map
, print_map
, &cfg_rep_map
, lwres_clausesets
};
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 },
3460 static cfg_clausedef_t
*
3461 rndcconf_options_clausesets
[] = {
3462 rndcconf_options_clauses
,
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 },
3478 static cfg_clausedef_t
*
3479 rndcconf_server_clausesets
[] = {
3480 rndcconf_server_clauses
,
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 },
3497 static cfg_clausedef_t
*
3498 rndcconf_clausesets
[] = {
3503 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndcconf
= {
3504 "rndcconf", parse_mapbody
, print_mapbody
, &cfg_rep_map
,
3508 static cfg_clausedef_t
3509 rndckey_clauses
[] = {
3510 { "key", &cfg_type_key
, 0 },
3514 static cfg_clausedef_t
*
3515 rndckey_clausesets
[] = {
3520 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndckey
= {
3521 "rndckey", parse_mapbody
, print_mapbody
, &cfg_rep_map
,
3527 cfg_gettoken(cfg_parser_t
*pctx
, int options
) {
3528 isc_result_t result
;
3531 return (ISC_R_SUCCESS
);
3533 options
|= (ISC_LEXOPT_EOF
| ISC_LEXOPT_NOMORE
);
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
);
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.
3553 elt
= ISC_LIST_TAIL(pctx
->open_files
->
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
);
3562 pctx
->seen_eof
= ISC_TRUE
;
3567 /* More understandable than "ran out of space". */
3568 parser_error(pctx
, LOG_NEAR
, "token too big");
3572 parser_error(pctx
, 0, "%s",
3573 isc_result_totext(result
));
3577 parser_error(pctx
, LOG_NEAR
, "%s",
3578 isc_result_totext(result
));
3585 cfg_ungettoken(cfg_parser_t
*pctx
) {
3588 isc_lex_ungettoken(pctx
->lexer
, &pctx
->token
);
3589 pctx
->ungotten
= ISC_TRUE
;
3593 cfg_peektoken(cfg_parser_t
*pctx
, int options
) {
3594 isc_result_t result
;
3595 CHECK(cfg_gettoken(pctx
, options
));
3596 cfg_ungettoken(pctx
);
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.
3606 cfg_getstringtoken(cfg_parser_t
*pctx
) {
3607 isc_result_t result
;
3609 result
= cfg_gettoken(pctx
, QSTRING
);
3610 if (result
!= ISC_R_SUCCESS
)
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
);
3622 parser_error(cfg_parser_t
*pctx
, unsigned int flags
, const char *fmt
, ...) {
3624 va_start(args
, fmt
);
3625 parser_complain(pctx
, ISC_FALSE
, flags
, fmt
, args
);
3631 parser_warning(cfg_parser_t
*pctx
, unsigned int flags
, const char *fmt
, ...) {
3633 va_start(args
, fmt
);
3634 parser_complain(pctx
, ISC_TRUE
, flags
, fmt
, args
);
3639 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
3642 current_file(cfg_parser_t
*pctx
) {
3643 static char none
[] = "none";
3647 if (pctx
->open_files
== NULL
)
3649 elt
= ISC_LIST_TAIL(pctx
->open_files
->value
.list
);
3654 INSIST(fileobj
->type
== &cfg_type_qstring
);
3655 return (fileobj
->value
.string
.base
);
3659 parser_complain(cfg_parser_t
*pctx
, isc_boolean_t is_warning
,
3660 unsigned int flags
, const char *format
,
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
= "";
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) {
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
) {
3690 isc_lex_getlasttokentext(pctx
->lexer
,
3692 if (r
.length
> MAX_LOG_TOKEN
)
3693 snprintf(tokenbuf
, sizeof(tokenbuf
),
3694 "'%.*s...'", MAX_LOG_TOKEN
, r
.base
);
3696 snprintf(tokenbuf
, sizeof(tokenbuf
),
3697 "'%.*s'", (int)r
.length
, r
.base
);
3700 /* Choose a preposition. */
3701 if (flags
& LOG_NEAR
)
3703 else if (flags
& LOG_BEFORE
)
3710 isc_log_write(pctx
->lctx
, CAT
, MOD
, level
,
3711 "%s%s%s%s", where
, message
, prep
, tokenbuf
);
3715 cfg_obj_log(cfg_obj_t
*obj
, isc_log_t
*lctx
, int level
, const char *fmt
, ...) {
3719 if (! isc_log_wouldlog(lctx
, level
))
3724 vsnprintf(msgbuf
, sizeof(msgbuf
), fmt
, ap
);
3725 isc_log_write(lctx
, CAT
, MOD
, level
,
3727 obj
->file
== NULL
? "<unknown file>" : obj
->file
,
3733 create_cfgobj(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
3736 obj
= isc_mem_get(pctx
->mctx
, sizeof(cfg_obj_t
));
3738 return (ISC_R_NOMEMORY
);
3740 obj
->file
= current_file(pctx
);
3741 obj
->line
= pctx
->line
;
3743 return (ISC_R_SUCCESS
);
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
;
3756 cfg_obj_destroy(pctx
, &obj
);
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
;
3775 return (ISC_R_SUCCESS
);
3779 isc_mem_put(pctx
->mctx
, obj
, sizeof(*obj
));
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
);
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'.
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
));
3806 free_noop(cfg_parser_t
*pctx
, cfg_obj_t
*obj
) {
3812 * Data and functions for printing grammar summaries.
3814 static struct flagtext
{
3818 { CFG_CLAUSEFLAG_NOTIMP
, "not implemented" },
3819 { CFG_CLAUSEFLAG_NYI
, "not yet implemented" },
3820 { CFG_CLAUSEFLAG_OBSOLETE
, "obsolete" },
3821 { CFG_CLAUSEFLAG_NEWDEFAULT
, "default changed" },
3826 print_clause_flags(cfg_printer_t
*pctx
, unsigned int flags
) {
3828 isc_boolean_t first
= ISC_TRUE
;
3829 for (p
= flagtexts
; p
->flag
!= 0; p
++) {
3830 if ((flags
& p
->flag
) != 0) {
3832 print(pctx
, " // ", 4);
3834 print(pctx
, ", ", 2);
3835 print_cstr(pctx
, p
->text
);
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
;
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);
3873 for (clauseset
= type
->of
; *clauseset
!= NULL
; clauseset
++) {
3874 for (clause
= *clauseset
;
3875 clause
->name
!= NULL
;
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);
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
++) {
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
);
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
;
3927 print(pctx
, "( ", 2);
3928 if (*flagp
& V4OK
) {
3930 print(pctx
, " | ", 3);
3931 print_cstr(pctx
, "<ipv4_address>");
3934 if (*flagp
& V6OK
) {
3936 print(pctx
, " | ", 3);
3937 print_cstr(pctx
, "<ipv6_address>");
3940 if (*flagp
& WILDOK
) {
3942 print(pctx
, " | ", 3);
3943 print(pctx
, "*", 1);
3946 print(pctx
, " ) ", 3);
3947 if (*flagp
& WILDOK
) {
3948 print_cstr(pctx
, "[ port ( <integer> | * ) ]");
3950 print_cstr(pctx
, "[ port <integer> ]");
3952 } else if (type
->print
== print_void
) {
3953 /* Print nothing. */
3955 print(pctx
, "<", 1);
3956 print_cstr(pctx
, type
->name
);
3957 print(pctx
, ">", 1);
3962 cfg_print_grammar(const cfg_type_t
*type
,
3963 void (*f
)(void *closure
, const char *text
, int textlen
),
3968 pctx
.closure
= closure
;
3970 print_grammar(&pctx
, type
);