Skip to content
Snippets Groups Projects
common.c 49.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • /* common.c - common routines for the ldap client tools */
    
    /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
     *
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
     * Copyright 1998-2007 The OpenLDAP Foundation.
    
     * Portions Copyright 2003 Kurt D. Zeilenga.
     * Portions Copyright 2003 IBM Corporation.
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted only as authorized by the OpenLDAP
     * Public License.
     *
     * A copy of this license is available in the file LICENSE in the
     * top-level directory of the distribution or, alternatively, at
     * <http://www.OpenLDAP.org/license.html>.
     */
    
    /* ACKNOWLEDGEMENTS:
     * This file was initially created by Hallvard B. Furuseth based (in
    
     * part) upon argument parsing code for individual tools located in
     * this directory.   Additional contributors include:
     *   Kurt D. Zeilenga (additional common argument and control support)
    
     */
    
    #include "portable.h"
    
    #include <stdio.h>
    
    #include <ac/stdlib.h>
    #include <ac/signal.h>
    #include <ac/string.h>
    
    #include <ac/ctype.h>
    
    #include <ac/unistd.h>
    #include <ac/errno.h>
    
    Howard Chu's avatar
    Howard Chu committed
    #include <ac/time.h>
    
    #include <ac/socket.h>
    
    #ifdef HAVE_CYRUS_SASL
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    #ifdef HAVE_SASL_SASL_H
    #include <sasl/sasl.h>
    #else
    #include <sasl.h>
    #endif
    
    #include "lutil_ldap.h"
    
    #include "ldap_pvt.h"
    
    #include "lber_pvt.h"
    
    /* input-related vars */
    
    /* misc. parameters */
    tool_type_t	tool_type;
    int		contoper = 0;
    int		debug = 0;
    char		*infile = NULL;
    int		dont = 0;
    int		referrals = 0;
    int		verbose = 0;
    int		ldif = 0;
    char		*prog = NULL;
    
    /* connection */
    char		*ldapuri = NULL;
    char		*ldaphost = NULL;
    int  		ldapport = 0;
    int		use_tls = 0;
    int		protocol = -1;
    int		version = 0;
    
    /* authc/authz */
    int		authmethod = -1;
    char		*binddn = NULL;
    int		want_bindpw = 0;
    struct berval	passwd = { 0, NULL };
    char		*pw_file = NULL;
    
    #ifdef HAVE_CYRUS_SASL
    
    unsigned	sasl_flags = LDAP_SASL_AUTOMATIC;
    char		*sasl_realm = NULL;
    char		*sasl_authc_id = NULL;
    char		*sasl_authz_id = NULL;
    char		*sasl_mech = NULL;
    char		*sasl_secprops = NULL;
    
    /* controls */
    int		assertctl;
    char		*assertion = NULL;
    char		*authzid = NULL;
    
    /* support deprecated early version of proxyAuthz */
    #define LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ	"2.16.840.1.113730.3.4.12"
    #ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
    char		*proxydn = NULL;
    #endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
    
    int		manageDIT = 0;
    int		manageDSAit = 0;
    int		noop = 0;
    int		ppolicy = 0;
    int		preread = 0;
    static char	*preread_attrs = NULL;
    int		postread = 0;
    static char	*postread_attrs = NULL;
    ber_int_t	pr_morePagedResults = 1;
    struct berval	pr_cookie = { 0, NULL };
    
    int		chaining = 0;
    static int	chainingResolve = -1;
    static int	chainingContinuation = -1;
    
    #ifdef LDAP_CONTROL_X_SESSION_TRACKING
    static int	sessionTracking = 0;
    struct berval	stValue;
    #endif /* LDAP_CONTROL_X_SESSION_TRACKING */
    
    LDAPControl	*unknown_ctrls = NULL;
    int		unknown_ctrls_num = 0;
    
    /* options */
    struct timeval	nettimeout = { -1 , 0 };
    
    
    typedef int (*print_ctrl_fn)( LDAP *ld, LDAPControl *ctrl );
    
    static int print_preread( LDAP *ld, LDAPControl *ctrl );
    static int print_postread( LDAP *ld, LDAPControl *ctrl );
    static int print_paged_results( LDAP *ld, LDAPControl *ctrl );
    #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
    static int print_ppolicy( LDAP *ld, LDAPControl *ctrl );
    #endif
    
    static struct tool_ctrls_t {
    	const char	*oid;
    	unsigned	mask;
    	print_ctrl_fn	func;
    } tool_ctrl_response[] = {
    	{ LDAP_CONTROL_PRE_READ,			TOOL_ALL,	print_preread },
    	{ LDAP_CONTROL_POST_READ,			TOOL_ALL,	print_postread },
    	{ LDAP_CONTROL_PAGEDRESULTS,			TOOL_SEARCH,	print_paged_results },
    #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
    	{ LDAP_CONTROL_PASSWORDPOLICYRESPONSE,		TOOL_ALL,	print_ppolicy },
    #endif
    	{ NULL,						0,		NULL }
    };
    
    /* "features" */
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    enum { Intr_None = 0, Intr_Abandon, Intr_Cancel, Intr_Ignore }; 
    static volatile sig_atomic_t	gotintr, abcan;
    
    
    #ifdef LDAP_CONTROL_X_SESSION_TRACKING
    static int
    st_value( LDAP *ld, struct berval *value )
    {
    	char		*ip = NULL, *name = NULL;
    	struct berval	id = { 0 };
    	char		namebuf[ MAXHOSTNAMELEN ];
    
    	if ( gethostname( namebuf, sizeof( namebuf ) ) == 0 ) {
    		struct hostent	*h;
    		struct in_addr	addr;
    
    		name = namebuf;
    
    		h = gethostbyname( name );
    		if ( h != NULL ) {
    			AC_MEMCPY( &addr, h->h_addr, sizeof( addr ) );
    			ip = inet_ntoa( addr );
    		}
    	}
    
    #ifdef HAVE_CYRUS_SASL
    	if ( sasl_authz_id != NULL ) {
    		ber_str2bv( sasl_authz_id, 0, 0, &id );
    
    	} else if ( sasl_authc_id != NULL ) {
    		ber_str2bv( sasl_authc_id, 0, 0, &id );
    
    	} else 
    #endif /* HAVE_CYRUS_SASL */
    	if ( binddn != NULL ) {
    		ber_str2bv( binddn, 0, 0, &id );
    	}
    
    	if ( ldap_create_session_tracking_value( ld,
    		ip, name, LDAP_CONTROL_X_SESSION_TRACKING_USERNAME,
    		&id, &stValue ) )
    	{
    		fprintf( stderr, _("Session tracking control encoding error!\n") );
    		return -1;
    	}
    
    	return 0;
    }
    #endif /* LDAP_CONTROL_X_SESSION_TRACKING */
    
    
    RETSIGTYPE
    do_sig( int sig )
    {
    	gotintr = abcan;
    }
    
    
    tool_init( tool_type_t type )
    
    	tool_type = type;
    
    	ldap_pvt_setlocale(LC_MESSAGES, "");
    	ldap_pvt_bindtextdomain(OPENLDAP_PACKAGE, LDAP_LOCALEDIR);
    	ldap_pvt_textdomain(OPENLDAP_PACKAGE);
    
    void
    tool_destroy( void )
    {
    #ifdef HAVE_CYRUS_SASL
    	sasl_done();
    #endif
    #ifdef HAVE_TLS
    	ldap_pvt_tls_destroy();
    #endif
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    
    	if ( ldapuri != NULL ) {
    		ber_memfree( ldapuri );
    		ldapuri = NULL;
    	}
    
    	if ( pr_cookie.bv_val != NULL ) {
    		ber_memfree( pr_cookie.bv_val );
    		pr_cookie.bv_val = NULL;
    		pr_cookie.bv_len = 0;
    	}
    
    void
    tool_common_usage( void )
    {
    	static const char *const descriptions[] = {
    
    N_("  -c         continuous operation mode (do not stop on errors)\n"),
    N_("  -d level   set LDAP debugging level to `level'\n"),
    N_("  -D binddn  bind DN\n"),
    
    N_("  -e [!]<ext>[=<extparam>] general extensions (! indicates criticality)\n")
    
    N_("             [!]assert=<filter>     (a RFC 4515 Filter string)\n")
    
    N_("             [!]authzid=<authzid>   (\"dn:<dn>\" or \"u:<user>\")\n")
    
    #ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
    #if 0
                     /* non-advertized support for proxyDN */
    
    N_("             [!]proxydn=<dn>        (a RFC 4514 DN string)\n")
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    N_("             [!]chaining[=<resolveBehavior>[/<continuationBehavior>]]\n")
    
    N_("                     one of \"chainingPreferred\", \"chainingRequired\",\n")
    N_("                     \"referralsPreferred\", \"referralsRequired\"\n")
    #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
    
    N_("             [!]manageDSAit\n")
    N_("             [!]noop\n")
    #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
    N_("             ppolicy\n")
    #endif
    
    N_("             [!]postread[=<attrs>]  (a comma-separated attribute list)\n")
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    N_("             [!]preread[=<attrs>]   (a comma-separated attribute list)\n")
    N_("             [!]relax\n")
    
    #ifdef LDAP_CONTROL_X_SESSION_TRACKING
    N_("             [!]sessiontracking\n")
    #endif /* LDAP_CONTROL_X_SESSION_TRACKING */
    
    N_("             abandon, cancel, ignore (SIGINT sends abandon/cancel,\n"
       "             or ignores response; if critical, doesn't wait for SIGINT.\n"
       "             not really controls)\n")
    
    N_("  -f file    read operations from `file'\n"),
    N_("  -h host    LDAP server\n"),
    
    N_("  -H URI     LDAP Uniform Resource Identifier(s)\n"),
    
    N_("  -I         use SASL Interactive mode\n"),
    N_("  -M         enable Manage DSA IT control (-MM to make critical)\n"),
    N_("  -n         show what would be done but don't actually do it\n"),
    N_("  -O props   SASL security properties\n"),
    
    N_("  -o <opt>[=<optparam] general options\n"),
    N_("             nettimeout=<timeout> (in seconds, or \"none\" or \"max\")\n"),
    
    N_("  -p port    port on LDAP server\n"),
    
    N_("  -P version protocol version (default: 3)\n"),
    
    N_("  -Q         use SASL Quiet mode\n"),
    N_("  -R realm   SASL realm\n"),
    N_("  -U authcid SASL authentication identity\n"),
    N_("  -v         run in verbose mode (diagnostics to standard output)\n"),
    N_("  -V         print version info (-VV only)\n"),
    N_("  -w passwd  bind password (for simple authentication)\n"),
    N_("  -W         prompt for bind password\n"),
    N_("  -x         Simple authentication\n"),
    N_("  -X authzid SASL authorization identity (\"dn:<dn>\" or \"u:<user>\")\n"),
    N_("  -y file    Read password from file\n"),
    N_("  -Y mech    SASL mechanism\n"),
    N_("  -Z         Start TLS request (-ZZ to require successful response)\n"),
    
    	const char *const *cpp;
    
    	fputs( _("Common options:\n"), stderr );
    
    	for( cpp = descriptions; *cpp != NULL; cpp++ ) {
    
    		if( strchr( options, (*cpp)[3] ) || (*cpp)[3] == ' ' ) {
    
    			fputs( _(*cpp), stderr );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    void tool_perror(
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	int err,
    
    	const char *extra,
    	const char *matched,
    	const char *info,
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	char **refs )
    {
    
    	fprintf( stderr, "%s: %s (%d)%s\n",
    		func, ldap_err2string( err ), err, extra ? extra : "" );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    	if ( matched && *matched ) {
    		fprintf( stderr, _("\tmatched DN: %s\n"), matched );
    	}
    
    	if ( info && *info ) {
    		fprintf( stderr, _("\tadditional info: %s\n"), info );
    	}
    
    	if ( refs && *refs ) {
    		int i;
    		fprintf( stderr, _("\treferrals:\n") );
    		for( i=0; refs[i]; i++ ) {
    			fprintf( stderr, "\t\t%s\n", refs[i] );
    		}
    	}
    }
    
    
    
    void
    tool_args( int argc, char **argv )
    {
    	int i;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    	while (( i = getopt( argc, argv, options )) != EOF ) {
    
    		int crit, ival;
    		char *control, *cvalue, *next;
    
    		switch( i ) {
    		case 'c':	/* continuous operation mode */
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    			contoper++;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    			referrals++;
    
    			ival = strtol( optarg, &next, 10 );
    			if (next == NULL || next[0] != '\0') {
    				fprintf( stderr, "%s: unable to parse debug value \"%s\"\n", prog, optarg);
    				exit(EXIT_FAILURE);
    			}
    			debug |= ival;
    
    			break;
    		case 'D':	/* bind DN */
    			if( binddn != NULL ) {
    				fprintf( stderr, "%s: -D previously specified\n", prog );
    				exit( EXIT_FAILURE );
    			}
    			binddn = ber_strdup( optarg );
    			break;
    
    		case 'e':	/* general extensions (controls and such) */
    
    			/* should be extended to support comma separated list of
    			 *	[!]key[=value] parameters, e.g.  -e !foo,bar=567
    			 */
    
    			crit = 0;
    			cvalue = NULL;
    			if( optarg[0] == '!' ) {
    				crit = 1;
    				optarg++;
    			}
    
    			control = ber_strdup( optarg );
    			if ( (cvalue = strchr( control, '=' )) != NULL ) {
    				*cvalue++ = '\0';
    			}
    
    
    			if ( strcasecmp( control, "assert" ) == 0 ) {
    
    				if( assertctl ) {
    
    					fprintf( stderr, "assert control previously specified\n");
    					exit( EXIT_FAILURE );
    				}
    				if( cvalue == NULL ) {
    					fprintf( stderr, "assert: control value expected\n" );
    					usage();
    				}
    
    
    				assertctl = 1 + crit;
    
    
    				assert( assertion == NULL );
    				assertion = cvalue;
    
    			} else if ( strcasecmp( control, "authzid" ) == 0 ) {
    
    				if( authzid != NULL ) {
    					fprintf( stderr, "authzid control previously specified\n");
    					exit( EXIT_FAILURE );
    				}
    
    #ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
    				if( proxydn != NULL ) {
    					fprintf( stderr, "authzid control incompatible with proxydn\n");
    					exit( EXIT_FAILURE );
    				}
    #endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
    
    				if( cvalue == NULL ) {
    					fprintf( stderr, "authzid: control value expected\n" );
    					usage();
    				}
    				if( !crit ) {
    					fprintf( stderr, "authzid: must be marked critical\n" );
    					usage();
    				}
    
    				assert( authzid == NULL );
    				authzid = cvalue;
    
    
    #ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
    			} else if ( strcasecmp( control, "proxydn" ) == 0 ) {
    				if( proxydn != NULL ) {
    					fprintf( stderr, "proxydn control previously specified\n");
    					exit( EXIT_FAILURE );
    				}
    				if( authzid != NULL ) {
    					fprintf( stderr, "proxydn control incompatible with authzid\n");
    					exit( EXIT_FAILURE );
    				}
    				if( cvalue == NULL ) {
    					fprintf( stderr, "proxydn: control value expected\n" );
    					usage();
    				}
    				if( !crit ) {
    					fprintf( stderr, "proxydn: must be marked critical\n" );
    					usage();
    				}
    
    				assert( proxydn == NULL );
    				proxydn = cvalue;
    #endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
    
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    			} else if ( ( strcasecmp( control, "relax" ) == 0 ) ||
    				( strcasecmp( control, "manageDIT" ) == 0 ) )
    			{
    
    				if( manageDIT ) {
    					fprintf( stderr,
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    						"relax control previously specified\n");
    
    					exit( EXIT_FAILURE );
    				}
    				if( cvalue != NULL ) {
    					fprintf( stderr,
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    						"relax: no control value expected\n" );
    
    					usage();
    				}
    
    				manageDIT = 1 + crit;
    
    
    			} else if ( strcasecmp( control, "manageDSAit" ) == 0 ) {
    				if( manageDSAit ) {
    					fprintf( stderr,
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    						"manageDSAit control previously specified\n");
    
    					exit( EXIT_FAILURE );
    				}
    				if( cvalue != NULL ) {
    					fprintf( stderr,
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    						"manageDSAit: no control value expected\n" );
    
    					usage();
    				}
    
    				manageDSAit = 1 + crit;
    
    			} else if ( strcasecmp( control, "noop" ) == 0 ) {
    				if( noop ) {
    					fprintf( stderr, "noop control previously specified\n");
    					exit( EXIT_FAILURE );
    				}
    				if( cvalue != NULL ) {
    					fprintf( stderr, "noop: no control value expected\n" );
    					usage();
    				}
    
    				noop = 1 + crit;
    
    
    #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
    
    			} else if ( strcasecmp( control, "ppolicy" ) == 0 ) {
    				if( ppolicy ) {
    					fprintf( stderr, "ppolicy control previously specified\n");
    					exit( EXIT_FAILURE );
    				}
    				if( cvalue != NULL ) {
    					fprintf( stderr, "ppolicy: no control value expected\n" );
    					usage();
    				}
    				if( crit ) {
    					fprintf( stderr, "ppolicy: critical flag not allowed\n" );
    					usage();
    				}
    
    				ppolicy = 1;
    
    			} else if ( strcasecmp( control, "preread" ) == 0 ) {
    				if( preread ) {
    					fprintf( stderr, "preread control previously specified\n");
    					exit( EXIT_FAILURE );
    				}
    
    				preread = 1 + crit;
    				preread_attrs = cvalue;
    
    			} else if ( strcasecmp( control, "postread" ) == 0 ) {
    				if( postread ) {
    					fprintf( stderr, "postread control previously specified\n");
    					exit( EXIT_FAILURE );
    				}
    
    				postread = 1 + crit;
    				postread_attrs = cvalue;
    
    
    #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
    			} else if ( strcasecmp( control, "chaining" ) == 0 ) {
    				chaining = 1 + crit;
    
    				if ( cvalue != NULL ) {
    					char	*continuation;
    
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    					continuation = strchr( cvalue, '/' );
    
    					if ( continuation ) {
    						/* FIXME: this makes sense only in searches */
    						*continuation++ = '\0';
    						if ( strcasecmp( continuation, "chainingPreferred" ) == 0 ) {
    							chainingContinuation = LDAP_CHAINING_PREFERRED;
    						} else if ( strcasecmp( continuation, "chainingRequired" ) == 0 ) {
    							chainingContinuation = LDAP_CHAINING_REQUIRED;
    						} else if ( strcasecmp( continuation, "referralsPreferred" ) == 0 ) {
    							chainingContinuation = LDAP_REFERRALS_PREFERRED;
    						} else if ( strcasecmp( continuation, "referralsRequired" ) == 0 ) {
    							chainingContinuation = LDAP_REFERRALS_REQUIRED;
    						} else {
    							fprintf( stderr,
    								"chaining behavior control "
    								"continuation value \"%s\" invalid\n",
    								continuation );
    							exit( EXIT_FAILURE );
    						}
    					}
    	
    					if ( strcasecmp( cvalue, "chainingPreferred" ) == 0 ) {
    						chainingResolve = LDAP_CHAINING_PREFERRED;
    					} else if ( strcasecmp( cvalue, "chainingRequired" ) == 0 ) {
    						chainingResolve = LDAP_CHAINING_REQUIRED;
    					} else if ( strcasecmp( cvalue, "referralsPreferred" ) == 0 ) {
    						chainingResolve = LDAP_REFERRALS_PREFERRED;
    					} else if ( strcasecmp( cvalue, "referralsRequired" ) == 0 ) {
    						chainingResolve = LDAP_REFERRALS_REQUIRED;
    					} else {
    						fprintf( stderr,
    							"chaining behavior control "
    							"resolve value \"%s\" invalid\n",
    							cvalue);
    						exit( EXIT_FAILURE );
    					}
    				}
    #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
    
    
    			/* this shouldn't go here, really; but it's a feature... */
    			} else if ( strcasecmp( control, "abandon" ) == 0 ) {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				abcan = Intr_Abandon;
    
    
    			} else if ( strcasecmp( control, "cancel" ) == 0 ) {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				abcan = Intr_Cancel;
    
    				if ( crit ) {
    					gotintr = abcan;
    				}
    
    			} else if ( strcasecmp( control, "ignore" ) == 0 ) {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				abcan = Intr_Ignore;
    
    #ifdef LDAP_CONTROL_X_SESSION_TRACKING
    			} else if ( strcasecmp( control, "sessiontracking" ) == 0 ) {
    				if ( sessionTracking ) {
    					fprintf( stderr, "%s: session tracking can be only specified once\n", prog );
    					exit( EXIT_FAILURE );
    				}
    				sessionTracking = 1;
    #endif /* LDAP_CONTROL_X_SESSION_TRACKING */
    
    			} else if ( tool_is_oid( control ) ) {
    				LDAPControl	*tmpctrls, ctrl;
    
    				tmpctrls = (LDAPControl *)realloc( unknown_ctrls,
    					(unknown_ctrls_num + 1)*sizeof( LDAPControl ) );
    				if ( tmpctrls == NULL ) {
    					fprintf( stderr, "%s: no memory?\n", prog );
    					exit( EXIT_FAILURE );
    				}
    				unknown_ctrls = tmpctrls;
    				ctrl.ldctl_oid = control;
    				ctrl.ldctl_value.bv_val = NULL;
    				ctrl.ldctl_value.bv_len = 0;
    				ctrl.ldctl_iscritical = crit;
    
    				if ( cvalue != NULL ) {
    					struct berval	bv;
    					size_t		len = strlen( cvalue );
    					int		retcode;
    
    					bv.bv_len = LUTIL_BASE64_DECODE_LEN( len );
    					bv.bv_val = ber_memalloc( bv.bv_len + 1 );
    
    					retcode = lutil_b64_pton( cvalue,
    						(unsigned char *)bv.bv_val,
    						bv.bv_len );
    
    					if ( retcode == -1 || retcode > bv.bv_len ) {
    						fprintf( stderr, "Unable to parse value of general control %s\n",
    							control );
    						usage();
    					}
    
    					bv.bv_len = retcode;
    					ctrl.ldctl_value = bv;
    				}
    
    				unknown_ctrls[ unknown_ctrls_num ] = ctrl;
    				unknown_ctrls_num++;
    
    
    			} else {
    				fprintf( stderr, "Invalid general control name: %s\n",
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    					control );
    
    				usage();
    			}
    			break;
    		case 'f':	/* read from file */
    			if( infile != NULL ) {
    				fprintf( stderr, "%s: -f previously specified\n", prog );
    				exit( EXIT_FAILURE );
    			}
    			infile = ber_strdup( optarg );
    			break;
    		case 'h':	/* ldap host */
    			if( ldaphost != NULL ) {
    				fprintf( stderr, "%s: -h previously specified\n", prog );
    				exit( EXIT_FAILURE );
    			}
    			ldaphost = ber_strdup( optarg );
    			break;
    		case 'H':	/* ldap URI */
    			if( ldapuri != NULL ) {
    				fprintf( stderr, "%s: -H previously specified\n", prog );
    				exit( EXIT_FAILURE );
    			}
    			ldapuri = ber_strdup( optarg );
    			break;
    		case 'I':
    #ifdef HAVE_CYRUS_SASL
    			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
    				fprintf( stderr, "%s: incompatible previous "
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    					"authentication choice\n",
    					prog );
    
    				exit( EXIT_FAILURE );
    			}
    			authmethod = LDAP_AUTH_SASL;
    			sasl_flags = LDAP_SASL_INTERACTIVE;
    			break;
    #else
    			fprintf( stderr, "%s: was not compiled with SASL support\n",
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				prog );
    
    			exit( EXIT_FAILURE );
    #endif
    		case 'M':
    			/* enable Manage DSA IT */
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    			manageDSAit++;
    
    			break;
    		case 'n':	/* print operations, don't actually do them */
    
    		case 'o':
    			control = ber_strdup( optarg );
    			if ( (cvalue = strchr( control, '=' )) != NULL ) {
    				*cvalue++ = '\0';
    			}
    
    			if ( strcasecmp( control, "nettimeout" ) == 0 ) {
    				if( nettimeout.tv_sec != -1 ) {
    					fprintf( stderr, "nettimeout option previously specified\n");
    					exit( EXIT_FAILURE );
    				}
    				if( cvalue == NULL || cvalue[0] == '\0' ) {
    					fprintf( stderr, "nettimeout: option value expected\n" );
    					usage();
    				}
    		 		if ( strcasecmp( cvalue, "none" ) == 0 ) {
    		 			nettimeout.tv_sec = 0;
    		 		} else if ( strcasecmp( cvalue, "max" ) == 0 ) {
    		 			nettimeout.tv_sec = LDAP_MAXINT;
    		 		} else {
    		 			ival = strtol( cvalue, &next, 10 );
    		 			if ( next == NULL || next[0] != '\0' ) {
    		 				fprintf( stderr,
    		 					_("Unable to parse network timeout \"%s\"\n"), cvalue );
    		 				exit( EXIT_FAILURE );
    		 			}
    		 			nettimeout.tv_sec = ival;
    		 		}
    		 		if( nettimeout.tv_sec < 0 || nettimeout.tv_sec > LDAP_MAXINT ) {
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    		 			fprintf( stderr, _("%s: invalid network timeout (%ld) specified\n"),
    		 				prog, (long)nettimeout.tv_sec );
    
    	 				exit( EXIT_FAILURE );
     				}
    			} else {
    				fprintf( stderr, "Invalid general option name: %s\n",
    					control );
    				usage();
    			}
    			break;
    
    		case 'O':
    #ifdef HAVE_CYRUS_SASL
    			if( sasl_secprops != NULL ) {
    				fprintf( stderr, "%s: -O previously specified\n", prog );
    				exit( EXIT_FAILURE );
    			}
    			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
    				fprintf( stderr, "%s: incompatible previous "
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    					"authentication choice\n", prog );
    
    				exit( EXIT_FAILURE );
    			}
    			authmethod = LDAP_AUTH_SASL;
    			sasl_secprops = ber_strdup( optarg );
    #else
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    			fprintf( stderr, "%s: not compiled with SASL support\n", prog );
    
    			exit( EXIT_FAILURE );
    #endif
    			break;
    		case 'p':
    			if( ldapport ) {
    				fprintf( stderr, "%s: -p previously specified\n", prog );
    				exit( EXIT_FAILURE );
    			}
    
    			ival = strtol( optarg, &next, 10 );
    			if ( next == NULL || next[0] != '\0' ) {
    				fprintf( stderr, "%s: unable to parse port number \"%s\"\n", prog, optarg );
    				exit( EXIT_FAILURE );
    			}
    			ldapport = ival;
    
    			ival = strtol( optarg, &next, 10 );
    			if ( next == NULL || next[0] != '\0' ) {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				fprintf( stderr, "%s: unable to parse protocol version \"%s\"\n", prog, optarg );
    
    				exit( EXIT_FAILURE );
    			}
    			switch( ival ) {
    
    				if( protocol == LDAP_VERSION3 ) {
    
    					fprintf( stderr, "%s: -P 2 incompatible with version %d\n",
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    						prog, protocol );
    
    				protocol = LDAP_VERSION2;
    
    				if( protocol == LDAP_VERSION2 ) {
    
    					fprintf( stderr, "%s: -P 2 incompatible with version %d\n",
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    						prog, protocol );
    
    				protocol = LDAP_VERSION3;
    
    				break;
    			default:
    				fprintf( stderr, "%s: protocol version should be 2 or 3\n",
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    					prog );
    
    				usage();
    			}
    			break;
    		case 'Q':
    #ifdef HAVE_CYRUS_SASL
    			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
    				fprintf( stderr, "%s: incompatible previous "
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    					"authentication choice\n",
    					prog );
    
    				exit( EXIT_FAILURE );
    			}
    			authmethod = LDAP_AUTH_SASL;
    			sasl_flags = LDAP_SASL_QUIET;
    			break;
    #else
    			fprintf( stderr, "%s: not compiled with SASL support\n",
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				prog );
    
    			exit( EXIT_FAILURE );
    #endif
    		case 'R':
    #ifdef HAVE_CYRUS_SASL
    			if( sasl_realm != NULL ) {
    				fprintf( stderr, "%s: -R previously specified\n", prog );
    				exit( EXIT_FAILURE );
    			}
    			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
    				fprintf( stderr, "%s: incompatible previous "
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    					"authentication choice\n",
    					prog );
    
    				exit( EXIT_FAILURE );
    			}
    			authmethod = LDAP_AUTH_SASL;
    			sasl_realm = ber_strdup( optarg );
    #else
    			fprintf( stderr, "%s: not compiled with SASL support\n",
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				prog );
    
    			exit( EXIT_FAILURE );
    #endif
    			break;
    		case 'U':
    #ifdef HAVE_CYRUS_SASL
    			if( sasl_authc_id != NULL ) {
    				fprintf( stderr, "%s: -U previously specified\n", prog );
    				exit( EXIT_FAILURE );
    			}
    			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
    				fprintf( stderr, "%s: incompatible previous "
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    					"authentication choice\n",
    					prog );
    
    				exit( EXIT_FAILURE );
    			}
    			authmethod = LDAP_AUTH_SASL;
    			sasl_authc_id = ber_strdup( optarg );
    #else
    			fprintf( stderr, "%s: not compiled with SASL support\n",
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				prog );
    
    			exit( EXIT_FAILURE );
    #endif
    			break;
    		case 'v':	/* verbose mode */
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    			verbose++;
    
    		case 'V':	/* version */
    			version++;
    			break;
    
    		case 'w':	/* password */
    			passwd.bv_val = ber_strdup( optarg );
    			{
    				char* p;
    
    				for( p = optarg; *p != '\0'; p++ ) {
    					*p = '\0';
    				}
    			}
    			passwd.bv_len = strlen( passwd.bv_val );
    			break;
    		case 'W':
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    			want_bindpw++;
    
    			break;
    		case 'y':
    			pw_file = optarg;
    			break;
    		case 'Y':
    #ifdef HAVE_CYRUS_SASL
    			if( sasl_mech != NULL ) {
    				fprintf( stderr, "%s: -Y previously specified\n", prog );
    				exit( EXIT_FAILURE );
    			}
    			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				fprintf( stderr,
    					"%s: incompatible with authentication choice\n", prog );
    
    				exit( EXIT_FAILURE );
    			}
    			authmethod = LDAP_AUTH_SASL;
    			sasl_mech = ber_strdup( optarg );
    #else
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    			fprintf( stderr, "%s: not compiled with SASL support\n", prog );
    
    			exit( EXIT_FAILURE );
    #endif
    			break;
    		case 'x':
    			if( authmethod != -1 && authmethod != LDAP_AUTH_SIMPLE ) {
    				fprintf( stderr, "%s: incompatible with previous "
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    					"authentication choice\n", prog );
    
    				exit( EXIT_FAILURE );
    			}
    			authmethod = LDAP_AUTH_SIMPLE;
    			break;
    		case 'X':
    #ifdef HAVE_CYRUS_SASL
    			if( sasl_authz_id != NULL ) {
    				fprintf( stderr, "%s: -X previously specified\n", prog );
    				exit( EXIT_FAILURE );
    			}
    			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
    				fprintf( stderr, "%s: -X incompatible with "
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    					"authentication choice\n", prog );
    
    				exit( EXIT_FAILURE );
    			}
    			authmethod = LDAP_AUTH_SASL;
    			sasl_authz_id = ber_strdup( optarg );
    #else
    			fprintf( stderr, "%s: not compiled with SASL support\n", prog );
    			exit( EXIT_FAILURE );
    #endif
    			break;
    		case 'Z':
    #ifdef HAVE_TLS
    
    #else
    			fprintf( stderr, "%s: not compiled with TLS support\n", prog );
    			exit( EXIT_FAILURE );
    #endif
    			break;
    		default:
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    			if( handle_private_option( i ) ) break;
    
    			fprintf( stderr, "%s: unrecognized option -%c\n",
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				prog, optopt );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	}
    
    	{
    		/* prevent bad linking */
    		LDAPAPIInfo api;
    		api.ldapai_info_version = LDAP_API_INFO_VERSION;
    
    
    		if ( ldap_get_option(NULL, LDAP_OPT_API_INFO, &api)
    			!= LDAP_OPT_SUCCESS )
    		{
    
    			fprintf( stderr, "%s: ldap_get_option(API_INFO) failed\n", prog );
    			exit( EXIT_FAILURE );
    		}
    
    		if (api.ldapai_info_version != LDAP_API_INFO_VERSION) {
    			fprintf( stderr, "LDAP APIInfo version mismatch: "
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				"library %d, header %d\n",
    
    				api.ldapai_info_version, LDAP_API_INFO_VERSION );
    			exit( EXIT_FAILURE );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		}
    
    
    		if( api.ldapai_api_version != LDAP_API_VERSION ) {
    			fprintf( stderr, "LDAP API version mismatch: "
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				"library %d, header %d\n",
    
    				api.ldapai_api_version, LDAP_API_VERSION );
    			exit( EXIT_FAILURE );
    		}
    
    		if( strcmp(api.ldapai_vendor_name, LDAP_VENDOR_NAME ) != 0 ) {
    			fprintf( stderr, "LDAP vendor name mismatch: "
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				"library %s, header %s\n",
    
    				api.ldapai_vendor_name, LDAP_VENDOR_NAME );
    			exit( EXIT_FAILURE );
    		}
    
    		if( api.ldapai_vendor_version != LDAP_VENDOR_VERSION ) {
    			fprintf( stderr, "LDAP vendor version mismatch: "
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				"library %d, header %d\n",
    
    				api.ldapai_vendor_version, LDAP_VENDOR_VERSION );
    			exit( EXIT_FAILURE );
    		}
    
    		if (version) {
    			fprintf( stderr, "%s: %s\t(LDAP library: %s %d)\n",
    				prog, __Version,
    				LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION );
    			if (version > 1) exit( EXIT_SUCCESS );
    		}
    
    Hallvard Furuseth's avatar
    Hallvard Furuseth committed
    
    		ldap_memfree( api.ldapai_vendor_name );
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    		ber_memvfree( (void **)api.ldapai_extensions );
    
    	}
    
    	if (protocol == -1)
    		protocol = LDAP_VERSION3;
    
    	if (authmethod == -1 && protocol > LDAP_VERSION2) {
    
    #ifdef HAVE_CYRUS_SASL
    		authmethod = LDAP_AUTH_SASL;
    #else
    		authmethod = LDAP_AUTH_SIMPLE;
    #endif
    	}
    
    
    	if( ldapuri == NULL ) {
    		if( ldapport && ( ldaphost == NULL )) {
    			fprintf( stderr, "%s: -p without -h is invalid.\n", prog );
    			exit( EXIT_FAILURE );
    		}
    	} else {
    
    		if( ldaphost != NULL ) {
    			fprintf( stderr, "%s: -H incompatible with -h\n", prog );
    			exit( EXIT_FAILURE );
    		}
    		if( ldapport ) {
    			fprintf( stderr, "%s: -H incompatible with -p\n", prog );
    			exit( EXIT_FAILURE );
    		}