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