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