1 /* $NetBSD: dlz_dlopen_driver.c,v 1.4 2015/07/08 17:28:55 christos Exp $ */
2
3 /*
4 * Copyright (C) 2011-2014 Internet Systems Consortium, Inc. ("ISC")
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
11 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
13 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /* Id: dlz_dlopen_driver.c,v 1.5 2011/10/14 00:52:32 marka Exp */
20
21 #include <config.h>
22
23 #include <windows.h>
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28
29 #include <dns/log.h>
30 #include <dns/result.h>
31 #include <dns/dlz_dlopen.h>
32
33 #include <isc/mem.h>
34 #include <isc/print.h>
35 #include <isc/result.h>
36 #include <isc/util.h>
37
38 #include <named/globals.h>
39
40 #include <dlz/dlz_dlopen_driver.h>
41
42 #ifdef ISC_DLZ_DLOPEN
43 static dns_sdlzimplementation_t *dlz_dlopen = NULL;
44
45
46 typedef struct dlopen_data {
47 isc_mem_t *mctx;
48 char *dl_path;
49 char *dlzname;
50 HMODULE dl_handle;
51 void *dbdata;
52 unsigned int flags;
53 isc_mutex_t lock;
54 int version;
55 isc_boolean_t in_configure;
56
57 dlz_dlopen_version_t *dlz_version;
58 dlz_dlopen_create_t *dlz_create;
59 dlz_dlopen_findzonedb_t *dlz_findzonedb;
60 dlz_dlopen_lookup_t *dlz_lookup;
61 dlz_dlopen_authority_t *dlz_authority;
62 dlz_dlopen_allnodes_t *dlz_allnodes;
63 dlz_dlopen_allowzonexfr_t *dlz_allowzonexfr;
64 dlz_dlopen_newversion_t *dlz_newversion;
65 dlz_dlopen_closeversion_t *dlz_closeversion;
66 dlz_dlopen_configure_t *dlz_configure;
67 dlz_dlopen_ssumatch_t *dlz_ssumatch;
68 dlz_dlopen_addrdataset_t *dlz_addrdataset;
69 dlz_dlopen_subrdataset_t *dlz_subrdataset;
70 dlz_dlopen_delrdataset_t *dlz_delrdataset;
71 dlz_dlopen_destroy_t *dlz_destroy;
72 } dlopen_data_t;
73
74 /* Modules can choose whether they are lock-safe or not. */
75 #define MAYBE_LOCK(cd) \
76 do { \
77 if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \
78 cd->in_configure == ISC_FALSE) \
79 LOCK(&cd->lock); \
80 } while (/*CONSTCOND*/0)
81
82 #define MAYBE_UNLOCK(cd) \
83 do { \
84 if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \
85 cd->in_configure == ISC_FALSE) \
86 UNLOCK(&cd->lock); \
87 } while (/*CONSTCOND*/0)
88
89 /*
90 * Log a message at the given level.
91 */
dlopen_log(int level,const char * fmt,...)92 static void dlopen_log(int level, const char *fmt, ...)
93 {
94 va_list ap;
95 va_start(ap, fmt);
96 isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE,
97 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(level),
98 fmt, ap);
99 va_end(ap);
100 }
101
102 /*
103 * SDLZ methods
104 */
105
106 static isc_result_t
dlopen_dlz_allnodes(const char * zone,void * driverarg,void * dbdata,dns_sdlzallnodes_t * allnodes)107 dlopen_dlz_allnodes(const char *zone, void *driverarg, void *dbdata,
108 dns_sdlzallnodes_t *allnodes)
109 {
110 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
111 isc_result_t result;
112
113
114 UNUSED(driverarg);
115
116 if (cd->dlz_allnodes == NULL) {
117 return (ISC_R_NOPERM);
118 }
119
120 MAYBE_LOCK(cd);
121 result = cd->dlz_allnodes(zone, cd->dbdata, allnodes);
122 MAYBE_UNLOCK(cd);
123 return (result);
124 }
125
126
127 static isc_result_t
dlopen_dlz_allowzonexfr(void * driverarg,void * dbdata,const char * name,const char * client)128 dlopen_dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name,
129 const char *client)
130 {
131 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
132 isc_result_t result;
133
134 UNUSED(driverarg);
135
136
137 if (cd->dlz_allowzonexfr == NULL) {
138 return (ISC_R_NOPERM);
139 }
140
141 MAYBE_LOCK(cd);
142 result = cd->dlz_allowzonexfr(cd->dbdata, name, client);
143 MAYBE_UNLOCK(cd);
144 return (result);
145 }
146
147 static isc_result_t
dlopen_dlz_authority(const char * zone,void * driverarg,void * dbdata,dns_sdlzlookup_t * lookup)148 dlopen_dlz_authority(const char *zone, void *driverarg, void *dbdata,
149 dns_sdlzlookup_t *lookup)
150 {
151 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
152 isc_result_t result;
153
154 UNUSED(driverarg);
155
156 if (cd->dlz_authority == NULL) {
157 return (ISC_R_NOTIMPLEMENTED);
158 }
159
160 MAYBE_LOCK(cd);
161 result = cd->dlz_authority(zone, cd->dbdata, lookup);
162 MAYBE_UNLOCK(cd);
163 return (result);
164 }
165
166 static isc_result_t
dlopen_dlz_findzonedb(void * driverarg,void * dbdata,const char * name,dns_clientinfomethods_t * methods,dns_clientinfo_t * clientinfo)167 dlopen_dlz_findzonedb(void *driverarg, void *dbdata, const char *name,
168 dns_clientinfomethods_t *methods,
169 dns_clientinfo_t *clientinfo)
170 {
171 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
172 isc_result_t result;
173
174 UNUSED(driverarg);
175
176 MAYBE_LOCK(cd);
177 result = cd->dlz_findzonedb(cd->dbdata, name, methods, clientinfo);
178 MAYBE_UNLOCK(cd);
179 return (result);
180 }
181
182
183 static isc_result_t
dlopen_dlz_lookup(const char * zone,const char * name,void * driverarg,void * dbdata,dns_sdlzlookup_t * lookup,dns_clientinfomethods_t * methods,dns_clientinfo_t * clientinfo)184 dlopen_dlz_lookup(const char *zone, const char *name, void *driverarg,
185 void *dbdata, dns_sdlzlookup_t *lookup,
186 dns_clientinfomethods_t *methods,
187 dns_clientinfo_t *clientinfo)
188 {
189 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
190 isc_result_t result;
191
192 UNUSED(driverarg);
193
194 MAYBE_LOCK(cd);
195 result = cd->dlz_lookup(zone, name, cd->dbdata, lookup,
196 methods, clientinfo);
197 MAYBE_UNLOCK(cd);
198 return (result);
199 }
200
201 /*
202 * Load a symbol from the library
203 */
204 static void *
dl_load_symbol(dlopen_data_t * cd,const char * symbol,isc_boolean_t mandatory)205 dl_load_symbol(dlopen_data_t *cd, const char *symbol, isc_boolean_t mandatory) {
206 void *ptr = GetProcAddress(cd->dl_handle, symbol);
207 if (ptr == NULL && mandatory) {
208 dlopen_log(ISC_LOG_ERROR,
209 "dlz_dlopen: library '%s' is missing "
210 "required symbol '%s'", cd->dl_path, symbol);
211 }
212 return (ptr);
213 }
214
215 /*
216 * Called at startup for each dlopen zone in named.conf
217 */
218 static isc_result_t
dlopen_dlz_create(const char * dlzname,unsigned int argc,char * argv[],void * driverarg,void ** dbdata)219 dlopen_dlz_create(const char *dlzname, unsigned int argc, char *argv[],
220 void *driverarg, void **dbdata)
221 {
222 dlopen_data_t *cd;
223 isc_mem_t *mctx = NULL;
224 isc_result_t result = ISC_R_FAILURE;
225 isc_boolean_t triedload = ISC_FALSE;
226
227 UNUSED(driverarg);
228
229 if (argc < 2) {
230 dlopen_log(ISC_LOG_ERROR,
231 "dlz_dlopen driver for '%s' needs a path to "
232 "the shared library", dlzname);
233 return (ISC_R_FAILURE);
234 }
235
236 isc_mem_create(0, 0, &mctx);
237
238 cd = isc_mem_get(mctx, sizeof(*cd));
239 if (cd == NULL) {
240 isc_mem_destroy(&mctx);
241 return (ISC_R_NOMEMORY);
242 }
243 memset(cd, 0, sizeof(*cd));
244
245 cd->mctx = mctx;
246
247 cd->dl_path = isc_mem_strdup(cd->mctx, argv[1]);
248 if (cd->dl_path == NULL) {
249 result = ISC_R_NOMEMORY;
250 goto failed;
251 }
252
253 cd->dlzname = isc_mem_strdup(cd->mctx, dlzname);
254 if (cd->dlzname == NULL) {
255 result = ISC_R_NOMEMORY;
256 goto failed;
257 }
258
259 triedload = ISC_TRUE;
260
261 /* Initialize the lock */
262 result = isc_mutex_init(&cd->lock);
263 if (result != ISC_R_SUCCESS)
264 goto failed;
265
266 /* Open the library */
267 cd->dl_handle = LoadLibraryA(cd->dl_path);
268 if (cd->dl_handle == NULL) {
269 unsigned int error = GetLastError();
270
271 dlopen_log(ISC_LOG_ERROR,
272 "dlz_dlopen failed to open library '%s' - %u",
273 cd->dl_path, error);
274 result = ISC_R_FAILURE;
275 goto cleanup_lock;
276 }
277
278 /* Find the symbols */
279 cd->dlz_version = (dlz_dlopen_version_t *)
280 dl_load_symbol(cd, "dlz_version", ISC_TRUE);
281 cd->dlz_create = (dlz_dlopen_create_t *)
282 dl_load_symbol(cd, "dlz_create", ISC_TRUE);
283 cd->dlz_lookup = (dlz_dlopen_lookup_t *)
284 dl_load_symbol(cd, "dlz_lookup", ISC_TRUE);
285 cd->dlz_findzonedb = (dlz_dlopen_findzonedb_t *)
286 dl_load_symbol(cd, "dlz_findzonedb", ISC_TRUE);
287
288 if (cd->dlz_create == NULL ||
289 cd->dlz_version == NULL ||
290 cd->dlz_lookup == NULL ||
291 cd->dlz_findzonedb == NULL)
292 {
293 /* We're missing a required symbol */
294 result = ISC_R_FAILURE;
295 goto cleanup_lock;
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
321 /* Check the version of the API is the same */
322 cd->version = cd->dlz_version(&cd->flags);
323 if (cd->version < (DLZ_DLOPEN_VERSION - DLZ_DLOPEN_AGE) ||
324 cd->version > DLZ_DLOPEN_VERSION)
325 {
326 dlopen_log(ISC_LOG_ERROR,
327 "dlz_dlopen: %s: incorrect driver API version %d, "
328 "requires %d",
329 cd->dl_path, cd->version, DLZ_DLOPEN_VERSION);
330 result = ISC_R_FAILURE;
331 goto cleanup_lock;
332 }
333
334 /*
335 * Call the library's create function. Note that this is an
336 * extended version of dlz create, with the addition of
337 * named function pointers for helper functions that the
338 * driver will need. This avoids the need for the backend to
339 * link the BIND9 libraries
340 */
341 MAYBE_LOCK(cd);
342 result = cd->dlz_create(dlzname, argc-1, argv+1,
343 &cd->dbdata,
344 "log", dlopen_log,
345 "putrr", dns_sdlz_putrr,
346 "putnamedrr", dns_sdlz_putnamedrr,
347 "writeable_zone", dns_dlz_writeablezone,
348 NULL);
349 MAYBE_UNLOCK(cd);
350 if (result != ISC_R_SUCCESS)
351 goto cleanup_lock;
352
353 *dbdata = cd;
354
355 return (ISC_R_SUCCESS);
356
357 cleanup_lock:
358 DESTROYLOCK(&cd->lock);
359 failed:
360 dlopen_log(ISC_LOG_ERROR, "dlz_dlopen of '%s' failed", dlzname);
361 if (cd->dl_path)
362 isc_mem_free(mctx, cd->dl_path);
363 if (cd->dlzname)
364 isc_mem_free(mctx, cd->dlzname);
365 if (triedload)
366 (void) isc_mutex_destroy(&cd->lock);
367 if (cd->dl_handle)
368 FreeLibrary(cd->dl_handle);
369 isc_mem_put(mctx, cd, sizeof(*cd));
370 isc_mem_destroy(&mctx);
371 return (result);
372 }
373
374
375 /*
376 * Called when bind is shutting down
377 */
378 static void
dlopen_dlz_destroy(void * driverarg,void * dbdata)379 dlopen_dlz_destroy(void *driverarg, void *dbdata) {
380 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
381 isc_mem_t *mctx;
382
383 UNUSED(driverarg);
384
385 if (cd->dlz_destroy) {
386 MAYBE_LOCK(cd);
387 cd->dlz_destroy(cd->dbdata);
388 MAYBE_UNLOCK(cd);
389 }
390
391 if (cd->dl_path)
392 isc_mem_free(cd->mctx, cd->dl_path);
393 if (cd->dlzname)
394 isc_mem_free(cd->mctx, cd->dlzname);
395
396 if (cd->dl_handle)
397 FreeLibrary(cd->dl_handle);
398
399 DESTROYLOCK(&cd->lock);
400
401 mctx = cd->mctx;
402 isc_mem_put(mctx, cd, sizeof(*cd));
403 isc_mem_destroy(&mctx);
404 }
405
406 /*
407 * Called to start a transaction
408 */
409 static isc_result_t
dlopen_dlz_newversion(const char * zone,void * driverarg,void * dbdata,void ** versionp)410 dlopen_dlz_newversion(const char *zone, void *driverarg, void *dbdata,
411 void **versionp)
412 {
413 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
414 isc_result_t result;
415
416 UNUSED(driverarg);
417
418 if (cd->dlz_newversion == NULL)
419 return (ISC_R_NOTIMPLEMENTED);
420
421 MAYBE_LOCK(cd);
422 result = cd->dlz_newversion(zone, cd->dbdata, versionp);
423 MAYBE_UNLOCK(cd);
424 return (result);
425 }
426
427 /*
428 * Called to end a transaction
429 */
430 static void
dlopen_dlz_closeversion(const char * zone,isc_boolean_t commit,void * driverarg,void * dbdata,void ** versionp)431 dlopen_dlz_closeversion(const char *zone, isc_boolean_t commit,
432 void *driverarg, void *dbdata, void **versionp)
433 {
434 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
435
436 UNUSED(driverarg);
437
438 if (cd->dlz_newversion == NULL) {
439 *versionp = NULL;
440 return;
441 }
442
443 MAYBE_LOCK(cd);
444 cd->dlz_closeversion(zone, commit, cd->dbdata, versionp);
445 MAYBE_UNLOCK(cd);
446 }
447
448 /*
449 * Called on startup to configure any writeable zones
450 */
451 static isc_result_t
dlopen_dlz_configure(dns_view_t * view,dns_dlzdb_t * dlzdb,void * driverarg,void * dbdata)452 dlopen_dlz_configure(dns_view_t *view, dns_dlzdb_t *dlzdb,
453 void *driverarg, void *dbdata)
454 {
455 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
456 isc_result_t result;
457
458 UNUSED(driverarg);
459
460 if (cd->dlz_configure == NULL)
461 return (ISC_R_SUCCESS);
462
463 MAYBE_LOCK(cd);
464 cd->in_configure = ISC_TRUE;
465 result = cd->dlz_configure(view, dlzdb, cd->dbdata);
466 cd->in_configure = ISC_FALSE;
467 MAYBE_UNLOCK(cd);
468
469 return (result);
470 }
471
472
473 /*
474 * Check for authority to change a name
475 */
476 static isc_boolean_t
dlopen_dlz_ssumatch(const char * signer,const char * name,const char * tcpaddr,const char * type,const char * key,isc_uint32_t keydatalen,unsigned char * keydata,void * driverarg,void * dbdata)477 dlopen_dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
478 const char *type, const char *key, isc_uint32_t keydatalen,
479 unsigned char *keydata, void *driverarg, void *dbdata)
480 {
481 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
482 isc_boolean_t ret;
483
484 UNUSED(driverarg);
485
486 if (cd->dlz_ssumatch == NULL)
487 return (ISC_FALSE);
488
489 MAYBE_LOCK(cd);
490 ret = cd->dlz_ssumatch(signer, name, tcpaddr, type, key, keydatalen,
491 keydata, cd->dbdata);
492 MAYBE_UNLOCK(cd);
493
494 return (ret);
495 }
496
497
498 /*
499 * Add an rdataset
500 */
501 static isc_result_t
dlopen_dlz_addrdataset(const char * name,const char * rdatastr,void * driverarg,void * dbdata,void * version)502 dlopen_dlz_addrdataset(const char *name, const char *rdatastr,
503 void *driverarg, void *dbdata, void *version)
504 {
505 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
506 isc_result_t result;
507
508 UNUSED(driverarg);
509
510 if (cd->dlz_addrdataset == NULL)
511 return (ISC_R_NOTIMPLEMENTED);
512
513 MAYBE_LOCK(cd);
514 result = cd->dlz_addrdataset(name, rdatastr, cd->dbdata, version);
515 MAYBE_UNLOCK(cd);
516
517 return (result);
518 }
519
520 /*
521 * Subtract an rdataset
522 */
523 static isc_result_t
dlopen_dlz_subrdataset(const char * name,const char * rdatastr,void * driverarg,void * dbdata,void * version)524 dlopen_dlz_subrdataset(const char *name, const char *rdatastr,
525 void *driverarg, void *dbdata, void *version)
526 {
527 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
528 isc_result_t result;
529
530 UNUSED(driverarg);
531
532 if (cd->dlz_subrdataset == NULL)
533 return (ISC_R_NOTIMPLEMENTED);
534
535 MAYBE_LOCK(cd);
536 result = cd->dlz_subrdataset(name, rdatastr, cd->dbdata, version);
537 MAYBE_UNLOCK(cd);
538
539 return (result);
540 }
541
542 /*
543 delete a rdataset
544 */
545 static isc_result_t
dlopen_dlz_delrdataset(const char * name,const char * type,void * driverarg,void * dbdata,void * version)546 dlopen_dlz_delrdataset(const char *name, const char *type,
547 void *driverarg, void *dbdata, void *version)
548 {
549 dlopen_data_t *cd = (dlopen_data_t *) dbdata;
550 isc_result_t result;
551
552 UNUSED(driverarg);
553
554 if (cd->dlz_delrdataset == NULL)
555 return (ISC_R_NOTIMPLEMENTED);
556
557 MAYBE_LOCK(cd);
558 result = cd->dlz_delrdataset(name, type, cd->dbdata, version);
559 MAYBE_UNLOCK(cd);
560
561 return (result);
562 }
563
564
565 static dns_sdlzmethods_t dlz_dlopen_methods = {
566 dlopen_dlz_create,
567 dlopen_dlz_destroy,
568 dlopen_dlz_findzonedb,
569 dlopen_dlz_lookup,
570 dlopen_dlz_authority,
571 dlopen_dlz_allnodes,
572 dlopen_dlz_allowzonexfr,
573 dlopen_dlz_newversion,
574 dlopen_dlz_closeversion,
575 dlopen_dlz_configure,
576 dlopen_dlz_ssumatch,
577 dlopen_dlz_addrdataset,
578 dlopen_dlz_subrdataset,
579 dlopen_dlz_delrdataset
580 };
581 #endif
582
583 /*
584 * Register driver with BIND
585 */
586 isc_result_t
dlz_dlopen_init(isc_mem_t * mctx)587 dlz_dlopen_init(isc_mem_t *mctx) {
588 #ifndef ISC_DLZ_DLOPEN
589 UNUSED(mctx);
590 return (ISC_R_NOTIMPLEMENTED);
591 #else
592 isc_result_t result;
593
594 dlopen_log(2, "Registering DLZ_dlopen driver");
595
596 result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
597 DNS_SDLZFLAG_RELATIVEOWNER |
598 DNS_SDLZFLAG_RELATIVERDATA |
599 DNS_SDLZFLAG_THREADSAFE,
600 mctx, &dlz_dlopen);
601
602 if (result != ISC_R_SUCCESS) {
603 UNEXPECTED_ERROR(__FILE__, __LINE__,
604 "dns_sdlzregister() failed: %s",
605 isc_result_totext(result));
606 result = ISC_R_UNEXPECTED;
607 }
608
609 return (result);
610 #endif
611 }
612
613
614 /*
615 * Unregister the driver
616 */
617 void
dlz_dlopen_clear(void)618 dlz_dlopen_clear(void) {
619 #ifdef ISC_DLZ_DLOPEN
620 dlopen_log(2, "Unregistering DLZ_dlopen driver");
621 if (dlz_dlopen != NULL)
622 dns_sdlzunregister(&dlz_dlopen);
623 #endif
624 }
625