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