]> git.sur5r.net Git - openldap/blob - libraries/libldap/url.c
7541e854152c83d07ad4bd50af95b11760b1b0fa
[openldap] / libraries / libldap / url.c
1 /*
2  * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
3  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
4  */
5 /*  Portions
6  *  Copyright (c) 1996 Regents of the University of Michigan.
7  *  All rights reserved.
8  *
9  *  LIBLDAP url.c -- LDAP URL related routines
10  *
11  *  LDAP URLs look like this:
12  *    l d a p : / / hostport / dn [ ? attributes [ ? scope [ ? filter ] ] ]
13  *
14  *  where:
15  *   attributes is a comma separated list
16  *   scope is one of these three strings:  base one sub (default=base)
17  *   filter is an string-represented filter as in RFC 1558
18  *
19  *  e.g.,  ldap://ldap.itd.umich.edu/c=US?o,description?one?o=umich
20  *
21  *  We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl>
22  */
23
24 #include "portable.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28
29 #include <ac/ctype.h>
30 #include <ac/socket.h>
31 #include <ac/string.h>
32 #include <ac/time.h>
33
34 #include "ldap-int.h"
35
36
37 /* local functions */
38 static const char* skip_url_prefix LDAP_P(( const char *url, int *enclosedp ));
39 static void hex_unescape LDAP_P(( char *s ));
40 static int unhex( char c );
41
42
43 int
44 ldap_is_ldap_url( LDAP_CONST char *url )
45 {
46         int     enclosed;
47
48         return( url != NULL && skip_url_prefix( url, &enclosed ) != NULL );
49 }
50
51
52 static const char*
53 skip_url_prefix( const char *url, int *enclosedp )
54 {
55 /*
56  * return non-zero if this looks like a LDAP URL; zero if not
57  * if non-zero returned, *urlp will be moved past "ldap://" part of URL
58  */
59         char* p;
60
61         if ( url == NULL ) {
62                 return( NULL );
63         }
64
65         p = (char *) url;
66
67         /* skip leading '<' (if any) */
68         if ( *p == '<' ) {
69                 *enclosedp = 1;
70                 ++p;
71         } else {
72                 *enclosedp = 0;
73         }
74
75         /* skip leading "URL:" (if any) */
76         if ( strlen( p ) >= LDAP_URL_URLCOLON_LEN
77                 && strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 )
78         {
79                 p += LDAP_URL_URLCOLON_LEN;
80         }
81
82         /* check for missing "ldap://" prefix */
83         if ( strlen( p ) < LDAP_URL_PREFIX_LEN ||
84             strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) != 0 ) {
85                 return( NULL );
86         }
87
88         /* skip over "ldap://" prefix and return success */
89         p += LDAP_URL_PREFIX_LEN;
90         return( p );
91 }
92
93
94
95 int
96 ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
97 {
98 /*
99  *  Pick apart the pieces of an LDAP URL.
100  */
101
102         LDAPURLDesc     *ludp;
103         char            *attrs, *p, *q;
104         int             enclosed, i, nattrs;
105         const char *url_tmp;
106         char *url;
107
108         Debug( LDAP_DEBUG_TRACE, "ldap_url_parse(%s)\n", url, 0, 0 );
109
110         *ludpp = NULL;  /* pessimistic */
111
112         url_tmp = skip_url_prefix( url_in, &enclosed );
113
114         if ( url_tmp == NULL ) {
115                 return( LDAP_URL_ERR_NOTLDAP );
116         }
117
118         /* make working copy of the remainder of the URL */
119         if (( url = strdup( url_tmp )) == NULL ) {
120                 return( LDAP_URL_ERR_MEM );
121         }
122
123         /* allocate return struct */
124         if (( ludp = (LDAPURLDesc *)calloc( 1, sizeof( LDAPURLDesc )))
125             == NULLLDAPURLDESC )
126         {
127                 free( url );
128                 return( LDAP_URL_ERR_MEM );
129         }
130
131
132         if ( enclosed && *((p = url + strlen( url ) - 1)) == '>' ) {
133                 *p = '\0';
134         }
135
136         /* set defaults */
137         ludp->lud_scope = LDAP_SCOPE_BASE;
138         ludp->lud_filter = "(objectClass=*)";
139
140         /* lud_string is the only malloc'd string space we use */
141         ludp->lud_string = url;
142
143         /* scan forward for '/' that marks end of hostport and begin. of dn */
144         if (( ludp->lud_dn = strchr( url, '/' )) == NULL ) {
145                 ldap_free_urldesc( ludp );
146                 return( LDAP_URL_ERR_NODN );
147         }
148
149         /* terminate hostport; point to start of dn */
150         *ludp->lud_dn++ = '\0';
151
152         if (( p = strchr( url, ':' )) != NULL ) {
153                 *p++ = '\0';
154                 ludp->lud_port = atoi( p );
155         }
156
157         if ( *url == '\0' ) {
158                 ludp->lud_host = NULL;
159         } else {
160                 ludp->lud_host = url;
161                 hex_unescape( ludp->lud_host );
162         }
163
164         /* scan for '?' that marks end of dn and beginning of attributes */
165         if (( attrs = strchr( ludp->lud_dn, '?' )) != NULL ) {
166                 /* terminate dn; point to start of attrs. */
167                 *attrs++ = '\0';
168
169                 /* scan for '?' that marks end of attrs and begin. of scope */
170                 if (( p = strchr( attrs, '?' )) != NULL ) {
171                         /*
172                          * terminate attrs; point to start of scope and scan for
173                          * '?' that marks end of scope and begin. of filter
174                          */
175                         *p++ = '\0';
176
177                         if (( q = strchr( p, '?' )) != NULL ) {
178                                 /* terminate scope; point to start of filter */
179                                 *q++ = '\0';
180                                 if ( *q != '\0' ) {
181                                         ludp->lud_filter = q;
182                                         hex_unescape( ludp->lud_filter );
183                                 }
184                         }
185
186                         if ( strcasecmp( p, "one" ) == 0 ) {
187                                 ludp->lud_scope = LDAP_SCOPE_ONELEVEL;
188                         } else if ( strcasecmp( p, "base" ) == 0 ) {
189                                 ludp->lud_scope = LDAP_SCOPE_BASE;
190                         } else if ( strcasecmp( p, "sub" ) == 0 ) {
191                                 ludp->lud_scope = LDAP_SCOPE_SUBTREE;
192                         } else if ( *p != '\0' ) {
193                                 ldap_free_urldesc( ludp );
194                                 return( LDAP_URL_ERR_BADSCOPE );
195                         }
196                 }
197         }
198
199         if ( *ludp->lud_dn == '\0' ) {
200                 ludp->lud_dn = NULL;
201         } else {
202                 hex_unescape( ludp->lud_dn );
203         }
204
205         /*
206          * if attrs list was included, turn it into a null-terminated array
207          */
208         if ( attrs != NULL && *attrs != '\0' ) {
209                 for ( nattrs = 1, p = attrs; *p != '\0'; ++p ) {
210                     if ( *p == ',' ) {
211                             ++nattrs;
212                     }
213                 }
214
215                 if (( ludp->lud_attrs = (char **)calloc( nattrs + 1,
216                     sizeof( char * ))) == NULL ) {
217                         ldap_free_urldesc( ludp );
218                         return( LDAP_URL_ERR_MEM );
219                 }
220
221                 for ( i = 0, p = attrs; i < nattrs; ++i ) {
222                         ludp->lud_attrs[ i ] = p;
223                         if (( p = strchr( p, ',' )) != NULL ) {
224                                 *p++ ='\0';
225                         }
226                         hex_unescape( ludp->lud_attrs[ i ] );
227                 }
228         }
229
230         *ludpp = ludp;
231
232         return( 0 );
233 }
234
235
236 void
237 ldap_free_urldesc( LDAPURLDesc *ludp )
238 {
239         if ( ludp != NULLLDAPURLDESC ) {
240                 if ( ludp->lud_string != NULL ) {
241                         free( ludp->lud_string );
242                 }
243                 if ( ludp->lud_attrs != NULL ) {
244                         free( ludp->lud_attrs );
245                 }
246                 free( ludp );
247         }
248 }
249
250
251
252 int
253 ldap_url_search( LDAP *ld, LDAP_CONST char *url, int attrsonly )
254 {
255         int             err;
256         LDAPURLDesc     *ludp;
257         BerElement      *ber;
258 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS
259         LDAPServer      *srv = NULL;
260 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS */
261
262         if ( ldap_url_parse( url, &ludp ) != 0 ) {
263                 ld->ld_errno = LDAP_PARAM_ERROR;
264                 return( -1 );
265         }
266
267         ber = ldap_build_search_req( ld, ludp->lud_dn, ludp->lud_scope,
268             ludp->lud_filter, ludp->lud_attrs, attrsonly, NULL, NULL,
269                 -1, -1 );
270
271         if ( ber == NULLBER ) {
272                 return( -1 );
273         }
274
275         err = 0;
276
277         if ( ludp->lud_host != NULL || ludp->lud_port != 0 ) {
278 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS
279                 if (( srv = (LDAPServer *)calloc( 1, sizeof( LDAPServer )))
280                     == NULL || ( srv->lsrv_host = strdup( ludp->lud_host ==
281                     NULL ? ld->ld_defhost : ludp->lud_host )) == NULL ) {
282                         if ( srv != NULL ) {
283                                 free( srv );
284                         }
285                         ld->ld_errno = LDAP_NO_MEMORY;
286                         err = -1;
287                 } else {
288                         if ( ludp->lud_port == 0 ) {
289                                 srv->lsrv_port = openldap_ldap_global_options.ldo_defport;
290                         } else {
291                                 srv->lsrv_port = ludp->lud_port;
292                         }
293                 }
294 #else /* LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS */
295                 ld->ld_errno = LDAP_LOCAL_ERROR;
296                 err = -1;
297 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS */
298         }
299
300         if ( err != 0 ) {
301                 ber_free( ber, 1 );
302         } else {
303 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS
304                 err = ldap_send_server_request( ld, ber, ld->ld_msgid, NULL, srv,
305                     NULL, 1 );
306 #else /* LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS */
307                 err = ldap_send_initial_request( ld, LDAP_REQ_SEARCH,
308                     ludp->lud_dn, ber );
309 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS */
310         }
311
312         ldap_free_urldesc( ludp );
313
314         return( err );
315 }
316
317
318 int
319 ldap_url_search_st( LDAP *ld, LDAP_CONST char *url, int attrsonly,
320         struct timeval *timeout, LDAPMessage **res )
321 {
322         int     msgid;
323
324         if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
325                 return( ld->ld_errno );
326         }
327
328         if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 ) {
329                 return( ld->ld_errno );
330         }
331
332         if ( ld->ld_errno == LDAP_TIMEOUT ) {
333                 (void) ldap_abandon( ld, msgid );
334                 ld->ld_errno = LDAP_TIMEOUT;
335                 return( ld->ld_errno );
336         }
337
338         return( ldap_result2error( ld, *res, 0 ));
339 }
340
341
342 int
343 ldap_url_search_s(
344         LDAP *ld, LDAP_CONST char *url, int attrsonly, LDAPMessage **res )
345 {
346         int     msgid;
347
348         if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
349                 return( ld->ld_errno );
350         }
351
352         if ( ldap_result( ld, msgid, 1, (struct timeval *)NULL, res ) == -1 ) {
353                 return( ld->ld_errno );
354         }
355
356         return( ldap_result2error( ld, *res, 0 ));
357 }
358
359
360 static void
361 hex_unescape( char *s )
362 {
363 /*
364  * Remove URL hex escapes from s... done in place.  The basic concept for
365  * this routine is borrowed from the WWW library HTUnEscape() routine.
366  */
367         char    *p;
368
369         for ( p = s; *s != '\0'; ++s ) {
370                 if ( *s == '%' ) {
371                         if ( *++s != '\0' ) {
372                                 *p = unhex( *s ) << 4;
373                         }
374                         if ( *++s != '\0' ) {
375                                 *p++ += unhex( *s );
376                         }
377                 } else {
378                         *p++ = *s;
379                 }
380         }
381
382         *p = '\0';
383 }
384
385
386 static int
387 unhex( char c )
388 {
389         return( c >= '0' && c <= '9' ? c - '0'
390             : c >= 'A' && c <= 'F' ? c - 'A' + 10
391             : c - 'a' + 10 );
392 }