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