1/*
2 * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the
6 * above copyright notice and this permission notice appear in all
7 * copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET
10 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
11 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
12 * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
13 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14 * 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
16 * USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * The development of Dynamically Loadable Zones (DLZ) for BIND 9 was
19 * conceived and contributed by Rob Butler.
20 *
21 * Permission to use, copy, modify, and distribute this software for any
22 * purpose with or without fee is hereby granted, provided that the
23 * above copyright notice and this permission notice appear in all
24 * copies.
25 *
26 * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER
27 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
29 * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
30 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
31 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
32 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
33 * USE OR PERFORMANCE OF THIS SOFTWARE.
34 */
35
36/*
37 * Copyright (C) 1999-2001  Internet Software Consortium.
38 *
39 * Permission to use, copy, modify, and distribute this software for any
40 * purpose with or without fee is hereby granted, provided that the above
41 * copyright notice and this permission notice appear in all copies.
42 *
43 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
44 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
46 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
47 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
48 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
49 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
50 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
51 */
52
53#ifdef DLZ_LDAP
54
55#include <config.h>
56#include <stdio.h>
57#include <string.h>
58#include <stdlib.h>
59
60#include <dns/log.h>
61#include <dns/sdlz.h>
62#include <dns/result.h>
63
64#include <isc/mem.h>
65#include <isc/platform.h>
66#include <isc/print.h>
67#include <isc/result.h>
68#include <isc/string.h>
69#include <isc/util.h>
70
71#include <named/globals.h>
72
73#include <dlz/sdlz_helper.h>
74#include <dlz/dlz_ldap_driver.h>
75
76/*
77 * Need older API functions from ldap.h.
78 */
79#define LDAP_DEPRECATED 1
80
81#include <ldap.h>
82
83#define SIMPLE "simple"
84#define KRB41 "krb41"
85#define KRB42 "krb42"
86#define V2 "v2"
87#define V3 "v3"
88
89static dns_sdlzimplementation_t *dlz_ldap = NULL;
90
91#define dbc_search_limit 30
92#define ALLNODES 1
93#define ALLOWXFR 2
94#define AUTHORITY 3
95#define FINDZONE 4
96#define LOOKUP 5
97
98/*%
99 * Structure to hold everthing needed by this "instance" of the LDAP
100 * driver remember, the driver code is only loaded once, but may have
101 * many separate instances.
102 */
103
104typedef struct {
105#ifdef ISC_PLATFORM_USETHREADS
106	db_list_t    *db; /*%< handle to a list of DB */
107#else
108	dbinstance_t *db; /*%< handle to db */
109#endif
110	int method;	/*%< security authentication method */
111	char *user;	/*%< who is authenticating */
112	char *cred;	/*%< password for simple authentication method */
113	int protocol;	/*%< LDAP communication protocol version */
114	char *hosts;	/*%< LDAP server hosts */
115} ldap_instance_t;
116
117/* forward references */
118
119static isc_result_t
120dlz_ldap_findzone(void *driverarg, void *dbdata, const char *name);
121
122static void
123dlz_ldap_destroy(void *driverarg, void *dbdata);
124
125/*
126 * Private methods
127 */
128
129/*% checks that the LDAP URL parameters make sense */
130static isc_result_t
131dlz_ldap_checkURL(char *URL, int attrCnt, const char *msg) {
132	isc_result_t result = ISC_R_SUCCESS;
133	int ldap_result;
134	LDAPURLDesc *ldap_url = NULL;
135
136	if (!ldap_is_ldap_url(URL)) {
137		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
138			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
139			      "%s query is not a valid LDAP URL", msg);
140		result = ISC_R_FAILURE;
141		goto cleanup;
142	}
143
144	ldap_result = ldap_url_parse(URL, &ldap_url);
145	if (ldap_result != LDAP_SUCCESS || ldap_url == NULL) {
146		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
147			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
148			      "parsing %s query failed", msg);
149		result = ISC_R_FAILURE;
150		goto cleanup;
151	}
152
153	if (ldap_count_values(ldap_url->lud_attrs) < attrCnt) {
154		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
155			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
156			      "%s query must specify at least "
157			      "%d attributes to return",
158			      msg, attrCnt);
159		result = ISC_R_FAILURE;
160		goto cleanup;
161	}
162
163	if (ldap_url->lud_host != NULL) {
164		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
165			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
166			      "%s query must not specify a host", msg);
167		result = ISC_R_FAILURE;
168		goto cleanup;
169	}
170
171	if (ldap_url->lud_port != 389) {
172		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
173			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
174			      "%s query must not specify a port", msg);
175		result = ISC_R_FAILURE;
176		goto cleanup;
177	}
178
179	if (ldap_url->lud_dn == NULL || strlen (ldap_url->lud_dn) < 1) {
180		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
181			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
182			      "%s query must specify a search base", msg);
183		result = ISC_R_FAILURE;
184		goto cleanup;
185	}
186
187	if (ldap_url->lud_exts != NULL || ldap_url->lud_crit_exts != 0) {
188		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
189			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
190			      "%s uses extensions. "
191			      "The driver does not support LDAP extensions.",
192			      msg);
193		result = ISC_R_FAILURE;
194		goto cleanup;
195	}
196
197 cleanup:
198	if (ldap_url != NULL)
199		ldap_free_urldesc(ldap_url);
200
201	return (result);
202}
203
204/*% Connects / reconnects to LDAP server */
205static isc_result_t
206dlz_ldap_connect(ldap_instance_t *dbi, dbinstance_t *dbc) {
207	isc_result_t result;
208	int ldap_result;
209
210	/* if we have a connection, get ride of it. */
211	if (dbc->dbconn != NULL) {
212		ldap_unbind_s((LDAP *) dbc->dbconn);
213		dbc->dbconn = NULL;
214	}
215
216	/* now connect / reconnect. */
217
218	/* initialize. */
219	dbc->dbconn = ldap_init(dbi->hosts, LDAP_PORT);
220	if (dbc->dbconn == NULL)
221		return (ISC_R_NOMEMORY);
222
223	/* set protocol version. */
224	ldap_result = ldap_set_option((LDAP *) dbc->dbconn,
225				      LDAP_OPT_PROTOCOL_VERSION,
226				      &(dbi->protocol));
227	if (ldap_result != LDAP_SUCCESS) {
228		result = ISC_R_NOPERM;
229		goto cleanup;
230	}
231
232	/* "bind" to server.  i.e. send username / pass */
233	ldap_result = ldap_bind_s((LDAP *) dbc->dbconn, dbi->user,
234				  dbi->cred, dbi->method);
235	if (ldap_result != LDAP_SUCCESS) {
236		result = ISC_R_FAILURE;
237		goto cleanup;
238	}
239
240	return (ISC_R_SUCCESS);
241
242 cleanup:
243
244	/* cleanup if failure. */
245	if (dbc->dbconn != NULL) {
246		ldap_unbind_s((LDAP *) dbc->dbconn);
247		dbc->dbconn = NULL;
248	}
249
250	return (result);
251}
252
253#ifdef ISC_PLATFORM_USETHREADS
254
255
256/*%
257 * Properly cleans up a list of database instances.
258 * This function is only used when the driver is compiled for
259 * multithreaded operation.
260 */
261static void
262ldap_destroy_dblist(db_list_t *dblist) {
263	dbinstance_t *ndbi = NULL;
264	dbinstance_t *dbi = NULL;
265
266	/* get the first DBI in the list */
267	ndbi = ISC_LIST_HEAD(*dblist);
268
269	/* loop through the list */
270	while (ndbi != NULL) {
271		dbi = ndbi;
272		/* get the next DBI in the list */
273		ndbi = ISC_LIST_NEXT(dbi, link);
274		/* release DB connection */
275		if (dbi->dbconn != NULL)
276			ldap_unbind_s((LDAP *) dbi->dbconn);
277		/* release all memory that comprised a DBI */
278		destroy_sqldbinstance(dbi);
279	}
280	/* release memory for the list structure */
281	isc_mem_put(ns_g_mctx, dblist, sizeof(db_list_t));
282}
283
284/*%
285 * Loops through the list of DB instances, attempting to lock
286 * on the mutex.  If successful, the DBI is reserved for use
287 * and the thread can perform queries against the database.
288 * If the lock fails, the next one in the list is tried.
289 * looping continues until a lock is obtained, or until
290 * the list has been searched dbc_search_limit times.
291 * This function is only used when the driver is compiled for
292 * multithreaded operation.
293 */
294static dbinstance_t *
295ldap_find_avail_conn(db_list_t *dblist) {
296	dbinstance_t *dbi = NULL;
297	dbinstance_t *head;
298	int count = 0;
299
300	/* get top of list */
301	head = dbi = ISC_LIST_HEAD(*dblist);
302
303	/* loop through list */
304	while (count < dbc_search_limit) {
305		/* try to lock on the mutex */
306		if (isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS)
307			return (dbi); /* success, return the DBI for use. */
308
309		/* not successful, keep trying */
310		dbi = ISC_LIST_NEXT(dbi, link);
311
312		/* check to see if we have gone to the top of the list. */
313		if (dbi == NULL) {
314			count++;
315			dbi = head;
316		}
317	}
318	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
319		      DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
320		      "LDAP driver unable to find available connection "
321		      "after searching %d times",
322		      count);
323	return (NULL);
324}
325#endif /* ISC_PLATFORM_USETHREADS */
326
327static isc_result_t
328ldap_process_results(LDAP *dbc, LDAPMessage *msg, char ** attrs,
329		     void *ptr, isc_boolean_t allnodes)
330{
331	isc_result_t result = ISC_R_SUCCESS;
332	int i = 0;
333	int j;
334	int len;
335	char *attribute = NULL;
336	LDAPMessage *entry;
337	char *endp = NULL;
338	char *host = NULL;
339	char *type = NULL;
340	char *data = NULL;
341	char **vals = NULL;
342	int ttl;
343
344	/* make sure there are at least some attributes to process. */
345	REQUIRE(attrs != NULL || attrs[0] != NULL);
346
347	/* get the first entry to process */
348	entry = ldap_first_entry(dbc, msg);
349	if (entry == NULL) {
350		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
351			      DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
352			      "LDAP no entries to process.");
353		return (ISC_R_FAILURE);
354	}
355
356	/* loop through all entries returned */
357	while (entry != NULL) {
358		/* reset for this loop */
359		ttl = 0;
360		len = 0;
361		i = 0;
362		attribute = attrs[i];
363
364		/* determine how much space we need for data string */
365		for (j = 0; attrs[j] != NULL; j++) {
366			/* get the list of values for this attribute. */
367			vals = ldap_get_values(dbc, entry, attrs[j]);
368			/* skip empty attributes. */
369			if (vals == NULL || ldap_count_values(vals) < 1)
370				continue;
371			/*
372			 * we only use the first value.  this driver
373			 * does not support multi-valued attributes.
374			 */
375			len = len + strlen(vals[0]) + 1;
376			/* free vals for next loop */
377			ldap_value_free(vals);
378		} /* end for (j = 0; attrs[j] != NULL, j++) loop */
379
380		/* allocate memory for data string */
381		data = isc_mem_allocate(ns_g_mctx, len + 1);
382		if (data == NULL) {
383			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
384				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
385				      "LDAP driver unable to allocate memory "
386				      "while processing results");
387			result = ISC_R_FAILURE;
388			goto cleanup;
389		}
390
391		/*
392		 * Make sure data is null termed at the beginning so
393		 * we can check if any data was stored to it later.
394		 */
395		data[0] = '\0';
396
397		/* reset j to re-use below */
398		j = 0;
399
400		/* loop through the attributes in the order specified. */
401		while (attribute != NULL) {
402			/* get the list of values for this attribute. */
403			vals = ldap_get_values(dbc, entry, attribute);
404
405			/* skip empty attributes. */
406			if (vals == NULL || vals[0] == NULL) {
407				/* increment attibute pointer */
408				attribute = attrs[++i];
409				/* start loop over */
410				continue;
411			}
412
413			/*
414			 * j initially = 0.  Increment j each time we
415			 * set a field that way next loop will set
416			 * next field.
417			 */
418			switch(j) {
419			case 0:
420				j++;
421				/*
422				 * convert text to int, make sure it
423				 * worked right
424				 */
425				ttl = strtol(vals[0], &endp, 10);
426				if (*endp != '\0' || ttl < 0) {
427					isc_log_write(dns_lctx,
428						      DNS_LOGCATEGORY_DATABASE,
429						      DNS_LOGMODULE_DLZ,
430						      ISC_LOG_ERROR,
431						      "LDAP driver ttl must "
432						      "be a postive number");
433					goto cleanup;
434				}
435				break;
436			case 1:
437				j++;
438				type = isc_mem_strdup(ns_g_mctx, vals[0]);
439				break;
440			case 2:
441				j++;
442				if (allnodes == isc_boolean_true) {
443					host = isc_mem_strdup(ns_g_mctx,
444							      vals[0]);
445				} else {
446					strcpy(data, vals[0]);
447				}
448				break;
449			case 3:
450				j++;
451				if (allnodes == isc_boolean_true) {
452					strcpy(data, vals[0]);
453				} else {
454					strcat(data, " ");
455					strcat(data, vals[0]);
456				}
457				break;
458			default:
459				strcat(data, " ");
460				strcat(data, vals[0]);
461				break;
462			} /* end switch(j) */
463
464			/* free values */
465			ldap_value_free(vals);
466			vals = NULL;
467
468			/* increment attibute pointer */
469			attribute = attrs[++i];
470		}	/* end while (attribute != NULL) */
471
472		if (type == NULL) {
473			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
474				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
475				      "LDAP driver unable "
476				      "to retrieve DNS type");
477			result = ISC_R_FAILURE;
478			goto cleanup;
479		}
480
481		if (strlen(data) < 1) {
482			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
483				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
484				      "LDAP driver unable "
485				      "to retrieve DNS data");
486			result = ISC_R_FAILURE;
487			goto cleanup;
488		}
489
490		if (allnodes == isc_boolean_true) {
491			if (strcasecmp(host, "~") == 0)
492				result = dns_sdlz_putnamedrr(
493						(dns_sdlzallnodes_t *) ptr,
494						"*", type, ttl, data);
495			else
496				result = dns_sdlz_putnamedrr(
497						(dns_sdlzallnodes_t *) ptr,
498						host, type, ttl, data);
499			if (result != ISC_R_SUCCESS)
500				isc_log_write(dns_lctx,
501					DNS_LOGCATEGORY_DATABASE,
502					DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
503					"dlz-ldap: putnamedrr failed "
504					"for \"%s %s %u %s\", %s",
505					host, type, ttl, data,
506					isc_result_totext(result));
507		} else {
508			result = dns_sdlz_putrr((dns_sdlzlookup_t *) ptr,
509						type, ttl, data);
510			if (result != ISC_R_SUCCESS)
511				isc_log_write(dns_lctx,
512					DNS_LOGCATEGORY_DATABASE,
513					DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
514					"dlz-ldap: putrr failed "
515					"for \"%s %u %s\", %s",
516					type, ttl, data,
517					isc_result_totext(result));
518		}
519
520		if (result != ISC_R_SUCCESS) {
521			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
522				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
523				      "LDAP driver failed "
524				      "while sending data to BIND.");
525			goto cleanup;
526		}
527
528		/* free memory for type, data and host for next loop */
529		isc_mem_free(ns_g_mctx, type);
530		isc_mem_free(ns_g_mctx, data);
531		if (host != NULL)
532			isc_mem_free(ns_g_mctx, host);
533
534		/* get the next entry to process */
535		entry = ldap_next_entry(dbc, entry);
536	} /* end while (entry != NULL) */
537
538 cleanup:
539	/* de-allocate memory */
540	if (vals != NULL)
541		ldap_value_free(vals);
542	if (host != NULL)
543		isc_mem_free(ns_g_mctx, host);
544	if (type != NULL)
545		isc_mem_free(ns_g_mctx, type);
546	if (data != NULL)
547		isc_mem_free(ns_g_mctx, data);
548
549	return (result);
550}
551
552/*%
553 * This function is the real core of the driver.   Zone, record
554 * and client strings are passed in (or NULL is passed if the
555 * string is not available).  The type of query we want to run
556 * is indicated by the query flag, and the dbdata object is passed
557 * passed in to.  dbdata really holds either:
558 *		1) a list of database instances (in multithreaded mode) OR
559 *		2) a single database instance (in single threaded mode)
560 * The function will construct the query and obtain an available
561 * database instance (DBI).  It will then run the query and hopefully
562 * obtain a result set.
563 */
564static isc_result_t
565ldap_get_results(const char *zone, const char *record,
566		 const char *client, unsigned int query,
567		 void *dbdata, void *ptr)
568{
569	isc_result_t result;
570	dbinstance_t *dbi = NULL;
571	char *querystring = NULL;
572	LDAPURLDesc *ldap_url = NULL;
573	int ldap_result = 0;
574	LDAPMessage *ldap_msg = NULL;
575	int i;
576	int entries;
577
578	/* get db instance / connection */
579#ifdef ISC_PLATFORM_USETHREADS
580
581	/* find an available DBI from the list */
582	dbi = ldap_find_avail_conn((db_list_t *)
583				   ((ldap_instance_t *)dbdata)->db);
584
585#else /* ISC_PLATFORM_USETHREADS */
586
587	/*
588	 * only 1 DBI - no need to lock instance lock either
589	 * only 1 thread in the whole process, no possible contention.
590	 */
591	dbi =  (dbinstance_t *) ((ldap_instance_t *)dbdata)->db;
592
593#endif /* ISC_PLATFORM_USETHREADS */
594
595	/* if DBI is null, can't do anything else */
596	if (dbi == NULL)
597		return (ISC_R_FAILURE);
598
599	/* set fields */
600	if (zone != NULL) {
601		dbi->zone = isc_mem_strdup(ns_g_mctx, zone);
602		if (dbi->zone == NULL) {
603			result = ISC_R_NOMEMORY;
604			goto cleanup;
605		}
606	} else {
607		dbi->zone = NULL;
608	}
609	if (record != NULL) {
610		dbi->record = isc_mem_strdup(ns_g_mctx, record);
611		if (dbi->record == NULL) {
612			result = ISC_R_NOMEMORY;
613			goto cleanup;
614		}
615	} else {
616		dbi->record = NULL;
617	}
618	if (client != NULL) {
619		dbi->client = isc_mem_strdup(ns_g_mctx, client);
620		if (dbi->client == NULL) {
621			result = ISC_R_NOMEMORY;
622			goto cleanup;
623		}
624	} else {
625		dbi->client = NULL;
626	}
627
628	/* what type of query are we going to run? */
629	switch(query) {
630	case ALLNODES:
631		/*
632		 * if the query was not passed in from the config file
633		 * then we can't run it.  return not_implemented, so
634		 * it's like the code for that operation was never
635		 * built into the driver.... AHHH flexibility!!!
636		 */
637		if (dbi->allnodes_q == NULL) {
638			result = ISC_R_NOTIMPLEMENTED;
639			goto cleanup;
640		} else {
641			querystring = build_querystring(ns_g_mctx,
642			dbi->allnodes_q);
643		}
644		break;
645	case ALLOWXFR:
646		/* same as comments as ALLNODES */
647		if (dbi->allowxfr_q == NULL) {
648			result = ISC_R_NOTIMPLEMENTED;
649			goto cleanup;
650		} else {
651			querystring = build_querystring(ns_g_mctx,
652			dbi->allowxfr_q);
653		}
654		break;
655	case AUTHORITY:
656		/* same as comments as ALLNODES */
657		if (dbi->authority_q == NULL) {
658			result = ISC_R_NOTIMPLEMENTED;
659			goto cleanup;
660		} else {
661			querystring = build_querystring(ns_g_mctx,
662			dbi->authority_q);
663		}
664		break;
665	case FINDZONE:
666		/* this is required.  It's the whole point of DLZ! */
667		if (dbi->findzone_q == NULL) {
668			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
669				      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
670				      "No query specified for findzone.  "
671				      "Findzone requires a query");
672			result = ISC_R_FAILURE;
673			goto cleanup;
674		} else {
675			querystring = build_querystring(ns_g_mctx,
676			dbi->findzone_q);
677		}
678		break;
679	case LOOKUP:
680		/* this is required.  It's also a major point of DLZ! */
681		if (dbi->lookup_q == NULL) {
682			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
683				      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
684				      "No query specified for lookup.  "
685				      "Lookup requires a query");
686			result = ISC_R_FAILURE;
687			goto cleanup;
688		} else {
689			querystring = build_querystring(ns_g_mctx,
690							dbi->lookup_q);
691		}
692		break;
693	default:
694		/*
695		 * this should never happen.  If it does, the code is
696		 * screwed up!
697		 */
698		UNEXPECTED_ERROR(__FILE__, __LINE__,
699				 "Incorrect query flag passed to "
700				 "ldap_get_results");
701		result = ISC_R_UNEXPECTED;
702		goto cleanup;
703	}
704
705	/* if the querystring is null, Bummer, outta RAM.  UPGRADE TIME!!!   */
706	if (querystring  == NULL) {
707		result = ISC_R_NOMEMORY;
708		goto cleanup;
709	}
710
711	/*
712	 * output the full query string during debug so we can see
713	 * what lame error the query has.
714	 */
715	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
716		      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
717		      "\nQuery String: %s\n", querystring);
718
719        /* break URL down into it's component parts, if error cleanup */
720	ldap_result = ldap_url_parse(querystring, &ldap_url);
721	if (ldap_result != LDAP_SUCCESS || ldap_url == NULL) {
722		result = ISC_R_FAILURE;
723		goto cleanup;
724	}
725
726	for (i = 0; i < 3; i++) {
727
728		/*
729		 * dbi->dbconn may be null if trying to reconnect on a
730		 * previous query failed.
731		 */
732		if (dbi->dbconn == NULL) {
733			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
734				      DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
735				      "LDAP driver attempting to re-connect");
736
737			result = dlz_ldap_connect((ldap_instance_t *) dbdata,
738						  dbi);
739			if (result != ISC_R_SUCCESS) {
740				result = ISC_R_FAILURE;
741				continue;
742			}
743		}
744
745		/* perform ldap search syncronously */
746		ldap_result = ldap_search_s((LDAP *) dbi->dbconn,
747					    ldap_url->lud_dn,
748					    ldap_url->lud_scope,
749					    ldap_url->lud_filter,
750					    ldap_url->lud_attrs, 0, &ldap_msg);
751
752		/*
753		 * check return code.  No such object is ok, just
754		 * didn't find what we wanted
755		 */
756		switch(ldap_result) {
757		case LDAP_NO_SUCH_OBJECT:
758    			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
759				      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
760				      "No object found matching "
761				      "query requirements");
762			result = ISC_R_NOTFOUND;
763			goto cleanup;
764			break;
765		case LDAP_SUCCESS:	/* on success do nothing */
766			result = ISC_R_SUCCESS;
767			i = 3;
768			break;
769		case LDAP_SERVER_DOWN:
770			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
771				      DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
772				      "LDAP driver attempting to re-connect");
773			result = dlz_ldap_connect((ldap_instance_t *) dbdata,
774						  dbi);
775			if (result != ISC_R_SUCCESS)
776				result = ISC_R_FAILURE;
777			break;
778		default:
779			/*
780			 * other errors not ok.  Log error message and
781			 * get out
782			 */
783    			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
784				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
785				      "LDAP error: %s",
786				      ldap_err2string(ldap_result));
787			result = ISC_R_FAILURE;
788			goto cleanup;
789			break;
790		} /* close switch(ldap_result) */
791	} /* end for (int i = 0 i < 3; i++) */
792
793	if (result != ISC_R_SUCCESS)
794		goto cleanup;
795
796	switch(query) {
797	case ALLNODES:
798		result = ldap_process_results((LDAP *) dbi->dbconn, ldap_msg,
799					      ldap_url->lud_attrs,
800					      ptr, isc_boolean_true);
801		break;
802	case AUTHORITY:
803	case LOOKUP:
804		result = ldap_process_results((LDAP *) dbi->dbconn, ldap_msg,
805					      ldap_url->lud_attrs,
806					      ptr, isc_boolean_false);
807		break;
808	case ALLOWXFR:
809		entries = ldap_count_entries((LDAP *) dbi->dbconn, ldap_msg);
810		if (entries == 0)
811			result = ISC_R_NOPERM;
812		else if (entries > 0)
813			result = ISC_R_SUCCESS;
814		else
815			result = ISC_R_FAILURE;
816		break;
817	case FINDZONE:
818		entries = ldap_count_entries((LDAP *) dbi->dbconn, ldap_msg);
819		if (entries == 0)
820			result = ISC_R_NOTFOUND;
821		else if (entries > 0)
822			result = ISC_R_SUCCESS;
823		else
824			result = ISC_R_FAILURE;
825		break;
826	default:
827		/*
828		 * this should never happen.  If it does, the code is
829		 * screwed up!
830		 */
831		UNEXPECTED_ERROR(__FILE__, __LINE__,
832				 "Incorrect query flag passed to "
833				 "ldap_get_results");
834		result = ISC_R_UNEXPECTED;
835	}
836
837 cleanup:
838	/* it's always good to cleanup after yourself */
839
840        /* if we retrieved results, free them */
841	if (ldap_msg != NULL)
842		ldap_msgfree(ldap_msg);
843
844	if (ldap_url != NULL)
845		ldap_free_urldesc(ldap_url);
846
847	/* cleanup */
848	if (dbi->zone != NULL)
849		isc_mem_free(ns_g_mctx, dbi->zone);
850	if (dbi->record != NULL)
851		isc_mem_free(ns_g_mctx, dbi->record);
852	if (dbi->client != NULL)
853		isc_mem_free(ns_g_mctx, dbi->client);
854
855#ifdef ISC_PLATFORM_USETHREADS
856
857	/* release the lock so another thread can use this dbi */
858	isc_mutex_unlock(&dbi->instance_lock);
859
860#endif /* ISC_PLATFORM_USETHREADS */
861
862        /* release query string */
863	if (querystring  != NULL)
864		isc_mem_free(ns_g_mctx, querystring );
865
866	/* return result */
867	return (result);
868}
869
870/*
871 * DLZ methods
872 */
873static isc_result_t
874dlz_ldap_allowzonexfr(void *driverarg, void *dbdata, const char *name,
875		      const char *client)
876{
877	isc_result_t result;
878
879	UNUSED(driverarg);
880
881	/* check to see if we are authoritative for the zone first */
882	result = dlz_ldap_findzone(driverarg, dbdata, name);
883	if (result != ISC_R_SUCCESS) {
884		return (result);
885	}
886
887        /* get all the zone data */
888	result = ldap_get_results(name, NULL, client, ALLOWXFR, dbdata, NULL);
889	return (result);
890}
891
892static isc_result_t
893dlz_ldap_allnodes(const char *zone, void *driverarg, void *dbdata,
894		  dns_sdlzallnodes_t *allnodes)
895{
896	UNUSED(driverarg);
897	return (ldap_get_results(zone, NULL, NULL, ALLNODES, dbdata, allnodes));
898}
899
900static isc_result_t
901dlz_ldap_authority(const char *zone, void *driverarg, void *dbdata,
902		   dns_sdlzlookup_t *lookup)
903{
904	UNUSED(driverarg);
905	return (ldap_get_results(zone, NULL, NULL, AUTHORITY, dbdata, lookup));
906}
907
908static isc_result_t
909dlz_ldap_findzone(void *driverarg, void *dbdata, const char *name) {
910	UNUSED(driverarg);
911	return (ldap_get_results(name, NULL, NULL, FINDZONE, dbdata, NULL));
912}
913
914static isc_result_t
915dlz_ldap_lookup(const char *zone, const char *name, void *driverarg,
916		void *dbdata, dns_sdlzlookup_t *lookup)
917{
918	isc_result_t result;
919	UNUSED(driverarg);
920
921	if (strcmp(name, "*") == 0)
922		result = ldap_get_results(zone, "~", NULL, LOOKUP,
923					  dbdata, lookup);
924	else
925		result = ldap_get_results(zone, name, NULL, LOOKUP,
926					  dbdata, lookup);
927	return (result);
928}
929
930
931static isc_result_t
932dlz_ldap_create(const char *dlzname, unsigned int argc, char *argv[],
933		void *driverarg, void **dbdata)
934{
935	isc_result_t result;
936	ldap_instance_t *ldap_inst = NULL;
937	dbinstance_t *dbi = NULL;
938	int protocol;
939	int method;
940
941#ifdef ISC_PLATFORM_USETHREADS
942	/* if multi-threaded, we need a few extra variables. */
943	int dbcount;
944	char *endp;
945/* db_list_t *dblist = NULL; */
946	int i;
947
948#endif /* ISC_PLATFORM_USETHREADS */
949
950	UNUSED(dlzname);
951	UNUSED(driverarg);
952
953#ifdef ISC_PLATFORM_USETHREADS
954	/* if debugging, let user know we are multithreaded. */
955	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
956		      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
957		      "LDAP driver running multithreaded");
958#else /* ISC_PLATFORM_USETHREADS */
959	/* if debugging, let user know we are single threaded. */
960	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
961		      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
962		      "LDAP driver running single threaded");
963#endif /* ISC_PLATFORM_USETHREADS */
964
965	if (argc < 9) {
966		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
967			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
968			      "LDAP driver requires at least "
969			      "8 command line args.");
970		return (ISC_R_FAILURE);
971	}
972
973	/* no more than 13 arg's should be passed to the driver */
974	if (argc > 12) {
975		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
976			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
977			      "LDAP driver cannot accept more than "
978			      "11 command line args.");
979		return (ISC_R_FAILURE);
980	}
981
982	/* determine protocol version. */
983	if (strncasecmp(argv[2], V2, strlen(V2)) == 0) {
984		protocol = 2;
985	} else if (strncasecmp(argv[2], V3, strlen(V3)) == 0) {
986		protocol = 3;
987	} else {
988		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
989			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
990			      "LDAP driver protocol must be either %s or %s",
991			      V2, V3);
992		return (ISC_R_FAILURE);
993	}
994
995	/* determine connection method. */
996	if (strncasecmp(argv[3], SIMPLE, strlen(SIMPLE)) == 0) {
997		method = LDAP_AUTH_SIMPLE;
998	} else if (strncasecmp(argv[3], KRB41, strlen(KRB41)) == 0) {
999		method = LDAP_AUTH_KRBV41;
1000	} else if (strncasecmp(argv[3], KRB42, strlen(KRB42)) == 0) {
1001		method = LDAP_AUTH_KRBV42;
1002	} else {
1003		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1004			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1005			      "LDAP driver authentication method must be "
1006			      "one of %s, %s or %s",
1007			      SIMPLE, KRB41, KRB42);
1008		return (ISC_R_FAILURE);
1009	}
1010
1011	/* multithreaded build can have multiple DB connections */
1012#ifdef ISC_PLATFORM_USETHREADS
1013
1014	/* check how many db connections we should create */
1015	dbcount = strtol(argv[1], &endp, 10);
1016	if (*endp != '\0' || dbcount < 0) {
1017		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1018			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1019			      "LDAP driver database connection count "
1020			      "must be positive.");
1021		return (ISC_R_FAILURE);
1022	}
1023#endif
1024
1025	/* check that LDAP URL parameters make sense */
1026	switch(argc) {
1027	case 12:
1028		result = dlz_ldap_checkURL(argv[11], 0, "allow zone transfer");
1029		if (result != ISC_R_SUCCESS)
1030			return (result);
1031	case 11:
1032		result = dlz_ldap_checkURL(argv[10], 3, "all nodes");
1033		if (result != ISC_R_SUCCESS)
1034			return (result);
1035	case 10:
1036		if (strlen(argv[9]) > 0) {
1037			result = dlz_ldap_checkURL(argv[9], 3, "authority");
1038			if (result != ISC_R_SUCCESS)
1039				return (result);
1040		}
1041	case 9:
1042		result = dlz_ldap_checkURL(argv[8], 3, "lookup");
1043		if (result != ISC_R_SUCCESS)
1044			return (result);
1045		result = dlz_ldap_checkURL(argv[7], 0, "find zone");
1046		if (result != ISC_R_SUCCESS)
1047			return (result);
1048		break;
1049	default:
1050		/* not really needed, should shut up compiler. */
1051		result = ISC_R_FAILURE;
1052	}
1053
1054	/* allocate memory for LDAP instance */
1055	ldap_inst = isc_mem_get(ns_g_mctx, sizeof(ldap_instance_t));
1056	if (ldap_inst == NULL)
1057		return (ISC_R_NOMEMORY);
1058	memset(ldap_inst, 0, sizeof(ldap_instance_t));
1059
1060	/* store info needed to automatically re-connect. */
1061	ldap_inst->protocol = protocol;
1062	ldap_inst->method = method;
1063	ldap_inst->hosts = isc_mem_strdup(ns_g_mctx, argv[6]);
1064	if (ldap_inst->hosts == NULL) {
1065		result = ISC_R_NOMEMORY;
1066		goto cleanup;
1067	}
1068	ldap_inst->user = isc_mem_strdup(ns_g_mctx, argv[4]);
1069	if (ldap_inst->user == NULL) {
1070		result = ISC_R_NOMEMORY;
1071		goto cleanup;
1072	}
1073	ldap_inst->cred = isc_mem_strdup(ns_g_mctx, argv[5]);
1074	if (ldap_inst->cred == NULL) {
1075		result = ISC_R_NOMEMORY;
1076		goto cleanup;
1077	}
1078
1079#ifdef ISC_PLATFORM_USETHREADS
1080	/* allocate memory for database connection list */
1081	ldap_inst->db = isc_mem_get(ns_g_mctx, sizeof(db_list_t));
1082	if (ldap_inst->db == NULL) {
1083		result = ISC_R_NOMEMORY;
1084		goto cleanup;
1085	}
1086
1087	/* initialize DB connection list */
1088	ISC_LIST_INIT(*(ldap_inst->db));
1089
1090	/*
1091	 * create the appropriate number of database instances (DBI)
1092	 * append each new DBI to the end of the list
1093	 */
1094	for (i = 0; i < dbcount; i++) {
1095
1096#endif /* ISC_PLATFORM_USETHREADS */
1097
1098		/* how many queries were passed in from config file? */
1099		switch(argc) {
1100		case 9:
1101			result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
1102						     NULL, argv[7], argv[8],
1103						     NULL, &dbi);
1104			break;
1105		case 10:
1106			result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
1107						     argv[9], argv[7], argv[8],
1108						     NULL, &dbi);
1109			break;
1110		case 11:
1111			result = build_sqldbinstance(ns_g_mctx, argv[10], NULL,
1112						     argv[9], argv[7], argv[8],
1113						     NULL, &dbi);
1114			break;
1115		case 12:
1116			result = build_sqldbinstance(ns_g_mctx, argv[10],
1117						     argv[11], argv[9],
1118						     argv[7], argv[8],
1119						     NULL, &dbi);
1120			break;
1121		default:
1122			/* not really needed, should shut up compiler. */
1123			result = ISC_R_FAILURE;
1124		}
1125
1126		if (result == ISC_R_SUCCESS) {
1127			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1128				      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1129				      "LDAP driver created "
1130				      "database instance object.");
1131		} else { /* unsuccessful?, log err msg and cleanup. */
1132			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1133				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1134				      "LDAP driver could not create "
1135				      "database instance object.");
1136			goto cleanup;
1137		}
1138
1139#ifdef ISC_PLATFORM_USETHREADS
1140		/* when multithreaded, build a list of DBI's */
1141		ISC_LINK_INIT(dbi, link);
1142		ISC_LIST_APPEND(*(ldap_inst->db), dbi, link);
1143#else
1144		/*
1145		 * when single threaded, hold onto the one connection
1146		 * instance.
1147		 */
1148		ldap_inst->db = dbi;
1149
1150#endif
1151		/* attempt to connect */
1152		result = dlz_ldap_connect(ldap_inst, dbi);
1153
1154		/*
1155		 * if db connection cannot be created, log err msg and
1156		 * cleanup.
1157		 */
1158		switch(result) {
1159			/* success, do nothing */
1160		case ISC_R_SUCCESS:
1161			break;
1162			/*
1163			 * no memory means ldap_init could not
1164			 * allocate memory
1165			 */
1166		case ISC_R_NOMEMORY:
1167#ifdef ISC_PLATFORM_USETHREADS
1168			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1169				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1170				      "LDAP driver could not allocate memory "
1171				      "for connection number %u",
1172				      i+1);
1173#else
1174			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1175				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1176				      "LDAP driver could not allocate memory "
1177				      "for connection");
1178#endif
1179			goto cleanup;
1180			break;
1181			/*
1182			 * no perm means ldap_set_option could not set
1183			 * protocol version
1184			 */
1185		case ISC_R_NOPERM:
1186			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1187				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1188				      "LDAP driver could not "
1189				      "set protocol version.");
1190			result = ISC_R_FAILURE;
1191			goto cleanup;
1192			break;
1193			/* failure means couldn't connect to ldap server */
1194		case ISC_R_FAILURE:
1195#ifdef ISC_PLATFORM_USETHREADS
1196			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1197				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1198				      "LDAP driver could not "
1199				      "bind connection number %u to server.",
1200				      i+1);
1201#else
1202			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1203				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1204				      "LDAP driver could not "
1205				      "bind connection to server.");
1206#endif
1207			goto cleanup;
1208			break;
1209			/*
1210			 * default should never happen.  If it does,
1211			 * major errors.
1212			 */
1213		default:
1214			UNEXPECTED_ERROR(__FILE__, __LINE__,
1215					 "dlz_ldap_create() failed: %s",
1216					 isc_result_totext(result));
1217			result = ISC_R_UNEXPECTED;
1218			goto cleanup;
1219			break;
1220		} /* end switch(result) */
1221
1222
1223#ifdef ISC_PLATFORM_USETHREADS
1224
1225		/* set DBI = null for next loop through. */
1226		dbi = NULL;
1227	}	/* end for loop */
1228
1229#endif /* ISC_PLATFORM_USETHREADS */
1230
1231
1232	/* set dbdata to the ldap_instance we created. */
1233	*dbdata = ldap_inst;
1234
1235	/* hey, we got through all of that ok, return success. */
1236	return(ISC_R_SUCCESS);
1237
1238 cleanup:
1239	dlz_ldap_destroy(NULL, ldap_inst);
1240
1241	return(ISC_R_FAILURE);
1242}
1243
1244void
1245dlz_ldap_destroy(void *driverarg, void *dbdata) {
1246	UNUSED(driverarg);
1247
1248	if (dbdata != NULL) {
1249#ifdef ISC_PLATFORM_USETHREADS
1250		/* cleanup the list of DBI's */
1251		ldap_destroy_dblist((db_list_t *)
1252				    ((ldap_instance_t *)dbdata)->db);
1253
1254#else /* ISC_PLATFORM_USETHREADS */
1255		if (((ldap_instance_t *)dbdata)->db->dbconn != NULL)
1256			ldap_unbind_s((LDAP *)
1257				      ((ldap_instance_t *)dbdata)->db->dbconn);
1258
1259		/* destroy single DB instance */
1260		destroy_sqldbinstance(((ldap_instance_t *)dbdata)->db);
1261#endif /* ISC_PLATFORM_USETHREADS */
1262
1263		if (((ldap_instance_t *)dbdata)->hosts != NULL)
1264			isc_mem_free(ns_g_mctx,
1265				     ((ldap_instance_t *)dbdata)->hosts);
1266
1267		if (((ldap_instance_t *)dbdata)->user != NULL)
1268			isc_mem_free(ns_g_mctx,
1269				     ((ldap_instance_t *)dbdata)->user);
1270
1271		if (((ldap_instance_t *)dbdata)->cred != NULL)
1272			isc_mem_free(ns_g_mctx,
1273				     ((ldap_instance_t *)dbdata)->cred);
1274
1275		isc_mem_put(ns_g_mctx, dbdata, sizeof(ldap_instance_t));
1276	}
1277}
1278
1279static dns_sdlzmethods_t dlz_ldap_methods = {
1280	dlz_ldap_create,
1281	dlz_ldap_destroy,
1282	dlz_ldap_findzone,
1283	dlz_ldap_lookup,
1284	dlz_ldap_authority,
1285	dlz_ldap_allnodes,
1286	dlz_ldap_allowzonexfr,
1287	NULL,
1288	NULL,
1289	NULL,
1290	NULL,
1291	NULL,
1292	NULL,
1293	NULL,
1294};
1295
1296/*%
1297 * Wrapper around dns_sdlzregister().
1298 */
1299isc_result_t
1300dlz_ldap_init(void) {
1301	isc_result_t result;
1302
1303	/*
1304	 * Write debugging message to log
1305	 */
1306	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1307		      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1308		      "Registering DLZ ldap driver.");
1309
1310	result = dns_sdlzregister("ldap", &dlz_ldap_methods, NULL,
1311				  DNS_SDLZFLAG_RELATIVEOWNER |
1312				  DNS_SDLZFLAG_RELATIVERDATA,
1313				  ns_g_mctx, &dlz_ldap);
1314	if (result != ISC_R_SUCCESS) {
1315		UNEXPECTED_ERROR(__FILE__, __LINE__,
1316				 "dns_sdlzregister() failed: %s",
1317				 isc_result_totext(result));
1318		result = ISC_R_UNEXPECTED;
1319	}
1320
1321	return (result);
1322}
1323
1324/*%
1325 * Wrapper around dns_sdlzunregister().
1326 */
1327void
1328dlz_ldap_clear(void) {
1329	/*
1330	 * Write debugging message to log
1331	 */
1332	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1333		      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1334		      "Unregistering DLZ ldap driver.");
1335
1336	if (dlz_ldap != NULL)
1337		dns_sdlzunregister(&dlz_ldap);
1338}
1339
1340#endif
1341