You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@apr.apache.org by Graham Leggett via dev <de...@apr.apache.org> on 2023/04/18 12:26:47 UTC

POC: updated LDAP support for apr-util 1.7

Hi all,

The following patch adds ldapi:// (LDAP over unix domain socket) support to apr-util. It is part of a wider cleanup covering the following:

- Add apr_ldap_t to hide the native LDAP type.
- Add apr_ldap_initialize() with URL support, allowing us to do ldapi://, including proper pool cleanups.
- Add apr_ldap_get_option_ex() and apr_ldap_set_option_ex() that use apr_ldap_t, and support all options used by httpd.
- Options passed to apr_ldap_get_option_ex() / apr_ldap_set_option_ex() are a strongly typed union rather than the native void pointers, the assumption being the union is extensible in future.

In theory this extends but does not break our ABI and is safe to go into apr-util 1.7, if this isn’t the case please tell me so I can fix it.

Regards,
Graham
—

Index: CHANGES
===================================================================
--- CHANGES	(revision 1909133)
+++ CHANGES	(working copy)
@@ -1,6 +1,11 @@
                                                      -*- coding: utf-8 -*-
 Changes with APR-util 1.7.0
 
+  *) apr_ldap: Add apr_ldap_t type. Add apr_ldap_initialize() with
+     URL and ldapi:// support. Add apr_ldap_get_option_ex() and
+     apr_ldap_set_option_ex() with support for the apr_ldap_t type.
+     [Graham Leggett]
+
   *) apr_crypto_openssl: Compatibility with OpenSSL 3.  [Yann Ylavic]
 
   *) configure: Fix configure for compilers which don't accept implicit
Index: build/apu-conf.m4
===================================================================
--- build/apu-conf.m4	(revision 1909133)
+++ build/apu-conf.m4	(working copy)
@@ -63,6 +63,7 @@
     AC_CHECK_LIB(${ldaplib}, ldap_init, 
       [
         LDADD_ldap_found="-l${ldaplib} ${extralib}"
+        AC_CHECK_LIB(${ldaplib}, ldap_initialize, apu_has_ldap_initialize="1", , ${extralib})
         AC_CHECK_LIB(${ldaplib}, ldapssl_client_init, apu_has_ldapssl_client_init="1", , ${extralib})
         AC_CHECK_LIB(${ldaplib}, ldapssl_client_deinit, apu_has_ldapssl_client_deinit="1", , ${extralib})
         AC_CHECK_LIB(${ldaplib}, ldapssl_add_trusted_cert, apu_has_ldapssl_add_trusted_cert="1", , ${extralib})
@@ -84,6 +85,7 @@
 echo $ac_n "${nl}checking for ldap support..."
 
 apu_has_ldap="0";
+apu_has_ldap_initialize="0"
 apu_has_ldapssl_client_init="0"
 apu_has_ldapssl_client_deinit="0"
 apu_has_ldapssl_add_trusted_cert="0"
@@ -277,6 +279,7 @@
 AC_SUBST(ldap_h)
 AC_SUBST(lber_h)
 AC_SUBST(ldap_ssl_h)
+AC_SUBST(apu_has_ldap_initialize)
 AC_SUBST(apu_has_ldapssl_client_init)
 AC_SUBST(apu_has_ldapssl_client_deinit)
 AC_SUBST(apu_has_ldapssl_add_trusted_cert)
Index: include/apr_ldap.h.in
===================================================================
--- include/apr_ldap.h.in	(revision 1909133)
+++ include/apr_ldap.h.in	(working copy)
@@ -84,6 +84,7 @@
 /*
  * Detected standard functions
  */
+#define APR_HAS_LDAP_INITIALIZE @apu_has_ldap_initialize@
 #define APR_HAS_LDAPSSL_CLIENT_INIT @apu_has_ldapssl_client_init@
 #define APR_HAS_LDAPSSL_CLIENT_DEINIT @apu_has_ldapssl_client_deinit@
 #define APR_HAS_LDAPSSL_ADD_TRUSTED_CERT @apu_has_ldapssl_add_trusted_cert@
@@ -151,6 +152,11 @@
     int rc;
 } apr_ldap_err_t;
 
+/**
+ * Opaque structure tracking the state of an LDAP connection.
+ */
+typedef struct apr_ldap_t apr_ldap_t;
+
 #ifdef __cplusplus
 }
 #endif
@@ -181,6 +187,9 @@
 #define apr_ldap_rebind_init apr__ldap_rebind_init
 #define apr_ldap_rebind_add apr__ldap_rebind_add
 #define apr_ldap_rebind_remove apr__ldap_rebind_remove
+#define apr_ldap_initialize apr__ldap_initialize
+#define apr_ldap_get_option_ex apr__ldap_get_option_ex
+#define apr_ldap_set_option_ex apr__ldap_set_option_ex
 
 #define APU_DECLARE_LDAP(type) type
 #else
Index: include/apr_ldap.hnw
===================================================================
--- include/apr_ldap.hnw	(revision 1909133)
+++ include/apr_ldap.hnw	(working copy)
@@ -79,6 +79,7 @@
 /*
  * Detected standard functions
  */
+#define APR_HAS_LDAP_INITIALIZE 0
 #define APR_HAS_LDAPSSL_CLIENT_INIT 1
 #define APR_HAS_LDAPSSL_CLIENT_DEINIT 1
 #define APR_HAS_LDAPSSL_ADD_TRUSTED_CERT 1
@@ -120,6 +121,11 @@
     int rc;
 } apr_ldap_err_t;
 
+/**
+ * Opaque structure tracking the state of an LDAP connection.
+ */
+typedef struct apr_ldap_t apr_ldap_t;
+
 #ifdef __cplusplus
 }
 #endif
@@ -141,6 +147,9 @@
 #define apr_ldap_rebind_init apr__ldap_rebind_init
 #define apr_ldap_rebind_add apr__ldap_rebind_add
 #define apr_ldap_rebind_remove apr__ldap_rebind_remove
+#define apr_ldap_initialize apr__ldap_initialize
+#define apr_ldap_get_option_ex apr__ldap_get_option_ex
+#define apr_ldap_set_option_ex apr__ldap_set_option_ex
 
 #define APU_DECLARE_LDAP(type) type
 #else
Index: include/apr_ldap.hw
===================================================================
--- include/apr_ldap.hw	(revision 1909133)
+++ include/apr_ldap.hw	(working copy)
@@ -82,6 +82,7 @@
 /*
  * Detected standard functions
  */
+#define APR_HAS_LDAP_INITIALIZE 0
 #define APR_HAS_LDAPSSL_CLIENT_INIT 0
 #define APR_HAS_LDAPSSL_CLIENT_DEINIT 0
 #define APR_HAS_LDAPSSL_ADD_TRUSTED_CERT 0
@@ -151,6 +152,11 @@
     int rc;
 } apr_ldap_err_t;
 
+/**
+ * Opaque structure tracking the state of an LDAP connection.
+ */
+typedef struct apr_ldap_t apr_ldap_t;
+
 #ifdef __cplusplus
 }
 #endif
@@ -181,6 +187,9 @@
 #define apr_ldap_rebind_init apr__ldap_rebind_init
 #define apr_ldap_rebind_add apr__ldap_rebind_add
 #define apr_ldap_rebind_remove apr__ldap_rebind_remove
+#define apr_ldap_initialize apr__ldap_initialize
+#define apr_ldap_get_option_ex apr__ldap_get_option_ex
+#define apr_ldap_set_option_ex apr__ldap_set_option_ex
 
 #define APU_DECLARE_LDAP(type) type
 #else
Index: include/apr_ldap_init.h
===================================================================
--- include/apr_ldap_init.h	(revision 1909133)
+++ include/apr_ldap_init.h	(working copy)
@@ -135,6 +135,7 @@
  * @param portno The port to connect to
  * @param secure The security mode to set
  * @param result_err The returned result
+ * @deprecated Replaced by apr_ldap_initialize()
  */
 APU_DECLARE_LDAP(int) apr_ldap_init(apr_pool_t *pool,
                                     LDAP **ldap,
@@ -154,6 +155,32 @@
 APU_DECLARE_LDAP(int) apr_ldap_info(apr_pool_t *pool,
                                     apr_ldap_err_t **result_err);
 
+/**
+ * APR LDAP initialise function
+ *
+ * This function is responsible for initialising an LDAP
+ * connection in a toolkit independant way. It does the
+ * job of ldap_initialize() from the C api.
+ *
+ * It handles the SSL case, the non-SSL case, and the IPC
+ * case, and attempts to hide the complexity setup from the
+ * user.
+ *
+ * A cleanup is registered in the pool.
+ *
+ * @param pool The pool to use
+ * @param uri The URI of the LDAP server
+ * @param ldap The ldap context returned
+ * @param result_err On error, error details are written to the
+ *        structure.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_initialize(apr_pool_t *pool,
+                                                   const char *uri,
+                                                   apr_ldap_t **ldap,
+                                                   apr_ldap_err_t *result)
+                                                   __attribute__((nonnull(1, 2, 3, 4)));
+
+
 #ifdef __cplusplus
 }
 #endif
Index: include/apr_ldap_option.h
===================================================================
--- include/apr_ldap_option.h	(revision 1909133)
+++ include/apr_ldap_option.h	(working copy)
@@ -27,6 +27,8 @@
  */
 
 #include "apr_ldap.h"
+#include "apr_tables.h"
+#include "apr_network_io.h"
 
 #if APR_HAS_LDAP
 
@@ -34,6 +36,43 @@
 extern "C" {
 #endif /* __cplusplus */
 
+
+/** LDAP API information */
+typedef struct apr_ldapi_apiinfo_t {
+    int api_version;                /* revision of API supported */
+    int protocol_version;           /* highest LDAP version supported */
+    const char **extensions;        /* names of API extensions */
+    const char *vendor_name;        /* name of supplier */
+    int vendor_version;             /* supplier-specific version * 100 */
+} apr_ldapi_apiinfo_t;
+
+typedef struct apr_ldap_apifeature_info_t {
+    const char *name;               /* LDAP_API_FEATURE_* (less prefix) */
+    int version;                    /* value of LDAP_API_FEATURE_... */
+} apr_ldap_apifeature_info_t;
+
+/** LDAP Protocol Versions */
+typedef enum {
+    APR_LDAP_VERSION1 = 1,          /**< LDAP version 1 */
+    APR_LDAP_VERSION2 = 2,          /**< LDAP version 2 */
+    APR_LDAP_VERSION3 = 3           /**< LDAP version 3 */
+} apr_ldap_protocol_version_e;
+
+/** LDAP deref settings */
+typedef enum {
+    APR_LDAP_DEREF_NEVER = 0,       /**< Aliases should never be dereferenced */
+    APR_LDAP_DEREF_SEARCHING = 1,   /**< Aliases should be dereferenced during the search, but not when locating the base object of the search. */
+    APR_LDAP_DEREF_FINDING = 2,     /**< Aliases should be dereferenced when locating the base object, but not during the search. */
+    APR_LDAP_DEREF_ALWAYS = 3       /**< Aliases should always be dereferenced */
+} apr_ldap_deref_e;
+
+/** LDAP options on or off */
+typedef enum {
+    APR_LDAP_OPT_OFF = 0,           /**< Option set off */
+    APR_LDAP_OPT_ON = 1             /**< Option set on */
+} apr_ldap_switch_e;
+
+
 /*
  * The following defines handle the different TLS certificate
  * options available. If these options are missing, APR will try and
@@ -65,11 +104,36 @@
  * chase before giving up on the search.
  */
 #define APR_LDAP_OPT_REFHOPLIMIT 0x6ffb
-
 /**
- * Structures for the apr_set_option() cases
+ * Get the native LDAP handle.
  */
+#define APR_LDAP_OPT_HANDLE 0x6ffa
+/**
+ * Get/Set the LDAP protocol version.
+ */
+#define APR_LDAP_OPT_PROTOCOL_VERSION 0x6ff9
+/**
+ * Get the LDAP API info.
+ */
+#define APR_LDAP_OPT_API_INFO 0x6ff8
+/**
+ * Get the LDAP API feature info.
+ */
+#define APR_LDAP_OPT_API_FEATURE_INFO 0x6ff7
+/**
+ * Get the dereference setting.
+ */
+#define APR_LDAP_OPT_DEREF 0x6ff6
+/**
+ * Get the most recent result code.
+ */
+#define APR_LDAP_OPT_RESULT_CODE 0x6ff5
+/**
+ * Get the underlying socket.
+ */
+#define APR_LDAP_OPT_DESC 0x6ff4
 
+
 /**
  * APR_LDAP_OPT_TLS_CERT
  *
@@ -202,7 +266,47 @@
 /** end TLS encryption (STOPTLS) */
 #define APR_LDAP_STOPTLS 3
 
+
+/** LDAP TLS options */
+typedef enum {
+    APR_LDAP_TLS_NONE = APR_LDAP_NONE,          /**< No encryption */
+    APR_LDAP_TLS_SSL = APR_LDAP_SSL,            /**< SSL encryption (ldaps://) */
+    APR_LDAP_TLS_STARTTLS = APR_LDAP_STARTTLS,  /**< TLS encryption (STARTTLS) */
+    APR_LDAP_TLS_STOPTLS = APR_LDAP_STOPTLS     /**< end TLS encryption (STOPTLS) */
+} apr_ldap_tls_e;
+
+
+/** LDAP TLS verify options */
+typedef enum {
+    APR_LDAP_VERIFY_OFF = 0,          /**< No verification */
+    APR_LDAP_VERIFY_ON = 1            /**< TLS verification */
+} apr_ldap_verify_e;
+
+
+/** @see apr_ldap_opt_t */
+typedef union apr_ldap_opt_t apr_ldap_opt_t;
 /**
+ * A union of all option structures so we know what
+ * the max size is.
+ */
+union apr_ldap_opt_t {
+    void *handle;                      /** LDAP native handle */
+    void *opt;                         /** LDAP native option */
+    apr_socket_t *socket;              /** LDAP native socket */
+    apr_ldapi_apiinfo_t info;          /** LDAP API information */
+    apr_ldap_apifeature_info_t ldfi;   /** LDAP API feature information */
+    apr_ldap_protocol_version_e pv;    /** Protocol version */
+    apr_array_header_t *certs;         /** TLS certificates */
+    apr_ldap_tls_e tls;                /** TLS on/off/starttls */
+    apr_ldap_verify_e verify;          /** TLS verification */
+    apr_ldap_deref_e deref;            /** Alias dereference */
+    apr_ldap_switch_e refs;            /** Referrals chased */
+    int refhoplimit;                   /** Referral hop limit */
+    int result;                        /** Result code */
+};
+
+
+/**
  * APR LDAP get option function
  *
  * This function gets option values from a given LDAP session if
@@ -213,6 +317,7 @@
  * @param outvalue The value returned (if any)
  * @param result_err The apr_ldap_err_t structure contained detailed results
  *        of the operation.
+ * @deprecated Replaced by apr_ldap_get_option_ex()
  */
 APU_DECLARE_LDAP(int) apr_ldap_get_option(apr_pool_t *pool,
                                           LDAP *ldap,
@@ -235,6 +340,7 @@
  * @param invalue The value to set
  * @param result_err The apr_ldap_err_t structure contained detailed results
  *        of the operation.
+ * @deprecated Replaced by apr_ldap_set_option_ex()
  */
 APU_DECLARE_LDAP(int) apr_ldap_set_option(apr_pool_t *pool,
                                           LDAP *ldap,
@@ -242,6 +348,45 @@
                                           const void *invalue,
                                           apr_ldap_err_t **result_err);
 
+/**
+ * APR LDAP get option function
+ *
+ * This function gets option values from a given LDAP session if
+ * one was specified. It maps to the native ldap_get_option() function.
+ * @param ldap The LDAP handle
+ * @param option The LDAP_OPT_* option to return
+ * @param outvalue The value returned (if any)
+ * @param result_err On error, error details are written to the
+ *        structure.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_get_option_ex(apr_ldap_t *ldap,
+                                                      int option,
+                                                      apr_ldap_opt_t *outvalue,
+                                                      apr_ldap_err_t *result_err)
+                                                      __attribute__((nonnull(3, 4)));
+
+/**
+ * APR LDAP set option function
+ *
+ * This function sets option values to a given LDAP session if
+ * one was specified. It maps to the native ldap_set_option() function.
+ *
+ * Where an option is not supported by an LDAP toolkit, this function
+ * will try and apply legacy functions to achieve the same effect,
+ * depending on the platform.
+ * @param ldap The LDAP handle
+ * @param option The LDAP_OPT_* option to set
+ * @param invalue The value to set
+ * @param result_err On error, error details are written to the
+ *        structure.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_set_option_ex(apr_ldap_t *ldap,
+                                                      int option,
+                                                      const apr_ldap_opt_t *invalue,
+                                                      apr_ldap_err_t *result_err)
+                                                      __attribute__((nonnull(4)));
+
+
 #ifdef __cplusplus
 }
 #endif
Index: include/private/apu_internal.h
===================================================================
--- include/private/apu_internal.h	(revision 1909133)
+++ include/private/apu_internal.h	(working copy)
@@ -40,29 +40,6 @@
 apr_status_t apu_dso_load(apr_dso_handle_t **dso, apr_dso_handle_sym_t *dsoptr, const char *module,
                           const char *modsym, apr_pool_t *pool);
 
-#if APR_HAS_LDAP
-
-/* For LDAP internal builds, wrap our LDAP namespace */
-
-struct apr__ldap_dso_fntable {
-    int (*info)(apr_pool_t *pool, apr_ldap_err_t **result_err);
-    int (*init)(apr_pool_t *pool, LDAP **ldap, const char *hostname,
-                int portno, int secure, apr_ldap_err_t **result_err);
-    int (*ssl_init)(apr_pool_t *pool, const char *cert_auth_file,
-                    int cert_file_type, apr_ldap_err_t **result_err);
-    int (*ssl_deinit)(void);
-    int (*get_option)(apr_pool_t *pool, LDAP *ldap, int option,
-                      void *outvalue, apr_ldap_err_t **result_err);
-    int (*set_option)(apr_pool_t *pool, LDAP *ldap, int option,
-                      const void *invalue, apr_ldap_err_t **result_err);
-    apr_status_t (*rebind_init)(apr_pool_t *pool);
-    apr_status_t (*rebind_add)(apr_pool_t *pool, LDAP *ld,
-                               const char *bindDN, const char *bindPW);
-    apr_status_t (*rebind_remove)(LDAP *ld);
-};
-
-#endif /* APR_HAS_LDAP */
-
 #ifdef __cplusplus
 }
 #endif
Index: ldap/apr_ldap_init.c
===================================================================
--- ldap/apr_ldap_init.c	(revision 1909133)
+++ ldap/apr_ldap_init.c	(working copy)
@@ -31,6 +31,7 @@
 #endif
 
 #include "apr_ldap.h"
+#include "apr_ldap_private.h"
 #include "apu_internal.h"
 #include "apr_errno.h"
 #include "apr_pools.h"
@@ -213,6 +214,96 @@
     
 }
 
+static apr_status_t ldap_cleanup(void *dptr)
+{
+    if (dptr) {
+
+        apr_ldap_t *ldap = dptr;
+
+        if (ldap->ld) {
+            ldap_unbind(ldap->ld);
+            ldap->ld = NULL;
+        }
+    }
+
+    return APR_SUCCESS;
+}
+
+/**
+ * APR LDAP initialise function
+ *
+ * This function is responsible for initialising an LDAP
+ * connection in a toolkit independant way. It does the
+ * job of ldap_initialize() from the C api.
+ *
+ * It handles the SSL case, the non-SSL case, and the IPC
+ * case, and attempts to hide the complexity setup from the
+ * user.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_initialize(apr_pool_t *pool,
+                                                   const char *uri,
+                                                   apr_ldap_t **ldap,
+                                                   apr_ldap_err_t *result)
+{
+    LDAP *ld = NULL;
+    int rc;
+
+    memset(result, 0, sizeof(*result));
+
+    *ldap = apr_pcalloc(pool, sizeof(apr_ldap_t));
+    if (!*ldap) {
+        return APR_ENOMEM;
+    }
+
+#if APR_HAS_LDAP_INITIALIZE
+
+    rc = ldap_initialize(&ld, uri);
+
+#else
+
+    {
+        apr_ldap_url_desc_t *urld;
+        apr_status_t status;
+        int secure;
+
+        status = apr_ldap_url_parse(pool, uri, &(urld), result_err); 
+        if (status != APR_SUCCESS) {
+            return status;
+        }
+
+        secure = apr_ldap_is_ldaps_url(uri);
+
+#if APR_HAS_LDAPSSL_INIT
+        ld = ldapssl_init(urld->lud_host, urld->lud_port, secure);
+#elif APR_HAS_LDAP_SSLINIT
+        ld = ldap_sslinit((char *)urld->lud_host, urld->lud_port, secure);
+#else
+        ld = ldap_init((char *)urld->lud_host, urld->lud_port);
+#endif
+
+    }
+
+#endif
+
+    if (rc != LDAP_SUCCESS) {
+
+        result->rc = rc;
+        result->msg = apr_pstrdup(pool, ldap_err2string(result-> rc));
+        result->reason = apr_pstrdup(pool, "LDAP: Could not initialise");
+
+        return APR_EINVAL;
+    }
+
+    (*ldap)->ld = ld;
+    (*ldap)->pool = pool;
+
+    apr_pool_cleanup_register(pool, (*ldap), ldap_cleanup,
+                              apr_pool_cleanup_null);
+
+    return APR_SUCCESS;
+}
+
+
 #if APU_DSO_BUILD
 
 /* For DSO builds, export the table of entry points into the apr_ldap DSO
@@ -227,7 +318,10 @@
     apr_ldap_set_option,
     apr_ldap_rebind_init,
     apr_ldap_rebind_add,
-    apr_ldap_rebind_remove
+    apr_ldap_rebind_remove,
+    apr_ldap_initialize,
+    apr_ldap_get_option_ex,
+    apr_ldap_set_option_ex
 };
 
 #endif /* APU_DSO_BUILD */
Index: ldap/apr_ldap_option.c
===================================================================
--- ldap/apr_ldap_option.c	(revision 1909133)
+++ ldap/apr_ldap_option.c	(working copy)
@@ -30,10 +30,12 @@
 #endif
 
 #include "apr_ldap.h"
+#include "apr_ldap_private.h"
 #include "apr_errno.h"
 #include "apr_pools.h"
 #include "apr_strings.h"
 #include "apr_tables.h"
+#include "apr_portable.h"
 
 #if APR_HAS_LDAP
 
@@ -648,5 +650,325 @@
 
 }
 
+/**
+ * APR LDAP get option function
+ *
+ * This function gets option values from a given LDAP session if
+ * one was specified.
+ *
+ * If result_err is NULL, no error detail is returned. If *result_err is
+ * NULL, an error detail will be created and returned. If *result_err is
+ * not NULL, an error detail will be written to this location.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_get_option_ex(apr_ldap_t *ldap,
+                                                      int option,
+                                                      apr_ldap_opt_t *outvalue,
+                                                      apr_ldap_err_t *result)
+{
+    int rc;
+
+    memset(result, 0, sizeof(*result));
+
+    switch (option) {
+    case APR_LDAP_OPT_API_INFO: {
+        LDAPAPIInfo info = { 0 };
+
+        info.ldapai_info_version = LDAP_API_INFO_VERSION;
+
+        rc = ldap_get_option(NULL, LDAP_OPT_API_INFO, &info);
+
+        outvalue->info.api_version = info.ldapai_api_version;
+        outvalue->info.protocol_version = info.ldapai_protocol_version;
+        outvalue->info.extensions = (const char **)info.ldapai_extensions;
+        outvalue->info.vendor_name = info.ldapai_vendor_name;
+        outvalue->info.vendor_version = info.ldapai_vendor_version;
+
+        break;
+
+    }
+    case APR_LDAP_OPT_API_FEATURE_INFO: {
+        LDAPAPIFeatureInfo ldfi = { 0 };
+
+        ldfi.ldapaif_info_version = LDAP_FEATURE_INFO_VERSION;
+        ldfi.ldapaif_name = (char *)outvalue->ldfi.name;
+
+        rc = ldap_get_option(NULL, LDAP_OPT_API_FEATURE_INFO, &ldfi);
+
+        outvalue->ldfi.version = ldfi.ldapaif_version;
+
+        break;
+
+    }
+    case APR_LDAP_OPT_PROTOCOL_VERSION: {
+
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_PROTOCOL_VERSION, &outvalue->pv);
+
+        break;
+    }
+    case APR_LDAP_OPT_HANDLE: {
+
+        outvalue->handle = ldap ? ldap->ld : NULL;
+
+        return APR_SUCCESS;
+    }
+    case APR_LDAP_OPT_DESC: {
+
+        apr_status_t status = APR_SUCCESS;
+
+        if (!ldap->socket) {
+            apr_os_sock_t sock;
+
+            rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_DESC, &sock);
+
+            if (rc == LDAP_SUCCESS) {
+                status = apr_os_sock_put(&ldap->socket, &sock, ldap->pool);
+            }
+            else {
+                status = APR_EGENERAL;
+            }
+        }
+        outvalue->socket = ldap->socket;
+
+        return status;
+    }
+    case APR_LDAP_OPT_DEREF: {
+
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_DEREF, &outvalue->deref);
+
+        break;
+    }
+    case APR_LDAP_OPT_REFERRALS: {
+        int refs;
+
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_REFERRALS, &refs);
+
+        if (rc == LDAP_SUCCESS) {
+            outvalue->refs = refs ? APR_LDAP_OPT_ON : APR_LDAP_OPT_OFF;
+        }
+
+        break;
+    }
+    case APR_LDAP_OPT_REFHOPLIMIT: {
+#if defined(LDAP_OPT_REFERRAL_HOP_LIMIT)
+        /* Microsoft and Mozilla SDKs define LDAP_OPT_REFERRAL_HOP_LIMIT
+         */
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_REFERRAL_HOP_LIMIT, &outvalue->refhoplimit);
+#else
+#if !defined(LDAP_OPT_REFHOPLIMIT) || APR_HAS_NOVELL_LDAPSDK || APR_HAS_OPENLDAP_LDAPSDK
+        result->reason = "LDAP: Referral hop limit not yet supported by APR on this "
+                         "LDAP SDK";
+        result->rc = LDAP_UNWILLING_TO_PERFORM;
+        return APR_ENOTIMPL;
+#else
+        /* Setting this option is supported on at least TIVOLI_SDK. Folks who know
+         * the NOVELL, NETSCAPE, MOZILLA, and SOLARIS SDKs should note here if
+         * the SDK at least tolerates this option being set.
+         */
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_REFHOPLIMIT, &outvalue->refhoplimit);
+#endif
+#endif
+
+        break;
+    }
+    case APR_LDAP_OPT_RESULT_CODE: {
+
+#ifdef LDAP_OPT_RESULT_CODE
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_RESULT_CODE, &outvalue->result);
+#else
+#ifdef LDAP_OPT_ERROR_NUMBER
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_ERROR_NUMBER, &outvalue->result);
+#endif
+#endif
+        break;
+    }
+    case APR_LDAP_OPT_TLS_CERT: {
+
+        result->reason = "LDAP: Could not get an option APR_LDAP_OPT_TLS_CERT: not implemented";
+
+        return APR_ENOTIMPL;
+    }
+    case APR_LDAP_OPT_TLS: {
+
+        result->reason = "LDAP: Could not get an option APR_LDAP_OPT_TLS: not implemented";
+
+        return APR_ENOTIMPL;
+    }
+    case APR_LDAP_OPT_VERIFY_CERT: {
+
+        result->reason = "LDAP: Could not get an option APR_LDAP_OPT_VERIFY_CERT: not implemented";
+
+        return APR_ENOTIMPL;
+    }
+    default:
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, option, &outvalue->opt);
+    }
+
+    if (rc != LDAP_SUCCESS) {
+
+        result->rc = rc;
+        result->msg = ldap_err2string(result->rc);
+        result->reason = "LDAP: Could not get an option";
+
+        return APR_EINVAL;
+    }
+
+    return APR_SUCCESS;
+}
+
+/**
+ * APR LDAP set option function
+ *
+ * This function sets option values to a given LDAP session if
+ * one was specified.
+ *
+ * Where an option is not supported by an LDAP toolkit, this function
+ * will try and apply legacy functions to achieve the same effect,
+ * depending on the platform.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_set_option_ex(apr_ldap_t *ldap,
+                                                      int option,
+                                                      const apr_ldap_opt_t *invalue,
+                                                      apr_ldap_err_t *result)
+{
+    int rc;
+
+    memset(result, 0, sizeof(*result));
+
+    switch (option) {
+    case APR_LDAP_OPT_API_INFO:
+        rc = LDAP_UNWILLING_TO_PERFORM;
+        break;
+
+    case APR_LDAP_OPT_API_FEATURE_INFO:
+        rc = LDAP_UNWILLING_TO_PERFORM;
+        break;
+
+    case APR_LDAP_OPT_PROTOCOL_VERSION:
+        rc = ldap_set_option(ldap ? ldap->ld : NULL, LDAP_OPT_PROTOCOL_VERSION, &invalue->pv);
+        break;
+
+    case APR_LDAP_OPT_HANDLE:
+        rc = LDAP_UNWILLING_TO_PERFORM;
+        break;
+
+    case APR_LDAP_OPT_DESC:
+        rc = LDAP_UNWILLING_TO_PERFORM;
+        break;
+
+    case APR_LDAP_OPT_DEREF:
+        rc = ldap_set_option(ldap ? ldap->ld : NULL, LDAP_OPT_DEREF, &invalue->deref);
+        break;
+
+    case APR_LDAP_OPT_REFERRALS: {
+        void *refs = invalue->refs ? LDAP_OPT_ON : LDAP_OPT_OFF;
+
+        /* Setting this option is supported on at least TIVOLI_SDK and OpenLDAP. Folks
+         * who know the NOVELL, NETSCAPE, MOZILLA, and SOLARIS SDKs should note here if
+         * the SDK at least tolerates this option being set, or add an elif to handle
+         * special cases (i.e. different LDAP_OPT_X value).
+         */
+        rc = ldap_set_option(ldap ? ldap->ld : NULL, LDAP_OPT_REFERRALS, refs);
+        break;
+
+    }
+    case APR_LDAP_OPT_REFHOPLIMIT:
+#if defined(LDAP_OPT_REFERRAL_HOP_LIMIT)
+        /* Microsoft and Mozilla SDKs define LDAP_OPT_REFERRAL_HOP_LIMIT
+         */
+        rc = ldap_set_option(ldap ? ldap->ld : NULL, LDAP_OPT_REFERRAL_HOP_LIMIT, &invalue->refhoplimit);
+#else
+#if !defined(LDAP_OPT_REFHOPLIMIT) || APR_HAS_NOVELL_LDAPSDK || APR_HAS_OPENLDAP_LDAPSDK
+        /* If the LDAP_OPT_REFHOPLIMIT symbol is missing, assume that the
+         * particular LDAP library has a reasonable default. So far certain
+         * versions of the OpenLDAP SDK miss this symbol (but default to 5),
+         * and the Microsoft SDK misses the symbol (the default is not known).
+         */
+        result->reason = "LDAP: Referral hop limit not yet supported by APR on this "
+                         "LDAP SDK";
+        result->rc = LDAP_UNWILLING_TO_PERFORM;
+        return APR_ENOTIMPL;
+#else
+        /* Setting this option is supported on at least TIVOLI_SDK. Folks who know
+         * the NOVELL, NETSCAPE, MOZILLA, and SOLARIS SDKs should note here if
+         * the SDK at least tolerates this option being set, or add an elif to handle
+         * special cases so an error isn't returned if there is a perfectly good
+         * default value that just can't be changed (like openLDAP).
+         */
+        rc = ldap_set_option(ldap ? ldap->ld : NULL, LDAP_OPT_REFHOPLIMIT, &invalue->refhoplimit);
+#endif
+#endif
+        break;
+
+    case APR_LDAP_OPT_RESULT_CODE:
+        rc = LDAP_UNWILLING_TO_PERFORM;
+        break;
+
+    case APR_LDAP_OPT_TLS_CERT:
+        option_set_cert(ldap->pool, ldap ? ldap->ld : NULL, invalue->certs, result);
+        break;
+
+    case APR_LDAP_OPT_TLS:
+        option_set_tls(ldap->pool, ldap ? ldap->ld : NULL, &invalue->tls, result);
+        break;
+
+    case APR_LDAP_OPT_VERIFY_CERT:
+#if APR_HAS_NETSCAPE_LDAPSDK || APR_HAS_SOLARIS_LDAPSDK || APR_HAS_MOZILLA_LDAPSK
+        result->reason = "LDAP: Verify certificate not yet supported by APR on the "
+                         "Netscape, Solaris or Mozilla LDAP SDKs";
+        result->rc = LDAP_UNWILLING_TO_PERFORM;
+        return APR_ENOTIMPL;
+#endif
+#if APR_HAS_NOVELL_LDAPSDK
+        if (invalue->verify) {
+            result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER);
+        }
+        else {
+            result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE);
+        }
+#endif
+#if APR_HAS_OPENLDAP_LDAPSDK
+#ifdef LDAP_OPT_X_TLS
+        /* This is not a per-connection setting so just pass NULL for the
+           Ldap connection handle */
+        if (invalue->verify) {
+            int i = LDAP_OPT_X_TLS_DEMAND;
+            result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
+        }
+        else {
+            int i = LDAP_OPT_X_TLS_NEVER;
+            result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
+        }
+#else
+        result->reason = "LDAP: SSL/TLS not yet supported by APR on this "
+                         "version of the OpenLDAP toolkit";
+        result->rc = LDAP_UNWILLING_TO_PERFORM;
+        return APR_ENOTIMPL;
+#endif
+#endif
+
+        /* handle the error case */
+        if (result->rc != LDAP_SUCCESS) {
+            result->msg = ldap_err2string(result->rc);
+            result->reason = "LDAP: Could not set verify mode";
+            return APR_EINVAL;
+        }
+        return APR_SUCCESS;
+
+    default:
+        rc = ldap_set_option(ldap ? ldap->ld : NULL, option, invalue->opt);
+    }
+
+    if (rc != LDAP_OPT_SUCCESS) {
+
+        result->rc = rc;
+        result->msg = apr_pstrdup(ldap->pool, ldap_err2string(result->rc));
+        result->reason = apr_pstrdup(ldap->pool, "LDAP: Could not set an option");
+
+        return APR_EINVAL;
+    }
+
+    return APR_SUCCESS;
+}
+
 #endif /* APR_HAS_LDAP */
 
