summaryrefslogtreecommitdiffstats
path: root/lib/ns/include/ns/hooks.h
blob: cb146ffa218dd053b20f2a041d4f0f6bcb420812 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
/*
 * 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.
 */

#pragma once

/*! \file */

#include <stdbool.h>

#include <isc/event.h>
#include <isc/list.h>
#include <isc/magic.h>
#include <isc/mem.h>
#include <isc/result.h>
#include <isc/task.h>

#include <dns/rdatatype.h>

#include <ns/client.h>
#include <ns/query.h>
/*
 * "Hooks" are a mechanism to call a defined function or set of functions once
 * a certain place in code is reached.  Hook actions can inspect and alter the
 * state of an ongoing process, allowing processing to continue afterward or
 * triggering an early return.
 *
 * Currently hooks are used in two ways: in plugins, which use them to
 * add functionality to query processing, and in the unit tests for libns,
 * where they are used to inspect state before and after certain functions have
 * run.
 *
 * Both of these uses are limited to libns, so hooks are currently defined in
 * the ns/hooks.h header file, and hook-related macro and function names are
 * prefixed with `NS_` and `ns_`.  However, the design is fairly generic and
 * could be repurposed for general use, e.g. as part of libisc, after some
 * further customization.
 *
 * Hooks are created by defining a hook point identifier in the ns_hookpoint_t
 * enum below, and placing a special call at a corresponding location in the
 * code which invokes the action(s) for that hook; there are two such special
 * calls currently implemented, namely the CALL_HOOK() and CALL_HOOK_NORETURN()
 * macros in query.c.  The former macro contains a "goto cleanup" statement
 * which is inlined into the function into which the hook has been inserted;
 * this enables the hook action to cause the calling function to return from
 * the hook insertion point.  For functions returning isc_result_t, if a hook
 * action intends to cause a return at hook insertion point, it also has to set
 * the value to be returned by the calling function.
 *
 * A hook table is an array (indexed by the value of the hook point identifier)
 * in which each cell contains a linked list of structures, each of which
 * contains a function pointer to a hook action and a pointer to data which is
 * to be passed to the action function when it is called.
 *
 * Each view has its own separate hook table, populated by loading plugin
 * modules specified in the "plugin" statements in named.conf.  There is also a
 * special, global hook table (ns__hook_table) that is only used by libns unit
 * tests and whose existence can be safely ignored by plugin modules.
 *
 * Hook actions are functions which:
 *
 *   - return an ns_hookresult_t value:
 *       - if NS_HOOK_RETURN is returned by the hook action, the function
 *         into which the hook is inserted will return and no further hook
 *         actions at the same hook point will be invoked,
 *       - if NS_HOOK_CONTINUE is returned by the hook action and there are
 *         further hook actions set up at the same hook point, they will be
 *         processed; if NS_HOOK_CONTINUE is returned and there are no
 *         further hook actions set up at the same hook point, execution of
 *         the function into which the hook has been inserted will be
 *         resumed.
 *
 *   - accept three pointers as arguments:
 *       - a pointer specified by the special call at the hook insertion point,
 *       - a pointer specified upon inserting the action into the hook table,
 *       - a pointer to an isc_result_t value which will be returned by the
 *         function into which the hook is inserted if the action returns
 *         NS_HOOK_RETURN.
 *
 * In order for a hook action to be called for a given hook, a pointer to that
 * action function (along with an optional pointer to action-specific data) has
 * to be inserted into the relevant hook table entry for that hook using an
 * ns_hook_add() call.  If multiple actions are set up at a single hook point
 * (e.g. by multiple plugin modules), they are processed in FIFO order, that is
 * they are performed in the same order in which their relevant ns_hook_add()
 * calls were issued.  Since the configuration is loaded from a single thread,
 * this means that multiple actions at a single hook point are determined by
 * the order in which the relevant plugin modules were declared in the
 * configuration file(s).  The hook API currently does not support changing
 * this order.
 *
 * As an example, consider the following hypothetical function in query.c:
 *
 * ----------------------------------------------------------------------------
 * static isc_result_t
 * query_foo(query_ctx_t *qctx) {
 *     isc_result_t result;
 *
 *     CALL_HOOK(NS_QUERY_FOO_BEGIN, qctx);
 *
 *     ns_client_log(qctx->client, NS_LOGCATEGORY_CLIENT, NS_LOGMODULE_QUERY,
 *                   ISC_LOG_DEBUG(99), "Lorem ipsum dolor sit amet...");
 *
 *     result = ISC_R_COMPLETE;
 *
 *  cleanup:
 *     return (result);
 * }
 * ----------------------------------------------------------------------------
 *
 * and the following hook action:
 *
 * ----------------------------------------------------------------------------
 * static ns_hookresult_t
 * cause_failure(void *hook_data, void *action_data, isc_result_t *resultp) {
 *     UNUSED(hook_data);
 *     UNUSED(action_data);
 *
 *     *resultp = ISC_R_FAILURE;
 *
 *     return (NS_HOOK_RETURN);
 * }
 * ----------------------------------------------------------------------------
 *
 * If this hook action was installed in the hook table using:
 *
 * ----------------------------------------------------------------------------
 * const ns_hook_t foo_fail = {
 *     .action = cause_failure,
 * };
 *
 * ns_hook_add(..., NS_QUERY_FOO_BEGIN, &foo_fail);
 * ----------------------------------------------------------------------------
 *
 * then query_foo() would return ISC_R_FAILURE every time it is called due
 * to the cause_failure() hook action returning NS_HOOK_RETURN and setting
 * '*resultp' to ISC_R_FAILURE.  query_foo() would also never log the
 * "Lorem ipsum dolor sit amet..." message.
 *
 * Consider a different hook action:
 *
 * ----------------------------------------------------------------------------
 * static ns_hookresult_t
 * log_qtype(void *hook_data, void *action_data, isc_result_t *resultp) {
 *     query_ctx_t *qctx = (query_ctx_t *)hook_data;
 *     FILE *stream = (FILE *)action_data;
 *
 *     UNUSED(resultp);
 *
 *     fprintf(stream, "QTYPE=%u\n", qctx->qtype);
 *
 *     return (NS_HOOK_CONTINUE);
 * }
 * ----------------------------------------------------------------------------
 *
 * If this hook action was installed in the hook table instead of
 * cause_failure(), using:
 *
 * ----------------------------------------------------------------------------
 * const ns_hook_t foo_log_qtype = {
 *     .action = log_qtype,
 *     .action_data = stderr,
 * };
 *
 * ns_hook_add(..., NS_QUERY_FOO_BEGIN, &foo_log_qtype);
 * ----------------------------------------------------------------------------
 *
 * then the QTYPE stored in the query context passed to query_foo() would be
 * logged to stderr upon each call to that function; 'qctx' would be passed to
 * the hook action in 'hook_data' since it is specified in the CALL_HOOK() call
 * inside query_foo() while stderr would be passed to the hook action in
 * 'action_data' since it is specified in the ns_hook_t structure passed to
 * ns_hook_add().  As the hook action returns NS_HOOK_CONTINUE,
 * query_foo() would also be logging the "Lorem ipsum dolor sit amet..."
 * message before returning ISC_R_COMPLETE.
 *
 * ASYNCHRONOUS EVENT HANDLING IN QUERY HOOKS
 *
 * Usually a hook action works synchronously; it completes some particular
 * job in the middle of query processing, thus blocking the caller (and the
 * worker thread handling the query).  But sometimes an action can be time
 * consuming and the blocking behavior may not be acceptable.  For example,
 * a hook may need to send some kind of query (like a DB lookup) to an
 * external backend server and wait for the response to complete the hook's
 * action.  Depending on the network condition, the external server's load,
 * etc, it may take several seconds or more.
 *
 * In order to handle such a situation, a hook action can start an
 * asynchronous event by calling ns_query_hookasync().  This is similar
 * to ns_query_recurse(), but more generic.  ns_query_hookasync() will
 * call the 'runasync' function with a specified 'arg' (both passed to
 * ns_query_hookasync()) and a set of task and associated event arguments
 * to be called to resume query handling upon completion of the
 * asynchronous event.
 *
 * The implementation of 'runasync' is assumed to allocate and build an
 * instance of ns_hook_resevent_t whose action, arg, and task are set to
 * the passed values from ns_query_hookasync().  Other fields of
 * ns_hook_resevent_t must be correctly set in the hook implementation
 * by the time it's sent to the specified task:
 *
 * - hookpoint: the point from which the query handling should be resumed
 *   (which should usually be the hook point that triggered the asynchronous
 *   event).
 * - origresult: the result code passed to the hook action that triggers the
 *   asynchronous event through the 'resultp' pointer.  Some hook points need
 *   this value to correctly resume the query handling.
 * - saved_qctx: the 'qctx' passed to 'runasync'.  This holds some
 *   intermediate data for resolving the query, and will be used to resume the
 *   query handling.  The 'runasync' implementation must not modify it.
 *
 * The hook implementation should somehow maintain the created event
 * instance so that it can eventually send the event.
 *
 * 'runasync' then creates an instance of ns_hookasync_t with specifying its
 * own cancel and destroy function, and returns it to ns_query_hookasync()
 * in the passed pointer.
 *
 * On return from ns_query_hookasync(), the hook action MUST return
 * NS_HOOK_RETURN to suspend the query handling.
 *
 * On the completion of the asynchronous event, the hook implementation is
 * supposed to send the resumeevent to the corresponding task.  The query
 * module resumes the query handling so that the hook action of the
 * specified hook point will be called, skipping some intermediate query
 * handling steps.  So, typically, the same hook action will be called
 * twice.  The hook implementation must somehow remember the context, and
 * handle the second call to complete its action using the result of the
 * asynchronous event.
 *
 * Example: assume the following hook-specific structure to manage
 * asynchronous events:
 *
 * typedef struct hookstate {
 * 	bool async;
 *	ns_hook_resevent_t *rev
 *	ns_hookpoint_t hookpoint;
 *	isc_result_t origresult;
 * } hookstate_t;
 *
 * 'async' is supposed to be true if and only if hook-triggered
 * asynchronous processing is taking place.
 *
 * A hook action that uses an asynchronous event would look something
 * like this:
 *
 * hook_recurse(void *hook_data, void *action_data, isc_result_t *resultp) {
 *	hookstate_t *state = somehow_retrieve_from(action_data);
 *	if (state->async) {
 *		// just resumed from an asynchronous hook action.
 *		// complete the hook's action using the result of the
 *		// internal asynchronous event.
 *		state->async = false;
 *		return (NS_HOOK_CONTINUE);
 *	}
 *
 *	// Initial call to the hook action.  Start the internal
 *	// asynchronous event, and have the query module suspend
 *	// its own handling by returning NS_HOOK_RETURN.
 *	state->hookpoint = ...; // would be hook point for this hook
 *	state->origresult = *resultp;
 *	ns_query_hookasync(hook_data, runasync, state);
 *	state->async = true;
 *	return (NS_HOOK_RETURN);
 * }
 *
 * And the 'runasync' function would be something like this:
 *
 * static isc_result_t
 * runasync(query_ctx_t *qctx, void *arg, isc_taskaction_t action,
 *	    void *evarg, isc_task_t *task, ns_hookasync_t **ctxp) {
 * 	hookstate_t *state = arg;
 *	ns_hook_resevent_t *rev = isc_event_allocate(
 *		mctx, task, NS_EVENT_HOOKASYNCDONE, action, evarg,
 *		sizeof(*rev));
 *	ns_hookasync_t *ctx = isc_mem_get(mctx, sizeof(*ctx));
 *
 *	*ctx = (ns_hookasync_t){ .private = NULL };
 *	isc_mem_attach(mctx, &ctx->mctx);
 *	ctx->cancel = ...;  // set the cancel function, which cancels the
 *			    // internal asynchronous event (if necessary).
 *			    // it should eventually result in sending
 *			    // the 'rev' event to the calling task.
 *	ctx->destroy = ...; // set the destroy function, which frees 'ctx'
 *
 *	rev->hookpoint = state->hookpoint;
 *	rev->origresult = state->origresult;
 *	rev->saved_qctx = qctx;
 *	rev->ctx = ctx;
 *
 *	state->rev = rev; // store the resume event so we can send it later
 *
 *	// initiate some asynchronous process here - for example, a
 *	// recursive fetch.
 *
 *	*ctxp = ctx;
 *	return (ISC_R_SUCCESS);
 * }
 *
 * Finally, in the completion handler for the asynchronous process, we
 * need to send a resumption event so that query processing can resume.
 * For example, the completion handler might call this function:
 *
 * static void
 * asyncproc_done(hookstate_t *state) {
 *	isc_event_t *ev = (isc_event_t *)state->rev;
 *	isc_task_send(ev->ev_sender, &ev);
 * }
 *
 * Caveats:
 * - On resuming from a hook-initiated asynchronous process, code in
 *   the query module before the hook point needs to be exercised.
 *   So if this part has side effects, it's possible that the resuming
 *   doesn't work well.  Currently, NS_QUERY_RESPOND_ANY_FOUND is
 *   explicitly prohibited to be used as the resume point.
 * - In general, hooks other than those called at the beginning of the
 *   caller function may not work safely with asynchronous processing for
 *   the reason stated in the previous bullet.  For example, a hook action
 *   for NS_QUERY_DONE_SEND may not be able to start an asychronous
 *   function safely.
 * - Hook-triggered asynchronous processing is not allowed to be running
 *   while the standard DNS recursive fetch is taking place (starting
 *   from a call to dns_resolver_createfetch()), as the two would be
 *   using some of the same context resources.  For this reason the
 *   NS_QUERY_NOTFOUND_RECURSE and NS_QUERY_ZEROTTL_RECURSE hook points
 *   are explicitly prohibited from being used for asynchronous hook
 *   actions.
 * - Specifying multiple hook actions for the same hook point at the
 *   same time may cause problems, as resumption from one hook action
 *   could cause another hook to be called twice unintentionally.
 *   It's generally not safe to assume such a use case works,
 *   especially if the hooks are developed independently. (Note that
 *   that's not necessarily specific to the use of asynchronous hook
 *   actions. As long as hook actions have side effects, including
 *   modifying the internal query state, it's not guaranteed safe
 *   to use multiple independent hooks at the same time.)
 */

