]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bind9/bin/named/unix/dlz_dlopen_driver.c
Update Bind to 9.8.5-P2
[FreeBSD/FreeBSD.git] / contrib / bind9 / bin / named / unix / dlz_dlopen_driver.c
1 /*
2  * Copyright (C) 2011-2013  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         result = isc_mem_create(0, 0, &mctx);
229         if (result != ISC_R_SUCCESS)
230                 return (result);
231
232         cd = isc_mem_get(mctx, sizeof(*cd));
233         if (cd == NULL) {
234                 isc_mem_destroy(&mctx);
235                 return (ISC_R_NOMEMORY);
236         }
237         memset(cd, 0, sizeof(*cd));
238
239         cd->mctx = mctx;
240
241         cd->dl_path = isc_mem_strdup(cd->mctx, argv[1]);
242         if (cd->dl_path == NULL) {
243                 goto failed;
244         }
245
246         cd->dlzname = isc_mem_strdup(cd->mctx, dlzname);
247         if (cd->dlzname == NULL) {
248                 goto failed;
249         }
250
251         /* Initialize the lock */
252         result = isc_mutex_init(&cd->lock);
253         if (result != ISC_R_SUCCESS)
254                 goto failed;
255
256         /* Open the library */
257         dlopen_flags = RTLD_NOW|RTLD_GLOBAL;
258
259 #ifdef RTLD_DEEPBIND
260         /*
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
267          * a segfault).
268          */
269         dlopen_flags |= RTLD_DEEPBIND;
270 #endif
271
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());
277                 goto failed;
278         }
279
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);
289
290         if (cd->dlz_create == NULL ||
291             cd->dlz_lookup == NULL ||
292             cd->dlz_findzonedb == NULL)
293         {
294                 /* We're missing a required symbol */
295                 goto failed;
296         }
297
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);
322
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);
330                 goto failed;
331         }
332
333         /*
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
339          */
340         MAYBE_LOCK(cd);
341         result = cd->dlz_create(dlzname, argc-1, argv+1,
342                                 &cd->dbdata,
343                                 "log", dlopen_log,
344                                 "putrr", dns_sdlz_putrr,
345                                 "putnamedrr", dns_sdlz_putnamedrr,
346                                 "writeable_zone", dns_dlz_writeablezone,
347                                 NULL);
348         MAYBE_UNLOCK(cd);
349         if (result != ISC_R_SUCCESS)
350                 goto failed;
351
352         *dbdata = cd;
353
354         return (ISC_R_SUCCESS);
355
356 failed:
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);
364 #ifdef HAVE_DLCLOSE
365         if (cd->dl_handle)
366                 dlclose(cd->dl_handle);
367 #endif
368         isc_mem_put(mctx, cd, sizeof(*cd));
369         isc_mem_destroy(&mctx);
370         return (result);
371 }
372
373
374 /*
375  * Called when bind is shutting down
376  */
377 static void
378 dlopen_dlz_destroy(void *driverarg, void *dbdata) {
379         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
380         isc_mem_t *mctx;
381
382         UNUSED(driverarg);
383
384         if (cd->dlz_destroy) {
385                 MAYBE_LOCK(cd);
386                 cd->dlz_destroy(cd->dbdata);
387                 MAYBE_UNLOCK(cd);
388         }
389
390         if (cd->dl_path)
391                 isc_mem_free(cd->mctx, cd->dl_path);
392         if (cd->dlzname)
393                 isc_mem_free(cd->mctx, cd->dlzname);
394
395 #ifdef HAVE_DLCLOSE
396         if (cd->dl_handle)
397                 dlclose(cd->dl_handle);
398 #endif
399
400         (void) isc_mutex_destroy(&cd->lock);
401
402         mctx = cd->mctx;
403         isc_mem_put(mctx, cd, sizeof(*cd));
404         isc_mem_destroy(&mctx);
405 }
406
407 /*
408  * Called to start a transaction
409  */
410 static isc_result_t
411 dlopen_dlz_newversion(const char *zone, void *driverarg, void *dbdata,
412                       void **versionp)
413 {
414         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
415         isc_result_t result;
416
417         UNUSED(driverarg);
418
419         if (cd->dlz_newversion == NULL)
420                 return (ISC_R_NOTIMPLEMENTED);
421
422         MAYBE_LOCK(cd);
423         result = cd->dlz_newversion(zone, cd->dbdata, versionp);
424         MAYBE_UNLOCK(cd);
425         return (result);
426 }
427
428 /*
429  * Called to end a transaction
430  */
431 static void
432 dlopen_dlz_closeversion(const char *zone, isc_boolean_t commit,
433                         void *driverarg, void *dbdata, void **versionp)
434 {
435         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
436
437         UNUSED(driverarg);
438
439         if (cd->dlz_newversion == NULL) {
440                 *versionp = NULL;
441                 return;
442         }
443
444         MAYBE_LOCK(cd);
445         cd->dlz_closeversion(zone, commit, cd->dbdata, versionp);
446         MAYBE_UNLOCK(cd);
447 }
448
449 /*
450  * Called on startup to configure any writeable zones
451  */
452 static isc_result_t
453 dlopen_dlz_configure(dns_view_t *view, void *driverarg, void *dbdata) {
454         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
455         isc_result_t result;
456
457         UNUSED(driverarg);
458
459         if (cd->dlz_configure == NULL)
460                 return (ISC_R_SUCCESS);
461
462         MAYBE_LOCK(cd);
463         cd->in_configure = ISC_TRUE;
464         result = cd->dlz_configure(view, cd->dbdata);
465         cd->in_configure = ISC_FALSE;
466         MAYBE_UNLOCK(cd);
467
468         return (result);
469 }
470
471
472 /*
473  * Check for authority to change a name
474  */
475 static isc_boolean_t
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)
479 {
480         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
481         isc_boolean_t ret;
482
483         UNUSED(driverarg);
484
485         if (cd->dlz_ssumatch == NULL)
486                 return (ISC_FALSE);
487
488         MAYBE_LOCK(cd);
489         ret = cd->dlz_ssumatch(signer, name, tcpaddr, type, key, keydatalen,
490                                keydata, cd->dbdata);
491         MAYBE_UNLOCK(cd);
492
493         return (ret);
494 }
495
496
497 /*
498  * Add an rdataset
499  */
500 static isc_result_t
501 dlopen_dlz_addrdataset(const char *name, const char *rdatastr,
502                        void *driverarg, void *dbdata, void *version)
503 {
504         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
505         isc_result_t result;
506
507         UNUSED(driverarg);
508
509         if (cd->dlz_addrdataset == NULL)
510                 return (ISC_R_NOTIMPLEMENTED);
511
512         MAYBE_LOCK(cd);
513         result = cd->dlz_addrdataset(name, rdatastr, cd->dbdata, version);
514         MAYBE_UNLOCK(cd);
515
516         return (result);
517 }
518
519 /*
520  * Subtract an rdataset
521  */
522 static isc_result_t
523 dlopen_dlz_subrdataset(const char *name, const char *rdatastr,
524                        void *driverarg, void *dbdata, void *version)
525 {
526         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
527         isc_result_t result;
528
529         UNUSED(driverarg);
530
531         if (cd->dlz_subrdataset == NULL)
532                 return (ISC_R_NOTIMPLEMENTED);
533
534         MAYBE_LOCK(cd);
535         result = cd->dlz_subrdataset(name, rdatastr, cd->dbdata, version);
536         MAYBE_UNLOCK(cd);
537
538         return (result);
539 }
540
541 /*
542   delete a rdataset
543  */
544 static isc_result_t
545 dlopen_dlz_delrdataset(const char *name, const char *type,
546                        void *driverarg, void *dbdata, void *version)
547 {
548         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
549         isc_result_t result;
550
551         UNUSED(driverarg);
552
553         if (cd->dlz_delrdataset == NULL)
554                 return (ISC_R_NOTIMPLEMENTED);
555
556         MAYBE_LOCK(cd);
557         result = cd->dlz_delrdataset(name, type, cd->dbdata, version);
558         MAYBE_UNLOCK(cd);
559
560         return (result);
561 }
562
563
564 static dns_sdlzmethods_t dlz_dlopen_methods = {
565         dlopen_dlz_create,
566         dlopen_dlz_destroy,
567         dlopen_dlz_findzonedb,
568         dlopen_dlz_lookup,
569         dlopen_dlz_authority,
570         dlopen_dlz_allnodes,
571         dlopen_dlz_allowzonexfr,
572         dlopen_dlz_newversion,
573         dlopen_dlz_closeversion,
574         dlopen_dlz_configure,
575         dlopen_dlz_ssumatch,
576         dlopen_dlz_addrdataset,
577         dlopen_dlz_subrdataset,
578         dlopen_dlz_delrdataset
579 };
580 #endif
581
582 /*
583  * Register driver with BIND
584  */
585 isc_result_t
586 dlz_dlopen_init(isc_mem_t *mctx) {
587 #ifndef ISC_DLZ_DLOPEN
588         UNUSED(mctx);
589         return (ISC_R_NOTIMPLEMENTED);
590 #else
591         isc_result_t result;
592
593         dlopen_log(2, "Registering DLZ_dlopen driver");
594
595         result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
596                                   DNS_SDLZFLAG_RELATIVEOWNER |
597                                   DNS_SDLZFLAG_THREADSAFE,
598                                   mctx, &dlz_dlopen);
599
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;
605         }
606
607         return (result);
608 #endif
609 }
610
611
612 /*
613  * Unregister the driver
614  */
615 void
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);
621 #endif
622 }