Index: ldap/apr_ldap_stub.c
===================================================================
--- ldap/apr_ldap_stub.c	(revision 1909133)
+++ ldap/apr_ldap_stub.c	(working copy)
@@ -18,6 +18,7 @@
 #include "apu.h"
 #include "apu_config.h"
 #include "apr_ldap.h"
+#include "apr_ldap_private.h"
 #include "apu_internal.h"
 #include "apr_dso.h"
 #include "apr_errno.h"
@@ -139,6 +140,33 @@
     return lfn->rebind_remove(ld);
 }
 
+APU_DECLARE_LDAP(int) apr_ldap_initialize(apr_pool_t *pool,
+                                          const char *uri,
+                                          apr_ldap_t **ldap,
+                                          apr_ldap_err_t *result)
+{
+    LOAD_LDAP_STUB(pool, -1);
+    return lfn->initialize(pool, uri, ldap, result);
+}
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_get_option_ex(apr_ldap_t *ldap,
+                                                      int option,
+                                                      apr_ldap_opt_t *outvalue,
+                                                      apr_ldap_err_t *result)
+{
+    LOAD_LDAP_STUB(ldap->pool, -1);
+    return lfn->get_option_ex(ldap, option, outvalue, result);
+}
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_set_option_ex(apr_ldap_t *ldap,
+                                                      int option,
+                                                      const apr_ldap_opt_t *invalue,
+                                                      apr_ldap_err_t *result)
+{
+    LOAD_LDAP_STUB(ldap->pool, -1);
+    return lfn->set_option_ex(ldap, option, invalue, result);
+}
+
 #endif /* APU_DSO_BUILD */
 
 #endif /* APR_HAS_LDAP */
Index: test/testldap.c
===================================================================
--- test/testldap.c	(revision 1909133)
+++ test/testldap.c	(working copy)
@@ -91,10 +91,8 @@
         return 0;
     }
 
-    ptr = strstr (ldap_host, "\r\n");
-    if (ptr) {
-        *ptr = '\0';
-    }
+    ptr = ldap_host;
+    strsep (&ptr, "\r\n");
     apr_file_close(thefile);
 
     return 1;
@@ -140,92 +138,197 @@
     return 0;
 }
 
-static void test_ldap_connection(abts_case *tc, LDAP *ldap)
+static void test_ldap_connection(abts_case *tc, apr_ldap_t *ldap)
 {
-    int version  = LDAP_VERSION3;
-    int failures, result;
-    
+    LDAP *ld = NULL;
+    apr_ldap_err_t result;
+    int version  = APR_LDAP_VERSION3;
+    int failures, rc;
+    apr_ldap_opt_t opt;
+    apr_status_t status;
+ 
     /* always default to LDAP V3 */
-    ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
+    opt.pv = APR_LDAP_VERSION3;
+    apr_ldap_set_option_ex(ldap, APR_LDAP_OPT_PROTOCOL_VERSION, &opt, &result);
 
+    apr_ldap_get_option_ex(ldap, APR_LDAP_OPT_HANDLE, &opt, &result);
+    ld = opt.handle;
+
     for (failures=0; failures<10; failures++)
     {
-        result = ldap_simple_bind_s(ldap,
-                                    (char *)NULL,
-                                    (char *)NULL);
-        if (LDAP_SERVER_DOWN != result)
+        rc = ldap_simple_bind_s(ld,
+                                (char *)NULL,
+                                (char *)NULL);
+        if (LDAP_SERVER_DOWN != rc)
             break;
     }
 
-    ABTS_TRUE(tc, result == LDAP_SUCCESS);
-    if (result != LDAP_SUCCESS) {
-        abts_log_message("%s\n", ldap_err2string(result));
+    ABTS_TRUE(tc, rc == LDAP_SUCCESS);
+    if (rc != LDAP_SUCCESS) {
+        abts_log_message("%s\n", ldap_err2string(rc));
     }
 
-    ldap_unbind_s(ldap);
+    status = apr_ldap_get_option_ex(ldap, APR_LDAP_OPT_DESC, &opt, &result);
+    ABTS_TRUE(tc, status == APR_SUCCESS);
+    ABTS_ASSERT(tc, "failed to get descriptor", opt.socket != NULL);
 
     return;
 }
 
-static void test_ldap(abts_case *tc, void *data)
+static void test_ldap_global_opts(abts_case *tc, void *data)
 {
     apr_pool_t *pool = p;
-    LDAP *ldap;
-    apr_ldap_err_t *result = NULL;
+    apr_ldap_t *ldap;
+    apr_ldap_err_t result;
+    const char *url;
+    apr_ldap_opt_t opt;
+    apr_status_t status;
 
+    status = apr_ldap_get_option_ex(NULL, APR_LDAP_OPT_API_INFO, &opt, &result);
 
+    ABTS_TRUE(tc, status == APR_SUCCESS);
+    ABTS_TRUE(tc, result.rc == 0);
+    ABTS_TRUE(tc, opt.info.vendor_name != NULL);
+
+    opt.ldfi.name = "THREAD_SAFE";
+    status = apr_ldap_get_option_ex(NULL, APR_LDAP_OPT_API_FEATURE_INFO, &opt, &result);
+    /* feature may exist, or may not */
+    ABTS_TRUE(tc, status == APR_SUCCESS || status == APR_EINVAL);
+
+}
+
+static void test_ldap_opts(abts_case *tc, void *data)
+{
+    apr_pool_t *pool;
+    apr_ldap_t *ldap;
+    apr_ldap_err_t result;
+    const char *url;
+
+    apr_pool_create(&pool, p);
+
+    url = apr_psprintf(pool, "ldap://%s:%d", "localhost", LDAP_PORT);
+
+    apr_ldap_initialize(pool, url, &ldap, &(result));
+
+    ABTS_TRUE(tc, ldap != NULL);
+
+    if (result.rc == LDAP_SUCCESS) {
+
+        apr_status_t status;
+        apr_ldap_opt_t opt;
+
+        opt.pv = APR_LDAP_VERSION3;
+        status = apr_ldap_set_option_ex(ldap, APR_LDAP_OPT_PROTOCOL_VERSION, &opt, &result);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        status = apr_ldap_get_option_ex(ldap, APR_LDAP_OPT_PROTOCOL_VERSION, &opt, &result);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        ABTS_ASSERT(tc, "failed to set protocol version", opt.pv == APR_LDAP_VERSION3);
+
+        opt.deref = APR_LDAP_DEREF_ALWAYS;
+        status = apr_ldap_set_option_ex(ldap, APR_LDAP_OPT_DEREF, &opt, &result);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        status = apr_ldap_get_option_ex(ldap, APR_LDAP_OPT_DEREF, &opt, &result);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        ABTS_ASSERT(tc, "failed to set deref", opt.deref == APR_LDAP_DEREF_ALWAYS);
+
+        opt.refs = APR_LDAP_OPT_ON;
+        status = apr_ldap_set_option_ex(ldap, APR_LDAP_OPT_REFERRALS, &opt, &result);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        status = apr_ldap_get_option_ex(ldap, APR_LDAP_OPT_REFERRALS, &opt, &result);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        ABTS_ASSERT(tc, "failed to set referrals", opt.refs == APR_LDAP_OPT_ON);
+
+        opt.refhoplimit = 5;
+        status = apr_ldap_set_option_ex(ldap, APR_LDAP_OPT_REFHOPLIMIT, &opt, &result);
+        if (APR_ENOTIMPL != status) {
+            ABTS_TRUE(tc, status == APR_SUCCESS); 
+            status = apr_ldap_get_option_ex(ldap, APR_LDAP_OPT_REFHOPLIMIT, &opt, &result);
+            ABTS_TRUE(tc, status == APR_SUCCESS); 
+            ABTS_ASSERT(tc, "failed to set refhoplimit", opt.refhoplimit == 5);
+        }
+
+    }
+
+    apr_pool_destroy(pool);
+}
+
+static void test_ldap(abts_case *tc, void *data)
+{
+    apr_pool_t *pool;
+    apr_ldap_t *ldap;
+    apr_ldap_err_t result;
+    const char *url;
+
+    apr_pool_create(&pool, p);
+
     ABTS_ASSERT(tc, "failed to get host", ldap_host[0] != '\0');
+
+    url = apr_psprintf(pool, "ldap://%s:%d", ldap_host, LDAP_PORT);
     
-    apr_ldap_init(pool, &ldap,
-                  ldap_host, LDAP_PORT,
-                  APR_LDAP_NONE, &(result));
+    apr_ldap_initialize(pool, url, &ldap, &(result));
 
     ABTS_TRUE(tc, ldap != NULL);
-    ABTS_PTR_NOTNULL(tc, result);
 
-    if (result->rc == LDAP_SUCCESS) {
+    if (result.rc == LDAP_SUCCESS) {
         test_ldap_connection(tc, ldap);
     }
+
+    apr_pool_destroy(pool);
 }
 
 static void test_ldaps(abts_case *tc, void *data)
 {
-    apr_pool_t *pool = p;
-    LDAP *ldap;
-    apr_ldap_err_t *result = NULL;
+    apr_pool_t *pool;
+    apr_ldap_t *ldap;
+    apr_ldap_err_t result;
+    const char *url; 
 
-    apr_ldap_init(pool, &ldap,
-                  ldap_host, LDAPS_PORT,
-                  APR_LDAP_SSL, &(result));
+    apr_pool_create(&pool, p);
 
+    url = apr_psprintf(pool, "ldaps://%s:%d", ldap_host, LDAPS_PORT);
+
+    apr_ldap_initialize(pool, url, &ldap, &(result));
+
     ABTS_TRUE(tc, ldap != NULL);
-    ABTS_PTR_NOTNULL(tc, result);
 
-    if (result->rc == LDAP_SUCCESS) {
+    if (result.rc == LDAP_SUCCESS) {
         add_ldap_certs(tc);
 
         test_ldap_connection(tc, ldap);
     }
+
+    apr_pool_destroy(pool);
 }
 
 static void test_ldap_tls(abts_case *tc, void *data)
 {
-    apr_pool_t *pool = p;
-    LDAP *ldap;
-    apr_ldap_err_t *result = NULL;
+    apr_pool_t *pool;
+    apr_ldap_t *ldap;
+    apr_ldap_err_t result;
+    const char *url;
+    apr_ldap_opt_t opt;
 
-    apr_ldap_init(pool, &ldap,
-                  ldap_host, LDAP_PORT,
-                  APR_LDAP_STARTTLS, &(result));
+    apr_pool_create(&pool, p);
 
+    opt.tls = APR_LDAP_TLS_STARTTLS;
+
+    url = apr_psprintf(pool, "ldap://%s:%d", ldap_host, LDAP_PORT);
+
+    apr_ldap_initialize(pool, url, &ldap, &(result));
+
+    if (result.rc == LDAP_SUCCESS) {
+        apr_ldap_set_option_ex(ldap, APR_LDAP_OPT_TLS, &opt, &(result));
+    }
+
     ABTS_TRUE(tc, ldap != NULL);
-    ABTS_PTR_NOTNULL(tc, result);
 
-    if (result->rc == LDAP_SUCCESS) {
+    if (result.rc == LDAP_SUCCESS) {
         add_ldap_certs(tc);
 
         test_ldap_connection(tc, ldap);
     }
+
+    apr_pool_destroy(pool);
 }
 
 #endif /* APR_HAS_LDAP */
@@ -238,11 +341,17 @@
 
     apr_ldap_ssl_init(p, NULL, 0, &result);
 
+    abts_run_test(suite, test_ldap_global_opts, NULL);
+    abts_run_test(suite, test_ldap_opts, NULL);
+
     if (get_ldap_host()) {
         abts_run_test(suite, test_ldap, NULL);
         abts_run_test(suite, test_ldaps, NULL);
         abts_run_test(suite, test_ldap_tls, NULL);
     }
+
+    apr_ldap_ssl_deinit();
+
 #endif /* APR_HAS_LDAP */
 
     return suite;



Re: POC: updated LDAP support for apr-util 1.7

Posted by Graham Leggett via dev <de...@apr.apache.org>.
On 22 Apr 2023, at 20:04, Graham Leggett <mi...@sharp.fm> wrote:

> Update to the POC. In addition to the above, the following is added:
> 
> - apr_ldap_bind() that currently maps to ldap_sasl_interactive_bind <https://serverfault.com/questions/936658/ldap-sasl-interactive-bind-s-cant-contact-ldap-server-1>(), supporting async requests. Timeout is now a proper parameter so that platform specific weirdness can be handled.
> - apr_ldap_result() that maps to ldap_result().
> - updated test/testldap demonstrating a SASL bind.
> 
> Still to be done:
> 
> - ldap_simple_bind() support. Will be emulated inside apr_ldap_bind().

Update to the POC:

- We have ldap_simple_bind() support.

Regards,
Graham
—



Index: CHANGES
===================================================================
--- CHANGES	(revision 1909133)
+++ CHANGES	(working copy)
@@ -1,6 +1,12 @@
                                                      -*- coding: utf-8 -*-
 Changes with APR-util 1.7.0
 
+  *) apr_ldap: Add apr_ldap_t type. Add apr_ldap_initialize() with
+     URL and ldapi:// support. Add apr_ldap_get_option_ex() and
+     apr_ldap_set_option_ex() with support for the apr_ldap_t type.
+     Add apr_ldap_bind() and apr_ldap_result with SASL bind support.
+     [Graham Leggett]
+
   *) apr_crypto_openssl: Compatibility with OpenSSL 3.  [Yann Ylavic]
 
   *) configure: Fix configure for compilers which don't accept implicit
Index: aprutil.dep
===================================================================
--- aprutil.dep	(revision 1909133)
+++ aprutil.dep	(working copy)
@@ -234,6 +234,7 @@
 	".\include\apr_ldap.h"\
 	".\include\apr_ldap_init.h"\
 	".\include\apr_ldap_option.h"\
+	".\include\apr_ldap_bind.h"\
 	".\include\apr_ldap_rebind.h"\
 	".\include\apr_ldap_url.h"\
 	".\include\apu.h"\
@@ -245,6 +246,7 @@
 	".\include\apr_ldap.h"\
 	".\include\apr_ldap_init.h"\
 	".\include\apr_ldap_option.h"\
+	".\include\apr_ldap_bind.h"\
 	".\include\apr_ldap_rebind.h"\
 	".\include\apr_ldap_url.h"\
 	".\include\apu.h"\
@@ -251,10 +253,22 @@
 	".\include\private\apu_config.h"\
 	
 
+.\ldap\apr_ldap_bind.c : \
+	".\include\apr_ldap.h"\
+	".\include\apr_ldap_init.h"\
+	".\include\apr_ldap_option.h"\
+	".\include\apr_ldap_bind.h"\
+	".\include\apr_ldap_rebind.h"\
+	".\include\apr_ldap_url.h"\
+	".\include\apu.h"\
+	".\include\private\apu_config.h"\
+
+
 .\ldap\apr_ldap_rebind.c : \
 	".\include\apr_ldap.h"\
 	".\include\apr_ldap_init.h"\
 	".\include\apr_ldap_option.h"\
+	".\include\apr_ldap_bind.h"\
 	".\include\apr_ldap_rebind.h"\
 	".\include\apr_ldap_url.h"\
 	".\include\apu.h"\
@@ -265,6 +279,7 @@
 	".\include\apr_ldap.h"\
 	".\include\apr_ldap_init.h"\
 	".\include\apr_ldap_option.h"\
+	".\include\apr_ldap_bind.h"\
 	".\include\apr_ldap_rebind.h"\
 	".\include\apr_ldap_url.h"\
 	".\include\apu.h"\
@@ -277,6 +292,7 @@
 	".\include\apr_ldap.h"\
 	".\include\apr_ldap_init.h"\
 	".\include\apr_ldap_option.h"\
+	".\include\apr_ldap_bind.h"\
 	".\include\apr_ldap_rebind.h"\
 	".\include\apr_ldap_url.h"\
 	".\include\apu.h"\
Index: aprutil.dsp
===================================================================
--- aprutil.dsp	(revision 1909133)
+++ aprutil.dsp	(working copy)
@@ -313,6 +313,10 @@
 
 SOURCE=.\ldap\apr_ldap_option.c
 # End Source File
+# Begin Source File 
+
+SOURCE=.\ldap\apr_ldap_bind.c
+# End Source File 
 # Begin Source File
 
 SOURCE=.\ldap\apr_ldap_rebind.c
Index: aprutil.mak
===================================================================
--- aprutil.mak	(revision 1909133)
+++ aprutil.mak	(working copy)
@@ -86,6 +86,7 @@
 	-@erase "$(INTDIR)\apr_hooks.obj"
 	-@erase "$(INTDIR)\apr_ldap_init.obj"
 	-@erase "$(INTDIR)\apr_ldap_option.obj"
+	-@erase "$(INTDIR)\apr_ldap_bind.obj"
 	-@erase "$(INTDIR)\apr_ldap_rebind.obj"
 	-@erase "$(INTDIR)\apr_ldap_stub.obj"
 	-@erase "$(INTDIR)\apr_ldap_url.obj"
@@ -198,6 +199,7 @@
 	"$(INTDIR)\apr_hooks.obj" \
 	"$(INTDIR)\apr_ldap_init.obj" \
 	"$(INTDIR)\apr_ldap_option.obj" \
+	"$(INTDIR)\apr_ldap_bind.obj" \
 	"$(INTDIR)\apr_ldap_rebind.obj" \
 	"$(INTDIR)\apr_ldap_stub.obj" \
 	"$(INTDIR)\apr_ldap_url.obj" \
@@ -278,6 +280,7 @@
 	-@erase "$(INTDIR)\apr_hooks.obj"
 	-@erase "$(INTDIR)\apr_ldap_init.obj"
 	-@erase "$(INTDIR)\apr_ldap_option.obj"
+	-@erase "$(INTDIR)\apr_ldap_bind.obj"
 	-@erase "$(INTDIR)\apr_ldap_rebind.obj"
 	-@erase "$(INTDIR)\apr_ldap_stub.obj"
 	-@erase "$(INTDIR)\apr_ldap_url.obj"
@@ -395,6 +398,7 @@
 	"$(INTDIR)\apr_hooks.obj" \
 	"$(INTDIR)\apr_ldap_init.obj" \
 	"$(INTDIR)\apr_ldap_option.obj" \
+	"$(INTDIR)\apr_ldap_bind.obj" \
 	"$(INTDIR)\apr_ldap_rebind.obj" \
 	"$(INTDIR)\apr_ldap_stub.obj" \
 	"$(INTDIR)\apr_ldap_url.obj" \
@@ -475,6 +479,7 @@
 	-@erase "$(INTDIR)\apr_hooks.obj"
 	-@erase "$(INTDIR)\apr_ldap_init.obj"
 	-@erase "$(INTDIR)\apr_ldap_option.obj"
+	-@erase "$(INTDIR)\apr_ldap_bind.obj"
 	-@erase "$(INTDIR)\apr_ldap_rebind.obj"
 	-@erase "$(INTDIR)\apr_ldap_stub.obj"
 	-@erase "$(INTDIR)\apr_ldap_url.obj"
@@ -592,6 +597,7 @@
 	"$(INTDIR)\apr_hooks.obj" \
 	"$(INTDIR)\apr_ldap_init.obj" \
 	"$(INTDIR)\apr_ldap_option.obj" \
+	"$(INTDIR)\apr_ldap_bind.obj" \
 	"$(INTDIR)\apr_ldap_rebind.obj" \
 	"$(INTDIR)\apr_ldap_stub.obj" \
 	"$(INTDIR)\apr_ldap_url.obj" \
@@ -672,6 +678,7 @@
 	-@erase "$(INTDIR)\apr_hooks.obj"
 	-@erase "$(INTDIR)\apr_ldap_init.obj"
 	-@erase "$(INTDIR)\apr_ldap_option.obj"
+	-@erase "$(INTDIR)\apr_ldap_bind.obj"
 	-@erase "$(INTDIR)\apr_ldap_rebind.obj"
 	-@erase "$(INTDIR)\apr_ldap_stub.obj"
 	-@erase "$(INTDIR)\apr_ldap_url.obj"
@@ -789,6 +796,7 @@
 	"$(INTDIR)\apr_hooks.obj" \
 	"$(INTDIR)\apr_ldap_init.obj" \
 	"$(INTDIR)\apr_ldap_option.obj" \
+	"$(INTDIR)\apr_ldap_bind.obj" \
 	"$(INTDIR)\apr_ldap_rebind.obj" \
 	"$(INTDIR)\apr_ldap_stub.obj" \
 	"$(INTDIR)\apr_ldap_url.obj" \
@@ -1051,6 +1059,12 @@
 	$(CPP) $(CPP_PROJ) $(SOURCE)
 
 
+SOURCE=.\ldap\apr_ldap_bind.c
+
+"$(INTDIR)\apr_ldap_bind.obj" : $(SOURCE) "$(INTDIR)" ".\include\apu.h" ".\include\private\apu_config.h" ".\include\apr_ldap.h"
+        $(CPP) $(CPP_PROJ) $(SOURCE) 
+
+
 SOURCE=.\ldap\apr_ldap_rebind.c
 
 "$(INTDIR)\apr_ldap_rebind.obj" : $(SOURCE) "$(INTDIR)" ".\include\apu.h" ".\include\private\apu_config.h" ".\include\apr_ldap.h"
Index: build/apu-conf.m4
===================================================================
--- build/apu-conf.m4	(revision 1909133)
+++ build/apu-conf.m4	(working copy)
@@ -63,6 +63,8 @@
     AC_CHECK_LIB(${ldaplib}, ldap_init, 
       [
         LDADD_ldap_found="-l${ldaplib} ${extralib}"
+        AC_CHECK_LIB(${ldaplib}, ldap_initialize, apu_has_ldap_initialize="1", , ${extralib})
+        AC_CHECK_LIB(${ldaplib}, ldap_sasl_interactive_bind, apu_has_ldap_sasl_interactive_bind="1", , ${extralib})
         AC_CHECK_LIB(${ldaplib}, ldapssl_client_init, apu_has_ldapssl_client_init="1", , ${extralib})
         AC_CHECK_LIB(${ldaplib}, ldapssl_client_deinit, apu_has_ldapssl_client_deinit="1", , ${extralib})
         AC_CHECK_LIB(${ldaplib}, ldapssl_add_trusted_cert, apu_has_ldapssl_add_trusted_cert="1", , ${extralib})
@@ -84,6 +86,8 @@
 echo $ac_n "${nl}checking for ldap support..."
 
 apu_has_ldap="0";
+apu_has_ldap_initialize="0"
+apu_has_ldap_sasl_interactive_bind="0"
 apu_has_ldapssl_client_init="0"
 apu_has_ldapssl_client_deinit="0"
 apu_has_ldapssl_add_trusted_cert="0"
@@ -274,9 +278,13 @@
     LIBS="$save_libs"
 fi
 
+AC_CHECK_HEADERS([sasl.h sasl/sasl.h])
+
 AC_SUBST(ldap_h)
 AC_SUBST(lber_h)
 AC_SUBST(ldap_ssl_h)
+AC_SUBST(apu_has_ldap_initialize)
+AC_SUBST(apu_has_ldap_sasl_interactive_bind)
 AC_SUBST(apu_has_ldapssl_client_init)
 AC_SUBST(apu_has_ldapssl_client_deinit)
 AC_SUBST(apu_has_ldapssl_add_trusted_cert)
Index: build.conf
===================================================================
--- build.conf	(revision 1909133)
+++ build.conf	(working copy)
@@ -102,6 +102,7 @@
 [ldap]
 paths = ldap/apr_ldap_init.c
         ldap/apr_ldap_option.c
+        ldap/apr_ldap_bind.c
         ldap/apr_ldap_rebind.c
 target = ldap/apr_ldap.la
 
Index: include/apr_ldap.h.in
===================================================================
--- include/apr_ldap.h.in	(revision 1909133)
+++ include/apr_ldap.h.in	(working copy)
@@ -84,6 +84,8 @@
 /*
  * Detected standard functions
  */
+#define APR_HAS_LDAP_INITIALIZE @apu_has_ldap_initialize@
+#define APR_HAS_LDAP_SASL_INTERACTIVE_BIND @apu_has_ldap_sasl_interactive_bind@
 #define APR_HAS_LDAPSSL_CLIENT_INIT @apu_has_ldapssl_client_init@
 #define APR_HAS_LDAPSSL_CLIENT_DEINIT @apu_has_ldapssl_client_deinit@
 #define APR_HAS_LDAPSSL_ADD_TRUSTED_CERT @apu_has_ldapssl_add_trusted_cert@
@@ -151,6 +153,11 @@
     int rc;
 } apr_ldap_err_t;
 
+/**
+ * Opaque structure tracking the state of an LDAP connection.
+ */
+typedef struct apr_ldap_t apr_ldap_t;
+
 #ifdef __cplusplus
 }
 #endif
@@ -181,6 +188,11 @@
 #define apr_ldap_rebind_init apr__ldap_rebind_init
 #define apr_ldap_rebind_add apr__ldap_rebind_add
 #define apr_ldap_rebind_remove apr__ldap_rebind_remove
+#define apr_ldap_initialize apr__ldap_initialize
+#define apr_ldap_get_option_ex apr__ldap_get_option_ex
+#define apr_ldap_set_option_ex apr__ldap_set_option_ex
+#define apr_ldap_result apr__ldap_result
+#define apr_ldap_bind apr__ldap_bind
 
 #define APU_DECLARE_LDAP(type) type
 #else
@@ -187,9 +199,11 @@
 #define APU_DECLARE_LDAP(type) APU_DECLARE(type)
 #endif
 
+#include "apu_errno.h"
 #include "apr_ldap_url.h"
 #include "apr_ldap_init.h"
 #include "apr_ldap_option.h"
+#include "apr_ldap_bind.h"
 #include "apr_ldap_rebind.h"
 
 #endif /* APR_HAS_LDAP */
Index: include/apr_ldap.hnw
===================================================================
--- include/apr_ldap.hnw	(revision 1909133)
+++ include/apr_ldap.hnw	(working copy)
@@ -79,6 +79,8 @@
 /*
  * Detected standard functions
  */
+#define APR_HAS_LDAP_INITIALIZE 0
+#define APR_HAS_LDAP_SASL_INTERACTIVE_BIND 0
 #define APR_HAS_LDAPSSL_CLIENT_INIT 1
 #define APR_HAS_LDAPSSL_CLIENT_DEINIT 1
 #define APR_HAS_LDAPSSL_ADD_TRUSTED_CERT 1
@@ -120,6 +122,11 @@
     int rc;
 } apr_ldap_err_t;
 