/*!
 * Currently-defined hook points. So long as these are unique, the order in
 * which they are declared is unimportant, but it currently matches the
 * order in which they are referenced in query.c.
 */
typedef enum {
	/* hookpoints from query.c */
	NS_QUERY_QCTX_INITIALIZED,
	NS_QUERY_QCTX_DESTROYED,
	NS_QUERY_SETUP,
	NS_QUERY_START_BEGIN,
	NS_QUERY_LOOKUP_BEGIN,
	NS_QUERY_RESUME_BEGIN,
	NS_QUERY_RESUME_RESTORED,
	NS_QUERY_GOT_ANSWER_BEGIN,
	NS_QUERY_RESPOND_ANY_BEGIN,
	NS_QUERY_RESPOND_ANY_FOUND,
	NS_QUERY_ADDANSWER_BEGIN,
	NS_QUERY_RESPOND_BEGIN,
	NS_QUERY_NOTFOUND_BEGIN,
	NS_QUERY_NOTFOUND_RECURSE,
	NS_QUERY_PREP_DELEGATION_BEGIN,
	NS_QUERY_ZONE_DELEGATION_BEGIN,
	NS_QUERY_DELEGATION_BEGIN,
	NS_QUERY_DELEGATION_RECURSE_BEGIN,
	NS_QUERY_NODATA_BEGIN,
	NS_QUERY_NXDOMAIN_BEGIN,
	NS_QUERY_NCACHE_BEGIN,
	NS_QUERY_ZEROTTL_RECURSE,
	NS_QUERY_CNAME_BEGIN,
	NS_QUERY_DNAME_BEGIN,
	NS_QUERY_PREP_RESPONSE_BEGIN,
	NS_QUERY_DONE_BEGIN,
	NS_QUERY_DONE_SEND,

	/* XXX other files could be added later */

	NS_HOOKPOINTS_COUNT /* MUST BE LAST */
} ns_hookpoint_t;

