2 * Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
27 #include <dns/result.h>
28 #include <dns/dlz_dlopen.h>
31 #include <isc/print.h>
32 #include <isc/result.h>
35 #include <named/globals.h>
37 #include <dlz/dlz_dlopen_driver.h>
40 static dns_sdlzimplementation_t *dlz_dlopen = NULL;
43 typedef struct dlopen_data {
52 isc_boolean_t in_configure;
54 dlz_dlopen_version_t *dlz_version;
55 dlz_dlopen_create_t *dlz_create;
56 dlz_dlopen_findzonedb_t *dlz_findzonedb;
57 dlz_dlopen_lookup_t *dlz_lookup;
58 dlz_dlopen_authority_t *dlz_authority;
59 dlz_dlopen_allnodes_t *dlz_allnodes;
60 dlz_dlopen_allowzonexfr_t *dlz_allowzonexfr;
61 dlz_dlopen_newversion_t *dlz_newversion;
62 dlz_dlopen_closeversion_t *dlz_closeversion;
63 dlz_dlopen_configure_t *dlz_configure;
64 dlz_dlopen_ssumatch_t *dlz_ssumatch;
65 dlz_dlopen_addrdataset_t *dlz_addrdataset;
66 dlz_dlopen_subrdataset_t *dlz_subrdataset;
67 dlz_dlopen_delrdataset_t *dlz_delrdataset;
68 dlz_dlopen_destroy_t *dlz_destroy;
71 /* Modules can choose whether they are lock-safe or not. */
72 #define MAYBE_LOCK(cd) \
74 if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \
75 cd->in_configure == ISC_FALSE) \
79 #define MAYBE_UNLOCK(cd) \
81 if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \
82 cd->in_configure == ISC_FALSE) \
87 * Log a message at the given level.
89 static void dlopen_log(int level, const char *fmt, ...)
93 isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE,
94 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(level),
104 dlopen_dlz_allnodes(const char *zone, void *driverarg, void *dbdata,
105 dns_sdlzallnodes_t *allnodes)
107 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
113 if (cd->dlz_allnodes == NULL) {
114 return (ISC_R_NOPERM);
118 result = cd->dlz_allnodes(zone, cd->dbdata, allnodes);
125 dlopen_dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name,
128 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
134 if (cd->dlz_allowzonexfr == NULL) {
135 return (ISC_R_NOPERM);
139 result = cd->dlz_allowzonexfr(cd->dbdata, name, client);
145 dlopen_dlz_authority(const char *zone, void *driverarg, void *dbdata,
146 dns_sdlzlookup_t *lookup)
148 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
153 if (cd->dlz_authority == NULL) {
154 return (ISC_R_NOTIMPLEMENTED);
158 result = cd->dlz_authority(zone, cd->dbdata, lookup);
164 dlopen_dlz_findzonedb(void *driverarg, void *dbdata, const char *name)
166 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
172 result = cd->dlz_findzonedb(cd->dbdata, name);
179 dlopen_dlz_lookup(const char *zone, const char *name, void *driverarg,
180 void *dbdata, dns_sdlzlookup_t *lookup,
181 dns_clientinfomethods_t *methods,
182 dns_clientinfo_t *clientinfo)
184 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
190 result = cd->dlz_lookup(zone, name, cd->dbdata, lookup,
191 methods, clientinfo);
197 * Load a symbol from the library
200 dl_load_symbol(dlopen_data_t *cd, const char *symbol, isc_boolean_t mandatory) {
201 void *ptr = dlsym(cd->dl_handle, symbol);
202 if (ptr == NULL && mandatory) {
203 dlopen_log(ISC_LOG_ERROR,
204 "dlz_dlopen: library '%s' is missing "
205 "required symbol '%s'", cd->dl_path, symbol);
211 * Called at startup for each dlopen zone in named.conf
214 dlopen_dlz_create(const char *dlzname, unsigned int argc, char *argv[],
215 void *driverarg, void **dbdata)
218 isc_mem_t *mctx = NULL;
219 isc_result_t result = ISC_R_FAILURE;
220 int dlopen_flags = 0;
225 dlopen_log(ISC_LOG_ERROR,
226 "dlz_dlopen driver for '%s' needs a path to "
227 "the shared library", dlzname);
228 return (ISC_R_FAILURE);
231 result = isc_mem_create(0, 0, &mctx);
232 if (result != ISC_R_SUCCESS)
235 cd = isc_mem_get(mctx, sizeof(*cd));
237 isc_mem_destroy(&mctx);
238 return (ISC_R_NOMEMORY);
240 memset(cd, 0, sizeof(*cd));
244 cd->dl_path = isc_mem_strdup(cd->mctx, argv[1]);
245 if (cd->dl_path == NULL) {
249 cd->dlzname = isc_mem_strdup(cd->mctx, dlzname);
250 if (cd->dlzname == NULL) {
254 /* Initialize the lock */
255 result = isc_mutex_init(&cd->lock);
256 if (result != ISC_R_SUCCESS)
259 /* Open the library */
260 dlopen_flags = RTLD_NOW|RTLD_GLOBAL;
264 * If RTLD_DEEPBIND is available then use it. This can avoid
265 * issues with a module using a different version of a system
266 * library than one that bind9 uses. For example, bind9 may link
267 * to MIT kerberos, but the module may use Heimdal. If we don't
268 * use RTLD_DEEPBIND then we could end up with Heimdal functions
269 * calling MIT functions, which leads to bizarre results (usually
272 dlopen_flags |= RTLD_DEEPBIND;
275 cd->dl_handle = dlopen(cd->dl_path, dlopen_flags);
276 if (cd->dl_handle == NULL) {
277 dlopen_log(ISC_LOG_ERROR,
278 "dlz_dlopen failed to open library '%s' - %s",
279 cd->dl_path, dlerror());
283 /* Find the symbols */
284 cd->dlz_version = (dlz_dlopen_version_t *)
285 dl_load_symbol(cd, "dlz_version", ISC_TRUE);
286 cd->dlz_create = (dlz_dlopen_create_t *)
287 dl_load_symbol(cd, "dlz_create", ISC_TRUE);
288 cd->dlz_lookup = (dlz_dlopen_lookup_t *)
289 dl_load_symbol(cd, "dlz_lookup", ISC_TRUE);
290 cd->dlz_findzonedb = (dlz_dlopen_findzonedb_t *)
291 dl_load_symbol(cd, "dlz_findzonedb", ISC_TRUE);
293 if (cd->dlz_create == NULL ||
294 cd->dlz_lookup == NULL ||
295 cd->dlz_findzonedb == NULL)
297 /* We're missing a required symbol */
301 cd->dlz_allowzonexfr = (dlz_dlopen_allowzonexfr_t *)
302 dl_load_symbol(cd, "dlz_allowzonexfr", ISC_FALSE);
303 cd->dlz_allnodes = (dlz_dlopen_allnodes_t *)
304 dl_load_symbol(cd, "dlz_allnodes",
305 ISC_TF(cd->dlz_allowzonexfr != NULL));
306 cd->dlz_authority = (dlz_dlopen_authority_t *)
307 dl_load_symbol(cd, "dlz_authority", ISC_FALSE);
308 cd->dlz_newversion = (dlz_dlopen_newversion_t *)
309 dl_load_symbol(cd, "dlz_newversion", ISC_FALSE);
310 cd->dlz_closeversion = (dlz_dlopen_closeversion_t *)
311 dl_load_symbol(cd, "dlz_closeversion",
312 ISC_TF(cd->dlz_newversion != NULL));
313 cd->dlz_configure = (dlz_dlopen_configure_t *)
314 dl_load_symbol(cd, "dlz_configure", ISC_FALSE);
315 cd->dlz_ssumatch = (dlz_dlopen_ssumatch_t *)
316 dl_load_symbol(cd, "dlz_ssumatch", ISC_FALSE);
317 cd->dlz_addrdataset = (dlz_dlopen_addrdataset_t *)
318 dl_load_symbol(cd, "dlz_addrdataset", ISC_FALSE);
319 cd->dlz_subrdataset = (dlz_dlopen_subrdataset_t *)
320 dl_load_symbol(cd, "dlz_subrdataset", ISC_FALSE);
321 cd->dlz_delrdataset = (dlz_dlopen_delrdataset_t *)
322 dl_load_symbol(cd, "dlz_delrdataset", ISC_FALSE);
323 cd->dlz_destroy = (dlz_dlopen_destroy_t *)
324 dl_load_symbol(cd, "dlz_destroy", ISC_FALSE);
326 /* Check the version of the API is the same */
327 cd->version = cd->dlz_version(&cd->flags);
328 if (cd->version != DLZ_DLOPEN_VERSION) {
329 dlopen_log(ISC_LOG_ERROR,
330 "dlz_dlopen: incorrect version %d "
331 "should be %d in '%s'",
332 cd->version, DLZ_DLOPEN_VERSION, cd->dl_path);
337 * Call the library's create function. Note that this is an
338 * extended version of dlz create, with the addition of
339 * named function pointers for helper functions that the
340 * driver will need. This avoids the need for the backend to
341 * link the BIND9 libraries
344 result = cd->dlz_create(dlzname, argc-1, argv+1,
347 "putrr", dns_sdlz_putrr,
348 "putnamedrr", dns_sdlz_putnamedrr,
349 "writeable_zone", dns_dlz_writeablezone,
352 if (result != ISC_R_SUCCESS)
357 return (ISC_R_SUCCESS);
360 dlopen_log(ISC_LOG_ERROR, "dlz_dlopen of '%s' failed", dlzname);
361 if (cd->dl_path != NULL)
362 isc_mem_free(mctx, cd->dl_path);
363 if (cd->dlzname != NULL)
364 isc_mem_free(mctx, cd->dlzname);
365 if (dlopen_flags != 0)
366 (void) isc_mutex_destroy(&cd->lock);
369 dlclose(cd->dl_handle);
371 isc_mem_put(mctx, cd, sizeof(*cd));
372 isc_mem_destroy(&mctx);
378 * Called when bind is shutting down
381 dlopen_dlz_destroy(void *driverarg, void *dbdata) {
382 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
387 if (cd->dlz_destroy) {
389 cd->dlz_destroy(cd->dbdata);
394 isc_mem_free(cd->mctx, cd->dl_path);
396 isc_mem_free(cd->mctx, cd->dlzname);
400 dlclose(cd->dl_handle);
403 (void) isc_mutex_destroy(&cd->lock);
406 isc_mem_put(mctx, cd, sizeof(*cd));
407 isc_mem_destroy(&mctx);
411 * Called to start a transaction
414 dlopen_dlz_newversion(const char *zone, void *driverarg, void *dbdata,
417 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
422 if (cd->dlz_newversion == NULL)
423 return (ISC_R_NOTIMPLEMENTED);
426 result = cd->dlz_newversion(zone, cd->dbdata, versionp);
432 * Called to end a transaction
435 dlopen_dlz_closeversion(const char *zone, isc_boolean_t commit,
436 void *driverarg, void *dbdata, void **versionp)
438 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
442 if (cd->dlz_newversion == NULL) {
448 cd->dlz_closeversion(zone, commit, cd->dbdata, versionp);
453 * Called on startup to configure any writeable zones
456 dlopen_dlz_configure(dns_view_t *view, void *driverarg, void *dbdata) {
457 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
462 if (cd->dlz_configure == NULL)
463 return (ISC_R_SUCCESS);
466 cd->in_configure = ISC_TRUE;
467 result = cd->dlz_configure(view, cd->dbdata);
468 cd->in_configure = ISC_FALSE;
476 * Check for authority to change a name
479 dlopen_dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
480 const char *type, const char *key, isc_uint32_t keydatalen,
481 unsigned char *keydata, void *driverarg, void *dbdata)
483 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
488 if (cd->dlz_ssumatch == NULL)
492 ret = cd->dlz_ssumatch(signer, name, tcpaddr, type, key, keydatalen,
493 keydata, cd->dbdata);
504 dlopen_dlz_addrdataset(const char *name, const char *rdatastr,
505 void *driverarg, void *dbdata, void *version)
507 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
512 if (cd->dlz_addrdataset == NULL)
513 return (ISC_R_NOTIMPLEMENTED);
516 result = cd->dlz_addrdataset(name, rdatastr, cd->dbdata, version);
523 * Subtract an rdataset
526 dlopen_dlz_subrdataset(const char *name, const char *rdatastr,
527 void *driverarg, void *dbdata, void *version)
529 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
534 if (cd->dlz_subrdataset == NULL)
535 return (ISC_R_NOTIMPLEMENTED);
538 result = cd->dlz_subrdataset(name, rdatastr, cd->dbdata, version);
548 dlopen_dlz_delrdataset(const char *name, const char *type,
549 void *driverarg, void *dbdata, void *version)
551 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
556 if (cd->dlz_delrdataset == NULL)
557 return (ISC_R_NOTIMPLEMENTED);
560 result = cd->dlz_delrdataset(name, type, cd->dbdata, version);
567 static dns_sdlzmethods_t dlz_dlopen_methods = {
570 dlopen_dlz_findzonedb,
572 dlopen_dlz_authority,
574 dlopen_dlz_allowzonexfr,
575 dlopen_dlz_newversion,
576 dlopen_dlz_closeversion,
577 dlopen_dlz_configure,
579 dlopen_dlz_addrdataset,
580 dlopen_dlz_subrdataset,
581 dlopen_dlz_delrdataset
586 * Register driver with BIND
589 dlz_dlopen_init(isc_mem_t *mctx) {
590 #ifndef ISC_DLZ_DLOPEN
592 return (ISC_R_NOTIMPLEMENTED);
596 dlopen_log(2, "Registering DLZ_dlopen driver");
598 result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
599 DNS_SDLZFLAG_RELATIVEOWNER |
600 DNS_SDLZFLAG_THREADSAFE,
603 if (result != ISC_R_SUCCESS) {
604 UNEXPECTED_ERROR(__FILE__, __LINE__,
605 "dns_sdlzregister() failed: %s",
606 isc_result_totext(result));
607 result = ISC_R_UNEXPECTED;
616 * Unregister the driver
619 dlz_dlopen_clear(void) {
620 #ifdef ISC_DLZ_DLOPEN
621 dlopen_log(2, "Unregistering DLZ_dlopen driver");
622 if (dlz_dlopen != NULL)
623 dns_sdlzunregister(&dlz_dlopen);