Commit 01529a9f18 for bind
commit 01529a9f18c6cbadcd77953c853c1b86e9d5d15d
Author: Alessio Podda <alessio@isc.org>
Date: Mon Sep 28 08:36:47 2026 +0200
Remove bracketed text
This commit removes the bracketedtext lexer token type, and replaces
it with a list of strings. The bracketedtext option made the grammar
substantially more complex and made several parser optimizations
impossible.
The new bracketed tokens category is slightly more restrictive, as
it doesn't distinguish quoted and unquoted text, and does not allow
bare escaped strings.
diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h
index 5f6bafa432..d3aea2f097 100644
--- a/bin/named/include/named/server.h
+++ b/bin/named/include/named/server.h
@@ -426,4 +426,4 @@ named_server_getmemprof(void);
isc_result_t
named_register_one_plugin(const cfg_obj_t *config, const cfg_obj_t *obj,
cfg_aclconfctx_t *aclctx, const char *plugin_path,
- const char *parameters, void *callback_data);
+ const char *const *parameters, void *callback_data);
diff --git a/bin/named/server.c b/bin/named/server.c
index fb8ac9f908..19574da0c5 100644
--- a/bin/named/server.c
+++ b/bin/named/server.c
@@ -1511,7 +1511,7 @@ configure_dyndb(const cfg_obj_t *dyndb, isc_mem_t *mctx,
obj = cfg_tuple_get(dyndb, "parameters");
if (obj != NULL) {
- result = dns_dyndb_load(library, name, cfg_obj_asstring(obj),
+ result = dns_dyndb_load(library, name, cfg_obj_astokens(obj),
cfg_obj_file(obj), cfg_obj_line(obj),
mctx, dctx);
}
@@ -3556,7 +3556,7 @@ create_mapped_acl(void) {
isc_result_t
named_register_one_plugin(const cfg_obj_t *config, const cfg_obj_t *obj,
cfg_aclconfctx_t *aclctx, const char *plugin_path,
- const char *parameters, void *callback_data) {
+ const char *const *parameters, void *callback_data) {
char full_path[PATH_MAX];
isc_result_t result;
ns_hook_data_t *hookdata = callback_data;
diff --git a/bin/plugins/filter-a.c b/bin/plugins/filter-a.c
index 7346846e6a..5a99cd3f64 100644
--- a/bin/plugins/filter-a.c
+++ b/bin/plugins/filter-a.c
@@ -39,7 +39,7 @@
#include <isccfg/aclconf.h>
#include <isccfg/cfg.h>
-#include <isccfg/grammar.h>
+#include <isccfg/tokens.h>
#include <ns/client.h>
#include <ns/hooks.h>
@@ -160,69 +160,100 @@ install_hooks(ns_hooktable_t *hooktable, isc_mem_t *mctx,
/*
* Support for parsing of parameters.
*/
-static const char *filter_a_enums[] = { "break-dnssec", NULL };
+typedef struct filter_params {
+ cfg_obj_t *aclobj;
+ filter_a_t v4_a;
+ filter_a_t v6_a;
+} filter_params_t;
static isc_result_t
-parse_filter_a(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
- return cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret);
-}
+parse_filter_a_on(cfg_tokens_t *tok, const char *name, filter_a_t *dstp) {
+ const char *value = NULL;
-static void
-doc_filter_a(cfg_printer_t *pctx, const cfg_type_t *type) {
- cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
-}
+ RETERR(cfg_tokens_getstring(tok, &value));
-static cfg_type_t cfg_type_filter_a = {
- "filter_a", parse_filter_a, cfg_print_ustring,
- doc_filter_a, &cfg_rep_string, filter_a_enums,
-};
+ if (strcasecmp(value, "yes") == 0 || strcasecmp(value, "true") == 0 ||
+ strcmp(value, "1") == 0)
+ {
+ *dstp = FILTER;
+ } else if (strcasecmp(value, "no") == 0 ||
+ strcasecmp(value, "false") == 0 || strcmp(value, "0") == 0)
+ {
+ *dstp = NONE;
+ } else if (strcasecmp(value, "break-dnssec") == 0) {
+ *dstp = BREAK_DNSSEC;
+ } else {
+ cfg_tokens_log(tok, ISC_LOG_ERROR, "'%s': invalid value '%s'",
+ name, value);
+ return ISC_R_UNEXPECTEDTOKEN;
+ }
-static cfg_clausedef_t param_clauses[] = {
- { "filter-a", &cfg_type_bracketed_aml, 0, NULL },
- { "filter-a-on-v6", &cfg_type_filter_a, 0, NULL },
- { "filter-a-on-v4", &cfg_type_filter_a, 0, NULL },
-};
+ return ISC_R_SUCCESS;
+}
-static cfg_clausedef_t *param_clausesets[] = { param_clauses, NULL };
+static isc_result_t
+parse_parameters(const char *const *parameters, const char *cfg_file,
+ unsigned long cfg_line, filter_params_t *params) {
+ isc_result_t result = ISC_R_SUCCESS;
+ cfg_tokens_t tok;
+ bool seen_v4 = false, seen_v6 = false;
-static cfg_type_t cfg_type_parameters = { "filter-a-params", cfg_parse_mapbody,
- cfg_print_mapbody, cfg_doc_mapbody,
- &cfg_rep_map, param_clausesets };
+ *params = (filter_params_t){ .v4_a = NONE, .v6_a = NONE };
-static isc_result_t
-parse_filter_a_on(const cfg_obj_t *param_obj, const char *param_name,
- filter_a_t *dstp) {
- const cfg_obj_t *obj = NULL;
- isc_result_t result;
+ cfg_tokens_init(&tok, parameters, cfg_file, cfg_line);
+ while (cfg_tokens_peek(&tok) != NULL) {
+ const char *name = NULL;
- result = cfg_map_get(param_obj, param_name, &obj);
- if (result != ISC_R_SUCCESS) {
- return ISC_R_SUCCESS;
- }
+ CHECK(cfg_tokens_getstring(&tok, &name));
- if (cfg_obj_isboolean(obj)) {
- if (cfg_obj_asboolean(obj)) {
- *dstp = FILTER;
+ if (strcasecmp(name, "filter-a") == 0) {
+ if (params->aclobj != NULL) {
+ cfg_tokens_log(&tok, ISC_LOG_ERROR,
+ "'%s' redefined", name);
+ CLEANUP(ISC_R_EXISTS);
+ }
+ CHECK(cfg_tokens_getaml(&tok, ¶ms->aclobj));
+ } else if (strcasecmp(name, "filter-a-on-v6") == 0) {
+ if (seen_v6) {
+ cfg_tokens_log(&tok, ISC_LOG_ERROR,
+ "'%s' redefined", name);
+ CLEANUP(ISC_R_EXISTS);
+ }
+ seen_v6 = true;
+ CHECK(parse_filter_a_on(&tok, name, ¶ms->v6_a));
+ } else if (strcasecmp(name, "filter-a-on-v4") == 0) {
+ if (seen_v4) {
+ cfg_tokens_log(&tok, ISC_LOG_ERROR,
+ "'%s' redefined", name);
+ CLEANUP(ISC_R_EXISTS);
+ }
+ seen_v4 = true;
+ CHECK(parse_filter_a_on(&tok, name, ¶ms->v4_a));
} else {
- *dstp = NONE;
+ cfg_tokens_log(&tok, ISC_LOG_ERROR,
+ "unknown option '%s'", name);
+ CLEANUP(ISC_R_FAILURE);
}
- } else if (strcasecmp(cfg_obj_asstring(obj), "break-dnssec") == 0) {
- *dstp = BREAK_DNSSEC;
- } else {
- result = ISC_R_UNEXPECTED;
+
+ CHECK(cfg_tokens_expect(&tok, CFG_TOKEN_END));
}
+cleanup:
+ cfg_tokens_clear(&tok);
+ if (result != ISC_R_SUCCESS && params->aclobj != NULL) {
+ cfg_obj_detach(¶ms->aclobj);
+ }
return result;
}
static isc_result_t
-check_syntax(cfg_obj_t *fmap, const void *cfg, isc_mem_t *mctx, void *aclctx) {
+check_syntax(const filter_params_t *params, const void *cfg, isc_mem_t *mctx,
+ void *aclctx) {
isc_result_t result = ISC_R_SUCCESS;
- const cfg_obj_t *aclobj = NULL;
+ const cfg_obj_t *aclobj = params->aclobj;
dns_acl_t *acl = NULL;
- filter_a_t f4 = NONE, f6 = NONE;
+ filter_a_t f4 = params->v4_a, f6 = params->v6_a;
- cfg_map_get(fmap, "filter-a", &aclobj);
if (aclobj == NULL) {
return result;
}
@@ -230,9 +261,6 @@ check_syntax(cfg_obj_t *fmap, const void *cfg, isc_mem_t *mctx, void *aclctx) {
CHECK(cfg_acl_fromconfig(aclobj, (const cfg_obj_t *)cfg,
(cfg_aclconfctx_t *)aclctx, mctx, 0, &acl));
- CHECK(parse_filter_a_on(fmap, "filter-a-on-v6", &f6));
- CHECK(parse_filter_a_on(fmap, "filter-a-on-v4", &f4));
-
if ((f4 != NONE || f6 != NONE) && dns_acl_isnone(acl)) {
cfg_obj_log(aclobj, ISC_LOG_WARNING,
"\"filter-a\" is 'none;' but "
@@ -256,25 +284,19 @@ cleanup:
}
static isc_result_t
-parse_parameters(filter_instance_t *inst, const char *parameters,
- const void *cfg, const char *cfg_file, unsigned long cfg_line,
- isc_mem_t *mctx, void *aclctx) {
+configure_instance(filter_instance_t *inst, const char *const *parameters,
+ const void *cfg, const char *cfg_file,
+ unsigned long cfg_line, isc_mem_t *mctx, void *aclctx) {
isc_result_t result = ISC_R_SUCCESS;
- cfg_obj_t *param_obj = NULL;
- const cfg_obj_t *obj = NULL;
- isc_buffer_t b;
+ filter_params_t params;
- isc_buffer_constinit(&b, parameters, strlen(parameters));
- isc_buffer_add(&b, strlen(parameters));
- CHECK(cfg_parse_buffer(&b, cfg_file, cfg_line, &cfg_type_parameters, 0,
- ¶m_obj));
+ CHECK(parse_parameters(parameters, cfg_file, cfg_line, ¶ms));
- CHECK(parse_filter_a_on(param_obj, "filter-a-on-v6", &inst->v6_a));
- CHECK(parse_filter_a_on(param_obj, "filter-a-on-v4", &inst->v4_a));
+ inst->v4_a = params.v4_a;
+ inst->v6_a = params.v6_a;
- result = cfg_map_get(param_obj, "filter-a", &obj);
- if (result == ISC_R_SUCCESS) {
- CHECK(cfg_acl_fromconfig(obj, (const cfg_obj_t *)cfg,
+ if (params.aclobj != NULL) {
+ CHECK(cfg_acl_fromconfig(params.aclobj, (const cfg_obj_t *)cfg,
(cfg_aclconfctx_t *)aclctx, mctx, 0,
&inst->a_acl));
} else {
@@ -282,8 +304,8 @@ parse_parameters(filter_instance_t *inst, const char *parameters,
}
cleanup:
- if (param_obj != NULL) {
- cfg_obj_detach(¶m_obj);
+ if (params.aclobj != NULL) {
+ cfg_obj_detach(¶ms.aclobj);
}
return result;
}
@@ -302,10 +324,10 @@ cleanup:
* register hook functions into the view hook table.
*/
isc_result_t
-plugin_register(const char *parameters, const void *cfg, const char *cfg_file,
- unsigned long cfg_line, isc_mem_t *mctx, void *aclctx,
- ns_hooktable_t *hooktable, const ns_pluginctx_t *ctx,
- void **instp) {
+plugin_register(const char *const *parameters, const void *cfg,
+ const char *cfg_file, unsigned long cfg_line, isc_mem_t *mctx,
+ void *aclctx, ns_hooktable_t *hooktable,
+ const ns_pluginctx_t *ctx, void **instp) {
filter_instance_t *inst = NULL;
isc_result_t result = ISC_R_SUCCESS;
@@ -321,8 +343,8 @@ plugin_register(const char *parameters, const void *cfg, const char *cfg_file,
isc_mem_attach(mctx, &inst->mctx);
if (parameters != NULL) {
- CHECK(parse_parameters(inst, parameters, cfg, cfg_file,
- cfg_line, mctx, aclctx));
+ CHECK(configure_instance(inst, parameters, cfg, cfg_file,
+ cfg_line, mctx, aclctx));
}
isc_ht_init(&inst->ht, mctx, 1, ISC_HT_CASE_SENSITIVE);
@@ -344,23 +366,18 @@ cleanup:
}
isc_result_t
-plugin_check(const char *parameters, const void *cfg, const char *cfg_file,
- unsigned long cfg_line, isc_mem_t *mctx, void *aclctx,
- const ns_pluginctx_t *ctx ISC_ATTR_UNUSED) {
+plugin_check(const char *const *parameters, const void *cfg,
+ const char *cfg_file, unsigned long cfg_line, isc_mem_t *mctx,
+ void *aclctx, const ns_pluginctx_t *ctx ISC_ATTR_UNUSED) {
isc_result_t result = ISC_R_SUCCESS;
- cfg_obj_t *param_obj = NULL;
- isc_buffer_t b;
-
- isc_buffer_constinit(&b, parameters, strlen(parameters));
- isc_buffer_add(&b, strlen(parameters));
- CHECK(cfg_parse_buffer(&b, cfg_file, cfg_line, &cfg_type_parameters, 0,
- ¶m_obj));
+ filter_params_t params;
- CHECK(check_syntax(param_obj, cfg, mctx, aclctx));
+ CHECK(parse_parameters(parameters, cfg_file, cfg_line, ¶ms));
+ CHECK(check_syntax(¶ms, cfg, mctx, aclctx));
cleanup:
- if (param_obj != NULL) {
- cfg_obj_detach(¶m_obj);
+ if (params.aclobj != NULL) {
+ cfg_obj_detach(¶ms.aclobj);
}
return result;
}
diff --git a/bin/plugins/filter-aaaa.c b/bin/plugins/filter-aaaa.c
index 8da56ba998..9b01918c0a 100644
--- a/bin/plugins/filter-aaaa.c
+++ b/bin/plugins/filter-aaaa.c
@@ -39,7 +39,7 @@
#include <isccfg/aclconf.h>
#include <isccfg/cfg.h>
-#include <isccfg/grammar.h>
+#include <isccfg/tokens.h>
#include <ns/client.h>
#include <ns/hooks.h>
@@ -160,70 +160,102 @@ install_hooks(ns_hooktable_t *hooktable, isc_mem_t *mctx,
/*
* Support for parsing of parameters.
*/
-static const char *filter_aaaa_enums[] = { "break-dnssec", NULL };
+typedef struct filter_params {
+ cfg_obj_t *aclobj;
+ filter_aaaa_t v4_aaaa;
+ filter_aaaa_t v6_aaaa;
+} filter_params_t;
static isc_result_t
-parse_filter_aaaa(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
- return cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret);
-}
+parse_filter_aaaa_on(cfg_tokens_t *tok, const char *name, filter_aaaa_t *dstp) {
+ const char *value = NULL;
-static void
-doc_filter_aaaa(cfg_printer_t *pctx, const cfg_type_t *type) {
- cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
-}
+ RETERR(cfg_tokens_getstring(tok, &value));
-static cfg_type_t cfg_type_filter_aaaa = {
- "filter_aaaa", parse_filter_aaaa, cfg_print_ustring,
- doc_filter_aaaa, &cfg_rep_string, filter_aaaa_enums,
-};
+ if (strcasecmp(value, "yes") == 0 || strcasecmp(value, "true") == 0 ||
+ strcmp(value, "1") == 0)
+ {
+ *dstp = FILTER;
+ } else if (strcasecmp(value, "no") == 0 ||
+ strcasecmp(value, "false") == 0 || strcmp(value, "0") == 0)
+ {
+ *dstp = NONE;
+ } else if (strcasecmp(value, "break-dnssec") == 0) {
+ *dstp = BREAK_DNSSEC;
+ } else {
+ cfg_tokens_log(tok, ISC_LOG_ERROR, "'%s': invalid value '%s'",
+ name, value);
+ return ISC_R_UNEXPECTEDTOKEN;
+ }
-static cfg_clausedef_t param_clauses[] = {
- { "filter-aaaa", &cfg_type_bracketed_aml, 0, NULL },
- { "filter-aaaa-on-v4", &cfg_type_filter_aaaa, 0, NULL },
- { "filter-aaaa-on-v6", &cfg_type_filter_aaaa, 0, NULL },
-};
+ return ISC_R_SUCCESS;
+}
-static cfg_clausedef_t *param_clausesets[] = { param_clauses, NULL };
+static isc_result_t
+parse_parameters(const char *const *parameters, const char *cfg_file,
+ unsigned long cfg_line, filter_params_t *params) {
+ isc_result_t result = ISC_R_SUCCESS;
+ cfg_tokens_t tok;
+ bool seen_v4 = false, seen_v6 = false;
-static cfg_type_t cfg_type_parameters = {
- "filter-aaaa-params", cfg_parse_mapbody, cfg_print_mapbody,
- cfg_doc_mapbody, &cfg_rep_map, param_clausesets
-};
+ *params = (filter_params_t){ .v4_aaaa = NONE, .v6_aaaa = NONE };
-static isc_result_t
-parse_filter_aaaa_on(const cfg_obj_t *param_obj, const char *param_name,
- filter_aaaa_t *dstp) {
- const cfg_obj_t *obj = NULL;
- isc_result_t result;
+ cfg_tokens_init(&tok, parameters, cfg_file, cfg_line);
+ while (cfg_tokens_peek(&tok) != NULL) {
+ const char *name = NULL;
- result = cfg_map_get(param_obj, param_name, &obj);
- if (result != ISC_R_SUCCESS) {
- return ISC_R_SUCCESS;
- }
+ CHECK(cfg_tokens_getstring(&tok, &name));
- if (cfg_obj_isboolean(obj)) {
- if (cfg_obj_asboolean(obj)) {
- *dstp = FILTER;
+ if (strcasecmp(name, "filter-aaaa") == 0) {
+ if (params->aclobj != NULL) {
+ cfg_tokens_log(&tok, ISC_LOG_ERROR,
+ "'%s' redefined", name);
+ CLEANUP(ISC_R_EXISTS);
+ }
+ CHECK(cfg_tokens_getaml(&tok, ¶ms->aclobj));
+ } else if (strcasecmp(name, "filter-aaaa-on-v4") == 0) {
+ if (seen_v4) {
+ cfg_tokens_log(&tok, ISC_LOG_ERROR,
+ "'%s' redefined", name);
+ CLEANUP(ISC_R_EXISTS);
+ }
+ seen_v4 = true;
+ CHECK(parse_filter_aaaa_on(&tok, name,
+ ¶ms->v4_aaaa));
+ } else if (strcasecmp(name, "filter-aaaa-on-v6") == 0) {
+ if (seen_v6) {
+ cfg_tokens_log(&tok, ISC_LOG_ERROR,
+ "'%s' redefined", name);
+ CLEANUP(ISC_R_EXISTS);
+ }
+ seen_v6 = true;
+ CHECK(parse_filter_aaaa_on(&tok, name,
+ ¶ms->v6_aaaa));
} else {
- *dstp = NONE;
+ cfg_tokens_log(&tok, ISC_LOG_ERROR,
+ "unknown option '%s'", name);
+ CLEANUP(ISC_R_FAILURE);
}
- } else if (strcasecmp(cfg_obj_asstring(obj), "break-dnssec") == 0) {
- *dstp = BREAK_DNSSEC;
- } else {
- result = ISC_R_UNEXPECTED;
+
+ CHECK(cfg_tokens_expect(&tok, CFG_TOKEN_END));
}
+cleanup:
+ cfg_tokens_clear(&tok);
+ if (result != ISC_R_SUCCESS && params->aclobj != NULL) {
+ cfg_obj_detach(¶ms->aclobj);
+ }
return result;
}
static isc_result_t
-check_syntax(cfg_obj_t *fmap, const void *cfg, isc_mem_t *mctx, void *aclctx) {
+check_syntax(const filter_params_t *params, const void *cfg, isc_mem_t *mctx,
+ void *aclctx) {
isc_result_t result = ISC_R_SUCCESS;
- const cfg_obj_t *aclobj = NULL;
+ const cfg_obj_t *aclobj = params->aclobj;
dns_acl_t *acl = NULL;
- filter_aaaa_t f4 = NONE, f6 = NONE;
+ filter_aaaa_t f4 = params->v4_aaaa, f6 = params->v6_aaaa;
- cfg_map_get(fmap, "filter-aaaa", &aclobj);
if (aclobj == NULL) {
return result;
}
@@ -231,9 +263,6 @@ check_syntax(cfg_obj_t *fmap, const void *cfg, isc_mem_t *mctx, void *aclctx) {
CHECK(cfg_acl_fromconfig(aclobj, (const cfg_obj_t *)cfg,
(cfg_aclconfctx_t *)aclctx, mctx, 0, &acl));
- CHECK(parse_filter_aaaa_on(fmap, "filter-aaaa-on-v4", &f4));
- CHECK(parse_filter_aaaa_on(fmap, "filter-aaaa-on-v6", &f6));
-
if ((f4 != NONE || f6 != NONE) && dns_acl_isnone(acl)) {
cfg_obj_log(aclobj, ISC_LOG_WARNING,
"\"filter-aaaa\" is 'none;' but "
@@ -257,27 +286,19 @@ cleanup:
}
static isc_result_t
-parse_parameters(filter_instance_t *inst, const char *parameters,
- const void *cfg, const char *cfg_file, unsigned long cfg_line,
- isc_mem_t *mctx, void *aclctx) {
+configure_instance(filter_instance_t *inst, const char *const *parameters,
+ const void *cfg, const char *cfg_file,
+ unsigned long cfg_line, isc_mem_t *mctx, void *aclctx) {
isc_result_t result = ISC_R_SUCCESS;
- cfg_obj_t *param_obj = NULL;
- const cfg_obj_t *obj = NULL;
- isc_buffer_t b;
-
- isc_buffer_constinit(&b, parameters, strlen(parameters));
- isc_buffer_add(&b, strlen(parameters));
- CHECK(cfg_parse_buffer(&b, cfg_file, cfg_line, &cfg_type_parameters, 0,
- ¶m_obj));
-
- CHECK(parse_filter_aaaa_on(param_obj, "filter-aaaa-on-v4",
- &inst->v4_aaaa));
- CHECK(parse_filter_aaaa_on(param_obj, "filter-aaaa-on-v6",
- &inst->v6_aaaa));
-
- result = cfg_map_get(param_obj, "filter-aaaa", &obj);
- if (result == ISC_R_SUCCESS) {
- CHECK(cfg_acl_fromconfig(obj, (const cfg_obj_t *)cfg,
+ filter_params_t params;
+
+ CHECK(parse_parameters(parameters, cfg_file, cfg_line, ¶ms));
+
+ inst->v4_aaaa = params.v4_aaaa;
+ inst->v6_aaaa = params.v6_aaaa;
+
+ if (params.aclobj != NULL) {
+ CHECK(cfg_acl_fromconfig(params.aclobj, (const cfg_obj_t *)cfg,
(cfg_aclconfctx_t *)aclctx, mctx, 0,
&inst->aaaa_acl));
} else {
@@ -285,8 +306,8 @@ parse_parameters(filter_instance_t *inst, const char *parameters,
}
cleanup:
- if (param_obj != NULL) {
- cfg_obj_detach(¶m_obj);
+ if (params.aclobj != NULL) {
+ cfg_obj_detach(¶ms.aclobj);
}
return result;
}
@@ -305,10 +326,10 @@ cleanup:
* register hook functions into the view hook table.
*/
isc_result_t
-plugin_register(const char *parameters, const void *cfg, const char *cfg_file,
- unsigned long cfg_line, isc_mem_t *mctx, void *aclctx,
- ns_hooktable_t *hooktable, const ns_pluginctx_t *ctx,
- void **instp) {
+plugin_register(const char *const *parameters, const void *cfg,
+ const char *cfg_file, unsigned long cfg_line, isc_mem_t *mctx,
+ void *aclctx, ns_hooktable_t *hooktable,
+ const ns_pluginctx_t *ctx, void **instp) {
filter_instance_t *inst = NULL;
isc_result_t result = ISC_R_SUCCESS;
@@ -325,8 +346,8 @@ plugin_register(const char *parameters, const void *cfg, const char *cfg_file,
isc_mem_attach(mctx, &inst->mctx);
if (parameters != NULL) {
- CHECK(parse_parameters(inst, parameters, cfg, cfg_file,
- cfg_line, mctx, aclctx));
+ CHECK(configure_instance(inst, parameters, cfg, cfg_file,
+ cfg_line, mctx, aclctx));
}
isc_ht_init(&inst->ht, mctx, 1, ISC_HT_CASE_SENSITIVE);
@@ -348,23 +369,18 @@ cleanup:
}
isc_result_t
-plugin_check(const char *parameters, const void *cfg, const char *cfg_file,
- unsigned long cfg_line, isc_mem_t *mctx, void *aclctx,
- const ns_pluginctx_t *ctx ISC_ATTR_UNUSED) {
+plugin_check(const char *const *parameters, const void *cfg,
+ const char *cfg_file, unsigned long cfg_line, isc_mem_t *mctx,
+ void *aclctx, const ns_pluginctx_t *ctx ISC_ATTR_UNUSED) {
isc_result_t result = ISC_R_SUCCESS;
- cfg_obj_t *param_obj = NULL;
- isc_buffer_t b;
-
- isc_buffer_constinit(&b, parameters, strlen(parameters));
- isc_buffer_add(&b, strlen(parameters));
- CHECK(cfg_parse_buffer(&b, cfg_file, cfg_line, &cfg_type_parameters, 0,
- ¶m_obj));
+ filter_params_t params;
- CHECK(check_syntax(param_obj, cfg, mctx, aclctx));
+ CHECK(parse_parameters(parameters, cfg_file, cfg_line, ¶ms));
+ CHECK(check_syntax(¶ms, cfg, mctx, aclctx));
cleanup:
- if (param_obj != NULL) {
- cfg_obj_detach(¶m_obj);
+ if (params.aclobj != NULL) {
+ cfg_obj_detach(¶ms.aclobj);
}
return result;
}
diff --git a/bin/plugins/synthrecord.c b/bin/plugins/synthrecord.c
index b42ab7c86e..337e7bbd2f 100644
--- a/bin/plugins/synthrecord.c
+++ b/bin/plugins/synthrecord.c
@@ -11,13 +11,15 @@
* information regarding copyright ownership.
*/
+#include <isc/parseint.h>
+
#include <dns/byaddr.h>
#include <dns/rdatalist.h>
#include <dns/view.h>
#include <isccfg/aclconf.h>
#include <isccfg/cfg.h>
-#include <isccfg/grammar.h>
+#include <isccfg/tokens.h>
#include <ns/hooks.h>
@@ -392,36 +394,96 @@ synthrecord_entry(void *arg, void *cbdata, isc_result_t *resp) {
}
}
-static cfg_clausedef_t synthrecord_cfgclauses[] = {
- { "prefix", &cfg_type_astring, 0, NULL },
- { "origin", &cfg_type_astring, 0, NULL },
- { "allow-synth", &cfg_type_bracketed_aml, 0, NULL },
- { "ttl", &cfg_type_uint32, 0, NULL }
-};
+/*
+ * The parsed plugin parameters. The cursor owns decoded strings until
+ * the parameters have been applied.
+ */
+typedef struct synthrecord_params {
+ cfg_tokens_t tokens;
+ const char *prefix;
+ const char *origin;
+ cfg_obj_t *allowsynth;
+ uint32_t ttl;
+ bool has_ttl;
+} synthrecord_params_t;
-static cfg_clausedef_t *synthrecord_cfgparamsclausesets[] = {
- synthrecord_cfgclauses, NULL
-};
+static isc_result_t
+synthrecord_parseparams(const char *const *parameters, const char *cfgfile,
+ unsigned long cfgline, synthrecord_params_t *params) {
+ isc_result_t result = ISC_R_SUCCESS;
+ cfg_tokens_t *tok = ¶ms->tokens;
-static cfg_type_t synthrecord_cfgparams = {
- "synthrecord-params", cfg_parse_mapbody, cfg_print_mapbody,
- cfg_doc_mapbody, &cfg_rep_map, synthrecord_cfgparamsclausesets
-};
+ *params = (synthrecord_params_t){ .ttl = DEFAULT_TTL };
+
+ cfg_tokens_init(tok, parameters, cfgfile, cfgline);
+ while (cfg_tokens_peek(tok) != NULL) {
+ const char *name = NULL, *value = NULL;
+
+ CHECK(cfg_tokens_getstring(tok, &name));
+
+ if (strcasecmp(name, "prefix") == 0) {
+ if (params->prefix != NULL) {
+ cfg_tokens_log(tok, ISC_LOG_ERROR,
+ "'%s' redefined", name);
+ CLEANUP(ISC_R_EXISTS);
+ }
+ CHECK(cfg_tokens_getstring(tok, ¶ms->prefix));
+ } else if (strcasecmp(name, "origin") == 0) {
+ if (params->origin != NULL) {
+ cfg_tokens_log(tok, ISC_LOG_ERROR,
+ "'%s' redefined", name);
+ CLEANUP(ISC_R_EXISTS);
+ }
+ CHECK(cfg_tokens_getstring(tok, ¶ms->origin));
+ } else if (strcasecmp(name, "allow-synth") == 0) {
+ if (params->allowsynth != NULL) {
+ cfg_tokens_log(tok, ISC_LOG_ERROR,
+ "'%s' redefined", name);
+ CLEANUP(ISC_R_EXISTS);
+ }
+ CHECK(cfg_tokens_getaml(tok, ¶ms->allowsynth));
+ } else if (strcasecmp(name, "ttl") == 0) {
+ if (params->has_ttl) {
+ cfg_tokens_log(tok, ISC_LOG_ERROR,
+ "'%s' redefined", name);
+ CLEANUP(ISC_R_EXISTS);
+ }
+ params->has_ttl = true;
+ CHECK(cfg_tokens_getstring(tok, &value));
+ if (isc_parse_uint32(¶ms->ttl, value, 0) !=
+ ISC_R_SUCCESS)
+ {
+ cfg_tokens_log(tok, ISC_LOG_ERROR,
+ "invalid ttl '%s'", value);
+ CLEANUP(ISC_R_BADNUMBER);
+ }
+ } else {
+ cfg_tokens_log(tok, ISC_LOG_ERROR,
+ "unknown option '%s'", name);
+ CLEANUP(ISC_R_FAILURE);
+ }
+
+ CHECK(cfg_tokens_expect(tok, CFG_TOKEN_END));
+ }
+
+cleanup:
+ if (result != ISC_R_SUCCESS && params->allowsynth != NULL) {
+ cfg_obj_detach(¶ms->allowsynth);
+ }
+ return result;
+}
static isc_result_t
-synthrecord_initprefix(synthrecord_t *inst, const cfg_obj_t *synthrecordcfg) {
- isc_result_t result;
- const char *base = NULL;
- const cfg_obj_t *obj = NULL;
+synthrecord_initprefix(synthrecord_t *inst,
+ const synthrecord_params_t *params) {
+ const char *base = params->prefix;
- result = cfg_map_get(synthrecordcfg, "prefix", &obj);
- if (result != ISC_R_SUCCESS) {
+ if (base == NULL) {
isc_log_write(NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
ISC_LOG_ERROR, "synthrecord: prefix not found");
- return result;
+ return ISC_R_NOTFOUND;
}
- base = obj->value.string;
if (strstr(base, ".") != NULL) {
isc_log_write(NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
ISC_LOG_ERROR,
@@ -441,28 +503,24 @@ synthrecord_initprefix(synthrecord_t *inst, const cfg_obj_t *synthrecordcfg) {
isc_ascii_lowercopy((uint8_t *)inst->prefix.base,
(uint8_t *)inst->prefix.base, inst->prefix.length);
- return result;
+ return ISC_R_SUCCESS;
}
static isc_result_t
-synthrecord_initorigin(synthrecord_t *inst, const cfg_obj_t *synthrecordcfg,
+synthrecord_initorigin(synthrecord_t *inst, const synthrecord_params_t *params,
const dns_name_t *zname) {
- isc_result_t result;
- const cfg_obj_t *obj = NULL;
- const char *originstr = NULL;
+ const char *originstr = params->origin;
- result = cfg_map_get(synthrecordcfg, "origin", &obj);
- if (inst->mode == REVERSE && result != ISC_R_SUCCESS) {
+ if (inst->mode == REVERSE && originstr == NULL) {
isc_log_write(NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
ISC_LOG_ERROR,
"'origin' must be set when configuring "
"'synthrecord' for a reverse zone");
- return result;
+ return ISC_R_NOTFOUND;
}
dns_name_init(&inst->origin);
- if (result == ISC_R_SUCCESS) {
- originstr = cfg_obj_asstring(obj);
+ if (originstr != NULL) {
RETERR(dns_name_fromstring(&inst->origin, originstr, NULL, 0,
inst->mctx));
@@ -494,24 +552,16 @@ synthrecord_setconfigmode(synthrecord_t *inst, const dns_name_t *zname) {
static isc_result_t
synthrecord_parseallowsynth(synthrecord_t *inst, const cfg_obj_t *cfg,
cfg_aclconfctx_t *aclctx,
- const cfg_obj_t *synthrecordcfg) {
- isc_result_t result;
- const cfg_obj_t *obj = NULL;
-
+ const synthrecord_params_t *params) {
INSIST(inst->allowedsynth == NULL);
- result = cfg_map_get(synthrecordcfg, "allow-synth", &obj);
- if (result == ISC_R_NOTFOUND) {
+ if (params->allowsynth == NULL) {
dns_acl_any(inst->mctx, &inst->allowedsynth);
return ISC_R_SUCCESS;
}
- if (result != ISC_R_SUCCESS) {
- return result;
- }
-
- RETERR(cfg_acl_fromconfig(obj, cfg, aclctx, inst->mctx, 0,
- &inst->allowedsynth));
+ RETERR(cfg_acl_fromconfig(params->allowsynth, cfg, aclctx, inst->mctx,
+ 0, &inst->allowedsynth));
for (unsigned int i = 0; i < inst->allowedsynth->length; i++) {
switch (inst->allowedsynth->elements[i].type) {
@@ -528,60 +578,39 @@ synthrecord_parseallowsynth(synthrecord_t *inst, const cfg_obj_t *cfg,
return ISC_R_UNEXPECTED;
}
}
- return result;
-}
-
-static isc_result_t
-synthrecord_parsettl(synthrecord_t *inst, const cfg_obj_t *synthrecordcfg) {
- isc_result_t result;
- const cfg_obj_t *obj = NULL;
-
- result = cfg_map_get(synthrecordcfg, "ttl", &obj);
-
- if (result == ISC_R_NOTFOUND) {
- inst->ttl = DEFAULT_TTL;
- result = ISC_R_SUCCESS;
- } else if (result == ISC_R_SUCCESS) {
- inst->ttl = cfg_obj_asuint32(obj);
- }
-
- return result;
+ return ISC_R_SUCCESS;
}
static isc_result_t
-synthrecord_parseconfig(synthrecord_t *inst, const char *parameters,
+synthrecord_parseconfig(synthrecord_t *inst, const char *const *parameters,
const cfg_obj_t *cfg, const char *cfgfile,
unsigned long cfgline, cfg_aclconfctx_t *aclctx,
const dns_name_t *zname) {
isc_result_t result;
- cfg_obj_t *synthrecordcfg = NULL;
- isc_buffer_t b;
-
- isc_buffer_constinit(&b, parameters, strlen(parameters));
- isc_buffer_add(&b, strlen(parameters));
+ synthrecord_params_t params;
- CHECK(cfg_parse_buffer(&b, cfgfile, cfgline, &synthrecord_cfgparams, 0,
- &synthrecordcfg));
+ CHECK(synthrecord_parseparams(parameters, cfgfile, cfgline, ¶ms));
synthrecord_setconfigmode(inst, zname);
- CHECK(synthrecord_initorigin(inst, synthrecordcfg, zname));
- CHECK(synthrecord_initprefix(inst, synthrecordcfg));
- CHECK(synthrecord_parseallowsynth(inst, cfg, aclctx, synthrecordcfg));
- CHECK(synthrecord_parsettl(inst, synthrecordcfg));
+ CHECK(synthrecord_initorigin(inst, ¶ms, zname));
+ CHECK(synthrecord_initprefix(inst, ¶ms));
+ CHECK(synthrecord_parseallowsynth(inst, cfg, aclctx, ¶ms));
+ inst->ttl = params.ttl;
cleanup:
- if (synthrecordcfg != NULL) {
- cfg_obj_detach(&synthrecordcfg);
+ cfg_tokens_clear(¶ms.tokens);
+ if (params.allowsynth != NULL) {
+ cfg_obj_detach(¶ms.allowsynth);
}
return result;
}
isc_result_t
-plugin_register(const char *parameters, const void *cfg, const char *cfgfile,
- unsigned long cfgline, isc_mem_t *mctx, void *aclctx,
- ns_hooktable_t *hooktable, const ns_pluginctx_t *ctx,
- void **instp) {
+plugin_register(const char *const *parameters, const void *cfg,
+ const char *cfgfile, unsigned long cfgline, isc_mem_t *mctx,
+ void *aclctx, ns_hooktable_t *hooktable,
+ const ns_pluginctx_t *ctx, void **instp) {
synthrecord_t *inst = NULL;
ns_hook_t hook;
isc_result_t result;
@@ -617,9 +646,9 @@ plugin_register(const char *parameters, const void *cfg, const char *cfgfile,
}
isc_result_t
-plugin_check(const char *parameters, const void *cfg, const char *cfgfile,
- unsigned long cfgline, isc_mem_t *mctx, void *aclctx,
- const ns_pluginctx_t *ctx) {
+plugin_check(const char *const *parameters, const void *cfg,
+ const char *cfgfile, unsigned long cfgline, isc_mem_t *mctx,
+ void *aclctx, const ns_pluginctx_t *ctx) {
isc_result_t result;
synthrecord_t *inst = NULL;
diff --git a/bin/tests/system/checkconf/good.conf.j2 b/bin/tests/system/checkconf/good.conf.j2
index 675ef1dc5b..a7ab3d9ed6 100644
--- a/bin/tests/system/checkconf/good.conf.j2
+++ b/bin/tests/system/checkconf/good.conf.j2
@@ -269,9 +269,9 @@ view "fifth" {
};
dyndb "name" "library.so" {
this;
- \};
+ "}";
is a {
- "test" { \{ of; the; };
+ test { "{" of; the; };
} bracketed;
"text \"";
system;
diff --git a/bin/tests/system/dyndb/driver/driver.c b/bin/tests/system/dyndb/driver/driver.c
index 7c2a65c759..a5f604176f 100644
--- a/bin/tests/system/dyndb/driver/driver.c
+++ b/bin/tests/system/dyndb/driver/driver.c
@@ -42,7 +42,6 @@
* it is undesirable.
*/
-#include <isc/commandline.h>
#include <isc/hash.h>
#include <isc/mem.h>
#include <isc/util.h>
@@ -51,6 +50,8 @@
#include <dns/dyndb.h>
#include <dns/types.h>
+#include <isccfg/tokens.h>
+
#include "db.h"
#include "instance.h"
#include "log.h"
@@ -73,9 +74,10 @@ dns_dyndb_version_t dyndb_version;
* @param[in] name User-defined string from dyndb "name" {}; definition
* in named.conf.
* The example above will have name = "example-name".
- * @param[in] parameters User-defined parameters from dyndb section as one
- * string. The example above will have
- * params = "param1 param2";
+ * @param[in] parameters User-defined parameters from dyndb section as a
+ * token array (see <isccfg/tokens.h>). The example
+ * above will have
+ * parameters = { "param1", "param2", NULL };
* @param[in] file The name of the file from which the parameters
* were read.
* @param[in] line The line number from which the parameters were read.
@@ -83,26 +85,29 @@ dns_dyndb_version_t dyndb_version;
* (for one dyndb section).
*/
isc_result_t
-dyndb_init(isc_mem_t *mctx, const char *name, const char *parameters,
+dyndb_init(isc_mem_t *mctx, const char *name, const char *const *parameters,
const char *file, unsigned long line, const dns_dyndbctx_t *dctx,
void **instp) {
- isc_result_t result;
- unsigned int argc;
+ isc_result_t result = ISC_R_SUCCESS;
+ unsigned int argc = 0, size = 0;
char **argv = NULL;
- char *s = NULL;
+ const char *token = NULL;
+ cfg_tokens_t tok;
sample_instance_t *sample_inst = NULL;
REQUIRE(name != NULL);
REQUIRE(dctx != NULL);
- s = isc_mem_strdup(mctx, parameters);
+ /* Every parameter is a plain string: build argv from them. */
+ for (const char *const *t = parameters; *t != NULL; t++) {
+ size++;
+ }
+ argv = isc_mem_cget(mctx, size + 1, sizeof(*argv));
- result = isc_commandline_strtoargv(mctx, s, &argc, &argv, 0);
- if (result != ISC_R_SUCCESS) {
- log_write(ISC_LOG_ERROR,
- "dyndb_init: isc_commandline_strtoargv -> %s\n",
- isc_result_totext(result));
- goto cleanup;
+ cfg_tokens_init(&tok, parameters, file, line);
+ while (cfg_tokens_peek(&tok) != NULL) {
+ CHECK(cfg_tokens_getstring(&tok, &token));
+ argv[argc++] = UNCONST(token);
}
log_write(ISC_LOG_DEBUG(9), "loading params for dyndb '%s' from %s:%lu",
@@ -133,10 +138,8 @@ dyndb_init(isc_mem_t *mctx, const char *name, const char *parameters,
*instp = sample_inst;
cleanup:
- isc_mem_free(mctx, s);
- if (argv != NULL) {
- isc_mem_cput(mctx, argv, argc, sizeof(*argv));
- }
+ cfg_tokens_clear(&tok);
+ isc_mem_cput(mctx, argv, size + 1, sizeof(*argv));
return result;
}
diff --git a/bin/tests/system/filters/conf/good-quoted-acls.conf.j2 b/bin/tests/system/filters/conf/good-quoted-acls.conf.j2
new file mode 100644
index 0000000000..46d7a1508e
--- /dev/null
+++ b/bin/tests/system/filters/conf/good-quoted-acls.conf.j2
@@ -0,0 +1,14 @@
+acl "key" { localhost; };
+acl "geoip" { localhost; };
+acl "192.0.2.1" { localhost; };
+acl "::1" { localhost; };
+
+plugin query "@TOP_BUILDDIR@/filter-a.@DYLIB@" {
+ filter-a { "key"; "geoip"; "192.0.2.1"; "::1"; };
+ filter-a-on-v4 "yes";
+};
+
+plugin query "@TOP_BUILDDIR@/filter-aaaa.@DYLIB@" {
+ filter-aaaa { "key"; "geoip"; "192.0.2.1"; "::1"; };
+ filter-aaaa-on-v4 "yes";
+};
diff --git a/bin/tests/system/hooks/driver/test-async.c b/bin/tests/system/hooks/driver/test-async.c
index af140e39ba..15aab8923b 100644
--- a/bin/tests/system/hooks/driver/test-async.c
+++ b/bin/tests/system/hooks/driver/test-async.c
@@ -115,10 +115,10 @@ logmsg(const char *fmt, ...) {
* register hook functions into the view hook table.
*/
isc_result_t
-plugin_register(const char *parameters, const void *cfg, const char *cfg_file,
- unsigned long cfg_line, isc_mem_t *mctx, void *aclctx,
- ns_hooktable_t *hooktable, const ns_pluginctx_t *ctx,
- void **instp) {
+plugin_register(const char *const *parameters, const void *cfg,
+ const char *cfg_file, unsigned long cfg_line, isc_mem_t *mctx,
+ void *aclctx, ns_hooktable_t *hooktable,
+ const ns_pluginctx_t *ctx, void **instp) {
async_instance_t *inst = NULL;
UNUSED(parameters);
@@ -148,9 +148,9 @@ plugin_register(const char *parameters, const void *cfg, const char *cfg_file,
}
isc_result_t
-plugin_check(const char *parameters, const void *cfg, const char *cfg_file,
- unsigned long cfg_line, isc_mem_t *mctx, void *aclctx,
- const ns_pluginctx_t *ctx) {
+plugin_check(const char *const *parameters, const void *cfg,
+ const char *cfg_file, unsigned long cfg_line, isc_mem_t *mctx,
+ void *aclctx, const ns_pluginctx_t *ctx) {
UNUSED(parameters);
UNUSED(cfg);
UNUSED(cfg_file);
diff --git a/bin/tests/system/hooks/driver/test-syncplugin.c b/bin/tests/system/hooks/driver/test-syncplugin.c
index 6eef90e4ef..2248fd93b6 100644
--- a/bin/tests/system/hooks/driver/test-syncplugin.c
+++ b/bin/tests/system/hooks/driver/test-syncplugin.c
@@ -13,7 +13,7 @@
#include <isccfg/aclconf.h>
#include <isccfg/cfg.h>
-#include <isccfg/grammar.h>
+#include <isccfg/tokens.h>
#include <ns/hooks.h>
@@ -58,30 +58,51 @@ syncplugin__hook(void *arg, void *cbdata, isc_result_t *resp) {
return NS_HOOK_RETURN;
}
-static cfg_clausedef_t syncplugin__cfgclauses[] = {
- { "rcode", &cfg_type_astring, 0, NULL },
- { "source", &cfg_type_astring, 0, NULL },
- { "firstlbl", &cfg_type_qstring, CFG_CLAUSEFLAG_OPTIONAL, NULL }
-};
+typedef struct {
+ cfg_tokens_t tokens;
+ const char *rcode;
+ const char *source;
+ const char *firstlbl;
+} syncplugin_params_t;
-static cfg_clausedef_t *syncplugin__cfgparamsclausesets[] = {
- syncplugin__cfgclauses, NULL
-};
+static isc_result_t
+syncplugin__parse_params(const char *const *parameters, const char *cfgfile,
+ unsigned long cfgline, syncplugin_params_t *params) {
+ cfg_tokens_t *tok = ¶ms->tokens;
+
+ *params = (syncplugin_params_t){ 0 };
+
+ cfg_tokens_init(tok, parameters, cfgfile, cfgline);
+ while (cfg_tokens_peek(tok) != NULL) {
+ const char *name = NULL;
+ const char **valuep = NULL;
+
+ RETERR(cfg_tokens_getstring(tok, &name));
+ if (strcmp(name, "rcode") == 0) {
+ valuep = ¶ms->rcode;
+ } else if (strcmp(name, "source") == 0) {
+ valuep = ¶ms->source;
+ } else if (strcmp(name, "firstlbl") == 0) {
+ valuep = ¶ms->firstlbl;
+ } else {
+ cfg_tokens_log(tok, ISC_LOG_ERROR,
+ "unknown option '%s'", name);
+ return ISC_R_FAILURE;
+ }
+ RETERR(cfg_tokens_getstring(tok, valuep));
+ RETERR(cfg_tokens_expect(tok, CFG_TOKEN_END));
+ }
-static cfg_type_t syncplugin__cfgparams = {
- "syncplugin-params", cfg_parse_mapbody, cfg_print_mapbody,
- cfg_doc_mapbody, &cfg_rep_map, syncplugin__cfgparamsclausesets
-};
+ if (params->rcode == NULL || params->source == NULL) {
+ return ISC_R_NOTFOUND;
+ }
+
+ return ISC_R_SUCCESS;
+}
static isc_result_t
-syncplugin__parse_rcode(const cfg_obj_t *syncplugincfg, uint8_t *rcode) {
+syncplugin__parse_rcode(const char *rcodestr, uint8_t *rcode) {
isc_result_t result = ISC_R_SUCCESS;
- const cfg_obj_t *obj = NULL;
- const char *rcodestr = NULL;
-
- RETERR(cfg_map_get(syncplugincfg, "rcode", &obj));
-
- rcodestr = obj->value.string;
if (strcmp("servfail", rcodestr) == 0) {
*rcode = dns_rcode_servfail;
@@ -101,17 +122,15 @@ syncplugin__parse_rcode(const cfg_obj_t *syncplugincfg, uint8_t *rcode) {
}
isc_result_t
-plugin_register(const char *parameters, const void *cfg, const char *cfgfile,
- unsigned long cfgline, isc_mem_t *mctx, void *aclctx,
- ns_hooktable_t *hooktable, const ns_pluginctx_t *ctx,
- void **instp) {
+plugin_register(const char *const *parameters, const void *cfg,
+ const char *cfgfile, unsigned long cfgline, isc_mem_t *mctx,
+ void *aclctx, ns_hooktable_t *hooktable,
+ const ns_pluginctx_t *ctx, void **instp) {
isc_result_t result;
- cfg_obj_t *syncplugincfg = NULL;
- const cfg_obj_t *obj = NULL;
- isc_buffer_t b;
+ syncplugin_params_t params;
ns_hook_t hook;
syncplugin_t *inst = NULL;
- char *sourcestr = NULL;
+ const char *sourcestr = NULL;
dns_name_t example2com = DNS_NAME_INITEMPTY;
dns_name_t example3com = DNS_NAME_INITEMPTY;
dns_name_t example4com = DNS_NAME_INITEMPTY;
@@ -124,25 +143,18 @@ plugin_register(const char *parameters, const void *cfg, const char *cfgfile,
*inst = (syncplugin_t){ .mctx = mctx };
*instp = inst;
- isc_buffer_constinit(&b, parameters, strlen(parameters));
- isc_buffer_add(&b, strlen(parameters));
+ CHECK(syncplugin__parse_params(parameters, cfgfile, cfgline, ¶ms));
- CHECK(cfg_parse_buffer(&b, cfgfile, cfgline, &syncplugin__cfgparams, 0,
- &syncplugincfg));
+ CHECK(syncplugin__parse_rcode(params.rcode, &inst->rcode));
- CHECK(syncplugin__parse_rcode(syncplugincfg, &inst->rcode));
-
- if (cfg_map_get(syncplugincfg, "firstlbl", &obj) == ISC_R_SUCCESS) {
- const char *firstlbl = cfg_obj_asstring(obj);
- size_t len = strlen(firstlbl) + 1;
+ if (params.firstlbl != NULL) {
+ size_t len = strlen(params.firstlbl) + 1;
inst->firstlbl = isc_mem_allocate(mctx, len);
- strncpy(inst->firstlbl, firstlbl, len);
+ strncpy(inst->firstlbl, params.firstlbl, len);
}
- obj = NULL;
- CHECK(cfg_map_get(syncplugincfg, "source", &obj));
- sourcestr = obj->value.string;
+ sourcestr = params.source;
if (strcmp(sourcestr, "zone") == 0) {
if (ctx->source != NS_HOOKSOURCE_ZONE) {
@@ -187,6 +199,7 @@ plugin_register(const char *parameters, const void *cfg, const char *cfgfile,
ns_hook_add(hooktable, mctx, NS_QUERY_NXDOMAIN_BEGIN, &hook);
cleanup:
+ cfg_tokens_clear(¶ms.tokens);
if (dns_name_dynamic(&example2com)) {
dns_name_free(&example2com, isc_g_mctx);
}
@@ -199,17 +212,13 @@ cleanup:
dns_name_free(&example4com, isc_g_mctx);
}
- if (syncplugincfg != NULL) {
- cfg_obj_detach(&syncplugincfg);
- }
-
return result;
}
isc_result_t
-plugin_check(const char *parameters, const void *cfg, const char *cfgfile,
- unsigned long cfgline, isc_mem_t *mctx, void *aclctx,
- const ns_pluginctx_t *ctx) {
+plugin_check(const char *const *parameters, const void *cfg,
+ const char *cfgfile, unsigned long cfgline, isc_mem_t *mctx,
+ void *aclctx, const ns_pluginctx_t *ctx) {
UNUSED(parameters);
UNUSED(cfg);
UNUSED(cfgfile);
diff --git a/bin/tests/system/synthrecord/ns1/named.conf.j2 b/bin/tests/system/synthrecord/ns1/named.conf.j2
index 798af6b747..9a116cf093 100644
--- a/bin/tests/system/synthrecord/ns1/named.conf.j2
+++ b/bin/tests/system/synthrecord/ns1/named.conf.j2
@@ -42,7 +42,7 @@ zone "example" {
::/16;
};
origin example.;
- ttl 3600;
+ ttl 0xe10; // 3600 in hexadecimal
};
};
@@ -59,7 +59,7 @@ zone "0.53.10.in-addr.arpa." {
10.53.0.0/24;
};
origin example.;
- ttl 3600;
+ ttl 07020; // 3600 in octal
};
};
diff --git a/lib/dns/dyndb.c b/lib/dns/dyndb.c
index b7feeba33b..6ce26d0d19 100644
--- a/lib/dns/dyndb.c
+++ b/lib/dns/dyndb.c
@@ -195,8 +195,9 @@ unload_library(dyndb_implementation_t **impp) {
}
isc_result_t
-dns_dyndb_load(const char *libname, const char *name, const char *parameters,
- const char *file, unsigned long line, isc_mem_t *mctx,
+dns_dyndb_load(const char *libname, const char *name,
+ const char *const *parameters, const char *file,
+ unsigned long line, isc_mem_t *mctx,
const dns_dyndbctx_t *dctx) {
isc_result_t result;
dyndb_implementation_t *implementation = NULL;
diff --git a/lib/dns/include/dns/dyndb.h b/lib/dns/include/dns/dyndb.h
index 64919af80a..113988355d 100644
--- a/lib/dns/include/dns/dyndb.h
+++ b/lib/dns/include/dns/dyndb.h
@@ -57,13 +57,15 @@ struct dns_dyndbctx {
#endif /* ifndef DNS_DYNDB_VERSION */
typedef isc_result_t
-dns_dyndb_register_t(isc_mem_t *mctx, const char *name, const char *parameters,
- const char *file, unsigned long line,
- const dns_dyndbctx_t *dctx, void **instp);
+dns_dyndb_register_t(isc_mem_t *mctx, const char *name,
+ const char *const *parameters, const char *file,
+ unsigned long line, const dns_dyndbctx_t *dctx,
+ void **instp);
/*%
* Called when registering a new driver instance. 'name' must be unique.
- * 'parameters' contains the driver configuration text. 'dctx' is the
- * initialization context set up in dns_dyndb_createctx().
+ * 'parameters' contains the driver configuration as a NULL-terminated
+ * token array (see <isccfg/tokens.h>). 'dctx' is the initialization
+ * context set up in dns_dyndb_createctx().
*
* '*instp' will be set to the driver instance handle if the function
* is successful.
@@ -96,9 +98,9 @@ dns_dyndb_version_t(unsigned int *flags);
*/
isc_result_t
-dns_dyndb_load(const char *libname, const char *name, const char *parameters,
- const char *file, unsigned long line, isc_mem_t *mctx,
- const dns_dyndbctx_t *dctx);
+dns_dyndb_load(const char *libname, const char *name,
+ const char *const *parameters, const char *file,
+ unsigned long line, isc_mem_t *mctx, const dns_dyndbctx_t *dctx);
/*%
* Load a dyndb module.
*
diff --git a/lib/isccfg/check.c b/lib/isccfg/check.c
index ce2f888ff1..34de1e5bfe 100644
--- a/lib/isccfg/check.c
+++ b/lib/isccfg/check.c
@@ -3224,7 +3224,7 @@ struct check_one_plugin_data {
static isc_result_t
check_one_plugin(const cfg_obj_t *config, const cfg_obj_t *obj,
cfg_aclconfctx_t *aclctx, const char *plugin_path,
- const char *parameters, void *callback_data) {
+ const char *const *parameters, void *callback_data) {
struct check_one_plugin_data *data = callback_data;
char full_path[PATH_MAX];
isc_result_t result = ISC_R_SUCCESS;
diff --git a/lib/isccfg/include/isccfg/cfg.h b/lib/isccfg/include/isccfg/cfg.h
index 73c7a926eb..3493cda7ef 100644
--- a/lib/isccfg/include/isccfg/cfg.h
+++ b/lib/isccfg/include/isccfg/cfg.h
@@ -359,6 +359,22 @@ cfg_obj_asstring(const cfg_obj_t *obj);
* \li A pointer to a null terminated string.
*/
+bool
+cfg_obj_istokens(const cfg_obj_t *obj);
+/*%<
+ * Return true iff 'obj' is a bracketed token list.
+ */
+
+const char *const *
+cfg_obj_astokens(const cfg_obj_t *obj);
+/*%<
+ * Returns the value of a bracketed token list as a NULL-terminated
+ * array of tokens, encoded as described in <isccfg/tokens.h>.
+ *
+ * Requires:
+ * \li 'obj' points to a valid bracketed token list object.
+ */
+
bool
cfg_obj_isboolean(const cfg_obj_t *obj);
/*%<
@@ -569,9 +585,12 @@ cfg_map_nextclause(const cfg_type_t *map, const void **clauses,
const cfg_clausedef_t *
cfg_map_findclause(const cfg_type_t *map, const char *name);
-typedef isc_result_t(pluginlist_cb_t)(
- const cfg_obj_t *config, const cfg_obj_t *obj, cfg_aclconfctx_t *aclctx,
- const char *plugin_path, const char *parameters, void *callback_data);
+typedef isc_result_t(pluginlist_cb_t)(const cfg_obj_t *config,
+ const cfg_obj_t *obj,
+ cfg_aclconfctx_t *aclctx,
+ const char *plugin_path,
+ const char *const *parameters,
+ void *callback_data);
/*%<
* Function prototype for the callback used with cfg_pluginlist_foreach().
* Called once for each element of the list passed to cfg_pluginlist_foreach().
@@ -581,7 +600,8 @@ typedef isc_result_t(pluginlist_cb_t)(
* \li 'config' - the 'config' object passed to cfg_pluginlist_foreach()
* \li 'obj' - object representing the specific "plugin" stanza to be processed
* \li 'plugin_path' - path to the shared object with plugin code
- * \li 'parameters' - configuration text for the plugin
+ * \li 'parameters' - configuration tokens for the plugin (see
+ * <isccfg/tokens.h>), or NULL if none were given
* \li 'callback_data' - the pointer passed to cfg_pluginlist_foreach()
*/
diff --git a/lib/isccfg/include/isccfg/grammar.h b/lib/isccfg/include/isccfg/grammar.h
index 1046735ae2..74b73991fc 100644
--- a/lib/isccfg/include/isccfg/grammar.h
+++ b/lib/isccfg/include/isccfg/grammar.h
@@ -228,6 +228,7 @@ struct cfg_obj {
uint32_t uint32;
uint64_t uint64;
char *string; /*%< null terminated */
+ char **tokens; /*%< see <isccfg/tokens.h> */
bool boolean;
cfg_map_t *map;
cfg_list_t *list;
@@ -316,6 +317,7 @@ struct cfg_parser {
extern cfg_rep_t cfg_rep_uint32;
extern cfg_rep_t cfg_rep_uint64;
extern cfg_rep_t cfg_rep_string;
+extern cfg_rep_t cfg_rep_tokens;
extern cfg_rep_t cfg_rep_boolean;
extern cfg_rep_t cfg_rep_map;
extern cfg_rep_t cfg_rep_list;
@@ -341,8 +343,8 @@ extern cfg_type_t cfg_type_astring;
extern cfg_type_t cfg_type_ustring;
extern cfg_type_t cfg_type_sstring;
extern cfg_type_t cfg_type_bracketed_aml;
-extern cfg_type_t cfg_type_bracketed_text;
-extern cfg_type_t cfg_type_optional_bracketed_text;
+extern cfg_type_t cfg_type_bracketed_tokens;
+extern cfg_type_t cfg_type_optional_bracketed_tokens;
extern cfg_type_t cfg_type_keyref;
extern cfg_type_t cfg_type_sockaddr;
extern cfg_type_t cfg_type_sockaddrtls;
@@ -507,6 +509,15 @@ void
cfg_print_cstr(cfg_printer_t *pctx, const char *s);
/*%< Print the null-terminated string 's' */
+void
+cfg_print_tokens(cfg_printer_t *pctx, const char *const *tokens,
+ const char *const *end);
+/*%<
+ * Print the token array 'tokens' (see <isccfg/tokens.h>) as a
+ * bracketed block, stopping at 'end' or at the terminating NULL,
+ * whichever comes first. 'end' may be NULL.
+ */
+
isc_result_t
cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
diff --git a/lib/isccfg/include/isccfg/tokens.h b/lib/isccfg/include/isccfg/tokens.h
new file mode 100644
index 0000000000..62af5ef769
--- /dev/null
+++ b/lib/isccfg/include/isccfg/tokens.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file
+ * \brief
+ * Bracketed token lists.
+ *
+ * The contents of the "{ ... }" block of a "plugin" or "dyndb" statement
+ * are lexed, but not parsed, by named. They are handed to the plugin or
+ * driver as a NULL-terminated array of tokens, in which:
+ *
+ * \li CFG_TOKEN_OPEN, CFG_TOKEN_CLOSE, CFG_TOKEN_END, CFG_TOKEN_SLASH
+ * and CFG_TOKEN_BANG stand for '{', '}', ';', '/' and '!' respectively;
+ * \li CFG_TOKEN_NEWLINE marks a line break in the original text, so
+ * that line numbers can be tracked;
+ * \li every other element preserves the token's original spelling,
+ * including quotes and escapes. For example, "10.0.0.0/8" is three
+ * tokens: "10.0.0.0", CFG_TOKEN_SLASH and "8".
+ *
+ * The outer brackets are not included. The cfg_tokens_*() functions
+ * provide a cursor to walk such an array.
+ */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/formatcheck.h>
+#include <isc/types.h>
+
+#include <isccfg/cfg.h>
+
+#define CFG_TOKEN_OPEN ((const char *)(uintptr_t)-1)
+#define CFG_TOKEN_NEWLINE ((const char *)(uintptr_t)-2)
+#define CFG_TOKEN_CLOSE ((const char *)(uintptr_t)-3)
+#define CFG_TOKEN_END ((const char *)(uintptr_t)-4)
+#define CFG_TOKEN_SLASH ((const char *)(uintptr_t)-5)
+#define CFG_TOKEN_BANG ((const char *)(uintptr_t)-6)
+
+/*%
+ * True if the (non-NULL) token 't' is a string rather than one of the
+ * CFG_TOKEN_* markers.
+ */
+#define CFG_TOKEN_ISSTRING(t) ((uintptr_t)(t) < (uintptr_t)CFG_TOKEN_BANG)
+
+typedef struct cfg_tokenstring cfg_tokenstring_t;
+
+typedef struct cfg_tokens {
+ const char *const *next;
+ const char *file;
+ unsigned long line;
+ cfg_tokenstring_t *strings;
+} cfg_tokens_t;
+
+void
+cfg_tokens_init(cfg_tokens_t *tok, const char *const *tokens, const char *file,
+ unsigned long line);
+/*%<
+ * Initialize the cursor 'tok' to walk 'tokens'. 'file' and 'line' are
+ * the location of the opening bracket, and are used for logging.
+ * 'tokens' may be NULL, which is treated as an empty list.
+ */
+
+void
+cfg_tokens_clear(cfg_tokens_t *tok);
+/*%<
+ * Release decoded strings owned by the cursor. Call after finishing
+ * with the cursor and all values returned by cfg_tokens_getstring().
+ */
+
+const char *
+cfg_tokens_peek(cfg_tokens_t *tok);
+/*%<
+ * Return the next token without consuming it, skipping (and counting)
+ * line breaks. Returns NULL at the end of the list.
+ */
+
+const char *
+cfg_tokens_next(cfg_tokens_t *tok);
+/*%<
+ * Like cfg_tokens_peek(), but consume the token.
+ */
+
+isc_result_t
+cfg_tokens_expect(cfg_tokens_t *tok, const char *marker);
+/*%<
+ * Consume the next token, which must be 'marker' (one of CFG_TOKEN_OPEN,
+ * CFG_TOKEN_CLOSE, CFG_TOKEN_END, CFG_TOKEN_SLASH or CFG_TOKEN_BANG).
+ * Logs an error if it is not.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li ISC_R_UNEXPECTEDTOKEN
+ * \li ISC_R_UNEXPECTEDEND
+ */
+
+isc_result_t
+cfg_tokens_getstring(cfg_tokens_t *tok, const char **strp);
+/*%<
+ * Consume the next token, which must be a string, and store it in
+ * '*strp'. Quoted strings are decoded using the configuration grammar.
+ * The result is valid until cfg_tokens_clear() or the token array is
+ * freed. Logs an error if the token is not a string.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li ISC_R_UNEXPECTEDTOKEN
+ * \li ISC_R_UNEXPECTEDEND
+ */
+
+isc_result_t
+cfg_tokens_getaml(cfg_tokens_t *tok, cfg_obj_t **objp);
+/*%<
+ * Consume a bracketed address match list ("{ ... }") and parse it into
+ * a configuration object suitable for cfg_acl_fromconfig(). The
+ * resulting object carries the file and line the list was found at.
+ *
+ * Requires:
+ * \li 'objp' is not NULL and '*objp' is NULL.
+ */
+
+void
+cfg_tokens_log(const cfg_tokens_t *tok, int level, const char *fmt, ...)
+ ISC_FORMAT_PRINTF(3, 4);
+/*%<
+ * Log a message prefixed with the current file and line of 'tok'.
+ */
diff --git a/lib/isccfg/meson.build b/lib/isccfg/meson.build
index 1d799e3194..87dc349f40 100644
--- a/lib/isccfg/meson.build
+++ b/lib/isccfg/meson.build
@@ -17,5 +17,6 @@ isccfg_srcset.add(
'kaspconf.c',
'namedconf.c',
'parser.c',
+ 'tokens.c',
),
)
diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c
index 7331c7b870..a2567befbd 100644
--- a/lib/isccfg/namedconf.c
+++ b/lib/isccfg/namedconf.c
@@ -2129,7 +2129,8 @@ static cfg_tuplefielddef_t rpz_fields[] = {
{ "nsip-enable", &cfg_type_boolean, 0 },
{ "nsdname-enable", &cfg_type_boolean, 0 },
{ "dnsrps-enable", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
- { "dnsrps-options", &cfg_type_bracketed_text, CFG_CLAUSEFLAG_OBSOLETE },
+ { "dnsrps-options", &cfg_type_bracketed_tokens,
+ CFG_CLAUSEFLAG_OBSOLETE },
{ NULL, NULL, 0 }
};
static cfg_type_t cfg_type_rpz = { "rpz",
@@ -2383,7 +2384,7 @@ static cfg_clausedef_t view_clauses[] = {
{ "dns64-contact", &cfg_type_astring, 0, NULL },
{ "dns64-server", &cfg_type_astring, 0, NULL },
{ "dnsrps-enable", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE, NULL },
- { "dnsrps-options", &cfg_type_bracketed_text, CFG_CLAUSEFLAG_OBSOLETE,
+ { "dnsrps-options", &cfg_type_bracketed_tokens, CFG_CLAUSEFLAG_OBSOLETE,
NULL },
{ "dnssec-accept-expired", &cfg_type_boolean, 0, NULL },
{ "dnssec-enable", NULL, CFG_CLAUSEFLAG_ANCIENT, NULL },
@@ -2944,7 +2945,7 @@ static cfg_type_t cfg_type_dlz = { "dlz", cfg_parse_named_map,
static cfg_tuplefielddef_t dyndb_fields[] = {
{ "name", &cfg_type_astring, 0 },
{ "library", &cfg_type_qstring, 0 },
- { "parameters", &cfg_type_bracketed_text, 0 },
+ { "parameters", &cfg_type_bracketed_tokens, 0 },
{ NULL, NULL, 0 }
};
@@ -2964,7 +2965,7 @@ static cfg_type_t cfg_type_plugintype = { "plugintype", cfg_parse_enum,
static cfg_tuplefielddef_t plugin_fields[] = {
{ "type", &cfg_type_plugintype, 0 },
{ "library", &cfg_type_astring, 0 },
- { "parameters", &cfg_type_optional_bracketed_text, 0 },
+ { "parameters", &cfg_type_optional_bracketed_tokens, 0 },
{ NULL, NULL, 0 }
};
static cfg_type_t cfg_type_plugin = { "plugin", cfg_parse_tuple,
diff --git a/lib/isccfg/parser.c b/lib/isccfg/parser.c
index 79bde3e1b8..306e5dc449 100644
--- a/lib/isccfg/parser.c
+++ b/lib/isccfg/parser.c
@@ -67,6 +67,7 @@
#include <isccfg/cfg.h>
#include <isccfg/grammar.h>
+#include <isccfg/tokens.h>
/*
* cfg_obj_t is used _a lot_ when building the configuration tree, which
@@ -121,6 +122,9 @@ create_list(cfg_obj_t *file, size_t line, const cfg_type_t *type,
static void
free_string(cfg_obj_t *obj);
+static void
+free_tokens(cfg_obj_t *obj);
+
static void
free_sockaddr(cfg_obj_t *obj);
@@ -233,6 +237,24 @@ copy_string(cfg_obj_t *to, const cfg_obj_t *from) {
to->value.string = isc_mem_strdup(isc_g_mctx, from->value.string);
}
+static void
+copy_tokens(cfg_obj_t *to, const cfg_obj_t *from) {
+ size_t n = 0;
+
+ while (from->value.tokens[n] != NULL) {
+ n++;
+ }
+ to->value.tokens = isc_mem_allocate(isc_g_mctx,
+ (n + 1) * sizeof(char *));
+ for (size_t i = 0; i < n; i++) {
+ char *t = from->value.tokens[i];
+ to->value.tokens[i] = CFG_TOKEN_ISSTRING(t)
+ ? isc_mem_strdup(isc_g_mctx, t)
+ : t;
+ }
+ to->value.tokens[n] = NULL;
+}
+
static void
copy_map_destroy(char *key ISC_ATTR_UNUSED, unsigned int type ISC_ATTR_UNUSED,
isc_symvalue_t symval, void *arg ISC_ATTR_UNUSED) {
@@ -333,6 +355,7 @@ copy_noop(cfg_obj_t *to ISC_ATTR_UNUSED,
cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop, copy_uint32 };
cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop, copy_uint64 };
cfg_rep_t cfg_rep_string = { "string", free_string, copy_string };
+cfg_rep_t cfg_rep_tokens = { "tokens", free_tokens, copy_tokens };
cfg_rep_t cfg_rep_boolean = { "boolean", free_noop, copy_boolean };
cfg_rep_t cfg_rep_map = { "map", free_map, copy_map };
cfg_rep_t cfg_rep_list = { "list", free_list, copy_list };
@@ -1473,44 +1496,190 @@ cleanup:
return result;
}
+static void
+tokens_append(cfg_obj_t *obj, size_t *countp, size_t *sizep, char *token) {
+ if (*countp + 1 >= *sizep) {
+ *sizep = (*sizep == 0) ? 16 : *sizep * 2;
+ obj->value.tokens = isc_mem_reallocate(
+ isc_g_mctx, obj->value.tokens,
+ *sizep * sizeof(obj->value.tokens[0]));
+ }
+ obj->value.tokens[(*countp)++] = token;
+ obj->value.tokens[*countp] = NULL;
+}
+
static isc_result_t
-parse_btext(cfg_parser_t *pctx, const cfg_type_t *type ISC_ATTR_UNUSED,
- cfg_obj_t **ret) {
+parse_btokens(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ size_t count = 0, size = 0;
+ unsigned int depth = 0;
- CHECK(cfg_gettoken(pctx, ISC_LEXOPT_BTEXT));
- if (pctx->token.type != isc_tokentype_btext) {
- cfg_parser_error(pctx, CFG_LOG_NEAR, "expected bracketed text");
+ CHECK(cfg_gettoken(pctx, 0));
+ if (pctx->token.type != isc_tokentype_special ||
+ pctx->token.value.as_char != '{')
+ {
+ cfg_parser_error(pctx, CFG_LOG_NEAR, "expected '{'");
return ISC_R_UNEXPECTEDTOKEN;
}
- cfg_string_create(pctx, TOKEN_STRING(pctx), &cfg_type_bracketed_text,
- ret);
- return ISC_R_SUCCESS;
+
+ /*
+ * Create the object now, so that it carries the line number of
+ * the opening bracket. Start with an empty, NULL-terminated array.
+ */
+ cfg_obj_create(cfg_parser_currentfile(pctx), pctx->line, type, &obj);
+ obj->value.tokens = NULL;
+ tokens_append(obj, &count, &size, NULL);
+ count = 0;
+
+ for (;;) {
+ char *token = NULL;
+
+ CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING | ISC_LEXOPT_EOL));
+ switch (pctx->token.type) {
+ case isc_tokentype_eol:
+ token = UNCONST(CFG_TOKEN_NEWLINE);
+ break;
+ case isc_tokentype_string:
+ case isc_tokentype_qstring: {
+ isc_region_t region;
+
+ isc_lex_getlasttokentext(pctx->lexer, &pctx->token,
+ ®ion);
+ token = isc_mem_allocate(isc_g_mctx, region.length + 1);
+ memmove(token, region.base, region.length);
+ token[region.length] = '\0';
+ break;
+ }
+ case isc_tokentype_special:
+ switch (pctx->token.value.as_char) {
+ case '{':
+ depth++;
+ token = UNCONST(CFG_TOKEN_OPEN);
+ break;
+ case '}':
+ if (depth == 0) {
+ *ret = obj;
+ return ISC_R_SUCCESS;
+ }
+ depth--;
+ token = UNCONST(CFG_TOKEN_CLOSE);
+ break;
+ case ';':
+ token = UNCONST(CFG_TOKEN_END);
+ break;
+ case '/':
+ token = UNCONST(CFG_TOKEN_SLASH);
+ break;
+ case '!':
+ token = UNCONST(CFG_TOKEN_BANG);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+ case isc_tokentype_eof:
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "unexpected end of input");
+ CLEANUP(ISC_R_UNEXPECTEDEND);
+ default:
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "unexpected token");
+ CLEANUP(ISC_R_UNEXPECTEDTOKEN);
+ }
+ tokens_append(obj, &count, &size, token);
+ }
cleanup:
+ if (obj != NULL) {
+ cfg_obj_detach(&obj);
+ }
return result;
}
-static void
-print_btext(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+void
+cfg_print_tokens(cfg_printer_t *pctx, const char *const *tokens,
+ const char *const *end) {
+ bool oneline = (pctx->flags & CFG_PRINTER_ONELINE) != 0;
+ bool newline = false;
+
/*
- * We need to print "{" instead of running print_open()
- * in order to preserve the exact original formatting
- * of the bracketed text. But we increment the indent value
- * so that print_close() will leave us back in our original
- * state.
+ * Line breaks are reproduced from the CFG_TOKEN_NEWLINE markers,
+ * so the output has the same line structure as the input; the
+ * indentation is derived from the bracket depth.
*/
pctx->indent++;
cfg_print_cstr(pctx, "{");
- cfg_print_chars(pctx, obj->value.string, strlen(obj->value.string));
- print_close(pctx);
+ for (; tokens != end && *tokens != NULL; tokens++) {
+ const char *t = *tokens;
+
+ if (t == CFG_TOKEN_NEWLINE) {
+ if (!oneline) {
+ cfg_print_cstr(pctx, "\n");
+ newline = true;
+ }
+ continue;
+ }
+
+ if (t == CFG_TOKEN_CLOSE) {
+ pctx->indent--;
+ }
+
+ if (newline) {
+ cfg_print_indent(pctx);
+ newline = false;
+ } else if (t != CFG_TOKEN_END) {
+ cfg_print_cstr(pctx, " ");
+ }
+
+ if (t == CFG_TOKEN_OPEN) {
+ cfg_print_cstr(pctx, "{");
+ pctx->indent++;
+ } else if (t == CFG_TOKEN_CLOSE) {
+ cfg_print_cstr(pctx, "}");
+ } else if (t == CFG_TOKEN_END) {
+ cfg_print_cstr(pctx, ";");
+ } else if (t == CFG_TOKEN_SLASH) {
+ cfg_print_cstr(pctx, "/");
+ } else if (t == CFG_TOKEN_BANG) {
+ cfg_print_cstr(pctx, "!");
+ } else {
+ cfg_print_cstr(pctx, t);
+ }
+ }
+
+ pctx->indent--;
+ if (newline) {
+ cfg_print_indent(pctx);
+ } else {
+ cfg_print_cstr(pctx, " ");
+ }
+ cfg_print_cstr(pctx, "}");
+}
+
+static void
+print_btokens(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ cfg_print_tokens(pctx, cfg_obj_astokens(obj), NULL);
}
static void
-doc_btext(cfg_printer_t *pctx, const cfg_type_t *type ISC_ATTR_UNUSED) {
+doc_btokens(cfg_printer_t *pctx, const cfg_type_t *type ISC_ATTR_UNUSED) {
cfg_print_cstr(pctx, "{ <unspecified-text> }");
}
+bool
+cfg_obj_istokens(const cfg_obj_t *obj) {
+ REQUIRE(VALID_CFGOBJ(obj));
+ return obj->type->rep == &cfg_rep_tokens;
+}
+
+const char *const *
+cfg_obj_astokens(const cfg_obj_t *obj) {
+ REQUIRE(VALID_CFGOBJ(obj));
+ REQUIRE(obj->type->rep == &cfg_rep_tokens);
+ return (const char *const *)obj->value.tokens;
+}
+
bool
cfg_is_enum(const char *s, const char *const *enums) {
const char *const *p;
@@ -1671,6 +1840,19 @@ free_string(cfg_obj_t *obj) {
isc_mem_free(isc_g_mctx, obj->value.string);
}
+static void
+free_tokens(cfg_obj_t *obj) {
+ if (obj->value.tokens == NULL) {
+ return;
+ }
+ for (char **t = obj->value.tokens; *t != NULL; t++) {
+ if (CFG_TOKEN_ISSTRING(*t)) {
+ isc_mem_free(isc_g_mctx, *t);
+ }
+ }
+ isc_mem_free(isc_g_mctx, obj->value.tokens);
+}
+
static void
free_sockaddr(cfg_obj_t *obj) {
isc_mem_put(isc_g_mctx, obj->value.sockaddr, sizeof(isc_sockaddr_t));
@@ -1722,13 +1904,14 @@ cfg_type_t cfg_type_sstring = { "string", cfg_parse_sstring,
&cfg_rep_string, NULL };
/*
- * Text enclosed in brackets. Used to pass a block of configuration
- * text to dynamic library or external application. Checked for
- * bracket balance, but not otherwise parsed.
+ * Tokens enclosed in brackets. Used to pass a block of configuration
+ * to a dynamic library, which walks the tokens itself (see
+ * <isccfg/tokens.h>). Checked for bracket balance, but not otherwise
+ * parsed.
*/
-cfg_type_t cfg_type_bracketed_text = { "bracketed_text", parse_btext,
- print_btext, doc_btext,
- &cfg_rep_string, NULL };
+cfg_type_t cfg_type_bracketed_tokens = { "bracketed_tokens", parse_btokens,
+ print_btokens, doc_btokens,
+ &cfg_rep_tokens, NULL };
#if defined(HAVE_GEOIP2)
/*
@@ -1915,16 +2098,19 @@ cfg_type_t cfg_type_bracketed_aml = { "bracketed_aml",
&cfg_type_addrmatchelt };
/*
- * Optional bracketed text
+ * Optional bracketed tokens
*/
static isc_result_t
-parse_optional_btext(cfg_parser_t *pctx, const cfg_type_t *type ISC_ATTR_UNUSED,
- cfg_obj_t **ret) {
+parse_optional_btokens(cfg_parser_t *pctx,
+ const cfg_type_t *type ISC_ATTR_UNUSED,
+ cfg_obj_t **ret) {
isc_result_t result;
- CHECK(cfg_peektoken(pctx, ISC_LEXOPT_BTEXT));
- if (pctx->token.type == isc_tokentype_btext) {
- CHECK(cfg_parse_obj(pctx, &cfg_type_bracketed_text, ret));
+ CHECK(cfg_peektoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_special &&
+ pctx->token.value.as_char == '{')
+ {
+ CHECK(cfg_parse_obj(pctx, &cfg_type_bracketed_tokens, ret));
} else {
CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
}
@@ -1933,29 +2119,26 @@ cleanup:
}
static void
-print_optional_btext(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+print_optional_btokens(cfg_printer_t *pctx, const cfg_obj_t *obj) {
if (obj->type == &cfg_type_void) {
return;
}
- pctx->indent++;
- cfg_print_cstr(pctx, "{");
- cfg_print_chars(pctx, obj->value.string, strlen(obj->value.string));
- print_close(pctx);
+ print_btokens(pctx, obj);
}
static void
-doc_optional_btext(cfg_printer_t *pctx,
- const cfg_type_t *type ISC_ATTR_UNUSED) {
+doc_optional_btokens(cfg_printer_t *pctx,
+ const cfg_type_t *type ISC_ATTR_UNUSED) {
cfg_print_cstr(pctx, "[ { <unspecified-text> } ]");
}
-cfg_type_t cfg_type_optional_bracketed_text = { "optional_btext",
- parse_optional_btext,
- print_optional_btext,
- doc_optional_btext,
- NULL,
- NULL };
+cfg_type_t cfg_type_optional_bracketed_tokens = { "optional_btokens",
+ parse_optional_btokens,
+ print_optional_btokens,
+ doc_optional_btokens,
+ NULL,
+ NULL };
/*
* Booleans
@@ -4157,7 +4340,7 @@ cfg_pluginlist_foreach(const cfg_obj_t *config, const cfg_obj_t *list,
const cfg_obj_t *plugin = cfg_listelt_value(element);
const cfg_obj_t *obj;
const char *type, *library;
- const char *parameters = NULL;
+ const char *const *parameters = NULL;
/* Get the path to the plugin module. */
obj = cfg_tuple_get(plugin, "type");
@@ -4173,8 +4356,8 @@ cfg_pluginlist_foreach(const cfg_obj_t *config, const cfg_obj_t *list,
library = cfg_obj_asstring(cfg_tuple_get(plugin, "library"));
obj = cfg_tuple_get(plugin, "parameters");
- if (obj != NULL && cfg_obj_isstring(obj)) {
- parameters = cfg_obj_asstring(obj);
+ if (obj != NULL && cfg_obj_istokens(obj)) {
+ parameters = cfg_obj_astokens(obj);
}
result = callback(config, obj, aclctx, library, parameters,
diff --git a/lib/isccfg/tokens.c b/lib/isccfg/tokens.c
new file mode 100644
index 0000000000..37f125dc38
--- /dev/null
+++ b/lib/isccfg/tokens.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <isc/buffer.h>
+#include <isc/log.h>
+#include <isc/mem.h>
+#include <isc/util.h>
+
+#include <isccfg/cfg.h>
+#include <isccfg/grammar.h>
+#include <isccfg/tokens.h>
+
+struct cfg_tokenstring {
+ cfg_obj_t *obj;
+ cfg_tokenstring_t *next;
+};
+
+static const char *const empty[] = { NULL };
+
+void
+cfg_tokens_init(cfg_tokens_t *tok, const char *const *tokens, const char *file,
+ unsigned long line) {
+ REQUIRE(tok != NULL);
+
+ *tok = (cfg_tokens_t){
+ .next = (tokens != NULL) ? tokens : empty,
+ .file = file,
+ .line = line,
+ };
+}
+
+void
+cfg_tokens_clear(cfg_tokens_t *tok) {
+ while (tok->strings != NULL) {
+ cfg_tokenstring_t *string = tok->strings;
+
+ tok->strings = string->next;
+ cfg_obj_detach(&string->obj);
+ isc_mem_put(isc_g_mctx, string, sizeof(*string));
+ }
+}
+
+const char *
+cfg_tokens_peek(cfg_tokens_t *tok) {
+ REQUIRE(tok != NULL);
+
+ while (*tok->next == CFG_TOKEN_NEWLINE) {
+ tok->next++;
+ tok->line++;
+ }
+ return *tok->next;
+}
+
+const char *
+cfg_tokens_next(cfg_tokens_t *tok) {
+ const char *token = cfg_tokens_peek(tok);
+
+ if (token != NULL) {
+ tok->next++;
+ }
+ return token;
+}
+
+void
+cfg_tokens_log(const cfg_tokens_t *tok, int level, const char *fmt, ...) {
+ va_list ap;
+ char msgbuf[2048];
+
+ REQUIRE(tok != NULL);
+
+ if (!isc_log_wouldlog(level)) {
+ return;
+ }
+
+ va_start(ap, fmt);
+ vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
+ va_end(ap);
+
+ isc_log_write(CFG_LOGCATEGORY_CONFIG, CFG_LOGMODULE_PARSER, level,
+ "%s:%lu: %s", tok->file != NULL ? tok->file : "none",
+ tok->line, msgbuf);
+}
+
+static const char *
+markername(const char *marker) {
+ if (marker == CFG_TOKEN_OPEN) {
+ return "'{'";
+ } else if (marker == CFG_TOKEN_CLOSE) {
+ return "'}'";
+ } else if (marker == CFG_TOKEN_END) {
+ return "';'";
+ } else if (marker == CFG_TOKEN_SLASH) {
+ return "'/'";
+ } else if (marker == CFG_TOKEN_BANG) {
+ return "'!'";
+ }
+ UNREACHABLE();
+}
+
+isc_result_t
+cfg_tokens_expect(cfg_tokens_t *tok, const char *marker) {
+ const char *token = cfg_tokens_next(tok);
+
+ if (token == NULL) {
+ cfg_tokens_log(tok, ISC_LOG_ERROR,
+ "expected %s, found end of input",
+ markername(marker));
+ return ISC_R_UNEXPECTEDEND;
+ }
+ if (token != marker) {
+ cfg_tokens_log(tok, ISC_LOG_ERROR, "expected %s",
+ markername(marker));
+ return ISC_R_UNEXPECTEDTOKEN;
+ }
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t
+cfg_tokens_getstring(cfg_tokens_t *tok, const char **strp) {
+ const char *token = NULL;
+
+ REQUIRE(strp != NULL);
+
+ token = cfg_tokens_next(tok);
+ if (token == NULL) {
+ cfg_tokens_log(tok, ISC_LOG_ERROR,
+ "expected string, found end of input");
+ return ISC_R_UNEXPECTEDEND;
+ }
+ if (!CFG_TOKEN_ISSTRING(token)) {
+ cfg_tokens_log(tok, ISC_LOG_ERROR, "expected string");
+ return ISC_R_UNEXPECTEDTOKEN;
+ }
+ if (token[0] == '"') {
+ cfg_obj_t *obj = NULL;
+ isc_buffer_t buffer;
+ size_t length = strlen(token);
+
+ isc_buffer_constinit(&buffer, token, length);
+ isc_buffer_add(&buffer, length);
+ RETERR(cfg_parse_buffer(&buffer, tok->file, tok->line,
+ &cfg_type_qstring, 0, &obj));
+
+ cfg_tokenstring_t *string = isc_mem_get(isc_g_mctx,
+ sizeof(*string));
+ *string = (cfg_tokenstring_t){ .obj = obj,
+ .next = tok->strings };
+ tok->strings = string;
+ *strp = cfg_obj_asstring(obj);
+ } else {
+ *strp = token;
+ }
+ return ISC_R_SUCCESS;
+}
+
+static void
+print_tobuffer(void *closure, const char *text, int textlen) {
+ isc_buffer_putmem(closure, (const unsigned char *)text, textlen);
+}
+
+isc_result_t
+cfg_tokens_getaml(cfg_tokens_t *tok, cfg_obj_t **objp) {
+ isc_result_t result;
+ const char *const *end = NULL;
+ unsigned long line;
+ unsigned int depth = 0;
+ isc_buffer_t *b = NULL;
+ cfg_printer_t pctx;
+
+ REQUIRE(objp != NULL && *objp == NULL);
+
+ RETERR(cfg_tokens_expect(tok, CFG_TOKEN_OPEN));
+ line = tok->line;
+
+ /* Find the matching closing bracket. */
+ for (end = tok->next;; end++) {
+ if (*end == NULL) {
+ cfg_tokens_log(tok, ISC_LOG_ERROR,
+ "expected '}', found end of input");
+ return ISC_R_UNEXPECTEDEND;
+ } else if (*end == CFG_TOKEN_OPEN) {
+ depth++;
+ } else if (*end == CFG_TOKEN_CLOSE) {
+ if (depth == 0) {
+ break;
+ }
+ depth--;
+ }
+ }
+
+ /*
+ * Turn the tokens back into text and parse that, so that the
+ * address match list grammar lives only in the configuration
+ * parser. The line breaks are preserved, so errors are reported
+ * at the right line.
+ */
+ isc_buffer_allocate(isc_g_mctx, &b, 256);
+ pctx = (cfg_printer_t){ .f = print_tobuffer, .closure = b };
+ cfg_print_tokens(&pctx, tok->next, end);
+
+ result = cfg_parse_buffer(b, tok->file, line, &cfg_type_bracketed_aml,
+ 0, objp);
+ isc_buffer_free(&b);
+
+ for (; tok->next != end; tok->next++) {
+ if (*tok->next == CFG_TOKEN_NEWLINE) {
+ tok->line++;
+ }
+ }
+ tok->next++;
+
+ return result;
+}
diff --git a/lib/ns/hooks.c b/lib/ns/hooks.c
index 5577a669cc..53dbabe312 100644
--- a/lib/ns/hooks.c
+++ b/lib/ns/hooks.c
@@ -211,9 +211,10 @@ unload_plugin(ns_plugin_t **pluginp) {
}
isc_result_t
-ns_plugin_register(const char *modpath, const char *parameters, const void *cfg,
- const char *cfg_file, unsigned long cfg_line,
- isc_mem_t *mctx, void *aclctx, ns_hook_data_t *hookdata) {
+ns_plugin_register(const char *modpath, const char *const *parameters,
+ const void *cfg, const char *cfg_file,
+ unsigned long cfg_line, isc_mem_t *mctx, void *aclctx,
+ ns_hook_data_t *hookdata) {
isc_result_t result;
ns_plugin_t *plugin = NULL;
@@ -247,9 +248,9 @@ cleanup:
}
isc_result_t
-ns_plugin_check(const char *modpath, const char *parameters, const void *cfg,
- const char *cfg_file, unsigned long cfg_line, isc_mem_t *mctx,
- void *aclctx, const ns_pluginctx_t *ctx) {
+ns_plugin_check(const char *modpath, const char *const *parameters,
+ const void *cfg, const char *cfg_file, unsigned long cfg_line,
+ isc_mem_t *mctx, void *aclctx, const ns_pluginctx_t *ctx) {
isc_result_t result;
ns_plugin_t *plugin = NULL;
diff --git a/lib/ns/include/ns/hooks.h b/lib/ns/include/ns/hooks.h
index 0da0b6a171..c530f23e28 100644
--- a/lib/ns/include/ns/hooks.h
+++ b/lib/ns/include/ns/hooks.h
@@ -515,19 +515,22 @@ typedef struct ns_hook_data {
* as well; if not, set NS_PLUGIN_AGE to 0.
*/
#ifndef NS_PLUGIN_VERSION
-#define NS_PLUGIN_VERSION 4
+#define NS_PLUGIN_VERSION 5
#define NS_PLUGIN_AGE 0
#endif /* ifndef NS_PLUGIN_VERSION */
typedef isc_result_t
-ns_plugin_register_t(const char *parameters, const void *cfg, const char *file,
- unsigned long line, isc_mem_t *mctx, void *aclctx,
- ns_hooktable_t *hooktable, const ns_pluginctx_t *ctx,
- void **instp);
+ns_plugin_register_t(const char *const *parameters, const void *cfg,
+ const char *file, unsigned long line, isc_mem_t *mctx,
+ void *aclctx, ns_hooktable_t *hooktable,
+ const ns_pluginctx_t *ctx, void **instp);
/*%<
* Called when registering a new plugin.
*
- * 'parameters' contains the plugin configuration text.
+ * 'parameters' contains the plugin configuration as a NULL-terminated
+ * token array (see <isccfg/tokens.h>), or is NULL if the plugin was
+ * configured without a parameter block. 'file' and 'line' are the
+ * location of the parameter block.
*
* '*instp' will be set to the module instance handle if the function
* is successful.
@@ -546,9 +549,9 @@ ns_plugin_destroy_t(void **instp);
*/
typedef isc_result_t
-ns_plugin_check_t(const char *parameters, const void *cfg, const char *file,
- unsigned long line, isc_mem_t *mctx, void *aclctx,
- const ns_pluginctx_t *ctx);
+ns_plugin_check_t(const char *const *parameters, const void *cfg,
+ const char *file, unsigned long line, isc_mem_t *mctx,
+ void *aclctx, const ns_pluginctx_t *ctx);
/*%<
* Check the validity of 'parameters'.
*/
@@ -594,9 +597,10 @@ ns_plugin_expandpath(const char *src, char *dst, size_t dstsize);
*/
isc_result_t
-ns_plugin_register(const char *modpath, const char *parameters, const void *cfg,
- const char *cfg_file, unsigned long cfg_line,
- isc_mem_t *mctx, void *aclctx, ns_hook_data_t *hookdata);
+ns_plugin_register(const char *modpath, const char *const *parameters,
+ const void *cfg, const char *cfg_file,
+ unsigned long cfg_line, isc_mem_t *mctx, void *aclctx,
+ ns_hook_data_t *hookdata);
/*%<
* Load the plugin module specified from the file 'modpath', and
* register an instance using 'parameters'.
@@ -613,9 +617,9 @@ ns_plugin_register(const char *modpath, const char *parameters, const void *cfg,
*/
isc_result_t
-ns_plugin_check(const char *modpath, const char *parameters, const void *cfg,
- const char *cfg_file, unsigned long cfg_line, isc_mem_t *mctx,
- void *aclctx, const ns_pluginctx_t *ctx);
+ns_plugin_check(const char *modpath, const char *const *parameters,
+ const void *cfg, const char *cfg_file, unsigned long cfg_line,
+ isc_mem_t *mctx, void *aclctx, const ns_pluginctx_t *ctx);
/*%<
* Open the plugin module at 'modpath' and check the validity of
* 'parameters', logging any errors or warnings found, then
diff --git a/tests/isccfg/parser_test.c b/tests/isccfg/parser_test.c
index 2f9ae5eccc..ae8f8886de 100644
--- a/tests/isccfg/parser_test.c
+++ b/tests/isccfg/parser_test.c
@@ -36,6 +36,7 @@
#include <isccfg/cfg.h>
#include <isccfg/grammar.h>
#include <isccfg/namedconf.h>
+#include <isccfg/tokens.h>
#include <tests/isc.h>
@@ -57,6 +58,149 @@ append(void *arg, const char *str, int len) {
snprintf(buf + l, 1024 - l, "%.*s", len, str);
}
+/* Preserve lexical spelling, including quoted punctuation and escapes. */
+ISC_RUN_TEST_IMPL(tokens_roundtrip) {
+ const char *spelling[] = {
+ "plain", "\"plain\"", "\"\"", "\"a\\ b\"",
+ "\"a\\;b\"", "\"a\\#b\"", "\"a\\/b\"", "\"a\\!b\"",
+ "\"a\\{b\"", "\"a\\}b\"", "\"a\\\tb\"", "\"a\\\"b\"",
+ "tail\\", "tail\\\\", "\"tail\\\\\"", "\"a\\\\\\\"b\"",
+ "\"/\"", "\"/\"", "\"/\"", "\"*\"",
+ "\"!\"", CFG_TOKEN_SLASH, CFG_TOKEN_BANG, CFG_TOKEN_OPEN,
+ "nested", CFG_TOKEN_END, CFG_TOKEN_CLOSE, CFG_TOKEN_END,
+ NULL
+ };
+ unsigned int flags[] = { 0, CFG_PRINTER_ONELINE };
+ char input[1024] = "{ ";
+
+ for (const char *const *t = spelling; *t != NULL; t++) {
+ const char *text = *t;
+ if (text == CFG_TOKEN_OPEN) {
+ text = "{";
+ } else if (text == CFG_TOKEN_CLOSE) {
+ text = "}";
+ } else if (text == CFG_TOKEN_END) {
+ text = ";";
+ } else if (text == CFG_TOKEN_SLASH) {
+ text = "/";
+ } else if (text == CFG_TOKEN_BANG) {
+ text = "!";
+ }
+ strlcat(input, text, sizeof(input));
+ strlcat(input, " ", sizeof(input));
+ }
+ strlcat(input, "}", sizeof(input));
+
+ for (size_t i = 0; i < ARRAY_SIZE(flags); i++) {
+ cfg_obj_t *obj = NULL, *copy = NULL;
+ char printed[1024] = "";
+ isc_buffer_t b;
+
+ isc_buffer_constinit(&b, input, strlen(input));
+ isc_buffer_add(&b, strlen(input));
+ assert_int_equal(cfg_parse_buffer(&b, "tokens", 1,
+ &cfg_type_bracketed_tokens, 0,
+ &obj),
+ ISC_R_SUCCESS);
+ cfg_printx(obj, flags[i], append, printed);
+ isc_buffer_constinit(&b, printed, strlen(printed));
+ isc_buffer_add(&b, strlen(printed));
+ assert_int_equal(cfg_parse_buffer(&b, "printed", 1,
+ &cfg_type_bracketed_tokens, 0,
+ ©),
+ ISC_R_SUCCESS);
+
+ const char *const *original = cfg_obj_astokens(obj);
+ const char *const *reparsed = cfg_obj_astokens(copy);
+ for (size_t j = 0; j < ARRAY_SIZE(spelling); j++) {
+ if (spelling[j] != NULL &&
+ CFG_TOKEN_ISSTRING(spelling[j]))
+ {
+ assert_non_null(original[j]);
+ assert_non_null(reparsed[j]);
+ assert_true(CFG_TOKEN_ISSTRING(original[j]));
+ assert_true(CFG_TOKEN_ISSTRING(reparsed[j]));
+ assert_string_equal(original[j], spelling[j]);
+ assert_string_equal(reparsed[j], spelling[j]);
+ } else {
+ assert_ptr_equal(original[j], spelling[j]);
+ assert_ptr_equal(reparsed[j], spelling[j]);
+ }
+ }
+ cfg_obj_detach(&obj);
+ cfg_obj_detach(©);
+ }
+}
+
+/* Decoding leaves lexical tokens intact and previous results valid. */
+ISC_RUN_TEST_IMPL(tokens_strings) {
+ const char *tokens[] = { "\"dynamic-\"", "\"\"",
+ "\"a\\\"b\"", "\"a\\ b\"",
+ "\"tail\\\\\"", "\"a\\\\\\\"b\"",
+ "a\\\"b", "\"a\nb\"",
+ "plain", NULL };
+ const char *expected[] = { "dynamic-", "", "a\"b",
+ "a\\ b", "tail\\\\", "a\\\\\"b",
+ "a\\\"b", "a\nb", "plain" };
+ const char *values[ARRAY_SIZE(expected)] = { NULL };
+ cfg_tokens_t tok;
+
+ cfg_tokens_init(&tok, tokens, "strings", 1);
+ for (size_t i = 0; i < ARRAY_SIZE(expected); i++) {
+ assert_ptr_equal(cfg_tokens_peek(&tok), tokens[i]);
+ assert_int_equal(cfg_tokens_getstring(&tok, &values[i]),
+ ISC_R_SUCCESS);
+ }
+ assert_null(cfg_tokens_peek(&tok));
+ for (size_t i = 0; i < ARRAY_SIZE(expected); i++) {
+ assert_string_equal(values[i], expected[i]);
+ }
+ assert_string_equal(tokens[0], "\"dynamic-\"");
+ cfg_tokens_clear(&tok);
+}
+
+/* ACL keywords, named references, and addresses must stay distinct. */
+ISC_RUN_TEST_IMPL(tokens_aml) {
+ const char *aml = "{ \"key\"; \"geoip\"; \"192.0.2.1\"; \"::1\"; "
+ "!10.0.0.0/8; key \"key name\"; any; { ::1; }; }";
+ char input[1024] = "{ ", printed[1024] = "", expected[1024] = "";
+ cfg_obj_t *direct = NULL, *tokens = NULL, *copy = NULL, *obj = NULL;
+ isc_buffer_t b;
+ cfg_tokens_t tok;
+
+ isc_buffer_constinit(&b, aml, strlen(aml));
+ isc_buffer_add(&b, strlen(aml));
+ assert_int_equal(cfg_parse_buffer(&b, "aml", 1, &cfg_type_bracketed_aml,
+ 0, &direct),
+ ISC_R_SUCCESS);
+ cfg_printx(direct, CFG_PRINTER_ONELINE, append, expected);
+ strlcat(input, aml, sizeof(input));
+ strlcat(input, " }", sizeof(input));
+ isc_buffer_constinit(&b, input, strlen(input));
+ isc_buffer_add(&b, strlen(input));
+ assert_int_equal(cfg_parse_buffer(&b, "tokens", 1,
+ &cfg_type_bracketed_tokens, 0,
+ &tokens),
+ ISC_R_SUCCESS);
+ cfg_printx(tokens, CFG_PRINTER_ONELINE, append, printed);
+ isc_buffer_constinit(&b, printed, strlen(printed));
+ isc_buffer_add(&b, strlen(printed));
+ assert_int_equal(cfg_parse_buffer(&b, "printed", 1,
+ &cfg_type_bracketed_tokens, 0, ©),
+ ISC_R_SUCCESS);
+ cfg_tokens_init(&tok, cfg_obj_astokens(copy), "printed", 1);
+ assert_int_equal(cfg_tokens_getaml(&tok, &obj), ISC_R_SUCCESS);
+ assert_null(cfg_tokens_next(&tok));
+ printed[0] = '\0';
+ cfg_printx(obj, CFG_PRINTER_ONELINE, append, printed);
+ assert_string_equal(printed, expected);
+ cfg_tokens_clear(&tok);
+ cfg_obj_detach(&direct);
+ cfg_obj_detach(&tokens);
+ cfg_obj_detach(©);
+ cfg_obj_detach(&obj);
+}
+
ISC_RUN_TEST_IMPL(addzoneconf) {
isc_result_t result;
isc_buffer_t b;
@@ -355,6 +499,9 @@ ISC_RUN_TEST_IMPL(cfg_map_findclause_empty) {
ISC_TEST_LIST_START
+ISC_TEST_ENTRY(tokens_roundtrip)
+ISC_TEST_ENTRY(tokens_strings)
+ISC_TEST_ENTRY(tokens_aml)
ISC_TEST_ENTRY(addzoneconf)
ISC_TEST_ENTRY(parse_buffer)
ISC_TEST_ENTRY(parse_nulbyte)