slapd-search.c revision 1.1.1.6 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