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