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