1 /* $NetBSD: slapd-read.c,v 1.3 2021/08/14 16:15:03 christos Exp $ */ 2 3 /* $OpenLDAP$ */ 4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1999-2021 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.3 2021/08/14 16:15:03 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( struct tester_conn_args *config, char *entry, LDAP **ld, 50 char **attrs, int noattrs, int nobind, int maxloop, int force ); 51 52 static void 53 do_random( struct tester_conn_args *config, char *sbase, 54 char *filter, char **attrs, int noattrs, int nobind, int force ); 55 56 static void 57 usage( char *name, int opt ) 58 { 59 if ( opt ) { 60 fprintf( stderr, "%s: unable to handle option \'%c\'\n\n", 61 name, opt ); 62 } 63 64 fprintf( stderr, "usage: %s " TESTER_COMMON_HELP 65 "-e <entry> " 66 "[-A] " 67 "[-F] " 68 "[-N] " 69 "[-S[S[S]]] " 70 "[-f filter] " 71 "[-T <attrs>] " 72 "[<attrs>] " 73 "\n", 74 name ); 75 exit( EXIT_FAILURE ); 76 } 77 78 /* -S: just send requests without reading responses 79 * -SS: send all requests asynchronous and immediately start reading responses 80 * -SSS: send all requests asynchronous; then read responses 81 */ 82 static int swamp; 83 84 int 85 main( int argc, char **argv ) 86 { 87 int i; 88 char *entry = NULL; 89 char *filter = NULL; 90 int force = 0; 91 char *srchattrs[] = { "1.1", NULL }; 92 char **attrs = srchattrs; 93 int noattrs = 0; 94 int nobind = 0; 95 struct tester_conn_args *config; 96 97 config = tester_init( "slapd-read", TESTER_READ ); 98 99 /* by default, tolerate referrals and no such object */ 100 tester_ignore_str2errlist( "REFERRAL,NO_SUCH_OBJECT" ); 101 102 while ( (i = getopt( argc, argv, TESTER_COMMON_OPTS "Ae:Ff:NST:" )) != EOF ) { 103 switch ( i ) { 104 case 'A': 105 noattrs++; 106 break; 107 108 case 'N': 109 nobind = TESTER_INIT_ONLY; 110 break; 111 112 case 'e': /* DN to search for */ 113 entry = optarg; 114 break; 115 116 case 'f': /* the search request */ 117 filter = optarg; 118 break; 119 120 case 'F': 121 force++; 122 break; 123 124 case 'S': 125 swamp++; 126 break; 127 128 case 'T': 129 attrs = ldap_str2charray( optarg, "," ); 130 if ( attrs == NULL ) { 131 usage( argv[0], i ); 132 } 133 break; 134 135 default: 136 if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) { 137 break; 138 } 139 usage( argv[0], i ); 140 break; 141 } 142 } 143 144 if ( entry == NULL ) 145 usage( argv[0], 0 ); 146 147 if ( *entry == '\0' ) { 148 fprintf( stderr, "%s: invalid EMPTY entry DN.\n", 149 argv[0] ); 150 exit( EXIT_FAILURE ); 151 } 152 153 if ( argv[optind] != NULL ) { 154 attrs = &argv[optind]; 155 } 156 157 tester_config_finish( config ); 158 159 for ( i = 0; i < config->outerloops; i++ ) { 160 if ( filter != NULL ) { 161 do_random( config, entry, filter, attrs, 162 noattrs, nobind, force ); 163 164 } else { 165 do_read( config, entry, NULL, attrs, 166 noattrs, nobind, config->loops, force ); 167 } 168 } 169 170 exit( EXIT_SUCCESS ); 171 } 172 173 static void 174 do_random( struct tester_conn_args *config, char *sbase, char *filter, 175 char **srchattrs, int noattrs, int nobind, int force ) 176 { 177 LDAP *ld = NULL; 178 int i = 0, do_retry = config->retries; 179 char *attrs[ 2 ]; 180 int rc = LDAP_SUCCESS; 181 int nvalues = 0; 182 char **values = NULL; 183 LDAPMessage *res = NULL, *e = NULL; 184 185 attrs[ 0 ] = LDAP_NO_ATTRS; 186 attrs[ 1 ] = NULL; 187 188 tester_init_ld( &ld, config, nobind ); 189 190 if ( do_retry == config->retries ) { 191 fprintf( stderr, "PID=%ld - Read(%d): base=\"%s\", filter=\"%s\".\n", 192 (long) pid, config->loops, sbase, filter ); 193 } 194 195 rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE, 196 filter, attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res ); 197 switch ( rc ) { 198 case LDAP_SIZELIMIT_EXCEEDED: 199 case LDAP_TIMELIMIT_EXCEEDED: 200 case LDAP_SUCCESS: 201 nvalues = ldap_count_entries( ld, res ); 202 if ( nvalues == 0 ) { 203 if ( rc ) { 204 tester_ldap_error( ld, "ldap_search_ext_s", NULL ); 205 } 206 break; 207 } 208 209 values = malloc( ( nvalues + 1 ) * sizeof( char * ) ); 210 if ( !values ) { 211 tester_error( "malloc failed" ); 212 exit( EXIT_FAILURE ); 213 } 214 for ( i = 0, e = ldap_first_entry( ld, res ); e != NULL; i++, e = ldap_next_entry( ld, e ) ) 215 { 216 values[ i ] = ldap_get_dn( ld, e ); 217 } 218 values[ i ] = NULL; 219 220 ldap_msgfree( res ); 221 222 if ( do_retry == config->retries ) { 223 fprintf( stderr, " PID=%ld - Read base=\"%s\" filter=\"%s\" got %d values.\n", 224 (long) pid, sbase, filter, nvalues ); 225 } 226 227 for ( i = 0; i < config->loops; i++ ) { 228 #if 0 /* use high-order bits for better randomness (Numerical Recipes in "C") */ 229 int r = rand() % nvalues; 230 #endif 231 int r = ((double)nvalues)*rand()/(RAND_MAX + 1.0); 232 233 do_read( config, values[ r ], &ld, 234 srchattrs, noattrs, nobind, 1, force ); 235 } 236 free( values ); 237 break; 238 239 default: 240 tester_ldap_error( ld, "ldap_search_ext_s", NULL ); 241 break; 242 } 243 244 fprintf( stderr, " PID=%ld - Read done (%d).\n", (long) pid, rc ); 245 246 if ( ld != NULL ) { 247 ldap_unbind_ext( ld, NULL, NULL ); 248 } 249 } 250 251 static void 252 do_read( struct tester_conn_args *config, char *entry, LDAP **ldp, 253 char **attrs, int noattrs, int nobind, int maxloop, int force ) 254 { 255 LDAP *ld = ldp ? *ldp : NULL; 256 int i = 0, do_retry = config->retries; 257 int rc = LDAP_SUCCESS; 258 int *msgids = NULL, active = 0; 259 260 /* make room for msgid */ 261 if ( swamp > 1 ) { 262 msgids = (int *)calloc( sizeof(int), maxloop ); 263 if ( !msgids ) { 264 tester_error( "calloc failed" ); 265 exit( EXIT_FAILURE ); 266 } 267 } 268 269 retry:; 270 if ( ld == NULL ) { 271 tester_init_ld( &ld, config, nobind ); 272 } 273 274 if ( do_retry == config->retries ) { 275 fprintf( stderr, "PID=%ld - Read(%d): entry=\"%s\".\n", 276 (long) pid, maxloop, entry ); 277 } 278 279 if ( swamp > 1 ) { 280 do { 281 LDAPMessage *res = NULL; 282 int j, msgid; 283 284 if ( i < maxloop ) { 285 rc = ldap_search_ext( ld, entry, LDAP_SCOPE_BASE, 286 NULL, attrs, noattrs, NULL, NULL, 287 NULL, LDAP_NO_LIMIT, &msgids[i] ); 288 289 active++; 290 #if 0 291 fprintf( stderr, 292 ">>> PID=%ld - Read maxloop=%d cnt=%d active=%d msgid=%d: " 293 "entry=\"%s\"\n", 294 (long) pid, maxloop, i, active, msgids[i], 295 entry ); 296 #endif 297 i++; 298 299 if ( rc ) { 300 char buf[BUFSIZ]; 301 int first = tester_ignore_err( rc ); 302 /* if ignore.. */ 303 if ( first ) { 304 /* only log if first occurrence */ 305 if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) { 306 tester_ldap_error( ld, "ldap_search_ext", NULL ); 307 } 308 continue; 309 } 310 311 /* busy needs special handling */ 312 snprintf( buf, sizeof( buf ), "entry=\"%s\"\n", entry ); 313 tester_ldap_error( ld, "ldap_search_ext", buf ); 314 if ( rc == LDAP_BUSY && do_retry > 0 ) { 315 ldap_unbind_ext( ld, NULL, NULL ); 316 ld = NULL; 317 do_retry--; 318 goto retry; 319 } 320 break; 321 } 322 323 if ( swamp > 2 ) { 324 continue; 325 } 326 } 327 328 rc = ldap_result( ld, LDAP_RES_ANY, 0, NULL, &res ); 329 switch ( rc ) { 330 case -1: 331 /* gone really bad */ 332 #if 0 333 fprintf( stderr, 334 ">>> PID=%ld - Read maxloop=%d cnt=%d active=%d: " 335 "entry=\"%s\" ldap_result()=%d\n", 336 (long) pid, maxloop, i, active, entry, rc ); 337 #endif 338 goto cleanup; 339 340 case 0: 341 /* timeout (impossible) */ 342 break; 343 344 case LDAP_RES_SEARCH_ENTRY: 345 case LDAP_RES_SEARCH_REFERENCE: 346 /* ignore */ 347 break; 348 349 case LDAP_RES_SEARCH_RESULT: 350 /* just remove, no error checking (TODO?) */ 351 msgid = ldap_msgid( res ); 352 ldap_parse_result( ld, res, &rc, NULL, NULL, NULL, NULL, 1 ); 353 res = NULL; 354 355 /* linear search, bah */ 356 for ( j = 0; j < i; j++ ) { 357 if ( msgids[ j ] == msgid ) { 358 msgids[ j ] = -1; 359 active--; 360 #if 0 361 fprintf( stderr, 362 "<<< PID=%ld - ReadDone maxloop=%d cnt=%d active=%d msgid=%d: " 363 "entry=\"%s\"\n", 364 (long) pid, maxloop, j, active, msgid, entry ); 365 #endif 366 break; 367 } 368 } 369 break; 370 371 default: 372 /* other messages unexpected */ 373 fprintf( stderr, 374 "### PID=%ld - Read(%d): " 375 "entry=\"%s\" attrs=%s%s. unexpected response tag=%d\n", 376 (long) pid, maxloop, 377 entry, attrs[0], attrs[1] ? " (more...)" : "", rc ); 378 break; 379 } 380 381 if ( res != NULL ) { 382 ldap_msgfree( res ); 383 } 384 } while ( i < maxloop || active > 0 ); 385 386 } else { 387 for ( ; i < maxloop; i++ ) { 388 LDAPMessage *res = NULL; 389 390 if (swamp) { 391 int msgid; 392 rc = ldap_search_ext( ld, entry, LDAP_SCOPE_BASE, 393 NULL, attrs, noattrs, NULL, NULL, 394 NULL, LDAP_NO_LIMIT, &msgid ); 395 if ( rc == LDAP_SUCCESS ) continue; 396 else break; 397 } 398 399 rc = ldap_search_ext_s( ld, entry, LDAP_SCOPE_BASE, 400 NULL, attrs, noattrs, NULL, NULL, NULL, 401 LDAP_NO_LIMIT, &res ); 402 if ( res != NULL ) { 403 ldap_msgfree( res ); 404 } 405 406 if ( rc ) { 407 int first = tester_ignore_err( rc ); 408 char buf[ BUFSIZ ]; 409 410 snprintf( buf, sizeof( buf ), "ldap_search_ext_s(%s)", entry ); 411 412 /* if ignore.. */ 413 if ( first ) { 414 /* only log if first occurrence */ 415 if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) { 416 tester_ldap_error( ld, buf, NULL ); 417 } 418 continue; 419 } 420 421 /* busy needs special handling */ 422 tester_ldap_error( ld, buf, NULL ); 423 if ( rc == LDAP_BUSY && do_retry > 0 ) { 424 ldap_unbind_ext( ld, NULL, NULL ); 425 ld = NULL; 426 do_retry--; 427 goto retry; 428 } 429 break; 430 } 431 } 432 } 433 434 cleanup:; 435 if ( msgids != NULL ) { 436 free( msgids ); 437 } 438 439 if ( ldp != NULL ) { 440 *ldp = ld; 441 442 } else { 443 fprintf( stderr, " PID=%ld - Read done (%d).\n", (long) pid, rc ); 444 445 if ( ld != NULL ) { 446 ldap_unbind_ext( ld, NULL, NULL ); 447 } 448 } 449 } 450 451