From: Alessio Podda Date: Thu, 19 Mar 2026 16:33:52 +0000 (+0100) Subject: Duplicate cfg_map X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4db25a3dd923bbf0635f038ad1f515b1ec0cabca;p=thirdparty%2Fbind9.git Duplicate cfg_map --- diff --git a/bin/plugins/filter-a.c b/bin/plugins/filter-a.c index f722fdcb813..0d94f41ae9e 100644 --- a/bin/plugins/filter-a.c +++ b/bin/plugins/filter-a.c @@ -191,9 +191,11 @@ static cfg_clausedef_t param_clauses[] = { static cfg_clausedef_t *param_clausesets[] = { param_clauses, NULL }; -static cfg_type_t cfg_type_parameters = { "filter-a-params", cfg_parse_mapbody, - cfg_print_mapbody, cfg_doc_mapbody, - &cfg_rep_map, param_clausesets }; +static cfg_type_t cfg_type_parameters = { + "filter-a-params", cfg_parse_mapbody_external, + cfg_print_mapbody_external, cfg_doc_mapbody_external, + &cfg_rep_map_external, param_clausesets +}; static isc_result_t parse_filter_a_on(const cfg_obj_t *param_obj, const char *param_name, @@ -201,7 +203,7 @@ parse_filter_a_on(const cfg_obj_t *param_obj, const char *param_name, const cfg_obj_t *obj = NULL; isc_result_t result; - result = cfg_map_get(param_obj, param_name, &obj); + result = cfg_map_external_get(param_obj, param_name, &obj); if (result != ISC_R_SUCCESS) { return ISC_R_SUCCESS; } @@ -278,7 +280,7 @@ parse_parameters(filter_instance_t *inst, const char *parameters, 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)); - result = cfg_map_get(param_obj, "filter-a", &obj); + result = cfg_map_external_get(param_obj, "filter-a", &obj); if (result == ISC_R_SUCCESS) { CHECK(cfg_acl_fromconfig(obj, (const cfg_obj_t *)cfg, (cfg_aclconfctx_t *)aclctx, mctx, 0, diff --git a/bin/plugins/filter-aaaa.c b/bin/plugins/filter-aaaa.c index 6a7a8d25e9b..32ded4f63a8 100644 --- a/bin/plugins/filter-aaaa.c +++ b/bin/plugins/filter-aaaa.c @@ -192,8 +192,9 @@ static cfg_clausedef_t param_clauses[] = { static cfg_clausedef_t *param_clausesets[] = { param_clauses, NULL }; static cfg_type_t cfg_type_parameters = { - "filter-aaaa-params", cfg_parse_mapbody, cfg_print_mapbody, - cfg_doc_mapbody, &cfg_rep_map, param_clausesets + "filter-aaaa-params", cfg_parse_mapbody_external, + cfg_print_mapbody_external, cfg_doc_mapbody_external, + &cfg_rep_map_external, param_clausesets }; static isc_result_t @@ -202,7 +203,7 @@ parse_filter_aaaa_on(const cfg_obj_t *param_obj, const char *param_name, const cfg_obj_t *obj = NULL; isc_result_t result; - result = cfg_map_get(param_obj, param_name, &obj); + result = cfg_map_external_get(param_obj, param_name, &obj); if (result != ISC_R_SUCCESS) { return ISC_R_SUCCESS; } @@ -281,7 +282,7 @@ parse_parameters(filter_instance_t *inst, const char *parameters, CHECK(parse_filter_aaaa_on(param_obj, "filter-aaaa-on-v6", &inst->v6_aaaa)); - result = cfg_map_get(param_obj, "filter-aaaa", &obj); + result = cfg_map_external_get(param_obj, "filter-aaaa", &obj); if (result == ISC_R_SUCCESS) { CHECK(cfg_acl_fromconfig(obj, (const cfg_obj_t *)cfg, (cfg_aclconfctx_t *)aclctx, mctx, 0, diff --git a/bin/plugins/synthrecord.c b/bin/plugins/synthrecord.c index fc2f79450bb..8d1470af32d 100644 --- a/bin/plugins/synthrecord.c +++ b/bin/plugins/synthrecord.c @@ -401,8 +401,9 @@ static cfg_clausedef_t *synthrecord_cfgparamsclausesets[] = { }; static cfg_type_t synthrecord_cfgparams = { - "synthrecord-params", cfg_parse_mapbody, cfg_print_mapbody, - cfg_doc_mapbody, &cfg_rep_map, synthrecord_cfgparamsclausesets + "synthrecord-params", cfg_parse_mapbody_external, + cfg_print_mapbody_external, cfg_doc_mapbody_external, + &cfg_rep_map_external, synthrecord_cfgparamsclausesets }; static isc_result_t @@ -411,7 +412,7 @@ synthrecord_initprefix(synthrecord_t *inst, const cfg_obj_t *synthrecordcfg) { const char *base = NULL; const cfg_obj_t *obj = NULL; - result = cfg_map_get(synthrecordcfg, "prefix", &obj); + result = cfg_map_external_get(synthrecordcfg, "prefix", &obj); if (result != ISC_R_SUCCESS) { isc_log_write(NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS, ISC_LOG_ERROR, "synthrecord: prefix not found"); @@ -448,7 +449,7 @@ synthrecord_initorigin(synthrecord_t *inst, const cfg_obj_t *synthrecordcfg, const cfg_obj_t *obj = NULL; const char *originstr = NULL; - result = cfg_map_get(synthrecordcfg, "origin", &obj); + result = cfg_map_external_get(synthrecordcfg, "origin", &obj); if (inst->mode == REVERSE && result != ISC_R_SUCCESS) { isc_log_write(NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS, ISC_LOG_ERROR, @@ -496,7 +497,7 @@ synthrecord_parseallowsynth(synthrecord_t *inst, const cfg_obj_t *cfg, const cfg_obj_t *obj = NULL; INSIST(inst->allowedsynth == NULL); - result = cfg_map_get(synthrecordcfg, "allow-synth", &obj); + result = cfg_map_external_get(synthrecordcfg, "allow-synth", &obj); if (result == ISC_R_NOTFOUND) { return dns_acl_any(inst->mctx, &inst->allowedsynth); @@ -532,7 +533,7 @@ 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); + result = cfg_map_external_get(synthrecordcfg, "ttl", &obj); if (result == ISC_R_NOTFOUND) { inst->ttl = DEFAULT_TTL; diff --git a/bin/tests/system/hooks/driver/test-syncplugin.c b/bin/tests/system/hooks/driver/test-syncplugin.c index 3ac08a2dc99..62bca1842be 100644 --- a/bin/tests/system/hooks/driver/test-syncplugin.c +++ b/bin/tests/system/hooks/driver/test-syncplugin.c @@ -68,8 +68,9 @@ static cfg_clausedef_t *syncplugin__cfgparamsclausesets[] = { }; static cfg_type_t syncplugin__cfgparams = { - "syncplugin-params", cfg_parse_mapbody, cfg_print_mapbody, - cfg_doc_mapbody, &cfg_rep_map, syncplugin__cfgparamsclausesets + "syncplugin-params", cfg_parse_mapbody_external, + cfg_print_mapbody_external, cfg_doc_mapbody_external, + &cfg_rep_map_external, syncplugin__cfgparamsclausesets }; static isc_result_t @@ -78,7 +79,7 @@ syncplugin__parse_rcode(const cfg_obj_t *syncplugincfg, uint8_t *rcode) { const cfg_obj_t *obj = NULL; const char *rcodestr = NULL; - RETERR(cfg_map_get(syncplugincfg, "rcode", &obj)); + RETERR(cfg_map_external_get(syncplugincfg, "rcode", &obj)); rcodestr = obj->value.string; @@ -131,7 +132,7 @@ plugin_register(const char *parameters, const void *cfg, const char *cfgfile, CHECK(syncplugin__parse_rcode(syncplugincfg, &inst->rcode)); - if (cfg_map_get(syncplugincfg, "firstlbl", &obj) == ISC_R_SUCCESS) { + if (cfg_map_external_get(syncplugincfg, "firstlbl", &obj) == ISC_R_SUCCESS) { const char *firstlbl = cfg_obj_asstring(obj); size_t len = strlen(firstlbl) + 1; @@ -140,7 +141,7 @@ plugin_register(const char *parameters, const void *cfg, const char *cfgfile, } obj = NULL; - CHECK(cfg_map_get(syncplugincfg, "source", &obj)); + CHECK(cfg_map_external_get(syncplugincfg, "source", &obj)); sourcestr = obj->value.string; if (strcmp(sourcestr, "zone") == 0) { diff --git a/lib/isccfg/include/isccfg/cfg.h b/lib/isccfg/include/isccfg/cfg.h index 5012f492171..035dd2d0dfc 100644 --- a/lib/isccfg/include/isccfg/cfg.h +++ b/lib/isccfg/include/isccfg/cfg.h @@ -580,6 +580,40 @@ 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); +bool +cfg_obj_ismap_external(const cfg_obj_t *obj); +/*%< + * Return true iff 'obj' is of a map_external type. + */ + +isc_result_t +cfg_map_external_get(const cfg_obj_t *mapobj, const char *name, + const cfg_obj_t **obj); + +const cfg_obj_t * +cfg_map_external_getname(const cfg_obj_t *mapobj); + +unsigned int +cfg_map_external_count(const cfg_obj_t *mapobj); + +isc_result_t +cfg_map_external_add(cfg_obj_t *mapobj, cfg_obj_t *obj, const char *clause); + +isc_result_t +cfg_map_external_addclone(cfg_obj_t *map, const cfg_obj_t *obj, + const cfg_clausedef_t *clause); + +const cfg_clausedef_t * +cfg_map_external_firstclause(const cfg_type_t *map, const void **clauses, + unsigned int *idx); + +const cfg_clausedef_t * +cfg_map_external_nextclause(const cfg_type_t *map, const void **clauses, + unsigned int *idx); + +const cfg_clausedef_t * +cfg_map_external_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); diff --git a/lib/isccfg/include/isccfg/grammar.h b/lib/isccfg/include/isccfg/grammar.h index 9ad371b7e06..98c7aaad419 100644 --- a/lib/isccfg/include/isccfg/grammar.h +++ b/lib/isccfg/include/isccfg/grammar.h @@ -230,6 +230,7 @@ struct cfg_obj { char *string; /*%< null terminated */ bool boolean; cfg_map_t *map; + cfg_map_t *map_external; cfg_list_t *list; cfg_obj_t **tuple; isc_sockaddr_t *sockaddr; @@ -329,6 +330,7 @@ extern cfg_rep_t cfg_rep_uint64; extern cfg_rep_t cfg_rep_string; extern cfg_rep_t cfg_rep_boolean; extern cfg_rep_t cfg_rep_map; +extern cfg_rep_t cfg_rep_map_external; extern cfg_rep_t cfg_rep_list; extern cfg_rep_t cfg_rep_tuple; extern cfg_rep_t cfg_rep_sockaddr; @@ -545,6 +547,26 @@ cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj); void cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type); +isc_result_t +cfg_parse_map_external(cfg_parser_t *pctx, const cfg_type_t *type, + cfg_obj_t **ret); + +void +cfg_print_map_external(cfg_printer_t *pctx, const cfg_obj_t *obj); + +void +cfg_doc_map_external(cfg_printer_t *pctx, const cfg_type_t *type); + +isc_result_t +cfg_parse_mapbody_external(cfg_parser_t *pctx, const cfg_type_t *type, + cfg_obj_t **ret); + +void +cfg_print_mapbody_external(cfg_printer_t *pctx, const cfg_obj_t *obj); + +void +cfg_doc_mapbody_external(cfg_printer_t *pctx, const cfg_type_t *type); + isc_result_t cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); diff --git a/lib/isccfg/parser.c b/lib/isccfg/parser.c index 83722cb858f..be7e3365028 100644 --- a/lib/isccfg/parser.c +++ b/lib/isccfg/parser.c @@ -133,6 +133,13 @@ create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp); static void free_map(cfg_obj_t *obj); +static void +create_map_external(cfg_parser_t *pctx, const cfg_type_t *type, + cfg_obj_t **objp); + +static void +free_map_external(cfg_obj_t *obj); + static isc_result_t parse_symtab_elt(cfg_parser_t *pctx, const cfg_clausedef_t *clause, isc_symtab_t *symtab); @@ -284,6 +291,42 @@ copy_map(cfg_obj_t *to, const cfg_obj_t *from) { to->value.map->clausesets = from->value.map->clausesets; } +static bool +copy_map_external_add(char *key, unsigned int type, isc_symvalue_t value, + void *arg) { + cfg_obj_t *to = arg; + cfg_obj_t *toelt = NULL; + + REQUIRE(VALID_CFGOBJ(value.as_pointer)); + + cfg_obj_clone(value.as_pointer, &toelt); + value.as_pointer = toelt; + + INSIST(isc_symtab_define(to->value.map_external->symtab, key, type, + value, isc_symexists_reject) == ISC_R_SUCCESS); + + return false; +} + +static void +copy_map_external(cfg_obj_t *to, const cfg_obj_t *from) { + to->value.map_external = + isc_mem_cget(isc_g_mctx, 1, sizeof(*to->value.map_external)); + + if (from->value.map_external->id != NULL) { + cfg_obj_clone(from->value.map_external->id, + &to->value.map_external->id); + } + + isc_symtab_create(isc_g_mctx, copy_map_destroy, NULL, false, + &to->value.map_external->symtab); + isc_symtab_foreach(from->value.map_external->symtab, + copy_map_external_add, to); + + to->value.map_external->clausesets = + from->value.map_external->clausesets; +} + static void copy_list(cfg_obj_t *to, const cfg_obj_t *from) { const cfg_listelt_t *fromelt = cfg_list_first(from); @@ -337,6 +380,8 @@ 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_boolean = { "boolean", free_noop, copy_boolean }; cfg_rep_t cfg_rep_map = { "map", free_map, copy_map }; +cfg_rep_t cfg_rep_map_external = { "map_external", free_map_external, + copy_map_external }; cfg_rep_t cfg_rep_list = { "list", free_list, copy_list }; cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple, copy_tuple }; cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_sockaddr, copy_sockaddr }; @@ -2955,193 +3000,650 @@ cfg_map_findclause(const cfg_type_t *map, const char *name) { return ((cfg_clausedef_t *)clauses) + idx; } -static char * -region_to_string(isc_region_t region) { - size_t len = region.length + 1; - char *str = isc_mem_allocate(isc_g_mctx, len); - - memmove(str, region.base, region.length); - str[region.length] = 0; - - return str; -} - -/* Parse an arbitrary token, storing its raw text representation. */ -static isc_result_t -parse_token(cfg_parser_t *pctx, const cfg_type_t *type ISC_ATTR_UNUSED, - cfg_obj_t **ret) { - cfg_obj_t *obj = NULL; - isc_result_t result; - isc_region_t r; - - cfg_obj_create(cfg_parser_currentfile(pctx), pctx->line, - &cfg_type_token, &obj); - CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); - if (pctx->token.type == isc_tokentype_eof) { - cfg_ungettoken(pctx); - CLEANUP(ISC_R_EOF); - } - - isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r); - obj->value.string = region_to_string(r); - - *ret = obj; - return result; - -cleanup: - if (obj != NULL) { - isc_mem_put(isc_g_mctx, obj, sizeof(*obj)); - } - return result; -} - -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 - * ending in a semicolon. + * map_external variants — identical logic to map, but access + * obj->value.map_external and check &cfg_rep_map_external. */ -static isc_result_t -parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { - cfg_obj_t *listobj = NULL; +isc_result_t +cfg_parse_mapbody_external(cfg_parser_t *pctx, const cfg_type_t *type, + cfg_obj_t **ret) { + const cfg_clausedef_t *const *clausesets; isc_result_t result; - int braces = 0; + const cfg_clausedef_t *const *clauseset; + const cfg_clausedef_t *clause; + cfg_obj_t *value = NULL; + cfg_obj_t *obj = NULL; + cfg_obj_t *eltobj = NULL; + cfg_obj_t *includename = NULL; + isc_symvalue_t symval; - create_list(cfg_parser_currentfile(pctx), pctx->line, type, &listobj); + REQUIRE(pctx != NULL); + REQUIRE(type != NULL); + REQUIRE(ret != NULL && *ret == NULL); - for (;;) { - cfg_listelt_t *elt = NULL; + clausesets = type->of; - CHECK(cfg_peektoken(pctx, 0)); - if (pctx->token.type == isc_tokentype_special) { - if (pctx->token.value.as_char == '{') { - braces++; - } else if (pctx->token.value.as_char == '}') { - braces--; - } else if (pctx->token.value.as_char == ';') { - if (braces == 0) { - break; - } - } - } - if (pctx->token.type == isc_tokentype_eof || braces < 0) { - cfg_parser_error(pctx, CFG_LOG_NEAR, - "unexpected token"); - CLEANUP(ISC_R_UNEXPECTEDTOKEN); - } + create_map_external(pctx, type, &obj); - CHECK(cfg_parse_listelt(pctx, listobj, &cfg_type_token, &elt)); - ISC_LIST_APPEND(*listobj->value.list, elt, link); - } - INSIST(braces == 0); - *ret = listobj; - return ISC_R_SUCCESS; + obj->value.map_external->clausesets = clausesets; -cleanup: - CLEANUP_OBJ(listobj); - return result; -} + for (;;) { + CHECK(cfg_gettoken(pctx, 0)); -cfg_type_t cfg_type_unsupported = { "unsupported", parse_unsupported, - cfg_print_spacelist, cfg_doc_terminal, - &cfg_rep_list, NULL }; + if (pctx->token.type != isc_tokentype_string) { + cfg_ungettoken(pctx); + break; + } -/* - * Try interpreting the current token as a network address. - * - * 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 -token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) { - char *s; - struct in_addr in4a; - struct in6_addr in6a; + if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) { + glob_t g; + int rc; - if (pctx->token.type != isc_tokentype_string) { - return ISC_R_UNEXPECTEDTOKEN; - } + CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, + &includename)); + CHECK(parse_semicolon(pctx)); - s = TOKEN_STRING(pctx); - 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 & CFG_ADDR_V6OK) != 0) { - isc_netaddr_any6(na); - return ISC_R_SUCCESS; - } else { - UNREACHABLE(); - } - } else { - 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 (includename->value.string[0] == 0) { + CLEANUP(ISC_R_FILENOTFOUND); } - } - if ((flags & CFG_ADDR_V4PREFIXOK) != 0 && strlen(s) <= 15U) { - char buf[64]; - int i; - strlcpy(buf, s, sizeof(buf)); - for (i = 0; i < 3; i++) { - strlcat(buf, ".0", sizeof(buf)); - if (inet_pton(AF_INET, buf, &in4a) == 1) { - isc_netaddr_fromin(na, &in4a); - return ISC_R_IPV4PREFIX; + rc = glob(cfg_obj_asstring(includename), GLOB_ERR, NULL, + &g); + + switch (rc) { + case 0: + break; + case GLOB_NOMATCH: + CLEANUP(ISC_R_FILENOTFOUND); + break; + case GLOB_NOSPACE: + CLEANUP(ISC_R_NOMEMORY); + break; + default: + if (errno == 0) { + CLEANUP(ISC_R_IOERROR); } + CHECK(isc_errno_toresult(errno)); } - } - if ((flags & CFG_ADDR_V6OK) != 0 && strlen(s) <= 127U) { - char buf[128]; /* see isc_getaddresses() */ - char *d; /* zone delimiter */ - uint32_t zone = 0; /* scope zone ID */ - strlcpy(buf, s, sizeof(buf)); - d = strchr(buf, '%'); - if (d != NULL) { - *d = '\0'; + for (size_t i = 0; i < g.gl_pathc; ++i) { + CHECK(parser_openfile(pctx, g.gl_pathv[i])); } - if (inet_pton(AF_INET6, buf, &in6a) == 1) { - if (d != NULL) { - RETERR(isc_netscope_pton( - AF_INET6, d + 1, &in6a, &zone)); - } + cfg_obj_detach(&includename); + globfree(&g); - isc_netaddr_fromin6(na, &in6a); - isc_netaddr_setzone(na, zone); - return ISC_R_SUCCESS; - } + continue; } - } - return ISC_R_UNEXPECTEDTOKEN; -} - -isc_result_t -cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) { - isc_result_t result; - const char *wild = ""; - const char *prefix = ""; - - REQUIRE(pctx != NULL); - REQUIRE(na != NULL); - CHECK(cfg_gettoken(pctx, 0)); - result = token_addr(pctx, flags, na); - if (result == ISC_R_UNEXPECTEDTOKEN) { - if ((flags & CFG_ADDR_WILDOK) != 0) { - wild = " or '*'"; + clause = NULL; + for (clauseset = clausesets; *clauseset != NULL; clauseset++) { + for (clause = *clauseset; clause->name != NULL; + clause++) + { + if (strcasecmp(TOKEN_STRING(pctx), + clause->name) == 0) + { + goto done; + } + } } - if ((flags & CFG_ADDR_V4PREFIXOK) != 0) { - wild = " or IPv4 prefix"; + done: + if (clause == NULL || clause->name == NULL) { + cfg_parser_error(pctx, CFG_LOG_NOPREP, + "unknown option"); + CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, + &eltobj)); + cfg_obj_detach(&eltobj); + CHECK(parse_semicolon(pctx)); + continue; + } + + if ((clause->flags & CFG_CLAUSEFLAG_ANCIENT) != 0) { + cfg_parser_error(pctx, 0, + "option '%s' no longer exists", + clause->name); + CLEANUP(ISC_R_FAILURE); + } + if ((pctx->flags & CFG_PCTX_ALLCONFIGS) == 0 && + (clause->flags & CFG_CLAUSEFLAG_NOTCONFIGURED) != 0) + { + cfg_parser_error(pctx, 0, + "option '%s' was not " + "enabled at compile time", + clause->name); + CLEANUP(ISC_R_FAILURE); + } + if ((pctx->flags & CFG_PCTX_BUILTIN) == 0 && + (clause->flags & CFG_CLAUSEFLAG_BUILTINONLY) != 0) + { + cfg_parser_error(pctx, 0, + "option '%s' is allowed in the " + "builtin configuration only", + clause->name); + CHECK(ISC_R_FAILURE); + } + + if ((pctx->flags & CFG_PCTX_NODEPRECATED) == 0 && + (clause->flags & CFG_CLAUSEFLAG_DEPRECATED) != 0) + { + cfg_parser_warning(pctx, 0, "option '%s' is deprecated", + clause->name); + } + if ((pctx->flags & CFG_PCTX_NOOBSOLETE) == 0 && + (clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0) + { + cfg_parser_warning(pctx, 0, + "option '%s' is obsolete and " + "should be removed ", + clause->name); + } + if ((pctx->flags & CFG_PCTX_NOEXPERIMENTAL) == 0 && + (clause->flags & CFG_CLAUSEFLAG_EXPERIMENTAL) != 0) + { + cfg_parser_warning(pctx, 0, + "option '%s' is experimental and " + "subject to change in the future", + clause->name); + } + + if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) { + cfg_obj_t *listobj = NULL; + cfg_listelt_t *elt = NULL; + + create_list(cfg_parser_currentfile(pctx), pctx->line, + &cfg_type_implicitlist, &listobj); + symval.as_pointer = listobj; + result = isc_symtab_define_and_return( + obj->value.map_external->symtab, clause->name, + SYMTAB_DUMMY_TYPE, symval, isc_symexists_reject, + &symval); + if (result == ISC_R_EXISTS) { + CLEANUP_OBJ(listobj); + listobj = symval.as_pointer; + } + + CHECK(cfg_parse_listelt(pctx, listobj, clause->type, + &elt)); + ISC_LIST_APPEND(*listobj->value.list, elt, link); + CHECK(parse_semicolon(pctx)); + } else { + result = parse_symtab_elt( + pctx, clause, + obj->value.map_external->symtab); + if (result == ISC_R_EXISTS) { + cfg_parser_error(pctx, CFG_LOG_NEAR, + "'%s' redefined", + clause->name); + CHECK(result); + } else if (result != ISC_R_SUCCESS) { + cfg_parser_error(pctx, CFG_LOG_NEAR, + "isc_symtab_define() failed"); + CHECK(result); + } + CHECK(parse_semicolon(pctx)); + } + } + + *ret = obj; + return ISC_R_SUCCESS; + +cleanup: + CLEANUP_OBJ(value); + CLEANUP_OBJ(obj); + CLEANUP_OBJ(eltobj); + CLEANUP_OBJ(includename); + return result; +} + +isc_result_t +cfg_parse_map_external(cfg_parser_t *pctx, const cfg_type_t *type, + cfg_obj_t **ret) { + isc_result_t result; + + REQUIRE(pctx != NULL); + REQUIRE(type != NULL); + REQUIRE(ret != NULL && *ret == NULL); + + CHECK(cfg_parse_special(pctx, '{')); + CHECK(cfg_parse_mapbody_external(pctx, type, ret)); + CHECK(cfg_parse_special(pctx, '}')); +cleanup: + return result; +} + +void +cfg_print_mapbody_external(cfg_printer_t *pctx, const cfg_obj_t *obj) { + const cfg_clausedef_t *const *clauseset; + + REQUIRE(pctx != NULL); + REQUIRE(VALID_CFGOBJ(obj)); + + for (clauseset = obj->value.map_external->clausesets; + *clauseset != NULL; clauseset++) + { + isc_symvalue_t symval; + const cfg_clausedef_t *clause; + + for (clause = *clauseset; clause->name != NULL; clause++) { + isc_result_t result; + + if ((clause->flags & CFG_CLAUSEFLAG_BUILTINONLY) != 0) { + continue; + } + + result = isc_symtab_lookup( + obj->value.map_external->symtab, clause->name, + SYMTAB_DUMMY_TYPE, &symval); + if (result == ISC_R_SUCCESS) { + cfg_obj_t *symobj = symval.as_pointer; + if (symobj->type == &cfg_type_implicitlist) { + cfg_list_t *list = symobj->value.list; + ISC_LIST_FOREACH(*list, elt, link) { + print_symval(pctx, clause->name, + elt->obj); + } + } else { + print_symval(pctx, clause->name, + symobj); + } + } else if (result == ISC_R_NOTFOUND) { + /* do nothing */ + } else { + UNREACHABLE(); + } + } + } +} + +void +cfg_print_map_external(cfg_printer_t *pctx, const cfg_obj_t *obj) { + REQUIRE(pctx != NULL); + REQUIRE(VALID_CFGOBJ(obj)); + + if (obj->value.map_external->id != NULL) { + cfg_print_obj(pctx, obj->value.map_external->id); + cfg_print_cstr(pctx, " "); + } + print_open(pctx); + cfg_print_mapbody_external(pctx, obj); + print_close(pctx); +} + +void +cfg_doc_mapbody_external(cfg_printer_t *pctx, const cfg_type_t *type) { + const cfg_clausedef_t *const *clauseset; + const cfg_clausedef_t *clause; + + REQUIRE(pctx != NULL); + REQUIRE(type != NULL); + + for (clauseset = type->of; *clauseset != NULL; clauseset++) { + for (clause = *clauseset; clause->name != NULL; clause++) { + if (((pctx->flags & CFG_PRINTER_ACTIVEONLY) != 0) && + (((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0) || + ((clause->flags & CFG_CLAUSEFLAG_TESTONLY) != 0))) + { + continue; + } + if ((clause->flags & CFG_CLAUSEFLAG_ANCIENT) != 0 || + (clause->flags & CFG_CLAUSEFLAG_NODOC) != 0) + { + continue; + } + cfg_print_cstr(pctx, clause->name); + cfg_print_cstr(pctx, " "); + cfg_doc_obj(pctx, clause->type); + cfg_print_cstr(pctx, ";"); + cfg_print_clauseflags(pctx, clause->flags); + cfg_print_cstr(pctx, "\n\n"); + } + } +} + +void +cfg_doc_map_external(cfg_printer_t *pctx, const cfg_type_t *type) { + const cfg_clausedef_t *const *clauseset; + const cfg_clausedef_t *clause; + + REQUIRE(pctx != NULL); + REQUIRE(type != NULL); + + if (type->parse == cfg_parse_named_map) { + cfg_doc_obj(pctx, &cfg_type_astring); + cfg_print_cstr(pctx, " "); + } else if (type->parse == cfg_parse_addressed_map) { + cfg_doc_obj(pctx, &cfg_type_netaddr); + cfg_print_cstr(pctx, " "); + } else if (type->parse == cfg_parse_netprefix_map) { + cfg_doc_obj(pctx, &cfg_type_netprefix); + cfg_print_cstr(pctx, " "); + } + + print_open(pctx); + + for (clauseset = type->of; *clauseset != NULL; clauseset++) { + for (clause = *clauseset; clause->name != NULL; clause++) { + if (((pctx->flags & CFG_PRINTER_ACTIVEONLY) != 0) && + (((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0) || + ((clause->flags & CFG_CLAUSEFLAG_TESTONLY) != 0))) + { + continue; + } + if ((clause->flags & CFG_CLAUSEFLAG_ANCIENT) != 0 || + (clause->flags & CFG_CLAUSEFLAG_NODOC) != 0) + { + continue; + } + cfg_print_indent(pctx); + cfg_print_cstr(pctx, clause->name); + if (clause->type->print != cfg_print_void) { + cfg_print_cstr(pctx, " "); + } + cfg_doc_obj(pctx, clause->type); + cfg_print_cstr(pctx, ";"); + cfg_print_clauseflags(pctx, clause->flags); + cfg_print_cstr(pctx, "\n"); + } + } + print_close(pctx); +} + +bool +cfg_obj_ismap_external(const cfg_obj_t *obj) { + REQUIRE(VALID_CFGOBJ(obj)); + return obj->type->rep == &cfg_rep_map_external; +} + +isc_result_t +cfg_map_external_get(const cfg_obj_t *mapobj, const char *name, + const cfg_obj_t **obj) { + isc_symvalue_t val; + const cfg_map_t *map; + + REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map_external); + REQUIRE(name != NULL); + REQUIRE(obj != NULL && *obj == NULL); + + map = mapobj->value.map_external; + + RETERR(isc_symtab_lookup(map->symtab, name, SYMTAB_DUMMY_TYPE, &val)); + *obj = val.as_pointer; + return ISC_R_SUCCESS; +} + +const cfg_obj_t * +cfg_map_external_getname(const cfg_obj_t *mapobj) { + REQUIRE(VALID_CFGOBJ(mapobj)); + REQUIRE(mapobj->type->rep == &cfg_rep_map_external); + return mapobj->value.map_external->id; +} + +unsigned int +cfg_map_external_count(const cfg_obj_t *mapobj) { + const cfg_map_t *map; + + REQUIRE(VALID_CFGOBJ(mapobj)); + REQUIRE(mapobj->type->rep == &cfg_rep_map_external); + + map = mapobj->value.map_external; + return isc_symtab_count(map->symtab); +} + +const cfg_clausedef_t * +cfg_map_external_firstclause(const cfg_type_t *map, const void **clauses, + unsigned int *idx) { + cfg_clausedef_t *const *clauseset; + + REQUIRE(map != NULL && map->rep == &cfg_rep_map_external); + REQUIRE(idx != NULL); + REQUIRE(clauses != NULL && *clauses == NULL); + + clauseset = map->of; + if (*clauseset == NULL) { + return NULL; + } + *clauses = *clauseset; + *idx = 0; + while ((*clauseset)[*idx].name == NULL) { + *clauses = (*++clauseset); + if (*clauses == NULL) { + return NULL; + } + } + return &(*clauseset)[*idx]; +} + +const cfg_clausedef_t * +cfg_map_external_nextclause(const cfg_type_t *map, const void **clauses, + unsigned int *idx) { + cfg_clausedef_t *const *clauseset; + + REQUIRE(map != NULL && map->rep == &cfg_rep_map_external); + REQUIRE(idx != NULL); + REQUIRE(clauses != NULL && *clauses != NULL); + + clauseset = map->of; + while (*clauseset != NULL && *clauseset != *clauses) { + clauseset++; + } + INSIST(*clauseset == *clauses); + (*idx)++; + while ((*clauseset)[*idx].name == NULL) { + *idx = 0; + *clauses = (*++clauseset); + if (*clauses == NULL) { + return NULL; + } + } + return &(*clauseset)[*idx]; +} + +const cfg_clausedef_t * +cfg_map_external_findclause(const cfg_type_t *map, const char *name) { + const cfg_clausedef_t *found = NULL; + const void *clauses = NULL; + unsigned int idx; + + REQUIRE(map != NULL && map->rep == &cfg_rep_map_external); + REQUIRE(name != NULL); + + found = cfg_map_external_firstclause(map, &clauses, &idx); + while (name != NULL && strcasecmp(name, found->name)) { + found = cfg_map_external_nextclause(map, &clauses, &idx); + } + + return ((cfg_clausedef_t *)clauses) + idx; +} + +static char * +region_to_string(isc_region_t region) { + size_t len = region.length + 1; + char *str = isc_mem_allocate(isc_g_mctx, len); + + memmove(str, region.base, region.length); + str[region.length] = 0; + + return str; +} + +/* Parse an arbitrary token, storing its raw text representation. */ +static isc_result_t +parse_token(cfg_parser_t *pctx, const cfg_type_t *type ISC_ATTR_UNUSED, + cfg_obj_t **ret) { + cfg_obj_t *obj = NULL; + isc_result_t result; + isc_region_t r; + + cfg_obj_create(cfg_parser_currentfile(pctx), pctx->line, + &cfg_type_token, &obj); + CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); + if (pctx->token.type == isc_tokentype_eof) { + cfg_ungettoken(pctx); + CLEANUP(ISC_R_EOF); + } + + isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r); + obj->value.string = region_to_string(r); + + *ret = obj; + return result; + +cleanup: + if (obj != NULL) { + isc_mem_put(isc_g_mctx, obj, sizeof(*obj)); + } + return result; +} + +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 + * ending in a semicolon. + */ + +static isc_result_t +parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + cfg_obj_t *listobj = NULL; + isc_result_t result; + int braces = 0; + + create_list(cfg_parser_currentfile(pctx), pctx->line, type, &listobj); + + for (;;) { + cfg_listelt_t *elt = NULL; + + CHECK(cfg_peektoken(pctx, 0)); + if (pctx->token.type == isc_tokentype_special) { + if (pctx->token.value.as_char == '{') { + braces++; + } else if (pctx->token.value.as_char == '}') { + braces--; + } else if (pctx->token.value.as_char == ';') { + if (braces == 0) { + break; + } + } + } + if (pctx->token.type == isc_tokentype_eof || braces < 0) { + cfg_parser_error(pctx, CFG_LOG_NEAR, + "unexpected token"); + CLEANUP(ISC_R_UNEXPECTEDTOKEN); + } + + CHECK(cfg_parse_listelt(pctx, listobj, &cfg_type_token, &elt)); + ISC_LIST_APPEND(*listobj->value.list, elt, link); + } + INSIST(braces == 0); + *ret = listobj; + return ISC_R_SUCCESS; + +cleanup: + CLEANUP_OBJ(listobj); + return result; +} + +cfg_type_t cfg_type_unsupported = { "unsupported", parse_unsupported, + cfg_print_spacelist, cfg_doc_terminal, + &cfg_rep_list, NULL }; + +/* + * Try interpreting the current token as a network address. + * + * 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 +token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) { + char *s; + struct in_addr in4a; + struct in6_addr in6a; + + if (pctx->token.type != isc_tokentype_string) { + return ISC_R_UNEXPECTEDTOKEN; + } + + s = TOKEN_STRING(pctx); + 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 & CFG_ADDR_V6OK) != 0) { + isc_netaddr_any6(na); + return ISC_R_SUCCESS; + } else { + UNREACHABLE(); + } + } else { + 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 & CFG_ADDR_V4PREFIXOK) != 0 && strlen(s) <= 15U) { + char buf[64]; + int i; + + strlcpy(buf, s, sizeof(buf)); + for (i = 0; i < 3; i++) { + strlcat(buf, ".0", sizeof(buf)); + if (inet_pton(AF_INET, buf, &in4a) == 1) { + isc_netaddr_fromin(na, &in4a); + return ISC_R_IPV4PREFIX; + } + } + } + if ((flags & CFG_ADDR_V6OK) != 0 && strlen(s) <= 127U) { + char buf[128]; /* see isc_getaddresses() */ + char *d; /* zone delimiter */ + uint32_t zone = 0; /* scope zone ID */ + + strlcpy(buf, s, sizeof(buf)); + d = strchr(buf, '%'); + if (d != NULL) { + *d = '\0'; + } + + if (inet_pton(AF_INET6, buf, &in6a) == 1) { + if (d != NULL) { + RETERR(isc_netscope_pton( + AF_INET6, d + 1, &in6a, &zone)); + } + + isc_netaddr_fromin6(na, &in6a); + isc_netaddr_setzone(na, zone); + return ISC_R_SUCCESS; + } + } + } + return ISC_R_UNEXPECTEDTOKEN; +} + +isc_result_t +cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) { + isc_result_t result; + const char *wild = ""; + const char *prefix = ""; + + REQUIRE(pctx != NULL); + REQUIRE(na != NULL); + + CHECK(cfg_gettoken(pctx, 0)); + result = token_addr(pctx, flags, na); + if (result == ISC_R_UNEXPECTEDTOKEN) { + if ((flags & CFG_ADDR_WILDOK) != 0) { + wild = " or '*'"; + } + if ((flags & CFG_ADDR_V4PREFIXOK) != 0) { + wild = " or IPv4 prefix"; } if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V4OK) { cfg_parser_error(pctx, CFG_LOG_NEAR, @@ -3937,6 +4439,31 @@ free_map(cfg_obj_t *obj) { isc_mem_put(isc_g_mctx, obj->value.map, sizeof(*obj->value.map)); } +static void +create_map_external(cfg_parser_t *pctx, const cfg_type_t *type, + cfg_obj_t **ret) { + isc_symtab_t *symtab = NULL; + cfg_obj_t *obj = NULL; + + cfg_obj_create(cfg_parser_currentfile(pctx), pctx->line, type, &obj); + isc_symtab_create(isc_g_mctx, map_symtabitem_destroy, pctx, false, + &symtab); + + obj->value.map_external = + isc_mem_cget(isc_g_mctx, 1, sizeof(*obj->value.map_external)); + obj->value.map_external->symtab = symtab; + + *ret = obj; +} + +static void +free_map_external(cfg_obj_t *obj) { + CLEANUP_OBJ(obj->value.map_external->id); + isc_symtab_destroy(&obj->value.map_external->symtab); + isc_mem_put(isc_g_mctx, obj->value.map_external, + sizeof(*obj->value.map_external)); +} + bool cfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type) { REQUIRE(VALID_CFGOBJ(obj)); @@ -4103,6 +4630,106 @@ cfg_map_addclone(cfg_obj_t *map, const cfg_obj_t *obj, return result; } +static isc_result_t +map_define_external(cfg_obj_t *mapobj, cfg_obj_t *obj, + const cfg_clausedef_t *clause) { + isc_result_t result; + const cfg_map_t *map; + isc_symvalue_t symval; + + map = mapobj->value.map_external; + result = isc_symtab_lookup(map->symtab, clause->name, SYMTAB_DUMMY_TYPE, + &symval); + if (result == ISC_R_NOTFOUND) { + if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) { + cfg_obj_t *destobj = NULL; + cfg_listelt_t *elt = NULL; + + create_list(obj->file, obj->line, + &cfg_type_implicitlist, &destobj); + cfg_listelt_create(&elt); + cfg_obj_attach(obj, &elt->obj); + ISC_LIST_APPEND(*destobj->value.list, elt, link); + symval.as_pointer = destobj; + } else { + symval.as_pointer = obj; + } + + result = isc_symtab_define(map->symtab, clause->name, + SYMTAB_DUMMY_TYPE, symval, + isc_symexists_reject); + INSIST(result == ISC_R_SUCCESS); + } else { + cfg_obj_t *destobj = symval.as_pointer; + cfg_listelt_t *elt = NULL; + + INSIST(result == ISC_R_SUCCESS); + + if (destobj->type == &cfg_type_implicitlist) { + cfg_listelt_create(&elt); + cfg_obj_attach(obj, &elt->obj); + ISC_LIST_APPEND(*destobj->value.list, elt, link); + } else { + result = ISC_R_EXISTS; + } + } + + return result; +} + +isc_result_t +cfg_map_external_add(cfg_obj_t *mapobj, cfg_obj_t *obj, + const char *clausename) { + const cfg_clausedef_t *clause; + + REQUIRE(VALID_CFGOBJ(obj)); + REQUIRE(VALID_CFGOBJ(mapobj)); + REQUIRE(mapobj->type->rep == &cfg_rep_map_external); + REQUIRE(clausename != NULL); + + clause = cfg_map_external_findclause(mapobj->type, clausename); + if (clause == NULL || clause->name == NULL) { + return ISC_R_FAILURE; + } + + return map_define_external(mapobj, obj, clause); +} + +isc_result_t +cfg_map_external_addclone(cfg_obj_t *map, const cfg_obj_t *obj, + const cfg_clausedef_t *clause) { + isc_result_t result = ISC_R_SUCCESS; + cfg_obj_t *clone = NULL; + + REQUIRE(VALID_CFGOBJ(obj)); + REQUIRE(VALID_CFGOBJ(map)); + REQUIRE(map->type->rep == &cfg_rep_map_external); + REQUIRE(clause != NULL && clause->name != NULL); + + if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) { + const cfg_listelt_t *elt = NULL; + + REQUIRE(cfg_obj_islist(obj)); + + elt = cfg_list_first(obj); + while (elt != NULL && result == ISC_R_SUCCESS) { + cfg_obj_clone(elt->obj, &clone); + clone->cloned = true; + + result = map_define_external(map, clone, clause); + elt = cfg_list_next(elt); + + cfg_obj_detach(&clone); + } + } else { + cfg_obj_clone(obj, &clone); + clone->cloned = true; + result = map_define_external(map, clone, clause); + } + + return result; +} + void cfg_list_addclone(cfg_obj_t *dst, const cfg_obj_t *src, bool prepend) { const cfg_listelt_t *srcelt = NULL;