mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-09-05 09:05:40 +00:00
193 lines
6.6 KiB
C
193 lines
6.6 KiB
C
/*
|
|
* Copyright (C) 2017, 2018 Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* 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 http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#ifndef NS_HOOKS_H
|
|
#define NS_HOOKS_H 1
|
|
|
|
#ifdef NS_HOOKS_ENABLE
|
|
|
|
/*! \file */
|
|
|
|
#include <isc/result.h>
|
|
|
|
/*
|
|
* Hooks provide a way of running a callback function once a certain place in
|
|
* code is reached. Current use is limited to libns unit tests and thus:
|
|
*
|
|
* - hook-related types and macros are not placed in libns header files,
|
|
* - hook-related code is compiled away unless --with-atf is used,
|
|
* - hook-related macro names are prefixed with "NS_".
|
|
*
|
|
* However, the implementation is pretty generic and could be repurposed for
|
|
* general use, e.g. as part of libisc, after some further customization.
|
|
*
|
|
* Hooks are created by inserting a macro into any function returning
|
|
* isc_result_t (NS_PROCESS_HOOK()) or void (NS_PROCESS_HOOK_VOID()). As both
|
|
* of these macros contain a return statement which is inlined into the
|
|
* function into which the hook is inserted, a hook callback is able to cause
|
|
* that function to return at hook insertion point. For functions returning
|
|
* isc_result_t, if a hook callback intends to cause a return at hook insertion
|
|
* point, it also has to set the value to be returned by the function.
|
|
*
|
|
* Hook callbacks are functions which:
|
|
*
|
|
* - return a boolean value; if ISC_TRUE is returned by the callback, the
|
|
* function into which the hook is inserted will return at hook insertion
|
|
* point; if ISC_FALSE is returned by the callback, execution of the
|
|
* function into which the hook is inserted continues normally,
|
|
*
|
|
* - accept three pointers as arguments:
|
|
*
|
|
* - a pointer specified by the hook itself,
|
|
* - a pointer specified upon inserting the callback into the hook table,
|
|
* - a pointer to isc_result_t which will be returned by the function
|
|
* into which the hook is inserted if the callback returns ISC_TRUE.
|
|
*
|
|
* Hook tables are arrays which consist of a number of tuples (one tuple per
|
|
* hook identifier), each of which determines the callback to be invoked when a
|
|
* given hook is processed and the data to be passed to that callback. In an
|
|
* attempt to keep things as simple as possible, current implementation uses
|
|
* hook tables which are statically-sized arrays only allowing a single
|
|
* callback to be invoked for each hook identifier.
|
|
*
|
|
* In order for a hook callback to be called for a given hook, a pointer to
|
|
* that callback (along with an optional pointer to callback-specific data) has
|
|
* to be inserted into the relevant hook table entry for that hook. Replacing
|
|
* whole hook tables is also possible.
|
|
*
|
|
* Consider the following sample code:
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
* ns_hook_t *foo_hook_table = NULL;
|
|
*
|
|
* isc_result_t
|
|
* foo_bar(void) {
|
|
* int val = 42;
|
|
*
|
|
* ...
|
|
*
|
|
* NS_PROCESS_HOOK(foo_hook_table, FOO_EXTRACT_VAL, &val);
|
|
*
|
|
* ...
|
|
*
|
|
* printf("This message may not be printed due to use of hooks.");
|
|
*
|
|
* return (ISC_R_SUCCESS);
|
|
* }
|
|
*
|
|
* isc_boolean_t
|
|
* cause_failure(void *hook_data, void *callback_data, isc_result_t *resultp) {
|
|
* int *valp = (int *)hook_data;
|
|
* isc_boolean_t *calledp = (isc_boolean_t *)callback_data;
|
|
*
|
|
* ...
|
|
*
|
|
* *resultp = ISC_R_FAILURE;
|
|
*
|
|
* return (ISC_TRUE);
|
|
* }
|
|
*
|
|
* isc_boolean_t
|
|
* examine_val(void *hook_data, void *callback_data, isc_result_t *resultp) {
|
|
* int *valp = (int *)hook_data;
|
|
* int *valcopyp = (int *)callback_data;
|
|
*
|
|
* UNUSED(resultp);
|
|
*
|
|
* ...
|
|
*
|
|
* return (ISC_FALSE);
|
|
* }
|
|
*
|
|
* void
|
|
* test_foo_bar(void) {
|
|
* isc_boolean_t called = ISC_FALSE;
|
|
* int valcopy;
|
|
*
|
|
* ns_hook_t my_hooks[FOO_HOOKS_COUNT] = {
|
|
* [FOO_EXTRACT_VAL] = {
|
|
* .callback = cause_failure,
|
|
* .callback_data = &called,
|
|
* },
|
|
* };
|
|
*
|
|
* foo_hook_table = my_hooks;
|
|
* foo_bar();
|
|
*
|
|
* {
|
|
* const ns_hook_t examine_hook = {
|
|
* .callback = examine_val,
|
|
* .callback_data = &valcopy,
|
|
* };
|
|
*
|
|
* my_hooks[FOO_EXTRACT_VAL] = examine_hook;
|
|
* }
|
|
* foo_bar();
|
|
*
|
|
* }
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* When test_foo_bar() is called, "foo_hook_table" is set to "my_hooks". Then
|
|
* foo_bar() gets invoked. Once execution reaches the insertion point for hook
|
|
* FOO_EXTRACT_VAL, cause_failure() will be called with &val as "hook_data" and
|
|
* &called as "callback_data". It can do whatever it pleases with these two
|
|
* values. Eventually, cause_failure() sets *resultp to ISC_R_FAILURE and
|
|
* returns ISC_TRUE, which causes foo_bar() to return ISC_R_FAILURE and never
|
|
* execute the printf() call below hook insertion point.
|
|
*
|
|
* Execution then returns to test_foo_bar(). Unlike before the first call to
|
|
* foo_bar(), this time only a single hook ("examine_hook") is defined instead
|
|
* of a complete hook table. This hook is then subsequently inserted at index
|
|
* FOO_EXTRACT_VAL into the "my_hook" hook table. This causes the hook
|
|
* previously set at that index (the one calling cause_failure()) to be
|
|
* replaced with "examine_hook". Thus, when the second call to foo_bar() is
|
|
* subsequently made, examine_val() will be called with &val as "hook_data" and
|
|
* &valcopy as "callback_data". Contrary to cause_failure(), extract_val()
|
|
* returns ISC_FALSE, which means it does not access "resultp" and does not
|
|
* cause foo_bar() to return at hook insertion point. Thus, printf() will be
|
|
* called this time and foo_bar() will return ISC_R_SUCCESS.
|
|
*/
|
|
|
|
enum {
|
|
NS_QUERY_SETUP_QCTX_INITIALIZED,
|
|
NS_QUERY_LOOKUP_BEGIN,
|
|
NS_QUERY_DONE_BEGIN,
|
|
NS_QUERY_HOOKS_COUNT /* MUST BE LAST */
|
|
};
|
|
|
|
typedef isc_boolean_t
|
|
(*ns_hook_cb_t)(void *hook_data, void *callback_data, isc_result_t *resultp);
|
|
|
|
typedef struct ns_hook {
|
|
ns_hook_cb_t callback;
|
|
void *callback_data;
|
|
} ns_hook_t;
|
|
|
|
#define _NS_PROCESS_HOOK(table, id, data, ...) \
|
|
if (table != NULL) { \
|
|
ns_hook_cb_t _callback = table[id].callback; \
|
|
void *_callback_data = table[id].callback_data; \
|
|
isc_result_t _result; \
|
|
\
|
|
if (_callback != NULL && \
|
|
_callback(data, _callback_data, &_result)) { \
|
|
return __VA_ARGS__; \
|
|
} \
|
|
}
|
|
|
|
#define NS_PROCESS_HOOK(table, id, data) \
|
|
_NS_PROCESS_HOOK(table, id, data, _result)
|
|
|
|
#define NS_PROCESS_HOOK_VOID(table, id, data) \
|
|
_NS_PROCESS_HOOK(table, id, data)
|
|
|
|
LIBNS_EXTERNAL_DATA extern ns_hook_t *ns__hook_table;
|
|
|
|
#endif /* NS_HOOKS_ENABLE */
|
|
#endif /* NS_HOOKS_H */
|