1 /* The following code comes from the OpenLDAP project.  The references
2    to the COPYRIGHT file below refer to the corresponding file in the
3    OpenLDAP distribution, which is reproduced here in full:
4 
5 Copyright 1998-2004 The OpenLDAP Foundation
6 All rights reserved.
7 
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted only as authorized by the OpenLDAP
10 Public License.
11 
12 A copy of this license is available in the file LICENSE in the
13 top-level directory of the distribution or, alternatively, at
14 <http://www.OpenLDAP.org/license.html>.
15 
16 OpenLDAP is a registered trademark of the OpenLDAP Foundation.
17 
18 Individual files and/or contributed packages may be copyright by
19 other parties and subject to additional restrictions.
20 
21 This work is derived from the University of Michigan LDAP v3.3
22 distribution.  Information concerning this software is available
23 at <http://www.umich.edu/~dirsvcs/ldap/>.
24 
25 This work also contains materials derived from public sources.
26 
27 Additional information about OpenLDAP can be obtained at
28 <http://www.openldap.org/>.
29 
30 ---
31 
32 Portions Copyright 1998-2004 Kurt D. Zeilenga.
33 Portions Copyright 1998-2004 Net Boolean Incorporated.
34 Portions Copyright 2001-2004 IBM Corporation.
35 All rights reserved.
36 
37 Redistribution and use in source and binary forms, with or without
38 modification, are permitted only as authorized by the OpenLDAP
39 Public License.
40 
41 ---
42 
43 Portions Copyright 1999-2003 Howard Y.H. Chu.
44 Portions Copyright 1999-2003 Symas Corporation.
45 Portions Copyright 1998-2003 Hallvard B. Furuseth.
46 All rights reserved.
47 
48 Redistribution and use in source and binary forms, with or without
49 modification, are permitted provided that this notice is preserved.
50 The names of the copyright holders may not be used to endorse or
51 promote products derived from this software without their specific
52 prior written permission.  This software is provided ``as is''
53 without express or implied warranty.
54 
55 ---
56 
57 Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
58 All rights reserved.
59 
60 Redistribution and use in source and binary forms are permitted
61 provided that this notice is preserved and that due credit is given
62 to the University of Michigan at Ann Arbor.  The name of the
63 University may not be used to endorse or promote products derived
64 from this software without specific prior written permission.  This
65 software is provided ``as is'' without express or implied warranty.  */
66 
67 
68 #include <config.h>
69 #include <assert.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <time.h>
74 
75 #include <winsock2.h>
76 #include <winldap.h>
77 #include "ldap-url.h"
78 #define LDAP_P(protos)		protos
79 #define LDAP_URL_URLCOLON	"URL:"
80 #define LDAP_URL_URLCOLON_LEN   (sizeof(LDAP_URL_URLCOLON)-1)
81 #define LDAP_URL_PREFIX         "ldap://"
82 #define LDAP_URL_PREFIX_LEN     (sizeof(LDAP_URL_PREFIX)-1)
83 #define LDAPS_URL_PREFIX        "ldaps://"
84 #define LDAPS_URL_PREFIX_LEN    (sizeof(LDAPS_URL_PREFIX)-1)
85 #define LDAPI_URL_PREFIX        "ldapi://"
86 #define LDAPI_URL_PREFIX_LEN    (sizeof(LDAPI_URL_PREFIX)-1)
87 #define LDAP_VFREE(v)           { int _i; for (_i = 0; (v)[_i]; _i++) free((v)[_i]); }
88 #define LDAP_FREE		free
89 #define LDAP_STRDUP		strdup
90 #define LDAP_CALLOC		calloc
91 #define LDAP_MALLOC		malloc
92 #define LDAP_REALLOC		realloc
93 #define ldap_utf8_strchr	strchr
94 #define ldap_utf8_strtok(n,d,s) strtok (n,d)
95 #define Debug(a,b,c,d,e)
96 void ldap_pvt_hex_unescape( char *s );
97 
98 
99 
100 /* $OpenLDAP: pkg/ldap/libraries/libldap/charray.c,v 1.9.2.2 2003/03/03 17:10:04 kurt Exp $ */
101 /*
102  * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
103  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
104  */
105 /* charray.c - routines for dealing with char * arrays */
106 
107 int
ldap_charray_add(char *** a,char * s)108 ldap_charray_add(
109     char	***a,
110     char	*s
111 )
112 {
113 	int	n;
114 
115 	if ( *a == NULL ) {
116 		*a = (char **) LDAP_MALLOC( 2 * sizeof(char *) );
117 		n = 0;
118 
119 		if( *a == NULL ) {
120 			return -1;
121 		}
122 
123 	} else {
124 		char **new;
125 
126 		for ( n = 0; *a != NULL && (*a)[n] != NULL; n++ ) {
127 			;	/* NULL */
128 		}
129 
130 		new = (char **) LDAP_REALLOC( (char *) *a,
131 		    (n + 2) * sizeof(char *) );
132 
133 		if( new == NULL ) {
134 			/* caller is required to call ldap_charray_free(*a) */
135 			return -1;
136 		}
137 
138 		*a = new;
139 	}
140 
141 	(*a)[n] = LDAP_STRDUP(s);
142 
143 	if( (*a)[n] == NULL ) {
144 		return 1;
145 	}
146 
147 	(*a)[++n] = NULL;
148 
149 	return 0;
150 }
151 
152 int
ldap_charray_merge(char *** a,char ** s)153 ldap_charray_merge(
154     char	***a,
155     char	**s
156 )
157 {
158 	int	i, n, nn;
159 	char **aa;
160 
161 	for ( n = 0; *a != NULL && (*a)[n] != NULL; n++ ) {
162 		;	/* NULL */
163 	}
164 	for ( nn = 0; s[nn] != NULL; nn++ ) {
165 		;	/* NULL */
166 	}
167 
168 	aa = (char **) LDAP_REALLOC( (char *) *a, (n + nn + 1) * sizeof(char *) );
169 
170 	if( aa == NULL ) {
171 		return -1;
172 	}
173 
174 	*a = aa;
175 
176 	for ( i = 0; i < nn; i++ ) {
177 		(*a)[n + i] = LDAP_STRDUP(s[i]);
178 
179 		if( (*a)[n + i] == NULL ) {
180 			for( --i ; i >= 0 ; i-- ) {
181 				LDAP_FREE( (*a)[n + i] );
182 				(*a)[n + i] = NULL;
183 			}
184 			return -1;
185 		}
186 	}
187 
188 	(*a)[n + nn] = NULL;
189 	return 0;
190 }
191 
192 void
ldap_charray_free(char ** a)193 ldap_charray_free( char **a )
194 {
195 	char	**p;
196 
197 	if ( a == NULL ) {
198 		return;
199 	}
200 
201 	for ( p = a; *p != NULL; p++ ) {
202 		if ( *p != NULL ) {
203 			LDAP_FREE( *p );
204 		}
205 	}
206 
207 	LDAP_FREE( (char *) a );
208 }
209 
210 int
ldap_charray_inlist(char ** a,char * s)211 ldap_charray_inlist(
212     char	**a,
213     char	*s
214 )
215 {
216 	int	i;
217 
218 	if( a == NULL ) return 0;
219 
220 	for ( i=0; a[i] != NULL; i++ ) {
221 		if ( strcasecmp( s, a[i] ) == 0 ) {
222 			return 1;
223 		}
224 	}
225 
226 	return 0;
227 }
228 
229 char **
ldap_charray_dup(char ** a)230 ldap_charray_dup( char **a )
231 {
232 	int	i;
233 	char	**new;
234 
235 	for ( i = 0; a[i] != NULL; i++ )
236 		;	/* NULL */
237 
238 	new = (char **) LDAP_MALLOC( (i + 1) * sizeof(char *) );
239 
240 	if( new == NULL ) {
241 		return NULL;
242 	}
243 
244 	for ( i = 0; a[i] != NULL; i++ ) {
245 		new[i] = LDAP_STRDUP( a[i] );
246 
247 		if( new[i] == NULL ) {
248 			for( --i ; i >= 0 ; i-- ) {
249 				LDAP_FREE( new[i] );
250 			}
251 			LDAP_FREE( new );
252 			return NULL;
253 		}
254 	}
255 	new[i] = NULL;
256 
257 	return( new );
258 }
259 
260 char **
ldap_str2charray(const char * str_in,const char * brkstr)261 ldap_str2charray( const char *str_in, const char *brkstr )
262 {
263 	char	**res;
264 	char	*str, *s;
265 	char	*lasts;
266 	int	i;
267 
268 	/* protect the input string from strtok */
269 	str = LDAP_STRDUP( str_in );
270 	if( str == NULL ) {
271 		return NULL;
272 	}
273 
274 	i = 1;
275 	for ( s = str; *s; s++ ) {
276 		if ( ldap_utf8_strchr( brkstr, *s ) != NULL ) {
277 			i++;
278 		}
279 	}
280 
281 	res = (char **) LDAP_MALLOC( (i + 1) * sizeof(char *) );
282 
283 	if( res == NULL ) {
284 		LDAP_FREE( str );
285 		return NULL;
286 	}
287 
288 	i = 0;
289 
290 	for ( s = ldap_utf8_strtok( str, brkstr, &lasts );
291 		s != NULL;
292 		s = ldap_utf8_strtok( NULL, brkstr, &lasts ) )
293 	{
294 		res[i] = LDAP_STRDUP( s );
295 
296 		if(res[i] == NULL) {
297 			for( --i ; i >= 0 ; i-- ) {
298 				LDAP_FREE( res[i] );
299 			}
300 			LDAP_FREE( res );
301 			LDAP_FREE( str );
302 			return NULL;
303 		}
304 
305 		i++;
306 	}
307 
308 	res[i] = NULL;
309 
310 	LDAP_FREE( str );
311 	return( res );
312 }
313 
ldap_charray2str(char ** a,const char * sep)314 char * ldap_charray2str( char **a, const char *sep )
315 {
316 	char *s, **v, *p;
317 	int len;
318 	int slen;
319 
320 	if( sep == NULL ) sep = " ";
321 
322 	slen = strlen( sep );
323 	len = 0;
324 
325 	for ( v = a; *v != NULL; v++ ) {
326 		len += strlen( *v ) + slen;
327 	}
328 
329 	if ( len == 0 ) {
330 		return NULL;
331 	}
332 
333 	/* trim extra sep len */
334 	len -= slen;
335 
336 	s = LDAP_MALLOC ( len + 1 );
337 
338 	if ( s == NULL ) {
339 		return NULL;
340 	}
341 
342 	p = s;
343 	for ( v = a; *v != NULL; v++ ) {
344 		if ( v != a ) {
345 			strncpy( p, sep, slen );
346 			p += slen;
347 		}
348 
349 		len = strlen( *v );
350 		strncpy( p, *v, len );
351 		p += len;
352 	}
353 
354 	*p = '\0';
355 	return s;
356 }
357 
358 
359 
360 /* $OpenLDAP: pkg/ldap/libraries/libldap/url.c,v 1.64.2.5 2003/03/03 17:10:05 kurt Exp $ */
361 /*
362  * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
363  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
364  */
365 /*  Portions
366  *  Copyright (c) 1996 Regents of the University of Michigan.
367  *  All rights reserved.
368  *
369  *  LIBLDAP url.c -- LDAP URL (RFC 2255) related routines
370  *
371  *  LDAP URLs look like this:
372  *    ldap[is]://host:port[/[dn[?[attributes][?[scope][?[filter][?exts]]]]]]
373  *
374  *  where:
375  *   attributes is a comma separated list
376  *   scope is one of these three strings:  base one sub (default=base)
377  *   filter is an string-represented filter as in RFC 2254
378  *
379  *  e.g.,  ldap://host:port/dc=com?o,cn?base?(o=openldap)?extension
380  *
381  *  We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl>
382  */
383 
384 /* local functions */
385 static const char* skip_url_prefix LDAP_P((
386 	const char *url,
387 	int *enclosedp,
388 	const char **scheme ));
389 
390 int
ldap_is_ldap_url(LDAP_CONST char * url)391 ldap_is_ldap_url( LDAP_CONST char *url )
392 {
393 	int	enclosed;
394 	const char * scheme;
395 
396 	if( url == NULL ) {
397 		return 0;
398 	}
399 
400 	if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
401 		return 0;
402 	}
403 
404 	return 1;
405 }
406 
407 
408 static const char*
skip_url_prefix(const char * url,int * enclosedp,const char ** scheme)409 skip_url_prefix(
410 	const char *url,
411 	int *enclosedp,
412 	const char **scheme )
413 {
414 	/*
415  	 * return non-zero if this looks like a LDAP URL; zero if not
416  	 * if non-zero returned, *urlp will be moved past "ldap://" part of URL
417  	 */
418 	const char *p;
419 
420 	if ( url == NULL ) {
421 		return( NULL );
422 	}
423 
424 	p = url;
425 
426 	/* skip leading '<' (if any) */
427 	if ( *p == '<' ) {
428 		*enclosedp = 1;
429 		++p;
430 	} else {
431 		*enclosedp = 0;
432 	}
433 
434 	/* skip leading "URL:" (if any) */
435 	if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) {
436 		p += LDAP_URL_URLCOLON_LEN;
437 	}
438 
439 	/* check for "ldap://" prefix */
440 	if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
441 		/* skip over "ldap://" prefix and return success */
442 		p += LDAP_URL_PREFIX_LEN;
443 		*scheme = "ldap";
444 		return( p );
445 	}
446 
447 	/* check for "ldaps://" prefix */
448 	if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
449 		/* skip over "ldaps://" prefix and return success */
450 		p += LDAPS_URL_PREFIX_LEN;
451 		*scheme = "ldaps";
452 		return( p );
453 	}
454 
455 	/* check for "ldapi://" prefix */
456 	if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) {
457 		/* skip over "ldapi://" prefix and return success */
458 		p += LDAPI_URL_PREFIX_LEN;
459 		*scheme = "ldapi";
460 		return( p );
461 	}
462 
463 #ifdef LDAP_CONNECTIONLESS
464 	/* check for "cldap://" prefix */
465 	if ( strncasecmp( p, LDAPC_URL_PREFIX, LDAPC_URL_PREFIX_LEN ) == 0 ) {
466 		/* skip over "cldap://" prefix and return success */
467 		p += LDAPC_URL_PREFIX_LEN;
468 		*scheme = "cldap";
469 		return( p );
470 	}
471 #endif
472 
473 	return( NULL );
474 }
475 
476 
str2scope(const char * p)477 static int str2scope( const char *p )
478 {
479 	if ( strcasecmp( p, "one" ) == 0 ) {
480 		return LDAP_SCOPE_ONELEVEL;
481 
482 	} else if ( strcasecmp( p, "onetree" ) == 0 ) {
483 		return LDAP_SCOPE_ONELEVEL;
484 
485 	} else if ( strcasecmp( p, "base" ) == 0 ) {
486 		return LDAP_SCOPE_BASE;
487 
488 	} else if ( strcasecmp( p, "sub" ) == 0 ) {
489 		return LDAP_SCOPE_SUBTREE;
490 
491 	} else if ( strcasecmp( p, "subtree" ) == 0 ) {
492 		return LDAP_SCOPE_SUBTREE;
493 	}
494 
495 	return( -1 );
496 }
497 
498 
499 int
ldap_url_parse_ext(LDAP_CONST char * url_in,LDAPURLDesc ** ludpp)500 ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
501 {
502 /*
503  *  Pick apart the pieces of an LDAP URL.
504  */
505 
506 	LDAPURLDesc	*ludp;
507 	char	*p, *q, *r;
508 	int		i, enclosed;
509 	const char *scheme = NULL;
510 	const char *url_tmp;
511 	char *url;
512 
513 	if( url_in == NULL || ludpp == NULL ) {
514 		return LDAP_URL_ERR_PARAM;
515 	}
516 
517 #ifndef LDAP_INT_IN_KERNEL
518 	/* Global options may not be created yet
519 	 * We can't test if the global options are initialized
520 	 * because a call to LDAP_INT_GLOBAL_OPT() will try to allocate
521 	 * the options and cause infinite recursion
522 	 */
523 #ifdef NEW_LOGGING
524 	LDAP_LOG ( OPERATION, ENTRY, "ldap_url_parse_ext(%s)\n", url_in, 0, 0 );
525 #else
526 	Debug( LDAP_DEBUG_TRACE, "ldap_url_parse_ext(%s)\n", url_in, 0, 0 );
527 #endif
528 #endif
529 
530 	*ludpp = NULL;	/* pessimistic */
531 
532 	url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
533 
534 	if ( url_tmp == NULL ) {
535 		return LDAP_URL_ERR_BADSCHEME;
536 	}
537 
538 	assert( scheme );
539 
540 	/* make working copy of the remainder of the URL */
541 	url = LDAP_STRDUP( url_tmp );
542 	if ( url == NULL ) {
543 		return LDAP_URL_ERR_MEM;
544 	}
545 
546 	if ( enclosed ) {
547 		p = &url[strlen(url)-1];
548 
549 		if( *p != '>' ) {
550 			LDAP_FREE( url );
551 			return LDAP_URL_ERR_BADENCLOSURE;
552 		}
553 
554 		*p = '\0';
555 	}
556 
557 	/* allocate return struct */
558 	ludp = (LDAPURLDesc *)LDAP_CALLOC( 1, sizeof( LDAPURLDesc ));
559 
560 	if ( ludp == NULL ) {
561 		LDAP_FREE( url );
562 		return LDAP_URL_ERR_MEM;
563 	}
564 
565 	ludp->lud_next = NULL;
566 	ludp->lud_host = NULL;
567 	ludp->lud_port = 0;
568 	ludp->lud_dn = NULL;
569 	ludp->lud_attrs = NULL;
570 	ludp->lud_filter = NULL;
571 	ludp->lud_scope = LDAP_SCOPE_DEFAULT;
572 	ludp->lud_filter = NULL;
573 	ludp->lud_exts = NULL;
574 
575 	ludp->lud_scheme = LDAP_STRDUP( scheme );
576 
577 	if ( ludp->lud_scheme == NULL ) {
578 		LDAP_FREE( url );
579 		ldap_free_urldesc( ludp );
580 		return LDAP_URL_ERR_MEM;
581 	}
582 
583 	/* scan forward for '/' that marks end of hostport and begin. of dn */
584 	p = strchr( url, '/' );
585 
586 	if( p != NULL ) {
587 		/* terminate hostport; point to start of dn */
588 		*p++ = '\0';
589 	}
590 
591 	/* IPv6 syntax with [ip address]:port */
592 	if ( *url == '[' ) {
593 		r = strchr( url, ']' );
594 		if ( r == NULL ) {
595 			LDAP_FREE( url );
596 			ldap_free_urldesc( ludp );
597 			return LDAP_URL_ERR_BADURL;
598 		}
599 		*r++ = '\0';
600 		q = strchr( r, ':' );
601 	} else {
602 		q = strchr( url, ':' );
603 	}
604 
605 	if ( q != NULL ) {
606 		*q++ = '\0';
607 		ldap_pvt_hex_unescape( q );
608 
609 		if( *q == '\0' ) {
610 			LDAP_FREE( url );
611 			ldap_free_urldesc( ludp );
612 			return LDAP_URL_ERR_BADURL;
613 		}
614 
615 		ludp->lud_port = atoi( q );
616 	}
617 
618 	ldap_pvt_hex_unescape( url );
619 
620 	/* If [ip address]:port syntax, url is [ip and we skip the [ */
621 	ludp->lud_host = LDAP_STRDUP( url + ( *url == '[' ) );
622 
623 	if( ludp->lud_host == NULL ) {
624 		LDAP_FREE( url );
625 		ldap_free_urldesc( ludp );
626 		return LDAP_URL_ERR_MEM;
627 	}
628 
629 	/*
630 	 * Kludge.  ldap://111.222.333.444:389??cn=abc,o=company
631 	 *
632 	 * On early Novell releases, search references/referrals were returned
633 	 * in this format, i.e., the dn was kind of in the scope position,
634 	 * but the required slash is missing. The whole thing is illegal syntax,
635 	 * but we need to account for it. Fortunately it can't be confused with
636 	 * anything real.
637 	 */
638 	if( (p == NULL) && (q != NULL) && ((q = strchr( q, '?')) != NULL)) {
639 		q++;
640 		/* ? immediately followed by question */
641 		if( *q == '?') {
642 			q++;
643 			if( *q != '\0' ) {
644 				/* parse dn part */
645 				ldap_pvt_hex_unescape( q );
646 				ludp->lud_dn = LDAP_STRDUP( q );
647 			} else {
648 				ludp->lud_dn = LDAP_STRDUP( "" );
649 			}
650 
651 			if( ludp->lud_dn == NULL ) {
652 				LDAP_FREE( url );
653 				ldap_free_urldesc( ludp );
654 				return LDAP_URL_ERR_MEM;
655 			}
656 		}
657 	}
658 
659 	if( p == NULL ) {
660 		LDAP_FREE( url );
661 		*ludpp = ludp;
662 		return LDAP_URL_SUCCESS;
663 	}
664 
665 	/* scan forward for '?' that may marks end of dn */
666 	q = strchr( p, '?' );
667 
668 	if( q != NULL ) {
669 		/* terminate dn part */
670 		*q++ = '\0';
671 	}
672 
673 	if( *p != '\0' ) {
674 		/* parse dn part */
675 		ldap_pvt_hex_unescape( p );
676 		ludp->lud_dn = LDAP_STRDUP( p );
677 	} else {
678 		ludp->lud_dn = LDAP_STRDUP( "" );
679 	}
680 
681 	if( ludp->lud_dn == NULL ) {
682 		LDAP_FREE( url );
683 		ldap_free_urldesc( ludp );
684 		return LDAP_URL_ERR_MEM;
685 	}
686 
687 	if( q == NULL ) {
688 		/* no more */
689 		LDAP_FREE( url );
690 		*ludpp = ludp;
691 		return LDAP_URL_SUCCESS;
692 	}
693 
694 	/* scan forward for '?' that may marks end of attributes */
695 	p = q;
696 	q = strchr( p, '?' );
697 
698 	if( q != NULL ) {
699 		/* terminate attributes part */
700 		*q++ = '\0';
701 	}
702 
703 	if( *p != '\0' ) {
704 		/* parse attributes */
705 		ldap_pvt_hex_unescape( p );
706 		ludp->lud_attrs = ldap_str2charray( p, "," );
707 
708 		if( ludp->lud_attrs == NULL ) {
709 			LDAP_FREE( url );
710 			ldap_free_urldesc( ludp );
711 			return LDAP_URL_ERR_BADATTRS;
712 		}
713 	}
714 
715 	if ( q == NULL ) {
716 		/* no more */
717 		LDAP_FREE( url );
718 		*ludpp = ludp;
719 		return LDAP_URL_SUCCESS;
720 	}
721 
722 	/* scan forward for '?' that may marks end of scope */
723 	p = q;
724 	q = strchr( p, '?' );
725 
726 	if( q != NULL ) {
727 		/* terminate the scope part */
728 		*q++ = '\0';
729 	}
730 
731 	if( *p != '\0' ) {
732 		/* parse the scope */
733 		ldap_pvt_hex_unescape( p );
734 		ludp->lud_scope = str2scope( p );
735 
736 		if( ludp->lud_scope == -1 ) {
737 			LDAP_FREE( url );
738 			ldap_free_urldesc( ludp );
739 			return LDAP_URL_ERR_BADSCOPE;
740 		}
741 	}
742 
743 	if ( q == NULL ) {
744 		/* no more */
745 		LDAP_FREE( url );
746 		*ludpp = ludp;
747 		return LDAP_URL_SUCCESS;
748 	}
749 
750 	/* scan forward for '?' that may marks end of filter */
751 	p = q;
752 	q = strchr( p, '?' );
753 
754 	if( q != NULL ) {
755 		/* terminate the filter part */
756 		*q++ = '\0';
757 	}
758 
759 	if( *p != '\0' ) {
760 		/* parse the filter */
761 		ldap_pvt_hex_unescape( p );
762 
763 		if( ! *p ) {
764 			/* missing filter */
765 			LDAP_FREE( url );
766 			ldap_free_urldesc( ludp );
767 			return LDAP_URL_ERR_BADFILTER;
768 		}
769 
770 		LDAP_FREE( ludp->lud_filter );
771 		ludp->lud_filter = LDAP_STRDUP( p );
772 
773 		if( ludp->lud_filter == NULL ) {
774 			LDAP_FREE( url );
775 			ldap_free_urldesc( ludp );
776 			return LDAP_URL_ERR_MEM;
777 		}
778 	}
779 
780 	if ( q == NULL ) {
781 		/* no more */
782 		LDAP_FREE( url );
783 		*ludpp = ludp;
784 		return LDAP_URL_SUCCESS;
785 	}
786 
787 	/* scan forward for '?' that may marks end of extensions */
788 	p = q;
789 	q = strchr( p, '?' );
790 
791 	if( q != NULL ) {
792 		/* extra '?' */
793 		LDAP_FREE( url );
794 		ldap_free_urldesc( ludp );
795 		return LDAP_URL_ERR_BADURL;
796 	}
797 
798 	/* parse the extensions */
799 	ludp->lud_exts = ldap_str2charray( p, "," );
800 
801 	if( ludp->lud_exts == NULL ) {
802 		LDAP_FREE( url );
803 		ldap_free_urldesc( ludp );
804 		return LDAP_URL_ERR_BADEXTS;
805 	}
806 
807 	for( i=0; ludp->lud_exts[i] != NULL; i++ ) {
808 		ldap_pvt_hex_unescape( ludp->lud_exts[i] );
809 
810 		if( *ludp->lud_exts[i] == '!' ) {
811 			/* count the number of critical extensions */
812 			ludp->lud_crit_exts++;
813 		}
814 	}
815 
816 	if( i == 0 ) {
817 		/* must have 1 or more */
818 		LDAP_FREE( url );
819 		ldap_free_urldesc( ludp );
820 		return LDAP_URL_ERR_BADEXTS;
821 	}
822 
823 	/* no more */
824 	*ludpp = ludp;
825 	LDAP_FREE( url );
826 	return LDAP_URL_SUCCESS;
827 }
828 
829 int
ldap_url_parse(LDAP_CONST char * url_in,LDAPURLDesc ** ludpp)830 ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
831 {
832 	int rc = ldap_url_parse_ext( url_in, ludpp );
833 
834 	if( rc != LDAP_URL_SUCCESS ) {
835 		return rc;
836 	}
837 
838 	if ((*ludpp)->lud_scope == LDAP_SCOPE_DEFAULT) {
839 		(*ludpp)->lud_scope = LDAP_SCOPE_BASE;
840 	}
841 
842 	if ((*ludpp)->lud_host != NULL && *(*ludpp)->lud_host == '\0') {
843 		LDAP_FREE( (*ludpp)->lud_host );
844 		(*ludpp)->lud_host = NULL;
845 	}
846 
847 	if ((*ludpp)->lud_port == 0) {
848 		if( strcmp((*ludpp)->lud_scheme, "ldap") == 0 ) {
849 			(*ludpp)->lud_port = LDAP_PORT;
850 #ifdef LDAP_CONNECTIONLESS
851 		} else if( strcmp((*ludpp)->lud_scheme, "cldap") == 0 ) {
852 			(*ludpp)->lud_port = LDAP_PORT;
853 #endif
854 		} else if( strcmp((*ludpp)->lud_scheme, "ldaps") == 0 ) {
855 			(*ludpp)->lud_port = LDAPS_PORT;
856 		}
857 	}
858 
859 	return rc;
860 }
861 
862 
863 void
ldap_free_urldesc(LDAPURLDesc * ludp)864 ldap_free_urldesc( LDAPURLDesc *ludp )
865 {
866 	if ( ludp == NULL ) {
867 		return;
868 	}
869 
870 	if ( ludp->lud_scheme != NULL ) {
871 		LDAP_FREE( ludp->lud_scheme );
872 	}
873 
874 	if ( ludp->lud_host != NULL ) {
875 		LDAP_FREE( ludp->lud_host );
876 	}
877 
878 	if ( ludp->lud_dn != NULL ) {
879 		LDAP_FREE( ludp->lud_dn );
880 	}
881 
882 	if ( ludp->lud_filter != NULL ) {
883 		LDAP_FREE( ludp->lud_filter);
884 	}
885 
886 	if ( ludp->lud_attrs != NULL ) {
887 		LDAP_VFREE( ludp->lud_attrs );
888 	}
889 
890 	if ( ludp->lud_exts != NULL ) {
891 		LDAP_VFREE( ludp->lud_exts );
892 	}
893 
894 	LDAP_FREE( ludp );
895 }
896 
897 
898 static int
ldap_int_unhex(int c)899 ldap_int_unhex( int c )
900 {
901 	return( c >= '0' && c <= '9' ? c - '0'
902 	    : c >= 'A' && c <= 'F' ? c - 'A' + 10
903 	    : c - 'a' + 10 );
904 }
905 
906 void
ldap_pvt_hex_unescape(char * s)907 ldap_pvt_hex_unescape( char *s )
908 {
909 	/*
910 	 * Remove URL hex escapes from s... done in place.  The basic concept for
911 	 * this routine is borrowed from the WWW library HTUnEscape() routine.
912 	 */
913 	char	*p;
914 
915 	for ( p = s; *s != '\0'; ++s ) {
916 		if ( *s == '%' ) {
917 			if ( *++s == '\0' ) {
918 				break;
919 			}
920 			*p = ldap_int_unhex( *s ) << 4;
921 			if ( *++s == '\0' ) {
922 				break;
923 			}
924 			*p++ += ldap_int_unhex( *s );
925 		} else {
926 			*p++ = *s;
927 		}
928 	}
929 
930 	*p = '\0';
931 }
932 
933