2 * Copyright (C) 2011 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.4 2011-03-17 09:41:06 fdupont 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 isc_mem_create(0, 0, &mctx);
230 cd = isc_mem_get(mctx, sizeof(*cd));
232 isc_mem_destroy(&mctx);
233 return (ISC_R_NOMEMORY);
235 memset(cd, 0, sizeof(*cd));
239 cd->dl_path = isc_mem_strdup(cd->mctx, argv[1]);
240 if (cd->dl_path == NULL) {
244 cd->dlzname = isc_mem_strdup(cd->mctx, dlzname);
245 if (cd->dlzname == NULL) {
249 /* Initialize the lock */
250 isc_mutex_init(&cd->lock);
252 /* Open the library */
253 dlopen_flags = RTLD_NOW;
257 * If RTLD_DEEPBIND is available then use it. This can avoid
258 * issues with a module using a different version of a system
259 * library than one that bind9 uses. For example, bind9 may link
260 * to MIT kerberos, but the module may use Heimdal. If we don't
261 * use RTLD_DEEPBIND then we could end up with Heimdal functions
262 * calling MIT functions, which leads to bizarre results (usually
265 dlopen_flags |= RTLD_DEEPBIND;
268 cd->dl_handle = dlopen(cd->dl_path, dlopen_flags);
269 if (cd->dl_handle == NULL) {
270 dlopen_log(ISC_LOG_ERROR,
271 "dlz_dlopen failed to open library '%s' - %s",
272 cd->dl_path, dlerror());
276 /* Find the symbols */
277 cd->dlz_version = (dlz_dlopen_version_t *)
278 dl_load_symbol(cd, "dlz_version", ISC_TRUE);
279 cd->dlz_create = (dlz_dlopen_create_t *)
280 dl_load_symbol(cd, "dlz_create", ISC_TRUE);
281 cd->dlz_lookup = (dlz_dlopen_lookup_t *)
282 dl_load_symbol(cd, "dlz_lookup", ISC_TRUE);
283 cd->dlz_findzonedb = (dlz_dlopen_findzonedb_t *)
284 dl_load_symbol(cd, "dlz_findzonedb", ISC_TRUE);
286 if (cd->dlz_create == NULL ||
287 cd->dlz_lookup == NULL ||
288 cd->dlz_findzonedb == NULL)
290 /* We're missing a required symbol */
294 cd->dlz_allowzonexfr = (dlz_dlopen_allowzonexfr_t *)
295 dl_load_symbol(cd, "dlz_allowzonexfr", ISC_FALSE);
296 cd->dlz_allnodes = (dlz_dlopen_allnodes_t *)
297 dl_load_symbol(cd, "dlz_allnodes",
298 ISC_TF(cd->dlz_allowzonexfr != NULL));
299 cd->dlz_authority = (dlz_dlopen_authority_t *)
300 dl_load_symbol(cd, "dlz_authority", ISC_FALSE);
301 cd->dlz_newversion = (dlz_dlopen_newversion_t *)
302 dl_load_symbol(cd, "dlz_newversion", ISC_FALSE);
303 cd->dlz_closeversion = (dlz_dlopen_closeversion_t *)
304 dl_load_symbol(cd, "dlz_closeversion",
305 ISC_TF(cd->dlz_newversion != NULL));
306 cd->dlz_configure = (dlz_dlopen_configure_t *)
307 dl_load_symbol(cd, "dlz_configure", ISC_FALSE);
308 cd->dlz_ssumatch = (dlz_dlopen_ssumatch_t *)
309 dl_load_symbol(cd, "dlz_ssumatch", ISC_FALSE);
310 cd->dlz_addrdataset = (dlz_dlopen_addrdataset_t *)
311 dl_load_symbol(cd, "dlz_addrdataset", ISC_FALSE);
312 cd->dlz_subrdataset = (dlz_dlopen_subrdataset_t *)
313 dl_load_symbol(cd, "dlz_subrdataset", ISC_FALSE);
314 cd->dlz_delrdataset = (dlz_dlopen_delrdataset_t *)
315 dl_load_symbol(cd, "dlz_delrdataset", ISC_FALSE);
317 /* Check the version of the API is the same */
318 cd->version = cd->dlz_version(&cd->flags);
319 if (cd->version != DLZ_DLOPEN_VERSION) {
320 dlopen_log(ISC_LOG_ERROR,
321 "dlz_dlopen: incorrect version %d "
322 "should be %d in '%s'",
323 cd->version, DLZ_DLOPEN_VERSION, cd->dl_path);
328 * Call the library's create function. Note that this is an
329 * extended version of dlz create, with the addition of
330 * named function pointers for helper functions that the
331 * driver will need. This avoids the need for the backend to
332 * link the BIND9 libraries
335 result = cd->dlz_create(dlzname, argc-1, argv+1,
338 "putrr", dns_sdlz_putrr,
339 "putnamedrr", dns_sdlz_putnamedrr,
340 "writeable_zone", dns_dlz_writeablezone,
343 if (result != ISC_R_SUCCESS)
348 return (ISC_R_SUCCESS);
351 dlopen_log(ISC_LOG_ERROR, "dlz_dlopen of '%s' failed", dlzname);
353 isc_mem_free(mctx, cd->dl_path);
355 isc_mem_free(mctx, cd->dlzname);
357 (void) isc_mutex_destroy(&cd->lock);
360 dlclose(cd->dl_handle);
362 isc_mem_put(mctx, cd, sizeof(*cd));
363 isc_mem_destroy(&mctx);
369 * Called when bind is shutting down
372 dlopen_dlz_destroy(void *driverarg, void *dbdata) {
373 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
378 if (cd->dlz_destroy) {
380 cd->dlz_destroy(cd->dbdata);
385 isc_mem_free(cd->mctx, cd->dl_path);
387 isc_mem_free(cd->mctx, cd->dlzname);
391 dlclose(cd->dl_handle);
394 (void) isc_mutex_destroy(&cd->lock);
397 isc_mem_put(mctx, cd, sizeof(*cd));
398 isc_mem_destroy(&mctx);
402 * Called to start a transaction
405 dlopen_dlz_newversion(const char *zone, void *driverarg, void *dbdata,
408 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
413 if (cd->dlz_newversion == NULL)
414 return (ISC_R_NOTIMPLEMENTED);
417 result = cd->dlz_newversion(zone, cd->dbdata, versionp);
423 * Called to end a transaction
426 dlopen_dlz_closeversion(const char *zone, isc_boolean_t commit,
427 void *driverarg, void *dbdata, void **versionp)
429 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
433 if (cd->dlz_newversion == NULL) {
439 cd->dlz_closeversion(zone, commit, cd->dbdata, versionp);
444 * Called on startup to configure any writeable zones
447 dlopen_dlz_configure(dns_view_t *view, void *driverarg, void *dbdata) {
448 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
453 if (cd->dlz_configure == NULL)
454 return (ISC_R_SUCCESS);
457 cd->in_configure = ISC_TRUE;
458 result = cd->dlz_configure(view, cd->dbdata);
459 cd->in_configure = ISC_FALSE;
467 * Check for authority to change a name
470 dlopen_dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
471 const char *type, const char *key, isc_uint32_t keydatalen,
472 unsigned char *keydata, void *driverarg, void *dbdata)
474 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
479 if (cd->dlz_ssumatch == NULL)
483 ret = cd->dlz_ssumatch(signer, name, tcpaddr, type, key, keydatalen,
484 keydata, cd->dbdata);
495 dlopen_dlz_addrdataset(const char *name, const char *rdatastr,
496 void *driverarg, void *dbdata, void *version)
498 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
503 if (cd->dlz_addrdataset == NULL)
504 return (ISC_R_NOTIMPLEMENTED);
507 result = cd->dlz_addrdataset(name, rdatastr, cd->dbdata, version);
514 * Subtract an rdataset
517 dlopen_dlz_subrdataset(const char *name, const char *rdatastr,
518 void *driverarg, void *dbdata, void *version)
520 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
525 if (cd->dlz_subrdataset == NULL)
526 return (ISC_R_NOTIMPLEMENTED);
529 result = cd->dlz_subrdataset(name, rdatastr, cd->dbdata, version);
539 dlopen_dlz_delrdataset(const char *name, const char *type,
540 void *driverarg, void *dbdata, void *version)
542 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
547 if (cd->dlz_delrdataset == NULL)
548 return (ISC_R_NOTIMPLEMENTED);
551 result = cd->dlz_delrdataset(name, type, cd->dbdata, version);
558 static dns_sdlzmethods_t dlz_dlopen_methods = {
561 dlopen_dlz_findzonedb,
563 dlopen_dlz_authority,
565 dlopen_dlz_allowzonexfr,
566 dlopen_dlz_newversion,
567 dlopen_dlz_closeversion,
568 dlopen_dlz_configure,
570 dlopen_dlz_addrdataset,
571 dlopen_dlz_subrdataset,
572 dlopen_dlz_delrdataset
577 * Register driver with BIND
580 dlz_dlopen_init(isc_mem_t *mctx) {
581 #ifndef ISC_DLZ_DLOPEN
583 return (ISC_R_NOTIMPLEMENTED);
587 dlopen_log(2, "Registering DLZ_dlopen driver");
589 result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
590 DNS_SDLZFLAG_RELATIVEOWNER |
591 DNS_SDLZFLAG_THREADSAFE,
594 if (result != ISC_R_SUCCESS) {
595 UNEXPECTED_ERROR(__FILE__, __LINE__,
596 "dns_sdlzregister() failed: %s",
597 isc_result_totext(result));
598 result = ISC_R_UNEXPECTED;
607 * Unregister the driver
610 dlz_dlopen_clear(void) {
611 #ifdef ISC_DLZ_DLOPEN
612 dlopen_log(2, "Unregistering DLZ_dlopen driver");
613 if (dlz_dlopen != NULL)
614 dns_sdlzunregister(&dlz_dlopen);