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;


Reply via email to