From 669e9657c731176df235832367f61435f7b83ddf Mon Sep 17 00:00:00 2001 From: Andreas Gustafsson Date: Fri, 4 Jan 2002 02:32:16 +0000 Subject: [PATCH] Split off the named.conf grammar into a source module separate from the configuration parser, to facilitate reuse of the latter for parsing non-BIND configuration files --- bin/check/named-checkconf.c | 4 +- bin/named/aclconf.c | 4 +- bin/named/config.c | 4 +- bin/named/controlconf.c | 4 +- bin/named/lwresd.c | 4 +- bin/named/server.c | 4 +- bin/rndc/rndc.c | 4 +- bin/tests/cfg_test.c | 4 +- doc/misc/options | 34 +- lib/isccfg/Makefile.in | 6 +- lib/isccfg/include/isccfg/cfg.h | 18 +- lib/isccfg/include/isccfg/namedconf.h | 44 + lib/isccfg/namedconf.c | 1573 +++++++++++++++ lib/isccfg/parser.c | 2660 +++++-------------------- util/copyrights | 2 + 15 files changed, 2117 insertions(+), 2252 deletions(-) create mode 100644 lib/isccfg/include/isccfg/namedconf.h create mode 100644 lib/isccfg/namedconf.c diff --git a/bin/check/named-checkconf.c b/bin/check/named-checkconf.c index 8dcd4d0dc0..4652f29574 100644 --- a/bin/check/named-checkconf.c +++ b/bin/check/named-checkconf.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: named-checkconf.c,v 1.19 2001/10/02 22:53:35 gson Exp $ */ +/* $Id: named-checkconf.c,v 1.20 2002/01/04 02:31:57 gson Exp $ */ #include @@ -31,7 +31,7 @@ #include #include -#include +#include #include diff --git a/bin/named/aclconf.c b/bin/named/aclconf.c index 828925052a..113ac8319d 100644 --- a/bin/named/aclconf.c +++ b/bin/named/aclconf.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: aclconf.c,v 1.27 2001/04/12 21:02:46 tale Exp $ */ +/* $Id: aclconf.c,v 1.28 2002/01/04 02:31:59 gson Exp $ */ #include @@ -23,6 +23,8 @@ #include /* Required for HP/UX (and others?) */ #include +#include + #include #include #include diff --git a/bin/named/config.c b/bin/named/config.c index f7652ab2a1..2761f884dd 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: config.c,v 1.23 2001/11/30 01:58:44 gson Exp $ */ +/* $Id: config.c,v 1.24 2002/01/04 02:32:00 gson Exp $ */ #include @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include diff --git a/bin/named/controlconf.c b/bin/named/controlconf.c index 993a4b4c6c..b3761ec8ed 100644 --- a/bin/named/controlconf.c +++ b/bin/named/controlconf.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: controlconf.c,v 1.36 2001/11/27 21:51:31 bwelling Exp $ */ +/* $Id: controlconf.c,v 1.37 2002/01/04 02:32:01 gson Exp $ */ #include @@ -31,7 +31,7 @@ #include #include -#include +#include #include diff --git a/bin/named/lwresd.c b/bin/named/lwresd.c index bbc7e631c0..884f4fe0ef 100644 --- a/bin/named/lwresd.c +++ b/bin/named/lwresd.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: lwresd.c,v 1.39 2001/11/30 01:58:46 gson Exp $ */ +/* $Id: lwresd.c,v 1.40 2002/01/04 02:32:02 gson Exp $ */ /* * Main program for the Lightweight Resolver Daemon. @@ -38,7 +38,7 @@ #include #include -#include +#include #include #include diff --git a/bin/named/server.c b/bin/named/server.c index fc8c9fa8ee..487efa3f08 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: server.c,v 1.366 2001/12/01 00:39:22 marka Exp $ */ +/* $Id: server.c,v 1.367 2002/01/04 02:32:04 gson Exp $ */ #include @@ -35,7 +35,7 @@ #include #include -#include +#include #include diff --git a/bin/rndc/rndc.c b/bin/rndc/rndc.c index 9516f37b09..978dd12c60 100644 --- a/bin/rndc/rndc.c +++ b/bin/rndc/rndc.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rndc.c,v 1.89 2001/12/01 00:34:20 marka Exp $ */ +/* $Id: rndc.c,v 1.90 2002/01/04 02:32:06 gson Exp $ */ /* * Principal Author: DCL @@ -38,7 +38,7 @@ #include #include -#include +#include #include #include diff --git a/bin/tests/cfg_test.c b/bin/tests/cfg_test.c index 0372996e32..cfbd2206c4 100644 --- a/bin/tests/cfg_test.c +++ b/bin/tests/cfg_test.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: cfg_test.c,v 1.12 2001/10/22 23:50:31 bwelling Exp $ */ +/* $Id: cfg_test.c,v 1.13 2002/01/04 02:32:07 gson Exp $ */ #include @@ -26,7 +26,7 @@ #include #include -#include +#include #include diff --git a/doc/misc/options b/doc/misc/options index 55e1920d45..35fd457026 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -45,8 +45,6 @@ options { use-id-pool ; // obsolete use-ixfr ; version ( | none ); - allow-notify { ; ... }; - allow-update-forwarding { ; ... }; allow-recursion { ; ... }; allow-v6-synthesis { ; ... }; sortlist { ; ... }; @@ -62,8 +60,6 @@ options { additional-from-cache ; query-source ; query-source-v6 ; - notify-source ( | * ) [ port ( | * ) ]; - notify-source-v6 ( | * ) [ port ( | * ) ]; cleaning-interval ; min-roots ; // not implemented lame-ttl ; @@ -73,9 +69,14 @@ options { max-cache-size ; check-names ; // not implemented cache-file ; + suppress-initial-notify ; // not yet implemented allow-query { ; ... }; allow-transfer { ; ... }; + allow-update-forwarding { ; ... }; + allow-notify { ; ... }; notify ; + notify-source ( | * ) [ port ( | * ) ]; + notify-source-v6 ( | * ) [ port ( | * ) ]; also-notify [ port ] { ( | ) [ port ]; ... }; dialup ; @@ -109,11 +110,11 @@ acl { ; ... }; logging { channel { - file ; + file ; syslog ; null; stderr; - severity ; + severity ; print-time ; print-severity ; print-category ; @@ -132,7 +133,6 @@ view { zone { type ( master | slave | stub | hint | forward ); allow-update { ; ... }; - allow-update-forwarding { ; ... }; file ; ixfr-base ; // obsolete ixfr-tmp-file ; // obsolete @@ -146,7 +146,13 @@ view { check-names ; // not implemented allow-query { ; ... }; allow-transfer { ; ... }; + allow-update-forwarding { ; ... }; + allow-notify { ; ... }; notify ; + notify-source ( | * ) [ port ( | * + ) ]; + notify-source-v6 ( | * ) [ port ( + | * ) ]; also-notify [ port ] { ( | ) [ port ]; ... }; dialup ; @@ -183,8 +189,6 @@ view { }; trusted-keys { ; ... }; - allow-notify { ; ... }; - allow-update-forwarding { ; ... }; allow-recursion { ; ... }; allow-v6-synthesis { ; ... }; sortlist { ; ... }; @@ -200,8 +204,6 @@ view { additional-from-cache ; query-source ; query-source-v6 ; - notify-source ( | * ) [ port ( | * ) ]; - notify-source-v6 ( | * ) [ port ( | * ) ]; cleaning-interval ; min-roots ; // not implemented lame-ttl ; @@ -211,9 +213,14 @@ view { max-cache-size ; check-names ; // not implemented cache-file ; + suppress-initial-notify ; // not yet implemented allow-query { ; ... }; allow-transfer { ; ... }; + allow-update-forwarding { ; ... }; + allow-notify { ; ... }; notify ; + notify-source ( | * ) [ port ( | * ) ]; + notify-source-v6 ( | * ) [ port ( | * ) ]; also-notify [ port ] { ( | ) [ port ]; ... }; dialup ; @@ -253,7 +260,6 @@ key { zone { type ( master | slave | stub | hint | forward ); allow-update { ; ... }; - allow-update-forwarding { ; ... }; file ; ixfr-base ; // obsolete ixfr-tmp-file ; // obsolete @@ -266,7 +272,11 @@ zone { check-names ; // not implemented allow-query { ; ... }; allow-transfer { ; ... }; + allow-update-forwarding { ; ... }; + allow-notify { ; ... }; notify ; + notify-source ( | * ) [ port ( | * ) ]; + notify-source-v6 ( | * ) [ port ( | * ) ]; also-notify [ port ] { ( | ) [ port ]; ... }; dialup ; diff --git a/lib/isccfg/Makefile.in b/lib/isccfg/Makefile.in index 84f101961c..14a970c6a5 100644 --- a/lib/isccfg/Makefile.in +++ b/lib/isccfg/Makefile.in @@ -13,7 +13,7 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.7 2001/09/20 15:16:57 marka Exp $ +# $Id: Makefile.in,v 1.8 2002/01/04 02:32:10 gson Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -41,10 +41,10 @@ LIBS = @LIBS@ SUBDIRS = include # Alphabetically -OBJS = log.@O@ parser.@O@ version.@O@ +OBJS = log.@O@ namedconf.@O@ parser.@O@ version.@O@ # Alphabetically -SRCS = log.c parser.c version.c +SRCS = log.c namedconf.c parser.c version.c TARGETS = timestamp diff --git a/lib/isccfg/include/isccfg/cfg.h b/lib/isccfg/include/isccfg/cfg.h index c2d14d8257..fa5d290422 100644 --- a/lib/isccfg/include/isccfg/cfg.h +++ b/lib/isccfg/include/isccfg/cfg.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: cfg.h,v 1.30 2001/08/03 23:19:01 gson Exp $ */ +/* $Id: cfg.h,v 1.31 2002/01/04 02:32:14 gson Exp $ */ #ifndef ISCCFG_CFG_H #define ISCCFG_CFG_H 1 @@ -28,7 +28,6 @@ * This is the new, table-driven, YACC-free configuration file parser. */ - /*** *** Imports ***/ @@ -398,21 +397,6 @@ cfg_obj_log(cfg_obj_t *obj, isc_log_t *lctx, int level, const char *fmt, ...) * with the file name(s) and line number where 'obj' was defined. */ -/* - * Configuration object types. - */ -LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_namedconf; -/* A complete named.conf file. */ - -LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_rndcconf; -/* A complete rndc.conf file. */ - -LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_rndckey; -/* A complete rndc.key file. */ - -LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_keyref; -/* A key reference, used as an ACL element */ - ISC_LANG_ENDDECLS #endif /* ISCCFG_CFG_H */ diff --git a/lib/isccfg/include/isccfg/namedconf.h b/lib/isccfg/include/isccfg/namedconf.h new file mode 100644 index 0000000000..9b3e63efc4 --- /dev/null +++ b/lib/isccfg/include/isccfg/namedconf.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2000-2002 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: namedconf.h,v 1.1 2002/01/04 02:32:15 gson Exp $ */ + +#ifndef ISCCFG_NAMEDCONF_H +#define ISCCFG_NAMEDCONF_H 1 + +/* + * This module defines the named.conf, rndc.conf, and rndc.key grammars. + */ + +#include + +/* + * Configuration object types. + */ +LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_namedconf; +/* A complete named.conf file. */ + +LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_rndcconf; +/* A complete rndc.conf file. */ + +LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_rndckey; +/* A complete rndc.key file. */ + +LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_keyref; +/* A key reference, used as an ACL element */ + +#endif /* ISCCFG_CFG_H */ diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c new file mode 100644 index 0000000000..3fd0497cba --- /dev/null +++ b/lib/isccfg/namedconf.c @@ -0,0 +1,1573 @@ +/* + * Copyright (C) 2000-2002 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: namedconf.c,v 1.1 2002/01/04 02:32:12 gson Exp $ */ + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +/* Check a return value. */ +#define CHECK(op) \ + do { result = (op); \ + if (result != ISC_R_SUCCESS) goto cleanup; \ + } while (0) + +/* Clean up a configuration object if non-NULL. */ +#define CLEANUP_OBJ(obj) \ + do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0) + + +/* + * Forward declarations of static functions. + */ + +static isc_result_t +parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype, + const cfg_type_t *othertype, cfg_obj_t **ret); + +static isc_result_t +parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); + +static isc_result_t +parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); + +static void +print_keyvalue(cfg_printer_t *pctx, cfg_obj_t *obj); + +static void +doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type); + +static void +doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type); + +static cfg_type_t cfg_type_optional_port; +static cfg_type_t cfg_type_bracketed_aml; +static cfg_type_t cfg_type_acl; +static cfg_type_t cfg_type_portiplist; +static cfg_type_t cfg_type_bracketed_sockaddrlist; +static cfg_type_t cfg_type_optional_keyref; +static cfg_type_t cfg_type_options; +static cfg_type_t cfg_type_view; +static cfg_type_t cfg_type_viewopts; +static cfg_type_t cfg_type_key; +static cfg_type_t cfg_type_server; +static cfg_type_t cfg_type_controls; +static cfg_type_t cfg_type_bracketed_sockaddrkeylist; +static cfg_type_t cfg_type_querysource4; +static cfg_type_t cfg_type_querysource6; +static cfg_type_t cfg_type_querysource; +static cfg_type_t cfg_type_sockaddr4wild; +static cfg_type_t cfg_type_sockaddr6wild; +static cfg_type_t cfg_type_zone; +static cfg_type_t cfg_type_zoneopts; +static cfg_type_t cfg_type_logging; +static cfg_type_t cfg_type_optional_facility; +static cfg_type_t cfg_type_optional_class; +static cfg_type_t cfg_type_destinationlist; +static cfg_type_t cfg_type_size; +static cfg_type_t cfg_type_sizenodefault; +static cfg_type_t cfg_type_negated; +static cfg_type_t cfg_type_addrmatchelt; +static cfg_type_t cfg_type_server_key_kludge; +static cfg_type_t cfg_type_optional_facility; +static cfg_type_t cfg_type_logseverity; +static cfg_type_t cfg_type_logfile; +static cfg_type_t cfg_type_lwres; +static cfg_type_t cfg_type_controls_sockaddr; +static cfg_type_t cfg_type_notifytype; +static cfg_type_t cfg_type_dialuptype; + +/* tkey-dhkey */ + +static cfg_tuplefielddef_t tkey_dhkey_fields[] = { + { "name", &cfg_type_qstring, 0 }, + { "keyid", &cfg_type_uint32, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_type_t cfg_type_tkey_dhkey = { + "tkey-dhkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + tkey_dhkey_fields +}; + +/* listen-on */ + +static cfg_tuplefielddef_t listenon_fields[] = { + { "port", &cfg_type_optional_port, 0 }, + { "acl", &cfg_type_bracketed_aml, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_listenon = { + "listenon", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, listenon_fields }; + +/* acl */ + +static cfg_tuplefielddef_t acl_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "value", &cfg_type_bracketed_aml, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_type_t cfg_type_acl = { + "acl", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, acl_fields }; + + +/* + * "sockaddrkeylist", a list of socket addresses with optional keys + * and an optional default port, as used in the masters option. + * E.g., + * "port 1234 { 10.0.0.1 key foo; 1::2 port 69; }" + */ + +static cfg_tuplefielddef_t sockaddrkey_fields[] = { + { "sockaddr", &cfg_type_sockaddr, 0 }, + { "key", &cfg_type_optional_keyref, 0 }, + { NULL, NULL, 0 }, +}; + +static cfg_type_t cfg_type_sockaddrkey = { + "sockaddrkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + sockaddrkey_fields +}; + +static cfg_type_t cfg_type_bracketed_sockaddrkeylist = { + "bracketed_sockaddrkeylist", cfg_parse_bracketed_list, + cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_sockaddrkey +}; + +static cfg_tuplefielddef_t sockaddrkeylist_fields[] = { + { "port", &cfg_type_optional_port, 0 }, + { "addresses", &cfg_type_bracketed_sockaddrkeylist, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_sockaddrkeylist = { + "sockaddrkeylist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + sockaddrkeylist_fields +}; + +/* + * A list of socket addresses with an optional default port, + * as used in the also-notify option. E.g., + * "port 1234 { 10.0.0.1; 1::2 port 69; }" + */ +static cfg_tuplefielddef_t portiplist_fields[] = { + { "port", &cfg_type_optional_port, 0 }, + { "addresses", &cfg_type_bracketed_sockaddrlist, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_portiplist = { + "portiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + portiplist_fields +}; + +/* + * A public key, as in the "pubkey" statement. + */ +static cfg_tuplefielddef_t pubkey_fields[] = { + { "flags", &cfg_type_uint32, 0 }, + { "protocol", &cfg_type_uint32, 0 }, + { "algorithm", &cfg_type_uint32, 0 }, + { "key", &cfg_type_qstring, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_pubkey = { + "pubkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, pubkey_fields }; + +/* + * A list of RR types, used in grant statements. + * Note that the old parser allows quotes around the RR type names. + */ +static cfg_type_t cfg_type_rrtypelist = { + "rrtypelist", cfg_parse_spacelist, cfg_print_spacelist, cfg_doc_terminal, + &cfg_rep_list, &cfg_type_astring +}; + +static const char *mode_enums[] = { "grant", "deny", NULL }; +static cfg_type_t cfg_type_mode = { + "mode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, + &mode_enums +}; + +static const char *matchtype_enums[] = { + "name", "subdomain", "wildcard", "self", NULL }; +static cfg_type_t cfg_type_matchtype = { + "matchtype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, + &matchtype_enums +}; + +/* + * A grant statement, used in the update policy. + */ +static cfg_tuplefielddef_t grant_fields[] = { + { "mode", &cfg_type_mode, 0 }, + { "identity", &cfg_type_astring, 0 }, /* domain name */ + { "matchtype", &cfg_type_matchtype, 0 }, + { "name", &cfg_type_astring, 0 }, /* domain name */ + { "types", &cfg_type_rrtypelist, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_grant = { + "grant", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, grant_fields }; + +static cfg_type_t cfg_type_updatepolicy = { + "update_policy", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, + &cfg_rep_list, &cfg_type_grant +}; + +/* + * A view statement. + */ +static cfg_tuplefielddef_t view_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "class", &cfg_type_optional_class, 0 }, + { "options", &cfg_type_viewopts, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_view = { + "view", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, view_fields }; + +/* + * A zone statement. + */ +static cfg_tuplefielddef_t zone_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "class", &cfg_type_optional_class, 0 }, + { "options", &cfg_type_zoneopts, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_zone = { + "zone", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, zone_fields }; + +/* + * A "category" clause in the "logging" statement. + */ +static cfg_tuplefielddef_t category_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "destinations", &cfg_type_destinationlist,0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_category = { + "category", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, category_fields }; + + +/* + * A trusted key, as used in the "trusted-keys" statement. + */ +static cfg_tuplefielddef_t trustedkey_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "flags", &cfg_type_uint32, 0 }, + { "protocol", &cfg_type_uint32, 0 }, + { "algorithm", &cfg_type_uint32, 0 }, + { "key", &cfg_type_qstring, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_trustedkey = { + "trustedkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + trustedkey_fields +}; + +static keyword_type_t wild_class_kw = { "class", &cfg_type_ustring }; + +static cfg_type_t cfg_type_optional_wild_class = { + "optional_wild_class", parse_optional_keyvalue, print_keyvalue, + doc_optional_keyvalue, &cfg_rep_string, &wild_class_kw +}; + +static keyword_type_t wild_type_kw = { "type", &cfg_type_ustring }; + +static cfg_type_t cfg_type_optional_wild_type = { + "optional_wild_type", parse_optional_keyvalue, + print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &wild_type_kw +}; + +static keyword_type_t wild_name_kw = { "name", &cfg_type_qstring }; + +static cfg_type_t cfg_type_optional_wild_name = { + "optional_wild_name", parse_optional_keyvalue, + print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &wild_name_kw +}; + +/* + * An rrset ordering element. + */ +static cfg_tuplefielddef_t rrsetorderingelement_fields[] = { + { "class", &cfg_type_optional_wild_class, 0 }, + { "type", &cfg_type_optional_wild_type, 0 }, + { "name", &cfg_type_optional_wild_name, 0 }, + { "order", &cfg_type_ustring, 0 }, /* must be literal "order" */ + { "ordering", &cfg_type_ustring, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_rrsetorderingelement = { + "rrsetorderingelement", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + rrsetorderingelement_fields +}; + +/* + * A global or view "check-names" option. Note that the zone + * "check-names" option has a different syntax. + */ +static cfg_tuplefielddef_t checknames_fields[] = { + { "type", &cfg_type_ustring, 0 }, + { "mode", &cfg_type_ustring, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_checknames = { + "checknames", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + checknames_fields +}; + +static cfg_type_t cfg_type_bracketed_sockaddrlist = { + "bracketed_sockaddrlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, + &cfg_rep_list, &cfg_type_sockaddr +}; + +static cfg_type_t cfg_type_rrsetorder = { + "rrsetorder", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, + &cfg_rep_list, &cfg_type_rrsetorderingelement +}; + +static keyword_type_t port_kw = { "port", &cfg_type_uint32 }; + +static cfg_type_t cfg_type_optional_port = { + "optional_port", parse_optional_keyvalue, print_keyvalue, + doc_optional_keyvalue, &cfg_rep_uint32, &port_kw +}; + +/* A list of keys, as in the "key" clause of the controls statement. */ +static cfg_type_t cfg_type_keylist = { + "keylist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, + &cfg_type_astring +}; + +static cfg_type_t cfg_type_trustedkeys = { + "trusted-keys", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, + &cfg_type_trustedkey +}; + +static const char *forwardtype_enums[] = { "first", "only", NULL }; +static cfg_type_t cfg_type_forwardtype = { + "forwardtype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, + &forwardtype_enums +}; + +static const char *zonetype_enums[] = { + "master", "slave", "stub", "hint", "forward", NULL }; +static cfg_type_t cfg_type_zonetype = { + "zonetype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, + &zonetype_enums +}; + +static const char *loglevel_enums[] = { + "critical", "error", "warning", "notice", "info", "dynamic", NULL }; +static cfg_type_t cfg_type_loglevel = { + "loglevel", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, + &loglevel_enums +}; + +static const char *transferformat_enums[] = { + "many-answers", "one-answer", NULL }; +static cfg_type_t cfg_type_transferformat = { + "transferformat", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, + &transferformat_enums +}; + +/* + * The special keyword "none", as used in the pid-file option. + */ + +static void +print_none(cfg_printer_t *pctx, cfg_obj_t *obj) { + UNUSED(obj); + cfg_print_chars(pctx, "none", 4); +} + +static cfg_type_t cfg_type_none = { + "none", NULL, print_none, NULL, &cfg_rep_void, NULL +}; + +/* + * A quoted string or the special keyword "none". Used in the pid-file option. + */ +static isc_result_t +parse_qstringornone(cfg_parser_t *pctx, const cfg_type_t *type, + cfg_obj_t **ret) +{ + isc_result_t result; + CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); + if (pctx->token.type == isc_tokentype_string && + strcasecmp(pctx->token.value.as_pointer, "none") == 0) + return (cfg_create_obj(pctx, &cfg_type_none, ret)); + cfg_ungettoken(pctx); + return (cfg_parse_qstring(pctx, type, ret)); + cleanup: + return (result); +} + +static void +doc_qstringornone(cfg_printer_t *pctx, const cfg_type_t *type) { + UNUSED(type); + cfg_print_chars(pctx, "( | none )", 26); +} + +static cfg_type_t cfg_type_qstringornone = { + "qstringornone", parse_qstringornone, NULL, doc_qstringornone, NULL, NULL }; + +/* + * Clauses that can be found within the top level of the named.conf + * file only. + */ +static cfg_clausedef_t +namedconf_clauses[] = { + { "options", &cfg_type_options, 0 }, + { "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI }, + { "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI }, + { "logging", &cfg_type_logging, 0 }, + { "view", &cfg_type_view, CFG_CLAUSEFLAG_MULTI }, + { "lwres", &cfg_type_lwres, CFG_CLAUSEFLAG_MULTI }, + { NULL, NULL, 0 } +}; + +/* + * Clauses that can occur at the top level or in the view + * statement, but not in the options block. + */ +static cfg_clausedef_t +namedconf_or_view_clauses[] = { + { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI }, + { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI }, + { "server", &cfg_type_server, CFG_CLAUSEFLAG_MULTI }, + { "trusted-keys", &cfg_type_trustedkeys, CFG_CLAUSEFLAG_MULTI }, + { NULL, NULL, 0 } +}; + +/* + * Clauses that can be found within the 'options' statement. + */ +static cfg_clausedef_t +options_clauses[] = { + { "blackhole", &cfg_type_bracketed_aml, 0 }, + { "coresize", &cfg_type_size, 0 }, + { "datasize", &cfg_type_size, 0 }, + { "deallocate-on-exit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "directory", &cfg_type_qstring, CFG_CLAUSEFLAG_CALLBACK }, + { "dump-file", &cfg_type_qstring, 0 }, + { "fake-iquery", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "files", &cfg_type_size, 0 }, + { "has-old-clients", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "heartbeat-interval", &cfg_type_uint32, 0 }, + { "host-statistics", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTIMP }, + { "hostname", &cfg_type_qstringornone, 0 }, + { "interface-interval", &cfg_type_uint32, 0 }, + { "listen-on", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI }, + { "listen-on-v6", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI }, + { "match-mapped-addresses", &cfg_type_boolean, 0 }, + { "max-journal-size", &cfg_type_sizenodefault, 0 }, + { "memstatistics-file", &cfg_type_qstring, 0 }, + { "multiple-cnames", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "named-xfer", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE }, + { "pid-file", &cfg_type_qstringornone, 0 }, + { "port", &cfg_type_uint32, 0 }, + { "random-device", &cfg_type_qstring, 0 }, + { "recursive-clients", &cfg_type_uint32, 0 }, + { "rrset-order", &cfg_type_rrsetorder, CFG_CLAUSEFLAG_NOTIMP }, + { "serial-queries", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE }, + { "serial-query-rate", &cfg_type_uint32, 0 }, + { "stacksize", &cfg_type_size, 0 }, + { "statistics-file", &cfg_type_qstring, 0 }, + { "statistics-interval", &cfg_type_uint32, CFG_CLAUSEFLAG_NYI }, + { "tcp-clients", &cfg_type_uint32, 0 }, + { "tkey-dhkey", &cfg_type_tkey_dhkey, 0 }, + { "tkey-gssapi-credential", &cfg_type_qstring, 0 }, + { "tkey-domain", &cfg_type_qstring, 0 }, + { "transfers-per-ns", &cfg_type_uint32, 0 }, + { "transfers-in", &cfg_type_uint32, 0 }, + { "transfers-out", &cfg_type_uint32, 0 }, + { "treat-cr-as-space", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "use-id-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "use-ixfr", &cfg_type_boolean, 0 }, + { "version", &cfg_type_qstringornone, 0 }, + { NULL, NULL, 0 } +}; + +/* + * Clauses that can be found within the 'view' statement, + * with defaults in the 'options' statement. + */ + +static cfg_clausedef_t +view_clauses[] = { + { "allow-recursion", &cfg_type_bracketed_aml, 0 }, + { "allow-v6-synthesis", &cfg_type_bracketed_aml, 0 }, + { "sortlist", &cfg_type_bracketed_aml, 0 }, + { "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_NOTIMP }, + { "auth-nxdomain", &cfg_type_boolean, CFG_CLAUSEFLAG_NEWDEFAULT }, + { "minimal-responses", &cfg_type_boolean, 0 }, + { "recursion", &cfg_type_boolean, 0 }, + { "provide-ixfr", &cfg_type_boolean, 0 }, + { "request-ixfr", &cfg_type_boolean, 0 }, + { "fetch-glue", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "rfc2308-type1", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI }, + { "additional-from-auth", &cfg_type_boolean, 0 }, + { "additional-from-cache", &cfg_type_boolean, 0 }, + /* + * Note that the query-source option syntax is different + * from the other -source options. + */ + { "query-source", &cfg_type_querysource4, 0 }, + { "query-source-v6", &cfg_type_querysource6, 0 }, + { "cleaning-interval", &cfg_type_uint32, 0 }, + { "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP }, + { "lame-ttl", &cfg_type_uint32, 0 }, + { "max-ncache-ttl", &cfg_type_uint32, 0 }, + { "max-cache-ttl", &cfg_type_uint32, 0 }, + { "transfer-format", &cfg_type_transferformat, 0 }, + { "max-cache-size", &cfg_type_sizenodefault, 0 }, + { "check-names", &cfg_type_checknames, + CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_NOTIMP }, + { "cache-file", &cfg_type_qstring, 0 }, + { "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI }, + { NULL, NULL, 0 } +}; + +/* + * Clauses that can be found within the 'view' statement only. + */ +static cfg_clausedef_t +view_only_clauses[] = { + { "match-clients", &cfg_type_bracketed_aml, 0 }, + { "match-destinations", &cfg_type_bracketed_aml, 0 }, + { "match-recursive-only", &cfg_type_boolean, 0 }, + { NULL, NULL, 0 } +}; + +/* + * Clauses that can be found in a 'zone' statement, + * with defaults in the 'view' or 'options' statement. + */ +static cfg_clausedef_t +zone_clauses[] = { + { "allow-query", &cfg_type_bracketed_aml, 0 }, + { "allow-transfer", &cfg_type_bracketed_aml, 0 }, + { "allow-update-forwarding", &cfg_type_bracketed_aml, 0 }, + { "allow-notify", &cfg_type_bracketed_aml, 0 }, + { "notify", &cfg_type_notifytype, 0 }, + { "notify-source", &cfg_type_sockaddr4wild, 0 }, + { "notify-source-v6", &cfg_type_sockaddr6wild, 0 }, + { "also-notify", &cfg_type_portiplist, 0 }, + { "dialup", &cfg_type_dialuptype, 0 }, + { "forward", &cfg_type_forwardtype, 0 }, + { "forwarders", &cfg_type_portiplist, 0 }, + { "ixfr-from-differences", &cfg_type_boolean, 0 }, + { "maintain-ixfr-base", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "max-ixfr-log-size", &cfg_type_size, CFG_CLAUSEFLAG_OBSOLETE }, + { "transfer-source", &cfg_type_sockaddr4wild, 0 }, + { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 }, + { "max-transfer-time-in", &cfg_type_uint32, 0 }, + { "max-transfer-time-out", &cfg_type_uint32, 0 }, + { "max-transfer-idle-in", &cfg_type_uint32, 0 }, + { "max-transfer-idle-out", &cfg_type_uint32, 0 }, + { "max-retry-time", &cfg_type_uint32, 0 }, + { "min-retry-time", &cfg_type_uint32, 0 }, + { "max-refresh-time", &cfg_type_uint32, 0 }, + { "min-refresh-time", &cfg_type_uint32, 0 }, + { "sig-validity-interval", &cfg_type_uint32, 0 }, + { "zone-statistics", &cfg_type_boolean, 0 }, + { NULL, NULL, 0 } +}; + +/* + * Clauses that can be found in a 'zone' statement + * only. + */ +static cfg_clausedef_t +zone_only_clauses[] = { + { "type", &cfg_type_zonetype, 0 }, + { "allow-update", &cfg_type_bracketed_aml, 0 }, + { "file", &cfg_type_qstring, 0 }, + { "ixfr-base", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE }, + { "ixfr-tmp-file", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE }, + { "masters", &cfg_type_sockaddrkeylist, 0 }, + { "pubkey", &cfg_type_pubkey, + CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_OBSOLETE }, + { "update-policy", &cfg_type_updatepolicy, 0 }, + { "database", &cfg_type_astring, 0 }, + /* + * Note that the format of the check-names option is different between + * the zone options and the global/view options. Ugh. + */ + { "check-names", &cfg_type_ustring, CFG_CLAUSEFLAG_NOTIMP }, + { NULL, NULL, 0 } +}; + + +/* The top-level named.conf syntax. */ + +static cfg_clausedef_t * +namedconf_clausesets[] = { + namedconf_clauses, + namedconf_or_view_clauses, + NULL +}; + +LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_namedconf = { + "namedconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, + &cfg_rep_map, namedconf_clausesets +}; + +/* The "options" statement syntax. */ + +static cfg_clausedef_t * +options_clausesets[] = { + options_clauses, + view_clauses, + zone_clauses, + NULL +}; +static cfg_type_t cfg_type_options = { + "options", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, options_clausesets }; + +/* The "view" statement syntax. */ + +static cfg_clausedef_t * +view_clausesets[] = { + view_only_clauses, + namedconf_or_view_clauses, + view_clauses, + zone_clauses, + NULL +}; +static cfg_type_t cfg_type_viewopts = { + "view", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, view_clausesets }; + +/* The "zone" statement syntax. */ + +static cfg_clausedef_t * +zone_clausesets[] = { + zone_only_clauses, + zone_clauses, + NULL +}; +static cfg_type_t cfg_type_zoneopts = { + "zoneopts", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, zone_clausesets }; + +/* + * Clauses that can be found within the 'key' statement. + */ +static cfg_clausedef_t +key_clauses[] = { + { "algorithm", &cfg_type_astring, 0 }, + { "secret", &cfg_type_astring, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_clausedef_t * +key_clausesets[] = { + key_clauses, + NULL +}; +static cfg_type_t cfg_type_key = { + "key", cfg_parse_named_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, key_clausesets }; + + +/* + * Clauses that can be found in a 'server' statement. + */ +static cfg_clausedef_t +server_clauses[] = { + { "bogus", &cfg_type_boolean, 0 }, + { "provide-ixfr", &cfg_type_boolean, 0 }, + { "request-ixfr", &cfg_type_boolean, 0 }, + { "support-ixfr", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "transfers", &cfg_type_uint32, 0 }, + { "transfer-format", &cfg_type_transferformat, 0 }, + { "keys", &cfg_type_server_key_kludge, 0 }, + { "edns", &cfg_type_boolean, 0 }, + { NULL, NULL, 0 } +}; +static cfg_clausedef_t * +server_clausesets[] = { + server_clauses, + NULL +}; +static cfg_type_t cfg_type_server = { + "server", cfg_parse_addressed_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, + server_clausesets +}; + + +/* + * Clauses that can be found in a 'channel' clause in the + * 'logging' statement. + * + * These have some additional constraints that need to be + * checked after parsing: + * - There must exactly one of file/syslog/null/stderr + * + */ +static cfg_clausedef_t +channel_clauses[] = { + /* Destinations. We no longer require these to be first. */ + { "file", &cfg_type_logfile, 0 }, + { "syslog", &cfg_type_optional_facility, 0 }, + { "null", &cfg_type_void, 0 }, + { "stderr", &cfg_type_void, 0 }, + /* Options. We now accept these for the null channel, too. */ + { "severity", &cfg_type_logseverity, 0 }, + { "print-time", &cfg_type_boolean, 0 }, + { "print-severity", &cfg_type_boolean, 0 }, + { "print-category", &cfg_type_boolean, 0 }, + { NULL, NULL, 0 } +}; +static cfg_clausedef_t * +channel_clausesets[] = { + channel_clauses, + NULL +}; +static cfg_type_t cfg_type_channel = { + "channel", cfg_parse_named_map, cfg_print_map, cfg_doc_map, + &cfg_rep_map, channel_clausesets +}; + +/* A list of log destination, used in the "category" clause. */ +static cfg_type_t cfg_type_destinationlist = { + "destinationlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, + &cfg_rep_list, &cfg_type_astring }; + +/* + * Clauses that can be found in a 'logging' statement. + */ +static cfg_clausedef_t +logging_clauses[] = { + { "channel", &cfg_type_channel, CFG_CLAUSEFLAG_MULTI }, + { "category", &cfg_type_category, CFG_CLAUSEFLAG_MULTI }, + { NULL, NULL, 0 } +}; +static cfg_clausedef_t * +logging_clausesets[] = { + logging_clauses, + NULL +}; +static cfg_type_t cfg_type_logging = { + "logging", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, logging_clausesets }; + + +static isc_result_t +parse_unitstring(char *str, isc_resourcevalue_t *valuep) { + char *endp; + unsigned int len; + isc_uint64_t value; + isc_uint64_t unit; + + value = isc_string_touint64(str, &endp, 10); + if (*endp == 0) { + *valuep = value; + return (ISC_R_SUCCESS); + } + + len = strlen(str); + if (len < 2 || endp[1] != '\0') + return (ISC_R_FAILURE); + + switch (str[len - 1]) { + case 'k': + case 'K': + unit = 1024; + break; + case 'm': + case 'M': + unit = 1024 * 1024; + break; + case 'g': + case 'G': + unit = 1024 * 1024 * 1024; + break; + default: + return (ISC_R_FAILURE); + } + if (value > ISC_UINT64_MAX / unit) + return (ISC_R_FAILURE); + *valuep = value * unit; + return (ISC_R_SUCCESS); +} + +static isc_result_t +parse_sizeval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + isc_result_t result; + cfg_obj_t *obj = NULL; + isc_uint64_t val; + + UNUSED(type); + + CHECK(cfg_gettoken(pctx, 0)); + CHECK(parse_unitstring(pctx->token.value.as_pointer, &val)); + + CHECK(cfg_create_obj(pctx, &cfg_type_uint64, &obj)); + obj->value.uint64 = val; + *ret = obj; + return (ISC_R_SUCCESS); + + cleanup: + cfg_parser_error(pctx, CFG_LOG_NEAR, "expected integer and optional unit"); + return (result); +} + +/* + * A size value (number + optional unit). + */ +static cfg_type_t cfg_type_sizeval = { + "sizeval", parse_sizeval, cfg_print_uint64, cfg_doc_terminal, + &cfg_rep_uint64, NULL }; + +/* + * A size, "unlimited", or "default". + */ + +static isc_result_t +parse_size(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + return (parse_enum_or_other(pctx, type, &cfg_type_sizeval, ret)); +} + +static const char *size_enums[] = { "unlimited", "default", NULL }; +static cfg_type_t cfg_type_size = { + "size", parse_size, cfg_print_ustring, cfg_doc_terminal, + &cfg_rep_string, size_enums +}; + +/* + * A size or "unlimited", but not "default". + */ +static const char *sizenodefault_enums[] = { "unlimited", NULL }; +static cfg_type_t cfg_type_sizenodefault = { + "size_no_default", parse_size, cfg_print_ustring, cfg_doc_terminal, + &cfg_rep_string, sizenodefault_enums +}; + +/* + * optional_keyvalue + */ +static isc_result_t +parse_maybe_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, + isc_boolean_t optional, cfg_obj_t **ret) +{ + isc_result_t result; + cfg_obj_t *obj = NULL; + const keyword_type_t *kw = type->of; + + CHECK(cfg_peektoken(pctx, 0)); + if (pctx->token.type == isc_tokentype_string && + strcasecmp(pctx->token.value.as_pointer, kw->name) == 0) { + CHECK(cfg_gettoken(pctx, 0)); + CHECK(kw->type->parse(pctx, kw->type, &obj)); + obj->type = type; /* XXX kludge */ + } else { + if (optional) { + CHECK(cfg_parse_void(pctx, NULL, &obj)); + } else { + cfg_parser_error(pctx, CFG_LOG_NEAR, "expected '%s'", + kw->name); + result = ISC_R_UNEXPECTEDTOKEN; + goto cleanup; + } + } + *ret = obj; + cleanup: + return (result); +} + +static isc_result_t +parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype, + const cfg_type_t *othertype, cfg_obj_t **ret) +{ + isc_result_t result; + CHECK(cfg_peektoken(pctx, 0)); + if (pctx->token.type == isc_tokentype_string && + cfg_is_enum(pctx->token.value.as_pointer, enumtype->of)) { + CHECK(cfg_parse_enum(pctx, enumtype, ret)); + } else { + CHECK(cfg_parse_obj(pctx, othertype, ret)); + } + cleanup: + return (result); +} + +static void +doc_enum_or_other(cfg_printer_t *pctx, const cfg_type_t *type) { + cfg_doc_terminal(pctx, type); +#if 0 /* XXX */ + cfg_print_chars(pctx, "( ", 2);... +#endif + +} + +static isc_result_t +parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + return (parse_maybe_optional_keyvalue(pctx, type, ISC_FALSE, ret)); +} + +static isc_result_t +parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + return (parse_maybe_optional_keyvalue(pctx, type, ISC_TRUE, ret)); +} + +static void +print_keyvalue(cfg_printer_t *pctx, cfg_obj_t *obj) { + const keyword_type_t *kw = obj->type->of; + cfg_print_cstr(pctx, kw->name); + cfg_print_chars(pctx, " ", 1); + kw->type->print(pctx, obj); +} + +static void +doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) { + const keyword_type_t *kw = type->of; + cfg_print_cstr(pctx, kw->name); + cfg_print_chars(pctx, " ", 1); + cfg_doc_obj(pctx, kw->type); +} + +static void +doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) { + const keyword_type_t *kw = type->of; + cfg_print_chars(pctx, "[ ", 2); + cfg_print_cstr(pctx, kw->name); + cfg_print_chars(pctx, " ", 1); + cfg_doc_obj(pctx, kw->type); + cfg_print_chars(pctx, " ]", 2); +} + +static const char *dialup_enums[] = { + "notify", "notify-passive", "refresh", "passive", NULL }; +static isc_result_t +parse_dialup_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); +} +static cfg_type_t cfg_type_dialuptype = { + "dialuptype", parse_dialup_type, cfg_print_ustring, doc_enum_or_other, + &cfg_rep_string, dialup_enums +}; + +static const char *notify_enums[] = { "explicit", NULL }; +static isc_result_t +parse_notify_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); +} +static cfg_type_t cfg_type_notifytype = { + "notifytype", parse_notify_type, cfg_print_ustring, doc_enum_or_other, + &cfg_rep_string, notify_enums, +}; + +static keyword_type_t key_kw = { "key", &cfg_type_astring }; + +LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_keyref = { + "keyref", parse_keyvalue, print_keyvalue, doc_keyvalue, + &cfg_rep_string, &key_kw +}; + +static cfg_type_t cfg_type_optional_keyref = { + "optional_keyref", parse_optional_keyvalue, print_keyvalue, + doc_optional_keyvalue, &cfg_rep_string, &key_kw +}; + +/* + * A "controls" statement is represented as a map with the multivalued + * "inet" and "unix" clauses. Inet controls are tuples; unix controls + * are cfg_unsupported_t objects. + */ + +static keyword_type_t controls_allow_kw = { + "allow", &cfg_type_bracketed_aml }; +static cfg_type_t cfg_type_controls_allow = { + "controls_allow", parse_keyvalue, + print_keyvalue, doc_keyvalue, + &cfg_rep_list, &controls_allow_kw +}; + +static keyword_type_t controls_keys_kw = { + "keys", &cfg_type_keylist }; +static cfg_type_t cfg_type_controls_keys = { + "controls_keys", parse_optional_keyvalue, + print_keyvalue, doc_optional_keyvalue, + &cfg_rep_list, &controls_keys_kw +}; + +static cfg_tuplefielddef_t inetcontrol_fields[] = { + { "address", &cfg_type_controls_sockaddr, 0 }, + { "allow", &cfg_type_controls_allow, 0 }, + { "keys", &cfg_type_controls_keys, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_inetcontrol = { + "inetcontrol", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + inetcontrol_fields +}; + +static cfg_clausedef_t +controls_clauses[] = { + { "inet", &cfg_type_inetcontrol, CFG_CLAUSEFLAG_MULTI }, + { "unix", &cfg_type_unsupported, + CFG_CLAUSEFLAG_MULTI|CFG_CLAUSEFLAG_NOTIMP }, + { NULL, NULL, 0 } +}; +static cfg_clausedef_t * +controls_clausesets[] = { + controls_clauses, + NULL +}; +static cfg_type_t cfg_type_controls = { + "controls", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, &controls_clausesets +}; + +/* + * An optional class, as used in view and zone statements. + */ +static isc_result_t +parse_optional_class(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + isc_result_t result; + UNUSED(type); + CHECK(cfg_peektoken(pctx, 0)); + if (pctx->token.type == isc_tokentype_string) + CHECK(cfg_parse_obj(pctx, &cfg_type_ustring, ret)); + else + CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret)); + cleanup: + return (result); +} + +static cfg_type_t cfg_type_optional_class = { + "optional_class", parse_optional_class, NULL, cfg_doc_terminal, + NULL, NULL +}; + +static isc_result_t +parse_querysource(cfg_parser_t *pctx, int flags, cfg_obj_t **ret) { + isc_result_t result; + cfg_obj_t *obj = NULL; + isc_netaddr_t netaddr; + in_port_t port; + unsigned int have_address = 0; + unsigned int have_port = 0; + + if ((flags & CFG_ADDR_V4OK) != 0) + isc_netaddr_any(&netaddr); + else if ((flags & CFG_ADDR_V6OK) != 0) + isc_netaddr_any6(&netaddr); + else + INSIST(0); + + port = 0; + + CHECK(cfg_create_obj(pctx, &cfg_type_querysource, &obj)); + for (;;) { + CHECK(cfg_peektoken(pctx, 0)); + if (pctx->token.type == isc_tokentype_string) { + if (strcasecmp(pctx->token.value.as_pointer, + "address") == 0) + { + /* read "address" */ + CHECK(cfg_gettoken(pctx, 0)); + CHECK(cfg_parse_rawaddr(pctx, flags|CFG_ADDR_WILDOK, &netaddr)); + have_address++; + } else if (strcasecmp(pctx->token.value.as_pointer, + "port") == 0) + { + /* read "port" */ + CHECK(cfg_gettoken(pctx, 0)); + CHECK(cfg_parse_rawport(pctx, CFG_ADDR_WILDOK, &port)); + have_port++; + } else { + cfg_parser_error(pctx, CFG_LOG_NEAR, + "expected 'address' or 'port'"); + return (ISC_R_UNEXPECTEDTOKEN); + } + } else + break; + } + if (have_address > 1 || have_port > 1 || + have_address + have_port == 0) { + cfg_parser_error(pctx, 0, "expected one address and/or port"); + return (ISC_R_UNEXPECTEDTOKEN); + } + + isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port); + *ret = obj; + return (ISC_R_SUCCESS); + + cleanup: + cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid query source"); + CLEANUP_OBJ(obj); + return (result); +} + +static isc_result_t +parse_querysource4(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + UNUSED(type); + return (parse_querysource(pctx, CFG_ADDR_V4OK, ret)); +} + +static isc_result_t +parse_querysource6(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + UNUSED(type); + return (parse_querysource(pctx, CFG_ADDR_V6OK, ret)); +} + +static void +print_querysource(cfg_printer_t *pctx, cfg_obj_t *obj) { + isc_netaddr_t na; + isc_netaddr_fromsockaddr(&na, &obj->value.sockaddr); + cfg_print_chars(pctx, "address ", 8); + cfg_print_rawaddr(pctx, &na); + cfg_print_chars(pctx, " port ", 6); + cfg_print_rawuint(pctx, isc_sockaddr_getport(&obj->value.sockaddr)); +} + +static cfg_type_t cfg_type_querysource4 = { + "querysource4", parse_querysource4, NULL, cfg_doc_terminal, + NULL, NULL +}; +static cfg_type_t cfg_type_querysource6 = { + "querysource6", parse_querysource6, NULL, cfg_doc_terminal, + NULL, NULL +}; +static cfg_type_t cfg_type_querysource = { + "querysource", NULL, print_querysource, NULL, &cfg_rep_sockaddr, NULL }; + +/* addrmatchelt */ + +static isc_result_t +parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + isc_result_t result; + UNUSED(type); + + CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING)); + + if (pctx->token.type == isc_tokentype_string || + pctx->token.type == isc_tokentype_qstring) { + if (pctx->token.type == isc_tokentype_string && + (strcasecmp(pctx->token.value.as_pointer, "key") == 0)) { + CHECK(cfg_parse_obj(pctx, &cfg_type_keyref, ret)); + } else { + if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | + CFG_ADDR_V4PREFIXOK | + CFG_ADDR_V6OK)) + { + CHECK(cfg_parse_netprefix(pctx, NULL, ret)); + } else { + CHECK(cfg_parse_astring(pctx, NULL, ret)); + } + } + } else if (pctx->token.type == isc_tokentype_special) { + if (pctx->token.value.as_char == '{') { + /* Nested match list. */ + CHECK(cfg_parse_obj(pctx, &cfg_type_bracketed_aml, ret)); + } else if (pctx->token.value.as_char == '!') { + CHECK(cfg_gettoken(pctx, 0)); /* read "!" */ + CHECK(cfg_parse_obj(pctx, &cfg_type_negated, ret)); + } else { + goto bad; + } + } else { + bad: + cfg_parser_error(pctx, CFG_LOG_NEAR, + "expected IP match list element"); + return (ISC_R_UNEXPECTEDTOKEN); + } + cleanup: + return (result); +} + +/* + * A negated address match list element (like "! 10.0.0.1"). + * Somewhat sneakily, the caller is expected to parse the + * "!", but not to print it. + */ + +static cfg_tuplefielddef_t negated_fields[] = { + { "value", &cfg_type_addrmatchelt, 0 }, + { NULL, NULL, 0 } +}; + +static void +print_negated(cfg_printer_t *pctx, cfg_obj_t *obj) { + cfg_print_chars(pctx, "!", 1); + cfg_print_tuple(pctx, obj); +} + +static cfg_type_t cfg_type_negated = { + "negated", cfg_parse_tuple, print_negated, NULL, &cfg_rep_tuple, + &negated_fields +}; + +/* An address match list element */ + +static cfg_type_t cfg_type_addrmatchelt = { + "address_match_element", parse_addrmatchelt, NULL, cfg_doc_terminal, + NULL, NULL +}; + +/* A bracketed address match list */ + +static cfg_type_t cfg_type_bracketed_aml = { + "bracketed_aml", cfg_parse_bracketed_list, cfg_print_bracketed_list, + cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_addrmatchelt +}; + +/* + * The socket address syntax in the "controls" statement is silly. + * It allows both socket address families, but also allows "*", + * whis is gratuitously interpreted as the IPv4 wildcard address. + */ +static unsigned int controls_sockaddr_flags = + CFG_ADDR_V4OK | CFG_ADDR_V6OK | CFG_ADDR_WILDOK; +static cfg_type_t cfg_type_controls_sockaddr = { + "controls_sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, + cfg_doc_sockaddr, &cfg_rep_sockaddr, &controls_sockaddr_flags +}; + + +/* + * Handle the special kludge syntax of the "keys" clause in the "server" + * statement, which takes a single key with or without braces and semicolon. + */ +static isc_result_t +parse_server_key_kludge(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) +{ + isc_result_t result; + isc_boolean_t braces = ISC_FALSE; + UNUSED(type); + + /* Allow opening brace. */ + CHECK(cfg_peektoken(pctx, 0)); + if (pctx->token.type == isc_tokentype_special && + pctx->token.value.as_char == '{') { + result = cfg_gettoken(pctx, 0); + braces = ISC_TRUE; + } + + CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret)); + + if (braces) { + /* Skip semicolon if present. */ + CHECK(cfg_peektoken(pctx, 0)); + if (pctx->token.type == isc_tokentype_special && + pctx->token.value.as_char == ';') + CHECK(cfg_gettoken(pctx, 0)); + + CHECK(cfg_parse_special(pctx, '}')); + } + cleanup: + return (result); +} +static cfg_type_t cfg_type_server_key_kludge = { + "server_key", parse_server_key_kludge, NULL, cfg_doc_terminal, + NULL, NULL +}; + + +/* + * An optional logging facility. + */ + +static isc_result_t +parse_optional_facility(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) +{ + isc_result_t result; + UNUSED(type); + + CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING)); + if (pctx->token.type == isc_tokentype_string || + pctx->token.type == isc_tokentype_qstring) { + CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret)); + } else { + CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret)); + } + cleanup: + return (result); +} + +static cfg_type_t cfg_type_optional_facility = { + "optional_facility", parse_optional_facility, NULL, cfg_doc_terminal, + NULL, NULL }; + + +/* + * A log severity. Return as a string, except "debug N", + * which is returned as a keyword object. + */ + +static keyword_type_t debug_kw = { "debug", &cfg_type_uint32 }; +static cfg_type_t cfg_type_debuglevel = { + "debuglevel", parse_keyvalue, + print_keyvalue, doc_keyvalue, + &cfg_rep_uint32, &debug_kw +}; + +static isc_result_t +parse_logseverity(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + isc_result_t result; + UNUSED(type); + + CHECK(cfg_peektoken(pctx, 0)); + if (pctx->token.type == isc_tokentype_string && + strcasecmp(pctx->token.value.as_pointer, "debug") == 0) { + CHECK(cfg_gettoken(pctx, 0)); /* read "debug" */ + CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER)); + if (pctx->token.type == isc_tokentype_number) { + CHECK(cfg_parse_uint32(pctx, NULL, ret)); + } else { + /* + * The debug level is optional and defaults to 1. + * This makes little sense, but we support it for + * compatibility with BIND 8. + */ + CHECK(cfg_create_obj(pctx, &cfg_type_uint32, ret)); + (*ret)->value.uint32 = 1; + } + (*ret)->type = &cfg_type_debuglevel; /* XXX kludge */ + } else { + CHECK(cfg_parse_obj(pctx, &cfg_type_loglevel, ret)); + } + cleanup: + return (result); +} + +static cfg_type_t cfg_type_logseverity = { + "log_severity", parse_logseverity, NULL, cfg_doc_terminal, + NULL, NULL }; + +/* + * The "file" clause of the "channel" statement. + * This is yet another special case. + */ + +static const char *logversions_enums[] = { "unlimited", NULL }; +static isc_result_t +parse_logversions(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + return (parse_enum_or_other(pctx, type, &cfg_type_uint32, ret)); +} +static cfg_type_t cfg_type_logversions = { + "logversions", parse_logversions, cfg_print_ustring, cfg_doc_terminal, + &cfg_rep_string, logversions_enums +}; + +static cfg_tuplefielddef_t logfile_fields[] = { + { "file", &cfg_type_qstring, 0 }, + { "versions", &cfg_type_logversions, 0 }, + { "size", &cfg_type_size, 0 }, + { NULL, NULL, 0 } +}; + +static isc_result_t +parse_logfile(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + isc_result_t result; + cfg_obj_t *obj = NULL; + const cfg_tuplefielddef_t *fields = type->of; + + CHECK(cfg_create_tuple(pctx, type, &obj)); + + /* Parse the mandatory "file" field */ + CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0])); + + /* Parse "versions" and "size" fields in any order. */ + for (;;) { + CHECK(cfg_peektoken(pctx, 0)); + if (pctx->token.type == isc_tokentype_string) { + CHECK(cfg_gettoken(pctx, 0)); + if (strcasecmp(pctx->token.value.as_pointer, + "versions") == 0 && + obj->value.tuple[1] == NULL) { + CHECK(cfg_parse_obj(pctx, fields[1].type, + &obj->value.tuple[1])); + } else if (strcasecmp(pctx->token.value.as_pointer, + "size") == 0 && + obj->value.tuple[2] == NULL) { + CHECK(cfg_parse_obj(pctx, fields[2].type, + &obj->value.tuple[2])); + } else { + break; + } + } else { + break; + } + } + + /* Create void objects for missing optional values. */ + if (obj->value.tuple[1] == NULL) + CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1])); + if (obj->value.tuple[2] == NULL) + CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2])); + + *ret = obj; + return (ISC_R_SUCCESS); + + cleanup: + CLEANUP_OBJ(obj); + return (result); +} + +static void +print_logfile(cfg_printer_t *pctx, cfg_obj_t *obj) { + cfg_print_obj(pctx, obj->value.tuple[0]); /* file */ + if (obj->value.tuple[1]->type->print != cfg_print_void) { + cfg_print_chars(pctx, " versions ", 10); + cfg_print_obj(pctx, obj->value.tuple[1]); + } + if (obj->value.tuple[2]->type->print != cfg_print_void) { + cfg_print_chars(pctx, " size ", 6); + cfg_print_obj(pctx, obj->value.tuple[2]); + } +} + +static cfg_type_t cfg_type_logfile = { + "log_file", parse_logfile, print_logfile, cfg_doc_terminal, + &cfg_rep_tuple, logfile_fields +}; + +/* An IPv4/IPv6 address with optional port, "*" accepted as wildcard. */ +static unsigned int sockaddr4wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V4OK; +static cfg_type_t cfg_type_sockaddr4wild = { + "sockaddr4wild", cfg_parse_sockaddr, cfg_print_sockaddr, + cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr4wild_flags +}; + +static unsigned int sockaddr6wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V6OK; +static cfg_type_t cfg_type_sockaddr6wild = { + "v6addrportwild", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr, + &cfg_rep_sockaddr, &sockaddr6wild_flags +}; + +/* + * lwres + */ + +static cfg_tuplefielddef_t lwres_view_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "class", &cfg_type_optional_class, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_lwres_view = { + "lwres_view", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + lwres_view_fields +}; + +static cfg_type_t cfg_type_lwres_searchlist = { + "lwres_searchlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, + &cfg_rep_list, &cfg_type_astring }; + +static cfg_clausedef_t +lwres_clauses[] = { + { "listen-on", &cfg_type_portiplist, 0 }, + { "view", &cfg_type_lwres_view, 0 }, + { "search", &cfg_type_lwres_searchlist, 0 }, + { "ndots", &cfg_type_uint32, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_clausedef_t * +lwres_clausesets[] = { + lwres_clauses, + NULL +}; +static cfg_type_t cfg_type_lwres = { + "lwres", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, lwres_clausesets }; + +/* + * rndc + */ + +static cfg_clausedef_t +rndcconf_options_clauses[] = { + { "default-server", &cfg_type_astring, 0 }, + { "default-key", &cfg_type_astring, 0 }, + { "default-port", &cfg_type_uint32, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_clausedef_t * +rndcconf_options_clausesets[] = { + rndcconf_options_clauses, + NULL +}; + +static cfg_type_t cfg_type_rndcconf_options = { + "rndcconf_options", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, + rndcconf_options_clausesets +}; + +static cfg_clausedef_t +rndcconf_server_clauses[] = { + { "key", &cfg_type_astring, 0 }, + { "port", &cfg_type_uint32, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_clausedef_t * +rndcconf_server_clausesets[] = { + rndcconf_server_clauses, + NULL +}; + +static cfg_type_t cfg_type_rndcconf_server = { + "rndcconf_server", cfg_parse_named_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, + rndcconf_server_clausesets +}; + +static cfg_clausedef_t +rndcconf_clauses[] = { + { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI }, + { "server", &cfg_type_rndcconf_server, CFG_CLAUSEFLAG_MULTI }, + { "options", &cfg_type_rndcconf_options, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_clausedef_t * +rndcconf_clausesets[] = { + rndcconf_clauses, + NULL +}; + +LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndcconf = { + "rndcconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, + &cfg_rep_map, rndcconf_clausesets +}; + +static cfg_clausedef_t +rndckey_clauses[] = { + { "key", &cfg_type_key, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_clausedef_t * +rndckey_clausesets[] = { + rndckey_clauses, + NULL +}; + +LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndckey = { + "rndckey", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, + &cfg_rep_map, rndckey_clausesets +}; diff --git a/lib/isccfg/parser.c b/lib/isccfg/parser.c index 5461037570..2209072f7e 100644 --- a/lib/isccfg/parser.c +++ b/lib/isccfg/parser.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: parser.c,v 1.95 2001/12/18 23:23:32 marka Exp $ */ +/* $Id: parser.c,v 1.96 2002/01/04 02:32:12 gson Exp $ */ #include @@ -34,49 +34,15 @@ #include #include +#include #include /* Shorthand */ #define CAT CFG_LOGCATEGORY_CONFIG #define MOD CFG_LOGMODULE_PARSER -#define QSTRING (ISC_LEXOPT_QSTRING | ISC_LEXOPT_QSTRINGMULTILINE) - -/* - * Pass one of these flags to parser_error() to include the - * token text in log message. - */ -#define LOG_NEAR 0x00000001 /* Say "near " */ -#define LOG_BEFORE 0x00000002 /* Say "before " */ -#define LOG_NOPREP 0x00000004 /* Say just "" */ - #define MAP_SYM 1 /* Unique type for isc_symtab */ -/* Clause may occur multiple times (e.g., "zone") */ -#define CFG_CLAUSEFLAG_MULTI 0x00000001 -/* Clause is obsolete */ -#define CFG_CLAUSEFLAG_OBSOLETE 0x00000002 -/* Clause is not implemented, and may never be */ -#define CFG_CLAUSEFLAG_NOTIMP 0x00000004 -/* Clause is not implemented yet */ -#define CFG_CLAUSEFLAG_NYI 0x00000008 -/* Default value has changed since earlier release */ -#define CFG_CLAUSEFLAG_NEWDEFAULT 0x00000010 -/* - * Clause needs to be interpreted during parsing - * by calling a callback function, like the - * "directory" option. - */ -#define CFG_CLAUSEFLAG_CALLBACK 0x00000020 - -/* - * Flags defining whether to accept certain types of network addresses. - */ -#define V4OK 0x00000001 -#define V4PREFIXOK 0x00000002 -#define V6OK 0x00000004 -#define WILDOK 0x00000008 - /* Check a return value. */ #define CHECK(op) \ do { result = (op); \ @@ -88,234 +54,12 @@ do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0) -typedef struct cfg_clausedef cfg_clausedef_t; -typedef struct cfg_tuplefielddef cfg_tuplefielddef_t; -typedef struct cfg_printer cfg_printer_t; -typedef ISC_LIST(cfg_listelt_t) cfg_list_t; -typedef struct cfg_map cfg_map_t; -typedef struct cfg_rep cfg_rep_t; - -/* - * Function types for configuration object methods - */ - -typedef isc_result_t (*cfg_parsefunc_t)(cfg_parser_t *, const cfg_type_t *type, - cfg_obj_t **); -typedef void (*cfg_printfunc_t)(cfg_printer_t *, cfg_obj_t *); -typedef void (*cfg_freefunc_t)(cfg_parser_t *, cfg_obj_t *); - - -/* - * Structure definitions - */ - -/* The parser object. */ -struct cfg_parser { - isc_mem_t * mctx; - isc_log_t * lctx; - isc_lex_t * lexer; - unsigned int errors; - unsigned int warnings; - isc_token_t token; - - /* We are at the end of all input. */ - isc_boolean_t seen_eof; - - /* The current token has been pushed back. */ - isc_boolean_t ungotten; - - /* - * The stack of currently active files, represented - * as a configuration list of configuration strings. - * The head is the top-level file, subsequent elements - * (if any) are the nested include files, and the - * last element is the file currently being parsed. - */ - cfg_obj_t * open_files; - - /* - * Names of files that we have parsed and closed - * and were previously on the open_file list. - * We keep these objects around after closing - * the files because the file names may still be - * referenced from other configuration objects - * for use in reporting semantic errors after - * parsing is complete. - */ - cfg_obj_t * closed_files; - - /* - * Current line number. We maintain our own - * copy of this so that it is available even - * when a file has just been closed. - */ - unsigned int line; - - cfg_parsecallback_t callback; - void *callbackarg; -}; - -/* - * A configuration printer object. This is an abstract - * interface to a destination to which text can be printed - * by calling the function 'f'. - */ -struct cfg_printer { - void (*f)(void *closure, const char *text, int textlen); - void *closure; - int indent; -}; - -/* A clause definition. */ - -struct cfg_clausedef { - const char *name; - cfg_type_t *type; - unsigned int flags; -}; - -/* A tuple field definition. */ - -struct cfg_tuplefielddef { - const char *name; - cfg_type_t *type; - unsigned int flags; -}; - -/* A configuration object type definition. */ -struct cfg_type { - const char *name; /* For debugging purposes only */ - cfg_parsefunc_t parse; - cfg_printfunc_t print; - cfg_rep_t * rep; /* Data representation */ - const void * of; /* For meta-types */ -}; - -/* A keyword-type definition, for things like "port ". */ - -typedef struct { - const char *name; - const cfg_type_t *type; -} keyword_type_t; - -struct cfg_map { - cfg_obj_t *id; /* Used for 'named maps' like keys, zones, &c */ - const cfg_clausedef_t * const *clausesets; /* The clauses that - can occur in this map; - used for printing */ - isc_symtab_t *symtab; -}; - -typedef struct cfg_netprefix cfg_netprefix_t; - -struct cfg_netprefix { - isc_netaddr_t address; /* IP4/IP6 */ - unsigned int prefixlen; -}; - -/* - * A configuration data representation. - */ -struct cfg_rep { - const char * name; /* For debugging only */ - cfg_freefunc_t free; /* How to free this kind of data. */ -}; - -/* - * A configuration object. This is the main building block - * of the configuration parse tree. - */ - -struct cfg_obj { - const cfg_type_t *type; - union { - isc_uint32_t uint32; - isc_uint64_t uint64; - isc_textregion_t string; /* null terminated, too */ - isc_boolean_t boolean; - cfg_map_t map; - cfg_list_t list; - cfg_obj_t ** tuple; - isc_sockaddr_t sockaddr; - cfg_netprefix_t netprefix; - } value; - char * file; - unsigned int line; -}; - - -/* A list element. */ - -struct cfg_listelt { - cfg_obj_t *obj; - ISC_LINK(cfg_listelt_t) link; -}; - /* * Forward declarations of static functions. */ -static isc_result_t -create_cfgobj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp); - -static isc_result_t -create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp); - -static isc_result_t -create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp); - -static void -free_list(cfg_parser_t *pctx, cfg_obj_t *obj); - -static isc_result_t -create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type, - cfg_obj_t **ret); - -static isc_result_t -parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); - static void -free_string(cfg_parser_t *pctx, cfg_obj_t *obj); - -static isc_result_t -create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp); - -static isc_result_t -create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp); - -static void -free_map(cfg_parser_t *pctx, cfg_obj_t *obj); - -static isc_result_t -get_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na); - -static void -print(cfg_printer_t *pctx, const char *text, int len); - -static void -print_void(cfg_printer_t *pctx, cfg_obj_t *obj); - -static isc_result_t -parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype, - const cfg_type_t *othertype, cfg_obj_t **ret); - -static isc_result_t -parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); - -static void -print_mapbody(cfg_printer_t *pctx, cfg_obj_t *obj); - -static isc_result_t -parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); - -static void -print_map(cfg_printer_t *pctx, cfg_obj_t *obj); - -static isc_result_t -parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); - -static isc_result_t -parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); +free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj); static isc_result_t parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); @@ -323,41 +67,24 @@ parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); static void print_list(cfg_printer_t *pctx, cfg_obj_t *obj); -static isc_result_t -parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); - -static void -print_tuple(cfg_printer_t *pctx, cfg_obj_t *obj); - static void -free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj); - -static isc_result_t -parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); - -static void -print_spacelist(cfg_printer_t *pctx, cfg_obj_t *obj); - -static void -print_sockaddr(cfg_printer_t *pctx, cfg_obj_t *obj); +free_list(cfg_parser_t *pctx, cfg_obj_t *obj); static isc_result_t -parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); +create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp); static isc_result_t -parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); +create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type, + cfg_obj_t **ret); static void -print_bracketed_list(cfg_printer_t *pctx, cfg_obj_t *obj); - -static isc_result_t -parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); +free_string(cfg_parser_t *pctx, cfg_obj_t *obj); static isc_result_t -parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); +create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp); static void -print_keyvalue(cfg_printer_t *pctx, cfg_obj_t *obj); +free_map(cfg_parser_t *pctx, cfg_obj_t *obj); static isc_result_t parse_symtab_elt(cfg_parser_t *pctx, const char *name, @@ -367,39 +94,13 @@ parse_symtab_elt(cfg_parser_t *pctx, const char *name, static void free_noop(cfg_parser_t *pctx, cfg_obj_t *obj); -static isc_result_t -cfg_gettoken(cfg_parser_t *pctx, int options); - -static void -cfg_ungettoken(cfg_parser_t *pctx); - -static isc_result_t -cfg_peektoken(cfg_parser_t *pctx, int options); - static isc_result_t cfg_getstringtoken(cfg_parser_t *pctx); -static void -parser_error(cfg_parser_t *pctx, unsigned int flags, - const char *fmt, ...) ISC_FORMAT_PRINTF(3, 4); - -static void -parser_warning(cfg_parser_t *pctx, unsigned int flags, - const char *fmt, ...) ISC_FORMAT_PRINTF(3, 4); - static void parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning, unsigned int flags, const char *format, va_list args); -static void -print_uint32(cfg_printer_t *pctx, cfg_obj_t *obj); - -static void -print_ustring(cfg_printer_t *pctx, cfg_obj_t *obj); - -static isc_result_t -parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); - /* * Data representations. These correspond to members of the * "value" union in struct cfg_obj (except "void", which does @@ -417,756 +118,31 @@ cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop }; cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop }; cfg_rep_t cfg_rep_void = { "void", free_noop }; -/* - * Forward declarations of configuration type definitions. - * Additional types are declared publicly in cfg.h. - */ - -static cfg_type_t cfg_type_boolean; -static cfg_type_t cfg_type_uint32; -static cfg_type_t cfg_type_qstring; -static cfg_type_t cfg_type_astring; -static cfg_type_t cfg_type_ustring; -static cfg_type_t cfg_type_optional_port; -static cfg_type_t cfg_type_bracketed_aml; -static cfg_type_t cfg_type_acl; -static cfg_type_t cfg_type_portiplist; -static cfg_type_t cfg_type_bracketed_sockaddrlist; -static cfg_type_t cfg_type_sockaddr; -static cfg_type_t cfg_type_netaddr; -static cfg_type_t cfg_type_optional_keyref; -static cfg_type_t cfg_type_options; -static cfg_type_t cfg_type_view; -static cfg_type_t cfg_type_viewopts; -static cfg_type_t cfg_type_key; -static cfg_type_t cfg_type_server; -static cfg_type_t cfg_type_controls; -static cfg_type_t cfg_type_bracketed_sockaddrkeylist; -static cfg_type_t cfg_type_querysource4; -static cfg_type_t cfg_type_querysource6; -static cfg_type_t cfg_type_querysource; -static cfg_type_t cfg_type_sockaddr4wild; -static cfg_type_t cfg_type_sockaddr6wild; -static cfg_type_t cfg_type_sockaddr; -static cfg_type_t cfg_type_netprefix; -static cfg_type_t cfg_type_zone; -static cfg_type_t cfg_type_zoneopts; -static cfg_type_t cfg_type_logging; -static cfg_type_t cfg_type_optional_facility; -static cfg_type_t cfg_type_void; -static cfg_type_t cfg_type_optional_class; -static cfg_type_t cfg_type_destinationlist; -static cfg_type_t cfg_type_size; -static cfg_type_t cfg_type_sizenodefault; -static cfg_type_t cfg_type_negated; -static cfg_type_t cfg_type_addrmatchelt; -static cfg_type_t cfg_type_unsupported; -static cfg_type_t cfg_type_token; -static cfg_type_t cfg_type_server_key_kludge; -static cfg_type_t cfg_type_optional_facility; -static cfg_type_t cfg_type_logseverity; -static cfg_type_t cfg_type_logfile; -static cfg_type_t cfg_type_lwres; -static cfg_type_t cfg_type_controls_sockaddr; -static cfg_type_t cfg_type_notifytype; -static cfg_type_t cfg_type_dialuptype; - /* * Configuration type definitions. */ -/* tkey-dhkey */ - -static cfg_tuplefielddef_t tkey_dhkey_fields[] = { - { "name", &cfg_type_qstring, 0 }, - { "keyid", &cfg_type_uint32, 0 }, - { NULL, NULL, 0 } -}; - -static cfg_type_t cfg_type_tkey_dhkey = { - "tkey-dhkey", parse_tuple, print_tuple, &cfg_rep_tuple, - tkey_dhkey_fields -}; - -/* listen-on */ - -static cfg_tuplefielddef_t listenon_fields[] = { - { "port", &cfg_type_optional_port, 0 }, - { "acl", &cfg_type_bracketed_aml, 0 }, - { NULL, NULL, 0 } -}; -static cfg_type_t cfg_type_listenon = { - "listenon", parse_tuple, print_tuple, &cfg_rep_tuple, listenon_fields }; - -/* acl */ - -static cfg_tuplefielddef_t acl_fields[] = { - { "name", &cfg_type_astring, 0 }, - { "value", &cfg_type_bracketed_aml, 0 }, - { NULL, NULL, 0 } -}; - -static cfg_type_t cfg_type_acl = { - "acl", parse_tuple, print_tuple, &cfg_rep_tuple, acl_fields }; - - -/* - * "sockaddrkeylist", a list of socket addresses with optional keys - * and an optional default port, as used in the masters option. - * E.g., - * "port 1234 { 10.0.0.1 key foo; 1::2 port 69; }" - */ - -static cfg_tuplefielddef_t sockaddrkey_fields[] = { - { "sockaddr", &cfg_type_sockaddr, 0 }, - { "key", &cfg_type_optional_keyref, 0 }, - { NULL, NULL, 0 }, -}; - -static cfg_type_t cfg_type_sockaddrkey = { - "sockaddrkey", parse_tuple, print_tuple, &cfg_rep_tuple, - sockaddrkey_fields -}; - -static cfg_type_t cfg_type_bracketed_sockaddrkeylist = { - "bracketed_sockaddrkeylist", parse_bracketed_list, - print_bracketed_list, &cfg_rep_list, &cfg_type_sockaddrkey -}; - -static cfg_tuplefielddef_t sockaddrkeylist_fields[] = { - { "port", &cfg_type_optional_port, 0 }, - { "addresses", &cfg_type_bracketed_sockaddrkeylist, 0 }, - { NULL, NULL, 0 } -}; -static cfg_type_t cfg_type_sockaddrkeylist = { - "sockaddrkeylist", parse_tuple, print_tuple, &cfg_rep_tuple, - sockaddrkeylist_fields -}; - -/* - * A list of socket addresses with an optional default port, - * as used in the also-notify option. E.g., - * "port 1234 { 10.0.0.1; 1::2 port 69; }" - */ -static cfg_tuplefielddef_t portiplist_fields[] = { - { "port", &cfg_type_optional_port, 0 }, - { "addresses", &cfg_type_bracketed_sockaddrlist, 0 }, - { NULL, NULL, 0 } -}; -static cfg_type_t cfg_type_portiplist = { - "portiplist", parse_tuple, print_tuple, &cfg_rep_tuple, - portiplist_fields -}; - -/* - * A public key, as in the "pubkey" statement. - */ -static cfg_tuplefielddef_t pubkey_fields[] = { - { "flags", &cfg_type_uint32, 0 }, - { "protocol", &cfg_type_uint32, 0 }, - { "algorithm", &cfg_type_uint32, 0 }, - { "key", &cfg_type_qstring, 0 }, - { NULL, NULL, 0 } -}; -static cfg_type_t cfg_type_pubkey = { - "pubkey", parse_tuple, print_tuple, &cfg_rep_tuple, pubkey_fields }; - - -/* - * A list of RR types, used in grant statements. - * Note that the old parser allows quotes around the RR type names. - */ -static cfg_type_t cfg_type_rrtypelist = { - "rrtypelist", parse_spacelist, print_spacelist, &cfg_rep_list, - &cfg_type_astring -}; - -static const char *mode_enums[] = { "grant", "deny", NULL }; -static cfg_type_t cfg_type_mode = { - "mode", parse_enum, print_ustring, &cfg_rep_string, - &mode_enums -}; - -static const char *matchtype_enums[] = { - "name", "subdomain", "wildcard", "self", NULL }; -static cfg_type_t cfg_type_matchtype = { - "matchtype", parse_enum, print_ustring, &cfg_rep_string, - &matchtype_enums -}; - -/* - * A grant statement, used in the update policy. - */ -static cfg_tuplefielddef_t grant_fields[] = { - { "mode", &cfg_type_mode, 0 }, - { "identity", &cfg_type_astring, 0 }, /* domain name */ - { "matchtype", &cfg_type_matchtype, 0 }, - { "name", &cfg_type_astring, 0 }, /* domain name */ - { "types", &cfg_type_rrtypelist, 0 }, - { NULL, NULL, 0 } -}; -static cfg_type_t cfg_type_grant = { - "grant", parse_tuple, print_tuple, &cfg_rep_tuple, grant_fields }; - -static cfg_type_t cfg_type_updatepolicy = { - "update_policy", parse_bracketed_list, print_bracketed_list, - &cfg_rep_list, &cfg_type_grant -}; - -/* - * A view statement. - */ -static cfg_tuplefielddef_t view_fields[] = { - { "name", &cfg_type_astring, 0 }, - { "class", &cfg_type_optional_class, 0 }, - { "options", &cfg_type_viewopts, 0 }, - { NULL, NULL, 0 } -}; -static cfg_type_t cfg_type_view = { - "view", parse_tuple, print_tuple, &cfg_rep_tuple, view_fields }; - -/* - * A zone statement. - */ -static cfg_tuplefielddef_t zone_fields[] = { - { "name", &cfg_type_astring, 0 }, - { "class", &cfg_type_optional_class, 0 }, - { "options", &cfg_type_zoneopts, 0 }, - { NULL, NULL, 0 } -}; -static cfg_type_t cfg_type_zone = { - "zone", parse_tuple, print_tuple, &cfg_rep_tuple, zone_fields }; - -/* - * A "category" clause in the "logging" statement. - */ -static cfg_tuplefielddef_t category_fields[] = { - { "name", &cfg_type_astring, 0 }, - { "destinations", &cfg_type_destinationlist,0 }, - { NULL, NULL, 0 } -}; -static cfg_type_t cfg_type_category = { - "category", parse_tuple, print_tuple, &cfg_rep_tuple, category_fields }; - - -/* - * A trusted key, as used in the "trusted-keys" statement. - */ -static cfg_tuplefielddef_t trustedkey_fields[] = { - { "name", &cfg_type_astring, 0 }, - { "flags", &cfg_type_uint32, 0 }, - { "protocol", &cfg_type_uint32, 0 }, - { "algorithm", &cfg_type_uint32, 0 }, - { "key", &cfg_type_qstring, 0 }, - { NULL, NULL, 0 } -}; -static cfg_type_t cfg_type_trustedkey = { - "trustedkey", parse_tuple, print_tuple, &cfg_rep_tuple, - trustedkey_fields -}; - - -static keyword_type_t wild_class_kw = { "class", &cfg_type_ustring }; - -static cfg_type_t cfg_type_optional_wild_class = { - "optional_wild_class", parse_optional_keyvalue, - print_keyvalue, &cfg_rep_string, &wild_class_kw -}; - -static keyword_type_t wild_type_kw = { "type", &cfg_type_ustring }; - -static cfg_type_t cfg_type_optional_wild_type = { - "optional_wild_type", parse_optional_keyvalue, - print_keyvalue, &cfg_rep_string, &wild_type_kw -}; - -static keyword_type_t wild_name_kw = { "name", &cfg_type_qstring }; - -static cfg_type_t cfg_type_optional_wild_name = { - "optional_wild_name", parse_optional_keyvalue, - print_keyvalue, &cfg_rep_string, &wild_name_kw -}; - -/* - * An rrset ordering element. - */ -static cfg_tuplefielddef_t rrsetorderingelement_fields[] = { - { "class", &cfg_type_optional_wild_class, 0 }, - { "type", &cfg_type_optional_wild_type, 0 }, - { "name", &cfg_type_optional_wild_name, 0 }, - { "order", &cfg_type_ustring, 0 }, /* must be literal "order" */ - { "ordering", &cfg_type_ustring, 0 }, - { NULL, NULL, 0 } -}; -static cfg_type_t cfg_type_rrsetorderingelement = { - "rrsetorderingelement", parse_tuple, print_tuple, &cfg_rep_tuple, - rrsetorderingelement_fields -}; - -/* - * A global or view "check-names" option. Note that the zone - * "check-names" option has a different syntax. - */ -static cfg_tuplefielddef_t checknames_fields[] = { - { "type", &cfg_type_ustring, 0 }, - { "mode", &cfg_type_ustring, 0 }, - { NULL, NULL, 0 } -}; -static cfg_type_t cfg_type_checknames = { - "checknames", parse_tuple, print_tuple, &cfg_rep_tuple, - checknames_fields -}; - -static cfg_type_t cfg_type_bracketed_sockaddrlist = { - "bracketed_sockaddrlist", parse_bracketed_list, print_bracketed_list, - &cfg_rep_list, &cfg_type_sockaddr -}; - -static cfg_type_t cfg_type_rrsetorder = { - "rrsetorder", parse_bracketed_list, print_bracketed_list, - &cfg_rep_list, &cfg_type_rrsetorderingelement -}; - -static keyword_type_t port_kw = { "port", &cfg_type_uint32 }; - -static cfg_type_t cfg_type_optional_port = { - "optional_port", parse_optional_keyvalue, print_keyvalue, - &cfg_rep_uint32, &port_kw -}; - -/* A list of keys, as in the "key" clause of the controls statement. */ -static cfg_type_t cfg_type_keylist = { - "keylist", parse_bracketed_list, print_bracketed_list, &cfg_rep_list, - &cfg_type_astring -}; - -static cfg_type_t cfg_type_trustedkeys = { - "trusted-keys", parse_bracketed_list, print_bracketed_list, &cfg_rep_list, - &cfg_type_trustedkey -}; - /* * An implicit list. These are formed by clauses that occur multiple times. */ static cfg_type_t cfg_type_implicitlist = { - "implicitlist", NULL, print_list, &cfg_rep_list, NULL }; - -static const char *forwardtype_enums[] = { "first", "only", NULL }; -static cfg_type_t cfg_type_forwardtype = { - "forwardtype", parse_enum, print_ustring, &cfg_rep_string, - &forwardtype_enums -}; - -static const char *zonetype_enums[] = { - "master", "slave", "stub", "hint", "forward", NULL }; -static cfg_type_t cfg_type_zonetype = { - "zonetype", parse_enum, print_ustring, &cfg_rep_string, - &zonetype_enums -}; - -static const char *loglevel_enums[] = { - "critical", "error", "warning", "notice", "info", "dynamic", NULL }; -static cfg_type_t cfg_type_loglevel = { - "loglevel", parse_enum, print_ustring, &cfg_rep_string, - &loglevel_enums -}; - -static const char *transferformat_enums[] = { - "many-answers", "one-answer", NULL }; -static cfg_type_t cfg_type_transferformat = { - "transferformat", parse_enum, print_ustring, &cfg_rep_string, - &transferformat_enums -}; - -/* - * The special keyword "none", as used in the pid-file option. - */ - -static void -print_none(cfg_printer_t *pctx, cfg_obj_t *obj) { - UNUSED(obj); - print(pctx, "none", 4); -} - -static cfg_type_t cfg_type_none = { - "none", NULL, print_none, &cfg_rep_void, NULL -}; - -/* - * A quoted string or the special keyword "none". Used in the pid-file option. - */ -static isc_result_t -parse_qstringornone(cfg_parser_t *pctx, const cfg_type_t *type, - cfg_obj_t **ret) -{ - isc_result_t result; - CHECK(cfg_gettoken(pctx, QSTRING)); - if (pctx->token.type == isc_tokentype_string && - strcasecmp(pctx->token.value.as_pointer, "none") == 0) - return (create_cfgobj(pctx, &cfg_type_none, ret)); - cfg_ungettoken(pctx); - return (parse_qstring(pctx, type, ret)); - cleanup: - return (result); -} - -static cfg_type_t cfg_type_qstringornone = { - "qstringornone", parse_qstringornone, NULL, NULL, NULL }; - -/* - * Clauses that can be found within the top level of the named.conf - * file only. - */ -static cfg_clausedef_t -namedconf_clauses[] = { - { "options", &cfg_type_options, 0 }, - { "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI }, - { "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI }, - { "logging", &cfg_type_logging, 0 }, - { "view", &cfg_type_view, CFG_CLAUSEFLAG_MULTI }, - { "lwres", &cfg_type_lwres, CFG_CLAUSEFLAG_MULTI }, - { NULL, NULL, 0 } -}; - -/* - * Clauses that can occur at the top level or in the view - * statement, but not in the options block. - */ -static cfg_clausedef_t -namedconf_or_view_clauses[] = { - { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI }, - { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI }, - { "server", &cfg_type_server, CFG_CLAUSEFLAG_MULTI }, - { "trusted-keys", &cfg_type_trustedkeys, CFG_CLAUSEFLAG_MULTI }, - { NULL, NULL, 0 } -}; - -/* - * Clauses that can be found within the 'options' statement. - */ -static cfg_clausedef_t -options_clauses[] = { - { "blackhole", &cfg_type_bracketed_aml, 0 }, - { "coresize", &cfg_type_size, 0 }, - { "datasize", &cfg_type_size, 0 }, - { "deallocate-on-exit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, - { "directory", &cfg_type_qstring, CFG_CLAUSEFLAG_CALLBACK }, - { "dump-file", &cfg_type_qstring, 0 }, - { "fake-iquery", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, - { "files", &cfg_type_size, 0 }, - { "has-old-clients", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, - { "heartbeat-interval", &cfg_type_uint32, 0 }, - { "host-statistics", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTIMP }, - { "hostname", &cfg_type_qstringornone, 0 }, - { "interface-interval", &cfg_type_uint32, 0 }, - { "listen-on", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI }, - { "listen-on-v6", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI }, - { "match-mapped-addresses", &cfg_type_boolean, 0 }, - { "max-journal-size", &cfg_type_sizenodefault, 0 }, - { "memstatistics-file", &cfg_type_qstring, 0 }, - { "multiple-cnames", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, - { "named-xfer", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE }, - { "pid-file", &cfg_type_qstringornone, 0 }, - { "port", &cfg_type_uint32, 0 }, - { "random-device", &cfg_type_qstring, 0 }, - { "recursive-clients", &cfg_type_uint32, 0 }, - { "rrset-order", &cfg_type_rrsetorder, CFG_CLAUSEFLAG_NOTIMP }, - { "serial-queries", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE }, - { "serial-query-rate", &cfg_type_uint32, 0 }, - { "stacksize", &cfg_type_size, 0 }, - { "statistics-file", &cfg_type_qstring, 0 }, - { "statistics-interval", &cfg_type_uint32, CFG_CLAUSEFLAG_NYI }, - { "tcp-clients", &cfg_type_uint32, 0 }, - { "tkey-dhkey", &cfg_type_tkey_dhkey, 0 }, - { "tkey-gssapi-credential", &cfg_type_qstring, 0 }, - { "tkey-domain", &cfg_type_qstring, 0 }, - { "transfers-per-ns", &cfg_type_uint32, 0 }, - { "transfers-in", &cfg_type_uint32, 0 }, - { "transfers-out", &cfg_type_uint32, 0 }, - { "treat-cr-as-space", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, - { "use-id-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, - { "use-ixfr", &cfg_type_boolean, 0 }, - { "version", &cfg_type_qstringornone, 0 }, - { NULL, NULL, 0 } -}; - -/* - * Clauses that can be found within the 'view' statement, - * with defaults in the 'options' statement. - */ - -static cfg_clausedef_t -view_clauses[] = { - { "allow-recursion", &cfg_type_bracketed_aml, 0 }, - { "allow-v6-synthesis", &cfg_type_bracketed_aml, 0 }, - { "sortlist", &cfg_type_bracketed_aml, 0 }, - { "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_NOTIMP }, - { "auth-nxdomain", &cfg_type_boolean, CFG_CLAUSEFLAG_NEWDEFAULT }, - { "minimal-responses", &cfg_type_boolean, 0 }, - { "recursion", &cfg_type_boolean, 0 }, - { "provide-ixfr", &cfg_type_boolean, 0 }, - { "request-ixfr", &cfg_type_boolean, 0 }, - { "fetch-glue", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, - { "rfc2308-type1", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI }, - { "additional-from-auth", &cfg_type_boolean, 0 }, - { "additional-from-cache", &cfg_type_boolean, 0 }, - /* - * Note that the query-source option syntax is different - * from the other -source options. - */ - { "query-source", &cfg_type_querysource4, 0 }, - { "query-source-v6", &cfg_type_querysource6, 0 }, - { "cleaning-interval", &cfg_type_uint32, 0 }, - { "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP }, - { "lame-ttl", &cfg_type_uint32, 0 }, - { "max-ncache-ttl", &cfg_type_uint32, 0 }, - { "max-cache-ttl", &cfg_type_uint32, 0 }, - { "transfer-format", &cfg_type_transferformat, 0 }, - { "max-cache-size", &cfg_type_sizenodefault, 0 }, - { "check-names", &cfg_type_checknames, - CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_NOTIMP }, - { "cache-file", &cfg_type_qstring, 0 }, - { "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI }, - { NULL, NULL, 0 } -}; - -/* - * Clauses that can be found within the 'view' statement only. - */ -static cfg_clausedef_t -view_only_clauses[] = { - { "match-clients", &cfg_type_bracketed_aml, 0 }, - { "match-destinations", &cfg_type_bracketed_aml, 0 }, - { "match-recursive-only", &cfg_type_boolean, 0 }, - { NULL, NULL, 0 } -}; - -/* - * Clauses that can be found in a 'zone' statement, - * with defaults in the 'view' or 'options' statement. - */ -static cfg_clausedef_t -zone_clauses[] = { - { "allow-query", &cfg_type_bracketed_aml, 0 }, - { "allow-transfer", &cfg_type_bracketed_aml, 0 }, - { "allow-update-forwarding", &cfg_type_bracketed_aml, 0 }, - { "allow-notify", &cfg_type_bracketed_aml, 0 }, - { "notify", &cfg_type_notifytype, 0 }, - { "notify-source", &cfg_type_sockaddr4wild, 0 }, - { "notify-source-v6", &cfg_type_sockaddr6wild, 0 }, - { "also-notify", &cfg_type_portiplist, 0 }, - { "dialup", &cfg_type_dialuptype, 0 }, - { "forward", &cfg_type_forwardtype, 0 }, - { "forwarders", &cfg_type_portiplist, 0 }, - { "ixfr-from-differences", &cfg_type_boolean, 0 }, - { "maintain-ixfr-base", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, - { "max-ixfr-log-size", &cfg_type_size, CFG_CLAUSEFLAG_OBSOLETE }, - { "transfer-source", &cfg_type_sockaddr4wild, 0 }, - { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 }, - { "max-transfer-time-in", &cfg_type_uint32, 0 }, - { "max-transfer-time-out", &cfg_type_uint32, 0 }, - { "max-transfer-idle-in", &cfg_type_uint32, 0 }, - { "max-transfer-idle-out", &cfg_type_uint32, 0 }, - { "max-retry-time", &cfg_type_uint32, 0 }, - { "min-retry-time", &cfg_type_uint32, 0 }, - { "max-refresh-time", &cfg_type_uint32, 0 }, - { "min-refresh-time", &cfg_type_uint32, 0 }, - { "sig-validity-interval", &cfg_type_uint32, 0 }, - { "zone-statistics", &cfg_type_boolean, 0 }, - { NULL, NULL, 0 } -}; - -/* - * Clauses that can be found in a 'zone' statement - * only. - */ -static cfg_clausedef_t -zone_only_clauses[] = { - { "type", &cfg_type_zonetype, 0 }, - { "allow-update", &cfg_type_bracketed_aml, 0 }, - { "file", &cfg_type_qstring, 0 }, - { "ixfr-base", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE }, - { "ixfr-tmp-file", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE }, - { "masters", &cfg_type_sockaddrkeylist, 0 }, - { "pubkey", &cfg_type_pubkey, - CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_OBSOLETE }, - { "update-policy", &cfg_type_updatepolicy, 0 }, - { "database", &cfg_type_astring, 0 }, - /* - * Note that the format of the check-names option is different between - * the zone options and the global/view options. Ugh. - */ - { "check-names", &cfg_type_ustring, CFG_CLAUSEFLAG_NOTIMP }, - { NULL, NULL, 0 } -}; - - -/* The top-level named.conf syntax. */ - -static cfg_clausedef_t * -namedconf_clausesets[] = { - namedconf_clauses, - namedconf_or_view_clauses, - NULL -}; - -LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_namedconf = { - "namedconf", parse_mapbody, print_mapbody, &cfg_rep_map, - namedconf_clausesets -}; - -/* The "options" statement syntax. */ - -static cfg_clausedef_t * -options_clausesets[] = { - options_clauses, - view_clauses, - zone_clauses, - NULL -}; -static cfg_type_t cfg_type_options = { - "options", parse_map, print_map, &cfg_rep_map, options_clausesets }; - -/* The "view" statement syntax. */ - -static cfg_clausedef_t * -view_clausesets[] = { - view_only_clauses, - namedconf_or_view_clauses, - view_clauses, - zone_clauses, - NULL -}; -static cfg_type_t cfg_type_viewopts = { - "view", parse_map, print_map, &cfg_rep_map, view_clausesets }; - -/* The "zone" statement syntax. */ - -static cfg_clausedef_t * -zone_clausesets[] = { - zone_only_clauses, - zone_clauses, - NULL -}; -static cfg_type_t cfg_type_zoneopts = { - "zoneopts", parse_map, print_map, &cfg_rep_map, zone_clausesets }; - -/* - * Clauses that can be found within the 'key' statement. - */ -static cfg_clausedef_t -key_clauses[] = { - { "algorithm", &cfg_type_astring, 0 }, - { "secret", &cfg_type_astring, 0 }, - { NULL, NULL, 0 } -}; - -static cfg_clausedef_t * -key_clausesets[] = { - key_clauses, - NULL -}; -static cfg_type_t cfg_type_key = { - "key", parse_named_map, print_map, &cfg_rep_map, key_clausesets }; - - -/* - * Clauses that can be found in a 'server' statement. - */ -static cfg_clausedef_t -server_clauses[] = { - { "bogus", &cfg_type_boolean, 0 }, - { "provide-ixfr", &cfg_type_boolean, 0 }, - { "request-ixfr", &cfg_type_boolean, 0 }, - { "support-ixfr", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, - { "transfers", &cfg_type_uint32, 0 }, - { "transfer-format", &cfg_type_transferformat, 0 }, - { "keys", &cfg_type_server_key_kludge, 0 }, - { "edns", &cfg_type_boolean, 0 }, - { NULL, NULL, 0 } -}; -static cfg_clausedef_t * -server_clausesets[] = { - server_clauses, - NULL -}; -static cfg_type_t cfg_type_server = { - "server", parse_addressed_map, print_map, &cfg_rep_map, - server_clausesets -}; - - -/* - * Clauses that can be found in a 'channel' clause in the - * 'logging' statement. - * - * These have some additional constraints that need to be - * checked after parsing: - * - There must exactly one of file/syslog/null/stderr - * - */ -static cfg_clausedef_t -channel_clauses[] = { - /* Destinations. We no longer require these to be first. */ - { "file", &cfg_type_logfile, 0 }, - { "syslog", &cfg_type_optional_facility, 0 }, - { "null", &cfg_type_void, 0 }, - { "stderr", &cfg_type_void, 0 }, - /* Options. We now accept these for the null channel, too. */ - { "severity", &cfg_type_logseverity, 0 }, - { "print-time", &cfg_type_boolean, 0 }, - { "print-severity", &cfg_type_boolean, 0 }, - { "print-category", &cfg_type_boolean, 0 }, - { NULL, NULL, 0 } -}; -static cfg_clausedef_t * -channel_clausesets[] = { - channel_clauses, - NULL -}; -static cfg_type_t cfg_type_channel = { - "channel", parse_named_map, print_map, - &cfg_rep_map, channel_clausesets -}; - -/* A list of log destination, used in the "category" clause. */ -static cfg_type_t cfg_type_destinationlist = { - "destinationlist", parse_bracketed_list, print_bracketed_list, - &cfg_rep_list, &cfg_type_astring }; - -/* - * Clauses that can be found in a 'logging' statement. - */ -static cfg_clausedef_t -logging_clauses[] = { - { "channel", &cfg_type_channel, CFG_CLAUSEFLAG_MULTI }, - { "category", &cfg_type_category, CFG_CLAUSEFLAG_MULTI }, - { NULL, NULL, 0 } -}; -static cfg_clausedef_t * -logging_clausesets[] = { - logging_clauses, - NULL -}; -static cfg_type_t cfg_type_logging = { - "logging", parse_map, print_map, &cfg_rep_map, logging_clausesets }; - + "implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL }; /* Functions. */ -static void -print_obj(cfg_printer_t *pctx, cfg_obj_t *obj) { +void +cfg_print_obj(cfg_printer_t *pctx, cfg_obj_t *obj) { obj->type->print(pctx, obj); } -static void -print(cfg_printer_t *pctx, const char *text, int len) { +void +cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) { pctx->f(pctx->closure, text, len); } static void print_open(cfg_printer_t *pctx) { - print(pctx, "{\n", 2); + cfg_print_chars(pctx, "{\n", 2); pctx->indent++; } @@ -1174,7 +150,7 @@ static void print_indent(cfg_printer_t *pctx) { int indent = pctx->indent; while (indent > 0) { - print(pctx, "\t", 1); + cfg_print_chars(pctx, "\t", 1); indent--; } } @@ -1183,11 +159,11 @@ static void print_close(cfg_printer_t *pctx) { pctx->indent--; print_indent(pctx); - print(pctx, "}", 1); + cfg_print_chars(pctx, "}", 1); } -static isc_result_t -parse(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { +isc_result_t +cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; INSIST(ret != NULL && *ret == NULL); result = type->parse(pctx, type, ret); @@ -1212,8 +188,8 @@ cfg_print(cfg_obj_t *obj, /* Tuples. */ -static isc_result_t -create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { +isc_result_t +cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; const cfg_tuplefielddef_t *fields = type->of; const cfg_tuplefielddef_t *f; @@ -1224,7 +200,7 @@ create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { for (f = fields; f->name != NULL; f++) nfields++; - CHECK(create_cfgobj(pctx, type, &obj)); + CHECK(cfg_create_obj(pctx, type, &obj)); obj->value.tuple = isc_mem_get(pctx->mctx, nfields * sizeof(cfg_obj_t *)); if (obj->value.tuple == NULL) { @@ -1241,8 +217,8 @@ create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (result); } -static isc_result_t -parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) +isc_result_t +cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; const cfg_tuplefielddef_t *fields = type->of; @@ -1250,9 +226,9 @@ parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) cfg_obj_t *obj = NULL; unsigned int i; - CHECK(create_tuple(pctx, type, &obj)); + CHECK(cfg_create_tuple(pctx, type, &obj)); for (f = fields, i = 0; f->name != NULL; f++, i++) - CHECK(parse(pctx, f->type, &obj->value.tuple[i])); + CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i])); *ret = obj; return (ISC_R_SUCCESS); @@ -1262,8 +238,8 @@ parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) return (result); } -static void -print_tuple(cfg_printer_t *pctx, cfg_obj_t *obj) { +void +cfg_print_tuple(cfg_printer_t *pctx, cfg_obj_t *obj) { unsigned int i; const cfg_tuplefielddef_t *fields = obj->type->of; const cfg_tuplefielddef_t *f; @@ -1272,9 +248,23 @@ print_tuple(cfg_printer_t *pctx, cfg_obj_t *obj) { for (f = fields, i = 0; f->name != NULL; f++, i++) { cfg_obj_t *fieldobj = obj->value.tuple[i]; if (need_space) - print(pctx, " ", 1); - print_obj(pctx, fieldobj); - need_space = ISC_TF(fieldobj->type->print != print_void); + cfg_print_chars(pctx, " ", 1); + cfg_print_obj(pctx, fieldobj); + need_space = ISC_TF(fieldobj->type->print != cfg_print_void); + } +} + +void +cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) { + const cfg_tuplefielddef_t *fields = type->of; + const cfg_tuplefielddef_t *f; + isc_boolean_t need_space = ISC_FALSE; + + for (f = fields; f->name != NULL; f++) { + if (need_space) + cfg_print_chars(pctx, " ", 1); + cfg_doc_obj(pctx, f->type); + need_space = ISC_TF(f->type->print != cfg_print_void); } } @@ -1319,18 +309,15 @@ cfg_tuple_get(cfg_obj_t *tupleobj, const char* name) { return (NULL); } -/* - * Parse a required special character. - */ -static isc_result_t -parse_special(cfg_parser_t *pctx, int special) { +isc_result_t +cfg_parse_special(cfg_parser_t *pctx, int special) { isc_result_t result; CHECK(cfg_gettoken(pctx, 0)); if (pctx->token.type == isc_tokentype_special && pctx->token.value.as_char == special) return (ISC_R_SUCCESS); - parser_error(pctx, LOG_NEAR, "'%c' expected", special); + cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special); return (ISC_R_UNEXPECTEDTOKEN); cleanup: return (result); @@ -1351,7 +338,7 @@ parse_semicolon(cfg_parser_t *pctx) { pctx->token.value.as_char == ';') return (ISC_R_SUCCESS); - parser_error(pctx, LOG_BEFORE, "missing ';'"); + cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'"); cfg_ungettoken(pctx); cleanup: return (result); @@ -1368,7 +355,7 @@ parse_eof(cfg_parser_t *pctx) { if (pctx->token.type == isc_tokentype_eof) return (ISC_R_SUCCESS); - parser_error(pctx, LOG_NEAR, "syntax error"); + cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error"); return (ISC_R_UNEXPECTEDTOKEN); cleanup: return(result); @@ -1377,13 +364,12 @@ parse_eof(cfg_parser_t *pctx) { /* A list of files, used internally for pctx->files. */ static cfg_type_t cfg_type_filelist = { - "filelist", NULL, print_list, &cfg_rep_list, + "filelist", NULL, print_list, NULL, &cfg_rep_list, &cfg_type_qstring }; isc_result_t -cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) -{ +cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) { isc_result_t result; cfg_parser_t *pctx; isc_lexspecials_t specials; @@ -1422,8 +408,8 @@ cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) ISC_LEXCOMMENT_CPLUSPLUS | ISC_LEXCOMMENT_SHELL)); - CHECK(create_list(pctx, &cfg_type_filelist, &pctx->open_files)); - CHECK(create_list(pctx, &cfg_type_filelist, &pctx->closed_files)); + CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files)); + CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files)); *ret = pctx; return (ISC_R_SUCCESS); @@ -1445,7 +431,7 @@ parser_openfile(cfg_parser_t *pctx, const char *filename) { result = isc_lex_openfile(pctx->lexer, filename); if (result != ISC_R_SUCCESS) { - parser_error(pctx, 0, "open: %s: %s", + cfg_parser_error(pctx, 0, "open: %s: %s", filename, isc_result_totext(result)); goto cleanup; } @@ -1479,7 +465,7 @@ parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; - result = parse(pctx, type, &obj); + result = cfg_parse_obj(pctx, type, &obj); if (pctx->errors != 0) { /* Errors have been logged. */ @@ -1490,7 +476,7 @@ parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { if (result != ISC_R_SUCCESS) { /* Parsing failed but no errors have been logged. */ - parser_error(pctx, 0, "parsing failed"); + cfg_parser_error(pctx, 0, "parsing failed"); goto cleanup; } @@ -1549,44 +535,51 @@ cfg_parser_destroy(cfg_parser_t **pctxp) { /* * void */ -static isc_result_t -parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { +isc_result_t +cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { UNUSED(type); - return (create_cfgobj(pctx, &cfg_type_void, ret)); + return (cfg_create_obj(pctx, &cfg_type_void, ret)); } -static void -print_void(cfg_printer_t *pctx, cfg_obj_t *obj) { +void +cfg_print_void(cfg_printer_t *pctx, cfg_obj_t *obj) { UNUSED(pctx); UNUSED(obj); } +void +cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) { + UNUSED(pctx); + UNUSED(type); +} + isc_boolean_t cfg_obj_isvoid(cfg_obj_t *obj) { REQUIRE(obj != NULL); return (ISC_TF(obj->type->rep == &cfg_rep_void)); } -static cfg_type_t cfg_type_void = { - "void", parse_void, print_void, &cfg_rep_void, NULL }; +cfg_type_t cfg_type_void = { + "void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void, + NULL }; /* * uint32 */ -static isc_result_t -parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { +isc_result_t +cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; UNUSED(type); CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER)); if (pctx->token.type != isc_tokentype_number) { - parser_error(pctx, LOG_NEAR, "expected number"); + cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number"); return (ISC_R_UNEXPECTEDTOKEN); } - CHECK(create_cfgobj(pctx, &cfg_type_uint32, &obj)); + CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj)); obj->value.uint32 = pctx->token.value.as_ulong; *ret = obj; @@ -1594,21 +587,21 @@ parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (result); } -static void -print_cstr(cfg_printer_t *pctx, const char *s) { - print(pctx, s, strlen(s)); +void +cfg_print_cstr(cfg_printer_t *pctx, const char *s) { + cfg_print_chars(pctx, s, strlen(s)); } -static void -print_uint(cfg_printer_t *pctx, unsigned int u) { +void +cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) { char buf[32]; snprintf(buf, sizeof(buf), "%u", u); - print_cstr(pctx, buf); + cfg_print_cstr(pctx, buf); } -static void -print_uint32(cfg_printer_t *pctx, cfg_obj_t *obj) { - print_uint(pctx, obj->value.uint32); +void +cfg_print_uint32(cfg_printer_t *pctx, cfg_obj_t *obj) { + cfg_print_rawuint(pctx, obj->value.uint32); } isc_boolean_t @@ -1623,8 +616,10 @@ cfg_obj_asuint32(cfg_obj_t *obj) { return (obj->value.uint32); } -static cfg_type_t cfg_type_uint32 = { - "integer", parse_uint32, print_uint32, &cfg_rep_uint32, NULL }; +cfg_type_t cfg_type_uint32 = { + "integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal, + &cfg_rep_uint32, NULL +}; /* @@ -1642,157 +637,21 @@ cfg_obj_asuint64(cfg_obj_t *obj) { return (obj->value.uint64); } -static isc_result_t -parse_unitstring(char *str, isc_resourcevalue_t *valuep) { - char *endp; - unsigned int len; - isc_uint64_t value; - isc_uint64_t unit; - - value = isc_string_touint64(str, &endp, 10); - if (*endp == 0) { - *valuep = value; - return (ISC_R_SUCCESS); - } - - len = strlen(str); - if (len < 2 || endp[1] != '\0') - return (ISC_R_FAILURE); - - switch (str[len - 1]) { - case 'k': - case 'K': - unit = 1024; - break; - case 'm': - case 'M': - unit = 1024 * 1024; - break; - case 'g': - case 'G': - unit = 1024 * 1024 * 1024; - break; - default: - return (ISC_R_FAILURE); - } - if (value > ISC_UINT64_MAX / unit) - return (ISC_R_FAILURE); - *valuep = value * unit; - return (ISC_R_SUCCESS); -} - -static void -print_uint64(cfg_printer_t *pctx, cfg_obj_t *obj) { +void +cfg_print_uint64(cfg_printer_t *pctx, cfg_obj_t *obj) { char buf[32]; sprintf(buf, "%" ISC_PRINT_QUADFORMAT "u", obj->value.uint64); - print_cstr(pctx, buf); -} - -static cfg_type_t cfg_type_uint64 = { - "64_bit_integer", NULL, print_uint64, &cfg_rep_uint64, NULL }; - -static isc_result_t -parse_sizeval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { - isc_result_t result; - cfg_obj_t *obj = NULL; - isc_uint64_t val; - - UNUSED(type); - - CHECK(cfg_gettoken(pctx, 0)); - CHECK(parse_unitstring(pctx->token.value.as_pointer, &val)); - - CHECK(create_cfgobj(pctx, &cfg_type_uint64, &obj)); - obj->value.uint64 = val; - *ret = obj; - return (ISC_R_SUCCESS); - - cleanup: - parser_error(pctx, LOG_NEAR, "expected integer and optional unit"); - return (result); + cfg_print_cstr(pctx, buf); } -/* - * A size value (number + optional unit). - */ -static cfg_type_t cfg_type_sizeval = { - "sizeval", parse_sizeval, print_uint64, &cfg_rep_uint64, NULL }; - -/* - * A size, "unlimited", or "default". - */ - -static isc_result_t -parse_size(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { - return (parse_enum_or_other(pctx, type, &cfg_type_sizeval, ret)); -} - -static const char *size_enums[] = { "unlimited", "default", NULL }; -static cfg_type_t cfg_type_size = { - "size", parse_size, print_ustring, &cfg_rep_string, size_enums +cfg_type_t cfg_type_uint64 = { + "64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal, + &cfg_rep_uint64, NULL }; /* - * A size or "unlimited", but not "default". - */ -static const char *sizenodefault_enums[] = { "unlimited", NULL }; -static cfg_type_t cfg_type_sizenodefault = { - "size_no_default", parse_size, print_ustring, &cfg_rep_string, - sizenodefault_enums -}; - -/* - * optional_keyvalue - */ -static isc_result_t -parse_maybe_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, - isc_boolean_t optional, cfg_obj_t **ret) -{ - isc_result_t result; - cfg_obj_t *obj = NULL; - const keyword_type_t *kw = type->of; - - CHECK(cfg_peektoken(pctx, 0)); - if (pctx->token.type == isc_tokentype_string && - strcasecmp(pctx->token.value.as_pointer, kw->name) == 0) { - CHECK(cfg_gettoken(pctx, 0)); - CHECK(kw->type->parse(pctx, kw->type, &obj)); - obj->type = type; /* XXX kludge */ - } else { - if (optional) { - CHECK(parse_void(pctx, NULL, &obj)); - } else { - parser_error(pctx, LOG_NEAR, "expected '%s'", - kw->name); - result = ISC_R_UNEXPECTEDTOKEN; - goto cleanup; - } - } - *ret = obj; - cleanup: - return (result); -} - -static isc_result_t -parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { - return (parse_maybe_optional_keyvalue(pctx, type, ISC_FALSE, ret)); -} - -static isc_result_t -parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { - return (parse_maybe_optional_keyvalue(pctx, type, ISC_TRUE, ret)); -} - -static void -print_keyvalue(cfg_printer_t *pctx, cfg_obj_t *obj) { - const keyword_type_t *kw = obj->type->of; - print_cstr(pctx, kw->name); - print(pctx, " ", 1); - kw->type->print(pctx, obj); -} - -/* - * qstring, ustring, astring + * qstring (quoted string), ustring (unquoted string), astring + * (any string) */ /* Create a string object from a null-terminated C string. */ @@ -1804,7 +663,7 @@ create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type, cfg_obj_t *obj = NULL; int len; - CHECK(create_cfgobj(pctx, type, &obj)); + CHECK(cfg_create_obj(pctx, type, &obj)); len = strlen(contents); obj->value.string.length = len; obj->value.string.base = isc_mem_get(pctx->mctx, len + 1); @@ -1820,14 +679,14 @@ create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type, return (result); } -static isc_result_t -parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { +isc_result_t +cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; UNUSED(type); - CHECK(cfg_gettoken(pctx, QSTRING)); + CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); if (pctx->token.type != isc_tokentype_qstring) { - parser_error(pctx, LOG_NEAR, "expected quoted string"); + cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string"); return (ISC_R_UNEXPECTEDTOKEN); } return (create_string(pctx, @@ -1845,7 +704,7 @@ parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { CHECK(cfg_gettoken(pctx, 0)); if (pctx->token.type != isc_tokentype_string) { - parser_error(pctx, LOG_NEAR, "expected unquoted string"); + cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string"); return (ISC_R_UNEXPECTEDTOKEN); } return (create_string(pctx, @@ -1856,8 +715,8 @@ parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (result); } -static isc_result_t -parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { +isc_result_t +cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; UNUSED(type); @@ -1870,8 +729,8 @@ parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (result); } -static isc_boolean_t -is_enum(const char *s, const char *const *enums) { +isc_boolean_t +cfg_is_enum(const char *s, const char *const *enums) { const char * const *p; for (p = enums; *p != NULL; p++) { if (strcasecmp(*p, s) == 0) @@ -1883,14 +742,14 @@ is_enum(const char *s, const char *const *enums) { static isc_result_t check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) { const char *s = obj->value.string.base; - if (is_enum(s, enums)) + if (cfg_is_enum(s, enums)) return (ISC_R_SUCCESS); - parser_error(pctx, 0, "'%s' unexpected", s); + cfg_parser_error(pctx, 0, "'%s' unexpected", s); return (ISC_R_UNEXPECTEDTOKEN); } -static isc_result_t -parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { +isc_result_t +cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; CHECK(parse_ustring(pctx, NULL, &obj)); @@ -1902,36 +761,28 @@ parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (result); } -static isc_result_t -parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype, - const cfg_type_t *othertype, cfg_obj_t **ret) -{ - isc_result_t result; - CHECK(cfg_peektoken(pctx, 0)); - if (pctx->token.type == isc_tokentype_string && - is_enum(pctx->token.value.as_pointer, enumtype->of)) { - CHECK(parse_enum(pctx, enumtype, ret)); - } else { - CHECK(parse(pctx, othertype, ret)); +void +cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) { + const char * const *p; + cfg_print_chars(pctx, "( ", 2); + for (p = type->of; *p != NULL; p++) { + cfg_print_cstr(pctx, *p); + if (p[1] != NULL) + cfg_print_chars(pctx, " | ", 3); } - cleanup: - return (result); + cfg_print_chars(pctx, " )", 2); } - -/* - * Print a string object. - */ -static void -print_ustring(cfg_printer_t *pctx, cfg_obj_t *obj) { - print(pctx, obj->value.string.base, obj->value.string.length); +void +cfg_print_ustring(cfg_printer_t *pctx, cfg_obj_t *obj) { + cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length); } static void print_qstring(cfg_printer_t *pctx, cfg_obj_t *obj) { - print(pctx, "\"", 1); - print_ustring(pctx, obj); - print(pctx, "\"", 1); + cfg_print_chars(pctx, "\"", 1); + cfg_print_ustring(pctx, obj); + cfg_print_chars(pctx, "\"", 1); } static void @@ -1952,6 +803,28 @@ cfg_obj_asstring(cfg_obj_t *obj) { return (obj->value.string.base); } +/* Quoted string only */ +cfg_type_t cfg_type_qstring = { + "quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal, + &cfg_rep_string, NULL +}; + +/* Unquoted string only */ +cfg_type_t cfg_type_ustring = { + "string", parse_ustring, cfg_print_ustring, cfg_doc_terminal, + &cfg_rep_string, NULL +}; + +/* Any string (quoted or unquoted); printed with quotes */ +cfg_type_t cfg_type_astring = { + "string", cfg_parse_astring, print_qstring, cfg_doc_terminal, + &cfg_rep_string, NULL +}; + +/* + * Booleans + */ + isc_boolean_t cfg_obj_isboolean(cfg_obj_t *obj) { REQUIRE(obj != NULL); @@ -1964,22 +837,6 @@ cfg_obj_asboolean(cfg_obj_t *obj) { return (obj->value.boolean); } -/* Quoted string only */ -static cfg_type_t cfg_type_qstring = { - "quoted_string", parse_qstring, print_qstring, &cfg_rep_string, NULL }; - -/* Unquoted string only */ -static cfg_type_t cfg_type_ustring = { - "string", parse_ustring, print_ustring, &cfg_rep_string, NULL }; - -/* Any string (quoted or unquoted); printed with quotes */ -static cfg_type_t cfg_type_astring = { - "string", parse_astring, print_qstring, &cfg_rep_string, NULL }; - - -/* - * boolean - */ static isc_result_t parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { @@ -2007,13 +864,13 @@ parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) goto bad_boolean; } - CHECK(create_cfgobj(pctx, &cfg_type_boolean, &obj)); + CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj)); obj->value.boolean = value; *ret = obj; return (result); bad_boolean: - parser_error(pctx, LOG_NEAR, "boolean expected"); + cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected"); return (ISC_R_UNEXPECTEDTOKEN); cleanup: @@ -2023,56 +880,24 @@ parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) static void print_boolean(cfg_printer_t *pctx, cfg_obj_t *obj) { if (obj->value.boolean) - print(pctx, "yes", 3); + cfg_print_chars(pctx, "yes", 3); else - print(pctx, "no", 2); -} - -static cfg_type_t cfg_type_boolean = { - "boolean", parse_boolean, print_boolean, &cfg_rep_boolean, NULL }; - -static const char *dialup_enums[] = { - "notify", "notify-passive", "refresh", "passive", NULL }; -static isc_result_t -parse_dialup_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { - return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); -} -static cfg_type_t cfg_type_dialuptype = { - "dialuptype", parse_dialup_type, print_ustring, - &cfg_rep_string, dialup_enums -}; - -static const char *notify_enums[] = { "explicit", NULL }; -static isc_result_t -parse_notify_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { - return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); + cfg_print_chars(pctx, "no", 2); } -static cfg_type_t cfg_type_notifytype = { - "notifytype", parse_notify_type, print_ustring, - &cfg_rep_string, notify_enums, -}; - -static keyword_type_t key_kw = { "key", &cfg_type_astring }; -LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_keyref = { - "keyref", parse_keyvalue, print_keyvalue, - &cfg_rep_string, &key_kw +cfg_type_t cfg_type_boolean = { + "boolean", parse_boolean, print_boolean, cfg_doc_terminal, + &cfg_rep_boolean, NULL }; -static cfg_type_t cfg_type_optional_keyref = { - "optional_keyref", parse_optional_keyvalue, print_keyvalue, - &cfg_rep_string, &key_kw -}; - - /* * Lists. */ -static isc_result_t -create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) { +isc_result_t +cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) { isc_result_t result; - CHECK(create_cfgobj(pctx, type, obj)); + CHECK(cfg_create_obj(pctx, type, obj)); ISC_LIST_INIT((*obj)->value.list); cleanup: return (result); @@ -2108,9 +933,9 @@ free_list(cfg_parser_t *pctx, cfg_obj_t *obj) { } } -static isc_result_t -parse_list_elt(cfg_parser_t *pctx, const cfg_type_t *elttype, - cfg_listelt_t **ret) +isc_result_t +cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype, + cfg_listelt_t **ret) { isc_result_t result; cfg_listelt_t *elt = NULL; @@ -2118,7 +943,7 @@ parse_list_elt(cfg_parser_t *pctx, const cfg_type_t *elttype, CHECK(create_listelt(pctx, &elt)); - result = parse(pctx, elttype, &value); + result = cfg_parse_obj(pctx, elttype, &value); if (result != ISC_R_SUCCESS) goto cleanup; @@ -2143,7 +968,7 @@ parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret) const cfg_type_t *listof = listtype->of; isc_result_t result; - CHECK(create_list(pctx, listtype, &listobj)); + CHECK(cfg_create_list(pctx, listtype, &listobj)); for (;;) { cfg_listelt_t *elt = NULL; @@ -2152,7 +977,7 @@ parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret) if (pctx->token.type == isc_tokentype_special && pctx->token.value.as_char == '}') break; - CHECK(parse_list_elt(pctx, listof, &elt)); + CHECK(cfg_parse_listelt(pctx, listof, &elt)); CHECK(parse_semicolon(pctx)); ISC_LIST_APPEND(listobj->value.list, elt, link); } @@ -2173,42 +998,51 @@ print_list(cfg_printer_t *pctx, cfg_obj_t *obj) { elt != NULL; elt = ISC_LIST_NEXT(elt, link)) { print_indent(pctx); - print_obj(pctx, elt->obj); - print(pctx, ";\n", 2); + cfg_print_obj(pctx, elt->obj); + cfg_print_chars(pctx, ";\n", 2); } } -static isc_result_t -parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) +isc_result_t +cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type, + cfg_obj_t **ret) { isc_result_t result; - CHECK(parse_special(pctx, '{')); + CHECK(cfg_parse_special(pctx, '{')); CHECK(parse_list(pctx, type, ret)); - CHECK(parse_special(pctx, '}')); + CHECK(cfg_parse_special(pctx, '}')); cleanup: return (result); } -static void -print_bracketed_list(cfg_printer_t *pctx, cfg_obj_t *obj) { +void +cfg_print_bracketed_list(cfg_printer_t *pctx, cfg_obj_t *obj) { print_open(pctx); print_list(pctx, obj); print_close(pctx); } +void +cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) { + cfg_print_chars(pctx, "{ ", 2); + cfg_doc_obj(pctx, type->of); + cfg_print_chars(pctx, "; ... }", 7); +} + /* * Parse a homogeneous list whose elements are of type 'elttype' * and where elements are separated by space. The list ends * before the first semicolon. */ -static isc_result_t -parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret) +isc_result_t +cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype, + cfg_obj_t **ret) { cfg_obj_t *listobj = NULL; const cfg_type_t *listof = listtype->of; isc_result_t result; - CHECK(create_list(pctx, listtype, &listobj)); + CHECK(cfg_create_list(pctx, listtype, &listobj)); for (;;) { cfg_listelt_t *elt = NULL; @@ -2217,7 +1051,7 @@ parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret) if (pctx->token.type == isc_tokentype_special && pctx->token.value.as_char == ';') break; - CHECK(parse_list_elt(pctx, listof, &elt)); + CHECK(cfg_parse_listelt(pctx, listof, &elt)); ISC_LIST_APPEND(listobj->value.list, elt, link); } *ret = listobj; @@ -2228,20 +1062,21 @@ parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret) return (result); } -static void -print_spacelist(cfg_printer_t *pctx, cfg_obj_t *obj) { +void +cfg_print_spacelist(cfg_printer_t *pctx, cfg_obj_t *obj) { cfg_list_t *list = &obj->value.list; cfg_listelt_t *elt; for (elt = ISC_LIST_HEAD(*list); elt != NULL; elt = ISC_LIST_NEXT(elt, link)) { - print_obj(pctx, elt->obj); + cfg_print_obj(pctx, elt->obj); if (ISC_LIST_NEXT(elt, link) != NULL) - print(pctx, " ", 1); + cfg_print_chars(pctx, " ", 1); } } + isc_boolean_t cfg_obj_islist(cfg_obj_t *obj) { REQUIRE(obj != NULL); @@ -2282,8 +1117,8 @@ cfg_listelt_value(cfg_listelt_t *elt) { * the named.conf syntax, as well as for the body of the * options, view, zone, and other statements. */ -static isc_result_t -parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) +isc_result_t +cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { const cfg_clausedef_t * const *clausesets = type->of; isc_result_t result; @@ -2324,7 +1159,7 @@ parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) * object just so that it is not overwritten by the * semicolon token. */ - CHECK(parse(pctx, &cfg_type_qstring, &includename)); + CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename)); CHECK(parse_semicolon(pctx)); CHECK(parser_openfile(pctx, includename-> value.string.base)); @@ -2344,28 +1179,28 @@ parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) } done: if (clause == NULL || clause->name == NULL) { - parser_error(pctx, LOG_NOPREP, "unknown option"); + cfg_parser_error(pctx, CFG_LOG_NOPREP, "unknown option"); /* * Try to recover by parsing this option as an unknown * option and discarding it. */ - CHECK(parse(pctx, &cfg_type_unsupported, &eltobj)); - cfg_obj_destroy(pctx, &eltobj); - CHECK(parse_semicolon(pctx)); - continue; + CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, &eltobj)); + cfg_obj_destroy(pctx, &eltobj); + CHECK(parse_semicolon(pctx)); + continue; } /* Clause is known. */ /* Issue warnings if appropriate */ if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0) - parser_warning(pctx, 0, "option '%s' is obsolete", + cfg_parser_warning(pctx, 0, "option '%s' is obsolete", clause->name); if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0) - parser_warning(pctx, 0, "option '%s' is " + cfg_parser_warning(pctx, 0, "option '%s' is " "not implemented", clause->name); if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0) - parser_warning(pctx, 0, "option '%s' is " + cfg_parser_warning(pctx, 0, "option '%s' is " "not implemented", clause->name); /* * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT @@ -2381,7 +1216,7 @@ parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) /* Multivalued clause */ cfg_obj_t *listobj = NULL; if (result == ISC_R_NOTFOUND) { - CHECK(create_list(pctx, + CHECK(cfg_create_list(pctx, &cfg_type_implicitlist, &listobj)); symval.as_pointer = listobj; @@ -2391,7 +1226,7 @@ parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) 1, symval, isc_symexists_reject); if (result != ISC_R_SUCCESS) { - parser_error(pctx, LOG_NEAR, + cfg_parser_error(pctx, CFG_LOG_NEAR, "isc_symtab_define(%s) " "failed", clause->name); isc_mem_put(pctx->mctx, list, @@ -2404,7 +1239,7 @@ parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) } elt = NULL; - CHECK(parse_list_elt(pctx, clause->type, &elt)); + CHECK(cfg_parse_listelt(pctx, clause->type, &elt)); CHECK(parse_semicolon(pctx)); ISC_LIST_APPEND(listobj->value.list, elt, link); @@ -2420,12 +1255,12 @@ parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) callback)); CHECK(parse_semicolon(pctx)); } else if (result == ISC_R_SUCCESS) { - parser_error(pctx, LOG_NEAR, "'%s' redefined", + cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined", clause->name); result = ISC_R_EXISTS; goto cleanup; } else { - parser_error(pctx, LOG_NEAR, + cfg_parser_error(pctx, CFG_LOG_NEAR, "isc_symtab_define() failed"); goto cleanup; } @@ -2453,7 +1288,7 @@ parse_symtab_elt(cfg_parser_t *pctx, const char *name, cfg_obj_t *obj = NULL; isc_symvalue_t symval; - CHECK(parse(pctx, elttype, &obj)); + CHECK(cfg_parse_obj(pctx, elttype, &obj)); if (callback && pctx->callback != NULL) CHECK(pctx->callback(name, obj, pctx->callbackarg)); @@ -2473,19 +1308,18 @@ parse_symtab_elt(cfg_parser_t *pctx, const char *name, /* * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }" */ -static isc_result_t -parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) -{ +isc_result_t +cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; - CHECK(parse_special(pctx, '{')); - CHECK(parse_mapbody(pctx, type, ret)); - CHECK(parse_special(pctx, '}')); + CHECK(cfg_parse_special(pctx, '{')); + CHECK(cfg_parse_mapbody(pctx, type, ret)); + CHECK(cfg_parse_special(pctx, '}')); cleanup: return (result); } /* - * Subroutine for parse_named_map() and parse_addressed_map(). + * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map(). */ static isc_result_t parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type, @@ -2495,8 +1329,8 @@ parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t * cfg_obj_t *idobj = NULL; cfg_obj_t *mapobj = NULL; - CHECK(parse(pctx, nametype, &idobj)); - CHECK(parse_map(pctx, type, &mapobj)); + CHECK(cfg_parse_obj(pctx, nametype, &idobj)); + CHECK(cfg_parse_map(pctx, type, &mapobj)); mapobj->value.map.id = idobj; idobj = NULL; *ret = mapobj; @@ -2509,8 +1343,8 @@ parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t * * Parse a map identified by a string name. E.g., "name { foo 1; }". * Used for the "key" and "channel" statements. */ -static isc_result_t -parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { +isc_result_t +cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_any_named_map(pctx, &cfg_type_astring, type, ret)); } @@ -2518,13 +1352,13 @@ parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { * Parse a map identified by a network address. * Used for the "server" statement. */ -static isc_result_t -parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { +isc_result_t +cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret)); } -static void -print_mapbody(cfg_printer_t *pctx, cfg_obj_t *obj) { +void +cfg_print_mapbody(cfg_printer_t *pctx, cfg_obj_t *obj) { isc_result_t result = ISC_R_SUCCESS; const cfg_clausedef_t * const *clauseset; @@ -2551,18 +1385,18 @@ print_mapbody(cfg_printer_t *pctx, cfg_obj_t *obj) { elt != NULL; elt = ISC_LIST_NEXT(elt, link)) { print_indent(pctx); - print_cstr(pctx, clause->name); - print(pctx, " ", 1); - print_obj(pctx, elt->obj); - print(pctx, ";\n", 2); + cfg_print_cstr(pctx, clause->name); + cfg_print_chars(pctx, " ", 1); + cfg_print_obj(pctx, elt->obj); + cfg_print_chars(pctx, ";\n", 2); } } else { /* Single-valued. */ print_indent(pctx); - print_cstr(pctx, clause->name); - print(pctx, " ", 1); - print_obj(pctx, obj); - print(pctx, ";\n", 2); + cfg_print_cstr(pctx, clause->name); + cfg_print_chars(pctx, " ", 1); + cfg_print_obj(pctx, obj); + cfg_print_chars(pctx, ";\n", 2); } } else if (result == ISC_R_NOTFOUND) { ; /* do nothing */ @@ -2573,14 +1407,89 @@ print_mapbody(cfg_printer_t *pctx, cfg_obj_t *obj) { } } -static void -print_map(cfg_printer_t *pctx, cfg_obj_t *obj) { +void +cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) { + const cfg_clausedef_t * const *clauseset; + const cfg_clausedef_t *clause; + + for (clauseset = type->of; *clauseset != NULL; clauseset++) { + for (clause = *clauseset; + clause->name != NULL; + clause++) { + cfg_print_cstr(pctx, clause->name); + cfg_print_chars(pctx, " ", 1); + cfg_doc_obj(pctx, clause->type); + cfg_print_chars(pctx, ";", 1); + /* XXX print flags here? */ + cfg_print_chars(pctx, "\n\n", 2); + } + } +} + +static struct flagtext { + unsigned int flag; + const char *text; +} flagtexts[] = { + { CFG_CLAUSEFLAG_NOTIMP, "not implemented" }, + { CFG_CLAUSEFLAG_NYI, "not yet implemented" }, + { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" }, + { CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" }, + { 0, NULL } +}; + +void +cfg_print_map(cfg_printer_t *pctx, cfg_obj_t *obj) { if (obj->value.map.id != NULL) { - print_obj(pctx, obj->value.map.id); - print(pctx, " ", 1); + cfg_print_obj(pctx, obj->value.map.id); + cfg_print_chars(pctx, " ", 1); + } + print_open(pctx); + cfg_print_mapbody(pctx, obj); + print_close(pctx); +} + +static void +print_clause_flags(cfg_printer_t *pctx, unsigned int flags) { + struct flagtext *p; + isc_boolean_t first = ISC_TRUE; + for (p = flagtexts; p->flag != 0; p++) { + if ((flags & p->flag) != 0) { + if (first) + cfg_print_chars(pctx, " // ", 4); + else + cfg_print_chars(pctx, ", ", 2); + cfg_print_cstr(pctx, p->text); + first = ISC_FALSE; + } + } +} + +void +cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) { + const cfg_clausedef_t * const *clauseset; + const cfg_clausedef_t *clause; + + if (type->parse == cfg_parse_named_map) { + cfg_doc_obj(pctx, &cfg_type_astring); + cfg_print_chars(pctx, " ", 1); } + print_open(pctx); - print_mapbody(pctx, obj); + + for (clauseset = type->of; *clauseset != NULL; clauseset++) { + for (clause = *clauseset; + clause->name != NULL; + clause++) { + print_indent(pctx); + cfg_print_cstr(pctx, clause->name); + if (clause->type->print != cfg_print_void) + cfg_print_chars(pctx, " ", 1); + cfg_doc_obj(pctx, clause->type); + cfg_print_chars(pctx, ";", 1); + print_clause_flags(pctx, clause->flags); + cfg_print_chars(pctx, "\n", 1); + } + } print_close(pctx); } @@ -2625,8 +1534,8 @@ parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { UNUSED(type); - CHECK(create_cfgobj(pctx, &cfg_type_token, &obj)); - CHECK(cfg_gettoken(pctx, QSTRING)); + CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj)); + CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); if (pctx->token.type == isc_tokentype_eof) { cfg_ungettoken(pctx); result = ISC_R_EOF; @@ -2645,8 +1554,10 @@ parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (result); } -static cfg_type_t cfg_type_token = { - "token", parse_token, print_ustring, &cfg_rep_string, NULL }; +cfg_type_t cfg_type_token = { + "token", parse_token, cfg_print_ustring, cfg_doc_terminal, + &cfg_rep_string, NULL +}; /* * An unsupported option. This is just a list of tokens with balanced braces @@ -2659,7 +1570,7 @@ parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; int braces = 0; - CHECK(create_list(pctx, type, &listobj)); + CHECK(cfg_create_list(pctx, type, &listobj)); for (;;) { cfg_listelt_t *elt = NULL; @@ -2675,12 +1586,12 @@ parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { break; } if (pctx->token.type == isc_tokentype_eof || braces < 0) { - parser_error(pctx, LOG_NEAR, "unexpected token"); + cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token"); result = ISC_R_UNEXPECTEDTOKEN; goto cleanup; } - CHECK(parse_list_elt(pctx, &cfg_type_token, &elt)); + CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt)); ISC_LIST_APPEND(listobj->value.list, elt, link); } INSIST(braces == 0); @@ -2692,85 +1603,18 @@ parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (result); } -static cfg_type_t cfg_type_unsupported = { - "unsupported", parse_unsupported, print_spacelist, +cfg_type_t cfg_type_unsupported = { + "unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal, &cfg_rep_list, NULL }; -/* - * A "controls" statement is represented as a map with the multivalued - * "inet" and "unix" clauses. Inet controls are tuples; unix controls - * are cfg_unsupported_t objects. - */ - -static keyword_type_t controls_allow_kw = { - "allow", &cfg_type_bracketed_aml }; -static cfg_type_t cfg_type_controls_allow = { - "controls_allow", parse_keyvalue, - print_keyvalue, &cfg_rep_list, &controls_allow_kw -}; - -static keyword_type_t controls_keys_kw = { - "keys", &cfg_type_keylist }; -static cfg_type_t cfg_type_controls_keys = { - "controls_keys", parse_optional_keyvalue, - print_keyvalue, &cfg_rep_list, &controls_keys_kw -}; - -static cfg_tuplefielddef_t inetcontrol_fields[] = { - { "address", &cfg_type_controls_sockaddr, 0 }, - { "allow", &cfg_type_controls_allow, 0 }, - { "keys", &cfg_type_controls_keys, 0 }, - { NULL, NULL, 0 } -}; -static cfg_type_t cfg_type_inetcontrol = { - "inetcontrol", parse_tuple, print_tuple, &cfg_rep_tuple, - inetcontrol_fields -}; - -static cfg_clausedef_t -controls_clauses[] = { - { "inet", &cfg_type_inetcontrol, CFG_CLAUSEFLAG_MULTI }, - { "unix", &cfg_type_unsupported, - CFG_CLAUSEFLAG_MULTI|CFG_CLAUSEFLAG_NOTIMP }, - { NULL, NULL, 0 } -}; -static cfg_clausedef_t * -controls_clausesets[] = { - controls_clauses, - NULL -}; -static cfg_type_t cfg_type_controls = { - "controls", parse_map, print_map, &cfg_rep_map, &controls_clausesets -}; - -/* - * An optional class, as used in view and zone statements. - */ -static isc_result_t -parse_optional_class(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { - isc_result_t result; - UNUSED(type); - CHECK(cfg_peektoken(pctx, 0)); - if (pctx->token.type == isc_tokentype_string) - CHECK(parse(pctx, &cfg_type_ustring, ret)); - else - CHECK(parse(pctx, &cfg_type_void, ret)); - cleanup: - return (result); -} - -static cfg_type_t cfg_type_optional_class = { - "optional_class", parse_optional_class, NULL, NULL, NULL }; - - /* * Try interpreting the current token as a network address. * - * If WILDOK is set in flags, "*" can be used as a wildcard - * and at least one of V4OK and V6OK must also be set. The - * "*" is interpreted as the IPv4 wildcard address if V4OK is - * set (including the case where V4OK and V6OK are both set), + * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard + * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set. The + * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is + * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set), * and the IPv6 wildcard address otherwise. */ static isc_result_t @@ -2783,24 +1627,24 @@ token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) { return (ISC_R_UNEXPECTEDTOKEN); s = pctx->token.value.as_pointer; - if ((flags & WILDOK) != 0 && strcmp(s, "*") == 0) { - if ((flags & V4OK) != 0) { + if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) { + if ((flags & CFG_ADDR_V4OK) != 0) { isc_netaddr_any(na); return (ISC_R_SUCCESS); - } else if ((flags & V6OK) != 0) { + } else if ((flags & CFG_ADDR_V6OK) != 0) { isc_netaddr_any6(na); return (ISC_R_SUCCESS); } else { INSIST(0); } } else { - if ((flags & (V4OK | V4PREFIXOK)) != 0) { + if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) { if (inet_pton(AF_INET, s, &in4a) == 1) { isc_netaddr_fromin(na, &in4a); return (ISC_R_SUCCESS); } } - if ((flags & V4PREFIXOK) != 0 && + if ((flags & CFG_ADDR_V4PREFIXOK) != 0 && strlen(s) <= 15) { char buf[64]; int i; @@ -2814,7 +1658,7 @@ token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) { } } } - if (flags & V6OK) { + if (flags & CFG_ADDR_V6OK) { if (inet_pton(AF_INET6, s, &in6a) == 1) { isc_netaddr_fromin6(na, &in6a); return (ISC_R_SUCCESS); @@ -2824,44 +1668,44 @@ token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) { return (ISC_R_UNEXPECTEDTOKEN); } -static isc_result_t -get_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) { +isc_result_t +cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) { isc_result_t result; CHECK(cfg_gettoken(pctx, 0)); result = token_addr(pctx, flags, na); if (result == ISC_R_UNEXPECTEDTOKEN) - parser_error(pctx, LOG_NEAR, "expected IP address"); + cfg_parser_error(pctx, CFG_LOG_NEAR, "expected IP address"); cleanup: return (result); } -static isc_boolean_t -looking_at_netaddr(cfg_parser_t *pctx, unsigned int flags) { +isc_boolean_t +cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) { isc_result_t result; isc_netaddr_t na_dummy; result = token_addr(pctx, flags, &na_dummy); return (ISC_TF(result == ISC_R_SUCCESS)); } -static isc_result_t -get_port(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) { +isc_result_t +cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) { isc_result_t result; CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER)); - if ((flags & WILDOK) != 0 && + if ((flags & CFG_ADDR_WILDOK) != 0 && pctx->token.type == isc_tokentype_string && strcmp(pctx->token.value.as_pointer, "*") == 0) { *port = 0; return (ISC_R_SUCCESS); } if (pctx->token.type != isc_tokentype_number) { - parser_error(pctx, LOG_NEAR, + cfg_parser_error(pctx, CFG_LOG_NEAR, "expected port number or '*'"); return (ISC_R_UNEXPECTEDTOKEN); } if (pctx->token.value.as_ulong >= 65536) { - parser_error(pctx, LOG_NEAR, + cfg_parser_error(pctx, CFG_LOG_NEAR, "port number out of range"); return (ISC_R_UNEXPECTEDTOKEN); } @@ -2871,80 +1715,8 @@ get_port(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) { return (result); } -static isc_result_t -parse_querysource(cfg_parser_t *pctx, int flags, cfg_obj_t **ret) { - isc_result_t result; - cfg_obj_t *obj = NULL; - isc_netaddr_t netaddr; - in_port_t port; - unsigned int have_address = 0; - unsigned int have_port = 0; - - if ((flags & V4OK) != 0) - isc_netaddr_any(&netaddr); - else if ((flags & V6OK) != 0) - isc_netaddr_any6(&netaddr); - else - INSIST(0); - - port = 0; - - CHECK(create_cfgobj(pctx, &cfg_type_querysource, &obj)); - for (;;) { - CHECK(cfg_peektoken(pctx, 0)); - if (pctx->token.type == isc_tokentype_string) { - if (strcasecmp(pctx->token.value.as_pointer, - "address") == 0) - { - /* read "address" */ - CHECK(cfg_gettoken(pctx, 0)); - CHECK(get_addr(pctx, flags|WILDOK, &netaddr)); - have_address++; - } else if (strcasecmp(pctx->token.value.as_pointer, - "port") == 0) - { - /* read "port" */ - CHECK(cfg_gettoken(pctx, 0)); - CHECK(get_port(pctx, WILDOK, &port)); - have_port++; - } else { - parser_error(pctx, LOG_NEAR, - "expected 'address' or 'port'"); - return (ISC_R_UNEXPECTEDTOKEN); - } - } else - break; - } - if (have_address > 1 || have_port > 1 || - have_address + have_port == 0) { - parser_error(pctx, 0, "expected one address and/or port"); - return (ISC_R_UNEXPECTEDTOKEN); - } - - isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port); - *ret = obj; - return (ISC_R_SUCCESS); - - cleanup: - parser_error(pctx, LOG_NEAR, "invalid query source"); - CLEANUP_OBJ(obj); - return (result); -} - -static isc_result_t -parse_querysource4(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { - UNUSED(type); - return (parse_querysource(pctx, V4OK, ret)); -} - -static isc_result_t -parse_querysource6(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { - UNUSED(type); - return (parse_querysource(pctx, V6OK, ret)); -} - -static void -print_isc_netaddr(cfg_printer_t *pctx, isc_netaddr_t *na) { +void +cfg_print_rawaddr(cfg_printer_t *pctx, isc_netaddr_t *na) { isc_result_t result; char text[128]; isc_buffer_t buf; @@ -2952,26 +1724,9 @@ print_isc_netaddr(cfg_printer_t *pctx, isc_netaddr_t *na) { isc_buffer_init(&buf, text, sizeof(text)); result = isc_netaddr_totext(na, &buf); RUNTIME_CHECK(result == ISC_R_SUCCESS); - print(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf)); + cfg_print_chars(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf)); } -static void -print_querysource(cfg_printer_t *pctx, cfg_obj_t *obj) { - isc_netaddr_t na; - isc_netaddr_fromsockaddr(&na, &obj->value.sockaddr); - print(pctx, "address ", 8); - print_isc_netaddr(pctx, &na); - print(pctx, " port ", 6); - print_uint(pctx, isc_sockaddr_getport(&obj->value.sockaddr)); -} - -static cfg_type_t cfg_type_querysource4 = { - "querysource4", parse_querysource4, NULL, NULL, NULL }; -static cfg_type_t cfg_type_querysource6 = { - "querysource6", parse_querysource6, NULL, NULL, NULL }; -static cfg_type_t cfg_type_querysource = { - "querysource", NULL, print_querysource, &cfg_rep_sockaddr, NULL }; - /* netaddr */ static isc_result_t @@ -2980,8 +1735,8 @@ parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { cfg_obj_t *obj = NULL; isc_netaddr_t netaddr; UNUSED(type); - CHECK(create_cfgobj(pctx, type, &obj)); - CHECK(get_addr(pctx, V4OK|V6OK, &netaddr)); + CHECK(cfg_create_obj(pctx, type, &obj)); + CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK, &netaddr)); isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0); *ret = obj; return (ISC_R_SUCCESS); @@ -2990,20 +1745,22 @@ parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (result); } -static cfg_type_t cfg_type_netaddr = { - "netaddr", parse_netaddr, print_sockaddr, &cfg_rep_sockaddr, NULL }; +cfg_type_t cfg_type_netaddr = { + "netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_terminal, + &cfg_rep_sockaddr, NULL +}; /* netprefix */ -static isc_result_t -parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { +isc_result_t +cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { cfg_obj_t *obj = NULL; isc_result_t result; isc_netaddr_t netaddr; unsigned int addrlen, prefixlen; UNUSED(type); - CHECK(get_addr(pctx, V4OK|V4PREFIXOK|V6OK, &netaddr)); + CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK | CFG_ADDR_V6OK, &netaddr)); switch (netaddr.family) { case AF_INET: addrlen = 32; @@ -3022,124 +1779,54 @@ parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { CHECK(cfg_gettoken(pctx, 0)); /* read "/" */ CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER)); if (pctx->token.type != isc_tokentype_number) { - parser_error(pctx, LOG_NEAR, + cfg_parser_error(pctx, CFG_LOG_NEAR, "expected prefix length"); return (ISC_R_UNEXPECTEDTOKEN); } prefixlen = pctx->token.value.as_ulong; if (prefixlen > addrlen) { - parser_error(pctx, LOG_NOPREP, + cfg_parser_error(pctx, CFG_LOG_NOPREP, "invalid prefix length"); return (ISC_R_RANGE); } } else { - prefixlen = addrlen; - } - CHECK(create_cfgobj(pctx, &cfg_type_netprefix, &obj)); - obj->value.netprefix.address = netaddr; - obj->value.netprefix.prefixlen = prefixlen; - *ret = obj; - return (ISC_R_SUCCESS); - cleanup: - parser_error(pctx, LOG_NEAR, "expected network prefix"); - return (result); -} - -static void -print_netprefix(cfg_printer_t *pctx, cfg_obj_t *obj) { - cfg_netprefix_t *p = &obj->value.netprefix; - print_isc_netaddr(pctx, &p->address); - print(pctx, "/", 1); - print_uint(pctx, p->prefixlen); -} - -isc_boolean_t -cfg_obj_isnetprefix(cfg_obj_t *obj) { - REQUIRE(obj != NULL); - return (ISC_TF(obj->type->rep == &cfg_rep_netprefix)); -} - -void -cfg_obj_asnetprefix(cfg_obj_t *obj, isc_netaddr_t *netaddr, - unsigned int *prefixlen) { - REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix); - *netaddr = obj->value.netprefix.address; - *prefixlen = obj->value.netprefix.prefixlen; -} - -static cfg_type_t cfg_type_netprefix = { - "netprefix", parse_netprefix, print_netprefix, &cfg_rep_netprefix, NULL }; - -/* addrmatchelt */ - -static isc_result_t -parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { - isc_result_t result; - UNUSED(type); - - CHECK(cfg_peektoken(pctx, QSTRING)); - - if (pctx->token.type == isc_tokentype_string || - pctx->token.type == isc_tokentype_qstring) { - if (pctx->token.type == isc_tokentype_string && - (strcasecmp(pctx->token.value.as_pointer, "key") == 0)) { - CHECK(parse(pctx, &cfg_type_keyref, ret)); - } else { - if (looking_at_netaddr(pctx, V4OK|V4PREFIXOK|V6OK)) { - CHECK(parse_netprefix(pctx, NULL, ret)); - } else { - CHECK(parse_astring(pctx, NULL, ret)); - } - } - } else if (pctx->token.type == isc_tokentype_special) { - if (pctx->token.value.as_char == '{') { - /* Nested match list. */ - CHECK(parse(pctx, &cfg_type_bracketed_aml, ret)); - } else if (pctx->token.value.as_char == '!') { - CHECK(cfg_gettoken(pctx, 0)); /* read "!" */ - CHECK(parse(pctx, &cfg_type_negated, ret)); - } else { - goto bad; - } - } else { - bad: - parser_error(pctx, LOG_NEAR, - "expected IP match list element"); - return (ISC_R_UNEXPECTEDTOKEN); + prefixlen = addrlen; } + CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj)); + obj->value.netprefix.address = netaddr; + obj->value.netprefix.prefixlen = prefixlen; + *ret = obj; + return (ISC_R_SUCCESS); cleanup: + cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix"); return (result); } -/* - * A negated address match list element (like "! 10.0.0.1"). - * Somewhat sneakily, the caller is expected to parse the - * "!", but not to print it. - */ - -static cfg_tuplefielddef_t negated_fields[] = { - { "value", &cfg_type_addrmatchelt, 0 }, - { NULL, NULL, 0 } -}; - static void -print_negated(cfg_printer_t *pctx, cfg_obj_t *obj) { - print(pctx, "!", 1); - print_tuple(pctx, obj); +print_netprefix(cfg_printer_t *pctx, cfg_obj_t *obj) { + cfg_netprefix_t *p = &obj->value.netprefix; + cfg_print_rawaddr(pctx, &p->address); + cfg_print_chars(pctx, "/", 1); + cfg_print_rawuint(pctx, p->prefixlen); } -static cfg_type_t cfg_type_negated = { - "negated", parse_tuple, print_negated, &cfg_rep_tuple, - &negated_fields -}; +isc_boolean_t +cfg_obj_isnetprefix(cfg_obj_t *obj) { + REQUIRE(obj != NULL); + return (ISC_TF(obj->type->rep == &cfg_rep_netprefix)); +} -/* an address match list element */ +void +cfg_obj_asnetprefix(cfg_obj_t *obj, isc_netaddr_t *netaddr, + unsigned int *prefixlen) { + REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix); + *netaddr = obj->value.netprefix.address; + *prefixlen = obj->value.netprefix.prefixlen; +} -static cfg_type_t cfg_type_addrmatchelt = { - "address_match_element", parse_addrmatchelt, NULL, NULL, NULL }; -static cfg_type_t cfg_type_bracketed_aml = { - "bracketed_aml", parse_bracketed_list, print_bracketed_list, - &cfg_rep_list, &cfg_type_addrmatchelt +cfg_type_t cfg_type_netprefix = { + "netprefix", cfg_parse_netprefix, print_netprefix, cfg_doc_terminal, + &cfg_rep_netprefix, NULL }; static isc_result_t @@ -3151,13 +1838,13 @@ parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type, in_port_t port = 0; cfg_obj_t *obj = NULL; - CHECK(create_cfgobj(pctx, type, &obj)); - CHECK(get_addr(pctx, flags, &netaddr)); + CHECK(cfg_create_obj(pctx, type, &obj)); + CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr)); CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_string && strcasecmp(pctx->token.value.as_pointer, "port") == 0) { CHECK(cfg_gettoken(pctx, 0)); /* read "port" */ - CHECK(get_port(pctx, flags, &port)); + CHECK(cfg_parse_rawport(pctx, flags, &port)); } isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port); *ret = obj; @@ -3168,25 +1855,62 @@ parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type, return (result); } -static isc_result_t -parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { +static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK; +cfg_type_t cfg_type_sockaddr = { + "sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr, + &cfg_rep_sockaddr, &sockaddr_flags +}; + +isc_result_t +cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { const unsigned int *flagp = type->of; - return (parse_sockaddrsub(pctx, &cfg_type_sockaddr4wild, *flagp, ret)); + return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret)); } -static void -print_sockaddr(cfg_printer_t *pctx, cfg_obj_t *obj) { +void +cfg_print_sockaddr(cfg_printer_t *pctx, cfg_obj_t *obj) { isc_netaddr_t netaddr; in_port_t port; char buf[ISC_NETADDR_FORMATSIZE]; isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr); isc_netaddr_format(&netaddr, buf, sizeof(buf)); - print_cstr(pctx, buf); + cfg_print_cstr(pctx, buf); port = isc_sockaddr_getport(&obj->value.sockaddr); if (port != 0) { - print(pctx, " port ", 6); - print_uint(pctx, port); + cfg_print_chars(pctx, " port ", 6); + cfg_print_rawuint(pctx, port); + } +} + +void +cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) { + const unsigned int *flagp = type->of; + int n = 0; + cfg_print_chars(pctx, "( ", 2); + if (*flagp & CFG_ADDR_V4OK) { + if (n != 0) + cfg_print_chars(pctx, " | ", 3); + cfg_print_cstr(pctx, ""); + n++; + } + if (*flagp & CFG_ADDR_V6OK) { + if (n != 0) + cfg_print_chars(pctx, " | ", 3); + cfg_print_cstr(pctx, ""); + n++; + } + if (*flagp & CFG_ADDR_WILDOK) { + if (n != 0) + cfg_print_chars(pctx, " | ", 3); + cfg_print_chars(pctx, "*", 1); + n++; + } + cfg_print_chars(pctx, " ) ", 3); + if (*flagp & CFG_ADDR_WILDOK) { + cfg_print_cstr(pctx, "[ port ( | * ) ]"); + } else { + cfg_print_cstr(pctx, "[ port ]"); } } @@ -3202,344 +1926,7 @@ cfg_obj_assockaddr(cfg_obj_t *obj) { return (&obj->value.sockaddr); } -/* An IPv4/IPv6 address with optional port, "*" accepted as wildcard. */ -static unsigned int sockaddr4wild_flags = WILDOK|V4OK; -static cfg_type_t cfg_type_sockaddr4wild = { - "sockaddr4wild", parse_sockaddr, print_sockaddr, - &cfg_rep_sockaddr, &sockaddr4wild_flags -}; - -static unsigned int sockaddr6wild_flags = WILDOK|V6OK; -static cfg_type_t cfg_type_sockaddr6wild = { - "v6addrportwild", parse_sockaddr, print_sockaddr, - &cfg_rep_sockaddr, &sockaddr6wild_flags -}; - -static unsigned int sockaddr_flags = V4OK|V6OK; -static cfg_type_t cfg_type_sockaddr = { - "sockaddr", parse_sockaddr, print_sockaddr, - &cfg_rep_sockaddr, &sockaddr_flags -}; - -/* - * The socket address syntax in the "controls" statement is silly. - * It allows both socket address families, but also allows "*", - * whis is gratuitously interpreted as the IPv4 wildcard address. - */ -static unsigned int controls_sockaddr_flags = V4OK|V6OK|WILDOK; -static cfg_type_t cfg_type_controls_sockaddr = { - "controls_sockaddr", parse_sockaddr, print_sockaddr, - &cfg_rep_sockaddr, &controls_sockaddr_flags }; - - -/* - * Handle the special kludge syntax of the "keys" clause in the "server" - * statement, which takes a single key with or without braces and semicolon. - */ -static isc_result_t -parse_server_key_kludge(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) -{ - isc_result_t result; - isc_boolean_t braces = ISC_FALSE; - UNUSED(type); - - /* Allow opening brace. */ - CHECK(cfg_peektoken(pctx, 0)); - if (pctx->token.type == isc_tokentype_special && - pctx->token.value.as_char == '{') { - result = cfg_gettoken(pctx, 0); - braces = ISC_TRUE; - } - - CHECK(parse(pctx, &cfg_type_astring, ret)); - - if (braces) { - /* Skip semicolon if present. */ - CHECK(cfg_peektoken(pctx, 0)); - if (pctx->token.type == isc_tokentype_special && - pctx->token.value.as_char == ';') - CHECK(cfg_gettoken(pctx, 0)); - - CHECK(parse_special(pctx, '}')); - } - cleanup: - return (result); -} -static cfg_type_t cfg_type_server_key_kludge = { - "server_key", parse_server_key_kludge, NULL, NULL, NULL }; - - -/* - * An optional logging facility. - */ - -static isc_result_t -parse_optional_facility(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) -{ - isc_result_t result; - UNUSED(type); - - CHECK(cfg_peektoken(pctx, QSTRING)); - if (pctx->token.type == isc_tokentype_string || - pctx->token.type == isc_tokentype_qstring) { - CHECK(parse(pctx, &cfg_type_astring, ret)); - } else { - CHECK(parse(pctx, &cfg_type_void, ret)); - } - cleanup: - return (result); -} - -static cfg_type_t cfg_type_optional_facility = { - "optional_facility", parse_optional_facility, NULL, NULL, NULL }; - - -/* - * A log severity. Return as a string, except "debug N", - * which is returned as a keyword object. - */ - -static keyword_type_t debug_kw = { "debug", &cfg_type_uint32 }; -static cfg_type_t cfg_type_debuglevel = { - "debuglevel", parse_keyvalue, - print_keyvalue, &cfg_rep_uint32, &debug_kw -}; - -static isc_result_t -parse_logseverity(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { - isc_result_t result; - UNUSED(type); - - CHECK(cfg_peektoken(pctx, 0)); - if (pctx->token.type == isc_tokentype_string && - strcasecmp(pctx->token.value.as_pointer, "debug") == 0) { - CHECK(cfg_gettoken(pctx, 0)); /* read "debug" */ - CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER)); - if (pctx->token.type == isc_tokentype_number) { - CHECK(parse_uint32(pctx, NULL, ret)); - } else { - /* - * The debug level is optional and defaults to 1. - * This makes little sense, but we support it for - * compatibility with BIND 8. - */ - CHECK(create_cfgobj(pctx, &cfg_type_uint32, ret)); - (*ret)->value.uint32 = 1; - } - (*ret)->type = &cfg_type_debuglevel; /* XXX kludge */ - } else { - CHECK(parse(pctx, &cfg_type_loglevel, ret)); - } - cleanup: - return (result); -} - -static cfg_type_t cfg_type_logseverity = { - "logseverity", parse_logseverity, NULL, NULL, NULL }; - -/* - * The "file" clause of the "channel" statement. - * This is yet another special case. - */ - -static const char *logversions_enums[] = { "unlimited", NULL }; -static isc_result_t -parse_logversions(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { - return (parse_enum_or_other(pctx, type, &cfg_type_uint32, ret)); -} -static cfg_type_t cfg_type_logversions = { - "logversions", parse_logversions, print_ustring, - &cfg_rep_string, logversions_enums -}; - -static cfg_tuplefielddef_t logfile_fields[] = { - { "file", &cfg_type_qstring, 0 }, - { "versions", &cfg_type_logversions, 0 }, - { "size", &cfg_type_size, 0 }, - { NULL, NULL, 0 } -}; - -static isc_result_t -parse_logfile(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { - isc_result_t result; - cfg_obj_t *obj = NULL; - const cfg_tuplefielddef_t *fields = type->of; - - CHECK(create_tuple(pctx, type, &obj)); - - /* Parse the mandatory "file" field */ - CHECK(parse(pctx, fields[0].type, &obj->value.tuple[0])); - - /* Parse "versions" and "size" fields in any order. */ - for (;;) { - CHECK(cfg_peektoken(pctx, 0)); - if (pctx->token.type == isc_tokentype_string) { - CHECK(cfg_gettoken(pctx, 0)); - if (strcasecmp(pctx->token.value.as_pointer, - "versions") == 0 && - obj->value.tuple[1] == NULL) { - CHECK(parse(pctx, fields[1].type, - &obj->value.tuple[1])); - } else if (strcasecmp(pctx->token.value.as_pointer, - "size") == 0 && - obj->value.tuple[2] == NULL) { - CHECK(parse(pctx, fields[2].type, - &obj->value.tuple[2])); - } else { - break; - } - } else { - break; - } - } - - /* Create void objects for missing optional values. */ - if (obj->value.tuple[1] == NULL) - CHECK(parse_void(pctx, NULL, &obj->value.tuple[1])); - if (obj->value.tuple[2] == NULL) - CHECK(parse_void(pctx, NULL, &obj->value.tuple[2])); - - *ret = obj; - return (ISC_R_SUCCESS); - - cleanup: - CLEANUP_OBJ(obj); - return (result); -} - -static void -print_logfile(cfg_printer_t *pctx, cfg_obj_t *obj) { - print_obj(pctx, obj->value.tuple[0]); /* file */ - if (obj->value.tuple[1]->type->print != print_void) { - print(pctx, " versions ", 10); - print_obj(pctx, obj->value.tuple[1]); - } - if (obj->value.tuple[2]->type->print != print_void) { - print(pctx, " size ", 6); - print_obj(pctx, obj->value.tuple[2]); - } -} - -static cfg_type_t cfg_type_logfile = { - "logfile", parse_logfile, print_logfile, &cfg_rep_tuple, - logfile_fields -}; - - -/* - * lwres - */ - -static cfg_tuplefielddef_t lwres_view_fields[] = { - { "name", &cfg_type_astring, 0 }, - { "class", &cfg_type_optional_class, 0 }, - { NULL, NULL, 0 } -}; -static cfg_type_t cfg_type_lwres_view = { - "lwres_view", parse_tuple, print_tuple, &cfg_rep_tuple, - lwres_view_fields -}; - -static cfg_type_t cfg_type_lwres_searchlist = { - "lwres_searchlist", parse_bracketed_list, print_bracketed_list, - &cfg_rep_list, &cfg_type_astring }; - -static cfg_clausedef_t -lwres_clauses[] = { - { "listen-on", &cfg_type_portiplist, 0 }, - { "view", &cfg_type_lwres_view, 0 }, - { "search", &cfg_type_lwres_searchlist, 0 }, - { "ndots", &cfg_type_uint32, 0 }, - { NULL, NULL, 0 } -}; - -static cfg_clausedef_t * -lwres_clausesets[] = { - lwres_clauses, - NULL -}; -static cfg_type_t cfg_type_lwres = { - "lwres", parse_map, print_map, &cfg_rep_map, lwres_clausesets }; - -/* - * rndc - */ - -static cfg_clausedef_t -rndcconf_options_clauses[] = { - { "default-server", &cfg_type_astring, 0 }, - { "default-key", &cfg_type_astring, 0 }, - { "default-port", &cfg_type_uint32, 0 }, - { NULL, NULL, 0 } -}; - -static cfg_clausedef_t * -rndcconf_options_clausesets[] = { - rndcconf_options_clauses, - NULL -}; - -static cfg_type_t cfg_type_rndcconf_options = { - "rndcconf_options", parse_map, print_map, &cfg_rep_map, - rndcconf_options_clausesets -}; - -static cfg_clausedef_t -rndcconf_server_clauses[] = { - { "key", &cfg_type_astring, 0 }, - { "port", &cfg_type_uint32, 0 }, - { NULL, NULL, 0 } -}; - -static cfg_clausedef_t * -rndcconf_server_clausesets[] = { - rndcconf_server_clauses, - NULL -}; - -static cfg_type_t cfg_type_rndcconf_server = { - "rndcconf_server", parse_named_map, print_map, &cfg_rep_map, - rndcconf_server_clausesets -}; - -static cfg_clausedef_t -rndcconf_clauses[] = { - { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI }, - { "server", &cfg_type_rndcconf_server, CFG_CLAUSEFLAG_MULTI }, - { "options", &cfg_type_rndcconf_options, 0 }, - { NULL, NULL, 0 } -}; - -static cfg_clausedef_t * -rndcconf_clausesets[] = { - rndcconf_clauses, - NULL -}; - -LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndcconf = { - "rndcconf", parse_mapbody, print_mapbody, &cfg_rep_map, - rndcconf_clausesets -}; - -static cfg_clausedef_t -rndckey_clauses[] = { - { "key", &cfg_type_key, 0 }, - { NULL, NULL, 0 } -}; - -static cfg_clausedef_t * -rndckey_clausesets[] = { - rndckey_clauses, - NULL -}; - -LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndckey = { - "rndckey", parse_mapbody, print_mapbody, &cfg_rep_map, - rndckey_clausesets -}; - - -static isc_result_t +isc_result_t cfg_gettoken(cfg_parser_t *pctx, int options) { isc_result_t result; @@ -3580,18 +1967,18 @@ cfg_gettoken(cfg_parser_t *pctx, int options) { case ISC_R_NOSPACE: /* More understandable than "ran out of space". */ - parser_error(pctx, LOG_NEAR, "token too big"); + cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big"); break; default: - parser_error(pctx, LOG_NEAR, "%s", + cfg_parser_error(pctx, CFG_LOG_NEAR, "%s", isc_result_totext(result)); break; } return (result); } -static void +void cfg_ungettoken(cfg_parser_t *pctx) { if (pctx->seen_eof) return; @@ -3599,7 +1986,7 @@ cfg_ungettoken(cfg_parser_t *pctx) { pctx->ungotten = ISC_TRUE; } -static isc_result_t +isc_result_t cfg_peektoken(cfg_parser_t *pctx, int options) { isc_result_t result; CHECK(cfg_gettoken(pctx, options)); @@ -3616,20 +2003,20 @@ static isc_result_t cfg_getstringtoken(cfg_parser_t *pctx) { isc_result_t result; - result = cfg_gettoken(pctx, QSTRING); + result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING); if (result != ISC_R_SUCCESS) return (result); if (pctx->token.type != isc_tokentype_string && pctx->token.type != isc_tokentype_qstring) { - parser_error(pctx, LOG_NEAR, "expected string"); + cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string"); return (ISC_R_UNEXPECTEDTOKEN); } return (ISC_R_SUCCESS); } -static void -parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) { +void +cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) { va_list args; va_start(args, fmt); parser_complain(pctx, ISC_FALSE, flags, fmt, args); @@ -3637,8 +2024,8 @@ parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) { pctx->errors++; } -static void -parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) { +void +cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) { va_list args; va_start(args, fmt); parser_complain(pctx, ISC_TRUE, flags, fmt, args); @@ -3685,7 +2072,7 @@ parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning, FATAL_ERROR(__FILE__, __LINE__, "error message would overflow"); - if ((flags & (LOG_NEAR|LOG_BEFORE|LOG_NOPREP)) != 0) { + if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) { isc_region_t r; if (pctx->ungotten) @@ -3705,9 +2092,9 @@ parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning, } /* Choose a preposition. */ - if (flags & LOG_NEAR) + if (flags & CFG_LOG_NEAR) prep = " near "; - else if (flags & LOG_BEFORE) + else if (flags & CFG_LOG_BEFORE) prep = " before "; else prep = " "; @@ -3736,8 +2123,8 @@ cfg_obj_log(cfg_obj_t *obj, isc_log_t *lctx, int level, const char *fmt, ...) { va_end(ap); } -static isc_result_t -create_cfgobj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { +isc_result_t +cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { cfg_obj_t *obj; obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t)); @@ -3770,7 +2157,7 @@ create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_symtab_t *symtab = NULL; cfg_obj_t *obj = NULL; - CHECK(create_cfgobj(pctx, type, &obj)); + CHECK(cfg_create_obj(pctx, type, &obj)); CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */ map_symtabitem_destroy, pctx, ISC_FALSE, &symtab)); @@ -3814,153 +2201,16 @@ free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) { UNUSED(obj); } -/* - * Data and functions for printing grammar summaries. - */ -static struct flagtext { - unsigned int flag; - const char *text; -} flagtexts[] = { - { CFG_CLAUSEFLAG_NOTIMP, "not implemented" }, - { CFG_CLAUSEFLAG_NYI, "not yet implemented" }, - { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" }, - { CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" }, - { 0, NULL } -}; - -static void -print_clause_flags(cfg_printer_t *pctx, unsigned int flags) { - struct flagtext *p; - isc_boolean_t first = ISC_TRUE; - for (p = flagtexts; p->flag != 0; p++) { - if ((flags & p->flag) != 0) { - if (first) - print(pctx, " // ", 4); - else - print(pctx, ", ", 2); - print_cstr(pctx, p->text); - first = ISC_FALSE; - } - } +void +cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) { + type->doc(pctx, type); } -static void -print_grammar(cfg_printer_t *pctx, const cfg_type_t *type) { - if (type->print == print_mapbody) { - const cfg_clausedef_t * const *clauseset; - const cfg_clausedef_t *clause; - - for (clauseset = type->of; *clauseset != NULL; clauseset++) { - for (clause = *clauseset; - clause->name != NULL; - clause++) { - print_cstr(pctx, clause->name); - print(pctx, " ", 1); - print_grammar(pctx, clause->type); - print(pctx, ";", 1); - /* XXX print flags here? */ - print(pctx, "\n\n", 2); - } - } - } else if (type->print == print_map) { - const cfg_clausedef_t * const *clauseset; - const cfg_clausedef_t *clause; - - if (type->parse == parse_named_map) { - print_grammar(pctx, &cfg_type_astring); - print(pctx, " ", 1); - } - - print_open(pctx); - - for (clauseset = type->of; *clauseset != NULL; clauseset++) { - for (clause = *clauseset; - clause->name != NULL; - clause++) { - print_indent(pctx); - print_cstr(pctx, clause->name); - if (clause->type->print != print_void) - print(pctx, " ", 1); - print_grammar(pctx, clause->type); - print(pctx, ";", 1); - print_clause_flags(pctx, clause->flags); - print(pctx, "\n", 1); - } - } - print_close(pctx); - } else if (type->print == print_tuple) { - const cfg_tuplefielddef_t *fields = type->of; - const cfg_tuplefielddef_t *f; - isc_boolean_t need_space = ISC_FALSE; - - for (f = fields; f->name != NULL; f++) { - if (need_space) - print(pctx, " ", 1); - print_grammar(pctx, f->type); - need_space = ISC_TF(f->type->print != print_void); - } - } else if (type->parse == parse_enum) { - const char * const *p; - print(pctx, "( ", 2); - for (p = type->of; *p != NULL; p++) { - print_cstr(pctx, *p); - if (p[1] != NULL) - print(pctx, " | ", 3); - } - print(pctx, " )", 2); - } else if (type->print == print_bracketed_list) { - print(pctx, "{ ", 2); - print_grammar(pctx, type->of); - print(pctx, "; ... }", 7); - } else if (type->parse == parse_keyvalue) { - const keyword_type_t *kw = type->of; - print_cstr(pctx, kw->name); - print(pctx, " ", 1); - print_grammar(pctx, kw->type); - } else if (type->parse == parse_optional_keyvalue) { - const keyword_type_t *kw = type->of; - print(pctx, "[ ", 2); - print_cstr(pctx, kw->name); - print(pctx, " ", 1); - print_grammar(pctx, kw->type); - print(pctx, " ]", 2); - } else if (type->parse == parse_sockaddr) { - const unsigned int *flagp = type->of; - int n = 0; - print(pctx, "( ", 2); - if (*flagp & V4OK) { - if (n != 0) - print(pctx, " | ", 3); - print_cstr(pctx, ""); - n++; - } - if (*flagp & V6OK) { - if (n != 0) - print(pctx, " | ", 3); - print_cstr(pctx, ""); - n++; - } - if (*flagp & WILDOK) { - if (n != 0) - print(pctx, " | ", 3); - print(pctx, "*", 1); - n++; - } - print(pctx, " ) ", 3); - if (*flagp & WILDOK) { - print_cstr(pctx, "[ port ( | * ) ]"); - } else { - print_cstr(pctx, "[ port ]"); - } - } else if (type->print == print_void) { - /* Print nothing. */ - } else if (type->parse == parse_qstringornone) { - print(pctx, "( | none )", 26); - } else { - print(pctx, "<", 1); - print_cstr(pctx, type->name); - print(pctx, ">", 1); - } +void +cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) { + cfg_print_chars(pctx, "<", 1); + cfg_print_cstr(pctx, type->name); + cfg_print_chars(pctx, ">", 1); } void @@ -3972,5 +2222,5 @@ cfg_print_grammar(const cfg_type_t *type, pctx.f = f; pctx.closure = closure; pctx.indent = 0; - print_grammar(&pctx, type); + cfg_doc_obj(&pctx, type); } diff --git a/util/copyrights b/util/copyrights index f776b7c4e0..eee11c65e0 100644 --- a/util/copyrights +++ b/util/copyrights @@ -2073,8 +2073,10 @@ ./lib/isccfg/include/isccfg/cfg.h C 2000,2001 ./lib/isccfg/include/isccfg/check.h C 2001 ./lib/isccfg/include/isccfg/log.h C 2001 +./lib/isccfg/include/isccfg/namedconf.h C 2001,2002 ./lib/isccfg/include/isccfg/version.h C 2001 ./lib/isccfg/log.c C 2001 +./lib/isccfg/namedconf.c C 2001,2002 ./lib/isccfg/parser.c C 2000,2001 ./lib/isccfg/version.c C 1998,1999,2000,2001 ./lib/isccfg/win32/DLLMain.c C 2001 -- GitLab