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
usage(char * name,int opt)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
main(int argc,char ** argv)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
do_random(struct tester_conn_args * config,char * sbase,char * filter,char ** srchattrs,int noattrs,int nobind,int force)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
do_read(struct tester_conn_args * config,char * entry,LDAP ** ldp,char ** attrs,int noattrs,int nobind,int maxloop,int force)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