diff --git a/contrib/sdb/INSTALL.ldap b/contrib/sdb/INSTALL.ldap index 80561a8b26..e54b500157 100644 --- a/contrib/sdb/INSTALL.ldap +++ b/contrib/sdb/INSTALL.ldap @@ -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: - -zone "ldap" { - type master; - database "ldap ldap://129.241.20.67/dc=venaas,dc=com,o=DNS,dc=venaas,dc=no 86400"; -}; - -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 - +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/ -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. +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 -Stig Venaas 2001-03-03 +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. + +Stig Venaas 2001-04-12 diff --git a/contrib/sdb/README.ldap b/contrib/sdb/README.ldap index e952531997..102d0ac969 100644 --- a/contrib/sdb/README.ldap +++ b/contrib/sdb/README.ldap @@ -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 2001-03-03 +Stig Venaas 2001-05-06 diff --git a/contrib/sdb/ldap/INSTALL.ldap b/contrib/sdb/ldap/INSTALL.ldap index 80561a8b26..e54b500157 100644 --- a/contrib/sdb/ldap/INSTALL.ldap +++ b/contrib/sdb/ldap/INSTALL.ldap @@ -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: - -zone "ldap" { - type master; - database "ldap ldap://129.241.20.67/dc=venaas,dc=com,o=DNS,dc=venaas,dc=no 86400"; -}; - -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 - +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/ -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. +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 -Stig Venaas 2001-03-03 +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. + +Stig Venaas 2001-04-12 diff --git a/contrib/sdb/ldap/README.ldap b/contrib/sdb/ldap/README.ldap index e952531997..102d0ac969 100644 --- a/contrib/sdb/ldap/README.ldap +++ b/contrib/sdb/ldap/README.ldap @@ -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 2001-03-03 +Stig Venaas 2001-05-06 diff --git a/contrib/sdb/ldap/ldapdb.c b/contrib/sdb/ldap/ldapdb.c index 5413ded941..0b6e88b48d 100644 --- a/contrib/sdb/ldap/ldapdb.c +++ b/contrib/sdb/ldap/ldapdb.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -26,19 +27,133 @@ #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); + } } diff --git a/contrib/sdb/ldapdb.c b/contrib/sdb/ldapdb.c index 5413ded941..0b6e88b48d 100644 --- a/contrib/sdb/ldapdb.c +++ b/contrib/sdb/ldapdb.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -26,19 +27,133 @@ #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); + } }