xref: /netbsd-src/external/bsd/openldap/dist/libraries/libldap/url.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
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