]> git.sur5r.net Git - openldap/blob - libraries/libldap/url.c
Update PF_INET6 and PF_UNIX detection, both default to auto
[openldap] / libraries / libldap / url.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2000 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[is]://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         const char **scheme ));
44
45 int ldap_pvt_url_scheme2proto( const char *scheme )
46 {
47         assert( scheme );
48
49         if( scheme == NULL ) {
50                 return -1;
51         }
52
53         if( strcmp("ldap", scheme) == 0 ) {
54                 return LDAP_PROTO_TCP;
55         }
56
57         if( strcmp("ldapi", scheme) == 0 ) {
58                 return LDAP_PROTO_IPC;
59         }
60
61         if( strcmp("ldaps", scheme) == 0 ) {
62                 return LDAP_PROTO_TCP;
63         }
64
65         return -1;
66 }
67
68 int ldap_pvt_url_scheme2tls( const char *scheme )
69 {
70         assert( scheme );
71
72         if( scheme == NULL ) {
73                 return -1;
74         }
75
76         return strcmp("ldaps", scheme) == 0;
77 }
78
79 int
80 ldap_is_ldap_url( LDAP_CONST char *url )
81 {
82         int     enclosed;
83         const char * scheme;
84
85         if( url == NULL ) {
86                 return 0;
87         }
88
89         if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
90                 return 0;
91         }
92
93         return 1;
94 }
95
96 int
97 ldap_is_ldaps_url( LDAP_CONST char *url )
98 {
99         int     enclosed;
100         const char * scheme;
101
102         if( url == NULL ) {
103                 return 0;
104         }
105
106         if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
107                 return 0;
108         }
109
110         return strcmp(scheme, "ldaps") == 0;
111 }
112
113 int
114 ldap_is_ldapi_url( LDAP_CONST char *url )
115 {
116         int     enclosed;
117         const char * scheme;
118
119         if( url == NULL ) {
120                 return 0;
121         }
122
123         if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
124                 return 0;
125         }
126
127         return strcmp(scheme, "ldapi") == 0;
128 }
129
130 static const char*
131 skip_url_prefix(
132         const char *url,
133         int *enclosedp,
134         const char **scheme )
135 {
136 /*
137  * return non-zero if this looks like a LDAP URL; zero if not
138  * if non-zero returned, *urlp will be moved past "ldap://" part of URL
139  */
140         const char *p;
141
142         if ( url == NULL ) {
143                 return( NULL );
144         }
145
146         p = url;
147
148         /* skip leading '<' (if any) */
149         if ( *p == '<' ) {
150                 *enclosedp = 1;
151                 ++p;
152         } else {
153                 *enclosedp = 0;
154         }
155
156         /* skip leading "URL:" (if any) */
157         if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 )
158         {
159                 p += LDAP_URL_URLCOLON_LEN;
160         }
161
162         /* check for "ldap://" prefix */
163         if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
164                 /* skip over "ldap://" prefix and return success */
165                 p += LDAP_URL_PREFIX_LEN;
166                 *scheme = "ldap";
167                 return( p );
168         }
169
170         /* check for "ldaps://" prefix */
171         if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
172                 /* skip over "ldaps://" prefix and return success */
173                 p += LDAPS_URL_PREFIX_LEN;
174                 *scheme = "ldaps";
175                 return( p );
176         }
177
178         /* check for "ldapi://" prefix */
179         if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) {
180                 /* skip over "ldapi://" prefix and return success */
181                 p += LDAPI_URL_PREFIX_LEN;
182                 *scheme = "ldapi";
183                 return( p );
184         }
185
186         return( NULL );
187 }
188
189
190 static int str2scope( const char *p )
191 {
192         if ( strcasecmp( p, "one" ) == 0 ) {
193                 return LDAP_SCOPE_ONELEVEL;
194
195         } else if ( strcasecmp( p, "onetree" ) == 0 ) {
196                 return LDAP_SCOPE_ONELEVEL;
197
198         } else if ( strcasecmp( p, "base" ) == 0 ) {
199                 return LDAP_SCOPE_BASE;
200
201         } else if ( strcasecmp( p, "sub" ) == 0 ) {
202                 return LDAP_SCOPE_SUBTREE;
203
204         } else if ( strcasecmp( p, "subtree" ) == 0 ) {
205                 return LDAP_SCOPE_SUBTREE;
206         }
207
208         return( -1 );
209 }
210
211
212 int
213 ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
214 {
215 /*
216  *  Pick apart the pieces of an LDAP URL.
217  */
218
219         LDAPURLDesc     *ludp;
220         char    *p, *q;
221         int             i, enclosed;
222         const char *scheme = NULL;
223         const char *url_tmp;
224         char *url;
225
226         if( url_in == NULL && ludpp == NULL ) {
227                 return LDAP_URL_ERR_PARAM;
228         }
229
230 #ifndef LDAP_INT_IN_KERNEL
231                 /* Global options may not be created yet
232                  * We can't test if the global options are initialized
233                  * because a call to LDAP_INT_GLOBAL_OPT() will try to allocate
234                  * the options and cause infinite recursion
235                  */
236         Debug( LDAP_DEBUG_TRACE, "ldap_url_parse(%s)\n", url_in, 0, 0 );
237 #endif
238
239         *ludpp = NULL;  /* pessimistic */
240
241         url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
242
243         if ( url_tmp == NULL ) {
244                 return LDAP_URL_ERR_BADSCHEME;
245         }
246
247         assert( scheme );
248
249         /* make working copy of the remainder of the URL */
250         if (( url = LDAP_STRDUP( url_tmp )) == NULL ) {
251                 return( LDAP_URL_ERR_MEM );
252         }
253
254         if ( enclosed ) {
255                 p = &url[strlen(url)-1];
256
257                 if( *p != '>' ) {
258                         LDAP_FREE( url );
259                         return LDAP_URL_ERR_BADENCLOSURE;
260                 }
261
262                 *p = '\0';
263         }
264
265         /* allocate return struct */
266         ludp = (LDAPURLDesc *)LDAP_CALLOC( 1, sizeof( LDAPURLDesc ));
267
268         if ( ludp == NULL ) {
269                 LDAP_FREE( url );
270                 return LDAP_URL_ERR_MEM;
271         }
272
273         ludp->lud_next = NULL;
274         ludp->lud_host = NULL;
275         ludp->lud_port = 0;
276         ludp->lud_dn = NULL;
277         ludp->lud_attrs = NULL;
278         ludp->lud_filter = NULL;
279         ludp->lud_scope = LDAP_SCOPE_BASE;
280         ludp->lud_filter = NULL;
281
282         ludp->lud_scheme = LDAP_STRDUP( scheme );
283
284         if ( ludp->lud_scheme == NULL ) {
285                 LDAP_FREE( url );
286                 ldap_free_urldesc( ludp );
287                 return LDAP_URL_ERR_MEM;
288         }
289
290         /* scan forward for '/' that marks end of hostport and begin. of dn */
291         p = strchr( url, '/' );
292
293         if( p != NULL ) {
294                 /* terminate hostport; point to start of dn */
295                 *p++ = '\0';
296         }
297
298         if (( q = strchr( url, ':' )) != NULL ) {
299                 *q++ = '\0';
300                 ldap_pvt_hex_unescape( q );
301
302                 if( *q == '\0' ) {
303                         LDAP_FREE( url );
304                         ldap_free_urldesc( ludp );
305                         return LDAP_URL_ERR_BADURL;
306                 }
307
308                 ludp->lud_port = atoi( q );
309         }
310
311         ldap_pvt_hex_unescape( url );
312         ludp->lud_host = LDAP_STRDUP( url );
313
314         if( ludp->lud_host == NULL ) {
315                 LDAP_FREE( url );
316                 ldap_free_urldesc( ludp );
317                 return LDAP_URL_ERR_MEM;
318         }
319
320         /*
321          * Kludge.  ldap://111.222.333.444:389??cn=abc,o=company
322          *
323          * On early Novell releases, search references/referrals were returned
324          * in this format, i.e., the dn was kind of in the scope position,
325          * but the required slash is missing. The whole thing is illegal syntax,
326          * but we need to account for it. Fortunately it can't be confused with
327          * anything real.
328          */
329         if( (p == NULL) && ((q = strchr( q, '?')) != NULL)) {
330                 q++;            
331                 /* ? immediately followed by question */
332                 if( *q == '?') {
333                         q++;
334                         if( *q != '\0' ) {
335                                 /* parse dn part */
336                                 ldap_pvt_hex_unescape( q );
337                                 ludp->lud_dn = LDAP_STRDUP( q );
338                         } else {
339                                 ludp->lud_dn = LDAP_STRDUP( "" );
340                         }
341
342                         if( ludp->lud_dn == NULL ) {
343                                 LDAP_FREE( url );
344                                 ldap_free_urldesc( ludp );
345                                 return LDAP_URL_ERR_MEM;
346                         }
347                 }
348         }
349
350         if( p == NULL ) {
351                 LDAP_FREE( url );
352                 *ludpp = ludp;
353                 return LDAP_URL_SUCCESS;
354         }
355
356         /* scan forward for '?' that may marks end of dn */
357         q = strchr( p, '?' );
358
359         if( q != NULL ) {
360                 /* terminate dn part */
361                 *q++ = '\0';
362         }
363
364         if( *p != '\0' ) {
365                 /* parse dn part */
366                 ldap_pvt_hex_unescape( p );
367                 ludp->lud_dn = LDAP_STRDUP( p );
368         } else {
369                 ludp->lud_dn = LDAP_STRDUP( "" );
370         }
371
372         if( ludp->lud_dn == NULL ) {
373                 LDAP_FREE( url );
374                 ldap_free_urldesc( ludp );
375                 return LDAP_URL_ERR_MEM;
376         }
377
378         if( q == NULL ) {
379                 /* no more */
380                 LDAP_FREE( url );
381                 *ludpp = ludp;
382                 return LDAP_URL_SUCCESS;
383         }
384
385         /* scan forward for '?' that may marks end of attributes */
386         p = q;
387         q = strchr( p, '?' );
388
389         if( q != NULL ) {
390                 /* terminate attributes part */
391                 *q++ = '\0';
392         }
393
394         if( *p != '\0' ) {
395                 /* parse attributes */
396                 ldap_pvt_hex_unescape( p );
397                 ludp->lud_attrs = ldap_str2charray( p, "," );
398
399                 if( ludp->lud_attrs == NULL ) {
400                         LDAP_FREE( url );
401                         ldap_free_urldesc( ludp );
402                         return LDAP_URL_ERR_BADATTRS;
403                 }
404         }
405
406         if ( q == NULL ) {
407                 /* no more */
408                 LDAP_FREE( url );
409                 *ludpp = ludp;
410                 return LDAP_URL_SUCCESS;
411         }
412
413         /* scan forward for '?' that may marks end of scope */
414         p = q;
415         q = strchr( p, '?' );
416
417         if( q != NULL ) {
418                 /* terminate the scope part */
419                 *q++ = '\0';
420         }
421
422         if( *p != '\0' ) {
423                 /* parse the scope */
424                 ldap_pvt_hex_unescape( p );
425                 ludp->lud_scope = str2scope( p );
426
427                 if( ludp->lud_scope == -1 ) {
428                         LDAP_FREE( url );
429                         ldap_free_urldesc( ludp );
430                         return LDAP_URL_ERR_BADSCOPE;
431                 }
432         }
433
434         if ( q == NULL ) {
435                 /* no more */
436                 LDAP_FREE( url );
437                 *ludpp = ludp;
438                 return LDAP_URL_SUCCESS;
439         }
440
441         /* scan forward for '?' that may marks end of filter */
442         p = q;
443         q = strchr( p, '?' );
444
445         if( q != NULL ) {
446                 /* terminate the filter part */
447                 *q++ = '\0';
448         }
449
450         if( *p != '\0' ) {
451                 /* parse the filter */
452                 ldap_pvt_hex_unescape( p );
453
454                 if( ! *p ) {
455                         /* missing filter */
456                         LDAP_FREE( url );
457                         ldap_free_urldesc( ludp );
458                         return LDAP_URL_ERR_BADFILTER;
459                 }
460
461                 LDAP_FREE( ludp->lud_filter );
462                 ludp->lud_filter = LDAP_STRDUP( p );
463
464                 if( ludp->lud_filter == NULL ) {
465                         LDAP_FREE( url );
466                         ldap_free_urldesc( ludp );
467                         return LDAP_URL_ERR_MEM;
468                 }
469         }
470
471         if ( q == NULL ) {
472                 /* no more */
473                 LDAP_FREE( url );
474                 *ludpp = ludp;
475                 return LDAP_URL_SUCCESS;
476         }
477
478         /* scan forward for '?' that may marks end of extensions */
479         p = q;
480         q = strchr( p, '?' );
481
482         if( q != NULL ) {
483                 /* extra '?' */
484                 LDAP_FREE( url );
485                 ldap_free_urldesc( ludp );
486                 return LDAP_URL_ERR_BADURL;
487         }
488
489         /* parse the extensions */
490         ludp->lud_exts = ldap_str2charray( p, "," );
491
492         if( ludp->lud_exts == NULL ) {
493                 LDAP_FREE( url );
494                 ldap_free_urldesc( ludp );
495                 return LDAP_URL_ERR_BADEXTS;
496         }
497
498         for( i=0; ludp->lud_exts[i] != NULL; i++ ) {
499                 ldap_pvt_hex_unescape( ludp->lud_exts[i] );
500         }
501
502         if( i == 0 ) {
503                 /* must have 1 or more */
504                 ldap_charray_free( ludp->lud_exts );
505                 LDAP_FREE( url );
506                 ldap_free_urldesc( ludp );
507                 return LDAP_URL_ERR_BADEXTS;
508         }
509
510         /* no more */
511         *ludpp = ludp;
512         LDAP_FREE( url );
513         return LDAP_URL_SUCCESS;
514 }
515
516 LDAPURLDesc *
517 ldap_url_dup ( LDAPURLDesc *ludp )
518 {
519         LDAPURLDesc *dest;
520
521         if ( ludp == NULL ) {
522                 return NULL;
523         }
524
525         dest = LDAP_MALLOC( sizeof(LDAPURLDesc) );
526         if (dest == NULL)
527                 return NULL;
528         
529         *dest = *ludp;
530         dest->lud_scheme = NULL;
531         dest->lud_host = NULL;
532         dest->lud_dn = NULL;
533         dest->lud_filter = NULL;
534         dest->lud_attrs = NULL;
535         dest->lud_exts = NULL;
536         dest->lud_next = NULL;
537
538         if ( ludp->lud_scheme != NULL ) {
539                 dest->lud_scheme = LDAP_STRDUP( ludp->lud_scheme );
540                 if (dest->lud_scheme == NULL) {
541                         ldap_free_urldesc(dest);
542                         return NULL;
543                 }
544         }
545
546         if ( ludp->lud_host != NULL ) {
547                 dest->lud_host = LDAP_STRDUP( ludp->lud_host );
548                 if (dest->lud_host == NULL) {
549                         ldap_free_urldesc(dest);
550                         return NULL;
551                 }
552         }
553
554         if ( ludp->lud_dn != NULL ) {
555                 dest->lud_dn = LDAP_STRDUP( ludp->lud_dn );
556                 if (dest->lud_dn == NULL) {
557                         ldap_free_urldesc(dest);
558                         return NULL;
559                 }
560         }
561
562         if ( ludp->lud_filter != NULL ) {
563                 dest->lud_filter = LDAP_STRDUP( ludp->lud_filter );
564                 if (dest->lud_filter == NULL) {
565                         ldap_free_urldesc(dest);
566                         return NULL;
567                 }
568         }
569
570         if ( ludp->lud_attrs != NULL ) {
571                 dest->lud_attrs = ldap_charray_dup( ludp->lud_attrs );
572                 if (dest->lud_attrs == NULL) {
573                         ldap_free_urldesc(dest);
574                         return NULL;
575                 }
576         }
577
578         if ( ludp->lud_exts != NULL ) {
579                 dest->lud_exts = ldap_charray_dup( ludp->lud_exts );
580                 if (dest->lud_exts == NULL) {
581                         ldap_free_urldesc(dest);
582                         return NULL;
583                 }
584         }
585
586         return dest;
587 }
588
589 LDAPURLDesc *
590 ldap_url_duplist (LDAPURLDesc *ludlist)
591 {
592         LDAPURLDesc *dest, *tail, *ludp, *newludp;
593
594         dest = NULL;
595         tail = NULL;
596         for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
597                 newludp = ldap_url_dup(ludp);
598                 if (newludp == NULL) {
599                         ldap_free_urllist(dest);
600                         return NULL;
601                 }
602                 if (tail == NULL)
603                         dest = newludp;
604                 else
605                         tail->lud_next = newludp;
606                 tail = newludp;
607         }
608         return dest;
609 }
610
611 int
612 ldap_url_parselist (LDAPURLDesc **ludlist, const char *url )
613 {
614         int i, rc;
615         LDAPURLDesc *ludp;
616         char **urls;
617
618         *ludlist = NULL;
619
620         if (url == NULL)
621                 return LDAP_PARAM_ERROR;
622
623         urls = ldap_str2charray((char *)url, ", ");
624         if (urls == NULL)
625                 return LDAP_NO_MEMORY;
626
627         /* count the URLs... */
628         for (i = 0; urls[i] != NULL; i++) ;
629         /* ...and put them in the "stack" backward */
630         while (--i >= 0) {
631                 rc = ldap_url_parse( urls[i], &ludp );
632                 if ( rc != 0 ) {
633                         ldap_charray_free(urls);
634                         ldap_free_urllist(*ludlist);
635                         *ludlist = NULL;
636                         return rc;
637                 }
638                 ludp->lud_next = *ludlist;
639                 *ludlist = ludp;
640         }
641         ldap_charray_free(urls);
642         return LDAP_SUCCESS;
643 }
644
645 int
646 ldap_url_parsehosts (LDAPURLDesc **ludlist, const char *hosts )
647 {
648         int i;
649         LDAPURLDesc *ludp;
650         char **specs, *p;
651
652         *ludlist = NULL;
653
654         if (hosts == NULL)
655                 return LDAP_PARAM_ERROR;
656
657         specs = ldap_str2charray((char *)hosts, ", ");
658         if (specs == NULL)
659                 return LDAP_NO_MEMORY;
660
661         /* count the URLs... */
662         for (i = 0; specs[i] != NULL; i++) /* EMPTY */;
663
664         /* ...and put them in the "stack" backward */
665         while (--i >= 0) {
666                 ludp = LDAP_CALLOC( 1, sizeof(LDAPURLDesc) );
667                 if (ludp == NULL) {
668                         ldap_charray_free(specs);
669                         ldap_free_urllist(*ludlist);
670                         *ludlist = NULL;
671                         return LDAP_NO_MEMORY;
672                 }
673                 ludp->lud_host = specs[i];
674                 specs[i] = NULL;
675                 p = strchr(ludp->lud_host, ':');
676                 if (p != NULL) {
677                         *p++ = 0;
678                         ldap_pvt_hex_unescape(p);
679                         ludp->lud_port = atoi(p);
680                 }
681                 ldap_pvt_hex_unescape(ludp->lud_host);
682                 ludp->lud_scheme = LDAP_STRDUP("ldap");
683                 ludp->lud_next = *ludlist;
684                 *ludlist = ludp;
685         }
686
687         /* this should be an array of NULLs now */
688         ldap_charray_free(specs);
689         return LDAP_SUCCESS;
690 }
691
692 char *
693 ldap_url_list2hosts (LDAPURLDesc *ludlist)
694 {
695         LDAPURLDesc *ludp;
696         int size;
697         char *s, *p, buf[32];   /* big enough to hold a long decimal # (overkill) */
698
699         if (ludlist == NULL)
700                 return NULL;
701
702         /* figure out how big the string is */
703         size = 1;       /* nul-term */
704         for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
705                 size += strlen(ludp->lud_host) + 1;             /* host and space */
706                 if (ludp->lud_port != 0)
707                         size += sprintf(buf, ":%d", ludp->lud_port);
708         }
709         s = LDAP_MALLOC(size);
710         if (s == NULL)
711                 return NULL;
712
713         p = s;
714         for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
715                 strcpy(p, ludp->lud_host);
716                 p += strlen(ludp->lud_host);
717                 if (ludp->lud_port != 0)
718                         p += sprintf(p, ":%d", ludp->lud_port);
719                 *p++ = ' ';
720         }
721         if (p != s)
722                 p--;    /* nuke that extra space */
723         *p = 0;
724         return s;
725 }
726
727 char *
728 ldap_url_list2urls(
729         LDAPURLDesc *ludlist )
730 {
731         LDAPURLDesc *ludp;
732         int size;
733         char *s, *p, buf[32];   /* big enough to hold a long decimal # (overkill) */
734
735         if (ludlist == NULL)
736                 return NULL;
737
738         /* figure out how big the string is */
739         size = 1;       /* nul-term */
740         for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
741                 size += strlen(ludp->lud_scheme) + strlen(ludp->lud_host);
742                 size += sizeof(":/// ");
743
744                 if (ludp->lud_port != 0) {
745                         size += sprintf(buf, ":%d", ludp->lud_port);
746                 }
747         }
748
749         s = LDAP_MALLOC(size);
750         if (s == NULL) {
751                 return NULL;
752         }
753
754         p = s;
755         for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
756                 p += sprintf(p, "%s://%s", ludp->lud_scheme, ludp->lud_host);
757                 if (ludp->lud_port != 0)
758                         p += sprintf(p, ":%d", ludp->lud_port);
759                 *p++ = '/';
760                 *p++ = ' ';
761         }
762         if (p != s)
763                 p--;    /* nuke that extra space */
764         *p = 0;
765         return s;
766 }
767
768 void
769 ldap_free_urllist( LDAPURLDesc *ludlist )
770 {
771         LDAPURLDesc *ludp, *next;
772
773         for (ludp = ludlist; ludp != NULL; ludp = next) {
774                 next = ludp->lud_next;
775                 ldap_free_urldesc(ludp);
776         }
777 }
778
779 void
780 ldap_free_urldesc( LDAPURLDesc *ludp )
781 {
782         if ( ludp == NULL ) {
783                 return;
784         }
785         
786         if ( ludp->lud_scheme != NULL ) {
787                 LDAP_FREE( ludp->lud_scheme );
788         }
789
790         if ( ludp->lud_host != NULL ) {
791                 LDAP_FREE( ludp->lud_host );
792         }
793
794         if ( ludp->lud_dn != NULL ) {
795                 LDAP_FREE( ludp->lud_dn );
796         }
797
798         if ( ludp->lud_filter != NULL ) {
799                 LDAP_FREE( ludp->lud_filter);
800         }
801
802         if ( ludp->lud_attrs != NULL ) {
803                 LDAP_VFREE( ludp->lud_attrs );
804         }
805
806         if ( ludp->lud_exts != NULL ) {
807                 LDAP_VFREE( ludp->lud_exts );
808         }
809
810         LDAP_FREE( ludp );
811 }
812
813
814
815 int
816 ldap_url_search( LDAP *ld, LDAP_CONST char *url, int attrsonly )
817 {
818         int             err;
819         LDAPURLDesc     *ludp;
820         BerElement      *ber;
821         LDAPreqinfo  bind;
822
823         if ( ldap_url_parse( url, &ludp ) != 0 ) {
824                 ld->ld_errno = LDAP_PARAM_ERROR;
825                 return( -1 );
826         }
827
828         ber = ldap_build_search_req( ld, ludp->lud_dn, ludp->lud_scope,
829             ludp->lud_filter, ludp->lud_attrs, attrsonly, NULL, NULL,
830                 -1, -1 );
831
832         if ( ber == NULL ) {
833                 err = -1;
834         } else {
835                 bind.ri_request = LDAP_REQ_SEARCH;
836                 bind.ri_msgid = ld->ld_msgid;
837                 bind.ri_url = (char *)url;
838                 err = ldap_send_server_request(
839                                         ld, ber, ld->ld_msgid, NULL,
840                                         (ludp->lud_host != NULL || ludp->lud_port != 0)
841                                                 ? ludp : NULL,
842                                         NULL, &bind );
843         }
844
845         ldap_free_urldesc( ludp );
846         return( err );
847 }
848
849
850 int
851 ldap_url_search_st( LDAP *ld, LDAP_CONST char *url, int attrsonly,
852         struct timeval *timeout, LDAPMessage **res )
853 {
854         int     msgid;
855
856         if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
857                 return( ld->ld_errno );
858         }
859
860         if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 ) {
861                 return( ld->ld_errno );
862         }
863
864         if ( ld->ld_errno == LDAP_TIMEOUT ) {
865                 (void) ldap_abandon( ld, msgid );
866                 ld->ld_errno = LDAP_TIMEOUT;
867                 return( ld->ld_errno );
868         }
869
870         return( ldap_result2error( ld, *res, 0 ));
871 }
872
873
874 int
875 ldap_url_search_s(
876         LDAP *ld, LDAP_CONST char *url, int attrsonly, LDAPMessage **res )
877 {
878         int     msgid;
879
880         if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
881                 return( ld->ld_errno );
882         }
883
884         if ( ldap_result( ld, msgid, 1, (struct timeval *)NULL, res ) == -1 ) {
885                 return( ld->ld_errno );
886         }
887
888         return( ldap_result2error( ld, *res, 0 ));
889 }
890
891
892 void
893 ldap_pvt_hex_unescape( char *s )
894 {
895 /*
896 * Remove URL hex escapes from s... done in place.  The basic concept for
897 * this routine is borrowed from the WWW library HTUnEscape() routine.
898 */
899         char    *p;
900
901         for ( p = s; *s != '\0'; ++s ) {
902                 if ( *s == '%' ) {
903                         if ( *++s != '\0' ) {
904                                 *p = ldap_pvt_unhex( *s ) << 4;
905                         }
906                         if ( *++s != '\0' ) {
907                                 *p++ += ldap_pvt_unhex( *s );
908                         }
909                 } else {
910                         *p++ = *s;
911                 }
912         }
913
914         *p = '\0';
915 }
916
917
918 int
919 ldap_pvt_unhex( int c )
920 {
921         return( c >= '0' && c <= '9' ? c - '0'
922             : c >= 'A' && c <= 'F' ? c - 'A' + 10
923             : c - 'a' + 10 );
924 }