1*bcda20f6Schristos /* $NetBSD: test-async.c,v 1.3 2025/01/26 16:24:50 christos Exp $ */ 28aaca124Schristos 38aaca124Schristos /* 48aaca124Schristos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 58aaca124Schristos * 68aaca124Schristos * SPDX-License-Identifier: MPL-2.0 78aaca124Schristos * 88aaca124Schristos * This Source Code Form is subject to the terms of the Mozilla Public 98aaca124Schristos * License, v. 2.0. If a copy of the MPL was not distributed with this 108aaca124Schristos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 118aaca124Schristos * 128aaca124Schristos * See the COPYRIGHT file distributed with this work for additional 138aaca124Schristos * information regarding copyright ownership. 148aaca124Schristos */ 158aaca124Schristos 168aaca124Schristos /*! \file */ 178aaca124Schristos 188aaca124Schristos /* aliases for the exported symbols */ 198aaca124Schristos 208aaca124Schristos #include <inttypes.h> 218aaca124Schristos #include <stdbool.h> 228aaca124Schristos #include <string.h> 238aaca124Schristos 24*bcda20f6Schristos #include <isc/async.h> 258aaca124Schristos #include <isc/buffer.h> 268aaca124Schristos #include <isc/hash.h> 278aaca124Schristos #include <isc/ht.h> 288aaca124Schristos #include <isc/log.h> 298aaca124Schristos #include <isc/mem.h> 308aaca124Schristos #include <isc/netaddr.h> 318aaca124Schristos #include <isc/result.h> 328aaca124Schristos #include <isc/types.h> 338aaca124Schristos #include <isc/util.h> 348aaca124Schristos 358aaca124Schristos #include <ns/client.h> 368aaca124Schristos #include <ns/hooks.h> 378aaca124Schristos #include <ns/log.h> 388aaca124Schristos #include <ns/query.h> 398aaca124Schristos #include <ns/types.h> 408aaca124Schristos 418aaca124Schristos #define CHECK(op) \ 428aaca124Schristos do { \ 438aaca124Schristos result = (op); \ 448aaca124Schristos if (result != ISC_R_SUCCESS) { \ 458aaca124Schristos goto cleanup; \ 468aaca124Schristos } \ 478aaca124Schristos } while (0) 488aaca124Schristos 498aaca124Schristos /* 508aaca124Schristos * Persistent data for use by this module. This will be associated 518aaca124Schristos * with client object address in the hash table, and will remain 528aaca124Schristos * accessible until the client object is detached. 538aaca124Schristos */ 548aaca124Schristos typedef struct async_instance { 558aaca124Schristos ns_plugin_t *module; 568aaca124Schristos isc_mem_t *mctx; 578aaca124Schristos isc_ht_t *ht; 588aaca124Schristos isc_mutex_t hlock; 598aaca124Schristos isc_log_t *lctx; 608aaca124Schristos } async_instance_t; 618aaca124Schristos 628aaca124Schristos typedef struct state { 638aaca124Schristos bool async; 64*bcda20f6Schristos ns_hook_resume_t *rev; 658aaca124Schristos ns_hookpoint_t hookpoint; 668aaca124Schristos isc_result_t origresult; 678aaca124Schristos } state_t; 688aaca124Schristos 698aaca124Schristos /* 708aaca124Schristos * Forward declarations of functions referenced in install_hooks(). 718aaca124Schristos */ 728aaca124Schristos static ns_hookresult_t 738aaca124Schristos async_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp); 748aaca124Schristos static ns_hookresult_t 758aaca124Schristos async_query_done_begin(void *arg, void *cbdata, isc_result_t *resp); 768aaca124Schristos static ns_hookresult_t 778aaca124Schristos async_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp); 788aaca124Schristos 798aaca124Schristos /*% 808aaca124Schristos * Register the functions to be called at each hook point in 'hooktable', using 818aaca124Schristos * memory context 'mctx' for allocating copies of stack-allocated structures 828aaca124Schristos * passed to ns_hook_add(). Make sure 'inst' will be passed as the 'cbdata' 838aaca124Schristos * argument to every callback. 848aaca124Schristos */ 858aaca124Schristos static void 868aaca124Schristos install_hooks(ns_hooktable_t *hooktable, isc_mem_t *mctx, 878aaca124Schristos async_instance_t *inst) { 888aaca124Schristos const ns_hook_t async_init = { 898aaca124Schristos .action = async_qctx_initialize, 908aaca124Schristos .action_data = inst, 918aaca124Schristos }; 928aaca124Schristos const ns_hook_t async_donebegin = { 938aaca124Schristos .action = async_query_done_begin, 948aaca124Schristos .action_data = inst, 958aaca124Schristos }; 968aaca124Schristos const ns_hook_t async_destroy = { 978aaca124Schristos .action = async_qctx_destroy, 988aaca124Schristos .action_data = inst, 998aaca124Schristos }; 1008aaca124Schristos 1018aaca124Schristos ns_hook_add(hooktable, mctx, NS_QUERY_QCTX_INITIALIZED, &async_init); 1028aaca124Schristos ns_hook_add(hooktable, mctx, NS_QUERY_DONE_BEGIN, &async_donebegin); 1038aaca124Schristos ns_hook_add(hooktable, mctx, NS_QUERY_QCTX_DESTROYED, &async_destroy); 1048aaca124Schristos } 1058aaca124Schristos 1068aaca124Schristos static void 1078aaca124Schristos logmsg(const char *fmt, ...) { 1088aaca124Schristos va_list ap; 1098aaca124Schristos 1108aaca124Schristos va_start(ap, fmt); 1118aaca124Schristos isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS, 1128aaca124Schristos ISC_LOG_INFO, fmt, ap); 1138aaca124Schristos va_end(ap); 1148aaca124Schristos } 1158aaca124Schristos 1168aaca124Schristos /** 1178aaca124Schristos ** Mandatory plugin API functions: 1188aaca124Schristos ** 1198aaca124Schristos ** - plugin_destroy 1208aaca124Schristos ** - plugin_register 1218aaca124Schristos ** - plugin_version 1228aaca124Schristos ** - plugin_check 1238aaca124Schristos **/ 1248aaca124Schristos 1258aaca124Schristos /* 1268aaca124Schristos * Called by ns_plugin_register() to initialize the plugin and 1278aaca124Schristos * register hook functions into the view hook table. 1288aaca124Schristos */ 1298aaca124Schristos isc_result_t 1308aaca124Schristos plugin_register(const char *parameters, const void *cfg, const char *cfg_file, 1318aaca124Schristos unsigned long cfg_line, isc_mem_t *mctx, isc_log_t *lctx, 1328aaca124Schristos void *actx, ns_hooktable_t *hooktable, void **instp) { 1338aaca124Schristos async_instance_t *inst = NULL; 1348aaca124Schristos 1358aaca124Schristos UNUSED(parameters); 1368aaca124Schristos UNUSED(cfg); 1378aaca124Schristos UNUSED(actx); 1388aaca124Schristos 1398aaca124Schristos isc_log_write(lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS, 1408aaca124Schristos ISC_LOG_INFO, 1418aaca124Schristos "registering 'test-async' module from %s:%lu", cfg_file, 1428aaca124Schristos cfg_line); 1438aaca124Schristos 1448aaca124Schristos inst = isc_mem_get(mctx, sizeof(*inst)); 1458aaca124Schristos *inst = (async_instance_t){ .mctx = NULL }; 1468aaca124Schristos isc_mem_attach(mctx, &inst->mctx); 1478aaca124Schristos 148*bcda20f6Schristos isc_ht_init(&inst->ht, mctx, 1, ISC_HT_CASE_SENSITIVE); 1498aaca124Schristos isc_mutex_init(&inst->hlock); 1508aaca124Schristos 1518aaca124Schristos /* 1528aaca124Schristos * Set hook points in the view's hooktable. 1538aaca124Schristos */ 1548aaca124Schristos install_hooks(hooktable, mctx, inst); 1558aaca124Schristos 1568aaca124Schristos *instp = inst; 1578aaca124Schristos 158*bcda20f6Schristos return ISC_R_SUCCESS; 1598aaca124Schristos } 1608aaca124Schristos 1618aaca124Schristos isc_result_t 1628aaca124Schristos plugin_check(const char *parameters, const void *cfg, const char *cfg_file, 1638aaca124Schristos unsigned long cfg_line, isc_mem_t *mctx, isc_log_t *lctx, 1648aaca124Schristos void *actx) { 1658aaca124Schristos UNUSED(parameters); 1668aaca124Schristos UNUSED(cfg); 1678aaca124Schristos UNUSED(cfg_file); 1688aaca124Schristos UNUSED(cfg_line); 1698aaca124Schristos UNUSED(mctx); 1708aaca124Schristos UNUSED(lctx); 1718aaca124Schristos UNUSED(actx); 1728aaca124Schristos 173*bcda20f6Schristos return ISC_R_SUCCESS; 1748aaca124Schristos } 1758aaca124Schristos 1768aaca124Schristos /* 1778aaca124Schristos * Called by ns_plugins_free(); frees memory allocated by 1788aaca124Schristos * the module when it was registered. 1798aaca124Schristos */ 1808aaca124Schristos void 1818aaca124Schristos plugin_destroy(void **instp) { 1828aaca124Schristos async_instance_t *inst = (async_instance_t *)*instp; 1838aaca124Schristos 1848aaca124Schristos if (inst->ht != NULL) { 1858aaca124Schristos isc_ht_destroy(&inst->ht); 1868aaca124Schristos isc_mutex_destroy(&inst->hlock); 1878aaca124Schristos } 1888aaca124Schristos 1898aaca124Schristos isc_mem_putanddetach(&inst->mctx, inst, sizeof(*inst)); 1908aaca124Schristos *instp = NULL; 1918aaca124Schristos 1928aaca124Schristos return; 1938aaca124Schristos } 1948aaca124Schristos 1958aaca124Schristos /* 1968aaca124Schristos * Returns plugin API version for compatibility checks. 1978aaca124Schristos */ 1988aaca124Schristos int 1998aaca124Schristos plugin_version(void) { 200*bcda20f6Schristos return NS_PLUGIN_VERSION; 2018aaca124Schristos } 2028aaca124Schristos 2038aaca124Schristos static state_t * 2048aaca124Schristos client_state_get(const query_ctx_t *qctx, async_instance_t *inst) { 2058aaca124Schristos state_t *state = NULL; 2068aaca124Schristos isc_result_t result; 2078aaca124Schristos 2088aaca124Schristos LOCK(&inst->hlock); 2098aaca124Schristos result = isc_ht_find(inst->ht, (const unsigned char *)&qctx->client, 2108aaca124Schristos sizeof(qctx->client), (void **)&state); 2118aaca124Schristos UNLOCK(&inst->hlock); 2128aaca124Schristos 213*bcda20f6Schristos return result == ISC_R_SUCCESS ? state : NULL; 2148aaca124Schristos } 2158aaca124Schristos 2168aaca124Schristos static void 2178aaca124Schristos client_state_create(const query_ctx_t *qctx, async_instance_t *inst) { 2188aaca124Schristos state_t *state = NULL; 2198aaca124Schristos isc_result_t result; 2208aaca124Schristos 2218aaca124Schristos state = isc_mem_get(inst->mctx, sizeof(*state)); 2228aaca124Schristos *state = (state_t){ .async = false }; 2238aaca124Schristos 2248aaca124Schristos LOCK(&inst->hlock); 2258aaca124Schristos result = isc_ht_add(inst->ht, (const unsigned char *)&qctx->client, 2268aaca124Schristos sizeof(qctx->client), state); 2278aaca124Schristos UNLOCK(&inst->hlock); 2288aaca124Schristos RUNTIME_CHECK(result == ISC_R_SUCCESS); 2298aaca124Schristos } 2308aaca124Schristos 2318aaca124Schristos static void 2328aaca124Schristos client_state_destroy(const query_ctx_t *qctx, async_instance_t *inst) { 2338aaca124Schristos state_t *state = client_state_get(qctx, inst); 2348aaca124Schristos isc_result_t result; 2358aaca124Schristos 2368aaca124Schristos if (state == NULL) { 2378aaca124Schristos return; 2388aaca124Schristos } 2398aaca124Schristos 2408aaca124Schristos LOCK(&inst->hlock); 2418aaca124Schristos result = isc_ht_delete(inst->ht, (const unsigned char *)&qctx->client, 2428aaca124Schristos sizeof(qctx->client)); 2438aaca124Schristos UNLOCK(&inst->hlock); 2448aaca124Schristos RUNTIME_CHECK(result == ISC_R_SUCCESS); 2458aaca124Schristos 2468aaca124Schristos isc_mem_put(inst->mctx, state, sizeof(*state)); 2478aaca124Schristos } 2488aaca124Schristos 2498aaca124Schristos static ns_hookresult_t 2508aaca124Schristos async_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp) { 2518aaca124Schristos query_ctx_t *qctx = (query_ctx_t *)arg; 2528aaca124Schristos async_instance_t *inst = (async_instance_t *)cbdata; 2538aaca124Schristos state_t *state = NULL; 2548aaca124Schristos 2558aaca124Schristos logmsg("qctx init hook"); 2568aaca124Schristos *resp = ISC_R_UNSET; 2578aaca124Schristos 2588aaca124Schristos state = client_state_get(qctx, inst); 2598aaca124Schristos if (state == NULL) { 2608aaca124Schristos client_state_create(qctx, inst); 2618aaca124Schristos } 2628aaca124Schristos 263*bcda20f6Schristos return NS_HOOK_CONTINUE; 2648aaca124Schristos } 2658aaca124Schristos 2668aaca124Schristos static void 2678aaca124Schristos cancelasync(ns_hookasync_t *hctx) { 2688aaca124Schristos UNUSED(hctx); 2698aaca124Schristos logmsg("cancelasync"); 2708aaca124Schristos } 2718aaca124Schristos 2728aaca124Schristos static void 2738aaca124Schristos destroyasync(ns_hookasync_t **ctxp) { 2748aaca124Schristos ns_hookasync_t *ctx = *ctxp; 2758aaca124Schristos 2768aaca124Schristos logmsg("destroyasync"); 2778aaca124Schristos *ctxp = NULL; 2788aaca124Schristos isc_mem_putanddetach(&ctx->mctx, ctx, sizeof(*ctx)); 2798aaca124Schristos } 2808aaca124Schristos 2818aaca124Schristos static isc_result_t 282*bcda20f6Schristos doasync(query_ctx_t *qctx, isc_mem_t *mctx, void *arg, isc_loop_t *loop, 283*bcda20f6Schristos isc_job_cb cb, void *evarg, ns_hookasync_t **ctxp) { 284*bcda20f6Schristos ns_hook_resume_t *rev = isc_mem_get(mctx, sizeof(*rev)); 2858aaca124Schristos ns_hookasync_t *ctx = isc_mem_get(mctx, sizeof(*ctx)); 2868aaca124Schristos state_t *state = (state_t *)arg; 2878aaca124Schristos 2888aaca124Schristos logmsg("doasync"); 289*bcda20f6Schristos *ctx = (ns_hookasync_t){ 290*bcda20f6Schristos .cancel = cancelasync, 291*bcda20f6Schristos .destroy = destroyasync, 292*bcda20f6Schristos }; 2938aaca124Schristos isc_mem_attach(mctx, &ctx->mctx); 2948aaca124Schristos 2958aaca124Schristos qctx->result = DNS_R_NOTIMP; 296*bcda20f6Schristos *rev = (ns_hook_resume_t){ 297*bcda20f6Schristos .hookpoint = state->hookpoint, 298*bcda20f6Schristos .origresult = qctx->result, 299*bcda20f6Schristos .saved_qctx = qctx, 300*bcda20f6Schristos .ctx = ctx, 301*bcda20f6Schristos .arg = evarg, 302*bcda20f6Schristos }; 3038aaca124Schristos 3048aaca124Schristos state->rev = rev; 3058aaca124Schristos 306*bcda20f6Schristos isc_async_run(loop, cb, rev); 3078aaca124Schristos 3088aaca124Schristos *ctxp = ctx; 309*bcda20f6Schristos return ISC_R_SUCCESS; 3108aaca124Schristos } 3118aaca124Schristos 3128aaca124Schristos static ns_hookresult_t 3138aaca124Schristos async_query_done_begin(void *arg, void *cbdata, isc_result_t *resp) { 3148aaca124Schristos query_ctx_t *qctx = (query_ctx_t *)arg; 3158aaca124Schristos async_instance_t *inst = (async_instance_t *)cbdata; 3168aaca124Schristos state_t *state = client_state_get(qctx, inst); 3178aaca124Schristos 3188aaca124Schristos UNUSED(qctx); 3198aaca124Schristos UNUSED(cbdata); 3208aaca124Schristos UNUSED(state); 3218aaca124Schristos 3228aaca124Schristos logmsg("done begin hook"); 3238aaca124Schristos if (state->async) { 3248aaca124Schristos /* resuming */ 3258aaca124Schristos state->async = false; 326*bcda20f6Schristos return NS_HOOK_CONTINUE; 3278aaca124Schristos } 3288aaca124Schristos 3298aaca124Schristos /* initial call */ 3308aaca124Schristos state->async = true; 3318aaca124Schristos state->hookpoint = NS_QUERY_DONE_BEGIN; 3328aaca124Schristos state->origresult = *resp; 3338aaca124Schristos ns_query_hookasync(qctx, doasync, state); 334*bcda20f6Schristos return NS_HOOK_RETURN; 3358aaca124Schristos } 3368aaca124Schristos 3378aaca124Schristos static ns_hookresult_t 3388aaca124Schristos async_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp) { 3398aaca124Schristos query_ctx_t *qctx = (query_ctx_t *)arg; 3408aaca124Schristos async_instance_t *inst = (async_instance_t *)cbdata; 3418aaca124Schristos 3428aaca124Schristos logmsg("qctx destroy hook"); 3438aaca124Schristos *resp = ISC_R_UNSET; 3448aaca124Schristos 3458aaca124Schristos if (!qctx->detach_client) { 346*bcda20f6Schristos return NS_HOOK_CONTINUE; 3478aaca124Schristos } 3488aaca124Schristos 3498aaca124Schristos client_state_destroy(qctx, inst); 3508aaca124Schristos 351*bcda20f6Schristos return NS_HOOK_CONTINUE; 3528aaca124Schristos } 353