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