]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
update to newer version
authorBrian Wellington <source@isc.org>
Wed, 30 May 2001 22:02:09 +0000 (22:02 +0000)
committerBrian Wellington <source@isc.org>
Wed, 30 May 2001 22:02:09 +0000 (22:02 +0000)
contrib/sdb/INSTALL.ldap
contrib/sdb/README.ldap
contrib/sdb/ldap/INSTALL.ldap
contrib/sdb/ldap/README.ldap
contrib/sdb/ldap/ldapdb.c
contrib/sdb/ldapdb.c

index 80561a8b26c89231bfddc5688f439f09f2251e62..e54b5001579e28f51b14edf4f0241d588b25a99c 100644 (file)
@@ -1,4 +1,4 @@
-This is the INSTALL file for 0.3. See
+This is the INSTALL file for 0.4. See
 http://www.venaas.no/ldap/bind-sdb/ for updates or other information.
 
 BUILDING
@@ -31,29 +31,25 @@ 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.
-If you like, you could try to use my LDAP server as a test. To test,
-add the following to your named.conf:
+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
 
-zone "ldap" {
+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 86400";
+        database "ldap ldap://129.241.20.67/dc=venaas,dc=com,o=DNS,dc=venaas,dc=no 172800";
 };
 
-and then try to do for instance host www.ldap. localhost
-
-The LDAP URL consists of a hostport part and the base (the node above
-where the zone is stored). BIND will do a one-level search with this
-base. Finally, the number 86400 is the TTL which will be used for all
-entries that haven't got the dNSTTL attribute.
-
-To store data in your own LDAP server you can use the Cosine dNSDomain
-class, or even better dNSDomain2. Your LDAP server probably comes
-with dNSDomain, you can find dNSDomain2 and further details on how
-to store the data in your LDAP server at
-
-http://www.venaas.no/ldap/bind-sdb/
-
-You can also see how I did it by searching in my LDAP server, the
-address and base is as in the LDAP URL above.
+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.
 
-Stig Venaas <venaas@uninett.no> 2001-03-03
+Stig Venaas <venaas@uninett.no> 2001-04-12
index e952531997b6e182f257150647383c5297b45771..102d0ac969d4ee6c5d456fe353e8cf765d5ebc03 100644 (file)
@@ -1,9 +1,18 @@
-This is an attempt at an LDAP back-end for BIND 9 using the new
-simplified database interface "sdb". This is the third release
-(0.3) and is not ready for production use yet. Bug reports,
-fixes, comments, questions or whatever, please contact me. See
-also http://www.venaas.no/ldap/bind-sdb/ for information.
+This is an attempt at an LDAP back-end for BIND 9 using the new simplified
+database interface "sdb". This is the fifth release (0.5) and is not ready
+for production use yet. Note that this version (and  0.4) uses a new schema
+and is not backwards compatible with versions before 0.4. 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-03-03
+Stig Venaas <venaas@uninett.no> 2001-05-06
index 80561a8b26c89231bfddc5688f439f09f2251e62..e54b5001579e28f51b14edf4f0241d588b25a99c 100644 (file)
@@ -1,4 +1,4 @@
-This is the INSTALL file for 0.3. See
+This is the INSTALL file for 0.4. See
 http://www.venaas.no/ldap/bind-sdb/ for updates or other information.
 
 BUILDING
@@ -31,29 +31,25 @@ 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.
-If you like, you could try to use my LDAP server as a test. To test,
-add the following to your named.conf:
+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
 
-zone "ldap" {
+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 86400";
+        database "ldap ldap://129.241.20.67/dc=venaas,dc=com,o=DNS,dc=venaas,dc=no 172800";
 };
 
-and then try to do for instance host www.ldap. localhost
-
-The LDAP URL consists of a hostport part and the base (the node above
-where the zone is stored). BIND will do a one-level search with this
-base. Finally, the number 86400 is the TTL which will be used for all
-entries that haven't got the dNSTTL attribute.
-
-To store data in your own LDAP server you can use the Cosine dNSDomain
-class, or even better dNSDomain2. Your LDAP server probably comes
-with dNSDomain, you can find dNSDomain2 and further details on how
-to store the data in your LDAP server at
-
-http://www.venaas.no/ldap/bind-sdb/
-
-You can also see how I did it by searching in my LDAP server, the
-address and base is as in the LDAP URL above.
+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.
 