+/**
+ * Opaque structure tracking the state of an LDAP connection.
+ */
+typedef struct apr_ldap_t apr_ldap_t;
+
 #ifdef __cplusplus
 }
 #endif
@@ -141,6 +148,11 @@
 #define apr_ldap_rebind_init apr__ldap_rebind_init
 #define apr_ldap_rebind_add apr__ldap_rebind_add
 #define apr_ldap_rebind_remove apr__ldap_rebind_remove
+#define apr_ldap_initialize apr__ldap_initialize
+#define apr_ldap_get_option_ex apr__ldap_get_option_ex
+#define apr_ldap_set_option_ex apr__ldap_set_option_ex
+#define apr_ldap_result apr__ldap_result
+#define apr_ldap_bind apr__ldap_bind
 
 #define APU_DECLARE_LDAP(type) type
 #else
@@ -147,9 +159,11 @@
 #define APU_DECLARE_LDAP(type) APU_DECLARE(type)
 #endif
 
+#include "apu_errno.h"
 #include "apr_ldap_url.h"
 #include "apr_ldap_init.h"
 #include "apr_ldap_option.h"
+#include "apr_ldap_bind.h"
 #include "apr_ldap_rebind.h"
 
 /** @} */
Index: include/apr_ldap.hw
===================================================================
--- include/apr_ldap.hw	(revision 1909133)
+++ include/apr_ldap.hw	(working copy)
@@ -82,6 +82,8 @@
 /*
  * Detected standard functions
  */
+#define APR_HAS_LDAP_INITIALIZE 0
+#define APR_HAS_LDAP_SASL_INTERACTIVE_BIND 0
 #define APR_HAS_LDAPSSL_CLIENT_INIT 0
 #define APR_HAS_LDAPSSL_CLIENT_DEINIT 0
 #define APR_HAS_LDAPSSL_ADD_TRUSTED_CERT 0
@@ -151,6 +153,11 @@
     int rc;
 } apr_ldap_err_t;
 
+/**
+ * Opaque structure tracking the state of an LDAP connection.
+ */
+typedef struct apr_ldap_t apr_ldap_t;
+
 #ifdef __cplusplus
 }
 #endif
@@ -181,6 +188,11 @@
 #define apr_ldap_rebind_init apr__ldap_rebind_init
 #define apr_ldap_rebind_add apr__ldap_rebind_add
 #define apr_ldap_rebind_remove apr__ldap_rebind_remove
+#define apr_ldap_initialize apr__ldap_initialize
+#define apr_ldap_get_option_ex apr__ldap_get_option_ex
+#define apr_ldap_set_option_ex apr__ldap_set_option_ex
+#define apr_ldap_result apr__ldap_result
+#define apr_ldap_bind apr__ldap_bind
 
 #define APU_DECLARE_LDAP(type) type
 #else
@@ -187,9 +199,11 @@
 #define APU_DECLARE_LDAP(type) APU_DECLARE(type)
 #endif
 
+#include "apu_errno.h"
 #include "apr_ldap_url.h"
 #include "apr_ldap_init.h"
 #include "apr_ldap_option.h"
+#include "apr_ldap_bind.h"
 #include "apr_ldap_rebind.h"
 
 /** @} */
Index: include/apr_ldap.hwc
===================================================================
--- include/apr_ldap.hwc	(revision 1909133)
+++ include/apr_ldap.hwc	(working copy)
@@ -82,6 +82,8 @@
 /*
  * Detected standard functions
  */
+#define APR_HAS_LDAP_INITIALIZE 0
+#define APR_HAS_LDAP_SASL_INTERACTIVE_BIND 0
 #define APR_HAS_LDAPSSL_CLIENT_INIT 0
 #define APR_HAS_LDAPSSL_CLIENT_DEINIT 0
 #define APR_HAS_LDAPSSL_ADD_TRUSTED_CERT 0
@@ -181,6 +183,11 @@
 #define apr_ldap_rebind_init apr__ldap_rebind_init
 #define apr_ldap_rebind_add apr__ldap_rebind_add
 #define apr_ldap_rebind_remove apr__ldap_rebind_remove
+#define apr_ldap_initialize apr__ldap_initialize
+#define apr_ldap_get_option_ex apr__ldap_get_option_ex
+#define apr_ldap_set_option_ex apr__ldap_set_option_ex
+#define apr_ldap_result apr__ldap_result
+#define apr_ldap_bind apr__ldap_bind
 
 #define APU_DECLARE_LDAP(type) type
 #else
@@ -187,9 +194,11 @@
 #define APU_DECLARE_LDAP(type) APU_DECLARE(type)
 #endif
 
+#include "apu_errno.h"
 #include "apr_ldap_url.h"
 #include "apr_ldap_init.h"
 #include "apr_ldap_option.h"
+#include "apr_ldap_bind.h"
 #include "apr_ldap_rebind.h"
 
 /** @} */
Index: include/apr_ldap_bind.h
===================================================================
--- include/apr_ldap_bind.h	(nonexistent)
+++ include/apr_ldap_bind.h	(working copy)
@@ -0,0 +1,122 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * The APR LDAP bind functions provide the ability to bind to a directory
+ * in a generic way.
+ *
+ * First we try binding interactively using SASL, falling back to SASL bind,
+ * then simple bind, as supported on the platform.
+ *
+ * @file apr_ldap_bind.h
+ * @brief Apache LDAP library
+ */
+
+#ifndef APU_LDAP_BIND_H
+#define APU_LDAP_BIND_H
+
+/**
+ * @addtogroup APR_Util_LDAP
+ * @{
+ **/
+
+#if defined(DOXYGEN)
+#include "apr_ldap.h"
+#endif
+
+/*
+ * Handle the case when LDAP is enabled
+ */
+#if APR_HAS_LDAP
+
+/** LDAP interaction identifiers */
+typedef enum {
+    APR_LDAP_INTERACT_GETREALM = 0x4008,          /**< SASL realm for the authentication attempt */
+    APR_LDAP_INTERACT_AUTHNAME = 0x4002,          /**< SASL username to authenticate */
+    APR_LDAP_INTERACT_USER = 0x4001,              /**< SASL username to use for proxy authorization */
+    APR_LDAP_INTERACT_PASS = 0x4004,              /**< SASL password for the provided username */
+    APR_LDAP_INTERACT_NOECHOPROMPT = 0x4006,      /**< SASL generic prompt for input with input echoing disabled */
+    APR_LDAP_INTERACT_ECHOPROMPT = 0x4005,        /**< SASL generic prompt for input with input echoing enabled */
+} apr_ldap_bind_interact_e;
+
+typedef struct apr_ldap_bind_interact_t {
+    apr_ldap_bind_interact_e id; /* same as client/user callback ID */
+    const char *challenge;       /* presented to user (e.g. OTP challenge) */
+    const char *prompt;          /* presented to user (e.g. "Username: ") */
+    const char *defresult;       /* default result string */
+    const void *result;          /* set to point to result */
+    apr_size_t len;              /* set to length of result */
+} apr_ldap_bind_interact_t;
+
+typedef apr_status_t (apr_ldap_interact_proc)(
+        apr_ldap_t *ld, unsigned int flags, apr_ldap_bind_interact_t *interact, void *ctx);
+
+typedef struct apr_ldap_message_t apr_ldap_message_t;
+
+
+/**
+ * APR result function.
+ *
+ * This function returns the result of a previous request, ready for further
+ * processing.
+ *
+ * @return APR_INCOMPLETE means that at least one result has been
+ * returned, and needs to be passed back to the function that
+ * generated this message for further processing.
+ * APR_SUCCESS means that no further messages are available, and
+ * results are complete. Other error codes indicate that the message
+ * read was not successful.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_result(apr_ldap_message_t *msg,
+                                               apr_interval_time_t timeout,
+                                               apu_err_t *result)
+                                               __attribute__((nonnull(1,3)));
+
+/**
+ * APR LDAP bind function
+ *
+ * This function binds a previously initialised LDAP connection
+ * to the directory.
+ *
+ * Binds are attempted as SASL interactive, falling back to a
+ * standard SASL bind, falling back to a simple bind, depending
+ * on the capabilities of the platform.
+ *
+ * Binds are attempted asynchronously. If APR_INCOMPLETE is returned,
+ * this function must be called again.
+ *
+ * @return APR_INCOMPLETE means that processing has occurred, and
+ * the message in reply needs to be fetched using apr_ldap_return().
+ * APR_SUCCESS means that the processing is complete, and the bind
+ * has been successful. Other error codes indicate that the bind
+ * was not successful.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_bind(apr_ldap_t *ldap,
+                                             const char *dn,
+                                             const char *mech,
+                                             apr_ldap_interact_proc *interact,
+                                             void *ctx,
+                                             apr_interval_time_t timeout,
+                                             apr_ldap_message_t **msg,
+                                             apu_err_t *result)
+                                             __attribute__((nonnull(1,4,7,8)));
+
+#endif /* APR_HAS_LDAP */
+
+/** @} */
+
+#endif /* APU_LDAP_BIND_H */
+
Index: include/apr_ldap_init.h
===================================================================
--- include/apr_ldap_init.h	(revision 1909133)
+++ include/apr_ldap_init.h	(working copy)
@@ -135,6 +135,7 @@
  * @param portno The port to connect to
  * @param secure The security mode to set
  * @param result_err The returned result
+ * @deprecated Replaced by apr_ldap_initialize()
  */
 APU_DECLARE_LDAP(int) apr_ldap_init(apr_pool_t *pool,
                                     LDAP **ldap,
@@ -154,6 +155,32 @@
 APU_DECLARE_LDAP(int) apr_ldap_info(apr_pool_t *pool,
                                     apr_ldap_err_t **result_err);
 
+/**
+ * APR LDAP initialise function
+ *
+ * This function is responsible for initialising an LDAP
+ * connection in a toolkit independant way. It does the
+ * job of ldap_initialize() from the C api.
+ *
+ * It handles the SSL case, the non-SSL case, and the IPC
+ * case, and attempts to hide the complexity setup from the
+ * user.
+ *
+ * A cleanup is registered in the pool.
+ *
+ * @param pool The pool to use
+ * @param uri The URI of the LDAP server
+ * @param ldap The ldap context returned
+ * @param result_err On error, error details are written to the
+ *        structure.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_initialize(apr_pool_t *pool,
+                                                   const char *uri,
+                                                   apr_ldap_t **ldap,
+                                                   apu_err_t *result)
+                                                   __attribute__((nonnull(1, 2, 3, 4)));
+
+
 #ifdef __cplusplus
 }
 #endif
Index: include/apr_ldap_option.h
===================================================================
--- include/apr_ldap_option.h	(revision 1909133)
+++ include/apr_ldap_option.h	(working copy)
@@ -27,6 +27,8 @@
  */
 
 #include "apr_ldap.h"
+#include "apr_tables.h"
+#include "apr_network_io.h"
 
 #if APR_HAS_LDAP
 
@@ -34,6 +36,43 @@
 extern "C" {
 #endif /* __cplusplus */
 
+
+/** LDAP API information */
+typedef struct apr_ldapi_apiinfo_t {
+    int api_version;                /* revision of API supported */
+    int protocol_version;           /* highest LDAP version supported */
+    const char **extensions;        /* names of API extensions */
+    const char *vendor_name;        /* name of supplier */
+    int vendor_version;             /* supplier-specific version * 100 */
+} apr_ldapi_apiinfo_t;
+
+typedef struct apr_ldap_apifeature_info_t {
+    const char *name;               /* LDAP_API_FEATURE_* (less prefix) */
+    int version;                    /* value of LDAP_API_FEATURE_... */
+} apr_ldap_apifeature_info_t;
+
+/** LDAP Protocol Versions */
+typedef enum {
+    APR_LDAP_VERSION1 = 1,          /**< LDAP version 1 */
+    APR_LDAP_VERSION2 = 2,          /**< LDAP version 2 */
+    APR_LDAP_VERSION3 = 3           /**< LDAP version 3 */
+} apr_ldap_protocol_version_e;
+
+/** LDAP deref settings */
+typedef enum {
+    APR_LDAP_DEREF_NEVER = 0,       /**< Aliases should never be dereferenced */
+    APR_LDAP_DEREF_SEARCHING = 1,   /**< Aliases should be dereferenced during the search, but not when locating the base object of the search. */
+    APR_LDAP_DEREF_FINDING = 2,     /**< Aliases should be dereferenced when locating the base object, but not during the search. */
+    APR_LDAP_DEREF_ALWAYS = 3       /**< Aliases should always be dereferenced */
+} apr_ldap_deref_e;
+
+/** LDAP options on or off */
+typedef enum {
+    APR_LDAP_OPT_OFF = 0,           /**< Option set off */
+    APR_LDAP_OPT_ON = 1             /**< Option set on */
+} apr_ldap_switch_e;
+
+
 /*
  * The following defines handle the different TLS certificate
  * options available. If these options are missing, APR will try and
@@ -65,11 +104,36 @@
  * chase before giving up on the search.
  */
 #define APR_LDAP_OPT_REFHOPLIMIT 0x6ffb
-
 /**
- * Structures for the apr_set_option() cases
+ * Get the native LDAP handle.
  */
+#define APR_LDAP_OPT_HANDLE 0x6ffa
+/**
+ * Get/Set the LDAP protocol version.
+ */
+#define APR_LDAP_OPT_PROTOCOL_VERSION 0x6ff9
+/**
+ * Get the LDAP API info.
+ */
+#define APR_LDAP_OPT_API_INFO 0x6ff8
+/**
+ * Get the LDAP API feature info.
+ */
+#define APR_LDAP_OPT_API_FEATURE_INFO 0x6ff7
+/**
+ * Get the dereference setting.
+ */
+#define APR_LDAP_OPT_DEREF 0x6ff6
+/**
+ * Get the most recent result code.
+ */
+#define APR_LDAP_OPT_RESULT_CODE 0x6ff5
+/**
+ * Get the underlying socket.
+ */
+#define APR_LDAP_OPT_DESC 0x6ff4
 
+
 /**
  * APR_LDAP_OPT_TLS_CERT
  *
@@ -202,7 +266,47 @@
 /** end TLS encryption (STOPTLS) */
 #define APR_LDAP_STOPTLS 3
 
+
+/** LDAP TLS options */
+typedef enum {
+    APR_LDAP_TLS_NONE = APR_LDAP_NONE,          /**< No encryption */
+    APR_LDAP_TLS_SSL = APR_LDAP_SSL,            /**< SSL encryption (ldaps://) */
+    APR_LDAP_TLS_STARTTLS = APR_LDAP_STARTTLS,  /**< TLS encryption (STARTTLS) */
+    APR_LDAP_TLS_STOPTLS = APR_LDAP_STOPTLS     /**< end TLS encryption (STOPTLS) */
+} apr_ldap_tls_e;
+
+
+/** LDAP TLS verify options */
+typedef enum {
+    APR_LDAP_VERIFY_OFF = 0,          /**< No verification */
+    APR_LDAP_VERIFY_ON = 1            /**< TLS verification */
+} apr_ldap_verify_e;
+
+
+/** @see apr_ldap_opt_t */
+typedef union apr_ldap_opt_t apr_ldap_opt_t;
 /**
+ * A union of all option structures so we know what
+ * the max size is.
+ */
+union apr_ldap_opt_t {
+    void *handle;                      /** LDAP native handle */
+    void *opt;                         /** LDAP native option */
+    apr_socket_t *socket;              /** LDAP native socket */
+    apr_ldapi_apiinfo_t info;          /** LDAP API information */
+    apr_ldap_apifeature_info_t ldfi;   /** LDAP API feature information */
+    apr_ldap_protocol_version_e pv;    /** Protocol version */
+    apr_array_header_t *certs;         /** TLS certificates */
+    apr_ldap_tls_e tls;                /** TLS on/off/starttls */
+    apr_ldap_verify_e verify;          /** TLS verification */
+    apr_ldap_deref_e deref;            /** Alias dereference */
+    apr_ldap_switch_e refs;            /** Referrals chased */
+    int refhoplimit;                   /** Referral hop limit */
+    int result;                        /** Result code */
+};
+
+
+/**
  * APR LDAP get option function
  *
  * This function gets option values from a given LDAP session if
@@ -213,6 +317,7 @@
  * @param outvalue The value returned (if any)
  * @param result_err The apr_ldap_err_t structure contained detailed results
  *        of the operation.
+ * @deprecated Replaced by apr_ldap_get_option_ex()
  */
 APU_DECLARE_LDAP(int) apr_ldap_get_option(apr_pool_t *pool,
                                           LDAP *ldap,
@@ -235,6 +340,7 @@
  * @param invalue The value to set
  * @param result_err The apr_ldap_err_t structure contained detailed results
  *        of the operation.
+ * @deprecated Replaced by apr_ldap_set_option_ex()
  */
 APU_DECLARE_LDAP(int) apr_ldap_set_option(apr_pool_t *pool,
                                           LDAP *ldap,
@@ -242,6 +348,45 @@
                                           const void *invalue,
                                           apr_ldap_err_t **result_err);
 
+/**
+ * APR LDAP get option function
+ *
+ * This function gets option values from a given LDAP session if
+ * one was specified. It maps to the native ldap_get_option() function.
+ * @param ldap The LDAP handle
+ * @param option The LDAP_OPT_* option to return
+ * @param outvalue The value returned (if any)
+ * @param result_err On error, error details are written to the
+ *        structure.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_get_option_ex(apr_ldap_t *ldap,
+                                                      int option,
+                                                      apr_ldap_opt_t *outvalue,
+                                                      apu_err_t *result_err)
+                                                      __attribute__((nonnull(3, 4)));
+
+/**
+ * APR LDAP set option function
+ *
+ * This function sets option values to a given LDAP session if
+ * one was specified. It maps to the native ldap_set_option() function.
+ *
+ * Where an option is not supported by an LDAP toolkit, this function
+ * will try and apply legacy functions to achieve the same effect,
+ * depending on the platform.
+ * @param ldap The LDAP handle
+ * @param option The LDAP_OPT_* option to set
+ * @param invalue The value to set
+ * @param result_err On error, error details are written to the
+ *        structure.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_set_option_ex(apr_ldap_t *ldap,
+                                                      int option,
+                                                      const apr_ldap_opt_t *invalue,
+                                                      apu_err_t *result_err)
+                                                      __attribute__((nonnull(4)));
+
+
 #ifdef __cplusplus
 }
 #endif
Index: include/apu_errno.h
===================================================================
--- include/apu_errno.h	(revision 1909133)
+++ include/apu_errno.h	(working copy)
@@ -54,6 +54,10 @@
  * APR_EINITENGINE    The engine could not be initialised
  * APR_EREINIT        Underlying crypto has already been initialised
  * APR_ENOVERIFY      The signature verification failed
+ * APR_PROXY_AUTH     Proxy authorization has failed
+ * APR_INAPPROPRIATE_AUTH  Authentication not appropriate for this entry
+ * APR_INVALID_CREDENTIALS Invalid credentials were presented
+ * APR_INSUFFICIENT_ACCESS The user has insufficient access
  * </PRE>
  *
  * <PRE>
@@ -88,6 +92,18 @@
 #define APR_EREINIT          (APR_UTIL_START_STATUS + 12)
 /** @see APR_STATUS_IS_ENOVERIFY */
 #define APR_ENOVERIFY        (APR_UTIL_START_STATUS + 13)
+/** @see APR_STATUS_IS_EDOWN */
+#define APR_EDOWN                      (APR_UTIL_START_STATUS + 101)
+/** @see APR_STATUS_IS_AUTH_UNKNOWN */
+#define APR_AUTH_UNKNOWN               (APR_UTIL_START_STATUS + 102)
+/** @see APR_STATUS_IS_PROXY_AUTH */
+#define APR_PROXY_AUTH                 (APR_UTIL_START_STATUS + 103)
+/** @see APR_STATUS_IS_INAPPROPRIATE_AUTH */
+#define APR_INAPPROPRIATE_AUTH         (APR_UTIL_START_STATUS + 104)
+/** @see APR_STATUS_IS_INVALID_CREDENTIALS */
+#define APR_INVALID_CREDENTIALS        (APR_UTIL_START_STATUS + 105)
+/** @see APR_STATUS_IS_INSUFFICIENT_ACCESS */
+#define APR_INSUFFICIENT_ACCESS        (APR_UTIL_START_STATUS + 106)
 /** @} */
 
 /**
@@ -160,8 +176,34 @@
  * The signature verification failed
  */
 #define APR_STATUS_IS_ENOVERIFY(s)        ((s) == APR_ENOVERIFY)
+/**
+ * The server is down
+ */
+#define APR_STATUS_IS_EDOWN(s)        ((s) == APR_EDOWN)
+/**
+ * Authentication mechanism not supoorted by this server
+ */
+#define APR_STATUS_IS_AUTH_UNKNOWN(s)        ((s) == APR_AUTH_UNKNOWN)
+/**
+ * Proxy authorization has failed
+ */
+#define APR_STATUS_IS_PROXY_AUTH(s)        ((s) == APR_PROXY_AUTH)
+/** 
+ * Inappropriate authentication was specified (e.g., simple auth
+ * was specified but the entry does not have a userPassword attribute).
+ */
+#define APR_STATUS_IS_INAPPROPRIATE_AUTH(s)        ((s) == APR_INAPPROPRIATE_AUTH)
+/** 
+ * Invalid credentials were presented (e.g., the wrong password).
+ */
+#define APR_STATUS_IS_INVALID_CREDENTIALS(s)        ((s) == APR_INVALID_CREDENTIALS)
+/** 
+ * The user has insufficient access to perform the operation.
+ */
+#define APR_STATUS_IS_INSUFFICIENT_ACCESS(s)        ((s) == APR_INSUFFICIENT_ACCESS)
 /** @} */
 
+
 /**
  * This structure allows the underlying API error codes to be returned
  * along with plain text error messages that explain to us mere mortals
Index: include/private/apr_ldap_private.h
===================================================================
--- include/private/apr_ldap_private.h	(nonexistent)
+++ include/private/apr_ldap_private.h	(working copy)
@@ -0,0 +1,93 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr.h"
+#include "apu.h"
+#include "apr_network_io.h"
+
+#ifndef APR_LDAP_PRIVATE_H
+#define APR_LDAP_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if APR_HAS_LDAP
+
+typedef struct apr_ldap_t {
+    apr_pool_t *pool;
+    LDAP *ld;
+    apr_socket_t *socket;
+} apr_ldap_t;
+
+typedef struct apr_ldap_message_t {
+    apr_ldap_t *ld;
+    const char *rmech;
+    LDAPMessage *message;
+    int msgid;
+} apr_ldap_message_t;
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_status(int rc, apr_status_t status);
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_message_clear(apr_ldap_message_t *msg);
+
+APU_DECLARE_LDAP(apr_ldap_message_t *) apr_ldap_message_make(apr_ldap_t *ldap);
+
+#if APU_DSO_BUILD
+
+/* For LDAP internal builds, wrap our LDAP namespace */
+
+struct apr__ldap_dso_fntable {
+    int (*info)(apr_pool_t *pool, apr_ldap_err_t **result_err);
+    int (*init)(apr_pool_t *pool, LDAP **ldap, const char *hostname,
+                int portno, int secure, apr_ldap_err_t **result_err);
+    int (*ssl_init)(apr_pool_t *pool, const char *cert_auth_file,
+                    int cert_file_type, apr_ldap_err_t **result_err);
+    int (*ssl_deinit)(void);
+    int (*get_option)(apr_pool_t *pool, LDAP *ldap, int option,
+                      void *outvalue, apr_ldap_err_t **result_err);
+    int (*set_option)(apr_pool_t *pool, LDAP *ldap, int option,
+                      const void *invalue, apr_ldap_err_t **result_err);
+    apr_status_t (*rebind_init)(apr_pool_t *pool);
+    apr_status_t (*rebind_add)(apr_pool_t *pool, LDAP *ld,
+                               const char *bindDN, const char *bindPW);
+    apr_status_t (*rebind_remove)(LDAP *ld);
+    apr_status_t (*initialize)(apr_pool_t *pool, const char *uri,
+                               apr_ldap_t **ldap,
+                               apu_err_t *result_err);
+    apr_status_t (*get_option_ex)(apr_ldap_t *ldap, int option,
+                      apr_ldap_opt_t *outvalue, apu_err_t *result_err);
+    apr_status_t (*set_option_ex)(apr_ldap_t *ldap, int option,
+                      const apr_ldap_opt_t *invalue, apu_err_t *result_err);
+    apr_status_t (*result)(apr_ldap_message_t *msg, apr_interval_time_t timeout,
+                           apu_err_t *result);
+    apr_status_t (*bind)(apr_ldap_t *ldap, const char *dn, const char *mech,
+                         apr_ldap_interact_proc *interact, void *ctx,
+                         apr_interval_time_t timeout,
+                         apr_ldap_message_t **msg, apu_err_t *result);
+};
+
+#endif /* APU_DSO_BUILD */
+
+#endif /* APR_HAS_LDAP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APR_LDAP_PRIVATE_H */
+
+
Index: include/private/apu_internal.h
===================================================================
--- include/private/apu_internal.h	(revision 1909133)
+++ include/private/apu_internal.h	(working copy)
@@ -40,29 +40,6 @@
 apr_status_t apu_dso_load(apr_dso_handle_t **dso, apr_dso_handle_sym_t *dsoptr, const char *module,
                           const char *modsym, apr_pool_t *pool);
 
-#if APR_HAS_LDAP
-
-/* For LDAP internal builds, wrap our LDAP namespace */
-
-struct apr__ldap_dso_fntable {
-    int (*info)(apr_pool_t *pool, apr_ldap_err_t **result_err);
-    int (*init)(apr_pool_t *pool, LDAP **ldap, const char *hostname,
-                int portno, int secure, apr_ldap_err_t **result_err);
-    int (*ssl_init)(apr_pool_t *pool, const char *cert_auth_file,
-                    int cert_file_type, apr_ldap_err_t **result_err);
-    int (*ssl_deinit)(void);
-    int (*get_option)(apr_pool_t *pool, LDAP *ldap, int option,
-                      void *outvalue, apr_ldap_err_t **result_err);
-    int (*set_option)(apr_pool_t *pool, LDAP *ldap, int option,
-                      const void *invalue, apr_ldap_err_t **result_err);
-    apr_status_t (*rebind_init)(apr_pool_t *pool);
-    apr_status_t (*rebind_add)(apr_pool_t *pool, LDAP *ld,
-                               const char *bindDN, const char *bindPW);
-    apr_status_t (*rebind_remove)(LDAP *ld);
-};
-
-#endif /* APR_HAS_LDAP */
-
 #ifdef __cplusplus
 }
 #endif
Index: ldap/NWGNUmakefile
===================================================================
--- ldap/NWGNUmakefile	(revision 1909133)
+++ ldap/NWGNUmakefile	(working copy)
@@ -232,6 +232,7 @@
 	$(OBJDIR)/apr_ldap_init.o \
 	$(OBJDIR)/apr_ldap_option.o \
 	$(OBJDIR)/apr_ldap_url.o \
+	$(OBJDIR)/apr_ldap_bind.o \
 	$(OBJDIR)/apr_ldap_rebind.o \
 	$(OBJDIR)/apr_ldap_stub.o \
 	$(EOLIST)
Index: ldap/apr_ldap.dsp
===================================================================
--- ldap/apr_ldap.dsp	(revision 1909133)
+++ ldap/apr_ldap.dsp	(working copy)
@@ -180,6 +180,10 @@
 # End Source File 
 # Begin Source File
 
+SOURCE=.\apr_ldap_bind.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\apr_ldap_rebind.c
 # End Source File
 # End Group
@@ -200,6 +204,10 @@
 # End Source File
 # Begin Source File
 
+SOURCE=..\include\apr_ldap_bind.h
+# End Source File
+# Begin Source File
+
 SOURCE=..\include\apr_ldap_rebind.h
 # End Source File
 # Begin Source File
Index: ldap/apr_ldap.mak
===================================================================
--- ldap/apr_ldap.mak	(revision 1909133)
+++ ldap/apr_ldap.mak	(working copy)
@@ -54,6 +54,7 @@
 	-@erase "$(INTDIR)\apr_ldap-1.res"
 	-@erase "$(INTDIR)\apr_ldap_init.obj"
 	-@erase "$(INTDIR)\apr_ldap_option.obj"
+	-@erase "$(INTDIR)\apr_ldap_bind.obj"
 	-@erase "$(INTDIR)\apr_ldap_rebind.obj"
 	-@erase "$(INTDIR)\apr_ldap_src.idb"
 	-@erase "$(INTDIR)\apr_ldap_src.pdb"
@@ -111,6 +112,7 @@
 LINK32_OBJS= \
 	"$(INTDIR)\apr_ldap_init.obj" \
 	"$(INTDIR)\apr_ldap_option.obj" \
+	"$(INTDIR)\apr_ldap_bind.obj" \
 	"$(INTDIR)\apr_ldap_rebind.obj" \
 	"$(INTDIR)\apr_ldap-1.res" \
 	"..\..\apr\Release\libapr-1.lib" \
@@ -161,6 +163,7 @@
 	-@erase "$(INTDIR)\apr_ldap-1.res"
 	-@erase "$(INTDIR)\apr_ldap_init.obj"
 	-@erase "$(INTDIR)\apr_ldap_option.obj"
+	-@erase "$(INTDIR)\apr_ldap_bind.obj"
 	-@erase "$(INTDIR)\apr_ldap_rebind.obj"
 	-@erase "$(INTDIR)\apr_ldap_src.idb"
 	-@erase "$(INTDIR)\apr_ldap_src.pdb"
@@ -218,6 +221,7 @@
 LINK32_OBJS= \
 	"$(INTDIR)\apr_ldap_init.obj" \
 	"$(INTDIR)\apr_ldap_option.obj" \
+	"$(INTDIR)\apr_ldap_bind.obj" \
 	"$(INTDIR)\apr_ldap_rebind.obj" \
 	"$(INTDIR)\apr_ldap-1.res" \
 	"..\..\apr\Debug\libapr-1.lib" \
@@ -268,6 +272,7 @@
 	-@erase "$(INTDIR)\apr_ldap-1.res"
 	-@erase "$(INTDIR)\apr_ldap_init.obj"
 	-@erase "$(INTDIR)\apr_ldap_option.obj"
+	-@erase "$(INTDIR)\apr_ldap_bind.obj"
 	-@erase "$(INTDIR)\apr_ldap_rebind.obj"
 	-@erase "$(INTDIR)\apr_ldap_src.idb"
 	-@erase "$(INTDIR)\apr_ldap_src.pdb"
@@ -325,6 +330,7 @@
 LINK32_OBJS= \
 	"$(INTDIR)\apr_ldap_init.obj" \
 	"$(INTDIR)\apr_ldap_option.obj" \
+	"$(INTDIR)\apr_ldap_bind.obj" \
 	"$(INTDIR)\apr_ldap_rebind.obj" \
 	"$(INTDIR)\apr_ldap-1.res" \
 	"..\..\apr\x64\Release\libapr-1.lib" \
@@ -375,6 +381,7 @@
 	-@erase "$(INTDIR)\apr_ldap-1.res"
 	-@erase "$(INTDIR)\apr_ldap_init.obj"
 	-@erase "$(INTDIR)\apr_ldap_option.obj"
+	-@erase "$(INTDIR)\apr_ldap_bind.obj"
 	-@erase "$(INTDIR)\apr_ldap_rebind.obj"
 	-@erase "$(INTDIR)\apr_ldap_src.idb"
 	-@erase "$(INTDIR)\apr_ldap_src.pdb"
@@ -432,6 +439,7 @@
 LINK32_OBJS= \
 	"$(INTDIR)\apr_ldap_init.obj" \
 	"$(INTDIR)\apr_ldap_option.obj" \
+	"$(INTDIR)\apr_ldap_bind.obj" \
 	"$(INTDIR)\apr_ldap_rebind.obj" \
 	"$(INTDIR)\apr_ldap-1.res" \
 	"..\..\apr\x64\Debug\libapr-1.lib" \
@@ -478,6 +486,11 @@
 "$(INTDIR)\apr_ldap_option.obj" : $(SOURCE) "$(INTDIR)"
 
 
+SOURCE=.\apr_ldap_bind.c
+
+"$(INTDIR)\apr_ldap_bind.obj" : $(SOURCE) "$(INTDIR)"
+
+
 SOURCE=.\apr_ldap_rebind.c
 
 "$(INTDIR)\apr_ldap_rebind.obj" : $(SOURCE) "$(INTDIR)"
Index: ldap/apr_ldap_bind.c
===================================================================
--- ldap/apr_ldap_bind.c	(nonexistent)
+++ ldap/apr_ldap_bind.c	(working copy)
@@ -0,0 +1,367 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * apr_ldap_bind.c: LDAP v2/v3 common initialise
+ *
+ */
+
+#include "apr.h"
+#include "apu.h"
+#include "apu_config.h"
+
+#if APU_DSO_BUILD
+#define APU_DSO_LDAP_BUILD
+#endif
+
+#include "apr_ldap.h"
+#include "apr_ldap_private.h"
+#include "apu_internal.h"
+#include "apr_errno.h"
+#include "apr_pools.h"
+#include "apr_strings.h"
+
+#ifdef HAVE_SASL_SASL_H
+#include <sasl/sasl.h>
+#else
+#ifdef HAVE_SASL_H
+#include <sasl.h>
+#endif
+#endif
+
+#if APR_HAS_LDAP
+
+typedef struct apr_ldap_bind_ctx_t {
+    apr_ldap_t *ld;
+    apr_ldap_interact_proc *interact;
+    void *ctx;
+    apr_status_t status;
+} apr_ldap_bind_ctx_t;
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_message_clear(apr_ldap_message_t *msg)
+{
+    if (msg) {
+        msg->rmech = NULL;
+        if (msg->message) {
+            ldap_msgfree(msg->message);
+            msg->message = NULL;
+        }
+        msg->msgid = 0;
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t message_cleanup(void *dptr)
+{
+    return apr_ldap_message_clear(dptr);
+}
+
+APU_DECLARE_LDAP(apr_ldap_message_t *) apr_ldap_message_make(apr_ldap_t *ldap)
+{
+    apr_ldap_message_t *msg = apr_pcalloc(ldap->pool, sizeof(apr_ldap_message_t));
+
+    if (msg) {
+        msg->ld = ldap;
+        apr_pool_cleanup_register(ldap->pool, msg, message_cleanup,
+                                  apr_pool_cleanup_null);
+    }
+
+    return msg;
+}
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_result(apr_ldap_message_t *msg,
+                                               apr_interval_time_t timeout,
+                                               apu_err_t *result)
+{
+    int res;
+
+    LDAP *ld = msg->ld->ld;
+
+    struct timeval tv, *tvptr;
+
+    if (timeout < 0) {
+        tvptr = NULL;
+    }
+    else {
+        tv.tv_sec = (long) apr_time_sec(timeout);
+        tv.tv_usec = (long) apr_time_usec(timeout);
+        tvptr = &tv;
+    }
+
+    if (msg->message) {
+        ldap_msgfree( msg->message );
+    }
+
+    res = ldap_result( ld, msg->msgid, LDAP_MSG_RECEIVED, tvptr, &msg->message );
+    if ( res == -1 ) {
+        result->reason = "LDAP: ldap_result() retrieval failed";
+
+        /* -1 is LDAP_SERVER_DOWN in openldap, use something else */
+#ifdef LDAP_OPT_ERROR_NUMBER
+        ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &result->rc);
+#endif 
+#ifdef LDAP_OPT_RESULT_CODE
+        ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &result->rc);
+#endif 
+        result->msg = ldap_err2string(result->rc);
+        return apr_ldap_status(result->rc, APR_EGENERAL);
+    }
+    else if (res == 0) {
+        result->reason = "LDAP: ldap_result() timed out";
+        result->rc = LDAP_TIMEOUT;
+        result->msg = ldap_err2string(result->rc);
+        return APR_ETIMEDOUT;
+    }
+
+    res = ldap_count_messages(ld, msg->message);
+    if ( res == -1 ) {
+        result->reason = "LDAP: ldap_count_entries() retrieval failed";
+
+        /* -1 is LDAP_SERVER_DOWN in openldap, use something else */
+#ifdef LDAP_OPT_ERROR_NUMBER
+        ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &result->rc);
+#endif
+#ifdef LDAP_OPT_RESULT_CODE
+        ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &result->rc);
+#endif
+        result->msg = ldap_err2string(result->rc);
+        return apr_ldap_status(result->rc, APR_EGENERAL);
+    }
+    else if (res == 0) {
+        return APR_SUCCESS;
+    }
+    else {
+        return APR_INCOMPLETE;
+    }
+
+}
+
+
+static int bind_sasl_interact(LDAP *ld, unsigned flags, void *ctx, void *in)
+{
+    apr_ldap_bind_ctx_t *payload = ctx;
+    sasl_interact_t *sasl_interact = in;
+
+    if (!ld) {
+        return LDAP_PARAM_ERROR;
+    }
+
+    while( sasl_interact->id != SASL_CB_LIST_END ) {
+
+        apr_ldap_bind_interact_t interaction;
+
+        apr_status_t status;
+
+        if (!payload->interact) {
+            return LDAP_PARAM_ERROR;
+        }
+
+        interaction.id = sasl_interact->id;
+        interaction.challenge = sasl_interact->challenge;
+        interaction.prompt = sasl_interact->prompt;
+        interaction.defresult = sasl_interact->defresult;
+        interaction.result = NULL;
+        interaction.len = 0;
+
+        status = payload->interact(payload->ld, flags, &interaction, payload->ctx);
+
+        if (status != APR_SUCCESS) {
+            payload->status = status;
+            return LDAP_PARAM_ERROR;
+        }
+
+        sasl_interact->result = interaction.result;
+        sasl_interact->len = interaction.len;
+
+        sasl_interact++;
+    }
+
+    return LDAP_SUCCESS;
+}
+
+
+/**
+ * APR LDAP bind function
+ *
+ * This function binds a previously initialised LDAP connection
+ * to the directory.
+ *
+ * Binds are attempted as SASL interactive, falling back to a
+ * standard SASL bind, falling back to a simple bind, depending
+ * on the capabilities of the platform.
+ *
+ * Binds are attempted asynchronously. If APR_EAGAIN is returned,
+ * this function must be called again.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_bind(apr_ldap_t *ldap,
+                                             const char *dn,
+                                             const char *mech,
+                                             apr_ldap_interact_proc *interact,
+                                             void *ctx,
+                                             apr_interval_time_t timeout,
+                                             apr_ldap_message_t **msg,
+                                             apu_err_t *result)
+{
+
+    LDAPControl *sctrls[] = { 0 };
+    LDAPControl *cctrls[] = { 0 };
+
+    unsigned int flags = LDAP_SASL_QUIET;
+
+    apr_ldap_bind_ctx_t payload;
+
+    struct timeval tv, *tvptr;
+
+    if (timeout < 0) {
+        tvptr = NULL;
+    }
+    else {
+        tv.tv_sec = (long) apr_time_sec(timeout);
+        tv.tv_usec = (long) apr_time_usec(timeout);
+        tvptr = &tv;
+    }
+
+    memset(result, 0, sizeof(*result));
+
+    payload.ld = ldap;
+    payload.interact = interact;
+    payload.ctx = ctx;
+    payload.status = APR_SUCCESS;
+
+    if (!*msg) {
+        *msg = apr_ldap_message_make(ldap);
+        if (!*msg) {
+            return APR_ENOMEM;
+        }
+    }
+
+#ifdef LDAP_OPT_NETWORK_TIMEOUT
+    if (!(*msg)->message) {
+        result->rc = ldap_set_option(ldap->ld, LDAP_OPT_NETWORK_TIMEOUT, tvptr);
+        if (result->rc != LDAP_SUCCESS) {
+            result->msg = ldap_err2string(result->rc);
+            result->reason = "LDAP: Could not set network timeout";
+            return APR_EINVAL;
+        }
+    }
+#endif
+
+    if (dn) {
+
+        /* Setting a distinguished name means we want a simple bind */
+
+        if (!(*msg)->msgid) {
+
+            apr_ldap_bind_interact_t interaction = { 0 };
+
+            interaction.id = APR_LDAP_INTERACT_PASS;
+            interaction.prompt = "Password";
+
+            payload.status = interact(ldap, 0, &interaction, ctx);
+
+            if (payload.status != APR_SUCCESS) {
+                return payload.status;
+            }
+
+            (*msg)->msgid = ldap_simple_bind(ldap->ld, dn, interaction.result);
+
+            if ((*msg)->msgid > -1) {
+                result->rc = LDAP_SUCCESS;
+                return APR_INCOMPLETE;
+            }
+            else {
+
+#ifdef LDAP_OPT_ERROR_NUMBER
+                ldap_get_option(ldap->ld, LDAP_OPT_ERROR_NUMBER, &result->rc);
+#endif
+#ifdef LDAP_OPT_RESULT_CODE
+                ldap_get_option(ldap->ld, LDAP_OPT_RESULT_CODE, &result->rc);
+#endif
+
+                apr_ldap_message_clear(*msg);
+                result->msg = ldap_err2string(result->rc);
+                result->reason = "LDAP: ldap_simple_bind() failed";
+                return apr_ldap_status(result->rc, APR_EGENERAL);
+            }
+
+        } 
+        else {
+
+            int res = ldap_parse_result(ldap->ld, (*msg)->message, &result->rc, NULL, NULL, NULL,
+                                 NULL, 0);
+
+            apr_ldap_message_clear(*msg);
+
+            if (res > -1) {
+                return APR_SUCCESS;
+            }
+            else {
+                result->msg = ldap_err2string(result->rc);
+                result->reason = "LDAP: ldap_parse_result() failed";
+                return apr_ldap_status(result->rc, APR_EGENERAL);
+            }
+
+        }
+
+    }
+    else {
+
+#if APR_HAS_LDAP_SASL_INTERACTIVE_BIND
+
+        /* No distinguished name is a SASL bind */
+
+        result->rc = ldap_sasl_interactive_bind(ldap->ld, dn, mech,
+                                                sctrls, cctrls, flags, bind_sasl_interact, &payload,
+                                                (*msg)->message, &(*msg)->rmech, &(*msg)->msgid );
+
+        if (APR_SUCCESS != payload.status) {
+            apr_ldap_message_clear(*msg);
+            return payload.status;
+        }
+        else if (result->rc == LDAP_SUCCESS) {
+            apr_ldap_message_clear(*msg);
+            return APR_SUCCESS;
+        }
+        else if (result->rc == LDAP_SASL_BIND_IN_PROGRESS) {
+            return APR_INCOMPLETE;
+        }
+        else {
+            apr_ldap_message_clear(*msg);
+            result->msg = ldap_err2string(result->rc);
+            result->reason = "LDAP: ldap_sasl_interactive_bind() failed";
+            return apr_ldap_status(result->rc, APR_EGENERAL);
+        }
+
+#else
+
+        /*
+         * for platforms that do not support ldap_sasl_interactive_bind(), alternative
+         * implementations using ldap_sasl_bind() go here.
+         */
+
+        result->reason = "LDAP: SASL bind not yet supported by APR on this "
+                         "LDAP SDK";
+        result->rc = LDAP_UNWILLING_TO_PERFORM;
+        return APR_ENOTIMPL;
+#endif
+
+    }
+
+}
+
+#endif /* APR_HAS_LDAP */
+
Index: ldap/apr_ldap_init.c
===================================================================
--- ldap/apr_ldap_init.c	(revision 1909133)
+++ ldap/apr_ldap_init.c	(working copy)
@@ -31,6 +31,7 @@
 #endif
 
 #include "apr_ldap.h"
