1 /*        $NetBSD: url.c,v 1.3 2021/08/14 16:14:56 christos Exp $     */
2 
3 /* LIBLDAP url.c -- LDAP URL (RFC 4516) related routines */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 1998-2021 The OpenLDAP Foundation.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* Portions Copyright (c) 1996 Regents of the University of Michigan.
19  * All rights reserved.
20  */
21 
22 
23 /*
24  *  LDAP URLs look like this:
25  *    [p]ldap[is]://host[:port][/[dn[?[attributes][?[scope][?[filter][?exts]]]]]]
26  *
27  *  where:
28  *   attributes is a comma separated list
29  *   scope is one of these three strings:  base one sub (default=base)
30  *   filter is an string-represented filter as in RFC 4515
31  *
32  *  e.g.,  ldap://host:port/dc=com?o,cn?base?(o=openldap)?extension
33  *
34  *  We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl>
35  */
36 
37 #include <sys/cdefs.h>
38 __RCSID("$NetBSD: url.c,v 1.3 2021/08/14 16:14:56 christos Exp $");
39 
40 #include "portable.h"
41 
42 #include <stdio.h>
43 
44 #include <ac/stdlib.h>
45 #include <ac/ctype.h>
46 
47 #include <ac/socket.h>
48 #include <ac/string.h>
49 #include <ac/time.h>
50 
51 #include "ldap-int.h"
52 
53 /* local functions */
54 static const char* skip_url_prefix LDAP_P((
55           const char *url,
56           int *enclosedp,
57           const char **scheme ));
58 
ldap_pvt_url_scheme2proto(const char * scheme)59 int ldap_pvt_url_scheme2proto( const char *scheme )
60 {
61           assert( scheme != NULL );
62 
63           if( scheme == NULL ) {
64                     return -1;
65           }
66 
67           if( strcmp("ldap", scheme) == 0 || strcmp("pldap", scheme) == 0 ) {
68                     return LDAP_PROTO_TCP;
69           }
70 
71           if( strcmp("ldapi", scheme) == 0 ) {
72                     return LDAP_PROTO_IPC;
73           }
74 
75           if( strcmp("ldaps", scheme) == 0 || strcmp("pldaps", scheme) == 0 ) {
76                     return LDAP_PROTO_TCP;
77           }
78 #ifdef LDAP_CONNECTIONLESS
79           if( strcmp("cldap", scheme) == 0 ) {
80                     return LDAP_PROTO_UDP;
81           }
82 #endif
83 
84           return -1;
85 }
86 
ldap_pvt_url_scheme_port(const char * scheme,int port)87 int ldap_pvt_url_scheme_port( const char *scheme, int port )
88 {
89           assert( scheme != NULL );
90 
91           if( port ) return port;
92           if( scheme == NULL ) return port;
93 
94           if( strcmp("ldap", scheme) == 0 || strcmp("pldap", scheme) == 0 ) {
95                     return LDAP_PORT;
96           }
97 
98           if( strcmp("ldapi", scheme) == 0 ) {
99                     return -1;
100           }
101 
102           if( strcmp("ldaps", scheme) == 0 || strcmp("pldaps", scheme) == 0 ) {
103                     return LDAPS_PORT;
104           }
105 
106 #ifdef LDAP_CONNECTIONLESS
107           if( strcmp("cldap", scheme) == 0 ) {
108                     return LDAP_PORT;
109           }
110 #endif
111 
112           return -1;
113 }
114 
115 int
ldap_pvt_url_scheme2tls(const char * scheme)116 ldap_pvt_url_scheme2tls( const char *scheme )
117 {
118           assert( scheme != NULL );
119 
120           if( scheme == NULL ) {
121                     return -1;
122           }
123 
124           return strcmp("ldaps", scheme) == 0 || strcmp("pldaps", scheme) == 0;
125 }
126 
127 int
ldap_pvt_url_scheme2proxied(const char * scheme)128 ldap_pvt_url_scheme2proxied( const char *scheme )
129 {
130           assert( scheme != NULL );
131 
132           if( scheme == NULL ) {
133                     return -1;
134           }
135 
136           return strcmp("pldap", scheme) == 0 || strcmp("pldaps", scheme) == 0;
137 }
138 
139 int
ldap_is_ldap_url(LDAP_CONST char * url)140 ldap_is_ldap_url( LDAP_CONST char *url )
141 {
142           int       enclosed;
143           const char * scheme;
144 
145           if( url == NULL ) {
146                     return 0;
147           }
148 
149           if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
150                     return 0;
151           }
152 
153           return 1;
154 }
155 
156 int
ldap_is_ldaps_url(LDAP_CONST char * url)157 ldap_is_ldaps_url( LDAP_CONST char *url )
158 {
159           int       enclosed;
160           const char * scheme;
161 
162           if( url == NULL ) {
163                     return 0;
164           }
165 
166           if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
167                     return 0;
168           }
169 
170           return strcmp(scheme, "ldaps") == 0 || strcmp(scheme, "pldaps") == 0;
171 }
172 
173 int
ldap_is_ldapi_url(LDAP_CONST char * url)174 ldap_is_ldapi_url( LDAP_CONST char *url )
175 {
176           int       enclosed;
177           const char * scheme;
178 
179           if( url == NULL ) {
180                     return 0;
181           }
182 
183           if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
184                     return 0;
185           }
186 
187           return strcmp(scheme, "ldapi") == 0;
188 }
189 
190 #ifdef LDAP_CONNECTIONLESS
191 int
ldap_is_ldapc_url(LDAP_CONST char * url)192 ldap_is_ldapc_url( LDAP_CONST char *url )
193 {
194           int       enclosed;
195           const char * scheme;
196 
197           if( url == NULL ) {
198                     return 0;
199           }
200 
201           if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
202                     return 0;
203           }
204 
205           return strcmp(scheme, "cldap") == 0;
206 }
207 #endif
208 
209 static const char*
skip_url_prefix(const char * url,int * enclosedp,const char ** scheme)210 skip_url_prefix(
211           const char *url,
212           int *enclosedp,
213           const char **scheme )
214 {
215           /*
216            * return non-zero if this looks like a LDAP URL; zero if not
217            * if non-zero returned, *urlp will be moved past "ldap://" part of URL
218            */
219           const char *p;
220 
221           if ( url == NULL ) {
222                     return( NULL );
223           }
224 
225           p = url;
226 
227           /* skip leading '<' (if any) */
228           if ( *p == '<' ) {
229                     *enclosedp = 1;
230                     ++p;
231           } else {
232                     *enclosedp = 0;
233           }
234 
235           /* skip leading "URL:" (if any) */
236           if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) {
237                     p += LDAP_URL_URLCOLON_LEN;
238           }
239 
240           /* check for "ldap://" prefix */
241           if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
242                     /* skip over "ldap://" prefix and return success */
243                     p += LDAP_URL_PREFIX_LEN;
244                     *scheme = "ldap";
245                     return( p );
246           }
247 
248           /* check for "pldap://" prefix */
249           if ( strncasecmp( p, PLDAP_URL_PREFIX, PLDAP_URL_PREFIX_LEN ) == 0 ) {
250                     /* skip over "pldap://" prefix and return success */
251                     p += PLDAP_URL_PREFIX_LEN;
252                     *scheme = "pldap";
253                     return( p );
254           }
255 
256           /* check for "ldaps://" prefix */
257           if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
258                     /* skip over "ldaps://" prefix and return success */
259                     p += LDAPS_URL_PREFIX_LEN;
260                     *scheme = "ldaps";
261                     return( p );
262           }
263 
264           /* check for "pldaps://" prefix */
265           if ( strncasecmp( p, PLDAPS_URL_PREFIX, PLDAPS_URL_PREFIX_LEN ) == 0 ) {
266                     /* skip over "pldaps://" prefix and return success */
267                     p += PLDAPS_URL_PREFIX_LEN;
268                     *scheme = "pldaps";
269                     return( p );
270           }
271 
272           /* check for "ldapi://" prefix */
273           if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) {
274                     /* skip over "ldapi://" prefix and return success */
275                     p += LDAPI_URL_PREFIX_LEN;
276                     *scheme = "ldapi";
277                     return( p );
278           }
279 
280 #ifdef LDAP_CONNECTIONLESS
281           /* check for "cldap://" prefix */
282           if ( strncasecmp( p, LDAPC_URL_PREFIX, LDAPC_URL_PREFIX_LEN ) == 0 ) {
283                     /* skip over "cldap://" prefix and return success */
284                     p += LDAPC_URL_PREFIX_LEN;
285                     *scheme = "cldap";
286                     return( p );
287           }
288 #endif
289 
290           return( NULL );
291 }
292 
293 int
ldap_pvt_scope2bv(int scope,struct berval * bv)294 ldap_pvt_scope2bv( int scope, struct berval *bv )
295 {
296           switch ( scope ) {
297           case LDAP_SCOPE_BASE:
298                     BER_BVSTR( bv, "base" );
299                     break;
300 
301           case LDAP_SCOPE_ONELEVEL:
302                     BER_BVSTR( bv, "one" );
303                     break;
304 
305           case LDAP_SCOPE_SUBTREE:
306                     BER_BVSTR( bv, "sub" );
307                     break;
308 
309           case LDAP_SCOPE_SUBORDINATE:
310                     BER_BVSTR( bv, "subordinate" );
311                     break;
312 
313           default:
314                     return LDAP_OTHER;
315           }
316 
317           return LDAP_SUCCESS;
318 }
319 
320 const char *
ldap_pvt_scope2str(int scope)321 ldap_pvt_scope2str( int scope )
322 {
323           struct berval       bv;
324 
325           if ( ldap_pvt_scope2bv( scope, &bv ) == LDAP_SUCCESS ) {
326                     return bv.bv_val;
327           }
328 
329           return NULL;
330 }
331 
332 int
ldap_pvt_bv2scope(struct berval * bv)333 ldap_pvt_bv2scope( struct berval *bv )
334 {
335           static struct {
336                     struct berval       bv;
337                     int                 scope;
338           }         v[] = {
339                     { BER_BVC( "one" ),           LDAP_SCOPE_ONELEVEL },
340                     { BER_BVC( "onelevel" ),      LDAP_SCOPE_ONELEVEL },
341                     { BER_BVC( "base" ),                    LDAP_SCOPE_BASE },
342                     { BER_BVC( "sub" ),           LDAP_SCOPE_SUBTREE },
343                     { BER_BVC( "subtree" ),                 LDAP_SCOPE_SUBTREE },
344                     { BER_BVC( "subord" ),                  LDAP_SCOPE_SUBORDINATE },
345                     { BER_BVC( "subordinate" ),   LDAP_SCOPE_SUBORDINATE },
346                     { BER_BVC( "children" ),      LDAP_SCOPE_SUBORDINATE },
347                     { BER_BVNULL,                           -1 }
348           };
349           int       i;
350 
351           for ( i = 0; v[ i ].scope != -1; i++ ) {
352                     if ( ber_bvstrcasecmp( bv, &v[ i ].bv ) == 0 ) {
353                               return v[ i ].scope;
354                     }
355           }
356 
357           return( -1 );
358 }
359 
360 int
ldap_pvt_str2scope(const char * p)361 ldap_pvt_str2scope( const char *p )
362 {
363           struct berval       bv;
364 
365           ber_str2bv( p, 0, 0, &bv );
366 
367           return ldap_pvt_bv2scope( &bv );
368 }
369 
370 static const char   hex[] = "0123456789ABCDEF";
371 
372 #define URLESC_NONE 0x0000U
373 #define URLESC_COMMA          0x0001U
374 #define URLESC_SLASH          0x0002U
375 
376 static int
hex_escape_len(const char * s,unsigned list)377 hex_escape_len( const char *s, unsigned list )
378 {
379           int       len;
380 
381           if ( s == NULL ) {
382                     return 0;
383           }
384 
385           for ( len = 0; s[0]; s++ ) {
386                     switch ( s[0] ) {
387                     /* RFC 2396: reserved */
388                     case '?':
389                               len += 3;
390                               break;
391 
392                     case ',':
393                               if ( list & URLESC_COMMA ) {
394                                         len += 3;
395                               } else {
396                                         len++;
397                               }
398                               break;
399 
400                     case '/':
401                               if ( list & URLESC_SLASH ) {
402                                         len += 3;
403                               } else {
404                                         len++;
405                               }
406                               break;
407 
408                     case ';':
409                     case ':':
410                     case '@':
411                     case '&':
412                     case '=':
413                     case '+':
414                     case '$':
415 
416                     /* RFC 2396: unreserved mark */
417                     case '-':
418                     case '_':
419                     case '.':
420                     case '!':
421                     case '~':
422                     case '*':
423                     case '\'':
424                     case '(':
425                     case ')':
426                               len++;
427                               break;
428 
429                     /* RFC 2396: unreserved alphanum */
430                     default:
431                               if ( !isalnum( (unsigned char) s[0] ) ) {
432                                         len += 3;
433                               } else {
434                                         len++;
435                               }
436                               break;
437                     }
438           }
439 
440           return len;
441 }
442 
443 static int
hex_escape(char * buf,int len,const char * s,unsigned list)444 hex_escape( char *buf, int len, const char *s, unsigned list )
445 {
446           int       i;
447           int       pos;
448 
449           if ( s == NULL ) {
450                     return 0;
451           }
452 
453           for ( pos = 0, i = 0; s[i] && pos < len; i++ ) {
454                     int       escape = 0;
455 
456                     switch ( s[i] ) {
457                     /* RFC 2396: reserved */
458                     case '?':
459                               escape = 1;
460                               break;
461 
462                     case ',':
463                               if ( list & URLESC_COMMA ) {
464                                         escape = 1;
465                               }
466                               break;
467 
468                     case '/':
469                               if ( list & URLESC_SLASH ) {
470                                         escape = 1;
471                               }
472                               break;
473 
474                     case ';':
475                     case ':':
476                     case '@':
477                     case '&':
478                     case '=':
479                     case '+':
480                     case '$':
481 
482                     /* RFC 2396: unreserved mark */
483                     case '-':
484                     case '_':
485                     case '.':
486                     case '!':
487                     case '~':
488                     case '*':
489                     case '\'':
490                     case '(':
491                     case ')':
492                               break;
493 
494                     /* RFC 2396: unreserved alphanum */
495                     default:
496                               if ( !isalnum( (unsigned char) s[i] ) ) {
497                                         escape = 1;
498                               }
499                               break;
500                     }
501 
502                     if ( escape ) {
503                               buf[pos++] = '%';
504                               buf[pos++] = hex[ (s[i] >> 4) & 0x0f ];
505                               buf[pos++] = hex[ s[i] & 0x0f ];
506 
507                     } else {
508                               buf[pos++] = s[i];
509                     }
510           }
511 
512           buf[pos] = '\0';
513 
514           return pos;
515 }
516 
517 static int
hex_escape_len_list(char ** s,unsigned flags)518 hex_escape_len_list( char **s, unsigned flags )
519 {
520           int       len;
521           int       i;
522 
523           if ( s == NULL ) {
524                     return 0;
525           }
526 
527           len = 0;
528           for ( i = 0; s[i] != NULL; i++ ) {
529                     if ( len ) {
530                               len++;
531                     }
532                     len += hex_escape_len( s[i], flags );
533           }
534 
535           return len;
536 }
537 
538 static int
hex_escape_list(char * buf,int len,char ** s,unsigned flags)539 hex_escape_list( char *buf, int len, char **s, unsigned flags )
540 {
541           int       pos;
542           int       i;
543 
544           if ( s == NULL ) {
545                     return 0;
546           }
547 
548           pos = 0;
549           for ( i = 0; s[i] != NULL; i++ ) {
550                     int       curlen;
551 
552                     if ( pos ) {
553                               buf[pos++] = ',';
554                               len--;
555                     }
556                     curlen = hex_escape( &buf[pos], len, s[i], flags );
557                     len -= curlen;
558                     pos += curlen;
559           }
560 
561           return pos;
562 }
563 
564 static int
desc2str_len(LDAPURLDesc * u)565 desc2str_len( LDAPURLDesc *u )
566 {
567           int                 sep = 0;
568           int                 len = 0;
569           int                 is_ipc = 0;
570           struct berval       scope;
571 
572           if ( u == NULL || u->lud_scheme == NULL ) {
573                     return -1;
574           }
575 
576           if ( !strcmp( "ldapi", u->lud_scheme )) {
577                     is_ipc = 1;
578           }
579 
580           if ( u->lud_exts ) {
581                     len += hex_escape_len_list( u->lud_exts, URLESC_COMMA );
582                     if ( !sep ) {
583                               sep = 5;
584                     }
585           }
586 
587           if ( u->lud_filter ) {
588                     len += hex_escape_len( u->lud_filter, URLESC_NONE );
589                     if ( !sep ) {
590                               sep = 4;
591                     }
592           }
593 
594           if ( ldap_pvt_scope2bv( u->lud_scope, &scope ) == LDAP_SUCCESS ) {
595                     len += scope.bv_len;
596                     if ( !sep ) {
597                               sep = 3;
598                     }
599           }
600 
601           if ( u->lud_attrs ) {
602                     len += hex_escape_len_list( u->lud_attrs, URLESC_NONE );
603                     if ( !sep ) {
604                               sep = 2;
605                     }
606           }
607 
608           if ( u->lud_dn && u->lud_dn[0] ) {
609                     len += hex_escape_len( u->lud_dn, URLESC_NONE );
610                     if ( !sep ) {
611                               sep = 1;
612                     }
613           };
614 
615           len += sep;
616 
617           if ( u->lud_port ) {
618                     unsigned p = u->lud_port;
619                     if ( p > 65535 )
620                               return -1;
621 
622                     len += (p > 999 ? 5 + (p > 9999) : p > 99 ? 4 : 2 + (p > 9));
623           }
624 
625           if ( u->lud_host && u->lud_host[0] ) {
626                     char *ptr;
627                     len += hex_escape_len( u->lud_host, URLESC_SLASH );
628                     if ( !is_ipc && ( ptr = strchr( u->lud_host, ':' ))) {
629                               if ( strchr( ptr+1, ':' ))
630                                         len += 2; /* IPv6, [] */
631                     }
632           }
633 
634           len += strlen( u->lud_scheme ) + STRLENOF( "://" );
635 
636           return len;
637 }
638 
639 static int
desc2str(LDAPURLDesc * u,char * s,int len)640 desc2str( LDAPURLDesc *u, char *s, int len )
641 {
642           int                 i;
643           int                 sep = 0;
644           int                 sofar = 0;
645           int                 is_v6 = 0;
646           int                 is_ipc = 0;
647           struct berval       scope = BER_BVNULL;
648           char                *ptr;
649 
650           if ( u == NULL ) {
651                     return -1;
652           }
653 
654           if ( s == NULL ) {
655                     return -1;
656           }
657 
658           if ( u->lud_scheme && !strcmp( "ldapi", u->lud_scheme )) {
659                     is_ipc = 1;
660           }
661 
662           ldap_pvt_scope2bv( u->lud_scope, &scope );
663 
664           if ( u->lud_exts ) {
665                     sep = 5;
666           } else if ( u->lud_filter ) {
667                     sep = 4;
668           } else if ( !BER_BVISEMPTY( &scope ) ) {
669                     sep = 3;
670           } else if ( u->lud_attrs ) {
671                     sep = 2;
672           } else if ( u->lud_dn && u->lud_dn[0] ) {
673                     sep = 1;
674           }
675 
676           if ( !is_ipc && u->lud_host && ( ptr = strchr( u->lud_host, ':' ))) {
677                     if ( strchr( ptr+1, ':' ))
678                               is_v6 = 1;
679           }
680 
681           if ( u->lud_port ) {
682                     sofar = sprintf( s, "%s://%s%s%s:%d", u->lud_scheme,
683                                         is_v6 ? "[" : "",
684                                         u->lud_host ? u->lud_host : "",
685                                         is_v6 ? "]" : "",
686                                         u->lud_port );
687                     len -= sofar;
688 
689           } else {
690                     sofar = sprintf( s, "%s://", u->lud_scheme );
691                     len -= sofar;
692                     if ( u->lud_host && u->lud_host[0] ) {
693                               if ( is_v6 ) {
694                                         s[sofar++] = '[';
695                                         len--;
696                               }
697                               i = hex_escape( &s[sofar], len, u->lud_host, URLESC_SLASH );
698                               sofar += i;
699                               len -= i;
700                               if ( is_v6 ) {
701                                         s[sofar++] = ']';
702                                         len--;
703                               }
704                     }
705           }
706 
707           assert( len >= 0 );
708 
709           if ( sep < 1 ) {
710                     goto done;
711           }
712 
713           s[sofar++] = '/';
714           len--;
715 
716           assert( len >= 0 );
717 
718           if ( u->lud_dn && u->lud_dn[0] ) {
719                     i = hex_escape( &s[sofar], len, u->lud_dn, URLESC_NONE );
720                     sofar += i;
721                     len -= i;
722 
723                     assert( len >= 0 );
724           }
725 
726           if ( sep < 2 ) {
727                     goto done;
728           }
729           s[sofar++] = '?';
730           len--;
731 
732           assert( len >= 0 );
733 
734           i = hex_escape_list( &s[sofar], len, u->lud_attrs, URLESC_NONE );
735           sofar += i;
736           len -= i;
737 
738           assert( len >= 0 );
739 
740           if ( sep < 3 ) {
741                     goto done;
742           }
743           s[sofar++] = '?';
744           len--;
745 
746           assert( len >= 0 );
747 
748           if ( !BER_BVISNULL( &scope ) ) {
749                     strcpy( &s[sofar], scope.bv_val );
750                     sofar += scope.bv_len;
751                     len -= scope.bv_len;
752           }
753 
754           assert( len >= 0 );
755 
756           if ( sep < 4 ) {
757                     goto done;
758           }
759           s[sofar++] = '?';
760           len--;
761 
762           assert( len >= 0 );
763 
764           i = hex_escape( &s[sofar], len, u->lud_filter, URLESC_NONE );
765           sofar += i;
766           len -= i;
767 
768           assert( len >= 0 );
769 
770           if ( sep < 5 ) {
771                     goto done;
772           }
773           s[sofar++] = '?';
774           len--;
775 
776           assert( len >= 0 );
777 
778           i = hex_escape_list( &s[sofar], len, u->lud_exts, URLESC_COMMA );
779           sofar += i;
780           len -= i;
781 
782           assert( len >= 0 );
783 
784 done:
785           if ( len < 0 ) {
786                     return -1;
787           }
788 
789           return sofar;
790 }
791 
792 char *
ldap_url_desc2str(LDAPURLDesc * u)793 ldap_url_desc2str( LDAPURLDesc *u )
794 {
795           int       len;
796           char      *s;
797 
798           if ( u == NULL ) {
799                     return NULL;
800           }
801 
802           len = desc2str_len( u );
803           if ( len < 0 ) {
804                     return NULL;
805           }
806 
807           /* allocate enough to hex escape everything -- overkill */
808           s = LDAP_MALLOC( len + 1 );
809 
810           if ( s == NULL ) {
811                     return NULL;
812           }
813 
814           if ( desc2str( u, s, len ) != len ) {
815                     LDAP_FREE( s );
816                     return NULL;
817           }
818 
819           s[len] = '\0';
820 
821           return s;
822 }
823 
824 int
ldap_url_parse_ext(LDAP_CONST char * url_in,LDAPURLDesc ** ludpp,unsigned flags)825 ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp, unsigned flags )
826 {
827 /*
828  *  Pick apart the pieces of an LDAP URL.
829  */
830 
831           LDAPURLDesc         *ludp;
832           char      *p, *q, *r;
833           int                 i, enclosed, proto, is_v6 = 0;
834           const char *scheme = NULL;
835           const char *url_tmp;
836           char *url;
837 
838           int       check_dn = 1;
839 
840           if( url_in == NULL || ludpp == NULL ) {
841                     return LDAP_URL_ERR_PARAM;
842           }
843 
844 #ifndef LDAP_INT_IN_KERNEL
845           /* Global options may not be created yet
846            * We can't test if the global options are initialized
847            * because a call to LDAP_INT_GLOBAL_OPT() will try to allocate
848            * the options and cause infinite recursion
849            */
850           Debug1( LDAP_DEBUG_TRACE, "ldap_url_parse_ext(%s)\n", url_in );
851 #endif
852 
853           *ludpp = NULL;      /* pessimistic */
854 
855           url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
856 
857           if ( url_tmp == NULL ) {
858                     return LDAP_URL_ERR_BADSCHEME;
859           }
860 
861           assert( scheme != NULL );
862 
863           proto = ldap_pvt_url_scheme2proto( scheme );
864           if ( proto == -1 ) {
865                     return LDAP_URL_ERR_BADSCHEME;
866           }
867 
868           /* make working copy of the remainder of the URL */
869           url = LDAP_STRDUP( url_tmp );
870           if ( url == NULL ) {
871                     return LDAP_URL_ERR_MEM;
872           }
873 
874           if ( enclosed ) {
875                     p = &url[strlen(url)-1];
876 
877                     if( *p != '>' ) {
878                               LDAP_FREE( url );
879                               return LDAP_URL_ERR_BADENCLOSURE;
880                     }
881 
882                     *p = '\0';
883           }
884 
885           /* allocate return struct */
886           ludp = (LDAPURLDesc *)LDAP_CALLOC( 1, sizeof( LDAPURLDesc ));
887 
888           if ( ludp == NULL ) {
889                     LDAP_FREE( url );
890                     return LDAP_URL_ERR_MEM;
891           }
892 
893           ludp->lud_next = NULL;
894           ludp->lud_host = NULL;
895           ludp->lud_port = 0;
896           ludp->lud_dn = NULL;
897           ludp->lud_attrs = NULL;
898           ludp->lud_scope = ( flags & LDAP_PVT_URL_PARSE_NODEF_SCOPE ) ? LDAP_SCOPE_BASE : LDAP_SCOPE_DEFAULT;
899           ludp->lud_filter = NULL;
900           ludp->lud_exts = NULL;
901 
902           ludp->lud_scheme = LDAP_STRDUP( scheme );
903 
904           if ( ludp->lud_scheme == NULL ) {
905                     LDAP_FREE( url );
906                     ldap_free_urldesc( ludp );
907                     return LDAP_URL_ERR_MEM;
908           }
909 
910           /* scan forward for '/' that marks end of hostport and begin. of dn */
911           p = strchr( url, '/' );
912           q = NULL;
913 
914           if( p != NULL ) {
915                     /* terminate hostport; point to start of dn */
916                     *p++ = '\0';
917           } else {
918                     /* check for Novell kludge, see below */
919                     p = strchr( url, '?' );
920                     if ( p ) {
921                               *p++ = '\0';
922                               q = p;
923                               p = NULL;
924                     }
925           }
926 
927           if ( proto != LDAP_PROTO_IPC ) {
928                     /* IPv6 syntax with [ip address]:port */
929                     if ( *url == '[' ) {
930                               r = strchr( url, ']' );
931                               if ( r == NULL ) {
932                                         LDAP_FREE( url );
933                                         ldap_free_urldesc( ludp );
934                                         return LDAP_URL_ERR_BADURL;
935                               }
936                               *r++ = '\0';
937                               q = strchr( r, ':' );
938                               if ( q && q != r ) {
939                                         LDAP_FREE( url );
940                                         ldap_free_urldesc( ludp );
941                                         return LDAP_URL_ERR_BADURL;
942                               }
943                               is_v6 = 1;
944                     } else {
945                               q = strchr( url, ':' );
946                     }
947 
948                     if ( q != NULL ) {
949                               char      *next;
950 
951                               *q++ = '\0';
952                               ldap_pvt_hex_unescape( q );
953 
954                               if( *q == '\0' ) {
955                                         LDAP_FREE( url );
956                                         ldap_free_urldesc( ludp );
957                                         return LDAP_URL_ERR_BADURL;
958                               }
959 
960                               ludp->lud_port = strtol( q, &next, 10 );
961                               if ( next == q || next[0] != '\0' ) {
962                                         LDAP_FREE( url );
963                                         ldap_free_urldesc( ludp );
964                                         return LDAP_URL_ERR_BADURL;
965                               }
966                               /* check for Novell kludge */
967                               if ( !p ) {
968                                         if ( *next != '\0' ) {
969                                                   q = &next[1];
970                                         } else {
971                                                   q = NULL;
972                                         }
973                               }
974                     }
975 
976                     if ( ( flags & LDAP_PVT_URL_PARSE_DEF_PORT ) && ludp->lud_port == 0 ) {
977                               if ( strcmp( ludp->lud_scheme, "ldaps" ) == 0 ) {
978                                         ludp->lud_port = LDAPS_PORT;
979                               } else {
980                                         ludp->lud_port = LDAP_PORT;
981                               }
982                     }
983           }
984 
985           ldap_pvt_hex_unescape( url );
986 
987           /* If [ip address]:port syntax, url is [ip and we skip the [ */
988           ludp->lud_host = LDAP_STRDUP( url + is_v6 );
989 
990           if( ludp->lud_host == NULL ) {
991                     LDAP_FREE( url );
992                     ldap_free_urldesc( ludp );
993                     return LDAP_URL_ERR_MEM;
994           }
995 
996           if ( ( flags & LDAP_PVT_URL_PARSE_NOEMPTY_HOST )
997                     && ludp->lud_host != NULL
998                     && *ludp->lud_host == '\0' )
999           {
1000                     LDAP_FREE( ludp->lud_host );
1001                     ludp->lud_host = NULL;
1002           }
1003 
1004           /*
1005            * Kludge.  ldap://111.222.333.444:389??cn=abc,o=company
1006            *
1007            * On early Novell releases, search references/referrals were returned
1008            * in this format, i.e., the dn was kind of in the scope position,
1009            * but the required slash is missing. The whole thing is illegal syntax,
1010            * but we need to account for it. Fortunately it can't be confused with
1011            * anything real.
1012            */
1013           if( (p == NULL) && (q != NULL) && (*q == '?') ) {
1014                     /* ? immediately followed by question */
1015                     q++;
1016                     if( *q != '\0' ) {
1017                               /* parse dn part */
1018                               ldap_pvt_hex_unescape( q );
1019                               ludp->lud_dn = LDAP_STRDUP( q );
1020 
1021                     } else if ( !( flags & LDAP_PVT_URL_PARSE_NOEMPTY_DN ) ) {
1022                               ludp->lud_dn = LDAP_STRDUP( "" );
1023 
1024                     } else {
1025                               check_dn = 0;
1026                     }
1027 
1028                     if ( check_dn && ludp->lud_dn == NULL ) {
1029                               LDAP_FREE( url );
1030                               ldap_free_urldesc( ludp );
1031                               return LDAP_URL_ERR_MEM;
1032                     }
1033           }
1034 
1035           if( p == NULL ) {
1036                     LDAP_FREE( url );
1037                     *ludpp = ludp;
1038                     return LDAP_URL_SUCCESS;
1039           }
1040 
1041           /* scan forward for '?' that may marks end of dn */
1042           q = strchr( p, '?' );
1043 
1044           if( q != NULL ) {
1045                     /* terminate dn part */
1046                     *q++ = '\0';
1047           }
1048 
1049           if( *p != '\0' ) {
1050                     /* parse dn part */
1051                     ldap_pvt_hex_unescape( p );
1052                     ludp->lud_dn = LDAP_STRDUP( p );
1053 
1054           } else if ( !( flags & LDAP_PVT_URL_PARSE_NOEMPTY_DN ) ) {
1055                     ludp->lud_dn = LDAP_STRDUP( "" );
1056 
1057           } else {
1058                     check_dn = 0;
1059           }
1060 
1061           if( check_dn && ludp->lud_dn == NULL ) {
1062                     LDAP_FREE( url );
1063                     ldap_free_urldesc( ludp );
1064                     return LDAP_URL_ERR_MEM;
1065           }
1066 
1067           if( q == NULL ) {
1068                     /* no more */
1069                     LDAP_FREE( url );
1070                     *ludpp = ludp;
1071                     return LDAP_URL_SUCCESS;
1072           }
1073 
1074           /* scan forward for '?' that may marks end of attributes */
1075           p = q;
1076           q = strchr( p, '?' );
1077 
1078           if( q != NULL ) {
1079                     /* terminate attributes part */
1080                     *q++ = '\0';
1081           }
1082 
1083           if( *p != '\0' ) {
1084                     /* parse attributes */
1085                     ldap_pvt_hex_unescape( p );
1086                     ludp->lud_attrs = ldap_str2charray( p, "," );
1087 
1088                     if( ludp->lud_attrs == NULL ) {
1089                               LDAP_FREE( url );
1090                               ldap_free_urldesc( ludp );
1091                               return LDAP_URL_ERR_BADATTRS;
1092                     }
1093           }
1094 
1095           if ( q == NULL ) {
1096                     /* no more */
1097                     LDAP_FREE( url );
1098                     *ludpp = ludp;
1099                     return LDAP_URL_SUCCESS;
1100           }
1101 
1102           /* scan forward for '?' that may marks end of scope */
1103           p = q;
1104           q = strchr( p, '?' );
1105 
1106           if( q != NULL ) {
1107                     /* terminate the scope part */
1108                     *q++ = '\0';
1109           }
1110 
1111           if( *p != '\0' ) {
1112                     /* parse the scope */
1113                     ldap_pvt_hex_unescape( p );
1114                     ludp->lud_scope = ldap_pvt_str2scope( p );
1115 
1116                     if( ludp->lud_scope == -1 ) {
1117                               LDAP_FREE( url );
1118                               ldap_free_urldesc( ludp );
1119                               return LDAP_URL_ERR_BADSCOPE;
1120                     }
1121           }
1122 
1123           if ( q == NULL ) {
1124                     /* no more */
1125                     LDAP_FREE( url );
1126                     *ludpp = ludp;
1127                     return LDAP_URL_SUCCESS;
1128           }
1129 
1130           /* scan forward for '?' that may marks end of filter */
1131           p = q;
1132           q = strchr( p, '?' );
1133 
1134           if( q != NULL ) {
1135                     /* terminate the filter part */
1136                     *q++ = '\0';
1137           }
1138 
1139           if( *p != '\0' ) {
1140                     /* parse the filter */
1141                     ldap_pvt_hex_unescape( p );
1142 
1143                     if( ! *p ) {
1144                               /* missing filter */
1145                               LDAP_FREE( url );
1146                               ldap_free_urldesc( ludp );
1147                               return LDAP_URL_ERR_BADFILTER;
1148                     }
1149 
1150                     ludp->lud_filter = LDAP_STRDUP( p );
1151 
1152                     if( ludp->lud_filter == NULL ) {
1153                               LDAP_FREE( url );
1154                               ldap_free_urldesc( ludp );
1155                               return LDAP_URL_ERR_MEM;
1156                     }
1157           }
1158 
1159           if ( q == NULL ) {
1160                     /* no more */
1161                     LDAP_FREE( url );
1162                     *ludpp = ludp;
1163                     return LDAP_URL_SUCCESS;
1164           }
1165 
1166           /* scan forward for '?' that may marks end of extensions */
1167           p = q;
1168           q = strchr( p, '?' );
1169 
1170           if( q != NULL ) {
1171                     /* extra '?' */
1172                     LDAP_FREE( url );
1173                     ldap_free_urldesc( ludp );
1174                     return LDAP_URL_ERR_BADURL;
1175           }
1176 
1177           /* parse the extensions */
1178           ludp->lud_exts = ldap_str2charray( p, "," );
1179 
1180           if( ludp->lud_exts == NULL ) {
1181                     LDAP_FREE( url );
1182                     ldap_free_urldesc( ludp );
1183                     return LDAP_URL_ERR_BADEXTS;
1184           }
1185 
1186           for( i=0; ludp->lud_exts[i] != NULL; i++ ) {
1187                     ldap_pvt_hex_unescape( ludp->lud_exts[i] );
1188 
1189                     if( *ludp->lud_exts[i] == '!' ) {
1190                               /* count the number of critical extensions */
1191                               ludp->lud_crit_exts++;
1192                     }
1193           }
1194 
1195           if( i == 0 ) {
1196                     /* must have 1 or more */
1197                     LDAP_FREE( url );
1198                     ldap_free_urldesc( ludp );
1199                     return LDAP_URL_ERR_BADEXTS;
1200           }
1201 
1202           /* no more */
1203           *ludpp = ludp;
1204           LDAP_FREE( url );
1205           return LDAP_URL_SUCCESS;
1206 }
1207 
1208 int
ldap_url_parse(LDAP_CONST char * url_in,LDAPURLDesc ** ludpp)1209 ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
1210 {
1211           return ldap_url_parse_ext( url_in, ludpp, LDAP_PVT_URL_PARSE_HISTORIC );
1212 }
1213 
1214 LDAPURLDesc *
ldap_url_dup(LDAPURLDesc * ludp)1215 ldap_url_dup ( LDAPURLDesc *ludp )
1216 {
1217           LDAPURLDesc *dest;
1218 
1219           if ( ludp == NULL ) {
1220                     return NULL;
1221           }
1222 
1223           dest = LDAP_MALLOC( sizeof(LDAPURLDesc) );
1224           if (dest == NULL)
1225                     return NULL;
1226 
1227           *dest = *ludp;
1228           dest->lud_scheme = NULL;
1229           dest->lud_host = NULL;
1230           dest->lud_dn = NULL;
1231           dest->lud_filter = NULL;
1232           dest->lud_attrs = NULL;
1233           dest->lud_exts = NULL;
1234           dest->lud_next = NULL;
1235 
1236           if ( ludp->lud_scheme != NULL ) {
1237                     dest->lud_scheme = LDAP_STRDUP( ludp->lud_scheme );
1238                     if (dest->lud_scheme == NULL) {
1239                               ldap_free_urldesc(dest);
1240                               return NULL;
1241                     }
1242           }
1243 
1244           if ( ludp->lud_host != NULL ) {
1245                     dest->lud_host = LDAP_STRDUP( ludp->lud_host );
1246                     if (dest->lud_host == NULL) {
1247                               ldap_free_urldesc(dest);
1248                               return NULL;
1249                     }
1250           }
1251 
1252           if ( ludp->lud_dn != NULL ) {
1253                     dest->lud_dn = LDAP_STRDUP( ludp->lud_dn );
1254                     if (dest->lud_dn == NULL) {
1255                               ldap_free_urldesc(dest);
1256                               return NULL;
1257                     }
1258           }
1259 
1260           if ( ludp->lud_filter != NULL ) {
1261                     dest->lud_filter = LDAP_STRDUP( ludp->lud_filter );
1262                     if (dest->lud_filter == NULL) {
1263                               ldap_free_urldesc(dest);
1264                               return NULL;
1265                     }
1266           }
1267 
1268           if ( ludp->lud_attrs != NULL ) {
1269                     dest->lud_attrs = ldap_charray_dup( ludp->lud_attrs );
1270                     if (dest->lud_attrs == NULL) {
1271                               ldap_free_urldesc(dest);
1272                               return NULL;
1273                     }
1274           }
1275 
1276           if ( ludp->lud_exts != NULL ) {
1277                     dest->lud_exts = ldap_charray_dup( ludp->lud_exts );
1278                     if (dest->lud_exts == NULL) {
1279                               ldap_free_urldesc(dest);
1280                               return NULL;
1281                     }
1282           }
1283 
1284           return dest;
1285 }
1286 
1287 LDAPURLDesc *
ldap_url_duplist(LDAPURLDesc * ludlist)1288 ldap_url_duplist (LDAPURLDesc *ludlist)
1289 {
1290           LDAPURLDesc *dest, *tail, *ludp, *newludp;
1291 
1292           dest = NULL;
1293           tail = NULL;
1294           for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
1295                     newludp = ldap_url_dup(ludp);
1296                     if (newludp == NULL) {
1297                               ldap_free_urllist(dest);
1298                               return NULL;
1299                     }
1300                     if (tail == NULL)
1301                               dest = newludp;
1302                     else
1303                               tail->lud_next = newludp;
1304                     tail = newludp;
1305           }
1306           return dest;
1307 }
1308 
1309 static int
ldap_url_parselist_int(LDAPURLDesc ** ludlist,const char * url,const char * sep,unsigned flags)1310 ldap_url_parselist_int (LDAPURLDesc **ludlist, const char *url, const char *sep, unsigned flags )
1311 
1312 {
1313           int i, rc;
1314           LDAPURLDesc *ludp;
1315           char **urls;
1316 
1317           assert( ludlist != NULL );
1318           assert( url != NULL );
1319 
1320           *ludlist = NULL;
1321 
1322           if ( sep == NULL ) {
1323                     sep = ", ";
1324           }
1325 
1326           urls = ldap_str2charray( url, sep );
1327           if (urls == NULL)
1328                     return LDAP_URL_ERR_MEM;
1329 
1330           /* count the URLs... */
1331           for (i = 0; urls[i] != NULL; i++) ;
1332           /* ...and put them in the "stack" backward */
1333           while (--i >= 0) {
1334                     rc = ldap_url_parse_ext( urls[i], &ludp, flags );
1335                     if ( rc != 0 ) {
1336                               ldap_charray_free( urls );
1337                               ldap_free_urllist( *ludlist );
1338                               *ludlist = NULL;
1339                               return rc;
1340                     }
1341                     ludp->lud_next = *ludlist;
1342                     *ludlist = ludp;
1343           }
1344           ldap_charray_free( urls );
1345           return LDAP_URL_SUCCESS;
1346 }
1347 
1348 int
ldap_url_parselist(LDAPURLDesc ** ludlist,const char * url)1349 ldap_url_parselist (LDAPURLDesc **ludlist, const char *url )
1350 {
1351           return ldap_url_parselist_int( ludlist, url, ", ", LDAP_PVT_URL_PARSE_HISTORIC );
1352 }
1353 
1354 int
ldap_url_parselist_ext(LDAPURLDesc ** ludlist,const char * url,const char * sep,unsigned flags)1355 ldap_url_parselist_ext (LDAPURLDesc **ludlist, const char *url, const char *sep, unsigned flags )
1356 {
1357           return ldap_url_parselist_int( ludlist, url, sep, flags );
1358 }
1359 
1360 int
ldap_url_parsehosts(LDAPURLDesc ** ludlist,const char * hosts,int port)1361 ldap_url_parsehosts(
1362           LDAPURLDesc **ludlist,
1363           const char *hosts,
1364           int port )
1365 {
1366           int i;
1367           LDAPURLDesc *ludp;
1368           char **specs, *p;
1369 
1370           assert( ludlist != NULL );
1371           assert( hosts != NULL );
1372 
1373           *ludlist = NULL;
1374 
1375           specs = ldap_str2charray(hosts, ", ");
1376           if (specs == NULL)
1377                     return LDAP_NO_MEMORY;
1378 
1379           /* count the URLs... */
1380           for (i = 0; specs[i] != NULL; i++) /* EMPTY */;
1381 
1382           /* ...and put them in the "stack" backward */
1383           while (--i >= 0) {
1384                     ludp = LDAP_CALLOC( 1, sizeof(LDAPURLDesc) );
1385                     if (ludp == NULL) {
1386                               ldap_charray_free(specs);
1387                               ldap_free_urllist(*ludlist);
1388                               *ludlist = NULL;
1389                               return LDAP_NO_MEMORY;
1390                     }
1391                     ludp->lud_port = port;
1392                     ludp->lud_host = specs[i];
1393                     specs[i] = NULL;
1394                     p = strchr(ludp->lud_host, ':');
1395                     if (p != NULL) {
1396                               /* more than one :, IPv6 address */
1397                               if ( strchr(p+1, ':') != NULL ) {
1398                                         /* allow [address] and [address]:port */
1399                                         if ( *ludp->lud_host == '[' ) {
1400                                                   p = LDAP_STRDUP(ludp->lud_host+1);
1401                                                   /* copied, make sure we free source later */
1402                                                   specs[i] = ludp->lud_host;
1403                                                   ludp->lud_host = p;
1404                                                   p = strchr( ludp->lud_host, ']' );
1405                                                   if ( p == NULL ) {
1406                                                             LDAP_FREE(ludp);
1407                                                             ldap_charray_free(specs);
1408                                                             return LDAP_PARAM_ERROR;
1409                                                   }
1410                                                   *p++ = '\0';
1411                                                   if ( *p != ':' ) {
1412                                                             if ( *p != '\0' ) {
1413                                                                       LDAP_FREE(ludp);
1414                                                                       ldap_charray_free(specs);
1415                                                                       return LDAP_PARAM_ERROR;
1416                                                             }
1417                                                             p = NULL;
1418                                                   }
1419                                         } else {
1420                                                   p = NULL;
1421                                         }
1422                               }
1423                               if (p != NULL) {
1424                                         char      *next;
1425 
1426                                         *p++ = 0;
1427                                         ldap_pvt_hex_unescape(p);
1428                                         ludp->lud_port = strtol( p, &next, 10 );
1429                                         if ( next == p || next[0] != '\0' ) {
1430                                                   LDAP_FREE(ludp);
1431                                                   ldap_charray_free(specs);
1432                                                   return LDAP_PARAM_ERROR;
1433                                         }
1434                               }
1435                     }
1436                     ldap_pvt_hex_unescape(ludp->lud_host);
1437                     ludp->lud_scheme = LDAP_STRDUP("ldap");
1438                     ludp->lud_next = *ludlist;
1439                     *ludlist = ludp;
1440           }
1441 
1442           /* this should be an array of NULLs now */
1443           /* except entries starting with [ */
1444           ldap_charray_free(specs);
1445           return LDAP_SUCCESS;
1446 }
1447 
1448 char *
ldap_url_list2hosts(LDAPURLDesc * ludlist)1449 ldap_url_list2hosts (LDAPURLDesc *ludlist)
1450 {
1451           LDAPURLDesc *ludp;
1452           int size;
1453           char *s, *p, buf[32];         /* big enough to hold a long decimal # (overkill) */
1454 
1455           if (ludlist == NULL)
1456                     return NULL;
1457 
1458           /* figure out how big the string is */
1459           size = 1; /* nul-term */
1460           for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
1461                     if ( ludp->lud_host == NULL ) continue;
1462                     size += strlen(ludp->lud_host) + 1;               /* host and space */
1463                     if (strchr(ludp->lud_host, ':'))        /* will add [ ] below */
1464                               size += 2;
1465                     if (ludp->lud_port != 0)
1466                               size += sprintf(buf, ":%d", ludp->lud_port);
1467           }
1468           s = LDAP_MALLOC(size);
1469           if (s == NULL)
1470                     return NULL;
1471 
1472           p = s;
1473           for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
1474                     if ( ludp->lud_host == NULL ) continue;
1475                     if (strchr(ludp->lud_host, ':')) {
1476                               p += sprintf(p, "[%s]", ludp->lud_host);
1477                     } else {
1478                               strcpy(p, ludp->lud_host);
1479                               p += strlen(ludp->lud_host);
1480                     }
1481                     if (ludp->lud_port != 0)
1482                               p += sprintf(p, ":%d", ludp->lud_port);
1483                     *p++ = ' ';
1484           }
1485           if (p != s)
1486                     p--;      /* nuke that extra space */
1487           *p = '\0';
1488           return s;
1489 }
1490 
1491 char *
ldap_url_list2urls(LDAPURLDesc * ludlist)1492 ldap_url_list2urls(
1493           LDAPURLDesc *ludlist )
1494 {
1495           LDAPURLDesc         *ludp;
1496           int                 size, sofar;
1497           char                *s;
1498 
1499           if ( ludlist == NULL ) {
1500                     return NULL;
1501           }
1502 
1503           /* figure out how big the string is */
1504           for ( size = 0, ludp = ludlist; ludp != NULL; ludp = ludp->lud_next ) {
1505                     int       len = desc2str_len( ludp );
1506                     if ( len < 0 ) {
1507                               return NULL;
1508                     }
1509                     size += len + 1;
1510           }
1511 
1512           s = LDAP_MALLOC( size );
1513 
1514           if ( s == NULL ) {
1515                     return NULL;
1516           }
1517 
1518           for ( sofar = 0, ludp = ludlist; ludp != NULL; ludp = ludp->lud_next ) {
1519                     int       len;
1520 
1521                     len = desc2str( ludp, &s[sofar], size );
1522 
1523                     if ( len < 0 ) {
1524                               LDAP_FREE( s );
1525                               return NULL;
1526                     }
1527 
1528                     sofar += len;
1529                     size -= len;
1530 
1531                     s[sofar++] = ' ';
1532                     size--;
1533 
1534                     assert( size >= 0 );
1535           }
1536 
1537           s[sofar - 1] = '\0';
1538 
1539           return s;
1540 }
1541 
1542 void
ldap_free_urllist(LDAPURLDesc * ludlist)1543 ldap_free_urllist( LDAPURLDesc *ludlist )
1544 {
1545           LDAPURLDesc *ludp, *next;
1546 
1547           for (ludp = ludlist; ludp != NULL; ludp = next) {
1548                     next = ludp->lud_next;
1549                     ldap_free_urldesc(ludp);
1550           }
1551 }
1552 
1553 void
ldap_free_urldesc(LDAPURLDesc * ludp)1554 ldap_free_urldesc( LDAPURLDesc *ludp )
1555 {
1556           if ( ludp == NULL ) {
1557                     return;
1558           }
1559 
1560           if ( ludp->lud_scheme != NULL ) {
1561                     LDAP_FREE( ludp->lud_scheme );
1562           }
1563 
1564           if ( ludp->lud_host != NULL ) {
1565                     LDAP_FREE( ludp->lud_host );
1566           }
1567 
1568           if ( ludp->lud_dn != NULL ) {
1569                     LDAP_FREE( ludp->lud_dn );
1570           }
1571 
1572           if ( ludp->lud_filter != NULL ) {
1573                     LDAP_FREE( ludp->lud_filter);
1574           }
1575 
1576           if ( ludp->lud_attrs != NULL ) {
1577                     LDAP_VFREE( ludp->lud_attrs );
1578           }
1579 
1580           if ( ludp->lud_exts != NULL ) {
1581                     LDAP_VFREE( ludp->lud_exts );
1582           }
1583 
1584           LDAP_FREE( ludp );
1585 }
1586 
1587 static int
ldap_int_is_hexpair(char * s)1588 ldap_int_is_hexpair( char *s )
1589 {
1590           int       i;
1591 
1592           for ( i = 0; i < 2; i++ ) {
1593                     if ( s[i] >= '0' && s[i] <= '9' ) {
1594                               continue;
1595                     }
1596 
1597                     if ( s[i] >= 'A' && s[i] <= 'F' ) {
1598                               continue;
1599                     }
1600 
1601                     if ( s[i] >= 'a' && s[i] <= 'f' ) {
1602                               continue;
1603                     }
1604 
1605                     return 0;
1606           }
1607 
1608           return 1;
1609 }
1610 
1611 static int
ldap_int_unhex(int c)1612 ldap_int_unhex( int c )
1613 {
1614           return( c >= '0' && c <= '9' ? c - '0'
1615               : c >= 'A' && c <= 'F' ? c - 'A' + 10
1616               : c - 'a' + 10 );
1617 }
1618 
1619 void
ldap_pvt_hex_unescape(char * s)1620 ldap_pvt_hex_unescape( char *s )
1621 {
1622           /*
1623            * Remove URL hex escapes from s... done in place.  The basic concept for
1624            * this routine is borrowed from the WWW library HTUnEscape() routine.
1625            */
1626           char      *p,
1627                     *save_s = s;
1628 
1629           for ( p = s; *s != '\0'; ++s ) {
1630                     if ( *s == '%' ) {
1631                               /*
1632                                * FIXME: what if '%' is followed
1633                                * by non-hexpair chars?
1634                                */
1635                               if ( !ldap_int_is_hexpair( s + 1 ) ) {
1636                                         p = save_s;
1637                                         break;
1638                               }
1639 
1640                               if ( *++s == '\0' ) {
1641                                         break;
1642                               }
1643                               *p = ldap_int_unhex( *s ) << 4;
1644                               if ( *++s == '\0' ) {
1645                                         break;
1646                               }
1647                               *p++ += ldap_int_unhex( *s );
1648                     } else {
1649                               *p++ = *s;
1650                     }
1651           }
1652 
1653           *p = '\0';
1654 }
1655 
1656