xref: /netbsd-src/external/bsd/openldap/dist/clients/tools/common.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
1 /* common.c - common routines for the ldap client tools */
2 /* $OpenLDAP: pkg/ldap/clients/tools/common.c,v 1.78.2.8 2008/07/09 00:29:57 quanah Exp $ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2008 The OpenLDAP Foundation.
6  * Portions Copyright 2003 Kurt D. Zeilenga.
7  * Portions Copyright 2003 IBM Corporation.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  * This file was initially created by Hallvard B. Furuseth based (in
20  * part) upon argument parsing code for individual tools located in
21  * this directory.   Additional contributors include:
22  *   Kurt D. Zeilenga (additional common argument and control support)
23  */
24 
25 #include "portable.h"
26 
27 #include <stdio.h>
28 
29 #include <ac/stdlib.h>
30 #include <ac/signal.h>
31 #include <ac/string.h>
32 #include <ac/ctype.h>
33 #include <ac/unistd.h>
34 #include <ac/errno.h>
35 #include <ac/time.h>
36 #include <ac/socket.h>
37 
38 #ifdef HAVE_CYRUS_SASL
39 #ifdef HAVE_SASL_SASL_H
40 #include <sasl/sasl.h>
41 #else
42 #include <sasl.h>
43 #endif
44 #endif
45 
46 #include <ldap.h>
47 
48 #include "ldif.h"
49 #include "lutil.h"
50 #include "lutil_ldap.h"
51 #include "ldap_defaults.h"
52 #include "ldap_pvt.h"
53 #include "lber_pvt.h"
54 
55 #include "common.h"
56 
57 /* input-related vars */
58 
59 /* misc. parameters */
60 tool_type_t	tool_type;
61 int		contoper = 0;
62 int		debug = 0;
63 char		*infile = NULL;
64 int		dont = 0;
65 int		referrals = 0;
66 int		verbose = 0;
67 int		ldif = 0;
68 char		*prog = NULL;
69 
70 /* connection */
71 char		*ldapuri = NULL;
72 char		*ldaphost = NULL;
73 int  		ldapport = 0;
74 int		use_tls = 0;
75 int		protocol = -1;
76 int		version = 0;
77 
78 /* authc/authz */
79 int		authmethod = -1;
80 char		*binddn = NULL;
81 int		want_bindpw = 0;
82 struct berval	passwd = { 0, NULL };
83 char		*pw_file = NULL;
84 #ifdef HAVE_CYRUS_SASL
85 unsigned	sasl_flags = LDAP_SASL_AUTOMATIC;
86 char		*sasl_realm = NULL;
87 char		*sasl_authc_id = NULL;
88 char		*sasl_authz_id = NULL;
89 char		*sasl_mech = NULL;
90 char		*sasl_secprops = NULL;
91 #endif
92 
93 /* controls */
94 int		assertctl;
95 char		*assertion = NULL;
96 struct berval	assertionvalue = BER_BVNULL;
97 char		*authzid = NULL;
98 /* support deprecated early version of proxyAuthz */
99 #define LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ	"2.16.840.1.113730.3.4.12"
100 #ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
101 char		*proxydn = NULL;
102 #endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
103 int		manageDIT = 0;
104 int		manageDSAit = 0;
105 int		noop = 0;
106 int		ppolicy = 0;
107 int		preread = 0;
108 static char	*preread_attrs = NULL;
109 int		postread = 0;
110 static char	*postread_attrs = NULL;
111 ber_int_t	pr_morePagedResults = 1;
112 struct berval	pr_cookie = { 0, NULL };
113 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
114 int		chaining = 0;
115 static int	chainingResolve = -1;
116 static int	chainingContinuation = -1;
117 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
118 #ifdef LDAP_CONTROL_X_SESSION_TRACKING
119 static int	sessionTracking = 0;
120 struct berval	stValue;
121 #endif /* LDAP_CONTROL_X_SESSION_TRACKING */
122 
123 LDAPControl	*unknown_ctrls = NULL;
124 int		unknown_ctrls_num = 0;
125 
126 /* options */
127 struct timeval	nettimeout = { -1 , 0 };
128 
129 typedef int (*print_ctrl_fn)( LDAP *ld, LDAPControl *ctrl );
130 
131 static int print_preread( LDAP *ld, LDAPControl *ctrl );
132 static int print_postread( LDAP *ld, LDAPControl *ctrl );
133 static int print_paged_results( LDAP *ld, LDAPControl *ctrl );
134 #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
135 static int print_ppolicy( LDAP *ld, LDAPControl *ctrl );
136 #endif
137 
138 static struct tool_ctrls_t {
139 	const char	*oid;
140 	unsigned	mask;
141 	print_ctrl_fn	func;
142 } tool_ctrl_response[] = {
143 	{ LDAP_CONTROL_PRE_READ,			TOOL_ALL,	print_preread },
144 	{ LDAP_CONTROL_POST_READ,			TOOL_ALL,	print_postread },
145 	{ LDAP_CONTROL_PAGEDRESULTS,			TOOL_SEARCH,	print_paged_results },
146 #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
147 	{ LDAP_CONTROL_PASSWORDPOLICYRESPONSE,		TOOL_ALL,	print_ppolicy },
148 #endif
149 	{ NULL,						0,		NULL }
150 };
151 
152 /* "features" */
153 enum { Intr_None = 0, Intr_Abandon, Intr_Cancel, Intr_Ignore };
154 static volatile sig_atomic_t	gotintr, abcan;
155 
156 
157 #ifdef LDAP_CONTROL_X_SESSION_TRACKING
158 static int
159 st_value( LDAP *ld, struct berval *value )
160 {
161 	char		*ip = NULL, *name = NULL;
162 	struct berval	id = { 0 };
163 	char		namebuf[ MAXHOSTNAMELEN ];
164 
165 	if ( gethostname( namebuf, sizeof( namebuf ) ) == 0 ) {
166 		struct hostent	*h;
167 		struct in_addr	addr;
168 
169 		name = namebuf;
170 
171 		h = gethostbyname( name );
172 		if ( h != NULL ) {
173 			AC_MEMCPY( &addr, h->h_addr, sizeof( addr ) );
174 			ip = inet_ntoa( addr );
175 		}
176 	}
177 
178 #ifdef HAVE_CYRUS_SASL
179 	if ( sasl_authz_id != NULL ) {
180 		ber_str2bv( sasl_authz_id, 0, 0, &id );
181 
182 	} else if ( sasl_authc_id != NULL ) {
183 		ber_str2bv( sasl_authc_id, 0, 0, &id );
184 
185 	} else
186 #endif /* HAVE_CYRUS_SASL */
187 	if ( binddn != NULL ) {
188 		ber_str2bv( binddn, 0, 0, &id );
189 	}
190 
191 	if ( ldap_create_session_tracking_value( ld,
192 		ip, name, LDAP_CONTROL_X_SESSION_TRACKING_USERNAME,
193 		&id, &stValue ) )
194 	{
195 		fprintf( stderr, _("Session tracking control encoding error!\n") );
196 		return -1;
197 	}
198 
199 	return 0;
200 }
201 #endif /* LDAP_CONTROL_X_SESSION_TRACKING */
202 
203 RETSIGTYPE
204 do_sig( int sig )
205 {
206 	gotintr = abcan;
207 }
208 
209 void
210 tool_init( tool_type_t type )
211 {
212 	tool_type = type;
213 	ldap_pvt_setlocale(LC_MESSAGES, "");
214 	ldap_pvt_bindtextdomain(OPENLDAP_PACKAGE, LDAP_LOCALEDIR);
215 	ldap_pvt_textdomain(OPENLDAP_PACKAGE);
216 }
217 
218 void
219 tool_destroy( void )
220 {
221 #ifdef HAVE_CYRUS_SASL
222 	sasl_done();
223 #endif
224 #ifdef HAVE_TLS
225 	ldap_pvt_tls_destroy();
226 #endif
227 
228 	if ( ldapuri != NULL ) {
229 		ber_memfree( ldapuri );
230 		ldapuri = NULL;
231 	}
232 
233 	if ( pr_cookie.bv_val != NULL ) {
234 		ber_memfree( pr_cookie.bv_val );
235 		pr_cookie.bv_val = NULL;
236 		pr_cookie.bv_len = 0;
237 	}
238 }
239 
240 void
241 tool_common_usage( void )
242 {
243 	static const char *const descriptions[] = {
244 N_("  -c         continuous operation mode (do not stop on errors)\n"),
245 N_("  -d level   set LDAP debugging level to `level'\n"),
246 N_("  -D binddn  bind DN\n"),
247 N_("  -e [!]<ext>[=<extparam>] general extensions (! indicates criticality)\n")
248 N_("             [!]assert=<filter>     (a RFC 4515 Filter string)\n")
249 N_("             [!]authzid=<authzid>   (\"dn:<dn>\" or \"u:<user>\")\n")
250 #ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
251 #if 0
252                  /* non-advertized support for proxyDN */
253 N_("             [!]proxydn=<dn>        (a RFC 4514 DN string)\n")
254 #endif
255 #endif
256 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
257 N_("             [!]chaining[=<resolveBehavior>[/<continuationBehavior>]]\n")
258 N_("                     one of \"chainingPreferred\", \"chainingRequired\",\n")
259 N_("                     \"referralsPreferred\", \"referralsRequired\"\n")
260 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
261 N_("             [!]manageDSAit\n")
262 N_("             [!]noop\n")
263 #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
264 N_("             ppolicy\n")
265 #endif
266 N_("             [!]postread[=<attrs>]  (a comma-separated attribute list)\n")
267 N_("             [!]preread[=<attrs>]   (a comma-separated attribute list)\n")
268 N_("             [!]relax\n")
269 #ifdef LDAP_CONTROL_X_SESSION_TRACKING
270 N_("             [!]sessiontracking\n")
271 #endif /* LDAP_CONTROL_X_SESSION_TRACKING */
272 N_("             abandon, cancel, ignore (SIGINT sends abandon/cancel,\n"
273    "             or ignores response; if critical, doesn't wait for SIGINT.\n"
274    "             not really controls)\n")
275 N_("  -f file    read operations from `file'\n"),
276 N_("  -h host    LDAP server\n"),
277 N_("  -H URI     LDAP Uniform Resource Identifier(s)\n"),
278 N_("  -I         use SASL Interactive mode\n"),
279 N_("  -M         enable Manage DSA IT control (-MM to make critical)\n"),
280 N_("  -n         show what would be done but don't actually do it\n"),
281 N_("  -O props   SASL security properties\n"),
282 N_("  -o <opt>[=<optparam] general options\n"),
283 N_("             nettimeout=<timeout> (in seconds, or \"none\" or \"max\")\n"),
284 N_("  -p port    port on LDAP server\n"),
285 N_("  -P version protocol version (default: 3)\n"),
286 N_("  -Q         use SASL Quiet mode\n"),
287 N_("  -R realm   SASL realm\n"),
288 N_("  -U authcid SASL authentication identity\n"),
289 N_("  -v         run in verbose mode (diagnostics to standard output)\n"),
290 N_("  -V         print version info (-VV only)\n"),
291 N_("  -w passwd  bind password (for simple authentication)\n"),
292 N_("  -W         prompt for bind password\n"),
293 N_("  -x         Simple authentication\n"),
294 N_("  -X authzid SASL authorization identity (\"dn:<dn>\" or \"u:<user>\")\n"),
295 N_("  -y file    Read password from file\n"),
296 N_("  -Y mech    SASL mechanism\n"),
297 N_("  -Z         Start TLS request (-ZZ to require successful response)\n"),
298 NULL
299 	};
300 	const char *const *cpp;
301 
302 	fputs( _("Common options:\n"), stderr );
303 	for( cpp = descriptions; *cpp != NULL; cpp++ ) {
304 		if( strchr( options, (*cpp)[3] ) || (*cpp)[3] == ' ' ) {
305 			fputs( _(*cpp), stderr );
306 		}
307 	}
308 }
309 
310 void tool_perror(
311 	const char *func,
312 	int err,
313 	const char *extra,
314 	const char *matched,
315 	const char *info,
316 	char **refs )
317 {
318 	fprintf( stderr, "%s: %s (%d)%s\n",
319 		func, ldap_err2string( err ), err, extra ? extra : "" );
320 
321 	if ( matched && *matched ) {
322 		fprintf( stderr, _("\tmatched DN: %s\n"), matched );
323 	}
324 
325 	if ( info && *info ) {
326 		fprintf( stderr, _("\tadditional info: %s\n"), info );
327 	}
328 
329 	if ( refs && *refs ) {
330 		int i;
331 		fprintf( stderr, _("\treferrals:\n") );
332 		for( i=0; refs[i]; i++ ) {
333 			fprintf( stderr, "\t\t%s\n", refs[i] );
334 		}
335 	}
336 }
337 
338 
339 void
340 tool_args( int argc, char **argv )
341 {
342 	int i;
343 
344 	while (( i = getopt( argc, argv, options )) != EOF ) {
345 		int crit, ival;
346 		char *control, *cvalue, *next;
347 		switch( i ) {
348 		case 'c':	/* continuous operation mode */
349 			contoper++;
350 			break;
351 		case 'C':
352 			referrals++;
353 			break;
354 		case 'd':
355 			ival = strtol( optarg, &next, 10 );
356 			if (next == NULL || next[0] != '\0') {
357 				fprintf( stderr, "%s: unable to parse debug value \"%s\"\n", prog, optarg);
358 				exit(EXIT_FAILURE);
359 			}
360 			debug |= ival;
361 			break;
362 		case 'D':	/* bind DN */
363 			if( binddn != NULL ) {
364 				fprintf( stderr, "%s: -D previously specified\n", prog );
365 				exit( EXIT_FAILURE );
366 			}
367 			binddn = ber_strdup( optarg );
368 			break;
369 		case 'e':	/* general extensions (controls and such) */
370 			/* should be extended to support comma separated list of
371 			 *	[!]key[=value] parameters, e.g.  -e !foo,bar=567
372 			 */
373 
374 			crit = 0;
375 			cvalue = NULL;
376 			if( optarg[0] == '!' ) {
377 				crit = 1;
378 				optarg++;
379 			}
380 
381 			control = ber_strdup( optarg );
382 			if ( (cvalue = strchr( control, '=' )) != NULL ) {
383 				*cvalue++ = '\0';
384 			}
385 
386 			if ( strcasecmp( control, "assert" ) == 0 ) {
387 				if( assertctl ) {
388 					fprintf( stderr, "assert control previously specified\n");
389 					exit( EXIT_FAILURE );
390 				}
391 				if( cvalue == NULL ) {
392 					fprintf( stderr, "assert: control value expected\n" );
393 					usage();
394 				}
395 
396 				assertctl = 1 + crit;
397 
398 				assert( assertion == NULL );
399 				assertion = cvalue;
400 
401 			} else if ( strcasecmp( control, "authzid" ) == 0 ) {
402 				if( authzid != NULL ) {
403 					fprintf( stderr, "authzid control previously specified\n");
404 					exit( EXIT_FAILURE );
405 				}
406 #ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
407 				if( proxydn != NULL ) {
408 					fprintf( stderr, "authzid control incompatible with proxydn\n");
409 					exit( EXIT_FAILURE );
410 				}
411 #endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
412 				if( cvalue == NULL ) {
413 					fprintf( stderr, "authzid: control value expected\n" );
414 					usage();
415 				}
416 				if( !crit ) {
417 					fprintf( stderr, "authzid: must be marked critical\n" );
418 					usage();
419 				}
420 
421 				assert( authzid == NULL );
422 				authzid = cvalue;
423 
424 #ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
425 			} else if ( strcasecmp( control, "proxydn" ) == 0 ) {
426 				if( proxydn != NULL ) {
427 					fprintf( stderr, "proxydn control previously specified\n");
428 					exit( EXIT_FAILURE );
429 				}
430 				if( authzid != NULL ) {
431 					fprintf( stderr, "proxydn control incompatible with authzid\n");
432 					exit( EXIT_FAILURE );
433 				}
434 				if( cvalue == NULL ) {
435 					fprintf( stderr, "proxydn: control value expected\n" );
436 					usage();
437 				}
438 				if( !crit ) {
439 					fprintf( stderr, "proxydn: must be marked critical\n" );
440 					usage();
441 				}
442 
443 				assert( proxydn == NULL );
444 				proxydn = cvalue;
445 #endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
446 
447 			} else if ( ( strcasecmp( control, "relax" ) == 0 ) ||
448 				( strcasecmp( control, "manageDIT" ) == 0 ) )
449 			{
450 				if( manageDIT ) {
451 					fprintf( stderr,
452 						"relax control previously specified\n");
453 					exit( EXIT_FAILURE );
454 				}
455 				if( cvalue != NULL ) {
456 					fprintf( stderr,
457 						"relax: no control value expected\n" );
458 					usage();
459 				}
460 
461 				manageDIT = 1 + crit;
462 
463 			} else if ( strcasecmp( control, "manageDSAit" ) == 0 ) {
464 				if( manageDSAit ) {
465 					fprintf( stderr,
466 						"manageDSAit control previously specified\n");
467 					exit( EXIT_FAILURE );
468 				}
469 				if( cvalue != NULL ) {
470 					fprintf( stderr,
471 						"manageDSAit: no control value expected\n" );
472 					usage();
473 				}
474 
475 				manageDSAit = 1 + crit;
476 
477 			} else if ( strcasecmp( control, "noop" ) == 0 ) {
478 				if( noop ) {
479 					fprintf( stderr, "noop control previously specified\n");
480 					exit( EXIT_FAILURE );
481 				}
482 				if( cvalue != NULL ) {
483 					fprintf( stderr, "noop: no control value expected\n" );
484 					usage();
485 				}
486 
487 				noop = 1 + crit;
488 
489 #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
490 			} else if ( strcasecmp( control, "ppolicy" ) == 0 ) {
491 				if( ppolicy ) {
492 					fprintf( stderr, "ppolicy control previously specified\n");
493 					exit( EXIT_FAILURE );
494 				}
495 				if( cvalue != NULL ) {
496 					fprintf( stderr, "ppolicy: no control value expected\n" );
497 					usage();
498 				}
499 				if( crit ) {
500 					fprintf( stderr, "ppolicy: critical flag not allowed\n" );
501 					usage();
502 				}
503 
504 				ppolicy = 1;
505 #endif
506 
507 			} else if ( strcasecmp( control, "preread" ) == 0 ) {
508 				if( preread ) {
509 					fprintf( stderr, "preread control previously specified\n");
510 					exit( EXIT_FAILURE );
511 				}
512 
513 				preread = 1 + crit;
514 				preread_attrs = cvalue;
515 
516 			} else if ( strcasecmp( control, "postread" ) == 0 ) {
517 				if( postread ) {
518 					fprintf( stderr, "postread control previously specified\n");
519 					exit( EXIT_FAILURE );
520 				}
521 
522 				postread = 1 + crit;
523 				postread_attrs = cvalue;
524 
525 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
526 			} else if ( strcasecmp( control, "chaining" ) == 0 ) {
527 				chaining = 1 + crit;
528 
529 				if ( cvalue != NULL ) {
530 					char	*continuation;
531 
532 					continuation = strchr( cvalue, '/' );
533 					if ( continuation ) {
534 						/* FIXME: this makes sense only in searches */
535 						*continuation++ = '\0';
536 						if ( strcasecmp( continuation, "chainingPreferred" ) == 0 ) {
537 							chainingContinuation = LDAP_CHAINING_PREFERRED;
538 						} else if ( strcasecmp( continuation, "chainingRequired" ) == 0 ) {
539 							chainingContinuation = LDAP_CHAINING_REQUIRED;
540 						} else if ( strcasecmp( continuation, "referralsPreferred" ) == 0 ) {
541 							chainingContinuation = LDAP_REFERRALS_PREFERRED;
542 						} else if ( strcasecmp( continuation, "referralsRequired" ) == 0 ) {
543 							chainingContinuation = LDAP_REFERRALS_REQUIRED;
544 						} else {
545 							fprintf( stderr,
546 								"chaining behavior control "
547 								"continuation value \"%s\" invalid\n",
548 								continuation );
549 							exit( EXIT_FAILURE );
550 						}
551 					}
552 
553 					if ( strcasecmp( cvalue, "chainingPreferred" ) == 0 ) {
554 						chainingResolve = LDAP_CHAINING_PREFERRED;
555 					} else if ( strcasecmp( cvalue, "chainingRequired" ) == 0 ) {
556 						chainingResolve = LDAP_CHAINING_REQUIRED;
557 					} else if ( strcasecmp( cvalue, "referralsPreferred" ) == 0 ) {
558 						chainingResolve = LDAP_REFERRALS_PREFERRED;
559 					} else if ( strcasecmp( cvalue, "referralsRequired" ) == 0 ) {
560 						chainingResolve = LDAP_REFERRALS_REQUIRED;
561 					} else {
562 						fprintf( stderr,
563 							"chaining behavior control "
564 							"resolve value \"%s\" invalid\n",
565 							cvalue);
566 						exit( EXIT_FAILURE );
567 					}
568 				}
569 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
570 
571 			/* this shouldn't go here, really; but it's a feature... */
572 			} else if ( strcasecmp( control, "abandon" ) == 0 ) {
573 				abcan = Intr_Abandon;
574 				if ( crit ) {
575 					gotintr = abcan;
576 				}
577 
578 			} else if ( strcasecmp( control, "cancel" ) == 0 ) {
579 				abcan = Intr_Cancel;
580 				if ( crit ) {
581 					gotintr = abcan;
582 				}
583 
584 			} else if ( strcasecmp( control, "ignore" ) == 0 ) {
585 				abcan = Intr_Ignore;
586 				if ( crit ) {
587 					gotintr = abcan;
588 				}
589 
590 #ifdef LDAP_CONTROL_X_SESSION_TRACKING
591 			} else if ( strcasecmp( control, "sessiontracking" ) == 0 ) {
592 				if ( sessionTracking ) {
593 					fprintf( stderr, "%s: session tracking can be only specified once\n", prog );
594 					exit( EXIT_FAILURE );
595 				}
596 				sessionTracking = 1;
597 #endif /* LDAP_CONTROL_X_SESSION_TRACKING */
598 
599 			} else if ( tool_is_oid( control ) ) {
600 				LDAPControl	*tmpctrls, ctrl;
601 
602 				tmpctrls = (LDAPControl *)realloc( unknown_ctrls,
603 					(unknown_ctrls_num + 1)*sizeof( LDAPControl ) );
604 				if ( tmpctrls == NULL ) {
605 					fprintf( stderr, "%s: no memory?\n", prog );
606 					exit( EXIT_FAILURE );
607 				}
608 				unknown_ctrls = tmpctrls;
609 				ctrl.ldctl_oid = control;
610 				ctrl.ldctl_value.bv_val = NULL;
611 				ctrl.ldctl_value.bv_len = 0;
612 				ctrl.ldctl_iscritical = crit;
613 
614 				if ( cvalue != NULL ) {
615 					struct berval	bv;
616 					size_t		len = strlen( cvalue );
617 					int		retcode;
618 
619 					bv.bv_len = LUTIL_BASE64_DECODE_LEN( len );
620 					bv.bv_val = ber_memalloc( bv.bv_len + 1 );
621 
622 					retcode = lutil_b64_pton( cvalue,
623 						(unsigned char *)bv.bv_val,
624 						bv.bv_len );
625 
626 					if ( retcode == -1 || retcode > bv.bv_len ) {
627 						fprintf( stderr, "Unable to parse value of general control %s\n",
628 							control );
629 						usage();
630 					}
631 
632 					bv.bv_len = retcode;
633 					ctrl.ldctl_value = bv;
634 				}
635 
636 				unknown_ctrls[ unknown_ctrls_num ] = ctrl;
637 				unknown_ctrls_num++;
638 
639 			} else {
640 				fprintf( stderr, "Invalid general control name: %s\n",
641 					control );
642 				usage();
643 			}
644 			break;
645 		case 'f':	/* read from file */
646 			if( infile != NULL ) {
647 				fprintf( stderr, "%s: -f previously specified\n", prog );
648 				exit( EXIT_FAILURE );
649 			}
650 			infile = ber_strdup( optarg );
651 			break;
652 		case 'h':	/* ldap host */
653 			if( ldaphost != NULL ) {
654 				fprintf( stderr, "%s: -h previously specified\n", prog );
655 				exit( EXIT_FAILURE );
656 			}
657 			ldaphost = ber_strdup( optarg );
658 			break;
659 		case 'H':	/* ldap URI */
660 			if( ldapuri != NULL ) {
661 				fprintf( stderr, "%s: -H previously specified\n", prog );
662 				exit( EXIT_FAILURE );
663 			}
664 			ldapuri = ber_strdup( optarg );
665 			break;
666 		case 'I':
667 #ifdef HAVE_CYRUS_SASL
668 			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
669 				fprintf( stderr, "%s: incompatible previous "
670 					"authentication choice\n",
671 					prog );
672 				exit( EXIT_FAILURE );
673 			}
674 			authmethod = LDAP_AUTH_SASL;
675 			sasl_flags = LDAP_SASL_INTERACTIVE;
676 			break;
677 #else
678 			fprintf( stderr, "%s: was not compiled with SASL support\n",
679 				prog );
680 			exit( EXIT_FAILURE );
681 #endif
682 		case 'M':
683 			/* enable Manage DSA IT */
684 			manageDSAit++;
685 			break;
686 		case 'n':	/* print operations, don't actually do them */
687 			dont++;
688 			break;
689 		case 'o':
690 			control = ber_strdup( optarg );
691 			if ( (cvalue = strchr( control, '=' )) != NULL ) {
692 				*cvalue++ = '\0';
693 			}
694 
695 			if ( strcasecmp( control, "nettimeout" ) == 0 ) {
696 				if( nettimeout.tv_sec != -1 ) {
697 					fprintf( stderr, "nettimeout option previously specified\n");
698 					exit( EXIT_FAILURE );
699 				}
700 				if( cvalue == NULL || cvalue[0] == '\0' ) {
701 					fprintf( stderr, "nettimeout: option value expected\n" );
702 					usage();
703 				}
704 		 		if ( strcasecmp( cvalue, "none" ) == 0 ) {
705 		 			nettimeout.tv_sec = 0;
706 		 		} else if ( strcasecmp( cvalue, "max" ) == 0 ) {
707 		 			nettimeout.tv_sec = LDAP_MAXINT;
708 		 		} else {
709 		 			ival = strtol( cvalue, &next, 10 );
710 		 			if ( next == NULL || next[0] != '\0' ) {
711 		 				fprintf( stderr,
712 		 					_("Unable to parse network timeout \"%s\"\n"), cvalue );
713 		 				exit( EXIT_FAILURE );
714 		 			}
715 		 			nettimeout.tv_sec = ival;
716 		 		}
717 		 		if( nettimeout.tv_sec < 0 || nettimeout.tv_sec > LDAP_MAXINT ) {
718 		 			fprintf( stderr, _("%s: invalid network timeout (%ld) specified\n"),
719 		 				prog, (long)nettimeout.tv_sec );
720 	 				exit( EXIT_FAILURE );
721  				}
722 			} else {
723 				fprintf( stderr, "Invalid general option name: %s\n",
724 					control );
725 				usage();
726 			}
727 			break;
728 		case 'O':
729 #ifdef HAVE_CYRUS_SASL
730 			if( sasl_secprops != NULL ) {
731 				fprintf( stderr, "%s: -O previously specified\n", prog );
732 				exit( EXIT_FAILURE );
733 			}
734 			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
735 				fprintf( stderr, "%s: incompatible previous "
736 					"authentication choice\n", prog );
737 				exit( EXIT_FAILURE );
738 			}
739 			authmethod = LDAP_AUTH_SASL;
740 			sasl_secprops = ber_strdup( optarg );
741 #else
742 			fprintf( stderr, "%s: not compiled with SASL support\n", prog );
743 			exit( EXIT_FAILURE );
744 #endif
745 			break;
746 		case 'p':
747 			if( ldapport ) {
748 				fprintf( stderr, "%s: -p previously specified\n", prog );
749 				exit( EXIT_FAILURE );
750 			}
751 			ival = strtol( optarg, &next, 10 );
752 			if ( next == NULL || next[0] != '\0' ) {
753 				fprintf( stderr, "%s: unable to parse port number \"%s\"\n", prog, optarg );
754 				exit( EXIT_FAILURE );
755 			}
756 			ldapport = ival;
757 			break;
758 		case 'P':
759 			ival = strtol( optarg, &next, 10 );
760 			if ( next == NULL || next[0] != '\0' ) {
761 				fprintf( stderr, "%s: unable to parse protocol version \"%s\"\n", prog, optarg );
762 				exit( EXIT_FAILURE );
763 			}
764 			switch( ival ) {
765 			case 2:
766 				if( protocol == LDAP_VERSION3 ) {
767 					fprintf( stderr, "%s: -P 2 incompatible with version %d\n",
768 						prog, protocol );
769 					exit( EXIT_FAILURE );
770 				}
771 				protocol = LDAP_VERSION2;
772 				break;
773 			case 3:
774 				if( protocol == LDAP_VERSION2 ) {
775 					fprintf( stderr, "%s: -P 2 incompatible with version %d\n",
776 						prog, protocol );
777 					exit( EXIT_FAILURE );
778 				}
779 				protocol = LDAP_VERSION3;
780 				break;
781 			default:
782 				fprintf( stderr, "%s: protocol version should be 2 or 3\n",
783 					prog );
784 				usage();
785 			}
786 			break;
787 		case 'Q':
788 #ifdef HAVE_CYRUS_SASL
789 			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
790 				fprintf( stderr, "%s: incompatible previous "
791 					"authentication choice\n",
792 					prog );
793 				exit( EXIT_FAILURE );
794 			}
795 			authmethod = LDAP_AUTH_SASL;
796 			sasl_flags = LDAP_SASL_QUIET;
797 			break;
798 #else
799 			fprintf( stderr, "%s: not compiled with SASL support\n",
800 				prog );
801 			exit( EXIT_FAILURE );
802 #endif
803 		case 'R':
804 #ifdef HAVE_CYRUS_SASL
805 			if( sasl_realm != NULL ) {
806 				fprintf( stderr, "%s: -R previously specified\n", prog );
807 				exit( EXIT_FAILURE );
808 			}
809 			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
810 				fprintf( stderr, "%s: incompatible previous "
811 					"authentication choice\n",
812 					prog );
813 				exit( EXIT_FAILURE );
814 			}
815 			authmethod = LDAP_AUTH_SASL;
816 			sasl_realm = ber_strdup( optarg );
817 #else
818 			fprintf( stderr, "%s: not compiled with SASL support\n",
819 				prog );
820 			exit( EXIT_FAILURE );
821 #endif
822 			break;
823 		case 'U':
824 #ifdef HAVE_CYRUS_SASL
825 			if( sasl_authc_id != NULL ) {
826 				fprintf( stderr, "%s: -U previously specified\n", prog );
827 				exit( EXIT_FAILURE );
828 			}
829 			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
830 				fprintf( stderr, "%s: incompatible previous "
831 					"authentication choice\n",
832 					prog );
833 				exit( EXIT_FAILURE );
834 			}
835 			authmethod = LDAP_AUTH_SASL;
836 			sasl_authc_id = ber_strdup( optarg );
837 #else
838 			fprintf( stderr, "%s: not compiled with SASL support\n",
839 				prog );
840 			exit( EXIT_FAILURE );
841 #endif
842 			break;
843 		case 'v':	/* verbose mode */
844 			verbose++;
845 			break;
846 		case 'V':	/* version */
847 			version++;
848 			break;
849 		case 'w':	/* password */
850 			passwd.bv_val = ber_strdup( optarg );
851 			{
852 				char* p;
853 
854 				for( p = optarg; *p != '\0'; p++ ) {
855 					*p = '\0';
856 				}
857 			}
858 			passwd.bv_len = strlen( passwd.bv_val );
859 			break;
860 		case 'W':
861 			want_bindpw++;
862 			break;
863 		case 'y':
864 			pw_file = optarg;
865 			break;
866 		case 'Y':
867 #ifdef HAVE_CYRUS_SASL
868 			if( sasl_mech != NULL ) {
869 				fprintf( stderr, "%s: -Y previously specified\n", prog );
870 				exit( EXIT_FAILURE );
871 			}
872 			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
873 				fprintf( stderr,
874 					"%s: incompatible with authentication choice\n", prog );
875 				exit( EXIT_FAILURE );
876 			}
877 			authmethod = LDAP_AUTH_SASL;
878 			sasl_mech = ber_strdup( optarg );
879 #else
880 			fprintf( stderr, "%s: not compiled with SASL support\n", prog );
881 			exit( EXIT_FAILURE );
882 #endif
883 			break;
884 		case 'x':
885 			if( authmethod != -1 && authmethod != LDAP_AUTH_SIMPLE ) {
886 				fprintf( stderr, "%s: incompatible with previous "
887 					"authentication choice\n", prog );
888 				exit( EXIT_FAILURE );
889 			}
890 			authmethod = LDAP_AUTH_SIMPLE;
891 			break;
892 		case 'X':
893 #ifdef HAVE_CYRUS_SASL
894 			if( sasl_authz_id != NULL ) {
895 				fprintf( stderr, "%s: -X previously specified\n", prog );
896 				exit( EXIT_FAILURE );
897 			}
898 			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
899 				fprintf( stderr, "%s: -X incompatible with "
900 					"authentication choice\n", prog );
901 				exit( EXIT_FAILURE );
902 			}
903 			authmethod = LDAP_AUTH_SASL;
904 			sasl_authz_id = ber_strdup( optarg );
905 #else
906 			fprintf( stderr, "%s: not compiled with SASL support\n", prog );
907 			exit( EXIT_FAILURE );
908 #endif
909 			break;
910 		case 'Z':
911 #ifdef HAVE_TLS
912 			use_tls++;
913 #else
914 			fprintf( stderr, "%s: not compiled with TLS support\n", prog );
915 			exit( EXIT_FAILURE );
916 #endif
917 			break;
918 		default:
919 			if( handle_private_option( i ) ) break;
920 			fprintf( stderr, "%s: unrecognized option -%c\n",
921 				prog, optopt );
922 			usage();
923 		}
924 	}
925 
926 	{
927 		/* prevent bad linking */
928 		LDAPAPIInfo api;
929 		api.ldapai_info_version = LDAP_API_INFO_VERSION;
930 
931 		if ( ldap_get_option(NULL, LDAP_OPT_API_INFO, &api)
932 			!= LDAP_OPT_SUCCESS )
933 		{
934 			fprintf( stderr, "%s: ldap_get_option(API_INFO) failed\n", prog );
935 			exit( EXIT_FAILURE );
936 		}
937 
938 		if (api.ldapai_info_version != LDAP_API_INFO_VERSION) {
939 			fprintf( stderr, "LDAP APIInfo version mismatch: "
940 				"library %d, header %d\n",
941 				api.ldapai_info_version, LDAP_API_INFO_VERSION );
942 			exit( EXIT_FAILURE );
943 		}
944 
945 		if( api.ldapai_api_version != LDAP_API_VERSION ) {
946 			fprintf( stderr, "LDAP API version mismatch: "
947 				"library %d, header %d\n",
948 				api.ldapai_api_version, LDAP_API_VERSION );
949 			exit( EXIT_FAILURE );
950 		}
951 
952 		if( strcmp(api.ldapai_vendor_name, LDAP_VENDOR_NAME ) != 0 ) {
953 			fprintf( stderr, "LDAP vendor name mismatch: "
954 				"library %s, header %s\n",
955 				api.ldapai_vendor_name, LDAP_VENDOR_NAME );
956 			exit( EXIT_FAILURE );
957 		}
958 
959 		if( api.ldapai_vendor_version != LDAP_VENDOR_VERSION ) {
960 			fprintf( stderr, "LDAP vendor version mismatch: "
961 				"library %d, header %d\n",
962 				api.ldapai_vendor_version, LDAP_VENDOR_VERSION );
963 			exit( EXIT_FAILURE );
964 		}
965 
966 		if (version) {
967 			fprintf( stderr, "%s: %s\t(LDAP library: %s %d)\n",
968 				prog, __Version,
969 				LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION );
970 			if (version > 1) exit( EXIT_SUCCESS );
971 		}
972 
973 		ldap_memfree( api.ldapai_vendor_name );
974 		ber_memvfree( (void **)api.ldapai_extensions );
975 	}
976 
977 	if (protocol == -1)
978 		protocol = LDAP_VERSION3;
979 
980 	if (authmethod == -1 && protocol > LDAP_VERSION2) {
981 #ifdef HAVE_CYRUS_SASL
982 		authmethod = LDAP_AUTH_SASL;
983 #else
984 		authmethod = LDAP_AUTH_SIMPLE;
985 #endif
986 	}
987 
988 	if( ldapuri == NULL ) {
989 		if( ldapport && ( ldaphost == NULL )) {
990 			fprintf( stderr, "%s: -p without -h is invalid.\n", prog );
991 			exit( EXIT_FAILURE );
992 		}
993 	} else {
994 		if( ldaphost != NULL ) {
995 			fprintf( stderr, "%s: -H incompatible with -h\n", prog );
996 			exit( EXIT_FAILURE );
997 		}
998 		if( ldapport ) {
999 			fprintf( stderr, "%s: -H incompatible with -p\n", prog );
1000 			exit( EXIT_FAILURE );
1001 		}
1002 	}
1003 
1004 	if( protocol == LDAP_VERSION2 ) {
1005 		if( assertctl || authzid || manageDIT || manageDSAit ||
1006 #ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
1007 			proxydn ||
1008 #endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
1009 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1010 			chaining ||
1011 #endif
1012 #ifdef LDAP_CONTROL_X_SESSION_TRACKING
1013 			sessionTracking ||
1014 #endif /* LDAP_CONTROL_X_SESSION_TRACKING */
1015 			noop || ppolicy || preread || postread )
1016 		{
1017 			fprintf( stderr, "%s: -e/-M incompatible with LDAPv2\n", prog );
1018 			exit( EXIT_FAILURE );
1019 		}
1020 #ifdef HAVE_TLS
1021 		if( use_tls ) {
1022 			fprintf( stderr, "%s: -Z incompatible with LDAPv2\n", prog );
1023 			exit( EXIT_FAILURE );
1024 		}
1025 #endif
1026 #ifdef HAVE_CYRUS_SASL
1027 		if( authmethod == LDAP_AUTH_SASL ) {
1028 			fprintf( stderr, "%s: -[IOQRUXY] incompatible with LDAPv2\n",
1029 				prog );
1030 			exit( EXIT_FAILURE );
1031 		}
1032 #endif
1033 	}
1034 }
1035 
1036 
1037 LDAP *
1038 tool_conn_setup( int dont, void (*private_setup)( LDAP * ) )
1039 {
1040 	LDAP *ld = NULL;
1041 
1042 	if ( debug ) {
1043 		if( ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug )
1044 			!= LBER_OPT_SUCCESS )
1045 		{
1046 			fprintf( stderr,
1047 				"Could not set LBER_OPT_DEBUG_LEVEL %d\n", debug );
1048 		}
1049 		if( ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug )
1050 			!= LDAP_OPT_SUCCESS )
1051 		{
1052 			fprintf( stderr,
1053 				"Could not set LDAP_OPT_DEBUG_LEVEL %d\n", debug );
1054 		}
1055 	}
1056 
1057 #ifdef SIGPIPE
1058 	(void) SIGNAL( SIGPIPE, SIG_IGN );
1059 #endif
1060 
1061 	if ( abcan ) {
1062 		SIGNAL( SIGINT, do_sig );
1063 	}
1064 
1065 	if ( !dont ) {
1066 		int rc;
1067 
1068 		if( ( ldaphost != NULL || ldapport ) && ( ldapuri == NULL ) ) {
1069 			/* construct URL */
1070 			LDAPURLDesc url;
1071 			memset( &url, 0, sizeof(url));
1072 
1073 			url.lud_scheme = "ldap";
1074 			url.lud_host = ldaphost;
1075 			url.lud_port = ldapport;
1076 			url.lud_scope = LDAP_SCOPE_DEFAULT;
1077 
1078 			ldapuri = ldap_url_desc2str( &url );
1079 
1080 		} else if ( ldapuri != NULL ) {
1081 			LDAPURLDesc	*ludlist, **ludp;
1082 			char		**urls = NULL;
1083 			int		nurls = 0;
1084 
1085 			rc = ldap_url_parselist( &ludlist, ldapuri );
1086 			if ( rc != LDAP_URL_SUCCESS ) {
1087 				fprintf( stderr,
1088 					"Could not parse LDAP URI(s)=%s (%d)\n",
1089 					ldapuri, rc );
1090 				exit( EXIT_FAILURE );
1091 			}
1092 
1093 			for ( ludp = &ludlist; *ludp != NULL; ) {
1094 				LDAPURLDesc	*lud = *ludp;
1095 				char		**tmp;
1096 
1097 				if ( lud->lud_dn != NULL && lud->lud_dn[ 0 ] != '\0' &&
1098 					( lud->lud_host == NULL || lud->lud_host[0] == '\0' ) )
1099 				{
1100 					/* if no host but a DN is provided,
1101 					 * use DNS SRV to gather the host list
1102 					 * and turn it into a list of URIs
1103 					 * using the scheme provided */
1104 					char	*domain = NULL,
1105 						*hostlist = NULL,
1106 						**hosts = NULL;
1107 					int	i,
1108 						len_proto = strlen( lud->lud_scheme );
1109 
1110 					if ( ldap_dn2domain( lud->lud_dn, &domain )
1111 						|| domain == NULL )
1112 					{
1113 						fprintf( stderr,
1114 							"DNS SRV: Could not turn "
1115 							"DN=\"%s\" into a domain\n",
1116 							lud->lud_dn );
1117 						goto dnssrv_free;
1118 					}
1119 
1120 					rc = ldap_domain2hostlist( domain, &hostlist );
1121 					if ( rc ) {
1122 						fprintf( stderr,
1123 							"DNS SRV: Could not turn "
1124 							"domain=%s into a hostlist\n",
1125 							domain );
1126 						goto dnssrv_free;
1127 					}
1128 
1129 					hosts = ldap_str2charray( hostlist, " " );
1130 					if ( hosts == NULL ) {
1131 						fprintf( stderr,
1132 							"DNS SRV: Could not parse "
1133 							"hostlist=\"%s\"\n",
1134 							hostlist );
1135 						goto dnssrv_free;
1136 					}
1137 
1138 					for ( i = 0; hosts[ i ] != NULL; i++ )
1139 						/* count'em */ ;
1140 
1141 					tmp = (char **)realloc( urls, sizeof( char * ) * ( nurls + i + 1 ) );
1142 					if ( tmp == NULL ) {
1143 						fprintf( stderr,
1144 							"DNS SRV: out of memory?\n" );
1145 						goto dnssrv_free;
1146 					}
1147 					urls = tmp;
1148 					urls[ nurls ] = NULL;
1149 
1150 					for ( i = 0; hosts[ i ] != NULL; i++ ) {
1151 						size_t	len = len_proto
1152 							+ STRLENOF( "://" )
1153 							+ strlen( hosts[ i ] )
1154 							+ 1;
1155 
1156 						urls[ nurls + i + 1 ] = NULL;
1157 						urls[ nurls + i ] = (char *)malloc( sizeof( char ) * len );
1158 						if ( urls[ nurls + i ] == NULL ) {
1159 							fprintf( stderr,
1160 								"DNS SRV: out of memory?\n" );
1161 							goto dnssrv_free;
1162 						}
1163 
1164 						snprintf( urls[ nurls + i ], len, "%s://%s",
1165 							lud->lud_scheme, hosts[ i ] );
1166 					}
1167 					nurls += i;
1168 
1169 dnssrv_free:;
1170 					ber_memvfree( (void **)hosts );
1171 					ber_memfree( hostlist );
1172 					ber_memfree( domain );
1173 
1174 				} else {
1175 					tmp = (char **)realloc( urls, sizeof( char * ) * ( nurls + 2 ) );
1176 					if ( tmp == NULL ) {
1177 						fprintf( stderr,
1178 							"DNS SRV: out of memory?\n" );
1179 						break;
1180 					}
1181 					urls = tmp;
1182 					urls[ nurls + 1 ] = NULL;
1183 
1184 					urls[ nurls ] = ldap_url_desc2str( lud );
1185 					if ( urls[ nurls ] == NULL ) {
1186 						fprintf( stderr,
1187 							"DNS SRV: out of memory?\n" );
1188 						break;
1189 					}
1190 					nurls++;
1191 				}
1192 
1193 				*ludp = lud->lud_next;
1194 
1195 				lud->lud_next = NULL;
1196 				ldap_free_urldesc( lud );
1197 			}
1198 
1199 			if ( ludlist != NULL ) {
1200 				ldap_free_urllist( ludlist );
1201 				exit( EXIT_FAILURE );
1202 
1203 			} else if ( urls == NULL ) {
1204 				exit( EXIT_FAILURE );
1205 			}
1206 
1207 			ldap_memfree( ldapuri );
1208 			ldapuri = ldap_charray2str( urls, " " );
1209 			ber_memvfree( (void **)urls );
1210 		}
1211 
1212 		if ( verbose ) {
1213 			fprintf( stderr, "ldap_initialize( %s )\n",
1214 				ldapuri != NULL ? ldapuri : "<DEFAULT>" );
1215 		}
1216 		rc = ldap_initialize( &ld, ldapuri );
1217 		if( rc != LDAP_SUCCESS ) {
1218 			fprintf( stderr,
1219 				"Could not create LDAP session handle for URI=%s (%d): %s\n",
1220 				ldapuri, rc, ldap_err2string(rc) );
1221 			exit( EXIT_FAILURE );
1222 		}
1223 
1224 		if( private_setup ) private_setup( ld );
1225 
1226 		/* referrals */
1227 		if( ldap_set_option( ld, LDAP_OPT_REFERRALS,
1228 			referrals ? LDAP_OPT_ON : LDAP_OPT_OFF ) != LDAP_OPT_SUCCESS )
1229 		{
1230 			fprintf( stderr, "Could not set LDAP_OPT_REFERRALS %s\n",
1231 				referrals ? "on" : "off" );
1232 			exit( EXIT_FAILURE );
1233 		}
1234 
1235 		if( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &protocol )
1236 			!= LDAP_OPT_SUCCESS )
1237 		{
1238 			fprintf( stderr, "Could not set LDAP_OPT_PROTOCOL_VERSION %d\n",
1239 				protocol );
1240 			exit( EXIT_FAILURE );
1241 		}
1242 
1243 		if ( use_tls ) {
1244 			rc = ldap_start_tls_s( ld, NULL, NULL );
1245 			if ( rc != LDAP_SUCCESS ) {
1246 				tool_perror( "ldap_start_tls", rc, NULL, NULL, NULL, NULL );
1247 				if ( use_tls > 1 ) {
1248 					exit( EXIT_FAILURE );
1249 				}
1250 			}
1251 		}
1252 
1253 		if ( nettimeout.tv_sec > 0 ) {
1254 	 		if ( ldap_set_option( ld, LDAP_OPT_NETWORK_TIMEOUT, (void *) &nettimeout )
1255 				!= LDAP_OPT_SUCCESS )
1256 			{
1257 		 		fprintf( stderr, "Could not set LDAP_OPT_NETWORK_TIMEOUT %ld\n",
1258 					(long)nettimeout.tv_sec );
1259 	 			exit( EXIT_FAILURE );
1260 			}
1261 		}
1262 	}
1263 
1264 	return ld;
1265 }
1266 
1267 
1268 void
1269 tool_bind( LDAP *ld )
1270 {
1271 	LDAPControl	**sctrlsp = NULL;
1272 	LDAPControl	*sctrls[3];
1273 	LDAPControl	sctrl[3];
1274 	int		nsctrls = 0;
1275 
1276 #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
1277 	if ( ppolicy ) {
1278 		LDAPControl c;
1279 		c.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST;
1280 		c.ldctl_value.bv_val = NULL;
1281 		c.ldctl_value.bv_len = 0;
1282 		c.ldctl_iscritical = 0;
1283 		sctrl[nsctrls] = c;
1284 		sctrls[nsctrls] = &sctrl[nsctrls];
1285 		sctrls[++nsctrls] = NULL;
1286 	}
1287 #endif
1288 
1289 #ifdef LDAP_CONTROL_X_SESSION_TRACKING
1290 	if ( sessionTracking ) {
1291 		LDAPControl c;
1292 
1293 		if (stValue.bv_val == NULL && st_value( ld, &stValue ) ) {
1294 			exit( EXIT_FAILURE );
1295 		}
1296 
1297 		c.ldctl_oid = LDAP_CONTROL_X_SESSION_TRACKING;
1298 		c.ldctl_iscritical = 0;
1299 		ber_dupbv( &c.ldctl_value, &stValue );
1300 
1301 		sctrl[nsctrls] = c;
1302 		sctrls[nsctrls] = &sctrl[nsctrls];
1303 		sctrls[++nsctrls] = NULL;
1304 	}
1305 #endif /* LDAP_CONTROL_X_SESSION_TRACKING */
1306 
1307 	if ( nsctrls ) {
1308 		sctrlsp = sctrls;
1309 	}
1310 
1311 	assert( nsctrls < sizeof(sctrls)/sizeof(sctrls[0]) );
1312 
1313 	if ( authmethod == LDAP_AUTH_SASL ) {
1314 #ifdef HAVE_CYRUS_SASL
1315 		void *defaults;
1316 		int rc;
1317 
1318 		if( sasl_secprops != NULL ) {
1319 			rc = ldap_set_option( ld, LDAP_OPT_X_SASL_SECPROPS,
1320 				(void *) sasl_secprops );
1321 
1322 			if( rc != LDAP_OPT_SUCCESS ) {
1323 				fprintf( stderr,
1324 					"Could not set LDAP_OPT_X_SASL_SECPROPS: %s\n",
1325 					sasl_secprops );
1326 				exit( LDAP_LOCAL_ERROR );
1327 			}
1328 		}
1329 
1330 		defaults = lutil_sasl_defaults( ld,
1331 			sasl_mech,
1332 			sasl_realm,
1333 			sasl_authc_id,
1334 			passwd.bv_val,
1335 			sasl_authz_id );
1336 
1337 		rc = ldap_sasl_interactive_bind_s( ld, binddn, sasl_mech,
1338 			sctrlsp,
1339 			NULL, sasl_flags, lutil_sasl_interact, defaults );
1340 
1341 		lutil_sasl_freedefs( defaults );
1342 		if( rc != LDAP_SUCCESS ) {
1343 			tool_perror( "ldap_sasl_interactive_bind_s",
1344 				rc, NULL, NULL, NULL, NULL );
1345 			exit( rc );
1346 		}
1347 #else
1348 		fprintf( stderr, "%s: not compiled with SASL support\n", prog );
1349 		exit( LDAP_NOT_SUPPORTED );
1350 #endif
1351 	} else {
1352 		int msgid, err, rc;
1353 		LDAPMessage *result;
1354 		LDAPControl **ctrls;
1355 		char msgbuf[256];
1356 		char *matched = NULL;
1357 		char *info = NULL;
1358 		char **refs = NULL;
1359 
1360 		msgbuf[0] = 0;
1361 
1362 		{
1363 			/* simple bind */
1364 			rc = ldap_sasl_bind( ld, binddn, LDAP_SASL_SIMPLE, &passwd,
1365 				sctrlsp, NULL, &msgid );
1366 			if ( msgid == -1 ) {
1367 				tool_perror( "ldap_sasl_bind(SIMPLE)", rc,
1368 					NULL, NULL, NULL, NULL );
1369 				exit( rc );
1370 			}
1371 		}
1372 
1373 		if ( ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 ) {
1374 			tool_perror( "ldap_result", -1, NULL, NULL, NULL, NULL );
1375 			exit( LDAP_LOCAL_ERROR );
1376 		}
1377 
1378 		rc = ldap_parse_result( ld, result, &err, &matched, &info, &refs,
1379 			&ctrls, 1 );
1380 		if ( rc != LDAP_SUCCESS ) {
1381 			tool_perror( "ldap_bind parse result", rc, NULL, matched, info, refs );
1382 			exit( LDAP_LOCAL_ERROR );
1383 		}
1384 
1385 #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
1386 		if ( ctrls && ppolicy ) {
1387 			LDAPControl *ctrl;
1388 			int expire, grace, len = 0;
1389 			LDAPPasswordPolicyError pErr = -1;
1390 
1391 			ctrl = ldap_control_find( LDAP_CONTROL_PASSWORDPOLICYRESPONSE,
1392 				ctrls, NULL );
1393 
1394 			if ( ctrl && ldap_parse_passwordpolicy_control( ld, ctrl,
1395 				&expire, &grace, &pErr ) == LDAP_SUCCESS )
1396 			{
1397 				if ( pErr != PP_noError ){
1398 					msgbuf[0] = ';';
1399 					msgbuf[1] = ' ';
1400 					strcpy( msgbuf+2, ldap_passwordpolicy_err2txt( pErr ));
1401 					len = strlen( msgbuf );
1402 				}
1403 				if ( expire >= 0 ) {
1404 					sprintf( msgbuf+len,
1405 						" (Password expires in %d seconds)",
1406 						expire );
1407 				} else if ( grace >= 0 ) {
1408 					sprintf( msgbuf+len,
1409 						" (Password expired, %d grace logins remain)",
1410 						grace );
1411 				}
1412 			}
1413 		}
1414 #endif
1415 
1416 		if ( ctrls ) {
1417 			ldap_controls_free( ctrls );
1418 		}
1419 
1420 		if ( err != LDAP_SUCCESS
1421 			|| msgbuf[0]
1422 			|| ( matched && matched[ 0 ] )
1423 			|| ( info && info[ 0 ] )
1424 			|| refs )
1425 		{
1426 			tool_perror( "ldap_bind", err, msgbuf, matched, info, refs );
1427 
1428 			if( matched ) ber_memfree( matched );
1429 			if( info ) ber_memfree( info );
1430 			if( refs ) ber_memvfree( (void **)refs );
1431 
1432 			if ( err != LDAP_SUCCESS ) exit( err );
1433 		}
1434 	}
1435 }
1436 
1437 void
1438 tool_unbind( LDAP *ld )
1439 {
1440 	int err = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, NULL );
1441 
1442 	if ( err != LDAP_OPT_SUCCESS ) {
1443 		fprintf( stderr, "Could not unset controls\n");
1444 	}
1445 
1446 	(void) ldap_unbind_ext( ld, NULL, NULL );
1447 }
1448 
1449 
1450 /* Set server controls.  Add controls extra_c[0..count-1], if set. */
1451 void
1452 tool_server_controls( LDAP *ld, LDAPControl *extra_c, int count )
1453 {
1454 	int i = 0, j, crit = 0, err;
1455 	LDAPControl c[16], **ctrls;
1456 
1457 	if ( ! ( assertctl
1458 		|| authzid
1459 #ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
1460 		|| proxydn
1461 #endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
1462 		|| manageDIT
1463 		|| manageDSAit
1464 		|| noop
1465 #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
1466 		|| ppolicy
1467 #endif
1468 		|| preread
1469 		|| postread
1470 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1471 		|| chaining
1472 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1473 #ifdef LDAP_CONTROL_X_SESSION_TRACKING
1474 		|| sessionTracking
1475 #endif /* LDAP_CONTROL_X_SESSION_TRACKING */
1476 		|| count
1477 		|| unknown_ctrls_num ) )
1478 	{
1479 		return;
1480 	}
1481 
1482 	ctrls = (LDAPControl**) malloc(sizeof(c) + (count + unknown_ctrls_num + 1)*sizeof(LDAPControl*));
1483 	if ( ctrls == NULL ) {
1484 		fprintf( stderr, "No memory\n" );
1485 		exit( EXIT_FAILURE );
1486 	}
1487 
1488 	if ( assertctl ) {
1489 		if ( BER_BVISNULL( &assertionvalue ) ) {
1490 			err = ldap_create_assertion_control_value( ld,
1491 				assertion, &assertionvalue );
1492 			if ( err ) {
1493 				fprintf( stderr,
1494 					"Unable to create assertion value "
1495 					"\"%s\" (%d)\n", assertion, err );
1496 			}
1497 		}
1498 
1499 		c[i].ldctl_oid = LDAP_CONTROL_ASSERT;
1500 		c[i].ldctl_value = assertionvalue;
1501 		c[i].ldctl_iscritical = assertctl > 1;
1502 		ctrls[i] = &c[i];
1503 		i++;
1504 	}
1505 
1506 	if ( authzid ) {
1507 		c[i].ldctl_value.bv_val = authzid;
1508 		c[i].ldctl_value.bv_len = strlen( authzid );
1509 		c[i].ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
1510 		c[i].ldctl_iscritical = 1;
1511 		ctrls[i] = &c[i];
1512 		i++;
1513 	}
1514 
1515 #ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
1516 	/* NOTE: doesn't need an extra count because it's incompatible
1517 	 * with authzid */
1518 	if ( proxydn ) {
1519 		BerElementBuffer berbuf;
1520 		BerElement *ber = (BerElement *)&berbuf;
1521 
1522 		ber_init2( ber, NULL, LBER_USE_DER );
1523 
1524 		if ( ber_printf( ber, "s", proxydn ) == LBER_ERROR ) {
1525 			exit( EXIT_FAILURE );
1526 		}
1527 
1528 		if ( ber_flatten2( ber, &c[i].ldctl_value, 0 ) == -1 ) {
1529 			exit( EXIT_FAILURE );
1530 		}
1531 
1532 		c[i].ldctl_oid = LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ;
1533 		c[i].ldctl_iscritical = 1;
1534 		ctrls[i] = &c[i];
1535 		i++;
1536 	}
1537 #endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
1538 
1539 	if ( manageDIT ) {
1540 		c[i].ldctl_oid = LDAP_CONTROL_MANAGEDIT;
1541 		BER_BVZERO( &c[i].ldctl_value );
1542 		c[i].ldctl_iscritical = manageDIT > 1;
1543 		ctrls[i] = &c[i];
1544 		i++;
1545 	}
1546 
1547 	if ( manageDSAit ) {
1548 		c[i].ldctl_oid = LDAP_CONTROL_MANAGEDSAIT;
1549 		BER_BVZERO( &c[i].ldctl_value );
1550 		c[i].ldctl_iscritical = manageDSAit > 1;
1551 		ctrls[i] = &c[i];
1552 		i++;
1553 	}
1554 
1555 	if ( noop ) {
1556 		c[i].ldctl_oid = LDAP_CONTROL_NOOP;
1557 		BER_BVZERO( &c[i].ldctl_value );
1558 		c[i].ldctl_iscritical = noop > 1;
1559 		ctrls[i] = &c[i];
1560 		i++;
1561 	}
1562 
1563 #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
1564 	if ( ppolicy ) {
1565 		c[i].ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST;
1566 		BER_BVZERO( &c[i].ldctl_value );
1567 		c[i].ldctl_iscritical = 0;
1568 		ctrls[i] = &c[i];
1569 		i++;
1570 	}
1571 #endif
1572 
1573 	if ( preread ) {
1574 		char berbuf[LBER_ELEMENT_SIZEOF];
1575 		BerElement *ber = (BerElement *)berbuf;
1576 		char **attrs = NULL;
1577 
1578 		if( preread_attrs ) {
1579 			attrs = ldap_str2charray( preread_attrs, "," );
1580 		}
1581 
1582 		ber_init2( ber, NULL, LBER_USE_DER );
1583 
1584 		if( ber_printf( ber, "{v}", attrs ) == -1 ) {
1585 			fprintf( stderr, "preread attrs encode failed.\n" );
1586 			exit( EXIT_FAILURE );
1587 		}
1588 
1589 		err = ber_flatten2( ber, &c[i].ldctl_value, 0 );
1590 		if( err < 0 ) {
1591 			fprintf( stderr, "preread flatten failed (%d)\n", err );
1592 			exit( EXIT_FAILURE );
1593 		}
1594 
1595 		c[i].ldctl_oid = LDAP_CONTROL_PRE_READ;
1596 		c[i].ldctl_iscritical = preread > 1;
1597 		ctrls[i] = &c[i];
1598 		i++;
1599 
1600 		if( attrs ) ldap_charray_free( attrs );
1601 	}
1602 
1603 	if ( postread ) {
1604 		char berbuf[LBER_ELEMENT_SIZEOF];
1605 		BerElement *ber = (BerElement *)berbuf;
1606 		char **attrs = NULL;
1607 
1608 		if( postread_attrs ) {
1609 			attrs = ldap_str2charray( postread_attrs, "," );
1610 		}
1611 
1612 		ber_init2( ber, NULL, LBER_USE_DER );
1613 
1614 		if( ber_printf( ber, "{v}", attrs ) == -1 ) {
1615 			fprintf( stderr, "postread attrs encode failed.\n" );
1616 			exit( EXIT_FAILURE );
1617 		}
1618 
1619 		err = ber_flatten2( ber, &c[i].ldctl_value, 0 );
1620 		if( err < 0 ) {
1621 			fprintf( stderr, "postread flatten failed (%d)\n", err );
1622 			exit( EXIT_FAILURE );
1623 		}
1624 
1625 		c[i].ldctl_oid = LDAP_CONTROL_POST_READ;
1626 		c[i].ldctl_iscritical = postread > 1;
1627 		ctrls[i] = &c[i];
1628 		i++;
1629 
1630 		if( attrs ) ldap_charray_free( attrs );
1631 	}
1632 
1633 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1634 	if ( chaining ) {
1635 		if ( chainingResolve > -1 ) {
1636 			BerElementBuffer berbuf;
1637 			BerElement *ber = (BerElement *)&berbuf;
1638 
1639 			ber_init2( ber, NULL, LBER_USE_DER );
1640 
1641 			err = ber_printf( ber, "{e" /* } */, chainingResolve );
1642 		    	if ( err == -1 ) {
1643 				ber_free( ber, 1 );
1644 				fprintf( stderr, _("Chaining behavior control encoding error!\n") );
1645 				exit( EXIT_FAILURE );
1646 			}
1647 
1648 			if ( chainingContinuation > -1 ) {
1649 				err = ber_printf( ber, "e", chainingContinuation );
1650 		    		if ( err == -1 ) {
1651 					ber_free( ber, 1 );
1652 					fprintf( stderr, _("Chaining behavior control encoding error!\n") );
1653 					exit( EXIT_FAILURE );
1654 				}
1655 			}
1656 
1657 			err = ber_printf( ber, /* { */ "N}" );
1658 		    	if ( err == -1 ) {
1659 				ber_free( ber, 1 );
1660 				fprintf( stderr, _("Chaining behavior control encoding error!\n") );
1661 				exit( EXIT_FAILURE );
1662 			}
1663 
1664 			if ( ber_flatten2( ber, &c[i].ldctl_value, 0 ) == -1 ) {
1665 				exit( EXIT_FAILURE );
1666 			}
1667 
1668 		} else {
1669 			BER_BVZERO( &c[i].ldctl_value );
1670 		}
1671 
1672 		c[i].ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1673 		c[i].ldctl_iscritical = chaining > 1;
1674 		ctrls[i] = &c[i];
1675 		i++;
1676 	}
1677 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1678 
1679 #ifdef LDAP_CONTROL_X_SESSION_TRACKING
1680 	if ( sessionTracking ) {
1681 		if ( stValue.bv_val == NULL && st_value( ld, &stValue ) ) {
1682 			exit( EXIT_FAILURE );
1683 		}
1684 
1685 		c[i].ldctl_oid = LDAP_CONTROL_X_SESSION_TRACKING;
1686 		c[i].ldctl_iscritical = 0;
1687 		ber_dupbv( &c[i].ldctl_value, &stValue );
1688 
1689 		ctrls[i] = &c[i];
1690 		i++;
1691 	}
1692 #endif /* LDAP_CONTROL_X_SESSION_TRACKING */
1693 
1694 	while ( count-- ) {
1695 		ctrls[i++] = extra_c++;
1696 	}
1697 	for ( count = 0; count < unknown_ctrls_num; count++ ) {
1698 		ctrls[i++] = &unknown_ctrls[count];
1699 	}
1700 	ctrls[i] = NULL;
1701 
1702 	err = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, ctrls );
1703 
1704 	if ( err != LDAP_OPT_SUCCESS ) {
1705 		for ( j = 0; j < i; j++ ) {
1706 			if ( ctrls[j]->ldctl_iscritical ) crit = 1;
1707 		}
1708 		fprintf( stderr, "Could not set %scontrols\n",
1709 			crit ? "critical " : "" );
1710 	}
1711 
1712  	free( ctrls );
1713 	if ( crit ) {
1714 		exit( EXIT_FAILURE );
1715 	}
1716 }
1717 
1718 int
1719 tool_check_abandon( LDAP *ld, int msgid )
1720 {
1721 	int	rc;
1722 
1723 	switch ( gotintr ) {
1724 	case Intr_Cancel:
1725 		rc = ldap_cancel_s( ld, msgid, NULL, NULL );
1726 		fprintf( stderr, "got interrupt, cancel got %d: %s\n",
1727 				rc, ldap_err2string( rc ) );
1728 		return -1;
1729 
1730 	case Intr_Abandon:
1731 		rc = ldap_abandon_ext( ld, msgid, NULL, NULL );
1732 		fprintf( stderr, "got interrupt, abandon got %d: %s\n",
1733 				rc, ldap_err2string( rc ) );
1734 		return -1;
1735 
1736 	case Intr_Ignore:
1737 		/* just unbind, ignoring the request */
1738 		return -1;
1739 	}
1740 
1741 	return 0;
1742 }
1743 
1744 static int
1745 print_prepostread( LDAP *ld, LDAPControl *ctrl, struct berval *what)
1746 {
1747 	BerElement	*ber;
1748 	struct berval	bv;
1749 
1750 	tool_write_ldif( LDIF_PUT_COMMENT, "==> ",
1751 		what->bv_val, what->bv_len );
1752 	ber = ber_init( &ctrl->ldctl_value );
1753 	if ( ber == NULL ) {
1754 		/* error? */
1755 		return 1;
1756 
1757 	} else if ( ber_scanf( ber, "{m{" /*}}*/, &bv ) == LBER_ERROR ) {
1758 		/* error? */
1759 		return 1;
1760 
1761 	} else {
1762 		tool_write_ldif( LDIF_PUT_VALUE, "dn", bv.bv_val, bv.bv_len );
1763 
1764 		while ( ber_scanf( ber, "{m" /*}*/, &bv ) != LBER_ERROR ) {
1765 			int		i;
1766 			BerVarray	vals = NULL;
1767 
1768 			if ( ber_scanf( ber, "[W]", &vals ) == LBER_ERROR ||
1769 				vals == NULL )
1770 			{
1771 				/* error? */
1772 				return 1;
1773 			}
1774 
1775 			for ( i = 0; vals[ i ].bv_val != NULL; i++ ) {
1776 				tool_write_ldif(
1777 					ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
1778 					bv.bv_val, vals[ i ].bv_val, vals[ i ].bv_len );
1779 			}
1780 
1781 			ber_bvarray_free( vals );
1782 		}
1783 	}
1784 
1785 	if ( ber != NULL ) {
1786 		ber_free( ber, 1 );
1787 	}
1788 
1789 	tool_write_ldif( LDIF_PUT_COMMENT, "<== ",
1790 		what->bv_val, what->bv_len );
1791 
1792 	return 0;
1793 }
1794 
1795 static int
1796 print_preread( LDAP *ld, LDAPControl *ctrl )
1797 {
1798 	static struct berval what = BER_BVC( "preread" );
1799 
1800 	return print_prepostread( ld, ctrl, &what );
1801 }
1802 
1803 static int
1804 print_postread( LDAP *ld, LDAPControl *ctrl )
1805 {
1806 	static struct berval what = BER_BVC( "postread" );
1807 
1808 	return print_prepostread( ld, ctrl, &what );
1809 }
1810 
1811 static int
1812 print_paged_results( LDAP *ld, LDAPControl *ctrl )
1813 {
1814 	ber_int_t estimate;
1815 
1816 	/* note: pr_cookie is being malloced; it's freed
1817 	 * the next time the control is sent, but the last
1818 	 * time it's not; we don't care too much, because
1819 	 * the last time an empty value is returned... */
1820 	if ( ldap_parse_pageresponse_control( ld, ctrl, &estimate, &pr_cookie )
1821 		!= LDAP_SUCCESS )
1822 	{
1823 		/* error? */
1824 		return 1;
1825 
1826 	} else {
1827 		/* FIXME: check buffer overflow */
1828 		char	buf[ BUFSIZ ], *ptr = buf;
1829 
1830 		if ( estimate > 0 ) {
1831 			ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
1832 				"estimate=%d", estimate );
1833 		}
1834 
1835 		if ( pr_cookie.bv_len > 0 ) {
1836 			struct berval	bv;
1837 
1838 			bv.bv_len = LUTIL_BASE64_ENCODE_LEN(
1839 				pr_cookie.bv_len ) + 1;
1840 			bv.bv_val = ber_memalloc( bv.bv_len + 1 );
1841 
1842 			bv.bv_len = lutil_b64_ntop(
1843 				(unsigned char *) pr_cookie.bv_val,
1844 				pr_cookie.bv_len,
1845 				bv.bv_val, bv.bv_len );
1846 
1847 			ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
1848 				"%scookie=%s", ptr == buf ? "" : " ",
1849 				bv.bv_val );
1850 
1851 			ber_memfree( bv.bv_val );
1852 
1853 			pr_morePagedResults = 1;
1854 
1855 		} else {
1856 			ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
1857 				"%scookie=", ptr == buf ? "" : " " );
1858 		}
1859 
1860 		tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
1861 			"pagedresults", buf, ptr - buf );
1862 	}
1863 
1864 	return 0;
1865 }
1866 
1867 #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
1868 static int
1869 print_ppolicy( LDAP *ld, LDAPControl *ctrl )
1870 {
1871 	int expire = 0, grace = 0, rc;
1872 	LDAPPasswordPolicyError	pperr;
1873 
1874 	rc = ldap_parse_passwordpolicy_control( ld, ctrl,
1875 		&expire, &grace, &pperr );
1876 	if ( rc == LDAP_SUCCESS ) {
1877 		char	buf[ BUFSIZ ], *ptr = buf;
1878 
1879 		if ( expire != -1 ) {
1880 			ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
1881 				"expire=%d", expire );
1882 		}
1883 
1884 		if ( grace != -1 ) {
1885 			ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
1886 				"%sgrace=%d", ptr == buf ? "" : " ", grace );
1887 		}
1888 
1889 		if ( pperr != PP_noError ) {
1890 			ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
1891 				"%serror=%d (%s)", ptr == buf ? "" : " ",
1892 				pperr,
1893 				ldap_passwordpolicy_err2txt( pperr ) );
1894 		}
1895 
1896 		tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
1897 			"ppolicy", buf, ptr - buf );
1898 	}
1899 
1900 	return rc;
1901 }
1902 #endif
1903 
1904 void tool_print_ctrls(
1905 	LDAP		*ld,
1906 	LDAPControl	**ctrls )
1907 {
1908 	int	i;
1909 	char	*ptr;
1910 
1911 	for ( i = 0; ctrls[i] != NULL; i++ ) {
1912 		/* control: OID criticality base64value */
1913 		struct berval b64 = BER_BVNULL;
1914 		ber_len_t len;
1915 		char *str;
1916 		int j;
1917 
1918 		/* FIXME: there might be cases where a control has NULL OID;
1919 		 * this makes little sense, especially when returned by the
1920 		 * server, but libldap happily allows it */
1921 		if ( ctrls[i]->ldctl_oid == NULL ) {
1922 			continue;
1923 		}
1924 
1925 		len = ldif ? 2 : 0;
1926 		len += strlen( ctrls[i]->ldctl_oid );
1927 
1928 		/* add enough for space after OID and the critical value itself */
1929 		len += ctrls[i]->ldctl_iscritical
1930 			? sizeof("true") : sizeof("false");
1931 
1932 		/* convert to base64 */
1933 		if ( !BER_BVISNULL( &ctrls[i]->ldctl_value ) ) {
1934 			b64.bv_len = LUTIL_BASE64_ENCODE_LEN(
1935 				ctrls[i]->ldctl_value.bv_len ) + 1;
1936 			b64.bv_val = ber_memalloc( b64.bv_len + 1 );
1937 
1938 			b64.bv_len = lutil_b64_ntop(
1939 				(unsigned char *) ctrls[i]->ldctl_value.bv_val,
1940 				ctrls[i]->ldctl_value.bv_len,
1941 				b64.bv_val, b64.bv_len );
1942 		}
1943 
1944 		if ( b64.bv_len ) {
1945 			len += 1 + b64.bv_len;
1946 		}
1947 
1948 		ptr = str = malloc( len + 1 );
1949 		if ( ldif ) {
1950 			ptr = lutil_strcopy( ptr, ": " );
1951 		}
1952 		ptr = lutil_strcopy( ptr, ctrls[i]->ldctl_oid );
1953 		ptr = lutil_strcopy( ptr, ctrls[i]->ldctl_iscritical
1954 			? " true" : " false" );
1955 
1956 		if ( b64.bv_len ) {
1957 			ptr = lutil_strcopy( ptr, " " );
1958 			ptr = lutil_strcopy( ptr, b64.bv_val );
1959 		}
1960 
1961 		if ( ldif < 2 ) {
1962 			tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
1963 				"control", str, len );
1964 		}
1965 
1966 		free( str );
1967 		if ( b64.bv_len ) {
1968 			ber_memfree( b64.bv_val );
1969 		}
1970 
1971 		/* known controls */
1972 		for ( j = 0; tool_ctrl_response[j].oid != NULL; j++ ) {
1973 			if ( strcmp( tool_ctrl_response[j].oid, ctrls[i]->ldctl_oid ) == 0 ) {
1974 				if ( !tool_ctrl_response[j].mask & tool_type ) {
1975 					/* this control should not appear
1976 					 * with this tool; warning? */
1977 				}
1978 				break;
1979 			}
1980 		}
1981 
1982 		if ( tool_ctrl_response[j].oid != NULL && tool_ctrl_response[j].func ) {
1983 			(void)tool_ctrl_response[j].func( ld, ctrls[i] );
1984 		}
1985 	}
1986 }
1987 
1988 int
1989 tool_write_ldif( int type, char *name, char *value, ber_len_t vallen )
1990 {
1991 	char	*ldif;
1992 
1993 	if (( ldif = ldif_put( type, name, value, vallen )) == NULL ) {
1994 		return( -1 );
1995 	}
1996 
1997 	fputs( ldif, stdout );
1998 	ber_memfree( ldif );
1999 
2000 	return( 0 );
2001 }
2002 
2003 int
2004 tool_is_oid( const char *s )
2005 {
2006 	int		first = 1;
2007 
2008 	if ( !isdigit( (unsigned char) s[ 0 ] ) ) {
2009 		return 0;
2010 	}
2011 
2012 	for ( ; s[ 0 ]; s++ ) {
2013 		if ( s[ 0 ] == '.' ) {
2014 			if ( s[ 1 ] == '\0' ) {
2015 				return 0;
2016 			}
2017 			first = 1;
2018 			continue;
2019 		}
2020 
2021 		if ( !isdigit( (unsigned char) s[ 0 ] ) ) {
2022 			return 0;
2023 		}
2024 
2025 		if ( first == 1 && s[ 0 ] == '0' && s[ 1 ] != '.' ) {
2026 			return 0;
2027 		}
2028 		first = 0;
2029 	}
2030 
2031 	return 1;
2032 }
2033 
2034