diff --git a/bin/hooks/filter-aaaa.c b/bin/hooks/filter-aaaa.c index e70c6c2bb9..ff4218a18b 100644 --- a/bin/hooks/filter-aaaa.c +++ b/bin/hooks/filter-aaaa.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -55,15 +56,36 @@ } while (0) /* - * Set up in the register function. + * Possible values for the settings of filter-aaaa-on-v4 and + * filter-aaaa-on-v6: "no" is NONE, "yes" is FILTER, "break-dnssec" + * is BREAK_DNSSEC. */ -static int module_id; +typedef enum { + NONE = 0, + FILTER = 1, + BREAK_DNSSEC = 2 +} filter_aaaa_t; /* - * Hook data pool. + * Persistent data for use by this module. This will be associated + * with client object address in the hash table, and will remain + * accessible until the client object is detached. + */ +typedef struct filter_data { + filter_aaaa_t mode; + uint32_t flags; +} filter_data_t; + +/* + * Memory pool for use with persistent data. */ static isc_mempool_t *datapool = NULL; +/* + * Hash table associating a client object with its persistent data. + */ +static isc_ht_t *client_ht = NULL; + /* * Per-client flags set by this module */ @@ -82,56 +104,51 @@ static isc_mempool_t *datapool = NULL; * be added to a hook table when this module is registered. */ static bool -filter_qctx_initialize(void *hookdata, void *cbdata, isc_result_t *resp); +filter_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp); static ns_hook_t filter_init = { .action = filter_qctx_initialize, + .action_data = &client_ht, }; static bool -filter_respond_begin(void *hookdata, void *cbdata, isc_result_t *resp); +filter_respond_begin(void *arg, void *cbdata, isc_result_t *resp); static ns_hook_t filter_respbegin = { .action = filter_respond_begin, + .action_data = &client_ht, }; static bool -filter_respond_any_found(void *hookdata, void *cbdata, isc_result_t *resp); +filter_respond_any_found(void *arg, void *cbdata, isc_result_t *resp); static ns_hook_t filter_respanyfound = { .action = filter_respond_any_found, + .action_data = &client_ht, }; static bool -filter_prep_response_begin(void *hookdata, void *cbdata, isc_result_t *resp); +filter_prep_response_begin(void *arg, void *cbdata, isc_result_t *resp); static ns_hook_t filter_prepresp = { .action = filter_prep_response_begin, + .action_data = &client_ht, }; static bool -filter_query_done_send(void *hookdata, void *cbdata, isc_result_t *resp); +filter_query_done_send(void *arg, void *cbdata, isc_result_t *resp); static ns_hook_t filter_donesend = { .action = filter_query_done_send, + .action_data = &client_ht, }; static bool -filter_qctx_destroy(void *hookdata, void *cbdata, isc_result_t *resp); +filter_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp); ns_hook_t filter_destroy = { .action = filter_qctx_destroy, + .action_data = &client_ht, }; /** ** Support for parsing of parameters and configuration of the module. **/ -/* - * Possible values for the settings of filter-aaaa-on-v4 and - * filter-aaaa-on-v6: "no" is NONE, "yes" is FILTER, "break-dnssec" - * is BREAK_DNSSEC. - */ -typedef enum { - NONE = 0, - FILTER = 1, - BREAK_DNSSEC = 2 -} filter_aaaa_t; - /* * Values configured when the module is loaded. */ @@ -256,7 +273,7 @@ parse_parameters(const char *parameters, const void *cfg, * a hook table. */ isc_result_t -hook_register(const unsigned int modid, const char *parameters, +hook_register(const char *parameters, const char *cfg_file, unsigned long cfg_line, const void *cfg, void *actx, ns_hookctx_t *hctx, ns_hooktable_t *hooktable, void **instp) @@ -265,8 +282,6 @@ hook_register(const unsigned int modid, const char *parameters, UNUSED(instp); - module_id = modid; - isc_log_write(hctx->lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS, ISC_LOG_INFO, "loading 'filter-aaaa' " @@ -285,9 +300,11 @@ hook_register(const unsigned int modid, const char *parameters, ns_hook_add(hooktable, NS_QUERY_DONE_SEND, &filter_donesend); ns_hook_add(hooktable, NS_QUERY_QCTX_DESTROYED, &filter_destroy); - CHECK(isc_mempool_create(hctx->mctx, sizeof(filter_aaaa_t), + CHECK(isc_mempool_create(hctx->mctx, sizeof(filter_data_t), &datapool)); + CHECK(isc_ht_init(&client_ht, hctx->mctx, 16)); + /* * Fill the mempool with 1K filter_aaaa state objects at * a time; ideally after a single allocation, the mempool will @@ -318,6 +335,9 @@ void hook_destroy(void **instp) { UNUSED(instp); + if (client_ht != NULL) { + isc_ht_destroy(&client_ht); + } if (datapool != NULL) { isc_mempool_destroy(&datapool); } @@ -369,25 +389,67 @@ is_v6_client(ns_client_t *client) { return (false); } -/* - * Shorthand to refer to the persistent data stored by this module in - * the query context structure. - */ -#define FILTER_MODE(qctx) ((filter_aaaa_t **) &qctx->hookdata[module_id]) +static filter_data_t * +client_state_get(const query_ctx_t *qctx, isc_ht_t **htp) { + filter_data_t *client_state = NULL; + isc_result_t result; + + result = isc_ht_find(*htp, (const unsigned char *)&qctx->client, + sizeof(qctx->client), (void **)&client_state); + + return (result == ISC_R_SUCCESS ? client_state : NULL); +} + +static void +client_state_create(const query_ctx_t *qctx, isc_ht_t **htp) { + filter_data_t *client_state; + isc_result_t result; + + client_state = isc_mempool_get(datapool); + if (client_state == NULL) { + return; + } + + client_state->mode = NONE; + client_state->flags = 0; + + result = isc_ht_add(*htp, (const unsigned char *)&qctx->client, + sizeof(qctx->client), client_state); + RUNTIME_CHECK(result == ISC_R_SUCCESS); +} + +static void +client_state_destroy(const query_ctx_t *qctx, isc_ht_t **htp) { + filter_data_t *client_state = client_state_get(qctx, htp); + isc_result_t result; + + if (client_state == NULL) { + return; + } + + result = isc_ht_delete(*htp, (const unsigned char *)&qctx->client, + sizeof(qctx->client)); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + isc_mempool_put(datapool, client_state); +} /* - * Initialize hook data in the query context, fetching from a memory - * pool. + * Initialize filter state, fetching it from a memory pool and storing it + * in a hash table keyed according to the client object; this enables + * us to retrieve persistent data related to a client query for as long + * as the object persists.. */ static bool -filter_qctx_initialize(void *hookdata, void *cbdata, isc_result_t *resp) { - query_ctx_t *qctx = (query_ctx_t *) hookdata; - filter_aaaa_t **mode = FILTER_MODE(qctx); +filter_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *) arg; + isc_ht_t **htp = (isc_ht_t **) cbdata; + filter_data_t *client_state; - UNUSED(cbdata); - - *mode = isc_mempool_get(datapool); - **mode = NONE; + client_state = client_state_get(qctx, htp); + if (client_state == NULL) { + client_state_create(qctx, htp); + } *resp = ISC_R_UNSET; return (false); @@ -399,12 +461,15 @@ filter_qctx_initialize(void *hookdata, void *cbdata, isc_result_t *resp) { * filter-aaaa-on-v4 and filter-aaaa-on-v6. */ static bool -filter_prep_response_begin(void *hookdata, void *cbdata, isc_result_t *resp) { - query_ctx_t *qctx = (query_ctx_t *) hookdata; - filter_aaaa_t **mode = FILTER_MODE(qctx); +filter_prep_response_begin(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *) arg; + isc_ht_t **htp = (isc_ht_t **) cbdata; + filter_data_t *client_state = client_state_get(qctx, htp); isc_result_t result; - UNUSED(cbdata); + if (client_state == NULL) { + return (false); + } if (v4_aaaa != NONE || v6_aaaa != NONE) { result = ns_client_checkaclsilent(qctx->client, NULL, @@ -413,12 +478,12 @@ filter_prep_response_begin(void *hookdata, void *cbdata, isc_result_t *resp) { v4_aaaa != NONE && is_v4_client(qctx->client)) { - **mode = v4_aaaa; + client_state->mode = v4_aaaa; } else if (result == ISC_R_SUCCESS && v6_aaaa != NONE && is_v6_client(qctx->client)) { - **mode = v6_aaaa; + client_state->mode = v6_aaaa; } } @@ -434,15 +499,18 @@ filter_prep_response_begin(void *hookdata, void *cbdata, isc_result_t *resp) { * queries; ANY queries are handled in query_filter_aaaa_any().) */ static bool -filter_respond_begin(void *hookdata, void *cbdata, isc_result_t *resp) { - query_ctx_t *qctx = (query_ctx_t *) hookdata; - filter_aaaa_t **mode = FILTER_MODE(qctx); +filter_respond_begin(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *) arg; + isc_ht_t **htp = (isc_ht_t **) cbdata; + filter_data_t *client_state = client_state_get(qctx, htp); isc_result_t result = ISC_R_UNSET; - UNUSED(cbdata); + if (client_state == NULL) { + return (false); + } - if (**mode != BREAK_DNSSEC && - (**mode != FILTER || + if (client_state->mode != BREAK_DNSSEC && + (client_state->mode != FILTER || (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL && dns_rdataset_isassociated(qctx->sigrdataset)))) { @@ -487,8 +555,7 @@ filter_respond_begin(void *hookdata, void *cbdata, isc_result_t *resp) { qctx->sigrdataset->attributes |= DNS_RDATASETATTR_RENDERED; } - qctx->client->hookflags[module_id] |= - FILTER_AAAA_FILTERED; + client_state->flags |= FILTER_AAAA_FILTERED; } else if (!qctx->authoritative && RECURSIONOK(qctx->client) && (result == DNS_R_DELEGATION || @@ -507,15 +574,13 @@ filter_respond_begin(void *hookdata, void *cbdata, isc_result_t *resp) { qctx->client->query.qname, NULL, NULL, qctx->resuming); if (result == ISC_R_SUCCESS) { - qctx->client->hookflags[module_id] |= - FILTER_AAAA_RECURSING; + client_state->flags |= FILTER_AAAA_RECURSING; qctx->client->query.attributes |= NS_QUERYATTR_RECURSING; } } } else if (qctx->qtype == dns_rdatatype_a && - ((qctx->client->hookflags[module_id] & - FILTER_AAAA_RECURSING) != 0)) + (client_state->flags & FILTER_AAAA_RECURSING) != 0) { dns_rdataset_t *mrdataset = NULL; dns_rdataset_t *sigrdataset = NULL; @@ -539,7 +604,7 @@ filter_respond_begin(void *hookdata, void *cbdata, isc_result_t *resp) { sigrdataset->attributes |= DNS_RDATASETATTR_RENDERED; } - qctx->client->hookflags[module_id] &= ~FILTER_AAAA_RECURSING; + client_state->flags &= ~FILTER_AAAA_RECURSING; result = ns_query_done(qctx); @@ -556,17 +621,20 @@ filter_respond_begin(void *hookdata, void *cbdata, isc_result_t *resp) { * When answering an ANY query, remove AAAA if A is present. */ static bool -filter_respond_any_found(void *hookdata, void *cbdata, isc_result_t *resp) { - query_ctx_t *qctx = (query_ctx_t *) hookdata; - filter_aaaa_t **mode = FILTER_MODE(qctx); +filter_respond_any_found(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *) arg; + isc_ht_t **htp = (isc_ht_t **) cbdata; + filter_data_t *client_state = client_state_get(qctx, htp); dns_name_t *name = NULL; dns_rdataset_t *aaaa = NULL, *aaaa_sig = NULL; dns_rdataset_t *a = NULL; bool have_a = true; - UNUSED(cbdata); + if (client_state == NULL) { + return (false); + } - if (**mode == NONE) { + if (client_state->mode == NONE) { *resp = ISC_R_UNSET; return (false); } @@ -598,7 +666,7 @@ filter_respond_any_found(void *hookdata, void *cbdata, isc_result_t *resp) { if (have_a && aaaa != NULL && (aaaa_sig == NULL || !WANTDNSSEC(qctx->client) || - **mode == BREAK_DNSSEC)) + client_state->mode == BREAK_DNSSEC)) { qctx->client->message->flags &= ~DNS_MESSAGEFLAG_AD; aaaa->attributes |= DNS_RDATASETATTR_RENDERED; @@ -617,14 +685,17 @@ filter_respond_any_found(void *hookdata, void *cbdata, isc_result_t *resp) { * section. */ static bool -filter_query_done_send(void *hookdata, void *cbdata, isc_result_t *resp) { - query_ctx_t *qctx = (query_ctx_t *) hookdata; - filter_aaaa_t **mode = FILTER_MODE(qctx); +filter_query_done_send(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *) arg; + isc_ht_t **htp = (isc_ht_t **) cbdata; + filter_data_t *client_state = client_state_get(qctx, htp); isc_result_t result; - UNUSED(cbdata); + if (client_state == NULL) { + return (false); + } - if (**mode == NONE) { + if (client_state->mode == NONE) { *resp = ISC_R_UNSET; return (false); } @@ -658,7 +729,7 @@ filter_query_done_send(void *hookdata, void *cbdata, isc_result_t *resp) { dns_rdatatype_aaaa, &aaaa_sig); if (aaaa_sig == NULL || !WANTDNSSEC(qctx->client) || - **mode == BREAK_DNSSEC) + client_state->mode == BREAK_DNSSEC) { aaaa->attributes |= DNS_RDATASETATTR_RENDERED; if (aaaa_sig != NULL) { @@ -668,7 +739,7 @@ filter_query_done_send(void *hookdata, void *cbdata, isc_result_t *resp) { } } - if ((qctx->client->hookflags[module_id] & FILTER_AAAA_FILTERED) != 0) { + if ((client_state->flags & FILTER_AAAA_FILTERED) != 0) { result = dns_message_firstname(qctx->client->message, DNS_SECTION_AUTHORITY); while (result == ISC_R_SUCCESS) { @@ -704,20 +775,20 @@ filter_query_done_send(void *hookdata, void *cbdata, isc_result_t *resp) { } /* - * Return hook data to the mempool. + * If the client is being detached, then we can delete our persistent + * data from hash table and return it to the memory pool. */ static bool -filter_qctx_destroy(void *hookdata, void *cbdata, isc_result_t *resp) { - query_ctx_t *qctx = (query_ctx_t *) hookdata; - filter_aaaa_t **mode = FILTER_MODE(qctx); +filter_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *) arg; + isc_ht_t **htp = (isc_ht_t **) cbdata; - UNUSED(cbdata); - - if (*mode != NULL) { - isc_mempool_put(datapool, *mode); - *mode = NULL; + if (!qctx->detach_client) { + return (false); } + client_state_destroy(qctx, htp); + *resp = ISC_R_UNSET; return (false); } diff --git a/bin/named/server.c b/bin/named/server.c index cd9ceb55ae..e4f1883edf 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -1537,8 +1537,7 @@ configure_dyndb(const cfg_obj_t *dyndb, isc_mem_t *mctx, } static isc_result_t -configure_hook(ns_hooktable_t *hooktable, const unsigned int modid, - const cfg_obj_t *hook, +configure_hook(ns_hooktable_t *hooktable, const cfg_obj_t *hook, const cfg_obj_t *config, ns_hookctx_t *hctx) { isc_result_t result = ISC_R_SUCCESS; @@ -1563,7 +1562,7 @@ configure_hook(ns_hooktable_t *hooktable, const unsigned int modid, if (obj != NULL && cfg_obj_isstring(obj)) { parameters = cfg_obj_asstring(obj); } - result = ns_hookmodule_load(library, modid, parameters, + result = ns_hookmodule_load(library, parameters, cfg_obj_file(obj), cfg_obj_line(obj), config, named_g_aclconfctx, hctx, hooktable); @@ -3760,7 +3759,6 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, unsigned int resolver_param; dns_ntatable_t *ntatable = NULL; const char *qminmode = NULL; - unsigned int module_counter = 0; REQUIRE(DNS_VIEW_VALID(view)); @@ -5310,8 +5308,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, #ifdef HAVE_DLOPEN if (hook_list != NULL) { - const void *hashinit = isc_hash_get_initializer(); - CHECK(ns_hook_createctx(mctx, hashinit, &hctx)); + CHECK(ns_hook_createctx(mctx, &hctx)); INSIST(view->hooktable == NULL); CHECK(ns_hooktable_create(view->mctx, @@ -5325,20 +5322,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, { const cfg_obj_t *hook = cfg_listelt_value(element); - if (view->hooktable == NULL) { - CHECK(ns_hooktable_create(view->mctx, - (ns_hooktable_t **) &view->hooktable)); - view->hooktable_free = ns_hooktable_free; - } - - if (hctx == NULL) { - CHECK(ns_hook_createctx(mctx, &hctx)); - } - - CHECK(configure_hook(view->hooktable, module_counter, - hook, config, hctx)); - - module_counter++; + CHECK(configure_hook(view->hooktable, hook, config, hctx)); } #endif diff --git a/lib/ns/client.c b/lib/ns/client.c index 1c45094833..36dbb1d2ab 100644 --- a/lib/ns/client.c +++ b/lib/ns/client.c @@ -3047,7 +3047,6 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) { ISC_QLINK_INIT(client, ilink); client->keytag = NULL; client->keytag_len = 0; - memset(client->hookflags, 0, sizeof(client->hookflags)); /* * We call the init routines for the various kinds of client here, diff --git a/lib/ns/hooks.c b/lib/ns/hooks.c index d94cfdfdcd..0f0abbf4ea 100644 --- a/lib/ns/hooks.c +++ b/lib/ns/hooks.c @@ -339,8 +339,7 @@ unload_library(ns_hook_module_t **hmodp) { #endif /* HAVE_DLFCN_H */ isc_result_t -ns_hookmodule_load(const char *modpath, const unsigned int modid, - const char *parameters, +ns_hookmodule_load(const char *modpath, const char *parameters, const char *cfg_file, unsigned long cfg_line, const void *cfg, void *actx, ns_hookctx_t *hctx, ns_hooktable_t *hooktable) @@ -357,9 +356,8 @@ ns_hookmodule_load(const char *modpath, const unsigned int modid, "loading module '%s'", modpath); CHECK(load_library(hctx->mctx, modpath, &hmod)); - CHECK(hmod->register_func(modid, parameters, cfg_file, cfg_line, - cfg, actx, hctx, hooktable, - &hmod->inst)); + CHECK(hmod->register_func(parameters, cfg_file, cfg_line, + cfg, actx, hctx, hooktable, &hmod->inst)); ISC_LIST_APPEND(hook_modules, hmod, link); diff --git a/lib/ns/include/ns/client.h b/lib/ns/include/ns/client.h index 6d030c3180..7d25eb9360 100644 --- a/lib/ns/include/ns/client.h +++ b/lib/ns/include/ns/client.h @@ -170,12 +170,6 @@ struct ns_client { uint32_t expire; unsigned char *keytag; uint16_t keytag_len; - - /*% - * Allows a hook module to set flags - * that persist across recursion. - */ - uint32_t hookflags[NS_MAX_MODULES]; }; typedef ISC_QUEUE(ns_client_t) client_queue_t; diff --git a/lib/ns/include/ns/hooks.h b/lib/ns/include/ns/hooks.h index f86364ac60..e0418051ad 100644 --- a/lib/ns/include/ns/hooks.h +++ b/lib/ns/include/ns/hooks.h @@ -246,8 +246,7 @@ typedef struct ns_hookctx { #define NS_HOOK_AGE 0 #endif -typedef isc_result_t ns_hook_register_t(const unsigned int modid, - const char *parameters, +typedef isc_result_t ns_hook_register_t(const char *parameters, const char *file, unsigned long line, const void *cfg, @@ -302,8 +301,7 @@ ns_hook_destroyctx(ns_hookctx_t **hctxp); */ isc_result_t -ns_hookmodule_load(const char *modpath, const unsigned int modid, - const char *parameters, +ns_hookmodule_load(const char *modpath, const char *parameters, const char *cfg_file, unsigned long cfg_line, const void *cfg, void *actx, ns_hookctx_t *hctx, ns_hooktable_t *hooktable); diff --git a/lib/ns/include/ns/query.h b/lib/ns/include/ns/query.h index b3fc3a742c..5e38d48fb2 100644 --- a/lib/ns/include/ns/query.h +++ b/lib/ns/include/ns/query.h @@ -27,12 +27,6 @@ #include -/* - * Maximum number of query hook modules that can be configured; - * more than this will overflow qctx->hookdata. - */ -#define NS_MAX_MODULES 16 - /*% nameserver database version structure */ typedef struct ns_dbversion { dns_db_t *db; @@ -156,6 +150,8 @@ struct query_ctx { dns_fixedname_t dsname; /* name needing DS */ ns_client_t *client; /* client object */ + bool detach_client; /* client needs detaching */ + dns_fetchevent_t *event; /* recursion event */ dns_db_t *db; /* zone or cache database */ @@ -174,8 +170,6 @@ struct query_ctx { dns_view_t *view; /* client view */ - void *hookdata[NS_MAX_MODULES]; /* data used by query hooks */ - isc_result_t result; /* query result */ int line; /* line to report error */ }; diff --git a/lib/ns/query.c b/lib/ns/query.c index 3dcf23e7cc..e9b703805d 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -4936,6 +4936,9 @@ qctx_destroy(query_ctx_t *qctx) { CALL_HOOK_NORETURN(NS_QUERY_QCTX_DESTROYED, qctx); dns_view_detach(&qctx->view); + if (qctx->detach_client) { + ns_client_detach(&qctx->client); + } } /*% @@ -6915,9 +6918,9 @@ query_respond_any(query_ctx_t *qctx) { } /* - * If we're here, no matching rdatasets were found in - * cache. If we were searching for RRSIG/SIG, that - * may be okay, but otherwise something's gone wrong. + * If we're here, no matching rdatasets were found. If we were + * searching for RRSIG/SIG, that may be okay, but otherwise + * something's gone wrong. */ INSIST(qctx->qtype == dns_rdatatype_rrsig || qctx->qtype == dns_rdatatype_sig); @@ -10517,7 +10520,7 @@ ns_query_done(query_ctx_t *qctx) { query_error(qctx->client, qctx->result, qctx->line); } - ns_client_detach(&qctx->client); + qctx->detach_client = true; return (qctx->result); } @@ -10561,7 +10564,7 @@ ns_query_done(query_ctx_t *qctx) { query_send(qctx->client); - ns_client_detach(&qctx->client); + qctx->detach_client = true; return (qctx->result); cleanup: