]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/bind9/bin/named/unix/dlz_dlopen_driver.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / bind9 / bin / named / unix / dlz_dlopen_driver.c
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
40 static dns_sdlzimplementation_t *dlz_dlopen = NULL;
41
42
43 typedef 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  */
89 static 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
103 static isc_result_t
104 dlopen_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
124 static isc_result_t
125 dlopen_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
144 static isc_result_t
145 dlopen_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
163 static isc_result_t
164 dlopen_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
178 static isc_result_t
179 dlopen_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  */
196 static void *
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);
203         }
204         return (ptr);
205 }
206
207 /*
208  * Called at startup for each dlopen zone in named.conf
209  */
210 static isc_result_t
211 dlopen_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
352 failed:
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  */
373 static void
374 dlopen_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  */
406 static isc_result_t
407 dlopen_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  */
427 static void
428 dlopen_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  */
448 static isc_result_t
449 dlopen_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  */
471 static isc_boolean_t
472 dlopen_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  */
496 static isc_result_t
497 dlopen_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  */
518 static isc_result_t
519 dlopen_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  */
540 static isc_result_t
541 dlopen_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
560 static 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  */
581 isc_result_t
582 dlz_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  */
611 void
612 dlz_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 }