xref: /netbsd-src/external/bsd/openldap/dist/tests/progs/slapd-read.c (revision d16b7486a53dcb8072b60ec6fcb4373a2d0c27b7)
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
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
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
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
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