xref: /netbsd-src/external/mpl/bind/dist/bin/tests/system/hooks/driver/test-async.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
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