1
0
Fork 0
bind9/bin/tests/system/hooks/driver/test-async.c
Daniel Baumann f66ff7eae6
Adding upstream version 1:9.20.9.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-21 13:32:37 +02:00

350 lines
8.2 KiB
C

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
/*! \file */
/* aliases for the exported symbols */
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
#include <isc/async.h>
#include <isc/buffer.h>
#include <isc/hash.h>
#include <isc/ht.h>
#include <isc/log.h>
#include <isc/mem.h>
#include <isc/netaddr.h>
#include <isc/result.h>
#include <isc/types.h>
#include <isc/util.h>
#include <ns/client.h>
#include <ns/hooks.h>
#include <ns/log.h>
#include <ns/query.h>
#include <ns/types.h>
#define CHECK(op) \
do { \
result = (op); \
if (result != ISC_R_SUCCESS) { \
goto cleanup; \
} \
} while (0)
/*
* 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 async_instance {
ns_plugin_t *module;
isc_mem_t *mctx;
isc_ht_t *ht;
isc_mutex_t hlock;
isc_log_t *lctx;
} async_instance_t;
typedef struct state {
bool async;
ns_hook_resume_t *rev;
ns_hookpoint_t hookpoint;
isc_result_t origresult;
} state_t;
/*
* Forward declarations of functions referenced in install_hooks().
*/
static ns_hookresult_t
async_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp);
static ns_hookresult_t
async_query_done_begin(void *arg, void *cbdata, isc_result_t *resp);
static ns_hookresult_t
async_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp);
/*%
* Register the functions to be called at each hook point in 'hooktable', using
* memory context 'mctx' for allocating copies of stack-allocated structures
* passed to ns_hook_add(). Make sure 'inst' will be passed as the 'cbdata'
* argument to every callback.
*/
static void
install_hooks(ns_hooktable_t *hooktable, isc_mem_t *mctx,
async_instance_t *inst) {
const ns_hook_t async_init = {
.action = async_qctx_initialize,
.action_data = inst,
};
const ns_hook_t async_donebegin = {
.action = async_query_done_begin,
.action_data = inst,
};
const ns_hook_t async_destroy = {
.action = async_qctx_destroy,
.action_data = inst,
};
ns_hook_add(hooktable, mctx, NS_QUERY_QCTX_INITIALIZED, &async_init);
ns_hook_add(hooktable, mctx, NS_QUERY_DONE_BEGIN, &async_donebegin);
ns_hook_add(hooktable, mctx, NS_QUERY_QCTX_DESTROYED, &async_destroy);
}
static void
logmsg(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
ISC_LOG_INFO, fmt, ap);
va_end(ap);
}
/**
** Mandatory plugin API functions:
**
** - plugin_destroy
** - plugin_register
** - plugin_version
** - plugin_check
**/
/*
* Called by ns_plugin_register() to initialize the plugin and
* register hook functions into the view hook table.
*/
isc_result_t
plugin_register(const char *parameters, const void *cfg, const char *cfg_file,
unsigned long cfg_line, isc_mem_t *mctx, isc_log_t *lctx,
void *actx, ns_hooktable_t *hooktable, void **instp) {
async_instance_t *inst = NULL;
UNUSED(parameters);
UNUSED(cfg);
UNUSED(actx);
isc_log_write(lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
ISC_LOG_INFO,
"registering 'test-async' module from %s:%lu", cfg_file,
cfg_line);
inst = isc_mem_get(mctx, sizeof(*inst));
*inst = (async_instance_t){ .mctx = NULL };
isc_mem_attach(mctx, &inst->mctx);
isc_ht_init(&inst->ht, mctx, 1, ISC_HT_CASE_SENSITIVE);
isc_mutex_init(&inst->hlock);
/*
* Set hook points in the view's hooktable.
*/
install_hooks(hooktable, mctx, inst);
*instp = inst;
return ISC_R_SUCCESS;
}
isc_result_t
plugin_check(const char *parameters, const void *cfg, const char *cfg_file,
unsigned long cfg_line, isc_mem_t *mctx, isc_log_t *lctx,
void *actx) {
UNUSED(parameters);
UNUSED(cfg);
UNUSED(cfg_file);
UNUSED(cfg_line);
UNUSED(mctx);
UNUSED(lctx);
UNUSED(actx);
return ISC_R_SUCCESS;
}
/*
* Called by ns_plugins_free(); frees memory allocated by
* the module when it was registered.
*/
void
plugin_destroy(void **instp) {
async_instance_t *inst = (async_instance_t *)*instp;
if (inst->ht != NULL) {
isc_ht_destroy(&inst->ht);
isc_mutex_destroy(&inst->hlock);
}
isc_mem_putanddetach(&inst->mctx, inst, sizeof(*inst));
*instp = NULL;
return;
}
/*
* Returns plugin API version for compatibility checks.
*/
int
plugin_version(void) {
return NS_PLUGIN_VERSION;
}
static state_t *
client_state_get(const query_ctx_t *qctx, async_instance_t *inst) {
state_t *state = NULL;
isc_result_t result;
LOCK(&inst->hlock);
result = isc_ht_find(inst->ht, (const unsigned char *)&qctx->client,
sizeof(qctx->client), (void **)&state);
UNLOCK(&inst->hlock);
return result == ISC_R_SUCCESS ? state : NULL;
}
static void
client_state_create(const query_ctx_t *qctx, async_instance_t *inst) {
state_t *state = NULL;
isc_result_t result;
state = isc_mem_get(inst->mctx, sizeof(*state));
*state = (state_t){ .async = false };
LOCK(&inst->hlock);
result = isc_ht_add(inst->ht, (const unsigned char *)&qctx->client,
sizeof(qctx->client), state);
UNLOCK(&inst->hlock);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
}
static void
client_state_destroy(const query_ctx_t *qctx, async_instance_t *inst) {
state_t *state = client_state_get(qctx, inst);
isc_result_t result;
if (state == NULL) {
return;
}
LOCK(&inst->hlock);
result = isc_ht_delete(inst->ht, (const unsigned char *)&qctx->client,
sizeof(qctx->client));
UNLOCK(&inst->hlock);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
isc_mem_put(inst->mctx, state, sizeof(*state));
}
static ns_hookresult_t
async_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp) {
query_ctx_t *qctx = (query_ctx_t *)arg;
async_instance_t *inst = (async_instance_t *)cbdata;
state_t *state = NULL;
logmsg("qctx init hook");
*resp = ISC_R_UNSET;
state = client_state_get(qctx, inst);
if (state == NULL) {
client_state_create(qctx, inst);
}
return NS_HOOK_CONTINUE;
}
static void
cancelasync(ns_hookasync_t *hctx) {
UNUSED(hctx);
logmsg("cancelasync");
}
static void
destroyasync(ns_hookasync_t **ctxp) {
ns_hookasync_t *ctx = *ctxp;
logmsg("destroyasync");
*ctxp = NULL;
isc_mem_putanddetach(&ctx->mctx, ctx, sizeof(*ctx));
}
static isc_result_t
doasync(query_ctx_t *qctx, isc_mem_t *mctx, void *arg, isc_loop_t *loop,
isc_job_cb cb, void *evarg, ns_hookasync_t **ctxp) {
ns_hook_resume_t *rev = isc_mem_get(mctx, sizeof(*rev));
ns_hookasync_t *ctx = isc_mem_get(mctx, sizeof(*ctx));
state_t *state = (state_t *)arg;
logmsg("doasync");
*ctx = (ns_hookasync_t){
.cancel = cancelasync,
.destroy = destroyasync,
};
isc_mem_attach(mctx, &ctx->mctx);
qctx->result = DNS_R_NOTIMP;
*rev = (ns_hook_resume_t){
.hookpoint = state->hookpoint,
.origresult = qctx->result,
.saved_qctx = qctx,
.ctx = ctx,
.arg = evarg,
};
state->rev = rev;
isc_async_run(loop, cb, rev);
*ctxp = ctx;
return ISC_R_SUCCESS;
}
static ns_hookresult_t
async_query_done_begin(void *arg, void *cbdata, isc_result_t *resp) {
query_ctx_t *qctx = (query_ctx_t *)arg;
async_instance_t *inst = (async_instance_t *)cbdata;
state_t *state = client_state_get(qctx, inst);
UNUSED(qctx);
UNUSED(cbdata);
UNUSED(state);
logmsg("done begin hook");
if (state->async) {
/* resuming */
state->async = false;
return NS_HOOK_CONTINUE;
}
/* initial call */
state->async = true;
state->hookpoint = NS_QUERY_DONE_BEGIN;
state->origresult = *resp;
ns_query_hookasync(qctx, doasync, state);
return NS_HOOK_RETURN;
}
static ns_hookresult_t
async_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp) {
query_ctx_t *qctx = (query_ctx_t *)arg;
async_instance_t *inst = (async_instance_t *)cbdata;
logmsg("qctx destroy hook");
*resp = ISC_R_UNSET;
if (!qctx->detach_client) {
return NS_HOOK_CONTINUE;
}
client_state_destroy(qctx, inst);
return NS_HOOK_CONTINUE;
}