X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=libraries%2Flibldap%2Furl.c;h=882d21da30c3919a6969eca9e8abe86a682b7829;hb=3d39ff68a94e4455074707d763138f01aaa1c5b1;hp=57c4391c806f125c1c29e32e9ca92c0de1171e92;hpb=de67e6d32761fc1dbf8144efca995d94e09380a6;p=openldap diff --git a/libraries/libldap/url.c b/libraries/libldap/url.c index 57c4391c80..882d21da30 100644 --- a/libraries/libldap/url.c +++ b/libraries/libldap/url.c @@ -1,22 +1,33 @@ -/* - * Copyright 1998-1999 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. +/* LIBLDAP url.c -- LDAP URL (RFC 4516) related routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . * - * LIBLDAP url.c -- LDAP URL (RFC 2255) related routines + * Copyright 1998-2007 The OpenLDAP Foundation. + * All rights reserved. * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * . + */ +/* Portions Copyright (c) 1996 Regents of the University of Michigan. + * All rights reserved. + */ + + +/* * LDAP URLs look like this: - * ldap[s]://host:port[/[dn[?[attributes][?[scope][?[filter][?exts]]]]]] + * 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 + * e.g., ldap://host:port/dc=com?o,cn?base?(o=openldap)?extension * * We also tolerate URLs that look like: and */ @@ -26,75 +37,175 @@ #include #include - #include + #include #include #include #include "ldap-int.h" - /* local functions */ static const char* skip_url_prefix LDAP_P(( const char *url, int *enclosedp, - int *ldaps )); -static void hex_unescape LDAP_P(( char *s )); -static int unhex( char c ); + const char **scheme )); + +int ldap_pvt_url_scheme2proto( const char *scheme ) +{ + assert( scheme != NULL ); + + if( scheme == NULL ) { + return -1; + } + + if( strcmp("ldap", scheme) == 0 ) { + return LDAP_PROTO_TCP; + } + + if( strcmp("ldapi", scheme) == 0 ) { + return LDAP_PROTO_IPC; + } + + if( strcmp("ldaps", scheme) == 0 ) { + return LDAP_PROTO_TCP; + } +#ifdef LDAP_CONNECTIONLESS + if( strcmp("cldap", scheme) == 0 ) { + return LDAP_PROTO_UDP; + } +#endif + + 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 != NULL ); + + if( scheme == NULL ) { + return -1; + } + return strcmp("ldaps", scheme) == 0; +} int ldap_is_ldap_url( LDAP_CONST char *url ) { int enclosed; - int ldaps; + const char * scheme; if( url == NULL ) { return 0; } - if( skip_url_prefix( url, &enclosed, &ldaps) == NULL ) { + if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) { return 0; } - return !ldaps; + return 1; } int ldap_is_ldaps_url( LDAP_CONST char *url ) { int enclosed; - int ldaps; + const char * scheme; + + if( url == NULL ) { + return 0; + } + + if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) { + return 0; + } + + return strcmp(scheme, "ldaps") == 0; +} + +int +ldap_is_ldapi_url( LDAP_CONST char *url ) +{ + int enclosed; + const char * scheme; if( url == NULL ) { return 0; } - if( skip_url_prefix( url, &enclosed, &ldaps) == NULL ) { + if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) { return 0; } - return ldaps; + return strcmp(scheme, "ldapi") == 0; } +#ifdef LDAP_CONNECTIONLESS +int +ldap_is_ldapc_url( LDAP_CONST char *url ) +{ + int enclosed; + const char * scheme; + + if( url == NULL ) { + return 0; + } + + if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) { + return 0; + } + + return strcmp(scheme, "cldap") == 0; +} +#endif + static const char* skip_url_prefix( const char *url, int *enclosedp, - int *ldaps ) + const char **scheme ) { -/* - * return non-zero if this looks like a LDAP URL; zero if not - * if non-zero returned, *urlp will be moved past "ldap://" part of URL - */ - char* p; + /* + * return non-zero if this looks like a LDAP URL; zero if not + * if non-zero returned, *urlp will be moved past "ldap://" part of URL + */ + const char *p; if ( url == NULL ) { return( NULL ); } - p = (char *) url; + p = url; /* skip leading '<' (if any) */ if ( *p == '<' ) { @@ -105,8 +216,7 @@ skip_url_prefix( } /* skip leading "URL:" (if any) */ - if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) - { + if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) { p += LDAP_URL_URLCOLON_LEN; } @@ -114,7 +224,7 @@ skip_url_prefix( if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) { /* skip over "ldap://" prefix and return success */ p += LDAP_URL_PREFIX_LEN; - *ldaps = 0; + *scheme = "ldap"; return( p ); } @@ -122,66 +232,604 @@ skip_url_prefix( if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) { /* skip over "ldaps://" prefix and return success */ p += LDAPS_URL_PREFIX_LEN; - *ldaps = 1; + *scheme = "ldaps"; + return( p ); + } + + /* check for "ldapi://" prefix */ + if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) { + /* skip over "ldapi://" prefix and return success */ + p += LDAPI_URL_PREFIX_LEN; + *scheme = "ldapi"; + return( p ); + } + +#ifdef LDAP_CONNECTIONLESS + /* check for "cldap://" prefix */ + if ( strncasecmp( p, LDAPC_URL_PREFIX, LDAPC_URL_PREFIX_LEN ) == 0 ) { + /* skip over "cldap://" prefix and return success */ + p += LDAPC_URL_PREFIX_LEN; + *scheme = "cldap"; return( p ); } +#endif return( NULL ); } - -static int str2scope( const char *p ) +int +ldap_pvt_scope2bv( int scope, struct berval *bv ) { - if ( strcasecmp( p, "one" ) == 0 ) { - return LDAP_SCOPE_ONELEVEL; + switch ( scope ) { + case LDAP_SCOPE_BASE: + BER_BVSTR( bv, "base" ); + break; - } else if ( strcasecmp( p, "onetree" ) == 0 ) { - return LDAP_SCOPE_ONELEVEL; + case LDAP_SCOPE_ONELEVEL: + BER_BVSTR( bv, "one" ); + break; - } else if ( strcasecmp( p, "base" ) == 0 ) { - return LDAP_SCOPE_BASE; + case LDAP_SCOPE_SUBTREE: + BER_BVSTR( bv, "sub" ); + break; - } else if ( strcasecmp( p, "sub" ) == 0 ) { - return LDAP_SCOPE_SUBTREE; + case LDAP_SCOPE_SUBORDINATE: + BER_BVSTR( bv, "subordinate" ); + break; - } else if ( strcasecmp( p, "subtree" ) == 0 ) { - return LDAP_SCOPE_SUBTREE; + default: + return LDAP_OTHER; + } + + return LDAP_SUCCESS; +} + +const char * +ldap_pvt_scope2str( int scope ) +{ + struct berval bv; + + if ( ldap_pvt_scope2bv( scope, &bv ) == LDAP_SUCCESS ) { + return bv.bv_val; + } + + return NULL; +} + +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; + + for ( i = 0; v[ i ].scope != -1; i++ ) { + if ( ber_bvstrcasecmp( bv, &v[ i ].bv ) == 0 ) { + return v[ i ].scope; + } } return( -1 ); } +int +ldap_pvt_str2scope( const char *p ) +{ + struct berval bv; + + ber_str2bv( p, 0, 0, &bv ); + + 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; + + case '/': + if ( list & URLESC_SLASH ) { + escape = 1; + } + break; + + 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 ) { + 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_len_list( char **s, unsigned flags ) +{ + 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 ); + } + + 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++ ) { + int curlen; + + if ( pos ) { + buf[pos++] = ','; + len--; + } + curlen = hex_escape( &buf[pos], len, s[i], flags ); + len -= curlen; + pos += curlen; + } + + return pos; +} + +static int +desc2str_len( LDAPURLDesc *u ) +{ + int sep = 0; + int len = 0; + int is_ipc = 0; + struct berval scope; + + if ( u == NULL || u->lud_scheme == NULL ) { + return -1; + } + + if ( !strcmp( "ldapi", u->lud_scheme )) { + is_ipc = 1; + } + + if ( u->lud_exts ) { + len += hex_escape_len_list( u->lud_exts, URLESC_COMMA ); + if ( !sep ) { + sep = 5; + } + } + + if ( u->lud_filter ) { + len += hex_escape_len( u->lud_filter, URLESC_NONE ); + if ( !sep ) { + sep = 4; + } + } + + if ( ldap_pvt_scope2bv( u->lud_scope, &scope ) == LDAP_SUCCESS ) { + len += scope.bv_len; + if ( !sep ) { + sep = 3; + } + } + + if ( u->lud_attrs ) { + len += hex_escape_len_list( u->lud_attrs, URLESC_NONE ); + if ( !sep ) { + sep = 2; + } + } + + if ( u->lud_dn && u->lud_dn[0] ) { + len += hex_escape_len( u->lud_dn, URLESC_NONE ); + if ( !sep ) { + sep = 1; + } + }; + + len += sep; + + if ( u->lud_port ) { + unsigned p = u->lud_port; + if ( p > 65535 ) + return -1; + + len += (p > 999 ? 5 + (p > 9999) : p > 99 ? 4 : 2 + (p > 9)); + } + + if ( u->lud_host && u->lud_host[0] ) { + len += hex_escape_len( u->lud_host, URLESC_SLASH ); + if ( !is_ipc && strchr( u->lud_host, ':' )) { + len += 2; /* IPv6, [] */ + } + } + + len += strlen( u->lud_scheme ) + STRLENOF( "://" ); + + return len; +} + +static int +desc2str( LDAPURLDesc *u, char *s, int len ) +{ + int i; + int sep = 0; + int sofar = 0; + int is_v6 = 0; + int is_ipc = 0; + struct berval scope = BER_BVNULL; + + if ( u == NULL ) { + return -1; + } + + if ( s == NULL ) { + return -1; + } + + if ( u->lud_scheme && !strcmp( "ldapi", u->lud_scheme )) { + is_ipc = 1; + } + + ldap_pvt_scope2bv( u->lud_scope, &scope ); + + 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 ( !is_ipc && u->lud_host && strchr( u->lud_host, ':' )) { + is_v6 = 1; + } + + if ( u->lud_port ) { + len -= sprintf( s, "%s://%s%s%s:%d%n", u->lud_scheme, + is_v6 ? "[" : "", + u->lud_host ? u->lud_host : "", + is_v6 ? "]" : "", + u->lud_port, &sofar ); + + } else { + len -= sprintf( s, "%s://%n", u->lud_scheme, &sofar ); + if ( u->lud_host && u->lud_host[0] ) { + if ( is_v6 ) { + s[sofar++] = '['; + len--; + } + i = hex_escape( &s[sofar], len, u->lud_host, URLESC_SLASH ); + sofar += i; + len -= i; + if ( is_v6 ) { + s[sofar++] = ']'; + len--; + } + } + } + + 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; + + assert( len >= 0 ); + } + + if ( sep < 2 ) { + goto done; + } + s[sofar++] = '?'; + len--; + + assert( len >= 0 ); + + i = hex_escape_list( &s[sofar], len, u->lud_attrs, URLESC_NONE ); + sofar += i; + len -= i; + + assert( len >= 0 ); + + if ( sep < 3 ) { + goto done; + } + s[sofar++] = '?'; + len--; + + assert( len >= 0 ); + + if ( !BER_BVISNULL( &scope ) ) { + strcpy( &s[sofar], scope.bv_val ); + sofar += scope.bv_len; + len -= scope.bv_len; + } + + assert( len >= 0 ); + + if ( sep < 4 ) { + goto done; + } + s[sofar++] = '?'; + len--; + + assert( len >= 0 ); + + i = hex_escape( &s[sofar], len, u->lud_filter, URLESC_NONE ); + sofar += i; + len -= i; + + assert( len >= 0 ); + + if ( sep < 5 ) { + goto done; + } + s[sofar++] = '?'; + len--; + + assert( len >= 0 ); + + i = hex_escape_list( &s[sofar], len, u->lud_exts, URLESC_COMMA ); + sofar += i; + len -= i; + + assert( len >= 0 ); + +done: + 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( 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. */ LDAPURLDesc *ludp; - char *p, *q; - int i, enclosed, ldaps; + char *p, *q, *r; + int i, enclosed, proto, is_v6 = 0; + const char *scheme = NULL; const char *url_tmp; char *url; - if( url_in == NULL && ludpp == NULL ) { + int check_dn = 1; + + if( url_in == NULL || ludpp == NULL ) { return LDAP_URL_ERR_PARAM; } - Debug( LDAP_DEBUG_TRACE, "ldap_url_parse(%s)\n", url_in, 0, 0 ); +#ifndef LDAP_INT_IN_KERNEL + /* Global options may not be created yet + * We can't test if the global options are initialized + * because a call to LDAP_INT_GLOBAL_OPT() will try to allocate + * the options and cause infinite recursion + */ + Debug( LDAP_DEBUG_TRACE, "ldap_url_parse_ext(%s)\n", url_in, 0, 0 ); +#endif *ludpp = NULL; /* pessimistic */ - url_tmp = skip_url_prefix( url_in, &enclosed, &ldaps ); + url_tmp = skip_url_prefix( url_in, &enclosed, &scheme ); if ( url_tmp == NULL ) { - return LDAP_URL_ERR_NOTLDAP; + return LDAP_URL_ERR_BADSCHEME; + } + + assert( scheme != NULL ); + + proto = ldap_pvt_url_scheme2proto( scheme ); + if ( proto == -1 ) { + return LDAP_URL_ERR_BADSCHEME; } /* make working copy of the remainder of the URL */ - if (( url = LDAP_STRDUP( url_tmp )) == NULL ) { - return( LDAP_URL_ERR_MEM ); + url = LDAP_STRDUP( url_tmp ); + if ( url == NULL ) { + return LDAP_URL_ERR_MEM; } if ( enclosed ) { @@ -203,17 +851,18 @@ ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) return LDAP_URL_ERR_MEM; } + ludp->lud_next = NULL; ludp->lud_host = NULL; ludp->lud_port = 0; - ludp->lud_dn = NULL; - ludp->lud_attrs = NULL; - ludp->lud_filter = NULL; - ludp->lud_ldaps = ldaps; - ludp->lud_scope = LDAP_SCOPE_BASE; + ludp->lud_dn = NULL; + ludp->lud_attrs = NULL; + ludp->lud_scope = ( flags & LDAP_PVT_URL_PARSE_NODEF_SCOPE ) ? LDAP_SCOPE_BASE : LDAP_SCOPE_DEFAULT; + ludp->lud_filter = NULL; + ludp->lud_exts = NULL; - ludp->lud_filter = LDAP_STRDUP("(objectClass=*)"); + ludp->lud_scheme = LDAP_STRDUP( scheme ); - if( ludp->lud_filter == NULL ) { + if ( ludp->lud_scheme == NULL ) { LDAP_FREE( url ); ldap_free_urldesc( ludp ); return LDAP_URL_ERR_MEM; @@ -221,27 +870,83 @@ ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) /* scan forward for '/' that marks end of hostport and begin. of dn */ p = strchr( url, '/' ); + q = NULL; if( p != NULL ) { /* terminate hostport; point to start of dn */ *p++ = '\0'; + } else { + /* check for Novell kludge, see below */ + p = strchr( url, '?' ); + if ( p ) { + *p++ = '\0'; + q = p; + p = NULL; + } } - if (( q = strchr( url, ':' )) != NULL ) { - *q++ = '\0'; - hex_unescape( q ); + if ( proto != LDAP_PROTO_IPC ) { + /* IPv6 syntax with [ip address]:port */ + if ( *url == '[' ) { + r = strchr( url, ']' ); + if ( r == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADURL; + } + *r++ = '\0'; + q = strchr( r, ':' ); + if ( q && q != r ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADURL; + } + is_v6 = 1; + } else { + q = strchr( url, ':' ); + } - if( *q == '\0' ) { - LDAP_FREE( url ); - ldap_free_urldesc( ludp ); - return LDAP_URL_ERR_BADURL; + if ( q != NULL ) { + char *next; + + *q++ = '\0'; + ldap_pvt_hex_unescape( q ); + + if( *q == '\0' ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADURL; + } + + 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; + } + /* check for Novell kludge */ + if ( !p ) { + if ( *next != '\0' ) { + q = &next[1]; + } else { + q = NULL; + } + } } - ludp->lud_port = atoi( q ); + if ( ( flags & LDAP_PVT_URL_PARSE_DEF_PORT ) && ludp->lud_port == 0 ) { + if ( strcmp( ludp->lud_scheme, "ldaps" ) == 0 ) { + ludp->lud_port = LDAPS_PORT; + } else { + ludp->lud_port = LDAP_PORT; + } + } } - hex_unescape( url ); - ludp->lud_host = LDAP_STRDUP( url ); + ldap_pvt_hex_unescape( url ); + + /* If [ip address]:port syntax, url is [ip and we skip the [ */ + ludp->lud_host = LDAP_STRDUP( url + is_v6 ); if( ludp->lud_host == NULL ) { LDAP_FREE( url ); @@ -249,6 +954,45 @@ ldap_url_parse( 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 + * + * On early Novell releases, search references/referrals were returned + * in this format, i.e., the dn was kind of in the scope position, + * but the required slash is missing. The whole thing is illegal syntax, + * but we need to account for it. Fortunately it can't be confused with + * anything real. + */ + if( (p == NULL) && (q != NULL) && (*q == '?') ) { + /* ? immediately followed by question */ + q++; + if( *q != '\0' ) { + /* parse dn part */ + ldap_pvt_hex_unescape( q ); + ludp->lud_dn = LDAP_STRDUP( q ); + + } else if ( !( flags & LDAP_PVT_URL_PARSE_NOEMPTY_DN ) ) { + ludp->lud_dn = LDAP_STRDUP( "" ); + + } else { + check_dn = 0; + } + + if ( check_dn && ludp->lud_dn == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_MEM; + } + } + if( p == NULL ) { LDAP_FREE( url ); *ludpp = ludp; @@ -265,13 +1009,17 @@ ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) if( *p != '\0' ) { /* parse dn part */ - hex_unescape( p ); + 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; @@ -295,7 +1043,7 @@ ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) if( *p != '\0' ) { /* parse attributes */ - hex_unescape( p ); + ldap_pvt_hex_unescape( p ); ludp->lud_attrs = ldap_str2charray( p, "," ); if( ludp->lud_attrs == NULL ) { @@ -323,8 +1071,8 @@ ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) if( *p != '\0' ) { /* parse the scope */ - hex_unescape( p ); - ludp->lud_scope = str2scope( p ); + ldap_pvt_hex_unescape( p ); + ludp->lud_scope = ldap_pvt_str2scope( p ); if( ludp->lud_scope == -1 ) { LDAP_FREE( url ); @@ -351,7 +1099,7 @@ ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) if( *p != '\0' ) { /* parse the filter */ - hex_unescape( p ); + ldap_pvt_hex_unescape( p ); if( ! *p ) { /* missing filter */ @@ -360,7 +1108,6 @@ ldap_url_parse( 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 ) { @@ -398,12 +1145,16 @@ ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) } for( i=0; ludp->lud_exts[i] != NULL; i++ ) { - hex_unescape( ludp->lud_exts[i] ); + ldap_pvt_hex_unescape( ludp->lud_exts[i] ); + + if( *ludp->lud_exts[i] == '!' ) { + /* count the number of critical extensions */ + ludp->lud_crit_exts++; + } } if( i == 0 ) { /* must have 1 or more */ - ldap_charray_free( ludp->lud_exts ); LDAP_FREE( url ); ldap_free_urldesc( ludp ); return LDAP_URL_ERR_BADEXTS; @@ -415,152 +1166,444 @@ ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) return LDAP_URL_SUCCESS; } +int +ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) +{ + return ldap_url_parse_ext( url_in, ludpp, LDAP_PVT_URL_PARSE_HISTORIC ); +} -void -ldap_free_urldesc( LDAPURLDesc *ludp ) +LDAPURLDesc * +ldap_url_dup ( LDAPURLDesc *ludp ) { + LDAPURLDesc *dest; + if ( ludp == NULL ) { - return; + return NULL; } + + dest = LDAP_MALLOC( sizeof(LDAPURLDesc) ); + if (dest == NULL) + return NULL; + *dest = *ludp; + dest->lud_scheme = NULL; + dest->lud_host = NULL; + dest->lud_dn = NULL; + dest->lud_filter = NULL; + dest->lud_attrs = NULL; + dest->lud_exts = NULL; + dest->lud_next = NULL; + + if ( ludp->lud_scheme != NULL ) { + dest->lud_scheme = LDAP_STRDUP( ludp->lud_scheme ); + if (dest->lud_scheme == NULL) { + ldap_free_urldesc(dest); + return NULL; + } + } + if ( ludp->lud_host != NULL ) { - LDAP_FREE( ludp->lud_host ); + dest->lud_host = LDAP_STRDUP( ludp->lud_host ); + if (dest->lud_host == NULL) { + ldap_free_urldesc(dest); + return NULL; + } } if ( ludp->lud_dn != NULL ) { - LDAP_FREE( ludp->lud_dn ); + dest->lud_dn = LDAP_STRDUP( ludp->lud_dn ); + if (dest->lud_dn == NULL) { + ldap_free_urldesc(dest); + return NULL; + } } if ( ludp->lud_filter != NULL ) { - LDAP_FREE( ludp->lud_filter); + dest->lud_filter = LDAP_STRDUP( ludp->lud_filter ); + if (dest->lud_filter == NULL) { + ldap_free_urldesc(dest); + return NULL; + } } if ( ludp->lud_attrs != NULL ) { - LDAP_VFREE( ludp->lud_attrs ); + dest->lud_attrs = ldap_charray_dup( ludp->lud_attrs ); + if (dest->lud_attrs == NULL) { + ldap_free_urldesc(dest); + return NULL; + } } if ( ludp->lud_exts != NULL ) { - LDAP_VFREE( ludp->lud_exts ); + dest->lud_exts = ldap_charray_dup( ludp->lud_exts ); + if (dest->lud_exts == NULL) { + ldap_free_urldesc(dest); + return NULL; + } } - LDAP_FREE( ludp ); + return dest; } +LDAPURLDesc * +ldap_url_duplist (LDAPURLDesc *ludlist) +{ + LDAPURLDesc *dest, *tail, *ludp, *newludp; + + dest = NULL; + tail = NULL; + for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) { + newludp = ldap_url_dup(ludp); + if (newludp == NULL) { + ldap_free_urllist(dest); + return NULL; + } + if (tail == NULL) + dest = newludp; + else + tail->lud_next = newludp; + tail = newludp; + } + return dest; +} - -int -ldap_url_search( LDAP *ld, LDAP_CONST char *url, int attrsonly ) +static int +ldap_url_parselist_int (LDAPURLDesc **ludlist, const char *url, const char *sep, unsigned flags ) + { - int err; - LDAPURLDesc *ludp; - BerElement *ber; - LDAPServer *srv = NULL; + int i, rc; + LDAPURLDesc *ludp; + char **urls; + + assert( ludlist != NULL ); + assert( url != NULL ); - if ( ldap_url_parse( url, &ludp ) != 0 ) { - ld->ld_errno = LDAP_PARAM_ERROR; - return( -1 ); + *ludlist = NULL; + + if ( sep == NULL ) { + sep = ", "; } - ber = ldap_build_search_req( ld, ludp->lud_dn, ludp->lud_scope, - ludp->lud_filter, ludp->lud_attrs, attrsonly, NULL, NULL, - -1, -1 ); + urls = ldap_str2charray( url, sep ); + if (urls == NULL) + return LDAP_URL_ERR_MEM; - if ( ber == NULL ) { - return( -1 ); + /* count the URLs... */ + for (i = 0; urls[i] != NULL; i++) ; + /* ...and put them in the "stack" backward */ + while (--i >= 0) { + rc = ldap_url_parse_ext( urls[i], &ludp, flags ); + if ( rc != 0 ) { + ldap_charray_free( urls ); + ldap_free_urllist( *ludlist ); + *ludlist = NULL; + return rc; + } + ludp->lud_next = *ludlist; + *ludlist = ludp; } + ldap_charray_free( urls ); + return LDAP_URL_SUCCESS; +} - err = 0; +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 ); +} - if ( ludp->lud_host != NULL || ludp->lud_port != 0 ) { - if (( srv = (LDAPServer *)LDAP_CALLOC( 1, sizeof( LDAPServer ))) - == NULL || ( srv->lsrv_host = LDAP_STRDUP( ludp->lud_host == - NULL ? ld->ld_defhost : ludp->lud_host )) == NULL ) { - if ( srv != NULL ) { - LDAP_FREE( srv ); +int +ldap_url_parsehosts( + LDAPURLDesc **ludlist, + const char *hosts, + int port ) +{ + int i; + LDAPURLDesc *ludp; + char **specs, *p; + + assert( ludlist != NULL ); + assert( hosts != NULL ); + + *ludlist = NULL; + + specs = ldap_str2charray(hosts, ", "); + if (specs == NULL) + return LDAP_NO_MEMORY; + + /* count the URLs... */ + for (i = 0; specs[i] != NULL; i++) /* EMPTY */; + + /* ...and put them in the "stack" backward */ + while (--i >= 0) { + ludp = LDAP_CALLOC( 1, sizeof(LDAPURLDesc) ); + if (ludp == NULL) { + ldap_charray_free(specs); + ldap_free_urllist(*ludlist); + *ludlist = NULL; + return LDAP_NO_MEMORY; + } + ludp->lud_port = port; + ludp->lud_host = specs[i]; + specs[i] = NULL; + p = strchr(ludp->lud_host, ':'); + if (p != NULL) { + /* more than one :, IPv6 address */ + if ( strchr(p+1, ':') != NULL ) { + /* allow [address] and [address]:port */ + if ( *ludp->lud_host == '[' ) { + p = LDAP_STRDUP(ludp->lud_host+1); + /* copied, make sure we free source later */ + specs[i] = ludp->lud_host; + ludp->lud_host = p; + p = strchr( ludp->lud_host, ']' ); + if ( p == NULL ) { + LDAP_FREE(ludp); + ldap_charray_free(specs); + return LDAP_PARAM_ERROR; + } + *p++ = '\0'; + if ( *p != ':' ) { + if ( *p != '\0' ) { + LDAP_FREE(ludp); + ldap_charray_free(specs); + return LDAP_PARAM_ERROR; + } + p = NULL; + } + } else { + p = NULL; + } } - ld->ld_errno = LDAP_NO_MEMORY; - err = -1; - } else { - if ( ludp->lud_port == 0 ) { - srv->lsrv_port = ldap_int_global_options.ldo_defport; - } else { - srv->lsrv_port = ludp->lud_port; + if (p != NULL) { + char *next; + + *p++ = 0; + ldap_pvt_hex_unescape(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); + ludp->lud_scheme = LDAP_STRDUP("ldap"); + ludp->lud_next = *ludlist; + *ludlist = ludp; } - if ( err != 0 ) { - ber_free( ber, 1 ); - } else { - err = ldap_send_server_request( ld, ber, ld->ld_msgid, NULL, srv, - NULL, 1 ); - } - - ldap_free_urldesc( ludp ); - - return( err ); + /* this should be an array of NULLs now */ + /* except entries starting with [ */ + ldap_charray_free(specs); + return LDAP_SUCCESS; } +char * +ldap_url_list2hosts (LDAPURLDesc *ludlist) +{ + LDAPURLDesc *ludp; + int size; + char *s, *p, buf[32]; /* big enough to hold a long decimal # (overkill) */ + + 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_host) + 1; /* host and space */ + if (strchr(ludp->lud_host, ':')) /* will add [ ] below */ + size += 2; + if (ludp->lud_port != 0) + size += sprintf(buf, ":%d", ludp->lud_port); + } + s = LDAP_MALLOC(size); + if (s == NULL) + return NULL; + + p = s; + for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) { + if (strchr(ludp->lud_host, ':')) { + p += sprintf(p, "[%s]", ludp->lud_host); + } else { + strcpy(p, ludp->lud_host); + p += strlen(ludp->lud_host); + } + if (ludp->lud_port != 0) + p += sprintf(p, ":%d", ludp->lud_port); + *p++ = ' '; + } + if (p != s) + p--; /* nuke that extra space */ + *p = 0; + return s; +} -int -ldap_url_search_st( LDAP *ld, LDAP_CONST char *url, int attrsonly, - struct timeval *timeout, LDAPMessage **res ) +char * +ldap_url_list2urls( + LDAPURLDesc *ludlist ) { - int msgid; + LDAPURLDesc *ludp; + int size, sofar; + char *s; - if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) { - return( ld->ld_errno ); + if ( ludlist == NULL ) { + return NULL; } - if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 ) { - return( ld->ld_errno ); + /* figure out how big the string is */ + 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 ); - if ( ld->ld_errno == LDAP_TIMEOUT ) { - (void) ldap_abandon( ld, msgid ); - ld->ld_errno = LDAP_TIMEOUT; - return( ld->ld_errno ); + if ( s == NULL ) { + return NULL; } - return( ldap_result2error( ld, *res, 0 )); + 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; + } + + sofar += len; + size -= len; + + s[sofar++] = ' '; + size--; + + assert( size >= 0 ); + } + + s[sofar - 1] = '\0'; + + return s; } +void +ldap_free_urllist( LDAPURLDesc *ludlist ) +{ + LDAPURLDesc *ludp, *next; -int -ldap_url_search_s( - LDAP *ld, LDAP_CONST char *url, int attrsonly, LDAPMessage **res ) + for (ludp = ludlist; ludp != NULL; ludp = next) { + next = ludp->lud_next; + ldap_free_urldesc(ludp); + } +} + +void +ldap_free_urldesc( LDAPURLDesc *ludp ) { - int msgid; + if ( ludp == NULL ) { + return; + } + + if ( ludp->lud_scheme != NULL ) { + LDAP_FREE( ludp->lud_scheme ); + } + + if ( ludp->lud_host != NULL ) { + LDAP_FREE( ludp->lud_host ); + } + + if ( ludp->lud_dn != NULL ) { + LDAP_FREE( ludp->lud_dn ); + } - if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) { - return( ld->ld_errno ); + if ( ludp->lud_filter != NULL ) { + LDAP_FREE( ludp->lud_filter); } - if ( ldap_result( ld, msgid, 1, (struct timeval *)NULL, res ) == -1 ) { - return( ld->ld_errno ); + if ( ludp->lud_attrs != NULL ) { + LDAP_VFREE( ludp->lud_attrs ); + } + + if ( ludp->lud_exts != NULL ) { + LDAP_VFREE( ludp->lud_exts ); } - return( ldap_result2error( ld, *res, 0 )); + LDAP_FREE( ludp ); } +static int +ldap_int_is_hexpair( char *s ) +{ + int i; -static void -hex_unescape( char *s ) + 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 ) { -/* - * 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; + return( c >= '0' && c <= '9' ? c - '0' + : c >= 'A' && c <= 'F' ? c - 'A' + 10 + : c - 'a' + 10 ); +} + +void +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, + *save_s = s; for ( p = s; *s != '\0'; ++s ) { if ( *s == '%' ) { - if ( *++s != '\0' ) { - *p = unhex( *s ) << 4; + /* + * FIXME: what if '%' is followed + * by non-hexpair chars? + */ + if ( !ldap_int_is_hexpair( s + 1 ) ) { + p = save_s; + break; } - if ( *++s != '\0' ) { - *p++ += unhex( *s ); + + if ( *++s == '\0' ) { + break; + } + *p = ldap_int_unhex( *s ) << 4; + if ( *++s == '\0' ) { + break; } + *p++ += ldap_int_unhex( *s ); } else { *p++ = *s; } @@ -569,11 +1612,3 @@ hex_unescape( char *s ) *p = '\0'; } - -static int -unhex( char c ) -{ - return( c >= '0' && c <= '9' ? c - '0' - : c >= 'A' && c <= 'F' ? c - 'A' + 10 - : c - 'a' + 10 ); -}