-Stig Venaas <venaas@uninett.no> 2001-03-03
+Stig Venaas <venaas@uninett.no> 2001-04-12
index e952531997b6e182f257150647383c5297b45771..102d0ac969d4ee6c5d456fe353e8cf765d5ebc03 100644 (file)
@@ -1,9 +1,18 @@
-This is an attempt at an LDAP back-end for BIND 9 using the new
-simplified database interface "sdb". This is the third release
-(0.3) and is not ready for production use yet. Bug reports,
-fixes, comments, questions or whatever, please contact me. See
-also http://www.venaas.no/ldap/bind-sdb/ for information.
+This is an attempt at an LDAP back-end for BIND 9 using the new simplified
+database interface "sdb". This is the fifth release (0.5) and is not ready
+for production use yet. Note that this version (and  0.4) uses a new schema
+and is not backwards compatible with versions before 0.4. 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-03-03
+Stig Venaas <venaas@uninett.no> 2001-05-06
index 5413ded941a6dbcb6e0778e86d60bfd43ecee05f..0b6e88b48de1a2e6215d42f2f7b2f3583f7f85f8 100644 (file)
@@ -17,6 +17,7 @@
 #include <isc/print.h>
 #include <isc/result.h>
 #include <isc/util.h>
+#include <isc/thread.h>
 
 #include <dns/sdb.h>
 
 #include "ldapdb.h"
 
 /*
- * A simple database driver for LDAP. Not production quality yet.
+ * A simple database driver for LDAP. Not production quality yet
  */ 
 
 static dns_sdbimplementation_t *ldapdb = NULL;
 
 struct ldapdb_data {
+       char *hostport;
        char *hostname;
        int portno;
        char *base;
        int defaultttl;
-       LDAP *ld;
 };
 
+/* 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;
+}
+
+/* callback routines */
 static isc_result_t
 ldapdb_create(const char *zone, int argc, char **argv,
              void *driverdata, void **dbdata)
@@ -46,10 +161,13 @@ ldapdb_create(const char *zone, int argc, char **argv,
        struct ldapdb_data *data;
        char *s;
        int defaultttl;
-               
+
        UNUSED(zone);
        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))
