summaryrefslogtreecommitdiffstats
path: root/tests/progs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/progs')
-rw-r--r--tests/progs/Makefile.in66
-rw-r--r--tests/progs/ldif-filter.c256
-rw-r--r--tests/progs/slapd-addel.c435
-rw-r--r--tests/progs/slapd-bind.c609
-rw-r--r--tests/progs/slapd-common.c300
-rw-r--r--tests/progs/slapd-common.h44
-rw-r--r--tests/progs/slapd-modify.c318
-rw-r--r--tests/progs/slapd-modrdn.c310
-rw-r--r--tests/progs/slapd-mtread.c837
-rw-r--r--tests/progs/slapd-read.c568
-rw-r--r--tests/progs/slapd-search.c618
-rw-r--r--tests/progs/slapd-tester.c1197
12 files changed, 5558 insertions, 0 deletions
diff --git a/tests/progs/Makefile.in b/tests/progs/Makefile.in
new file mode 100644
index 0000000..1f316ae
--- /dev/null
+++ b/tests/progs/Makefile.in
@@ -0,0 +1,66 @@
+## Makefile.in for test programs
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2021 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+PROGRAMS = slapd-tester slapd-search slapd-read slapd-addel slapd-modrdn \
+ slapd-modify slapd-bind slapd-mtread ldif-filter
+
+SRCS = slapd-common.c \
+ slapd-tester.c slapd-search.c slapd-read.c slapd-addel.c \
+ slapd-modrdn.c slapd-modify.c slapd-bind.c slapd-mtread.c \
+ ldif-filter.c
+
+LDAP_INCDIR= ../../include
+LDAP_LIBDIR= ../../libraries
+
+XLIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLUTIL_A) $(LDAP_LIBLBER_LA)
+XRLIBS = $(LDAP_LIBLDAP_R_LA) $(LDAP_LIBLUTIL_A) $(LDAP_LIBLBER_LA)
+XXLIBS = $(SECURITY_LIBS) $(LUTIL_LIBS)
+RLIBS = $(XRLIBS) $(XXLIBS) $(AC_LIBS) $(XXXLIBS)
+
+
+OBJS = slapd-common.o
+
+# build-tools: FORCE
+# $(MAKE) $(MFLAGS) load-tools
+
+# load-tools: $(PROGRAMS)
+
+slapd-tester: slapd-tester.o $(OBJS) $(XLIBS)
+ $(LTLINK) -o $@ slapd-tester.o $(OBJS) $(LIBS)
+
+slapd-search: slapd-search.o $(OBJS) $(XLIBS)
+ $(LTLINK) -o $@ slapd-search.o $(OBJS) $(LIBS)
+
+slapd-read: slapd-read.o $(OBJS) $(XLIBS)
+ $(LTLINK) -o $@ slapd-read.o $(OBJS) $(LIBS)
+
+slapd-addel: slapd-addel.o $(OBJS) $(XLIBS)
+ $(LTLINK) -o $@ slapd-addel.o $(OBJS) $(LIBS)
+
+slapd-modrdn: slapd-modrdn.o $(OBJS) $(XLIBS)
+ $(LTLINK) -o $@ slapd-modrdn.o $(OBJS) $(LIBS)
+
+slapd-modify: slapd-modify.o $(OBJS) $(XLIBS)
+ $(LTLINK) -o $@ slapd-modify.o $(OBJS) $(LIBS)
+
+slapd-bind: slapd-bind.o $(OBJS) $(XLIBS)
+ $(LTLINK) -o $@ slapd-bind.o $(OBJS) $(LIBS)
+
+ldif-filter: ldif-filter.o $(XLIBS)
+ $(LTLINK) -o $@ ldif-filter.o $(LIBS)
+
+slapd-mtread: slapd-mtread.o $(OBJS) $(XRLIBS)
+ $(LTLINK) -o $@ slapd-mtread.o $(OBJS) $(RLIBS)
+
diff --git a/tests/progs/ldif-filter.c b/tests/progs/ldif-filter.c
new file mode 100644
index 0000000..2b40399
--- /dev/null
+++ b/tests/progs/ldif-filter.c
@@ -0,0 +1,256 @@
+/* ldif-filter -- clean up LDIF testdata from stdin */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2009-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/ctype.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#ifdef _WIN32
+#include <fcntl.h>
+#endif
+
+#define DEFAULT_SPECS "ndb=a,null=n"
+
+typedef struct { char *val; size_t len, alloc; } String;
+typedef struct { String *val; size_t len, alloc; } Strings;
+
+/* Flags and corresponding program options */
+enum { SORT_ATTRS = 1, SORT_ENTRIES = 2, NO_OUTPUT = 4, DUMMY_FLAG = 8 };
+static const char spec_options[] = "aen"; /* option index = log2(enum flag) */
+
+static const char *progname = "ldif-filter";
+static const String null_string = { NULL, 0, 0 };
+
+static void
+usage( void )
+{
+ fprintf( stderr, "\
+Usage: %s [-b backend] [-s spec[,spec]...]\n\
+Filter standard input by first <spec> matching '[<backend>]=[a][e][n]':\n\
+ - Remove LDIF comments.\n\
+ - 'a': Sort attributes in entries.\n\
+ - 'e': Sort any entries separated by just one empty line.\n\
+ - 'n': Output nothing.\n\
+<backend> defaults to the $BACKEND environment variable.\n\
+Use specs '%s' if no spec on the command line applies.\n",
+ progname, DEFAULT_SPECS );
+ exit( EXIT_FAILURE );
+}
+
+/* Return flags from "backend=flags" in spec; nonzero if backend found */
+static unsigned
+get_flags( const char *backend, const char *spec )
+{
+ size_t len = strlen( backend );
+ unsigned flags = DUMMY_FLAG;
+ const char *end, *tmp;
+
+ for ( ;; spec = end + ( *end != '\0' )) {
+ if ( !*spec )
+ return 0;
+ end = spec + strcspn( spec, "," );
+ if ( !(tmp = memchr( spec, '=', end-spec )))
+ break;
+ if ( tmp-spec == len && !memcmp( spec, backend, len )) {
+ spec = tmp+1;
+ break;
+ }
+ }
+
+ for ( ; spec < end; spec++ ) {
+ if ( (tmp = strchr( spec_options, *spec )) == NULL ) {
+ usage();
+ }
+ flags |= 1U << (tmp - spec_options);
+ }
+ return flags;
+}
+
+#define APPEND(s /* String or Strings */, data, count, isString) do { \
+ size_t slen = (s)->len, salloc = (s)->alloc, sz = sizeof *(s)->val; \
+ if ( salloc <= slen + (count) ) { \
+ (s)->alloc = salloc += salloc + ((count)|7) + 1; \
+ (s)->val = xrealloc( (s)->val, sz * salloc ); \
+ } \
+ memcpy( (s)->val + slen, data, sz * ((count) + !!(isString)) ); \
+ (s)->len = slen + (count); \
+} while (0)
+
+static void *
+xrealloc( void *ptr, size_t len )
+{
+ if ( (ptr = realloc( ptr, len )) == NULL ) {
+ perror( progname );
+ exit( EXIT_FAILURE );
+ }
+ return ptr;
+}
+
+static int
+cmp( const void *s, const void *t )
+{
+ return strcmp( ((const String *) s)->val, ((const String *) t)->val );
+}
+
+static void
+sort_strings( Strings *ss, size_t offset )
+{
+ qsort( ss->val + offset, ss->len - offset, sizeof(*ss->val), cmp );
+}
+
+/* Build entry ss[n] from attrs ss[n...], and free the attrs */
+static void
+build_entry( Strings *ss, size_t n, unsigned flags, size_t new_len )
+{
+ String *vals = ss->val, *e = &vals[n];
+ size_t end = ss->len;
+ char *ptr;
+
+ if ( flags & SORT_ATTRS ) {
+ sort_strings( ss, n + 1 );
+ }
+ e->val = xrealloc( e->val, e->alloc = new_len + 1 );
+ ptr = e->val + e->len;
+ e->len = new_len;
+ ss->len = ++n;
+ for ( ; n < end; free( vals[n++].val )) {
+ ptr = strcpy( ptr, vals[n].val ) + vals[n].len;
+ }
+ assert( ptr == e->val + new_len );
+}
+
+/* Flush entries to stdout and free them */
+static void
+flush_entries( Strings *ss, const char *sep, unsigned flags )
+{
+ size_t i, end = ss->len;
+ const char *prefix = "";
+
+ if ( flags & SORT_ENTRIES ) {
+ sort_strings( ss, 0 );
+ }
+ for ( i = 0; i < end; i++, prefix = sep ) {
+ if ( printf( "%s%s", prefix, ss->val[i].val ) < 0 ) {
+ perror( progname );
+ exit( EXIT_FAILURE );
+ }
+ free( ss->val[i].val );
+ }
+ ss->len = 0;
+}
+
+static void
+filter_stdin( unsigned flags )
+{
+ char line[256];
+ Strings ss = { NULL, 0, 0 }; /* entries + attrs of partial entry */
+ size_t entries = 0, attrs_totlen = 0, line_len;
+ const char *entry_sep = "\n", *sep = "";
+ int comment = 0, eof = 0, eol, prev_eol = 1; /* flags */
+ String *s;
+
+ /* LDIF = Entries ss[..entries-1] + sep + attrs ss[entries..] + line */
+ for ( ; !eof || ss.len || *sep; prev_eol = eol ) {
+ if ( eof || (eof = !fgets( line, sizeof(line), stdin ))) {
+ strcpy( line, prev_eol ? "" : *sep ? sep : "\n" );
+ }
+ line_len = strlen( line );
+ eol = (line_len == 0 || line[line_len - 1] == '\n');
+
+ if ( *line == ' ' ) { /* continuation line? */
+ prev_eol = 0;
+ } else if ( prev_eol ) { /* start of logical line? */
+ comment = (*line == '#');
+ }
+ if ( comment || (flags & NO_OUTPUT) ) {
+ continue;
+ }
+
+ /* Collect attrs for partial entry in ss[entries...] */
+ if ( !prev_eol && attrs_totlen != 0 ) {
+ goto grow_attr;
+ } else if ( line_len > (*line == '\r' ? 2 : 1) ) {
+ APPEND( &ss, &null_string, 1, 0 ); /* new attr */
+ grow_attr:
+ s = &ss.val[ss.len - 1];
+ APPEND( s, line, line_len, 1 ); /* strcat to attr */
+ attrs_totlen += line_len;
+ continue;
+ }
+
+ /* Empty line - consume sep+attrs or entries+sep */
+ if ( attrs_totlen != 0 ) {
+ entry_sep = sep;
+ if ( entries == 0 )
+ fputs( sep, stdout );
+ build_entry( &ss, entries++, flags, attrs_totlen );
+ attrs_totlen = 0;
+ } else {
+ flush_entries( &ss, entry_sep, flags );
+ fputs( sep, stdout );
+ entries = 0;
+ }
+ sep = "\r\n" + 2 - line_len; /* sep = copy(line) */
+ }
+
+ free( ss.val );
+}
+
+int
+main( int argc, char **argv )
+{
+ const char *backend = getenv( "BACKEND" ), *specs = "", *tmp;
+ unsigned flags;
+ int i;
+
+ if ( argc > 0 ) {
+ progname = (tmp = strrchr( argv[0], '/' )) ? tmp+1 : argv[0];
+ }
+
+ while ( (i = getopt( argc, argv, "b:s:" )) != EOF ) {
+ switch ( i ) {
+ case 'b':
+ backend = optarg;
+ break;
+ case 's':
+ specs = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+ if ( optind < argc ) {
+ usage();
+ }
+ if ( backend == NULL ) {
+ backend = "";
+ }
+
+#ifdef _WIN32
+ _setmode(1, _O_BINARY); /* don't convert \n to \r\n on stdout */
+#endif
+ flags = get_flags( backend, specs );
+ filter_stdin( flags ? flags : get_flags( backend, DEFAULT_SPECS ));
+ if ( fclose( stdout ) == EOF ) {
+ perror( progname );
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/progs/slapd-addel.c b/tests/progs/slapd-addel.c
new file mode 100644
index 0000000..58e6c86
--- /dev/null
+++ b/tests/progs/slapd-addel.c
@@ -0,0 +1,435 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Kurt Spanier for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/stdlib.h"
+
+#include "ac/ctype.h"
+#include "ac/param.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+#include "ac/unistd.h"
+#include "ac/wait.h"
+
+#include "ldap.h"
+#include "lutil.h"
+
+#include "slapd-common.h"
+
+#define LOOPS 100
+#define RETRIES 0
+
+static char *
+get_add_entry( char *filename, LDAPMod ***mods );
+
+static void
+do_addel( char *uri, char *manager, struct berval *passwd,
+ char *dn, LDAPMod **attrs, int maxloop, int maxretries, int delay,
+ int friendly, int chaserefs );
+
+static void
+usage( char *name )
+{
+ fprintf( stderr,
+ "usage: %s "
+ "-H <uri> | ([-h <host>] -p <port>) "
+ "-D <manager> "
+ "-w <passwd> "
+ "-f <addfile> "
+ "[-i <ignore>] "
+ "[-l <loops>] "
+ "[-L <outerloops>] "
+ "[-r <maxretries>] "
+ "[-t <delay>] "
+ "[-F] "
+ "[-C]\n",
+ name );
+ exit( EXIT_FAILURE );
+}
+
+int
+main( int argc, char **argv )
+{
+ int i;
+ char *host = "localhost";
+ char *uri = NULL;
+ int port = -1;
+ char *manager = NULL;
+ struct berval passwd = { 0, NULL };
+ char *filename = NULL;
+ char *entry = NULL;
+ int loops = LOOPS;
+ int outerloops = 1;
+ int retries = RETRIES;
+ int delay = 0;
+ int friendly = 0;
+ int chaserefs = 0;
+ LDAPMod **attrs = NULL;
+
+ tester_init( "slapd-addel", TESTER_ADDEL );
+
+ while ( ( i = getopt( argc, argv, "CD:Ff:H:h:i:L:l:p:r:t:w:" ) ) != EOF )
+ {
+ switch ( i ) {
+ case 'C':
+ chaserefs++;
+ break;
+
+ case 'F':
+ friendly++;
+ break;
+
+ case 'H': /* the server's URI */
+ uri = strdup( optarg );
+ break;
+
+ case 'h': /* the servers host */
+ host = strdup( optarg );
+ break;
+
+ case 'i':
+ /* ignored (!) by now */
+ break;
+
+ case 'p': /* the servers port */
+ if ( lutil_atoi( &port, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'D': /* the servers manager */
+ manager = strdup( optarg );
+ break;
+
+ case 'w': /* the server managers password */
+ passwd.bv_val = strdup( optarg );
+ passwd.bv_len = strlen( optarg );
+ memset( optarg, '*', passwd.bv_len );
+ break;
+
+ case 'f': /* file with entry search request */
+ filename = strdup( optarg );
+ break;
+
+ case 'l': /* the number of loops */
+ if ( lutil_atoi( &loops, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'L': /* the number of outerloops */
+ if ( lutil_atoi( &outerloops, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'r': /* number of retries */
+ if ( lutil_atoi( &retries, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 't': /* delay in seconds */
+ if ( lutil_atoi( &delay, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ default:
+ usage( argv[0] );
+ break;
+ }
+ }
+
+ if (( filename == NULL ) || ( port == -1 && uri == NULL ) ||
+ ( manager == NULL ) || ( passwd.bv_val == NULL ))
+ usage( argv[0] );
+
+ entry = get_add_entry( filename, &attrs );
+ if (( entry == NULL ) || ( *entry == '\0' )) {
+
+ fprintf( stderr, "%s: invalid entry DN in file \"%s\".\n",
+ argv[0], filename );
+ exit( EXIT_FAILURE );
+
+ }
+
+ if (( attrs == NULL ) || ( *attrs == '\0' )) {
+
+ fprintf( stderr, "%s: invalid attrs in file \"%s\".\n",
+ argv[0], filename );
+ exit( EXIT_FAILURE );
+
+ }
+
+ uri = tester_uri( uri, host, port );
+
+ for ( i = 0; i < outerloops; i++ ) {
+ do_addel( uri, manager, &passwd, entry, attrs,
+ loops, retries, delay, friendly, chaserefs );
+ }
+
+ exit( EXIT_SUCCESS );
+}
+
+
+static void
+addmodifyop( LDAPMod ***pmodsp, int modop, char *attr, char *value, int vlen )
+{
+ LDAPMod **pmods;
+ int i, j;
+ struct berval *bvp;
+
+ pmods = *pmodsp;
+ modop |= LDAP_MOD_BVALUES;
+
+ i = 0;
+ if ( pmods != NULL ) {
+ for ( ; pmods[ i ] != NULL; ++i ) {
+ if ( strcasecmp( pmods[ i ]->mod_type, attr ) == 0 &&
+ pmods[ i ]->mod_op == modop ) {
+ break;
+ }
+ }
+ }
+
+ if ( pmods == NULL || pmods[ i ] == NULL ) {
+ if (( pmods = (LDAPMod **)realloc( pmods, (i + 2) *
+ sizeof( LDAPMod * ))) == NULL ) {
+ tester_perror( "realloc", NULL );
+ exit( EXIT_FAILURE );
+ }
+ *pmodsp = pmods;
+ pmods[ i + 1 ] = NULL;
+ if (( pmods[ i ] = (LDAPMod *)calloc( 1, sizeof( LDAPMod )))
+ == NULL ) {
+ tester_perror( "calloc", NULL );
+ exit( EXIT_FAILURE );
+ }
+ pmods[ i ]->mod_op = modop;
+ if (( pmods[ i ]->mod_type = strdup( attr )) == NULL ) {
+ tester_perror( "strdup", NULL );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ if ( value != NULL ) {
+ j = 0;
+ if ( pmods[ i ]->mod_bvalues != NULL ) {
+ for ( ; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
+ ;
+ }
+ }
+ if (( pmods[ i ]->mod_bvalues =
+ (struct berval **)ber_memrealloc( pmods[ i ]->mod_bvalues,
+ (j + 2) * sizeof( struct berval * ))) == NULL ) {
+ tester_perror( "ber_memrealloc", NULL );
+ exit( EXIT_FAILURE );
+ }
+ pmods[ i ]->mod_bvalues[ j + 1 ] = NULL;
+ if (( bvp = (struct berval *)ber_memalloc( sizeof( struct berval )))
+ == NULL ) {
+ tester_perror( "ber_memalloc", NULL );
+ exit( EXIT_FAILURE );
+ }
+ pmods[ i ]->mod_bvalues[ j ] = bvp;
+
+ bvp->bv_len = vlen;
+ if (( bvp->bv_val = (char *)malloc( vlen + 1 )) == NULL ) {
+ tester_perror( "malloc", NULL );
+ exit( EXIT_FAILURE );
+ }
+ AC_MEMCPY( bvp->bv_val, value, vlen );
+ bvp->bv_val[ vlen ] = '\0';
+ }
+}
+
+
+static char *
+get_add_entry( char *filename, LDAPMod ***mods )
+{
+ FILE *fp;
+ char *entry = NULL;
+
+ if ( (fp = fopen( filename, "r" )) != NULL ) {
+ char line[BUFSIZ];
+
+ if ( fgets( line, BUFSIZ, fp )) {
+ char *nl;
+
+ if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
+ *nl = '\0';
+ nl = line;
+ if ( !strncasecmp( nl, "dn: ", 4 ))
+ nl += 4;
+ entry = strdup( nl );
+
+ }
+
+ while ( fgets( line, BUFSIZ, fp )) {
+ char *nl;
+ char *value;
+
+ if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
+ *nl = '\0';
+
+ if ( *line == '\0' ) break;
+ if ( !( value = strchr( line, ':' ))) break;
+
+ *value++ = '\0';
+ while ( *value && isspace( (unsigned char) *value ))
+ value++;
+
+ addmodifyop( mods, LDAP_MOD_ADD, line, value, strlen( value ));
+
+ }
+ fclose( fp );
+ }
+
+ return( entry );
+}
+
+
+static void
+do_addel(
+ char *uri,
+ char *manager,
+ struct berval *passwd,
+ char *entry,
+ LDAPMod **attrs,
+ int maxloop,
+ int maxretries,
+ int delay,
+ int friendly,
+ int chaserefs )
+{
+ LDAP *ld = NULL;
+ int i = 0, do_retry = maxretries;
+ int rc = LDAP_SUCCESS;
+ int version = LDAP_VERSION3;
+
+retry:;
+ ldap_initialize( &ld, uri );
+ if ( ld == NULL ) {
+ tester_perror( "ldap_initialize", NULL );
+ exit( EXIT_FAILURE );
+ }
+
+ (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+ (void) ldap_set_option( ld, LDAP_OPT_REFERRALS,
+ chaserefs ? LDAP_OPT_ON : LDAP_OPT_OFF );
+
+ if ( do_retry == maxretries ) {
+ fprintf( stderr, "PID=%ld - Add/Delete(%d): entry=\"%s\".\n",
+ (long) pid, maxloop, entry );
+ }
+
+ rc = ldap_sasl_bind_s( ld, manager, LDAP_SASL_SIMPLE, passwd, NULL, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_sasl_bind_s", NULL );
+ switch ( rc ) {
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ do_retry--;
+ if ( delay != 0 ) {
+ sleep( delay );
+ }
+ goto retry;
+ }
+ /* fallthru */
+ default:
+ break;
+ }
+ exit( EXIT_FAILURE );
+ }
+
+ for ( ; i < maxloop; i++ ) {
+
+ /* add the entry */
+ rc = ldap_add_ext_s( ld, entry, attrs, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_add_ext_s", NULL );
+ switch ( rc ) {
+ case LDAP_ALREADY_EXISTS:
+ /* NOTE: this likely means
+ * the delete failed
+ * during the previous round... */
+ if ( !friendly ) {
+ goto done;
+ }
+ break;
+
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ do_retry--;
+ goto retry;
+ }
+ /* fall thru */
+
+ default:
+ goto done;
+ }
+ }
+
+#if 0
+ /* wait a second for the add to really complete */
+ /* This masks some race conditions though. */
+ sleep( 1 );
+#endif
+
+ /* now delete the entry again */
+ rc = ldap_delete_ext_s( ld, entry, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_delete_ext_s", NULL );
+ switch ( rc ) {
+ case LDAP_NO_SUCH_OBJECT:
+ /* NOTE: this likely means
+ * the add failed
+ * during the previous round... */
+ if ( !friendly ) {
+ goto done;
+ }
+ break;
+
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ do_retry--;
+ goto retry;
+ }
+ /* fall thru */
+
+ default:
+ goto done;
+ }
+ }
+ }
+
+done:;
+ fprintf( stderr, " PID=%ld - Add/Delete done (%d).\n", (long) pid, rc );
+
+ ldap_unbind_ext( ld, NULL, NULL );
+}
+
+
diff --git a/tests/progs/slapd-bind.c b/tests/progs/slapd-bind.c
new file mode 100644
index 0000000..ce51b17
--- /dev/null
+++ b/tests/progs/slapd-bind.c
@@ -0,0 +1,609 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/stdlib.h"
+#include "ac/time.h"
+
+#include "ac/ctype.h"
+#include "ac/param.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+#include "ac/unistd.h"
+#include "ac/wait.h"
+#include "ac/time.h"
+
+#include "ldap.h"
+#include "lutil.h"
+#include "lber_pvt.h"
+#include "ldap_pvt.h"
+
+#include "slapd-common.h"
+
+#define LOOPS 100
+
+static int
+do_bind( char *uri, char *dn, struct berval *pass, int maxloop,
+ int force, int chaserefs, int noinit, LDAP **ldp,
+ int action_type, void *action );
+
+static int
+do_base( char *uri, char *dn, struct berval *pass, char *base, char *filter, char *pwattr,
+ int maxloop, int force, int chaserefs, int noinit, int delay,
+ int action_type, void *action );
+
+/* This program can be invoked two ways: if -D is used to specify a Bind DN,
+ * that DN will be used repeatedly for all of the Binds. If instead -b is used
+ * to specify a base DN, a search will be done for all "person" objects under
+ * that base DN. Then DNs from this list will be randomly selected for each
+ * Bind request. All of the users must have identical passwords. Also it is
+ * assumed that the users are all onelevel children of the base.
+ */
+static void
+usage( char *name, char opt )
+{
+ if ( opt ) {
+ fprintf( stderr, "%s: unable to handle option \'%c\'\n\n",
+ name, opt );
+ }
+
+ fprintf( stderr, "usage: %s "
+ "[-H uri | -h <host> [-p port]] "
+ "[-D <dn> [-w <passwd>]] "
+ "[-b <baseDN> [-f <searchfilter>] [-a pwattr]] "
+ "[-l <loops>] "
+ "[-L <outerloops>] "
+ "[-B <extra>[,...]] "
+ "[-F] "
+ "[-C] "
+ "[-I] "
+ "[-i <ignore>] "
+ "[-t delay]\n",
+ name );
+ exit( EXIT_FAILURE );
+}
+
+int
+main( int argc, char **argv )
+{
+ int i;
+ char *uri = NULL;
+ char *host = "localhost";
+ char *dn = NULL;
+ char *base = NULL;
+ char *filter = "(objectClass=person)";
+ struct berval pass = { 0, NULL };
+ char *pwattr = NULL;
+ int port = -1;
+ int loops = LOOPS;
+ int outerloops = 1;
+ int force = 0;
+ int chaserefs = 0;
+ int noinit = 1;
+ int delay = 0;
+
+ /* extra action to do after bind... */
+ struct berval type[] = {
+ BER_BVC( "tester=" ),
+ BER_BVC( "add=" ),
+ BER_BVC( "bind=" ),
+ BER_BVC( "modify=" ),
+ BER_BVC( "modrdn=" ),
+ BER_BVC( "read=" ),
+ BER_BVC( "search=" ),
+ BER_BVNULL
+ };
+
+ LDAPURLDesc *extra_ludp = NULL;
+
+ tester_init( "slapd-bind", TESTER_BIND );
+
+ /* by default, tolerate invalid credentials */
+ tester_ignore_str2errlist( "INVALID_CREDENTIALS" );
+
+ while ( ( i = getopt( argc, argv, "a:B:b:D:Ff:H:h:Ii:L:l:p:t:w:" ) ) != EOF )
+ {
+ switch ( i ) {
+ case 'a':
+ pwattr = optarg;
+ break;
+
+ case 'b': /* base DN of a tree of user DNs */
+ base = optarg;
+ break;
+
+ case 'B':
+ {
+ int c;
+
+ for ( c = 0; type[c].bv_val; c++ ) {
+ if ( strncasecmp( optarg, type[c].bv_val, type[c].bv_len ) == 0 )
+ {
+ break;
+ }
+ }
+
+ if ( type[c].bv_val == NULL ) {
+ usage( argv[0], 'B' );
+ }
+
+ switch ( c ) {
+ case TESTER_TESTER:
+ case TESTER_BIND:
+ /* invalid */
+ usage( argv[0], 'B' );
+
+ case TESTER_SEARCH:
+ {
+ if ( ldap_url_parse( &optarg[type[c].bv_len], &extra_ludp ) != LDAP_URL_SUCCESS )
+ {
+ usage( argv[0], 'B' );
+ }
+ } break;
+
+ case TESTER_ADDEL:
+ case TESTER_MODIFY:
+ case TESTER_MODRDN:
+ case TESTER_READ:
+ /* nothing to do */
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ } break;
+
+ case 'C':
+ chaserefs++;
+ break;
+
+ case 'H': /* the server uri */
+ uri = optarg;
+ break;
+
+ case 'h': /* the servers host */
+ host = optarg;
+ break;
+
+ case 'i':
+ tester_ignore_str2errlist( optarg );
+ break;
+
+ case 'p': /* the servers port */
+ if ( lutil_atoi( &port, optarg ) != 0 ) {
+ usage( argv[0], 'p' );
+ }
+ break;
+
+ case 'D':
+ dn = optarg;
+ break;
+
+ case 'w':
+ ber_str2bv( optarg, 0, 1, &pass );
+ memset( optarg, '*', pass.bv_len );
+ break;
+
+ case 'l': /* the number of loops */
+ if ( lutil_atoi( &loops, optarg ) != 0 ) {
+ usage( argv[0], 'l' );
+ }
+ break;
+
+ case 'L': /* the number of outerloops */
+ if ( lutil_atoi( &outerloops, optarg ) != 0 ) {
+ usage( argv[0], 'L' );
+ }
+ break;
+
+ case 'f':
+ filter = optarg;
+ break;
+
+ case 'F':
+ force++;
+ break;
+
+ case 'I':
+ /* reuse connection */
+ noinit = 0;
+ break;
+
+ case 't':
+ /* sleep between binds */
+ if ( lutil_atoi( &delay, optarg ) != 0 ) {
+ usage( argv[0], 't' );
+ }
+ break;
+
+ default:
+ usage( argv[0], i );
+ break;
+ }
+ }
+
+ if ( port == -1 && uri == NULL ) {
+ usage( argv[0], '\0' );
+ }
+
+ uri = tester_uri( uri, host, port );
+
+ for ( i = 0; i < outerloops; i++ ) {
+ int rc;
+
+ if ( base != NULL ) {
+ rc = do_base( uri, dn, &pass, base, filter, pwattr, loops,
+ force, chaserefs, noinit, delay, -1, NULL );
+ } else {
+ rc = do_bind( uri, dn, &pass, loops,
+ force, chaserefs, noinit, NULL, -1, NULL );
+ }
+ if ( rc == LDAP_SERVER_DOWN )
+ break;
+ }
+
+ exit( EXIT_SUCCESS );
+}
+
+
+static int
+do_bind( char *uri, char *dn, struct berval *pass, int maxloop,
+ int force, int chaserefs, int noinit, LDAP **ldp,
+ int action_type, void *action )
+{
+ LDAP *ld = ldp ? *ldp : NULL;
+ int i, rc = -1;
+
+ /* for internal search */
+ int timelimit = 0;
+ int sizelimit = 0;
+
+ switch ( action_type ) {
+ case -1:
+ break;
+
+ case TESTER_SEARCH:
+ {
+ LDAPURLDesc *ludp = (LDAPURLDesc *)action;
+
+ assert( action != NULL );
+
+ if ( ludp->lud_exts != NULL ) {
+ for ( i = 0; ludp->lud_exts[ i ] != NULL; i++ ) {
+ char *ext = ludp->lud_exts[ i ];
+ int crit = 0;
+
+ if (ext[0] == '!') {
+ crit++;
+ ext++;
+ }
+
+ if ( strncasecmp( ext, "x-timelimit=", STRLENOF( "x-timelimit=" ) ) == 0 ) {
+ if ( lutil_atoi( &timelimit, &ext[ STRLENOF( "x-timelimit=" ) ] ) && crit ) {
+ tester_error( "unable to parse critical extension x-timelimit" );
+ }
+
+ } else if ( strncasecmp( ext, "x-sizelimit=", STRLENOF( "x-sizelimit=" ) ) == 0 ) {
+ if ( lutil_atoi( &sizelimit, &ext[ STRLENOF( "x-sizelimit=" ) ] ) && crit ) {
+ tester_error( "unable to parse critical extension x-sizelimit" );
+ }
+
+ } else if ( crit ) {
+ tester_error( "unknown critical extension" );
+ }
+ }
+ }
+ } break;
+
+ default:
+ /* nothing to do yet */
+ break;
+ }
+
+ if ( maxloop > 1 ) {
+ fprintf( stderr, "PID=%ld - Bind(%d): dn=\"%s\".\n",
+ (long) pid, maxloop, dn );
+ }
+
+ for ( i = 0; i < maxloop; i++ ) {
+ if ( !noinit || ld == NULL ) {
+ int version = LDAP_VERSION3;
+ ldap_initialize( &ld, uri );
+ if ( ld == NULL ) {
+ tester_perror( "ldap_initialize", NULL );
+ rc = -1;
+ break;
+ }
+
+ (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,
+ &version );
+ (void) ldap_set_option( ld, LDAP_OPT_REFERRALS,
+ chaserefs ? LDAP_OPT_ON: LDAP_OPT_OFF );
+ }
+
+ rc = ldap_sasl_bind_s( ld, dn, LDAP_SASL_SIMPLE, pass, NULL, NULL, NULL );
+ if ( rc ) {
+ int first = tester_ignore_err( rc );
+
+ /* if ignore.. */
+ if ( first ) {
+ /* only log if first occurrence */
+ if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
+ tester_ldap_error( ld, "ldap_sasl_bind_s", NULL );
+ }
+ rc = LDAP_SUCCESS;
+
+ } else {
+ tester_ldap_error( ld, "ldap_sasl_bind_s", NULL );
+ }
+ }
+
+ switch ( action_type ) {
+ case -1:
+ break;
+
+ case TESTER_SEARCH:
+ {
+ LDAPURLDesc *ludp = (LDAPURLDesc *)action;
+ LDAPMessage *res = NULL;
+ struct timeval tv = { 0 }, *tvp = NULL;
+
+ if ( timelimit ) {
+ tv.tv_sec = timelimit;
+ tvp = &tv;
+ }
+
+ assert( action != NULL );
+
+ rc = ldap_search_ext_s( ld,
+ ludp->lud_dn, ludp->lud_scope,
+ ludp->lud_filter, ludp->lud_attrs, 0,
+ NULL, NULL, tvp, sizelimit, &res );
+ ldap_msgfree( res );
+ } break;
+
+ default:
+ /* nothing to do yet */
+ break;
+ }
+
+ if ( !noinit ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ ld = NULL;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+ }
+
+ if ( maxloop > 1 ) {
+ fprintf( stderr, " PID=%ld - Bind done (%d).\n", (long) pid, rc );
+ }
+
+ if ( ldp && noinit ) {
+ *ldp = ld;
+
+ } else if ( ld != NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ }
+
+ return rc;
+}
+
+
+static int
+do_base( char *uri, char *dn, struct berval *pass, char *base, char *filter, char *pwattr,
+ int maxloop, int force, int chaserefs, int noinit, int delay,
+ int action_type, void *action )
+{
+ LDAP *ld = NULL;
+ int i = 0;
+ int rc = LDAP_SUCCESS;
+ ber_int_t msgid;
+ LDAPMessage *res, *msg;
+ char **dns = NULL;
+ struct berval *creds = NULL;
+ char *attrs[] = { LDAP_NO_ATTRS, NULL };
+ int ndns = 0;
+#ifdef _WIN32
+ DWORD beg, end;
+#else
+ struct timeval beg, end;
+#endif
+ int version = LDAP_VERSION3;
+ char *nullstr = "";
+
+ ldap_initialize( &ld, uri );
+ if ( ld == NULL ) {
+ tester_perror( "ldap_initialize", NULL );
+ exit( EXIT_FAILURE );
+ }
+
+ (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+ (void) ldap_set_option( ld, LDAP_OPT_REFERRALS,
+ chaserefs ? LDAP_OPT_ON: LDAP_OPT_OFF );
+
+ rc = ldap_sasl_bind_s( ld, dn, LDAP_SASL_SIMPLE, pass, NULL, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_sasl_bind_s", NULL );
+ exit( EXIT_FAILURE );
+ }
+
+ fprintf( stderr, "PID=%ld - Bind(%d): base=\"%s\", filter=\"%s\" attr=\"%s\".\n",
+ (long) pid, maxloop, base, filter, pwattr );
+
+ if ( pwattr != NULL ) {
+ attrs[ 0 ] = pwattr;
+ }
+ rc = ldap_search_ext( ld, base, LDAP_SCOPE_SUBTREE,
+ filter, attrs, 0, NULL, NULL, 0, 0, &msgid );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_search_ext", NULL );
+ exit( EXIT_FAILURE );
+ }
+
+ while ( ( rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ONE, NULL, &res ) ) > 0 )
+ {
+ BerElement *ber;
+ struct berval bv;
+ int done = 0;
+
+ for ( msg = ldap_first_message( ld, res ); msg;
+ msg = ldap_next_message( ld, msg ) )
+ {
+ switch ( ldap_msgtype( msg ) ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ rc = ldap_get_dn_ber( ld, msg, &ber, &bv );
+ dns = realloc( dns, (ndns + 1)*sizeof(char *) );
+ dns[ndns] = ber_strdup( bv.bv_val );
+ if ( pwattr != NULL ) {
+ struct berval **values = ldap_get_values_len( ld, msg, pwattr );
+
+ creds = realloc( creds, (ndns + 1)*sizeof(struct berval) );
+ if ( values == NULL ) {
+novals:;
+ creds[ndns].bv_len = 0;
+ creds[ndns].bv_val = nullstr;
+
+ } else {
+ static struct berval cleartext = BER_BVC( "{CLEARTEXT} " );
+ struct berval value = *values[ 0 ];
+
+ if ( value.bv_val[ 0 ] == '{' ) {
+ char *end = ber_bvchr( &value, '}' );
+
+ if ( end ) {
+ if ( ber_bvcmp( &value, &cleartext ) == 0 ) {
+ value.bv_val += cleartext.bv_len;
+ value.bv_len -= cleartext.bv_len;
+
+ } else {
+ ldap_value_free_len( values );
+ goto novals;
+ }
+ }
+
+ }
+
+ ber_dupbv( &creds[ndns], &value );
+ ldap_value_free_len( values );
+ }
+ }
+ ndns++;
+ ber_free( ber, 0 );
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ done = 1;
+ break;
+ }
+ if ( done )
+ break;
+ }
+ ldap_msgfree( res );
+ if ( done ) break;
+ }
+
+#ifdef _WIN32
+ beg = GetTickCount();
+#else
+ gettimeofday( &beg, NULL );
+#endif
+
+ if ( ndns == 0 ) {
+ tester_error( "No DNs" );
+ return 1;
+ }
+
+ fprintf( stderr, " PID=%ld - Bind base=\"%s\" filter=\"%s\" got %d values.\n",
+ (long) pid, base, filter, ndns );
+
+ /* Ok, got list of DNs, now start binding to each */
+ for ( i = 0; i < maxloop; i++ ) {
+ int j;
+ struct berval cred = { 0, NULL };
+
+
+#if 0 /* use high-order bits for better randomness (Numerical Recipes in "C") */
+ j = rand() % ndns;
+#endif
+ j = ((double)ndns)*rand()/(RAND_MAX + 1.0);
+
+ if ( creds && !BER_BVISEMPTY( &creds[j] ) ) {
+ cred = creds[j];
+ }
+
+ if ( do_bind( uri, dns[j], &cred, 1, force, chaserefs, noinit, &ld,
+ action_type, action ) && !force )
+ {
+ break;
+ }
+
+ if ( delay ) {
+ sleep( delay );
+ }
+ }
+
+ if ( ld != NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ ld = NULL;
+ }
+
+#ifdef _WIN32
+ end = GetTickCount();
+ end -= beg;
+
+ fprintf( stderr, " PID=%ld - Bind done %d in %d.%03d seconds.\n",
+ (long) pid, i, end / 1000, end % 1000 );
+#else
+ gettimeofday( &end, NULL );
+ end.tv_usec -= beg.tv_usec;
+ if (end.tv_usec < 0 ) {
+ end.tv_usec += 1000000;
+ end.tv_sec -= 1;
+ }
+ end.tv_sec -= beg.tv_sec;
+
+ fprintf( stderr, " PID=%ld - Bind done %d in %ld.%06ld seconds.\n",
+ (long) pid, i, (long) end.tv_sec, (long) end.tv_usec );
+#endif
+
+ if ( dns ) {
+ for ( i = 0; i < ndns; i++ ) {
+ ber_memfree( dns[i] );
+ }
+ free( dns );
+ }
+
+ if ( creds ) {
+ for ( i = 0; i < ndns; i++ ) {
+ if ( creds[i].bv_val != nullstr ) {
+ ber_memfree( creds[i].bv_val );
+ }
+ }
+ free( creds );
+ }
+
+ return 0;
+}
diff --git a/tests/progs/slapd-common.c b/tests/progs/slapd-common.c
new file mode 100644
index 0000000..2a3773b
--- /dev/null
+++ b/tests/progs/slapd-common.c
@@ -0,0 +1,300 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/stdlib.h"
+#include "ac/unistd.h"
+#include "ac/string.h"
+#include "ac/errno.h"
+
+#include "ldap.h"
+
+#include "ldap_pvt.h"
+#include "slapd-common.h"
+
+/* global vars */
+pid_t pid;
+
+/* static vars */
+static char progname[ BUFSIZ ];
+tester_t progtype;
+
+/*
+ * ignore_count[] is indexed by result code:
+ * negative for OpenLDAP client-side errors, positive for protocol codes.
+ */
+#define TESTER_CLIENT_FIRST LDAP_REFERRAL_LIMIT_EXCEEDED /* negative */
+#define TESTER_SERVER_LAST LDAP_OTHER
+static int ignore_base [ -TESTER_CLIENT_FIRST + TESTER_SERVER_LAST + 1 ];
+#define ignore_count (ignore_base - TESTER_CLIENT_FIRST)
+
+static const struct {
+ const char *name;
+ int err;
+} ignore_str2err[] = {
+ { "OPERATIONS_ERROR", LDAP_OPERATIONS_ERROR },
+ { "PROTOCOL_ERROR", LDAP_PROTOCOL_ERROR },
+ { "TIMELIMIT_EXCEEDED", LDAP_TIMELIMIT_EXCEEDED },
+ { "SIZELIMIT_EXCEEDED", LDAP_SIZELIMIT_EXCEEDED },
+ { "COMPARE_FALSE", LDAP_COMPARE_FALSE },
+ { "COMPARE_TRUE", LDAP_COMPARE_TRUE },
+ { "AUTH_METHOD_NOT_SUPPORTED", LDAP_AUTH_METHOD_NOT_SUPPORTED },
+ { "STRONG_AUTH_NOT_SUPPORTED", LDAP_STRONG_AUTH_NOT_SUPPORTED },
+ { "STRONG_AUTH_REQUIRED", LDAP_STRONG_AUTH_REQUIRED },
+ { "STRONGER_AUTH_REQUIRED", LDAP_STRONGER_AUTH_REQUIRED },
+ { "PARTIAL_RESULTS", LDAP_PARTIAL_RESULTS },
+
+ { "REFERRAL", LDAP_REFERRAL },
+ { "ADMINLIMIT_EXCEEDED", LDAP_ADMINLIMIT_EXCEEDED },
+ { "UNAVAILABLE_CRITICAL_EXTENSION", LDAP_UNAVAILABLE_CRITICAL_EXTENSION },
+ { "CONFIDENTIALITY_REQUIRED", LDAP_CONFIDENTIALITY_REQUIRED },
+ { "SASL_BIND_IN_PROGRESS", LDAP_SASL_BIND_IN_PROGRESS },
+
+ { "NO_SUCH_ATTRIBUTE", LDAP_NO_SUCH_ATTRIBUTE },
+ { "UNDEFINED_TYPE", LDAP_UNDEFINED_TYPE },
+ { "INAPPROPRIATE_MATCHING", LDAP_INAPPROPRIATE_MATCHING },
+ { "CONSTRAINT_VIOLATION", LDAP_CONSTRAINT_VIOLATION },
+ { "TYPE_OR_VALUE_EXISTS", LDAP_TYPE_OR_VALUE_EXISTS },
+ { "INVALID_SYNTAX", LDAP_INVALID_SYNTAX },
+
+ { "NO_SUCH_OBJECT", LDAP_NO_SUCH_OBJECT },
+ { "ALIAS_PROBLEM", LDAP_ALIAS_PROBLEM },
+ { "INVALID_DN_SYNTAX", LDAP_INVALID_DN_SYNTAX },
+ { "IS_LEAF", LDAP_IS_LEAF },
+ { "ALIAS_DEREF_PROBLEM", LDAP_ALIAS_DEREF_PROBLEM },
+
+ /* obsolete */
+ { "PROXY_AUTHZ_FAILURE", LDAP_X_PROXY_AUTHZ_FAILURE },
+ { "INAPPROPRIATE_AUTH", LDAP_INAPPROPRIATE_AUTH },
+ { "INVALID_CREDENTIALS", LDAP_INVALID_CREDENTIALS },
+ { "INSUFFICIENT_ACCESS", LDAP_INSUFFICIENT_ACCESS },
+
+ { "BUSY", LDAP_BUSY },
+ { "UNAVAILABLE", LDAP_UNAVAILABLE },
+ { "UNWILLING_TO_PERFORM", LDAP_UNWILLING_TO_PERFORM },
+ { "LOOP_DETECT", LDAP_LOOP_DETECT },
+
+ { "NAMING_VIOLATION", LDAP_NAMING_VIOLATION },
+ { "OBJECT_CLASS_VIOLATION", LDAP_OBJECT_CLASS_VIOLATION },
+ { "NOT_ALLOWED_ON_NONLEAF", LDAP_NOT_ALLOWED_ON_NONLEAF },
+ { "NOT_ALLOWED_ON_RDN", LDAP_NOT_ALLOWED_ON_RDN },
+ { "ALREADY_EXISTS", LDAP_ALREADY_EXISTS },
+ { "NO_OBJECT_CLASS_MODS", LDAP_NO_OBJECT_CLASS_MODS },
+ { "RESULTS_TOO_LARGE", LDAP_RESULTS_TOO_LARGE },
+ { "AFFECTS_MULTIPLE_DSAS", LDAP_AFFECTS_MULTIPLE_DSAS },
+
+ { "OTHER", LDAP_OTHER },
+
+ { "SERVER_DOWN", LDAP_SERVER_DOWN },
+ { "LOCAL_ERROR", LDAP_LOCAL_ERROR },
+ { "ENCODING_ERROR", LDAP_ENCODING_ERROR },
+ { "DECODING_ERROR", LDAP_DECODING_ERROR },
+ { "TIMEOUT", LDAP_TIMEOUT },
+ { "AUTH_UNKNOWN", LDAP_AUTH_UNKNOWN },
+ { "FILTER_ERROR", LDAP_FILTER_ERROR },
+ { "USER_CANCELLED", LDAP_USER_CANCELLED },
+ { "PARAM_ERROR", LDAP_PARAM_ERROR },
+ { "NO_MEMORY", LDAP_NO_MEMORY },
+ { "CONNECT_ERROR", LDAP_CONNECT_ERROR },
+ { "NOT_SUPPORTED", LDAP_NOT_SUPPORTED },
+ { "CONTROL_NOT_FOUND", LDAP_CONTROL_NOT_FOUND },
+ { "NO_RESULTS_RETURNED", LDAP_NO_RESULTS_RETURNED },
+ { "MORE_RESULTS_TO_RETURN", LDAP_MORE_RESULTS_TO_RETURN },
+ { "CLIENT_LOOP", LDAP_CLIENT_LOOP },
+ { "REFERRAL_LIMIT_EXCEEDED", LDAP_REFERRAL_LIMIT_EXCEEDED },
+
+ { NULL }
+};
+
+#define UNKNOWN_ERR (1234567890)
+
+static int
+tester_ignore_str2err( const char *err )
+{
+ int i, ignore = 1;
+
+ if ( strcmp( err, "ALL" ) == 0 ) {
+ for ( i = 0; ignore_str2err[ i ].name != NULL; i++ ) {
+ ignore_count[ ignore_str2err[ i ].err ] = 1;
+ }
+ ignore_count[ LDAP_SUCCESS ] = 0;
+
+ return 0;
+ }
+
+ if ( err[ 0 ] == '!' ) {
+ ignore = 0;
+ err++;
+
+ } else if ( err[ 0 ] == '*' ) {
+ ignore = -1;
+ err++;
+ }
+
+ for ( i = 0; ignore_str2err[ i ].name != NULL; i++ ) {
+ if ( strcmp( err, ignore_str2err[ i ].name ) == 0 ) {
+ int err = ignore_str2err[ i ].err;
+
+ if ( err != LDAP_SUCCESS ) {
+ ignore_count[ err ] = ignore;
+ }
+
+ return err;
+ }
+ }
+
+ return UNKNOWN_ERR;
+}
+
+int
+tester_ignore_str2errlist( const char *err )
+{
+ int i;
+ char **errs = ldap_str2charray( err, "," );
+
+ for ( i = 0; errs[ i ] != NULL; i++ ) {
+ /* TODO: allow <err>:<prog> to ignore <err> only when <prog> */
+ (void)tester_ignore_str2err( errs[ i ] );
+ }
+
+ ldap_charray_free( errs );
+
+ return 0;
+}
+
+int
+tester_ignore_err( int err )
+{
+ int rc = 1;
+
+ if ( err && TESTER_CLIENT_FIRST <= err && err <= TESTER_SERVER_LAST ) {
+ rc = ignore_count[ err ];
+ if ( rc != 0 ) {
+ ignore_count[ err ] = rc + (rc > 0 ? 1 : -1);
+ }
+ }
+
+ /* SUCCESS is always "ignored" */
+ return rc;
+}
+
+void
+tester_init( const char *pname, tester_t ptype )
+{
+ pid = getpid();
+ srand( pid );
+ snprintf( progname, sizeof( progname ), "%s PID=%d", pname, pid );
+ progtype = ptype;
+}
+
+char *
+tester_uri( char *uri, char *host, int port )
+{
+ static char uribuf[ BUFSIZ ];
+
+ if ( uri != NULL ) {
+ return uri;
+ }
+
+ snprintf( uribuf, sizeof( uribuf ), "ldap://%s:%d", host, port );
+
+ return uribuf;
+}
+
+void
+tester_ldap_error( LDAP *ld, const char *fname, const char *msg )
+{
+ int err;
+ char *text = NULL;
+ LDAPControl **ctrls = NULL;
+
+ ldap_get_option( ld, LDAP_OPT_RESULT_CODE, (void *)&err );
+ if ( err != LDAP_SUCCESS ) {
+ ldap_get_option( ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void *)&text );
+ }
+
+ fprintf( stderr, "%s: %s: %s (%d) %s %s\n",
+ progname, fname, ldap_err2string( err ), err,
+ text == NULL ? "" : text,
+ msg ? msg : "" );
+
+ if ( text ) {
+ ldap_memfree( text );
+ text = NULL;
+ }
+
+ ldap_get_option( ld, LDAP_OPT_MATCHED_DN, (void *)&text );
+ if ( text != NULL ) {
+ if ( text[ 0 ] != '\0' ) {
+ fprintf( stderr, "\tmatched: %s\n", text );
+ }
+ ldap_memfree( text );
+ text = NULL;
+ }
+
+ ldap_get_option( ld, LDAP_OPT_SERVER_CONTROLS, (void *)&ctrls );
+ if ( ctrls != NULL ) {
+ int i;
+
+ fprintf( stderr, "\tcontrols:\n" );
+ for ( i = 0; ctrls[ i ] != NULL; i++ ) {
+ fprintf( stderr, "\t\t%s\n", ctrls[ i ]->ldctl_oid );
+ }
+ ldap_controls_free( ctrls );
+ ctrls = NULL;
+ }
+
+ if ( err == LDAP_REFERRAL ) {
+ char **refs = NULL;
+
+ ldap_get_option( ld, LDAP_OPT_REFERRAL_URLS, (void *)&refs );
+
+ if ( refs ) {
+ int i;
+
+ fprintf( stderr, "\treferral:\n" );
+ for ( i = 0; refs[ i ] != NULL; i++ ) {
+ fprintf( stderr, "\t\t%s\n", refs[ i ] );
+ }
+
+ ber_memvfree( (void **)refs );
+ }
+ }
+}
+
+void
+tester_perror( const char *fname, const char *msg )
+{
+ int save_errno = errno;
+ char buf[ BUFSIZ ];
+
+ fprintf( stderr, "%s: %s: (%d) %s %s\n",
+ progname, fname, save_errno,
+ AC_STRERROR_R( save_errno, buf, sizeof( buf ) ),
+ msg ? msg : "" );
+}
+
+void
+tester_error( const char *msg )
+{
+ fprintf( stderr, "%s: %s\n", progname, msg );
+}
diff --git a/tests/progs/slapd-common.h b/tests/progs/slapd-common.h
new file mode 100644
index 0000000..f2d292b
--- /dev/null
+++ b/tests/progs/slapd-common.h
@@ -0,0 +1,44 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software.
+ */
+
+#ifndef SLAPD_COMMON_H
+#define SLAPD_COMMON_H
+
+typedef enum {
+ TESTER_TESTER,
+ TESTER_ADDEL,
+ TESTER_BIND,
+ TESTER_MODIFY,
+ TESTER_MODRDN,
+ TESTER_READ,
+ TESTER_SEARCH,
+ TESTER_LAST
+} tester_t;
+
+extern void tester_init( const char *pname, tester_t ptype );
+extern char * tester_uri( char *uri, char *host, int port );
+extern void tester_error( const char *msg );
+extern void tester_perror( const char *fname, const char *msg );
+extern void tester_ldap_error( LDAP *ld, const char *fname, const char *msg );
+extern int tester_ignore_str2errlist( const char *err );
+extern int tester_ignore_err( int err );
+
+extern pid_t pid;
+
+#endif /* SLAPD_COMMON_H */
diff --git a/tests/progs/slapd-modify.c b/tests/progs/slapd-modify.c
new file mode 100644
index 0000000..a9a53ae
--- /dev/null
+++ b/tests/progs/slapd-modify.c
@@ -0,0 +1,318 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/stdlib.h"
+
+#include "ac/ctype.h"
+#include "ac/param.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+#include "ac/unistd.h"
+#include "ac/wait.h"
+
+#include "ldap.h"
+#include "lutil.h"
+
+#include "slapd-common.h"
+
+#define LOOPS 100
+#define RETRIES 0
+
+static void
+do_modify( char *uri, char *manager, struct berval *passwd,
+ char *entry, char *attr, char *value, int maxloop,
+ int maxretries, int delay, int friendly, int chaserefs );
+
+
+static void
+usage( char *name )
+{
+ fprintf( stderr,
+ "usage: %s "
+ "-H <uri> | ([-h <host>] -p <port>) "
+ "-D <manager> "
+ "-w <passwd> "
+ "-e <entry> "
+ "[-i <ignore>] "
+ "[-l <loops>] "
+ "[-L <outerloops>] "
+ "[-r <maxretries>] "
+ "[-t <delay>] "
+ "[-F] "
+ "[-C]\n",
+ name );
+ exit( EXIT_FAILURE );
+}
+
+int
+main( int argc, char **argv )
+{
+ int i;
+ char *uri = NULL;
+ char *host = "localhost";
+ int port = -1;
+ char *manager = NULL;
+ struct berval passwd = { 0, NULL };
+ char *entry = NULL;
+ char *ava = NULL;
+ char *value = NULL;
+ int loops = LOOPS;
+ int outerloops = 1;
+ int retries = RETRIES;
+ int delay = 0;
+ int friendly = 0;
+ int chaserefs = 0;
+
+ tester_init( "slapd-modify", TESTER_MODIFY );
+
+ while ( ( i = getopt( argc, argv, "a:CD:e:FH:h:i:L:l:p:r:t:w:" ) ) != EOF )
+ {
+ switch ( i ) {
+ case 'C':
+ chaserefs++;
+ break;
+
+ case 'F':
+ friendly++;
+ break;
+
+ case 'H': /* the server uri */
+ uri = strdup( optarg );
+ break;
+
+ case 'h': /* the servers host */
+ host = strdup( optarg );
+ break;
+
+ case 'i':
+ /* ignored (!) by now */
+ break;
+
+ case 'p': /* the servers port */
+ if ( lutil_atoi( &port, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'D': /* the servers manager */
+ manager = strdup( optarg );
+ break;
+
+ case 'w': /* the server managers password */
+ passwd.bv_val = strdup( optarg );
+ passwd.bv_len = strlen( optarg );
+ memset( optarg, '*', passwd.bv_len );
+ break;
+
+ case 'e': /* entry to modify */
+ entry = strdup( optarg );
+ break;
+
+ case 'a':
+ ava = strdup( optarg );
+ break;
+
+ case 'l': /* the number of loops */
+ if ( lutil_atoi( &loops, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'L': /* the number of outerloops */
+ if ( lutil_atoi( &outerloops, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'r': /* number of retries */
+ if ( lutil_atoi( &retries, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 't': /* delay in seconds */
+ if ( lutil_atoi( &delay, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ default:
+ usage( argv[0] );
+ break;
+ }
+ }
+
+ if (( entry == NULL ) || ( ava == NULL ) || ( port == -1 && uri == NULL ))
+ usage( argv[0] );
+
+ if ( *entry == '\0' ) {
+
+ fprintf( stderr, "%s: invalid EMPTY entry DN.\n",
+ argv[0] );
+ exit( EXIT_FAILURE );
+
+ }
+ if ( *ava == '\0' ) {
+ fprintf( stderr, "%s: invalid EMPTY AVA.\n",
+ argv[0] );
+ exit( EXIT_FAILURE );
+ }
+
+ if ( !( value = strchr( ava, ':' ))) {
+ fprintf( stderr, "%s: invalid AVA.\n",
+ argv[0] );
+ exit( EXIT_FAILURE );
+ }
+ *value++ = '\0';
+ while ( *value && isspace( (unsigned char) *value ))
+ value++;
+
+ uri = tester_uri( uri, host, port );
+
+ for ( i = 0; i < outerloops; i++ ) {
+ do_modify( uri, manager, &passwd, entry, ava, value,
+ loops, retries, delay, friendly, chaserefs );
+ }
+
+ exit( EXIT_SUCCESS );
+}
+
+
+static void
+do_modify( char *uri, char *manager,
+ struct berval *passwd, char *entry, char* attr, char* value,
+ int maxloop, int maxretries, int delay, int friendly, int chaserefs )
+{
+ LDAP *ld = NULL;
+ int i = 0, do_retry = maxretries;
+ int rc = LDAP_SUCCESS;
+
+ struct ldapmod mod;
+ struct ldapmod *mods[2];
+ char *values[2];
+ int version = LDAP_VERSION3;
+
+ values[0] = value;
+ values[1] = NULL;
+ mod.mod_op = LDAP_MOD_ADD;
+ mod.mod_type = attr;
+ mod.mod_values = values;
+ mods[0] = &mod;
+ mods[1] = NULL;
+
+retry:;
+ ldap_initialize( &ld, uri );
+ if ( ld == NULL ) {
+ tester_perror( "ldap_initialize", NULL );
+ exit( EXIT_FAILURE );
+ }
+
+ (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+ (void) ldap_set_option( ld, LDAP_OPT_REFERRALS,
+ chaserefs ? LDAP_OPT_ON : LDAP_OPT_OFF );
+
+ if ( do_retry == maxretries ) {
+ fprintf( stderr, "PID=%ld - Modify(%d): entry=\"%s\".\n",
+ (long) pid, maxloop, entry );
+ }
+
+ rc = ldap_sasl_bind_s( ld, manager, LDAP_SASL_SIMPLE, passwd, NULL, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_sasl_bind_s", NULL );
+ switch ( rc ) {
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ do_retry--;
+ if ( delay > 0 ) {
+ sleep( delay );
+ }
+ goto retry;
+ }
+ /* fallthru */
+ default:
+ break;
+ }
+ exit( EXIT_FAILURE );
+ }
+
+ for ( ; i < maxloop; i++ ) {
+ mod.mod_op = LDAP_MOD_ADD;
+ rc = ldap_modify_ext_s( ld, entry, mods, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_modify_ext_s", NULL );
+ switch ( rc ) {
+ case LDAP_TYPE_OR_VALUE_EXISTS:
+ /* NOTE: this likely means
+ * the second modify failed
+ * during the previous round... */
+ if ( !friendly ) {
+ goto done;
+ }
+ break;
+
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ do_retry--;
+ goto retry;
+ }
+ /* fall thru */
+
+ default:
+ goto done;
+ }
+ }
+
+ mod.mod_op = LDAP_MOD_DELETE;
+ rc = ldap_modify_ext_s( ld, entry, mods, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_modify_ext_s", NULL );
+ switch ( rc ) {
+ case LDAP_NO_SUCH_ATTRIBUTE:
+ /* NOTE: this likely means
+ * the first modify failed
+ * during the previous round... */
+ if ( !friendly ) {
+ goto done;
+ }
+ break;
+
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ do_retry--;
+ goto retry;
+ }
+ /* fall thru */
+
+ default:
+ goto done;
+ }
+ }
+
+ }
+
+done:;
+ fprintf( stderr, " PID=%ld - Modify done (%d).\n", (long) pid, rc );
+
+ ldap_unbind_ext( ld, NULL, NULL );
+}
+
+
diff --git a/tests/progs/slapd-modrdn.c b/tests/progs/slapd-modrdn.c
new file mode 100644
index 0000000..c717152
--- /dev/null
+++ b/tests/progs/slapd-modrdn.c
@@ -0,0 +1,310 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu, based in part
+ * on other OpenLDAP test tools, for inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/stdlib.h"
+
+#include "ac/ctype.h"
+#include "ac/param.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+#include "ac/unistd.h"
+#include "ac/wait.h"
+
+#include "ldap.h"
+#include "lutil.h"
+
+#include "slapd-common.h"
+
+#define LOOPS 100
+#define RETRIES 0
+
+static void
+do_modrdn( char *uri, char *manager, struct berval *passwd,
+ char *entry, int maxloop, int maxretries, int delay,
+ int friendly, int chaserefs );
+
+static void
+usage( char *name )
+{
+ fprintf( stderr,
+ "usage: %s "
+ "-H <uri> | ([-h <host>] -p <port>) "
+ "-D <manager> "
+ "-w <passwd> "
+ "-e <entry> "
+ "[-i <ignore>] "
+ "[-l <loops>] "
+ "[-L <outerloops>] "
+ "[-r <maxretries>] "
+ "[-t <delay>] "
+ "[-F] "
+ "[-C]\n",
+ name );
+ exit( EXIT_FAILURE );
+}
+
+int
+main( int argc, char **argv )
+{
+ int i;
+ char *uri = NULL;
+ char *host = "localhost";
+ int port = -1;
+ char *manager = NULL;
+ struct berval passwd = { 0, NULL };
+ char *entry = NULL;
+ int loops = LOOPS;
+ int outerloops = 1;
+ int retries = RETRIES;
+ int delay = 0;
+ int friendly = 0;
+ int chaserefs = 0;
+
+ tester_init( "slapd-modrdn", TESTER_MODRDN );
+
+ while ( ( i = getopt( argc, argv, "CD:e:FH:h:i:L:l:p:r:t:w:" ) ) != EOF )
+ {
+ switch ( i ) {
+ case 'C':
+ chaserefs++;
+ break;
+
+ case 'F':
+ friendly++;
+ break;
+
+ case 'H': /* the server uri */
+ uri = strdup( optarg );
+ break;
+
+ case 'h': /* the servers host */
+ host = strdup( optarg );
+ break;
+
+ case 'i':
+ /* ignored (!) by now */
+ break;
+
+ case 'p': /* the servers port */
+ if ( lutil_atoi( &port, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'D': /* the servers manager */
+ manager = strdup( optarg );
+ break;
+
+ case 'w': /* the server managers password */
+ passwd.bv_val = strdup( optarg );
+ passwd.bv_len = strlen( optarg );
+ memset( optarg, '*', passwd.bv_len );
+ break;
+
+ case 'e': /* entry to rename */
+ entry = strdup( optarg );
+ break;
+
+ case 'l': /* the number of loops */
+ if ( lutil_atoi( &loops, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'L': /* the number of outerloops */
+ if ( lutil_atoi( &outerloops, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'r': /* the number of retries */
+ if ( lutil_atoi( &retries, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 't': /* delay in seconds */
+ if ( lutil_atoi( &delay, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ default:
+ usage( argv[0] );
+ break;
+ }
+ }
+
+ if (( entry == NULL ) || ( port == -1 && uri == NULL ))
+ usage( argv[0] );
+
+ if ( *entry == '\0' ) {
+
+ fprintf( stderr, "%s: invalid EMPTY entry DN.\n",
+ argv[0] );
+ exit( EXIT_FAILURE );
+
+ }
+
+ uri = tester_uri( uri, host, port );
+
+ for ( i = 0; i < outerloops; i++ ) {
+ do_modrdn( uri, manager, &passwd, entry,
+ loops, retries, delay, friendly, chaserefs );
+ }
+
+ exit( EXIT_SUCCESS );
+}
+
+
+static void
+do_modrdn( char *uri, char *manager,
+ struct berval *passwd, char *entry, int maxloop, int maxretries,
+ int delay, int friendly, int chaserefs )
+{
+ LDAP *ld = NULL;
+ int i, do_retry = maxretries;
+ char *DNs[2];
+ char *rdns[2];
+ int rc = LDAP_SUCCESS;
+ char *p1, *p2;
+ int version = LDAP_VERSION3;
+
+ DNs[0] = entry;
+ DNs[1] = strdup( entry );
+
+ /* reverse the RDN, make new DN */
+ p1 = strchr( entry, '=' ) + 1;
+ p2 = strchr( p1, ',' );
+
+ *p2 = '\0';
+ rdns[1] = strdup( entry );
+ *p2-- = ',';
+
+ for (i = p1 - entry;p2 >= p1;)
+ DNs[1][i++] = *p2--;
+
+ DNs[1][i] = '\0';
+ rdns[0] = strdup( DNs[1] );
+ DNs[1][i] = ',';
+
+ i = 0;
+
+retry:;
+ ldap_initialize( &ld, uri );
+ if ( ld == NULL ) {
+ tester_perror( "ldap_initialize", NULL );
+ exit( EXIT_FAILURE );
+ }
+
+ (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+ (void) ldap_set_option( ld, LDAP_OPT_REFERRALS,
+ chaserefs ? LDAP_OPT_ON : LDAP_OPT_OFF );
+
+ if ( do_retry == maxretries ) {
+ fprintf( stderr, "PID=%ld - Modrdn(%d): entry=\"%s\".\n",
+ (long) pid, maxloop, entry );
+ }
+
+ rc = ldap_sasl_bind_s( ld, manager, LDAP_SASL_SIMPLE, passwd, NULL, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_sasl_bind_s", NULL );
+ switch ( rc ) {
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ do_retry--;
+ if ( delay > 0) {
+ sleep( delay );
+ }
+ goto retry;
+ }
+ /* fallthru */
+ default:
+ break;
+ }
+ exit( EXIT_FAILURE );
+ }
+
+ for ( ; i < maxloop; i++ ) {
+ rc = ldap_rename_s( ld, DNs[0], rdns[0], NULL, 0, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_rename_s", NULL );
+ switch ( rc ) {
+ case LDAP_NO_SUCH_OBJECT:
+ /* NOTE: this likely means
+ * the second modrdn failed
+ * during the previous round... */
+ if ( !friendly ) {
+ goto done;
+ }
+ break;
+
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ do_retry--;
+ goto retry;
+ }
+ /* fall thru */
+
+ default:
+ goto done;
+ }
+ }
+ rc = ldap_rename_s( ld, DNs[1], rdns[1], NULL, 1, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_rename_s", NULL );
+ switch ( rc ) {
+ case LDAP_NO_SUCH_OBJECT:
+ /* NOTE: this likely means
+ * the first modrdn failed
+ * during the previous round... */
+ if ( !friendly ) {
+ goto done;
+ }
+ break;
+
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ do_retry--;
+ goto retry;
+ }
+ /* fall thru */
+
+ default:
+ goto done;
+ }
+ }
+ }
+
+done:;
+ fprintf( stderr, " PID=%ld - Modrdn done (%d).\n", (long) pid, rc );
+
+ ldap_unbind_ext( ld, NULL, NULL );
+
+ free( DNs[1] );
+ free( rdns[0] );
+ free( rdns[1] );
+}
diff --git a/tests/progs/slapd-mtread.c b/tests/progs/slapd-mtread.c
new file mode 100644
index 0000000..5fca735
--- /dev/null
+++ b/tests/progs/slapd-mtread.c
@@ -0,0 +1,837 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Kurt Spanier for inclusion
+ * in OpenLDAP Software.
+ */
+
+/*
+ * This tool is a MT reader. It behaves like slapd-read however
+ * with one or more threads simultaneously using the same connection.
+ * If -M is enabled, then M threads will also perform write operations.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "ldap_pvt_thread.h"
+
+#include "ac/stdlib.h"
+
+#include "ac/ctype.h"
+#include "ac/param.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+#include "ac/unistd.h"
+#include "ac/wait.h"
+
+#include "ldap.h"
+#include "lutil.h"
+
+#include "ldap_pvt.h"
+
+#include "slapd-common.h"
+
+#define MAXCONN 512
+#define LOOPS 100
+#define RETRIES 0
+#define DEFAULT_BASE "ou=people,dc=example,dc=com"
+
+static void
+do_conn( char *uri, char *manager, struct berval *passwd,
+ LDAP **ld, int nobind, int maxretries, int conn_num );
+
+static void
+do_read( LDAP *ld, char *entry,
+ char **attrs, int noattrs, int nobind, int maxloop,
+ int maxretries, int delay, int force, int chaserefs, int idx );
+
+static void
+do_random( LDAP *ld,
+ char *sbase, char *filter, char **attrs, int noattrs, int nobind,
+ int innerloop, int maxretries, int delay, int force, int chaserefs,
+ int idx );
+
+static void
+do_random2( LDAP *ld,
+ char *sbase, char *filter, char **attrs, int noattrs, int nobind,
+ int innerloop, int maxretries, int delay, int force, int chaserefs,
+ int idx );
+
+static void *
+do_onethread( void *arg );
+
+static void *
+do_onerwthread( void *arg );
+
+#define MAX_THREAD 1024
+/* Use same array for readers and writers, offset writers by MAX_THREAD */
+int rt_pass[MAX_THREAD*2];
+int rt_fail[MAX_THREAD*2];
+int *rwt_pass = rt_pass + MAX_THREAD;
+int *rwt_fail = rt_fail + MAX_THREAD;
+ldap_pvt_thread_t rtid[MAX_THREAD*2], *rwtid = rtid + MAX_THREAD;
+
+/*
+ * Shared globals (command line args)
+ */
+LDAP *ld = NULL;
+char *entry = NULL;
+char *filter = NULL;
+int loops = LOOPS;
+int outerloops = 1;
+int retries = RETRIES;
+int delay = 0;
+int force = 0;
+int chaserefs = 0;
+char *srchattrs[] = { "1.1", NULL };
+char **attrs = srchattrs;
+int noattrs = 0;
+int nobind = 0;
+int threads = 1;
+int rwthreads = 0;
+int verbose = 0;
+
+int noconns = 1;
+LDAP **lds = NULL;
+
+static void
+thread_error(int idx, char *string)
+{
+ char thrstr[BUFSIZ];
+
+ snprintf(thrstr, BUFSIZ, "error on tidx: %d: %s", idx, string);
+ tester_error( thrstr );
+}
+
+static void
+thread_output(int idx, char *string)
+{
+ char thrstr[BUFSIZ];
+
+ snprintf(thrstr, BUFSIZ, "tidx: %d says: %s", idx, string);
+ tester_error( thrstr );
+}
+
+static void
+thread_verbose(int idx, char *string)
+{
+ char thrstr[BUFSIZ];
+
+ if (!verbose)
+ return;
+ snprintf(thrstr, BUFSIZ, "tidx: %d says: %s", idx, string);
+ tester_error( thrstr );
+}
+
+static void
+usage( char *name )
+{
+ fprintf( stderr,
+ "usage: %s "
+ "-H <uri> | ([-h <host>] -p <port>) "
+ "-D <manager> "
+ "-w <passwd> "
+ "-e <entry> "
+ "[-A] "
+ "[-C] "
+ "[-F] "
+ "[-N] "
+ "[-v] "
+ "[-c connections] "
+ "[-f filter] "
+ "[-i <ignore>] "
+ "[-l <loops>] "
+ "[-L <outerloops>] "
+ "[-m threads] "
+ "[-M threads] "
+ "[-r <maxretries>] "
+ "[-t <delay>] "
+ "[-T <attrs>] "
+ "[<attrs>] "
+ "\n",
+ name );
+ exit( EXIT_FAILURE );
+}
+
+int
+main( int argc, char **argv )
+{
+ int i;
+ char *uri = NULL;
+ char *host = "localhost";
+ int port = -1;
+ char *manager = NULL;
+ struct berval passwd = { 0, NULL };
+ char outstr[BUFSIZ];
+ int ptpass;
+ int testfail = 0;
+
+
+ tester_init( "slapd-mtread", TESTER_READ );
+
+ /* by default, tolerate referrals and no such object */
+ tester_ignore_str2errlist( "REFERRAL,NO_SUCH_OBJECT" );
+
+ while ( (i = getopt( argc, argv, "ACc:D:e:Ff:H:h:i:L:l:M:m:Np:r:t:T:w:v" )) != EOF ) {
+ switch ( i ) {
+ case 'A':
+ noattrs++;
+ break;
+
+ case 'C':
+ chaserefs++;
+ break;
+
+ case 'H': /* the server uri */
+ uri = strdup( optarg );
+ break;
+
+ case 'h': /* the servers host */
+ host = strdup( optarg );
+ break;
+
+ case 'i':
+ tester_ignore_str2errlist( optarg );
+ break;
+
+ case 'N':
+ nobind++;
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+
+ case 'p': /* the servers port */
+ if ( lutil_atoi( &port, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'D': /* the servers manager */
+ manager = strdup( optarg );
+ break;
+
+ case 'w': /* the server managers password */
+ passwd.bv_val = strdup( optarg );
+ passwd.bv_len = strlen( optarg );
+ memset( optarg, '*', passwd.bv_len );
+ break;
+
+ case 'c': /* the number of connections */
+ if ( lutil_atoi( &noconns, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'e': /* DN to search for */
+ entry = strdup( optarg );
+ break;
+
+ case 'f': /* the search request */
+ filter = strdup( optarg );
+ break;
+
+ case 'F':
+ force++;
+ break;
+
+ case 'l': /* the number of loops */
+ if ( lutil_atoi( &loops, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'L': /* the number of outerloops */
+ if ( lutil_atoi( &outerloops, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'M': /* the number of R/W threads */
+ if ( lutil_atoi( &rwthreads, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ if (rwthreads > MAX_THREAD)
+ rwthreads = MAX_THREAD;
+ break;
+
+ case 'm': /* the number of threads */
+ if ( lutil_atoi( &threads, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ if (threads > MAX_THREAD)
+ threads = MAX_THREAD;
+ break;
+
+ case 'r': /* the number of retries */
+ if ( lutil_atoi( &retries, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 't': /* delay in seconds */
+ if ( lutil_atoi( &delay, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'T':
+ attrs = ldap_str2charray( optarg, "," );
+ if ( attrs == NULL ) {
+ usage( argv[0] );
+ }
+ break;
+
+ default:
+ usage( argv[0] );
+ break;
+ }
+ }
+
+ if (( entry == NULL ) || ( port == -1 && uri == NULL ))
+ usage( argv[0] );
+
+ if ( *entry == '\0' ) {
+ fprintf( stderr, "%s: invalid EMPTY entry DN.\n",
+ argv[0] );
+ exit( EXIT_FAILURE );
+ }
+
+ if ( argv[optind] != NULL ) {
+ attrs = &argv[optind];
+ }
+
+ if (noconns < 1)
+ noconns = 1;
+ if (noconns > MAXCONN)
+ noconns = MAXCONN;
+ lds = (LDAP **) calloc( sizeof(LDAP *), noconns);
+ if (lds == NULL) {
+ fprintf( stderr, "%s: Memory error: calloc noconns.\n",
+ argv[0] );
+ exit( EXIT_FAILURE );
+ }
+
+ uri = tester_uri( uri, host, port );
+ /* One connection and one connection only */
+ do_conn( uri, manager, &passwd, &ld, nobind, retries, 0 );
+ lds[0] = ld;
+ for(i = 1; i < noconns; i++) {
+ do_conn( uri, manager, &passwd, &lds[i], nobind, retries, i );
+ }
+
+ ldap_pvt_thread_initialize();
+
+ snprintf(outstr, BUFSIZ, "MT Test Start: conns: %d (%s)", noconns, uri);
+ tester_error(outstr);
+ snprintf(outstr, BUFSIZ, "Threads: RO: %d RW: %d", threads, rwthreads);
+ tester_error(outstr);
+
+ /* Set up read only threads */
+ for ( i = 0; i < threads; i++ ) {
+ ldap_pvt_thread_create( &rtid[i], 0, do_onethread, &rtid[i]);
+ snprintf(outstr, BUFSIZ, "Created RO thread %d", i);
+ thread_verbose(-1, outstr);
+ }
+ /* Set up read/write threads */
+ for ( i = 0; i < rwthreads; i++ ) {
+ ldap_pvt_thread_create( &rwtid[i], 0, do_onerwthread, &rwtid[i]);
+ snprintf(outstr, BUFSIZ, "Created RW thread %d", i + MAX_THREAD);
+ thread_verbose(-1, outstr);
+ }
+
+ ptpass = outerloops * loops;
+
+ /* wait for read only threads to complete */
+ for ( i = 0; i < threads; i++ )
+ ldap_pvt_thread_join(rtid[i], NULL);
+ /* wait for read/write threads to complete */
+ for ( i = 0; i < rwthreads; i++ )
+ ldap_pvt_thread_join(rwtid[i], NULL);
+
+ for(i = 0; i < noconns; i++) {
+ if ( lds[i] != NULL ) {
+ ldap_unbind_ext( lds[i], NULL, NULL );
+ }
+ }
+ free( lds );
+
+ for ( i = 0; i < threads; i++ ) {
+ snprintf(outstr, BUFSIZ, "RO thread %d pass=%d fail=%d", i,
+ rt_pass[i], rt_fail[i]);
+ tester_error(outstr);
+ if (rt_fail[i] != 0 || rt_pass[i] != ptpass) {
+ snprintf(outstr, BUFSIZ, "FAIL RO thread %d", i);
+ tester_error(outstr);
+ testfail++;
+ }
+ }
+ for ( i = 0; i < rwthreads; i++ ) {
+ snprintf(outstr, BUFSIZ, "RW thread %d pass=%d fail=%d", i + MAX_THREAD,
+ rwt_pass[i], rwt_fail[i]);
+ tester_error(outstr);
+ if (rwt_fail[i] != 0 || rwt_pass[i] != ptpass) {
+ snprintf(outstr, BUFSIZ, "FAIL RW thread %d", i);
+ tester_error(outstr);
+ testfail++;
+ }
+ }
+ snprintf(outstr, BUFSIZ, "MT Test complete" );
+ tester_error(outstr);
+
+ if (testfail)
+ exit( EXIT_FAILURE );
+ exit( EXIT_SUCCESS );
+}
+
+static void *
+do_onethread( void *arg )
+{
+ int i, j, thisconn;
+ LDAP **mlds;
+ char thrstr[BUFSIZ];
+ int rc, refcnt = 0;
+ int idx = (ldap_pvt_thread_t *)arg - rtid;
+
+ mlds = (LDAP **) calloc( sizeof(LDAP *), noconns);
+ if (mlds == NULL) {
+ thread_error( idx, "Memory error: thread calloc for noconns" );
+ exit( EXIT_FAILURE );
+ }
+
+ for ( j = 0; j < outerloops; j++ ) {
+ for(i = 0; i < noconns; i++) {
+ mlds[i] = ldap_dup(lds[i]);
+ if (mlds[i] == NULL) {
+ thread_error( idx, "ldap_dup error" );
+ }
+ }
+ rc = ldap_get_option(mlds[0], LDAP_OPT_SESSION_REFCNT, &refcnt);
+ snprintf(thrstr, BUFSIZ,
+ "RO Thread conns: %d refcnt: %d (rc = %d)",
+ noconns, refcnt, rc);
+ thread_verbose(idx, thrstr);
+
+ thisconn = (idx + j) % noconns;
+ if (thisconn < 0 || thisconn >= noconns)
+ thisconn = 0;
+ if (mlds[thisconn] == NULL) {
+ thread_error( idx, "(failed to dup)");
+ tester_perror( "ldap_dup", "(failed to dup)" );
+ exit( EXIT_FAILURE );
+ }
+ snprintf(thrstr, BUFSIZ, "Using conn %d", thisconn);
+ thread_verbose(idx, thrstr);
+ if ( filter != NULL ) {
+ if (strchr(filter, '['))
+ do_random2( mlds[thisconn], entry, filter, attrs,
+ noattrs, nobind, loops, retries, delay, force,
+ chaserefs, idx );
+ else
+ do_random( mlds[thisconn], entry, filter, attrs,
+ noattrs, nobind, loops, retries, delay, force,
+ chaserefs, idx );
+
+ } else {
+ do_read( mlds[thisconn], entry, attrs,
+ noattrs, nobind, loops, retries, delay, force,
+ chaserefs, idx );
+ }
+ for(i = 0; i < noconns; i++) {
+ (void) ldap_destroy(mlds[i]);
+ mlds[i] = NULL;
+ }
+ }
+ free( mlds );
+ return( NULL );
+}
+
+static void *
+do_onerwthread( void *arg )
+{
+ int i, j, thisconn;
+ LDAP **mlds, *ld;
+ char thrstr[BUFSIZ];
+ char dn[256], uids[32], cns[32], *base;
+ LDAPMod *attrp[5], attrs[4];
+ char *oc_vals[] = { "top", "OpenLDAPperson", NULL };
+ char *cn_vals[] = { NULL, NULL };
+ char *sn_vals[] = { NULL, NULL };
+ char *uid_vals[] = { NULL, NULL };
+ int ret;
+ int adds = 0;
+ int dels = 0;
+ int rc, refcnt = 0;
+ int idx = (ldap_pvt_thread_t *)arg - rtid;
+
+ mlds = (LDAP **) calloc( sizeof(LDAP *), noconns);
+ if (mlds == NULL) {
+ thread_error( idx, "Memory error: thread calloc for noconns" );
+ exit( EXIT_FAILURE );
+ }
+
+ snprintf(uids, sizeof(uids), "rwtest%04d", idx);
+ snprintf(cns, sizeof(cns), "rwtest%04d", idx);
+ /* add setup */
+ for (i = 0; i < 4; i++) {
+ attrp[i] = &attrs[i];
+ attrs[i].mod_op = 0;
+ }
+ attrp[4] = NULL;
+ attrs[0].mod_type = "objectClass";
+ attrs[0].mod_values = oc_vals;
+ attrs[1].mod_type = "cn";
+ attrs[1].mod_values = cn_vals;
+ cn_vals[0] = &cns[0];
+ attrs[2].mod_type = "sn";
+ attrs[2].mod_values = sn_vals;
+ sn_vals[0] = &cns[0];
+ attrs[3].mod_type = "uid";
+ attrs[3].mod_values = uid_vals;
+ uid_vals[0] = &uids[0];
+
+ for ( j = 0; j < outerloops; j++ ) {
+ for(i = 0; i < noconns; i++) {
+ mlds[i] = ldap_dup(lds[i]);
+ if (mlds[i] == NULL) {
+ thread_error( idx, "ldap_dup error" );
+ }
+ }
+ rc = ldap_get_option(mlds[0], LDAP_OPT_SESSION_REFCNT, &refcnt);
+ snprintf(thrstr, BUFSIZ,
+ "RW Thread conns: %d refcnt: %d (rc = %d)",
+ noconns, refcnt, rc);
+ thread_verbose(idx, thrstr);
+
+ thisconn = (idx + j) % noconns;
+ if (thisconn < 0 || thisconn >= noconns)
+ thisconn = 0;
+ if (mlds[thisconn] == NULL) {
+ thread_error( idx, "(failed to dup)");
+ tester_perror( "ldap_dup", "(failed to dup)" );
+ exit( EXIT_FAILURE );
+ }
+ snprintf(thrstr, BUFSIZ, "START RW Thread using conn %d", thisconn);
+ thread_verbose(idx, thrstr);
+
+ ld = mlds[thisconn];
+ if (entry != NULL)
+ base = entry;
+ else
+ base = DEFAULT_BASE;
+ snprintf(dn, 256, "cn=%s,%s", cns, base);
+
+ adds = 0;
+ dels = 0;
+ for (i = 0; i < loops; i++) {
+ ret = ldap_add_ext_s(ld, dn, &attrp[0], NULL, NULL);
+ if (ret == LDAP_SUCCESS) {
+ adds++;
+ ret = ldap_delete_ext_s(ld, dn, NULL, NULL);
+ if (ret == LDAP_SUCCESS) {
+ dels++;
+ rt_pass[idx]++;
+ } else {
+ thread_output(idx, ldap_err2string(ret));
+ rt_fail[idx]++;
+ }
+ } else {
+ thread_output(idx, ldap_err2string(ret));
+ rt_fail[idx]++;
+ }
+ }
+
+ snprintf(thrstr, BUFSIZ,
+ "INNER STOP RW Thread using conn %d (%d/%d)",
+ thisconn, adds, dels);
+ thread_verbose(idx, thrstr);
+
+ for(i = 0; i < noconns; i++) {
+ (void) ldap_destroy(mlds[i]);
+ mlds[i] = NULL;
+ }
+ }
+
+ free( mlds );
+ return( NULL );
+}
+
+static void
+do_conn( char *uri, char *manager, struct berval *passwd,
+ LDAP **ldp, int nobind, int maxretries, int conn_num )
+{
+ LDAP *ld = NULL;
+ int version = LDAP_VERSION3;
+ int do_retry = maxretries;
+ int rc = LDAP_SUCCESS;
+ char thrstr[BUFSIZ];
+
+retry:;
+ ldap_initialize( &ld, uri );
+ if ( ld == NULL ) {
+ snprintf( thrstr, BUFSIZ, "connection: %d", conn_num );
+ tester_error( thrstr );
+ tester_perror( "ldap_initialize", NULL );
+ exit( EXIT_FAILURE );
+ }
+
+ (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+ (void) ldap_set_option( ld, LDAP_OPT_REFERRALS,
+ chaserefs ? LDAP_OPT_ON : LDAP_OPT_OFF );
+
+ if ( do_retry == maxretries ) {
+ snprintf( thrstr, BUFSIZ, "do_conn #%d\n", conn_num );
+ thread_verbose( -1, thrstr );
+ }
+
+ if ( nobind == 0 ) {
+ rc = ldap_sasl_bind_s( ld, manager, LDAP_SASL_SIMPLE, passwd, NULL, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( thrstr, BUFSIZ, "connection: %d", conn_num );
+ tester_error( thrstr );
+ tester_ldap_error( ld, "ldap_sasl_bind_s", NULL );
+ switch ( rc ) {
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ ld = NULL;
+ do_retry--;
+ if ( delay != 0 ) {
+ sleep( delay );
+ }
+ goto retry;
+ }
+ /* fallthru */
+ default:
+ break;
+ }
+ exit( EXIT_FAILURE );
+ }
+ }
+ *ldp = ld;
+}
+
+static void
+do_random( LDAP *ld,
+ char *sbase, char *filter, char **srchattrs, int noattrs, int nobind,
+ int innerloop, int maxretries, int delay, int force, int chaserefs,
+ int idx )
+{
+ int i = 0, do_retry = maxretries;
+ char *attrs[ 2 ];
+ int rc = LDAP_SUCCESS;
+ int nvalues = 0;
+ char **values = NULL;
+ LDAPMessage *res = NULL, *e = NULL;
+ char thrstr[BUFSIZ];
+
+ attrs[ 0 ] = LDAP_NO_ATTRS;
+ attrs[ 1 ] = NULL;
+
+ snprintf( thrstr, BUFSIZ,
+ "Read(%d): base=\"%s\", filter=\"%s\".\n",
+ innerloop, sbase, filter );
+ thread_verbose( idx, thrstr );
+
+ rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE,
+ filter, attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res );
+ switch ( rc ) {
+ case LDAP_SIZELIMIT_EXCEEDED:
+ case LDAP_TIMELIMIT_EXCEEDED:
+ case LDAP_SUCCESS:
+ nvalues = ldap_count_entries( ld, res );
+ if ( nvalues == 0 ) {
+ if ( rc ) {
+ tester_ldap_error( ld, "ldap_search_ext_s", NULL );
+ }
+ break;
+ }
+
+ values = malloc( ( nvalues + 1 ) * sizeof( char * ) );
+ for ( i = 0, e = ldap_first_entry( ld, res ); e != NULL; i++, e = ldap_next_entry( ld, e ) )
+ {
+ values[ i ] = ldap_get_dn( ld, e );
+ }
+ values[ i ] = NULL;
+
+ ldap_msgfree( res );
+
+ if ( do_retry == maxretries ) {
+ snprintf( thrstr, BUFSIZ,
+ "Read base=\"%s\" filter=\"%s\" got %d values.\n",
+ sbase, filter, nvalues );
+ thread_verbose( idx, thrstr );
+ }
+
+ for ( i = 0; i < innerloop; i++ ) {
+ int r = ((double)nvalues)*rand()/(RAND_MAX + 1.0);
+
+ do_read( ld, values[ r ],
+ srchattrs, noattrs, nobind, 1, maxretries,
+ delay, force, chaserefs, idx );
+ }
+ for( i = 0; i < nvalues; i++) {
+ if (values[i] != NULL)
+ ldap_memfree( values[i] );
+ }
+ free( values );
+ break;
+
+ default:
+ tester_ldap_error( ld, "ldap_search_ext_s", NULL );
+ break;
+ }
+
+ snprintf( thrstr, BUFSIZ, "Search done (%d).\n", rc );
+ thread_verbose( idx, thrstr );
+}
+
+/* substitute a generated int into the filter */
+static void
+do_random2( LDAP *ld,
+ char *sbase, char *filter, char **srchattrs, int noattrs, int nobind,
+ int innerloop, int maxretries, int delay, int force, int chaserefs,
+ int idx )
+{
+ int i = 0, do_retry = maxretries;
+ int rc = LDAP_SUCCESS;
+ int lo, hi, range;
+ int flen;
+ LDAPMessage *res = NULL;
+ char *ptr, *ftail;
+ char thrstr[BUFSIZ];
+ char fbuf[BUFSIZ];
+
+ snprintf( thrstr, BUFSIZ,
+ "Read(%d): base=\"%s\", filter=\"%s\".\n",
+ innerloop, sbase, filter );
+ thread_verbose( idx, thrstr );
+
+ ptr = strchr(filter, '[');
+ if (!ptr)
+ return;
+ ftail = strchr(filter, ']');
+ if (!ftail || ftail < ptr)
+ return;
+
+ sscanf(ptr, "[%d-%d]", &lo, &hi);
+ range = hi - lo + 1;
+
+ flen = ptr - filter;
+ ftail++;
+
+ for ( i = 0; i < innerloop; i++ ) {
+ int r = ((double)range)*rand()/(RAND_MAX + 1.0);
+ sprintf(fbuf, "%.*s%d%s", flen, filter, r, ftail);
+
+ rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE,
+ fbuf, srchattrs, noattrs, NULL, NULL, NULL,
+ LDAP_NO_LIMIT, &res );
+ if ( res != NULL ) {
+ ldap_msgfree( res );
+ }
+ if ( rc == 0 ) {
+ rt_pass[idx]++;
+ } else {
+ int first = tester_ignore_err( rc );
+ char buf[ BUFSIZ ];
+
+ rt_fail[idx]++;
+ snprintf( buf, sizeof( buf ), "ldap_search_ext_s(%s)", entry );
+
+ /* if ignore.. */
+ if ( first ) {
+ /* only log if first occurrence */
+ if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
+ tester_ldap_error( ld, buf, NULL );
+ }
+ continue;
+ }
+
+ /* busy needs special handling */
+ tester_ldap_error( ld, buf, NULL );
+ if ( rc == LDAP_BUSY && do_retry > 0 ) {
+ do_retry--;
+ continue;
+ }
+ break;
+ }
+ }
+
+ snprintf( thrstr, BUFSIZ, "Search done (%d).\n", rc );
+ thread_verbose( idx, thrstr );
+}
+
+static void
+do_read( LDAP *ld, char *entry,
+ char **attrs, int noattrs, int nobind, int maxloop,
+ int maxretries, int delay, int force, int chaserefs, int idx )
+{
+ int i = 0, do_retry = maxretries;
+ int rc = LDAP_SUCCESS;
+ char thrstr[BUFSIZ];
+
+retry:;
+ if ( do_retry == maxretries ) {
+ snprintf( thrstr, BUFSIZ, "Read(%d): entry=\"%s\".\n",
+ maxloop, entry );
+ thread_verbose( idx, thrstr );
+ }
+
+ snprintf(thrstr, BUFSIZ, "LD %p cnt: %d (retried %d) (%s)", \
+ (void *) ld, maxloop, (do_retry - maxretries), entry);
+ thread_verbose( idx, thrstr );
+
+ for ( ; i < maxloop; i++ ) {
+ LDAPMessage *res = NULL;
+
+ rc = ldap_search_ext_s( ld, entry, LDAP_SCOPE_BASE,
+ NULL, attrs, noattrs, NULL, NULL, NULL,
+ LDAP_NO_LIMIT, &res );
+ if ( res != NULL ) {
+ ldap_msgfree( res );
+ }
+
+ if ( rc == 0 ) {
+ rt_pass[idx]++;
+ } else {
+ int first = tester_ignore_err( rc );
+ char buf[ BUFSIZ ];
+
+ rt_fail[idx]++;
+ snprintf( buf, sizeof( buf ), "ldap_search_ext_s(%s)", entry );
+
+ /* if ignore.. */
+ if ( first ) {
+ /* only log if first occurrence */
+ if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
+ tester_ldap_error( ld, buf, NULL );
+ }
+ continue;
+ }
+
+ /* busy needs special handling */
+ tester_ldap_error( ld, buf, NULL );
+ if ( rc == LDAP_BUSY && do_retry > 0 ) {
+ do_retry--;
+ goto retry;
+ }
+ break;
+ }
+ }
+}
diff --git a/tests/progs/slapd-read.c b/tests/progs/slapd-read.c
new file mode 100644
index 0000000..1728d38
--- /dev/null
+++ b/tests/progs/slapd-read.c
@@ -0,0 +1,568 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Kurt Spanier for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/stdlib.h"
+
+#include "ac/ctype.h"
+#include "ac/param.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+#include "ac/unistd.h"
+#include "ac/wait.h"
+
+#include "ldap.h"
+#include "lutil.h"
+
+#include "ldap_pvt.h"
+
+#include "slapd-common.h"
+
+#define LOOPS 100
+#define RETRIES 0
+
+static void
+do_read( char *uri, char *manager, struct berval *passwd,
+ char *entry, LDAP **ld,
+ char **attrs, int noattrs, int nobind, int maxloop,
+ int maxretries, int delay, int force, int chaserefs );
+
+static void
+do_random( char *uri, char *manager, struct berval *passwd,
+ char *sbase, char *filter, char **attrs, int noattrs, int nobind,
+ int innerloop, int maxretries, int delay, int force, int chaserefs );
+
+static void
+usage( char *name )
+{
+ fprintf( stderr,
+ "usage: %s "
+ "-H <uri> | ([-h <host>] -p <port>) "
+ "-D <manager> "
+ "-w <passwd> "
+ "-e <entry> "
+ "[-A] "
+ "[-C] "
+ "[-F] "
+ "[-N] "
+ "[-f filter] "
+ "[-i <ignore>] "
+ "[-l <loops>] "
+ "[-L <outerloops>] "
+ "[-r <maxretries>] "
+ "[-t <delay>] "
+ "[-T <attrs>] "
+ "[<attrs>] "
+ "\n",
+ name );
+ exit( EXIT_FAILURE );
+}
+
+/* -S: just send requests without reading responses
+ * -SS: send all requests asynchronous and immediately start reading responses
+ * -SSS: send all requests asynchronous; then read responses
+ */
+static int swamp;
+
+int
+main( int argc, char **argv )
+{
+ int i;
+ char *uri = NULL;
+ char *host = "localhost";
+ int port = -1;
+ char *manager = NULL;
+ struct berval passwd = { 0, NULL };
+ char *entry = NULL;
+ char *filter = NULL;
+ int loops = LOOPS;
+ int outerloops = 1;
+ int retries = RETRIES;
+ int delay = 0;
+ int force = 0;
+ int chaserefs = 0;
+ char *srchattrs[] = { "1.1", NULL };
+ char **attrs = srchattrs;
+ int noattrs = 0;
+ int nobind = 0;
+
+ tester_init( "slapd-read", TESTER_READ );
+
+ /* by default, tolerate referrals and no such object */
+ tester_ignore_str2errlist( "REFERRAL,NO_SUCH_OBJECT" );
+
+ while ( (i = getopt( argc, argv, "ACD:e:Ff:H:h:i:L:l:p:r:St:T:w:" )) != EOF ) {
+ switch ( i ) {
+ case 'A':
+ noattrs++;
+ break;
+
+ case 'C':
+ chaserefs++;
+ break;
+
+ case 'H': /* the server uri */
+ uri = strdup( optarg );
+ break;
+
+ case 'h': /* the servers host */
+ host = strdup( optarg );
+ break;
+
+ case 'i':
+ tester_ignore_str2errlist( optarg );
+ break;
+
+ case 'N':
+ nobind++;
+ break;
+
+ case 'p': /* the servers port */
+ if ( lutil_atoi( &port, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'D': /* the servers manager */
+ manager = strdup( optarg );
+ break;
+
+ case 'w': /* the server managers password */
+ passwd.bv_val = strdup( optarg );
+ passwd.bv_len = strlen( optarg );
+ memset( optarg, '*', passwd.bv_len );
+ break;
+
+ case 'e': /* DN to search for */
+ entry = strdup( optarg );
+ break;
+
+ case 'f': /* the search request */
+ filter = strdup( optarg );
+ break;
+
+ case 'F':
+ force++;
+ break;
+
+ case 'l': /* the number of loops */
+ if ( lutil_atoi( &loops, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'L': /* the number of outerloops */
+ if ( lutil_atoi( &outerloops, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'r': /* the number of retries */
+ if ( lutil_atoi( &retries, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'S':
+ swamp++;
+ break;
+
+ case 't': /* delay in seconds */
+ if ( lutil_atoi( &delay, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'T':
+ attrs = ldap_str2charray( optarg, "," );
+ if ( attrs == NULL ) {
+ usage( argv[0] );
+ }
+ break;
+
+ default:
+ usage( argv[0] );
+ break;
+ }
+ }
+
+ if (( entry == NULL ) || ( port == -1 && uri == NULL ))
+ usage( argv[0] );
+
+ if ( *entry == '\0' ) {
+ fprintf( stderr, "%s: invalid EMPTY entry DN.\n",
+ argv[0] );
+ exit( EXIT_FAILURE );
+ }
+
+ if ( argv[optind] != NULL ) {
+ attrs = &argv[optind];
+ }
+
+ uri = tester_uri( uri, host, port );
+
+ for ( i = 0; i < outerloops; i++ ) {
+ if ( filter != NULL ) {
+ do_random( uri, manager, &passwd, entry, filter, attrs,
+ noattrs, nobind, loops, retries, delay, force,
+ chaserefs );
+
+ } else {
+ do_read( uri, manager, &passwd, entry, NULL, attrs,
+ noattrs, nobind, loops, retries, delay, force,
+ chaserefs );
+ }
+ }
+
+ exit( EXIT_SUCCESS );
+}
+
+static void
+do_random( char *uri, char *manager, struct berval *passwd,
+ char *sbase, char *filter, char **srchattrs, int noattrs, int nobind,
+ int innerloop, int maxretries, int delay, int force, int chaserefs )
+{
+ LDAP *ld = NULL;
+ int i = 0, do_retry = maxretries;
+ char *attrs[ 2 ];
+ int rc = LDAP_SUCCESS;
+ int version = LDAP_VERSION3;
+ int nvalues = 0;
+ char **values = NULL;
+ LDAPMessage *res = NULL, *e = NULL;
+
+ attrs[ 0 ] = LDAP_NO_ATTRS;
+ attrs[ 1 ] = NULL;
+
+ ldap_initialize( &ld, uri );
+ if ( ld == NULL ) {
+ tester_perror( "ldap_initialize", NULL );
+ exit( EXIT_FAILURE );
+ }
+
+ (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+ (void) ldap_set_option( ld, LDAP_OPT_REFERRALS,
+ chaserefs ? LDAP_OPT_ON : LDAP_OPT_OFF );
+
+ if ( do_retry == maxretries ) {
+ fprintf( stderr, "PID=%ld - Read(%d): base=\"%s\", filter=\"%s\".\n",
+ (long) pid, innerloop, sbase, filter );
+ }
+
+ if ( nobind == 0 ) {
+ rc = ldap_sasl_bind_s( ld, manager, LDAP_SASL_SIMPLE, passwd, NULL, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_sasl_bind_s", NULL );
+ switch ( rc ) {
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ /* fallthru */
+ default:
+ break;
+ }
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE,
+ filter, attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res );
+ switch ( rc ) {
+ case LDAP_SIZELIMIT_EXCEEDED:
+ case LDAP_TIMELIMIT_EXCEEDED:
+ case LDAP_SUCCESS:
+ nvalues = ldap_count_entries( ld, res );
+ if ( nvalues == 0 ) {
+ if ( rc ) {
+ tester_ldap_error( ld, "ldap_search_ext_s", NULL );
+ }
+ break;
+ }
+
+ values = malloc( ( nvalues + 1 ) * sizeof( char * ) );
+ for ( i = 0, e = ldap_first_entry( ld, res ); e != NULL; i++, e = ldap_next_entry( ld, e ) )
+ {
+ values[ i ] = ldap_get_dn( ld, e );
+ }
+ values[ i ] = NULL;
+
+ ldap_msgfree( res );
+
+ if ( do_retry == maxretries ) {
+ fprintf( stderr, " PID=%ld - Read base=\"%s\" filter=\"%s\" got %d values.\n",
+ (long) pid, sbase, filter, nvalues );
+ }
+
+ for ( i = 0; i < innerloop; i++ ) {
+#if 0 /* use high-order bits for better randomness (Numerical Recipes in "C") */
+ int r = rand() % nvalues;
+#endif
+ int r = ((double)nvalues)*rand()/(RAND_MAX + 1.0);
+
+ do_read( uri, manager, passwd, values[ r ], &ld,
+ srchattrs, noattrs, nobind, 1, maxretries,
+ delay, force, chaserefs );
+ }
+ free( values );
+ break;
+
+ default:
+ tester_ldap_error( ld, "ldap_search_ext_s", NULL );
+ break;
+ }
+
+ fprintf( stderr, " PID=%ld - Read done (%d).\n", (long) pid, rc );
+
+ if ( ld != NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ }
+}
+
+static void
+do_read( char *uri, char *manager, struct berval *passwd, char *entry,
+ LDAP **ldp, char **attrs, int noattrs, int nobind, int maxloop,
+ int maxretries, int delay, int force, int chaserefs )
+{
+ LDAP *ld = ldp ? *ldp : NULL;
+ int i = 0, do_retry = maxretries;
+ int rc = LDAP_SUCCESS;
+ int version = LDAP_VERSION3;
+ int *msgids = NULL, active = 0;
+
+ /* make room for msgid */
+ if ( swamp > 1 ) {
+ msgids = (int *)calloc( sizeof(int), maxloop );
+ }
+
+retry:;
+ if ( ld == NULL ) {
+ ldap_initialize( &ld, uri );
+ if ( ld == NULL ) {
+ tester_perror( "ldap_initialize", NULL );
+ exit( EXIT_FAILURE );
+ }
+
+ (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+ (void) ldap_set_option( ld, LDAP_OPT_REFERRALS,
+ chaserefs ? LDAP_OPT_ON : LDAP_OPT_OFF );
+
+ if ( do_retry == maxretries ) {
+ fprintf( stderr, "PID=%ld - Read(%d): entry=\"%s\".\n",
+ (long) pid, maxloop, entry );
+ }
+
+ if ( nobind == 0 ) {
+ rc = ldap_sasl_bind_s( ld, manager, LDAP_SASL_SIMPLE, passwd, NULL, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_sasl_bind_s", NULL );
+ switch ( rc ) {
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ ld = NULL;
+ do_retry--;
+ if ( delay != 0 ) {
+ sleep( delay );
+ }
+ goto retry;
+ }
+ /* fallthru */
+ default:
+ break;
+ }
+ exit( EXIT_FAILURE );
+ }
+ }
+ }
+
+ if ( swamp > 1 ) {
+ do {
+ LDAPMessage *res = NULL;
+ int j, msgid;
+
+ if ( i < maxloop ) {
+ rc = ldap_search_ext( ld, entry, LDAP_SCOPE_BASE,
+ NULL, attrs, noattrs, NULL, NULL,
+ NULL, LDAP_NO_LIMIT, &msgids[i] );
+
+ active++;
+#if 0
+ fprintf( stderr,
+ ">>> PID=%ld - Read maxloop=%d cnt=%d active=%d msgid=%d: "
+ "entry=\"%s\"\n",
+ (long) pid, maxloop, i, active, msgids[i],
+ entry );
+#endif
+ i++;
+
+ if ( rc ) {
+ char buf[BUFSIZ];
+ int first = tester_ignore_err( rc );
+ /* if ignore.. */
+ if ( first ) {
+ /* only log if first occurrence */
+ if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
+ tester_ldap_error( ld, "ldap_search_ext", NULL );
+ }
+ continue;
+ }
+
+ /* busy needs special handling */
+ snprintf( buf, sizeof( buf ), "entry=\"%s\"\n", entry );
+ tester_ldap_error( ld, "ldap_search_ext", buf );
+ if ( rc == LDAP_BUSY && do_retry > 0 ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ ld = NULL;
+ do_retry--;
+ goto retry;
+ }
+ break;
+ }
+
+ if ( swamp > 2 ) {
+ continue;
+ }
+ }
+
+ rc = ldap_result( ld, LDAP_RES_ANY, 0, NULL, &res );
+ switch ( rc ) {
+ case -1:
+ /* gone really bad */
+#if 0
+ fprintf( stderr,
+ ">>> PID=%ld - Read maxloop=%d cnt=%d active=%d: "
+ "entry=\"%s\" ldap_result()=%d\n",
+ (long) pid, maxloop, i, active, entry, rc );
+#endif
+ goto cleanup;
+
+ case 0:
+ /* timeout (impossible) */
+ break;
+
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ /* ignore */
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ /* just remove, no error checking (TODO?) */
+ msgid = ldap_msgid( res );
+ ldap_parse_result( ld, res, &rc, NULL, NULL, NULL, NULL, 1 );
+ res = NULL;
+
+ /* linear search, bah */
+ for ( j = 0; j < i; j++ ) {
+ if ( msgids[ j ] == msgid ) {
+ msgids[ j ] = -1;
+ active--;
+#if 0
+ fprintf( stderr,
+ "<<< PID=%ld - ReadDone maxloop=%d cnt=%d active=%d msgid=%d: "
+ "entry=\"%s\"\n",
+ (long) pid, maxloop, j, active, msgid, entry );
+#endif
+ break;
+ }
+ }
+ break;
+
+ default:
+ /* other messages unexpected */
+ fprintf( stderr,
+ "### PID=%ld - Read(%d): "
+ "entry=\"%s\" attrs=%s%s. unexpected response tag=%d\n",
+ (long) pid, maxloop,
+ entry, attrs[0], attrs[1] ? " (more...)" : "", rc );
+ break;
+ }
+
+ if ( res != NULL ) {
+ ldap_msgfree( res );
+ }
+ } while ( i < maxloop || active > 0 );
+
+ } else {
+ for ( ; i < maxloop; i++ ) {
+ LDAPMessage *res = NULL;
+
+ if (swamp) {
+ int msgid;
+ rc = ldap_search_ext( ld, entry, LDAP_SCOPE_BASE,
+ NULL, attrs, noattrs, NULL, NULL,
+ NULL, LDAP_NO_LIMIT, &msgid );
+ if ( rc == LDAP_SUCCESS ) continue;
+ else break;
+ }
+
+ rc = ldap_search_ext_s( ld, entry, LDAP_SCOPE_BASE,
+ NULL, attrs, noattrs, NULL, NULL, NULL,
+ LDAP_NO_LIMIT, &res );
+ if ( res != NULL ) {
+ ldap_msgfree( res );
+ }
+
+ if ( rc ) {
+ int first = tester_ignore_err( rc );
+ char buf[ BUFSIZ ];
+
+ snprintf( buf, sizeof( buf ), "ldap_search_ext_s(%s)", entry );
+
+ /* if ignore.. */
+ if ( first ) {
+ /* only log if first occurrence */
+ if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
+ tester_ldap_error( ld, buf, NULL );
+ }
+ continue;
+ }
+
+ /* busy needs special handling */
+ tester_ldap_error( ld, buf, NULL );
+ if ( rc == LDAP_BUSY && do_retry > 0 ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ ld = NULL;
+ do_retry--;
+ goto retry;
+ }
+ break;
+ }
+ }
+ }
+
+cleanup:;
+ if ( msgids != NULL ) {
+ free( msgids );
+ }
+
+ if ( ldp != NULL ) {
+ *ldp = ld;
+
+ } else {
+ fprintf( stderr, " PID=%ld - Read done (%d).\n", (long) pid, rc );
+
+ if ( ld != NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ }
+ }
+}
+
diff --git a/tests/progs/slapd-search.c b/tests/progs/slapd-search.c
new file mode 100644
index 0000000..89478aa
--- /dev/null
+++ b/tests/progs/slapd-search.c
@@ -0,0 +1,618 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Kurt Spanier for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/stdlib.h"
+
+#include "ac/ctype.h"
+#include "ac/param.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+#include "ac/unistd.h"
+#include "ac/wait.h"
+
+#include "ldap.h"
+#include "lutil.h"
+#include "ldap_pvt.h"
+
+#include "slapd-common.h"
+
+#define LOOPS 100
+#define RETRIES 0
+
+static void
+do_search( char *uri, char *manager, struct berval *passwd,
+ char *sbase, int scope, char *filter, LDAP **ldp,
+ char **attrs, int noattrs, int nobind,
+ int innerloop, int maxretries, int delay, int force, int chaserefs );
+
+static void
+do_random( char *uri, char *manager, struct berval *passwd,
+ char *sbase, int scope, char *filter, char *attr,
+ char **attrs, int noattrs, int nobind,
+ int innerloop, int maxretries, int delay, int force, int chaserefs );
+
+static void
+usage( char *name, char o )
+{
+ if ( o != '\0' ) {
+ fprintf( stderr, "unknown/incorrect option \"%c\"\n", o );
+ }
+
+ fprintf( stderr,
+ "usage: %s "
+ "-H <uri> | ([-h <host>] -p <port>) "
+ "-D <manager> "
+ "-w <passwd> "
+ "-b <searchbase> "
+ "-s <scope> "
+ "-f <searchfilter> "
+ "[-a <attr>] "
+ "[-A] "
+ "[-C] "
+ "[-F] "
+ "[-N] "
+ "[-S[S[S]]] "
+ "[-i <ignore>] "
+ "[-l <loops>] "
+ "[-L <outerloops>] "
+ "[-r <maxretries>] "
+ "[-t <delay>] "
+ "[<attrs>] "
+ "\n",
+ name );
+ exit( EXIT_FAILURE );
+}
+
+/* -S: just send requests without reading responses
+ * -SS: send all requests asynchronous and immediately start reading responses
+ * -SSS: send all requests asynchronous; then read responses
+ */
+static int swamp;
+
+int
+main( int argc, char **argv )
+{
+ int i;
+ char *uri = NULL;
+ char *host = "localhost";
+ int port = -1;
+ char *manager = NULL;
+ struct berval passwd = { 0, NULL };
+ char *sbase = NULL;
+ int scope = LDAP_SCOPE_SUBTREE;
+ char *filter = NULL;
+ char *attr = NULL;
+ char *srchattrs[] = { "cn", "sn", NULL };
+ char **attrs = srchattrs;
+ int loops = LOOPS;
+ int outerloops = 1;
+ int retries = RETRIES;
+ int delay = 0;
+ int force = 0;
+ int chaserefs = 0;
+ int noattrs = 0;
+ int nobind = 0;
+
+ tester_init( "slapd-search", TESTER_SEARCH );
+
+ /* by default, tolerate referrals and no such object */
+ tester_ignore_str2errlist( "REFERRAL,NO_SUCH_OBJECT" );
+
+ while ( ( i = getopt( argc, argv, "Aa:b:CD:f:FH:h:i:l:L:Np:r:Ss:t:T:w:" ) ) != EOF )
+ {
+ switch ( i ) {
+ case 'A':
+ noattrs++;
+ break;
+
+ case 'C':
+ chaserefs++;
+ break;
+
+ case 'H': /* the server uri */
+ uri = strdup( optarg );
+ break;
+
+ case 'h': /* the servers host */
+ host = strdup( optarg );
+ break;
+
+ case 'i':
+ tester_ignore_str2errlist( optarg );
+ break;
+
+ case 'N':
+ nobind++;
+ break;
+
+ case 'p': /* the servers port */
+ if ( lutil_atoi( &port, optarg ) != 0 ) {
+ usage( argv[0], i );
+ }
+ break;
+
+ case 'D': /* the servers manager */
+ manager = strdup( optarg );
+ break;
+
+ case 'w': /* the server managers password */
+ passwd.bv_val = strdup( optarg );
+ passwd.bv_len = strlen( optarg );
+ memset( optarg, '*', passwd.bv_len );
+ break;
+
+ case 'a':
+ attr = strdup( optarg );
+ break;
+
+ case 'b': /* file with search base */
+ sbase = strdup( optarg );
+ break;
+
+ case 'f': /* the search request */
+ filter = strdup( optarg );
+ break;
+
+ case 'F':
+ force++;
+ break;
+
+ case 'l': /* number of loops */
+ if ( lutil_atoi( &loops, optarg ) != 0 ) {
+ usage( argv[0], i );
+ }
+ break;
+
+ case 'L': /* number of loops */
+ if ( lutil_atoi( &outerloops, optarg ) != 0 ) {
+ usage( argv[0], i );
+ }
+ break;
+
+ case 'r': /* number of retries */
+ if ( lutil_atoi( &retries, optarg ) != 0 ) {
+ usage( argv[0], i );
+ }
+ break;
+
+ case 't': /* delay in seconds */
+ if ( lutil_atoi( &delay, optarg ) != 0 ) {
+ usage( argv[0], i );
+ }
+ break;
+
+ case 'T':
+ attrs = ldap_str2charray( optarg, "," );
+ if ( attrs == NULL ) {
+ usage( argv[0], i );
+ }
+ break;
+
+ case 'S':
+ swamp++;
+ break;
+
+ case 's':
+ scope = ldap_pvt_str2scope( optarg );
+ if ( scope == -1 ) {
+ usage( argv[0], i );
+ }
+ break;
+
+ default:
+ usage( argv[0], i );
+ break;
+ }
+ }
+
+ if (( sbase == NULL ) || ( filter == NULL ) || ( port == -1 && uri == NULL ))
+ usage( argv[0], '\0' );
+
+ if ( *filter == '\0' ) {
+
+ fprintf( stderr, "%s: invalid EMPTY search filter.\n",
+ argv[0] );
+ exit( EXIT_FAILURE );
+
+ }
+
+ if ( argv[optind] != NULL ) {
+ attrs = &argv[optind];
+ }
+
+ uri = tester_uri( uri, host, port );
+
+ for ( i = 0; i < outerloops; i++ ) {
+ if ( attr != NULL ) {
+ do_random( uri, manager, &passwd,
+ sbase, scope, filter, attr,
+ attrs, noattrs, nobind,
+ loops, retries, delay, force, chaserefs );
+
+ } else {
+ do_search( uri, manager, &passwd,
+ sbase, scope, filter, NULL,
+ attrs, noattrs, nobind,
+ loops, retries, delay, force, chaserefs );
+ }
+ }
+
+ exit( EXIT_SUCCESS );
+}
+
+
+static void
+do_random( char *uri, char *manager, struct berval *passwd,
+ char *sbase, int scope, char *filter, char *attr,
+ char **srchattrs, int noattrs, int nobind,
+ int innerloop, int maxretries, int delay, int force, int chaserefs )
+{
+ LDAP *ld = NULL;
+ int i = 0, do_retry = maxretries;
+ char *attrs[ 2 ];
+ int rc = LDAP_SUCCESS;
+ int version = LDAP_VERSION3;
+ int nvalues = 0;
+ char **values = NULL;
+ LDAPMessage *res = NULL, *e = NULL;
+
+ attrs[ 0 ] = attr;
+ attrs[ 1 ] = NULL;
+
+ ldap_initialize( &ld, uri );
+ if ( ld == NULL ) {
+ tester_perror( "ldap_initialize", NULL );
+ exit( EXIT_FAILURE );
+ }
+
+ (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+ (void) ldap_set_option( ld, LDAP_OPT_REFERRALS,
+ chaserefs ? LDAP_OPT_ON : LDAP_OPT_OFF );
+
+ if ( do_retry == maxretries ) {
+ fprintf( stderr, "PID=%ld - Search(%d): base=\"%s\", filter=\"%s\" attr=\"%s\".\n",
+ (long) pid, innerloop, sbase, filter, attr );
+ }
+
+ if ( nobind == 0 ) {
+ rc = ldap_sasl_bind_s( ld, manager, LDAP_SASL_SIMPLE, passwd, NULL, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_sasl_bind_s", NULL );
+ switch ( rc ) {
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ /* fallthru */
+ default:
+ break;
+ }
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE,
+ filter, attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res );
+ switch ( rc ) {
+ case LDAP_SIZELIMIT_EXCEEDED:
+ case LDAP_TIMELIMIT_EXCEEDED:
+ case LDAP_SUCCESS:
+ if ( ldap_count_entries( ld, res ) == 0 ) {
+ if ( rc ) {
+ tester_ldap_error( ld, "ldap_search_ext_s", NULL );
+ }
+ break;
+ }
+
+ for ( e = ldap_first_entry( ld, res ); e != NULL; e = ldap_next_entry( ld, e ) )
+ {
+ struct berval **v = ldap_get_values_len( ld, e, attr );
+
+ if ( v != NULL ) {
+ int n = ldap_count_values_len( v );
+ int j;
+
+ values = realloc( values, ( nvalues + n + 1 )*sizeof( char * ) );
+ for ( j = 0; j < n; j++ ) {
+ values[ nvalues + j ] = strdup( v[ j ]->bv_val );
+ }
+ values[ nvalues + j ] = NULL;
+ nvalues += n;
+ ldap_value_free_len( v );
+ }
+ }
+
+ ldap_msgfree( res );
+
+ if ( !values ) {
+ fprintf( stderr, " PID=%ld - Search base=\"%s\" filter=\"%s\" got %d values.\n",
+ (long) pid, sbase, filter, nvalues );
+ exit(EXIT_FAILURE);
+ }
+
+ if ( do_retry == maxretries ) {
+ fprintf( stderr, " PID=%ld - Search base=\"%s\" filter=\"%s\" got %d values.\n",
+ (long) pid, sbase, filter, nvalues );
+ }
+
+ for ( i = 0; i < innerloop; i++ ) {
+ char buf[ BUFSIZ ];
+#if 0 /* use high-order bits for better randomness (Numerical Recipes in "C") */
+ int r = rand() % nvalues;
+#endif
+ int r = ((double)nvalues)*rand()/(RAND_MAX + 1.0);
+
+ snprintf( buf, sizeof( buf ), "(%s=%s)", attr, values[ r ] );
+
+ do_search( uri, manager, passwd,
+ sbase, scope, buf, &ld,
+ srchattrs, noattrs, nobind,
+ 1, maxretries, delay, force, chaserefs );
+ }
+ break;
+
+ default:
+ tester_ldap_error( ld, "ldap_search_ext_s", NULL );
+ break;
+ }
+
+ fprintf( stderr, " PID=%ld - Search done (%d).\n", (long) pid, rc );
+
+ if ( ld != NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ }
+}
+
+static void
+do_search( char *uri, char *manager, struct berval *passwd,
+ char *sbase, int scope, char *filter, LDAP **ldp,
+ char **attrs, int noattrs, int nobind,
+ int innerloop, int maxretries, int delay, int force, int chaserefs )
+{
+ LDAP *ld = ldp ? *ldp : NULL;
+ int i = 0, do_retry = maxretries;
+ int rc = LDAP_SUCCESS;
+ int version = LDAP_VERSION3;
+ char buf[ BUFSIZ ];
+ int *msgids = NULL, active = 0;
+
+ /* make room for msgid */
+ if ( swamp > 1 ) {
+ msgids = (int *)calloc( sizeof(int), innerloop );
+ }
+
+retry:;
+ if ( ld == NULL ) {
+ ldap_initialize( &ld, uri );
+ if ( ld == NULL ) {
+ tester_perror( "ldap_initialize", NULL );
+ exit( EXIT_FAILURE );
+ }
+
+ (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+ (void) ldap_set_option( ld, LDAP_OPT_REFERRALS,
+ chaserefs ? LDAP_OPT_ON : LDAP_OPT_OFF );
+
+ if ( do_retry == maxretries ) {
+ fprintf( stderr,
+ "PID=%ld - Search(%d): "
+ "base=\"%s\" scope=%s filter=\"%s\" "
+ "attrs=%s%s.\n",
+ (long) pid, innerloop,
+ sbase, ldap_pvt_scope2str( scope ), filter,
+ attrs[0], attrs[1] ? " (more...)" : "" );
+ }
+
+ if ( nobind == 0 ) {
+ rc = ldap_sasl_bind_s( ld, manager, LDAP_SASL_SIMPLE, passwd, NULL, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( buf, sizeof( buf ),
+ "bindDN=\"%s\"", manager );
+ tester_ldap_error( ld, "ldap_sasl_bind_s", buf );
+ switch ( rc ) {
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ ld = NULL;
+ do_retry--;
+ if ( delay != 0 ) {
+ sleep( delay );
+ }
+ goto retry;
+ }
+ /* fallthru */
+ default:
+ break;
+ }
+ exit( EXIT_FAILURE );
+ }
+ }
+ }
+
+ if ( swamp > 1 ) {
+ do {
+ LDAPMessage *res = NULL;
+ int j, msgid;
+
+ if ( i < innerloop ) {
+ rc = ldap_search_ext( ld, sbase, scope,
+ filter, NULL, noattrs, NULL, NULL,
+ NULL, LDAP_NO_LIMIT, &msgids[i] );
+
+ active++;
+#if 0
+ fprintf( stderr,
+ ">>> PID=%ld - Search maxloop=%d cnt=%d active=%d msgid=%d: "
+ "base=\"%s\" scope=%s filter=\"%s\"\n",
+ (long) pid, innerloop, i, active, msgids[i],
+ sbase, ldap_pvt_scope2str( scope ), filter );
+#endif
+ i++;
+
+ if ( rc ) {
+ int first = tester_ignore_err( rc );
+ /* if ignore.. */
+ if ( first ) {
+ /* only log if first occurrence */
+ if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
+ tester_ldap_error( ld, "ldap_search_ext", NULL );
+ }
+ continue;
+ }
+
+ /* busy needs special handling */
+ snprintf( buf, sizeof( buf ),
+ "base=\"%s\" filter=\"%s\"\n",
+ sbase, filter );
+ tester_ldap_error( ld, "ldap_search_ext", buf );
+ if ( rc == LDAP_BUSY && do_retry > 0 ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ ld = NULL;
+ do_retry--;
+ goto retry;
+ }
+ break;
+ }
+
+ if ( swamp > 2 ) {
+ continue;
+ }
+ }
+
+ rc = ldap_result( ld, LDAP_RES_ANY, 0, NULL, &res );
+ switch ( rc ) {
+ case -1:
+ /* gone really bad */
+ goto cleanup;
+
+ case 0:
+ /* timeout (impossible) */
+ break;
+
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ /* ignore */
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ /* just remove, no error checking (TODO?) */
+ msgid = ldap_msgid( res );
+ ldap_parse_result( ld, res, &rc, NULL, NULL, NULL, NULL, 1 );
+ res = NULL;
+
+ /* linear search, bah */
+ for ( j = 0; j < i; j++ ) {
+ if ( msgids[ j ] == msgid ) {
+ msgids[ j ] = -1;
+ active--;
+#if 0
+ fprintf( stderr,
+ "<<< PID=%ld - SearchDone maxloop=%d cnt=%d active=%d msgid=%d: "
+ "base=\"%s\" scope=%s filter=\"%s\"\n",
+ (long) pid, innerloop, j, active, msgid,
+ sbase, ldap_pvt_scope2str( scope ), filter );
+#endif
+ break;
+ }
+ }
+ break;
+
+ default:
+ /* other messages unexpected */
+ fprintf( stderr,
+ "### PID=%ld - Search(%d): "
+ "base=\"%s\" scope=%s filter=\"%s\" "
+ "attrs=%s%s. unexpected response tag=%d\n",
+ (long) pid, innerloop,
+ sbase, ldap_pvt_scope2str( scope ), filter,
+ attrs[0], attrs[1] ? " (more...)" : "", rc );
+ break;
+ }
+
+ if ( res != NULL ) {
+ ldap_msgfree( res );
+ }
+ } while ( i < innerloop || active > 0 );
+
+ } else {
+ for ( ; i < innerloop; i++ ) {
+ LDAPMessage *res = NULL;
+
+ if (swamp) {
+ int msgid;
+ rc = ldap_search_ext( ld, sbase, scope,
+ filter, NULL, noattrs, NULL, NULL,
+ NULL, LDAP_NO_LIMIT, &msgid );
+ if ( rc == LDAP_SUCCESS ) continue;
+ else break;
+ }
+
+ rc = ldap_search_ext_s( ld, sbase, scope,
+ filter, attrs, noattrs, NULL, NULL,
+ NULL, LDAP_NO_LIMIT, &res );
+ if ( res != NULL ) {
+ ldap_msgfree( res );
+ }
+
+ if ( rc ) {
+ int first = tester_ignore_err( rc );
+ /* if ignore.. */
+ if ( first ) {
+ /* only log if first occurrence */
+ if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
+ tester_ldap_error( ld, "ldap_search_ext_s", NULL );
+ }
+ continue;
+ }
+
+ /* busy needs special handling */
+ snprintf( buf, sizeof( buf ),
+ "base=\"%s\" filter=\"%s\"\n",
+ sbase, filter );
+ tester_ldap_error( ld, "ldap_search_ext_s", buf );
+ if ( rc == LDAP_BUSY && do_retry > 0 ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ ld = NULL;
+ do_retry--;
+ goto retry;
+ }
+ break;
+ }
+ }
+ }
+
+cleanup:;
+ if ( msgids != NULL ) {
+ free( msgids );
+ }
+
+ if ( ldp != NULL ) {
+ *ldp = ld;
+
+ } else {
+ fprintf( stderr, " PID=%ld - Search done (%d).\n", (long) pid, rc );
+
+ if ( ld != NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ }
+ }
+}
diff --git a/tests/progs/slapd-tester.c b/tests/progs/slapd-tester.c
new file mode 100644
index 0000000..693a485
--- /dev/null
+++ b/tests/progs/slapd-tester.c
@@ -0,0 +1,1197 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Kurt Spanier for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/stdlib.h"
+
+#include "ac/ctype.h"
+#include "ac/dirent.h"
+#include "ac/param.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+#include "ac/unistd.h"
+#include "ac/wait.h"
+
+
+#include "ldap_defaults.h"
+#include "lutil.h"
+
+#include "ldap.h"
+#include "ldap_pvt.h"
+#include "lber_pvt.h"
+#include "slapd-common.h"
+
+#ifdef _WIN32
+#define EXE ".exe"
+#else
+#define EXE
+#endif
+
+#define SEARCHCMD "slapd-search" EXE
+#define READCMD "slapd-read" EXE
+#define ADDCMD "slapd-addel" EXE
+#define MODRDNCMD "slapd-modrdn" EXE
+#define MODIFYCMD "slapd-modify" EXE
+#define BINDCMD "slapd-bind" EXE
+#define MAXARGS 100
+#define MAXREQS 5000
+#define LOOPS 100
+#define OUTERLOOPS "1"
+#define RETRIES "0"
+
+#define TSEARCHFILE "do_search.0"
+#define TREADFILE "do_read.0"
+#define TADDFILE "do_add."
+#define TMODRDNFILE "do_modrdn.0"
+#define TMODIFYFILE "do_modify.0"
+#define TBINDFILE "do_bind.0"
+
+static char *get_file_name( char *dirname, char *filename );
+static int get_search_filters( char *filename, char *filters[], char *attrs[], char *bases[], LDAPURLDesc *luds[] );
+static int get_read_entries( char *filename, char *entries[], char *filters[] );
+static void fork_child( char *prog, char **args );
+static void wait4kids( int nkidval );
+
+static int maxkids = 20;
+static int nkids;
+
+#ifdef HAVE_WINSOCK
+static HANDLE *children;
+static char argbuf[BUFSIZ];
+#define ArgDup(x) strdup(strcat(strcat(strcpy(argbuf,"\""),x),"\""))
+#else
+#define ArgDup(x) strdup(x)
+#endif
+
+static void
+usage( char *name, char opt )
+{
+ if ( opt ) {
+ fprintf( stderr, "%s: unable to handle option \'%c\'\n\n",
+ name, opt );
+ }
+
+ fprintf( stderr,
+ "usage: %s "
+ "-H <uri> | ([-h <host>] -p <port>) "
+ "-D <manager> "
+ "-w <passwd> "
+ "-d <datadir> "
+ "[-i <ignore>] "
+ "[-j <maxchild>] "
+ "[-l {<loops>|<type>=<loops>[,...]}] "
+ "[-L <outerloops>] "
+ "-P <progdir> "
+ "[-r <maxretries>] "
+ "[-t <delay>] "
+ "[-C] "
+ "[-F] "
+ "[-I] "
+ "[-N]\n",
+ name );
+ exit( EXIT_FAILURE );
+}
+
+int
+main( int argc, char **argv )
+{
+ int i, j;
+ char *uri = NULL;
+ char *host = "localhost";
+ char *port = NULL;
+ char *manager = NULL;
+ char *passwd = NULL;
+ char *dirname = NULL;
+ char *progdir = NULL;
+ int loops = LOOPS;
+ char *outerloops = OUTERLOOPS;
+ char *retries = RETRIES;
+ char *delay = "0";
+ DIR *datadir;
+ struct dirent *file;
+ int friendly = 0;
+ int chaserefs = 0;
+ int noattrs = 0;
+ int nobind = 0;
+ int noinit = 1;
+ char *ignore = NULL;
+ /* search */
+ char *sfile = NULL;
+ char *sreqs[MAXREQS];
+ char *sattrs[MAXREQS];
+ char *sbase[MAXREQS];
+ LDAPURLDesc *slud[MAXREQS];
+ int snum = 0;
+ char *sargs[MAXARGS];
+ int sanum;
+ int sextra_args = 0;
+ char scmd[MAXPATHLEN];
+ int swamp = 0;
+ char swampopt[sizeof("-SSS")];
+ /* static so that its address can be used in initializer below. */
+ static char sloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
+ /* read */
+ char *rfile = NULL;
+ char *rreqs[MAXREQS];
+ int rnum = 0;
+ char *rargs[MAXARGS];
+ char *rflts[MAXREQS];
+ int ranum;
+ int rextra_args = 0;
+ char rcmd[MAXPATHLEN];
+ static char rloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
+ /* addel */
+ char *afiles[MAXREQS];
+ int anum = 0;
+ char *aargs[MAXARGS];
+ int aanum;
+ char acmd[MAXPATHLEN];
+ static char aloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
+ /* modrdn */
+ char *nfile = NULL;
+ char *nreqs[MAXREQS];
+ int nnum = 0;
+ char *nargs[MAXARGS];
+ int nanum;
+ char ncmd[MAXPATHLEN];
+ static char nloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
+ /* modify */
+ char *mfile = NULL;
+ char *mreqs[MAXREQS];
+ char *mdn[MAXREQS];
+ int mnum = 0;
+ char *margs[MAXARGS];
+ int manum;
+ char mcmd[MAXPATHLEN];
+ static char mloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
+ /* bind */
+ char *bfile = NULL;
+ char *breqs[MAXREQS];
+ char *bcreds[MAXREQS];
+ char *battrs[MAXREQS];
+ int bnum = 0;
+ char *bargs[MAXARGS];
+ int banum;
+ char bcmd[MAXPATHLEN];
+ static char bloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
+ char **bargs_extra = NULL;
+
+ char *friendlyOpt = NULL;
+ int pw_ask = 0;
+ char *pw_file = NULL;
+
+ /* extra action to do after bind... */
+ typedef struct extra_t {
+ char *action;
+ struct extra_t *next;
+ } extra_t;
+
+ extra_t *extra = NULL;
+ int nextra = 0;
+
+ tester_init( "slapd-tester", TESTER_TESTER );
+
+ sloops[0] = '\0';
+ rloops[0] = '\0';
+ aloops[0] = '\0';
+ nloops[0] = '\0';
+ mloops[0] = '\0';
+ bloops[0] = '\0';
+
+ while ( ( i = getopt( argc, argv, "AB:CD:d:FH:h:Ii:j:L:l:NP:p:r:St:Ww:y:" ) ) != EOF )
+ {
+ switch ( i ) {
+ case 'A':
+ noattrs++;
+ break;
+
+ case 'B': {
+ char **p,
+ **b = ldap_str2charray( optarg, "," );
+ extra_t **epp;
+
+ for ( epp = &extra; *epp; epp = &(*epp)->next )
+ ;
+
+ for ( p = b; p[0]; p++ ) {
+ *epp = calloc( 1, sizeof( extra_t ) );
+ (*epp)->action = p[0];
+ epp = &(*epp)->next;
+ nextra++;
+ }
+
+ ldap_memfree( b );
+ } break;
+
+ case 'C':
+ chaserefs++;
+ break;
+
+ case 'D': /* slapd manager */
+ manager = ArgDup( optarg );
+ break;
+
+ case 'd': /* data directory */
+ dirname = strdup( optarg );
+ break;
+
+ case 'F':
+ friendly++;
+ break;
+
+ case 'H': /* slapd uri */
+ uri = strdup( optarg );
+ break;
+
+ case 'h': /* slapd host */
+ host = strdup( optarg );
+ break;
+
+ case 'I':
+ noinit = 0;
+ break;
+
+ case 'i':
+ ignore = optarg;
+ break;
+
+ case 'j': /* the number of parallel clients */
+ if ( lutil_atoi( &maxkids, optarg ) != 0 ) {
+ usage( argv[0], 'j' );
+ }
+ break;
+
+ case 'l': /* the number of loops per client */
+ if ( !isdigit( (unsigned char) optarg[0] ) ) {
+ char **p,
+ **l = ldap_str2charray( optarg, "," );
+
+ for ( p = l; p[0]; p++) {
+ struct {
+ struct berval type;
+ char *buf;
+ } types[] = {
+ { BER_BVC( "add=" ), aloops },
+ { BER_BVC( "bind=" ), bloops },
+ { BER_BVC( "modify=" ), mloops },
+ { BER_BVC( "modrdn=" ), nloops },
+ { BER_BVC( "read=" ), rloops },
+ { BER_BVC( "search=" ), sloops },
+ { BER_BVNULL, NULL }
+ };
+ int c, n;
+
+ for ( c = 0; types[c].type.bv_val; c++ ) {
+ if ( strncasecmp( p[0], types[c].type.bv_val, types[c].type.bv_len ) == 0 ) {
+ break;
+ }
+ }
+
+ if ( types[c].type.bv_val == NULL ) {
+ usage( argv[0], 'l' );
+ }
+
+ if ( lutil_atoi( &n, &p[0][types[c].type.bv_len] ) != 0 ) {
+ usage( argv[0], 'l' );
+ }
+
+ snprintf( types[c].buf, sizeof( aloops ), "%d", n );
+ }
+
+ ldap_charray_free( l );
+
+ } else if ( lutil_atoi( &loops, optarg ) != 0 ) {
+ usage( argv[0], 'l' );
+ }
+ break;
+
+ case 'L': /* the number of outerloops per client */
+ outerloops = strdup( optarg );
+ break;
+
+ case 'N':
+ nobind++;
+ break;
+
+ case 'P': /* prog directory */
+ progdir = strdup( optarg );
+ break;
+
+ case 'p': /* the servers port number */
+ port = strdup( optarg );
+ break;
+
+ case 'r': /* the number of retries in case of error */
+ retries = strdup( optarg );
+ break;
+
+ case 'S':
+ swamp++;
+ break;
+
+ case 't': /* the delay in seconds between each retry */
+ delay = strdup( optarg );
+ break;
+
+ case 'w': /* the managers passwd */
+ passwd = ArgDup( optarg );
+ memset( optarg, '*', strlen( optarg ) );
+ break;
+
+ case 'W':
+ pw_ask++;
+ break;
+
+ case 'y':
+ pw_file = optarg;
+ break;
+
+ default:
+ usage( argv[0], '\0' );
+ break;
+ }
+ }
+
+ if (( dirname == NULL ) || ( port == NULL && uri == NULL ) ||
+ ( manager == NULL ) || ( passwd == NULL ) || ( progdir == NULL ))
+ {
+ usage( argv[0], '\0' );
+ }
+
+#ifdef HAVE_WINSOCK
+ children = malloc( maxkids * sizeof(HANDLE) );
+#endif
+ /* get the file list */
+ if ( ( datadir = opendir( dirname )) == NULL ) {
+ fprintf( stderr, "%s: couldn't open data directory \"%s\".\n",
+ argv[0], dirname );
+ exit( EXIT_FAILURE );
+ }
+
+ /* look for search, read, modrdn, and add/delete files */
+ for ( file = readdir( datadir ); file; file = readdir( datadir )) {
+
+ if ( !strcasecmp( file->d_name, TSEARCHFILE )) {
+ sfile = get_file_name( dirname, file->d_name );
+ continue;
+ } else if ( !strcasecmp( file->d_name, TREADFILE )) {
+ rfile = get_file_name( dirname, file->d_name );
+ continue;
+ } else if ( !strcasecmp( file->d_name, TMODRDNFILE )) {
+ nfile = get_file_name( dirname, file->d_name );
+ continue;
+ } else if ( !strcasecmp( file->d_name, TMODIFYFILE )) {
+ mfile = get_file_name( dirname, file->d_name );
+ continue;
+ } else if ( !strncasecmp( file->d_name, TADDFILE, strlen( TADDFILE ))
+ && ( anum < MAXREQS )) {
+ afiles[anum++] = get_file_name( dirname, file->d_name );
+ continue;
+ } else if ( !strcasecmp( file->d_name, TBINDFILE )) {
+ bfile = get_file_name( dirname, file->d_name );
+ continue;
+ }
+ }
+
+ closedir( datadir );
+
+ if ( pw_ask ) {
+ passwd = getpassphrase( _("Enter LDAP Password: ") );
+
+ } else if ( pw_file ) {
+ struct berval pw;
+
+ if ( lutil_get_filed_password( pw_file, &pw ) ) {
+ exit( EXIT_FAILURE );
+ }
+
+ passwd = pw.bv_val;
+ }
+
+ if ( !sfile && !rfile && !nfile && !mfile && !bfile && !anum ) {
+ fprintf( stderr, "no data files found.\n" );
+ exit( EXIT_FAILURE );
+ }
+
+ /* look for search requests */
+ if ( sfile ) {
+ snum = get_search_filters( sfile, sreqs, sattrs, sbase, slud );
+ if ( snum < 0 ) {
+ fprintf( stderr,
+ "unable to parse file \"%s\" line %d\n",
+ sfile, -2*(snum + 1));
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ /* look for read requests */
+ if ( rfile ) {
+ rnum = get_read_entries( rfile, rreqs, rflts );
+ if ( rnum < 0 ) {
+ fprintf( stderr,
+ "unable to parse file \"%s\" line %d\n",
+ rfile, -2*(rnum + 1) );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ /* look for modrdn requests */
+ if ( nfile ) {
+ nnum = get_read_entries( nfile, nreqs, NULL );
+ if ( nnum < 0 ) {
+ fprintf( stderr,
+ "unable to parse file \"%s\" line %d\n",
+ nfile, -2*(nnum + 1) );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ /* look for modify requests */
+ if ( mfile ) {
+ mnum = get_search_filters( mfile, mreqs, NULL, mdn, NULL );
+ if ( mnum < 0 ) {
+ fprintf( stderr,
+ "unable to parse file \"%s\" line %d\n",
+ mfile, -2*(mnum + 1) );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ /* look for bind requests */
+ if ( bfile ) {
+ bnum = get_search_filters( bfile, bcreds, battrs, breqs, NULL );
+ if ( bnum < 0 ) {
+ fprintf( stderr,
+ "unable to parse file \"%s\" line %d\n",
+ bfile, -2*(bnum + 1) );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ /* setup friendly option */
+ switch ( friendly ) {
+ case 0:
+ break;
+
+ case 1:
+ friendlyOpt = "-F";
+ break;
+
+ default:
+ /* NOTE: right now we don't need it more than twice */
+ case 2:
+ friendlyOpt = "-FF";
+ break;
+ }
+
+ /* setup swamp option */
+ if ( swamp ) {
+ swampopt[0] = '-';
+ if ( swamp > 3 ) swamp = 3;
+ swampopt[swamp + 1] = '\0';
+ for ( ; swamp-- > 0; ) swampopt[swamp + 1] = 'S';
+ }
+
+ /* setup loop options */
+ if ( sloops[0] == '\0' ) snprintf( sloops, sizeof( sloops ), "%d", 10 * loops );
+ if ( rloops[0] == '\0' ) snprintf( rloops, sizeof( rloops ), "%d", 20 * loops );
+ if ( aloops[0] == '\0' ) snprintf( aloops, sizeof( aloops ), "%d", loops );
+ if ( nloops[0] == '\0' ) snprintf( nloops, sizeof( nloops ), "%d", loops );
+ if ( mloops[0] == '\0' ) snprintf( mloops, sizeof( mloops ), "%d", loops );
+ if ( bloops[0] == '\0' ) snprintf( bloops, sizeof( bloops ), "%d", 20 * loops );
+
+ /*
+ * generate the search clients
+ */
+
+ sanum = 0;
+ snprintf( scmd, sizeof scmd, "%s" LDAP_DIRSEP SEARCHCMD,
+ progdir );
+ sargs[sanum++] = scmd;
+ if ( uri ) {
+ sargs[sanum++] = "-H";
+ sargs[sanum++] = uri;
+ } else {
+ sargs[sanum++] = "-h";
+ sargs[sanum++] = host;
+ sargs[sanum++] = "-p";
+ sargs[sanum++] = port;
+ }
+ sargs[sanum++] = "-D";
+ sargs[sanum++] = manager;
+ sargs[sanum++] = "-w";
+ sargs[sanum++] = passwd;
+ sargs[sanum++] = "-l";
+ sargs[sanum++] = sloops;
+ sargs[sanum++] = "-L";
+ sargs[sanum++] = outerloops;
+ sargs[sanum++] = "-r";
+ sargs[sanum++] = retries;
+ sargs[sanum++] = "-t";
+ sargs[sanum++] = delay;
+ if ( friendly ) {
+ sargs[sanum++] = friendlyOpt;
+ }
+ if ( chaserefs ) {
+ sargs[sanum++] = "-C";
+ }
+ if ( noattrs ) {
+ sargs[sanum++] = "-A";
+ }
+ if ( nobind ) {
+ sargs[sanum++] = "-N";
+ }
+ if ( ignore ) {
+ sargs[sanum++] = "-i";
+ sargs[sanum++] = ignore;
+ }
+ if ( swamp ) {
+ sargs[sanum++] = swampopt;
+ }
+ sargs[sanum++] = "-b";
+ sargs[sanum++] = NULL; /* will hold the search base */
+ sargs[sanum++] = "-s";
+ sargs[sanum++] = NULL; /* will hold the search scope */
+ sargs[sanum++] = "-f";
+ sargs[sanum++] = NULL; /* will hold the search request */
+
+ sargs[sanum++] = NULL;
+ sargs[sanum++] = NULL; /* might hold the "attr" request */
+ sextra_args += 2;
+
+ sargs[sanum] = NULL;
+
+ /*
+ * generate the read clients
+ */
+
+ ranum = 0;
+ snprintf( rcmd, sizeof rcmd, "%s" LDAP_DIRSEP READCMD,
+ progdir );
+ rargs[ranum++] = rcmd;
+ if ( uri ) {
+ rargs[ranum++] = "-H";
+ rargs[ranum++] = uri;
+ } else {
+ rargs[ranum++] = "-h";
+ rargs[ranum++] = host;
+ rargs[ranum++] = "-p";
+ rargs[ranum++] = port;
+ }
+ rargs[ranum++] = "-D";
+ rargs[ranum++] = manager;
+ rargs[ranum++] = "-w";
+ rargs[ranum++] = passwd;
+ rargs[ranum++] = "-l";
+ rargs[ranum++] = rloops;
+ rargs[ranum++] = "-L";
+ rargs[ranum++] = outerloops;
+ rargs[ranum++] = "-r";
+ rargs[ranum++] = retries;
+ rargs[ranum++] = "-t";
+ rargs[ranum++] = delay;
+ if ( friendly ) {
+ rargs[ranum++] = friendlyOpt;
+ }
+ if ( chaserefs ) {
+ rargs[ranum++] = "-C";
+ }
+ if ( noattrs ) {
+ rargs[ranum++] = "-A";
+ }
+ if ( ignore ) {
+ rargs[ranum++] = "-i";
+ rargs[ranum++] = ignore;
+ }
+ if ( swamp ) {
+ rargs[ranum++] = swampopt;
+ }
+ rargs[ranum++] = "-e";
+ rargs[ranum++] = NULL; /* will hold the read entry */
+
+ rargs[ranum++] = NULL;
+ rargs[ranum++] = NULL; /* might hold the filter arg */
+ rextra_args += 2;
+
+ rargs[ranum] = NULL;
+
+ /*
+ * generate the modrdn clients
+ */
+
+ nanum = 0;
+ snprintf( ncmd, sizeof ncmd, "%s" LDAP_DIRSEP MODRDNCMD,
+ progdir );
+ nargs[nanum++] = ncmd;
+ if ( uri ) {
+ nargs[nanum++] = "-H";
+ nargs[nanum++] = uri;
+ } else {
+ nargs[nanum++] = "-h";
+ nargs[nanum++] = host;
+ nargs[nanum++] = "-p";
+ nargs[nanum++] = port;
+ }
+ nargs[nanum++] = "-D";
+ nargs[nanum++] = manager;
+ nargs[nanum++] = "-w";
+ nargs[nanum++] = passwd;
+ nargs[nanum++] = "-l";
+ nargs[nanum++] = nloops;
+ nargs[nanum++] = "-L";
+ nargs[nanum++] = outerloops;
+ nargs[nanum++] = "-r";
+ nargs[nanum++] = retries;
+ nargs[nanum++] = "-t";
+ nargs[nanum++] = delay;
+ if ( friendly ) {
+ nargs[nanum++] = friendlyOpt;
+ }
+ if ( chaserefs ) {
+ nargs[nanum++] = "-C";
+ }
+ if ( ignore ) {
+ nargs[nanum++] = "-i";
+ nargs[nanum++] = ignore;
+ }
+ nargs[nanum++] = "-e";
+ nargs[nanum++] = NULL; /* will hold the modrdn entry */
+ nargs[nanum] = NULL;
+
+ /*
+ * generate the modify clients
+ */
+
+ manum = 0;
+ snprintf( mcmd, sizeof mcmd, "%s" LDAP_DIRSEP MODIFYCMD,
+ progdir );
+ margs[manum++] = mcmd;
+ if ( uri ) {
+ margs[manum++] = "-H";
+ margs[manum++] = uri;
+ } else {
+ margs[manum++] = "-h";
+ margs[manum++] = host;
+ margs[manum++] = "-p";
+ margs[manum++] = port;
+ }
+ margs[manum++] = "-D";
+ margs[manum++] = manager;
+ margs[manum++] = "-w";
+ margs[manum++] = passwd;
+ margs[manum++] = "-l";
+ margs[manum++] = mloops;
+ margs[manum++] = "-L";
+ margs[manum++] = outerloops;
+ margs[manum++] = "-r";
+ margs[manum++] = retries;
+ margs[manum++] = "-t";
+ margs[manum++] = delay;
+ if ( friendly ) {
+ margs[manum++] = friendlyOpt;
+ }
+ if ( chaserefs ) {
+ margs[manum++] = "-C";
+ }
+ if ( ignore ) {
+ margs[manum++] = "-i";
+ margs[manum++] = ignore;
+ }
+ margs[manum++] = "-e";
+ margs[manum++] = NULL; /* will hold the modify entry */
+ margs[manum++] = "-a";;
+ margs[manum++] = NULL; /* will hold the ava */
+ margs[manum] = NULL;
+
+ /*
+ * generate the add/delete clients
+ */
+
+ aanum = 0;
+ snprintf( acmd, sizeof acmd, "%s" LDAP_DIRSEP ADDCMD,
+ progdir );
+ aargs[aanum++] = acmd;
+ if ( uri ) {
+ aargs[aanum++] = "-H";
+ aargs[aanum++] = uri;
+ } else {
+ aargs[aanum++] = "-h";
+ aargs[aanum++] = host;
+ aargs[aanum++] = "-p";
+ aargs[aanum++] = port;
+ }
+ aargs[aanum++] = "-D";
+ aargs[aanum++] = manager;
+ aargs[aanum++] = "-w";
+ aargs[aanum++] = passwd;
+ aargs[aanum++] = "-l";
+ aargs[aanum++] = aloops;
+ aargs[aanum++] = "-L";
+ aargs[aanum++] = outerloops;
+ aargs[aanum++] = "-r";
+ aargs[aanum++] = retries;
+ aargs[aanum++] = "-t";
+ aargs[aanum++] = delay;
+ if ( friendly ) {
+ aargs[aanum++] = friendlyOpt;
+ }
+ if ( chaserefs ) {
+ aargs[aanum++] = "-C";
+ }
+ if ( ignore ) {
+ aargs[aanum++] = "-i";
+ aargs[aanum++] = ignore;
+ }
+ aargs[aanum++] = "-f";
+ aargs[aanum++] = NULL; /* will hold the add data file */
+ aargs[aanum] = NULL;
+
+ /*
+ * generate the bind clients
+ */
+
+ banum = 0;
+ snprintf( bcmd, sizeof bcmd, "%s" LDAP_DIRSEP BINDCMD,
+ progdir );
+ bargs[banum++] = bcmd;
+ if ( !noinit ) {
+ bargs[banum++] = "-I"; /* init on each bind */
+ }
+ if ( uri ) {
+ bargs[banum++] = "-H";
+ bargs[banum++] = uri;
+ } else {
+ bargs[banum++] = "-h";
+ bargs[banum++] = host;
+ bargs[banum++] = "-p";
+ bargs[banum++] = port;
+ }
+ bargs[banum++] = "-l";
+ bargs[banum++] = bloops;
+ bargs[banum++] = "-L";
+ bargs[banum++] = outerloops;
+#if 0
+ bargs[banum++] = "-r";
+ bargs[banum++] = retries;
+ bargs[banum++] = "-t";
+ bargs[banum++] = delay;
+#endif
+ if ( friendly ) {
+ bargs[banum++] = friendlyOpt;
+ }
+ if ( chaserefs ) {
+ bargs[banum++] = "-C";
+ }
+ if ( ignore ) {
+ bargs[banum++] = "-i";
+ bargs[banum++] = ignore;
+ }
+ if ( nextra ) {
+ bargs[banum++] = "-B";
+ bargs_extra = &bargs[banum++];
+ }
+ bargs[banum++] = "-D";
+ bargs[banum++] = NULL;
+ bargs[banum++] = "-w";
+ bargs[banum++] = NULL;
+ bargs[banum] = NULL;
+
+#define DOREQ(n,j) ((n) && ((maxkids > (n)) ? ((j) < maxkids ) : ((j) < (n))))
+
+ for ( j = 0; j < MAXREQS; j++ ) {
+ /* search */
+ if ( DOREQ( snum, j ) ) {
+ int jj = j % snum;
+ int x = sanum - sextra_args;
+
+ /* base */
+ if ( sbase[jj] != NULL ) {
+ sargs[sanum - 7] = sbase[jj];
+
+ } else {
+ sargs[sanum - 7] = slud[jj]->lud_dn;
+ }
+
+ /* scope */
+ if ( slud[jj] != NULL ) {
+ sargs[sanum - 5] = (char *)ldap_pvt_scope2str( slud[jj]->lud_scope );
+
+ } else {
+ sargs[sanum - 5] = "sub";
+ }
+
+ /* filter */
+ if ( sreqs[jj] != NULL ) {
+ sargs[sanum - 3] = sreqs[jj];
+
+ } else if ( slud[jj]->lud_filter != NULL ) {
+ sargs[sanum - 3] = slud[jj]->lud_filter;
+
+ } else {
+ sargs[sanum - 3] = "(objectClass=*)";
+ }
+
+ /* extras */
+ sargs[x] = NULL;
+
+ /* attr */
+ if ( sattrs[jj] != NULL ) {
+ sargs[x++] = "-a";
+ sargs[x++] = sattrs[jj];
+ }
+
+ /* attrs */
+ if ( slud[jj] != NULL && slud[jj]->lud_attrs != NULL ) {
+ int i;
+
+ for ( i = 0; slud[jj]->lud_attrs[ i ] != NULL && x + i < MAXARGS - 1; i++ ) {
+ sargs[x + i] = slud[jj]->lud_attrs[ i ];
+ }
+ sargs[x + i] = NULL;
+ }
+
+ fork_child( scmd, sargs );
+ }
+
+ /* read */
+ if ( DOREQ( rnum, j ) ) {
+ int jj = j % rnum;
+ int x = ranum - rextra_args;
+
+ rargs[ranum - 3] = rreqs[jj];
+ if ( rflts[jj] != NULL ) {
+ rargs[x++] = "-f";
+ rargs[x++] = rflts[jj];
+ }
+ rargs[x] = NULL;
+ fork_child( rcmd, rargs );
+ }
+
+ /* rename */
+ if ( j < nnum ) {
+ nargs[nanum - 1] = nreqs[j];
+ fork_child( ncmd, nargs );
+ }
+
+ /* modify */
+ if ( j < mnum ) {
+ margs[manum - 3] = mdn[j];
+ margs[manum - 1] = mreqs[j];
+ fork_child( mcmd, margs );
+ }
+
+ /* add/delete */
+ if ( j < anum ) {
+ aargs[aanum - 1] = afiles[j];
+ fork_child( acmd, aargs );
+ }
+
+ /* bind */
+ if ( DOREQ( bnum, j ) ) {
+ int jj = j % bnum;
+
+ if ( nextra ) {
+ int n = ((double)nextra)*rand()/(RAND_MAX + 1.0);
+ extra_t *e;
+
+ for ( e = extra; n-- > 0; e = e->next )
+ ;
+ *bargs_extra = e->action;
+ }
+
+ if ( battrs[jj] != NULL ) {
+ bargs[banum - 3] = manager ? manager : "";
+ bargs[banum - 1] = passwd ? passwd : "";
+
+ bargs[banum + 0] = "-b";
+ bargs[banum + 1] = breqs[jj];
+ bargs[banum + 2] = "-f";
+ bargs[banum + 3] = bcreds[jj];
+ bargs[banum + 4] = "-a";
+ bargs[banum + 5] = battrs[jj];
+ bargs[banum + 6] = NULL;
+
+ } else {
+ bargs[banum - 3] = breqs[jj];
+ bargs[banum - 1] = bcreds[jj];
+ bargs[banum] = NULL;
+ }
+
+ fork_child( bcmd, bargs );
+ bargs[banum] = NULL;
+ }
+ }
+
+ wait4kids( -1 );
+
+ exit( EXIT_SUCCESS );
+}
+
+static char *
+get_file_name( char *dirname, char *filename )
+{
+ char buf[MAXPATHLEN];
+
+ snprintf( buf, sizeof buf, "%s" LDAP_DIRSEP "%s",
+ dirname, filename );
+ return( strdup( buf ));
+}
+
+
+static int
+get_search_filters( char *filename, char *filters[], char *attrs[], char *bases[], LDAPURLDesc *luds[] )
+{
+ FILE *fp;
+ int filter = 0;
+
+ if ( (fp = fopen( filename, "r" )) != NULL ) {
+ char line[BUFSIZ];
+
+ while (( filter < MAXREQS ) && ( fgets( line, BUFSIZ, fp ))) {
+ char *nl;
+ int got_URL = 0;
+
+ if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
+ *nl = '\0';
+
+ if ( luds ) luds[filter] = NULL;
+
+ if ( luds && strncmp( line, "ldap:///", STRLENOF( "ldap:///" ) ) == 0 ) {
+ LDAPURLDesc *lud;
+
+ got_URL = 1;
+ bases[filter] = NULL;
+ if ( ldap_url_parse( line, &lud ) != LDAP_URL_SUCCESS ) {
+ filter = -filter - 1;
+ break;
+ }
+
+ if ( lud->lud_dn == NULL || lud->lud_exts != NULL ) {
+ filter = -filter - 1;
+ ldap_free_urldesc( lud );
+ break;
+ }
+
+ luds[filter] = lud;
+
+ } else {
+ bases[filter] = ArgDup( line );
+ }
+ if ( fgets( line, BUFSIZ, fp ) == NULL )
+ *line = '\0';
+ if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
+ *nl = '\0';
+
+ filters[filter] = ArgDup( line );
+ if ( attrs ) {
+ if ( filters[filter][0] == '+') {
+ char *sep = strchr( filters[filter], ':' );
+
+ attrs[ filter ] = &filters[ filter ][ 1 ];
+ if ( sep != NULL ) {
+ sep[ 0 ] = '\0';
+ /* NOTE: don't free this! */
+ filters[ filter ] = &sep[ 1 ];
+ }
+
+ } else {
+ attrs[ filter ] = NULL;
+ }
+ }
+ filter++;
+
+ }
+ fclose( fp );
+ }
+
+ return filter;
+}
+
+
+static int
+get_read_entries( char *filename, char *entries[], char *filters[] )
+{
+ FILE *fp;
+ int entry = 0;
+
+ if ( (fp = fopen( filename, "r" )) != NULL ) {
+ char line[BUFSIZ];
+
+ while (( entry < MAXREQS ) && ( fgets( line, BUFSIZ, fp ))) {
+ char *nl;
+
+ if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
+ *nl = '\0';
+ if ( filters != NULL && line[0] == '+' ) {
+ LDAPURLDesc *lud;
+
+ if ( ldap_url_parse( &line[1], &lud ) != LDAP_URL_SUCCESS ) {
+ entry = -entry - 1;
+ break;
+ }
+
+ if ( lud->lud_dn == NULL || lud->lud_dn[ 0 ] == '\0' ) {
+ ldap_free_urldesc( lud );
+ entry = -entry - 1;
+ break;
+ }
+
+ entries[entry] = ArgDup( lud->lud_dn );
+
+ if ( lud->lud_filter ) {
+ filters[entry] = ArgDup( lud->lud_filter );
+
+ } else {
+ filters[entry] = ArgDup( "(objectClass=*)" );
+ }
+ ldap_free_urldesc( lud );
+
+ } else {
+ if ( filters != NULL )
+ filters[entry] = NULL;
+
+ entries[entry] = ArgDup( line );
+ }
+
+ entry++;
+
+ }
+ fclose( fp );
+ }
+
+ return( entry );
+}
+
+#ifndef HAVE_WINSOCK
+static void
+fork_child( char *prog, char **args )
+{
+ /* note: obscures global pid var; intended */
+ pid_t pid;
+
+ wait4kids( maxkids );
+
+ switch ( pid = fork() ) {
+ case 0: /* child */
+#ifdef HAVE_EBCDIC
+ /* The __LIBASCII execvp only handles ASCII "prog",
+ * we still need to translate the arg vec ourselves.
+ */
+ { char *arg2[MAXREQS];
+ int i;
+
+ for (i=0; args[i]; i++) {
+ arg2[i] = ArgDup(args[i]);
+ __atoe(arg2[i]);
+ }
+ arg2[i] = NULL;
+ args = arg2; }
+#endif
+ execvp( prog, args );
+ tester_perror( "execvp", NULL );
+ { int i;
+ for (i=0; args[i]; i++);
+ fprintf(stderr,"%d args\n", i);
+ for (i=0; args[i]; i++)
+ fprintf(stderr,"%d %s\n", i, args[i]);
+ }
+
+ exit( EXIT_FAILURE );
+ break;
+
+ case -1: /* trouble */
+ tester_perror( "fork", NULL );
+ break;
+
+ default: /* parent */
+ nkids++;
+ break;
+ }
+}
+
+static void
+wait4kids( int nkidval )
+{
+ int status;
+
+ while ( nkids >= nkidval ) {
+ pid_t pid = wait( &status );
+
+ if ( WIFSTOPPED(status) ) {
+ fprintf( stderr,
+ "stopping: child PID=%ld stopped with signal %d\n",
+ (long) pid, (int) WSTOPSIG(status) );
+
+ } else if ( WIFSIGNALED(status) ) {
+ fprintf( stderr,
+ "stopping: child PID=%ld terminated with signal %d%s\n",
+ (long) pid, (int) WTERMSIG(status),
+#ifdef WCOREDUMP
+ WCOREDUMP(status) ? ", core dumped" : ""
+#else
+ ""
+#endif
+ );
+ exit( WEXITSTATUS(status) );
+
+ } else if ( WEXITSTATUS(status) != 0 ) {
+ fprintf( stderr,
+ "stopping: child PID=%ld exited with status %d\n",
+ (long) pid, (int) WEXITSTATUS(status) );
+ exit( WEXITSTATUS(status) );
+
+ } else {
+ nkids--;
+ }
+ }
+}
+#else
+
+static void
+wait4kids( int nkidval )
+{
+ int rc, i;
+
+ while ( nkids >= nkidval ) {
+ rc = WaitForMultipleObjects( nkids, children, FALSE, INFINITE );
+ for ( i=rc - WAIT_OBJECT_0; i<nkids-1; i++)
+ children[i] = children[i+1];
+ nkids--;
+ }
+}
+
+static void
+fork_child( char *prog, char **args )
+{
+ int rc;
+
+ wait4kids( maxkids );
+
+ rc = _spawnvp( _P_NOWAIT, prog, args );
+
+ if ( rc == -1 ) {
+ tester_perror( "_spawnvp", NULL );
+ } else {
+ children[nkids++] = (HANDLE)rc;
+ }
+}
+#endif