X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=libraries%2Flibldap%2Furl.c;h=1eadf029f152df0f0a7d4d209ce323eb45b49fc7;hb=c3e28a5488a8011ef0352f48fca85c48679205ba;hp=00413dc0880e8e34a65631ee958cfd8ba483c077;hpb=262d021ecf7b52bb37a6cc7b6be5b3f930da9d9e;p=openldap diff --git a/libraries/libldap/url.c b/libraries/libldap/url.c index 00413dc088..1eadf029f1 100644 --- a/libraries/libldap/url.c +++ b/libraries/libldap/url.c @@ -1,21 +1,31 @@ +/* LIBLDAP url.c -- LDAP URL (RFC 4516) related routines */ /* $OpenLDAP$ */ -/* - * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved. - * COPYING RESTRICTIONS APPLY, see COPYRIGHT file - */ -/* Portions - * Copyright (c) 1996 Regents of the University of Michigan. - * All rights reserved. +/* This work is part of OpenLDAP Software . + * + * Copyright 1998-2007 The OpenLDAP Foundation. + * All rights reserved. * - * LIBLDAP url.c -- LDAP URL (RFC 2255) related routines + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * . + */ +/* Portions Copyright (c) 1996 Regents of the University of Michigan. + * All rights reserved. + */ + + +/* * LDAP URLs look like this: * ldap[is]://host:port[/[dn[?[attributes][?[scope][?[filter][?exts]]]]]] * * where: * attributes is a comma separated list * scope is one of these three strings: base one sub (default=base) - * filter is an string-represented filter as in RFC 2254 + * filter is an string-represented filter as in RFC 4515 * * e.g., ldap://host:port/dc=com?o,cn?base?(o=openldap)?extension * @@ -27,6 +37,7 @@ #include #include +#include #include #include @@ -42,7 +53,7 @@ static const char* skip_url_prefix LDAP_P(( int ldap_pvt_url_scheme2proto( const char *scheme ) { - assert( scheme ); + assert( scheme != NULL ); if( scheme == NULL ) { return -1; @@ -68,10 +79,38 @@ int ldap_pvt_url_scheme2proto( const char *scheme ) return -1; } +int ldap_pvt_url_scheme_port( const char *scheme, int port ) +{ + assert( scheme != NULL ); + + if( port ) return port; + if( scheme == NULL ) return port; + + if( strcmp("ldap", scheme) == 0 ) { + return LDAP_PORT; + } + + if( strcmp("ldapi", scheme) == 0 ) { + return -1; + } + + if( strcmp("ldaps", scheme) == 0 ) { + return LDAPS_PORT; + } + +#ifdef LDAP_CONNECTIONLESS + if( strcmp("cldap", scheme) == 0 ) { + return LDAP_PORT; + } +#endif + + return -1; +} + int ldap_pvt_url_scheme2tls( const char *scheme ) { - assert( scheme ); + assert( scheme != NULL ); if( scheme == NULL ) { return -1; @@ -218,210 +257,507 @@ skip_url_prefix( return( NULL ); } +int +ldap_pvt_scope2bv( int scope, struct berval *bv ) +{ + switch ( scope ) { + case LDAP_SCOPE_BASE: + BER_BVSTR( bv, "base" ); + break; + + case LDAP_SCOPE_ONELEVEL: + BER_BVSTR( bv, "one" ); + break; + + case LDAP_SCOPE_SUBTREE: + BER_BVSTR( bv, "sub" ); + break; -static int str2scope( const char *p ) + case LDAP_SCOPE_SUBORDINATE: + BER_BVSTR( bv, "subordinate" ); + break; + + default: + return LDAP_OTHER; + } + + return LDAP_SUCCESS; +} + +const char * +ldap_pvt_scope2str( int scope ) { - if ( strcasecmp( p, "one" ) == 0 ) { - return LDAP_SCOPE_ONELEVEL; + struct berval bv; - } else if ( strcasecmp( p, "onetree" ) == 0 ) { - return LDAP_SCOPE_ONELEVEL; + if ( ldap_pvt_scope2bv( scope, &bv ) == LDAP_SUCCESS ) { + return bv.bv_val; + } - } else if ( strcasecmp( p, "base" ) == 0 ) { - return LDAP_SCOPE_BASE; + return NULL; +} - } else if ( strcasecmp( p, "sub" ) == 0 ) { - return LDAP_SCOPE_SUBTREE; +int +ldap_pvt_bv2scope( struct berval *bv ) +{ + static struct { + struct berval bv; + int scope; + } v[] = { + { BER_BVC( "one" ), LDAP_SCOPE_ONELEVEL }, + { BER_BVC( "onelevel" ), LDAP_SCOPE_ONELEVEL }, + { BER_BVC( "base" ), LDAP_SCOPE_BASE }, + { BER_BVC( "sub" ), LDAP_SCOPE_SUBTREE }, + { BER_BVC( "subtree" ), LDAP_SCOPE_SUBTREE }, + { BER_BVC( "subord" ), LDAP_SCOPE_SUBORDINATE }, + { BER_BVC( "subordinate" ), LDAP_SCOPE_SUBORDINATE }, + { BER_BVC( "children" ), LDAP_SCOPE_SUBORDINATE }, + { BER_BVNULL, -1 } + }; + int i; - } else if ( strcasecmp( p, "subtree" ) == 0 ) { - return LDAP_SCOPE_SUBTREE; + for ( i = 0; v[ i ].scope != -1; i++ ) { + if ( ber_bvstrcasecmp( bv, &v[ i ].bv ) == 0 ) { + return v[ i ].scope; + } } return( -1 ); } -static int hex_escape( char *buf, const char *s, int list ) +int +ldap_pvt_str2scope( const char *p ) { - int i; - int pos; - static const char hex[] = "0123456789ABCDEF"; + struct berval bv; - if( s == NULL ) return 0; + ber_str2bv( p, 0, 0, &bv ); - for( pos=0,i=0; s[i]; i++ ) { - int escape = 0; - switch( s[i] ) { - case ',': - escape = list; - break; - case '%': - case '?': - case ' ': - case '<': - case '>': - case '"': - case '#': - case '{': - case '}': - case '|': - case '\\': - case '^': - case '~': - case '`': - case '[': - case ']': + return ldap_pvt_bv2scope( &bv ); +} + +static const char hex[] = "0123456789ABCDEF"; + +#define URLESC_NONE 0x0000U +#define URLESC_COMMA 0x0001U +#define URLESC_SLASH 0x0002U + +static int +hex_escape_len( const char *s, unsigned list ) +{ + int len; + + if ( s == NULL ) { + return 0; + } + + for ( len = 0; s[0]; s++ ) { + switch ( s[0] ) { + /* RFC 2396: reserved */ + case '?': + len += 3; + break; + + case ',': + if ( list & URLESC_COMMA ) { + len += 3; + } else { + len++; + } + break; + + case '/': + if ( list & URLESC_SLASH ) { + len += 3; + } else { + len++; + } + break; + + case ';': + case ':': + case '@': + case '&': + case '=': + case '+': + case '$': + + /* RFC 2396: unreserved mark */ + case '-': + case '_': + case '.': + case '!': + case '~': + case '*': + case '\'': + case '(': + case ')': + len++; + break; + + /* RFC 2396: unreserved alphanum */ + default: + if ( !isalnum( (unsigned char) s[0] ) ) { + len += 3; + } else { + len++; + } + break; + } + } + + return len; +} + +static int +hex_escape( char *buf, int len, const char *s, unsigned list ) +{ + int i; + int pos; + + if ( s == NULL ) { + return 0; + } + + for ( pos = 0, i = 0; s[i] && pos < len; i++ ) { + int escape = 0; + + switch ( s[i] ) { + /* RFC 2396: reserved */ + case '?': + escape = 1; + break; + + case ',': + if ( list & URLESC_COMMA ) { escape = 1; - break; + } + break; + + case '/': + if ( list & URLESC_SLASH ) { + escape = 1; + } + break; - default: - escape = s[i] < 0x20 || 0x1f >= s[i]; + case ';': + case ':': + case '@': + case '&': + case '=': + case '+': + case '$': + + /* RFC 2396: unreserved mark */ + case '-': + case '_': + case '.': + case '!': + case '~': + case '*': + case '\'': + case '(': + case ')': + break; + + /* RFC 2396: unreserved alphanum */ + default: + if ( !isalnum( (unsigned char) s[i] ) ) { + escape = 1; + } + break; } - if( escape ) { + if ( escape ) { buf[pos++] = '%'; buf[pos++] = hex[ (s[i] >> 4) & 0x0f ]; buf[pos++] = hex[ s[i] & 0x0f ]; + } else { buf[pos++] = s[i]; } } buf[pos] = '\0'; + return pos; } -static int hex_escape_args( char *buf, char **s ) +static int +hex_escape_len_list( char **s, unsigned flags ) { - int pos; - int i; + int len; + int i; + + if ( s == NULL ) { + return 0; + } + + len = 0; + for ( i = 0; s[i] != NULL; i++ ) { + if ( len ) { + len++; + } + len += hex_escape_len( s[i], flags ); + } - if( s == NULL ) return 0; + return len; +} + +static int +hex_escape_list( char *buf, int len, char **s, unsigned flags ) +{ + int pos; + int i; + + if ( s == NULL ) { + return 0; + } pos = 0; - for( i=0; s[i] != NULL; i++ ) { - if( pos ) { + for ( i = 0; s[i] != NULL; i++ ) { + int curlen; + + if ( pos ) { buf[pos++] = ','; + len--; } - pos += hex_escape( &buf[pos], s[i], 1 ); + curlen = hex_escape( &buf[pos], len, s[i], flags ); + len -= curlen; + pos += curlen; } return pos; } -char * ldap_url_desc2str( LDAPURLDesc *u ) +static int +desc2str_len( LDAPURLDesc *u ) { - char *s; - int i; - int sep = 0; - int sofar; - size_t len = 0; - if( u == NULL ) return NULL; - - if( u->lud_exts ) { - for( i=0; u->lud_exts[i]; i++ ) { - len += strlen( u->lud_exts[i] ) + 1; - } - if( !sep ) sep = 5; + int sep = 0; + int len = 0; + struct berval scope; + + if ( u == NULL ) { + return -1; } - if( u->lud_filter ) { - len += strlen( u->lud_filter ); - if( !sep ) sep = 4; + if ( u->lud_exts ) { + len += hex_escape_len_list( u->lud_exts, URLESC_COMMA ); + if ( !sep ) { + sep = 5; + } } - if ( len ) len++; /* ? */ - switch( u->lud_scope ) { - case LDAP_SCOPE_ONELEVEL: - case LDAP_SCOPE_SUBTREE: - case LDAP_SCOPE_BASE: - len += sizeof("base"); - if( !sep ) sep = 3; - break; + if ( u->lud_filter ) { + len += hex_escape_len( u->lud_filter, URLESC_NONE ); + if ( !sep ) { + sep = 4; + } + } - default: - if ( len ) len++; /* ? */ + if ( ldap_pvt_scope2bv( u->lud_scope, &scope ) == LDAP_SUCCESS ) { + len += scope.bv_len; + if ( !sep ) { + sep = 3; + } } - if( u->lud_attrs ) { - for( i=0; u->lud_attrs[i]; i++ ) { - len += strlen( u->lud_attrs[i] ) + 1; + if ( u->lud_attrs ) { + len += hex_escape_len_list( u->lud_attrs, URLESC_NONE ); + if ( !sep ) { + sep = 2; } - if( !sep ) sep = 2; - } else if ( len ) len++; /* ? */ + } - if( u->lud_dn ) { - len += strlen( u->lud_dn ) + 1; - if( !sep ) sep = 1; + if ( u->lud_dn && u->lud_dn[0] ) { + len += hex_escape_len( u->lud_dn, URLESC_NONE ); + if ( !sep ) { + sep = 1; + } }; - if( u->lud_port ) { - len += sizeof(":65535") - 1; + len += sep; + + if ( u->lud_port ) { + char buf[] = ":65535"; + + len += snprintf( buf, sizeof( buf ), ":%d", u->lud_port ); + if ( u->lud_host && u->lud_host[0] ) { + len += strlen( u->lud_host ); + } + + } else { + if ( u->lud_host && u->lud_host[0] ) { + len += hex_escape_len( u->lud_host, URLESC_SLASH ); + } } - if( u->lud_host ) { - len+=strlen( u->lud_host ); + len += strlen( u->lud_scheme ) + STRLENOF( "://" ); + + return len; +} + +int +desc2str( LDAPURLDesc *u, char *s, int len ) +{ + int i; + int sep = 0; + int sofar = 0; + struct berval scope = BER_BVNULL; + + if ( u == NULL ) { + return -1; } - len += strlen( u->lud_scheme ) + sizeof("://"); + if ( s == NULL ) { + return -1; + } - /* allocate enough to hex escape everything -- overkill */ - s = LDAP_MALLOC( 3*len ); + ldap_pvt_scope2bv( u->lud_scope, &scope ); - if( s == NULL ) return NULL; + if ( u->lud_exts ) { + sep = 5; + } else if ( u->lud_filter ) { + sep = 4; + } else if ( !BER_BVISEMPTY( &scope ) ) { + sep = 3; + } else if ( u->lud_attrs ) { + sep = 2; + } else if ( u->lud_dn && u->lud_dn[0] ) { + sep = 1; + } + + if ( u->lud_port ) { + len -= sprintf( s, "%s://%s:%d%n", u->lud_scheme, + u->lud_host ? u->lud_host : "", + u->lud_port, &sofar ); - if( u->lud_port ) { - sprintf( s, "%s://%s:%d%n", u->lud_scheme, - u->lud_host, u->lud_port, &sofar ); } else { - sprintf( s, "%s://%s%n", u->lud_scheme, - u->lud_host, &sofar ); + len -= sprintf( s, "%s://%n", u->lud_scheme, &sofar ); + if ( u->lud_host && u->lud_host[0] ) { + i = hex_escape( &s[sofar], len, u->lud_host, URLESC_SLASH ); + sofar += i; + len -= i; + } } - - if( sep < 1 ) goto done; + + assert( len >= 0 ); + + if ( sep < 1 ) { + goto done; + } + s[sofar++] = '/'; + len--; + + assert( len >= 0 ); + + if ( u->lud_dn && u->lud_dn[0] ) { + i = hex_escape( &s[sofar], len, u->lud_dn, URLESC_NONE ); + sofar += i; + len -= i; - sofar += hex_escape( &s[sofar], u->lud_dn, 0 ); + assert( len >= 0 ); + } - if( sep < 2 ) goto done; + if ( sep < 2 ) { + goto done; + } s[sofar++] = '?'; + len--; + + assert( len >= 0 ); - sofar += hex_escape_args( &s[sofar], u->lud_attrs ); + i = hex_escape_list( &s[sofar], len, u->lud_attrs, URLESC_NONE ); + sofar += i; + len -= i; - if( sep < 3 ) goto done; + assert( len >= 0 ); + + if ( sep < 3 ) { + goto done; + } s[sofar++] = '?'; + len--; - switch( u->lud_scope ) { - case LDAP_SCOPE_BASE: - strcpy( &s[sofar], "base" ); - sofar += sizeof("base") - 1; - break; - case LDAP_SCOPE_ONELEVEL: - strcpy( &s[sofar], "one" ); - sofar += sizeof("one") - 1; - break; - case LDAP_SCOPE_SUBTREE: - strcpy( &s[sofar], "sub" ); - sofar += sizeof("sub") - 1; - break; + assert( len >= 0 ); + + if ( !BER_BVISNULL( &scope ) ) { + strcpy( &s[sofar], scope.bv_val ); + sofar += scope.bv_len; + len -= scope.bv_len; } - if( sep < 4 ) goto done; + assert( len >= 0 ); + + if ( sep < 4 ) { + goto done; + } s[sofar++] = '?'; + len--; + + assert( len >= 0 ); - sofar += hex_escape( &s[sofar], u->lud_filter, 0 ); + i = hex_escape( &s[sofar], len, u->lud_filter, URLESC_NONE ); + sofar += i; + len -= i; - if( sep < 5 ) goto done; + assert( len >= 0 ); + + if ( sep < 5 ) { + goto done; + } s[sofar++] = '?'; + len--; + + assert( len >= 0 ); - sofar += hex_escape_args( &s[sofar], u->lud_exts ); + i = hex_escape_list( &s[sofar], len, u->lud_exts, URLESC_COMMA ); + sofar += i; + len -= i; + + assert( len >= 0 ); done: - s[sofar] = '\0'; + if ( len < 0 ) { + return -1; + } + + return sofar; +} + +char * +ldap_url_desc2str( LDAPURLDesc *u ) +{ + int len; + char *s; + + if ( u == NULL ) { + return NULL; + } + + len = desc2str_len( u ); + if ( len < 0 ) { + return NULL; + } + + /* allocate enough to hex escape everything -- overkill */ + s = LDAP_MALLOC( len + 1 ); + + if ( s == NULL ) { + return NULL; + } + + if ( desc2str( u, s, len ) != len ) { + LDAP_FREE( s ); + return NULL; + } + + s[len] = '\0'; + return s; } int -ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) +ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp, unsigned flags ) { /* * Pick apart the pieces of an LDAP URL. @@ -434,6 +770,8 @@ ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) const char *url_tmp; char *url; + int check_dn = 1; + if( url_in == NULL || ludpp == NULL ) { return LDAP_URL_ERR_PARAM; } @@ -444,11 +782,7 @@ ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) * because a call to LDAP_INT_GLOBAL_OPT() will try to allocate * the options and cause infinite recursion */ -#ifdef NEW_LOGGING - LDAP_LOG ( OPERATION, ENTRY, "ldap_url_parse_ext(%s)\n", url_in, 0, 0 ); -#else Debug( LDAP_DEBUG_TRACE, "ldap_url_parse_ext(%s)\n", url_in, 0, 0 ); -#endif #endif *ludpp = NULL; /* pessimistic */ @@ -459,7 +793,7 @@ ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) return LDAP_URL_ERR_BADSCHEME; } - assert( scheme ); + assert( scheme != NULL ); /* make working copy of the remainder of the URL */ url = LDAP_STRDUP( url_tmp ); @@ -491,8 +825,7 @@ ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) ludp->lud_port = 0; ludp->lud_dn = NULL; ludp->lud_attrs = NULL; - ludp->lud_filter = NULL; - ludp->lud_scope = LDAP_SCOPE_DEFAULT; + ludp->lud_scope = ( flags & LDAP_PVT_URL_PARSE_NODEF_SCOPE ) ? LDAP_SCOPE_BASE : LDAP_SCOPE_DEFAULT; ludp->lud_filter = NULL; ludp->lud_exts = NULL; @@ -527,6 +860,8 @@ ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) } if ( q != NULL ) { + char *next; + *q++ = '\0'; ldap_pvt_hex_unescape( q ); @@ -536,7 +871,24 @@ ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) return LDAP_URL_ERR_BADURL; } - ludp->lud_port = atoi( q ); + ludp->lud_port = strtol( q, &next, 10 ); + if ( next == q || next[0] != '\0' ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADURL; + } + } + + if ( ( flags & LDAP_PVT_URL_PARSE_DEF_PORT ) && ludp->lud_port == 0 ) { + if ( strcmp( ludp->lud_scheme, "ldap" ) == 0 ) { + ludp->lud_port = LDAP_PORT; +#ifdef LDAP_CONNECTIONLESS + } else if ( strcmp( ludp->lud_scheme, "cldap" ) == 0 ) { + ludp->lud_port = LDAP_PORT; +#endif + } else if ( strcmp( ludp->lud_scheme, "ldaps" ) == 0 ) { + ludp->lud_port = LDAPS_PORT; + } } ldap_pvt_hex_unescape( url ); @@ -550,6 +902,14 @@ ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) return LDAP_URL_ERR_MEM; } + if ( ( flags & LDAP_PVT_URL_PARSE_NOEMPTY_HOST ) + && ludp->lud_host != NULL + && *ludp->lud_host == '\0' ) + { + LDAP_FREE( ludp->lud_host ); + ludp->lud_host = NULL; + } + /* * Kludge. ldap://111.222.333.444:389??cn=abc,o=company * @@ -568,11 +928,15 @@ ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) /* parse dn part */ ldap_pvt_hex_unescape( q ); ludp->lud_dn = LDAP_STRDUP( q ); - } else { + + } else if ( !( flags & LDAP_PVT_URL_PARSE_NOEMPTY_DN ) ) { ludp->lud_dn = LDAP_STRDUP( "" ); + + } else { + check_dn = 0; } - if( ludp->lud_dn == NULL ) { + if ( check_dn && ludp->lud_dn == NULL ) { LDAP_FREE( url ); ldap_free_urldesc( ludp ); return LDAP_URL_ERR_MEM; @@ -598,11 +962,15 @@ ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) /* parse dn part */ ldap_pvt_hex_unescape( p ); ludp->lud_dn = LDAP_STRDUP( p ); - } else { + + } else if ( !( flags & LDAP_PVT_URL_PARSE_NOEMPTY_DN ) ) { ludp->lud_dn = LDAP_STRDUP( "" ); + + } else { + check_dn = 0; } - if( ludp->lud_dn == NULL ) { + if( check_dn && ludp->lud_dn == NULL ) { LDAP_FREE( url ); ldap_free_urldesc( ludp ); return LDAP_URL_ERR_MEM; @@ -655,7 +1023,7 @@ ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) if( *p != '\0' ) { /* parse the scope */ ldap_pvt_hex_unescape( p ); - ludp->lud_scope = str2scope( p ); + ludp->lud_scope = ldap_pvt_str2scope( p ); if( ludp->lud_scope == -1 ) { LDAP_FREE( url ); @@ -691,7 +1059,6 @@ ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) return LDAP_URL_ERR_BADFILTER; } - LDAP_FREE( ludp->lud_filter ); ludp->lud_filter = LDAP_STRDUP( p ); if( ludp->lud_filter == NULL ) { @@ -753,34 +1120,7 @@ ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) int ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) { - int rc = ldap_url_parse_ext( url_in, ludpp ); - - if( rc != LDAP_URL_SUCCESS ) { - return rc; - } - - if ((*ludpp)->lud_scope == LDAP_SCOPE_DEFAULT) { - (*ludpp)->lud_scope = LDAP_SCOPE_BASE; - } - - if ((*ludpp)->lud_host != NULL && *(*ludpp)->lud_host == '\0') { - LDAP_FREE( (*ludpp)->lud_host ); - (*ludpp)->lud_host = NULL; - } - - if ((*ludpp)->lud_port == 0) { - if( strcmp((*ludpp)->lud_scheme, "ldap") == 0 ) { - (*ludpp)->lud_port = LDAP_PORT; -#ifdef LDAP_CONNECTIONLESS - } else if( strcmp((*ludpp)->lud_scheme, "cldap") == 0 ) { - (*ludpp)->lud_port = LDAP_PORT; -#endif - } else if( strcmp((*ludpp)->lud_scheme, "ldaps") == 0 ) { - (*ludpp)->lud_port = LDAPS_PORT; - } - } - - return rc; + return ldap_url_parse_ext( url_in, ludpp, LDAP_PVT_URL_PARSE_HISTORIC ); } LDAPURLDesc * @@ -878,14 +1218,9 @@ ldap_url_duplist (LDAPURLDesc *ludlist) return dest; } -int -ldap_url_parselist (LDAPURLDesc **ludlist, const char *url ) -{ - return ldap_url_parselist_ext( ludlist, url, ", " ); -} - -int -ldap_url_parselist_ext (LDAPURLDesc **ludlist, const char *url, const char *sep ) +static int +ldap_url_parselist_int (LDAPURLDesc **ludlist, const char *url, const char *sep, unsigned flags ) + { int i, rc; LDAPURLDesc *ludp; @@ -896,26 +1231,42 @@ ldap_url_parselist_ext (LDAPURLDesc **ludlist, const char *url, const char *sep *ludlist = NULL; - urls = ldap_str2charray(url, sep); + if ( sep == NULL ) { + sep = ", "; + } + + urls = ldap_str2charray( url, sep ); if (urls == NULL) - return LDAP_NO_MEMORY; + return LDAP_URL_ERR_MEM; /* count the URLs... */ for (i = 0; urls[i] != NULL; i++) ; /* ...and put them in the "stack" backward */ while (--i >= 0) { - rc = ldap_url_parse( urls[i], &ludp ); + rc = ldap_url_parse_ext( urls[i], &ludp, flags ); if ( rc != 0 ) { - ldap_charray_free(urls); - ldap_free_urllist(*ludlist); + ldap_charray_free( urls ); + ldap_free_urllist( *ludlist ); *ludlist = NULL; return rc; } ludp->lud_next = *ludlist; *ludlist = ludp; } - ldap_charray_free(urls); - return LDAP_SUCCESS; + ldap_charray_free( urls ); + return LDAP_URL_SUCCESS; +} + +int +ldap_url_parselist (LDAPURLDesc **ludlist, const char *url ) +{ + return ldap_url_parselist_int( ludlist, url, ", ", LDAP_PVT_URL_PARSE_HISTORIC ); +} + +int +ldap_url_parselist_ext (LDAPURLDesc **ludlist, const char *url, const char *sep, unsigned flags ) +{ + return ldap_url_parselist_int( ludlist, url, sep, flags ); } int @@ -963,12 +1314,18 @@ ldap_url_parsehosts( specs[i] = ludp->lud_host; ludp->lud_host = p; p = strchr( ludp->lud_host, ']' ); - if ( p == NULL ) + if ( p == NULL ) { + LDAP_FREE(ludp); + ldap_charray_free(specs); return LDAP_PARAM_ERROR; + } *p++ = '\0'; if ( *p != ':' ) { - if ( *p != '\0' ) + if ( *p != '\0' ) { + LDAP_FREE(ludp); + ldap_charray_free(specs); return LDAP_PARAM_ERROR; + } p = NULL; } } else { @@ -976,9 +1333,16 @@ ldap_url_parsehosts( } } if (p != NULL) { + char *next; + *p++ = 0; ldap_pvt_hex_unescape(p); - ludp->lud_port = atoi(p); + ludp->lud_port = strtol( p, &next, 10 ); + if ( next == p || next[0] != '\0' ) { + LDAP_FREE(ludp); + ldap_charray_free(specs); + return LDAP_PARAM_ERROR; + } } } ldap_pvt_hex_unescape(ludp->lud_host); @@ -1038,50 +1402,50 @@ char * ldap_url_list2urls( LDAPURLDesc *ludlist ) { - LDAPURLDesc *ludp; - int size; - char *s, *p, buf[32]; /* big enough to hold a long decimal # (overkill) */ + LDAPURLDesc *ludp; + int size, sofar; + char *s; - if (ludlist == NULL) + if ( ludlist == NULL ) { return NULL; + } /* figure out how big the string is */ - size = 1; /* nul-term */ - for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) { - size += strlen(ludp->lud_scheme); - if ( ludp->lud_host ) { - size += strlen(ludp->lud_host); - /* will add [ ] below */ - if (strchr(ludp->lud_host, ':')) - size += 2; - } - size += sizeof(":/// "); - - if (ludp->lud_port != 0) { - size += sprintf(buf, ":%d", ludp->lud_port); + for ( size = 0, ludp = ludlist; ludp != NULL; ludp = ludp->lud_next ) { + int len = desc2str_len( ludp ); + if ( len < 0 ) { + return NULL; } + size += len + 1; } + + s = LDAP_MALLOC( size ); - s = LDAP_MALLOC(size); - if (s == NULL) { + if ( s == NULL ) { return NULL; } - p = s; - for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) { - p += sprintf(p, "%s://", ludp->lud_scheme); - if ( ludp->lud_host ) { - p += sprintf(p, strchr(ludp->lud_host, ':') - ? "[%s]" : "%s", ludp->lud_host); + for ( sofar = 0, ludp = ludlist; ludp != NULL; ludp = ludp->lud_next ) { + int len; + + len = desc2str( ludp, &s[sofar], size ); + + if ( len < 0 ) { + LDAP_FREE( s ); + return NULL; } - if (ludp->lud_port != 0) - p += sprintf(p, ":%d", ludp->lud_port); - *p++ = '/'; - *p++ = ' '; + + sofar += len; + size -= len; + + s[sofar++] = ' '; + size--; + + assert( size >= 0 ); } - if (p != s) - p--; /* nuke that extra space */ - *p = 0; + + s[sofar - 1] = '\0'; + return s; } @@ -1130,6 +1494,30 @@ ldap_free_urldesc( LDAPURLDesc *ludp ) LDAP_FREE( ludp ); } +static int +ldap_int_is_hexpair( char *s ) +{ + int i; + + for ( i = 0; i < 2; i++ ) { + if ( s[i] >= '0' && s[i] <= '9' ) { + continue; + } + + if ( s[i] >= 'A' && s[i] <= 'F' ) { + continue; + } + + if ( s[i] >= 'a' && s[i] <= 'f' ) { + continue; + } + + return 0; + } + + return 1; +} + static int ldap_int_unhex( int c ) { @@ -1145,10 +1533,20 @@ ldap_pvt_hex_unescape( char *s ) * Remove URL hex escapes from s... done in place. The basic concept for * this routine is borrowed from the WWW library HTUnEscape() routine. */ - char *p; + char *p, + *save_s = s; for ( p = s; *s != '\0'; ++s ) { if ( *s == '%' ) { + /* + * FIXME: what if '%' is followed + * by non-hexpair chars? + */ + if ( !ldap_int_is_hexpair( s + 1 ) ) { + p = save_s; + break; + } + if ( *++s == '\0' ) { break; } @@ -1165,4 +1563,3 @@ ldap_pvt_hex_unescape( char *s ) *p = '\0'; } -