2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1999-2017 The OpenLDAP Foundation.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted only as authorized by the OpenLDAP
11 * A copy of this license is available in file LICENSE in the
12 * top-level directory of the distribution or, alternatively, at
13 * <http://www.OpenLDAP.org/license.html>.
16 * This work was initially developed by Kurt Spanier for inclusion
17 * in OpenLDAP Software.
24 #include "ac/stdlib.h"
28 #include "ac/socket.h"
29 #include "ac/string.h"
30 #include "ac/unistd.h"
37 #include "slapd-common.h"
43 do_search( struct tester_conn_args *config,
44 char *sbase, int scope, char *filter, LDAP **ldp,
45 char **attrs, int noattrs, int nobind,
46 int innerloop, int force );
49 do_random( struct tester_conn_args *config,
50 char *sbase, int scope, char *filter, char *attr,
51 char **attrs, int noattrs, int nobind, int force );
54 usage( char *name, char opt )
57 fprintf( stderr, "unknown/incorrect option \"%c\"\n", opt );
60 fprintf( stderr, "usage: %s " TESTER_COMMON_HELP
75 /* -S: just send requests without reading responses
76 * -SS: send all requests asynchronous and immediately start reading responses
77 * -SSS: send all requests asynchronous; then read responses
82 main( int argc, char **argv )
86 int scope = LDAP_SCOPE_SUBTREE;
89 char *srchattrs[] = { "cn", "sn", NULL };
90 char **attrs = srchattrs;
94 struct tester_conn_args *config;
96 config = tester_init( "slapd-search", TESTER_SEARCH );
98 /* by default, tolerate referrals and no such object */
99 tester_ignore_str2errlist( "REFERRAL,NO_SUCH_OBJECT" );
101 while ( ( i = getopt( argc, argv, TESTER_COMMON_OPTS "Aa:b:f:FNSs:T:" ) ) != EOF )
109 nobind = TESTER_INIT_ONLY;
113 attr = strdup( optarg );
116 case 'b': /* file with search base */
117 sbase = strdup( optarg );
120 case 'f': /* the search request */
121 filter = strdup( optarg );
129 attrs = ldap_str2charray( optarg, "," );
130 if ( attrs == NULL ) {
140 scope = ldap_pvt_str2scope( optarg );
147 if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) {
155 if (( sbase == NULL ) || ( filter == NULL ))
158 if ( *filter == '\0' ) {
160 fprintf( stderr, "%s: invalid EMPTY search filter.\n",
162 exit( EXIT_FAILURE );
166 if ( argv[optind] != NULL ) {
167 attrs = &argv[optind];
170 tester_config_finish( config );
172 for ( i = 0; i < config->outerloops; i++ ) {
173 if ( attr != NULL ) {
175 sbase, scope, filter, attr,
176 attrs, noattrs, nobind, force );
179 do_search( config, sbase, scope, filter,
180 NULL, attrs, noattrs, config->loops,
185 exit( EXIT_SUCCESS );
190 do_random( struct tester_conn_args *config,
191 char *sbase, int scope, char *filter, char *attr,
192 char **srchattrs, int noattrs, int nobind, int force )
195 int i = 0, do_retry = config->retries;
197 int rc = LDAP_SUCCESS;
199 char **values = NULL;
200 LDAPMessage *res = NULL, *e = NULL;
205 tester_init_ld( &ld, config, nobind );
207 rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE,
208 filter, attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res );
210 case LDAP_SIZELIMIT_EXCEEDED:
211 case LDAP_TIMELIMIT_EXCEEDED:
213 if ( ldap_count_entries( ld, res ) == 0 ) {
215 tester_ldap_error( ld, "ldap_search_ext_s", NULL );
220 for ( e = ldap_first_entry( ld, res ); e != NULL; e = ldap_next_entry( ld, e ) )
222 struct berval **v = ldap_get_values_len( ld, e, attr );
225 int n = ldap_count_values_len( v );
228 values = realloc( values, ( nvalues + n + 1 )*sizeof( char * ) );
229 for ( j = 0; j < n; j++ ) {
230 values[ nvalues + j ] = strdup( v[ j ]->bv_val );
232 values[ nvalues + j ] = NULL;
234 ldap_value_free_len( v );
241 fprintf( stderr, " PID=%ld - Search base=\"%s\" filter=\"%s\" got %d values.\n",
242 (long) pid, sbase, filter, nvalues );
246 if ( do_retry == config->retries ) {
247 fprintf( stderr, " PID=%ld - Search base=\"%s\" filter=\"%s\" got %d values.\n",
248 (long) pid, sbase, filter, nvalues );
251 for ( i = 0; i < config->loops; i++ ) {
253 #if 0 /* use high-order bits for better randomness (Numerical Recipes in "C") */
254 int r = rand() % nvalues;
256 int r = ((double)nvalues)*rand()/(RAND_MAX + 1.0);
258 snprintf( buf, sizeof( buf ), "(%s=%s)", attr, values[ r ] );
261 sbase, scope, buf, &ld,
262 srchattrs, noattrs, nobind,
268 tester_ldap_error( ld, "ldap_search_ext_s", NULL );
272 fprintf( stderr, " PID=%ld - Search done (%d).\n", (long) pid, rc );
275 for ( i = 0; i < nvalues; i++ ) {
282 ldap_unbind_ext( ld, NULL, NULL );
287 do_search( struct tester_conn_args *config,
288 char *sbase, int scope, char *filter, LDAP **ldp,
289 char **attrs, int noattrs, int nobind,
290 int innerloop, int force )
292 LDAP *ld = ldp ? *ldp : NULL;
293 int i = 0, do_retry = config->retries;
294 int rc = LDAP_SUCCESS;
296 int *msgids = NULL, active = 0;
298 /* make room for msgid */
300 msgids = (int *)calloc( sizeof(int), innerloop );
306 "PID=%ld - Search(%d): "
307 "base=\"%s\" scope=%s filter=\"%s\" "
309 (long) pid, innerloop,
310 sbase, ldap_pvt_scope2str( scope ), filter,
311 attrs[0], attrs[1] ? " (more...)" : "" );
313 tester_init_ld( &ld, config, nobind );
318 LDAPMessage *res = NULL;
321 if ( i < innerloop ) {
322 rc = ldap_search_ext( ld, sbase, scope,
323 filter, NULL, noattrs, NULL, NULL,
324 NULL, LDAP_NO_LIMIT, &msgids[i] );
329 ">>> PID=%ld - Search maxloop=%d cnt=%d active=%d msgid=%d: "
330 "base=\"%s\" scope=%s filter=\"%s\"\n",
331 (long) pid, innerloop, i, active, msgids[i],
332 sbase, ldap_pvt_scope2str( scope ), filter );
337 int first = tester_ignore_err( rc );
340 /* only log if first occurrence */
341 if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
342 tester_ldap_error( ld, "ldap_search_ext", NULL );
347 /* busy needs special handling */
348 snprintf( buf, sizeof( buf ),
349 "base=\"%s\" filter=\"%s\"\n",
351 tester_ldap_error( ld, "ldap_search_ext", buf );
352 if ( rc == LDAP_BUSY && do_retry > 0 ) {
353 ldap_unbind_ext( ld, NULL, NULL );
366 rc = ldap_result( ld, LDAP_RES_ANY, 0, NULL, &res );
369 /* gone really bad */
373 /* timeout (impossible) */
376 case LDAP_RES_SEARCH_ENTRY:
377 case LDAP_RES_SEARCH_REFERENCE:
381 case LDAP_RES_SEARCH_RESULT:
382 /* just remove, no error checking (TODO?) */
383 msgid = ldap_msgid( res );
384 ldap_parse_result( ld, res, &rc, NULL, NULL, NULL, NULL, 1 );
387 /* linear search, bah */
388 for ( j = 0; j < i; j++ ) {
389 if ( msgids[ j ] == msgid ) {
394 "<<< PID=%ld - SearchDone maxloop=%d cnt=%d active=%d msgid=%d: "
395 "base=\"%s\" scope=%s filter=\"%s\"\n",
396 (long) pid, innerloop, j, active, msgid,
397 sbase, ldap_pvt_scope2str( scope ), filter );
405 /* other messages unexpected */
407 "### PID=%ld - Search(%d): "
408 "base=\"%s\" scope=%s filter=\"%s\" "
409 "attrs=%s%s. unexpected response tag=%d\n",
410 (long) pid, innerloop,
411 sbase, ldap_pvt_scope2str( scope ), filter,
412 attrs[0], attrs[1] ? " (more...)" : "", rc );
419 } while ( i < innerloop || active > 0 );
422 for ( ; i < innerloop; i++ ) {
423 LDAPMessage *res = NULL;
427 rc = ldap_search_ext( ld, sbase, scope,
428 filter, NULL, noattrs, NULL, NULL,
429 NULL, LDAP_NO_LIMIT, &msgid );
430 if ( rc == LDAP_SUCCESS ) continue;
434 rc = ldap_search_ext_s( ld, sbase, scope,
435 filter, attrs, noattrs, NULL, NULL,
436 NULL, LDAP_NO_LIMIT, &res );
442 int first = tester_ignore_err( rc );
445 /* only log if first occurrence */
446 if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
447 tester_ldap_error( ld, "ldap_search_ext_s", NULL );
452 /* busy needs special handling */
453 snprintf( buf, sizeof( buf ),
454 "base=\"%s\" filter=\"%s\"\n",
456 tester_ldap_error( ld, "ldap_search_ext_s", buf );
457 if ( rc == LDAP_BUSY && do_retry > 0 ) {
458 ldap_unbind_ext( ld, NULL, NULL );
469 if ( msgids != NULL ) {
477 fprintf( stderr, " PID=%ld - Search done (%d).\n", (long) pid, rc );
480 ldap_unbind_ext( ld, NULL, NULL );