/*
 * Returned by a hook action to indicate how to proceed after it has
 * been called: continue processing, or return immediately.
 */
typedef enum {
	NS_HOOK_CONTINUE,
	NS_HOOK_RETURN,
} ns_hookresult_t;

typedef ns_hookresult_t (*ns_hook_action_t)(void *arg, void *data,
					    isc_result_t *resultp);

typedef struct ns_hook {
	isc_mem_t	*mctx;
	ns_hook_action_t action;
	void		*action_data;
	ISC_LINK(struct ns_hook) link;
} ns_hook_t;

typedef ISC_LIST(ns_hook_t) ns_hooklist_t;
typedef ns_hooklist_t ns_hooktable_t[NS_HOOKPOINTS_COUNT];

/*%
 * ns__hook_table is a global hook table, which is used if view->hooktable
 * is NULL.  It's intended only for use by unit tests.
 */
extern ns_hooktable_t *ns__hook_table;

typedef void (*ns_hook_cancelasync_t)(ns_hookasync_t *);
typedef void (*ns_hook_destroyasync_t)(ns_hookasync_t **);

/*%
 * Context for a hook-initiated asynchronous process. This works
 * similarly to dns_fetch_t.
 */
struct ns_hookasync {
	isc_mem_t *mctx;

	/*
	 * The following two are equivalent to dns_resolver_cancelfetch and
	 * dns_resolver_destroyfetch, respectively, but specified as function
	 * pointers since they can be hook-specific.
	 */
	ns_hook_cancelasync_t  cancel;
	ns_hook_destroyasync_t destroy;

