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