123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653 |
- /* LIBLDAP url.c -- LDAP URL (RFC 4516) related routines */
- /* $OpenLDAP$ */
- /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
- *
- * Copyright 1998-2022 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
- * <http://www.OpenLDAP.org/license.html>.
- */
- /* Portions Copyright (c) 1996 Regents of the University of Michigan.
- * All rights reserved.
- */
- /*
- * LDAP URLs look like this:
- * [p]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 4515
- *
- * e.g., ldap://host:port/dc=com?o,cn?base?(o=openldap)?extension
- *
- * We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl>
- */
- #include "portable.h"
- #include <stdio.h>
- #include <ac/stdlib.h>
- #include <ac/ctype.h>
- #include <ac/socket.h>
- #include <ac/string.h>
- #include <ac/time.h>
- #include "ldap-int.h"
- /* local functions */
- static const char* skip_url_prefix LDAP_P((
- const char *url,
- int *enclosedp,
- const char **scheme ));
- int ldap_pvt_url_scheme2proto( const char *scheme )
- {
- assert( scheme != NULL );
- if( scheme == NULL ) {
- return -1;
- }
- if( strcmp("ldap", scheme) == 0 || strcmp("pldap", scheme) == 0 ) {
- return LDAP_PROTO_TCP;
- }
- if( strcmp("ldapi", scheme) == 0 ) {
- return LDAP_PROTO_IPC;
- }
- if( strcmp("ldaps", scheme) == 0 || strcmp("pldaps", 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 || strcmp("pldap", scheme) == 0 ) {
- return LDAP_PORT;
- }
- if( strcmp("ldapi", scheme) == 0 ) {
- return -1;
- }
- if( strcmp("ldaps", scheme) == 0 || strcmp("pldaps", 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 || strcmp("pldaps", scheme) == 0;
- }
- int
- ldap_pvt_url_scheme2proxied( const char *scheme )
- {
- assert( scheme != NULL );
- if( scheme == NULL ) {
- return -1;
- }
- return strcmp("pldap", scheme) == 0 || strcmp("pldaps", scheme) == 0;
- }
- int
- ldap_is_ldap_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 1;
- }
- int
- ldap_is_ldaps_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, "ldaps") == 0 || strcmp(scheme, "pldaps") == 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, &scheme ) == NULL ) {
- return 0;
- }
- 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,
- 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
- */
- const char *p;
- if ( url == NULL ) {
- return( NULL );
- }
- p = url;
- /* skip leading '<' (if any) */
- if ( *p == '<' ) {
- *enclosedp = 1;
- ++p;
- } else {
- *enclosedp = 0;
- }
- /* skip leading "URL:" (if any) */
- if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) {
- p += LDAP_URL_URLCOLON_LEN;
- }
- /* check for "ldap://" prefix */
- if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
- /* skip over "ldap://" prefix and return success */
- p += LDAP_URL_PREFIX_LEN;
- *scheme = "ldap";
- return( p );
- }
- /* check for "pldap://" prefix */
- if ( strncasecmp( p, PLDAP_URL_PREFIX, PLDAP_URL_PREFIX_LEN ) == 0 ) {
- /* skip over "pldap://" prefix and return success */
- p += PLDAP_URL_PREFIX_LEN;
- *scheme = "pldap";
- return( p );
- }
- /* check for "ldaps://" prefix */
- if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
- /* skip over "ldaps://" prefix and return success */
- p += LDAPS_URL_PREFIX_LEN;
- *scheme = "ldaps";
- return( p );
- }
- /* check for "pldaps://" prefix */
- if ( strncasecmp( p, PLDAPS_URL_PREFIX, PLDAPS_URL_PREFIX_LEN ) == 0 ) {
- /* skip over "pldaps://" prefix and return success */
- p += PLDAPS_URL_PREFIX_LEN;
- *scheme = "pldaps";
- 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 );
- }
- 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;
- case LDAP_SCOPE_SUBORDINATE:
- BER_BVSTR( bv, "subordinate" );
- break;
- 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] ) {
- char *ptr;
- len += hex_escape_len( u->lud_host, URLESC_SLASH );
- if ( !is_ipc && ( ptr = strchr( u->lud_host, ':' ))) {
- if ( strchr( ptr+1, ':' ))
- 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;
- char *ptr;
- 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 && ( ptr = strchr( u->lud_host, ':' ))) {
- if ( strchr( ptr+1, ':' ))
- is_v6 = 1;
- }
- if ( u->lud_port ) {
- sofar = sprintf( s, "%s://%s%s%s:%d", u->lud_scheme,
- is_v6 ? "[" : "",
- u->lud_host ? u->lud_host : "",
- is_v6 ? "]" : "",
- u->lud_port );
- len -= sofar;
- } else {
- sofar = sprintf( s, "%s://", u->lud_scheme );
- len -= 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_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp, unsigned flags )
- {
- /*
- * Pick apart the pieces of an LDAP URL.
- */
- LDAPURLDesc *ludp;
- char *p, *q, *r;
- int i, enclosed, proto, is_v6 = 0;
- const char *scheme = NULL;
- const char *url_tmp;
- char *url;
- int check_dn = 1;
- if( url_in == NULL || ludpp == NULL ) {
- return LDAP_URL_ERR_PARAM;
- }
- #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
- */
- Debug1( LDAP_DEBUG_TRACE, "ldap_url_parse_ext(%s)\n", url_in );
- #endif
- *ludpp = NULL; /* pessimistic */
- url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
- if ( url_tmp == NULL ) {
- 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 */
- url = LDAP_STRDUP( url_tmp );
- if ( url == NULL ) {
- return LDAP_URL_ERR_MEM;
- }
- if ( enclosed ) {
- p = &url[strlen(url)-1];
- if( *p != '>' ) {
- LDAP_FREE( url );
- return LDAP_URL_ERR_BADENCLOSURE;
- }
- *p = '\0';
- }
- /* allocate return struct */
- ludp = (LDAPURLDesc *)LDAP_CALLOC( 1, sizeof( LDAPURLDesc ));
- if ( ludp == NULL ) {
- LDAP_FREE( url );
- 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_scope = ( flags & LDAP_PVT_URL_PARSE_NODEF_SCOPE ) ? LDAP_SCOPE_BASE : LDAP_SCOPE_DEFAULT;
- ludp->lud_filter = NULL;
- ludp->lud_exts = NULL;
- ludp->lud_scheme = LDAP_STRDUP( scheme );
- if ( ludp->lud_scheme == NULL ) {
- LDAP_FREE( url );
- ldap_free_urldesc( ludp );
- return LDAP_URL_ERR_MEM;
- }
- /* 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 ( 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 != 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;
- }
- }
- }
- 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;
- }
- }
- }
- 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 );
- ldap_free_urldesc( ludp );
- 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;
- return LDAP_URL_SUCCESS;
- }
- /* scan forward for '?' that may marks end of dn */
- q = strchr( p, '?' );
- if( q != NULL ) {
- /* terminate dn part */
- *q++ = '\0';
- }
- if( *p != '\0' ) {
- /* parse dn part */
- ldap_pvt_hex_unescape( p );
- ludp->lud_dn = LDAP_STRDUP( p );
- } 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( q == NULL ) {
- /* no more */
- LDAP_FREE( url );
- *ludpp = ludp;
- return LDAP_URL_SUCCESS;
- }
- /* scan forward for '?' that may marks end of attributes */
- p = q;
- q = strchr( p, '?' );
- if( q != NULL ) {
- /* terminate attributes part */
- *q++ = '\0';
- }
- if( *p != '\0' ) {
- /* parse attributes */
- ldap_pvt_hex_unescape( p );
- ludp->lud_attrs = ldap_str2charray( p, "," );
- if( ludp->lud_attrs == NULL ) {
- LDAP_FREE( url );
- ldap_free_urldesc( ludp );
- return LDAP_URL_ERR_BADATTRS;
- }
- }
- if ( q == NULL ) {
- /* no more */
- LDAP_FREE( url );
- *ludpp = ludp;
- return LDAP_URL_SUCCESS;
- }
- /* scan forward for '?' that may marks end of scope */
- p = q;
- q = strchr( p, '?' );
- if( q != NULL ) {
- /* terminate the scope part */
- *q++ = '\0';
- }
- if( *p != '\0' ) {
- /* parse the scope */
- ldap_pvt_hex_unescape( p );
- ludp->lud_scope = ldap_pvt_str2scope( p );
- if( ludp->lud_scope == -1 ) {
- LDAP_FREE( url );
- ldap_free_urldesc( ludp );
- return LDAP_URL_ERR_BADSCOPE;
- }
- }
- if ( q == NULL ) {
- /* no more */
- LDAP_FREE( url );
- *ludpp = ludp;
- return LDAP_URL_SUCCESS;
- }
- /* scan forward for '?' that may marks end of filter */
- p = q;
- q = strchr( p, '?' );
- if( q != NULL ) {
- /* terminate the filter part */
- *q++ = '\0';
- }
- if( *p != '\0' ) {
- /* parse the filter */
- ldap_pvt_hex_unescape( p );
- if( ! *p ) {
- /* missing filter */
- LDAP_FREE( url );
- ldap_free_urldesc( ludp );
- return LDAP_URL_ERR_BADFILTER;
- }
- ludp->lud_filter = LDAP_STRDUP( p );
- if( ludp->lud_filter == NULL ) {
- LDAP_FREE( url );
- ldap_free_urldesc( ludp );
- return LDAP_URL_ERR_MEM;
- }
- }
- if ( q == NULL ) {
- /* no more */
- LDAP_FREE( url );
- *ludpp = ludp;
- return LDAP_URL_SUCCESS;
- }
- /* scan forward for '?' that may marks end of extensions */
- p = q;
- q = strchr( p, '?' );
- if( q != NULL ) {
- /* extra '?' */
- LDAP_FREE( url );
- ldap_free_urldesc( ludp );
- return LDAP_URL_ERR_BADURL;
- }
- /* parse the extensions */
- ludp->lud_exts = ldap_str2charray( p, "," );
- if( ludp->lud_exts == NULL ) {
- LDAP_FREE( url );
- ldap_free_urldesc( ludp );
- return LDAP_URL_ERR_BADEXTS;
- }
- for( i=0; ludp->lud_exts[i] != NULL; 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_FREE( url );
- ldap_free_urldesc( ludp );
- return LDAP_URL_ERR_BADEXTS;
- }
- /* no more */
- *ludpp = ludp;
- LDAP_FREE( url );
- 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 );
- }
- LDAPURLDesc *
- ldap_url_dup ( LDAPURLDesc *ludp )
- {
- LDAPURLDesc *dest;
- if ( ludp == NULL ) {
- 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 ) {
- dest->lud_host = LDAP_STRDUP( ludp->lud_host );
- if (dest->lud_host == NULL) {
- ldap_free_urldesc(dest);
- return NULL;
- }
- }
- if ( ludp->lud_dn != NULL ) {
- dest->lud_dn = LDAP_STRDUP( ludp->lud_dn );
- if (dest->lud_dn == NULL) {
- ldap_free_urldesc(dest);
- return NULL;
- }
- }
- if ( ludp->lud_filter != NULL ) {
- dest->lud_filter = LDAP_STRDUP( ludp->lud_filter );
- if (dest->lud_filter == NULL) {
- ldap_free_urldesc(dest);
- return NULL;
- }
- }
- if ( ludp->lud_attrs != NULL ) {
- 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 ) {
- dest->lud_exts = ldap_charray_dup( ludp->lud_exts );
- if (dest->lud_exts == NULL) {
- ldap_free_urldesc(dest);
- return NULL;
- }
- }
- 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;
- }
- static int
- ldap_url_parselist_int (LDAPURLDesc **ludlist, const char *url, const char *sep, unsigned flags )
-
- {
- int i, rc;
- LDAPURLDesc *ludp;
- char **urls;
- assert( ludlist != NULL );
- assert( url != NULL );
- *ludlist = NULL;
- if ( sep == NULL ) {
- sep = ", ";
- }
- urls = ldap_str2charray( url, sep );
- if (urls == NULL)
- 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_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;
- }
- 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
- 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];
- 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 = strchr( ludp->lud_host+1, ']' );
- if ( p == NULL ) {
- LDAP_FREE(ludp);
- ldap_charray_free(specs);
- return LDAP_PARAM_ERROR;
- }
- /* Truncate trailing ']' and shift hostname down 1 char */
- *p = '\0';
- AC_MEMCPY( ludp->lud_host, ludp->lud_host+1, p - ludp->lud_host );
- p++;
- if ( *p != ':' ) {
- if ( *p != '\0' ) {
- LDAP_FREE(ludp);
- ldap_charray_free(specs);
- return LDAP_PARAM_ERROR;
- }
- p = NULL;
- }
- } else {
- p = NULL;
- }
- }
- 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;
- }
- }
- }
- ludp->lud_scheme = LDAP_STRDUP("ldap");
- if ( ludp->lud_scheme == NULL ) {
- LDAP_FREE(ludp);
- ldap_charray_free(specs);
- return LDAP_NO_MEMORY;
- }
- specs[i] = NULL;
- ldap_pvt_hex_unescape(ludp->lud_host);
- ludp->lud_next = *ludlist;
- *ludlist = ludp;
- }
- /* this should be an array of NULLs now */
- 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) {
- if ( ludp->lud_host == NULL ) continue;
- 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 ( ludp->lud_host == NULL ) continue;
- 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;
- }
- char *
- ldap_url_list2urls(
- LDAPURLDesc *ludlist )
- {
- LDAPURLDesc *ludp;
- int size, sofar;
- char *s;
- if ( ludlist == NULL ) {
- return NULL;
- }
- /* 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 ( s == NULL ) {
- return NULL;
- }
- 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;
- for (ludp = ludlist; ludp != NULL; ludp = next) {
- next = ludp->lud_next;
- ldap_free_urldesc(ludp);
- }
- }
- void
- ldap_free_urldesc( LDAPURLDesc *ludp )
- {
- 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 ( ludp->lud_filter != NULL ) {
- LDAP_FREE( ludp->lud_filter);
- }
- if ( ludp->lud_attrs != NULL ) {
- LDAP_VFREE( ludp->lud_attrs );
- }
- if ( ludp->lud_exts != NULL ) {
- LDAP_VFREE( ludp->lud_exts );
- }
- 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 )
- {
- 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 == '%' ) {
- /*
- * FIXME: what if '%' is followed
- * by non-hexpair chars?
- */
- if ( !ldap_int_is_hexpair( s + 1 ) ) {
- p = save_s;
- break;
- }
- if ( *++s == '\0' ) {
- break;
- }
- *p = ldap_int_unhex( *s ) << 4;
- if ( *++s == '\0' ) {
- break;
- }
- *p++ += ldap_int_unhex( *s );
- } else {
- *p++ = *s;
- }
- }
- *p = '\0';
- }
|