	void *private; /* hook-specific data */
};

/*
 * isc_event to be sent on the completion of a hook-initiated asyncronous
 * process, similar to dns_fetchevent_t.
 */
typedef struct ns_hook_resevent {
	ISC_EVENT_COMMON(struct ns_hook_resevent);
	ns_hookasync_t *ctx;	   /* asynchronous processing context */
	ns_hookpoint_t	hookpoint; /* hook point from which to resume */
	isc_result_t origresult; /* result code at the point of call to hook */
	query_ctx_t *saved_qctx; /* qctx at the point of call to hook */
} ns_hook_resevent_t;

/*
 * Plugin API version
 *
 * When the API changes, increment NS_PLUGIN_VERSION. If the
 * change is backward-compatible (e.g., adding a new function call
 * but not changing or removing an old one), increment NS_PLUGIN_AGE
 * as well; if not, set NS_PLUGIN_AGE to 0.
 */
#ifndef NS_PLUGIN_VERSION
#define NS_PLUGIN_VERSION 1
#define NS_PLUGIN_AGE	  0
#endif /* ifndef NS_PLUGIN_VERSION */

typedef isc_result_t
ns_plugin_register_t(const char *parameters, const void *cfg, const char *file,
		     unsigned long line, isc_mem_t *mctx, isc_log_t *lctx,
		     void *actx, ns_hooktable_t *hooktable, void **instp);
