xref: /netbsd-src/external/mpl/bind/dist/bin/tests/system/hooks/driver/test-async.c (revision ccd9df534e375a4366c5b55f23782053c7a98d82)
1 /*	$NetBSD: test-async.c,v 1.2 2024/02/21 22:51:27 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 /*! \file */
17 
18 /* aliases for the exported symbols */
19 
20 #include <inttypes.h>
21 #include <stdbool.h>
22 #include <string.h>
23 
24 #include <isc/buffer.h>
25 #include <isc/hash.h>
26 #include <isc/ht.h>
27 #include <isc/log.h>
28 #include <isc/mem.h>
29 #include <isc/netaddr.h>
30 #include <isc/result.h>
31 #include <isc/types.h>
32 #include <isc/util.h>
33 
34 #include <ns/client.h>
35 #include <ns/events.h>
36 #include <ns/hooks.h>
37 #include <ns/log.h>
38 #include <ns/query.h>
39 #include <ns/types.h>
40 
41 #define CHECK(op)                              \
42 	do {                                   \
43 		result = (op);                 \
44 		if (result != ISC_R_SUCCESS) { \
45 			goto cleanup;          \
46 		}                              \
47 	} while (0)
48 
49 /*
50  * Persistent data for use by this module. This will be associated
51  * with client object address in the hash table, and will remain
52  * accessible until the client object is detached.
53  */
54 typedef struct async_instance {
55 	ns_plugin_t *module;
56 	isc_mem_t *mctx;
57 	isc_ht_t *ht;
58 	isc_mutex_t hlock;
59 	isc_log_t *lctx;
60 } async_instance_t;
61 
62 typedef struct state {
63 	bool async;
64 	ns_hook_resevent_t *rev;
65 	ns_hookpoint_t hookpoint;
66 	isc_result_t origresult;
67 } state_t;
68 
69 /*
70  * Forward declarations of functions referenced in install_hooks().
71  */
72 static ns_hookresult_t
73 async_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp);
74 static ns_hookresult_t
75 async_query_done_begin(void *arg, void *cbdata, isc_result_t *resp);
76 static ns_hookresult_t
77 async_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp);
78 
79 /*%
80  * Register the functions to be called at each hook point in 'hooktable', using
81  * memory context 'mctx' for allocating copies of stack-allocated structures
82  * passed to ns_hook_add().  Make sure 'inst' will be passed as the 'cbdata'
83  * argument to every callback.
84  */
85 static void
86 install_hooks(ns_hooktable_t *hooktable, isc_mem_t *mctx,
87 	      async_instance_t *inst) {
88 	const ns_hook_t async_init = {
89 		.action = async_qctx_initialize,
90 		.action_data = inst,
91 	};
92 	const ns_hook_t async_donebegin = {
93 		.action = async_query_done_begin,
94 		.action_data = inst,
95 	};
96 	const ns_hook_t async_destroy = {
97 		.action = async_qctx_destroy,
98 		.action_data = inst,
99 	};
100 
101 	ns_hook_add(hooktable, mctx, NS_QUERY_QCTX_INITIALIZED, &async_init);
102 	ns_hook_add(hooktable, mctx, NS_QUERY_DONE_BEGIN, &async_donebegin);
103 	ns_hook_add(hooktable, mctx, NS_QUERY_QCTX_DESTROYED, &async_destroy);
104 }
105 
106 static void
107 logmsg(const char *fmt, ...) {
108 	va_list ap;
109 
110 	va_start(ap, fmt);
111 	isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
112 		      ISC_LOG_INFO, fmt, ap);
113 	va_end(ap);
114 }
115 
116 /**
117 ** Mandatory plugin API functions:
118 **
119 ** - plugin_destroy
120 ** - plugin_register
121 ** - plugin_version
122 ** - plugin_check
123 **/
124 
125 /*
126  * Called by ns_plugin_register() to initialize the plugin and
127  * register hook functions into the view hook table.
128  */
129 isc_result_t
130 plugin_register(const char *parameters, const void *cfg, const char *cfg_file,
131 		unsigned long cfg_line, isc_mem_t *mctx, isc_log_t *lctx,
132 		void *actx, ns_hooktable_t *hooktable, void **instp) {
133 	async_instance_t *inst = NULL;
134 
135 	UNUSED(parameters);
136 	UNUSED(cfg);
137 	UNUSED(actx);
138 
139 	isc_log_write(lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
140 		      ISC_LOG_INFO,
141 		      "registering 'test-async' module from %s:%lu", cfg_file,
142 		      cfg_line);
143 
144 	inst = isc_mem_get(mctx, sizeof(*inst));
145 	*inst = (async_instance_t){ .mctx = NULL };
146 	isc_mem_attach(mctx, &inst->mctx);
147 
148 	isc_ht_init(&inst->ht, mctx, 16, ISC_HT_CASE_SENSITIVE);
149 	isc_mutex_init(&inst->hlock);
150 
151 	/*
152 	 * Set hook points in the view's hooktable.
153 	 */
154 	install_hooks(hooktable, mctx, inst);
155 
156 	*instp = inst;
157 
158 	return (ISC_R_SUCCESS);
159 }
160 
161 isc_result_t
162 plugin_check(const char *parameters, const void *cfg, const char *cfg_file,
163 	     unsigned long cfg_line, isc_mem_t *mctx, isc_log_t *lctx,
164 	     void *actx) {
165 	UNUSED(parameters);
166 	UNUSED(cfg);
167 	UNUSED(cfg_file);
168 	UNUSED(cfg_line);
169 	UNUSED(mctx);
170 	UNUSED(lctx);
171 	UNUSED(actx);
172 
173 	return (ISC_R_SUCCESS);
174 }
175 
176 /*
177  * Called by ns_plugins_free(); frees memory allocated by
178  * the module when it was registered.
179  */
180 void
181 plugin_destroy(void **instp) {
182 	async_instance_t *inst = (async_instance_t *)*instp;
183 
184 	if (inst->ht != NULL) {
185 		isc_ht_destroy(&inst->ht);
186 		isc_mutex_destroy(&inst->hlock);
187 	}
188 
189 	isc_mem_putanddetach(&inst->mctx, inst, sizeof(*inst));
190 	*instp = NULL;
191 
192 	return;
193 }
194 
195 /*
196  * Returns plugin API version for compatibility checks.
197  */
198 int
199 plugin_version(void) {
200 	return (NS_PLUGIN_VERSION);
201 }
202 
203 static state_t *
204 client_state_get(const query_ctx_t *qctx, async_instance_t *inst) {
205 	state_t *state = NULL;
206 	isc_result_t result;
207 
208 	LOCK(&inst->hlock);
209 	result = isc_ht_find(inst->ht, (const unsigned char *)&qctx->client,
210 			     sizeof(qctx->client), (void **)&state);
211 	UNLOCK(&inst->hlock);
212 
213 	return (result == ISC_R_SUCCESS ? state : NULL);
214 }
215 
216 static void
217 client_state_create(const query_ctx_t *qctx, async_instance_t *inst) {
218 	state_t *state = NULL;
219 	isc_result_t result;
220 
221 	state = isc_mem_get(inst->mctx, sizeof(*state));
222 	*state = (state_t){ .async = false };
223 
224 	LOCK(&inst->hlock);
225 	result = isc_ht_add(inst->ht, (const unsigned char *)&qctx->client,
226 			    sizeof(qctx->client), state);
227 	UNLOCK(&inst->hlock);
228 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
229 }
230 
231 static void
232 client_state_destroy(const query_ctx_t *qctx, async_instance_t *inst) {
233 	state_t *state = client_state_get(qctx, inst);
234 	isc_result_t result;
235 
236 	if (state == NULL) {
237 		return;
238 	}
239 
240 	LOCK(&inst->hlock);
241 	result = isc_ht_delete(inst->ht, (const unsigned char *)&qctx->client,
242 			       sizeof(qctx->client));
243 	UNLOCK(&inst->hlock);
244 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
245 
246 	isc_mem_put(inst->mctx, state, sizeof(*state));
247 }
248 
249 static ns_hookresult_t
250 async_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp) {
251 	query_ctx_t *qctx = (query_ctx_t *)arg;
252 	async_instance_t *inst = (async_instance_t *)cbdata;
253 	state_t *state = NULL;
254 
255 	logmsg("qctx init hook");
256 	*resp = ISC_R_UNSET;
257 
258 	state = client_state_get(qctx, inst);
259 	if (state == NULL) {
260 		client_state_create(qctx, inst);
261 	}
262 
263 	return (NS_HOOK_CONTINUE);
264 }
265 
266 static void
267 cancelasync(ns_hookasync_t *hctx) {
268 	UNUSED(hctx);
269 	logmsg("cancelasync");
270 }
271 
272 static void
273 destroyasync(ns_hookasync_t **ctxp) {
274 	ns_hookasync_t *ctx = *ctxp;
275 
276 	logmsg("destroyasync");
277 	*ctxp = NULL;
278 	isc_mem_putanddetach(&ctx->mctx, ctx, sizeof(*ctx));
279 }
280 
281 static isc_result_t
282 doasync(query_ctx_t *qctx, isc_mem_t *mctx, void *arg, isc_task_t *task,
283 	isc_taskaction_t action, void *evarg, ns_hookasync_t **ctxp) {
284 	ns_hook_resevent_t *rev = (ns_hook_resevent_t *)isc_event_allocate(
285 		mctx, task, NS_EVENT_HOOKASYNCDONE, action, evarg,
286 		sizeof(*rev));
287 	ns_hookasync_t *ctx = isc_mem_get(mctx, sizeof(*ctx));
288 	state_t *state = (state_t *)arg;
289 
290 	logmsg("doasync");
291 	*ctx = (ns_hookasync_t){ .mctx = NULL };
292 	isc_mem_attach(mctx, &ctx->mctx);
293 	ctx->cancel = cancelasync;
294 	ctx->destroy = destroyasync;
295 
296 	rev->hookpoint = state->hookpoint;
297 	rev->origresult = state->origresult;
298 	qctx->result = DNS_R_NOTIMP;
299 	rev->saved_qctx = qctx;
300 	rev->ctx = ctx;
301 
302 	state->rev = rev;
303 
304 	isc_task_send(task, (isc_event_t **)&rev);
305 
306 	*ctxp = ctx;
307 	return (ISC_R_SUCCESS);
308 }
309 
310 static ns_hookresult_t
311 async_query_done_begin(void *arg, void *cbdata, isc_result_t *resp) {
312 	query_ctx_t *qctx = (query_ctx_t *)arg;
313 	async_instance_t *inst = (async_instance_t *)cbdata;
314 	state_t *state = client_state_get(qctx, inst);
315 
316 	UNUSED(qctx);
317 	UNUSED(cbdata);
318 	UNUSED(state);
319 
320 	logmsg("done begin hook");
321 	if (state->async) {
322 		/* resuming */
323 		state->async = false;
324 		return (NS_HOOK_CONTINUE);
325 	}
326 
327 	/* initial call */
328 	state->async = true;
329 	state->hookpoint = NS_QUERY_DONE_BEGIN;
330 	state->origresult = *resp;
331 	ns_query_hookasync(qctx, doasync, state);
332 	return (NS_HOOK_RETURN);
333 }
334 
335 static ns_hookresult_t
336 async_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp) {
337 	query_ctx_t *qctx = (query_ctx_t *)arg;
338 	async_instance_t *inst = (async_instance_t *)cbdata;
339 
340 	logmsg("qctx destroy hook");
341 	*resp = ISC_R_UNSET;
342 
343 	if (!qctx->detach_client) {
344 		return (NS_HOOK_CONTINUE);
345 	}
346 
347 	client_state_destroy(qctx, inst);
348 
349 	return (NS_HOOK_CONTINUE);
350 }
351