xref: /netbsd-src/external/bsd/openldap/dist/libraries/libldap/url.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1*549b59edSchristos /*	$NetBSD: url.c,v 1.3 2021/08/14 16:14:56 christos Exp $	*/
24e6df137Slukem 
32de962bdSlukem /* LIBLDAP url.c -- LDAP URL (RFC 4516) related routines */
4d11b170bStron /* $OpenLDAP$ */
52de962bdSlukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
62de962bdSlukem  *
7*549b59edSchristos  * Copyright 1998-2021 The OpenLDAP Foundation.
82de962bdSlukem  * All rights reserved.
92de962bdSlukem  *
102de962bdSlukem  * Redistribution and use in source and binary forms, with or without
112de962bdSlukem  * modification, are permitted only as authorized by the OpenLDAP
122de962bdSlukem  * Public License.
132de962bdSlukem  *
142de962bdSlukem  * A copy of this license is available in the file LICENSE in the
152de962bdSlukem  * top-level directory of the distribution or, alternatively, at
162de962bdSlukem  * <http://www.OpenLDAP.org/license.html>.
172de962bdSlukem  */
182de962bdSlukem /* Portions Copyright (c) 1996 Regents of the University of Michigan.
192de962bdSlukem  * All rights reserved.
202de962bdSlukem  */
212de962bdSlukem 
222de962bdSlukem 
232de962bdSlukem /*
242de962bdSlukem  *  LDAP URLs look like this:
25*549b59edSchristos  *    [p]ldap[is]://host[:port][/[dn[?[attributes][?[scope][?[filter][?exts]]]]]]
262de962bdSlukem  *
272de962bdSlukem  *  where:
282de962bdSlukem  *   attributes is a comma separated list
292de962bdSlukem  *   scope is one of these three strings:  base one sub (default=base)
302de962bdSlukem  *   filter is an string-represented filter as in RFC 4515
312de962bdSlukem  *
322de962bdSlukem  *  e.g.,  ldap://host:port/dc=com?o,cn?base?(o=openldap)?extension
332de962bdSlukem  *
342de962bdSlukem  *  We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl>
352de962bdSlukem  */
362de962bdSlukem 
37376af7d7Schristos #include <sys/cdefs.h>
38*549b59edSchristos __RCSID("$NetBSD: url.c,v 1.3 2021/08/14 16:14:56 christos Exp $");
39376af7d7Schristos 
402de962bdSlukem #include "portable.h"
412de962bdSlukem 
422de962bdSlukem #include <stdio.h>
432de962bdSlukem 
442de962bdSlukem #include <ac/stdlib.h>
452de962bdSlukem #include <ac/ctype.h>
462de962bdSlukem 
472de962bdSlukem #include <ac/socket.h>
482de962bdSlukem #include <ac/string.h>
492de962bdSlukem #include <ac/time.h>
502de962bdSlukem 
512de962bdSlukem #include "ldap-int.h"
522de962bdSlukem 
532de962bdSlukem /* local functions */
542de962bdSlukem static const char* skip_url_prefix LDAP_P((
552de962bdSlukem 	const char *url,
562de962bdSlukem 	int *enclosedp,
572de962bdSlukem 	const char **scheme ));
582de962bdSlukem 
ldap_pvt_url_scheme2proto(const char * scheme)592de962bdSlukem int ldap_pvt_url_scheme2proto( const char *scheme )
602de962bdSlukem {
612de962bdSlukem 	assert( scheme != NULL );
622de962bdSlukem 
632de962bdSlukem 	if( scheme == NULL ) {
642de962bdSlukem 		return -1;
652de962bdSlukem 	}
662de962bdSlukem 
67*549b59edSchristos 	if( strcmp("ldap", scheme) == 0 || strcmp("pldap", scheme) == 0 ) {
682de962bdSlukem 		return LDAP_PROTO_TCP;
692de962bdSlukem 	}
702de962bdSlukem 
712de962bdSlukem 	if( strcmp("ldapi", scheme) == 0 ) {
722de962bdSlukem 		return LDAP_PROTO_IPC;
732de962bdSlukem 	}
742de962bdSlukem 
75*549b59edSchristos 	if( strcmp("ldaps", scheme) == 0 || strcmp("pldaps", scheme) == 0 ) {
762de962bdSlukem 		return LDAP_PROTO_TCP;
772de962bdSlukem 	}
782de962bdSlukem #ifdef LDAP_CONNECTIONLESS
792de962bdSlukem 	if( strcmp("cldap", scheme) == 0 ) {
802de962bdSlukem 		return LDAP_PROTO_UDP;
812de962bdSlukem 	}
822de962bdSlukem #endif
832de962bdSlukem 
842de962bdSlukem 	return -1;
852de962bdSlukem }
862de962bdSlukem 
ldap_pvt_url_scheme_port(const char * scheme,int port)872de962bdSlukem int ldap_pvt_url_scheme_port( const char *scheme, int port )
882de962bdSlukem {
892de962bdSlukem 	assert( scheme != NULL );
902de962bdSlukem 
912de962bdSlukem 	if( port ) return port;
922de962bdSlukem 	if( scheme == NULL ) return port;
932de962bdSlukem 
94*549b59edSchristos 	if( strcmp("ldap", scheme) == 0 || strcmp("pldap", scheme) == 0 ) {
952de962bdSlukem 		return LDAP_PORT;
962de962bdSlukem 	}
972de962bdSlukem 
982de962bdSlukem 	if( strcmp("ldapi", scheme) == 0 ) {
992de962bdSlukem 		return -1;
1002de962bdSlukem 	}
1012de962bdSlukem 
102*549b59edSchristos 	if( strcmp("ldaps", scheme) == 0 || strcmp("pldaps", scheme) == 0 ) {
1032de962bdSlukem 		return LDAPS_PORT;
1042de962bdSlukem 	}
1052de962bdSlukem 
1062de962bdSlukem #ifdef LDAP_CONNECTIONLESS
1072de962bdSlukem 	if( strcmp("cldap", scheme) == 0 ) {
1082de962bdSlukem 		return LDAP_PORT;
1092de962bdSlukem 	}
1102de962bdSlukem #endif
1112de962bdSlukem 
1122de962bdSlukem 	return -1;
1132de962bdSlukem }
1142de962bdSlukem 
1152de962bdSlukem int
ldap_pvt_url_scheme2tls(const char * scheme)1162de962bdSlukem ldap_pvt_url_scheme2tls( const char *scheme )
1172de962bdSlukem {
1182de962bdSlukem 	assert( scheme != NULL );
1192de962bdSlukem 
1202de962bdSlukem 	if( scheme == NULL ) {
1212de962bdSlukem 		return -1;
1222de962bdSlukem 	}
1232de962bdSlukem 
124*549b59edSchristos 	return strcmp("ldaps", scheme) == 0 || strcmp("pldaps", scheme) == 0;
125*549b59edSchristos }
126*549b59edSchristos 
127*549b59edSchristos int
ldap_pvt_url_scheme2proxied(const char * scheme)128*549b59edSchristos ldap_pvt_url_scheme2proxied( const char *scheme )
129*549b59edSchristos {
130*549b59edSchristos 	assert( scheme != NULL );
131*549b59edSchristos 
132*549b59edSchristos 	if( scheme == NULL ) {
133*549b59edSchristos 		return -1;
134*549b59edSchristos 	}
135*549b59edSchristos 
136*549b59edSchristos 	return strcmp("pldap", scheme) == 0 || strcmp("pldaps", scheme) == 0;
1372de962bdSlukem }
1382de962bdSlukem 
1392de962bdSlukem int
ldap_is_ldap_url(LDAP_CONST char * url)1402de962bdSlukem ldap_is_ldap_url( LDAP_CONST char *url )
1412de962bdSlukem {
1422de962bdSlukem 	int	enclosed;
1432de962bdSlukem 	const char * scheme;
1442de962bdSlukem 
1452de962bdSlukem 	if( url == NULL ) {
1462de962bdSlukem 		return 0;
1472de962bdSlukem 	}
1482de962bdSlukem 
1492de962bdSlukem 	if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
1502de962bdSlukem 		return 0;
1512de962bdSlukem 	}
1522de962bdSlukem 
1532de962bdSlukem 	return 1;
1542de962bdSlukem }
1552de962bdSlukem 
1562de962bdSlukem int
ldap_is_ldaps_url(LDAP_CONST char * url)1572de962bdSlukem ldap_is_ldaps_url( LDAP_CONST char *url )
1582de962bdSlukem {
1592de962bdSlukem 	int	enclosed;
1602de962bdSlukem 	const char * scheme;
1612de962bdSlukem 
1622de962bdSlukem 	if( url == NULL ) {
1632de962bdSlukem 		return 0;
1642de962bdSlukem 	}
1652de962bdSlukem 
1662de962bdSlukem 	if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
1672de962bdSlukem 		return 0;
1682de962bdSlukem 	}
1692de962bdSlukem 
170*549b59edSchristos 	return strcmp(scheme, "ldaps") == 0 || strcmp(scheme, "pldaps") == 0;
1712de962bdSlukem }
1722de962bdSlukem 
1732de962bdSlukem int
ldap_is_ldapi_url(LDAP_CONST char * url)1742de962bdSlukem ldap_is_ldapi_url( LDAP_CONST char *url )
1752de962bdSlukem {
1762de962bdSlukem 	int	enclosed;
1772de962bdSlukem 	const char * scheme;
1782de962bdSlukem 
1792de962bdSlukem 	if( url == NULL ) {
1802de962bdSlukem 		return 0;
1812de962bdSlukem 	}
1822de962bdSlukem 
1832de962bdSlukem 	if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
1842de962bdSlukem 		return 0;
1852de962bdSlukem 	}
1862de962bdSlukem 
1872de962bdSlukem 	return strcmp(scheme, "ldapi") == 0;
1882de962bdSlukem }
1892de962bdSlukem 
1902de962bdSlukem #ifdef LDAP_CONNECTIONLESS
1912de962bdSlukem int
ldap_is_ldapc_url(LDAP_CONST char * url)1922de962bdSlukem ldap_is_ldapc_url( LDAP_CONST char *url )
1932de962bdSlukem {
1942de962bdSlukem 	int	enclosed;
1952de962bdSlukem 	const char * scheme;
1962de962bdSlukem 
1972de962bdSlukem 	if( url == NULL ) {
1982de962bdSlukem 		return 0;
1992de962bdSlukem 	}
2002de962bdSlukem 
2012de962bdSlukem 	if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
2022de962bdSlukem 		return 0;
2032de962bdSlukem 	}
2042de962bdSlukem 
2052de962bdSlukem 	return strcmp(scheme, "cldap") == 0;
2062de962bdSlukem }
2072de962bdSlukem #endif
2082de962bdSlukem 
2092de962bdSlukem static const char*
skip_url_prefix(const char * url,int * enclosedp,const char ** scheme)2102de962bdSlukem skip_url_prefix(
2112de962bdSlukem 	const char *url,
2122de962bdSlukem 	int *enclosedp,
2132de962bdSlukem 	const char **scheme )
2142de962bdSlukem {
2152de962bdSlukem 	/*
2162de962bdSlukem  	 * return non-zero if this looks like a LDAP URL; zero if not
2172de962bdSlukem  	 * if non-zero returned, *urlp will be moved past "ldap://" part of URL
2182de962bdSlukem  	 */
2192de962bdSlukem 	const char *p;
2202de962bdSlukem 
2212de962bdSlukem 	if ( url == NULL ) {
2222de962bdSlukem 		return( NULL );
2232de962bdSlukem 	}
2242de962bdSlukem 
2252de962bdSlukem 	p = url;
2262de962bdSlukem 
2272de962bdSlukem 	/* skip leading '<' (if any) */
2282de962bdSlukem 	if ( *p == '<' ) {
2292de962bdSlukem 		*enclosedp = 1;
2302de962bdSlukem 		++p;
2312de962bdSlukem 	} else {
2322de962bdSlukem 		*enclosedp = 0;
2332de962bdSlukem 	}
2342de962bdSlukem 
2352de962bdSlukem 	/* skip leading "URL:" (if any) */
2362de962bdSlukem 	if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) {
2372de962bdSlukem 		p += LDAP_URL_URLCOLON_LEN;
2382de962bdSlukem 	}
2392de962bdSlukem 
2402de962bdSlukem 	/* check for "ldap://" prefix */
2412de962bdSlukem 	if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
2422de962bdSlukem 		/* skip over "ldap://" prefix and return success */
2432de962bdSlukem 		p += LDAP_URL_PREFIX_LEN;
2442de962bdSlukem 		*scheme = "ldap";
2452de962bdSlukem 		return( p );
2462de962bdSlukem 	}
2472de962bdSlukem 
248*549b59edSchristos 	/* check for "pldap://" prefix */
249*549b59edSchristos 	if ( strncasecmp( p, PLDAP_URL_PREFIX, PLDAP_URL_PREFIX_LEN ) == 0 ) {
250*549b59edSchristos 		/* skip over "pldap://" prefix and return success */
251*549b59edSchristos 		p += PLDAP_URL_PREFIX_LEN;
252*549b59edSchristos 		*scheme = "pldap";
253*549b59edSchristos 		return( p );
254*549b59edSchristos 	}
255*549b59edSchristos 
2562de962bdSlukem 	/* check for "ldaps://" prefix */
2572de962bdSlukem 	if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
2582de962bdSlukem 		/* skip over "ldaps://" prefix and return success */
2592de962bdSlukem 		p += LDAPS_URL_PREFIX_LEN;
2602de962bdSlukem 		*scheme = "ldaps";
2612de962bdSlukem 		return( p );
2622de962bdSlukem 	}
2632de962bdSlukem 
264*549b59edSchristos 	/* check for "pldaps://" prefix */
265*549b59edSchristos 	if ( strncasecmp( p, PLDAPS_URL_PREFIX, PLDAPS_URL_PREFIX_LEN ) == 0 ) {
266*549b59edSchristos 		/* skip over "pldaps://" prefix and return success */
267*549b59edSchristos 		p += PLDAPS_URL_PREFIX_LEN;
268*549b59edSchristos 		*scheme = "pldaps";
269*549b59edSchristos 		return( p );
270*549b59edSchristos 	}
271*549b59edSchristos 
2722de962bdSlukem 	/* check for "ldapi://" prefix */
2732de962bdSlukem 	if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) {
2742de962bdSlukem 		/* skip over "ldapi://" prefix and return success */
2752de962bdSlukem 		p += LDAPI_URL_PREFIX_LEN;
2762de962bdSlukem 		*scheme = "ldapi";
2772de962bdSlukem 		return( p );
2782de962bdSlukem 	}
2792de962bdSlukem 
2802de962bdSlukem #ifdef LDAP_CONNECTIONLESS
2812de962bdSlukem 	/* check for "cldap://" prefix */
2822de962bdSlukem 	if ( strncasecmp( p, LDAPC_URL_PREFIX, LDAPC_URL_PREFIX_LEN ) == 0 ) {
2832de962bdSlukem 		/* skip over "cldap://" prefix and return success */
2842de962bdSlukem 		p += LDAPC_URL_PREFIX_LEN;
2852de962bdSlukem 		*scheme = "cldap";
2862de962bdSlukem 		return( p );
2872de962bdSlukem 	}
2882de962bdSlukem #endif
2892de962bdSlukem 
2902de962bdSlukem 	return( NULL );
2912de962bdSlukem }
2922de962bdSlukem 
2932de962bdSlukem int
ldap_pvt_scope2bv(int scope,struct berval * bv)2942de962bdSlukem ldap_pvt_scope2bv( int scope, struct berval *bv )
2952de962bdSlukem {
2962de962bdSlukem 	switch ( scope ) {
2972de962bdSlukem 	case LDAP_SCOPE_BASE:
2982de962bdSlukem 		BER_BVSTR( bv, "base" );
2992de962bdSlukem 		break;
3002de962bdSlukem 
3012de962bdSlukem 	case LDAP_SCOPE_ONELEVEL:
3022de962bdSlukem 		BER_BVSTR( bv, "one" );
3032de962bdSlukem 		break;
3042de962bdSlukem 
3052de962bdSlukem 	case LDAP_SCOPE_SUBTREE:
3062de962bdSlukem 		BER_BVSTR( bv, "sub" );
3072de962bdSlukem 		break;
3082de962bdSlukem 
3092de962bdSlukem 	case LDAP_SCOPE_SUBORDINATE:
3102de962bdSlukem 		BER_BVSTR( bv, "subordinate" );
3112de962bdSlukem 		break;
3122de962bdSlukem 
3132de962bdSlukem 	default:
3142de962bdSlukem 		return LDAP_OTHER;
3152de962bdSlukem 	}
3162de962bdSlukem 
3172de962bdSlukem 	return LDAP_SUCCESS;
3182de962bdSlukem }
3192de962bdSlukem 
3202de962bdSlukem const char *
ldap_pvt_scope2str(int scope)3212de962bdSlukem ldap_pvt_scope2str( int scope )
3222de962bdSlukem {
3232de962bdSlukem 	struct berval	bv;
3242de962bdSlukem 
3252de962bdSlukem 	if ( ldap_pvt_scope2bv( scope, &bv ) == LDAP_SUCCESS ) {
3262de962bdSlukem 		return bv.bv_val;
3272de962bdSlukem 	}
3282de962bdSlukem 
3292de962bdSlukem 	return NULL;
3302de962bdSlukem }
3312de962bdSlukem 
3322de962bdSlukem int
ldap_pvt_bv2scope(struct berval * bv)3332de962bdSlukem ldap_pvt_bv2scope( struct berval *bv )
3342de962bdSlukem {
3352de962bdSlukem 	static struct {
3362de962bdSlukem 		struct berval	bv;
3372de962bdSlukem 		int		scope;
3382de962bdSlukem 	}	v[] = {
3392de962bdSlukem 		{ BER_BVC( "one" ),		LDAP_SCOPE_ONELEVEL },
3402de962bdSlukem 		{ BER_BVC( "onelevel" ),	LDAP_SCOPE_ONELEVEL },
3412de962bdSlukem 		{ BER_BVC( "base" ),		LDAP_SCOPE_BASE },
3422de962bdSlukem 		{ BER_BVC( "sub" ),		LDAP_SCOPE_SUBTREE },
3432de962bdSlukem 		{ BER_BVC( "subtree" ),		LDAP_SCOPE_SUBTREE },
3442de962bdSlukem 		{ BER_BVC( "subord" ),		LDAP_SCOPE_SUBORDINATE },
3452de962bdSlukem 		{ BER_BVC( "subordinate" ),	LDAP_SCOPE_SUBORDINATE },
3462de962bdSlukem 		{ BER_BVC( "children" ),	LDAP_SCOPE_SUBORDINATE },
3472de962bdSlukem 		{ BER_BVNULL,			-1 }
3482de962bdSlukem 	};
3492de962bdSlukem 	int	i;
3502de962bdSlukem 
3512de962bdSlukem 	for ( i = 0; v[ i ].scope != -1; i++ ) {
3522de962bdSlukem 		if ( ber_bvstrcasecmp( bv, &v[ i ].bv ) == 0 ) {
3532de962bdSlukem 			return v[ i ].scope;
3542de962bdSlukem 		}
3552de962bdSlukem 	}
3562de962bdSlukem 
3572de962bdSlukem 	return( -1 );
3582de962bdSlukem }
3592de962bdSlukem 
3602de962bdSlukem int
ldap_pvt_str2scope(const char * p)3612de962bdSlukem ldap_pvt_str2scope( const char *p )
3622de962bdSlukem {
3632de962bdSlukem 	struct berval	bv;
3642de962bdSlukem 
3652de962bdSlukem 	ber_str2bv( p, 0, 0, &bv );
3662de962bdSlukem 
3672de962bdSlukem 	return ldap_pvt_bv2scope( &bv );
3682de962bdSlukem }
3692de962bdSlukem 
3702de962bdSlukem static const char	hex[] = "0123456789ABCDEF";
3712de962bdSlukem 
3722de962bdSlukem #define URLESC_NONE	0x0000U
3732de962bdSlukem #define URLESC_COMMA	0x0001U
3742de962bdSlukem #define URLESC_SLASH	0x0002U
3752de962bdSlukem 
3762de962bdSlukem static int
hex_escape_len(const char * s,unsigned list)3772de962bdSlukem hex_escape_len( const char *s, unsigned list )
3782de962bdSlukem {
3792de962bdSlukem 	int	len;
3802de962bdSlukem 
3812de962bdSlukem 	if ( s == NULL ) {
3822de962bdSlukem 		return 0;
3832de962bdSlukem 	}
3842de962bdSlukem 
3852de962bdSlukem 	for ( len = 0; s[0]; s++ ) {
3862de962bdSlukem 		switch ( s[0] ) {
3872de962bdSlukem 		/* RFC 2396: reserved */
3882de962bdSlukem 		case '?':
3892de962bdSlukem 			len += 3;
3902de962bdSlukem 			break;
3912de962bdSlukem 
3922de962bdSlukem 		case ',':
3932de962bdSlukem 			if ( list & URLESC_COMMA ) {
3942de962bdSlukem 				len += 3;
3952de962bdSlukem 			} else {
3962de962bdSlukem 				len++;
3972de962bdSlukem 			}
3982de962bdSlukem 			break;
3992de962bdSlukem 
4002de962bdSlukem 		case '/':
4012de962bdSlukem 			if ( list & URLESC_SLASH ) {
4022de962bdSlukem 				len += 3;
4032de962bdSlukem 			} else {
4042de962bdSlukem 				len++;
4052de962bdSlukem 			}
4062de962bdSlukem 			break;
4072de962bdSlukem 
4082de962bdSlukem 		case ';':
4092de962bdSlukem 		case ':':
4102de962bdSlukem 		case '@':
4112de962bdSlukem 		case '&':
4122de962bdSlukem 		case '=':
4132de962bdSlukem 		case '+':
4142de962bdSlukem 		case '$':
4152de962bdSlukem 
4162de962bdSlukem 		/* RFC 2396: unreserved mark */
4172de962bdSlukem 		case '-':
4182de962bdSlukem 		case '_':
4192de962bdSlukem 		case '.':
4202de962bdSlukem 		case '!':
4212de962bdSlukem 		case '~':
4222de962bdSlukem 		case '*':
4232de962bdSlukem 		case '\'':
4242de962bdSlukem 		case '(':
4252de962bdSlukem 		case ')':
4262de962bdSlukem 			len++;
4272de962bdSlukem 			break;
4282de962bdSlukem 
4292de962bdSlukem 		/* RFC 2396: unreserved alphanum */
4302de962bdSlukem 		default:
4312de962bdSlukem 			if ( !isalnum( (unsigned char) s[0] ) ) {
4322de962bdSlukem 				len += 3;
4332de962bdSlukem 			} else {
4342de962bdSlukem 				len++;
4352de962bdSlukem 			}
4362de962bdSlukem 			break;
4372de962bdSlukem 		}
4382de962bdSlukem 	}
4392de962bdSlukem 
4402de962bdSlukem 	return len;
4412de962bdSlukem }
4422de962bdSlukem 
4432de962bdSlukem static int
hex_escape(char * buf,int len,const char * s,unsigned list)4442de962bdSlukem hex_escape( char *buf, int len, const char *s, unsigned list )
4452de962bdSlukem {
4462de962bdSlukem 	int	i;
4472de962bdSlukem 	int	pos;
4482de962bdSlukem 
4492de962bdSlukem 	if ( s == NULL ) {
4502de962bdSlukem 		return 0;
4512de962bdSlukem 	}
4522de962bdSlukem 
4532de962bdSlukem 	for ( pos = 0, i = 0; s[i] && pos < len; i++ ) {
4542de962bdSlukem 		int	escape = 0;
4552de962bdSlukem 
4562de962bdSlukem 		switch ( s[i] ) {
4572de962bdSlukem 		/* RFC 2396: reserved */
4582de962bdSlukem 		case '?':
4592de962bdSlukem 			escape = 1;
4602de962bdSlukem 			break;
4612de962bdSlukem 
4622de962bdSlukem 		case ',':
4632de962bdSlukem 			if ( list & URLESC_COMMA ) {
4642de962bdSlukem 				escape = 1;
4652de962bdSlukem 			}
4662de962bdSlukem 			break;
4672de962bdSlukem 
4682de962bdSlukem 		case '/':
4692de962bdSlukem 			if ( list & URLESC_SLASH ) {
4702de962bdSlukem 				escape = 1;
4712de962bdSlukem 			}
4722de962bdSlukem 			break;
4732de962bdSlukem 
4742de962bdSlukem 		case ';':
4752de962bdSlukem 		case ':':
4762de962bdSlukem 		case '@':
4772de962bdSlukem 		case '&':
4782de962bdSlukem 		case '=':
4792de962bdSlukem 		case '+':
4802de962bdSlukem 		case '$':
4812de962bdSlukem 
4822de962bdSlukem 		/* RFC 2396: unreserved mark */
4832de962bdSlukem 		case '-':
4842de962bdSlukem 		case '_':
4852de962bdSlukem 		case '.':
4862de962bdSlukem 		case '!':
4872de962bdSlukem 		case '~':
4882de962bdSlukem 		case '*':
4892de962bdSlukem 		case '\'':
4902de962bdSlukem 		case '(':
4912de962bdSlukem 		case ')':
4922de962bdSlukem 			break;
4932de962bdSlukem 
4942de962bdSlukem 		/* RFC 2396: unreserved alphanum */
4952de962bdSlukem 		default:
4962de962bdSlukem 			if ( !isalnum( (unsigned char) s[i] ) ) {
4972de962bdSlukem 				escape = 1;
4982de962bdSlukem 			}
4992de962bdSlukem 			break;
5002de962bdSlukem 		}
5012de962bdSlukem 
5022de962bdSlukem 		if ( escape ) {
5032de962bdSlukem 			buf[pos++] = '%';
5042de962bdSlukem 			buf[pos++] = hex[ (s[i] >> 4) & 0x0f ];
5052de962bdSlukem 			buf[pos++] = hex[ s[i] & 0x0f ];
5062de962bdSlukem 
5072de962bdSlukem 		} else {
5082de962bdSlukem 			buf[pos++] = s[i];
5092de962bdSlukem 		}
5102de962bdSlukem 	}
5112de962bdSlukem 
5122de962bdSlukem 	buf[pos] = '\0';
5132de962bdSlukem 
5142de962bdSlukem 	return pos;
5152de962bdSlukem }
5162de962bdSlukem 
5172de962bdSlukem static int
hex_escape_len_list(char ** s,unsigned flags)5182de962bdSlukem hex_escape_len_list( char **s, unsigned flags )
5192de962bdSlukem {
5202de962bdSlukem 	int	len;
5212de962bdSlukem 	int	i;
5222de962bdSlukem 
5232de962bdSlukem 	if ( s == NULL ) {
5242de962bdSlukem 		return 0;
5252de962bdSlukem 	}
5262de962bdSlukem 
5272de962bdSlukem 	len = 0;
5282de962bdSlukem 	for ( i = 0; s[i] != NULL; i++ ) {
5292de962bdSlukem 		if ( len ) {
5302de962bdSlukem 			len++;
5312de962bdSlukem 		}
5322de962bdSlukem 		len += hex_escape_len( s[i], flags );
5332de962bdSlukem 	}
5342de962bdSlukem 
5352de962bdSlukem 	return len;
5362de962bdSlukem }
5372de962bdSlukem 
5382de962bdSlukem static int
hex_escape_list(char * buf,int len,char ** s,unsigned flags)5392de962bdSlukem hex_escape_list( char *buf, int len, char **s, unsigned flags )
5402de962bdSlukem {
5412de962bdSlukem 	int	pos;
5422de962bdSlukem 	int	i;
5432de962bdSlukem 
5442de962bdSlukem 	if ( s == NULL ) {
5452de962bdSlukem 		return 0;
5462de962bdSlukem 	}
5472de962bdSlukem 
5482de962bdSlukem 	pos = 0;
5492de962bdSlukem 	for ( i = 0; s[i] != NULL; i++ ) {
5502de962bdSlukem 		int	curlen;
5512de962bdSlukem 
5522de962bdSlukem 		if ( pos ) {
5532de962bdSlukem 			buf[pos++] = ',';
5542de962bdSlukem 			len--;
5552de962bdSlukem 		}
5562de962bdSlukem 		curlen = hex_escape( &buf[pos], len, s[i], flags );
5572de962bdSlukem 		len -= curlen;
5582de962bdSlukem 		pos += curlen;
5592de962bdSlukem 	}
5602de962bdSlukem 
5612de962bdSlukem 	return pos;
5622de962bdSlukem }
5632de962bdSlukem 
5642de962bdSlukem static int
desc2str_len(LDAPURLDesc * u)5652de962bdSlukem desc2str_len( LDAPURLDesc *u )
5662de962bdSlukem {
5672de962bdSlukem 	int		sep = 0;
5682de962bdSlukem 	int		len = 0;
5692de962bdSlukem 	int		is_ipc = 0;
5702de962bdSlukem 	struct berval	scope;
5712de962bdSlukem 
5722de962bdSlukem 	if ( u == NULL || u->lud_scheme == NULL ) {
5732de962bdSlukem 		return -1;
5742de962bdSlukem 	}
5752de962bdSlukem 
5762de962bdSlukem 	if ( !strcmp( "ldapi", u->lud_scheme )) {
5772de962bdSlukem 		is_ipc = 1;
5782de962bdSlukem 	}
5792de962bdSlukem 
5802de962bdSlukem 	if ( u->lud_exts ) {
5812de962bdSlukem 		len += hex_escape_len_list( u->lud_exts, URLESC_COMMA );
5822de962bdSlukem 		if ( !sep ) {
5832de962bdSlukem 			sep = 5;
5842de962bdSlukem 		}
5852de962bdSlukem 	}
5862de962bdSlukem 
5872de962bdSlukem 	if ( u->lud_filter ) {
5882de962bdSlukem 		len += hex_escape_len( u->lud_filter, URLESC_NONE );
5892de962bdSlukem 		if ( !sep ) {
5902de962bdSlukem 			sep = 4;
5912de962bdSlukem 		}
5922de962bdSlukem 	}
5932de962bdSlukem 
5942de962bdSlukem 	if ( ldap_pvt_scope2bv( u->lud_scope, &scope ) == LDAP_SUCCESS ) {
5952de962bdSlukem 		len += scope.bv_len;
5962de962bdSlukem 		if ( !sep ) {
5972de962bdSlukem 			sep = 3;
5982de962bdSlukem 		}
5992de962bdSlukem 	}
6002de962bdSlukem 
6012de962bdSlukem 	if ( u->lud_attrs ) {
6022de962bdSlukem 		len += hex_escape_len_list( u->lud_attrs, URLESC_NONE );
6032de962bdSlukem 		if ( !sep ) {
6042de962bdSlukem 			sep = 2;
6052de962bdSlukem 		}
6062de962bdSlukem 	}
6072de962bdSlukem 
6082de962bdSlukem 	if ( u->lud_dn && u->lud_dn[0] ) {
6092de962bdSlukem 		len += hex_escape_len( u->lud_dn, URLESC_NONE );
6102de962bdSlukem 		if ( !sep ) {
6112de962bdSlukem 			sep = 1;
6122de962bdSlukem 		}
6132de962bdSlukem 	};
6142de962bdSlukem 
6152de962bdSlukem 	len += sep;
6162de962bdSlukem 
6172de962bdSlukem 	if ( u->lud_port ) {
6182de962bdSlukem 		unsigned p = u->lud_port;
6192de962bdSlukem 		if ( p > 65535 )
6202de962bdSlukem 			return -1;
6212de962bdSlukem 
6222de962bdSlukem 		len += (p > 999 ? 5 + (p > 9999) : p > 99 ? 4 : 2 + (p > 9));
6232de962bdSlukem 	}
6242de962bdSlukem 
6252de962bdSlukem 	if ( u->lud_host && u->lud_host[0] ) {
6264e6df137Slukem 		char *ptr;
6272de962bdSlukem 		len += hex_escape_len( u->lud_host, URLESC_SLASH );
6284e6df137Slukem 		if ( !is_ipc && ( ptr = strchr( u->lud_host, ':' ))) {
6294e6df137Slukem 			if ( strchr( ptr+1, ':' ))
6302de962bdSlukem 				len += 2;	/* IPv6, [] */
6312de962bdSlukem 		}
6322de962bdSlukem 	}
6332de962bdSlukem 
6342de962bdSlukem 	len += strlen( u->lud_scheme ) + STRLENOF( "://" );
6352de962bdSlukem 
6362de962bdSlukem 	return len;
6372de962bdSlukem }
6382de962bdSlukem 
6392de962bdSlukem static int
desc2str(LDAPURLDesc * u,char * s,int len)6402de962bdSlukem desc2str( LDAPURLDesc *u, char *s, int len )
6412de962bdSlukem {
6422de962bdSlukem 	int		i;
6432de962bdSlukem 	int		sep = 0;
6442de962bdSlukem 	int		sofar = 0;
6452de962bdSlukem 	int		is_v6 = 0;
6462de962bdSlukem 	int		is_ipc = 0;
6472de962bdSlukem 	struct berval	scope = BER_BVNULL;
6484e6df137Slukem 	char		*ptr;
6492de962bdSlukem 
6502de962bdSlukem 	if ( u == NULL ) {
6512de962bdSlukem 		return -1;
6522de962bdSlukem 	}
6532de962bdSlukem 
6542de962bdSlukem 	if ( s == NULL ) {
6552de962bdSlukem 		return -1;
6562de962bdSlukem 	}
6572de962bdSlukem 
6582de962bdSlukem 	if ( u->lud_scheme && !strcmp( "ldapi", u->lud_scheme )) {
6592de962bdSlukem 		is_ipc = 1;
6602de962bdSlukem 	}
6612de962bdSlukem 
6622de962bdSlukem 	ldap_pvt_scope2bv( u->lud_scope, &scope );
6632de962bdSlukem 
6642de962bdSlukem 	if ( u->lud_exts ) {
6652de962bdSlukem 		sep = 5;
6662de962bdSlukem 	} else if ( u->lud_filter ) {
6672de962bdSlukem 		sep = 4;
6682de962bdSlukem 	} else if ( !BER_BVISEMPTY( &scope ) ) {
6692de962bdSlukem 		sep = 3;
6702de962bdSlukem 	} else if ( u->lud_attrs ) {
6712de962bdSlukem 		sep = 2;
6722de962bdSlukem 	} else if ( u->lud_dn && u->lud_dn[0] ) {
6732de962bdSlukem 		sep = 1;
6742de962bdSlukem 	}
6752de962bdSlukem 
6764e6df137Slukem 	if ( !is_ipc && u->lud_host && ( ptr = strchr( u->lud_host, ':' ))) {
6774e6df137Slukem 		if ( strchr( ptr+1, ':' ))
6782de962bdSlukem 			is_v6 = 1;
6792de962bdSlukem 	}
6802de962bdSlukem 
6812de962bdSlukem 	if ( u->lud_port ) {
6822de962bdSlukem 		sofar = sprintf( s, "%s://%s%s%s:%d", u->lud_scheme,
6832de962bdSlukem 				is_v6 ? "[" : "",
6842de962bdSlukem 				u->lud_host ? u->lud_host : "",
6852de962bdSlukem 				is_v6 ? "]" : "",
6862de962bdSlukem 				u->lud_port );
6872de962bdSlukem 		len -= sofar;
6882de962bdSlukem 
6892de962bdSlukem 	} else {
6902de962bdSlukem 		sofar = sprintf( s, "%s://", u->lud_scheme );
6912de962bdSlukem 		len -= sofar;
6922de962bdSlukem 		if ( u->lud_host && u->lud_host[0] ) {
6932de962bdSlukem 			if ( is_v6 ) {
6942de962bdSlukem 				s[sofar++] = '[';
6952de962bdSlukem 				len--;
6962de962bdSlukem 			}
6972de962bdSlukem 			i = hex_escape( &s[sofar], len, u->lud_host, URLESC_SLASH );
6982de962bdSlukem 			sofar += i;
6992de962bdSlukem 			len -= i;
7002de962bdSlukem 			if ( is_v6 ) {
7012de962bdSlukem 				s[sofar++] = ']';
7022de962bdSlukem 				len--;
7032de962bdSlukem 			}
7042de962bdSlukem 		}
7052de962bdSlukem 	}
7062de962bdSlukem 
7072de962bdSlukem 	assert( len >= 0 );
7082de962bdSlukem 
7092de962bdSlukem 	if ( sep < 1 ) {
7102de962bdSlukem 		goto done;
7112de962bdSlukem 	}
7122de962bdSlukem 
7132de962bdSlukem 	s[sofar++] = '/';
7142de962bdSlukem 	len--;
7152de962bdSlukem 
7162de962bdSlukem 	assert( len >= 0 );
7172de962bdSlukem 
7182de962bdSlukem 	if ( u->lud_dn && u->lud_dn[0] ) {
7192de962bdSlukem 		i = hex_escape( &s[sofar], len, u->lud_dn, URLESC_NONE );
7202de962bdSlukem 		sofar += i;
7212de962bdSlukem 		len -= i;
7222de962bdSlukem 
7232de962bdSlukem 		assert( len >= 0 );
7242de962bdSlukem 	}
7252de962bdSlukem 
7262de962bdSlukem 	if ( sep < 2 ) {
7272de962bdSlukem 		goto done;
7282de962bdSlukem 	}
7292de962bdSlukem 	s[sofar++] = '?';
7302de962bdSlukem 	len--;
7312de962bdSlukem 
7322de962bdSlukem 	assert( len >= 0 );
7332de962bdSlukem 
7342de962bdSlukem 	i = hex_escape_list( &s[sofar], len, u->lud_attrs, URLESC_NONE );
7352de962bdSlukem 	sofar += i;
7362de962bdSlukem 	len -= i;
7372de962bdSlukem 
7382de962bdSlukem 	assert( len >= 0 );
7392de962bdSlukem 
7402de962bdSlukem 	if ( sep < 3 ) {
7412de962bdSlukem 		goto done;
7422de962bdSlukem 	}
7432de962bdSlukem 	s[sofar++] = '?';
7442de962bdSlukem 	len--;
7452de962bdSlukem 
7462de962bdSlukem 	assert( len >= 0 );
7472de962bdSlukem 
7482de962bdSlukem 	if ( !BER_BVISNULL( &scope ) ) {
7492de962bdSlukem 		strcpy( &s[sofar], scope.bv_val );
7502de962bdSlukem 		sofar += scope.bv_len;
7512de962bdSlukem 		len -= scope.bv_len;
7522de962bdSlukem 	}
7532de962bdSlukem 
7542de962bdSlukem 	assert( len >= 0 );
7552de962bdSlukem 
7562de962bdSlukem 	if ( sep < 4 ) {
7572de962bdSlukem 		goto done;
7582de962bdSlukem 	}
7592de962bdSlukem 	s[sofar++] = '?';
7602de962bdSlukem 	len--;
7612de962bdSlukem 
7622de962bdSlukem 	assert( len >= 0 );
7632de962bdSlukem 
7642de962bdSlukem 	i = hex_escape( &s[sofar], len, u->lud_filter, URLESC_NONE );
7652de962bdSlukem 	sofar += i;
7662de962bdSlukem 	len -= i;
7672de962bdSlukem 
7682de962bdSlukem 	assert( len >= 0 );
7692de962bdSlukem 
7702de962bdSlukem 	if ( sep < 5 ) {
7712de962bdSlukem 		goto done;
7722de962bdSlukem 	}
7732de962bdSlukem 	s[sofar++] = '?';
7742de962bdSlukem 	len--;
7752de962bdSlukem 
7762de962bdSlukem 	assert( len >= 0 );
7772de962bdSlukem 
7782de962bdSlukem 	i = hex_escape_list( &s[sofar], len, u->lud_exts, URLESC_COMMA );
7792de962bdSlukem 	sofar += i;
7802de962bdSlukem 	len -= i;
7812de962bdSlukem 
7822de962bdSlukem 	assert( len >= 0 );
7832de962bdSlukem 
7842de962bdSlukem done:
7852de962bdSlukem 	if ( len < 0 ) {
7862de962bdSlukem 		return -1;
7872de962bdSlukem 	}
7882de962bdSlukem 
7892de962bdSlukem 	return sofar;
7902de962bdSlukem }
7912de962bdSlukem 
7922de962bdSlukem char *
ldap_url_desc2str(LDAPURLDesc * u)7932de962bdSlukem ldap_url_desc2str( LDAPURLDesc *u )
7942de962bdSlukem {
7952de962bdSlukem 	int	len;
7962de962bdSlukem 	char	*s;
7972de962bdSlukem 
7982de962bdSlukem 	if ( u == NULL ) {
7992de962bdSlukem 		return NULL;
8002de962bdSlukem 	}
8012de962bdSlukem 
8022de962bdSlukem 	len = desc2str_len( u );
8032de962bdSlukem 	if ( len < 0 ) {
8042de962bdSlukem 		return NULL;
8052de962bdSlukem 	}
8062de962bdSlukem 
8072de962bdSlukem 	/* allocate enough to hex escape everything -- overkill */
8082de962bdSlukem 	s = LDAP_MALLOC( len + 1 );
8092de962bdSlukem 
8102de962bdSlukem 	if ( s == NULL ) {
8112de962bdSlukem 		return NULL;
8122de962bdSlukem 	}
8132de962bdSlukem 
8142de962bdSlukem 	if ( desc2str( u, s, len ) != len ) {
8152de962bdSlukem 		LDAP_FREE( s );
8162de962bdSlukem 		return NULL;
8172de962bdSlukem 	}
8182de962bdSlukem 
8192de962bdSlukem 	s[len] = '\0';
8202de962bdSlukem 
8212de962bdSlukem 	return s;
8222de962bdSlukem }
8232de962bdSlukem 
8242de962bdSlukem int
ldap_url_parse_ext(LDAP_CONST char * url_in,LDAPURLDesc ** ludpp,unsigned flags)8252de962bdSlukem ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp, unsigned flags )
8262de962bdSlukem {
8272de962bdSlukem /*
8282de962bdSlukem  *  Pick apart the pieces of an LDAP URL.
8292de962bdSlukem  */
8302de962bdSlukem 
8312de962bdSlukem 	LDAPURLDesc	*ludp;
8322de962bdSlukem 	char	*p, *q, *r;
8332de962bdSlukem 	int		i, enclosed, proto, is_v6 = 0;
8342de962bdSlukem 	const char *scheme = NULL;
8352de962bdSlukem 	const char *url_tmp;
8362de962bdSlukem 	char *url;
8372de962bdSlukem 
8382de962bdSlukem 	int	check_dn = 1;
8392de962bdSlukem 
8402de962bdSlukem 	if( url_in == NULL || ludpp == NULL ) {
8412de962bdSlukem 		return LDAP_URL_ERR_PARAM;
8422de962bdSlukem 	}
8432de962bdSlukem 
8442de962bdSlukem #ifndef LDAP_INT_IN_KERNEL
8452de962bdSlukem 	/* Global options may not be created yet
8462de962bdSlukem 	 * We can't test if the global options are initialized
8472de962bdSlukem 	 * because a call to LDAP_INT_GLOBAL_OPT() will try to allocate
8482de962bdSlukem 	 * the options and cause infinite recursion
8492de962bdSlukem 	 */
850*549b59edSchristos 	Debug1( LDAP_DEBUG_TRACE, "ldap_url_parse_ext(%s)\n", url_in );
8512de962bdSlukem #endif
8522de962bdSlukem 
8532de962bdSlukem 	*ludpp = NULL;	/* pessimistic */
8542de962bdSlukem 
8552de962bdSlukem 	url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
8562de962bdSlukem 
8572de962bdSlukem 	if ( url_tmp == NULL ) {
8582de962bdSlukem 		return LDAP_URL_ERR_BADSCHEME;
8592de962bdSlukem 	}
8602de962bdSlukem 
8612de962bdSlukem 	assert( scheme != NULL );
8622de962bdSlukem 
8632de962bdSlukem 	proto = ldap_pvt_url_scheme2proto( scheme );
8642de962bdSlukem 	if ( proto == -1 ) {
8652de962bdSlukem 		return LDAP_URL_ERR_BADSCHEME;
8662de962bdSlukem 	}
8672de962bdSlukem 
8682de962bdSlukem 	/* make working copy of the remainder of the URL */
8692de962bdSlukem 	url = LDAP_STRDUP( url_tmp );
8702de962bdSlukem 	if ( url == NULL ) {
8712de962bdSlukem 		return LDAP_URL_ERR_MEM;
8722de962bdSlukem 	}
8732de962bdSlukem 
8742de962bdSlukem 	if ( enclosed ) {
8752de962bdSlukem 		p = &url[strlen(url)-1];
8762de962bdSlukem 
8772de962bdSlukem 		if( *p != '>' ) {
8782de962bdSlukem 			LDAP_FREE( url );
8792de962bdSlukem 			return LDAP_URL_ERR_BADENCLOSURE;
8802de962bdSlukem 		}
8812de962bdSlukem 
8822de962bdSlukem 		*p = '\0';
8832de962bdSlukem 	}
8842de962bdSlukem 
8852de962bdSlukem 	/* allocate return struct */
8862de962bdSlukem 	ludp = (LDAPURLDesc *)LDAP_CALLOC( 1, sizeof( LDAPURLDesc ));
8872de962bdSlukem 
8882de962bdSlukem 	if ( ludp == NULL ) {
8892de962bdSlukem 		LDAP_FREE( url );
8902de962bdSlukem 		return LDAP_URL_ERR_MEM;
8912de962bdSlukem 	}
8922de962bdSlukem 
8932de962bdSlukem 	ludp->lud_next = NULL;
8942de962bdSlukem 	ludp->lud_host = NULL;
8952de962bdSlukem 	ludp->lud_port = 0;
8962de962bdSlukem 	ludp->lud_dn = NULL;
8972de962bdSlukem 	ludp->lud_attrs = NULL;
8982de962bdSlukem 	ludp->lud_scope = ( flags & LDAP_PVT_URL_PARSE_NODEF_SCOPE ) ? LDAP_SCOPE_BASE : LDAP_SCOPE_DEFAULT;
8992de962bdSlukem 	ludp->lud_filter = NULL;
9002de962bdSlukem 	ludp->lud_exts = NULL;
9012de962bdSlukem 
9022de962bdSlukem 	ludp->lud_scheme = LDAP_STRDUP( scheme );
9032de962bdSlukem 
9042de962bdSlukem 	if ( ludp->lud_scheme == NULL ) {
9052de962bdSlukem 		LDAP_FREE( url );
9062de962bdSlukem 		ldap_free_urldesc( ludp );
9072de962bdSlukem 		return LDAP_URL_ERR_MEM;
9082de962bdSlukem 	}
9092de962bdSlukem 
9102de962bdSlukem 	/* scan forward for '/' that marks end of hostport and begin. of dn */
9112de962bdSlukem 	p = strchr( url, '/' );
9122de962bdSlukem 	q = NULL;
9132de962bdSlukem 
9142de962bdSlukem 	if( p != NULL ) {
9152de962bdSlukem 		/* terminate hostport; point to start of dn */
9162de962bdSlukem 		*p++ = '\0';
9172de962bdSlukem 	} else {
9182de962bdSlukem 		/* check for Novell kludge, see below */
9192de962bdSlukem 		p = strchr( url, '?' );
9202de962bdSlukem 		if ( p ) {
9212de962bdSlukem 			*p++ = '\0';
9222de962bdSlukem 			q = p;
9232de962bdSlukem 			p = NULL;
9242de962bdSlukem 		}
9252de962bdSlukem 	}
9262de962bdSlukem 
9272de962bdSlukem 	if ( proto != LDAP_PROTO_IPC ) {
9282de962bdSlukem 		/* IPv6 syntax with [ip address]:port */
9292de962bdSlukem 		if ( *url == '[' ) {
9302de962bdSlukem 			r = strchr( url, ']' );
9312de962bdSlukem 			if ( r == NULL ) {
9322de962bdSlukem 				LDAP_FREE( url );
9332de962bdSlukem 				ldap_free_urldesc( ludp );
9342de962bdSlukem 				return LDAP_URL_ERR_BADURL;
9352de962bdSlukem 			}
9362de962bdSlukem 			*r++ = '\0';
9372de962bdSlukem 			q = strchr( r, ':' );
9382de962bdSlukem 			if ( q && q != r ) {
9392de962bdSlukem 				LDAP_FREE( url );
9402de962bdSlukem 				ldap_free_urldesc( ludp );
9412de962bdSlukem 				return LDAP_URL_ERR_BADURL;
9422de962bdSlukem 			}
9432de962bdSlukem 			is_v6 = 1;
9442de962bdSlukem 		} else {
9452de962bdSlukem 			q = strchr( url, ':' );
9462de962bdSlukem 		}
9472de962bdSlukem 
9482de962bdSlukem 		if ( q != NULL ) {
9492de962bdSlukem 			char	*next;
9502de962bdSlukem 
9512de962bdSlukem 			*q++ = '\0';
9522de962bdSlukem 			ldap_pvt_hex_unescape( q );
9532de962bdSlukem 
9542de962bdSlukem 			if( *q == '\0' ) {
9552de962bdSlukem 				LDAP_FREE( url );
9562de962bdSlukem 				ldap_free_urldesc( ludp );
9572de962bdSlukem 				return LDAP_URL_ERR_BADURL;
9582de962bdSlukem 			}
9592de962bdSlukem 
9602de962bdSlukem 			ludp->lud_port = strtol( q, &next, 10 );
9612de962bdSlukem 			if ( next == q || next[0] != '\0' ) {
9622de962bdSlukem 				LDAP_FREE( url );
9632de962bdSlukem 				ldap_free_urldesc( ludp );
9642de962bdSlukem 				return LDAP_URL_ERR_BADURL;
9652de962bdSlukem 			}
9662de962bdSlukem 			/* check for Novell kludge */
9672de962bdSlukem 			if ( !p ) {
9682de962bdSlukem 				if ( *next != '\0' ) {
9692de962bdSlukem 					q = &next[1];
9702de962bdSlukem 				} else {
9712de962bdSlukem 					q = NULL;
9722de962bdSlukem 				}
9732de962bdSlukem 			}
9742de962bdSlukem 		}
9752de962bdSlukem 
9762de962bdSlukem 		if ( ( flags & LDAP_PVT_URL_PARSE_DEF_PORT ) && ludp->lud_port == 0 ) {
9772de962bdSlukem 			if ( strcmp( ludp->lud_scheme, "ldaps" ) == 0 ) {
9782de962bdSlukem 				ludp->lud_port = LDAPS_PORT;
9792de962bdSlukem 			} else {
9802de962bdSlukem 				ludp->lud_port = LDAP_PORT;
9812de962bdSlukem 			}
9822de962bdSlukem 		}
9832de962bdSlukem 	}
9842de962bdSlukem 
9852de962bdSlukem 	ldap_pvt_hex_unescape( url );
9862de962bdSlukem 
9872de962bdSlukem 	/* If [ip address]:port syntax, url is [ip and we skip the [ */
9882de962bdSlukem 	ludp->lud_host = LDAP_STRDUP( url + is_v6 );
9892de962bdSlukem 
9902de962bdSlukem 	if( ludp->lud_host == NULL ) {
9912de962bdSlukem 		LDAP_FREE( url );
9922de962bdSlukem 		ldap_free_urldesc( ludp );
9932de962bdSlukem 		return LDAP_URL_ERR_MEM;
9942de962bdSlukem 	}
9952de962bdSlukem 
9962de962bdSlukem 	if ( ( flags & LDAP_PVT_URL_PARSE_NOEMPTY_HOST )
9972de962bdSlukem 		&& ludp->lud_host != NULL
9982de962bdSlukem 		&& *ludp->lud_host == '\0' )
9992de962bdSlukem 	{
10002de962bdSlukem 		LDAP_FREE( ludp->lud_host );
10012de962bdSlukem 		ludp->lud_host = NULL;
10022de962bdSlukem 	}
10032de962bdSlukem 
10042de962bdSlukem 	/*
10052de962bdSlukem 	 * Kludge.  ldap://111.222.333.444:389??cn=abc,o=company
10062de962bdSlukem 	 *
10072de962bdSlukem 	 * On early Novell releases, search references/referrals were returned
10082de962bdSlukem 	 * in this format, i.e., the dn was kind of in the scope position,
10092de962bdSlukem 	 * but the required slash is missing. The whole thing is illegal syntax,
10102de962bdSlukem 	 * but we need to account for it. Fortunately it can't be confused with
10112de962bdSlukem 	 * anything real.
10122de962bdSlukem 	 */
10132de962bdSlukem 	if( (p == NULL) && (q != NULL) && (*q == '?') ) {
10142de962bdSlukem 		/* ? immediately followed by question */
10152de962bdSlukem 		q++;
10162de962bdSlukem 		if( *q != '\0' ) {
10172de962bdSlukem 			/* parse dn part */
10182de962bdSlukem 			ldap_pvt_hex_unescape( q );
10192de962bdSlukem 			ludp->lud_dn = LDAP_STRDUP( q );
10202de962bdSlukem 
10212de962bdSlukem 		} else if ( !( flags & LDAP_PVT_URL_PARSE_NOEMPTY_DN ) ) {
10222de962bdSlukem 			ludp->lud_dn = LDAP_STRDUP( "" );
10232de962bdSlukem 
10242de962bdSlukem 		} else {
10252de962bdSlukem 			check_dn = 0;
10262de962bdSlukem 		}
10272de962bdSlukem 
10282de962bdSlukem 		if ( check_dn && ludp->lud_dn == NULL ) {
10292de962bdSlukem 			LDAP_FREE( url );
10302de962bdSlukem 			ldap_free_urldesc( ludp );
10312de962bdSlukem 			return LDAP_URL_ERR_MEM;
10322de962bdSlukem 		}
10332de962bdSlukem 	}
10342de962bdSlukem 
10352de962bdSlukem 	if( p == NULL ) {
10362de962bdSlukem 		LDAP_FREE( url );
10372de962bdSlukem 		*ludpp = ludp;
10382de962bdSlukem 		return LDAP_URL_SUCCESS;
10392de962bdSlukem 	}
10402de962bdSlukem 
10412de962bdSlukem 	/* scan forward for '?' that may marks end of dn */
10422de962bdSlukem 	q = strchr( p, '?' );
10432de962bdSlukem 
10442de962bdSlukem 	if( q != NULL ) {
10452de962bdSlukem 		/* terminate dn part */
10462de962bdSlukem 		*q++ = '\0';
10472de962bdSlukem 	}
10482de962bdSlukem 
10492de962bdSlukem 	if( *p != '\0' ) {
10502de962bdSlukem 		/* parse dn part */
10512de962bdSlukem 		ldap_pvt_hex_unescape( p );
10522de962bdSlukem 		ludp->lud_dn = LDAP_STRDUP( p );
10532de962bdSlukem 
10542de962bdSlukem 	} else if ( !( flags & LDAP_PVT_URL_PARSE_NOEMPTY_DN ) ) {
10552de962bdSlukem 		ludp->lud_dn = LDAP_STRDUP( "" );
10562de962bdSlukem 
10572de962bdSlukem 	} else {
10582de962bdSlukem 		check_dn = 0;
10592de962bdSlukem 	}
10602de962bdSlukem 
10612de962bdSlukem 	if( check_dn && ludp->lud_dn == NULL ) {
10622de962bdSlukem 		LDAP_FREE( url );
10632de962bdSlukem 		ldap_free_urldesc( ludp );
10642de962bdSlukem 		return LDAP_URL_ERR_MEM;
10652de962bdSlukem 	}
10662de962bdSlukem 
10672de962bdSlukem 	if( q == NULL ) {
10682de962bdSlukem 		/* no more */
10692de962bdSlukem 		LDAP_FREE( url );
10702de962bdSlukem 		*ludpp = ludp;
10712de962bdSlukem 		return LDAP_URL_SUCCESS;
10722de962bdSlukem 	}
10732de962bdSlukem 
10742de962bdSlukem 	/* scan forward for '?' that may marks end of attributes */
10752de962bdSlukem 	p = q;
10762de962bdSlukem 	q = strchr( p, '?' );
10772de962bdSlukem 
10782de962bdSlukem 	if( q != NULL ) {
10792de962bdSlukem 		/* terminate attributes part */
10802de962bdSlukem 		*q++ = '\0';
10812de962bdSlukem 	}
10822de962bdSlukem 
10832de962bdSlukem 	if( *p != '\0' ) {
10842de962bdSlukem 		/* parse attributes */
10852de962bdSlukem 		ldap_pvt_hex_unescape( p );
10862de962bdSlukem 		ludp->lud_attrs = ldap_str2charray( p, "," );
10872de962bdSlukem 
10882de962bdSlukem 		if( ludp->lud_attrs == NULL ) {
10892de962bdSlukem 			LDAP_FREE( url );
10902de962bdSlukem 			ldap_free_urldesc( ludp );
10912de962bdSlukem 			return LDAP_URL_ERR_BADATTRS;
10922de962bdSlukem 		}
10932de962bdSlukem 	}
10942de962bdSlukem 
10952de962bdSlukem 	if ( q == NULL ) {
10962de962bdSlukem 		/* no more */
10972de962bdSlukem 		LDAP_FREE( url );
10982de962bdSlukem 		*ludpp = ludp;
10992de962bdSlukem 		return LDAP_URL_SUCCESS;
11002de962bdSlukem 	}
11012de962bdSlukem 
11022de962bdSlukem 	/* scan forward for '?' that may marks end of scope */
11032de962bdSlukem 	p = q;
11042de962bdSlukem 	q = strchr( p, '?' );
11052de962bdSlukem 
11062de962bdSlukem 	if( q != NULL ) {
11072de962bdSlukem 		/* terminate the scope part */
11082de962bdSlukem 		*q++ = '\0';
11092de962bdSlukem 	}
11102de962bdSlukem 
11112de962bdSlukem 	if( *p != '\0' ) {
11122de962bdSlukem 		/* parse the scope */
11132de962bdSlukem 		ldap_pvt_hex_unescape( p );
11142de962bdSlukem 		ludp->lud_scope = ldap_pvt_str2scope( p );
11152de962bdSlukem 
11162de962bdSlukem 		if( ludp->lud_scope == -1 ) {
11172de962bdSlukem 			LDAP_FREE( url );
11182de962bdSlukem 			ldap_free_urldesc( ludp );
11192de962bdSlukem 			return LDAP_URL_ERR_BADSCOPE;
11202de962bdSlukem 		}
11212de962bdSlukem 	}
11222de962bdSlukem 
11232de962bdSlukem 	if ( q == NULL ) {
11242de962bdSlukem 		/* no more */
11252de962bdSlukem 		LDAP_FREE( url );
11262de962bdSlukem 		*ludpp = ludp;
11272de962bdSlukem 		return LDAP_URL_SUCCESS;
11282de962bdSlukem 	}
11292de962bdSlukem 
11302de962bdSlukem 	/* scan forward for '?' that may marks end of filter */
11312de962bdSlukem 	p = q;
11322de962bdSlukem 	q = strchr( p, '?' );
11332de962bdSlukem 
11342de962bdSlukem 	if( q != NULL ) {
11352de962bdSlukem 		/* terminate the filter part */
11362de962bdSlukem 		*q++ = '\0';
11372de962bdSlukem 	}
11382de962bdSlukem 
11392de962bdSlukem 	if( *p != '\0' ) {
11402de962bdSlukem 		/* parse the filter */
11412de962bdSlukem 		ldap_pvt_hex_unescape( p );
11422de962bdSlukem 
11432de962bdSlukem 		if( ! *p ) {
11442de962bdSlukem 			/* missing filter */
11452de962bdSlukem 			LDAP_FREE( url );
11462de962bdSlukem 			ldap_free_urldesc( ludp );
11472de962bdSlukem 			return LDAP_URL_ERR_BADFILTER;
11482de962bdSlukem 		}
11492de962bdSlukem 
11502de962bdSlukem 		ludp->lud_filter = LDAP_STRDUP( p );
11512de962bdSlukem 
11522de962bdSlukem 		if( ludp->lud_filter == NULL ) {
11532de962bdSlukem 			LDAP_FREE( url );
11542de962bdSlukem 			ldap_free_urldesc( ludp );
11552de962bdSlukem 			return LDAP_URL_ERR_MEM;
11562de962bdSlukem 		}
11572de962bdSlukem 	}
11582de962bdSlukem 
11592de962bdSlukem 	if ( q == NULL ) {
11602de962bdSlukem 		/* no more */
11612de962bdSlukem 		LDAP_FREE( url );
11622de962bdSlukem 		*ludpp = ludp;
11632de962bdSlukem 		return LDAP_URL_SUCCESS;
11642de962bdSlukem 	}
11652de962bdSlukem 
11662de962bdSlukem 	/* scan forward for '?' that may marks end of extensions */
11672de962bdSlukem 	p = q;
11682de962bdSlukem 	q = strchr( p, '?' );
11692de962bdSlukem 
11702de962bdSlukem 	if( q != NULL ) {
11712de962bdSlukem 		/* extra '?' */
11722de962bdSlukem 		LDAP_FREE( url );
11732de962bdSlukem 		ldap_free_urldesc( ludp );
11742de962bdSlukem 		return LDAP_URL_ERR_BADURL;
11752de962bdSlukem 	}
11762de962bdSlukem 
11772de962bdSlukem 	/* parse the extensions */
11782de962bdSlukem 	ludp->lud_exts = ldap_str2charray( p, "," );
11792de962bdSlukem 
11802de962bdSlukem 	if( ludp->lud_exts == NULL ) {
11812de962bdSlukem 		LDAP_FREE( url );
11822de962bdSlukem 		ldap_free_urldesc( ludp );
11832de962bdSlukem 		return LDAP_URL_ERR_BADEXTS;
11842de962bdSlukem 	}
11852de962bdSlukem 
11862de962bdSlukem 	for( i=0; ludp->lud_exts[i] != NULL; i++ ) {
11872de962bdSlukem 		ldap_pvt_hex_unescape( ludp->lud_exts[i] );
11882de962bdSlukem 
11892de962bdSlukem 		if( *ludp->lud_exts[i] == '!' ) {
11902de962bdSlukem 			/* count the number of critical extensions */
11912de962bdSlukem 			ludp->lud_crit_exts++;
11922de962bdSlukem 		}
11932de962bdSlukem 	}
11942de962bdSlukem 
11952de962bdSlukem 	if( i == 0 ) {
11962de962bdSlukem 		/* must have 1 or more */
11972de962bdSlukem 		LDAP_FREE( url );
11982de962bdSlukem 		ldap_free_urldesc( ludp );
11992de962bdSlukem 		return LDAP_URL_ERR_BADEXTS;
12002de962bdSlukem 	}
12012de962bdSlukem 
12022de962bdSlukem 	/* no more */
12032de962bdSlukem 	*ludpp = ludp;
12042de962bdSlukem 	LDAP_FREE( url );
12052de962bdSlukem 	return LDAP_URL_SUCCESS;
12062de962bdSlukem }
12072de962bdSlukem 
12082de962bdSlukem int
ldap_url_parse(LDAP_CONST char * url_in,LDAPURLDesc ** ludpp)12092de962bdSlukem ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
12102de962bdSlukem {
12112de962bdSlukem 	return ldap_url_parse_ext( url_in, ludpp, LDAP_PVT_URL_PARSE_HISTORIC );
12122de962bdSlukem }
12132de962bdSlukem 
12142de962bdSlukem LDAPURLDesc *
ldap_url_dup(LDAPURLDesc * ludp)12152de962bdSlukem ldap_url_dup ( LDAPURLDesc *ludp )
12162de962bdSlukem {
12172de962bdSlukem 	LDAPURLDesc *dest;
12182de962bdSlukem 
12192de962bdSlukem 	if ( ludp == NULL ) {
12202de962bdSlukem 		return NULL;
12212de962bdSlukem 	}
12222de962bdSlukem 
12232de962bdSlukem 	dest = LDAP_MALLOC( sizeof(LDAPURLDesc) );
12242de962bdSlukem 	if (dest == NULL)
12252de962bdSlukem 		return NULL;
12262de962bdSlukem 
12272de962bdSlukem 	*dest = *ludp;
12282de962bdSlukem 	dest->lud_scheme = NULL;
12292de962bdSlukem 	dest->lud_host = NULL;
12302de962bdSlukem 	dest->lud_dn = NULL;
12312de962bdSlukem 	dest->lud_filter = NULL;
12322de962bdSlukem 	dest->lud_attrs = NULL;
12332de962bdSlukem 	dest->lud_exts = NULL;
12342de962bdSlukem 	dest->lud_next = NULL;
12352de962bdSlukem 
12362de962bdSlukem 	if ( ludp->lud_scheme != NULL ) {
12372de962bdSlukem 		dest->lud_scheme = LDAP_STRDUP( ludp->lud_scheme );
12382de962bdSlukem 		if (dest->lud_scheme == NULL) {
12392de962bdSlukem 			ldap_free_urldesc(dest);
12402de962bdSlukem 			return NULL;
12412de962bdSlukem 		}
12422de962bdSlukem 	}
12432de962bdSlukem 
12442de962bdSlukem 	if ( ludp->lud_host != NULL ) {
12452de962bdSlukem 		dest->lud_host = LDAP_STRDUP( ludp->lud_host );
12462de962bdSlukem 		if (dest->lud_host == NULL) {
12472de962bdSlukem 			ldap_free_urldesc(dest);
12482de962bdSlukem 			return NULL;
12492de962bdSlukem 		}
12502de962bdSlukem 	}
12512de962bdSlukem 
12522de962bdSlukem 	if ( ludp->lud_dn != NULL ) {
12532de962bdSlukem 		dest->lud_dn = LDAP_STRDUP( ludp->lud_dn );
12542de962bdSlukem 		if (dest->lud_dn == NULL) {
12552de962bdSlukem 			ldap_free_urldesc(dest);
12562de962bdSlukem 			return NULL;
12572de962bdSlukem 		}
12582de962bdSlukem 	}
12592de962bdSlukem 
12602de962bdSlukem 	if ( ludp->lud_filter != NULL ) {
12612de962bdSlukem 		dest->lud_filter = LDAP_STRDUP( ludp->lud_filter );
12622de962bdSlukem 		if (dest->lud_filter == NULL) {
12632de962bdSlukem 			ldap_free_urldesc(dest);
12642de962bdSlukem 			return NULL;
12652de962bdSlukem 		}
12662de962bdSlukem 	}
12672de962bdSlukem 
12682de962bdSlukem 	if ( ludp->lud_attrs != NULL ) {
12692de962bdSlukem 		dest->lud_attrs = ldap_charray_dup( ludp->lud_attrs );
12702de962bdSlukem 		if (dest->lud_attrs == NULL) {
12712de962bdSlukem 			ldap_free_urldesc(dest);
12722de962bdSlukem 			return NULL;
12732de962bdSlukem 		}
12742de962bdSlukem 	}
12752de962bdSlukem 
12762de962bdSlukem 	if ( ludp->lud_exts != NULL ) {
12772de962bdSlukem 		dest->lud_exts = ldap_charray_dup( ludp->lud_exts );
12782de962bdSlukem 		if (dest->lud_exts == NULL) {
12792de962bdSlukem 			ldap_free_urldesc(dest);
12802de962bdSlukem 			return NULL;
12812de962bdSlukem 		}
12822de962bdSlukem 	}
12832de962bdSlukem 
12842de962bdSlukem 	return dest;
12852de962bdSlukem }
12862de962bdSlukem 
12872de962bdSlukem LDAPURLDesc *
ldap_url_duplist(LDAPURLDesc * ludlist)12882de962bdSlukem ldap_url_duplist (LDAPURLDesc *ludlist)
12892de962bdSlukem {
12902de962bdSlukem 	LDAPURLDesc *dest, *tail, *ludp, *newludp;
12912de962bdSlukem 
12922de962bdSlukem 	dest = NULL;
12932de962bdSlukem 	tail = NULL;
12942de962bdSlukem 	for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
12952de962bdSlukem 		newludp = ldap_url_dup(ludp);
12962de962bdSlukem 		if (newludp == NULL) {
12972de962bdSlukem 			ldap_free_urllist(dest);
12982de962bdSlukem 			return NULL;
12992de962bdSlukem 		}
13002de962bdSlukem 		if (tail == NULL)
13012de962bdSlukem 			dest = newludp;
13022de962bdSlukem 		else
13032de962bdSlukem 			tail->lud_next = newludp;
13042de962bdSlukem 		tail = newludp;
13052de962bdSlukem 	}
13062de962bdSlukem 	return dest;
13072de962bdSlukem }
13082de962bdSlukem 
13092de962bdSlukem static int
ldap_url_parselist_int(LDAPURLDesc ** ludlist,const char * url,const char * sep,unsigned flags)13102de962bdSlukem ldap_url_parselist_int (LDAPURLDesc **ludlist, const char *url, const char *sep, unsigned flags )
13112de962bdSlukem 
13122de962bdSlukem {
13132de962bdSlukem 	int i, rc;
13142de962bdSlukem 	LDAPURLDesc *ludp;
13152de962bdSlukem 	char **urls;
13162de962bdSlukem 
13172de962bdSlukem 	assert( ludlist != NULL );
13182de962bdSlukem 	assert( url != NULL );
13192de962bdSlukem 
13202de962bdSlukem 	*ludlist = NULL;
13212de962bdSlukem 
13222de962bdSlukem 	if ( sep == NULL ) {
13232de962bdSlukem 		sep = ", ";
13242de962bdSlukem 	}
13252de962bdSlukem 
13262de962bdSlukem 	urls = ldap_str2charray( url, sep );
13272de962bdSlukem 	if (urls == NULL)
13282de962bdSlukem 		return LDAP_URL_ERR_MEM;
13292de962bdSlukem 
13302de962bdSlukem 	/* count the URLs... */
13312de962bdSlukem 	for (i = 0; urls[i] != NULL; i++) ;
13322de962bdSlukem 	/* ...and put them in the "stack" backward */
13332de962bdSlukem 	while (--i >= 0) {
13342de962bdSlukem 		rc = ldap_url_parse_ext( urls[i], &ludp, flags );
13352de962bdSlukem 		if ( rc != 0 ) {
13362de962bdSlukem 			ldap_charray_free( urls );
13372de962bdSlukem 			ldap_free_urllist( *ludlist );
13382de962bdSlukem 			*ludlist = NULL;
13392de962bdSlukem 			return rc;
13402de962bdSlukem 		}
13412de962bdSlukem 		ludp->lud_next = *ludlist;
13422de962bdSlukem 		*ludlist = ludp;
13432de962bdSlukem 	}
13442de962bdSlukem 	ldap_charray_free( urls );
13452de962bdSlukem 	return LDAP_URL_SUCCESS;
13462de962bdSlukem }
13472de962bdSlukem 
13482de962bdSlukem int
ldap_url_parselist(LDAPURLDesc ** ludlist,const char * url)13492de962bdSlukem ldap_url_parselist (LDAPURLDesc **ludlist, const char *url )
13502de962bdSlukem {
13512de962bdSlukem 	return ldap_url_parselist_int( ludlist, url, ", ", LDAP_PVT_URL_PARSE_HISTORIC );
13522de962bdSlukem }
13532de962bdSlukem 
13542de962bdSlukem int
ldap_url_parselist_ext(LDAPURLDesc ** ludlist,const char * url,const char * sep,unsigned flags)13552de962bdSlukem ldap_url_parselist_ext (LDAPURLDesc **ludlist, const char *url, const char *sep, unsigned flags )
13562de962bdSlukem {
13572de962bdSlukem 	return ldap_url_parselist_int( ludlist, url, sep, flags );
13582de962bdSlukem }
13592de962bdSlukem 
13602de962bdSlukem int
ldap_url_parsehosts(LDAPURLDesc ** ludlist,const char * hosts,int port)13612de962bdSlukem ldap_url_parsehosts(
13622de962bdSlukem 	LDAPURLDesc **ludlist,
13632de962bdSlukem 	const char *hosts,
13642de962bdSlukem 	int port )
13652de962bdSlukem {
13662de962bdSlukem 	int i;
13672de962bdSlukem 	LDAPURLDesc *ludp;
13682de962bdSlukem 	char **specs, *p;
13692de962bdSlukem 
13702de962bdSlukem 	assert( ludlist != NULL );
13712de962bdSlukem 	assert( hosts != NULL );
13722de962bdSlukem 
13732de962bdSlukem 	*ludlist = NULL;
13742de962bdSlukem 
13752de962bdSlukem 	specs = ldap_str2charray(hosts, ", ");
13762de962bdSlukem 	if (specs == NULL)
13772de962bdSlukem 		return LDAP_NO_MEMORY;
13782de962bdSlukem 
13792de962bdSlukem 	/* count the URLs... */
13802de962bdSlukem 	for (i = 0; specs[i] != NULL; i++) /* EMPTY */;
13812de962bdSlukem 
13822de962bdSlukem 	/* ...and put them in the "stack" backward */
13832de962bdSlukem 	while (--i >= 0) {
13842de962bdSlukem 		ludp = LDAP_CALLOC( 1, sizeof(LDAPURLDesc) );
13852de962bdSlukem 		if (ludp == NULL) {
13862de962bdSlukem 			ldap_charray_free(specs);
13872de962bdSlukem 			ldap_free_urllist(*ludlist);
13882de962bdSlukem 			*ludlist = NULL;
13892de962bdSlukem 			return LDAP_NO_MEMORY;
13902de962bdSlukem 		}
13912de962bdSlukem 		ludp->lud_port = port;
13922de962bdSlukem 		ludp->lud_host = specs[i];
13932de962bdSlukem 		specs[i] = NULL;
13942de962bdSlukem 		p = strchr(ludp->lud_host, ':');
13952de962bdSlukem 		if (p != NULL) {
13962de962bdSlukem 			/* more than one :, IPv6 address */
13972de962bdSlukem 			if ( strchr(p+1, ':') != NULL ) {
13982de962bdSlukem 				/* allow [address] and [address]:port */
13992de962bdSlukem 				if ( *ludp->lud_host == '[' ) {
14002de962bdSlukem 					p = LDAP_STRDUP(ludp->lud_host+1);
14012de962bdSlukem 					/* copied, make sure we free source later */
14022de962bdSlukem 					specs[i] = ludp->lud_host;
14032de962bdSlukem 					ludp->lud_host = p;
14042de962bdSlukem 					p = strchr( ludp->lud_host, ']' );
14052de962bdSlukem 					if ( p == NULL ) {
14062de962bdSlukem 						LDAP_FREE(ludp);
14072de962bdSlukem 						ldap_charray_free(specs);
14082de962bdSlukem 						return LDAP_PARAM_ERROR;
14092de962bdSlukem 					}
14102de962bdSlukem 					*p++ = '\0';
14112de962bdSlukem 					if ( *p != ':' ) {
14122de962bdSlukem 						if ( *p != '\0' ) {
14132de962bdSlukem 							LDAP_FREE(ludp);
14142de962bdSlukem 							ldap_charray_free(specs);
14152de962bdSlukem 							return LDAP_PARAM_ERROR;
14162de962bdSlukem 						}
14172de962bdSlukem 						p = NULL;
14182de962bdSlukem 					}
14192de962bdSlukem 				} else {
14202de962bdSlukem 					p = NULL;
14212de962bdSlukem 				}
14222de962bdSlukem 			}
14232de962bdSlukem 			if (p != NULL) {
14242de962bdSlukem 				char	*next;
14252de962bdSlukem 
14262de962bdSlukem 				*p++ = 0;
14272de962bdSlukem 				ldap_pvt_hex_unescape(p);
14282de962bdSlukem 				ludp->lud_port = strtol( p, &next, 10 );
14292de962bdSlukem 				if ( next == p || next[0] != '\0' ) {
14302de962bdSlukem 					LDAP_FREE(ludp);
14312de962bdSlukem 					ldap_charray_free(specs);
14322de962bdSlukem 					return LDAP_PARAM_ERROR;
14332de962bdSlukem 				}
14342de962bdSlukem 			}
14352de962bdSlukem 		}
14362de962bdSlukem 		ldap_pvt_hex_unescape(ludp->lud_host);
14372de962bdSlukem 		ludp->lud_scheme = LDAP_STRDUP("ldap");
14382de962bdSlukem 		ludp->lud_next = *ludlist;
14392de962bdSlukem 		*ludlist = ludp;
14402de962bdSlukem 	}
14412de962bdSlukem 
14422de962bdSlukem 	/* this should be an array of NULLs now */
14432de962bdSlukem 	/* except entries starting with [ */
14442de962bdSlukem 	ldap_charray_free(specs);
14452de962bdSlukem 	return LDAP_SUCCESS;
14462de962bdSlukem }
14472de962bdSlukem 
14482de962bdSlukem char *
ldap_url_list2hosts(LDAPURLDesc * ludlist)14492de962bdSlukem ldap_url_list2hosts (LDAPURLDesc *ludlist)
14502de962bdSlukem {
14512de962bdSlukem 	LDAPURLDesc *ludp;
14522de962bdSlukem 	int size;
14532de962bdSlukem 	char *s, *p, buf[32];	/* big enough to hold a long decimal # (overkill) */
14542de962bdSlukem 
14552de962bdSlukem 	if (ludlist == NULL)
14562de962bdSlukem 		return NULL;
14572de962bdSlukem 
14582de962bdSlukem 	/* figure out how big the string is */
14592de962bdSlukem 	size = 1;	/* nul-term */
14602de962bdSlukem 	for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
1461d11b170bStron 		if ( ludp->lud_host == NULL ) continue;
14622de962bdSlukem 		size += strlen(ludp->lud_host) + 1;		/* host and space */
14632de962bdSlukem 		if (strchr(ludp->lud_host, ':'))        /* will add [ ] below */
14642de962bdSlukem 			size += 2;
14652de962bdSlukem 		if (ludp->lud_port != 0)
14662de962bdSlukem 			size += sprintf(buf, ":%d", ludp->lud_port);
14672de962bdSlukem 	}
14682de962bdSlukem 	s = LDAP_MALLOC(size);
14692de962bdSlukem 	if (s == NULL)
14702de962bdSlukem 		return NULL;
14712de962bdSlukem 
14722de962bdSlukem 	p = s;
14732de962bdSlukem 	for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
1474d11b170bStron 		if ( ludp->lud_host == NULL ) continue;
14752de962bdSlukem 		if (strchr(ludp->lud_host, ':')) {
14762de962bdSlukem 			p += sprintf(p, "[%s]", ludp->lud_host);
14772de962bdSlukem 		} else {
14782de962bdSlukem 			strcpy(p, ludp->lud_host);
14792de962bdSlukem 			p += strlen(ludp->lud_host);
14802de962bdSlukem 		}
14812de962bdSlukem 		if (ludp->lud_port != 0)
14822de962bdSlukem 			p += sprintf(p, ":%d", ludp->lud_port);
14832de962bdSlukem 		*p++ = ' ';
14842de962bdSlukem 	}
14852de962bdSlukem 	if (p != s)
14862de962bdSlukem 		p--;	/* nuke that extra space */
1487d11b170bStron 	*p = '\0';
14882de962bdSlukem 	return s;
14892de962bdSlukem }
14902de962bdSlukem 
14912de962bdSlukem char *
ldap_url_list2urls(LDAPURLDesc * ludlist)14922de962bdSlukem ldap_url_list2urls(
14932de962bdSlukem 	LDAPURLDesc *ludlist )
14942de962bdSlukem {
14952de962bdSlukem 	LDAPURLDesc	*ludp;
14962de962bdSlukem 	int		size, sofar;
14972de962bdSlukem 	char		*s;
14982de962bdSlukem 
14992de962bdSlukem 	if ( ludlist == NULL ) {
15002de962bdSlukem 		return NULL;
15012de962bdSlukem 	}
15022de962bdSlukem 
15032de962bdSlukem 	/* figure out how big the string is */
15042de962bdSlukem 	for ( size = 0, ludp = ludlist; ludp != NULL; ludp = ludp->lud_next ) {
15052de962bdSlukem 		int	len = desc2str_len( ludp );
15062de962bdSlukem 		if ( len < 0 ) {
15072de962bdSlukem 			return NULL;
15082de962bdSlukem 		}
15092de962bdSlukem 		size += len + 1;
15102de962bdSlukem 	}
15112de962bdSlukem 
15122de962bdSlukem 	s = LDAP_MALLOC( size );
15132de962bdSlukem 
15142de962bdSlukem 	if ( s == NULL ) {
15152de962bdSlukem 		return NULL;
15162de962bdSlukem 	}
15172de962bdSlukem 
15182de962bdSlukem 	for ( sofar = 0, ludp = ludlist; ludp != NULL; ludp = ludp->lud_next ) {
15192de962bdSlukem 		int	len;
15202de962bdSlukem 
15212de962bdSlukem 		len = desc2str( ludp, &s[sofar], size );
15222de962bdSlukem 
15232de962bdSlukem 		if ( len < 0 ) {
15242de962bdSlukem 			LDAP_FREE( s );
15252de962bdSlukem 			return NULL;
15262de962bdSlukem 		}
15272de962bdSlukem 
15282de962bdSlukem 		sofar += len;
15292de962bdSlukem 		size -= len;
15302de962bdSlukem 
15312de962bdSlukem 		s[sofar++] = ' ';
15322de962bdSlukem 		size--;
15332de962bdSlukem 
15342de962bdSlukem 		assert( size >= 0 );
15352de962bdSlukem 	}
15362de962bdSlukem 
15372de962bdSlukem 	s[sofar - 1] = '\0';
15382de962bdSlukem 
15392de962bdSlukem 	return s;
15402de962bdSlukem }
15412de962bdSlukem 
15422de962bdSlukem void
ldap_free_urllist(LDAPURLDesc * ludlist)15432de962bdSlukem ldap_free_urllist( LDAPURLDesc *ludlist )
15442de962bdSlukem {
15452de962bdSlukem 	LDAPURLDesc *ludp, *next;
15462de962bdSlukem 
15472de962bdSlukem 	for (ludp = ludlist; ludp != NULL; ludp = next) {
15482de962bdSlukem 		next = ludp->lud_next;
15492de962bdSlukem 		ldap_free_urldesc(ludp);
15502de962bdSlukem 	}
15512de962bdSlukem }
15522de962bdSlukem 
15532de962bdSlukem void
ldap_free_urldesc(LDAPURLDesc * ludp)15542de962bdSlukem ldap_free_urldesc( LDAPURLDesc *ludp )
15552de962bdSlukem {
15562de962bdSlukem 	if ( ludp == NULL ) {
15572de962bdSlukem 		return;
15582de962bdSlukem 	}
15592de962bdSlukem 
15602de962bdSlukem 	if ( ludp->lud_scheme != NULL ) {
15612de962bdSlukem 		LDAP_FREE( ludp->lud_scheme );
15622de962bdSlukem 	}
15632de962bdSlukem 
15642de962bdSlukem 	if ( ludp->lud_host != NULL ) {
15652de962bdSlukem 		LDAP_FREE( ludp->lud_host );
15662de962bdSlukem 	}
15672de962bdSlukem 
15682de962bdSlukem 	if ( ludp->lud_dn != NULL ) {
15692de962bdSlukem 		LDAP_FREE( ludp->lud_dn );
15702de962bdSlukem 	}
15712de962bdSlukem 
15722de962bdSlukem 	if ( ludp->lud_filter != NULL ) {
15732de962bdSlukem 		LDAP_FREE( ludp->lud_filter);
15742de962bdSlukem 	}
15752de962bdSlukem 
15762de962bdSlukem 	if ( ludp->lud_attrs != NULL ) {
15772de962bdSlukem 		LDAP_VFREE( ludp->lud_attrs );
15782de962bdSlukem 	}
15792de962bdSlukem 
15802de962bdSlukem 	if ( ludp->lud_exts != NULL ) {
15812de962bdSlukem 		LDAP_VFREE( ludp->lud_exts );
15822de962bdSlukem 	}
15832de962bdSlukem 
15842de962bdSlukem 	LDAP_FREE( ludp );
15852de962bdSlukem }
15862de962bdSlukem 
15872de962bdSlukem static int
ldap_int_is_hexpair(char * s)15882de962bdSlukem ldap_int_is_hexpair( char *s )
15892de962bdSlukem {
15902de962bdSlukem 	int	i;
15912de962bdSlukem 
15922de962bdSlukem 	for ( i = 0; i < 2; i++ ) {
15932de962bdSlukem 		if ( s[i] >= '0' && s[i] <= '9' ) {
15942de962bdSlukem 			continue;
15952de962bdSlukem 		}
15962de962bdSlukem 
15972de962bdSlukem 		if ( s[i] >= 'A' && s[i] <= 'F' ) {
15982de962bdSlukem 			continue;
15992de962bdSlukem 		}
16002de962bdSlukem 
16012de962bdSlukem 		if ( s[i] >= 'a' && s[i] <= 'f' ) {
16022de962bdSlukem 			continue;
16032de962bdSlukem 		}
16042de962bdSlukem 
16052de962bdSlukem 		return 0;
16062de962bdSlukem 	}
16072de962bdSlukem 
16082de962bdSlukem 	return 1;
16092de962bdSlukem }
16102de962bdSlukem 
16112de962bdSlukem static int
ldap_int_unhex(int c)16122de962bdSlukem ldap_int_unhex( int c )
16132de962bdSlukem {
16142de962bdSlukem 	return( c >= '0' && c <= '9' ? c - '0'
16152de962bdSlukem 	    : c >= 'A' && c <= 'F' ? c - 'A' + 10
16162de962bdSlukem 	    : c - 'a' + 10 );
16172de962bdSlukem }
16182de962bdSlukem 
16192de962bdSlukem void
ldap_pvt_hex_unescape(char * s)16202de962bdSlukem ldap_pvt_hex_unescape( char *s )
16212de962bdSlukem {
16222de962bdSlukem 	/*
16232de962bdSlukem 	 * Remove URL hex escapes from s... done in place.  The basic concept for
16242de962bdSlukem 	 * this routine is borrowed from the WWW library HTUnEscape() routine.
16252de962bdSlukem 	 */
16262de962bdSlukem 	char	*p,
16272de962bdSlukem 		*save_s = s;
16282de962bdSlukem 
16292de962bdSlukem 	for ( p = s; *s != '\0'; ++s ) {
16302de962bdSlukem 		if ( *s == '%' ) {
16312de962bdSlukem 			/*
16322de962bdSlukem 			 * FIXME: what if '%' is followed
16332de962bdSlukem 			 * by non-hexpair chars?
16342de962bdSlukem 			 */
16352de962bdSlukem 			if ( !ldap_int_is_hexpair( s + 1 ) ) {
16362de962bdSlukem 				p = save_s;
16372de962bdSlukem 				break;
16382de962bdSlukem 			}
16392de962bdSlukem 
16402de962bdSlukem 			if ( *++s == '\0' ) {
16412de962bdSlukem 				break;
16422de962bdSlukem 			}
16432de962bdSlukem 			*p = ldap_int_unhex( *s ) << 4;
16442de962bdSlukem 			if ( *++s == '\0' ) {
16452de962bdSlukem 				break;
16462de962bdSlukem 			}
16472de962bdSlukem 			*p++ += ldap_int_unhex( *s );
16482de962bdSlukem 		} else {
16492de962bdSlukem 			*p++ = *s;
16502de962bdSlukem 		}
16512de962bdSlukem 	}
16522de962bdSlukem 
16532de962bdSlukem 	*p = '\0';
16542de962bdSlukem }
16552de962bdSlukem 
1656