+#include "apr_ldap_private.h"
 #include "apu_internal.h"
 #include "apr_errno.h"
 #include "apr_pools.h"
@@ -213,6 +214,130 @@
     
 }
 
+static apr_status_t ldap_cleanup(void *dptr)
+{
+    if (dptr) {
+
+        apr_ldap_t *ldap = dptr;
+
+        if (ldap->ld) {
+            ldap_unbind(ldap->ld);
+            ldap->ld = NULL;
+        }
+    }
+
+    return APR_SUCCESS;
+}
+
+/**
+ * APR LDAP initialise function
+ *
+ * This function is responsible for initialising an LDAP
+ * connection in a toolkit independant way. It does the
+ * job of ldap_initialize() from the C api.
+ *
+ * It handles the SSL case, the non-SSL case, and the IPC
+ * case, and attempts to hide the complexity setup from the
+ * user.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_initialize(apr_pool_t *pool,
+                                                   const char *uri,
+                                                   apr_ldap_t **ldap,
+                                                   apu_err_t *result)
+{
+    LDAP *ld = NULL;
+    int rc;
+
+    memset(result, 0, sizeof(*result));
+
+    *ldap = apr_pcalloc(pool, sizeof(apr_ldap_t));
+    if (!*ldap) {
+        return APR_ENOMEM;
+    }
+
+#if APR_HAS_LDAP_INITIALIZE
+
+    rc = ldap_initialize(&ld, uri);
+
+#else
+
+    {
+        apr_ldap_url_desc_t *urld;
+        apr_status_t status;
+        int secure;
+
+        status = apr_ldap_url_parse(pool, uri, &(urld), result_err); 
+        if (status != APR_SUCCESS) {
+            return status;
+        }
+
+        secure = apr_ldap_is_ldaps_url(uri);
+
+#if APR_HAS_LDAPSSL_INIT
+        ld = ldapssl_init(urld->lud_host, urld->lud_port, secure);
+#elif APR_HAS_LDAP_SSLINIT
+        ld = ldap_sslinit((char *)urld->lud_host, urld->lud_port, secure);
+#else
+        ld = ldap_init((char *)urld->lud_host, urld->lud_port);
+#endif
+
+    }
+
+#endif
+
+    if (rc != LDAP_SUCCESS) {
+
+        result->rc = rc;
+        result->msg = apr_pstrdup(pool, ldap_err2string(result-> rc));
+        result->reason = apr_pstrdup(pool, "LDAP: Could not initialise");
+
+        return APR_EINVAL;
+    }
+
+    (*ldap)->ld = ld;
+    (*ldap)->pool = pool;
+
+    apr_pool_cleanup_register(pool, (*ldap), ldap_cleanup,
+                              apr_pool_cleanup_null);
+
+    return APR_SUCCESS;
+}
+
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_status(int rc, apr_status_t status)
+{
+
+    switch (rc) {
+    case LDAP_TIMEOUT:
+        return APR_ETIMEDOUT;
+
+    case LDAP_SERVER_DOWN:
+        return APR_EDOWN;
+
+    case LDAP_AUTH_UNKNOWN:
+        return APR_AUTH_UNKNOWN;
+
+#ifdef LDAP_X_PROXY_AUTHZ_FAILURE
+    case LDAP_X_PROXY_AUTHZ_FAILURE:
+        return APR_PROXY_AUTH;
+
+#endif
+    case LDAP_INAPPROPRIATE_AUTH:
+        return APR_INAPPROPRIATE_AUTH;
+
+    case LDAP_INVALID_CREDENTIALS:
+        return APR_INVALID_CREDENTIALS;
+
+    case LDAP_INSUFFICIENT_ACCESS:
+        return APR_INSUFFICIENT_ACCESS;
+
+    default:
+        return status;
+    }
+
+}
+
+
 #if APU_DSO_BUILD
 
 /* For DSO builds, export the table of entry points into the apr_ldap DSO
@@ -227,7 +352,12 @@
     apr_ldap_set_option,
     apr_ldap_rebind_init,
     apr_ldap_rebind_add,
-    apr_ldap_rebind_remove
+    apr_ldap_rebind_remove,
+    apr_ldap_initialize,
+    apr_ldap_get_option_ex,
+    apr_ldap_set_option_ex,
+    apr_ldap_result,
+    apr_ldap_bind
 };
 
 #endif /* APU_DSO_BUILD */
Index: ldap/apr_ldap_option.c
===================================================================
--- ldap/apr_ldap_option.c	(revision 1909133)
+++ ldap/apr_ldap_option.c	(working copy)
@@ -30,17 +30,19 @@
 #endif
 
 #include "apr_ldap.h"
+#include "apr_ldap_private.h"
 #include "apr_errno.h"
 #include "apr_pools.h"
 #include "apr_strings.h"
 #include "apr_tables.h"
+#include "apr_portable.h"
 
 #if APR_HAS_LDAP
 
-static void option_set_cert(apr_pool_t *pool, LDAP *ldap, const void *invalue,
-                           apr_ldap_err_t *result);
-static void option_set_tls(apr_pool_t *pool, LDAP *ldap, const void *invalue,
-                          apr_ldap_err_t *result);
+static int option_set_cert(apr_pool_t *pool, LDAP *ldap, const void *invalue,
+                           apu_err_t *result);
+static int option_set_tls(apr_pool_t *pool, LDAP *ldap, const void *invalue,
+                          apu_err_t *result);
 
 /**
  * APR LDAP get option function
@@ -102,11 +104,11 @@
 
     switch (option) {
     case APR_LDAP_OPT_TLS_CERT:
-        option_set_cert(pool, ldap, invalue, result);
+        option_set_cert(pool, ldap, invalue, (apu_err_t *)result);
         break;
 
     case APR_LDAP_OPT_TLS:
-        option_set_tls(pool, ldap, invalue, result);
+        option_set_tls(pool, ldap, invalue, (apu_err_t *)result);
         break;
         
     case APR_LDAP_OPT_VERIFY_CERT:
@@ -220,8 +222,8 @@
  * APR_LDAP_STARTTLS: STARTTLS encryption
  * APR_LDAP_STOPTLS: Stop existing TLS connecttion
  */
-static void option_set_tls(apr_pool_t *pool, LDAP *ldap, const void *invalue,
-                          apr_ldap_err_t *result)
+static int option_set_tls(apr_pool_t *pool, LDAP *ldap, const void *invalue,
+                          apu_err_t *result)
 {
 #if APR_HAS_LDAP_SSL /* compiled with ssl support */
 
@@ -378,6 +380,7 @@
 
 #endif /* APR_HAS_LDAP_SSL */
 
+    return result->rc;
 }
 
 /**
@@ -393,8 +396,8 @@
  * Microsoft: unknown
  * Solaris: unknown
  */
-static void option_set_cert(apr_pool_t *pool, LDAP *ldap,
-                           const void *invalue, apr_ldap_err_t *result)
+static int option_set_cert(apr_pool_t *pool, LDAP *ldap,
+                           const void *invalue, apu_err_t *result)
 {
 #if APR_HAS_LDAP_SSL
 #if APR_HAS_LDAPSSL_CLIENT_INIT || APR_HAS_OPENLDAP_LDAPSDK
@@ -646,7 +649,332 @@
     result->rc = -1;
 #endif /* APR_HAS_LDAP_SSL */
 
+    return result->rc;
 }
 
+/**
+ * APR LDAP get option function
+ *
+ * This function gets option values from a given LDAP session if
+ * one was specified.
+ *
+ * If result_err is NULL, no error detail is returned. If *result_err is
+ * NULL, an error detail will be created and returned. If *result_err is
+ * not NULL, an error detail will be written to this location.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_get_option_ex(apr_ldap_t *ldap,
+                                                      int option,
+                                                      apr_ldap_opt_t *outvalue,
+                                                      apu_err_t *result)
+{
+    int rc;
+
+    memset(result, 0, sizeof(*result));
+
+    switch (option) {
+    case APR_LDAP_OPT_API_INFO: {
+        LDAPAPIInfo info = { 0 };
+
+        info.ldapai_info_version = LDAP_API_INFO_VERSION;
+
+        rc = ldap_get_option(NULL, LDAP_OPT_API_INFO, &info);
+
+        outvalue->info.api_version = info.ldapai_api_version;
+        outvalue->info.protocol_version = info.ldapai_protocol_version;
+        outvalue->info.extensions = (const char **)info.ldapai_extensions;
+        outvalue->info.vendor_name = info.ldapai_vendor_name;
+        outvalue->info.vendor_version = info.ldapai_vendor_version;
+
+        break;
+
+    }
+    case APR_LDAP_OPT_API_FEATURE_INFO: {
+        LDAPAPIFeatureInfo ldfi = { 0 };
+
+        ldfi.ldapaif_info_version = LDAP_FEATURE_INFO_VERSION;
+        ldfi.ldapaif_name = (char *)outvalue->ldfi.name;
+
+        rc = ldap_get_option(NULL, LDAP_OPT_API_FEATURE_INFO, &ldfi);
+
+        outvalue->ldfi.version = ldfi.ldapaif_version;
+
+        break;
+
+    }
+    case APR_LDAP_OPT_PROTOCOL_VERSION: {
+
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_PROTOCOL_VERSION, &outvalue->pv);
+
+        break;
+    }
+    case APR_LDAP_OPT_HANDLE: {
+
+        outvalue->handle = ldap ? ldap->ld : NULL;
+
+        return APR_SUCCESS;
+    }
+    case APR_LDAP_OPT_DESC: {
+
+        apr_status_t status = APR_SUCCESS;
+
+        if (!ldap->socket) {
+            apr_os_sock_t sock;
+
+            rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_DESC, &sock);
+
+            if (rc == LDAP_SUCCESS) {
+                status = apr_os_sock_put(&ldap->socket, &sock, ldap->pool);
+            }
+            else {
+                status = APR_EGENERAL;
+            }
+        }
+        outvalue->socket = ldap->socket;
+
+        return status;
+    }
+    case APR_LDAP_OPT_DEREF: {
+
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_DEREF, &outvalue->deref);
+
+        break;
+    }
+    case APR_LDAP_OPT_REFERRALS: {
+        int refs;
+
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_REFERRALS, &refs);
+
+        if (rc == LDAP_SUCCESS) {
+            outvalue->refs = refs ? APR_LDAP_OPT_ON : APR_LDAP_OPT_OFF;
+        }
+
+        break;
+    }
+    case APR_LDAP_OPT_REFHOPLIMIT: {
+#if defined(LDAP_OPT_REFERRAL_HOP_LIMIT)
+        /* Microsoft and Mozilla SDKs define LDAP_OPT_REFERRAL_HOP_LIMIT
+         */
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_REFERRAL_HOP_LIMIT, &outvalue->refhoplimit);
+#else
+#if !defined(LDAP_OPT_REFHOPLIMIT) || APR_HAS_NOVELL_LDAPSDK || APR_HAS_OPENLDAP_LDAPSDK
+        result->reason = "LDAP: Referral hop limit not yet supported by APR on this "
+                         "LDAP SDK";
+        result->rc = LDAP_UNWILLING_TO_PERFORM;
+        return APR_ENOTIMPL;
+#else
+        /* Setting this option is supported on at least TIVOLI_SDK. Folks who know
+         * the NOVELL, NETSCAPE, MOZILLA, and SOLARIS SDKs should note here if
+         * the SDK at least tolerates this option being set.
+         */
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_REFHOPLIMIT, &outvalue->refhoplimit);
+#endif
+#endif
+
+        break;
+    }
+    case APR_LDAP_OPT_RESULT_CODE: {
+
+#ifdef LDAP_OPT_RESULT_CODE
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_RESULT_CODE, &outvalue->result);
+#else
+#ifdef LDAP_OPT_ERROR_NUMBER
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_ERROR_NUMBER, &outvalue->result);
+#endif
+#endif
+        break;
+    }
+    case APR_LDAP_OPT_TLS_CERT: {
+
+        result->reason = "LDAP: Could not get an option APR_LDAP_OPT_TLS_CERT: not implemented";
+
+        return APR_ENOTIMPL;
+    }
+    case APR_LDAP_OPT_TLS: {
+
+        result->reason = "LDAP: Could not get an option APR_LDAP_OPT_TLS: not implemented";
+
+        return APR_ENOTIMPL;
+    }
+    case APR_LDAP_OPT_VERIFY_CERT: {
+
+        result->reason = "LDAP: Could not get an option APR_LDAP_OPT_VERIFY_CERT: not implemented";
+
+        return APR_ENOTIMPL;
+    }
+    default:
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, option, &outvalue->opt);
+    }
+
+    if (rc != LDAP_SUCCESS) {
+
+        result->rc = rc;
+        result->msg = ldap_err2string(result->rc);
+        result->reason = "LDAP: Could not get an option";
+
+        return APR_EINVAL;
+    }
+
+    return APR_SUCCESS;
+}
+
+/**
+ * APR LDAP set option function
+ *
+ * This function sets option values to a given LDAP session if
+ * one was specified.
+ *
+ * Where an option is not supported by an LDAP toolkit, this function
+ * will try and apply legacy functions to achieve the same effect,
+ * depending on the platform.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_set_option_ex(apr_ldap_t *ldap,
+                                                      int option,
+                                                      const apr_ldap_opt_t *invalue,
+                                                      apu_err_t *result)
+{
+    int rc;
+
+    memset(result, 0, sizeof(*result));
+
+    switch (option) {
+    case APR_LDAP_OPT_API_INFO:
+        rc = LDAP_UNWILLING_TO_PERFORM;
+        break;
+
+    case APR_LDAP_OPT_API_FEATURE_INFO:
+        rc = LDAP_UNWILLING_TO_PERFORM;
+        break;
+
+    case APR_LDAP_OPT_PROTOCOL_VERSION:
+        rc = ldap_set_option(ldap ? ldap->ld : NULL, LDAP_OPT_PROTOCOL_VERSION, &invalue->pv);
+        break;
+
+    case APR_LDAP_OPT_HANDLE:
+        rc = LDAP_UNWILLING_TO_PERFORM;
+        break;
+
+    case APR_LDAP_OPT_DESC:
+        rc = LDAP_UNWILLING_TO_PERFORM;
+        break;
+
+    case APR_LDAP_OPT_DEREF:
+        rc = ldap_set_option(ldap ? ldap->ld : NULL, LDAP_OPT_DEREF, &invalue->deref);
+        break;
+
+    case APR_LDAP_OPT_REFERRALS: {
+        void *refs = invalue->refs ? LDAP_OPT_ON : LDAP_OPT_OFF;
+
+        /* Setting this option is supported on at least TIVOLI_SDK and OpenLDAP. Folks
+         * who know the NOVELL, NETSCAPE, MOZILLA, and SOLARIS SDKs should note here if
+         * the SDK at least tolerates this option being set, or add an elif to handle
+         * special cases (i.e. different LDAP_OPT_X value).
+         */
+        rc = ldap_set_option(ldap ? ldap->ld : NULL, LDAP_OPT_REFERRALS, refs);
+        break;
+
+    }
+    case APR_LDAP_OPT_REFHOPLIMIT:
+#if defined(LDAP_OPT_REFERRAL_HOP_LIMIT)
+        /* Microsoft and Mozilla SDKs define LDAP_OPT_REFERRAL_HOP_LIMIT
+         */
+        rc = ldap_set_option(ldap ? ldap->ld : NULL, LDAP_OPT_REFERRAL_HOP_LIMIT, &invalue->refhoplimit);
+#else
+#if !defined(LDAP_OPT_REFHOPLIMIT) || APR_HAS_NOVELL_LDAPSDK || APR_HAS_OPENLDAP_LDAPSDK
+        /* If the LDAP_OPT_REFHOPLIMIT symbol is missing, assume that the
+         * particular LDAP library has a reasonable default. So far certain
+         * versions of the OpenLDAP SDK miss this symbol (but default to 5),
+         * and the Microsoft SDK misses the symbol (the default is not known).
+         */
+        result->reason = "LDAP: Referral hop limit not yet supported by APR on this "
+                         "LDAP SDK";
+        result->rc = LDAP_UNWILLING_TO_PERFORM;
+        return APR_ENOTIMPL;
+#else
+        /* Setting this option is supported on at least TIVOLI_SDK. Folks who know
+         * the NOVELL, NETSCAPE, MOZILLA, and SOLARIS SDKs should note here if
+         * the SDK at least tolerates this option being set, or add an elif to handle
+         * special cases so an error isn't returned if there is a perfectly good
+         * default value that just can't be changed (like openLDAP).
+         */
+        rc = ldap_set_option(ldap ? ldap->ld : NULL, LDAP_OPT_REFHOPLIMIT, &invalue->refhoplimit);
+#endif
+#endif
+        break;
+
+    case APR_LDAP_OPT_RESULT_CODE:
+        rc = LDAP_UNWILLING_TO_PERFORM;
+        break;
+
+    case APR_LDAP_OPT_TLS_CERT:
+        rc = option_set_cert(ldap->pool, ldap ? ldap->ld : NULL, invalue->certs, result);
+        break;
+
+    case APR_LDAP_OPT_TLS:
+        rc = option_set_tls(ldap->pool, ldap ? ldap->ld : NULL, &invalue->tls, result);
+        break;
+
+    case APR_LDAP_OPT_VERIFY_CERT:
+#if APR_HAS_NETSCAPE_LDAPSDK || APR_HAS_SOLARIS_LDAPSDK || APR_HAS_MOZILLA_LDAPSK
+        result->reason = "LDAP: Verify certificate not yet supported by APR on the "
+                         "Netscape, Solaris or Mozilla LDAP SDKs";
+        result->rc = LDAP_UNWILLING_TO_PERFORM;
+        return APR_ENOTIMPL;
+#endif
+#if APR_HAS_NOVELL_LDAPSDK
+        if (invalue->verify) {
+            result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER);
+        }
+        else {
+            result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE);
+        }
+#endif
+#if APR_HAS_OPENLDAP_LDAPSDK
+#ifdef LDAP_OPT_X_TLS
+        /* This is not a per-connection setting so just pass NULL for the
+           Ldap connection handle */
+        if (invalue->verify) {
+            int i = LDAP_OPT_X_TLS_DEMAND;
+            result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
+        }
+        else {
+            int i = LDAP_OPT_X_TLS_NEVER;
+            result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
+        }
+#else
+        result->reason = "LDAP: SSL/TLS not yet supported by APR on this "
+                         "version of the OpenLDAP toolkit";
+        result->rc = LDAP_UNWILLING_TO_PERFORM;
+        return APR_ENOTIMPL;
+#endif
+#endif
+
+        /* handle the error case */
+        if (result->rc != LDAP_SUCCESS) {
+            result->msg = ldap_err2string(result->rc);
+            result->reason = "LDAP: Could not set verify mode";
+            return APR_EINVAL;
+        }
+        return APR_SUCCESS;
+
+    default:
+        rc = ldap_set_option(ldap ? ldap->ld : NULL, option, invalue->opt);
+    }
+
+    if (rc != LDAP_OPT_SUCCESS) {
+
+        result->rc = rc;
+        if (!result->msg) {
+            result->msg = apr_pstrdup(ldap->pool, ldap_err2string(result->rc));
+        }
+        if (result->reason) {
+            result->reason = apr_pstrdup(ldap->pool, "LDAP: Could not set an option");
+        }
+
+        return APR_EINVAL;
+    }
+
+    return APR_SUCCESS;
+}
+
 #endif /* APR_HAS_LDAP */
 
