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.
17 /* $Id: dlz_dlopen_driver.c,v 1.1.4.6 2012/02/22 23:46:35 tbox Exp $ */
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)
182 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
188 result = cd->dlz_lookup(zone, name, cd->dbdata, lookup);
194 * Load a symbol from the library
197 dl_load_symbol(dlopen_data_t *cd, const char *symbol, isc_boolean_t mandatory) {
198 void *ptr = dlsym(cd->dl_handle, symbol);
199 if (ptr == NULL && mandatory) {
200 dlopen_log(ISC_LOG_ERROR,
201 "dlz_dlopen: library '%s' is missing "
202 "required symbol '%s'", cd->dl_path, symbol);
208 * Called at startup for each dlopen zone in named.conf
211 dlopen_dlz_create(const char *dlzname, unsigned int argc, char *argv[],
212 void *driverarg, void **dbdata)
215 isc_mem_t *mctx = NULL;
216 isc_result_t result = ISC_R_FAILURE;
217 int dlopen_flags = 0;
222 dlopen_log(ISC_LOG_ERROR,
223 "dlz_dlopen driver for '%s' needs a path to "
224 "the shared library", dlzname);
225 return (ISC_R_FAILURE);
228 result = isc_mem_create(0, 0, &mctx);
229 if (result != ISC_R_SUCCESS)
232 cd = isc_mem_get(mctx, sizeof(*cd));
234 isc_mem_destroy(&mctx);
235 return (ISC_R_NOMEMORY);
237 memset(cd, 0, sizeof(*cd));
241 cd->dl_path = isc_mem_strdup(cd->mctx, argv[1]);
242 if (cd->dl_path == NULL) {
246 cd->dlzname = isc_mem_strdup(cd->mctx, dlzname);
247 if (cd->dlzname == NULL) {
251 /* Initialize the lock */
252 result = isc_mutex_init(&cd->lock);
253 if (result != ISC_R_SUCCESS)
256 /* Open the library */
257 dlopen_flags = RTLD_NOW|RTLD_GLOBAL;
261 * If RTLD_DEEPBIND is available then use it. This can avoid
262 * issues with a module using a different version of a system
263 * library than one that bind9 uses. For example, bind9 may link
264 * to MIT kerberos, but the module may use Heimdal. If we don't
265 * use RTLD_DEEPBIND then we could end up with Heimdal functions
266 * calling MIT functions, which leads to bizarre results (usually
269 dlopen_flags |= RTLD_DEEPBIND;
272 cd->dl_handle = dlopen(cd->dl_path, dlopen_flags);
273 if (cd->dl_handle == NULL) {
274 dlopen_log(ISC_LOG_ERROR,
275 "dlz_dlopen failed to open library '%s' - %s",
276 cd->dl_path, dlerror());
280 /* Find the symbols */
281 cd->dlz_version = (dlz_dlopen_version_t *)
282 dl_load_symbol(cd, "dlz_version", ISC_TRUE);
283 cd->dlz_create = (dlz_dlopen_create_t *)
284 dl_load_symbol(cd, "dlz_create", ISC_TRUE);
285 cd->dlz_lookup = (dlz_dlopen_lookup_t *)
286 dl_load_symbol(cd, "dlz_lookup", ISC_TRUE);
287 cd->dlz_findzonedb = (dlz_dlopen_findzonedb_t *)
288 dl_load_symbol(cd, "dlz_findzonedb", ISC_TRUE);
290 if (cd->dlz_create == NULL ||
291 cd->dlz_lookup == NULL ||
292 cd->dlz_findzonedb == NULL)
294 /* We're missing a required symbol */
298 cd->dlz_allowzonexfr = (dlz_dlopen_allowzonexfr_t *)
299 dl_load_symbol(cd, "dlz_allowzonexfr", ISC_FALSE);
300 cd->dlz_allnodes = (dlz_dlopen_allnodes_t *)
301 dl_load_symbol(cd, "dlz_allnodes",
302 ISC_TF(cd->dlz_allowzonexfr != NULL));
303 cd->dlz_authority = (dlz_dlopen_authority_t *)
304 dl_load_symbol(cd, "dlz_authority", ISC_FALSE);
305 cd->dlz_newversion = (dlz_dlopen_newversion_t *)
306 dl_load_symbol(cd, "dlz_newversion", ISC_FALSE);
307 cd->dlz_closeversion = (dlz_dlopen_closeversion_t *)
308 dl_load_symbol(cd, "dlz_closeversion",
309 ISC_TF(cd->dlz_newversion != NULL));
310 cd->dlz_configure = (dlz_dlopen_configure_t *)
311 dl_load_symbol(cd, "dlz_configure", ISC_FALSE);
312 cd->dlz_ssumatch = (dlz_dlopen_ssumatch_t *)
313 dl_load_symbol(cd, "dlz_ssumatch", ISC_FALSE);
314 cd->dlz_addrdataset = (dlz_dlopen_addrdataset_t *)
315 dl_load_symbol(cd, "dlz_addrdataset", ISC_FALSE);
316 cd->dlz_subrdataset = (dlz_dlopen_subrdataset_t *)
317 dl_load_symbol(cd, "dlz_subrdataset", ISC_FALSE);
318 cd->dlz_delrdataset = (dlz_dlopen_delrdataset_t *)
319 dl_load_symbol(cd, "dlz_delrdataset", ISC_FALSE);
320 cd->dlz_destroy = (dlz_dlopen_destroy_t *)
321 dl_load_symbol(cd, "dlz_destroy", ISC_FALSE);
323 /* Check the version of the API is the same */
324 cd->version = cd->dlz_version(&cd->flags);
325 if (cd->version != DLZ_DLOPEN_VERSION) {
326 dlopen_log(ISC_LOG_ERROR,
327 "dlz_dlopen: incorrect version %d "
328 "should be %d in '%s'",
329 cd->version, DLZ_DLOPEN_VERSION, cd->dl_path);
334 * Call the library's create function. Note that this is an
335 * extended version of dlz create, with the addition of
336 * named function pointers for helper functions that the
337 * driver will need. This avoids the need for the backend to
338 * link the BIND9 libraries
341 result = cd->dlz_create(dlzname, argc-1, argv+1,
344 "putrr", dns_sdlz_putrr,
345 "putnamedrr", dns_sdlz_putnamedrr,
346 "writeable_zone", dns_dlz_writeablezone,
349 if (result != ISC_R_SUCCESS)
354 return (ISC_R_SUCCESS);
357 dlopen_log(ISC_LOG_ERROR, "dlz_dlopen of '%s' failed", dlzname);
358 if (cd->dl_path != NULL)
359 isc_mem_free(mctx, cd->dl_path);
360 if (cd->dlzname != NULL)
361 isc_mem_free(mctx, cd->dlzname);
362 if (dlopen_flags != 0)
363 (void) isc_mutex_destroy(&cd->lock);
366 dlclose(cd->dl_handle);
368 isc_mem_put(mctx, cd, sizeof(*cd));
369 isc_mem_destroy(&mctx);
375 * Called when bind is shutting down
378 dlopen_dlz_destroy(void *driverarg, void *dbdata) {
379 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
384 if (cd->dlz_destroy) {
386 cd->dlz_destroy(cd->dbdata);
391 isc_mem_free(cd->mctx, cd->dl_path);
393 isc_mem_free(cd->mctx, cd->dlzname);
397 dlclose(cd->dl_handle);
400 (void) isc_mutex_destroy(&cd->lock);
403 isc_mem_put(mctx, cd, sizeof(*cd));
404 isc_mem_destroy(&mctx);
408 * Called to start a transaction
411 dlopen_dlz_newversion(const char *zone, void *driverarg, void *dbdata,
414 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
419 if (cd->dlz_newversion == NULL)
420 return (ISC_R_NOTIMPLEMENTED);
423 result = cd->dlz_newversion(zone, cd->dbdata, versionp);
429 * Called to end a transaction
432 dlopen_dlz_closeversion(const char *zone, isc_boolean_t commit,
433 void *driverarg, void *dbdata, void **versionp)
435 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
439 if (cd->dlz_newversion == NULL) {
445 cd->dlz_closeversion(zone, commit, cd->dbdata, versionp);
450 * Called on startup to configure any writeable zones
453 dlopen_dlz_configure(dns_view_t *view, void *driverarg, void *dbdata) {
454 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
459 if (cd->dlz_configure == NULL)
460 return (ISC_R_SUCCESS);
463 cd->in_configure = ISC_TRUE;
464 result = cd->dlz_configure(view, cd->dbdata);
465 cd->in_configure = ISC_FALSE;
473 * Check for authority to change a name
476 dlopen_dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
477 const char *type, const char *key, isc_uint32_t keydatalen,
478 unsigned char *keydata, void *driverarg, void *dbdata)
480 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
485 if (cd->dlz_ssumatch == NULL)
489 ret = cd->dlz_ssumatch(signer, name, tcpaddr, type, key, keydatalen,
490 keydata, cd->dbdata);
501 dlopen_dlz_addrdataset(const char *name, const char *rdatastr,
502 void *driverarg, void *dbdata, void *version)
504 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
509 if (cd->dlz_addrdataset == NULL)
510 return (ISC_R_NOTIMPLEMENTED);
513 result = cd->dlz_addrdataset(name, rdatastr, cd->dbdata, version);
520 * Subtract an rdataset
523 dlopen_dlz_subrdataset(const char *name, const char *rdatastr,
524 void *driverarg, void *dbdata, void *version)
526 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
531 if (cd->dlz_subrdataset == NULL)
532 return (ISC_R_NOTIMPLEMENTED);
535 result = cd->dlz_subrdataset(name, rdatastr, cd->dbdata, version);
545 dlopen_dlz_delrdataset(const char *name, const char *type,
546 void *driverarg, void *dbdata, void *version)
548 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
553 if (cd->dlz_delrdataset == NULL)
554 return (ISC_R_NOTIMPLEMENTED);
557 result = cd->dlz_delrdataset(name, type, cd->dbdata, version);
564 static dns_sdlzmethods_t dlz_dlopen_methods = {
567 dlopen_dlz_findzonedb,
569 dlopen_dlz_authority,
571 dlopen_dlz_allowzonexfr,
572 dlopen_dlz_newversion,
573 dlopen_dlz_closeversion,
574 dlopen_dlz_configure,
576 dlopen_dlz_addrdataset,
577 dlopen_dlz_subrdataset,
578 dlopen_dlz_delrdataset
583 * Register driver with BIND
586 dlz_dlopen_init(isc_mem_t *mctx) {
587 #ifndef ISC_DLZ_DLOPEN
589 return (ISC_R_NOTIMPLEMENTED);
593 dlopen_log(2, "Registering DLZ_dlopen driver");
595 result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
596 DNS_SDLZFLAG_RELATIVEOWNER |
597 DNS_SDLZFLAG_THREADSAFE,
600 if (result != ISC_R_SUCCESS) {
601 UNEXPECTED_ERROR(__FILE__, __LINE__,
602 "dns_sdlzregister() failed: %s",
603 isc_result_totext(result));
604 result = ISC_R_UNEXPECTED;
613 * Unregister the driver
616 dlz_dlopen_clear(void) {
617 #ifdef ISC_DLZ_DLOPEN
618 dlopen_log(2, "Unregistering DLZ_dlopen driver");
619 if (dlz_dlopen != NULL)
620 dns_sdlzunregister(&dlz_dlopen);