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