@@ -57,24 +175,39 @@ ldapdb_create(const char *zone, int argc, char **argv,
         data = isc_mem_get(ns_g_mctx, sizeof(struct ldapdb_data));
         if (data == NULL)
                 return (ISC_R_NOMEMORY);
-       data->hostname = isc_mem_strdup(ns_g_mctx, argv[0] + strlen("ldap://"));
-       if (data->hostname == NULL) {
+       data->hostport = isc_mem_strdup(ns_g_mctx, argv[0] + strlen("ldap://"));
+       if (data->hostport == NULL) {
                isc_mem_put(ns_g_mctx, data, sizeof(struct ldapdb_data));
                return (ISC_R_NOMEMORY);
        }
        data->defaultttl = defaultttl;
-       s = strchr(data->hostname, '/');
+       s = strchr(data->hostport, '/');
        if (s != NULL) {
                *s++ = '\0';
                data->base = *s != '\0' ? s : NULL;
        }
-       s = strchr(data->hostname, ':');
+
+       /* support URLs with literal IPv6 addresses */
+       data->hostname = isc_mem_strdup(ns_g_mctx, data->hostport +
+                                       (*data->hostport == '[' ? 1 : 0));
+       if (data->hostname == NULL) {
+               isc_mem_free(ns_g_mctx, data->hostport);
+               isc_mem_put(ns_g_mctx, data, sizeof(struct ldapdb_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;
-       data->ld = NULL;
+
        *dbdata = data;
        return (ISC_R_SUCCESS);
 }
@@ -86,26 +219,24 @@ ldapdb_destroy(const char *zone, void *driverdata, void **dbdata) {
         UNUSED(zone);
         UNUSED(driverdata);
 
-       if (data == NULL)
-               return;
-       if (data->ld != NULL)
-               ldap_unbind(data->ld);
+       if (data->hostport != NULL)
+               isc_mem_free(ns_g_mctx, data->hostport);
        if (data->hostname != NULL)
                isc_mem_free(ns_g_mctx, data->hostname);
         isc_mem_put(ns_g_mctx, data, sizeof(struct ldapdb_data));
 }
 
 static void
-ldapdb_bind(struct ldapdb_data *data)
+ldapdb_bind(struct ldapdb_data *data, LDAP **ldp)
 {
-       if (data->ld != NULL)
-               ldap_unbind(data->ld);
-       data->ld = ldap_open(data->hostname, data->portno);
-       if (data->ld == NULL)
+       if (*ldp != NULL)
+               ldap_unbind(*ldp);
+       *ldp = ldap_open(data->hostname, data->portno);
+       if (*ldp == NULL)
                return;
-       if (ldap_simple_bind_s(data->ld, NULL, NULL) != LDAP_SUCCESS) {
-               ldap_unbind(data->ld);
-               data->ld = NULL;
+       if (ldap_simple_bind_s(*ldp, NULL, NULL) != LDAP_SUCCESS) {
+               ldap_unbind(*ldp);
+               *ldp = NULL;
        }
 }
 
@@ -115,37 +246,48 @@ ldapdb_lookup(const char *zone, const char *name, void *dbdata,
 {
         isc_result_t result = ISC_R_NOTFOUND;
        struct ldapdb_data *data = dbdata;
-       LDAPMessage     *res, *e;
+       LDAP **ldp;
+       LDAPMessage *res, *e;
        char *fltr, *a, **vals;
        char type[64];
        BerElement *ptr;
        int i;
-       
-       UNUSED(zone);
 
-       if (data->ld == NULL) {
-               ldapdb_bind(data);
-               if (data->ld == NULL)
+       ldp = ldapdb_getconn(data);
+       if (ldp == NULL)
+               return (ISC_R_FAILURE);
+       if (*ldp == NULL) {
+               ldapdb_bind(data, ldp);
+               if (*ldp == NULL)
                        return (ISC_R_FAILURE);
        }
-       fltr = isc_mem_get(ns_g_mctx, strlen(name) + strlen("(dc=)") + 1);
+       fltr = isc_mem_get(ns_g_mctx, strlen(zone) + strlen(name) +
+                          strlen("(&(zoneName=)(relativeDomainName=))") + 1);
         if (fltr == NULL)
                 return (ISC_R_NOMEMORY);
-       strcpy(fltr, "(dc=");
+
+       strcpy(fltr, "(&(zoneName=");
+       strcat(fltr, zone);
+       strcat(fltr, ")(relativeDomainName=");
        strcat(fltr, name);
-       strcat(fltr, ")");
-       if (ldap_search_s(data->ld, data->base, LDAP_SCOPE_ONELEVEL, fltr, NULL, 0, &res) != LDAP_SUCCESS) {
-               ldapdb_bind(data);
-               if (data->ld != NULL)
-                       ldap_search_s(data->ld, data->base, LDAP_SCOPE_ONELEVEL, fltr, NULL, 0, &res);
+       strcat(fltr, "))");
+
+       if (ldap_search_s(*ldp, data->base, LDAP_SCOPE_SUBTREE, fltr, NULL, 0,
+                         &res) != LDAP_SUCCESS) {
+               ldapdb_bind(data, ldp);
+               if (*ldp != NULL)
+                       ldap_search_s(*ldp, data->base, LDAP_SCOPE_SUBTREE,
+                                     fltr, NULL, 0, &res);
        }
+
        isc_mem_put(ns_g_mctx, fltr, strlen(fltr) + 1);
-       if (data->ld == NULL)
+
+       if (*ldp == NULL)
                goto exit;
        
-       for (e = ldap_first_entry(data->ld, res); e != NULL;
-            e = ldap_next_entry(data->ld, e)) {
-               LDAP *ld = data->ld;
+       for (e = ldap_first_entry(*ldp, res); e != NULL;
+            e = ldap_next_entry(*ldp, e)) {
+               LDAP *ld = *ldp;
                int ttl = data->defaultttl;
                
                for (a = ldap_first_attribute(ld, e, &ptr); a != NULL;
@@ -175,7 +317,8 @@ ldapdb_lookup(const char *zone, const char *name, void *dbdata,
                        type[s - a] = '\0';
                        vals = ldap_get_values(ld, e, a);
                        for (i=0; vals[i] != NULL; i++) {
-                               result = dns_sdb_putrr(lookup, type, ttl, vals[i]);
+                               result = dns_sdb_putrr(lookup, type, ttl,
+                                                      vals[i]);
                                if (result != ISC_R_SUCCESS) {
                                        ldap_value_free(vals);
                                        ldap_memfree(a);
@@ -193,32 +336,47 @@ ldapdb_lookup(const char *zone, const char *name, void *dbdata,
 }
 
 static isc_result_t
-ldapdb_allnodes(const char *zone, void *dbdata, dns_sdballnodes_t *allnodes) {
+ldapdb_allnodes(const char *zone, void *dbdata,
+               dns_sdballnodes_t *allnodes) {
         isc_result_t result = ISC_R_NOTFOUND;
        struct ldapdb_data *data = dbdata;
+       LDAP **ldp;
        LDAPMessage     *res, *e;
        char type[64];
-       char *a, **vals;
+       char *fltr, *a, **vals;
        BerElement *ptr;
        int i;
 
-        UNUSED(zone);
-
-       if (data->ld == NULL) {
-               ldapdb_bind(data);
-               if (data->ld == NULL)
+       ldp = ldapdb_getconn(data);
+       if (ldp == NULL)
+               return (ISC_R_FAILURE);
+       if (*ldp == NULL) {
+               ldapdb_bind(data, ldp);
+               if (*ldp == NULL)
                        return (ISC_R_FAILURE);
        }
 
-       if (ldap_search_s(data->ld, data->base, LDAP_SCOPE_ONELEVEL, "(objectclass=*)", NULL, 0, &res) != LDAP_SUCCESS) {
-               ldapdb_bind(data);
-               if (data->ld != NULL)
-                       ldap_search_s(data->ld, data->base, LDAP_SCOPE_ONELEVEL, "(objectclass=*)", NULL, 0, &res);
+       fltr = isc_mem_get(ns_g_mctx, strlen(zone) + strlen("(zoneName=)") + 1);
+        if (fltr == NULL)
+                return (ISC_R_NOMEMORY);
+
+       strcpy(fltr, "(zoneName=");
+       strcat(fltr, zone);
+       strcat(fltr, ")");
+
+       if (ldap_search_s(*ldp, data->base, LDAP_SCOPE_SUBTREE, fltr, NULL, 0,
+                         &res) != LDAP_SUCCESS) {
+               ldapdb_bind(data, ldp);
+               if (*ldp != NULL)
+                       ldap_search_s(*ldp, data->base, LDAP_SCOPE_SUBTREE,
+                                     fltr, NULL, 0, &res);
        }
 
-       for (e = ldap_first_entry(data->ld, res); e != NULL;
-            e = ldap_next_entry(data->ld, e)) {
-               LDAP *ld = data->ld;
+       isc_mem_put(ns_g_mctx, fltr, strlen(fltr) + 1);
+
+       for (e = ldap_first_entry(*ldp, res); e != NULL;
+            e = ldap_next_entry(*ldp, e)) {
+               LDAP *ld = *ldp;
                char *name = NULL;
                int ttl = data->defaultttl;
                
@@ -228,7 +386,7 @@ ldapdb_allnodes(const char *zone, void *dbdata, dns_sdballnodes_t *allnodes) {
                                vals = ldap_get_values(ld, e, a);
                                ttl = atoi(vals[0]);
                                ldap_value_free(vals);
-                       } else if (!strcmp(a, "dc")) {
+                       } else if (!strcmp(a, "relativeDomainName")) {
                                vals = ldap_get_values(ld, e, a);
                                name = isc_mem_strdup(ns_g_mctx, vals[0]);
                                ldap_value_free(vals);
@@ -255,7 +413,8 @@ ldapdb_allnodes(const char *zone, void *dbdata, dns_sdballnodes_t *allnodes) {
                        type[s - a] = '\0';
                        vals = ldap_get_values(ld, e, a);
                        for (i=0; vals[i] != NULL; i++) {
-                               result = dns_sdb_putnamedrr(allnodes, name, type, ttl, vals[i]);
+                               result = dns_sdb_putnamedrr(allnodes, name,
+                                                           type, ttl, vals[i]);
                                if (result != ISC_R_SUCCESS) {
                                        ldap_value_free(vals);
                                        ldap_memfree(a);
@@ -283,22 +442,25 @@ static dns_sdbmethods_t ldapdb_methods = {
        ldapdb_destroy
 };
 
-/*
- * Wrapper around dns_sdb_register().
- */
+/* Wrapper around dns_sdb_register() */
 isc_result_t
 ldapdb_init(void) {
-       unsigned int flags;
-       flags = DNS_SDBFLAG_RELATIVEOWNER | DNS_SDBFLAG_RELATIVERDATA;
+       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().
- */
+/* Wrapper around dns_sdb_unregister() */
 void
 ldapdb_clear(void) {
-       if (ldapdb != NULL)
+       if (ldapdb != NULL) {
+               /* clean up thread data */
+               ldapdb_getconn(NULL);
                dns_sdb_unregister(&ldapdb);
+       }
 }
index 5413ded941a6dbcb6e0778e86d60bfd43ecee05f..0b6e88b48de1a2e6215d42f2f7b2f3583f7f85f8 100644 (file)
@@ -17,6 +17,7 @@
 #include <isc/print.h>
 #include <isc/result.h>
 #include <isc/util.h>
+#include <isc/thread.h>
 
 #include <dns/sdb.h>
 
 #include "ldapdb.h"
 
 /*
- * A simple database driver for LDAP. Not production quality yet.
+ * A simple database driver for LDAP. Not production quality yet
  */ 
 
 static dns_sdbimplementation_t *ldapdb = NULL;
 
 struct ldapdb_data {
+       char *hostport;
        char *hostname;
        int portno;
        char *base;
        int defaultttl;
-       LDAP *ld;
 };
 
+/* 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;
+}
+
+/* callback routines */
 static isc_result_t
 ldapdb_create(const char *zone, int argc, char **argv,
              void *driverdata, void **dbdata)
@@ -46,10 +161,13 @@ ldapdb_create(const char *zone, int argc, char **argv,
        struct ldapdb_data *data;
        char *s;
        int defaultttl;
-               
+
        UNUSED(zone);
        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))
@@ -57,24 +175,39 @@ ldapdb_create(const char *zone, int argc, char **argv,
         data = isc_mem_get(ns_g_mctx, sizeof(struct ldapdb_data));
         if (data == NULL)
                 return (ISC_R_NOMEMORY);
-       data->hostname = isc_mem_strdup(ns_g_mctx, argv[0] + strlen("ldap://"));
-       if (data->hostname == NULL) {
+       data->hostport = isc_mem_strdup(ns_g_mctx, argv[0] + strlen("ldap://"));
+       if (data->hostport == NULL) {
                isc_mem_put(ns_g_mctx, data, sizeof(struct ldapdb_data));
                return (ISC_R_NOMEMORY);
        }
        data->defaultttl = defaultttl;
-       s = strchr(data->hostname, '/');
+       s = strchr(data->hostport, '/');
        if (s != NULL) {
                *s++ = '\0';
                data->base = *s != '\0' ? s : NULL;
        }
-       s = strchr(data->hostname, ':');
+
+       /* support URLs with literal IPv6 addresses */
+       data->hostname = isc_mem_strdup(ns_g_mctx, data->hostport +
+                                       (*data->hostport == '[' ? 1 : 0));
+       if (data->hostname == NULL) {
+               isc_mem_free(ns_g_mctx, data->hostport);
+               isc_mem_put(ns_g_mctx, data, sizeof(struct ldapdb_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;
-       data->ld = NULL;
+
        *dbdata = data;
        return (ISC_R_SUCCESS);
 }
@@ -86,26 +219,24 @@ ldapdb_destroy(const char *zone, void *driverdata, void **dbdata) {
         UNUSED(zone);
         UNUSED(driverdata);
 
-       if (data == NULL)
-               return;
-       if (data->ld != NULL)
-               ldap_unbind(data->ld);
+       if (data->hostport != NULL)
+               isc_mem_free(ns_g_mctx, data->hostport);
        if (data->hostname != NULL)
                isc_mem_free(ns_g_mctx, data->hostname);
         isc_mem_put(ns_g_mctx, data, sizeof(struct ldapdb_data));
 }
 
 static void
-ldapdb_bind(struct ldapdb_data *data)
+ldapdb_bind(struct ldapdb_data *data, LDAP **ldp)
 {
-       if (data->ld != NULL)
-               ldap_unbind(data->ld);
-       data->ld = ldap_open(data->hostname, data->portno);
-       if (data->ld == NULL)
+       if (*ldp != NULL)
+               ldap_unbind(*ldp);
+       *ldp = ldap_open(data->hostname, data->portno);
+       if (*ldp == NULL)
                return;
-       if (ldap_simple_bind_s(data->ld, NULL, NULL) != LDAP_SUCCESS) {
-               ldap_unbind(data->ld);
-               data->ld = NULL;
+       if (ldap_simple_bind_s(*ldp, NULL, NULL) != LDAP_SUCCESS) {
+               ldap_unbind(*ldp);
+               *ldp = NULL;
        }
 }
 
@@ -115,37 +246,48 @@ ldapdb_lookup(const char *zone, const char *name, void *dbdata,
 {
         isc_result_t result = ISC_R_NOTFOUND;
        struct ldapdb_data *data = dbdata;
-       LDAPMessage     *res, *e;
+       LDAP **ldp;
+       LDAPMessage *res, *e;
        char *fltr, *a, **vals;
        char type[64];
        BerElement *ptr;
        int i;
-       
-       UNUSED(zone);
 
-       if (data->ld == NULL) {
-               ldapdb_bind(data);
-               if (data->ld == NULL)
+       ldp = ldapdb_getconn(data);
+       if (ldp == NULL)
+               return (ISC_R_FAILURE);
+       if (*ldp == NULL) {
+               ldapdb_bind(data, ldp);
+               if (*ldp == NULL)
                        return (ISC_R_FAILURE);
        }
-       fltr = isc_mem_get(ns_g_mctx, strlen(name) + strlen("(dc=)") + 1);
+       fltr = isc_mem_get(ns_g_mctx, strlen(zone) + strlen(name) +
+                          strlen("(&(zoneName=)(relativeDomainName=))") + 1);
         if (fltr == NULL)
                 return (ISC_R_NOMEMORY);
-       strcpy(fltr, "(dc=");
+
+       strcpy(fltr, "(&(zoneName=");
+       strcat(fltr, zone);
+       strcat(fltr, ")(relativeDomainName=");
        strcat(fltr, name);
-       strcat(fltr, ")");
-       if (ldap_search_s(data->ld, data->base, LDAP_SCOPE_ONELEVEL, fltr, NULL, 0, &res) != LDAP_SUCCESS) {
-               ldapdb_bind(data);
-               if (data->ld != NULL)
-                       ldap_search_s(data->ld, data->base, LDAP_SCOPE_ONELEVEL, fltr, NULL, 0, &res);
+       strcat(fltr, "))");
+
+       if (ldap_search_s(*ldp, data->base, LDAP_SCOPE_SUBTREE, fltr, NULL, 0,
+                         &res) != LDAP_SUCCESS) {
+               ldapdb_bind(data, ldp);
+               if (*ldp != NULL)
+                       ldap_search_s(*ldp, data->base, LDAP_SCOPE_SUBTREE,
+                                     fltr, NULL, 0, &res);
        }
+
        isc_mem_put(ns_g_mctx, fltr, strlen(fltr) + 1);
-       if (data->ld == NULL)
+
+       if (*ldp == NULL)
                goto exit;
        
-       for (e = ldap_first_entry(data->ld, res); e != NULL;
-            e = ldap_next_entry(data->ld, e)) {
-               LDAP *ld = data->ld;
+       for (e = ldap_first_entry(*ldp, res); e != NULL;
+            e = ldap_next_entry(*ldp, e)) {
+               LDAP *ld = *ldp;
                int ttl = data->defaultttl;
                
                for (a = ldap_first_attribute(ld, e, &ptr); a != NULL;
@@ -175,7 +317,8 @@ ldapdb_lookup(const char *zone, const char *name, void *dbdata,
                        type[s - a] = '\0';
                        vals = ldap_get_values(ld, e, a);
                        for (i=0; vals[i] != NULL; i++) {
-                               result = dns_sdb_putrr(lookup, type, ttl, vals[i]);
+                               result = dns_sdb_putrr(lookup, type, ttl,
+                                                      vals[i]);
                                if (result != ISC_R_SUCCESS) {
                                        ldap_value_free(vals);
                                        ldap_memfree(a);
@@ -193,32 +336,47 @@ ldapdb_lookup(const char *zone, const char *name, void *dbdata,
 }
 
 static isc_result_t
-ldapdb_allnodes(const char *zone, void *dbdata, dns_sdballnodes_t *allnodes) {
+ldapdb_allnodes(const char *zone, void *dbdata,
+               dns_sdballnodes_t *allnodes) {
         isc_result_t result = ISC_R_NOTFOUND;
        struct ldapdb_data *data = dbdata;
+       LDAP **ldp;
        LDAPMessage     *res, *e;
        char type[64];
-       char *a, **vals;
+       char *fltr, *a, **vals;
        BerElement *ptr;
        int i;
 
-        UNUSED(zone);
-
-       if (data->ld == NULL) {
-               ldapdb_bind(data);
-               if (data->ld == NULL)
+       ldp = ldapdb_getconn(data);
+       if (ldp == NULL)
+               return (ISC_R_FAILURE);
+       if (*ldp == NULL) {
+               ldapdb_bind(data, ldp);
+               if (*ldp == NULL)
                        return (ISC_R_FAILURE);
        }
 
-       if (ldap_search_s(data->ld, data->base, LDAP_SCOPE_ONELEVEL, "(objectclass=*)", NULL, 0, &res) != LDAP_SUCCESS) {
-               ldapdb_bind(data);
-               if (data->ld != NULL)
-                       ldap_search_s(data->ld, data->base, LDAP_SCOPE_ONELEVEL, "(objectclass=*)", NULL, 0, &res);
+       fltr = isc_mem_get(ns_g_mctx, strlen(zone) + strlen("(zoneName=)") + 1);
+        if (fltr == NULL)
+                return (ISC_R_NOMEMORY);
+
+       strcpy(fltr, "(zoneName=");
+       strcat(fltr, zone);
+       strcat(fltr, ")");
+
+       if (ldap_search_s(*ldp, data->base, LDAP_SCOPE_SUBTREE, fltr, NULL, 0,
+                         &res) != LDAP_SUCCESS) {
+               ldapdb_bind(data, ldp);
+               if (*ldp != NULL)
+                       ldap_search_s(*ldp, data->base, LDAP_SCOPE_SUBTREE,
+                                     fltr, NULL, 0, &res);
        }
 
-       for (e = ldap_first_entry(data->ld, res); e != NULL;
-            e = ldap_next_entry(data->ld, e)) {
-               LDAP *ld = data->ld;
+       isc_mem_put(ns_g_mctx, fltr, strlen(fltr) + 1);
+
+       for (e = ldap_first_entry(*ldp, res); e != NULL;
+            e = ldap_next_entry(*ldp, e)) {
+               LDAP *ld = *ldp;
                char *name = NULL;
                int ttl = data->defaultttl;
                
@@ -228,7 +386,7 @@ ldapdb_allnodes(const char *zone, void *dbdata, dns_sdballnodes_t *allnodes) {
                                vals = ldap_get_values(ld, e, a);
                                ttl = atoi(vals[0]);
                                ldap_value_free(vals);
-                       } else if (!strcmp(a, "dc")) {
+                       } else if (!strcmp(a, "relativeDomainName")) {
                                vals = ldap_get_values(ld, e, a);
                                name = isc_mem_strdup(ns_g_mctx, vals[0]);
                                ldap_value_free(vals);
@@ -255,7 +413,8 @@ ldapdb_allnodes(const char *zone, void *dbdata, dns_sdballnodes_t *allnodes) {
                        type[s - a] = '\0';
                        vals = ldap_get_values(ld, e, a);
                        for (i=0; vals[i] != NULL; i++) {
-                               result = dns_sdb_putnamedrr(allnodes, name, type, ttl, vals[i]);
+                               result = dns_sdb_putnamedrr(allnodes, name,
+                                                           type, ttl, vals[i]);
                                if (result != ISC_R_SUCCESS) {
                                        ldap_value_free(vals);
                                        ldap_memfree(a);
@@ -283,22 +442,25 @@ static dns_sdbmethods_t ldapdb_methods = {
        ldapdb_destroy
 };
 
-/*
- * Wrapper around dns_sdb_register().
- */
+/* Wrapper around dns_sdb_register() */
 isc_result_t
 ldapdb_init(void) {
-       unsigned int flags;
-       flags = DNS_SDBFLAG_RELATIVEOWNER | DNS_SDBFLAG_RELATIVERDATA;
+       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().
- */
+/* Wrapper around dns_sdb_unregister() */
 void
 ldapdb_clear(void) {
-       if (ldapdb != NULL)
+       if (ldapdb != NULL) {
+               /* clean up thread data */
+               ldapdb_getconn(NULL);
                dns_sdb_unregister(&ldapdb);
+       }
 }