url.c 28.7 KB
Newer Older
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1
/* LIBLDAP url.c -- LDAP URL (RFC 4516) related routines */
2
/* $OpenLDAP$ */
3
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
4
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
5
 * Copyright 1998-2010 The OpenLDAP Foundation.
6
 * All rights reserved.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
7
 *
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* Portions Copyright (c) 1996 Regents of the University of Michigan.
 * All rights reserved.
 */


/*
Kurt Zeilenga's avatar
Kurt Zeilenga committed
22
 *  LDAP URLs look like this:
23
 *    ldap[is]://host[:port][/[dn[?[attributes][?[scope][?[filter][?exts]]]]]]
Kurt Zeilenga's avatar
Kurt Zeilenga committed
24
25
26
27
 *
 *  where:
 *   attributes is a comma separated list
 *   scope is one of these three strings:  base one sub (default=base)
Kurt Zeilenga's avatar
Kurt Zeilenga committed
28
 *   filter is an string-represented filter as in RFC 4515
Kurt Zeilenga's avatar
Kurt Zeilenga committed
29
 *
30
 *  e.g.,  ldap://host:port/dc=com?o,cn?base?(o=openldap)?extension
Kurt Zeilenga's avatar
Kurt Zeilenga committed
31
32
33
34
 *
 *  We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl>
 */

35
36
#include "portable.h"

Kurt Zeilenga's avatar
Kurt Zeilenga committed
37
#include <stdio.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
38
39

#include <ac/stdlib.h>
40
#include <ac/ctype.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
41

42
43
44
#include <ac/socket.h>
#include <ac/string.h>
#include <ac/time.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
45
46
47

#include "ldap-int.h"

48
/* local functions */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
49
50
51
static const char* skip_url_prefix LDAP_P((
	const char *url,
	int *enclosedp,
52
	const char **scheme ));
Kurt Zeilenga's avatar
Kurt Zeilenga committed
53

54
55
int ldap_pvt_url_scheme2proto( const char *scheme )
{
56
	assert( scheme != NULL );
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

	if( scheme == NULL ) {
		return -1;
	}

	if( strcmp("ldap", scheme) == 0 ) {
		return LDAP_PROTO_TCP;
	}

	if( strcmp("ldapi", scheme) == 0 ) {
		return LDAP_PROTO_IPC;
	}

	if( strcmp("ldaps", scheme) == 0 ) {
		return LDAP_PROTO_TCP;
	}
73
74
75
76
77
#ifdef LDAP_CONNECTIONLESS
	if( strcmp("cldap", scheme) == 0 ) {
		return LDAP_PROTO_UDP;
	}
#endif
78
79
80
81

	return -1;
}

82
83
int ldap_pvt_url_scheme_port( const char *scheme, int port )
{
84
	assert( scheme != NULL );
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109

	if( port ) return port;
	if( scheme == NULL ) return port;

	if( strcmp("ldap", scheme) == 0 ) {
		return LDAP_PORT;
	}

	if( strcmp("ldapi", scheme) == 0 ) {
		return -1;
	}

	if( strcmp("ldaps", scheme) == 0 ) {
		return LDAPS_PORT;
	}

#ifdef LDAP_CONNECTIONLESS
	if( strcmp("cldap", scheme) == 0 ) {
		return LDAP_PORT;
	}
#endif

	return -1;
}

Howard Chu's avatar
Howard Chu committed
110
111
int
ldap_pvt_url_scheme2tls( const char *scheme )
112
{
113
	assert( scheme != NULL );
114
115
116
117
118
119
120

	if( scheme == NULL ) {
		return -1;
	}

	return strcmp("ldaps", scheme) == 0;
}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
121
122

int
Kurt Zeilenga's avatar
Kurt Zeilenga committed
123
ldap_is_ldap_url( LDAP_CONST char *url )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
124
{
125
126
	int	enclosed;
	const char * scheme;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
127

Kurt Zeilenga's avatar
Kurt Zeilenga committed
128
129
130
131
	if( url == NULL ) {
		return 0;
	}

132
	if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
133
134
135
		return 0;
	}

136
	return 1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
