hooks.c revision 1.1 1 /* $NetBSD: hooks.c,v 1.1 2019/01/09 16:48:22 christos Exp $ */
2
3 /*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public
7 * License, v. 2.0. If a copy of the MPL was not distributed with this
8 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 *
10 * See the COPYRIGHT file distributed with this work for additional
11 * information regarding copyright ownership.
12 */
13
14 /*! \file */
15
16 #include <config.h>
17
18 #include <string.h>
19
20 #if HAVE_DLFCN_H
21 #include <dlfcn.h>
22 #elif _WIN32
23 #include <windows.h>
24 #endif
25
26 #include <isc/list.h>
27 #include <isc/log.h>
28 #include <isc/mem.h>
29 #include <isc/mutex.h>
30 #include <isc/result.h>
31 #include <isc/platform.h>
32 #include <isc/util.h>
33 #include <isc/types.h>
34
35 #include <dns/view.h>
36
37 #include <ns/hooks.h>
38 #include <ns/log.h>
39 #include <ns/query.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 struct ns_plugin {
50 isc_mem_t *mctx;
51 void *handle;
52 void *inst;
53 char *modpath;
54 ns_plugin_check_t *check_func;
55 ns_plugin_register_t *register_func;
56 ns_plugin_destroy_t *destroy_func;
57 LINK(ns_plugin_t) link;
58 };
59
60 static ns_hooklist_t default_hooktable[NS_HOOKPOINTS_COUNT];
61 LIBNS_EXTERNAL_DATA ns_hooktable_t *ns__hook_table = &default_hooktable;
62
63 #if HAVE_DLFCN_H && HAVE_DLOPEN
64 static isc_result_t
65 load_symbol(void *handle, const char *modpath,
66 const char *symbol_name, void **symbolp)
67 {
68 void *symbol = NULL;
69
70 REQUIRE(handle != NULL);
71 REQUIRE(symbolp != NULL && *symbolp == NULL);
72
73 /*
74 * Clear any pre-existing error conditions before running dlsym().
75 * (In this case, we expect dlsym() to return non-NULL values
76 * and will always return an error if it returns NULL, but
77 * this ensures that we'll report the correct error condition
78 * if there is one.)
79 */
80 dlerror();
81 symbol = dlsym(handle, symbol_name);
82 if (symbol == NULL) {
83 const char *errmsg = dlerror();
84 if (errmsg == NULL) {
85 errmsg = "returned function pointer is NULL";
86 }
87 isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
88 NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
89 "failed to look up symbol %s in "
90 "plugin '%s': %s",
91 symbol_name, modpath, errmsg);
92 return (ISC_R_FAILURE);
93 }
94
95 *symbolp = symbol;
96
97 return (ISC_R_SUCCESS);
98 }
99
100 static isc_result_t
101 load_plugin(isc_mem_t *mctx, const char *modpath, ns_plugin_t **pluginp) {
102 isc_result_t result;
103 void *handle = NULL;
104 ns_plugin_t *plugin = NULL;
105 ns_plugin_check_t *check_func = NULL;
106 ns_plugin_register_t *register_func = NULL;
107 ns_plugin_destroy_t *destroy_func = NULL;
108 ns_plugin_version_t *version_func = NULL;
109 int version, flags;
110
111 REQUIRE(pluginp != NULL && *pluginp == NULL);
112
113 flags = RTLD_LAZY | RTLD_LOCAL;
114 #ifdef RTLD_DEEPBIND
115 flags |= RTLD_DEEPBIND;
116 #endif
117
118 handle = dlopen(modpath, flags);
119 if (handle == NULL) {
120 const char *errmsg = dlerror();
121 if (errmsg == NULL) {
122 errmsg = "unknown error";
123 }
124 isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
125 NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
126 "failed to dlopen() plugin '%s': %s",
127 modpath, errmsg);
128 return (ISC_R_FAILURE);
129 }
130
131 CHECK(load_symbol(handle, modpath, "plugin_version",
132 (void **)&version_func));
133
134 version = version_func();
135 if (version < (NS_PLUGIN_VERSION - NS_PLUGIN_AGE) ||
136 version > NS_PLUGIN_VERSION)
137 {
138 isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
139 NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
140 "plugin API version mismatch: %d/%d",
141 version, NS_PLUGIN_VERSION);
142 CHECK(ISC_R_FAILURE);
143 }
144
145 CHECK(load_symbol(handle, modpath, "plugin_check",
146 (void **)&check_func));
147 CHECK(load_symbol(handle, modpath, "plugin_register",
148 (void **)®ister_func));
149 CHECK(load_symbol(handle, modpath, "plugin_destroy",
150 (void **)&destroy_func));
151
152 plugin = isc_mem_get(mctx, sizeof(*plugin));
153 memset(plugin, 0, sizeof(*plugin));
154 isc_mem_attach(mctx, &plugin->mctx);
155 plugin->handle = handle;
156 plugin->modpath = isc_mem_strdup(plugin->mctx, modpath);
157 plugin->check_func = check_func;
158 plugin->register_func = register_func;
159 plugin->destroy_func = destroy_func;
160
161 ISC_LINK_INIT(plugin, link);
162
163 *pluginp = plugin;
164 plugin = NULL;
165
166 cleanup:
167 if (result != ISC_R_SUCCESS) {
168 isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
169 NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
170 "failed to dynamically load "
171 "plugin '%s': %s", modpath,
172 isc_result_totext(result));
173
174 if (plugin != NULL) {
175 isc_mem_putanddetach(&plugin->mctx, plugin,
176 sizeof(*plugin));
177 }
178
179 if (handle != NULL) {
180 (void) dlclose(handle);
181 }
182 }
183
184 return (result);
185 }
186
187 static void
188 unload_plugin(ns_plugin_t **pluginp) {
189 ns_plugin_t *plugin = NULL;
190
191 REQUIRE(pluginp != NULL && *pluginp != NULL);
192
193 plugin = *pluginp;
194 *pluginp = NULL;
195
196 isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
197 NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
198 "unloading plugin '%s'", plugin->modpath);
199
200 if (plugin->inst != NULL) {
201 plugin->destroy_func(&plugin->inst);
202 }
203 if (plugin->handle != NULL) {
204 (void) dlclose(plugin->handle);
205 }
206 if (plugin->modpath != NULL) {
207 isc_mem_free(plugin->mctx, plugin->modpath);
208 }
209
210 isc_mem_putanddetach(&plugin->mctx, plugin, sizeof(*plugin));
211 }
212 #elif _WIN32
213 static isc_result_t
214 load_symbol(HMODULE handle, const char *modpath,
215 const char *symbol_name, void **symbolp)
216 {
217 void *symbol = NULL;
218
219 REQUIRE(handle != NULL);
220 REQUIRE(symbolp != NULL && *symbolp == NULL);
221
222 symbol = GetProcAddress(handle, symbol_name);
223 if (symbol == NULL) {
224 int errstatus = GetLastError();
225 isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
226 NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
227 "failed to look up symbol %s in "
228 "plugin '%s': %d",
229 symbol_name, modpath, errstatus);
230 return (ISC_R_FAILURE);
231 }
232
233 *symbolp = symbol;
234
235 return (ISC_R_SUCCESS);
236 }
237
238 static isc_result_t
239 load_plugin(isc_mem_t *mctx, const char *modpath, ns_plugin_t **pluginp) {
240 isc_result_t result;
241 HMODULE handle;
242 ns_plugin_t *plugin = NULL;
243 ns_plugin_register_t *register_func = NULL;
244 ns_plugin_destroy_t *destroy_func = NULL;
245 ns_plugin_version_t *version_func = NULL;
246 int version;
247
248 REQUIRE(pluginp != NULL && *pluginp == NULL);
249
250 handle = LoadLibraryA(modpath);
251 if (handle == NULL) {
252 CHECK(ISC_R_FAILURE);
253 }
254
255 CHECK(load_symbol(handle, modpath, "plugin_version",
256 (void **)&version_func));
257
258 version = version_func(NULL);
259 if (version < (NS_PLUGIN_VERSION - NS_PLUGIN_AGE) ||
260 version > NS_PLUGIN_VERSION)
261 {
262 isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
263 NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
264 "plugin API version mismatch: %d/%d",
265 version, NS_PLUGIN_VERSION);
266 CHECK(ISC_R_FAILURE);
267 }
268
269 CHECK(load_symbol(handle, modpath, "plugin_register",
270 (void **)®ister_func));
271 CHECK(load_symbol(handle, modpath, "plugin_destroy",
272 (void **)&destroy_func));
273
274 plugin = isc_mem_get(mctx, sizeof(*plugin));
275 memset(plugin, 0, sizeof(*plugin));
276 isc_mem_attach(mctx, &plugin->mctx);
277 plugin->handle = handle;
278 plugin->modpath = isc_mem_strdup(plugin->mctx, modpath);
279 plugin->register_func = register_func;
280 plugin->destroy_func = destroy_func;
281
282 ISC_LINK_INIT(plugin, link);
283
284 *pluginp = plugin;
285 plugin = NULL;
286
287 cleanup:
288 if (result != ISC_R_SUCCESS) {
289 isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
290 NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
291 "failed to dynamically load "
292 "plugin '%s': %d (%s)", modpath,
293 GetLastError(), isc_result_totext(result));
294
295 if (plugin != NULL) {
296 isc_mem_putanddetach(&plugin->mctx, plugin,
297 sizeof(*plugin));
298 }
299
300 if (handle != NULL) {
301 FreeLibrary(handle);
302 }
303 }
304
305 return (result);
306 }
307
308 static void
309 unload_plugin(ns_plugin_t **pluginp) {
310 ns_plugin_t *plugin = NULL;
311
312 REQUIRE(pluginp != NULL && *pluginp != NULL);
313
314 plugin = *pluginp;
315 *pluginp = NULL;
316
317 isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
318 NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
319 "unloading plugin '%s'", plugin->modpath);
320
321 if (plugin->inst != NULL) {
322 plugin->destroy_func(&plugin->inst);
323 }
324 if (plugin->handle != NULL) {
325 FreeLibrary(plugin->handle);
326 }
327
328 if (plugin->modpath != NULL) {
329 isc_mem_free(plugin->mctx, plugin->modpath);
330 }
331
332 isc_mem_putanddetach(&plugin->mctx, plugin, sizeof(*plugin));
333 }
334 #else /* HAVE_DLFCN_H || _WIN32 */
335 static isc_result_t
336 load_plugin(isc_mem_t *mctx, const char *modpath, ns_plugin_t **pluginp) {
337 UNUSED(mctx);
338 UNUSED(modpath);
339 UNUSED(pluginp);
340
341 isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
342 NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
343 "plugin support is not implemented");
344
345 return (ISC_R_NOTIMPLEMENTED);
346 }
347
348 static void
349 unload_plugin(ns_plugin_t **pluginp) {
350 UNUSED(pluginp);
351 }
352 #endif /* HAVE_DLFCN_H */
353
354 isc_result_t
355 ns_plugin_register(const char *modpath, const char *parameters,
356 const void *cfg, const char *cfg_file,
357 unsigned long cfg_line,
358 isc_mem_t *mctx, isc_log_t *lctx, void *actx,
359 dns_view_t *view)
360 {
361 isc_result_t result;
362 ns_plugin_t *plugin = NULL;
363
364 REQUIRE(mctx != NULL);
365 REQUIRE(lctx != NULL);
366 REQUIRE(view != NULL);
367
368 isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
369 NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
370 "loading plugin '%s'", modpath);
371
372 CHECK(load_plugin(mctx, modpath, &plugin));
373
374 isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
375 NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
376 "registering plugin '%s'", modpath);
377
378 CHECK(plugin->register_func(parameters, cfg, cfg_file, cfg_line,
379 mctx, lctx, actx, view->hooktable,
380 &plugin->inst));
381
382 ISC_LIST_APPEND(*(ns_plugins_t *)view->plugins, plugin, link);
383
384 cleanup:
385 if (result != ISC_R_SUCCESS && plugin != NULL) {
386 unload_plugin(&plugin);
387 }
388
389 return (result);
390 }
391
392 isc_result_t
393 ns_plugin_check(const char *modpath, const char *parameters,
394 const void *cfg, const char *cfg_file, unsigned long cfg_line,
395 isc_mem_t *mctx, isc_log_t *lctx, void *actx)
396 {
397 isc_result_t result;
398 ns_plugin_t *plugin = NULL;
399
400 CHECK(load_plugin(mctx, modpath, &plugin));
401
402 result = plugin->check_func(parameters, cfg, cfg_file, cfg_line,
403 mctx, lctx, actx);
404
405 cleanup:
406 if (plugin != NULL) {
407 unload_plugin(&plugin);
408 }
409
410 return (result);
411 }
412
413 void
414 ns_hooktable_init(ns_hooktable_t *hooktable) {
415 int i;
416
417 for (i = 0; i < NS_HOOKPOINTS_COUNT; i++) {
418 ISC_LIST_INIT((*hooktable)[i]);
419 }
420 }
421
422 isc_result_t
423 ns_hooktable_create(isc_mem_t *mctx, ns_hooktable_t **tablep) {
424 ns_hooktable_t *hooktable = NULL;
425
426 REQUIRE(tablep != NULL && *tablep == NULL);
427
428 hooktable = isc_mem_get(mctx, sizeof(*hooktable));
429
430 ns_hooktable_init(hooktable);
431
432 *tablep = hooktable;
433
434 return (ISC_R_SUCCESS);
435 }
436
437 void
438 ns_hooktable_free(isc_mem_t *mctx, void **tablep) {
439 ns_hooktable_t *table = NULL;
440 ns_hook_t *hook = NULL, *next = NULL;
441 int i = 0;
442
443 REQUIRE(tablep != NULL && *tablep != NULL);
444
445 table = *tablep;
446 *tablep = NULL;
447
448 for (i = 0; i < NS_HOOKPOINTS_COUNT; i++) {
449 for (hook = ISC_LIST_HEAD((*table)[i]);
450 hook != NULL;
451 hook = next)
452 {
453 next = ISC_LIST_NEXT(hook, link);
454 ISC_LIST_UNLINK((*table)[i], hook, link);
455 if (hook->mctx != NULL) {
456 isc_mem_putanddetach(&hook->mctx,
457 hook, sizeof(*hook));
458 }
459 }
460 }
461
462 isc_mem_put(mctx, table, sizeof(*table));
463 }
464
465 void
466 ns_hook_add(ns_hooktable_t *hooktable, isc_mem_t *mctx,
467 ns_hookpoint_t hookpoint, const ns_hook_t *hook)
468 {
469 ns_hook_t *copy = NULL;
470
471 REQUIRE(hooktable != NULL);
472 REQUIRE(mctx != NULL);
473 REQUIRE(hookpoint < NS_HOOKPOINTS_COUNT);
474 REQUIRE(hook != NULL);
475
476 copy = isc_mem_get(mctx, sizeof(*copy));
477 memset(copy, 0, sizeof(*copy));
478
479 copy->action = hook->action;
480 copy->action_data = hook->action_data;
481 isc_mem_attach(mctx, ©->mctx);
482
483 ISC_LINK_INIT(copy, link);
484 ISC_LIST_APPEND((*hooktable)[hookpoint], copy, link);
485 }
486
487 void
488 ns_plugins_create(isc_mem_t *mctx, ns_plugins_t **listp) {
489 ns_plugins_t *plugins = NULL;
490
491 REQUIRE(listp != NULL && *listp == NULL);
492
493 plugins = isc_mem_get(mctx, sizeof(*plugins));
494 memset(plugins, 0, sizeof(*plugins));
495 ISC_LIST_INIT(*plugins);
496
497 *listp = plugins;
498 }
499
500 void
501 ns_plugins_free(isc_mem_t *mctx, void **listp) {
502 ns_plugins_t *list = NULL;
503 ns_plugin_t *plugin = NULL, *next = NULL;
504
505 REQUIRE(listp != NULL && *listp != NULL);
506
507 list = *listp;
508 *listp = NULL;
509
510 for (plugin = ISC_LIST_HEAD(*list);
511 plugin != NULL;
512 plugin = next)
513 {
514 next = ISC_LIST_NEXT(plugin, link);
515 ISC_LIST_UNLINK(*list, plugin, link);
516 unload_plugin(&plugin);
517 }
518
519 isc_mem_put(mctx, list, sizeof(*list));
520 }
521