Index: ldap/apr_ldap_stub.c
===================================================================
--- ldap/apr_ldap_stub.c	(revision 1909133)
+++ ldap/apr_ldap_stub.c	(working copy)
@@ -18,6 +18,7 @@
 #include "apu.h"
 #include "apu_config.h"
 #include "apr_ldap.h"
+#include "apr_ldap_private.h"
 #include "apu_internal.h"
 #include "apr_dso.h"
 #include "apr_errno.h"
@@ -139,6 +140,55 @@
     return lfn->rebind_remove(ld);
 }
 
+APU_DECLARE_LDAP(int) apr_ldap_initialize(apr_pool_t *pool,
+                                          const char *uri,
+                                          apr_ldap_t **ldap,
+                                          apu_err_t *result)
+{
+    LOAD_LDAP_STUB(pool, -1);
+    return lfn->initialize(pool, uri, ldap, result);
+}
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_get_option_ex(apr_ldap_t *ldap,
+                                                      int option,
+                                                      apr_ldap_opt_t *outvalue,
+                                                      apu_err_t *result)
+{
+    LOAD_LDAP_STUB(ldap->pool, -1);
+    return lfn->get_option_ex(ldap, option, outvalue, result);
+}
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_set_option_ex(apr_ldap_t *ldap,
+                                                      int option,
+                                                      const apr_ldap_opt_t *invalue,
+                                                      apu_err_t *result)
+{
+    LOAD_LDAP_STUB(ldap->pool, -1);
+    return lfn->set_option_ex(ldap, option, invalue, result);
+}
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_result(apr_ldap_message_t *msg,
+                                               apr_interval_time_t timeout,
+                                               apu_err_t *result)
+{
+    LOAD_LDAP_STUB(msg->ld->pool, APR_EGENERAL);
+    return lfn->result(msg, timeout, result);
+}
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_bind(apr_ldap_t *ldap,
+                                             const char *dn,
+                                             const char *mech,
+                                             apr_ldap_interact_proc *interact,
+                                             void *ctx,
+                                             apr_interval_time_t timeout,
+                                             apr_ldap_message_t **msg,
+                                             apu_err_t *result)
+{
+    LOAD_LDAP_STUB(ldap->pool, -1);
+    return lfn->bind(ldap, dn, mech, interact, ctx, timeout, msg, result);
+}
+
+
 #endif /* APU_DSO_BUILD */
 
 #endif /* APR_HAS_LDAP */
Index: libaprutil.dsp
===================================================================
--- libaprutil.dsp	(revision 1909133)
+++ libaprutil.dsp	(working copy)
@@ -360,6 +360,11 @@
 # End Source File 
 # Begin Source File
 
+SOURCE=.\ldap\apr_ldap_bind.c
+# PROP Exclude_From_Build 1
+# End Source File
+# Begin Source File
+
 SOURCE=.\ldap\apr_ldap_rebind.c
 # PROP Exclude_From_Build 1
 # End Source File
Index: libaprutil.mak
===================================================================
--- libaprutil.mak	(revision 1909133)
+++ libaprutil.mak	(working copy)
@@ -1006,7 +1006,8 @@
 
 SOURCE=.\ldap\apr_ldap_init.c
 SOURCE=.\ldap\apr_ldap_option.c
-SOURCE=.\ldap\apr_ldap_rebind.c
+SOURCE=.\ldap\apr_ldap_bind.c
+SOURCE=.\ldap\apr_ldap_rebind.c 
 SOURCE=.\ldap\apr_ldap_stub.c
 
 "$(INTDIR)\apr_ldap_stub.obj" : $(SOURCE) "$(INTDIR)" ".\include\apu.h" ".\include\private\apu_config.h" ".\include\apr_ldap.h"
Index: test/testldap.c
===================================================================
--- test/testldap.c	(revision 1909133)
+++ test/testldap.c	(working copy)
@@ -91,10 +91,8 @@
         return 0;
     }
 
-    ptr = strstr (ldap_host, "\r\n");
-    if (ptr) {
-        *ptr = '\0';
-    }
+    ptr = ldap_host;
+    strsep (&ptr, "\r\n");
     apr_file_close(thefile);
 
     return 1;
@@ -140,92 +138,268 @@
     return 0;
 }
 
-static void test_ldap_connection(abts_case *tc, LDAP *ldap)
+static apr_status_t bind_interact_external(apr_ldap_t *ld, unsigned int flags, apr_ldap_bind_interact_t *interact, void *ctx)
 {
-    int version  = LDAP_VERSION3;
-    int failures, result;
-    
+    return APR_SUCCESS;
+}
+
+static apr_status_t bind_interact_plain(apr_ldap_t *ld, unsigned int flags, apr_ldap_bind_interact_t *interact, void *ctx)
+{
+
+    switch (interact->id) {
+    case APR_LDAP_INTERACT_USER:
+        break;
+    case APR_LDAP_INTERACT_AUTHNAME:
+        interact->result = "user";
+        interact->len = strlen(interact->result);
+        break;
+    case APR_LDAP_INTERACT_PASS:
+        interact->result = "opensesame";
+        interact->len = strlen(interact->result);
+        break;
+    default:
+        break;
+    }
+
+    return APR_SUCCESS;
+}
+
+static void test_ldap_connection(abts_case *tc, apr_ldap_t *ldap, const char *mech, apr_ldap_interact_proc *interact)
+{
+    apu_err_t result;
+    apr_ldap_message_t *msg = NULL;
+    apr_ldap_opt_t opt;
+    apr_status_t status;
+ 
     /* always default to LDAP V3 */
-    ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
+    opt.pv = APR_LDAP_VERSION3;
+    apr_ldap_set_option_ex(ldap, APR_LDAP_OPT_PROTOCOL_VERSION, &opt, &result);
 
-    for (failures=0; failures<10; failures++)
-    {
-        result = ldap_simple_bind_s(ldap,
-                                    (char *)NULL,
-                                    (char *)NULL);
-        if (LDAP_SERVER_DOWN != result)
+    apr_ldap_get_option_ex(ldap, APR_LDAP_OPT_HANDLE, &opt, &result);
+    ABTS_TRUE(tc, opt.handle != NULL);
+
+    do {
+        status = apr_ldap_bind(ldap, NULL, mech, interact, NULL, apr_time_from_sec(5), &msg, &result);
+
+        if (APR_SUCCESS == status) {
             break;
-    }
+        }
+        else if (APR_INCOMPLETE == status) {
+            /* keep going */
+        }
+        else if (APR_STATUS_IS_ETIMEDOUT(status)) {
+            abts_log_message("%s - %s (%d)\n", result.reason, result.msg, result.rc);
+            break;
+        }
+        else if (APR_STATUS_IS_EDOWN(status)) {
+            abts_log_message("%s - %s (%d)\n", result.reason, result.msg, result.rc);
+            break;
+        }
+        else if (APR_STATUS_IS_AUTH_UNKNOWN(status)) {
+            abts_log_message("%s - %s (%d)\n", result.reason, result.msg, result.rc);
+            break;
+        }
+        else if (APR_STATUS_IS_PROXY_AUTH(status)) {
+            abts_log_message("%s - %s (%d)\n", result.reason, result.msg, result.rc);
+            break;
+        }
+        else if (APR_STATUS_IS_INAPPROPRIATE_AUTH(status)) {
+            abts_log_message("%s - %s (%d)\n", result.reason, result.msg, result.rc);
+            break;
+        }
+        else if (APR_STATUS_IS_INVALID_CREDENTIALS(status)) {
+            abts_log_message("%s - %s (%d)\n", result.reason, result.msg, result.rc);
+            break;
+        }
+        else if (APR_STATUS_IS_INSUFFICIENT_ACCESS(status)) {
+            abts_log_message("%s - %s (%d)\n", result.reason, result.msg, result.rc);
+            break;
+        }
+        else {
+            abts_log_message("%s - %s (%d)\n", result.reason, result.msg, result.rc);
+            ABTS_TRUE(tc, status == LDAP_SUCCESS);
+            break;
+        }
 
-    ABTS_TRUE(tc, result == LDAP_SUCCESS);
-    if (result != LDAP_SUCCESS) {
-        abts_log_message("%s\n", ldap_err2string(result));
-    }
+        ABTS_TRUE(tc, msg != NULL);
 
-    ldap_unbind_s(ldap);
+        status = apr_ldap_result(msg, apr_time_from_sec(5), &result);
 
+        if (APR_SUCCESS == status) {
+            /* no more messages */
+            break;
+        }
+        else if (APR_INCOMPLETE == status) {
+            /* keep going */
+        }
+        else {
+            abts_log_message("%s\n", result.reason);
+            ABTS_TRUE(tc, status == LDAP_SUCCESS);
+            break;
+        }   
+
+    } while (APR_INCOMPLETE == status);
+
+    status = apr_ldap_get_option_ex(ldap, APR_LDAP_OPT_DESC, &opt, &result);
+    ABTS_TRUE(tc, status == APR_SUCCESS);
+    ABTS_ASSERT(tc, "failed to get descriptor", opt.socket != NULL);
+
     return;
 }
 
+static void test_ldap_global_opts(abts_case *tc, void *data)
+{
+    apu_err_t result;
+    apr_ldap_opt_t opt;
+    apr_status_t status;
+
+    status = apr_ldap_get_option_ex(NULL, APR_LDAP_OPT_API_INFO, &opt, &result);
+
+    ABTS_TRUE(tc, status == APR_SUCCESS);
+    ABTS_TRUE(tc, result.rc == 0);
+    ABTS_TRUE(tc, opt.info.vendor_name != NULL);
+
+    opt.ldfi.name = "THREAD_SAFE";
+    status = apr_ldap_get_option_ex(NULL, APR_LDAP_OPT_API_FEATURE_INFO, &opt, &result);
+    /* feature may exist, or may not */
+    ABTS_TRUE(tc, status == APR_SUCCESS || status == APR_EINVAL);
+
+}
+
+static void test_ldap_opts(abts_case *tc, void *data)
+{
+    apr_pool_t *pool;
+    apr_ldap_t *ldap;
+    apu_err_t result;
+    const char *url;
+
+    apr_pool_create(&pool, p);
+
+    url = apr_psprintf(pool, "ldap://%s:%d", "localhost", LDAP_PORT);
+
+    apr_ldap_initialize(pool, url, &ldap, &(result));
+
+    ABTS_TRUE(tc, ldap != NULL);
+
+    if (result.rc == LDAP_SUCCESS) {
+
+        apr_status_t status;
+        apr_ldap_opt_t opt;
+
+        opt.pv = APR_LDAP_VERSION3;
+        status = apr_ldap_set_option_ex(ldap, APR_LDAP_OPT_PROTOCOL_VERSION, &opt, &result);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        status = apr_ldap_get_option_ex(ldap, APR_LDAP_OPT_PROTOCOL_VERSION, &opt, &result);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        ABTS_ASSERT(tc, "failed to set protocol version", opt.pv == APR_LDAP_VERSION3);
+
+        opt.deref = APR_LDAP_DEREF_ALWAYS;
+        status = apr_ldap_set_option_ex(ldap, APR_LDAP_OPT_DEREF, &opt, &result);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        status = apr_ldap_get_option_ex(ldap, APR_LDAP_OPT_DEREF, &opt, &result);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        ABTS_ASSERT(tc, "failed to set deref", opt.deref == APR_LDAP_DEREF_ALWAYS);
+
+        opt.refs = APR_LDAP_OPT_ON;
+        status = apr_ldap_set_option_ex(ldap, APR_LDAP_OPT_REFERRALS, &opt, &result);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        status = apr_ldap_get_option_ex(ldap, APR_LDAP_OPT_REFERRALS, &opt, &result);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        ABTS_ASSERT(tc, "failed to set referrals", opt.refs == APR_LDAP_OPT_ON);
+
+        opt.refhoplimit = 5;
+        status = apr_ldap_set_option_ex(ldap, APR_LDAP_OPT_REFHOPLIMIT, &opt, &result);
+        if (APR_ENOTIMPL != status) {
+            ABTS_TRUE(tc, status == APR_SUCCESS); 
+            status = apr_ldap_get_option_ex(ldap, APR_LDAP_OPT_REFHOPLIMIT, &opt, &result);
+            ABTS_TRUE(tc, status == APR_SUCCESS); 
+            ABTS_ASSERT(tc, "failed to set refhoplimit", opt.refhoplimit == 5);
+        }
+
+    }
+
+    apr_pool_destroy(pool);
+}
+
 static void test_ldap(abts_case *tc, void *data)
 {
-    apr_pool_t *pool = p;
-    LDAP *ldap;
-    apr_ldap_err_t *result = NULL;
+    apr_pool_t *pool;
+    apr_ldap_t *ldap;
+    apu_err_t result;
+    const char *url;
 
+    apr_pool_create(&pool, p);
 
     ABTS_ASSERT(tc, "failed to get host", ldap_host[0] != '\0');
+
+    url = apr_psprintf(pool, "ldap://%s:%d", ldap_host, LDAP_PORT);
     
-    apr_ldap_init(pool, &ldap,
-                  ldap_host, LDAP_PORT,
-                  APR_LDAP_NONE, &(result));
+    apr_ldap_initialize(pool, url, &ldap, &(result));
 
     ABTS_TRUE(tc, ldap != NULL);
-    ABTS_PTR_NOTNULL(tc, result);
 
-    if (result->rc == LDAP_SUCCESS) {
-        test_ldap_connection(tc, ldap);
+    if (result.rc == LDAP_SUCCESS) {
+        test_ldap_connection(tc, ldap, "PLAIN", bind_interact_plain);
     }
+
+    apr_pool_destroy(pool);
 }
 
 static void test_ldaps(abts_case *tc, void *data)
 {
-    apr_pool_t *pool = p;
-    LDAP *ldap;
-    apr_ldap_err_t *result = NULL;
+    apr_pool_t *pool;
+    apr_ldap_t *ldap;
+    apu_err_t result;
+    const char *url; 
 
-    apr_ldap_init(pool, &ldap,
-                  ldap_host, LDAPS_PORT,
-                  APR_LDAP_SSL, &(result));
+    apr_pool_create(&pool, p);
 
+    url = apr_psprintf(pool, "ldaps://%s:%d", ldap_host, LDAPS_PORT);
+
+    apr_ldap_initialize(pool, url, &ldap, &(result));
+
     ABTS_TRUE(tc, ldap != NULL);
-    ABTS_PTR_NOTNULL(tc, result);
 
-    if (result->rc == LDAP_SUCCESS) {
+    if (result.rc == LDAP_SUCCESS) {
         add_ldap_certs(tc);
 
-        test_ldap_connection(tc, ldap);
+        test_ldap_connection(tc, ldap, "EXTERNAL", bind_interact_external);
+        test_ldap_connection(tc, ldap, "PLAIN", bind_interact_plain);
     }
+
+    apr_pool_destroy(pool);
 }
 
 static void test_ldap_tls(abts_case *tc, void *data)
 {
-    apr_pool_t *pool = p;
-    LDAP *ldap;
-    apr_ldap_err_t *result = NULL;
+    apr_pool_t *pool;
+    apr_ldap_t *ldap;
+    apu_err_t result;
+    const char *url;
+    apr_ldap_opt_t opt;
 
-    apr_ldap_init(pool, &ldap,
-                  ldap_host, LDAP_PORT,
-                  APR_LDAP_STARTTLS, &(result));
+    apr_pool_create(&pool, p);
 
+    opt.tls = APR_LDAP_TLS_STARTTLS;
+
+    url = apr_psprintf(pool, "ldap://%s:%d", ldap_host, LDAP_PORT);
+
+    apr_ldap_initialize(pool, url, &ldap, &(result));
+
+    if (result.rc == LDAP_SUCCESS) {
+        apr_ldap_set_option_ex(ldap, APR_LDAP_OPT_TLS, &opt, &(result));
+    }
+
     ABTS_TRUE(tc, ldap != NULL);
-    ABTS_PTR_NOTNULL(tc, result);
 
-    if (result->rc == LDAP_SUCCESS) {
+    if (result.rc == LDAP_SUCCESS) {
         add_ldap_certs(tc);
 
-        test_ldap_connection(tc, ldap);
+        test_ldap_connection(tc, ldap, "EXTERNAL", bind_interact_external);
+        test_ldap_connection(tc, ldap, "PLAIN", bind_interact_plain);
     }
+
+    apr_pool_destroy(pool);
 }
 
 #endif /* APR_HAS_LDAP */
@@ -238,11 +412,17 @@
 
     apr_ldap_ssl_init(p, NULL, 0, &result);
 
+    abts_run_test(suite, test_ldap_global_opts, NULL);
+    abts_run_test(suite, test_ldap_opts, NULL);
+
     if (get_ldap_host()) {
         abts_run_test(suite, test_ldap, NULL);
         abts_run_test(suite, test_ldaps, NULL);
         abts_run_test(suite, test_ldap_tls, NULL);
     }
+
+    apr_ldap_ssl_deinit();
+
 #endif /* APR_HAS_LDAP */
 
     return suite;


Re: POC: updated LDAP support for apr-util 1.7

Posted by Graham Leggett via dev <de...@apr.apache.org>.
On 18 Apr 2023, at 13:26, Graham Leggett via dev <de...@apr.apache.org> wrote:

> The following patch adds ldapi:// (LDAP over unix domain socket) support to apr-util. It is part of a wider cleanup covering the following:
> 
> - Add apr_ldap_t to hide the native LDAP type.
> - Add apr_ldap_initialize() with URL support, allowing us to do ldapi://, including proper pool cleanups.
> - Add apr_ldap_get_option_ex() and apr_ldap_set_option_ex() that use apr_ldap_t, and support all options used by httpd.
> - Options passed to apr_ldap_get_option_ex() / apr_ldap_set_option_ex() are a strongly typed union rather than the native void pointers, the assumption being the union is extensible in future.
> 
> In theory this extends but does not break our ABI and is safe to go into apr-util 1.7, if this isn’t the case please tell me so I can fix it.

Update to the POC. In addition to the above, the following is added:

- apr_ldap_bind() that currently maps to ldap_sasl_interactive_bind <https://serverfault.com/questions/936658/ldap-sasl-interactive-bind-s-cant-contact-ldap-server-1>(), supporting async requests. Timeout is now a proper parameter so that platform specific weirdness can be handled.
- apr_ldap_result() that maps to ldap_result().
- updated test/testldap demonstrating a SASL bind.

Still to be done:

- ldap_simple_bind() support. Will be emulated inside apr_ldap_bind().

Regards,
Graham
—

Index: CHANGES
===================================================================
--- CHANGES	(revision 1909133)
+++ CHANGES	(working copy)
@@ -1,6 +1,11 @@
                                                      -*- coding: utf-8 -*-
 Changes with APR-util 1.7.0
 
+  *) apr_ldap: Add apr_ldap_t type. Add apr_ldap_initialize() with
+     URL and ldapi:// support. Add apr_ldap_get_option_ex() and
+     apr_ldap_set_option_ex() with support for the apr_ldap_t type.
+     [Graham Leggett]
+
   *) apr_crypto_openssl: Compatibility with OpenSSL 3.  [Yann Ylavic]
 
   *) configure: Fix configure for compilers which don't accept implicit
Index: aprutil.dep
===================================================================
--- aprutil.dep	(revision 1909133)
+++ aprutil.dep	(working copy)
@@ -234,6 +234,7 @@
 	".\include\apr_ldap.h"\
 	".\include\apr_ldap_init.h"\
 	".\include\apr_ldap_option.h"\
+	".\include\apr_ldap_bind.h"\
 	".\include\apr_ldap_rebind.h"\
 	".\include\apr_ldap_url.h"\
 	".\include\apu.h"\
@@ -245,6 +246,7 @@
 	".\include\apr_ldap.h"\
 	".\include\apr_ldap_init.h"\
 	".\include\apr_ldap_option.h"\
+	".\include\apr_ldap_bind.h"\
 	".\include\apr_ldap_rebind.h"\
 	".\include\apr_ldap_url.h"\
 	".\include\apu.h"\
@@ -251,10 +253,22 @@
 	".\include\private\apu_config.h"\
 	
 
+.\ldap\apr_ldap_bind.c : \
+	".\include\apr_ldap.h"\
+	".\include\apr_ldap_init.h"\
+	".\include\apr_ldap_option.h"\
+	".\include\apr_ldap_bind.h"\
+	".\include\apr_ldap_rebind.h"\
+	".\include\apr_ldap_url.h"\
+	".\include\apu.h"\
+	".\include\private\apu_config.h"\
+
+
 .\ldap\apr_ldap_rebind.c : \
 	".\include\apr_ldap.h"\
 	".\include\apr_ldap_init.h"\
 	".\include\apr_ldap_option.h"\
+	".\include\apr_ldap_bind.h"\
 	".\include\apr_ldap_rebind.h"\
 	".\include\apr_ldap_url.h"\
 	".\include\apu.h"\
@@ -265,6 +279,7 @@
 	".\include\apr_ldap.h"\
 	".\include\apr_ldap_init.h"\
 	".\include\apr_ldap_option.h"\
+	".\include\apr_ldap_bind.h"\
 	".\include\apr_ldap_rebind.h"\
 	".\include\apr_ldap_url.h"\
 	".\include\apu.h"\
@@ -277,6 +292,7 @@
 	".\include\apr_ldap.h"\
 	".\include\apr_ldap_init.h"\
 	".\include\apr_ldap_option.h"\
+	".\include\apr_ldap_bind.h"\
 	".\include\apr_ldap_rebind.h"\
 	".\include\apr_ldap_url.h"\
 	".\include\apu.h"\
Index: aprutil.dsp
===================================================================
--- aprutil.dsp	(revision 1909133)
+++ aprutil.dsp	(working copy)
@@ -313,6 +313,10 @@
 
 SOURCE=.\ldap\apr_ldap_option.c
 # End Source File
+# Begin Source File 
+
+SOURCE=.\ldap\apr_ldap_bind.c
+# End Source File 
 # Begin Source File
 
 SOURCE=.\ldap\apr_ldap_rebind.c
Index: aprutil.mak
===================================================================
--- aprutil.mak	(revision 1909133)
+++ aprutil.mak	(working copy)
@@ -86,6 +86,7 @@
 	-@erase "$(INTDIR)\apr_hooks.obj"
 	-@erase "$(INTDIR)\apr_ldap_init.obj"
 	-@erase "$(INTDIR)\apr_ldap_option.obj"
+	-@erase "$(INTDIR)\apr_ldap_bind.obj"
 	-@erase "$(INTDIR)\apr_ldap_rebind.obj"
 	-@erase "$(INTDIR)\apr_ldap_stub.obj"
 	-@erase "$(INTDIR)\apr_ldap_url.obj"
@@ -198,6 +199,7 @@
 	"$(INTDIR)\apr_hooks.obj" \
 	"$(INTDIR)\apr_ldap_init.obj" \
 	"$(INTDIR)\apr_ldap_option.obj" \
+	"$(INTDIR)\apr_ldap_bind.obj" \
 	"$(INTDIR)\apr_ldap_rebind.obj" \
 	"$(INTDIR)\apr_ldap_stub.obj" \
 	"$(INTDIR)\apr_ldap_url.obj" \
@@ -278,6 +280,7 @@
 	-@erase "$(INTDIR)\apr_hooks.obj"
 	-@erase "$(INTDIR)\apr_ldap_init.obj"
 	-@erase "$(INTDIR)\apr_ldap_option.obj"
+	-@erase "$(INTDIR)\apr_ldap_bind.obj"
 	-@erase "$(INTDIR)\apr_ldap_rebind.obj"
 	-@erase "$(INTDIR)\apr_ldap_stub.obj"
 	-@erase "$(INTDIR)\apr_ldap_url.obj"
@@ -395,6 +398,7 @@
 	"$(INTDIR)\apr_hooks.obj" \
 	"$(INTDIR)\apr_ldap_init.obj" \
 	"$(INTDIR)\apr_ldap_option.obj" \
+	"$(INTDIR)\apr_ldap_bind.obj" \
 	"$(INTDIR)\apr_ldap_rebind.obj" \
 	"$(INTDIR)\apr_ldap_stub.obj" \
 	"$(INTDIR)\apr_ldap_url.obj" \
@@ -475,6 +479,7 @@
 	-@erase "$(INTDIR)\apr_hooks.obj"
 	-@erase "$(INTDIR)\apr_ldap_init.obj"
 	-@erase "$(INTDIR)\apr_ldap_option.obj"
+	-@erase "$(INTDIR)\apr_ldap_bind.obj"
 	-@erase "$(INTDIR)\apr_ldap_rebind.obj"
 	-@erase "$(INTDIR)\apr_ldap_stub.obj"
 	-@erase "$(INTDIR)\apr_ldap_url.obj"
@@ -592,6 +597,7 @@
 	"$(INTDIR)\apr_hooks.obj" \
 	"$(INTDIR)\apr_ldap_init.obj" \
 	"$(INTDIR)\apr_ldap_option.obj" \
+	"$(INTDIR)\apr_ldap_bind.obj" \
 	"$(INTDIR)\apr_ldap_rebind.obj" \
 	"$(INTDIR)\apr_ldap_stub.obj" \
 	"$(INTDIR)\apr_ldap_url.obj" \
@@ -672,6 +678,7 @@
 	-@erase "$(INTDIR)\apr_hooks.obj"
 	-@erase "$(INTDIR)\apr_ldap_init.obj"
 	-@erase "$(INTDIR)\apr_ldap_option.obj"
+	-@erase "$(INTDIR)\apr_ldap_bind.obj"
 	-@erase "$(INTDIR)\apr_ldap_rebind.obj"
 	-@erase "$(INTDIR)\apr_ldap_stub.obj"
 	-@erase "$(INTDIR)\apr_ldap_url.obj"
@@ -789,6 +796,7 @@
 	"$(INTDIR)\apr_hooks.obj" \
 	"$(INTDIR)\apr_ldap_init.obj" \
 	"$(INTDIR)\apr_ldap_option.obj" \
+	"$(INTDIR)\apr_ldap_bind.obj" \
 	"$(INTDIR)\apr_ldap_rebind.obj" \
 	"$(INTDIR)\apr_ldap_stub.obj" \
 	"$(INTDIR)\apr_ldap_url.obj" \
@@ -1051,6 +1059,12 @@
 	$(CPP) $(CPP_PROJ) $(SOURCE)
 
 
