1/* $OpenLDAP$ */ 2/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 3 * 4 * Copyright 1999-2011 The OpenLDAP Foundation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted only as authorized by the OpenLDAP 9 * Public License. 10 * 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>. 14 */ 15/* ACKNOWLEDGEMENTS: 16 * This work was initially developed by Kurt Spanier for inclusion 17 * in OpenLDAP Software. 18 */ 19 20#include "portable.h" 21 22#include <stdio.h> 23 24#include "ac/stdlib.h" 25 26#include "ac/ctype.h" 27#include "ac/param.h" 28#include "ac/socket.h" 29#include "ac/string.h" 30#include "ac/unistd.h" 31#include "ac/wait.h" 32 33#include "ldap.h" 34#include "lutil.h" 35#include "ldap_pvt.h" 36 37#include "slapd-common.h" 38 39#define LOOPS 100 40#define RETRIES 0 41 42static void 43do_search( char *uri, char *manager, struct berval *passwd, 44 char *sbase, int scope, char *filter, LDAP **ldp, 45 char **attrs, int noattrs, int nobind, 46 int innerloop, int maxretries, int delay, int force, int chaserefs ); 47 48static void 49do_random( char *uri, char *manager, struct berval *passwd, 50 char *sbase, int scope, char *filter, char *attr, 51 char **attrs, int noattrs, int nobind, 52 int innerloop, int maxretries, int delay, int force, int chaserefs ); 53 54static void 55usage( char *name, char o ) 56{ 57 if ( o != '\0' ) { 58 fprintf( stderr, "unknown/incorrect option \"%c\"\n", o ); 59 } 60 61 fprintf( stderr, 62 "usage: %s " 63 "-H <uri> | ([-h <host>] -p <port>) " 64 "-D <manager> " 65 "-w <passwd> " 66 "-b <searchbase> " 67 "-s <scope> " 68 "-f <searchfilter> " 69 "[-a <attr>] " 70 "[-A] " 71 "[-C] " 72 "[-F] " 73 "[-N] " 74 "[-S[S[S]]] " 75 "[-i <ignore>] " 76 "[-l <loops>] " 77 "[-L <outerloops>] " 78 "[-r <maxretries>] " 79 "[-t <delay>] " 80 "[<attrs>] " 81 "\n", 82 name ); 83 exit( EXIT_FAILURE ); 84} 85 86/* -S: just send requests without reading responses 87 * -SS: send all requests asynchronous and immediately start reading responses 88 * -SSS: send all requests asynchronous; then read responses 89 */ 90static int swamp; 91 92int 93main( int argc, char **argv ) 94{ 95 int i; 96 char *uri = NULL; 97 char *host = "localhost"; 98 int port = -1; 99 char *manager = NULL; 100 struct berval passwd = { 0, NULL }; 101 char *sbase = NULL; 102 int scope = LDAP_SCOPE_SUBTREE; 103 char *filter = NULL; 104 char *attr = NULL; 105 char *srchattrs[] = { "cn", "sn", NULL }; 106 char **attrs = srchattrs; 107 int loops = LOOPS; 108 int outerloops = 1; 109 int retries = RETRIES; 110 int delay = 0; 111 int force = 0; 112 int chaserefs = 0; 113 int noattrs = 0; 114 int nobind = 0; 115 116 tester_init( "slapd-search", TESTER_SEARCH ); 117 118 /* by default, tolerate referrals and no such object */ 119 tester_ignore_str2errlist( "REFERRAL,NO_SUCH_OBJECT" ); 120 121 while ( ( i = getopt( argc, argv, "Aa:b:CD:f:FH:h:i:l:L:Np:r:Ss:t:T:w:" ) ) != EOF ) 122 { 123 switch ( i ) { 124 case 'A': 125 noattrs++; 126 break; 127 128 case 'C': 129 chaserefs++; 130 break; 131 132 case 'H': /* the server uri */ 133 uri = strdup( optarg ); 134 break; 135 136 case 'h': /* the servers host */ 137 host = strdup( optarg ); 138 break; 139 140 case 'i': 141 tester_ignore_str2errlist( optarg ); 142 break; 143 144 case 'N': 145 nobind++; 146 break; 147 148 case 'p': /* the servers port */ 149 if ( lutil_atoi( &port, optarg ) != 0 ) { 150 usage( argv[0], i ); 151 } 152 break; 153 154 case 'D': /* the servers manager */ 155 manager = strdup( optarg ); 156 break; 157 158 case 'w': /* the server managers password */ 159 passwd.bv_val = strdup( optarg ); 160 passwd.bv_len = strlen( optarg ); 161 memset( optarg, '*', passwd.bv_len ); 162 break; 163 164 case 'a': 165 attr = strdup( optarg ); 166 break; 167 168 case 'b': /* file with search base */ 169 sbase = strdup( optarg ); 170 break; 171 172 case 'f': /* the search request */ 173 filter = strdup( optarg ); 174 break; 175 176 case 'F': 177 force++; 178 break; 179 180 case 'l': /* number of loops */ 181 if ( lutil_atoi( &loops, optarg ) != 0 ) { 182 usage( argv[0], i ); 183 } 184 break; 185 186 case 'L': /* number of loops */ 187 if ( lutil_atoi( &outerloops, optarg ) != 0 ) { 188 usage( argv[0], i ); 189 } 190 break; 191 192 case 'r': /* number of retries */ 193 if ( lutil_atoi( &retries, optarg ) != 0 ) { 194 usage( argv[0], i ); 195 } 196 break; 197 198 case 't': /* delay in seconds */ 199 if ( lutil_atoi( &delay, optarg ) != 0 ) { 200 usage( argv[0], i ); 201 } 202 break; 203 204 case 'T': 205 attrs = ldap_str2charray( optarg, "," ); 206 if ( attrs == NULL ) { 207 usage( argv[0], i ); 208 } 209 break; 210 211 case 'S': 212 swamp++; 213 break; 214 215 case 's': 216 scope = ldap_pvt_str2scope( optarg ); 217 if ( scope == -1 ) { 218 usage( argv[0], i ); 219 } 220 break; 221 222 default: 223 usage( argv[0], i ); 224 break; 225 } 226 } 227 228 if (( sbase == NULL ) || ( filter == NULL ) || ( port == -1 && uri == NULL )) 229 usage( argv[0], '\0' ); 230 231 if ( *filter == '\0' ) { 232 233 fprintf( stderr, "%s: invalid EMPTY search filter.\n", 234 argv[0] ); 235 exit( EXIT_FAILURE ); 236 237 } 238 239 if ( argv[optind] != NULL ) { 240 attrs = &argv[optind]; 241 } 242 243 uri = tester_uri( uri, host, port ); 244 245 for ( i = 0; i < outerloops; i++ ) { 246 if ( attr != NULL ) { 247 do_random( uri, manager, &passwd, 248 sbase, scope, filter, attr, 249 attrs, noattrs, nobind, 250 loops, retries, delay, force, chaserefs ); 251 252 } else { 253 do_search( uri, manager, &passwd, 254 sbase, scope, filter, NULL, 255 attrs, noattrs, nobind, 256 loops, retries, delay, force, chaserefs ); 257 } 258 } 259 260 exit( EXIT_SUCCESS ); 261} 262 263 264static void 265do_random( char *uri, char *manager, struct berval *passwd, 266 char *sbase, int scope, char *filter, char *attr, 267 char **srchattrs, int noattrs, int nobind, 268 int innerloop, int maxretries, int delay, int force, int chaserefs ) 269{ 270 LDAP *ld = NULL; 271 int i = 0, do_retry = maxretries; 272 char *attrs[ 2 ]; 273 int rc = LDAP_SUCCESS; 274 int version = LDAP_VERSION3; 275 int nvalues = 0; 276 char **values = NULL; 277 LDAPMessage *res = NULL, *e = NULL; 278 279 attrs[ 0 ] = attr; 280 attrs[ 1 ] = NULL; 281 282 ldap_initialize( &ld, uri ); 283 if ( ld == NULL ) { 284 tester_perror( "ldap_initialize", NULL ); 285 exit( EXIT_FAILURE ); 286 } 287 288 (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version ); 289 (void) ldap_set_option( ld, LDAP_OPT_REFERRALS, 290 chaserefs ? LDAP_OPT_ON : LDAP_OPT_OFF ); 291 292 if ( do_retry == maxretries ) { 293 fprintf( stderr, "PID=%ld - Search(%d): base=\"%s\", filter=\"%s\" attr=\"%s\".\n", 294 (long) pid, innerloop, sbase, filter, attr ); 295 } 296 297 if ( nobind == 0 ) { 298 rc = ldap_sasl_bind_s( ld, manager, LDAP_SASL_SIMPLE, passwd, NULL, NULL, NULL ); 299 if ( rc != LDAP_SUCCESS ) { 300 tester_ldap_error( ld, "ldap_sasl_bind_s", NULL ); 301 switch ( rc ) { 302 case LDAP_BUSY: 303 case LDAP_UNAVAILABLE: 304 /* fallthru */ 305 default: 306 break; 307 } 308 exit( EXIT_FAILURE ); 309 } 310 } 311 312 rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE, 313 filter, attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res ); 314 switch ( rc ) { 315 case LDAP_SIZELIMIT_EXCEEDED: 316 case LDAP_TIMELIMIT_EXCEEDED: 317 case LDAP_SUCCESS: 318 if ( ldap_count_entries( ld, res ) == 0 ) { 319 if ( rc ) { 320 tester_ldap_error( ld, "ldap_search_ext_s", NULL ); 321 } 322 break; 323 } 324 325 for ( e = ldap_first_entry( ld, res ); e != NULL; e = ldap_next_entry( ld, e ) ) 326 { 327 struct berval **v = ldap_get_values_len( ld, e, attr ); 328 329 if ( v != NULL ) { 330 int n = ldap_count_values_len( v ); 331 int j; 332 333 values = realloc( values, ( nvalues + n + 1 )*sizeof( char * ) ); 334 for ( j = 0; j < n; j++ ) { 335 values[ nvalues + j ] = strdup( v[ j ]->bv_val ); 336 } 337 values[ nvalues + j ] = NULL; 338 nvalues += n; 339 ldap_value_free_len( v ); 340 } 341 } 342 343 ldap_msgfree( res ); 344 345 if ( !values ) { 346 fprintf( stderr, " PID=%ld - Search base=\"%s\" filter=\"%s\" got %d values.\n", 347 (long) pid, sbase, filter, nvalues ); 348 exit(EXIT_FAILURE); 349 } 350 351 if ( do_retry == maxretries ) { 352 fprintf( stderr, " PID=%ld - Search base=\"%s\" filter=\"%s\" got %d values.\n", 353 (long) pid, sbase, filter, nvalues ); 354 } 355 356 for ( i = 0; i < innerloop; i++ ) { 357 char buf[ BUFSIZ ]; 358#if 0 /* use high-order bits for better randomness (Numerical Recipes in "C") */ 359 int r = rand() % nvalues; 360#endif 361 int r = ((double)nvalues)*rand()/(RAND_MAX + 1.0); 362 363 snprintf( buf, sizeof( buf ), "(%s=%s)", attr, values[ r ] ); 364 365 do_search( uri, manager, passwd, 366 sbase, scope, buf, &ld, 367 srchattrs, noattrs, nobind, 368 1, maxretries, delay, force, chaserefs ); 369 } 370 break; 371 372 default: 373 tester_ldap_error( ld, "ldap_search_ext_s", NULL ); 374 break; 375 } 376 377 fprintf( stderr, " PID=%ld - Search done (%d).\n", (long) pid, rc ); 378 379 if ( ld != NULL ) { 380 ldap_unbind_ext( ld, NULL, NULL ); 381 } 382} 383 384static void 385do_search( char *uri, char *manager, struct berval *passwd, 386 char *sbase, int scope, char *filter, LDAP **ldp, 387 char **attrs, int noattrs, int nobind, 388 int innerloop, int maxretries, int delay, int force, int chaserefs ) 389{ 390 LDAP *ld = ldp ? *ldp : NULL; 391 int i = 0, do_retry = maxretries; 392 int rc = LDAP_SUCCESS; 393 int version = LDAP_VERSION3; 394 char buf[ BUFSIZ ]; 395 int *msgids = NULL, active = 0; 396 397 /* make room for msgid */ 398 if ( swamp > 1 ) { 399 msgids = (int *)calloc( sizeof(int), innerloop ); 400 } 401 402retry:; 403 if ( ld == NULL ) { 404 ldap_initialize( &ld, uri ); 405 if ( ld == NULL ) { 406 tester_perror( "ldap_initialize", NULL ); 407 exit( EXIT_FAILURE ); 408 } 409 410 (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version ); 411 (void) ldap_set_option( ld, LDAP_OPT_REFERRALS, 412 chaserefs ? LDAP_OPT_ON : LDAP_OPT_OFF ); 413 414 if ( do_retry == maxretries ) { 415 fprintf( stderr, 416 "PID=%ld - Search(%d): " 417 "base=\"%s\" scope=%s filter=\"%s\" " 418 "attrs=%s%s.\n", 419 (long) pid, innerloop, 420 sbase, ldap_pvt_scope2str( scope ), filter, 421 attrs[0], attrs[1] ? " (more...)" : "" ); 422 } 423 424 if ( nobind == 0 ) { 425 rc = ldap_sasl_bind_s( ld, manager, LDAP_SASL_SIMPLE, passwd, NULL, NULL, NULL ); 426 if ( rc != LDAP_SUCCESS ) { 427 snprintf( buf, sizeof( buf ), 428 "bindDN=\"%s\"", manager ); 429 tester_ldap_error( ld, "ldap_sasl_bind_s", buf ); 430 switch ( rc ) { 431 case LDAP_BUSY: 432 case LDAP_UNAVAILABLE: 433 if ( do_retry > 0 ) { 434 ldap_unbind_ext( ld, NULL, NULL ); 435 ld = NULL; 436 do_retry--; 437 if ( delay != 0 ) { 438 sleep( delay ); 439 } 440 goto retry; 441 } 442 /* fallthru */ 443 default: 444 break; 445 } 446 exit( EXIT_FAILURE ); 447 } 448 } 449 } 450 451 if ( swamp > 1 ) { 452 do { 453 LDAPMessage *res = NULL; 454 int j, msgid; 455 456 if ( i < innerloop ) { 457 rc = ldap_search_ext( ld, sbase, scope, 458 filter, NULL, noattrs, NULL, NULL, 459 NULL, LDAP_NO_LIMIT, &msgids[i] ); 460 461 active++; 462#if 0 463 fprintf( stderr, 464 ">>> PID=%ld - Search maxloop=%d cnt=%d active=%d msgid=%d: " 465 "base=\"%s\" scope=%s filter=\"%s\"\n", 466 (long) pid, innerloop, i, active, msgids[i], 467 sbase, ldap_pvt_scope2str( scope ), filter ); 468#endif 469 i++; 470 471 if ( rc ) { 472 int first = tester_ignore_err( rc ); 473 /* if ignore.. */ 474 if ( first ) { 475 /* only log if first occurrence */ 476 if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) { 477 tester_ldap_error( ld, "ldap_search_ext", NULL ); 478 } 479 continue; 480 } 481 482 /* busy needs special handling */ 483 snprintf( buf, sizeof( buf ), 484 "base=\"%s\" filter=\"%s\"\n", 485 sbase, filter ); 486 tester_ldap_error( ld, "ldap_search_ext", buf ); 487 if ( rc == LDAP_BUSY && do_retry > 0 ) { 488 ldap_unbind_ext( ld, NULL, NULL ); 489 ld = NULL; 490 do_retry--; 491 goto retry; 492 } 493 break; 494 } 495 496 if ( swamp > 2 ) { 497 continue; 498 } 499 } 500 501 rc = ldap_result( ld, LDAP_RES_ANY, 0, NULL, &res ); 502 switch ( rc ) { 503 case -1: 504 /* gone really bad */ 505 goto cleanup; 506 507 case 0: 508 /* timeout (impossible) */ 509 break; 510 511 case LDAP_RES_SEARCH_ENTRY: 512 case LDAP_RES_SEARCH_REFERENCE: 513 /* ignore */ 514 break; 515 516 case LDAP_RES_SEARCH_RESULT: 517 /* just remove, no error checking (TODO?) */ 518 msgid = ldap_msgid( res ); 519 ldap_parse_result( ld, res, &rc, NULL, NULL, NULL, NULL, 1 ); 520 res = NULL; 521 522 /* linear search, bah */ 523 for ( j = 0; j < i; j++ ) { 524 if ( msgids[ j ] == msgid ) { 525 msgids[ j ] = -1; 526 active--; 527#if 0 528 fprintf( stderr, 529 "<<< PID=%ld - SearchDone maxloop=%d cnt=%d active=%d msgid=%d: " 530 "base=\"%s\" scope=%s filter=\"%s\"\n", 531 (long) pid, innerloop, j, active, msgid, 532 sbase, ldap_pvt_scope2str( scope ), filter ); 533#endif 534 break; 535 } 536 } 537 break; 538 539 default: 540 /* other messages unexpected */ 541 fprintf( stderr, 542 "### PID=%ld - Search(%d): " 543 "base=\"%s\" scope=%s filter=\"%s\" " 544 "attrs=%s%s. unexpected response tag=%d\n", 545 (long) pid, innerloop, 546 sbase, ldap_pvt_scope2str( scope ), filter, 547 attrs[0], attrs[1] ? " (more...)" : "", rc ); 548 break; 549 } 550 551 if ( res != NULL ) { 552 ldap_msgfree( res ); 553 } 554 } while ( i < innerloop || active > 0 ); 555 556 } else { 557 for ( ; i < innerloop; i++ ) { 558 LDAPMessage *res = NULL; 559 560 if (swamp) { 561 int msgid; 562 rc = ldap_search_ext( ld, sbase, scope, 563 filter, NULL, noattrs, NULL, NULL, 564 NULL, LDAP_NO_LIMIT, &msgid ); 565 if ( rc == LDAP_SUCCESS ) continue; 566 else break; 567 } 568 569 rc = ldap_search_ext_s( ld, sbase, scope, 570 filter, attrs, noattrs, NULL, NULL, 571 NULL, LDAP_NO_LIMIT, &res ); 572 if ( res != NULL ) { 573 ldap_msgfree( res ); 574 } 575 576 if ( rc ) { 577 int first = tester_ignore_err( rc ); 578 /* if ignore.. */ 579 if ( first ) { 580 /* only log if first occurrence */ 581 if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) { 582 tester_ldap_error( ld, "ldap_search_ext_s", NULL ); 583 } 584 continue; 585 } 586 587 /* busy needs special handling */ 588 snprintf( buf, sizeof( buf ), 589 "base=\"%s\" filter=\"%s\"\n", 590 sbase, filter ); 591 tester_ldap_error( ld, "ldap_search_ext_s", buf ); 592 if ( rc == LDAP_BUSY && do_retry > 0 ) { 593 ldap_unbind_ext( ld, NULL, NULL ); 594 ld = NULL; 595 do_retry--; 596 goto retry; 597 } 598 break; 599 } 600 } 601 } 602 603cleanup:; 604 if ( msgids != NULL ) { 605 free( msgids ); 606 } 607 608 if ( ldp != NULL ) { 609 *ldp = ld; 610 611 } else { 612 fprintf( stderr, " PID=%ld - Search done (%d).\n", (long) pid, rc ); 613 614 if ( ld != NULL ) { 615 ldap_unbind_ext( ld, NULL, NULL ); 616 } 617 } 618} 619