PageRenderTime 51ms CodeModel.GetById 13ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/bind9/bin/named/unix/dlz_dlopen_driver.c

https://bitbucket.org/freebsd/freebsd-head/
C | 618 lines | 428 code | 110 blank | 80 comment | 64 complexity | 423473338225d4c043c8a410b1f12de4 MD5 | raw file
  1/*
  2 * Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
  3 *
  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.
  7 *
  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.
 15 */
 16
 17/* $Id: dlz_dlopen_driver.c,v 1.1.4.6 2012/02/22 23:46:35 tbox Exp $ */
 18
 19#include <config.h>
 20
 21#include <stdio.h>
 22#include <string.h>
 23#include <stdlib.h>
 24#include <dlfcn.h>
 25
 26#include <dns/log.h>
 27#include <dns/result.h>
 28#include <dns/dlz_dlopen.h>
 29
 30#include <isc/mem.h>
 31#include <isc/print.h>
 32#include <isc/result.h>
 33#include <isc/util.h>
 34
 35#include <named/globals.h>
 36
 37#include <dlz/dlz_dlopen_driver.h>
 38
 39#ifdef ISC_DLZ_DLOPEN
 40static dns_sdlzimplementation_t *dlz_dlopen = NULL;
 41
 42
 43typedef struct dlopen_data {
 44	isc_mem_t *mctx;
 45	char *dl_path;
 46	char *dlzname;
 47	void *dl_handle;
 48	void *dbdata;
 49	unsigned int flags;
 50	isc_mutex_t lock;
 51	int version;
 52	isc_boolean_t in_configure;
 53
 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;
 69} dlopen_data_t;
 70
 71/* Modules can choose whether they are lock-safe or not. */
 72#define MAYBE_LOCK(cd) \
 73	do { \
 74		if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \
 75		    cd->in_configure == ISC_FALSE) \
 76			LOCK(&cd->lock); \
 77	} while (0)
 78
 79#define MAYBE_UNLOCK(cd) \
 80	do { \
 81		if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \
 82		    cd->in_configure == ISC_FALSE) \
 83			UNLOCK(&cd->lock); \
 84	} while (0)
 85
 86/*
 87 * Log a message at the given level.
 88 */
 89static void dlopen_log(int level, const char *fmt, ...)
 90{
 91	va_list ap;
 92	va_start(ap, fmt);
 93	isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE,
 94		       DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(level),
 95		       fmt, ap);
 96	va_end(ap);
 97}
 98
 99/*
100 * SDLZ methods
101 */
102
103static isc_result_t
104dlopen_dlz_allnodes(const char *zone, void *driverarg, void *dbdata,
105		    dns_sdlzallnodes_t *allnodes)
106{
107	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
108	isc_result_t result;
109
110
111	UNUSED(driverarg);
112
113	if (cd->dlz_allnodes == NULL) {
114		return (ISC_R_NOPERM);
115	}
116
117	MAYBE_LOCK(cd);
118	result = cd->dlz_allnodes(zone, cd->dbdata, allnodes);
119	MAYBE_UNLOCK(cd);
120	return (result);
121}
122
123
124static isc_result_t
125dlopen_dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name,
126			const char *client)
127{
128	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
129	isc_result_t result;
130
131	UNUSED(driverarg);
132
133
134	if (cd->dlz_allowzonexfr == NULL) {
135		return (ISC_R_NOPERM);
136	}
137
138	MAYBE_LOCK(cd);
139	result = cd->dlz_allowzonexfr(cd->dbdata, name, client);
140	MAYBE_UNLOCK(cd);
141	return (result);
142}
143
144static isc_result_t
145dlopen_dlz_authority(const char *zone, void *driverarg, void *dbdata,
146		   dns_sdlzlookup_t *lookup)
147{
148	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
149	isc_result_t result;
150
151	UNUSED(driverarg);
152
153	if (cd->dlz_authority == NULL) {
154		return (ISC_R_NOTIMPLEMENTED);
155	}
156
157	MAYBE_LOCK(cd);
158	result = cd->dlz_authority(zone, cd->dbdata, lookup);
159	MAYBE_UNLOCK(cd);
160	return (result);
161}
162
163static isc_result_t
164dlopen_dlz_findzonedb(void *driverarg, void *dbdata, const char *name)
165{
166	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
167	isc_result_t result;
168
169	UNUSED(driverarg);
170
171	MAYBE_LOCK(cd);
172	result = cd->dlz_findzonedb(cd->dbdata, name);
173	MAYBE_UNLOCK(cd);
174	return (result);
175}
176
177
178static isc_result_t
179dlopen_dlz_lookup(const char *zone, const char *name, void *driverarg,
180		  void *dbdata, dns_sdlzlookup_t *lookup)
181{
182	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
183	isc_result_t result;
184
185	UNUSED(driverarg);
186
187	MAYBE_LOCK(cd);
188	result = cd->dlz_lookup(zone, name, cd->dbdata, lookup);
189	MAYBE_UNLOCK(cd);
190	return (result);
191}
192
193/*
194 * Load a symbol from the library
195 */
196static void *
197dl_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);
203	}
204	return (ptr);
205}
206
207/*
208 * Called at startup for each dlopen zone in named.conf
209 */
210static isc_result_t
211dlopen_dlz_create(const char *dlzname, unsigned int argc, char *argv[],
212		  void *driverarg, void **dbdata)
213{
214	dlopen_data_t *cd;
215	isc_mem_t *mctx = NULL;
216	isc_result_t result = ISC_R_FAILURE;
217	int dlopen_flags = 0;
218
219	UNUSED(driverarg);
220
221	if (argc < 2) {
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);
226	}
227
228	isc_mem_create(0, 0, &mctx);
229
230	cd = isc_mem_get(mctx, sizeof(*cd));
231	if (cd == NULL) {
232		isc_mem_destroy(&mctx);
233		return (ISC_R_NOMEMORY);
234	}
235	memset(cd, 0, sizeof(*cd));
236
237	cd->mctx = mctx;
238
239	cd->dl_path = isc_mem_strdup(cd->mctx, argv[1]);
240	if (cd->dl_path == NULL) {
241		goto failed;
242	}
243
244	cd->dlzname = isc_mem_strdup(cd->mctx, dlzname);
245	if (cd->dlzname == NULL) {
246		goto failed;
247	}
248
249	/* Initialize the lock */
250	isc_mutex_init(&cd->lock);
251
252	/* Open the library */
253	dlopen_flags = RTLD_NOW|RTLD_GLOBAL;
254
255#ifdef RTLD_DEEPBIND
256	/*
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
263	 * a segfault).
264	 */
265	dlopen_flags |= RTLD_DEEPBIND;
266#endif
267
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());
273		goto failed;
274	}
275
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);
285
286	if (cd->dlz_create == NULL ||
287	    cd->dlz_lookup == NULL ||
288	    cd->dlz_findzonedb == NULL)
289	{
290		/* We're missing a required symbol */
291		goto failed;
292	}
293
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);
316	cd->dlz_destroy = (dlz_dlopen_destroy_t *)
317		dl_load_symbol(cd, "dlz_destroy", ISC_FALSE);
318
319	/* Check the version of the API is the same */
320	cd->version = cd->dlz_version(&cd->flags);
321	if (cd->version != DLZ_DLOPEN_VERSION) {
322		dlopen_log(ISC_LOG_ERROR,
323			   "dlz_dlopen: incorrect version %d "
324			   "should be %d in '%s'",
325			   cd->version, DLZ_DLOPEN_VERSION, cd->dl_path);
326		goto failed;
327	}
328
329	/*
330	 * Call the library's create function. Note that this is an
331	 * extended version of dlz create, with the addition of
332	 * named function pointers for helper functions that the
333	 * driver will need. This avoids the need for the backend to
334	 * link the BIND9 libraries
335	 */
336	MAYBE_LOCK(cd);
337	result = cd->dlz_create(dlzname, argc-1, argv+1,
338				&cd->dbdata,
339				"log", dlopen_log,
340				"putrr", dns_sdlz_putrr,
341				"putnamedrr", dns_sdlz_putnamedrr,
342				"writeable_zone", dns_dlz_writeablezone,
343				NULL);
344	MAYBE_UNLOCK(cd);
345	if (result != ISC_R_SUCCESS)
346		goto failed;
347
348	*dbdata = cd;
349
350	return (ISC_R_SUCCESS);
351
352failed:
353	dlopen_log(ISC_LOG_ERROR, "dlz_dlopen of '%s' failed", dlzname);
354	if (cd->dl_path)
355		isc_mem_free(mctx, cd->dl_path);
356	if (cd->dlzname)
357		isc_mem_free(mctx, cd->dlzname);
358	if (dlopen_flags)
359		(void) isc_mutex_destroy(&cd->lock);
360#ifdef HAVE_DLCLOSE
361	if (cd->dl_handle)
362		dlclose(cd->dl_handle);
363#endif
364	isc_mem_put(mctx, cd, sizeof(*cd));
365	isc_mem_destroy(&mctx);
366	return (result);
367}
368
369
370/*
371 * Called when bind is shutting down
372 */
373static void
374dlopen_dlz_destroy(void *driverarg, void *dbdata) {
375	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
376	isc_mem_t *mctx;
377
378	UNUSED(driverarg);
379
380	if (cd->dlz_destroy) {
381		MAYBE_LOCK(cd);
382		cd->dlz_destroy(cd->dbdata);
383		MAYBE_UNLOCK(cd);
384	}
385
386	if (cd->dl_path)
387		isc_mem_free(cd->mctx, cd->dl_path);
388	if (cd->dlzname)
389		isc_mem_free(cd->mctx, cd->dlzname);
390
391#ifdef HAVE_DLCLOSE
392	if (cd->dl_handle)
393		dlclose(cd->dl_handle);
394#endif
395
396	(void) isc_mutex_destroy(&cd->lock);
397
398	mctx = cd->mctx;
399	isc_mem_put(mctx, cd, sizeof(*cd));
400	isc_mem_destroy(&mctx);
401}
402
403/*
404 * Called to start a transaction
405 */
406static isc_result_t
407dlopen_dlz_newversion(const char *zone, void *driverarg, void *dbdata,
408		      void **versionp)
409{
410	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
411	isc_result_t result;
412
413	UNUSED(driverarg);
414
415	if (cd->dlz_newversion == NULL)
416		return (ISC_R_NOTIMPLEMENTED);
417
418	MAYBE_LOCK(cd);
419	result = cd->dlz_newversion(zone, cd->dbdata, versionp);
420	MAYBE_UNLOCK(cd);
421	return (result);
422}
423
424/*
425 * Called to end a transaction
426 */
427static void
428dlopen_dlz_closeversion(const char *zone, isc_boolean_t commit,
429			void *driverarg, void *dbdata, void **versionp)
430{
431	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
432
433	UNUSED(driverarg);
434
435	if (cd->dlz_newversion == NULL) {
436		*versionp = NULL;
437		return;
438	}
439
440	MAYBE_LOCK(cd);
441	cd->dlz_closeversion(zone, commit, cd->dbdata, versionp);
442	MAYBE_UNLOCK(cd);
443}
444
445/*
446 * Called on startup to configure any writeable zones
447 */
448static isc_result_t
449dlopen_dlz_configure(dns_view_t *view, void *driverarg, void *dbdata) {
450	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
451	isc_result_t result;
452
453	UNUSED(driverarg);
454
455	if (cd->dlz_configure == NULL)
456		return (ISC_R_SUCCESS);
457
458	MAYBE_LOCK(cd);
459	cd->in_configure = ISC_TRUE;
460	result = cd->dlz_configure(view, cd->dbdata);
461	cd->in_configure = ISC_FALSE;
462	MAYBE_UNLOCK(cd);
463
464	return (result);
465}
466
467
468/*
469 * Check for authority to change a name
470 */
471static isc_boolean_t
472dlopen_dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
473		    const char *type, const char *key, isc_uint32_t keydatalen,
474		    unsigned char *keydata, void *driverarg, void *dbdata)
475{
476	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
477	isc_boolean_t ret;
478
479	UNUSED(driverarg);
480
481	if (cd->dlz_ssumatch == NULL)
482		return (ISC_FALSE);
483
484	MAYBE_LOCK(cd);
485	ret = cd->dlz_ssumatch(signer, name, tcpaddr, type, key, keydatalen,
486			       keydata, cd->dbdata);
487	MAYBE_UNLOCK(cd);
488
489	return (ret);
490}
491
492
493/*
494 * Add an rdataset
495 */
496static isc_result_t
497dlopen_dlz_addrdataset(const char *name, const char *rdatastr,
498		       void *driverarg, void *dbdata, void *version)
499{
500	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
501	isc_result_t result;
502
503	UNUSED(driverarg);
504
505	if (cd->dlz_addrdataset == NULL)
506		return (ISC_R_NOTIMPLEMENTED);
507
508	MAYBE_LOCK(cd);
509	result = cd->dlz_addrdataset(name, rdatastr, cd->dbdata, version);
510	MAYBE_UNLOCK(cd);
511
512	return (result);
513}
514
515/*
516 * Subtract an rdataset
517 */
518static isc_result_t
519dlopen_dlz_subrdataset(const char *name, const char *rdatastr,
520		       void *driverarg, void *dbdata, void *version)
521{
522	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
523	isc_result_t result;
524
525	UNUSED(driverarg);
526
527	if (cd->dlz_subrdataset == NULL)
528		return (ISC_R_NOTIMPLEMENTED);
529
530	MAYBE_LOCK(cd);
531	result = cd->dlz_subrdataset(name, rdatastr, cd->dbdata, version);
532	MAYBE_UNLOCK(cd);
533
534	return (result);
535}
536
537/*
538  delete a rdataset
539 */
540static isc_result_t
541dlopen_dlz_delrdataset(const char *name, const char *type,
542		       void *driverarg, void *dbdata, void *version)
543{
544	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
545	isc_result_t result;
546
547	UNUSED(driverarg);
548
549	if (cd->dlz_delrdataset == NULL)
550		return (ISC_R_NOTIMPLEMENTED);
551
552	MAYBE_LOCK(cd);
553	result = cd->dlz_delrdataset(name, type, cd->dbdata, version);
554	MAYBE_UNLOCK(cd);
555
556	return (result);
557}
558
559
560static dns_sdlzmethods_t dlz_dlopen_methods = {
561	dlopen_dlz_create,
562	dlopen_dlz_destroy,
563	dlopen_dlz_findzonedb,
564	dlopen_dlz_lookup,
565	dlopen_dlz_authority,
566	dlopen_dlz_allnodes,
567	dlopen_dlz_allowzonexfr,
568	dlopen_dlz_newversion,
569	dlopen_dlz_closeversion,
570	dlopen_dlz_configure,
571	dlopen_dlz_ssumatch,
572	dlopen_dlz_addrdataset,
573	dlopen_dlz_subrdataset,
574	dlopen_dlz_delrdataset
575};
576#endif
577
578/*
579 * Register driver with BIND
580 */
581isc_result_t
582dlz_dlopen_init(isc_mem_t *mctx) {
583#ifndef ISC_DLZ_DLOPEN
584	UNUSED(mctx);
585	return (ISC_R_NOTIMPLEMENTED);
586#else
587	isc_result_t result;
588
589	dlopen_log(2, "Registering DLZ_dlopen driver");
590
591	result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
592				  DNS_SDLZFLAG_RELATIVEOWNER |
593				  DNS_SDLZFLAG_THREADSAFE,
594				  mctx, &dlz_dlopen);
595
596	if (result != ISC_R_SUCCESS) {
597		UNEXPECTED_ERROR(__FILE__, __LINE__,
598				 "dns_sdlzregister() failed: %s",
599				 isc_result_totext(result));
600		result = ISC_R_UNEXPECTED;
601	}
602
603	return (result);
604#endif
605}
606
607
608/*
609 * Unregister the driver
610 */
611void
612dlz_dlopen_clear(void) {
613#ifdef ISC_DLZ_DLOPEN
614	dlopen_log(2, "Unregistering DLZ_dlopen driver");
615	if (dlz_dlopen != NULL)
616		dns_sdlzunregister(&dlz_dlopen);
617#endif
618}