137
138
}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
139
140
141
int
ldap_is_ldaps_url( LDAP_CONST char *url )
{
142
143
	int	enclosed;
	const char * scheme;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
144
145
146
147
148

	if( url == NULL ) {
		return 0;
	}

149
	if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
150
151
152
		return 0;
	}

153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
	return strcmp(scheme, "ldaps") == 0;
}

int
ldap_is_ldapi_url( LDAP_CONST char *url )
{
	int	enclosed;
	const char * scheme;

	if( url == NULL ) {
		return 0;
	}

	if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
		return 0;
	}

	return strcmp(scheme, "ldapi") == 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
171
}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
172

173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#ifdef LDAP_CONNECTIONLESS
int
ldap_is_ldapc_url( LDAP_CONST char *url )
{
	int	enclosed;
	const char * scheme;

	if( url == NULL ) {
		return 0;
	}

	if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
		return 0;
	}

	return strcmp(scheme, "cldap") == 0;
}
#endif

Kurt Zeilenga's avatar
Kurt Zeilenga committed
192
static const char*
Kurt Zeilenga's avatar
Kurt Zeilenga committed
193
194
195
skip_url_prefix(
	const char *url,
	int *enclosedp,
196
	const char **scheme )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
197
{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
198
199
200
201
	/*
 	 * return non-zero if this looks like a LDAP URL; zero if not
 	 * if non-zero returned, *urlp will be moved past "ldap://" part of URL
 	 */
202
	const char *p;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
203
204
205

	if ( url == NULL ) {
		return( NULL );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
206
207
	}

208
	p = url;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
209

Kurt Zeilenga's avatar
Kurt Zeilenga committed
210
	/* skip leading '<' (if any) */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
211
	if ( *p == '<' ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
212
		*enclosedp = 1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
213
		++p;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
214
215
216
217
218
	} else {
		*enclosedp = 0;
	}

	/* skip leading "URL:" (if any) */
219
	if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
220
		p += LDAP_URL_URLCOLON_LEN;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
221
222
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
223
224
225
226
	/* check for "ldap://" prefix */
	if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
		/* skip over "ldap://" prefix and return success */
		p += LDAP_URL_PREFIX_LEN;
227
		*scheme = "ldap";
Kurt Zeilenga's avatar
Kurt Zeilenga committed
228
229
230
231
232
233
234
		return( p );
	}

	/* check for "ldaps://" prefix */
	if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
		/* skip over "ldaps://" prefix and return success */
		p += LDAPS_URL_PREFIX_LEN;
235
		*scheme = "ldaps";
236
237
238
239
240
241
242
		return( p );
	}

	/* check for "ldapi://" prefix */
	if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) {
		/* skip over "ldapi://" prefix and return success */
		p += LDAPI_URL_PREFIX_LEN;
243
		*scheme = "ldapi";
Kurt Zeilenga's avatar
Kurt Zeilenga committed
244
		return( p );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
245
246
	}

247
248
249
250
251
252
253
254
255
256
#ifdef LDAP_CONNECTIONLESS
	/* check for "cldap://" prefix */
	if ( strncasecmp( p, LDAPC_URL_PREFIX, LDAPC_URL_PREFIX_LEN ) == 0 ) {
		/* skip over "cldap://" prefix and return success */
		p += LDAPC_URL_PREFIX_LEN;
		*scheme = "cldap";
		return( p );
	}
#endif

Kurt Zeilenga's avatar
Kurt Zeilenga committed
257
	return( NULL );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
