]> git.sur5r.net Git - openldap/blob - libraries/libldap/url.c
5dc0b7038f904b4ad597ce396dbd734a1a628090
[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/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         const char **scheme ));
43
44 int ldap_pvt_url_scheme2proto( const char *scheme )
45 {
46         assert( scheme );
47
48         if( scheme == NULL ) {
49                 return -1;
50         }
51
52         if( strcmp("ldap", scheme) == 0 ) {
53                 return LDAP_PROTO_TCP;
54         }
55
56         if( strcmp("ldapi", scheme) == 0 ) {
57                 return LDAP_PROTO_IPC;
58         }
59
60         if( strcmp("ldaps", scheme) == 0 ) {
61                 return LDAP_PROTO_TCP;
62         }
63 #ifdef LDAP_CONNECTIONLESS
64         if( strcmp("cldap", scheme) == 0 ) {
65                 return LDAP_PROTO_UDP;
66         }
67 #endif
68
69         return -1;
70 }
71
72 LDAP_F(int) ldap_pvt_url_scheme2tls( const char *scheme )
73 {
74         assert( scheme );
75
76         if( scheme == NULL ) {
77                 return -1;
78         }
79
80         return strcmp("ldaps", scheme) == 0;
81 }
82
83 int
84 ldap_is_ldap_url( LDAP_CONST char *url )
85 {
86         int     enclosed;
87         const char * scheme;
88
89         if( url == NULL ) {
90                 return 0;
91         }
92
93         if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
94                 return 0;
95         }
96
97         return 1;
98 }
99
100 int
101 ldap_is_ldaps_url( LDAP_CONST char *url )
102 {
103         int     enclosed;
104         const char * scheme;
105
106         if( url == NULL ) {
107                 return 0;
108         }
109
110         if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
111                 return 0;
112         }
113
114         return strcmp(scheme, "ldaps") == 0;
115 }
116
117 int
118 ldap_is_ldapi_url( LDAP_CONST char *url )
119 {
120         int     enclosed;
121         const char * scheme;
122
123         if( url == NULL ) {
124                 return 0;
125         }
126
127         if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
128                 return 0;
129         }
130
131         return strcmp(scheme, "ldapi") == 0;
132 }
133
134 #ifdef LDAP_CONNECTIONLESS
135 int
136 ldap_is_ldapc_url( LDAP_CONST char *url )
137 {
138         int     enclosed;
139         const char * scheme;
140
141         if( url == NULL ) {
142                 return 0;
143         }
144
145         if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
146                 return 0;
147         }
148
149         return strcmp(scheme, "cldap") == 0;
150 }
151 #endif
152
153 static const char*
154 skip_url_prefix(
155         const char *url,
156         int *enclosedp,
157         const char **scheme )
158 {
159 /*
160  * return non-zero if this looks like a LDAP URL; zero if not
161  * if non-zero returned, *urlp will be moved past "ldap://" part of URL
162  */
163         const char *p;
164
165         if ( url == NULL ) {
166                 return( NULL );
167         }
168
169         p = url;
170
171         /* skip leading '<' (if any) */
172         if ( *p == '<' ) {
173                 *enclosedp = 1;
174                 ++p;
175         } else {
176                 *enclosedp = 0;
177         }
178
179         /* skip leading "URL:" (if any) */
180         if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) {
181                 p += LDAP_URL_URLCOLON_LEN;
182         }
183
184         /* check for "ldap://" prefix */
185         if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
186                 /* skip over "ldap://" prefix and return success */
187                 p += LDAP_URL_PREFIX_LEN;
188                 *scheme = "ldap";
189                 return( p );
190         }
191
192         /* check for "ldaps://" prefix */
193         if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
194                 /* skip over "ldaps://" prefix and return success */
195                 p += LDAPS_URL_PREFIX_LEN;
196                 *scheme = "ldaps";
197                 return( p );
198         }
199
200         /* check for "ldapi://" prefix */
201         if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) {
202                 /* skip over "ldapi://" prefix and return success */
203                 p += LDAPI_URL_PREFIX_LEN;
204                 *scheme = "ldapi";
205                 return( p );
206         }
207
208 #ifdef LDAP_CONNECTIONLESS
209         /* check for "cldap://" prefix */
210         if ( strncasecmp( p, LDAPC_URL_PREFIX, LDAPC_URL_PREFIX_LEN ) == 0 ) {
211                 /* skip over "cldap://" prefix and return success */
212                 p += LDAPC_URL_PREFIX_LEN;
213                 *scheme = "cldap";
214                 return( p );
215         }
216 #endif
217
218         return( NULL );
219 }
220
221
222 static int str2scope( const char *p )
223 {
224         if ( strcasecmp( p, "one" ) == 0 ) {
225                 return LDAP_SCOPE_ONELEVEL;
226
227         } else if ( strcasecmp( p, "onetree" ) == 0 ) {
228                 return LDAP_SCOPE_ONELEVEL;
229
230         } else if ( strcasecmp( p, "base" ) == 0 ) {
231                 return LDAP_SCOPE_BASE;
232
233         } else if ( strcasecmp( p, "sub" ) == 0 ) {
234                 return LDAP_SCOPE_SUBTREE;
235
236         } else if ( strcasecmp( p, "subtree" ) == 0 ) {
237                 return LDAP_SCOPE_SUBTREE;
238         }
239
240         return( -1 );
241 }
242
243 static int hex_escape( char *buf, const char *s, int list )
244 {
245         int i;
246         int pos;
247         static const char hex[] = "0123456789ABCDEF";
248
249         if( s == NULL ) return 0;
250
251         for( pos=0,i=0; s[i]; i++ ) {
252                 int escape = 0;
253                 switch( s[i] ) {
254                         case ',':
255                                 escape = list;
256                                 break;
257                         case '%':
258                         case '?':
259                         case ' ':
260                         case '<':
261                         case '>':
262                         case '"':
263                         case '#':
264                         case '{':
265                         case '}':
266                         case '|':
267                         case '\\':
268                         case '^':
269                         case '~':
270                         case '`':
271                         case '[':
272                         case ']':
273                                 escape = 1;
274                                 break;
275
276                         default:
277                                 escape = s[i] < 0x20 || 0x1f >= s[i];
278                 }
279
280                 if( escape ) {
281                         buf[pos++] = '%';
282                         buf[pos++] = hex[ (s[i] >> 4) & 0x0f ];
283                         buf[pos++] = hex[ s[i] & 0x0f ];
284                 } else {
285                         buf[pos++] = s[i];
286                 }
287         }
288
289         buf[pos] = '\0';
290         return pos;
291 }
292
293 static int hex_escape_args( char *buf, char **s )
294 {
295         int pos;
296         int i;
297
298         if( s == NULL ) return 0;
299
300         pos = 0;
301         for( i=0; s[i] != NULL; i++ ) {
302                 if( pos ) {
303                         buf[pos++] = ',';
304                 }
305                 pos += hex_escape( &buf[pos], s[i], 1 );
306         }
307
308         return pos;
309 }
310
311 char * ldap_url_desc2str( LDAPURLDesc *u )
312 {
313         char *s;
314         int i;
315         int sep = 0;
316         int sofar;
317         size_t len = 0;
318         if( u == NULL ) return NULL;
319
320         if( u->lud_exts ) {
321                 for( i=0; u->lud_exts[i]; i++ ) {
322                         len += strlen( u->lud_exts[i] ) + 1;
323                 }
324                 if( !sep ) sep = 5;
325         }
326
327         if( u->lud_filter ) {
328                 len += strlen( u->lud_filter );
329                 if( !sep ) sep = 4;
330         }
331         if ( len ) len++; /* ? */
332
333         switch( u->lud_scope ) {
334                 case LDAP_SCOPE_ONELEVEL:
335                 case LDAP_SCOPE_SUBTREE:
336                 case LDAP_SCOPE_BASE:
337                         len += sizeof("base");
338                         if( !sep ) sep = 3;
339                         break;
340
341                 default:
342                         if ( len ) len++; /* ? */
343         }
344
345         if( u->lud_attrs ) {
346                 for( i=0; u->lud_attrs[i]; i++ ) {
347                         len += strlen( u->lud_attrs[i] ) + 1;
348                 }
349                 if( !sep ) sep = 2;
350         } else if ( len ) len++; /* ? */
351
352         if( u->lud_dn ) {
353                 len += strlen( u->lud_dn ) + 1;
354                 if( !sep ) sep = 1;
355         };
356
357         if( u->lud_port ) {
358                 len+=6;
359         }
360
361         if( u->lud_host ) {
362                 len+=strlen( u->lud_host );
363         }
364
365         len += strlen( u->lud_scheme ) + sizeof("://");
366
367         /* allocate enough to hex escape everything -- overkill */
368         s = LDAP_MALLOC( 3*len );
369
370         if( s == NULL ) return NULL;
371
372         if( u->lud_port ) {
373                 sprintf( s,     "%s://%s:%d%n", u->lud_scheme,
374                         u->lud_host, u->lud_port, &sofar );
375         } else {
376                 sprintf( s,     "%s://%s%n", u->lud_scheme,
377                         u->lud_host, &sofar );
378         }
379         
380         if( sep < 1 ) goto done;
381         s[sofar++] = '/';
382
383         sofar += hex_escape( &s[sofar], u->lud_dn, 0 );
384
385         if( sep < 2 ) goto done;
386         s[sofar++] = '?';
387
388         sofar += hex_escape_args( &s[sofar], u->lud_attrs );
389
390         if( sep < 3 ) goto done;
391         s[sofar++] = '?';
392
393         switch( u->lud_scope ) {
394         case LDAP_SCOPE_BASE:
395                 strcpy( &s[sofar], "base" );
396                 sofar += sizeof("base") - 1;
397                 break;
398         case LDAP_SCOPE_ONELEVEL:
399                 strcpy( &s[sofar], "one" );
400                 sofar += sizeof("one") - 1;
401                 break;
402         case LDAP_SCOPE_SUBTREE:
403                 strcpy( &s[sofar], "sub" );
404                 sofar += sizeof("sub") - 1;
405                 break;
406         }
407
408         if( sep < 4 ) goto done;
409         s[sofar++] = '?';
410
411         sofar += hex_escape( &s[sofar], u->lud_filter, 0 );
412
413         if( sep < 5 ) goto done;
414         s[sofar++] = '?';
415
416         sofar += hex_escape_args( &s[sofar], u->lud_exts );
417
418 done:
419         s[sofar] = '\0';
420         return s;
421 }
422
423 int
424 ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
425 {
426 /*
427  *  Pick apart the pieces of an LDAP URL.
428  */
429
430         LDAPURLDesc     *ludp;
431         char    *p, *q, *r;
432         int             i, enclosed;
433         const char *scheme = NULL;
434         const char *url_tmp;
435         char *url;
436
437         if( url_in == NULL || ludpp == NULL ) {
438                 return LDAP_URL_ERR_PARAM;
439         }
440
441 #ifndef LDAP_INT_IN_KERNEL
442         /* Global options may not be created yet
443          * We can't test if the global options are initialized
444          * because a call to LDAP_INT_GLOBAL_OPT() will try to allocate
445          * the options and cause infinite recursion
446          */
447         Debug( LDAP_DEBUG_TRACE, "ldap_url_parse_ext(%s)\n", url_in, 0, 0 );
448 #endif
449
450         *ludpp = NULL;  /* pessimistic */
451
452         url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
453
454         if ( url_tmp == NULL ) {
455                 return LDAP_URL_ERR_BADSCHEME;
456         }
457
458         assert( scheme );
459
460         /* make working copy of the remainder of the URL */
461         url = LDAP_STRDUP( url_tmp );
462         if ( url == NULL ) {
463                 return LDAP_URL_ERR_MEM;
464         }
465
466         if ( enclosed ) {
467                 p = &url[strlen(url)-1];
468
469                 if( *p != '>' ) {
470                         LDAP_FREE( url );
471                         return LDAP_URL_ERR_BADENCLOSURE;
472                 }
473
474                 *p = '\0';
475         }
476
477         /* allocate return struct */
478         ludp = (LDAPURLDesc *)LDAP_CALLOC( 1, sizeof( LDAPURLDesc ));
479
480         if ( ludp == NULL ) {
481                 LDAP_FREE( url );
482                 return LDAP_URL_ERR_MEM;
483         }
484
485         ludp->lud_next = NULL;
486         ludp->lud_host = NULL;
487         ludp->lud_port = 0;
488         ludp->lud_dn = NULL;
489         ludp->lud_attrs = NULL;
490         ludp->lud_filter = NULL;
491         ludp->lud_scope = LDAP_SCOPE_DEFAULT;
492         ludp->lud_filter = NULL;
493         ludp->lud_exts = NULL;
494
495         ludp->lud_scheme = LDAP_STRDUP( scheme );
496
497         if ( ludp->lud_scheme == NULL ) {
498                 LDAP_FREE( url );
499                 ldap_free_urldesc( ludp );
500                 return LDAP_URL_ERR_MEM;
501         }
502
503         /* scan forward for '/' that marks end of hostport and begin. of dn */
504         p = strchr( url, '/' );
505
506         if( p != NULL ) {
507                 /* terminate hostport; point to start of dn */
508                 *p++ = '\0';
509         }
510
511         /* IPv6 syntax with [ip address]:port */
512         if ( *url == '[' ) {
513                 r = strchr( url, ']' );
514                 if ( r == NULL ) {
515                         LDAP_FREE( url );
516                         ldap_free_urldesc( ludp );
517                         return LDAP_URL_ERR_BADURL;
518                 }
519                 *r++ = '\0';
520                 q = strchr( r, ':' );
521         } else {
522                 q = strchr( url, ':' );
523         }
524
525         if ( q != NULL ) {
526                 *q++ = '\0';
527                 ldap_pvt_hex_unescape( q );
528
529                 if( *q == '\0' ) {
530                         LDAP_FREE( url );
531                         ldap_free_urldesc( ludp );
532                         return LDAP_URL_ERR_BADURL;
533                 }
534
535                 ludp->lud_port = atoi( q );
536         }
537
538         ldap_pvt_hex_unescape( url );
539
540         /* If [ip address]:port syntax, url is [ip and we skip the [ */
541         ludp->lud_host = LDAP_STRDUP( url + ( *url == '[' ) );
542
543         if( ludp->lud_host == NULL ) {
544                 LDAP_FREE( url );
545                 ldap_free_urldesc( ludp );
546                 return LDAP_URL_ERR_MEM;
547         }
548
549         /*
550          * Kludge.  ldap://111.222.333.444:389??cn=abc,o=company
551          *
552          * On early Novell releases, search references/referrals were returned
553          * in this format, i.e., the dn was kind of in the scope position,
554          * but the required slash is missing. The whole thing is illegal syntax,
555          * but we need to account for it. Fortunately it can't be confused with
556          * anything real.
557          */
558         if( (p == NULL) && (q != NULL) && ((q = strchr( q, '?')) != NULL)) {
559                 q++;            
560                 /* ? immediately followed by question */
561                 if( *q == '?') {
562                         q++;
563                         if( *q != '\0' ) {
564                                 /* parse dn part */
565                                 ldap_pvt_hex_unescape( q );
566                                 ludp->lud_dn = LDAP_STRDUP( q );
567                         } else {
568                                 ludp->lud_dn = LDAP_STRDUP( "" );
569                         }
570
571                         if( ludp->lud_dn == NULL ) {
572                                 LDAP_FREE( url );
573                                 ldap_free_urldesc( ludp );
574                                 return LDAP_URL_ERR_MEM;
575                         }
576                 }
577         }
578
579         if( p == NULL ) {
580                 LDAP_FREE( url );
581                 *ludpp = ludp;
582                 return LDAP_URL_SUCCESS;
583         }
584
585         /* scan forward for '?' that may marks end of dn */
586         q = strchr( p, '?' );
587
588         if( q != NULL ) {
589                 /* terminate dn part */
590                 *q++ = '\0';
591         }
592
593         if( *p != '\0' ) {
594                 /* parse dn part */
595                 ldap_pvt_hex_unescape( p );
596                 ludp->lud_dn = LDAP_STRDUP( p );
597         } else {
598                 ludp->lud_dn = LDAP_STRDUP( "" );
599         }
600
601         if( ludp->lud_dn == NULL ) {
602                 LDAP_FREE( url );
603                 ldap_free_urldesc( ludp );
604                 return LDAP_URL_ERR_MEM;
605         }
606
607         if( q == NULL ) {
608                 /* no more */
609                 LDAP_FREE( url );
610                 *ludpp = ludp;
611                 return LDAP_URL_SUCCESS;
612         }
613
614         /* scan forward for '?' that may marks end of attributes */
615         p = q;
616         q = strchr( p, '?' );
617
618         if( q != NULL ) {
619                 /* terminate attributes part */
620                 *q++ = '\0';
621         }
622
623         if( *p != '\0' ) {
624                 /* parse attributes */
625                 ldap_pvt_hex_unescape( p );
626                 ludp->lud_attrs = ldap_str2charray( p, "," );
627
628                 if( ludp->lud_attrs == NULL ) {
629                         LDAP_FREE( url );
630                         ldap_free_urldesc( ludp );
631                         return LDAP_URL_ERR_BADATTRS;
632                 }
633         }
634
635         if ( q == NULL ) {
636                 /* no more */
637                 LDAP_FREE( url );
638                 *ludpp = ludp;
639                 return LDAP_URL_SUCCESS;
640         }
641
642         /* scan forward for '?' that may marks end of scope */
643         p = q;
644         q = strchr( p, '?' );
645
646         if( q != NULL ) {
647                 /* terminate the scope part */
648                 *q++ = '\0';
649         }
650
651         if( *p != '\0' ) {
652                 /* parse the scope */
653                 ldap_pvt_hex_unescape( p );
654                 ludp->lud_scope = str2scope( p );
655
656                 if( ludp->lud_scope == -1 ) {
657                         LDAP_FREE( url );
658                         ldap_free_urldesc( ludp );
659                         return LDAP_URL_ERR_BADSCOPE;
660                 }
661         }
662
663         if ( q == NULL ) {
664                 /* no more */
665                 LDAP_FREE( url );
666                 *ludpp = ludp;
667                 return LDAP_URL_SUCCESS;
668         }
669
670         /* scan forward for '?' that may marks end of filter */
671         p = q;
672         q = strchr( p, '?' );
673
674         if( q != NULL ) {
675                 /* terminate the filter part */
676                 *q++ = '\0';
677         }
678
679         if( *p != '\0' ) {
680                 /* parse the filter */
681                 ldap_pvt_hex_unescape( p );
682
683                 if( ! *p ) {
684                         /* missing filter */
685                         LDAP_FREE( url );
686                         ldap_free_urldesc( ludp );
687                         return LDAP_URL_ERR_BADFILTER;
688                 }
689
690                 LDAP_FREE( ludp->lud_filter );
691                 ludp->lud_filter = LDAP_STRDUP( p );
692
693                 if( ludp->lud_filter == NULL ) {
694                         LDAP_FREE( url );
695                         ldap_free_urldesc( ludp );
696                         return LDAP_URL_ERR_MEM;
697                 }
698         }
699
700         if ( q == NULL ) {
701                 /* no more */
702                 LDAP_FREE( url );
703                 *ludpp = ludp;
704                 return LDAP_URL_SUCCESS;
705         }
706
707         /* scan forward for '?' that may marks end of extensions */
708         p = q;
709         q = strchr( p, '?' );
710
711         if( q != NULL ) {
712                 /* extra '?' */
713                 LDAP_FREE( url );
714                 ldap_free_urldesc( ludp );
715                 return LDAP_URL_ERR_BADURL;
716         }
717
718         /* parse the extensions */
719         ludp->lud_exts = ldap_str2charray( p, "," );
720
721         if( ludp->lud_exts == NULL ) {
722                 LDAP_FREE( url );
723                 ldap_free_urldesc( ludp );
724                 return LDAP_URL_ERR_BADEXTS;
725         }
726
727         for( i=0; ludp->lud_exts[i] != NULL; i++ ) {
728                 ldap_pvt_hex_unescape( ludp->lud_exts[i] );
729
730                 if( *ludp->lud_exts[i] == '!' ) {
731                         /* count the number of critical extensions */
732                         ludp->lud_crit_exts++;
733                 }
734         }
735
736         if( i == 0 ) {
737                 /* must have 1 or more */
738                 LDAP_FREE( url );
739                 ldap_free_urldesc( ludp );
740                 return LDAP_URL_ERR_BADEXTS;
741         }
742
743         /* no more */
744         *ludpp = ludp;
745         LDAP_FREE( url );
746         return LDAP_URL_SUCCESS;
747 }
748
749 int
750 ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
751 {
752         int rc = ldap_url_parse_ext( url_in, ludpp );
753
754         if( rc != LDAP_URL_SUCCESS ) {
755                 return rc;
756         }
757
758         if ((*ludpp)->lud_scope == LDAP_SCOPE_DEFAULT) {
759                 (*ludpp)->lud_scope = LDAP_SCOPE_BASE;
760         }
761
762         if ((*ludpp)->lud_host != NULL && *(*ludpp)->lud_host == '\0') {
763                 LDAP_FREE( (*ludpp)->lud_host );
764                 (*ludpp)->lud_host = NULL;
765         }
766
767         if ((*ludpp)->lud_port == 0) {
768                 if( strcmp((*ludpp)->lud_scheme, "ldap") == 0 ) {
769                         (*ludpp)->lud_port = LDAP_PORT;
770 #ifdef LDAP_CONNECTIONLESS
771                 } else if( strcmp((*ludpp)->lud_scheme, "cldap") == 0 ) {
772                         (*ludpp)->lud_port = LDAP_PORT;
773 #endif
774                 } else if( strcmp((*ludpp)->lud_scheme, "ldaps") == 0 ) {
775                         (*ludpp)->lud_port = LDAPS_PORT;
776                 }
777         }
778
779         return rc;
780 }
781
782 LDAPURLDesc *
783 ldap_url_dup ( LDAPURLDesc *ludp )
784 {
785         LDAPURLDesc *dest;
786
787         if ( ludp == NULL ) {
788                 return NULL;
789         }
790
791         dest = LDAP_MALLOC( sizeof(LDAPURLDesc) );
792         if (dest == NULL)
793                 return NULL;
794         
795         *dest = *ludp;
796         dest->lud_scheme = NULL;
797         dest->lud_host = NULL;
798         dest->lud_dn = NULL;
799         dest->lud_filter = NULL;
800         dest->lud_attrs = NULL;
801         dest->lud_exts = NULL;
802         dest->lud_next = NULL;
803
804         if ( ludp->lud_scheme != NULL ) {
805                 dest->lud_scheme = LDAP_STRDUP( ludp->lud_scheme );
806                 if (dest->lud_scheme == NULL) {
807                         ldap_free_urldesc(dest);
808                         return NULL;
809                 }
810         }
811
812         if ( ludp->lud_host != NULL ) {
813                 dest->lud_host = LDAP_STRDUP( ludp->lud_host );
814                 if (dest->lud_host == NULL) {
815                         ldap_free_urldesc(dest);
816                         return NULL;
817                 }
818         }
819
820         if ( ludp->lud_dn != NULL ) {
821                 dest->lud_dn = LDAP_STRDUP( ludp->lud_dn );
822                 if (dest->lud_dn == NULL) {
823                         ldap_free_urldesc(dest);
824                         return NULL;
825                 }
826         }
827
828         if ( ludp->lud_filter != NULL ) {
829                 dest->lud_filter = LDAP_STRDUP( ludp->lud_filter );
830                 if (dest->lud_filter == NULL) {
831                         ldap_free_urldesc(dest);
832                         return NULL;
833                 }
834         }
835
836         if ( ludp->lud_attrs != NULL ) {
837                 dest->lud_attrs = ldap_charray_dup( ludp->lud_attrs );
838                 if (dest->lud_attrs == NULL) {
839                         ldap_free_urldesc(dest);
840                         return NULL;
841                 }
842         }
843
844         if ( ludp->lud_exts != NULL ) {
845                 dest->lud_exts = ldap_charray_dup( ludp->lud_exts );
846                 if (dest->lud_exts == NULL) {
847                         ldap_free_urldesc(dest);
848                         return NULL;
849                 }
850         }
851
852         return dest;
853 }
854
855 LDAPURLDesc *
856 ldap_url_duplist (LDAPURLDesc *ludlist)
857 {
858         LDAPURLDesc *dest, *tail, *ludp, *newludp;
859
860         dest = NULL;
861         tail = NULL;
862         for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
863                 newludp = ldap_url_dup(ludp);
864                 if (newludp == NULL) {
865                         ldap_free_urllist(dest);
866                         return NULL;
867                 }
868                 if (tail == NULL)
869                         dest = newludp;
870                 else
871                         tail->lud_next = newludp;
872                 tail = newludp;
873         }
874         return dest;
875 }
876
877 int
878 ldap_url_parselist (LDAPURLDesc **ludlist, const char *url )
879 {
880         int i, rc;
881         LDAPURLDesc *ludp;
882         char **urls;
883
884         *ludlist = NULL;
885
886         if (url == NULL)
887                 return LDAP_PARAM_ERROR;
888
889         urls = ldap_str2charray((char *)url, ", ");
890         if (urls == NULL)
891                 return LDAP_NO_MEMORY;
892
893         /* count the URLs... */
894         for (i = 0; urls[i] != NULL; i++) ;
895         /* ...and put them in the "stack" backward */
896         while (--i >= 0) {
897                 rc = ldap_url_parse( urls[i], &ludp );
898                 if ( rc != 0 ) {
899                         ldap_charray_free(urls);
900                         ldap_free_urllist(*ludlist);
901                         *ludlist = NULL;
902                         return rc;
903                 }
904                 ludp->lud_next = *ludlist;
905                 *ludlist = ludp;
906         }
907         ldap_charray_free(urls);
908         return LDAP_SUCCESS;
909 }
910
911 int
912 ldap_url_parsehosts(
913         LDAPURLDesc **ludlist,
914         const char *hosts,
915         int port )
916 {
917         int i;
918         LDAPURLDesc *ludp;
919         char **specs, *p;
920
921         *ludlist = NULL;
922
923         if (hosts == NULL)
924                 return LDAP_PARAM_ERROR;
925
926         specs = ldap_str2charray((char *)hosts, ", ");
927         if (specs == NULL)
928                 return LDAP_NO_MEMORY;
929
930         /* count the URLs... */
931         for (i = 0; specs[i] != NULL; i++) /* EMPTY */;
932
933         /* ...and put them in the "stack" backward */
934         while (--i >= 0) {
935                 ludp = LDAP_CALLOC( 1, sizeof(LDAPURLDesc) );
936                 if (ludp == NULL) {
937                         ldap_charray_free(specs);
938                         ldap_free_urllist(*ludlist);
939                         *ludlist = NULL;
940                         return LDAP_NO_MEMORY;
941                 }
942                 ludp->lud_port = port;
943                 ludp->lud_host = specs[i];
944                 specs[i] = NULL;
945                 p = strchr(ludp->lud_host, ':');
946                 if (p != NULL) {
947                         /* more than one :, IPv6 address */
948                         if ( strchr(p+1, ':') != NULL ) {
949                                 /* allow [address] and [address]:port */
950                                 if ( *ludp->lud_host == '[' ) {
951                                         p = LDAP_STRDUP(ludp->lud_host+1);
952                                         /* copied, make sure we free source later */
953                                         specs[i] = ludp->lud_host;
954                                         ludp->lud_host = p;
955                                         p = strchr( ludp->lud_host, ']' );
956                                         if ( p == NULL )
957                                                 return LDAP_PARAM_ERROR;
958                                         *p++ = '\0';
959                                         if ( *p != ':' ) {
960                                                 if ( *p != '\0' )
961                                                         return LDAP_PARAM_ERROR;
962                                                 p = NULL;
963                                         }
964                                 } else {
965                                         p = NULL;
966                                 }
967                         }
968                         if (p != NULL) {
969                                 *p++ = 0;
970                                 ldap_pvt_hex_unescape(p);
971                                 ludp->lud_port = atoi(p);
972                         }
973                 }
974                 ldap_pvt_hex_unescape(ludp->lud_host);
975                 ludp->lud_scheme = LDAP_STRDUP("ldap");
976                 ludp->lud_next = *ludlist;
977                 *ludlist = ludp;
978         }
979
980         /* this should be an array of NULLs now */
981         /* except entries starting with [ */
982         ldap_charray_free(specs);
983         return LDAP_SUCCESS;
984 }
985
986 char *
987 ldap_url_list2hosts (LDAPURLDesc *ludlist)
988 {
989         LDAPURLDesc *ludp;
990         int size;
991         char *s, *p, buf[32];   /* big enough to hold a long decimal # (overkill) */
992
993         if (ludlist == NULL)
994                 return NULL;
995
996         /* figure out how big the string is */
997         size = 1;       /* nul-term */
998         for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
999                 size += strlen(ludp->lud_host) + 1;             /* host and space */
1000                 if (strchr(ludp->lud_host, ':'))        /* will add [ ] below */
1001                         size += 2;
1002                 if (ludp->lud_port != 0)
1003                         size += sprintf(buf, ":%d", ludp->lud_port);
1004         }
1005         s = LDAP_MALLOC(size);
1006         if (s == NULL)
1007                 return NULL;
1008
1009         p = s;
1010         for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
1011                 if (strchr(ludp->lud_host, ':')) {
1012                         p += sprintf(p, "[%s]", ludp->lud_host);
1013                 } else {
1014                         strcpy(p, ludp->lud_host);
1015                         p += strlen(ludp->lud_host);
1016                 }
1017                 if (ludp->lud_port != 0)
1018                         p += sprintf(p, ":%d", ludp->lud_port);
1019                 *p++ = ' ';
1020         }
1021         if (p != s)
1022                 p--;    /* nuke that extra space */
1023         *p = 0;
1024         return s;
1025 }
1026
1027 char *
1028 ldap_url_list2urls(
1029         LDAPURLDesc *ludlist )
1030 {
1031         LDAPURLDesc *ludp;
1032         int size;
1033         char *s, *p, buf[32];   /* big enough to hold a long decimal # (overkill) */
1034
1035         if (ludlist == NULL)
1036                 return NULL;
1037
1038         /* figure out how big the string is */
1039         size = 1;       /* nul-term */
1040         for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
1041                 size += strlen(ludp->lud_scheme) + strlen(ludp->lud_host);
1042                 if (strchr(ludp->lud_host, ':'))        /* will add [ ] below */
1043                         size += 2;
1044                 size += sizeof(":/// ");
1045
1046                 if (ludp->lud_port != 0) {
1047                         size += sprintf(buf, ":%d", ludp->lud_port);
1048                 }
1049         }
1050
1051         s = LDAP_MALLOC(size);
1052         if (s == NULL) {
1053                 return NULL;
1054         }
1055
1056         p = s;
1057         for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
1058                 p += sprintf(p,
1059                              strchr(ludp->lud_host, ':') ? "%s://[%s]" : "%s://%s",
1060                              ludp->lud_scheme, ludp->lud_host);
1061                 if (ludp->lud_port != 0)
1062                         p += sprintf(p, ":%d", ludp->lud_port);
1063                 *p++ = '/';
1064                 *p++ = ' ';
1065         }
1066         if (p != s)
1067                 p--;    /* nuke that extra space */
1068         *p = 0;
1069         return s;
1070 }
1071
1072 void
1073 ldap_free_urllist( LDAPURLDesc *ludlist )
1074 {
1075         LDAPURLDesc *ludp, *next;
1076
1077         for (ludp = ludlist; ludp != NULL; ludp = next) {
1078                 next = ludp->lud_next;
1079                 ldap_free_urldesc(ludp);
1080         }
1081 }
1082
1083 void
1084 ldap_free_urldesc( LDAPURLDesc *ludp )
1085 {
1086         if ( ludp == NULL ) {
1087                 return;
1088         }
1089         
1090         if ( ludp->lud_scheme != NULL ) {
1091                 LDAP_FREE( ludp->lud_scheme );
1092         }
1093
1094         if ( ludp->lud_host != NULL ) {
1095                 LDAP_FREE( ludp->lud_host );
1096         }
1097
1098         if ( ludp->lud_dn != NULL ) {
1099                 LDAP_FREE( ludp->lud_dn );
1100         }
1101
1102         if ( ludp->lud_filter != NULL ) {
1103                 LDAP_FREE( ludp->lud_filter);
1104         }
1105
1106         if ( ludp->lud_attrs != NULL ) {
1107                 LDAP_VFREE( ludp->lud_attrs );
1108         }
1109
1110         if ( ludp->lud_exts != NULL ) {
1111                 LDAP_VFREE( ludp->lud_exts );
1112         }
1113
1114         LDAP_FREE( ludp );
1115 }
1116
1117
1118
1119 int
1120 ldap_url_search( LDAP *ld, LDAP_CONST char *url, int attrsonly )
1121 {
1122         int             err;
1123         LDAPURLDesc     *ludp;
1124         BerElement      *ber;
1125         LDAPreqinfo  bind;
1126
1127         assert( ld != NULL );
1128         assert( LDAP_VALID( ld ) );
1129
1130         if ( ldap_url_parse( url, &ludp ) != 0 ) {
1131                 ld->ld_errno = LDAP_PARAM_ERROR;
1132                 return( -1 );
1133         }
1134
1135         if( ludp->lud_crit_exts ) {
1136                 /* we don't support any extension (yet) */
1137                 ld->ld_errno = LDAP_NOT_SUPPORTED;
1138                 return( -1 );
1139         }
1140
1141         ber = ldap_build_search_req( ld, ludp->lud_dn, ludp->lud_scope,
1142             ludp->lud_filter, ludp->lud_attrs, attrsonly, NULL, NULL,
1143                 -1, -1 );
1144
1145         if ( ber == NULL ) {
1146                 err = -1;
1147         } else {
1148                 bind.ri_request = LDAP_REQ_SEARCH;
1149                 bind.ri_msgid = ld->ld_msgid;
1150                 bind.ri_url = (char *)url;
1151                 err = ldap_send_server_request(
1152                                         ld, ber, ld->ld_msgid, NULL,
1153                                         NULL, NULL, &bind );
1154         }
1155
1156         ldap_free_urldesc( ludp );
1157         return( err );
1158 }
1159
1160
1161 int
1162 ldap_url_search_st( LDAP *ld, LDAP_CONST char *url, int attrsonly,
1163         struct timeval *timeout, LDAPMessage **res )
1164 {
1165         int     msgid;
1166
1167         if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
1168                 return( ld->ld_errno );
1169         }
1170
1171         if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 ) {
1172                 return( ld->ld_errno );
1173         }
1174
1175         if ( ld->ld_errno == LDAP_TIMEOUT ) {
1176                 (void) ldap_abandon( ld, msgid );
1177                 ld->ld_errno = LDAP_TIMEOUT;
1178                 return( ld->ld_errno );
1179         }
1180
1181         return( ldap_result2error( ld, *res, 0 ));
1182 }
1183
1184
1185 int
1186 ldap_url_search_s(
1187         LDAP *ld, LDAP_CONST char *url, int attrsonly, LDAPMessage **res )
1188 {
1189         int     msgid;
1190
1191         if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
1192                 return( ld->ld_errno );
1193         }
1194
1195         if ( ldap_result( ld, msgid, 1, (struct timeval *)NULL, res ) == -1 ) {
1196                 return( ld->ld_errno );
1197         }
1198
1199         return( ldap_result2error( ld, *res, 0 ));
1200 }
1201
1202
1203 void
1204 ldap_pvt_hex_unescape( char *s )
1205 {
1206         /*
1207          * Remove URL hex escapes from s... done in place.  The basic concept for
1208          * this routine is borrowed from the WWW library HTUnEscape() routine.
1209          */
1210         char    *p;
1211
1212         for ( p = s; *s != '\0'; ++s ) {
1213                 if ( *s == '%' ) {
1214                         if ( *++s != '\0' ) {
1215                                 *p = ldap_pvt_unhex( *s ) << 4;
1216                         }
1217                         if ( *++s != '\0' ) {
1218                                 *p++ += ldap_pvt_unhex( *s );
1219                         }
1220                 } else {
1221                         *p++ = *s;
1222                 }
1223         }
1224
1225         *p = '\0';
1226 }
1227
1228
1229 int
1230 ldap_pvt_unhex( int c )
1231 {
1232         return( c >= '0' && c <= '9' ? c - '0'
1233             : c >= 'A' && c <= 'F' ? c - 'A' + 10
1234             : c - 'a' + 10 );
1235 }