+SOURCE=.\ldap\apr_ldap_bind.c
+
+"$(INTDIR)\apr_ldap_bind.obj" : $(SOURCE) "$(INTDIR)" ".\include\apu.h" ".\include\private\apu_config.h" ".\include\apr_ldap.h"
+        $(CPP) $(CPP_PROJ) $(SOURCE) 
+
+
 SOURCE=.\ldap\apr_ldap_rebind.c
 
 "$(INTDIR)\apr_ldap_rebind.obj" : $(SOURCE) "$(INTDIR)" ".\include\apu.h" ".\include\private\apu_config.h" ".\include\apr_ldap.h"
Index: build/apu-conf.m4
===================================================================
--- build/apu-conf.m4	(revision 1909133)
+++ build/apu-conf.m4	(working copy)
@@ -63,6 +63,8 @@
     AC_CHECK_LIB(${ldaplib}, ldap_init, 
       [
         LDADD_ldap_found="-l${ldaplib} ${extralib}"
+        AC_CHECK_LIB(${ldaplib}, ldap_initialize, apu_has_ldap_initialize="1", , ${extralib})
+        AC_CHECK_LIB(${ldaplib}, ldap_sasl_interactive_bind, apu_has_ldap_sasl_interactive_bind="1", , ${extralib})
         AC_CHECK_LIB(${ldaplib}, ldapssl_client_init, apu_has_ldapssl_client_init="1", , ${extralib})
         AC_CHECK_LIB(${ldaplib}, ldapssl_client_deinit, apu_has_ldapssl_client_deinit="1", , ${extralib})
         AC_CHECK_LIB(${ldaplib}, ldapssl_add_trusted_cert, apu_has_ldapssl_add_trusted_cert="1", , ${extralib})
@@ -84,6 +86,8 @@
 echo $ac_n "${nl}checking for ldap support..."
 
 apu_has_ldap="0";
+apu_has_ldap_initialize="0"
+apu_has_ldap_sasl_interactive_bind="0"
 apu_has_ldapssl_client_init="0"
 apu_has_ldapssl_client_deinit="0"
 apu_has_ldapssl_add_trusted_cert="0"
@@ -274,9 +278,13 @@
     LIBS="$save_libs"
 fi
 
+AC_CHECK_HEADERS([sasl.h sasl/sasl.h])
+
 AC_SUBST(ldap_h)
 AC_SUBST(lber_h)
 AC_SUBST(ldap_ssl_h)
+AC_SUBST(apu_has_ldap_initialize)
+AC_SUBST(apu_has_ldap_sasl_interactive_bind)
 AC_SUBST(apu_has_ldapssl_client_init)
 AC_SUBST(apu_has_ldapssl_client_deinit)
 AC_SUBST(apu_has_ldapssl_add_trusted_cert)
Index: build.conf
===================================================================
--- build.conf	(revision 1909133)
+++ build.conf	(working copy)
@@ -102,6 +102,7 @@
 [ldap]
 paths = ldap/apr_ldap_init.c
         ldap/apr_ldap_option.c
+        ldap/apr_ldap_bind.c
         ldap/apr_ldap_rebind.c
 target = ldap/apr_ldap.la
 
Index: include/apr_ldap.h.in
===================================================================
--- include/apr_ldap.h.in	(revision 1909133)
+++ include/apr_ldap.h.in	(working copy)
@@ -84,6 +84,8 @@
 /*
  * Detected standard functions
  */
+#define APR_HAS_LDAP_INITIALIZE @apu_has_ldap_initialize@
+#define APR_HAS_LDAP_SASL_INTERACTIVE_BIND @apu_has_ldap_sasl_interactive_bind@
 #define APR_HAS_LDAPSSL_CLIENT_INIT @apu_has_ldapssl_client_init@
 #define APR_HAS_LDAPSSL_CLIENT_DEINIT @apu_has_ldapssl_client_deinit@
 #define APR_HAS_LDAPSSL_ADD_TRUSTED_CERT @apu_has_ldapssl_add_trusted_cert@
@@ -151,6 +153,11 @@
     int rc;
 } apr_ldap_err_t;
 
+/**
+ * Opaque structure tracking the state of an LDAP connection.
+ */
+typedef struct apr_ldap_t apr_ldap_t;
+
 #ifdef __cplusplus
 }
 #endif
@@ -181,6 +188,11 @@
 #define apr_ldap_rebind_init apr__ldap_rebind_init
 #define apr_ldap_rebind_add apr__ldap_rebind_add
 #define apr_ldap_rebind_remove apr__ldap_rebind_remove
+#define apr_ldap_initialize apr__ldap_initialize
+#define apr_ldap_get_option_ex apr__ldap_get_option_ex
+#define apr_ldap_set_option_ex apr__ldap_set_option_ex
+#define apr_ldap_result apr__ldap_result
+#define apr_ldap_bind apr__ldap_bind
 
 #define APU_DECLARE_LDAP(type) type
 #else
@@ -187,9 +199,11 @@
 #define APU_DECLARE_LDAP(type) APU_DECLARE(type)
 #endif
 
+#include "apu_errno.h"
 #include "apr_ldap_url.h"
 #include "apr_ldap_init.h"
 #include "apr_ldap_option.h"
+#include "apr_ldap_bind.h"
 #include "apr_ldap_rebind.h"
 
 #endif /* APR_HAS_LDAP */
Index: include/apr_ldap.hnw
===================================================================
--- include/apr_ldap.hnw	(revision 1909133)
+++ include/apr_ldap.hnw	(working copy)
@@ -79,6 +79,8 @@
 /*
  * Detected standard functions
  */
+#define APR_HAS_LDAP_INITIALIZE 0
+#define APR_HAS_LDAP_SASL_INTERACTIVE_BIND 0
 #define APR_HAS_LDAPSSL_CLIENT_INIT 1
 #define APR_HAS_LDAPSSL_CLIENT_DEINIT 1
 #define APR_HAS_LDAPSSL_ADD_TRUSTED_CERT 1
@@ -120,6 +122,11 @@
     int rc;
 } apr_ldap_err_t;
 
+/**
+ * Opaque structure tracking the state of an LDAP connection.
+ */
+typedef struct apr_ldap_t apr_ldap_t;
+
 #ifdef __cplusplus
 }
 #endif
@@ -141,6 +148,11 @@
 #define apr_ldap_rebind_init apr__ldap_rebind_init
 #define apr_ldap_rebind_add apr__ldap_rebind_add
 #define apr_ldap_rebind_remove apr__ldap_rebind_remove
+#define apr_ldap_initialize apr__ldap_initialize
+#define apr_ldap_get_option_ex apr__ldap_get_option_ex
+#define apr_ldap_set_option_ex apr__ldap_set_option_ex
+#define apr_ldap_result apr__ldap_result
+#define apr_ldap_bind apr__ldap_bind
 
 #define APU_DECLARE_LDAP(type) type
 #else
@@ -147,9 +159,11 @@
 #define APU_DECLARE_LDAP(type) APU_DECLARE(type)
 #endif
 
+#include "apu_errno.h"
 #include "apr_ldap_url.h"
 #include "apr_ldap_init.h"
 #include "apr_ldap_option.h"
+#include "apr_ldap_bind.h"
 #include "apr_ldap_rebind.h"
 
 /** @} */
Index: include/apr_ldap.hw
===================================================================
--- include/apr_ldap.hw	(revision 1909133)
+++ include/apr_ldap.hw	(working copy)
@@ -82,6 +82,8 @@
 /*
  * Detected standard functions
  */
+#define APR_HAS_LDAP_INITIALIZE 0
+#define APR_HAS_LDAP_SASL_INTERACTIVE_BIND 0
 #define APR_HAS_LDAPSSL_CLIENT_INIT 0
 #define APR_HAS_LDAPSSL_CLIENT_DEINIT 0
 #define APR_HAS_LDAPSSL_ADD_TRUSTED_CERT 0
@@ -151,6 +153,11 @@
     int rc;
 } apr_ldap_err_t;
 
+/**
+ * Opaque structure tracking the state of an LDAP connection.
+ */
+typedef struct apr_ldap_t apr_ldap_t;
+
 #ifdef __cplusplus
 }
 #endif
@@ -181,6 +188,11 @@
 #define apr_ldap_rebind_init apr__ldap_rebind_init
 #define apr_ldap_rebind_add apr__ldap_rebind_add
 #define apr_ldap_rebind_remove apr__ldap_rebind_remove
+#define apr_ldap_initialize apr__ldap_initialize
+#define apr_ldap_get_option_ex apr__ldap_get_option_ex
+#define apr_ldap_set_option_ex apr__ldap_set_option_ex
+#define apr_ldap_result apr__ldap_result
+#define apr_ldap_bind apr__ldap_bind
 
 #define APU_DECLARE_LDAP(type) type
 #else
@@ -187,9 +199,11 @@
 #define APU_DECLARE_LDAP(type) APU_DECLARE(type)
 #endif
 
+#include "apu_errno.h"
 #include "apr_ldap_url.h"
 #include "apr_ldap_init.h"
 #include "apr_ldap_option.h"
+#include "apr_ldap_bind.h"
 #include "apr_ldap_rebind.h"
 
 /** @} */
Index: include/apr_ldap.hwc
===================================================================
--- include/apr_ldap.hwc	(revision 1909133)
+++ include/apr_ldap.hwc	(working copy)
@@ -82,6 +82,8 @@
 /*
  * Detected standard functions
  */
+#define APR_HAS_LDAP_INITIALIZE 0
+#define APR_HAS_LDAP_SASL_INTERACTIVE_BIND 0
 #define APR_HAS_LDAPSSL_CLIENT_INIT 0
 #define APR_HAS_LDAPSSL_CLIENT_DEINIT 0
 #define APR_HAS_LDAPSSL_ADD_TRUSTED_CERT 0
@@ -181,6 +183,11 @@
 #define apr_ldap_rebind_init apr__ldap_rebind_init
 #define apr_ldap_rebind_add apr__ldap_rebind_add
 #define apr_ldap_rebind_remove apr__ldap_rebind_remove
+#define apr_ldap_initialize apr__ldap_initialize
+#define apr_ldap_get_option_ex apr__ldap_get_option_ex
+#define apr_ldap_set_option_ex apr__ldap_set_option_ex
+#define apr_ldap_result apr__ldap_result
+#define apr_ldap_bind apr__ldap_bind
 
 #define APU_DECLARE_LDAP(type) type
 #else
@@ -187,9 +194,11 @@
 #define APU_DECLARE_LDAP(type) APU_DECLARE(type)
 #endif
 
+#include "apu_errno.h"
 #include "apr_ldap_url.h"
 #include "apr_ldap_init.h"
 #include "apr_ldap_option.h"
+#include "apr_ldap_bind.h"
 #include "apr_ldap_rebind.h"
 
 /** @} */
Index: include/apr_ldap_bind.h
===================================================================
--- include/apr_ldap_bind.h	(nonexistent)
+++ include/apr_ldap_bind.h	(working copy)
@@ -0,0 +1,122 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * The APR LDAP bind functions provide the ability to bind to a directory
+ * in a generic way.
+ *
+ * First we try binding interactively using SASL, falling back to SASL bind,
+ * then simple bind, as supported on the platform.
+ *
+ * @file apr_ldap_bind.h
+ * @brief Apache LDAP library
+ */
+
+#ifndef APU_LDAP_BIND_H
+#define APU_LDAP_BIND_H
+
+/**
+ * @addtogroup APR_Util_LDAP
+ * @{
+ **/
+
+#if defined(DOXYGEN)
+#include "apr_ldap.h"
+#endif
+
+/*
+ * Handle the case when LDAP is enabled
+ */
+#if APR_HAS_LDAP
+
+/** LDAP interaction identifiers */
+typedef enum {
+    APR_LDAP_INTERACT_GETREALM = 0x4008,          /**< SASL realm for the authentication attempt */
+    APR_LDAP_INTERACT_AUTHNAME = 0x4002,          /**< SASL username to authenticate */
+    APR_LDAP_INTERACT_USER = 0x4001,              /**< SASL username to use for proxy authorization */
+    APR_LDAP_INTERACT_PASS = 0x4004,              /**< SASL password for the provided username */
+    APR_LDAP_INTERACT_NOECHOPROMPT = 0x4006,      /**< SASL generic prompt for input with input echoing disabled */
+    APR_LDAP_INTERACT_ECHOPROMPT = 0x4005,        /**< SASL generic prompt for input with input echoing enabled */
+} apr_ldap_bind_interact_e;
+
+typedef struct apr_ldap_bind_interact_t {
+    apr_ldap_bind_interact_e id; /* same as client/user callback ID */
+    const char *challenge;       /* presented to user (e.g. OTP challenge) */
+    const char *prompt;          /* presented to user (e.g. "Username: ") */
+    const char *defresult;       /* default result string */
+    const void *result;          /* set to point to result */
+    unsigned len;                /* set to length of result */
+} apr_ldap_bind_interact_t;
+
+typedef int (apr_ldap_interact_proc)(
+        apr_ldap_t *ld, unsigned int flags, apr_ldap_bind_interact_t *interact, void *ctx);
+
+typedef struct apr_ldap_message_t apr_ldap_message_t;
+
+
+/**
+ * APR result function.
+ *
+ * This function returns the result of a previous request, ready for further
+ * processing.
+ *
+ * @return APR_INCOMPLETE means that at least one result has been
+ * returned, and needs to be passed back to the function that
+ * generated this message for further processing.
+ * APR_SUCCESS means that no further messages are available, and
+ * results are complete. Other error codes indicate that the message
+ * read was not successful.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_result(apr_ldap_message_t *msg,
+                                               apr_interval_time_t timeout,
+                                               apu_err_t *result)
+                                               __attribute__((nonnull(1,3)));
+
+/**
+ * APR LDAP bind function
+ *
+ * This function binds a previously initialised LDAP connection
+ * to the directory.
+ *
+ * Binds are attempted as SASL interactive, falling back to a
+ * standard SASL bind, falling back to a simple bind, depending
+ * on the capabilities of the platform.
+ *
+ * Binds are attempted asynchronously. If APR_INCOMPLETE is returned,
+ * this function must be called again.
+ *
+ * @return APR_INCOMPLETE means that processing has occurred, and
+ * the message in reply needs to be fetched using apr_ldap_return().
+ * APR_SUCCESS means that the processing is complete, and the bind
+ * has been successful. Other error codes indicate that the bind
+ * was not successful.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_bind(apr_ldap_t *ldap,
+                                             const char *dn,
+                                             const char *mech,
+                                             apr_ldap_interact_proc *interact,
+                                             void *ctx,
+                                             apr_interval_time_t timeout,
+                                             apr_ldap_message_t **msg,
+                                             apu_err_t *result)
+                                             __attribute__((nonnull(1,4,7,8)));
+
+#endif /* APR_HAS_LDAP */
+
+/** @} */
+
+#endif /* APU_LDAP_BIND_H */
+
Index: include/apr_ldap_init.h
===================================================================
--- include/apr_ldap_init.h	(revision 1909133)
+++ include/apr_ldap_init.h	(working copy)
@@ -135,6 +135,7 @@
  * @param portno The port to connect to
  * @param secure The security mode to set
  * @param result_err The returned result
