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