]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - contrib/bind9/bin/named/unix/dlz_dlopen_driver.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / contrib / bind9 / bin / named / unix / dlz_dlopen_driver.c
1 /*
2  * Copyright (C) 2011  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.4 2011-03-17 09:41:06 fdupont 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;
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
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);
324                 goto failed;
325         }
326
327         /*
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
333          */
334         MAYBE_LOCK(cd);
335         result = cd->dlz_create(dlzname, argc-1, argv+1,
336                                 &cd->dbdata,
337                                 "log", dlopen_log,
338                                 "putrr", dns_sdlz_putrr,
339                                 "putnamedrr", dns_sdlz_putnamedrr,
340                                 "writeable_zone", dns_dlz_writeablezone,
341                                 NULL);
342         MAYBE_UNLOCK(cd);
343         if (result != ISC_R_SUCCESS)
344                 goto failed;
345
346         *dbdata = cd;
347
348         return (ISC_R_SUCCESS);
349
350 failed:
351         dlopen_log(ISC_LOG_ERROR, "dlz_dlopen of '%s' failed", dlzname);
352         if (cd->dl_path)
353                 isc_mem_free(mctx, cd->dl_path);
354         if (cd->dlzname)
355                 isc_mem_free(mctx, cd->dlzname);
356         if (dlopen_flags)
357                 (void) isc_mutex_destroy(&cd->lock);
358 #ifdef HAVE_DLCLOSE
359         if (cd->dl_handle)
360                 dlclose(cd->dl_handle);
361 #endif
362         isc_mem_put(mctx, cd, sizeof(*cd));
363         isc_mem_destroy(&mctx);
364         return (result);
365 }
366
367
368 /*
369  * Called when bind is shutting down
370  */
371 static void
372 dlopen_dlz_destroy(void *driverarg, void *dbdata) {
373         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
374         isc_mem_t *mctx;
375
376         UNUSED(driverarg);
377
378         if (cd->dlz_destroy) {
379                 MAYBE_LOCK(cd);
380                 cd->dlz_destroy(cd->dbdata);
381                 MAYBE_UNLOCK(cd);
382         }
383
384         if (cd->dl_path)
385                 isc_mem_free(cd->mctx, cd->dl_path);
386         if (cd->dlzname)
387                 isc_mem_free(cd->mctx, cd->dlzname);
388
389 #ifdef HAVE_DLCLOSE
390         if (cd->dl_handle)
391                 dlclose(cd->dl_handle);
392 #endif
393
394         (void) isc_mutex_destroy(&cd->lock);
395
396         mctx = cd->mctx;
397         isc_mem_put(mctx, cd, sizeof(*cd));
398         isc_mem_destroy(&mctx);
399 }
400
401 /*
402  * Called to start a transaction
403  */
404 static isc_result_t
405 dlopen_dlz_newversion(const char *zone, void *driverarg, void *dbdata,
406                       void **versionp)
407 {
408         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
409         isc_result_t result;
410
411         UNUSED(driverarg);
412
413         if (cd->dlz_newversion == NULL)
414                 return (ISC_R_NOTIMPLEMENTED);
415
416         MAYBE_LOCK(cd);
417         result = cd->dlz_newversion(zone, cd->dbdata, versionp);
418         MAYBE_UNLOCK(cd);
419         return (result);
420 }
421
422 /*
423  * Called to end a transaction
424  */
425 static void
426 dlopen_dlz_closeversion(const char *zone, isc_boolean_t commit,
427                         void *driverarg, void *dbdata, void **versionp)
428 {
429         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
430
431         UNUSED(driverarg);
432
433         if (cd->dlz_newversion == NULL) {
434                 *versionp = NULL;
435                 return;
436         }
437
438         MAYBE_LOCK(cd);
439         cd->dlz_closeversion(zone, commit, cd->dbdata, versionp);
440         MAYBE_UNLOCK(cd);
441 }
442
443 /*
444  * Called on startup to configure any writeable zones
445  */
446 static isc_result_t
447 dlopen_dlz_configure(dns_view_t *view, void *driverarg, void *dbdata) {
448         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
449         isc_result_t result;
450
451         UNUSED(driverarg);
452
453         if (cd->dlz_configure == NULL)
454                 return (ISC_R_SUCCESS);
455
456         MAYBE_LOCK(cd);
457         cd->in_configure = ISC_TRUE;
458         result = cd->dlz_configure(view, cd->dbdata);
459         cd->in_configure = ISC_FALSE;
460         MAYBE_UNLOCK(cd);
461
462         return (result);
463 }
464
465
466 /*
467  * Check for authority to change a name
468  */
469 static isc_boolean_t
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)
473 {
474         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
475         isc_boolean_t ret;
476
477         UNUSED(driverarg);
478
479         if (cd->dlz_ssumatch == NULL)
480                 return (ISC_FALSE);
481
482         MAYBE_LOCK(cd);
483         ret = cd->dlz_ssumatch(signer, name, tcpaddr, type, key, keydatalen,
484                                keydata, cd->dbdata);
485         MAYBE_UNLOCK(cd);
486
487         return (ret);
488 }
489
490
491 /*
492  * Add an rdataset
493  */
494 static isc_result_t
495 dlopen_dlz_addrdataset(const char *name, const char *rdatastr,
496                        void *driverarg, void *dbdata, void *version)
497 {
498         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
499         isc_result_t result;
500
501         UNUSED(driverarg);
502
503         if (cd->dlz_addrdataset == NULL)
504                 return (ISC_R_NOTIMPLEMENTED);
505
506         MAYBE_LOCK(cd);
507         result = cd->dlz_addrdataset(name, rdatastr, cd->dbdata, version);
508         MAYBE_UNLOCK(cd);
509
510         return (result);
511 }
512
513 /*
514  * Subtract an rdataset
515  */
516 static isc_result_t
517 dlopen_dlz_subrdataset(const char *name, const char *rdatastr,
518                        void *driverarg, void *dbdata, void *version)
519 {
520         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
521         isc_result_t result;
522
523         UNUSED(driverarg);
524
525         if (cd->dlz_subrdataset == NULL)
526                 return (ISC_R_NOTIMPLEMENTED);
527
528         MAYBE_LOCK(cd);
529         result = cd->dlz_subrdataset(name, rdatastr, cd->dbdata, version);
530         MAYBE_UNLOCK(cd);
531
532         return (result);
533 }
534
535 /*
536   delete a rdataset
537  */
538 static isc_result_t
539 dlopen_dlz_delrdataset(const char *name, const char *type,
540                        void *driverarg, void *dbdata, void *version)
541 {
542         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
543         isc_result_t result;
544
545         UNUSED(driverarg);
546
547         if (cd->dlz_delrdataset == NULL)
548                 return (ISC_R_NOTIMPLEMENTED);
549
550         MAYBE_LOCK(cd);
551         result = cd->dlz_delrdataset(name, type, cd->dbdata, version);
552         MAYBE_UNLOCK(cd);
553
554         return (result);
555 }
556
557
558 static dns_sdlzmethods_t dlz_dlopen_methods = {
559         dlopen_dlz_create,
560         dlopen_dlz_destroy,
561         dlopen_dlz_findzonedb,
562         dlopen_dlz_lookup,
563         dlopen_dlz_authority,
564         dlopen_dlz_allnodes,
565         dlopen_dlz_allowzonexfr,
566         dlopen_dlz_newversion,
567         dlopen_dlz_closeversion,
568         dlopen_dlz_configure,
569         dlopen_dlz_ssumatch,
570         dlopen_dlz_addrdataset,
571         dlopen_dlz_subrdataset,
572         dlopen_dlz_delrdataset
573 };
574 #endif
575
576 /*
577  * Register driver with BIND
578  */
579 isc_result_t
580 dlz_dlopen_init(isc_mem_t *mctx) {
581 #ifndef ISC_DLZ_DLOPEN
582         UNUSED(mctx);
583         return (ISC_R_NOTIMPLEMENTED);
584 #else
585         isc_result_t result;
586
587         dlopen_log(2, "Registering DLZ_dlopen driver");
588
589         result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
590                                   DNS_SDLZFLAG_RELATIVEOWNER |
591                                   DNS_SDLZFLAG_THREADSAFE,
592                                   mctx, &dlz_dlopen);
593
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;
599         }
600
601         return (result);
602 #endif
603 }
604
605
606 /*
607  * Unregister the driver
608  */
609 void
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);
615 #endif
616 }