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