/*%<
 * Called when registering a new plugin.
 *
 * 'parameters' contains the plugin configuration text.
 *
 * '*instp' will be set to the module instance handle if the function
 * is successful.
 *
 * Returns:
 *\li	#ISC_R_SUCCESS
 *\li	#ISC_R_NOMEMORY
 *\li	Other errors are possible
 */

typedef void
ns_plugin_destroy_t(void **instp);
/*%<
 * Destroy a plugin instance.
 *
 * '*instp' must be set to NULL by the function before it returns.
 */

typedef isc_result_t
ns_plugin_check_t(const char *parameters, const void *cfg, const char *file,
		  unsigned long line, isc_mem_t *mctx, isc_log_t *lctx,
		  void *actx);
/*%<
 * Check the validity of 'parameters'.
 */

typedef int
ns_plugin_version_t(void);
/*%<
 * Return the API version number a plugin was compiled with.
 *
 * If the returned version number is no greater than
 * NS_PLUGIN_VERSION, and no less than NS_PLUGIN_VERSION - NS_PLUGIN_AGE,
 * then the module is API-compatible with named.
 */

/*%
 * Prototypes for API functions to be defined in each module.
 */
ns_plugin_check_t    plugin_check;
ns_plugin_destroy_t  plugin_destroy;
ns_plugin_register_t plugin_register;
ns_plugin_version_t  plugin_version;

