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