You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@apr.apache.org by mi...@apache.org on 2024/04/18 07:48:22 UTC

svn commit: r1917099 - /apr/apr/trunk/test/testldap.c

Author: minfrin
Date: Thu Apr 18 07:48:21 2024
New Revision: 1917099

URL: http://svn.apache.org/viewvc?rev=1917099&view=rev
Log:
Add the LDAP test case.

Added:
    apr/apr/trunk/test/testldap.c   (with props)

Added: apr/apr/trunk/test/testldap.c
URL: http://svn.apache.org/viewvc/apr/apr/trunk/test/testldap.c?rev=1917099&view=auto
==============================================================================
--- apr/apr/trunk/test/testldap.c (added)
+++ apr/apr/trunk/test/testldap.c Thu Apr 18 07:48:21 2024
@@ -0,0 +1,1140 @@
+/* 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.
+ */
+ 
+ /* Setup:
+  *  - Set environment variables as below for each URL being
+  *     tested, along with dns and credentials.
+  *  - Copy the server certificates to the data/ directory.
+  *     All DER type certificates must have the .der extention.
+  *     All BASE64 or PEM certificates must have the .b64
+  *     extension.  All certificate files copied to the /data
+  *     directory will be added to the ldap certificate store.
+  */
+   
+ /* This test covers the following three types of connections:
+  *
+  *  - Unsecure ldap://
+  *  - Secure ldaps://
+  *  - Secure ldap://+Start_TLS
+  *  - Unix ldapi://
+  *
+  * Environment variables:
+  *
+  * One or more of the following, based on what you have available.
+  *
+  * - TESTLDAP=ldap://localhost:389
+  * - TESTLDAP_TLS=ldap://starttls.server:389
+  * - TESTLDAPS=ldaps://ssl.server:636
+  * - TESTLDAPI=ldapi://%2ftmp%2fslapd-server.socket/o=example,c=gb?mail?sub
+  *
+  * Simple bind:
+  *
+  * - TESTLDAP_DN=cn=Directory Manager
+  * - TESTLDAP_PASS=opensesame
+  *
+  * SASL PLAIN bind:
+  *
+  * - TESTLDAP_USER=sasluser
+  * - TESTLDAP_AUTHNAME=sasluser
+  * - TESTLDAP_PASS=opensesame
+  *
+  * SASL EXTERNAL bind is attempted where supported.
+  */
+
+ /*
+  * For a working example of an asynchronous LDAP conversation involving a bind,
+  * followed by a search, followed by a compare, start at test_ldap_connection()
+  * below.
+  */
+
+#include "testutil.h"
+
+#include "apr.h"
+#include "apr_general.h"
+#include "apr_ldap.h"
+#include "apr_escape.h"
+#include "apr_file_io.h"
+#include "apr_file_info.h"
+#include "apr_poll.h"
+#include "apr_strings.h"
+#if APR_HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#define APR_WANT_STDIO
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+
+#define DIRNAME "data"
+#define FILENAME DIRNAME "/host.data"
+#define CERTFILEDER DIRNAME "/*.der"
+#define CERTFILEB64 DIRNAME "/*.b64"
+
+#if APR_HAS_LDAP
+
+static int add_ldap_certs(abts_case *tc)
+{
+    apr_status_t status;
+    apr_dir_t *thedir;
+    apr_finfo_t dirent;
+    apu_err_t err;
+
+    if ((status = apr_dir_open(&thedir, DIRNAME, p)) == APR_SUCCESS) {
+        apr_ldap_opt_tls_cert_t *cert = (apr_ldap_opt_tls_cert_t *)apr_pcalloc(p, sizeof(apr_ldap_opt_tls_cert_t));
+
+        do {
+            status = apr_dir_read(&dirent, APR_FINFO_MIN | APR_FINFO_NAME, thedir);
+            if (APR_STATUS_IS_INCOMPLETE(status)) {
+                continue; /* ignore un-stat()able files */
+            }
+            else if (status != APR_SUCCESS) {
+                break;
+            }
+
+            if (strstr(dirent.name, ".der")) {
+                cert->type = APR_LDAP_CA_TYPE_DER;
+                cert->path = apr_pstrcat (p, DIRNAME, "/", dirent.name, NULL);
+                apr_ldap_option_set(p, NULL, APR_LDAP_OPT_TLS_CERT, (void *)cert, &err);
+                ABTS_TRUE(tc, err.rc == APR_SUCCESS);
+            }
+            if (strstr(dirent.name, ".b64")) {
+                cert->type = APR_LDAP_CA_TYPE_BASE64;
+                cert->path = apr_pstrcat (p, DIRNAME, "/", dirent.name, NULL);
+                apr_ldap_option_set(p, NULL, APR_LDAP_OPT_TLS_CERT, (void *)cert, &err);
+                ABTS_TRUE(tc, err.rc == APR_SUCCESS);
+            }
+
+        } while (1);
+
+        apr_dir_close(thedir);
+    }
+    return 0;
+}
+
+static apr_status_t bind_interact_simple(apr_ldap_t *ld, unsigned int flags, apr_ldap_bind_interact_t *interact, void *ctx)
+{ 
+
+    switch (interact->id) {
+    case APR_LDAP_INTERACT_DN: {
+        char *dn = getenv("TESTLDAP_DN");
+        apr_buffer_str_set(&interact->result, dn ? dn : "", APR_BUFFER_STRING);
+        break;
+    }
+    case APR_LDAP_INTERACT_PASS: {
+        char *pass = getenv("TESTLDAP_PASS");
+        apr_buffer_str_set(&interact->result, pass ? pass : "opensesame", APR_BUFFER_STRING);
+        break;
+    }
+    default:
+        break;
+    }
+
+    return APR_SUCCESS;
+} 
+  
+static apr_status_t bind_interact_external(apr_ldap_t *ld, unsigned int flags, apr_ldap_bind_interact_t *interact, void *ctx)
+{
+    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: {
+        char *user = getenv("TESTLDAP_USER");
+        apr_buffer_str_set(&interact->result, user ? user : "", APR_BUFFER_STRING);
+        break;
+    }   
+    case APR_LDAP_INTERACT_AUTHNAME: {
+        char *authname = getenv("TESTLDAP_AUTHNAME");
+        apr_buffer_str_set(&interact->result, authname ? authname : "", APR_BUFFER_STRING);
+        break;
+    }
+    case APR_LDAP_INTERACT_PASS: {
+        char *pass = getenv("TESTLDAP_PASS");
+        apr_buffer_str_set(&interact->result, pass ? pass : "opensesame", APR_BUFFER_STRING);
+        break;
+    }    
+    default:
+        break;
+    }   
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t bind_interact_abandon(apr_ldap_t *ld, unsigned int flags, apr_ldap_bind_interact_t *interact, void *ctx)
+{
+
+    switch (interact->id) {
+    case APR_LDAP_INTERACT_USER: {
+        apr_buffer_str_set(&interact->result, "cn=notexist", APR_BUFFER_STRING);
+        break;
+    }
+    case APR_LDAP_INTERACT_AUTHNAME: {
+        apr_buffer_str_set(&interact->result, "cn=notexist", APR_BUFFER_STRING);
+        break;
+    }
+    case APR_LDAP_INTERACT_PASS: {
+        apr_buffer_str_set(&interact->result, "wrongpassword", APR_BUFFER_STRING);
+        break;
+    }
+    default:
+        break;
+    }
+
+    return APR_SUCCESS;
+}
+
+typedef struct test_ldap_connection_t {
+    abts_case *tc;
+    apr_pool_t *pool;
+    apr_ldap_t *ldap;
+    apr_pool_t *bind_pool;
+    const char *mech;
+    apr_ldap_bind_interact_cb *interact;
+    apr_pollcb_t *poll;
+    apr_pollfd_t socket_read;
+    apr_pollfd_t socket_write;
+    apu_err_t err;
+    int must_bind;
+} test_ldap_connection_t;
+
+static apr_status_t test_ldap_compare_cb(apr_ldap_t *ldap,
+                                         apr_status_t status,
+                                         const char *matcheddn,
+                                         apr_ldap_control_t **serverctrls,
+                                         void *ctx, apu_err_t *err)
+{
+    test_ldap_connection_t *test = (test_ldap_connection_t *)ctx;
+
+    /*
+     * Step 13: compare result callback triggered, result is complete.
+     */
+
+    abts_log_message("comparison matcheddn: \n", matcheddn);
+
+    ABTS_INT_EQUAL(test->tc, APR_COMPARE_TRUE, status);
+
+    return APR_SUCCESS;
+}      
+
+static apr_status_t test_ldap_search_entry_cb(apr_ldap_t *ldap,
+                                              const char *dn,
+                                              int eidx,
+                                              int nattrs,
+                                              int aidx,
+                                              const char *attr,
+                                              int nvals,
+                                              int vidx,
+                                              apr_buffer_t *val,
+                                              int binary,
+                                              void *ctx, apu_err_t *err)
+{
+    test_ldap_connection_t *test = (test_ldap_connection_t *)ctx;
+
+    /*
+     * Step 8: search entry callback triggered, start processing results.
+     */
+
+    if (!nattrs && !vidx && attr) {
+        /* first attribute and first value and attr present? output dn */
+        abts_log_message("dn: %s", dn);
+    }
+
+    if (val) {
+        abts_log_message("%s: %s", attr, apr_buffer_pstrdup(test->pool, val));
+    }
+
+    return APR_SUCCESS;
+}
+
+
+static apr_status_t test_ldap_search_result_cb(apr_ldap_t *ldap, 
+                                               apr_status_t status,
+                                               apr_size_t nentries,
+                                               const char *matcheddn,
+                                               apr_ldap_control_t **serverctrls,
+                                               void *ctx, apu_err_t *err)
+{
+    char errbuf[128];
+    test_ldap_connection_t *test = (test_ldap_connection_t *)ctx;
+
+    /*
+     * Step 9: search result callback triggered, finish up.
+     */
+
+    abts_log_message("\n");
+
+    ABTS_INT_EQUAL(test->tc, APR_SUCCESS, status);
+
+    if (APR_SUCCESS == status) {
+
+        /*
+         * Step 10: search successful, it is time to trigger the compare.
+         */
+
+        apr_buffer_t val;
+        apr_buffer_str_set(&val, "top", APR_BUFFER_STRING);
+
+        /*
+         * Step 11: we're writable, trigger a compare, then wait for readable.
+         */
+
+        status = apr_ldap_compare(test->pool, test->ldap, "" /* root dn */,
+                                 "objectclass" /* attribute */, &val /* value */,
+                                 NULL /* serverctls */, NULL /* clientctls */,
+                                 apr_time_from_sec(5) /* timeout */,
+                                 test_ldap_compare_cb, test, &test->err);
+
+        ABTS_INT_EQUAL(test->tc, APR_WANT_READ, status);
+
+        switch (status) {
+        case APR_SUCCESS:
+        case APR_WANT_READ:
+        case APR_WANT_WRITE:
+            break;
+        default:
+            abts_log_message("apr_ldap_result: %s [%s]\n", test->err.reason,
+                              apr_strerror(status, errbuf, sizeof(errbuf)));
+        }
+
+    }
+    else {
+        abts_log_message("apr_ldap_search() failed: %s\n", apr_strerror(status, errbuf, sizeof(errbuf)));
+    }
+
+    return status;
+}
+
+static apr_status_t test_ldap_bind_cb(apr_ldap_t *ldap, apr_status_t status,
+                                         const char *matcheddn,
+                                         apr_ldap_control_t **serverctrls,
+                                         void *ctx, apu_err_t *err)
+{
+    char errbuf[128];
+    test_ldap_connection_t *test = (test_ldap_connection_t *)ctx;
+
+    if (APR_SUCCESS == status) {
+
+        /*
+         * Step 5: bind successful, it is time to trigger the search.
+         */
+
+        const char *attrs[2];
+        attrs[0] = "+";
+        attrs[1] = NULL;
+
+        /*
+         * Step 6: we're writable, trigger a search, then wait for readable.
+         */
+
+        status = apr_ldap_search(test->pool, test->ldap, "" /* root dn */,
+                                 APR_LDAP_SCOPE_BASE, "(objectclass=*)" /* filter */,
+                                 attrs /* attrs */, APR_LDAP_OPT_OFF /* attrsonly */,
+                                 NULL /* serverctls */, NULL /* clientctls */,
+                                 apr_time_from_sec(5) /* timelimit */, 0 /* sizelimit */,
+                                 test_ldap_search_result_cb, test_ldap_search_entry_cb, test, &test->err);
+
+        ABTS_INT_EQUAL(test->tc, APR_WANT_READ, status);
+
+        switch (status) {
+        case APR_SUCCESS:
+        case APR_WANT_READ:
+        case APR_WANT_WRITE:
+            break;
+        default:
+            abts_log_message("apr_ldap_result: %s [%s]\n", test->err.reason,
+                              apr_strerror(status, errbuf, sizeof(errbuf)));
+        }
+
+    }
+    else {
+        abts_log_message("apr_ldap_bind() failed: %s\n", apr_strerror(status, errbuf, sizeof(errbuf)));
+    }
+
+    return status;
+}
+
+static apr_status_t test_ldap_initialise_cb(apr_ldap_t *ldap, apr_status_t status,
+                                            void *ctx, apu_err_t *err)
+{
+    char errbuf[128];
+    test_ldap_connection_t *test = (test_ldap_connection_t *)ctx;
+
+    if (APR_SUCCESS == status) {
+
+        /*
+         * Step 2: we're writable, trigger the initial bind, then wait for readable.
+         */
+
+        status = apr_ldap_bind(test->pool, test->ldap, test->mech, test->interact, NULL,
+                               apr_time_from_sec(0), test_ldap_bind_cb, test, &test->err);
+
+        ABTS_INT_EQUAL(test->tc, APR_WANT_READ, status);
+
+        switch (status) {
+        case APR_SUCCESS:
+        case APR_WANT_READ:
+        case APR_WANT_WRITE:
+            return status;
+        default:
+            abts_log_message("apr_ldap_result: %s [%s]\n", test->err.reason,
+                              apr_strerror(status, errbuf, sizeof(errbuf)));
+            return status;
+        }
+
+    }
+    else {
+        abts_log_message("initialise failed: %s\n", apr_strerror(status, errbuf, sizeof(errbuf)));
+    }
+
+    return status;
+}
+
+static apr_status_t test_ldap_connection_cb(void *baton, apr_pollfd_t *descriptor)
+{
+    char errbuf[128];
+    test_ldap_connection_t *test = (test_ldap_connection_t *) baton;
+
+    apr_status_t status = APR_SUCCESS;
+
+    /* remove our event */
+    apr_pollcb_remove(test->poll, descriptor);
+
+    /* are we ready to write? */
+    if (descriptor->rtnevents & APR_POLLOUT) {
+
+        /*
+         * Step 4: bind callback triggered, continue the bind processing.
+         */
+
+        /* handle oustanding tasks */
+        status = apr_ldap_process(test->pool, test->ldap, apr_time_from_sec(0), &test->err);
+
+        switch (status) {
+        case APR_SUCCESS:
+        case APR_WANT_READ:
+        case APR_WANT_WRITE:
+            break;
+        default:
+            abts_log_message("apr_ldap_result: %s [%s]\n", test->err.reason,
+                              apr_strerror(status, errbuf, sizeof(errbuf)));
+        }
+
+    }
+
+    /* are we ready to read? */
+    else if (descriptor->rtnevents & APR_POLLIN) {
+
+        /*
+         * Step 3: we're readable, fetch the bind result, then request a write to be scheduled.
+         */
+
+        /*
+         * Step 7: we're readable, fetch the search result, then trigger search callback.
+         */
+
+        /*
+         * Step 12: we're readable, fetch the compare result, then trigger compare callback.
+         */
+
+        status = apr_ldap_result(test->pool, test->ldap, -1, &test->err);
+
+        switch (status) {
+        case APR_SUCCESS:
+        case APR_WANT_READ:
+        case APR_WANT_WRITE:
+            break;
+        default:
+            abts_log_message("apr_ldap_result: %s [%s]\n", test->err.reason,
+                              apr_strerror(status, errbuf, sizeof(errbuf)));
+        }
+
+    }
+
+    return status;
+}
+
+/*
+ * Asynchronous test of an LDAP connection.
+ *
+ * Like all async code, we jump around a lot. In this example we make
+ * an attempt to bind, and that that was successful, we make an
+ * attempt to search for the root DSE, then if that was successful,
+ * we attempt a compare.
+ *
+ * Start at "Step 1" and follow each step through the callbacks.
+ */
+
+static void test_ldap_connection(abts_case *tc, apr_pool_t *pool, apr_ldap_t *ldap,
+                                 const char *mech, apr_ldap_bind_interact_cb *interact)
+{
+    char errbuf[128];
+    apu_err_t err;
+    test_ldap_connection_t test;
+    apr_ldap_opt_t opt;
+    apr_status_t status;
+
+    memset(&test, 0, sizeof(test_ldap_connection_t));
+ 
+    test.tc = tc;
+    test.pool = pool;
+    test.ldap = ldap;
+    test.mech = mech;
+    test.interact = interact;
+
+    /* always default to LDAP V3 */
+    opt.pv = APR_LDAP_VERSION3;
+    apr_ldap_option_set(pool, ldap, APR_LDAP_OPT_PROTOCOL_VERSION, &opt, &err);
+
+
+    /*
+     * Step 1: call us back when we're writable.
+     */
+
+    /* set the initialise callback */
+    apr_ldap_prepare(pool, ldap, test_ldap_initialise_cb, &test);
+
+
+    apr_ldap_option_get(pool, ldap, APR_LDAP_OPT_HANDLE, &opt, &err);
+    ABTS_TRUE(tc, opt.handle != NULL);
+
+    status = apr_ldap_connect(pool, ldap, apr_time_from_sec(5), &err);
+
+    if (APR_SUCCESS != status) {
+        abts_log_message("%s - %s (%d)\n", err.reason, err.msg, err.rc);
+        return;
+    }
+
+
+    status = apr_ldap_option_get(pool, ldap, APR_LDAP_OPT_DESC, &opt, &err);
+    ABTS_TRUE(tc, status == APR_SUCCESS);
+    ABTS_ASSERT(tc, "failed to get descriptor", opt.socket != NULL);
+
+    status = apr_pollcb_create(&test.poll, 1, pool, APR_POLLSET_DEFAULT);
+    if (APR_ENOTIMPL == status) {
+        abts_log_message("apr_pollcb_create() not implemented on this platform, skipping test\n");
+        return;
+    }
+    ABTS_TRUE(tc, status == APR_SUCCESS);
+    if (APR_SUCCESS != status) {
+        return;
+    }
+
+    /* set up read descriptor */
+    test.socket_read.desc_type = APR_POLL_SOCKET;
+    test.socket_read.reqevents = APR_POLLIN;
+    test.socket_read.desc.s = opt.socket;
+    test.socket_read.client_data = opt.socket;
+
+    /* set up write descriptor */
+    test.socket_write.desc_type = APR_POLL_SOCKET;
+    test.socket_write.reqevents = APR_POLLOUT;
+    test.socket_write.desc.s = opt.socket;
+    test.socket_write.client_data = opt.socket;
+
+    status = APR_WANT_WRITE;
+
+    do {
+
+        if (APR_SUCCESS == status) {
+            abts_log_message("apr_pollcb_poll() complete\n");
+            break;
+        }
+        else if (APR_WANT_READ == status) {
+
+            /* wait for socket to be readable, then process another result */
+            status = apr_pollcb_add(test.poll, &test.socket_read);
+            ABTS_INT_EQUAL(tc, APR_SUCCESS, status);
+            if (APR_SUCCESS != status) {
+                break;
+            }
+
+        }
+        else if (APR_WANT_WRITE == status) {
+
+            /* wait for socket to be writeable, then process result */
+            status = apr_pollcb_add(test.poll, &test.socket_write);
+            ABTS_INT_EQUAL(tc, APR_SUCCESS, status);
+            if (APR_SUCCESS != status) {
+                break;
+            }
+
+        }
+        else {
+            abts_log_message("apr_ldap_process/apr_ldap_result: %s [%s]\n", test.err.reason,
+                              apr_strerror(status, errbuf, sizeof(errbuf)));
+            break;
+        }
+
+        status = apr_pollcb_poll(test.poll, -1, test_ldap_connection_cb, &test);
+
+    } while (1);
+
+
+    /*
+     * Step 14: bind, search, compare, result is complete, event loop unwound.
+     */
+
+    return;
+}
+
+/*
+ * Synchronous test of an LDAP connection.
+ *
+ * The code below is identical to the above code, but uses the event loop inside
+ * apr_ldap_poll().
+ *
+ * The initial callback test_ldap_initialise_cb is set, all queries are bootstrapped
+ * from this callback.
+ */
+
+#if 0
+static void test_ldap_connection(abts_case *tc, apr_pool_t *pool, apr_ldap_t *ldap,
+                                 const char *mech, apr_ldap_bind_interact_cb *interact)
+{
+    char errbuf[128];
+    apu_err_t err;
+    test_ldap_connection_t test;
+    apr_ldap_opt_t opt;
+    apr_status_t status;
+
+    memset(&test, 0, sizeof(test_ldap_connection_t));
+
+    test.tc = tc;
+    test.pool = pool;
+    test.ldap = ldap;
+    test.mech = mech;
+    test.interact = interact;
+
+
+    /* always default to LDAP V3 */
+    opt.pv = APR_LDAP_VERSION3;
+    apr_ldap_option_set(pool, ldap, APR_LDAP_OPT_PROTOCOL_VERSION, &opt, &err);
+
+
+    /* set the initialise callback */
+    apr_ldap_prepare(pool, ldap, test_ldap_initialise_cb, &test);
+
+
+    /* initial connect */
+    status = apr_ldap_connect(pool, ldap, apr_time_from_sec(5), &err);
+
+    if (APR_SUCCESS != status) {
+        abts_log_message("%s - %s (%d)\n", err.reason, err.msg, err.rc);
+        return;
+    }
+
+
+    /* run the callbacks */
+    status = apr_ldap_poll(pool, ldap, apr_time_from_sec(5), &err);
+
+    if (APR_SUCCESS == status) {
+        abts_log_message("apr_pollcb_poll() complete\n");
+    }
+    else {
+        abts_log_message("apr_ldap_process/apr_ldap_result: %s [%s]\n", test.err.reason,
+                              apr_strerror(status, errbuf, sizeof(errbuf)));
+    }
+
+
+    return;
+}
+#endif
+
+
+static apr_status_t test_ldap_abandon_cb(void *baton, apr_pollfd_t *descriptor)
+{
+    char errbuf[128];
+    test_ldap_connection_t *test = (test_ldap_connection_t *) baton;
+
+    apr_status_t status = APR_SUCCESS;
+
+    /* remove our event */
+    apr_pollcb_remove(test->poll, descriptor);
+
+    /* are we ready to write? */
+    if (descriptor->rtnevents & APR_POLLOUT) {
+
+        if (test->must_bind) {
+
+            /*
+             * Step B: we're writable, trigger the initial bind from the bind_pool, then wait for readable.
+             */
+
+            status = apr_ldap_bind(test->bind_pool, test->ldap, test->mech, test->interact, NULL, apr_time_from_sec(0), test_ldap_bind_cb, test, &test->err);
+
+            test->must_bind = 0;
+
+            ABTS_INT_EQUAL(test->tc, APR_WANT_READ, status);
+
+            if (APR_SUCCESS == status) {
+                /* should not have happened */
+                return status;
+            }
+            else if (APR_WANT_READ == status) {
+
+                /*
+                 * Step C: spanner in the works - lets abandon the bind.
+                 */
+
+                apr_pool_destroy(test->bind_pool);
+
+                test->bind_pool = NULL;
+
+                abts_log_message("bind abandon triggered\n");
+
+                /* and then continue as if nothing had happened */
+
+                return status;
+
+            }
+            else {
+                return status;
+            }
+
+        }
+
+        /*
+         * Step E: write callback triggered, send the abandon.
+         */
+
+        /* handle oustanding tasks */
+        status = apr_ldap_process(test->pool, test->ldap, apr_time_from_sec(0), &test->err);
+
+        ABTS_INT_EQUAL(test->tc, APR_SUCCESS, status);
+
+        switch (status) {
+        case APR_SUCCESS:
+        case APR_WANT_READ:
+        case APR_WANT_WRITE:
+            return status;
+        default:
+            abts_log_message("apr_ldap_result: %s [%s]\n", test->err.reason,
+                              apr_strerror(status, errbuf, sizeof(errbuf)));
+            return status;
+        }
+
+    }
+
+    /* are we ready to read? */
+    if (descriptor->rtnevents & APR_POLLIN) {
+
+        /*
+         * Step D: we're readable, but our bind has been abandoned.
+         */
+
+        status = apr_ldap_result(test->pool, test->ldap, -1, &test->err);
+
+        ABTS_INT_EQUAL(test->tc, APR_WANT_WRITE, status);
+
+        switch (status) {
+        case APR_SUCCESS:
+        case APR_WANT_READ:
+        case APR_WANT_WRITE:
+            return status;
+        default:
+            abts_log_message("apr_ldap_result: %s [%s]\n", test->err.reason,
+                              apr_strerror(status, errbuf, sizeof(errbuf)));
+            return status;
+        }
+
+    }
+
+    return status;
+}
+
+static void test_ldap_abandon(abts_case *tc, apr_pool_t *pool, apr_ldap_t *ldap)
+{
+    char errbuf[128];
+    apu_err_t err;
+    test_ldap_connection_t test;
+    apr_ldap_opt_t opt;
+    apr_status_t status;
+
+    memset(&test, 0, sizeof(test_ldap_connection_t));
+
+    test.tc = tc;
+    test.pool = pool;
+    test.ldap = ldap;
+    test.mech = "PLAIN";
+    test.interact = bind_interact_abandon;
+
+    test.must_bind = 1;
+
+    apr_pool_create(&test.bind_pool, test.pool);
+
+
+    /* always default to LDAP V3 */
+    opt.pv = APR_LDAP_VERSION3;
+    apr_ldap_option_set(pool, ldap, APR_LDAP_OPT_PROTOCOL_VERSION, &opt, &err);
+
+    apr_ldap_option_get(pool, ldap, APR_LDAP_OPT_HANDLE, &opt, &err);
+    ABTS_TRUE(tc, opt.handle != NULL);
+
+    status = apr_ldap_connect(pool, ldap, apr_time_from_sec(5), &err);
+
+    if (APR_SUCCESS != status) {
+        abts_log_message("%s - %s (%d)\n", err.reason, err.msg, err.rc);
+        return;
+    }
+
+    status = apr_ldap_option_get(pool, ldap, APR_LDAP_OPT_DESC, &opt, &err);
+    ABTS_TRUE(tc, status == APR_SUCCESS);
+    ABTS_ASSERT(tc, "failed to get descriptor", opt.socket != NULL);
+
+    status = apr_pollcb_create(&test.poll, 1, pool, APR_POLLSET_DEFAULT);
+    if (APR_ENOTIMPL == status) {
+        abts_log_message("apr_pollcb_create() not implemented on this platform, skipping test\n");
+        return;
+    }
+    ABTS_TRUE(tc, status == APR_SUCCESS);
+    if (APR_SUCCESS != status) {
+        return;
+    }
+
+    /* set up read descriptor */
+    test.socket_read.desc_type = APR_POLL_SOCKET;
+    test.socket_read.reqevents = APR_POLLIN;
+    test.socket_read.desc.s = opt.socket;
+    test.socket_read.client_data = opt.socket;
+
+    /* set up write descriptor */
+    test.socket_write.desc_type = APR_POLL_SOCKET;
+    test.socket_write.reqevents = APR_POLLOUT;
+    test.socket_write.desc.s = opt.socket;
+    test.socket_write.client_data = opt.socket;
+
+
+    /*
+     * Step A: call us back when we're writable.
+     */
+
+    status = APR_WANT_WRITE;
+
+    do {
+
+        if (APR_SUCCESS == status) {
+            abts_log_message("apr_pollcb_poll() complete\n");
+            break;
+        }
+        else if (APR_WANT_READ == status) {
+
+            /* wait for socket to be readable, then process another result */
+            status = apr_pollcb_add(test.poll, &test.socket_read);
+            ABTS_INT_EQUAL(tc, APR_SUCCESS, status);
+            if (APR_SUCCESS != status) {
+                break;
+            }
+
+        }
+        else if (APR_WANT_WRITE == status) {
+
+            /* wait for socket to be writeable, then process result */
+            status = apr_pollcb_add(test.poll, &test.socket_write);
+            ABTS_INT_EQUAL(tc, APR_SUCCESS, status);
+            if (APR_SUCCESS != status) {
+                break;
+            }
+
+        }
+        else {
+            abts_log_message("apr_ldap_result: %s [%s]\n", test.err.reason,
+                              apr_strerror(status, errbuf, sizeof(errbuf)));
+            break;
+        }
+
+        status = apr_pollcb_poll(test.poll, -1, test_ldap_abandon_cb, &test);
+
+    } while (1);
+
+    /*
+     * Step F: bind, result, abandon is complete, event loop unwound.
+     */
+
+    return;
+}
+
+
+static void test_ldap_global_opts(abts_case *tc, void *data)
+{
+    apr_pool_t *pool;
+    apu_err_t err;
+    apr_ldap_opt_t opt;
+    apr_status_t status;
+
+    apr_pool_create(&pool, p);
+
+    status = apr_ldap_option_get(pool, NULL, APR_LDAP_OPT_API_INFO, &opt, &err);
+
+    ABTS_TRUE(tc, status == APR_SUCCESS);
+    ABTS_TRUE(tc, err.rc == 0);
+    ABTS_TRUE(tc, opt.info.vendor_name != NULL);
+
+    opt.ldfi.name = "THREAD_SAFE";
+    status = apr_ldap_option_get(pool, NULL, APR_LDAP_OPT_API_FEATURE_INFO, &opt, &err);
+    /* feature may exist, or may not */
+    ABTS_TRUE(tc, status == APR_SUCCESS || status == APR_EINVAL);
+
+    apr_pool_destroy(pool);
+}
+
+static void test_ldap_opts(abts_case *tc, void *data)
+{
+    apr_pool_t *pool;
+    apr_ldap_t *ldap = NULL;
+    apu_err_t err;
+    const char *url;
+
+    apr_pool_create(&pool, p);
+
+    url = apr_psprintf(pool, "ldap://%s:%d", "localhost", APR_LDAP_PORT);
+
+    apr_ldap_initialise(pool, &ldap, &(err));
+
+    ABTS_TRUE(tc, ldap != NULL);
+
+    if (ldap) {
+
+        apr_status_t status;
+        apr_ldap_opt_t opt;
+
+        opt.uri = url;
+        status = apr_ldap_option_set(pool, ldap, APR_LDAP_OPT_URI, &opt, &err);
+        ABTS_TRUE(tc, status == APR_SUCCESS); 
+        status = apr_ldap_option_get(pool, ldap, APR_LDAP_OPT_URI, &opt, &err);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        ABTS_ASSERT(tc, "failed to set uri", !strcmp(opt.uri, url));
+
+        opt.pv = APR_LDAP_VERSION3;
+        status = apr_ldap_option_set(pool, ldap, APR_LDAP_OPT_PROTOCOL_VERSION, &opt, &err);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        status = apr_ldap_option_get(pool, ldap, APR_LDAP_OPT_PROTOCOL_VERSION, &opt, &err);
+        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_option_set(pool, ldap, APR_LDAP_OPT_DEREF, &opt, &err);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        status = apr_ldap_option_get(pool, ldap, APR_LDAP_OPT_DEREF, &opt, &err);
+        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_option_set(pool, ldap, APR_LDAP_OPT_REFERRALS, &opt, &err);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        status = apr_ldap_option_get(pool, ldap, APR_LDAP_OPT_REFERRALS, &opt, &err);
+        ABTS_TRUE(tc, status == APR_SUCCESS);
+        ABTS_ASSERT(tc, "failed to set referrals", opt.refs == APR_LDAP_OPT_ON);
+
+#if 0
+        opt.refhoplimit = 5;
+        status = apr_ldap_option_set(pool, ldap, APR_LDAP_OPT_REFHOPLIMIT, &opt, &err);
+        if (APR_ENOTIMPL != status) {
+            ABTS_TRUE(tc, status == APR_SUCCESS); 
+            status = apr_ldap_option_get(pool, ldap, APR_LDAP_OPT_REFHOPLIMIT, &opt, &err);
+            ABTS_TRUE(tc, status == APR_SUCCESS); 
+            ABTS_ASSERT(tc, "failed to set refhoplimit", opt.refhoplimit == 5);
+        }
+#endif
+
+    }
+
+    apr_pool_destroy(pool);
+}
+
+static void test_ldap(abts_case *tc, void *data)
+{
+    apr_pool_t *pool;
+    apr_ldap_t *ldap;
+    apu_err_t err;
+    const char *url;
+    apr_ldap_opt_t opt;
+    apr_status_t status;
+
+    const char *testldap = getenv("TESTLDAP");
+
+    if (!testldap) {
+        ABTS_NOT_IMPL(tc, "Environment TESTLDAP unset - skipping");
+        return;
+    }
+
+    apr_pool_create(&pool, p);
+
+    url = apr_psprintf(pool, "ldap://%s:%d", testldap, APR_LDAP_PORT);
+    
+    apr_ldap_initialise(pool, &ldap, &(err));
+
+    ABTS_TRUE(tc, ldap != NULL);
+
+    opt.uri = url;
+    status = apr_ldap_option_set(pool, ldap, APR_LDAP_OPT_URI, &opt, &err);
+
+    if (status == APR_SUCCESS) {
+        test_ldap_connection(tc, pool, ldap, NULL, bind_interact_simple);
+    }
+
+    apr_pool_destroy(pool);
+}
+
+static void test_ldaps(abts_case *tc, void *data)
+{
+    apr_pool_t *pool;
+    apr_ldap_t *ldap;
+    apu_err_t err;
+    const char *url; 
+    apr_ldap_opt_t opt;
+    apr_status_t status;
+
+    const char *testldaps = getenv("TESTLDAPS");
+
+    if (!testldaps) {
+        ABTS_NOT_IMPL(tc, "Environment TESTLDAPS unset - skipping");
+        return;
+    }
+
+    apr_pool_create(&pool, p);
+
+    url = apr_psprintf(pool, "ldaps://%s:%d", testldaps, APR_LDAPS_PORT);
+
+    apr_ldap_initialise(pool, &ldap, &(err));
+
+    ABTS_TRUE(tc, ldap != NULL);
+
+    opt.uri = url;
+    status = apr_ldap_option_set(pool, ldap, APR_LDAP_OPT_URI, &opt, &err);
+
+    if (status == APR_SUCCESS) {
+        add_ldap_certs(tc);
+
+        test_ldap_connection(tc, pool, ldap, "EXTERNAL", bind_interact_external);
+        test_ldap_connection(tc, pool, ldap, "PLAIN", bind_interact_plain);
+        test_ldap_connection(tc, pool, ldap, NULL, bind_interact_simple);
+    }
+
+    apr_pool_destroy(pool);
+}
+
+static void test_ldap_tls(abts_case *tc, void *data)
+{
+    apr_pool_t *pool;
+    apr_ldap_t *ldap;
+    apu_err_t err;
+    const char *url;
+    apr_ldap_opt_t opt;
+    apr_status_t status;
+
+    const char *testldap_tls = getenv("TESTLDAP_TLS");
+
+    if (!testldap_tls) {
+        ABTS_NOT_IMPL(tc, "Environment TESTLDAP_TLS unset - skipping");
+        return;
+    }
+
+    apr_pool_create(&pool, p);
+
+    opt.tls = APR_LDAP_TLS_STARTTLS;
+
+    url = apr_psprintf(pool, "ldap://%s:%d", testldap_tls, APR_LDAP_PORT);
+
+    apr_ldap_initialise(pool, &ldap, &(err));
+
+    if (err.rc == APR_SUCCESS) {
+        apr_ldap_option_set(pool, ldap, APR_LDAP_OPT_TLS, &opt, &(err));
+    }
+
+    ABTS_TRUE(tc, ldap != NULL);
+
+    opt.uri = url;
+    status = apr_ldap_option_set(pool, ldap, APR_LDAP_OPT_URI, &opt, &err);
+
+    if (status == APR_SUCCESS) {
+        add_ldap_certs(tc);
+
+        test_ldap_connection(tc, pool, ldap, "EXTERNAL", bind_interact_external);
+        test_ldap_connection(tc, pool, ldap, "PLAIN", bind_interact_plain);
+        test_ldap_connection(tc, pool, ldap, NULL, bind_interact_simple);
+    }
+
+    apr_pool_destroy(pool);
+}
+
+static void test_ldapi(abts_case *tc, void *data)
+{
+    apr_pool_t *pool;
+    apr_ldap_t *ldap;
+    apu_err_t err;
+    const char *url;
+    apr_ldap_opt_t opt;
+    apr_status_t status;
+
+    const char *testldapi = getenv("TESTLDAPI");
+
+    if (!testldapi) {
+        ABTS_NOT_IMPL(tc, "Environment TESTLDAPI unset - skipping");
+        return;
+    }
+
+    apr_pool_create(&pool, p);
+
+    opt.tls = APR_LDAP_TLS_STARTTLS;
+
+    url = apr_psprintf(pool, "ldapi://%s", apr_pescape_urlencoded(pool, testldapi));
+
+    apr_ldap_initialise(pool, &ldap, &(err));
+
+    ABTS_TRUE(tc, ldap != NULL);
+
+    opt.uri = url;
+    status = apr_ldap_option_set(pool, ldap, APR_LDAP_OPT_URI, &opt, &err);
+    ABTS_TRUE(tc, status == APR_SUCCESS);
+
+    abts_log_message("Initialiased LDAP url [%s]: %s - %s (%d)\n", url, err.reason, err.msg, err.rc);
+
+    if (status == APR_SUCCESS) {
+        test_ldap_connection(tc, pool, ldap, "EXTERNAL", bind_interact_external);
+        test_ldap_connection(tc, pool, ldap, "PLAIN", bind_interact_plain);
+        test_ldap_connection(tc, pool, ldap, NULL, bind_interact_simple);
+
+        test_ldap_abandon(tc, pool, ldap);
+    }
+
+    status = apr_ldap_unbind(ldap, NULL, NULL, &err);
+    ABTS_TRUE(tc, status == APR_SUCCESS);
+
+    apr_pool_destroy(pool);
+}
+
+#endif /* APR_HAS_LDAP */
+
+abts_suite *testldap(abts_suite *suite)
+{
+#if APR_HAS_LDAP
+    suite = ADD_SUITE(suite);
+
+    abts_run_test(suite, test_ldap_global_opts, NULL);
+    abts_run_test(suite, test_ldap_opts, NULL);
+
+    abts_run_test(suite, test_ldap, NULL);
+    abts_run_test(suite, test_ldaps, NULL);
+    abts_run_test(suite, test_ldap_tls, NULL);
+    abts_run_test(suite, test_ldapi, NULL);
+
+#endif /* APR_HAS_LDAP */
+
+    return suite;
+}
+