isc_result_t
ns_plugin_expandpath(const char *src, char *dst, size_t dstsize);
/*%<
 * Prepare the plugin location to be passed to dlopen() based on the plugin
 * path or filename found in the configuration file ('src').  Store the result
 * in 'dst', which is 'dstsize' bytes large.
 *
 * On Unix systems, two classes of 'src' are recognized:
 *
 *   - If 'src' is an absolute or relative path, it will be copied to 'dst'
 *     verbatim.
 *
 *   - If 'src' is a filename (i.e. does not contain a path separator), the
 *     path to the directory into which named plugins are installed will be
 *     prepended to it and the result will be stored in 'dst'.
 *
 * Returns:
 *\li	#ISC_R_SUCCESS	Success
 *\li	#ISC_R_NOSPACE	'dst' is not large enough to hold the output string
 *\li	Other result	snprintf() returned a negative value
 */

isc_result_t
ns_plugin_register(const char *modpath, const char *parameters, const void *cfg,
		   const char *cfg_file, unsigned long cfg_line,
		   isc_mem_t *mctx, isc_log_t *lctx, void *actx,
		   dns_view_t *view);
/*%<
 * Load the plugin module specified from the file 'modpath', and
 * register an instance using 'parameters'.
 *
 * 'cfg_file' and 'cfg_line' specify the location of the plugin
 * declaration in the configuration file.
 *
 * 'cfg' and 'actx' are the configuration context and ACL configuration
 * context, respectively; they are passed as void * here in order to
 * prevent this library from having a dependency on libisccfg).
 *
 * 'instp' will be left pointing to the instance of the plugin
 * created by the module's plugin_register function.
 */

isc_result_t
ns_plugin_check(const char *modpath, const char *parameters, const void *cfg,
		const char *cfg_file, unsigned long cfg_line, isc_mem_t *mctx,
		isc_log_t *lctx, void *actx);
/*%<
 * Open the plugin module at 'modpath' and check the validity of
 * 'parameters', logging any errors or warnings found, then
 * close it without configuring it.
 */

void
ns_plugins_create(isc_mem_t *mctx, ns_plugins_t **listp);
/*%<
 * Create and initialize a plugin list.
 */

void
ns_plugins_free(isc_mem_t *mctx, void **listp);
/*%<
 * Close each plugin module in a plugin list, then free the list object.
 */

void
ns_hooktable_free(isc_mem_t *mctx, void **tablep);
/*%<
 * Free a hook table.
 */

void
ns_hook_add(ns_hooktable_t *hooktable, isc_mem_t *mctx,
	    ns_hookpoint_t hookpoint, const ns_hook_t *hook);
/*%<
 * Allocate (using memory context 'mctx') a copy of the 'hook' structure
 * describing a hook action and append it to the list of hooks at 'hookpoint'
 * in 'hooktable'.
 *
 * Requires:
 *\li 'hooktable' is not NULL
 *
 *\li 'mctx' is not NULL
 *
 *\li 'hookpoint' is less than NS_QUERY_HOOKS_COUNT
 *
 *\li 'hook' is not NULL
 */

void
ns_hooktable_init(ns_hooktable_t *hooktable);
/*%<
 * Initialize a hook table.
 */

isc_result_t
ns_hooktable_create(isc_mem_t *mctx, ns_hooktable_t **tablep);
/*%<
 * Allocate and initialize a hook table.
 */