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