Propchange: apr/apr/trunk/test/testldap.c
------------------------------------------------------------------------------
    svn:eol-style = native



Re: svn commit: r1917099 - /apr/apr/trunk/test/testldap.c

Posted by Ivan Zhakov via dev <de...@apr.apache.org>.
On Thu, 18 Apr 2024 at 09:58, Graham Leggett via dev <de...@apr.apache.org>
wrote:

> On 18 Apr 2024, at 08:48, minfrin@apache.org wrote:
>
> > URL: http://svn.apache.org/viewvc?rev=1917099&view=rev
> > Log:
> > Add the LDAP test case.
>
> I am getting a failure on Windows about a missing symbol, I am assuming
> the changes to cmake are incomplete.
>
> Would it be possible for someone to look at the cmake / visual studio
> config and confirm what I've missed?
>
> abts.obj : error LNK2001: unresolved external symbol testldap
> [D:\a\apr\apr\build\testall.vcxproj]
> D:\a\apr\apr\build\Debug\testall.exe : fatal error LNK1120: 1 unresolved
> externals [D:\a\apr\apr\build\testall.vcxproj]
>
> I fixed it in r1917104 <https://svn.apache.org/r1917104>.

PS: Maybe it makes sense to switch to using CMake for all platforms to
avoid such problems in future...

-- 
Ivan Zhakov

Re: svn commit: r1917099 - /apr/apr/trunk/test/testldap.c

Posted by Graham Leggett via dev <de...@apr.apache.org>.
On 18 Apr 2024, at 08:48, minfrin@apache.org wrote:

> URL: http://svn.apache.org/viewvc?rev=1917099&view=rev
> Log:
> Add the LDAP test case.

I am getting a failure on Windows about a missing symbol, I am assuming the changes to cmake are incomplete.

Would it be possible for someone to look at the cmake / visual studio config and confirm what I've missed?

abts.obj : error LNK2001: unresolved external symbol testldap [D:\a\apr\apr\build\testall.vcxproj]
D:\a\apr\apr\build\Debug\testall.exe : fatal error LNK1120: 1 unresolved externals [D:\a\apr\apr\build\testall.vcxproj]

Regards,
Graham
--