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