+ * @deprecated Replaced by apr_ldap_initialize()
  */
 APU_DECLARE_LDAP(int) apr_ldap_init(apr_pool_t *pool,
                                     LDAP **ldap,
@@ -154,6 +155,32 @@
 APU_DECLARE_LDAP(int) apr_ldap_info(apr_pool_t *pool,
                                     apr_ldap_err_t **result_err);
 
+/**
+ * APR LDAP initialise function
+ *
+ * This function is responsible for initialising an LDAP
+ * connection in a toolkit independant way. It does the
+ * job of ldap_initialize() from the C api.
+ *
+ * It handles the SSL case, the non-SSL case, and the IPC
+ * case, and attempts to hide the complexity setup from the
+ * user.
+ *
+ * A cleanup is registered in the pool.
+ *
+ * @param pool The pool to use
+ * @param uri The URI of the LDAP server
+ * @param ldap The ldap context returned
+ * @param result_err On error, error details are written to the
+ *        structure.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_initialize(apr_pool_t *pool,
+                                                   const char *uri,
+                                                   apr_ldap_t **ldap,
+                                                   apu_err_t *result)
+                                                   __attribute__((nonnull(1, 2, 3, 4)));
+
+
 #ifdef __cplusplus
 }
 #endif
Index: include/apr_ldap_option.h
===================================================================
--- include/apr_ldap_option.h	(revision 1909133)
+++ include/apr_ldap_option.h	(working copy)
@@ -27,6 +27,8 @@
  */
 
 #include "apr_ldap.h"
+#include "apr_tables.h"
+#include "apr_network_io.h"
 
 #if APR_HAS_LDAP
 
@@ -34,6 +36,43 @@
 extern "C" {
 #endif /* __cplusplus */
 
+
+/** LDAP API information */
+typedef struct apr_ldapi_apiinfo_t {
+    int api_version;                /* revision of API supported */
+    int protocol_version;           /* highest LDAP version supported */
+    const char **extensions;        /* names of API extensions */
+    const char *vendor_name;        /* name of supplier */
+    int vendor_version;             /* supplier-specific version * 100 */
+} apr_ldapi_apiinfo_t;
+
+typedef struct apr_ldap_apifeature_info_t {
+    const char *name;               /* LDAP_API_FEATURE_* (less prefix) */
+    int version;                    /* value of LDAP_API_FEATURE_... */
+} apr_ldap_apifeature_info_t;
+
+/** LDAP Protocol Versions */
+typedef enum {
+    APR_LDAP_VERSION1 = 1,          /**< LDAP version 1 */
+    APR_LDAP_VERSION2 = 2,          /**< LDAP version 2 */
+    APR_LDAP_VERSION3 = 3           /**< LDAP version 3 */
+} apr_ldap_protocol_version_e;
+
+/** LDAP deref settings */
+typedef enum {
+    APR_LDAP_DEREF_NEVER = 0,       /**< Aliases should never be dereferenced */
+    APR_LDAP_DEREF_SEARCHING = 1,   /**< Aliases should be dereferenced during the search, but not when locating the base object of the search. */
+    APR_LDAP_DEREF_FINDING = 2,     /**< Aliases should be dereferenced when locating the base object, but not during the search. */
+    APR_LDAP_DEREF_ALWAYS = 3       /**< Aliases should always be dereferenced */
+} apr_ldap_deref_e;
+
+/** LDAP options on or off */
+typedef enum {
+    APR_LDAP_OPT_OFF = 0,           /**< Option set off */
+    APR_LDAP_OPT_ON = 1             /**< Option set on */
+} apr_ldap_switch_e;
+
+
 /*
  * The following defines handle the different TLS certificate
  * options available. If these options are missing, APR will try and
@@ -65,11 +104,36 @@
  * chase before giving up on the search.
  */
 #define APR_LDAP_OPT_REFHOPLIMIT 0x6ffb
-
 /**
- * Structures for the apr_set_option() cases
+ * Get the native LDAP handle.
  */
+#define APR_LDAP_OPT_HANDLE 0x6ffa
+/**
+ * Get/Set the LDAP protocol version.
+ */
+#define APR_LDAP_OPT_PROTOCOL_VERSION 0x6ff9
+/**
+ * Get the LDAP API info.
+ */
+#define APR_LDAP_OPT_API_INFO 0x6ff8
+/**
+ * Get the LDAP API feature info.
+ */
+#define APR_LDAP_OPT_API_FEATURE_INFO 0x6ff7
+/**
+ * Get the dereference setting.
+ */
+#define APR_LDAP_OPT_DEREF 0x6ff6
+/**
+ * Get the most recent result code.
+ */
+#define APR_LDAP_OPT_RESULT_CODE 0x6ff5
+/**
+ * Get the underlying socket.
+ */
+#define APR_LDAP_OPT_DESC 0x6ff4
 
+
 /**
  * APR_LDAP_OPT_TLS_CERT
  *
@@ -202,7 +266,47 @@
 /** end TLS encryption (STOPTLS) */
 #define APR_LDAP_STOPTLS 3
 
+
+/** LDAP TLS options */
+typedef enum {
+    APR_LDAP_TLS_NONE = APR_LDAP_NONE,          /**< No encryption */
+    APR_LDAP_TLS_SSL = APR_LDAP_SSL,            /**< SSL encryption (ldaps://) */
+    APR_LDAP_TLS_STARTTLS = APR_LDAP_STARTTLS,  /**< TLS encryption (STARTTLS) */
+    APR_LDAP_TLS_STOPTLS = APR_LDAP_STOPTLS     /**< end TLS encryption (STOPTLS) */
+} apr_ldap_tls_e;
+
+
+/** LDAP TLS verify options */
+typedef enum {
+    APR_LDAP_VERIFY_OFF = 0,          /**< No verification */
+    APR_LDAP_VERIFY_ON = 1            /**< TLS verification */
+} apr_ldap_verify_e;
+
+
+/** @see apr_ldap_opt_t */
+typedef union apr_ldap_opt_t apr_ldap_opt_t;
 /**
+ * A union of all option structures so we know what
+ * the max size is.
+ */
+union apr_ldap_opt_t {
+    void *handle;                      /** LDAP native handle */
+    void *opt;                         /** LDAP native option */
+    apr_socket_t *socket;              /** LDAP native socket */
+    apr_ldapi_apiinfo_t info;          /** LDAP API information */
+    apr_ldap_apifeature_info_t ldfi;   /** LDAP API feature information */
+    apr_ldap_protocol_version_e pv;    /** Protocol version */
+    apr_array_header_t *certs;         /** TLS certificates */
+    apr_ldap_tls_e tls;                /** TLS on/off/starttls */
+    apr_ldap_verify_e verify;          /** TLS verification */
+    apr_ldap_deref_e deref;            /** Alias dereference */
+    apr_ldap_switch_e refs;            /** Referrals chased */
+    int refhoplimit;                   /** Referral hop limit */
+    int result;                        /** Result code */
+};
+
+
+/**
  * APR LDAP get option function
  *
  * This function gets option values from a given LDAP session if
@@ -213,6 +317,7 @@
  * @param outvalue The value returned (if any)
  * @param result_err The apr_ldap_err_t structure contained detailed results
  *        of the operation.
+ * @deprecated Replaced by apr_ldap_get_option_ex()
  */
 APU_DECLARE_LDAP(int) apr_ldap_get_option(apr_pool_t *pool,
                                           LDAP *ldap,
@@ -235,6 +340,7 @@
  * @param invalue The value to set
  * @param result_err The apr_ldap_err_t structure contained detailed results
  *        of the operation.
+ * @deprecated Replaced by apr_ldap_set_option_ex()
  */
 APU_DECLARE_LDAP(int) apr_ldap_set_option(apr_pool_t *pool,
                                           LDAP *ldap,
@@ -242,6 +348,45 @@
                                           const void *invalue,
                                           apr_ldap_err_t **result_err);
 
+/**
+ * APR LDAP get option function
+ *
+ * This function gets option values from a given LDAP session if
+ * one was specified. It maps to the native ldap_get_option() function.
+ * @param ldap The LDAP handle
+ * @param option The LDAP_OPT_* option to return
+ * @param outvalue The value returned (if any)
+ * @param result_err On error, error details are written to the
+ *        structure.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_get_option_ex(apr_ldap_t *ldap,
+                                                      int option,
+                                                      apr_ldap_opt_t *outvalue,
+                                                      apu_err_t *result_err)
+                                                      __attribute__((nonnull(3, 4)));
+
+/**
+ * APR LDAP set option function
+ *
+ * This function sets option values to a given LDAP session if
+ * one was specified. It maps to the native ldap_set_option() function.
+ *
+ * Where an option is not supported by an LDAP toolkit, this function
+ * will try and apply legacy functions to achieve the same effect,
+ * depending on the platform.
+ * @param ldap The LDAP handle
+ * @param option The LDAP_OPT_* option to set
+ * @param invalue The value to set
+ * @param result_err On error, error details are written to the
+ *        structure.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_set_option_ex(apr_ldap_t *ldap,
+                                                      int option,
+                                                      const apr_ldap_opt_t *invalue,
+                                                      apu_err_t *result_err)
+                                                      __attribute__((nonnull(4)));
+
+
 #ifdef __cplusplus
 }
 #endif
Index: include/apu_errno.h
===================================================================
--- include/apu_errno.h	(revision 1909133)
+++ include/apu_errno.h	(working copy)
@@ -54,6 +54,10 @@
  * APR_EINITENGINE    The engine could not be initialised
  * APR_EREINIT        Underlying crypto has already been initialised
  * APR_ENOVERIFY      The signature verification failed
+ * APR_PROXY_AUTH     Proxy authorization has failed
+ * APR_INAPPROPRIATE_AUTH  Authentication not appropriate for this entry
+ * APR_INVALID_CREDENTIALS Invalid credentials were presented
+ * APR_INSUFFICIENT_ACCESS The user has insufficient access
  * </PRE>
  *
  * <PRE>
@@ -88,6 +92,18 @@
 #define APR_EREINIT          (APR_UTIL_START_STATUS + 12)
 /** @see APR_STATUS_IS_ENOVERIFY */
 #define APR_ENOVERIFY        (APR_UTIL_START_STATUS + 13)
+/** @see APR_STATUS_IS_EDOWN */
+#define APR_EDOWN                      (APR_UTIL_START_STATUS + 101)
+/** @see APR_STATUS_IS_AUTH_UNKNOWN */
+#define APR_AUTH_UNKNOWN               (APR_UTIL_START_STATUS + 102)
+/** @see APR_STATUS_IS_PROXY_AUTH */
+#define APR_PROXY_AUTH                 (APR_UTIL_START_STATUS + 103)
+/** @see APR_STATUS_IS_INAPPROPRIATE_AUTH */
+#define APR_INAPPROPRIATE_AUTH         (APR_UTIL_START_STATUS + 104)
+/** @see APR_STATUS_IS_INVALID_CREDENTIALS */
+#define APR_INVALID_CREDENTIALS        (APR_UTIL_START_STATUS + 105)
+/** @see APR_STATUS_IS_INSUFFICIENT_ACCESS */
+#define APR_INSUFFICIENT_ACCESS        (APR_UTIL_START_STATUS + 106)
 /** @} */
 
 /**
@@ -160,8 +176,34 @@
  * The signature verification failed
  */
 #define APR_STATUS_IS_ENOVERIFY(s)        ((s) == APR_ENOVERIFY)
+/**
+ * The server is down
+ */
+#define APR_STATUS_IS_EDOWN(s)        ((s) == APR_EDOWN)
+/**
+ * Authentication mechanism not supoorted by this server
+ */
+#define APR_STATUS_IS_AUTH_UNKNOWN(s)        ((s) == APR_AUTH_UNKNOWN)
+/**
+ * Proxy authorization has failed
+ */
+#define APR_STATUS_IS_PROXY_AUTH(s)        ((s) == APR_PROXY_AUTH)
+/** 
+ * Inappropriate authentication was specified (e.g., simple auth
+ * was specified but the entry does not have a userPassword attribute).
+ */
+#define APR_STATUS_IS_INAPPROPRIATE_AUTH(s)        ((s) == APR_INAPPROPRIATE_AUTH)
+/** 
+ * Invalid credentials were presented (e.g., the wrong password).
+ */
+#define APR_STATUS_IS_INVALID_CREDENTIALS(s)        ((s) == APR_INVALID_CREDENTIALS)
+/** 
+ * The user has insufficient access to perform the operation.
+ */
+#define APR_STATUS_IS_INSUFFICIENT_ACCESS(s)        ((s) == APR_INSUFFICIENT_ACCESS)
 /** @} */
 
+
 /**
  * This structure allows the underlying API error codes to be returned
  * along with plain text error messages that explain to us mere mortals
Index: include/private/apr_ldap_private.h
===================================================================
--- include/private/apr_ldap_private.h	(nonexistent)
+++ include/private/apr_ldap_private.h	(working copy)
@@ -0,0 +1,93 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr.h"
+#include "apu.h"
+#include "apr_network_io.h"
+
+#ifndef APR_LDAP_PRIVATE_H
+#define APR_LDAP_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if APR_HAS_LDAP
+
+typedef struct apr_ldap_t {
+    apr_pool_t *pool;
+    LDAP *ld;
+    apr_socket_t *socket;
+} apr_ldap_t;
+
+typedef struct apr_ldap_message_t {
+    apr_ldap_t *ld;
+    const char *rmech;
+    LDAPMessage *message;
+    int msgid;
+} apr_ldap_message_t;
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_status(int rc, apr_status_t status);
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_message_clear(apr_ldap_message_t *msg);
+
+APU_DECLARE_LDAP(apr_ldap_message_t *) apr_ldap_message_make(apr_ldap_t *ldap);
+
+#if APU_DSO_BUILD
+
+/* For LDAP internal builds, wrap our LDAP namespace */
+
+struct apr__ldap_dso_fntable {
+    int (*info)(apr_pool_t *pool, apr_ldap_err_t **result_err);
+    int (*init)(apr_pool_t *pool, LDAP **ldap, const char *hostname,
+                int portno, int secure, apr_ldap_err_t **result_err);
+    int (*ssl_init)(apr_pool_t *pool, const char *cert_auth_file,
+                    int cert_file_type, apr_ldap_err_t **result_err);
+    int (*ssl_deinit)(void);
+    int (*get_option)(apr_pool_t *pool, LDAP *ldap, int option,
+                      void *outvalue, apr_ldap_err_t **result_err);
+    int (*set_option)(apr_pool_t *pool, LDAP *ldap, int option,
+                      const void *invalue, apr_ldap_err_t **result_err);
+    apr_status_t (*rebind_init)(apr_pool_t *pool);
+    apr_status_t (*rebind_add)(apr_pool_t *pool, LDAP *ld,
+                               const char *bindDN, const char *bindPW);
+    apr_status_t (*rebind_remove)(LDAP *ld);
+    apr_status_t (*initialize)(apr_pool_t *pool, const char *uri,
+                               apr_ldap_t **ldap,
+                               apu_err_t *result_err);
+    apr_status_t (*get_option_ex)(apr_ldap_t *ldap, int option,
+                      apr_ldap_opt_t *outvalue, apu_err_t *result_err);
+    apr_status_t (*set_option_ex)(apr_ldap_t *ldap, int option,
+                      const apr_ldap_opt_t *invalue, apu_err_t *result_err);
+    apr_status_t (*result)(apr_ldap_message_t *msg, apr_interval_time_t timeout,
+                           apu_err_t *result);
+    apr_status_t (*bind)(apr_ldap_t *ldap, const char *dn, const char *mech,
+                         apr_ldap_interact_proc *interact, void *ctx,
+                         apr_interval_time_t timeout,
+                         apr_ldap_message_t **msg, apu_err_t *result);
+};
+
+#endif /* APU_DSO_BUILD */
+
+#endif /* APR_HAS_LDAP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APR_LDAP_PRIVATE_H */
+
+
Index: include/private/apu_internal.h
===================================================================
--- include/private/apu_internal.h	(revision 1909133)
+++ include/private/apu_internal.h	(working copy)
@@ -40,29 +40,6 @@
 apr_status_t apu_dso_load(apr_dso_handle_t **dso, apr_dso_handle_sym_t *dsoptr, const char *module,
                           const char *modsym, apr_pool_t *pool);
 
-#if APR_HAS_LDAP
-
-/* For LDAP internal builds, wrap our LDAP namespace */
-
-struct apr__ldap_dso_fntable {
-    int (*info)(apr_pool_t *pool, apr_ldap_err_t **result_err);
-    int (*init)(apr_pool_t *pool, LDAP **ldap, const char *hostname,
-                int portno, int secure, apr_ldap_err_t **result_err);
-    int (*ssl_init)(apr_pool_t *pool, const char *cert_auth_file,
-                    int cert_file_type, apr_ldap_err_t **result_err);
-    int (*ssl_deinit)(void);
-    int (*get_option)(apr_pool_t *pool, LDAP *ldap, int option,
-                      void *outvalue, apr_ldap_err_t **result_err);
-    int (*set_option)(apr_pool_t *pool, LDAP *ldap, int option,
-                      const void *invalue, apr_ldap_err_t **result_err);
-    apr_status_t (*rebind_init)(apr_pool_t *pool);
-    apr_status_t (*rebind_add)(apr_pool_t *pool, LDAP *ld,
-                               const char *bindDN, const char *bindPW);
-    apr_status_t (*rebind_remove)(LDAP *ld);
-};
-
-#endif /* APR_HAS_LDAP */
-
 #ifdef __cplusplus
 }
 #endif
Index: ldap/NWGNUmakefile
===================================================================
--- ldap/NWGNUmakefile	(revision 1909133)
+++ ldap/NWGNUmakefile	(working copy)
@@ -232,6 +232,7 @@
 	$(OBJDIR)/apr_ldap_init.o \
 	$(OBJDIR)/apr_ldap_option.o \
 	$(OBJDIR)/apr_ldap_url.o \
+	$(OBJDIR)/apr_ldap_bind.o \
 	$(OBJDIR)/apr_ldap_rebind.o \
 	$(OBJDIR)/apr_ldap_stub.o \
 	$(EOLIST)
Index: ldap/apr_ldap.dsp
===================================================================
--- ldap/apr_ldap.dsp	(revision 1909133)
+++ ldap/apr_ldap.dsp	(working copy)
@@ -180,6 +180,10 @@
 # End Source File 
 # Begin Source File
 
+SOURCE=.\apr_ldap_bind.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\apr_ldap_rebind.c
 # End Source File
 # End Group
@@ -200,6 +204,10 @@
 # End Source File
 # Begin Source File
 
+SOURCE=..\include\apr_ldap_bind.h
+# End Source File
+# Begin Source File
+
 SOURCE=..\include\apr_ldap_rebind.h
 # End Source File
 # Begin Source File
Index: ldap/apr_ldap.mak
===================================================================
--- ldap/apr_ldap.mak	(revision 1909133)
+++ ldap/apr_ldap.mak	(working copy)
@@ -54,6 +54,7 @@
 	-@erase "$(INTDIR)\apr_ldap-1.res"
 	-@erase "$(INTDIR)\apr_ldap_init.obj"
 	-@erase "$(INTDIR)\apr_ldap_option.obj"
+	-@erase "$(INTDIR)\apr_ldap_bind.obj"
 	-@erase "$(INTDIR)\apr_ldap_rebind.obj"
 	-@erase "$(INTDIR)\apr_ldap_src.idb"
 	-@erase "$(INTDIR)\apr_ldap_src.pdb"
@@ -111,6 +112,7 @@
 LINK32_OBJS= \
 	"$(INTDIR)\apr_ldap_init.obj" \
 	"$(INTDIR)\apr_ldap_option.obj" \
+	"$(INTDIR)\apr_ldap_bind.obj" \
 	"$(INTDIR)\apr_ldap_rebind.obj" \
 	"$(INTDIR)\apr_ldap-1.res" \
 	"..\..\apr\Release\libapr-1.lib" \
@@ -161,6 +163,7 @@
 	-@erase "$(INTDIR)\apr_ldap-1.res"
 	-@erase "$(INTDIR)\apr_ldap_init.obj"
 	-@erase "$(INTDIR)\apr_ldap_option.obj"
+	-@erase "$(INTDIR)\apr_ldap_bind.obj"
 	-@erase "$(INTDIR)\apr_ldap_rebind.obj"
 	-@erase "$(INTDIR)\apr_ldap_src.idb"
 	-@erase "$(INTDIR)\apr_ldap_src.pdb"
@@ -218,6 +221,7 @@
 LINK32_OBJS= \
 	"$(INTDIR)\apr_ldap_init.obj" \
 	"$(INTDIR)\apr_ldap_option.obj" \
+	"$(INTDIR)\apr_ldap_bind.obj" \
 	"$(INTDIR)\apr_ldap_rebind.obj" \
 	"$(INTDIR)\apr_ldap-1.res" \
 	"..\..\apr\Debug\libapr-1.lib" \
@@ -268,6 +272,7 @@
 	-@erase "$(INTDIR)\apr_ldap-1.res"
 	-@erase "$(INTDIR)\apr_ldap_init.obj"
 	-@erase "$(INTDIR)\apr_ldap_option.obj"
+	-@erase "$(INTDIR)\apr_ldap_bind.obj"
 	-@erase "$(INTDIR)\apr_ldap_rebind.obj"
 	-@erase "$(INTDIR)\apr_ldap_src.idb"
 	-@erase "$(INTDIR)\apr_ldap_src.pdb"
@@ -325,6 +330,7 @@
 LINK32_OBJS= \
 	"$(INTDIR)\apr_ldap_init.obj" \
 	"$(INTDIR)\apr_ldap_option.obj" \
+	"$(INTDIR)\apr_ldap_bind.obj" \
 	"$(INTDIR)\apr_ldap_rebind.obj" \
 	"$(INTDIR)\apr_ldap-1.res" \
 	"..\..\apr\x64\Release\libapr-1.lib" \
@@ -375,6 +381,7 @@
 	-@erase "$(INTDIR)\apr_ldap-1.res"
 	-@erase "$(INTDIR)\apr_ldap_init.obj"
 	-@erase "$(INTDIR)\apr_ldap_option.obj"
+	-@erase "$(INTDIR)\apr_ldap_bind.obj"
 	-@erase "$(INTDIR)\apr_ldap_rebind.obj"
 	-@erase "$(INTDIR)\apr_ldap_src.idb"
 	-@erase "$(INTDIR)\apr_ldap_src.pdb"
@@ -432,6 +439,7 @@
 LINK32_OBJS= \
 	"$(INTDIR)\apr_ldap_init.obj" \
 	"$(INTDIR)\apr_ldap_option.obj" \
+	"$(INTDIR)\apr_ldap_bind.obj" \
 	"$(INTDIR)\apr_ldap_rebind.obj" \
 	"$(INTDIR)\apr_ldap-1.res" \
 	"..\..\apr\x64\Debug\libapr-1.lib" \
@@ -478,6 +486,11 @@
 "$(INTDIR)\apr_ldap_option.obj" : $(SOURCE) "$(INTDIR)"
 
 
+SOURCE=.\apr_ldap_bind.c
+
+"$(INTDIR)\apr_ldap_bind.obj" : $(SOURCE) "$(INTDIR)"
+
+
 SOURCE=.\apr_ldap_rebind.c
 
 "$(INTDIR)\apr_ldap_rebind.obj" : $(SOURCE) "$(INTDIR)"
Index: ldap/apr_ldap_bind.c
===================================================================
--- ldap/apr_ldap_bind.c	(nonexistent)
+++ ldap/apr_ldap_bind.c	(working copy)
@@ -0,0 +1,284 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * apr_ldap_bind.c: LDAP v2/v3 common initialise
+ *
+ */
+
+#include "apr.h"
+#include "apu.h"
+#include "apu_config.h"
+
+#if APU_DSO_BUILD
+#define APU_DSO_LDAP_BUILD
+#endif
+
+#include "apr_ldap.h"
+#include "apr_ldap_private.h"
+#include "apu_internal.h"
+#include "apr_errno.h"
+#include "apr_pools.h"
+#include "apr_strings.h"
+
+#ifdef HAVE_SASL_SASL_H
+#include <sasl/sasl.h>
+#else
+#ifdef HAVE_SASL_H
+#include <sasl.h>
+#endif
+#endif
+
+#if APR_HAS_LDAP
+
+typedef struct apr_ldap_bind_ctx_t {
+    apr_ldap_t *ld;
+    apr_ldap_interact_proc *interact;
+    void *ctx;
+} apr_ldap_bind_ctx_t;
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_message_clear(apr_ldap_message_t *msg)
+{
+    if (msg) {
+        msg->rmech = NULL;
+        if (msg->message) {
+            ldap_msgfree(msg->message);
+            msg->message = NULL;
+        }
+        msg->msgid = 0;
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t message_cleanup(void *dptr)
+{
+    return apr_ldap_message_clear(dptr);
+}
+
+APU_DECLARE_LDAP(apr_ldap_message_t *) apr_ldap_message_make(apr_ldap_t *ldap)
+{
+    apr_ldap_message_t *msg = apr_pcalloc(ldap->pool, sizeof(apr_ldap_message_t));
+
+    if (msg) {
+        msg->ld = ldap;
+        apr_pool_cleanup_register(ldap->pool, msg, message_cleanup,
+                                  apr_pool_cleanup_null);
+    }
+
+    return msg;
+}
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_result(apr_ldap_message_t *msg,
+                                               apr_interval_time_t timeout,
+                                               apu_err_t *result)
+{
+    int res;
+
+    LDAP *ld = msg->ld->ld;
+
+    struct timeval tv, *tvptr;
+
+    if (timeout < 0) {
+        tvptr = NULL;
+    }
+    else {
+        tv.tv_sec = (long) apr_time_sec(timeout);
+        tv.tv_usec = (long) apr_time_usec(timeout);
+        tvptr = &tv;
+    }
+
+    if (msg->message) {
+        ldap_msgfree( msg->message );
+    }
+
+    res = ldap_result( ld, msg->msgid, LDAP_MSG_RECEIVED, tvptr, &msg->message );
+    if ( res == -1 ) {
+        result->reason = "LDAP: ldap_result() retrieval failed";
+
+        /* -1 is LDAP_SERVER_DOWN in openldap, use something else */
+#ifdef LDAP_OPT_ERROR_NUMBER
+        ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &result->rc);
+#endif 
+#ifdef LDAP_OPT_RESULT_CODE
+        ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &result->rc);
+#endif 
+        result->msg = ldap_err2string(result->rc);
+        return apr_ldap_status(result->rc, APR_EGENERAL);
+    }
+    else if (res == 0) {
+        result->reason = "LDAP: ldap_result() timed out";
+        result->rc = LDAP_TIMEOUT;
+        result->msg = ldap_err2string(result->rc);
+        return APR_ETIMEDOUT;
+    }
+
+    res = ldap_count_messages(ld, msg->message);
+    if ( res == -1 ) {
+        result->reason = "LDAP: ldap_count_entries() retrieval failed";
+
+        /* -1 is LDAP_SERVER_DOWN in openldap, use something else */
+#ifdef LDAP_OPT_ERROR_NUMBER
+        ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &result->rc);
+#endif
+#ifdef LDAP_OPT_RESULT_CODE
+        ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &result->rc);
+#endif
+        result->msg = ldap_err2string(result->rc);
+        return apr_ldap_status(result->rc, APR_EGENERAL);
+    }
+    else if (res == 0) {
+        return APR_SUCCESS;
+    }
+    else {
+        return APR_INCOMPLETE;
+    }
+
+}
+
+
+static int bind_sasl_interact(LDAP *ld, unsigned flags, void *ctx, void *in )
+{
+    apr_ldap_bind_ctx_t *payload = ctx;
+    sasl_interact_t *sasl_interact = in;
+
+    if (!ld) {
+        return LDAP_PARAM_ERROR;
+    }
+
+    while( sasl_interact->id != SASL_CB_LIST_END ) {
+
+        apr_ldap_bind_interact_t interaction;
+
+        interaction.id = sasl_interact->id;
+        interaction.challenge = sasl_interact->challenge;
+        interaction.prompt = sasl_interact->prompt;
+        interaction.defresult = sasl_interact->defresult;
+        interaction.result = NULL;
+        interaction.len = 0;
+
+        int rc = payload->interact(payload->ld, flags, &interaction, payload->ctx );
+
+        if (rc) {
+            return rc;
+        }
+
+        sasl_interact->result = interaction.result;
+        sasl_interact->len = interaction.len;
+
+        sasl_interact++;
+    }
+
+    return LDAP_SUCCESS;
+}
+
+
+/**
+ * APR LDAP bind function
+ *
+ * This function binds a previously initialised LDAP connection
+ * to the directory.
+ *
+ * Binds are attempted as SASL interactive, falling back to a
+ * standard SASL bind, falling back to a simple bind, depending
+ * on the capabilities of the platform.
+ *
+ * Binds are attempted asynchronously. If APR_EAGAIN is returned,
+ * this function must be called again.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_bind(apr_ldap_t *ldap,
+                                             const char *dn,
+                                             const char *mech,
+                                             apr_ldap_interact_proc *interact,
+                                             void *ctx,
+                                             apr_interval_time_t timeout,
+                                             apr_ldap_message_t **msg,
+                                             apu_err_t *result)
+{
+#if APR_HAS_LDAP_SASL_INTERACTIVE_BIND
+
+    LDAPControl *sctrls[] = { 0 };
+    LDAPControl *cctrls[] = { 0 };
+
+    unsigned int flags = LDAP_SASL_QUIET;
+
+    apr_ldap_bind_ctx_t payload;
+
+    struct timeval tv, *tvptr;
+
+    if (timeout < 0) {
+        tvptr = NULL;
+    }
+    else {
+        tv.tv_sec = (long) apr_time_sec(timeout);
+        tv.tv_usec = (long) apr_time_usec(timeout);
+        tvptr = &tv;
+    }
+
+    memset(result, 0, sizeof(*result));
+
+    payload.ld = ldap;
+    payload.interact = interact;
+    payload.ctx = ctx;
+
+    if (!*msg) {
+        *msg = apr_ldap_message_make(ldap);
+        if (!*msg) {
+            return APR_ENOMEM;
+        }
+    }
+
+#ifdef LDAP_OPT_NETWORK_TIMEOUT
+    if (!(*msg)->message) {
+        result->rc = ldap_set_option(ldap->ld, LDAP_OPT_NETWORK_TIMEOUT, tvptr);
+        if (result->rc != LDAP_SUCCESS) {
+            result->msg = ldap_err2string(result->rc);
+            result->reason = "LDAP: Could not set network timeout";
+            return APR_EINVAL;
+        }
+    }
+#endif
+
+    result->rc = ldap_sasl_interactive_bind(ldap->ld, dn, mech,
+                                            sctrls, cctrls, flags, bind_sasl_interact, &payload,
+                                            (*msg)->message, &(*msg)->rmech, &(*msg)->msgid );
+
+    if (result->rc == LDAP_SUCCESS) {
+        apr_ldap_message_clear(*msg);
+        return APR_SUCCESS;
+    }
+    else if (result->rc == LDAP_SASL_BIND_IN_PROGRESS) {
+        return APR_INCOMPLETE;
+    }
+    else {
+        apr_ldap_message_clear(*msg);
+        result->msg = ldap_err2string(result->rc);
+        result->reason = "LDAP: ldap_sasl_interactive_bind() failed";
+        return apr_ldap_status(result->rc, APR_EGENERAL);
+    }
+
+#else
+    result->reason = "LDAP: Bind not yet supported by APR on this "
+                     "LDAP SDK";
+    result->rc = LDAP_UNWILLING_TO_PERFORM;
+    return APR_ENOTIMPL;
+#endif
+}
+
+
+
+#endif /* APR_HAS_LDAP */
+
Index: ldap/apr_ldap_init.c
===================================================================
--- ldap/apr_ldap_init.c	(revision 1909133)
+++ ldap/apr_ldap_init.c	(working copy)
@@ -31,6 +31,7 @@
 #endif
 
 #include "apr_ldap.h"
+#include "apr_ldap_private.h"
 #include "apu_internal.h"
 #include "apr_errno.h"
 #include "apr_pools.h"
@@ -213,6 +214,130 @@
     
 }
 
+static apr_status_t ldap_cleanup(void *dptr)
+{
+    if (dptr) {
+
+        apr_ldap_t *ldap = dptr;
+
+        if (ldap->ld) {
+            ldap_unbind(ldap->ld);
+            ldap->ld = NULL;
+        }
+    }
+
+    return APR_SUCCESS;
+}
+
+/**
+ * APR LDAP initialise function
+ *
+ * This function is responsible for initialising an LDAP
+ * connection in a toolkit independant way. It does the
+ * job of ldap_initialize() from the C api.
+ *
+ * It handles the SSL case, the non-SSL case, and the IPC
+ * case, and attempts to hide the complexity setup from the
+ * user.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_initialize(apr_pool_t *pool,
+                                                   const char *uri,
+                                                   apr_ldap_t **ldap,
+                                                   apu_err_t *result)
+{
+    LDAP *ld = NULL;
+    int rc;
+
+    memset(result, 0, sizeof(*result));
+
+    *ldap = apr_pcalloc(pool, sizeof(apr_ldap_t));
+    if (!*ldap) {
+        return APR_ENOMEM;
+    }
+
+#if APR_HAS_LDAP_INITIALIZE
+
+    rc = ldap_initialize(&ld, uri);
+
+#else
+
+    {
+        apr_ldap_url_desc_t *urld;
+        apr_status_t status;
+        int secure;
+
+        status = apr_ldap_url_parse(pool, uri, &(urld), result_err); 
+        if (status != APR_SUCCESS) {
+            return status;
+        }
+
+        secure = apr_ldap_is_ldaps_url(uri);
+
+#if APR_HAS_LDAPSSL_INIT
+        ld = ldapssl_init(urld->lud_host, urld->lud_port, secure);
+#elif APR_HAS_LDAP_SSLINIT
+        ld = ldap_sslinit((char *)urld->lud_host, urld->lud_port, secure);
+#else
+        ld = ldap_init((char *)urld->lud_host, urld->lud_port);
+#endif
+
+    }
+
+#endif
+
+    if (rc != LDAP_SUCCESS) {
+
+        result->rc = rc;
+        result->msg = apr_pstrdup(pool, ldap_err2string(result-> rc));
+        result->reason = apr_pstrdup(pool, "LDAP: Could not initialise");
+
+        return APR_EINVAL;
+    }
+
+    (*ldap)->ld = ld;
+    (*ldap)->pool = pool;
+
+    apr_pool_cleanup_register(pool, (*ldap), ldap_cleanup,
+                              apr_pool_cleanup_null);
+
+    return APR_SUCCESS;
+}
+
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_status(int rc, apr_status_t status)
+{
+
+    switch (rc) {
+    case LDAP_TIMEOUT:
+        return APR_ETIMEDOUT;
+
+    case LDAP_SERVER_DOWN:
+        return APR_EDOWN;
+
+    case LDAP_AUTH_UNKNOWN:
+        return APR_AUTH_UNKNOWN;
+
+#ifdef LDAP_X_PROXY_AUTHZ_FAILURE
+    case LDAP_X_PROXY_AUTHZ_FAILURE:
+        return APR_PROXY_AUTH;
+
+#endif
+    case LDAP_INAPPROPRIATE_AUTH:
+        return APR_INAPPROPRIATE_AUTH;
+
+    case LDAP_INVALID_CREDENTIALS:
+        return APR_INVALID_CREDENTIALS;
+
+    case LDAP_INSUFFICIENT_ACCESS:
+        return APR_INSUFFICIENT_ACCESS;
+
+    default:
+        return status;
+    }
+
+}
+
+
 #if APU_DSO_BUILD
 
 /* For DSO builds, export the table of entry points into the apr_ldap DSO
@@ -227,7 +352,12 @@
     apr_ldap_set_option,
     apr_ldap_rebind_init,
     apr_ldap_rebind_add,
-    apr_ldap_rebind_remove
+    apr_ldap_rebind_remove,
+    apr_ldap_initialize,
+    apr_ldap_get_option_ex,
+    apr_ldap_set_option_ex,
+    apr_ldap_result,
+    apr_ldap_bind
 };
 
 #endif /* APU_DSO_BUILD */
Index: ldap/apr_ldap_option.c
===================================================================
--- ldap/apr_ldap_option.c	(revision 1909133)
+++ ldap/apr_ldap_option.c	(working copy)
@@ -30,17 +30,19 @@
 #endif
 
 #include "apr_ldap.h"
+#include "apr_ldap_private.h"
 #include "apr_errno.h"
 #include "apr_pools.h"
 #include "apr_strings.h"
 #include "apr_tables.h"
+#include "apr_portable.h"
 
 #if APR_HAS_LDAP
 
-static void option_set_cert(apr_pool_t *pool, LDAP *ldap, const void *invalue,
-                           apr_ldap_err_t *result);
-static void option_set_tls(apr_pool_t *pool, LDAP *ldap, const void *invalue,
-                          apr_ldap_err_t *result);
+static int option_set_cert(apr_pool_t *pool, LDAP *ldap, const void *invalue,
+                           apu_err_t *result);
+static int option_set_tls(apr_pool_t *pool, LDAP *ldap, const void *invalue,
+                          apu_err_t *result);
 
 /**
  * APR LDAP get option function
@@ -102,11 +104,11 @@
 
     switch (option) {
     case APR_LDAP_OPT_TLS_CERT:
-        option_set_cert(pool, ldap, invalue, result);
+        option_set_cert(pool, ldap, invalue, (apu_err_t *)result);
         break;
 
     case APR_LDAP_OPT_TLS:
-        option_set_tls(pool, ldap, invalue, result);
+        option_set_tls(pool, ldap, invalue, (apu_err_t *)result);
         break;
         
     case APR_LDAP_OPT_VERIFY_CERT:
@@ -220,8 +222,8 @@
  * APR_LDAP_STARTTLS: STARTTLS encryption
  * APR_LDAP_STOPTLS: Stop existing TLS connecttion
  */
-static void option_set_tls(apr_pool_t *pool, LDAP *ldap, const void *invalue,
-                          apr_ldap_err_t *result)
+static int option_set_tls(apr_pool_t *pool, LDAP *ldap, const void *invalue,
+                          apu_err_t *result)
 {
 #if APR_HAS_LDAP_SSL /* compiled with ssl support */
 
@@ -378,6 +380,7 @@
 
 #endif /* APR_HAS_LDAP_SSL */
 
+    return result->rc;
 }
 
 /**
@@ -393,8 +396,8 @@
  * Microsoft: unknown
  * Solaris: unknown
  */
-static void option_set_cert(apr_pool_t *pool, LDAP *ldap,
-                           const void *invalue, apr_ldap_err_t *result)
+static int option_set_cert(apr_pool_t *pool, LDAP *ldap,
+                           const void *invalue, apu_err_t *result)
 {
 #if APR_HAS_LDAP_SSL
 #if APR_HAS_LDAPSSL_CLIENT_INIT || APR_HAS_OPENLDAP_LDAPSDK
@@ -646,7 +649,332 @@
     result->rc = -1;
 #endif /* APR_HAS_LDAP_SSL */
 
+    return result->rc;
 }
 
