--- /dev/null
+diff -urN bind-9.2.3-orig/bin/named/Makefile.in bind-9.2.3/bin/named/Makefile.in
+--- bind-9.2.3-orig/bin/named/Makefile.in 2001-05-31 18:45:00.000000000 -0600
++++ bind-9.2.3/bin/named/Makefile.in 2003-11-16 14:52:51.000000000 -0700
+@@ -26,10 +26,10 @@
+ #
+ # Add database drivers here.
+ #
+-DBDRIVER_OBJS =
+-DBDRIVER_SRCS =
++DBDRIVER_OBJS = ldapdb.@O@
++DBDRIVER_SRCS = ldapdb.c
+ DBDRIVER_INCLUDES =
+-DBDRIVER_LIBS =
++DBDRIVER_LIBS = -lldap -llber -lresolv
+
+ CINCLUDES = -I${srcdir}/include -I${srcdir}/unix/include \
+ ${LWRES_INCLUDES} ${DNS_INCLUDES} \
+diff -urN bind-9.2.3-orig/bin/named/include/ldapdb.h bind-9.2.3/bin/named/include/ldapdb.h
+--- bind-9.2.3-orig/bin/named/include/ldapdb.h 1969-12-31 17:00:00.000000000 -0700
++++ bind-9.2.3/bin/named/include/ldapdb.h 2003-11-16 14:52:51.000000000 -0700
+@@ -0,0 +1,6 @@
++#include <isc/types.h>
++
++isc_result_t ldapdb_init(void);
++
++void ldapdb_clear(void);
++
+diff -urN bind-9.2.3-orig/bin/named/ldapdb.c bind-9.2.3/bin/named/ldapdb.c
+--- bind-9.2.3-orig/bin/named/ldapdb.c 1969-12-31 17:00:00.000000000 -0700
++++ bind-9.2.3/bin/named/ldapdb.c 2003-11-16 14:52:51.000000000 -0700
+@@ -0,0 +1,552 @@
++/*
++ * ldapdb.c version 0.9
++ *
++ * Copyright (C) 2002 Stig Venaas
++ *
++ * Permission to use, copy, modify, and distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ */
++
++/*
++ * If you are using an old LDAP API uncomment the define below. Only do this
++ * if you know what you're doing or get compilation errors on ldap_memfree().
++ */
++/* #define RFC1823API */
++
++#include <config.h>
++
++#include <string.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <ctype.h>
++
++#include <isc/mem.h>
++#include <isc/print.h>
++#include <isc/result.h>
++#include <isc/util.h>
++#include <isc/thread.h>
++
++#include <dns/sdb.h>
++
++#include <named/globals.h>
++#include <named/log.h>
++
++#include <ldap.h>
++#include "ldapdb.h"
++
++/*
++ * A simple database driver for LDAP
++ */
++
++/* enough for name with 8 labels of max length */
++#define MAXNAMELEN 519
++
++static dns_sdbimplementation_t *ldapdb = NULL;
++
++struct ldapdb_data {
++ char *hostport;
++ char *hostname;
++ int portno;
++ char *base;
++ int defaultttl;
++ char *filterall;
++ int filteralllen;
++ char *filterone;
++ int filteronelen;
++ char *filtername;
++};
++
++/* used by ldapdb_getconn */
++
++struct ldapdb_entry {
++ void *index;
++ size_t size;
++ void *data;
++ struct ldapdb_entry *next;
++};
++
++static struct ldapdb_entry *ldapdb_find(struct ldapdb_entry *stack,
++ const void *index, size_t size) {
++ while (stack != NULL) {
++ if (stack->size == size && !memcmp(stack->index, index, size))
++ return stack;
++ stack = stack->next;
++ }
++ return NULL;
++}
++
++static void ldapdb_insert(struct ldapdb_entry **stack,
++ struct ldapdb_entry *item) {
++ item->next = *stack;
++ *stack = item;
++}
++
++static void ldapdb_lock(int what) {
++ static isc_mutex_t lock;
++
++ switch (what) {
++ case 0:
++ isc_mutex_init(&lock);
++ break;
++ case 1:
++ LOCK(&lock);
++ break;
++ case -1:
++ UNLOCK(&lock);
++ break;
++ }
++}
++
++/* data == NULL means cleanup */
++static LDAP **
++ldapdb_getconn(struct ldapdb_data *data)
++{
++ static struct ldapdb_entry *allthreadsdata = NULL;
++ struct ldapdb_entry *threaddata, *conndata;
++ unsigned long threadid;
++
++ if (data == NULL) {
++ /* cleanup */
++ /* lock out other threads */
++ ldapdb_lock(1);
++ while (allthreadsdata != NULL) {
++ threaddata = allthreadsdata;
++ free(threaddata->index);
++ while (threaddata->data != NULL) {
++ conndata = threaddata->data;
++ free(conndata->index);
++ if (conndata->data != NULL)
++ ldap_unbind((LDAP *)conndata->data);
++ threaddata->data = conndata->next;
++ free(conndata);
++ }
++ allthreadsdata = threaddata->next;
++ free(threaddata);
++ }
++ ldapdb_lock(-1);
++ return (NULL);
++ }
++
++ /* look for connection data for current thread */
++ threadid = isc_thread_self();
++ threaddata = ldapdb_find(allthreadsdata, &threadid, sizeof(threadid));
++ if (threaddata == NULL) {
++ /* no data for this thread, create empty connection list */
++ threaddata = malloc(sizeof(*threaddata));
++ if (threaddata == NULL)
++ return (NULL);
++ threaddata->index = malloc(sizeof(threadid));
++ if (threaddata->index == NULL) {
++ free(threaddata);
++ return (NULL);
++ }
++ *(unsigned long *)threaddata->index = threadid;
++ threaddata->size = sizeof(threadid);
++ threaddata->data = NULL;
++
++ /* need to lock out other threads here */
++ ldapdb_lock(1);
++ ldapdb_insert(&allthreadsdata, threaddata);
++ ldapdb_lock(-1);
++ }
++
++ /* threaddata points at the connection list for current thread */
++ /* look for existing connection to our server */
++ conndata = ldapdb_find((struct ldapdb_entry *)threaddata->data,
++ data->hostport, strlen(data->hostport));
++ if (conndata == NULL) {
++ /* no connection data structure for this server, create one */
++ conndata = malloc(sizeof(*conndata));
++ if (conndata == NULL)
++ return (NULL);
++ (char *)conndata->index = data->hostport;
++ conndata->size = strlen(data->hostport);
++ conndata->data = NULL;
++ ldapdb_insert((struct ldapdb_entry **)&threaddata->data,
++ conndata);
++ }
++
++ return (LDAP **)&conndata->data;
++}
++
++static void
++ldapdb_bind(struct ldapdb_data *data, LDAP **ldp)
++{
++ if (*ldp != NULL)
++ ldap_unbind(*ldp);
++ *ldp = ldap_open(data->hostname, data->portno);
++ if (*ldp == NULL)
++ return;
++ if (ldap_simple_bind_s(*ldp, NULL, NULL) != LDAP_SUCCESS) {
++ ldap_unbind(*ldp);
++ *ldp = NULL;
++ }
++}
++
++static isc_result_t
++ldapdb_search(const char *zone, const char *name, void *dbdata, void *retdata)
++{
++ struct ldapdb_data *data = dbdata;
++ isc_result_t result = ISC_R_NOTFOUND;
++ LDAP **ldp;
++ LDAPMessage *res, *e;
++ char *fltr, *a, **vals, **names;
++ char type[64];
++#ifdef RFC1823API
++ void *ptr;
++#else
++ BerElement *ptr;
++#endif
++ int i, j, errno, msgid;
++
++ ldp = ldapdb_getconn(data);
++ if (ldp == NULL)
++ return (ISC_R_FAILURE);
++ if (*ldp == NULL) {
++ ldapdb_bind(data, ldp);
++ if (*ldp == NULL) {
++ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
++ "LDAP sdb zone '%s': bind failed", zone);
++ return (ISC_R_FAILURE);
++ }
++ }
++
++ if (name == NULL) {
++ fltr = data->filterall;
++ } else {
++ if (strlen(name) > MAXNAMELEN) {
++ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
++ "LDAP sdb zone '%s': name %s too long", zone, name);
++ return (ISC_R_FAILURE);
++ }
++ sprintf(data->filtername, "%s))", name);
++ fltr = data->filterone;
++ }
++
++ msgid = ldap_search(*ldp, data->base, LDAP_SCOPE_SUBTREE, fltr, NULL, 0);
++ if (msgid == -1) {
++ ldapdb_bind(data, ldp);
++ if (*ldp != NULL)
++ msgid = ldap_search(*ldp, data->base, LDAP_SCOPE_SUBTREE, fltr, NULL, 0);
++ }
++
++ if (*ldp == NULL || msgid == -1) {
++ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
++ "LDAP sdb zone '%s': search failed, filter %s", zone, fltr);
++ return (ISC_R_FAILURE);
++ }
++
++ /* Get the records one by one as they arrive and return them to bind */
++ while ((errno = ldap_result(*ldp, msgid, 0, NULL, &res)) != LDAP_RES_SEARCH_RESULT ) {
++ LDAP *ld = *ldp;
++ int ttl = data->defaultttl;
++
++ /* not supporting continuation references at present */
++ if (errno != LDAP_RES_SEARCH_ENTRY) {
++ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
++ "LDAP sdb zone '%s': ldap_result returned %d", zone, errno);
++ ldap_msgfree(res);
++ return (ISC_R_FAILURE);
++ }
++
++ /* only one entry per result message */
++ e = ldap_first_entry(ld, res);
++ if (e == NULL) {
++ ldap_msgfree(res);
++ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
++ "LDAP sdb zone '%s': ldap_first_entry failed", zone);
++ return (ISC_R_FAILURE);
++ }
++
++ if (name == NULL) {
++ names = ldap_get_values(ld, e, "relativeDomainName");
++ if (names == NULL)
++ continue;
++ }
++
++ vals = ldap_get_values(ld, e, "dNSTTL");
++ if (vals != NULL) {
++ ttl = atoi(vals[0]);
++ ldap_value_free(vals);
++ }
++
++ for (a = ldap_first_attribute(ld, e, &ptr); a != NULL; a = ldap_next_attribute(ld, e, ptr)) {
++ char *s;
++
++ for (s = a; *s; s++)
++ *s = toupper(*s);
++ s = strstr(a, "RECORD");
++ if ((s == NULL) || (s == a) || (s - a >= (signed int)sizeof(type))) {
++#ifndef RFC1823API
++ ldap_memfree(a);
++#endif
++ continue;
++ }
++
++ strncpy(type, a, s - a);
++ type[s - a] = '\0';
++ vals = ldap_get_values(ld, e, a);
++ if (vals != NULL) {
++ for (i = 0; vals[i] != NULL; i++) {
++ if (name != NULL) {
++ result = dns_sdb_putrr(retdata, type, ttl, vals[i]);
++ } else {
++ for (j = 0; names[j] != NULL; j++) {
++ result = dns_sdb_putnamedrr(retdata, names[j], type, ttl, vals[i]);
++ if (result != ISC_R_SUCCESS)
++ break;
++ }
++ }
++; if (result != ISC_R_SUCCESS) {
++ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
++ "LDAP sdb zone '%s': dns_sdb_put... failed for %s", zone, vals[i]);
++ ldap_value_free(vals);
++#ifndef RFC1823API
++ ldap_memfree(a);
++ if (ptr != NULL)
++ ber_free(ptr, 0);
++#endif
++ if (name == NULL)
++ ldap_value_free(names);
++ ldap_msgfree(res);
++ return (ISC_R_FAILURE);
++ }
++ }
++ ldap_value_free(vals);
++ }
++#ifndef RFC1823API
++ ldap_memfree(a);
++#endif
++ }
++#ifndef RFC1823API
++ if (ptr != NULL)
++ ber_free(ptr, 0);
++#endif
++ if (name == NULL)
++ ldap_value_free(names);
++
++ /* cleanup this result */
++ ldap_msgfree(res);
++ }
++
++ return (result);
++}
++
++
++/* callback routines */
++static isc_result_t
++ldapdb_lookup(const char *zone, const char *name, void *dbdata,
++ dns_sdblookup_t *lookup)
++{
++ return ldapdb_search(zone, name, dbdata, lookup);
++}
++
++static isc_result_t
++ldapdb_allnodes(const char *zone, void *dbdata,
++ dns_sdballnodes_t *allnodes)
++{
++ return ldapdb_search(zone, NULL, dbdata, allnodes);
++}
++
++static char *
++unhex(char *in)
++{
++ static const char hexdigits[] = "0123456789abcdef";
++ char *p, *s = in;
++ int d1, d2;
++
++ while ((s = strchr(s, '%'))) {
++ if (!(s[1] && s[2]))
++ return NULL;
++ if ((p = strchr(hexdigits, tolower(s[1]))) == NULL)
++ return NULL;
++ d1 = p - hexdigits;
++ if ((p = strchr(hexdigits, tolower(s[2]))) == NULL)
++ return NULL;
++ d2 = p - hexdigits;
++ *s++ = d1 << 4 | d2;
++ memmove(s, s + 2, strlen(s) - 1);
++ }
++ return in;
++}
++
++
++
++static void
++free_data(struct ldapdb_data *data)
++{
++ if (data->hostport != NULL)
++ isc_mem_free(ns_g_mctx, data->hostport);
++ if (data->hostname != NULL)
++ isc_mem_free(ns_g_mctx, data->hostname);
++ if (data->filterall != NULL)
++ isc_mem_put(ns_g_mctx, data->filterall, data->filteralllen);
++ if (data->filterone != NULL)
++ isc_mem_put(ns_g_mctx, data->filterone, data->filteronelen);
++ isc_mem_put(ns_g_mctx, data, sizeof(struct ldapdb_data));
++}
++
++
++static isc_result_t
++ldapdb_create(const char *zone, int argc, char **argv,
++ void *driverdata, void **dbdata)
++{
++ struct ldapdb_data *data;
++ char *s, *filter = NULL;
++ int defaultttl;
++
++ UNUSED(driverdata);
++
++ /* we assume that only one thread will call create at a time */
++ /* want to do this only once for all instances */
++
++ if ((argc < 2)
++ || (argv[0] != strstr( argv[0], "ldap://"))
++ || ((defaultttl = atoi(argv[1])) < 1))
++ return (ISC_R_FAILURE);
++ data = isc_mem_get(ns_g_mctx, sizeof(struct ldapdb_data));
++ if (data == NULL)
++ return (ISC_R_NOMEMORY);
++
++ memset(data, 0, sizeof(struct ldapdb_data));
++ data->hostport = isc_mem_strdup(ns_g_mctx, argv[0] + strlen("ldap://"));
++ if (data->hostport == NULL) {
++ free_data(data);
++ return (ISC_R_NOMEMORY);
++ }
++
++ data->defaultttl = defaultttl;
++
++ s = strchr(data->hostport, '/');
++ if (s != NULL) {
++ *s++ = '\0';
++ data->base = s;
++ /* attrs, scope, filter etc? */
++ s = strchr(s, '?');
++ if (s != NULL) {
++ *s++ = '\0';
++ /* ignore attributes */
++ s = strchr(s, '?');
++ if (s != NULL) {
++ *s++ = '\0';
++ /* ignore scope */
++ s = strchr(s, '?');
++ if (s != NULL) {
++ *s++ = '\0';
++ /* filter */
++ filter = s;
++ s = strchr(s, '?');
++ if (s != NULL) {
++ *s++ = '\0';
++ }
++ if (*filter == '\0') {
++ filter = NULL;
++ }
++ }
++ }
++ }
++ if (*data->base == '\0') {
++ data->base = NULL;
++ }
++
++ if ((data->base != NULL && unhex(data->base) == NULL) || (filter != NULL && unhex(filter) == NULL)) {
++ free_data(data);
++ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
++ "LDAP sdb zone '%s': bad hex values", zone);
++ return (ISC_R_FAILURE);
++ }
++ }
++
++ /* compute filterall and filterone once and for all */
++ if (filter == NULL) {
++ data->filteralllen = strlen(zone) + strlen("(zoneName=)") + 1;
++ data->filteronelen = strlen(zone) + strlen("(&(zoneName=)(relativeDomainName=))") + MAXNAMELEN + 1;
++ } else {
++ data->filteralllen = strlen(filter) + strlen(zone) + strlen("(&(zoneName=))") + 1;
++ data->filteronelen = strlen(filter) + strlen(zone) + strlen("(&(zoneName=)(relativeDomainName=))") + MAXNAMELEN + 1;
++ }
++
++ data->filterall = isc_mem_get(ns_g_mctx, data->filteralllen);
++ if (data->filterall == NULL) {
++ free_data(data);
++ return (ISC_R_NOMEMORY);
++ }
++ data->filterone = isc_mem_get(ns_g_mctx, data->filteronelen);
++ if (data->filterone == NULL) {
++ free_data(data);
++ return (ISC_R_NOMEMORY);
++ }
++
++ if (filter == NULL) {
++ sprintf(data->filterall, "(zoneName=%s)", zone);
++ sprintf(data->filterone, "(&(zoneName=%s)(relativeDomainName=", zone);
++ } else {
++ sprintf(data->filterall, "(&%s(zoneName=%s))", filter, zone);
++ sprintf(data->filterone, "(&%s(zoneName=%s)(relativeDomainName=", filter, zone);
++ }
++ data->filtername = data->filterone + strlen(data->filterone);
++
++ /* support URLs with literal IPv6 addresses */
++ data->hostname = isc_mem_strdup(ns_g_mctx, data->hostport + (*data->hostport == '[' ? 1 : 0));
++ if (data->hostname == NULL) {
++ free_data(data);
++ return (ISC_R_NOMEMORY);
++ }
++
++ if (*data->hostport == '[' &&
++ (s = strchr(data->hostname, ']')) != NULL )
++ *s++ = '\0';
++ else
++ s = data->hostname;
++ s = strchr(s, ':');
++ if (s != NULL) {
++ *s++ = '\0';
++ data->portno = atoi(s);
++ } else
++ data->portno = LDAP_PORT;
++
++ *dbdata = data;
++ return (ISC_R_SUCCESS);
++}
++
++static void
++ldapdb_destroy(const char *zone, void *driverdata, void **dbdata) {
++ struct ldapdb_data *data = *dbdata;
++
++ UNUSED(zone);
++ UNUSED(driverdata);
++
++ free_data(data);
++}
++
++static dns_sdbmethods_t ldapdb_methods = {
++ ldapdb_lookup,
++ NULL, /* authority */
++ ldapdb_allnodes,
++ ldapdb_create,
++ ldapdb_destroy
++};
++
++/* Wrapper around dns_sdb_register() */
++isc_result_t
++ldapdb_init(void) {
++ unsigned int flags =
++ DNS_SDBFLAG_RELATIVEOWNER |
++ DNS_SDBFLAG_RELATIVERDATA |
++ DNS_SDBFLAG_THREADSAFE;
++
++ ldapdb_lock(0);
++ return (dns_sdb_register("ldap", &ldapdb_methods, NULL, flags,
++ ns_g_mctx, &ldapdb));
++}
++
++/* Wrapper around dns_sdb_unregister() */
++void
++ldapdb_clear(void) {
++ if (ldapdb != NULL) {
++ /* clean up thread data */
++ ldapdb_getconn(NULL);
++ dns_sdb_unregister(&ldapdb);
++ }
++}
+diff -urN bind-9.2.3-orig/bin/named/main.c bind-9.2.3/bin/named/main.c
+--- bind-9.2.3-orig/bin/named/main.c 2003-10-09 01:32:33.000000000 -0600
++++ bind-9.2.3/bin/named/main.c 2003-11-16 14:52:51.000000000 -0700
+@@ -64,6 +64,7 @@
+ * Include header files for database drivers here.
+ */
+ /* #include "xxdb.h" */
++#include <ldapdb.h>
+
+ static isc_boolean_t want_stats = ISC_FALSE;
+ static char program_name[ISC_DIR_NAMEMAX] = "named";
+@@ -544,6 +545,7 @@
+ * Add calls to register sdb drivers here.
+ */
+ /* xxdb_init(); */
++ ldapdb_init();
+
+ ns_server_create(ns_g_mctx, &ns_g_server);
+ }
+@@ -558,6 +560,7 @@
+ * Add calls to unregister sdb drivers here.
+ */
+ /* xxdb_clear(); */
++ ldapdb_clear();
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
+ ISC_LOG_NOTICE, "exiting");
+diff -urN bind-9.2.3-orig/doc/INSTALL.sdb-ldap bind-9.2.3/doc/INSTALL.sdb-ldap
+--- bind-9.2.3-orig/doc/INSTALL.sdb-ldap 1969-12-31 17:00:00.000000000 -0700
++++ bind-9.2.3/doc/INSTALL.sdb-ldap 2003-11-16 14:53:32.000000000 -0700
+@@ -0,0 +1,59 @@
++This is the INSTALL file for 0.9. See
++http://www.venaas.no/ldap/bind-sdb/ for updates or other information.
++
++BUILDING
++
++You need the source for BIND 9.1.0 or newer (for zone transfers you
++will need at least 9.1.1rc3 due to a bug). Basically you need to follow
++the instructions in doc/misc/sdb, if my instructions doesn't make sense,
++please have a look at that as well.
++
++Copy ldapdb.c to bin/named and ldapdb.h to bin/named/include in the
++source tree.
++
++Next alter bin/named/Makefile.in. Add ldapdb.@O@ to DBDRIVER_OBJS and
++ldapdb.c to DBDRIVER_SRCS. You also need to add something like
++-I/usr/local/include to DBDRIVER_INCLUDES and
++-L/usr/local/lib -lldap -llber -lresolv to DBDRIVER_LIBS
++depending on what LDAP library you have and where you installed it.
++
++Finally you need to edit bin/named/main.c. Below where it says
++"#include "xxdb.h"", add the line "#include <ldapdb.h>". Below where
++it says "xxdb_init();" add the line "ldapdb_init();", and finally
++below where it says "xxdb_clear();", add "ldapdb_clear();".
++
++Now you should hopefully be able to build it. If you get an error
++message about ldap_memfree() not being defined, you're probably
++using an LDAP library with the interface defined in RFC 1823. To
++build, uncomment the #define RFC1823API line near the top of ldapdb.c.
++
++
++CONFIGURING
++
++Before you do any configuring of LDAP stuff, please try to configure
++and start bind as usual to see if things work.
++
++To do anything useful, you need to store a zone in some LDAP server.
++From this release on, you must use a schema called dNSZone. Note that
++it relies on some attribute definitions in the Cosine schema, so that
++must be included as well. The Cosine schema probably comes with your
++LDAP server. You can find dNSZone and further details on how to store
++the data in your LDAP server at
++http://www.venaas.no/ldap/bind-sdb/
++
++For an example, have a look at my venaas.com zone. Try a subtree search
++for objectClass=* at
++ldap ldap://129.241.20.67/dc=venaas,dc=com,o=DNS,dc=venaas,dc=no
++
++To use it with BIND, I've added the following to named.conf:
++zone "venaas.com" {
++ type master;
++ database "ldap ldap://129.241.20.67/dc=venaas,dc=com,o=DNS,dc=venaas,dc=no 172800";
++};
++
++When doing lookups BIND will do a sub-tree search below the base in the
++URL. The number 172800 is the TTL which will be used for all entries that
++haven't got the dNSTTL attribute. It is also possible to add an filter to
++the URL, say ldap://host/base???(o=internal)
++
++Stig Venaas <venaas@uninett.no> 2002-04-17
+diff -urN bind-9.2.3-orig/doc/README.sdb-ldap bind-9.2.3/doc/README.sdb-ldap
+--- bind-9.2.3-orig/doc/README.sdb-ldap 1969-12-31 17:00:00.000000000 -0700
++++ bind-9.2.3/doc/README.sdb-ldap 2003-11-16 14:53:18.000000000 -0700
+@@ -0,0 +1,40 @@
++This is an attempt at an LDAP back-end for BIND 9 using the new simplified
++database interface "sdb". This is the nineth release (0.9) and seems to
++be pretty stable. Note that since version 0.4 a new schema is used.
++It is not backwards compatible with versions before 0.4.
++
++In 0.9 the code has been cleaned up a bit and should be slightly faster
++than previous versions. It also fixes an error with zone transfers (AXFR)
++and entries with multiple relativeDomainName values. The problem was
++that it would only use the first value in the result. There's no need
++to upgrade unless you use such entries.
++
++0.8 uses asynchronous LDAP search which should give better performance.
++Thanks to Ashley Burston for providing patch. Another new feature is
++allowing filters in URLs. The syntax is as in RFC 2255. Few people will
++need this, but if you have say an internal and external version of the
++same zone, you could stick say o=internal and o=external into different
++entries, and specify for instance ldap://host/base???(o=internal)
++Some error logging has also been added.
++
++0.7 allows space and other characters to be used in URLs by use of %-quoting.
++For instance space can be written as %20. It also fixes a problem with some
++servers and/or APIs that do not preserve attribute casing.
++
++0.6 fixes some memory leaks present in older versions unless compiled with
++the RFC 1823 API.
++
++The big changes in 0.5 are thread support and improved connection handling.
++Multiple threads can now access the back-end simultaneously, and rather than
++having one connection per zone, there is now one connection per thread per
++LDAP server. This should help people with multiple CPUs and people with a
++huge number of zones. One final change is support for literal IPv6 addresses
++in LDAP URLs. At least OpenLDAP 2 has IPv6 support, so if you use OpenLDAP 2
++libraries and server, you got all you need.
++
++If you have bug reports, fixes, comments, questions or whatever, please
++contact me. See also http://www.venaas.no/ldap/bind-sdb/ for information.
++
++See INSTALL for how to build, install and use.
++
++Stig Venaas <venaas@uninett.no> 2001-12-29