1 /* $OpenLDAP: pkg/ldap/libraries/libldap/request.c,v 1.125.2.7 2008/02/11 23:26:41 kurt Exp $ */ 2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 3 * 4 * Copyright 1998-2008 The OpenLDAP Foundation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted only as authorized by the OpenLDAP 9 * Public License. 10 * 11 * A copy of this license is available in the file LICENSE in the 12 * top-level directory of the distribution or, alternatively, at 13 * <http://www.OpenLDAP.org/license.html>. 14 */ 15 /* Portions Copyright (c) 1995 Regents of the University of Michigan. 16 * All rights reserved. 17 */ 18 /* This notice applies to changes, created by or for Novell, Inc., 19 * to preexisting works for which notices appear elsewhere in this file. 20 * 21 * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved. 22 * 23 * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES. 24 * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION 25 * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT 26 * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE 27 * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS 28 * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC 29 * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE 30 * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 31 *--- 32 * Modification to OpenLDAP source by Novell, Inc. 33 * April 2000 sfs Added code to chase V3 referrals 34 * request.c - sending of ldap requests; handling of referrals 35 *--- 36 * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License 37 * can be found in the file "build/LICENSE-2.0.1" in this distribution 38 * of OpenLDAP Software. 39 */ 40 41 #include "portable.h" 42 43 #include <stdio.h> 44 45 #include <ac/stdlib.h> 46 47 #include <ac/errno.h> 48 #include <ac/socket.h> 49 #include <ac/string.h> 50 #include <ac/time.h> 51 #include <ac/unistd.h> 52 53 #include "ldap-int.h" 54 #include "lber.h" 55 56 static LDAPConn *find_connection LDAP_P(( LDAP *ld, LDAPURLDesc *srv, int any )); 57 static void use_connection LDAP_P(( LDAP *ld, LDAPConn *lc )); 58 static void ldap_free_request_int LDAP_P(( LDAP *ld, LDAPRequest *lr )); 59 60 static BerElement * 61 re_encode_request( LDAP *ld, 62 BerElement *origber, 63 ber_int_t msgid, 64 int sref, 65 LDAPURLDesc *srv, 66 int *type ); 67 68 BerElement * 69 ldap_alloc_ber_with_options( LDAP *ld ) 70 { 71 BerElement *ber; 72 73 ber = ber_alloc_t( ld->ld_lberoptions ); 74 if ( ber == NULL ) { 75 ld->ld_errno = LDAP_NO_MEMORY; 76 } 77 78 return( ber ); 79 } 80 81 82 void 83 ldap_set_ber_options( LDAP *ld, BerElement *ber ) 84 { 85 ber->ber_options = ld->ld_lberoptions; 86 } 87 88 89 ber_int_t 90 ldap_send_initial_request( 91 LDAP *ld, 92 ber_tag_t msgtype, 93 const char *dn, 94 BerElement *ber, 95 ber_int_t msgid) 96 { 97 int rc = 1; 98 99 Debug( LDAP_DEBUG_TRACE, "ldap_send_initial_request\n", 0, 0, 0 ); 100 101 #ifdef LDAP_R_COMPILE 102 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex ); 103 #endif 104 if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, NULL ) == -1 ) { 105 /* not connected yet */ 106 rc = ldap_open_defconn( ld ); 107 108 } 109 #ifdef LDAP_R_COMPILE 110 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex ); 111 #endif 112 if( rc < 0 ) { 113 ber_free( ber, 1 ); 114 return( -1 ); 115 } else if ( rc == 0 ) { 116 Debug( LDAP_DEBUG_TRACE, 117 "ldap_open_defconn: successful\n", 118 0, 0, 0 ); 119 } 120 121 #ifdef LDAP_CONNECTIONLESS 122 if (LDAP_IS_UDP(ld)) { 123 if (msgtype == LDAP_REQ_BIND) { 124 if (ld->ld_options.ldo_cldapdn) 125 ldap_memfree(ld->ld_options.ldo_cldapdn); 126 ld->ld_options.ldo_cldapdn = ldap_strdup(dn); 127 return 0; 128 } 129 if (msgtype != LDAP_REQ_ABANDON && msgtype != LDAP_REQ_SEARCH) 130 return LDAP_PARAM_ERROR; 131 } 132 #endif 133 #ifdef LDAP_R_COMPILE 134 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex ); 135 #endif 136 rc = ldap_send_server_request( ld, ber, msgid, NULL, 137 NULL, NULL, NULL ); 138 #ifdef LDAP_R_COMPILE 139 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex ); 140 #endif 141 return(rc); 142 } 143 144 145 int 146 ldap_int_flush_request( 147 LDAP *ld, 148 LDAPRequest *lr ) 149 { 150 LDAPConn *lc = lr->lr_conn; 151 152 if ( ber_flush2( lc->lconn_sb, lr->lr_ber, LBER_FLUSH_FREE_NEVER ) != 0 ) { 153 if ( sock_errno() == EAGAIN ) { 154 /* need to continue write later */ 155 lr->lr_status = LDAP_REQST_WRITING; 156 ldap_mark_select_write( ld, lc->lconn_sb ); 157 ld->ld_errno = LDAP_BUSY; 158 return -2; 159 } else { 160 ld->ld_errno = LDAP_SERVER_DOWN; 161 ldap_free_request( ld, lr ); 162 ldap_free_connection( ld, lc, 0, 0 ); 163 return( -1 ); 164 } 165 } else { 166 if ( lr->lr_parent == NULL ) { 167 lr->lr_ber->ber_end = lr->lr_ber->ber_ptr; 168 lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf; 169 } 170 lr->lr_status = LDAP_REQST_INPROGRESS; 171 172 /* sent -- waiting for a response */ 173 ldap_mark_select_read( ld, lc->lconn_sb ); 174 } 175 return 0; 176 } 177 178 int 179 ldap_send_server_request( 180 LDAP *ld, 181 BerElement *ber, 182 ber_int_t msgid, 183 LDAPRequest *parentreq, 184 LDAPURLDesc **srvlist, 185 LDAPConn *lc, 186 LDAPreqinfo *bind ) 187 { 188 LDAPRequest *lr; 189 int incparent, rc; 190 191 Debug( LDAP_DEBUG_TRACE, "ldap_send_server_request\n", 0, 0, 0 ); 192 193 incparent = 0; 194 ld->ld_errno = LDAP_SUCCESS; /* optimistic */ 195 196 if ( lc == NULL ) { 197 if ( srvlist == NULL ) { 198 lc = ld->ld_defconn; 199 } else { 200 lc = find_connection( ld, *srvlist, 1 ); 201 if ( lc == NULL ) { 202 if ( (bind != NULL) && (parentreq != NULL) ) { 203 /* Remember the bind in the parent */ 204 incparent = 1; 205 ++parentreq->lr_outrefcnt; 206 } 207 lc = ldap_new_connection( ld, srvlist, 0, 1, bind ); 208 } 209 } 210 } 211 212 /* async connect... */ 213 if ( lc != NULL && lc->lconn_status == LDAP_CONNST_CONNECTING ) { 214 ber_socket_t sd = AC_SOCKET_ERROR; 215 struct timeval tv = { 0 }; 216 217 ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sd ); 218 219 /* poll ... */ 220 switch ( ldap_int_poll( ld, sd, &tv ) ) { 221 case 0: 222 /* go on! */ 223 lc->lconn_status = LDAP_CONNST_CONNECTED; 224 break; 225 226 case -2: 227 /* async only occurs if a network timeout is set */ 228 229 /* honor network timeout */ 230 if ( time( NULL ) - lc->lconn_created <= ld->ld_options.ldo_tm_net.tv_sec ) 231 { 232 /* caller will have to call again */ 233 ld->ld_errno = LDAP_X_CONNECTING; 234 } 235 /* fallthru */ 236 237 default: 238 /* error */ 239 break; 240 } 241 } 242 243 if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) { 244 if ( ld->ld_errno == LDAP_SUCCESS ) { 245 ld->ld_errno = LDAP_SERVER_DOWN; 246 } 247 248 ber_free( ber, 1 ); 249 if ( incparent ) { 250 /* Forget about the bind */ 251 --parentreq->lr_outrefcnt; 252 } 253 return( -1 ); 254 } 255 256 use_connection( ld, lc ); 257 258 #ifdef LDAP_CONNECTIONLESS 259 if ( LDAP_IS_UDP( ld )) { 260 BerElement tmpber = *ber; 261 ber_rewind( &tmpber ); 262 rc = ber_write( &tmpber, ld->ld_options.ldo_peer, 263 sizeof( struct sockaddr ), 0 ); 264 if ( rc == -1 ) { 265 ld->ld_errno = LDAP_ENCODING_ERROR; 266 return rc; 267 } 268 } 269 #endif 270 271 /* If we still have an incomplete write, try to finish it before 272 * dealing with the new request. If we don't finish here, return 273 * LDAP_BUSY and let the caller retry later. We only allow a single 274 * request to be in WRITING state. 275 */ 276 rc = 0; 277 if ( ld->ld_requests && 278 ld->ld_requests->lr_status == LDAP_REQST_WRITING && 279 ldap_int_flush_request( ld, ld->ld_requests ) < 0 ) 280 { 281 rc = -1; 282 } 283 if ( rc ) return rc; 284 285 lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ) ); 286 if ( lr == NULL ) { 287 ld->ld_errno = LDAP_NO_MEMORY; 288 ldap_free_connection( ld, lc, 0, 0 ); 289 ber_free( ber, 1 ); 290 if ( incparent ) { 291 /* Forget about the bind */ 292 --parentreq->lr_outrefcnt; 293 } 294 return( -1 ); 295 } 296 lr->lr_msgid = msgid; 297 lr->lr_status = LDAP_REQST_INPROGRESS; 298 lr->lr_res_errno = LDAP_SUCCESS; /* optimistic */ 299 lr->lr_ber = ber; 300 lr->lr_conn = lc; 301 if ( parentreq != NULL ) { /* sub-request */ 302 if ( !incparent ) { 303 /* Increment if we didn't do it before the bind */ 304 ++parentreq->lr_outrefcnt; 305 } 306 lr->lr_origid = parentreq->lr_origid; 307 lr->lr_parentcnt = ++parentreq->lr_parentcnt; 308 lr->lr_parent = parentreq; 309 lr->lr_refnext = parentreq->lr_child; 310 parentreq->lr_child = lr; 311 } else { /* original request */ 312 lr->lr_origid = lr->lr_msgid; 313 } 314 315 /* Extract requestDN for future reference */ 316 { 317 BerElement tmpber = *ber; 318 ber_int_t bint; 319 ber_tag_t tag, rtag; 320 321 ber_reset( &tmpber, 1 ); 322 rtag = ber_scanf( &tmpber, "{it", /*}*/ &bint, &tag ); 323 switch ( tag ) { 324 case LDAP_REQ_BIND: 325 rtag = ber_scanf( &tmpber, "{i" /*}*/, &bint ); 326 break; 327 case LDAP_REQ_DELETE: 328 break; 329 default: 330 rtag = ber_scanf( &tmpber, "{" /*}*/ ); 331 case LDAP_REQ_ABANDON: 332 break; 333 } 334 if ( tag != LDAP_REQ_ABANDON ) { 335 ber_skip_tag( &tmpber, &lr->lr_dn.bv_len ); 336 lr->lr_dn.bv_val = tmpber.ber_ptr; 337 } 338 } 339 340 lr->lr_prev = NULL; 341 lr->lr_next = ld->ld_requests; 342 if ( lr->lr_next != NULL ) { 343 lr->lr_next->lr_prev = lr; 344 } 345 ld->ld_requests = lr; 346 347 ld->ld_errno = LDAP_SUCCESS; 348 if ( ldap_int_flush_request( ld, lr ) == -1 ) { 349 msgid = -1; 350 } 351 352 return( msgid ); 353 } 354 355 LDAPConn * 356 ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb, 357 int connect, LDAPreqinfo *bind ) 358 { 359 LDAPConn *lc; 360 int async = 0; 361 362 Debug( LDAP_DEBUG_TRACE, "ldap_new_connection %d %d %d\n", 363 use_ldsb, connect, (bind != NULL) ); 364 /* 365 * make a new LDAP server connection 366 * XXX open connection synchronously for now 367 */ 368 lc = (LDAPConn *)LDAP_CALLOC( 1, sizeof( LDAPConn ) ); 369 if ( lc == NULL ) { 370 ld->ld_errno = LDAP_NO_MEMORY; 371 return( NULL ); 372 } 373 374 if ( use_ldsb ) { 375 assert( ld->ld_sb != NULL ); 376 lc->lconn_sb = ld->ld_sb; 377 378 } else { 379 lc->lconn_sb = ber_sockbuf_alloc(); 380 if ( lc->lconn_sb == NULL ) { 381 LDAP_FREE( (char *)lc ); 382 ld->ld_errno = LDAP_NO_MEMORY; 383 return( NULL ); 384 } 385 } 386 387 if ( connect ) { 388 LDAPURLDesc **srvp, *srv = NULL; 389 390 async = LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_CONNECT_ASYNC ); 391 392 for ( srvp = srvlist; *srvp != NULL; srvp = &(*srvp)->lud_next ) { 393 int rc; 394 395 rc = ldap_int_open_connection( ld, lc, *srvp, async ); 396 if ( rc != -1 ) { 397 srv = *srvp; 398 399 if ( ld->ld_urllist_proc && ( !async || rc != -2 ) ) { 400 ld->ld_urllist_proc( ld, srvlist, srvp, ld->ld_urllist_params ); 401 } 402 403 break; 404 } 405 } 406 407 if ( srv == NULL ) { 408 if ( !use_ldsb ) { 409 ber_sockbuf_free( lc->lconn_sb ); 410 } 411 LDAP_FREE( (char *)lc ); 412 ld->ld_errno = LDAP_SERVER_DOWN; 413 return( NULL ); 414 } 415 416 lc->lconn_server = ldap_url_dup( srv ); 417 } 418 419 lc->lconn_status = async ? LDAP_CONNST_CONNECTING : LDAP_CONNST_CONNECTED; 420 #ifdef LDAP_R_COMPILE 421 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex ); 422 #endif 423 lc->lconn_next = ld->ld_conns; 424 ld->ld_conns = lc; 425 #ifdef LDAP_R_COMPILE 426 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex ); 427 #endif 428 429 if ( bind != NULL ) { 430 int err = 0; 431 LDAPConn *savedefconn; 432 433 /* Set flag to prevent additional referrals 434 * from being processed on this 435 * connection until the bind has completed 436 */ 437 lc->lconn_rebind_inprogress = 1; 438 /* V3 rebind function */ 439 if ( ld->ld_rebind_proc != NULL) { 440 LDAPURLDesc *srvfunc; 441 442 srvfunc = ldap_url_dup( *srvlist ); 443 if ( srvfunc == NULL ) { 444 ld->ld_errno = LDAP_NO_MEMORY; 445 err = -1; 446 } else { 447 savedefconn = ld->ld_defconn; 448 ++lc->lconn_refcnt; /* avoid premature free */ 449 ld->ld_defconn = lc; 450 451 Debug( LDAP_DEBUG_TRACE, "Call application rebind_proc\n", 0, 0, 0); 452 #ifdef LDAP_R_COMPILE 453 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex ); 454 ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex ); 455 #endif 456 err = (*ld->ld_rebind_proc)( ld, 457 bind->ri_url, bind->ri_request, bind->ri_msgid, 458 ld->ld_rebind_params ); 459 #ifdef LDAP_R_COMPILE 460 ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex ); 461 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex ); 462 #endif 463 464 ld->ld_defconn = savedefconn; 465 --lc->lconn_refcnt; 466 467 if ( err != 0 ) { 468 err = -1; 469 ldap_free_connection( ld, lc, 1, 0 ); 470 lc = NULL; 471 } 472 ldap_free_urldesc( srvfunc ); 473 } 474 475 } else { 476 int msgid, rc; 477 struct berval passwd = BER_BVNULL; 478 479 savedefconn = ld->ld_defconn; 480 ++lc->lconn_refcnt; /* avoid premature free */ 481 ld->ld_defconn = lc; 482 483 Debug( LDAP_DEBUG_TRACE, 484 "anonymous rebind via ldap_sasl_bind(\"\")\n", 485 0, 0, 0); 486 487 #ifdef LDAP_R_COMPILE 488 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex ); 489 ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex ); 490 #endif 491 rc = ldap_sasl_bind( ld, "", LDAP_SASL_SIMPLE, &passwd, 492 NULL, NULL, &msgid ); 493 if ( rc != LDAP_SUCCESS ) { 494 err = -1; 495 496 } else { 497 for ( err = 1; err > 0; ) { 498 struct timeval tv = { 0, 100000 }; 499 LDAPMessage *res = NULL; 500 501 switch ( ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res ) ) { 502 case -1: 503 err = -1; 504 break; 505 506 case 0: 507 #ifdef LDAP_R_COMPILE 508 ldap_pvt_thread_yield(); 509 #endif 510 break; 511 512 case LDAP_RES_BIND: 513 rc = ldap_parse_result( ld, res, &err, NULL, NULL, NULL, NULL, 1 ); 514 if ( rc != LDAP_SUCCESS ) { 515 err = -1; 516 517 } else if ( err != LDAP_SUCCESS ) { 518 err = -1; 519 } 520 /* else err == LDAP_SUCCESS == 0 */ 521 break; 522 523 default: 524 Debug( LDAP_DEBUG_TRACE, 525 "ldap_new_connection %p: " 526 "unexpected response %d " 527 "from BIND request id=%d\n", 528 (void *) ld, ldap_msgtype( res ), msgid ); 529 err = -1; 530 break; 531 } 532 } 533 } 534 #ifdef LDAP_R_COMPILE 535 ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex ); 536 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex ); 537 #endif 538 ld->ld_defconn = savedefconn; 539 --lc->lconn_refcnt; 540 541 if ( err != 0 ) { 542 ldap_free_connection( ld, lc, 1, 0 ); 543 lc = NULL; 544 } 545 } 546 if ( lc != NULL ) 547 lc->lconn_rebind_inprogress = 0; 548 } 549 550 return( lc ); 551 } 552 553 554 static LDAPConn * 555 find_connection( LDAP *ld, LDAPURLDesc *srv, int any ) 556 /* 557 * return an existing connection (if any) to the server srv 558 * if "any" is non-zero, check for any server in the "srv" chain 559 */ 560 { 561 LDAPConn *lc; 562 LDAPURLDesc *lcu, *lsu; 563 int lcu_port, lsu_port; 564 int found = 0; 565 566 #ifdef LDAP_R_COMPILE 567 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex ); 568 #endif 569 for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) { 570 lcu = lc->lconn_server; 571 lcu_port = ldap_pvt_url_scheme_port( lcu->lud_scheme, 572 lcu->lud_port ); 573 574 for ( lsu = srv; lsu != NULL; lsu = lsu->lud_next ) { 575 lsu_port = ldap_pvt_url_scheme_port( lsu->lud_scheme, 576 lsu->lud_port ); 577 578 if ( lsu_port == lcu_port 579 && strcmp( lcu->lud_scheme, lsu->lud_scheme ) == 0 580 && lcu->lud_host != NULL && *lcu->lud_host != '\0' 581 && lsu->lud_host != NULL && *lsu->lud_host != '\0' 582 && strcasecmp( lsu->lud_host, lcu->lud_host ) == 0 ) 583 { 584 found = 1; 585 break; 586 } 587 588 if ( !any ) break; 589 } 590 if ( found ) 591 break; 592 } 593 #ifdef LDAP_R_COMPILE 594 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex ); 595 #endif 596 return lc; 597 } 598 599 600 601 static void 602 use_connection( LDAP *ld, LDAPConn *lc ) 603 { 604 ++lc->lconn_refcnt; 605 lc->lconn_lastused = time( NULL ); 606 } 607 608 609 void 610 ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind ) 611 { 612 LDAPConn *tmplc, *prevlc; 613 614 Debug( LDAP_DEBUG_TRACE, 615 "ldap_free_connection %d %d\n", 616 force, unbind, 0 ); 617 618 if ( force || --lc->lconn_refcnt <= 0 ) { 619 /* remove from connections list first */ 620 #ifdef LDAP_R_COMPILE 621 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex ); 622 #endif 623 624 for ( prevlc = NULL, tmplc = ld->ld_conns; 625 tmplc != NULL; 626 tmplc = tmplc->lconn_next ) 627 { 628 if ( tmplc == lc ) { 629 if ( prevlc == NULL ) { 630 ld->ld_conns = tmplc->lconn_next; 631 } else { 632 prevlc->lconn_next = tmplc->lconn_next; 633 } 634 break; 635 } 636 prevlc = tmplc; 637 } 638 #ifdef LDAP_R_COMPILE 639 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex ); 640 #endif 641 642 if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) { 643 ldap_mark_select_clear( ld, lc->lconn_sb ); 644 if ( unbind ) { 645 ldap_send_unbind( ld, lc->lconn_sb, 646 NULL, NULL ); 647 } 648 } 649 650 if ( lc->lconn_ber != NULL ) { 651 ber_free( lc->lconn_ber, 1 ); 652 } 653 654 ldap_int_sasl_close( ld, lc ); 655 656 ldap_free_urllist( lc->lconn_server ); 657 658 /* FIXME: is this at all possible? 659 * ldap_ld_free() in unbind.c calls ldap_free_connection() 660 * with force == 1 __after__ explicitly calling 661 * ldap_free_request() on all requests */ 662 if ( force ) { 663 LDAPRequest *lr; 664 665 for ( lr = ld->ld_requests; lr; ) { 666 LDAPRequest *lr_next = lr->lr_next; 667 668 if ( lr->lr_conn == lc ) { 669 ldap_free_request_int( ld, lr ); 670 } 671 672 lr = lr_next; 673 } 674 } 675 676 if ( lc->lconn_sb != ld->ld_sb ) { 677 ber_sockbuf_free( lc->lconn_sb ); 678 } 679 680 if ( lc->lconn_rebind_queue != NULL) { 681 int i; 682 for( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) { 683 LDAP_VFREE( lc->lconn_rebind_queue[i] ); 684 } 685 LDAP_FREE( lc->lconn_rebind_queue ); 686 } 687 688 LDAP_FREE( lc ); 689 690 Debug( LDAP_DEBUG_TRACE, 691 "ldap_free_connection: actually freed\n", 692 0, 0, 0 ); 693 694 } else { 695 lc->lconn_lastused = time( NULL ); 696 Debug( LDAP_DEBUG_TRACE, "ldap_free_connection: refcnt %d\n", 697 lc->lconn_refcnt, 0, 0 ); 698 } 699 } 700 701 702 #ifdef LDAP_DEBUG 703 void 704 ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all ) 705 { 706 LDAPConn *lc; 707 char timebuf[32]; 708 709 Debug( LDAP_DEBUG_TRACE, "** ld %p Connection%s:\n", (void *)ld, all ? "s" : "", 0 ); 710 for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) { 711 if ( lc->lconn_server != NULL ) { 712 Debug( LDAP_DEBUG_TRACE, "* host: %s port: %d%s\n", 713 ( lc->lconn_server->lud_host == NULL ) ? "(null)" 714 : lc->lconn_server->lud_host, 715 lc->lconn_server->lud_port, ( lc->lconn_sb == 716 ld->ld_sb ) ? " (default)" : "" ); 717 } 718 Debug( LDAP_DEBUG_TRACE, " refcnt: %d status: %s\n", lc->lconn_refcnt, 719 ( lc->lconn_status == LDAP_CONNST_NEEDSOCKET ) 720 ? "NeedSocket" : 721 ( lc->lconn_status == LDAP_CONNST_CONNECTING ) 722 ? "Connecting" : "Connected", 0 ); 723 Debug( LDAP_DEBUG_TRACE, " last used: %s%s\n", 724 ldap_pvt_ctime( &lc->lconn_lastused, timebuf ), 725 lc->lconn_rebind_inprogress ? " rebind in progress" : "", 0 ); 726 if ( lc->lconn_rebind_inprogress ) { 727 if ( lc->lconn_rebind_queue != NULL) { 728 int i; 729 730 for ( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) { 731 int j; 732 for( j = 0; lc->lconn_rebind_queue[i][j] != 0; j++ ) { 733 Debug( LDAP_DEBUG_TRACE, " queue %d entry %d - %s\n", 734 i, j, lc->lconn_rebind_queue[i][j] ); 735 } 736 } 737 } else { 738 Debug( LDAP_DEBUG_TRACE, " queue is empty\n", 0, 0, 0 ); 739 } 740 } 741 Debug( LDAP_DEBUG_TRACE, "\n", 0, 0, 0 ); 742 if ( !all ) { 743 break; 744 } 745 } 746 } 747 748 749 void 750 ldap_dump_requests_and_responses( LDAP *ld ) 751 { 752 LDAPRequest *lr; 753 LDAPMessage *lm, *l; 754 int i; 755 756 Debug( LDAP_DEBUG_TRACE, "** ld %p Outstanding Requests:\n", 757 (void *)ld, 0, 0 ); 758 lr = ld->ld_requests; 759 if ( lr == NULL ) { 760 Debug( LDAP_DEBUG_TRACE, " Empty\n", 0, 0, 0 ); 761 } 762 for ( i = 0; lr != NULL; lr = lr->lr_next, i++ ) { 763 Debug( LDAP_DEBUG_TRACE, " * msgid %d, origid %d, status %s\n", 764 lr->lr_msgid, lr->lr_origid, 765 ( lr->lr_status == LDAP_REQST_INPROGRESS ) ? "InProgress" : 766 ( lr->lr_status == LDAP_REQST_CHASINGREFS ) ? "ChasingRefs" : 767 ( lr->lr_status == LDAP_REQST_NOTCONNECTED ) ? "NotConnected" : 768 ( lr->lr_status == LDAP_REQST_WRITING ) ? "Writing" : 769 ( lr->lr_status == LDAP_REQST_COMPLETED ) ? "RequestCompleted" 770 : "InvalidStatus" ); 771 Debug( LDAP_DEBUG_TRACE, " outstanding referrals %d, parent count %d\n", 772 lr->lr_outrefcnt, lr->lr_parentcnt, 0 ); 773 } 774 Debug( LDAP_DEBUG_TRACE, " ld %p request count %d (abandoned %lu)\n", 775 (void *)ld, i, ld->ld_nabandoned ); 776 Debug( LDAP_DEBUG_TRACE, "** ld %p Response Queue:\n", (void *)ld, 0, 0 ); 777 if ( ( lm = ld->ld_responses ) == NULL ) { 778 Debug( LDAP_DEBUG_TRACE, " Empty\n", 0, 0, 0 ); 779 } 780 for ( i = 0; lm != NULL; lm = lm->lm_next, i++ ) { 781 Debug( LDAP_DEBUG_TRACE, " * msgid %d, type %lu\n", 782 lm->lm_msgid, (unsigned long)lm->lm_msgtype, 0 ); 783 if ( lm->lm_chain != NULL ) { 784 Debug( LDAP_DEBUG_TRACE, " chained responses:\n", 0, 0, 0 ); 785 for ( l = lm->lm_chain; l != NULL; l = l->lm_chain ) { 786 Debug( LDAP_DEBUG_TRACE, 787 " * msgid %d, type %lu\n", 788 l->lm_msgid, 789 (unsigned long)l->lm_msgtype, 0 ); 790 } 791 } 792 } 793 Debug( LDAP_DEBUG_TRACE, " ld %p response count %d\n", (void *)ld, i, 0 ); 794 } 795 #endif /* LDAP_DEBUG */ 796 797 static void 798 ldap_free_request_int( LDAP *ld, LDAPRequest *lr ) 799 { 800 /* if lr_refcnt > 0, the request has been looked up 801 * by ldap_find_request_by_msgid(); if in the meanwhile 802 * the request is free()'d by someone else, just decrease 803 * the reference count and extract it from the request 804 * list; later on, it will be freed. */ 805 if ( lr->lr_prev == NULL ) { 806 if ( lr->lr_refcnt == 0 ) { 807 /* free'ing the first request? */ 808 assert( ld->ld_requests == lr ); 809 } 810 811 if ( ld->ld_requests == lr ) { 812 ld->ld_requests = lr->lr_next; 813 } 814 815 } else { 816 lr->lr_prev->lr_next = lr->lr_next; 817 } 818 819 if ( lr->lr_next != NULL ) { 820 lr->lr_next->lr_prev = lr->lr_prev; 821 } 822 823 if ( lr->lr_refcnt > 0 ) { 824 lr->lr_refcnt = -lr->lr_refcnt; 825 826 lr->lr_prev = NULL; 827 lr->lr_next = NULL; 828 829 return; 830 } 831 832 if ( lr->lr_ber != NULL ) { 833 ber_free( lr->lr_ber, 1 ); 834 lr->lr_ber = NULL; 835 } 836 837 if ( lr->lr_res_error != NULL ) { 838 LDAP_FREE( lr->lr_res_error ); 839 lr->lr_res_error = NULL; 840 } 841 842 if ( lr->lr_res_matched != NULL ) { 843 LDAP_FREE( lr->lr_res_matched ); 844 lr->lr_res_matched = NULL; 845 } 846 847 LDAP_FREE( lr ); 848 } 849 850 void 851 ldap_free_request( LDAP *ld, LDAPRequest *lr ) 852 { 853 #ifdef LDAP_R_COMPILE 854 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); 855 #endif 856 857 Debug( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n", 858 lr->lr_origid, lr->lr_msgid, 0 ); 859 860 /* free all referrals (child requests) */ 861 while ( lr->lr_child ) { 862 ldap_free_request( ld, lr->lr_child ); 863 } 864 865 if ( lr->lr_parent != NULL ) { 866 LDAPRequest **lrp; 867 868 --lr->lr_parent->lr_outrefcnt; 869 for ( lrp = &lr->lr_parent->lr_child; 870 *lrp && *lrp != lr; 871 lrp = &(*lrp)->lr_refnext ); 872 873 if ( *lrp == lr ) { 874 *lrp = lr->lr_refnext; 875 } 876 } 877 ldap_free_request_int( ld, lr ); 878 } 879 880 /* 881 * call first time with *cntp = -1 882 * when returns *cntp == -1, no referrals are left 883 * 884 * NOTE: may replace *refsp, or shuffle the contents 885 * of the original array. 886 */ 887 static int ldap_int_nextref( 888 LDAP *ld, 889 char ***refsp, 890 int *cntp, 891 void *params ) 892 { 893 assert( refsp != NULL ); 894 assert( *refsp != NULL ); 895 assert( cntp != NULL ); 896 897 if ( *cntp < -1 ) { 898 *cntp = -1; 899 return -1; 900 } 901 902 (*cntp)++; 903 904 if ( (*refsp)[ *cntp ] == NULL ) { 905 *cntp = -1; 906 } 907 908 return 0; 909 } 910 911 /* 912 * Chase v3 referrals 913 * 914 * Parameters: 915 * (IN) ld = LDAP connection handle 916 * (IN) lr = LDAP Request structure 917 * (IN) refs = array of pointers to referral strings that we will chase 918 * The array will be free'd by this function when no longer needed 919 * (IN) sref != 0 if following search reference 920 * (OUT) errstrp = Place to return a string of referrals which could not be followed 921 * (OUT) hadrefp = 1 if sucessfully followed referral 922 * 923 * Return value - number of referrals followed 924 */ 925 int 926 ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char **errstrp, int *hadrefp ) 927 { 928 char *unfollowed; 929 int unfollowedcnt = 0; 930 LDAPRequest *origreq; 931 LDAPURLDesc *srv = NULL; 932 BerElement *ber; 933 char **refarray = NULL; 934 LDAPConn *lc; 935 int rc, count, i, j, id; 936 LDAPreqinfo rinfo; 937 938 ld->ld_errno = LDAP_SUCCESS; /* optimistic */ 939 *hadrefp = 0; 940 941 Debug( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n", 0, 0, 0 ); 942 943 unfollowed = NULL; 944 rc = count = 0; 945 946 /* If no referrals in array, return */ 947 if ( (refs == NULL) || ( (refs)[0] == NULL) ) { 948 rc = 0; 949 goto done; 950 } 951 952 /* Check for hop limit exceeded */ 953 if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) { 954 Debug( LDAP_DEBUG_ANY, 955 "more than %d referral hops (dropping)\n", ld->ld_refhoplimit, 0, 0 ); 956 ld->ld_errno = LDAP_REFERRAL_LIMIT_EXCEEDED; 957 rc = -1; 958 goto done; 959 } 960 961 /* find original request */ 962 for ( origreq = lr; 963 origreq->lr_parent != NULL; 964 origreq = origreq->lr_parent ) 965 { 966 /* empty */ ; 967 } 968 969 refarray = refs; 970 refs = NULL; 971 972 if ( ld->ld_nextref_proc == NULL ) { 973 ld->ld_nextref_proc = ldap_int_nextref; 974 } 975 976 /* parse out & follow referrals */ 977 i = -1; 978 for ( ld->ld_nextref_proc( ld, &refarray, &i, ld->ld_nextref_params ); 979 i != -1; 980 ld->ld_nextref_proc( ld, &refarray, &i, ld->ld_nextref_params ) ) 981 { 982 983 /* Parse the referral URL */ 984 rc = ldap_url_parse_ext( refarray[i], &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN ); 985 if ( rc != LDAP_URL_SUCCESS ) { 986 /* ldap_url_parse_ext() returns LDAP_URL_* errors 987 * which do not map on API errors */ 988 ld->ld_errno = LDAP_PARAM_ERROR; 989 rc = -1; 990 goto done; 991 } 992 993 if( srv->lud_crit_exts ) { 994 /* we do not support any extensions */ 995 ld->ld_errno = LDAP_NOT_SUPPORTED; 996 rc = -1; 997 goto done; 998 } 999 1000 /* check connection for re-bind in progress */ 1001 if (( lc = find_connection( ld, srv, 1 )) != NULL ) { 1002 /* See if we've already requested this DN with this conn */ 1003 LDAPRequest *lp; 1004 int looped = 0; 1005 int len = srv->lud_dn ? strlen( srv->lud_dn ) : 0; 1006 for ( lp = origreq; lp; ) { 1007 if ( lp->lr_conn == lc 1008 && len == lp->lr_dn.bv_len 1009 && len 1010 && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) == 0 ) 1011 { 1012 looped = 1; 1013 break; 1014 } 1015 if ( lp == origreq ) { 1016 lp = lp->lr_child; 1017 } else { 1018 lp = lp->lr_refnext; 1019 } 1020 } 1021 if ( looped ) { 1022 ldap_free_urllist( srv ); 1023 srv = NULL; 1024 ld->ld_errno = LDAP_CLIENT_LOOP; 1025 rc = -1; 1026 continue; 1027 } 1028 1029 if ( lc->lconn_rebind_inprogress ) { 1030 /* We are already chasing a referral or search reference and a 1031 * bind on that connection is in progress. We must queue 1032 * referrals on that connection, so we don't get a request 1033 * going out before the bind operation completes. This happens 1034 * if two search references come in one behind the other 1035 * for the same server with different contexts. 1036 */ 1037 Debug( LDAP_DEBUG_TRACE, 1038 "ldap_chase_v3referrals: queue referral \"%s\"\n", 1039 refarray[i], 0, 0); 1040 if( lc->lconn_rebind_queue == NULL ) { 1041 /* Create a referral list */ 1042 lc->lconn_rebind_queue = 1043 (char ***) LDAP_MALLOC( sizeof(void *) * 2); 1044 1045 if( lc->lconn_rebind_queue == NULL) { 1046 ld->ld_errno = LDAP_NO_MEMORY; 1047 rc = -1; 1048 goto done; 1049 } 1050 1051 lc->lconn_rebind_queue[0] = refarray; 1052 lc->lconn_rebind_queue[1] = NULL; 1053 refarray = NULL; 1054 1055 } else { 1056 /* Count how many referral arrays we already have */ 1057 for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) { 1058 /* empty */; 1059 } 1060 1061 /* Add the new referral to the list */ 1062 lc->lconn_rebind_queue = (char ***) LDAP_REALLOC( 1063 lc->lconn_rebind_queue, sizeof(void *) * (j + 2)); 1064 1065 if( lc->lconn_rebind_queue == NULL ) { 1066 ld->ld_errno = LDAP_NO_MEMORY; 1067 rc = -1; 1068 goto done; 1069 } 1070 lc->lconn_rebind_queue[j] = refarray; 1071 lc->lconn_rebind_queue[j+1] = NULL; 1072 refarray = NULL; 1073 } 1074 1075 /* We have queued the referral/reference, now just return */ 1076 rc = 0; 1077 *hadrefp = 1; 1078 count = 1; /* Pretend we already followed referral */ 1079 goto done; 1080 } 1081 } 1082 /* Re-encode the request with the new starting point of the search. 1083 * Note: In the future we also need to replace the filter if one 1084 * was provided with the search reference 1085 */ 1086 1087 /* For references we don't want old dn if new dn empty */ 1088 if ( sref && srv->lud_dn == NULL ) { 1089 srv->lud_dn = LDAP_STRDUP( "" ); 1090 } 1091 1092 LDAP_NEXT_MSGID( ld, id ); 1093 ber = re_encode_request( ld, origreq->lr_ber, id, 1094 sref, srv, &rinfo.ri_request ); 1095 1096 if( ber == NULL ) { 1097 ld->ld_errno = LDAP_ENCODING_ERROR; 1098 rc = -1; 1099 goto done; 1100 } 1101 1102 Debug( LDAP_DEBUG_TRACE, 1103 "ldap_chase_v3referral: msgid %d, url \"%s\"\n", 1104 lr->lr_msgid, refarray[i], 0); 1105 1106 /* Send the new request to the server - may require a bind */ 1107 rinfo.ri_msgid = origreq->lr_origid; 1108 rinfo.ri_url = refarray[i]; 1109 #ifdef LDAP_R_COMPILE 1110 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex ); 1111 #endif 1112 rc = ldap_send_server_request( ld, ber, id, 1113 origreq, &srv, NULL, &rinfo ); 1114 #ifdef LDAP_R_COMPILE 1115 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex ); 1116 #endif 1117 if ( rc < 0 ) { 1118 /* Failure, try next referral in the list */ 1119 Debug( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%d: %s)\n", 1120 refarray[i], ld->ld_errno, ldap_err2string( ld->ld_errno ) ); 1121 unfollowedcnt += ldap_append_referral( ld, &unfollowed, refarray[i] ); 1122 ldap_free_urllist( srv ); 1123 srv = NULL; 1124 ld->ld_errno = LDAP_REFERRAL; 1125 } else { 1126 /* Success, no need to try this referral list further */ 1127 rc = 0; 1128 ++count; 1129 *hadrefp = 1; 1130 1131 /* check if there is a queue of referrals that came in during bind */ 1132 if ( lc == NULL) { 1133 lc = find_connection( ld, srv, 1 ); 1134 if ( lc == NULL ) { 1135 ld->ld_errno = LDAP_OPERATIONS_ERROR; 1136 rc = -1; 1137 goto done; 1138 } 1139 } 1140 1141 if ( lc->lconn_rebind_queue != NULL ) { 1142 /* Release resources of previous list */ 1143 LDAP_VFREE( refarray ); 1144 refarray = NULL; 1145 ldap_free_urllist( srv ); 1146 srv = NULL; 1147 1148 /* Pull entries off end of queue so list always null terminated */ 1149 for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++ ) 1150 ; 1151 refarray = lc->lconn_rebind_queue[j - 1]; 1152 lc->lconn_rebind_queue[j-1] = NULL; 1153 /* we pulled off last entry from queue, free queue */ 1154 if ( j == 1 ) { 1155 LDAP_FREE( lc->lconn_rebind_queue ); 1156 lc->lconn_rebind_queue = NULL; 1157 } 1158 /* restart the loop the with new referral list */ 1159 i = -1; 1160 continue; 1161 } 1162 break; /* referral followed, break out of for loop */ 1163 } 1164 } /* end for loop */ 1165 done: 1166 LDAP_VFREE( refarray ); 1167 ldap_free_urllist( srv ); 1168 LDAP_FREE( *errstrp ); 1169 1170 if( rc == 0 ) { 1171 *errstrp = NULL; 1172 LDAP_FREE( unfollowed ); 1173 return count; 1174 } else { 1175 *errstrp = unfollowed; 1176 return rc; 1177 } 1178 } 1179 1180 /* 1181 * XXX merging of errors in this routine needs to be improved 1182 */ 1183 int 1184 ldap_chase_referrals( LDAP *ld, 1185 LDAPRequest *lr, 1186 char **errstrp, 1187 int sref, 1188 int *hadrefp ) 1189 { 1190 int rc, count, id; 1191 unsigned len; 1192 char *p, *ref, *unfollowed; 1193 LDAPRequest *origreq; 1194 LDAPURLDesc *srv; 1195 BerElement *ber; 1196 LDAPreqinfo rinfo; 1197 LDAPConn *lc; 1198 1199 Debug( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n", 0, 0, 0 ); 1200 1201 ld->ld_errno = LDAP_SUCCESS; /* optimistic */ 1202 *hadrefp = 0; 1203 1204 if ( *errstrp == NULL ) { 1205 return( 0 ); 1206 } 1207 1208 len = strlen( *errstrp ); 1209 for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) { 1210 if ( strncasecmp( p, LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) { 1211 *p = '\0'; 1212 p += LDAP_REF_STR_LEN; 1213 break; 1214 } 1215 } 1216 1217 if ( len < LDAP_REF_STR_LEN ) { 1218 return( 0 ); 1219 } 1220 1221 if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) { 1222 Debug( LDAP_DEBUG_ANY, 1223 "more than %d referral hops (dropping)\n", 1224 ld->ld_refhoplimit, 0, 0 ); 1225 /* XXX report as error in ld->ld_errno? */ 1226 return( 0 ); 1227 } 1228 1229 /* find original request */ 1230 for ( origreq = lr; origreq->lr_parent != NULL; 1231 origreq = origreq->lr_parent ) { 1232 /* empty */; 1233 } 1234 1235 unfollowed = NULL; 1236 rc = count = 0; 1237 1238 /* parse out & follow referrals */ 1239 for ( ref = p; rc == 0 && ref != NULL; ref = p ) { 1240 p = strchr( ref, '\n' ); 1241 if ( p != NULL ) { 1242 *p++ = '\0'; 1243 } 1244 1245 rc = ldap_url_parse_ext( ref, &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN ); 1246 if ( rc != LDAP_URL_SUCCESS ) { 1247 Debug( LDAP_DEBUG_TRACE, 1248 "ignoring %s referral <%s>\n", 1249 ref, rc == LDAP_URL_ERR_BADSCHEME ? "unknown" : "incorrect", 0 ); 1250 rc = ldap_append_referral( ld, &unfollowed, ref ); 1251 *hadrefp = 1; 1252 continue; 1253 } 1254 1255 Debug( LDAP_DEBUG_TRACE, 1256 "chasing LDAP referral: <%s>\n", ref, 0, 0 ); 1257 1258 *hadrefp = 1; 1259 1260 /* See if we've already been here */ 1261 if (( lc = find_connection( ld, srv, 1 )) != NULL ) { 1262 LDAPRequest *lp; 1263 int looped = 0; 1264 int len = srv->lud_dn ? strlen( srv->lud_dn ) : 0; 1265 for ( lp = lr; lp; lp = lp->lr_parent ) { 1266 if ( lp->lr_conn == lc 1267 && len == lp->lr_dn.bv_len ) 1268 { 1269 if ( len && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) ) 1270 continue; 1271 looped = 1; 1272 break; 1273 } 1274 } 1275 if ( looped ) { 1276 ldap_free_urllist( srv ); 1277 ld->ld_errno = LDAP_CLIENT_LOOP; 1278 rc = -1; 1279 continue; 1280 } 1281 } 1282 1283 LDAP_NEXT_MSGID( ld, id ); 1284 ber = re_encode_request( ld, origreq->lr_ber, 1285 id, sref, srv, &rinfo.ri_request ); 1286 1287 if ( ber == NULL ) { 1288 return -1 ; 1289 } 1290 1291 /* copy the complete referral for rebind process */ 1292 rinfo.ri_url = LDAP_STRDUP( ref ); 1293 1294 rinfo.ri_msgid = origreq->lr_origid; 1295 1296 #ifdef LDAP_R_COMPILE 1297 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex ); 1298 #endif 1299 rc = ldap_send_server_request( ld, ber, id, 1300 lr, &srv, NULL, &rinfo ); 1301 #ifdef LDAP_R_COMPILE 1302 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex ); 1303 #endif 1304 1305 LDAP_FREE( rinfo.ri_url ); 1306 1307 if( rc >= 0 ) { 1308 ++count; 1309 } else { 1310 Debug( LDAP_DEBUG_ANY, 1311 "Unable to chase referral \"%s\" (%d: %s)\n", 1312 ref, ld->ld_errno, ldap_err2string( ld->ld_errno ) ); 1313 rc = ldap_append_referral( ld, &unfollowed, ref ); 1314 } 1315 1316 ldap_free_urllist(srv); 1317 } 1318 1319 LDAP_FREE( *errstrp ); 1320 *errstrp = unfollowed; 1321 1322 return(( rc == 0 ) ? count : rc ); 1323 } 1324 1325 1326 int 1327 ldap_append_referral( LDAP *ld, char **referralsp, char *s ) 1328 { 1329 int first; 1330 1331 if ( *referralsp == NULL ) { 1332 first = 1; 1333 *referralsp = (char *)LDAP_MALLOC( strlen( s ) + LDAP_REF_STR_LEN 1334 + 1 ); 1335 } else { 1336 first = 0; 1337 *referralsp = (char *)LDAP_REALLOC( *referralsp, 1338 strlen( *referralsp ) + strlen( s ) + 2 ); 1339 } 1340 1341 if ( *referralsp == NULL ) { 1342 ld->ld_errno = LDAP_NO_MEMORY; 1343 return( -1 ); 1344 } 1345 1346 if ( first ) { 1347 strcpy( *referralsp, LDAP_REF_STR ); 1348 } else { 1349 strcat( *referralsp, "\n" ); 1350 } 1351 strcat( *referralsp, s ); 1352 1353 return( 0 ); 1354 } 1355 1356 1357 1358 static BerElement * 1359 re_encode_request( LDAP *ld, 1360 BerElement *origber, 1361 ber_int_t msgid, 1362 int sref, 1363 LDAPURLDesc *srv, 1364 int *type ) 1365 { 1366 /* 1367 * XXX this routine knows way too much about how the lber library works! 1368 */ 1369 ber_int_t along; 1370 ber_tag_t tag; 1371 ber_tag_t rtag; 1372 ber_int_t ver; 1373 ber_int_t scope; 1374 int rc; 1375 BerElement tmpber, *ber; 1376 struct berval dn; 1377 1378 Debug( LDAP_DEBUG_TRACE, 1379 "re_encode_request: new msgid %ld, new dn <%s>\n", 1380 (long) msgid, 1381 ( srv == NULL || srv->lud_dn == NULL) ? "NONE" : srv->lud_dn, 0 ); 1382 1383 tmpber = *origber; 1384 1385 /* 1386 * all LDAP requests are sequences that start with a message id. 1387 * For all except delete, this is followed by a sequence that is 1388 * tagged with the operation code. For delete, the provided DN 1389 * is not wrapped by a sequence. 1390 */ 1391 rtag = ber_scanf( &tmpber, "{it", /*}*/ &along, &tag ); 1392 1393 if ( rtag == LBER_ERROR ) { 1394 ld->ld_errno = LDAP_DECODING_ERROR; 1395 return( NULL ); 1396 } 1397 1398 assert( tag != 0); 1399 if ( tag == LDAP_REQ_BIND ) { 1400 /* bind requests have a version number before the DN & other stuff */ 1401 rtag = ber_scanf( &tmpber, "{im" /*}*/, &ver, &dn ); 1402 1403 } else if ( tag == LDAP_REQ_DELETE ) { 1404 /* delete requests don't have a DN wrapping sequence */ 1405 rtag = ber_scanf( &tmpber, "m", &dn ); 1406 1407 } else if ( tag == LDAP_REQ_SEARCH ) { 1408 /* search requests need to be re-scope-ed */ 1409 rtag = ber_scanf( &tmpber, "{me" /*"}"*/, &dn, &scope ); 1410 1411 if( srv->lud_scope != LDAP_SCOPE_DEFAULT ) { 1412 /* use the scope provided in reference */ 1413 scope = srv->lud_scope; 1414 1415 } else if ( sref ) { 1416 /* use scope implied by previous operation 1417 * base -> base 1418 * one -> base 1419 * subtree -> subtree 1420 * subordinate -> subtree 1421 */ 1422 switch( scope ) { 1423 default: 1424 case LDAP_SCOPE_BASE: 1425 case LDAP_SCOPE_ONELEVEL: 1426 scope = LDAP_SCOPE_BASE; 1427 break; 1428 case LDAP_SCOPE_SUBTREE: 1429 case LDAP_SCOPE_SUBORDINATE: 1430 scope = LDAP_SCOPE_SUBTREE; 1431 break; 1432 } 1433 } 1434 1435 } else { 1436 rtag = ber_scanf( &tmpber, "{m" /*}*/, &dn ); 1437 } 1438 1439 if( rtag == LBER_ERROR ) { 1440 ld->ld_errno = LDAP_DECODING_ERROR; 1441 return NULL; 1442 } 1443 1444 /* restore character zero'd out by ber_scanf*/ 1445 dn.bv_val[dn.bv_len] = tmpber.ber_tag; 1446 1447 if (( ber = ldap_alloc_ber_with_options( ld )) == NULL ) { 1448 return NULL; 1449 } 1450 1451 if ( srv->lud_dn ) { 1452 ber_str2bv( srv->lud_dn, 0, 0, &dn ); 1453 } 1454 1455 if ( tag == LDAP_REQ_BIND ) { 1456 rc = ber_printf( ber, "{it{iO" /*}}*/, msgid, tag, ver, &dn ); 1457 } else if ( tag == LDAP_REQ_DELETE ) { 1458 rc = ber_printf( ber, "{itON}", msgid, tag, &dn ); 1459 } else if ( tag == LDAP_REQ_SEARCH ) { 1460 rc = ber_printf( ber, "{it{Oe" /*}}*/, msgid, tag, &dn, scope ); 1461 } else { 1462 rc = ber_printf( ber, "{it{O" /*}}*/, msgid, tag, &dn ); 1463 } 1464 1465 if ( rc == -1 ) { 1466 ld->ld_errno = LDAP_ENCODING_ERROR; 1467 ber_free( ber, 1 ); 1468 return NULL; 1469 } 1470 1471 if ( tag != LDAP_REQ_DELETE && ( 1472 ber_write(ber, tmpber.ber_ptr, ( tmpber.ber_end - tmpber.ber_ptr ), 0) 1473 != ( tmpber.ber_end - tmpber.ber_ptr ) || 1474 ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) ) 1475 { 1476 ld->ld_errno = LDAP_ENCODING_ERROR; 1477 ber_free( ber, 1 ); 1478 return NULL; 1479 } 1480 1481 #ifdef LDAP_DEBUG 1482 if ( ldap_debug & LDAP_DEBUG_PACKETS ) { 1483 Debug( LDAP_DEBUG_ANY, "re_encode_request new request is:\n", 1484 0, 0, 0 ); 1485 ber_log_dump( LDAP_DEBUG_BER, ldap_debug, ber, 0 ); 1486 } 1487 #endif /* LDAP_DEBUG */ 1488 1489 *type = tag; /* return request type */ 1490 return ber; 1491 } 1492 1493 1494 LDAPRequest * 1495 ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid ) 1496 { 1497 LDAPRequest *lr; 1498 1499 #ifdef LDAP_R_COMPILE 1500 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex ); 1501 #endif 1502 for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) { 1503 if ( lr->lr_status == LDAP_REQST_COMPLETED ) { 1504 continue; /* Skip completed requests */ 1505 } 1506 if ( msgid == lr->lr_msgid ) { 1507 lr->lr_refcnt++; 1508 break; 1509 } 1510 } 1511 #ifdef LDAP_R_COMPILE 1512 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex ); 1513 #endif 1514 1515 return( lr ); 1516 } 1517 1518 void 1519 ldap_return_request( LDAP *ld, LDAPRequest *lrx, int freeit ) 1520 { 1521 LDAPRequest *lr; 1522 1523 #ifdef LDAP_R_COMPILE 1524 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex ); 1525 #endif 1526 for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) { 1527 if ( lr == lrx ) { 1528 if ( lr->lr_refcnt > 0 ) { 1529 lr->lr_refcnt--; 1530 1531 } else if ( lr->lr_refcnt < 0 ) { 1532 lr->lr_refcnt++; 1533 if ( lr->lr_refcnt == 0 ) { 1534 lr = NULL; 1535 } 1536 } 1537 break; 1538 } 1539 } 1540 if ( lr == NULL ) { 1541 ldap_free_request_int( ld, lrx ); 1542 1543 } else if ( freeit ) { 1544 ldap_free_request( ld, lrx ); 1545 } 1546 #ifdef LDAP_R_COMPILE 1547 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex ); 1548 #endif 1549 } 1550