+/**
+ * APR LDAP get option function
+ *
+ * This function gets option values from a given LDAP session if
+ * one was specified.
+ *
+ * If result_err is NULL, no error detail is returned. If *result_err is
+ * NULL, an error detail will be created and returned. If *result_err is
+ * not NULL, an error detail will be written to this location.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_get_option_ex(apr_ldap_t *ldap,
+                                                      int option,
+                                                      apr_ldap_opt_t *outvalue,
+                                                      apu_err_t *result)
+{
+    int rc;
+
+    memset(result, 0, sizeof(*result));
+
+    switch (option) {
+    case APR_LDAP_OPT_API_INFO: {
+        LDAPAPIInfo info = { 0 };
+
+        info.ldapai_info_version = LDAP_API_INFO_VERSION;
+
+        rc = ldap_get_option(NULL, LDAP_OPT_API_INFO, &info);
+
+        outvalue->info.api_version = info.ldapai_api_version;
+        outvalue->info.protocol_version = info.ldapai_protocol_version;
+        outvalue->info.extensions = (const char **)info.ldapai_extensions;
+        outvalue->info.vendor_name = info.ldapai_vendor_name;
+        outvalue->info.vendor_version = info.ldapai_vendor_version;
+
+        break;
+
+    }
+    case APR_LDAP_OPT_API_FEATURE_INFO: {
+        LDAPAPIFeatureInfo ldfi = { 0 };
+
+        ldfi.ldapaif_info_version = LDAP_FEATURE_INFO_VERSION;
+        ldfi.ldapaif_name = (char *)outvalue->ldfi.name;
+
+        rc = ldap_get_option(NULL, LDAP_OPT_API_FEATURE_INFO, &ldfi);
+
+        outvalue->ldfi.version = ldfi.ldapaif_version;
+
+        break;
+
+    }
+    case APR_LDAP_OPT_PROTOCOL_VERSION: {
+
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_PROTOCOL_VERSION, &outvalue->pv);
+
+        break;
+    }
+    case APR_LDAP_OPT_HANDLE: {
+
+        outvalue->handle = ldap ? ldap->ld : NULL;
+
+        return APR_SUCCESS;
+    }
+    case APR_LDAP_OPT_DESC: {
+
+        apr_status_t status = APR_SUCCESS;
+
+        if (!ldap->socket) {
+            apr_os_sock_t sock;
+
+            rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_DESC, &sock);
+
+            if (rc == LDAP_SUCCESS) {
+                status = apr_os_sock_put(&ldap->socket, &sock, ldap->pool);
+            }
+            else {
+                status = APR_EGENERAL;
+            }
+        }
+        outvalue->socket = ldap->socket;
+
+        return status;
+    }
+    case APR_LDAP_OPT_DEREF: {
+
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_DEREF, &outvalue->deref);
+
+        break;
+    }
+    case APR_LDAP_OPT_REFERRALS: {
+        int refs;
+
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_REFERRALS, &refs);
+
+        if (rc == LDAP_SUCCESS) {
+            outvalue->refs = refs ? APR_LDAP_OPT_ON : APR_LDAP_OPT_OFF;
+        }
+
+        break;
+    }
+    case APR_LDAP_OPT_REFHOPLIMIT: {
+#if defined(LDAP_OPT_REFERRAL_HOP_LIMIT)
+        /* Microsoft and Mozilla SDKs define LDAP_OPT_REFERRAL_HOP_LIMIT
+         */
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_REFERRAL_HOP_LIMIT, &outvalue->refhoplimit);
+#else
+#if !defined(LDAP_OPT_REFHOPLIMIT) || APR_HAS_NOVELL_LDAPSDK || APR_HAS_OPENLDAP_LDAPSDK
+        result->reason = "LDAP: Referral hop limit not yet supported by APR on this "
+                         "LDAP SDK";
+        result->rc = LDAP_UNWILLING_TO_PERFORM;
+        return APR_ENOTIMPL;
+#else
+        /* Setting this option is supported on at least TIVOLI_SDK. Folks who know
+         * the NOVELL, NETSCAPE, MOZILLA, and SOLARIS SDKs should note here if
+         * the SDK at least tolerates this option being set.
+         */
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_REFHOPLIMIT, &outvalue->refhoplimit);
+#endif
+#endif
+
+        break;
+    }
+    case APR_LDAP_OPT_RESULT_CODE: {
+
+#ifdef LDAP_OPT_RESULT_CODE
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_RESULT_CODE, &outvalue->result);
+#else
+#ifdef LDAP_OPT_ERROR_NUMBER
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, LDAP_OPT_ERROR_NUMBER, &outvalue->result);
+#endif
+#endif
+        break;
+    }
+    case APR_LDAP_OPT_TLS_CERT: {
+
+        result->reason = "LDAP: Could not get an option APR_LDAP_OPT_TLS_CERT: not implemented";
+
+        return APR_ENOTIMPL;
+    }
+    case APR_LDAP_OPT_TLS: {
+
+        result->reason = "LDAP: Could not get an option APR_LDAP_OPT_TLS: not implemented";
+
+        return APR_ENOTIMPL;
+    }
+    case APR_LDAP_OPT_VERIFY_CERT: {
+
+        result->reason = "LDAP: Could not get an option APR_LDAP_OPT_VERIFY_CERT: not implemented";
+
+        return APR_ENOTIMPL;
+    }
+    default:
+        rc = ldap_get_option(ldap ? ldap->ld : NULL, option, &outvalue->opt);
+    }
+
+    if (rc != LDAP_SUCCESS) {
+
+        result->rc = rc;
+        result->msg = ldap_err2string(result->rc);
+        result->reason = "LDAP: Could not get an option";
+
+        return APR_EINVAL;
+    }
+
+    return APR_SUCCESS;
+}
+
+/**
+ * APR LDAP set option function
+ *
+ * This function sets option values to a given LDAP session if
+ * one was specified.
+ *
+ * Where an option is not supported by an LDAP toolkit, this function
+ * will try and apply legacy functions to achieve the same effect,
+ * depending on the platform.
+ */
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_set_option_ex(apr_ldap_t *ldap,
+                                                      int option,
+                                                      const apr_ldap_opt_t *invalue,
+                                                      apu_err_t *result)
+{
+    int rc;
+
+    memset(result, 0, sizeof(*result));
+
+    switch (option) {
+    case APR_LDAP_OPT_API_INFO:
+        rc = LDAP_UNWILLING_TO_PERFORM;
+        break;
+
+    case APR_LDAP_OPT_API_FEATURE_INFO:
+        rc = LDAP_UNWILLING_TO_PERFORM;
+        break;
+
+    case APR_LDAP_OPT_PROTOCOL_VERSION:
+        rc = ldap_set_option(ldap ? ldap->ld : NULL, LDAP_OPT_PROTOCOL_VERSION, &invalue->pv);
+        break;
+
+    case APR_LDAP_OPT_HANDLE:
+        rc = LDAP_UNWILLING_TO_PERFORM;
+        break;
+
+    case APR_LDAP_OPT_DESC:
+        rc = LDAP_UNWILLING_TO_PERFORM;
+        break;
+
+    case APR_LDAP_OPT_DEREF:
+        rc = ldap_set_option(ldap ? ldap->ld : NULL, LDAP_OPT_DEREF, &invalue->deref);
+        break;
+
+    case APR_LDAP_OPT_REFERRALS: {
+        void *refs = invalue->refs ? LDAP_OPT_ON : LDAP_OPT_OFF;
+
+        /* Setting this option is supported on at least TIVOLI_SDK and OpenLDAP. Folks
+         * who know the NOVELL, NETSCAPE, MOZILLA, and SOLARIS SDKs should note here if
+         * the SDK at least tolerates this option being set, or add an elif to handle
+         * special cases (i.e. different LDAP_OPT_X value).
+         */
+        rc = ldap_set_option(ldap ? ldap->ld : NULL, LDAP_OPT_REFERRALS, refs);
+        break;
+
+    }
+    case APR_LDAP_OPT_REFHOPLIMIT:
+#if defined(LDAP_OPT_REFERRAL_HOP_LIMIT)
+        /* Microsoft and Mozilla SDKs define LDAP_OPT_REFERRAL_HOP_LIMIT
+         */
+        rc = ldap_set_option(ldap ? ldap->ld : NULL, LDAP_OPT_REFERRAL_HOP_LIMIT, &invalue->refhoplimit);
+#else
+#if !defined(LDAP_OPT_REFHOPLIMIT) || APR_HAS_NOVELL_LDAPSDK || APR_HAS_OPENLDAP_LDAPSDK
+        /* If the LDAP_OPT_REFHOPLIMIT symbol is missing, assume that the
+         * particular LDAP library has a reasonable default. So far certain
+         * versions of the OpenLDAP SDK miss this symbol (but default to 5),
+         * and the Microsoft SDK misses the symbol (the default is not known).
+         */
+        result->reason = "LDAP: Referral hop limit not yet supported by APR on this "
+                         "LDAP SDK";
+        result->rc = LDAP_UNWILLING_TO_PERFORM;
+        return APR_ENOTIMPL;
+#else
+        /* Setting this option is supported on at least TIVOLI_SDK. Folks who know
+         * the NOVELL, NETSCAPE, MOZILLA, and SOLARIS SDKs should note here if
+         * the SDK at least tolerates this option being set, or add an elif to handle
+         * special cases so an error isn't returned if there is a perfectly good
+         * default value that just can't be changed (like openLDAP).
+         */
+        rc = ldap_set_option(ldap ? ldap->ld : NULL, LDAP_OPT_REFHOPLIMIT, &invalue->refhoplimit);
+#endif
+#endif
+        break;
+
+    case APR_LDAP_OPT_RESULT_CODE:
+        rc = LDAP_UNWILLING_TO_PERFORM;
+        break;
+
+    case APR_LDAP_OPT_TLS_CERT:
+        rc = option_set_cert(ldap->pool, ldap ? ldap->ld : NULL, invalue->certs, result);
+        break;
+
+    case APR_LDAP_OPT_TLS:
+        rc = option_set_tls(ldap->pool, ldap ? ldap->ld : NULL, &invalue->tls, result);
+        break;
+
+    case APR_LDAP_OPT_VERIFY_CERT:
+#if APR_HAS_NETSCAPE_LDAPSDK || APR_HAS_SOLARIS_LDAPSDK || APR_HAS_MOZILLA_LDAPSK
+        result->reason = "LDAP: Verify certificate not yet supported by APR on the "
+                         "Netscape, Solaris or Mozilla LDAP SDKs";
+        result->rc = LDAP_UNWILLING_TO_PERFORM;
+        return APR_ENOTIMPL;
+#endif
+#if APR_HAS_NOVELL_LDAPSDK
+        if (invalue->verify) {
+            result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER);
+        }
+        else {
+            result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE);
+        }
+#endif
+#if APR_HAS_OPENLDAP_LDAPSDK
+#ifdef LDAP_OPT_X_TLS
+        /* This is not a per-connection setting so just pass NULL for the
+           Ldap connection handle */
+        if (invalue->verify) {
+            int i = LDAP_OPT_X_TLS_DEMAND;
+            result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
+        }
+        else {
+            int i = LDAP_OPT_X_TLS_NEVER;
+            result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
+        }
+#else
+        result->reason = "LDAP: SSL/TLS not yet supported by APR on this "
+                         "version of the OpenLDAP toolkit";
+        result->rc = LDAP_UNWILLING_TO_PERFORM;
+        return APR_ENOTIMPL;
+#endif
+#endif
+
+        /* handle the error case */
+        if (result->rc != LDAP_SUCCESS) {
+            result->msg = ldap_err2string(result->rc);
+            result->reason = "LDAP: Could not set verify mode";
+            return APR_EINVAL;
+        }
+        return APR_SUCCESS;
+
+    default:
+        rc = ldap_set_option(ldap ? ldap->ld : NULL, option, invalue->opt);
+    }
+
+    if (rc != LDAP_OPT_SUCCESS) {
+
+        result->rc = rc;
+        if (!result->msg) {
+            result->msg = apr_pstrdup(ldap->pool, ldap_err2string(result->rc));
+        }
+        if (result->reason) {
+            result->reason = apr_pstrdup(ldap->pool, "LDAP: Could not set an option");
+        }
+
+        return APR_EINVAL;
+    }
+
+    return APR_SUCCESS;
+}
+
 #endif /* APR_HAS_LDAP */
 
Index: ldap/apr_ldap_stub.c
===================================================================
--- ldap/apr_ldap_stub.c	(revision 1909133)
+++ ldap/apr_ldap_stub.c	(working copy)
@@ -18,6 +18,7 @@
 #include "apu.h"
 #include "apu_config.h"
 #include "apr_ldap.h"
+#include "apr_ldap_private.h"
 #include "apu_internal.h"
 #include "apr_dso.h"
 #include "apr_errno.h"
@@ -139,6 +140,55 @@
     return lfn->rebind_remove(ld);
 }
 
+APU_DECLARE_LDAP(int) apr_ldap_initialize(apr_pool_t *pool,
+                                          const char *uri,
+                                          apr_ldap_t **ldap,
+                                          apu_err_t *result)
+{
+    LOAD_LDAP_STUB(pool, -1);
+    return lfn->initialize(pool, uri, ldap, result);
+}
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_get_option_ex(apr_ldap_t *ldap,
+                                                      int option,
+                                                      apr_ldap_opt_t *outvalue,
+                                                      apu_err_t *result)
+{
+    LOAD_LDAP_STUB(ldap->pool, -1);
+    return lfn->get_option_ex(ldap, option, outvalue, result);
+}
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_set_option_ex(apr_ldap_t *ldap,
+                                                      int option,
+                                                      const apr_ldap_opt_t *invalue,
+                                                      apu_err_t *result)
+{
+    LOAD_LDAP_STUB(ldap->pool, -1);
+    return lfn->set_option_ex(ldap, option, invalue, result);
+}
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_result(apr_ldap_message_t *msg,
+                                               apr_interval_time_t timeout,
+                                               apu_err_t *result)
+{
+    LOAD_LDAP_STUB(msg->ld->pool, APR_EGENERAL);
+    return lfn->result(msg, timeout, result);
+}
+
+APU_DECLARE_LDAP(apr_status_t) apr_ldap_bind(apr_ldap_t *ldap,
+                                             const char *dn,
+                                             const char *mech,
+                                             apr_ldap_interact_proc *interact,
+                                             void *ctx,
+                                             apr_interval_time_t timeout,
+                                             apr_ldap_message_t **msg,
+                                             apu_err_t *result)
+{
+    LOAD_LDAP_STUB(ldap->pool, -1);
+    return lfn->bind(ldap, dn, mech, interact, ctx, timeout, msg, result);
+}
+
+
 #endif /* APU_DSO_BUILD */
 
 #endif /* APR_HAS_LDAP */
Index: libaprutil.dsp
===================================================================
--- libaprutil.dsp	(revision 1909133)
+++ libaprutil.dsp	(working copy)
@@ -360,6 +360,11 @@
 # End Source File 
 # Begin Source File
 
+SOURCE=.\ldap\apr_ldap_bind.c
+# PROP Exclude_From_Build 1
+# End Source File
+# Begin Source File
+
 SOURCE=.\ldap\apr_ldap_rebind.c
 # PROP Exclude_From_Build 1
 # End Source File
Index: libaprutil.mak
===================================================================
--- libaprutil.mak	(revision 1909133)
+++ libaprutil.mak	(working copy)
@@ -1006,7 +1006,8 @@
 
 SOURCE=.\ldap\apr_ldap_init.c
 SOURCE=.\ldap\apr_ldap_option.c
-SOURCE=.\ldap\apr_ldap_rebind.c
+SOURCE=.\ldap\apr_ldap_bind.c
+SOURCE=.\ldap\apr_ldap_rebind.c 
 SOURCE=.\ldap\apr_ldap_stub.c
 
 "$(INTDIR)\apr_ldap_stub.obj" : $(SOURCE) "$(INTDIR)" ".\include\apu.h" ".\include\private\apu_config.h" ".\include\apr_ldap.h"
Index: test/testldap.c
===================================================================
--- test/testldap.c	(revision 1909133)
+++ test/testldap.c	(working copy)
@@ -91,10 +91,8 @@
         return 0;
     }
 
-    ptr = strstr (ldap_host, "\r\n");
-    if (ptr) {
-        *ptr = '\0';
-    }
+    ptr = ldap_host;
+    strsep (&ptr, "\r\n");
     apr_file_close(thefile);
 
     return 1;
@@ -140,92 +138,268 @@
     return 0;
 }
 
-static void test_ldap_connection(abts_case *tc, LDAP *ldap)
+static apr_status_t bind_interact_external(apr_ldap_t *ld, unsigned int flags, apr_ldap_bind_interact_t *interact, void *ctx)
 {
-    int version  = LDAP_VERSION3;
-    int failures, result;
-    
+    return APR_SUCCESS;
+}
+
+static apr_status_t bind_interact_plain(apr_ldap_t *ld, unsigned int flags, apr_ldap_bind_interact_t *interact, void *ctx)
+{
+
+    switch (interact->id) {
+    case APR_LDAP_INTERACT_USER:
+        break;
+    case APR_LDAP_INTERACT_AUTHNAME:
+        interact->result = "user";
+        interact->len = strlen(interact->result);
+        break;
+    case APR_LDAP_INTERACT_PASS:
+        interact->result = "opensesame";
+        interact->len = strlen(interact->result);
+        break;
+    default:
+        break;
+    }
+
+    return APR_SUCCESS;
+}
+
+static void test_ldap_connection(abts_case *tc, apr_ldap_t *ldap, const char *mech, apr_ldap_interact_proc *interact)
+{
+    apu_err_t result;
+    apr_ldap_message_t *msg = NULL;
+    apr_ldap_opt_t opt;
+    apr_status_t status;
+ 
     /* always default to LDAP V3 */
-    ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
+    opt.pv = APR_LDAP_VERSION3;
+    apr_ldap_set_option_ex(ldap, APR_LDAP_OPT_PROTOCOL_VERSION, &opt, &result);
 
-    for (failures=0; failures<10; failures++)
-    {
-        result = ldap_simple_bind_s(ldap,
-                                    (char *)NULL,
-                                    (char *)NULL);
-        if (LDAP_SERVER_DOWN != result)
+    apr_ldap_get_option_ex(ldap, APR_LDAP_OPT_HANDLE, &opt, &result);
+    ABTS_TRUE(tc, opt.handle != NULL);
+
+    do {
+        status = apr_ldap_bind(ldap, NULL, mech, interact, NULL, apr_time_from_sec(5), &msg, &result);
+
+        if (APR_SUCCESS == status) {
             break;
-    }
+        }
+        else if (APR_INCOMPLETE == status) {
+            /* keep going */
+        }
+        else if (APR_STATUS_IS_ETIMEDOUT(status)) {
+            abts_log_message("%s - %s (%d)\n", result.reason, result.msg, result.rc);
+            break;
+        }
+        else if (APR_STATUS_IS_EDOWN(status)) {
+            abts_log_message("%s - %s (%d)\n", result.reason, result.msg, result.rc);
+            break;
+        }
+        else if (APR_STATUS_IS_AUTH_UNKNOWN(status)) {
+            abts_log_message("%s - %s (%d)\n", result.reason, result.msg, result.rc);
+            break;
+        }
+        else if (APR_STATUS_IS_PROXY_AUTH(status)) {
+            abts_log_message("%s - %s (%d)\n", result.reason, result.msg, result.rc);
+            break;
+        }
+        else if (APR_STATUS_IS_INAPPROPRIATE_AUTH(status)) {
+            abts_log_message("%s - %s (%d)\n", result.reason, result.msg, result.rc);
+            break;
+        }
+        else if (APR_STATUS_IS_INVALID_CREDENTIALS(status)) {
+            abts_log_message("%s - %s (%d)\n", result.reason, result.msg, result.rc);
+            break;
+        }
+        else if (APR_STATUS_IS_INSUFFICIENT_ACCESS(status)) {
+            abts_log_message("%s - %s (%d)\n", result.reason, result.msg, result.rc);
+            break;
+        }
+        else {
+            abts_log_message("%s - %s (%d)\n", result.reason, result.msg, result.rc);
+            ABTS_TRUE(tc, status == LDAP_SUCCESS);
+            break;
+        }
 
-    ABTS_TRUE(tc, result == LDAP_SUCCESS);
-    if (result != LDAP_SUCCESS) {
-        abts_log_message("%s\n", ldap_err2string(result));
-    }
+        ABTS_TRUE(tc, msg != NULL);
 
-    ldap_unbind_s(ldap);
+        status = apr_ldap_result(msg, apr_time_from_sec(5), &result);
 
+        if (APR_SUCCESS == status) {
+            /* no more messages */
+            break;
+        }
+        else if (APR_INCOMPLETE == status) {
+            /* keep going */
+        }
+        else {
+            abts_log_message("%s\n", result.reason);
+            ABTS_TRUE(tc, status == LDAP_SUCCESS);
+            break;
+        }   
+
+    } while (APR_INCOMPLETE == status);
+
+    status = apr_ldap_get_option_ex(ldap, APR_LDAP_OPT_DESC, &opt, &result);
+    ABTS_TRUE(tc, status == APR_SUCCESS);
+    ABTS_ASSERT(tc, "failed to get descriptor", opt.socket != NULL);
+
     return;
 }
 
+static void test_ldap_global_opts(abts_case *tc, void *data)
+{
+    apu_err_t result;
+    apr_ldap_opt_t opt;
+    apr_status_t status;
+
+    status = apr_ldap_get_option_ex(NULL, APR_LDAP_OPT_API_INFO, &opt, &result);
+
+    ABTS_TRUE(tc, status == APR_SUCCESS);
+    ABTS_TRUE(tc, result.rc == 0);
+    ABTS_TRUE(tc, opt.info.vendor_name != NULL);
+
+    opt.ldfi.name = "THREAD_SAFE";
+    status = apr_ldap_get_option_ex(NULL, APR_LDAP_OPT_API_FEATURE_INFO, &opt, &result);
+    /* feature may exist, or may not */
+    ABTS_TRUE(tc, status == APR_SUCCESS || status == APR_EINVAL);
+
+}
+
+static void test_ldap_opts(abts_case *tc, void *data)
+{
+    apr_pool_t *pool;
+    apr_ldap_t *ldap;
+    apu_err_t result;
+    const char *url;
+
+    apr_pool_create(&pool, p);
+
+    url = apr_psprintf(pool, "ldap://%s:%d", "localhost", LDAP_PORT);
+
+    apr_ldap_initialize(pool, url, &ldap, &(result));
+
+    ABTS_TRUE(tc, ldap != NULL);
+
+    if (result.rc == LDAP_SUCCESS) {
+
+        apr_status_t status;
+        apr_ldap_opt_t opt;
+
+        opt.pv = APR_LDAP_VERSION3;
+        status = apr_ldap_set_option_ex(ldap, APR_LDAP_OPT_PROTOCOL_VERSION, &opt, &result);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        status = apr_ldap_get_option_ex(ldap, APR_LDAP_OPT_PROTOCOL_VERSION, &opt, &result);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        ABTS_ASSERT(tc, "failed to set protocol version", opt.pv == APR_LDAP_VERSION3);
+
+        opt.deref = APR_LDAP_DEREF_ALWAYS;
+        status = apr_ldap_set_option_ex(ldap, APR_LDAP_OPT_DEREF, &opt, &result);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        status = apr_ldap_get_option_ex(ldap, APR_LDAP_OPT_DEREF, &opt, &result);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        ABTS_ASSERT(tc, "failed to set deref", opt.deref == APR_LDAP_DEREF_ALWAYS);
+
+        opt.refs = APR_LDAP_OPT_ON;
+        status = apr_ldap_set_option_ex(ldap, APR_LDAP_OPT_REFERRALS, &opt, &result);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        status = apr_ldap_get_option_ex(ldap, APR_LDAP_OPT_REFERRALS, &opt, &result);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        ABTS_ASSERT(tc, "failed to set referrals", opt.refs == APR_LDAP_OPT_ON);
+
+        opt.refhoplimit = 5;
+        status = apr_ldap_set_option_ex(ldap, APR_LDAP_OPT_REFHOPLIMIT, &opt, &result);
+        if (APR_ENOTIMPL != status) {
+            ABTS_TRUE(tc, status == APR_SUCCESS); 
+            status = apr_ldap_get_option_ex(ldap, APR_LDAP_OPT_REFHOPLIMIT, &opt, &result);
+            ABTS_TRUE(tc, status == APR_SUCCESS); 
+            ABTS_ASSERT(tc, "failed to set refhoplimit", opt.refhoplimit == 5);
+        }
+
+    }
+
+    apr_pool_destroy(pool);
+}
+
 static void test_ldap(abts_case *tc, void *data)
 {
-    apr_pool_t *pool = p;
-    LDAP *ldap;
-    apr_ldap_err_t *result = NULL;
+    apr_pool_t *pool;
+    apr_ldap_t *ldap;
+    apu_err_t result;
+    const char *url;
 
+    apr_pool_create(&pool, p);
 
     ABTS_ASSERT(tc, "failed to get host", ldap_host[0] != '\0');
+
+    url = apr_psprintf(pool, "ldap://%s:%d", ldap_host, LDAP_PORT);
     
-    apr_ldap_init(pool, &ldap,
-                  ldap_host, LDAP_PORT,
-                  APR_LDAP_NONE, &(result));
+    apr_ldap_initialize(pool, url, &ldap, &(result));
 
     ABTS_TRUE(tc, ldap != NULL);
-    ABTS_PTR_NOTNULL(tc, result);
 
-    if (result->rc == LDAP_SUCCESS) {
-        test_ldap_connection(tc, ldap);
+    if (result.rc == LDAP_SUCCESS) {
+        test_ldap_connection(tc, ldap, "PLAIN", bind_interact_plain);
     }
+
+    apr_pool_destroy(pool);
 }
 
 static void test_ldaps(abts_case *tc, void *data)
 {
-    apr_pool_t *pool = p;
-    LDAP *ldap;
-    apr_ldap_err_t *result = NULL;
+    apr_pool_t *pool;
+    apr_ldap_t *ldap;
+    apu_err_t result;
+    const char *url; 
 
-    apr_ldap_init(pool, &ldap,
-                  ldap_host, LDAPS_PORT,
-                  APR_LDAP_SSL, &(result));
+    apr_pool_create(&pool, p);
 
+    url = apr_psprintf(pool, "ldaps://%s:%d", ldap_host, LDAPS_PORT);
+
+    apr_ldap_initialize(pool, url, &ldap, &(result));
+
     ABTS_TRUE(tc, ldap != NULL);
-    ABTS_PTR_NOTNULL(tc, result);
 
-    if (result->rc == LDAP_SUCCESS) {
+    if (result.rc == LDAP_SUCCESS) {
         add_ldap_certs(tc);
 
-        test_ldap_connection(tc, ldap);
+        test_ldap_connection(tc, ldap, "EXTERNAL", bind_interact_external);
+        test_ldap_connection(tc, ldap, "PLAIN", bind_interact_plain);
     }
+
+    apr_pool_destroy(pool);
 }
 
 static void test_ldap_tls(abts_case *tc, void *data)
 {
-    apr_pool_t *pool = p;
-    LDAP *ldap;
-    apr_ldap_err_t *result = NULL;
+    apr_pool_t *pool;
+    apr_ldap_t *ldap;
+    apu_err_t result;
+    const char *url;
+    apr_ldap_opt_t opt;
 
-    apr_ldap_init(pool, &ldap,
-                  ldap_host, LDAP_PORT,
-                  APR_LDAP_STARTTLS, &(result));
+    apr_pool_create(&pool, p);
 
+    opt.tls = APR_LDAP_TLS_STARTTLS;
+
+    url = apr_psprintf(pool, "ldap://%s:%d", ldap_host, LDAP_PORT);
+
+    apr_ldap_initialize(pool, url, &ldap, &(result));
+
+    if (result.rc == LDAP_SUCCESS) {
+        apr_ldap_set_option_ex(ldap, APR_LDAP_OPT_TLS, &opt, &(result));
+    }
+
     ABTS_TRUE(tc, ldap != NULL);
-    ABTS_PTR_NOTNULL(tc, result);
 
-    if (result->rc == LDAP_SUCCESS) {
+    if (result.rc == LDAP_SUCCESS) {
         add_ldap_certs(tc);
 
-        test_ldap_connection(tc, ldap);
+        test_ldap_connection(tc, ldap, "EXTERNAL", bind_interact_external);
+        test_ldap_connection(tc, ldap, "PLAIN", bind_interact_plain);
     }
+
+    apr_pool_destroy(pool);
 }
 
 #endif /* APR_HAS_LDAP */
@@ -238,11 +412,17 @@
 
     apr_ldap_ssl_init(p, NULL, 0, &result);
 
+    abts_run_test(suite, test_ldap_global_opts, NULL);
+    abts_run_test(suite, test_ldap_opts, NULL);
+
     if (get_ldap_host()) {
         abts_run_test(suite, test_ldap, NULL);
         abts_run_test(suite, test_ldaps, NULL);
         abts_run_test(suite, test_ldap_tls, NULL);
     }
+
+    apr_ldap_ssl_deinit();
+
 #endif /* APR_HAS_LDAP */
 
     return suite;


Re: POC: updated LDAP support for apr-util 1.7

Posted by Graham Leggett via dev <de...@apr.apache.org>.
On 19 Sep 2023, at 11:34, Ruediger Pluem <rp...@apache.org> wrote:

>> /* For DSO builds, export the table of entry points into the apr_ldap DSO
> 
> In the end I think this effort only makes sense if we find a way to get LDAP back into trunk.
> I cannot remember if these enhancements would be enough to address the points that caused the LDAP API to be removed from trunk in
> r1129809 about 12 years ago.

My understanding was the objection was to the native LDAP type being exposed. This is an effort to no longer expose the native LDAP type.

I am still going work to eliminate the native LDAP type from all the httpd code, but there are too many bugs in the world and not enough hours in the day.

Regards,
Graham
—


Re: POC: updated LDAP support for apr-util 1.7

Posted by Ruediger Pluem <rp...@apache.org>.

On 4/18/23 2:26 PM, Graham Leggett via dev wrote:
> Hi all,
> 
> The following patch adds ldapi:// (LDAP over unix domain socket) support to apr-util. It is part of a wider cleanup covering the following:
> 
> - Add apr_ldap_t to hide the native LDAP type.
> - Add apr_ldap_initialize() with URL support, allowing us to do ldapi://, including proper pool cleanups.
> - Add apr_ldap_get_option_ex() and apr_ldap_set_option_ex() that use apr_ldap_t, and support all options used by httpd.
> - Options passed to apr_ldap_get_option_ex() / apr_ldap_set_option_ex() are a strongly typed union rather than the native void pointers, the assumption being the union is extensible in future.
> 
> In theory this extends but does not break our ABI and is safe to go into apr-util 1.7, if this isn’t the case please tell me so I can fix it.
> 
> Regards,
> Graham
> —
> 

> Index: include/private/apu_internal.h
> ===================================================================
> --- include/private/apu_internal.h	(revision 1909133)
> +++ include/private/apu_internal.h	(working copy)
> @@ -40,29 +40,6 @@
>  apr_status_t apu_dso_load(apr_dso_handle_t **dso, apr_dso_handle_sym_t *dsoptr, const char *module,
>                            const char *modsym, apr_pool_t *pool);
>  
> -#if APR_HAS_LDAP
> -
> -/* For LDAP internal builds, wrap our LDAP namespace */
> -
> -struct apr__ldap_dso_fntable {
> -    int (*info)(apr_pool_t *pool, apr_ldap_err_t **result_err);
> -    int (*init)(apr_pool_t *pool, LDAP **ldap, const char *hostname,
> -                int portno, int secure, apr_ldap_err_t **result_err);
> -    int (*ssl_init)(apr_pool_t *pool, const char *cert_auth_file,
> -                    int cert_file_type, apr_ldap_err_t **result_err);
> -    int (*ssl_deinit)(void);
> -    int (*get_option)(apr_pool_t *pool, LDAP *ldap, int option,
> -                      void *outvalue, apr_ldap_err_t **result_err);
> -    int (*set_option)(apr_pool_t *pool, LDAP *ldap, int option,
> -                      const void *invalue, apr_ldap_err_t **result_err);
> -    apr_status_t (*rebind_init)(apr_pool_t *pool);
> -    apr_status_t (*rebind_add)(apr_pool_t *pool, LDAP *ld,
> -                               const char *bindDN, const char *bindPW);
> -    apr_status_t (*rebind_remove)(LDAP *ld);
> -};
> -
> -#endif /* APR_HAS_LDAP */
> -

Why is this struct removed?

>  #ifdef __cplusplus
>  }
>  #endif
> Index: ldap/apr_ldap_init.c
> ===================================================================
> --- ldap/apr_ldap_init.c	(revision 1909133)
> +++ ldap/apr_ldap_init.c	(working copy)

> @@ -213,6 +214,96 @@
>      
>  }
>  
> +static apr_status_t ldap_cleanup(void *dptr)
> +{
> +    if (dptr) {
> +
> +        apr_ldap_t *ldap = dptr;
> +
> +        if (ldap->ld) {
> +            ldap_unbind(ldap->ld);
> +            ldap->ld = NULL;
> +        }
> +    }
> +
> +    return APR_SUCCESS;
> +}
> +
> +/**
> + * APR LDAP initialise function
> + *
> + * This function is responsible for initialising an LDAP
> + * connection in a toolkit independant way. It does the
> + * job of ldap_initialize() from the C api.
> + *
> + * It handles the SSL case, the non-SSL case, and the IPC
> + * case, and attempts to hide the complexity setup from the
> + * user.
> + */
> +APU_DECLARE_LDAP(apr_status_t) apr_ldap_initialize(apr_pool_t *pool,
> +                                                   const char *uri,
> +                                                   apr_ldap_t **ldap,
> +                                                   apr_ldap_err_t *result)
> +{
> +    LDAP *ld = NULL;
> +    int rc;
> +
> +    memset(result, 0, sizeof(*result));
> +
> +    *ldap = apr_pcalloc(pool, sizeof(apr_ldap_t));
> +    if (!*ldap) {
> +        return APR_ENOMEM;
> +    }
> +
> +#if APR_HAS_LDAP_INITIALIZE
> +
> +    rc = ldap_initialize(&ld, uri);
> +
> +#else
> +
> +    {
> +        apr_ldap_url_desc_t *urld;
> +        apr_status_t status;
> +        int secure;
> +
> +        status = apr_ldap_url_parse(pool, uri, &(urld), result_err);

Typo? Should be result instead of result_err?


> +        if (status != APR_SUCCESS) {
> +            return status;
> +        }
> +
> +        secure = apr_ldap_is_ldaps_url(uri);
> +
> +#if APR_HAS_LDAPSSL_INIT
> +        ld = ldapssl_init(urld->lud_host, urld->lud_port, secure);
> +#elif APR_HAS_LDAP_SSLINIT
> +        ld = ldap_sslinit((char *)urld->lud_host, urld->lud_port, secure);
> +#else
> +        ld = ldap_init((char *)urld->lud_host, urld->lud_port);
> +#endif
> +
> +    }
> +
> +#endif
> +
> +    if (rc != LDAP_SUCCESS) {
> +
> +        result->rc = rc;
> +        result->msg = apr_pstrdup(pool, ldap_err2string(result-> rc));
> +        result->reason = apr_pstrdup(pool, "LDAP: Could not initialise");
> +
> +        return APR_EINVAL;
> +    }
> +
> +    (*ldap)->ld = ld;
> +    (*ldap)->pool = pool;
> +
> +    apr_pool_cleanup_register(pool, (*ldap), ldap_cleanup,
> +                              apr_pool_cleanup_null);
> +
> +    return APR_SUCCESS;
> +}
> +
> +
>  #if APU_DSO_BUILD
>  
>  /* For DSO builds, export the table of entry points into the apr_ldap DSO

In the end I think this effort only makes sense if we find a way to get LDAP back into trunk.
I cannot remember if these enhancements would be enough to address the points that caused the LDAP API to be removed from trunk in
r1129809 about 12 years ago.

Regards

Rüdiger