]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/bind9/bin/named/unix/dlz_dlopen_driver.c
MFC r254651:
[FreeBSD/stable/9.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$ */
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                   dns_clientinfomethods_t *methods,
182                   dns_clientinfo_t *clientinfo)
183 {
184         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
185         isc_result_t result;
186
187         UNUSED(driverarg);
188
189         MAYBE_LOCK(cd);
190         result = cd->dlz_lookup(zone, name, cd->dbdata, lookup,
191                                 methods, clientinfo);
192         MAYBE_UNLOCK(cd);
193         return (result);
194 }
195
196 /*
197  * Load a symbol from the library
198  */
199 static void *
200 dl_load_symbol(dlopen_data_t *cd, const char *symbol, isc_boolean_t mandatory) {
201         void *ptr = dlsym(cd->dl_handle, symbol);
202         if (ptr == NULL && mandatory) {
203                 dlopen_log(ISC_LOG_ERROR,
204                            "dlz_dlopen: library '%s' is missing "
205                            "required symbol '%s'", cd->dl_path, symbol);
206         }
207         return (ptr);
208 }
209
210 /*
211  * Called at startup for each dlopen zone in named.conf
212  */
213 static isc_result_t
214 dlopen_dlz_create(const char *dlzname, unsigned int argc, char *argv[],
215                   void *driverarg, void **dbdata)
216 {
217         dlopen_data_t *cd;
218         isc_mem_t *mctx = NULL;
219         isc_result_t result = ISC_R_FAILURE;
220         int dlopen_flags = 0;
221
222         UNUSED(driverarg);
223
224         if (argc < 2) {
225                 dlopen_log(ISC_LOG_ERROR,
226                            "dlz_dlopen driver for '%s' needs a path to "
227                            "the shared library", dlzname);
228                 return (ISC_R_FAILURE);
229         }
230
231         result = isc_mem_create(0, 0, &mctx);
232         if (result != ISC_R_SUCCESS)
233                 return (result);
234
235         cd = isc_mem_get(mctx, sizeof(*cd));
236         if (cd == NULL) {
237                 isc_mem_destroy(&mctx);
238                 return (ISC_R_NOMEMORY);
239         }
240         memset(cd, 0, sizeof(*cd));
241
242         cd->mctx = mctx;
243
244         cd->dl_path = isc_mem_strdup(cd->mctx, argv[1]);
245         if (cd->dl_path == NULL) {
246                 goto failed;
247         }
248
249         cd->dlzname = isc_mem_strdup(cd->mctx, dlzname);
250         if (cd->dlzname == NULL) {
251                 goto failed;
252         }
253
254         /* Initialize the lock */
255         result = isc_mutex_init(&cd->lock);
256         if (result != ISC_R_SUCCESS)
257                 goto failed;
258
259         /* Open the library */
260         dlopen_flags = RTLD_NOW|RTLD_GLOBAL;
261
262 #ifdef RTLD_DEEPBIND
263         /*
264          * If RTLD_DEEPBIND is available then use it. This can avoid
265          * issues with a module using a different version of a system
266          * library than one that bind9 uses. For example, bind9 may link
267          * to MIT kerberos, but the module may use Heimdal. If we don't
268          * use RTLD_DEEPBIND then we could end up with Heimdal functions
269          * calling MIT functions, which leads to bizarre results (usually
270          * a segfault).
271          */
272         dlopen_flags |= RTLD_DEEPBIND;
273 #endif
274
275         cd->dl_handle = dlopen(cd->dl_path, dlopen_flags);
276         if (cd->dl_handle == NULL) {
277                 dlopen_log(ISC_LOG_ERROR,
278                            "dlz_dlopen failed to open library '%s' - %s",
279                            cd->dl_path, dlerror());
280                 goto failed;
281         }
282
283         /* Find the symbols */
284         cd->dlz_version = (dlz_dlopen_version_t *)
285                 dl_load_symbol(cd, "dlz_version", ISC_TRUE);
286         cd->dlz_create = (dlz_dlopen_create_t *)
287                 dl_load_symbol(cd, "dlz_create", ISC_TRUE);
288         cd->dlz_lookup = (dlz_dlopen_lookup_t *)
289                 dl_load_symbol(cd, "dlz_lookup", ISC_TRUE);
290         cd->dlz_findzonedb = (dlz_dlopen_findzonedb_t *)
291                 dl_load_symbol(cd, "dlz_findzonedb", ISC_TRUE);
292
293         if (cd->dlz_create == NULL ||
294             cd->dlz_lookup == NULL ||
295             cd->dlz_findzonedb == NULL)
296         {
297                 /* We're missing a required symbol */
298                 goto failed;
299         }
300
301         cd->dlz_allowzonexfr = (dlz_dlopen_allowzonexfr_t *)
302                 dl_load_symbol(cd, "dlz_allowzonexfr", ISC_FALSE);
303         cd->dlz_allnodes = (dlz_dlopen_allnodes_t *)
304                 dl_load_symbol(cd, "dlz_allnodes",
305                                ISC_TF(cd->dlz_allowzonexfr != NULL));
306         cd->dlz_authority = (dlz_dlopen_authority_t *)
307                 dl_load_symbol(cd, "dlz_authority", ISC_FALSE);
308         cd->dlz_newversion = (dlz_dlopen_newversion_t *)
309                 dl_load_symbol(cd, "dlz_newversion", ISC_FALSE);
310         cd->dlz_closeversion = (dlz_dlopen_closeversion_t *)
311                 dl_load_symbol(cd, "dlz_closeversion",
312                                ISC_TF(cd->dlz_newversion != NULL));
313         cd->dlz_configure = (dlz_dlopen_configure_t *)
314                 dl_load_symbol(cd, "dlz_configure", ISC_FALSE);
315         cd->dlz_ssumatch = (dlz_dlopen_ssumatch_t *)
316                 dl_load_symbol(cd, "dlz_ssumatch", ISC_FALSE);
317         cd->dlz_addrdataset = (dlz_dlopen_addrdataset_t *)
318                 dl_load_symbol(cd, "dlz_addrdataset", ISC_FALSE);
319         cd->dlz_subrdataset = (dlz_dlopen_subrdataset_t *)
320                 dl_load_symbol(cd, "dlz_subrdataset", ISC_FALSE);
321         cd->dlz_delrdataset = (dlz_dlopen_delrdataset_t *)
322                 dl_load_symbol(cd, "dlz_delrdataset", ISC_FALSE);
323         cd->dlz_destroy = (dlz_dlopen_destroy_t *)
324                 dl_load_symbol(cd, "dlz_destroy", ISC_FALSE);
325
326         /* Check the version of the API is the same */
327         cd->version = cd->dlz_version(&cd->flags);
328         if (cd->version != DLZ_DLOPEN_VERSION) {
329                 dlopen_log(ISC_LOG_ERROR,
330                            "dlz_dlopen: incorrect version %d "
331                            "should be %d in '%s'",
332                            cd->version, DLZ_DLOPEN_VERSION, cd->dl_path);
333                 goto failed;
334         }
335
336         /*
337          * Call the library's create function. Note that this is an
338          * extended version of dlz create, with the addition of
339          * named function pointers for helper functions that the
340          * driver will need. This avoids the need for the backend to
341          * link the BIND9 libraries
342          */
343         MAYBE_LOCK(cd);
344         result = cd->dlz_create(dlzname, argc-1, argv+1,
345                                 &cd->dbdata,
346                                 "log", dlopen_log,
347                                 "putrr", dns_sdlz_putrr,
348                                 "putnamedrr", dns_sdlz_putnamedrr,
349                                 "writeable_zone", dns_dlz_writeablezone,
350                                 NULL);
351         MAYBE_UNLOCK(cd);
352         if (result != ISC_R_SUCCESS)
353                 goto failed;
354
355         *dbdata = cd;
356
357         return (ISC_R_SUCCESS);
358
359 failed:
360         dlopen_log(ISC_LOG_ERROR, "dlz_dlopen of '%s' failed", dlzname);
361         if (cd->dl_path != NULL)
362                 isc_mem_free(mctx, cd->dl_path);
363         if (cd->dlzname != NULL)
364                 isc_mem_free(mctx, cd->dlzname);
365         if (dlopen_flags != 0)
366                 (void) isc_mutex_destroy(&cd->lock);
367 #ifdef HAVE_DLCLOSE
368         if (cd->dl_handle)
369                 dlclose(cd->dl_handle);
370 #endif
371         isc_mem_put(mctx, cd, sizeof(*cd));
372         isc_mem_destroy(&mctx);
373         return (result);
374 }
375
376
377 /*
378  * Called when bind is shutting down
379  */
380 static void
381 dlopen_dlz_destroy(void *driverarg, void *dbdata) {
382         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
383         isc_mem_t *mctx;
384
385         UNUSED(driverarg);
386
387         if (cd->dlz_destroy) {
388                 MAYBE_LOCK(cd);
389                 cd->dlz_destroy(cd->dbdata);
390                 MAYBE_UNLOCK(cd);
391         }
392
393         if (cd->dl_path)
394                 isc_mem_free(cd->mctx, cd->dl_path);
395         if (cd->dlzname)
396                 isc_mem_free(cd->mctx, cd->dlzname);
397
398 #ifdef HAVE_DLCLOSE
399         if (cd->dl_handle)
400                 dlclose(cd->dl_handle);
401 #endif
402
403         (void) isc_mutex_destroy(&cd->lock);
404
405         mctx = cd->mctx;
406         isc_mem_put(mctx, cd, sizeof(*cd));
407         isc_mem_destroy(&mctx);
408 }
409
410 /*
411  * Called to start a transaction
412  */
413 static isc_result_t
414 dlopen_dlz_newversion(const char *zone, void *driverarg, void *dbdata,
415                       void **versionp)
416 {
417         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
418         isc_result_t result;
419
420         UNUSED(driverarg);
421
422         if (cd->dlz_newversion == NULL)
423                 return (ISC_R_NOTIMPLEMENTED);
424
425         MAYBE_LOCK(cd);
426         result = cd->dlz_newversion(zone, cd->dbdata, versionp);
427         MAYBE_UNLOCK(cd);
428         return (result);
429 }
430
431 /*
432  * Called to end a transaction
433  */
434 static void
435 dlopen_dlz_closeversion(const char *zone, isc_boolean_t commit,
436                         void *driverarg, void *dbdata, void **versionp)
437 {
438         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
439
440         UNUSED(driverarg);
441
442         if (cd->dlz_newversion == NULL) {
443                 *versionp = NULL;
444                 return;
445         }
446
447         MAYBE_LOCK(cd);
448         cd->dlz_closeversion(zone, commit, cd->dbdata, versionp);
449         MAYBE_UNLOCK(cd);
450 }
451
452 /*
453  * Called on startup to configure any writeable zones
454  */
455 static isc_result_t
456 dlopen_dlz_configure(dns_view_t *view, void *driverarg, void *dbdata) {
457         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
458         isc_result_t result;
459
460         UNUSED(driverarg);
461
462         if (cd->dlz_configure == NULL)
463                 return (ISC_R_SUCCESS);
464
465         MAYBE_LOCK(cd);
466         cd->in_configure = ISC_TRUE;
467         result = cd->dlz_configure(view, cd->dbdata);
468         cd->in_configure = ISC_FALSE;
469         MAYBE_UNLOCK(cd);
470
471         return (result);
472 }
473
474
475 /*
476  * Check for authority to change a name
477  */
478 static isc_boolean_t
479 dlopen_dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
480                     const char *type, const char *key, isc_uint32_t keydatalen,
481                     unsigned char *keydata, void *driverarg, void *dbdata)
482 {
483         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
484         isc_boolean_t ret;
485
486         UNUSED(driverarg);
487
488         if (cd->dlz_ssumatch == NULL)
489                 return (ISC_FALSE);
490
491         MAYBE_LOCK(cd);
492         ret = cd->dlz_ssumatch(signer, name, tcpaddr, type, key, keydatalen,
493                                keydata, cd->dbdata);
494         MAYBE_UNLOCK(cd);
495
496         return (ret);
497 }
498
499
500 /*
501  * Add an rdataset
502  */
503 static isc_result_t
504 dlopen_dlz_addrdataset(const char *name, const char *rdatastr,
505                        void *driverarg, void *dbdata, void *version)
506 {
507         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
508         isc_result_t result;
509
510         UNUSED(driverarg);
511
512         if (cd->dlz_addrdataset == NULL)
513                 return (ISC_R_NOTIMPLEMENTED);
514
515         MAYBE_LOCK(cd);
516         result = cd->dlz_addrdataset(name, rdatastr, cd->dbdata, version);
517         MAYBE_UNLOCK(cd);
518
519         return (result);
520 }
521
522 /*
523  * Subtract an rdataset
524  */
525 static isc_result_t
526 dlopen_dlz_subrdataset(const char *name, const char *rdatastr,
527                        void *driverarg, void *dbdata, void *version)
528 {
529         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
530         isc_result_t result;
531
532         UNUSED(driverarg);
533
534         if (cd->dlz_subrdataset == NULL)
535                 return (ISC_R_NOTIMPLEMENTED);
536
537         MAYBE_LOCK(cd);
538         result = cd->dlz_subrdataset(name, rdatastr, cd->dbdata, version);
539         MAYBE_UNLOCK(cd);
540
541         return (result);
542 }
543
544 /*
545   delete a rdataset
546  */
547 static isc_result_t
548 dlopen_dlz_delrdataset(const char *name, const char *type,
549                        void *driverarg, void *dbdata, void *version)
550 {
551         dlopen_data_t *cd = (dlopen_data_t *) dbdata;
552         isc_result_t result;
553
554         UNUSED(driverarg);
555
556         if (cd->dlz_delrdataset == NULL)
557                 return (ISC_R_NOTIMPLEMENTED);
558
559         MAYBE_LOCK(cd);
560         result = cd->dlz_delrdataset(name, type, cd->dbdata, version);
561         MAYBE_UNLOCK(cd);
562
563         return (result);
564 }
565
566
567 static dns_sdlzmethods_t dlz_dlopen_methods = {
568         dlopen_dlz_create,
569         dlopen_dlz_destroy,
570         dlopen_dlz_findzonedb,
571         dlopen_dlz_lookup,
572         dlopen_dlz_authority,
573         dlopen_dlz_allnodes,
574         dlopen_dlz_allowzonexfr,
575         dlopen_dlz_newversion,
576         dlopen_dlz_closeversion,
577         dlopen_dlz_configure,
578         dlopen_dlz_ssumatch,
579         dlopen_dlz_addrdataset,
580         dlopen_dlz_subrdataset,
581         dlopen_dlz_delrdataset
582 };
583 #endif
584
585 /*
586  * Register driver with BIND
587  */
588 isc_result_t
589 dlz_dlopen_init(isc_mem_t *mctx) {
590 #ifndef ISC_DLZ_DLOPEN
591         UNUSED(mctx);
592         return (ISC_R_NOTIMPLEMENTED);
593 #else
594         isc_result_t result;
595
596         dlopen_log(2, "Registering DLZ_dlopen driver");
597
598         result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
599                                   DNS_SDLZFLAG_RELATIVEOWNER |
600                                   DNS_SDLZFLAG_THREADSAFE,
601                                   mctx, &dlz_dlopen);
602
603         if (result != ISC_R_SUCCESS) {
604                 UNEXPECTED_ERROR(__FILE__, __LINE__,
605                                  "dns_sdlzregister() failed: %s",
606                                  isc_result_totext(result));
607                 result = ISC_R_UNEXPECTED;
608         }
609
610         return (result);
611 #endif
612 }
613
614
615 /*
616  * Unregister the driver
617  */
618 void
619 dlz_dlopen_clear(void) {
620 #ifdef ISC_DLZ_DLOPEN
621         dlopen_log(2, "Unregistering DLZ_dlopen driver");
622         if (dlz_dlopen != NULL)
623                 dns_sdlzunregister(&dlz_dlopen);
624 #endif
625 }