258
259
}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
260
261
int
ldap_pvt_scope2bv( int scope, struct berval *bv )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
262
{
Pierangelo Masarati's avatar
Pierangelo Masarati committed
263
264
265
266
	switch ( scope ) {
	case LDAP_SCOPE_BASE:
		BER_BVSTR( bv, "base" );
		break;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
267

Pierangelo Masarati's avatar
Pierangelo Masarati committed
268
269
270
	case LDAP_SCOPE_ONELEVEL:
		BER_BVSTR( bv, "one" );
		break;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
271

Pierangelo Masarati's avatar
Pierangelo Masarati committed
272
273
274
	case LDAP_SCOPE_SUBTREE:
		BER_BVSTR( bv, "sub" );
		break;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
275

Pierangelo Masarati's avatar
Pierangelo Masarati committed
276
277
278
279
280
281
282
	case LDAP_SCOPE_SUBORDINATE:
		BER_BVSTR( bv, "subordinate" );
		break;

	default:
		return LDAP_OTHER;
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
283

Pierangelo Masarati's avatar
Pierangelo Masarati committed
284
285
	return LDAP_SUCCESS;
}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
286

Pierangelo Masarati's avatar
Pierangelo Masarati committed
287
288
289
290
const char *
ldap_pvt_scope2str( int scope )
{
	struct berval	bv;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
291

Pierangelo Masarati's avatar
Pierangelo Masarati committed
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
	if ( ldap_pvt_scope2bv( scope, &bv ) == LDAP_SUCCESS ) {
		return bv.bv_val;
	}

	return NULL;
}

int
ldap_pvt_bv2scope( struct berval *bv )
{
	static struct {
		struct berval	bv;
		int		scope;
	}	v[] = {
		{ BER_BVC( "one" ),		LDAP_SCOPE_ONELEVEL },
		{ BER_BVC( "onelevel" ),	LDAP_SCOPE_ONELEVEL },
		{ BER_BVC( "base" ),		LDAP_SCOPE_BASE },
		{ BER_BVC( "sub" ),		LDAP_SCOPE_SUBTREE },
		{ BER_BVC( "subtree" ),		LDAP_SCOPE_SUBTREE },
		{ BER_BVC( "subord" ),		LDAP_SCOPE_SUBORDINATE },
		{ BER_BVC( "subordinate" ),	LDAP_SCOPE_SUBORDINATE },
		{ BER_BVC( "children" ),	LDAP_SCOPE_SUBORDINATE },
		{ BER_BVNULL,			-1 }
	};
	int	i;

	for ( i = 0; v[ i ].scope != -1; i++ ) {
		if ( ber_bvstrcasecmp( bv, &v[ i ].bv ) == 0 ) {
			return v[ i ].scope;
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
322
323
324
325
326
	}

	return( -1 );
}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
327
328
329
330
331
332
333
334
335
336
int
ldap_pvt_str2scope( const char *p )
{
	struct berval	bv;

	ber_str2bv( p, 0, 0, &bv );

	return ldap_pvt_bv2scope( &bv );
}

337
338
339
340
341
342
343
344
static const char	hex[] = "0123456789ABCDEF";

#define URLESC_NONE	0x0000U
#define URLESC_COMMA	0x0001U
#define URLESC_SLASH	0x0002U

static int
hex_escape_len( const char *s, unsigned list )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
345
{
346
	int	len;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
347

348
349
350
	if ( s == NULL ) {
		return 0;
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
351

352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
	for ( len = 0; s[0]; s++ ) {
		switch ( s[0] ) {
		/* RFC 2396: reserved */
		case '?':
			len += 3;
			break;

		case ',':
			if ( list & URLESC_COMMA ) {
				len += 3;
			} else {
				len++;
			}
			break;

		case '/':
			if ( list & URLESC_SLASH ) {
				len += 3;
			} else {
				len++;
			}
			break;

		case ';':
		case ':':
		case '@':
		case '&':
		case '=':
		case '+':
		case '$':

		/* RFC 2396: unreserved mark */
		case '-':
		case '_':
		case '.':
		case '!':
		case '~':
		case '*':
		case '\'':
		case '(':
		case ')':
			len++;
			break;
			
		/* RFC 2396: unreserved alphanum */
		default:
398
			if ( !isalnum( (unsigned char) s[0] ) ) {
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
				len += 3;
			} else {
				len++;
			}
			break;
		}
	}

	return len;
}

static int
hex_escape( char *buf, int len, const char *s, unsigned list )
{
	int	i;
	int	pos;

	if ( s == NULL ) {
		return 0;
	}

	for ( pos = 0, i = 0; s[i] && pos < len; i++ ) {
		int	escape = 0;

		switch ( s[i] ) {
		/* RFC 2396: reserved */
		case '?':
			escape = 1;
			break;

		case ',':
			if ( list & URLESC_COMMA ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
431
				escape = 1;
432
433
			}
			break;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
434

435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
		case '/':
			if ( list & URLESC_SLASH ) {
				escape = 1;
			}
			break;

		case ';':
		case ':':
		case '@':
		case '&':
		case '=':
		case '+':
		case '$':

		/* RFC 2396: unreserved mark */
		case '-':
		case '_':
		case '.':
		case '!':
		case '~':
		case '*':
		case '\'':
		case '(':
		case ')':
			break;
			
		/* RFC 2396: unreserved alphanum */
		default:
463
			if ( !isalnum( (unsigned char) s[i] ) ) {
464
465
466
				escape = 1;
			}
			break;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
467
468
		}

469
		if ( escape ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
470
471
472
			buf[pos++] = '%';
			buf[pos++] = hex[ (s[i] >> 4) & 0x0f ];
			buf[pos++] = hex[ s[i] & 0x0f ];
473

Kurt Zeilenga's avatar
Kurt Zeilenga committed
474
475
476
477
478
		} else {
			buf[pos++] = s[i];
		}
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
479
	buf[pos] = '\0';
480

Kurt Zeilenga's avatar
Kurt Zeilenga committed
481
482
483
	return pos;
}

484
485
static int
hex_escape_len_list( char **s, unsigned flags )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
486
{
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
	int	len;
	int	i;

	if ( s == NULL ) {
		return 0;
	}

	len = 0;
	for ( i = 0; s[i] != NULL; i++ ) {
		if ( len ) {
			len++;
		}
		len += hex_escape_len( s[i], flags );
	}

	return len;
}

static int
hex_escape_list( char *buf, int len, char **s, unsigned flags )
{
	int	pos;
	int	i;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
510

511
512
513
	if ( s == NULL ) {
		return 0;
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
514
515

	pos = 0;
516
517
518
519
	for ( i = 0; s[i] != NULL; i++ ) {
		int	curlen;

		if ( pos ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
520
			buf[pos++] = ',';
521
			len--;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
522
		}
523
524
525
		curlen = hex_escape( &buf[pos], len, s[i], flags );
		len -= curlen;
		pos += curlen;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
526
527
528
529
530
	}

	return pos;
}

531
532
static int
desc2str_len( LDAPURLDesc *u )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
533
{
Pierangelo Masarati's avatar
Pierangelo Masarati committed
534
535
	int		sep = 0;
	int		len = 0;
536
	int		is_ipc = 0;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
537
	struct berval	scope;
538

539
	if ( u == NULL || u->lud_scheme == NULL ) {
540
541
542
		return -1;
	}

543
544
545
546
	if ( !strcmp( "ldapi", u->lud_scheme )) {
		is_ipc = 1;
	}

547
548
549
550
	if ( u->lud_exts ) {
		len += hex_escape_len_list( u->lud_exts, URLESC_COMMA );
		if ( !sep ) {
			sep = 5;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
551
552
553
		}
	}

554
	if ( u->lud_filter ) {
555
		len += hex_escape_len( u->lud_filter, URLESC_NONE );
556
557
558
		if ( !sep ) {
			sep = 4;
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
559
560
	}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
561
562
	if ( ldap_pvt_scope2bv( u->lud_scope, &scope ) == LDAP_SUCCESS ) {
		len += scope.bv_len;
563
564
565
		if ( !sep ) {
			sep = 3;
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
566
567
	}

568
	if ( u->lud_attrs ) {
569
		len += hex_escape_len_list( u->lud_attrs, URLESC_NONE );
570
571
		if ( !sep ) {
			sep = 2;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
572
		}
573
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
574

575
576
577
578
579
	if ( u->lud_dn && u->lud_dn[0] ) {
		len += hex_escape_len( u->lud_dn, URLESC_NONE );
		if ( !sep ) {
			sep = 1;
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
580
581
	};

582
583
584
	len += sep;

	if ( u->lud_port ) {
585
586
587
		unsigned p = u->lud_port;
		if ( p > 65535 )
			return -1;
588

589
590
		len += (p > 999 ? 5 + (p > 9999) : p > 99 ? 4 : 2 + (p > 9));
	}
591

592
	if ( u->lud_host && u->lud_host[0] ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
593
		char *ptr;
594
		len += hex_escape_len( u->lud_host, URLESC_SLASH );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
595
596
597
		if ( !is_ipc && ( ptr = strchr( u->lud_host, ':' ))) {
			if ( strchr( ptr+1, ':' ))
				len += 2;	/* IPv6, [] */
598
599
600
601
602
603
604
605
		}
	}

	len += strlen( u->lud_scheme ) + STRLENOF( "://" );

	return len;
}

606
static int
607
608
desc2str( LDAPURLDesc *u, char *s, int len )
{
Pierangelo Masarati's avatar
Pierangelo Masarati committed
609
610
611
	int		i;
	int		sep = 0;
	int		sofar = 0;
612
613
	int		is_v6 = 0;
	int		is_ipc = 0;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
614
	struct berval	scope = BER_BVNULL;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
615
	char		*ptr;
616
617
618

	if ( u == NULL ) {
		return -1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
619
620
	}

621
622
	if ( s == NULL ) {
		return -1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
623
624
	}

625
626
627
628
	if ( u->lud_scheme && !strcmp( "ldapi", u->lud_scheme )) {
		is_ipc = 1;
	}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
629
	ldap_pvt_scope2bv( u->lud_scope, &scope );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
630

631
632
633
634
	if ( u->lud_exts ) {
		sep = 5;
	} else if ( u->lud_filter ) {
		sep = 4;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
635
	} else if ( !BER_BVISEMPTY( &scope ) ) {
636
637
638
639
640
641
		sep = 3;
	} else if ( u->lud_attrs ) {
		sep = 2;
	} else if ( u->lud_dn && u->lud_dn[0] ) {
		sep = 1;
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
642

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
643
644
645
	if ( !is_ipc && u->lud_host && ( ptr = strchr( u->lud_host, ':' ))) {
		if ( strchr( ptr+1, ':' ))
			is_v6 = 1;
646
647
	}

648
	if ( u->lud_port ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
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 ? "]" : "",
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
653
654
				u->lud_port );
		len -= sofar;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
655
656

	} else {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
657
658
		sofar = sprintf( s, "%s://", u->lud_scheme );
		len -= sofar;
659
		if ( u->lud_host && u->lud_host[0] ) {
660
661
662
663
			if ( is_v6 ) {
				s[sofar++] = '[';
				len--;
			}
664
665
666
			i = hex_escape( &s[sofar], len, u->lud_host, URLESC_SLASH );
			sofar += i;
			len -= i;
667
668
669
670
			if ( is_v6 ) {
				s[sofar++] = ']';
				len--;
			}
671
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
672
	}
673
674
675
676
677
678
679

	assert( len >= 0 );

	if ( sep < 1 ) {
		goto done;
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
680
	s[sofar++] = '/';
681
682
683
	len--;

	assert( len >= 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
684

685
686
687
688
	if ( u->lud_dn && u->lud_dn[0] ) {
		i = hex_escape( &s[sofar], len, u->lud_dn, URLESC_NONE );
		sofar += i;
		len -= i;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
689

690
691
692
693
694
695
		assert( len >= 0 );
	}

	if ( sep < 2 ) {
		goto done;
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
696
	s[sofar++] = '?';
697
	len--;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
698

699
	assert( len >= 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
700

701
702
703
704
705
706
707
708
709
	i = hex_escape_list( &s[sofar], len, u->lud_attrs, URLESC_NONE );
	sofar += i;
	len -= i;

	assert( len >= 0 );

	if ( sep < 3 ) {
		goto done;
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
710
	s[sofar++] = '?';
711
	len--;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
712

713
714
	assert( len >= 0 );

Pierangelo Masarati's avatar
Pierangelo Masarati committed
715
716
717
718
	if ( !BER_BVISNULL( &scope ) ) {
		strcpy( &s[sofar], scope.bv_val );
		sofar += scope.bv_len;
		len -= scope.bv_len;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
719
720
	}

721
722
723
724
725
	assert( len >= 0 );

	if ( sep < 4 ) {
		goto done;
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
726
	s[sofar++] = '?';
727
728
729
	len--;

	assert( len >= 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
730

731
732
733
	i = hex_escape( &s[sofar], len, u->lud_filter, URLESC_NONE );
	sofar += i;
	len -= i;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
734

735
736
737
738
739
	assert( len >= 0 );

	if ( sep < 5 ) {
		goto done;
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
740
	s[sofar++] = '?';
741
742
743
744
745
746
747
	len--;

	assert( len >= 0 );

	i = hex_escape_list( &s[sofar], len, u->lud_exts, URLESC_COMMA );
	sofar += i;
	len -= i;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
748

749
	assert( len >= 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
750
751

done:
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
	if ( len < 0 ) {
		return -1;
	}

	return sofar;
}

char *
ldap_url_desc2str( LDAPURLDesc *u )
{
	int	len;
	char	*s;

	if ( u == NULL ) {
		return NULL;
	}

	len = desc2str_len( u );
	if ( len < 0 ) {
		return NULL;
	}
	
	/* allocate enough to hex escape everything -- overkill */
	s = LDAP_MALLOC( len + 1 );

	if ( s == NULL ) {
		return NULL;
	}

	if ( desc2str( u, s, len ) != len ) {
		LDAP_FREE( s );
		return NULL;
	}

	s[len] = '\0';

Kurt Zeilenga's avatar
Kurt Zeilenga committed
788
789
	return s;
}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
790
791

int
792
ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp, unsigned flags )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
793
794
795
796
797
798
{
/*
 *  Pick apart the pieces of an LDAP URL.
 */

	LDAPURLDesc	*ludp;
799
	char	*p, *q, *r;
800
	int		i, enclosed, proto, is_v6 = 0;
801
	const char *scheme = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
802
803
	const char *url_tmp;
	char *url;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
804

805
806
	int	check_dn = 1;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
807
	if( url_in == NULL || ludpp == NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
808
809
810
		return LDAP_URL_ERR_PARAM;
	}

811
#ifndef LDAP_INT_IN_KERNEL
Kurt Zeilenga's avatar
Kurt Zeilenga committed
812
813
814
815
816
	/* Global options may not be created yet
	 * We can't test if the global options are initialized
	 * because a call to LDAP_INT_GLOBAL_OPT() will try to allocate
	 * the options and cause infinite recursion
	 */
817
	Debug( LDAP_DEBUG_TRACE, "ldap_url_parse_ext(%s)\n", url_in, 0, 0 );
818
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
819
820
821

	*ludpp = NULL;	/* pessimistic */

822
	url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
823
824

	if ( url_tmp == NULL ) {
825
		return LDAP_URL_ERR_BADSCHEME;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
826
827
	}

828
	assert( scheme != NULL );
829

830
831
832
833
834
	proto = ldap_pvt_url_scheme2proto( scheme );
	if ( proto == -1 ) {
		return LDAP_URL_ERR_BADSCHEME;
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
835
	/* make working copy of the remainder of the URL */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
836
837
838
	url = LDAP_STRDUP( url_tmp );
	if ( url == NULL ) {
		return LDAP_URL_ERR_MEM;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
839
840
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
841
842
	if ( enclosed ) {
		p = &url[strlen(url)-1];
Kurt Zeilenga's avatar
Kurt Zeilenga committed
843

Kurt Zeilenga's avatar
Kurt Zeilenga committed
844
845
846
847
		if( *p != '>' ) {
			LDAP_FREE( url );
			return LDAP_URL_ERR_BADENCLOSURE;
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
848

Kurt Zeilenga's avatar
Kurt Zeilenga committed
849
850
851
		*p = '\0';
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
852
853
854
855
856
857
858
859
	/* allocate return struct */
	ludp = (LDAPURLDesc *)LDAP_CALLOC( 1, sizeof( LDAPURLDesc ));

	if ( ludp == NULL ) {
		LDAP_FREE( url );
		return LDAP_URL_ERR_MEM;
	}

860
	ludp->lud_next = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
861
	ludp->lud_host = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
862
	ludp->lud_port = 0;
863
864
	ludp->lud_dn = NULL;
	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;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
868

869
	ludp->lud_scheme = LDAP_STRDUP( scheme );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
870

871
	if ( ludp->lud_scheme == NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
872
873
874
875
		LDAP_FREE( url );
		ldap_free_urldesc( ludp );
		return LDAP_URL_ERR_MEM;
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
876
877

	/* scan forward for '/' that marks end of hostport and begin. of dn */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
878
	p = strchr( url, '/' );
879
	q = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
880

881
882
883
	if( p != NULL ) {
		/* terminate hostport; point to start of dn */
		*p++ = '\0';
884
885
886
887
888
889
890
891
	} else {
		/* check for Novell kludge, see below */
		p = strchr( url, '?' );
		if ( p ) {
			*p++ = '\0';
			q = p;
			p = NULL;
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
892
893
	}

894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
	if ( proto != LDAP_PROTO_IPC ) {
		/* IPv6 syntax with [ip address]:port */
		if ( *url == '[' ) {
			r = strchr( url, ']' );
			if ( r == NULL ) {
				LDAP_FREE( url );
				ldap_free_urldesc( ludp );
				return LDAP_URL_ERR_BADURL;
			}
			*r++ = '\0';
			q = strchr( r, ':' );
			if ( q && q != r ) {
				LDAP_FREE( url );
				ldap_free_urldesc( ludp );
				return LDAP_URL_ERR_BADURL;
			}
			is_v6 = 1;
		} else {
			q = strchr( url, ':' );
913
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
914

915
916
		if ( q != NULL ) {
			char	*next;
917

918
919
			*q++ = '\0';
			ldap_pvt_hex_unescape( q );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
920

921
922
923
924
925
			if( *q == '\0' ) {
				LDAP_FREE( url );
				ldap_free_urldesc( ludp );
				return LDAP_URL_ERR_BADURL;
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
926

927
928
929
930
931
932
933
934
935
936
937
938
939
940
			ludp->lud_port = strtol( q, &next, 10 );
			if ( next == q || next[0] != '\0' ) {
				LDAP_FREE( url );
				ldap_free_urldesc( ludp );
				return LDAP_URL_ERR_BADURL;
			}
			/* check for Novell kludge */
			if ( !p ) {
				if ( *next != '\0' ) {
					q = &next[1];
				} else {
					q = NULL;
				}
			}
941
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
942

943
944
945
946
947
948
		if ( ( flags & LDAP_PVT_URL_PARSE_DEF_PORT ) && ludp->lud_port == 0 ) {
			if ( strcmp( ludp->lud_scheme, "ldaps" ) == 0 ) {
				ludp->lud_port = LDAPS_PORT;
			} else {
				ludp->lud_port = LDAP_PORT;
			}
949
950
951
		}
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
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 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
956
957
958
959
960
961
962

	if( ludp->lud_host == NULL ) {
		LDAP_FREE( url );
		ldap_free_urldesc( ludp );
		return LDAP_URL_ERR_MEM;
	}

963
964
965
966
967
968
969
970
	if ( ( flags & LDAP_PVT_URL_PARSE_NOEMPTY_HOST )
		&& ludp->lud_host != NULL
		&& *ludp->lud_host == '\0' )
	{
		LDAP_FREE( ludp->lud_host );
		ludp->lud_host = NULL;
	}

971
	/*
972
	 * Kludge.  ldap://111.222.333.444:389??cn=abc,o=company
973
974
975
976
977
978
979
	 *
	 * On early Novell releases, search references/referrals were returned
	 * in this format, i.e., the dn was kind of in the scope position,
	 * but the required slash is missing. The whole thing is illegal syntax,
	 * but we need to account for it. Fortunately it can't be confused with
	 * anything real.
	 */
980
	if( (p == NULL) && (q != NULL) && (*q == '?') ) {
981
		/* ? immediately followed by question */
982
983
984
985
986
		q++;
		if( *q != '\0' ) {
			/* parse dn part */
			ldap_pvt_hex_unescape( q );
			ludp->lud_dn = LDAP_STRDUP( q );
987

988
989
		} else if ( !( flags & LDAP_PVT_URL_PARSE_NOEMPTY_DN ) ) {
			ludp->lud_dn = LDAP_STRDUP( "" );
990

991
992
993
		} else {
			check_dn = 0;
		}
994

995
996
997
998
		if ( check_dn && ludp->lud_dn == NULL ) {
			LDAP_FREE( url );
			ldap_free_urldesc( ludp );
			return LDAP_URL_ERR_MEM;
999
1000
		}
	}
For faster browsing, not all history is shown. View entire blame