xref: /illumos-gate/usr/src/cmd/ldap/common/ldaptest.c (revision 6a634c9d)
1 /*
2  * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 
5 #include <stdio.h>
6 #include <ctype.h>
7 #include <string.h>
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <sys/time.h>
11 #include <sys/stat.h>
12 #include <sys/file.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15 
16 #include "lber.h"
17 #include "ldap.h"
18 
19 #define MOD_USE_BVALS
20 
21 #ifdef NEEDPROTOS
22 static void handle_result( LDAP *ld, LDAPMessage *lm );
23 static void print_ldap_result( LDAP *ld, LDAPMessage *lm, char *s );
24 static void print_search_entry( LDAP *ld, LDAPMessage *res );
25 static void free_list( char **list );
26 #else
27 static void handle_result();
28 static void print_ldap_result();
29 static void print_search_entry();
30 static void free_list();
31 #endif /* NEEDPROTOS */
32 
33 #define NOCACHEERRMSG	"don't compile with -DNO_CACHE if you desire local caching"
34 
35 char *dnsuffix;
36 
37 static char *
38 getaline( char *line, int len, FILE *fp, char *prompt )
39 {
40 	printf(prompt);
41 
42 	if ( fgets( line, len, fp ) == NULL )
43 		return( NULL );
44 
45 	line[ strlen( line ) - 1 ] = '\0';
46 
47 	return( line );
48 }
49 
50 static char **
51 get_list( char *prompt )
52 {
53 	static char	buf[256];
54 	int		num;
55 	char		**result;
56 
57 	num = 0;
58 	result = (char **) 0;
59 	while ( 1 ) {
60 		getaline( buf, sizeof(buf), stdin, prompt );
61 
62 		if ( *buf == '\0' )
63 			break;
64 
65 		if ( result == (char **) 0 )
66 			result = (char **) malloc( sizeof(char *) );
67 		else
68 			result = (char **) realloc( result,
69 			    sizeof(char *) * (num + 1) );
70 
71 		result[num++] = (char *) strdup( buf );
72 	}
73 	if ( result == (char **) 0 )
74 		return( NULL );
75 	result = (char **) realloc( result, sizeof(char *) * (num + 1) );
76 	result[num] = NULL;
77 
78 	return( result );
79 }
80 
81 
82 static void
83 free_list( char **list )
84 {
85 	int	i;
86 
87 	if ( list != NULL ) {
88 		for ( i = 0; list[ i ] != NULL; ++i ) {
89 			free( list[ i ] );
90 		}
91 		free( (char *)list );
92 	}
93 }
94 
95 
96 #ifdef MOD_USE_BVALS
97 static int
98 file_read( char *path, struct berval *bv )
99 {
100 	FILE		*fp;
101 	long		rlen;
102 	int		eof;
103 
104 	if (( fp = fopen( path, "r" )) == NULL ) {
105 	    	perror( path );
106 		return( -1 );
107 	}
108 
109 	if ( fseek( fp, 0L, SEEK_END ) != 0 ) {
110 		perror( path );
111 		fclose( fp );
112 		return( -1 );
113 	}
114 
115 	bv->bv_len = ftell( fp );
116 
117 	if (( bv->bv_val = (char *)malloc( bv->bv_len )) == NULL ) {
118 		perror( "malloc" );
119 		fclose( fp );
120 		return( -1 );
121 	}
122 
123 	if ( fseek( fp, 0L, SEEK_SET ) != 0 ) {
124 		perror( path );
125 		fclose( fp );
126 		return( -1 );
127 	}
128 
129 	rlen = fread( bv->bv_val, 1, bv->bv_len, fp );
130 	eof = feof( fp );
131 	fclose( fp );
132 
133 	if ( rlen != bv->bv_len ) {
134 		perror( path );
135 		free( bv->bv_val );
136 		return( -1 );
137 	}
138 
139 	return( bv->bv_len );
140 }
141 #endif /* MOD_USE_BVALS */
142 
143 
144 static LDAPMod **
145 get_modlist( char *prompt1, char *prompt2, char *prompt3 )
146 {
147 	static char	buf[256];
148 	int		num;
149 	LDAPMod		tmp;
150 	LDAPMod		**result;
151 #ifdef MOD_USE_BVALS
152 	struct berval	**bvals;
153 #endif /* MOD_USE_BVALS */
154 
155 	num = 0;
156 	result = NULL;
157 	while ( 1 ) {
158 		if ( prompt1 ) {
159 			getaline( buf, sizeof(buf), stdin, prompt1 );
160 			tmp.mod_op = atoi( buf );
161 
162 			if ( tmp.mod_op == -1 || buf[0] == '\0' )
163 				break;
164 		}
165 
166 		getaline( buf, sizeof(buf), stdin, prompt2 );
167 		if ( buf[0] == '\0' )
168 			break;
169 		tmp.mod_type = strdup( buf );
170 
171 		tmp.mod_values = get_list( prompt3 );
172 #ifdef MOD_USE_BVALS
173 		if ( tmp.mod_values != NULL ) {
174 			int	i;
175 
176 			for ( i = 0; tmp.mod_values[i] != NULL; ++i )
177 				;
178 			bvals = (struct berval **)calloc( i + 1,
179 			    sizeof( struct berval *));
180 			for ( i = 0; tmp.mod_values[i] != NULL; ++i ) {
181 				bvals[i] = (struct berval *)malloc(
182 				    sizeof( struct berval ));
183 				if ( strncmp( tmp.mod_values[i], "{FILE}",
184 				    6 ) == 0 ) {
185 					if ( file_read( tmp.mod_values[i] + 6,
186 					    bvals[i] ) < 0 ) {
187 						return( NULL );
188 					}
189 				} else {
190 					bvals[i]->bv_val = tmp.mod_values[i];
191 					bvals[i]->bv_len =
192 					    strlen( tmp.mod_values[i] );
193 				}
194 			}
195 			tmp.mod_bvalues = bvals;
196 			tmp.mod_op |= LDAP_MOD_BVALUES;
197 		}
198 #endif /* MOD_USE_BVALS */
199 
200 		if ( result == NULL )
201 			result = (LDAPMod **) malloc( sizeof(LDAPMod *) );
202 		else
203 			result = (LDAPMod **) realloc( result,
204 			    sizeof(LDAPMod *) * (num + 1) );
205 
206 		result[num] = (LDAPMod *) malloc( sizeof(LDAPMod) );
207 		*(result[num]) = tmp;	/* struct copy */
208 		num++;
209 	}
210 	if ( result == NULL )
211 		return( NULL );
212 	result = (LDAPMod **) realloc( result, sizeof(LDAPMod *) * (num + 1) );
213 	result[num] = NULL;
214 
215 	return( result );
216 }
217 
218 
219 int
220 bind_prompt( LDAP *ld, char **dnp, char **passwdp, int *authmethodp,
221 	int freeit )
222 {
223 	static char	dn[256], passwd[256];
224 
225 	if ( !freeit ) {
226 #ifdef KERBEROS
227 		getaline( dn, sizeof(dn), stdin,
228 		    "re-bind method (0->simple, 1->krbv41, 2->krbv42, 3->krbv41&2)? " );
229 		if (( *authmethodp = atoi( dn )) == 3 ) {
230 			*authmethodp = LDAP_AUTH_KRBV4;
231 		} else {
232 			*authmethodp |= 0x80;
233 		}
234 #else /* KERBEROS */
235 		*authmethodp = LDAP_AUTH_SIMPLE;
236 #endif /* KERBEROS */
237 
238 		getaline( dn, sizeof(dn), stdin, "re-bind dn? " );
239 		strcat( dn, dnsuffix );
240 		*dnp = dn;
241 
242 		if ( *authmethodp == LDAP_AUTH_SIMPLE && dn[0] != '\0' ) {
243 			getaline( passwd, sizeof(passwd), stdin,
244 			    "re-bind password? " );
245 		} else {
246 			passwd[0] = '\0';
247 		}
248 		*passwdp = passwd;
249 	}
250 
251 	return( LDAP_SUCCESS );
252 }
253 
254 
255 int
256 main(int argc, char **argv )
257 {
258 	LDAP	*ld;
259 	int		i, c, port, cldapflg, errflg, method, id,
260 		msgtype, delrdn, theInt, sizelimit, err;
261 	char	line[256], command1, command2, command3;
262 	char	passwd[64], dn[256], rdn[64], attr[64], value[256];
263 	char	filter[256], *host, **types;
264 	char 	*mechanism;
265 
266 	char	**exdn;
267 	char	*usage = "usage: %s [-u] [-h host] [-d level] [-s dnsuffix] [-p port] [-t file] [-T file]\n";
268 	int		bound, all, scope, attrsonly;
269 	LDAPMessage	*res;
270 	LDAPMod	**mods, **attrs;
271 	struct timeval	timeout, timelimit;
272 	char	*copyfname = NULL;
273 	int		copyoptions = 0, resultusetimelimit = 0;
274 	LDAPURLDesc	*ludp;
275 	struct berval bv, cred, *srvcrds = NULL;
276 	extern char	*optarg;
277 	extern int	optind;
278 	LDAPControl *ctrls[2];
279 	LDAPControl aCtrl;
280 
281 
282 #ifdef MACOS
283 	if (( argv = get_list( "cmd line arg?" )) == NULL ) {
284 		exit( 1 );
285 	}
286 	for ( argc = 0; argv[ argc ] != NULL; ++argc ) {
287 		;
288 	}
289 #endif /* MACOS */
290 
291 	host = NULL;
292 	port = LDAP_PORT;
293 	dnsuffix = "";
294 	cldapflg = errflg = 0;
295 	ctrls[0] = &aCtrl;
296 	ctrls[1] = NULL;
297 
298 	while (( c = getopt( argc, argv, "uh:d:s:p:t:T:" )) != -1 ) {
299 		switch( c ) {
300 		case 'u':
301 #ifdef CLDAP
302 			cldapflg++;
303 #else /* CLDAP */
304 			printf( "Compile with -DCLDAP for UDP support\n" );
305 #endif /* CLDAP */
306 			break;
307 
308 		case 'd':
309 #ifdef LDAP_DEBUG
310 			ldap_debug = atoi( optarg );
311 			if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
312 				lber_debug = ldap_debug;
313 			}
314 #else
315 			printf( "Compile with -DLDAP_DEBUG for debugging\n" );
316 #endif
317 			break;
318 
319 		case 'h':
320 			host = optarg;
321 			break;
322 
323 		case 's':
324 			dnsuffix = optarg;
325 			break;
326 
327 		case 'p':
328 			port = atoi( optarg );
329 			break;
330 
331 #if !defined(MACOS) && !defined(DOS)
332 		case 't':	/* copy ber's to given file */
333 			copyfname = strdup( optarg );
334 			copyoptions = LBER_TO_FILE;
335 			break;
336 
337 		case 'T':	/* only output ber's to given file */
338 			copyfname = strdup( optarg );
339 			copyoptions = (LBER_TO_FILE | LBER_TO_FILE_ONLY);
340 			break;
341 #endif
342 
343 		default:
344 		    ++errflg;
345 		}
346 	}
347 
348 	if ( host == NULL && optind == argc - 1 ) {
349 		host = argv[ optind ];
350 		++optind;
351 	}
352 
353 	if ( errflg || optind < argc - 1 ) {
354 		fprintf( stderr, usage, argv[ 0 ] );
355 		exit( 1 );
356 	}
357 
358 	printf( "%s( %s, %d )\n", cldapflg ? "cldap_open" : "ldap_init",
359 		host == NULL ? "(null)" : host, port );
360 
361 	if ( cldapflg ) {
362 #ifdef CLDAP
363 		ld = cldap_open( host, port );
364 #endif /* CLDAP */
365 	} else {
366 		ld = ldap_init( host, port );
367 	}
368 
369 	if ( ld == NULL ) {
370 		perror( "ldap_init" );
371 		exit(1);
372 	}
373 
374 #if !defined(MACOS) && !defined(DOS)
375 	if ( copyfname != NULL ) {
376 		if ( (ld->ld_sb.sb_fd = open( copyfname, O_WRONLY | O_CREAT,
377 		    0600 ))  == -1 ) {
378 			perror( copyfname );
379 			exit ( 1 );
380 		}
381 		ld->ld_sb.sb_options = copyoptions;
382 	}
383 #endif
384 
385 	bound = 0;
386 	timeout.tv_sec = 0;
387 	timeout.tv_usec = 0;
388 	timelimit.tv_sec = 0;
389 	timelimit.tv_usec = 0;
390 
391 	(void) memset( line, '\0', sizeof(line) );
392 	while ( getaline( line, sizeof(line), stdin, "\ncommand? " ) != NULL ) {
393 		command1 = line[0];
394 		command2 = line[1];
395 		command3 = line[2];
396 
397 		switch ( command1 ) {
398 		case 'a':	/* add or abandon */
399 			switch ( command2 ) {
400 			case 'd':	/* add */
401 				getaline( dn, sizeof(dn), stdin, "dn? " );
402 				strcat( dn, dnsuffix );
403 				if ( (attrs = get_modlist( NULL, "attr? ",
404 				    "value? " )) == NULL )
405 					break;
406 				if (ldap_get_option(ld, LDAP_OPT_PROTOCOL_VERSION, &i) == LDAP_SUCCESS && i == LDAP_VERSION3){
407 					if ((err = ldap_add_ext( ld, dn, attrs, NULL, NULL, &id )) != LDAP_SUCCESS )
408 						printf( "Error in ldap_add_ext: %s\n", ldap_err2string(err) );
409 					else
410 						printf( "Add initiated with id %d\n", id );
411 				}
412 				else {
413 					if ( (id = ldap_add( ld, dn, attrs )) == -1 )
414 						ldap_perror( ld, "ldap_add" );
415 					else
416 						printf( "Add initiated with id %d\n", id );
417 				}
418 
419 				break;
420 
421 			case 'b':	/* abandon */
422 				getaline( line, sizeof(line), stdin, "msgid? " );
423 				id = atoi( line );
424 				if ( ldap_abandon( ld, id ) != 0 )
425 					ldap_perror( ld, "ldap_abandon" );
426 				else
427 					printf( "Abandon successful\n" );
428 				break;
429 			default:
430 				printf( "Possibilities: [ad]d, [ab]ort\n" );
431 			}
432 			break;
433 
434 		case 'b':	/* asynch bind */
435 #ifdef KERBEROS
436 			getaline( line, sizeof(line), stdin,
437 			    "method (0->simple, 1->krbv41, 2->krbv42)? " );
438 			method = atoi( line ) | 0x80;
439 #else /* KERBEROS */
440 			method = LDAP_AUTH_SIMPLE;
441 #endif /* KERBEROS */
442 			getaline( dn, sizeof(dn), stdin, "dn? " );
443 			strcat( dn, dnsuffix );
444 
445 			if ( method == LDAP_AUTH_SIMPLE && dn[0] != '\0' )
446 				getaline( passwd, sizeof(passwd), stdin,
447 				    "password? " );
448 			else
449 				passwd[0] = '\0';
450 
451 			if ( ldap_bind( ld, dn, passwd, method ) == -1 ) {
452 				fprintf( stderr, "ldap_bind failed\n" );
453 				ldap_perror( ld, "ldap_bind" );
454 			} else {
455 				printf( "Bind initiated\n" );
456 				bound = 1;
457 			}
458 			break;
459 
460 		case 'B':	/* synch bind */
461 #ifdef KERBEROS
462 			getaline( line, sizeof(line), stdin,
463 			    "method 0->simple 1->krbv41 2->krbv42 3->krb? " );
464 			method = atoi( line );
465 			if ( method == 3 )
466 				method = LDAP_AUTH_KRBV4;
467 			else
468 				method = method | 0x80;
469 #else /* KERBEROS */
470 			getaline( line, sizeof(line), stdin,
471 					 "method 0->simple, 1->SASL? ");
472 			method = atoi (line);
473 			if (method == 1){
474 				method = LDAP_AUTH_SASL;
475 				getaline( line, sizeof(line), stdin,
476 						 "mechanism 0->CRAM_MD5, 1->TLS? ");
477 				theInt = atoi(line);
478 				if (theInt == 0){
479 					mechanism = LDAP_SASL_CRAM_MD5;
480 				}
481 				else{
482 					mechanism = LDAP_SASL_X511_STRONG;
483 				}
484 			} else {
485 				method = LDAP_AUTH_SIMPLE;
486 			}
487 
488 #endif /* KERBEROS */
489 			getaline( dn, sizeof(dn), stdin, "dn? " );
490 			strcat( dn, dnsuffix );
491 
492 			if ( dn[0] != '\0' )
493 				getaline( passwd, sizeof(passwd), stdin,
494 				    "password? " );
495 			else
496 				passwd[0] = '\0';
497 
498 			if (method == LDAP_AUTH_SIMPLE) {
499 				if ( ldap_bind_s( ld, dn, passwd, method ) !=
500 					 LDAP_SUCCESS ) {
501 					fprintf( stderr, "ldap_bind_s failed\n" );
502 					ldap_perror( ld, "ldap_bind_s" );
503 				} else {
504 					printf( "Bind successful\n" );
505 					bound = 1;
506 				}
507 			} else {
508 				if (strcmp(mechanism, LDAP_SASL_CRAM_MD5) == 0){
509 					cred.bv_val = passwd;
510 					cred.bv_len = strlen(passwd);
511 
512 					if ( ldap_sasl_cram_md5_bind_s(ld, dn, &cred, NULL, NULL) != LDAP_SUCCESS ){
513 						fprintf( stderr, "ldap_sasl_cram_md5_bind_s failed\n" );
514 						ldap_perror( ld, "ldap_sasl_cram_md5_bind_s" );
515 					} else {
516 						printf ( "Bind successful\n");
517 						bound = 1;
518 					}
519 				} else {
520 					if (ldap_sasl_bind_s(ld, dn, mechanism, &cred, NULL, NULL, &srvcrds ) != LDAP_SUCCESS){
521 						fprintf( stderr, "ldap_sasl_bind_s failed\n" );
522 						ldap_perror( ld, "ldap_sasl_bind_s" );
523 					}
524 				}
525 			}
526 			break;
527 
528 		case 'c':	/* compare */
529 			getaline( dn, sizeof(dn), stdin, "dn? " );
530 			strcat( dn, dnsuffix );
531 			getaline( attr, sizeof(attr), stdin, "attr? " );
532 			getaline( value, sizeof(value), stdin, "value? " );
533 
534 			if (ldap_get_option(ld, LDAP_OPT_PROTOCOL_VERSION, &i) == LDAP_SUCCESS && i == LDAP_VERSION3){
535 				bv.bv_val = value;
536 				bv.bv_len = strlen(value);
537 				if ((err = ldap_compare_ext( ld, dn, attr, &bv, NULL, NULL, &id )) != LDAP_SUCCESS )
538 					printf( "Error in ldap_compare_ext: %s\n", ldap_err2string(err) );
539 				else
540 					printf( "Compare initiated with id %d\n", id );
541 			} else {
542 				if ( (id = ldap_compare( ld, dn, attr, value )) == -1 )
543 					ldap_perror( ld, "ldap_compare" );
544 				else
545 					printf( "Compare initiated with id %d\n", id );
546 			}
547 			break;
548 
549 		case 'd':	/* turn on debugging */
550 #ifdef LDAP_DEBUG
551 			getaline( line, sizeof(line), stdin, "debug level? " );
552 			ldap_debug = atoi( line );
553 			if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
554 				lber_debug = ldap_debug;
555 			}
556 #else
557 			printf( "Compile with -DLDAP_DEBUG for debugging\n" );
558 #endif
559 			break;
560 
561 		case 'E':	/* explode a dn */
562 			getaline( line, sizeof(line), stdin, "dn? " );
563 			exdn = ldap_explode_dn( line, 0 );
564 			for ( i = 0; exdn != NULL && exdn[i] != NULL; i++ ) {
565 				printf( "\t%s\n", exdn[i] );
566 			}
567 			break;
568 
569 		case 'g':	/* set next msgid */
570 			getaline( line, sizeof(line), stdin, "msgid? " );
571 			ld->ld_msgid = atoi( line );
572 			break;
573 
574 		case 'v':	/* set version number */
575 			getaline( line, sizeof(line), stdin, "version? " );
576 			theInt = atoi(line);
577 			ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &theInt);
578 			break;
579 
580 		case 'm':	/* modify or modifyrdn */
581 			if ( strncmp( line, "modify", 4 ) == 0 ) {
582 				getaline( dn, sizeof(dn), stdin, "dn? " );
583 				strcat( dn, dnsuffix );
584 				if ( (mods = get_modlist(
585 				    "mod (0=>add, 1=>delete, 2=>replace -1=>done)? ",
586 				    "attribute type? ", "attribute value? " ))
587 				    == NULL )
588 					break;
589 				if (ldap_get_option(ld, LDAP_OPT_PROTOCOL_VERSION, &i) == LDAP_SUCCESS && i == LDAP_VERSION3){
590 					if ((err = ldap_modify_ext( ld, dn, mods, NULL, NULL, &id )) != LDAP_SUCCESS )
591 						printf( "Error in ldap_modify_ext: %s\n", ldap_err2string(err) );
592 					else
593 						printf( "Modify initiated with id %d\n", id );
594 				}
595 				else {
596 					if ( (id = ldap_modify( ld, dn, mods )) == -1 )
597 						ldap_perror( ld, "ldap_modify" );
598 					else
599 						printf( "Modify initiated with id %d\n", id );
600 				}
601 			} else if ( strncmp( line, "modrdn", 4 ) == 0 ) {
602 				getaline( dn, sizeof(dn), stdin, "dn? " );
603 				strcat( dn, dnsuffix );
604 				getaline( rdn, sizeof(rdn), stdin, "newrdn? " );
605 				getaline( line, sizeof(line), stdin, "delete old rdn (0=>no, 1=>yes)?");
606 				delrdn = atoi(line);
607 				if (ldap_get_option(ld, LDAP_OPT_PROTOCOL_VERSION, &i) == LDAP_SUCCESS && i == LDAP_VERSION3){
608 					if ((err = ldap_rename(ld, dn, rdn, NULL, delrdn, NULL,NULL, &id)) != LDAP_SUCCESS){
609 						printf( "Error in ldap_rename (modrdn): %s\n", ldap_err2string(err));
610 					}
611 					else
612 						printf( "Modrdn initiated with id %d\n", id );
613 				}
614 				else {
615 					if ( (id = ldap_modrdn( ld, dn, rdn, delrdn )) == -1 )
616 						ldap_perror( ld, "ldap_modrdn" );
617 					else
618 						printf( "Modrdn initiated with id %d\n", id );
619 				}
620 			} else {
621 				printf( "Possibilities: [modi]fy, [modr]dn\n" );
622 			}
623 			break;
624 
625 		case 'q':	/* quit */
626 #ifdef CLDAP
627 			if ( cldapflg )
628 				cldap_close( ld );
629 #endif /* CLDAP */
630 			if ( !cldapflg )
631 				ldap_unbind( ld );
632 			exit( 0 );
633 			break;
634 
635 		case 'r':	/* result or remove */
636 			switch ( command3 ) {
637 			case 's':	/* result */
638 				getaline( line, sizeof(line), stdin,
639 				    "msgid (-1=>any)? " );
640 				if ( line[0] == '\0' )
641 					id = -1;
642 				else
643 					id = atoi( line );
644 				getaline( line, sizeof(line), stdin,
645 				    "all (0=>any, 1=>all)? " );
646 				if ( line[0] == '\0' )
647 					all = 1;
648 				else
649 					all = atoi( line );
650 
651 				if (( msgtype = ldap_result( ld, id, all,
652 				    resultusetimelimit ? &timelimit : &timeout, &res )) < 1 ) {
653 					ldap_perror( ld, "ldap_result" );
654 					break;
655 				}
656 				printf( "\nresult: msgtype %d msgid %d\n",
657 				    msgtype, res->lm_msgid );
658 				handle_result( ld, res );
659 				if (all || msgtype == LDAP_RES_SEARCH_RESULT)
660 					resultusetimelimit = 0;
661 				res = NULLMSG;
662 				break;
663 
664 			case 'm':	/* remove */
665 				getaline( dn, sizeof(dn), stdin, "dn? " );
666 				strcat( dn, dnsuffix );
667 				if (ldap_get_option(ld, LDAP_OPT_PROTOCOL_VERSION, &i) == LDAP_SUCCESS && i == LDAP_VERSION3){
668 					if ((err = ldap_delete_ext( ld, dn, NULL, NULL, &id )) != LDAP_SUCCESS )
669 						printf( "Error in ldap_delete_ext: %s\n", ldap_err2string(err) );
670 					else
671 						printf( "Remove initiated with id %d\n", id );
672 				} else {
673 					if ( (id = ldap_delete( ld, dn )) == -1 )
674 						ldap_perror( ld, "ldap_delete" );
675 					else
676 						printf( "Remove initiated with id %d\n", id );
677 				}
678 				break;
679 
680 			default:
681 				printf( "Possibilities: [rem]ove, [res]ult\n" );
682 				break;
683 			}
684 			break;
685 
686 		case 's':	/* search */
687 			getaline( dn, sizeof(dn), stdin, "searchbase? " );
688 			strcat( dn, dnsuffix );
689 			getaline( line, sizeof(line), stdin,
690 			    "scope (0=Base, 1=One Level, 2=Subtree)? " );
691 			scope = atoi( line );
692 			getaline( filter, sizeof(filter), stdin,
693 			    "search filter (e.g. sn=jones)? " );
694 			types = get_list( "attrs to return? " );
695 			getaline( line, sizeof(line), stdin,
696 			    "attrsonly (0=attrs&values, 1=attrs only)? " );
697 			attrsonly = atoi( line );
698 
699 			if ( cldapflg ) {
700 #ifdef CLDAP
701 			    getaline( line, sizeof(line), stdin,
702 				"Requestor DN (for logging)? " );
703 			    if ( cldap_search_s( ld, dn, scope, filter, types,
704 				    attrsonly, &res, line ) != 0 ) {
705 				ldap_perror( ld, "cldap_search_s" );
706 			    } else {
707 				printf( "\nresult: msgid %d\n",
708 				    res->lm_msgid );
709 				handle_result( ld, res );
710 				res = NULLMSG;
711 			    }
712 #endif /* CLDAP */
713 			} else {
714 				theInt = 0;
715 				if (ldap_get_option(ld, LDAP_OPT_PROTOCOL_VERSION, &i) == LDAP_SUCCESS && i == LDAP_VERSION3){
716 					resultusetimelimit = 1;
717 					getaline( line, sizeof(line), stdin,
718 							 "ldap_search_ext (0=>no, 1=>yes - default: yes)? " );
719 					if (line[0] == '\0')
720 						theInt = 1;
721 					else
722 						theInt = atoi( line );
723 				}
724 				if (theInt){
725 					getaline(line, sizeof(line), stdin, "time limit?");
726 					timelimit.tv_sec = atoi(line);
727 					resultusetimelimit = 1;
728 					getaline(line, sizeof(line), stdin, "size limit?");
729 					sizelimit = atoi(line);
730 					if (( err = ldap_search_ext(ld, dn, scope, filter, types, attrsonly, NULL, NULL,
731 												&timelimit, sizelimit, &id)) != LDAP_SUCCESS){
732 						printf( "Error in ldap_search_ext: %s\n", ldap_err2string(err));
733 					} else {
734 						printf( "Search initiated with id %d\n", id );
735 					}
736 				} else {
737 					if (( id = ldap_search( ld, dn, scope, filter,
738 											types, attrsonly  )) == -1 ) {
739 						ldap_perror( ld, "ldap_search" );
740 					} else {
741 						printf( "Search initiated with id %d\n", id );
742 					}
743 				}
744 			}
745 			free_list( types );
746 			break;
747 
748 		case 't':	/* set timeout value */
749 			getaline( line, sizeof(line), stdin, "timeout? " );
750 			timeout.tv_sec = atoi( line );
751 			break;
752 
753 		case 'U':	/* set ufn search prefix */
754 			getaline( line, sizeof(line), stdin, "ufn prefix? " );
755 			ldap_ufn_setprefix( ld, line );
756 			break;
757 
758 		case 'u':	/* user friendly search w/optional timeout */
759 			getaline( dn, sizeof(dn), stdin, "ufn? " );
760 			strcat( dn, dnsuffix );
761 			types = get_list( "attrs to return? " );
762 			getaline( line, sizeof(line), stdin,
763 			    "attrsonly (0=attrs&values, 1=attrs only)? " );
764 			attrsonly = atoi( line );
765 
766 			if ( command2 == 't' ) {
767 				id = ldap_ufn_search_c( ld, dn, types,
768 				    attrsonly, &res, ldap_ufn_timeout,
769 				    &timeout );
770 			} else {
771 				id = ldap_ufn_search_s( ld, dn, types,
772 				    attrsonly, &res );
773 			}
774 			if ( res == NULL )
775 				ldap_perror( ld, "ldap_ufn_search" );
776 			else {
777 				printf( "\nresult: err %d\n", id );
778 				handle_result( ld, res );
779 				res = NULLMSG;
780 			}
781 			free_list( types );
782 			break;
783 
784 		case 'l':	/* URL search */
785 			getaline( line, sizeof(line), stdin,
786 			    "attrsonly (0=attrs&values, 1=attrs only)? " );
787 			attrsonly = atoi( line );
788 			getaline( line, sizeof(line), stdin, "LDAP URL? " );
789 			if (( id = ldap_url_search( ld, line, attrsonly  ))
790 				== -1 ) {
791 			    ldap_perror( ld, "ldap_url_search" );
792 			} else {
793 			    printf( "URL search initiated with id %d\n", id );
794 			}
795 			break;
796 
797 		case 'p':	/* parse LDAP URL */
798 			getaline( line, sizeof(line), stdin, "LDAP URL? " );
799 			if (( i = ldap_url_parse( line, &ludp )) != 0 ) {
800 			    fprintf( stderr, "ldap_url_parse: error %d\n", i );
801 			} else {
802 			    printf( "\t  host: " );
803 			    if ( ludp->lud_host == NULL ) {
804 				printf( "DEFAULT\n" );
805 			    } else {
806 				printf( "<%s>\n", ludp->lud_host );
807 			    }
808 			    printf( "\t  port: " );
809 			    if ( ludp->lud_port == 0 ) {
810 				printf( "DEFAULT\n" );
811 			    } else {
812 				printf( "%d\n", ludp->lud_port );
813 			    }
814 			    printf( "\t    dn: <%s>\n", ludp->lud_dn );
815 			    printf( "\t attrs:" );
816 			    if ( ludp->lud_attrs == NULL ) {
817 				printf( " ALL" );
818 			    } else {
819 				for ( i = 0; ludp->lud_attrs[ i ] != NULL; ++i ) {
820 				    printf( " <%s>", ludp->lud_attrs[ i ] );
821 				}
822 			    }
823 			    printf( "\n\t scope: %s\n", ludp->lud_scope == LDAP_SCOPE_UNKNOWN ? "DEFAULT (base)" :
824 						ludp->lud_scope == LDAP_SCOPE_ONELEVEL ? "ONE" :
825 						ludp->lud_scope == LDAP_SCOPE_BASE ? "BASE" :
826 						ludp->lud_scope == LDAP_SCOPE_SUBTREE ? "SUB" : "**invalid**" );
827 			    printf( "\tfilter: <%s>\n", ludp->lud_filter ? ludp->lud_filter : "NONE");
828 				if (ludp->lud_extensions){
829 					printf("\textensions: \n");
830 					for (i = 0; ludp->lud_extensions[i] != NULL; i++)
831 						printf("\t\t%s (%s)\n", ludp->lud_extensions[i]->lue_type,
832 							   ludp->lud_extensions[i]->lue_iscritical ? "Critical" : "Non critical");
833 				}
834 
835 			    ldap_free_urldesc( ludp );
836 			}
837 			    break;
838 
839 		case 'n':	/* set dn suffix, for convenience */
840 			getaline( line, sizeof(line), stdin, "DN suffix? " );
841 			strcpy( dnsuffix, line );
842 			break;
843 
844 		case 'e':	/* enable cache */
845 #ifdef NO_CACHE
846 			printf( NOCACHEERRMSG );
847 #else /* NO_CACHE */
848 			getaline( line, sizeof(line), stdin, "Cache timeout (secs)? " );
849 			i = atoi( line );
850 			getaline( line, sizeof(line), stdin, "Maximum memory to use (bytes)? " );
851 			if ( ldap_enable_cache( ld, i, atoi( line )) == 0 ) {
852 				printf( "local cache is on\n" );
853 			} else {
854 				printf( "ldap_enable_cache failed\n" );
855 			}
856 #endif /* NO_CACHE */
857 			break;
858 
859 		case 'x':	/* uncache entry */
860 #ifdef NO_CACHE
861 			printf( NOCACHEERRMSG );
862 #else /* NO_CACHE */
863 			getaline( line, sizeof(line), stdin, "DN? " );
864 			ldap_uncache_entry( ld, line );
865 #endif /* NO_CACHE */
866 			break;
867 
868 		case 'X':	/* uncache request */
869 #ifdef NO_CACHE
870 			printf( NOCACHEERRMSG );
871 #else /* NO_CACHE */
872 			getaline( line, sizeof(line), stdin, "request msgid? " );
873 			ldap_uncache_request( ld, atoi( line ));
874 #endif /* NO_CACHE */
875 			break;
876 
877 		case 'o':	/* set ldap options */
878 			getaline( line, sizeof(line), stdin, "alias deref (0=never, 1=searching, 2=finding, 3=always)?" );
879 			theInt = atoi(line);
880 			ldap_set_option(ld, LDAP_OPT_DEREF, &theInt );
881 			getaline( line, sizeof(line), stdin, "timelimit?" );
882 			theInt = atoi(line);
883 			ldap_set_option(ld, LDAP_OPT_TIMELIMIT,  &theInt);
884 			getaline( line, sizeof(line), stdin, "sizelimit?" );
885 			theInt = atoi(line);
886 			ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &theInt);
887 
888 			ld->ld_options = 0;
889 
890 #ifdef STR_TRANSLATION
891 			getaline( line, sizeof(line), stdin,
892 				"Automatic translation of T.61 strings (0=no, 1=yes)?" );
893 			if ( atoi( line ) == 0 ) {
894 				ld->ld_lberoptions &= ~LBER_TRANSLATE_STRINGS;
895 			} else {
896 				ld->ld_lberoptions |= LBER_TRANSLATE_STRINGS;
897 #ifdef LDAP_CHARSET_8859
898 				getaline( line, sizeof(line), stdin,
899 					"Translate to/from ISO-8859 (0=no, 1=yes?" );
900 				if ( atoi( line ) != 0 ) {
901 					ldap_set_string_translators( ld,
902 					    ldap_8859_to_t61,
903 					    ldap_t61_to_8859 );
904 				}
905 #endif /* LDAP_CHARSET_8859 */
906 			}
907 #endif /* STR_TRANSLATION */
908 
909 #ifdef LDAP_DNS
910 			getaline( line, sizeof(line), stdin,
911 				"Use DN & DNS to determine where to send requests (0=no, 1=yes)?" );
912 			if ( atoi( line ) != 0 ) {
913 				ld->ld_options |= LDAP_OPT_DNS;
914 			}
915 #endif /* LDAP_DNS */
916 
917 			getaline( line, sizeof(line), stdin,
918 				"Recognize and chase referrals (0=no, 1=yes)?" );
919 			if ( atoi( line ) != 0 ) {
920 				theInt = LDAP_OPT_ON;
921 				getaline( line, sizeof(line), stdin,
922 						 "Prompt for bind credentials when chasing referrals (0=no, 1=yes)?" );
923 				if ( atoi( line ) != 0 ) {
924 					ldap_set_option( ld, LDAP_OPT_REBIND_FN, bind_prompt );
925 				}
926 			} else {
927 				theInt = LDAP_OPT_OFF;
928 			}
929 			ldap_set_option(ld, LDAP_OPT_REFERRALS, &theInt);
930 			break;
931 
932 		case 'k': /* Set some controls */
933 			getaline( line, sizeof(line), stdin,
934 					 "Set control: (0 for none, 1 for ManageDSA, 2 for preferredLang, 3 for BAD)?");
935 			theInt = atoi(line);
936 			switch (theInt){
937 			case 0:
938 				ldap_set_option(ld, LDAP_OPT_SERVER_CONTROLS, NULL);
939 				break;
940 			case 1:
941 				aCtrl.ldctl_oid = "2.16.840.1.113730.3.4.2";
942 				aCtrl.ldctl_iscritical = 1;
943 				aCtrl.ldctl_value = NULL;
944 				ldap_set_option(ld, LDAP_OPT_SERVER_CONTROLS, ctrls);
945 				break;
946 			case 2:
947 				getaline( line, sizeof(line), stdin,
948 						 "Preferred Language Control : lang ?");
949 				aCtrl.ldctl_oid = "1.3.6.1.4.1.1466.20035";
950 				aCtrl.ldctl_iscritical = 1;
951 				bv.bv_val = strdup(line);
952 				bv.bv_len = strlen(line);
953 				aCtrl.ldctl_value = &bv;
954 				ldap_set_option(ld, LDAP_OPT_SERVER_CONTROLS, ctrls);
955 				break;
956 			default:
957 				getaline( line, sizeof(line), stdin,
958 						 "Bad Control is critical (0=false, 1=true)?");
959 				aCtrl.ldctl_oid = "1.1.1.1.1.1";
960 				aCtrl.ldctl_iscritical = atoi(line);
961 				aCtrl.ldctl_value = NULL;
962 				ldap_set_option(ld, LDAP_OPT_SERVER_CONTROLS, ctrls);
963 				break;
964 			}
965 			break;
966 
967 		case 'O':	/* set cache options */
968 #ifdef NO_CACHE
969 			printf( NOCACHEERRMSG );
970 #else /* NO_CACHE */
971 			getaline( line, sizeof(line), stdin, "cache errors (0=smart, 1=never, 2=always)?" );
972 			switch( atoi( line )) {
973 			case 0:
974 				ldap_set_cache_options( ld, 0 );
975 				break;
976 			case 1:
977 				ldap_set_cache_options( ld,
978 					LDAP_CACHE_OPT_CACHENOERRS );
979 				break;
980 			case 2:
981 				ldap_set_cache_options( ld,
982 					LDAP_CACHE_OPT_CACHEALLERRS );
983 				break;
984 			default:
985 				printf( "not a valid cache option\n" );
986 			}
987 #endif /* NO_CACHE */
988 			break;
989 
990 		case '?':	/* help */
991     printf( "Commands: [ad]d         [ab]andon         [b]ind\n" );
992     printf( "          [B]ind async  [c]ompare         [l]URL search\n" );
993     printf( "          [modi]fy      [modr]dn          [rem]ove\n" );
994     printf( "          [res]ult      [s]earch          [q]uit/unbind\n\n" );
995     printf( "          [u]fn search  [ut]fn search with timeout\n" );
996     printf( "          [d]ebug       [e]nable cache    set ms[g]id\n" );
997     printf( "          d[n]suffix    [t]imeout         [v]ersion\n" );
998     printf( "          [U]fn prefix  [x]uncache entry  [X]uncache request\n" );
999     printf( "          [?]help       [o]ptions         [O]cache options\n" );
1000     printf( "          [E]xplode dn  [p]arse LDAP URL\n" );
1001 			break;
1002 
1003 		default:
1004 			printf( "Invalid command.  Type ? for help.\n" );
1005 			break;
1006 		}
1007 
1008 		(void) memset( line, '\0', sizeof(line) );
1009 	}
1010 
1011 	return( 0 );
1012 }
1013 
1014 static void
1015 handle_result( LDAP *ld, LDAPMessage *lm )
1016 {
1017 	switch ( lm->lm_msgtype ) {
1018 	case LDAP_RES_COMPARE:
1019 		printf( "Compare result\n" );
1020 		print_ldap_result( ld, lm, "compare" );
1021 		break;
1022 
1023 	case LDAP_RES_SEARCH_RESULT:
1024 		printf( "Search result\n" );
1025 		print_ldap_result( ld, lm, "search" );
1026 		break;
1027 
1028 	case LDAP_RES_SEARCH_REFERENCE:
1029 		printf( "Search reference\n" );
1030 		print_search_entry( ld, lm );
1031 		break;
1032 
1033 	case LDAP_RES_SEARCH_ENTRY:
1034 		printf( "Search entry\n" );
1035 		print_search_entry( ld, lm );
1036 		break;
1037 
1038 	case LDAP_RES_ADD:
1039 		printf( "Add result\n" );
1040 		print_ldap_result( ld, lm, "add" );
1041 		break;
1042 
1043 	case LDAP_RES_DELETE:
1044 		printf( "Delete result\n" );
1045 		print_ldap_result( ld, lm, "delete" );
1046 		break;
1047 
1048 	case LDAP_RES_MODIFY:
1049 		printf( "Modify result\n" );
1050 		print_ldap_result( ld, lm, "modify" );
1051 		break;
1052 
1053 	case LDAP_RES_MODRDN:
1054 		printf( "ModRDN result\n" );
1055 		print_ldap_result( ld, lm, "modrdn" );
1056 		break;
1057 
1058 	case LDAP_RES_BIND:
1059 		printf( "Bind result\n" );
1060 		print_ldap_result( ld, lm, "bind" );
1061 		break;
1062 
1063 	default:
1064 		printf( "Unknown result type 0x%x\n", lm->lm_msgtype );
1065 		print_ldap_result( ld, lm, "unknown" );
1066 	}
1067 }
1068 
1069 static void
1070 print_ldap_result( LDAP *ld, LDAPMessage *lm, char *s )
1071 {
1072 	int rc, i;
1073 	int errCode;
1074 	char *matched = NULL, *errMsg = NULL, **referrals = NULL;
1075 	LDAPControl **srvctrls = NULL;
1076 
1077 	if ((rc = ldap_parse_result(ld, lm, &errCode, &matched, &errMsg, &referrals, &srvctrls, 0)) != LDAP_SUCCESS){
1078 		fprintf(stderr, "%s: error while parsing result (%s)\n", s, ldap_err2string(rc));
1079 		return;
1080 	}
1081 
1082 
1083 	fprintf(stderr, "%s: %s\n", s, ldap_err2string(errCode));
1084 	if (errCode == LDAP_REFERRAL){
1085 		fprintf(stderr, "\tReferrals returned: \n");
1086 		for (i = 0; referrals[i] != NULL; i++)
1087 			fprintf(stderr, "\t\t%s\n", referrals[i]);
1088 	}
1089 	if (errMsg && *errMsg)
1090 		fprintf(stderr, "\tAdditional info: %s\n", errMsg);
1091 	free(errMsg);
1092 	if (NAME_ERROR(errCode) && matched && *matched){
1093 		fprintf(stderr, "\tMatched DN: %s\n", matched);
1094 		free(matched);
1095 	}
1096 	if (srvctrls != NULL){
1097 		fprintf(stderr, "\tLDAPControls returned: \n");
1098 		for (i=0;srvctrls[i] != NULL; i++)
1099 			fprintf(stderr, "\t\t%s (%s)\n", srvctrls[i]->ldctl_oid, srvctrls[i]->ldctl_iscritical ? "Critical" : "Not critical");
1100 	}
1101 	return;
1102 }
1103 
1104 static void
1105 print_search_entry( LDAP *ld, LDAPMessage *res )
1106 {
1107 	BerElement	*ber;
1108 	char		*a, *dn, *ufn;
1109 	struct berval	**vals;
1110 	int		i;
1111 	LDAPMessage	*e;
1112 
1113 	for ( e = ldap_first_message( ld, res ); e != NULLMSG;
1114 	    e = ldap_next_message( ld, e ) ) {
1115 		if ( e->lm_msgtype == LDAP_RES_SEARCH_RESULT )
1116 			break;
1117 
1118 		dn = ldap_get_dn( ld, e );
1119 		printf( "\tDN: %s\n", dn );
1120 
1121 		ufn = ldap_dn2ufn( dn );
1122 		printf( "\tUFN: %s\n", ufn );
1123 		free( dn );
1124 		free( ufn );
1125 
1126 		if ( e->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ){
1127 			char **urls = ldap_get_reference_urls(ld, e);
1128 			if (urls == NULL){
1129 				printf("\t\tError with references: %s\n", ldap_err2string(ld->ld_errno));
1130 			} else {
1131 				for (i=0;urls[i] != NULL;i++)
1132 					printf("\t\tURL: %s\n", urls[i]);
1133 			}
1134 		} else {
1135 			for ( a = ldap_first_attribute( ld, e, &ber ); a != NULL;
1136 				  a = ldap_next_attribute( ld, e, ber ) ) {
1137 				printf( "\t\tATTR: %s\n", a );
1138 				if ( (vals = ldap_get_values_len( ld, e, a ))
1139 					 == NULL ) {
1140 					printf( "\t\t\t(no values)\n" );
1141 				} else {
1142 					for ( i = 0; vals[i] != NULL; i++ ) {
1143 						int	j, nonascii;
1144 
1145 						nonascii = 0;
1146 						for ( j = 0; j < vals[i]->bv_len; j++ )
1147 							if ( !isascii( vals[i]->bv_val[j] ) ) {
1148 							nonascii = 1;
1149 							break;
1150 							}
1151 
1152 						if ( nonascii ) {
1153 							printf( "\t\t\tlength (%ld) (not ascii)\n", vals[i]->bv_len );
1154 #ifdef BPRINT_NONASCII
1155 							lber_bprint( vals[i]->bv_val,
1156 										 vals[i]->bv_len );
1157 #endif /* BPRINT_NONASCII */
1158 							continue;
1159 						}
1160 						printf( "\t\t\tlength (%ld) %s\n",
1161 								vals[i]->bv_len, vals[i]->bv_val );
1162 					}
1163 					ber_bvecfree( vals );
1164 				}
1165 			}
1166 		}
1167 	}
1168 
1169 	if ( res->lm_msgtype == LDAP_RES_SEARCH_RESULT
1170 	    || res->lm_chain != NULLMSG )
1171 		print_ldap_result( ld, res, "search" );
1172 }
1173