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