From 7731832751ab9f3c6ddeb66f186d3d7fa1934a6d Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 13:11:40 +0200 Subject: Adding upstream version 2.4.57+dfsg. Signed-off-by: Daniel Baumann --- tests/progs/Makefile.in | 66 +++ tests/progs/ldif-filter.c | 256 ++++++++++ tests/progs/slapd-addel.c | 435 ++++++++++++++++ tests/progs/slapd-bind.c | 609 ++++++++++++++++++++++ tests/progs/slapd-common.c | 300 +++++++++++ tests/progs/slapd-common.h | 44 ++ tests/progs/slapd-modify.c | 318 ++++++++++++ tests/progs/slapd-modrdn.c | 310 ++++++++++++ tests/progs/slapd-mtread.c | 837 +++++++++++++++++++++++++++++++ tests/progs/slapd-read.c | 568 +++++++++++++++++++++ tests/progs/slapd-search.c | 618 +++++++++++++++++++++++ tests/progs/slapd-tester.c | 1197 ++++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 5558 insertions(+) create mode 100644 tests/progs/Makefile.in create mode 100644 tests/progs/ldif-filter.c create mode 100644 tests/progs/slapd-addel.c create mode 100644 tests/progs/slapd-bind.c create mode 100644 tests/progs/slapd-common.c create mode 100644 tests/progs/slapd-common.h create mode 100644 tests/progs/slapd-modify.c create mode 100644 tests/progs/slapd-modrdn.c create mode 100644 tests/progs/slapd-mtread.c create mode 100644 tests/progs/slapd-read.c create mode 100644 tests/progs/slapd-search.c create mode 100644 tests/progs/slapd-tester.c (limited to 'tests/progs') 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 . +## +## 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 +## . + +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 . + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#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 matching '[]=[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\ + 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 . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Kurt Spanier for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include + +#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 | ([-h ] -p ) " + "-D " + "-w " + "-f " + "[-i ] " + "[-l ] " + "[-L ] " + "[-r ] " + "[-t ] " + "[-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 . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Howard Chu for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include + +#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 [-p port]] " + "[-D [-w ]] " + "[-b [-f ] [-a pwattr]] " + "[-l ] " + "[-L ] " + "[-B [,...]] " + "[-F] " + "[-C] " + "[-I] " + "[-i ] " + "[-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 . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Howard Chu for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include + +#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 : to ignore only when */ + (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 . + * + * 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 + * . + */ +/* 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 . + * + * 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 + * . + */ + +#include "portable.h" + +#include + +#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 | ([-h ] -p ) " + "-D " + "-w " + "-e " + "[-i ] " + "[-l ] " + "[-L ] " + "[-r ] " + "[-t ] " + "[-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 . + * + * 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 + * . + */ +/* 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 + +#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 | ([-h ] -p ) " + "-D " + "-w " + "-e " + "[-i ] " + "[-l ] " + "[-L ] " + "[-r ] " + "[-t ] " + "[-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 . + * + * 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 + * . + */ +/* 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 +#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 | ([-h ] -p ) " + "-D " + "-w " + "-e " + "[-A] " + "[-C] " + "[-F] " + "[-N] " + "[-v] " + "[-c connections] " + "[-f filter] " + "[-i ] " + "[-l ] " + "[-L ] " + "[-m threads] " + "[-M threads] " + "[-r ] " + "[-t ] " + "[-T ] " + "[] " + "\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 . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Kurt Spanier for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include + +#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 | ([-h ] -p ) " + "-D " + "-w " + "-e " + "[-A] " + "[-C] " + "[-F] " + "[-N] " + "[-f filter] " + "[-i ] " + "[-l ] " + "[-L ] " + "[-r ] " + "[-t ] " + "[-T ] " + "[] " + "\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 . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Kurt Spanier for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include + +#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 | ([-h ] -p ) " + "-D " + "-w " + "-b " + "-s " + "-f " + "[-a ] " + "[-A] " + "[-C] " + "[-F] " + "[-N] " + "[-S[S[S]]] " + "[-i ] " + "[-l ] " + "[-L ] " + "[-r ] " + "[-t ] " + "[] " + "\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 . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Kurt Spanier for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include + +#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 | ([-h ] -p ) " + "-D " + "-w " + "-d " + "[-i ] " + "[-j ] " + "[-l {|=[,...]}] " + "[-L ] " + "-P " + "[-r ] " + "[-t ] " + "[-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