slapd-search.c revision 1.1.1.3.24.1 1 /* $NetBSD: slapd-search.c,v 1.1.1.3.24.1 2014/08/10 07:09:52 tls 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