]> git.sur5r.net Git - openldap/blob - libraries/libldap/url.c
aa5b27645ff90dca69fb697bb1ede0a5db46ed70
[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  *    ldap[s]://host:port/dn[[?attributes[?scope[?filter[?extensions]]]]
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://host:port/dc=com?o,cn?base?o=openldap?extension
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
28 #include <ac/stdlib.h>
29
30 #include <ac/ctype.h>
31 #include <ac/socket.h>
32 #include <ac/string.h>
33 #include <ac/time.h>
34
35 #include "ldap-int.h"
36
37
38 /* local functions */
39 static const char* skip_url_prefix LDAP_P((
40         const char *url,
41         int *enclosedp,
42         int *ldaps ));
43 static void hex_unescape LDAP_P(( char *s ));
44 static int unhex( char c );
45
46
47 int
48 ldap_is_ldap_url( LDAP_CONST char *url )
49 {
50         int     enclosed;
51         int ldaps;
52
53         if( url == NULL ) {
54                 return 0;
55         }
56
57         if( skip_url_prefix( url, &enclosed, &ldaps) == NULL ) {
58                 return 0;
59         }
60
61         return !ldaps;
62 }
63
64 int
65 ldap_is_ldaps_url( LDAP_CONST char *url )
66 {
67         int     enclosed;
68         int ldaps;
69
70         if( url == NULL ) {
71                 return 0;
72         }
73
74         if( skip_url_prefix( url, &enclosed, &ldaps) == NULL ) {
75                 return 0;
76         }
77
78         return ldaps;
79 }
80
81 static const char*
82 skip_url_prefix(
83         const char *url,
84         int *enclosedp,
85         int *ldaps )
86 {
87 /*
88  * return non-zero if this looks like a LDAP URL; zero if not
89  * if non-zero returned, *urlp will be moved past "ldap://" part of URL
90  */
91         char* p;
92
93         if ( url == NULL ) {
94                 return( NULL );
95         }
96
97         p = (char *) url;
98
99         /* skip leading '<' (if any) */
100         if ( *p == '<' ) {
101                 *enclosedp = 1;
102                 ++p;
103         } else {
104                 *enclosedp = 0;
105         }
106
107         /* skip leading "URL:" (if any) */
108         if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 )
109         {
110                 p += LDAP_URL_URLCOLON_LEN;
111         }
112
113         /* check for "ldap://" prefix */
114         if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
115                 /* skip over "ldap://" prefix and return success */
116                 p += LDAP_URL_PREFIX_LEN;
117                 *ldaps = 0;
118                 return( p );
119         }
120
121         /* check for "ldaps://" prefix */
122         if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
123                 /* skip over "ldaps://" prefix and return success */
124                 p += LDAPS_URL_PREFIX_LEN;
125                 *ldaps = 1;
126                 return( p );
127         }
128
129         return( NULL );
130 }
131
132
133 static int str2scope( const char *p )
134 {
135         if ( strcasecmp( p, "one" ) == 0 ) {
136                 return LDAP_SCOPE_ONELEVEL;
137
138         } else if ( strcasecmp( p, "onetree" ) == 0 ) {
139                 return LDAP_SCOPE_ONELEVEL;
140
141         } else if ( strcasecmp( p, "base" ) == 0 ) {
142                 return LDAP_SCOPE_BASE;
143
144         } else if ( strcasecmp( p, "sub" ) == 0 ) {
145                 return LDAP_SCOPE_SUBTREE;
146
147         } else if ( strcasecmp( p, "subtree" ) == 0 ) {
148                 return LDAP_SCOPE_SUBTREE;
149         }
150
151         return( -1 );
152 }
153
154
155 int
156 ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
157 {
158 /*
159  *  Pick apart the pieces of an LDAP URL.
160  */
161
162         LDAPURLDesc     *ludp;
163         char    *p, *q;
164         int             i, enclosed, ldaps;
165         const char *url_tmp;
166         char *url;
167
168         if( url_in == NULL && ludpp == NULL ) {
169                 return LDAP_URL_ERR_PARAM;
170         }
171
172         Debug( LDAP_DEBUG_TRACE, "ldap_url_parse(%s)\n", url_in, 0, 0 );
173
174         *ludpp = NULL;  /* pessimistic */
175
176         url_tmp = skip_url_prefix( url_in, &enclosed, &ldaps );
177
178         if ( url_tmp == NULL ) {
179                 return LDAP_URL_ERR_NOTLDAP;
180         }
181
182         /* make working copy of the remainder of the URL */
183         if (( url = LDAP_STRDUP( url_tmp )) == NULL ) {
184                 return( LDAP_URL_ERR_MEM );
185         }
186
187         if ( enclosed ) {
188                 p = &url[strlen(url)-1];
189
190                 if( *p != '>' ) {
191                         LDAP_FREE( url );
192                         return LDAP_URL_ERR_BADENCLOSURE;
193                 }
194
195                 *p = '\0';
196         }
197
198         /* allocate return struct */
199         ludp = (LDAPURLDesc *)LDAP_CALLOC( 1, sizeof( LDAPURLDesc ));
200
201         if ( ludp == NULL ) {
202                 LDAP_FREE( url );
203                 return LDAP_URL_ERR_MEM;
204         }
205
206         ludp->lud_host = NULL;
207         ludp->lud_port = 0;
208     ludp->lud_dn = NULL;
209     ludp->lud_attrs = NULL;
210     ludp->lud_filter = NULL;
211         ludp->lud_ldaps = ldaps;
212         ludp->lud_scope = LDAP_SCOPE_BASE;
213         ludp->lud_filter = LDAP_STRDUP("(objectClass=*)");
214
215         if( ludp->lud_filter == NULL ) {
216                 LDAP_FREE( url );
217                 ldap_free_urldesc( ludp );
218                 return LDAP_URL_ERR_MEM;
219         }
220
221         /* scan forward for '/' that marks end of hostport and begin. of dn */
222         p = strchr( url, '/' );
223
224         if( p == NULL ) {
225                 LDAP_FREE( url );
226                 ldap_free_urldesc( ludp );
227                 return LDAP_URL_ERR_BADURL;
228         }
229
230         /* terminate hostport; point to start of dn */
231         *p++ = '\0';
232
233         if (( q = strchr( url, ':' )) != NULL ) {
234                 *q++ = '\0';
235                 hex_unescape( q );
236
237                 if( *q == '\0' ) {
238                         LDAP_FREE( url );
239                         ldap_free_urldesc( ludp );
240                         return LDAP_URL_ERR_BADURL;
241                 }
242
243                 ludp->lud_port = atoi( q );
244         }
245
246         hex_unescape( url );
247         ludp->lud_host = LDAP_STRDUP( url );
248
249         if( ludp->lud_host == NULL ) {
250                 LDAP_FREE( url );
251                 ldap_free_urldesc( ludp );
252                 return LDAP_URL_ERR_MEM;
253         }
254
255         /* scan forward for '?' that may marks end of dn */
256         q = strchr( p, '?' );
257
258         if( q == NULL ) {
259                 /* no '?' */
260                 hex_unescape( p );
261                 ludp->lud_dn = LDAP_STRDUP( p );
262
263                 if( ludp->lud_dn == NULL ) {
264                         LDAP_FREE( url );
265                         ldap_free_urldesc( ludp );
266                         return LDAP_URL_ERR_MEM;
267                 }
268
269                 LDAP_FREE( url );
270                 *ludpp = ludp;
271                 return LDAP_URL_SUCCESS;
272         }
273
274         *q++ = '\0';
275         hex_unescape( p );
276         ludp->lud_dn = LDAP_STRDUP( p );
277
278         if( ludp->lud_dn == NULL ) {
279                 LDAP_FREE( url );
280                 ldap_free_urldesc( ludp );
281                 return LDAP_URL_ERR_MEM;
282         }
283
284         /* scan forward for '?' that may marks end of attributes */
285         p = q;
286         q = strchr( p, '?' );
287
288         if( q == NULL ) {
289                 /* no '?' */
290                 hex_unescape( p );
291                 ludp->lud_attrs = ldap_str2charray( p, "," );
292
293                 if( ludp->lud_attrs == NULL ) {
294                         LDAP_FREE( url );
295                         ldap_free_urldesc( ludp );
296                         return LDAP_URL_ERR_BADATTRS;
297                 }
298
299                 LDAP_FREE( url );
300                 *ludpp = ludp;
301                 return LDAP_URL_SUCCESS;
302         }
303
304         *q++ = '\0';
305         hex_unescape( p );
306         ludp->lud_attrs = ldap_str2charray( p, "," );
307
308         /* scan forward for '?' that may marks end of scope */
309         p = q;
310         q = strchr( p, '?' );
311
312         if( q == NULL ) {
313                 /* no '?' */
314                 hex_unescape( p );
315                 ludp->lud_scope = str2scope( p );
316
317                 if( ludp->lud_scope == -1 ) {
318                         LDAP_FREE( url );
319                         ldap_free_urldesc( ludp );
320                         return LDAP_URL_ERR_BADSCOPE;
321                 }
322
323                 LDAP_FREE( url );
324                 *ludpp = ludp;
325                 return LDAP_URL_SUCCESS;
326         }
327
328         *q++ = '\0';
329         hex_unescape( p );
330
331         if( *p != '\0' ) {
332                 ludp->lud_scope = str2scope( p );
333
334                 if( ludp->lud_scope == -1 ) {
335                         LDAP_FREE( url );
336                         ldap_free_urldesc( ludp );
337                         return LDAP_URL_ERR_BADSCOPE;
338                 }
339
340                 LDAP_FREE( url );
341                 *ludpp = ludp;
342                 return LDAP_URL_SUCCESS;
343         }
344
345         /* scan forward for '?' that may marks end of filter */
346         p = q;
347         q = strchr( p, '?' );
348
349         if( *p != '\0' ) {
350                 /* no '?' */
351                 hex_unescape( p );
352
353                 if( ! *p ) {
354                         /* missing filter */
355                         LDAP_FREE( url );
356                         ldap_free_urldesc( ludp );
357                         return LDAP_URL_ERR_BADFILTER;
358                 }
359
360                 ludp->lud_filter = LDAP_STRDUP( p );
361
362                 if( ludp->lud_filter == NULL ) {
363                         LDAP_FREE( url );
364                         ldap_free_urldesc( ludp );
365                         return LDAP_URL_ERR_MEM;
366                 }
367
368                 LDAP_FREE( url );
369                 *ludpp = ludp;
370                 return LDAP_URL_SUCCESS;
371         }
372
373         *q++ = '\0';
374         hex_unescape( p );
375
376         if( *p != '\0' ) {
377                 ludp->lud_filter = LDAP_STRDUP( p );
378
379                 if( ludp->lud_filter == NULL ) {
380                         LDAP_FREE( url );
381                         ldap_free_urldesc( ludp );
382                         return LDAP_URL_ERR_MEM;
383                 }
384         }
385         
386         /* scan forward for '?' that may marks end of extensions */
387         p = q;
388         q = strchr( p, '?' );
389
390         if( q != NULL ) {
391                 /* extra '?' */
392                 LDAP_FREE( url );
393                 ldap_free_urldesc( ludp );
394                 return LDAP_URL_ERR_BADURL;
395         }
396
397         ludp->lud_exts = ldap_str2charray( p, "," );
398
399         if( ludp->lud_exts == NULL ) {
400                 LDAP_FREE( url );
401                 ldap_free_urldesc( ludp );
402                 return LDAP_URL_ERR_BADEXTS;
403         }
404
405         for( i=0; ludp->lud_exts[i] != NULL; i++ ) {
406                 hex_unescape( ludp->lud_exts[i] );
407         }
408
409         if( i == 0 ) {
410                 ldap_charray_free( ludp->lud_exts );
411                 LDAP_FREE( url );
412                 ldap_free_urldesc( ludp );
413                 return LDAP_URL_ERR_BADEXTS;
414         }
415
416         *ludpp = ludp;
417
418         LDAP_FREE( url );
419         return LDAP_URL_SUCCESS;
420 }
421
422
423 void
424 ldap_free_urldesc( LDAPURLDesc *ludp )
425 {
426         if ( ludp == NULL ) {
427                 return;
428         }
429         
430         if ( ludp->lud_host != NULL ) {
431                 LDAP_FREE( ludp->lud_host );
432         }
433
434         if ( ludp->lud_dn != NULL ) {
435                 LDAP_FREE( ludp->lud_dn );
436         }
437
438         if ( ludp->lud_filter != NULL ) {
439                 LDAP_FREE( ludp->lud_filter);
440         }
441
442         if ( ludp->lud_attrs != NULL ) {
443                 LDAP_VFREE( ludp->lud_attrs );
444         }
445
446         if ( ludp->lud_exts != NULL ) {
447                 LDAP_VFREE( ludp->lud_exts );
448         }
449
450         LDAP_FREE( ludp );
451 }
452
453
454
455 int
456 ldap_url_search( LDAP *ld, LDAP_CONST char *url, int attrsonly )
457 {
458         int             err;
459         LDAPURLDesc     *ludp;
460         BerElement      *ber;
461         LDAPServer      *srv = NULL;
462
463         if ( ldap_url_parse( url, &ludp ) != 0 ) {
464                 ld->ld_errno = LDAP_PARAM_ERROR;
465                 return( -1 );
466         }
467
468         ber = ldap_build_search_req( ld, ludp->lud_dn, ludp->lud_scope,
469             ludp->lud_filter, ludp->lud_attrs, attrsonly, NULL, NULL,
470                 -1, -1 );
471
472         if ( ber == NULL ) {
473                 return( -1 );
474         }
475
476         err = 0;
477
478         if ( ludp->lud_host != NULL || ludp->lud_port != 0 ) {
479                 if (( srv = (LDAPServer *)LDAP_CALLOC( 1, sizeof( LDAPServer )))
480                     == NULL || ( srv->lsrv_host = LDAP_STRDUP( ludp->lud_host ==
481                     NULL ? ld->ld_defhost : ludp->lud_host )) == NULL ) {
482                         if ( srv != NULL ) {
483                                 LDAP_FREE( srv );
484                         }
485                         ld->ld_errno = LDAP_NO_MEMORY;
486                         err = -1;
487                 } else {
488                         if ( ludp->lud_port == 0 ) {
489                                 srv->lsrv_port = ldap_int_global_options.ldo_defport;
490                         } else {
491                                 srv->lsrv_port = ludp->lud_port;
492                         }
493                 }
494         }
495
496         if ( err != 0 ) {
497                 ber_free( ber, 1 );
498         } else {
499                 err = ldap_send_server_request( ld, ber, ld->ld_msgid, NULL, srv,
500                     NULL, 1 );
501         }
502
503         ldap_free_urldesc( ludp );
504
505         return( err );
506 }
507
508
509 int
510 ldap_url_search_st( LDAP *ld, LDAP_CONST char *url, int attrsonly,
511         struct timeval *timeout, LDAPMessage **res )
512 {
513         int     msgid;
514
515         if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
516                 return( ld->ld_errno );
517         }
518
519         if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 ) {
520                 return( ld->ld_errno );
521         }
522
523         if ( ld->ld_errno == LDAP_TIMEOUT ) {
524                 (void) ldap_abandon( ld, msgid );
525                 ld->ld_errno = LDAP_TIMEOUT;
526                 return( ld->ld_errno );
527         }
528
529         return( ldap_result2error( ld, *res, 0 ));
530 }
531
532
533 int
534 ldap_url_search_s(
535         LDAP *ld, LDAP_CONST char *url, int attrsonly, LDAPMessage **res )
536 {
537         int     msgid;
538
539         if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
540                 return( ld->ld_errno );
541         }
542
543         if ( ldap_result( ld, msgid, 1, (struct timeval *)NULL, res ) == -1 ) {
544                 return( ld->ld_errno );
545         }
546
547         return( ldap_result2error( ld, *res, 0 ));
548 }
549
550
551 static void
552 hex_unescape( char *s )
553 {
554 /*
555  * Remove URL hex escapes from s... done in place.  The basic concept for
556  * this routine is borrowed from the WWW library HTUnEscape() routine.
557  */
558         char    *p;
559
560         for ( p = s; *s != '\0'; ++s ) {
561                 if ( *s == '%' ) {
562                         if ( *++s != '\0' ) {
563                                 *p = unhex( *s ) << 4;
564                         }
565                         if ( *++s != '\0' ) {
566                                 *p++ += unhex( *s );
567                         }
568                 } else {
569                         *p++ = *s;
570                 }
571         }
572
573         *p = '\0';
574 }
575
576
577 static int
578 unhex( char c )
579 {
580         return( c >= '0' && c <= '9' ? c - '0'
581             : c >= 'A' && c <= 'F' ? c - 'A' + 10
582             : c - 'a' + 10 );
583 }