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