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