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