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