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