Back to index

openldap  2.4.31
request.c
Go to the documentation of this file.
00001 /* $OpenLDAP$ */
00002 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00003  *
00004  * Copyright 1998-2012 The OpenLDAP Foundation.
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted only as authorized by the OpenLDAP
00009  * Public License.
00010  *
00011  * A copy of this license is available in the file LICENSE in the
00012  * top-level directory of the distribution or, alternatively, at
00013  * <http://www.OpenLDAP.org/license.html>.
00014  */
00015 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
00016  * All rights reserved.
00017  */
00018 /* This notice applies to changes, created by or for Novell, Inc.,
00019  * to preexisting works for which notices appear elsewhere in this file.
00020  *
00021  * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
00022  *
00023  * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
00024  * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
00025  * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
00026  * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
00027  * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
00028  * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
00029  * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
00030  * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 
00031  *---
00032  * Modification to OpenLDAP source by Novell, Inc.
00033  * April 2000 sfs  Added code to chase V3 referrals
00034  *  request.c - sending of ldap requests; handling of referrals
00035  *---
00036  * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License 
00037  * can be found in the file "build/LICENSE-2.0.1" in this distribution
00038  * of OpenLDAP Software.
00039  */
00040 
00041 #include "portable.h"
00042 
00043 #include <stdio.h>
00044 
00045 #include <ac/stdlib.h>
00046 
00047 #include <ac/errno.h>
00048 #include <ac/socket.h>
00049 #include <ac/string.h>
00050 #include <ac/time.h>
00051 #include <ac/unistd.h>
00052 
00053 #include "ldap-int.h"
00054 #include "lber.h"
00055 
00056 /* used by ldap_send_server_request and ldap_new_connection */
00057 #ifdef LDAP_R_COMPILE
00058 #define LDAP_CONN_LOCK_IF(nolock) \
00059        { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); }
00060 #define LDAP_CONN_UNLOCK_IF(nolock) \
00061        { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); }
00062 #define LDAP_REQ_LOCK_IF(nolock) \
00063        { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); }
00064 #define LDAP_REQ_UNLOCK_IF(nolock) \
00065        { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); }
00066 #define LDAP_RES_LOCK_IF(nolock) \
00067        { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); }
00068 #define LDAP_RES_UNLOCK_IF(nolock) \
00069        { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); }
00070 #else
00071 #define LDAP_CONN_LOCK_IF(nolock)
00072 #define LDAP_CONN_UNLOCK_IF(nolock)
00073 #define LDAP_REQ_LOCK_IF(nolock)
00074 #define LDAP_REQ_UNLOCK_IF(nolock)
00075 #define LDAP_RES_LOCK_IF(nolock)
00076 #define LDAP_RES_UNLOCK_IF(nolock)
00077 #endif
00078 
00079 static LDAPConn *find_connection LDAP_P(( LDAP *ld, LDAPURLDesc *srv, int any ));
00080 static void use_connection LDAP_P(( LDAP *ld, LDAPConn *lc ));
00081 static void ldap_free_request_int LDAP_P(( LDAP *ld, LDAPRequest *lr ));
00082 
00083 static BerElement *
00084 re_encode_request( LDAP *ld,
00085        BerElement *origber,
00086        ber_int_t msgid,
00087        int sref,
00088        LDAPURLDesc *srv,
00089        int *type );
00090 
00091 BerElement *
00092 ldap_alloc_ber_with_options( LDAP *ld )
00093 {
00094        BerElement    *ber;
00095 
00096        ber = ber_alloc_t( ld->ld_lberoptions );
00097        if ( ber == NULL ) {
00098               ld->ld_errno = LDAP_NO_MEMORY;
00099        }
00100 
00101        return( ber );
00102 }
00103 
00104 
00105 void
00106 ldap_set_ber_options( LDAP *ld, BerElement *ber )
00107 {
00108        /* ld_lberoptions is constant, hence no lock */
00109        ber->ber_options = ld->ld_lberoptions;
00110 }
00111 
00112 
00113 /* sets needed mutexes - no mutexes set to this point */
00114 ber_int_t
00115 ldap_send_initial_request(
00116        LDAP *ld,
00117        ber_tag_t msgtype,
00118        const char *dn,
00119        BerElement *ber,
00120        ber_int_t msgid)
00121 {
00122        int rc = 1;
00123        ber_socket_t sd = AC_SOCKET_INVALID;
00124 
00125        Debug( LDAP_DEBUG_TRACE, "ldap_send_initial_request\n", 0, 0, 0 );
00126 
00127        LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
00128        if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ) == -1 ) {
00129               /* not connected yet */
00130               rc = ldap_open_defconn( ld );
00131 
00132        }
00133        if ( ld->ld_defconn && ld->ld_defconn->lconn_status == LDAP_CONNST_CONNECTING )
00134               rc = ldap_int_check_async_open( ld, sd );
00135        if( rc < 0 ) {
00136               ber_free( ber, 1 );
00137               LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
00138               return( -1 );
00139        } else if ( rc == 0 ) {
00140               Debug( LDAP_DEBUG_TRACE,
00141                      "ldap_open_defconn: successful\n",
00142                      0, 0, 0 );
00143        }
00144 
00145 #ifdef LDAP_CONNECTIONLESS
00146        if (LDAP_IS_UDP(ld)) {
00147               if (msgtype == LDAP_REQ_BIND) {
00148                      LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
00149                      if (ld->ld_options.ldo_cldapdn)
00150                             ldap_memfree(ld->ld_options.ldo_cldapdn);
00151                      ld->ld_options.ldo_cldapdn = ldap_strdup(dn);
00152                      ber_free( ber, 1 );
00153                      LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
00154                      LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
00155                      return 0;
00156               }
00157               if (msgtype != LDAP_REQ_ABANDON && msgtype != LDAP_REQ_SEARCH)
00158               {
00159                      ber_free( ber, 1 );
00160                      LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
00161                      return LDAP_PARAM_ERROR;
00162               }
00163        }
00164 #endif
00165        LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
00166        rc = ldap_send_server_request( ld, ber, msgid, NULL,
00167               NULL, NULL, NULL, 0, 0 );
00168        LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
00169        LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
00170        return(rc);
00171 }
00172 
00173 
00174 /* protected by conn_mutex */
00175 int
00176 ldap_int_flush_request(
00177        LDAP *ld,
00178        LDAPRequest *lr )
00179 {
00180        LDAPConn *lc = lr->lr_conn;
00181 
00182        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
00183        if ( ber_flush2( lc->lconn_sb, lr->lr_ber, LBER_FLUSH_FREE_NEVER ) != 0 ) {
00184               if ( sock_errno() == EAGAIN ) {
00185                      /* need to continue write later */
00186                      lr->lr_status = LDAP_REQST_WRITING;
00187                      ldap_mark_select_write( ld, lc->lconn_sb );
00188                      ld->ld_errno = LDAP_BUSY;
00189                      return -2;
00190               } else {
00191                      ld->ld_errno = LDAP_SERVER_DOWN;
00192                      ldap_free_request( ld, lr );
00193                      ldap_free_connection( ld, lc, 0, 0 );
00194                      return( -1 );
00195               }
00196        } else {
00197               if ( lr->lr_parent == NULL ) {
00198                      lr->lr_ber->ber_end = lr->lr_ber->ber_ptr;
00199                      lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf;
00200               }
00201               lr->lr_status = LDAP_REQST_INPROGRESS;
00202 
00203               /* sent -- waiting for a response */
00204               ldap_mark_select_read( ld, lc->lconn_sb );
00205               ldap_clear_select_write( ld, lc->lconn_sb );
00206        }
00207        return 0;
00208 }
00209 
00210 /*
00211  * protected by req_mutex
00212  * if m_noconn then protect using conn_lock
00213  * else already protected with conn_lock
00214  * if m_res then also protected by res_mutex
00215  */
00216 
00217 int
00218 ldap_send_server_request(
00219        LDAP *ld,
00220        BerElement *ber,
00221        ber_int_t msgid,
00222        LDAPRequest *parentreq,
00223        LDAPURLDesc **srvlist,
00224        LDAPConn *lc,
00225        LDAPreqinfo *bind,
00226        int m_noconn,
00227        int m_res )
00228 {
00229        LDAPRequest   *lr;
00230        int           incparent, rc;
00231 
00232        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
00233        Debug( LDAP_DEBUG_TRACE, "ldap_send_server_request\n", 0, 0, 0 );
00234 
00235        incparent = 0;
00236        ld->ld_errno = LDAP_SUCCESS;       /* optimistic */
00237 
00238        LDAP_CONN_LOCK_IF(m_noconn);
00239        if ( lc == NULL ) {
00240               if ( srvlist == NULL ) {
00241                      lc = ld->ld_defconn;
00242               } else {
00243                      lc = find_connection( ld, *srvlist, 1 );
00244                      if ( lc == NULL ) {
00245                             if ( (bind != NULL) && (parentreq != NULL) ) {
00246                                    /* Remember the bind in the parent */
00247                                    incparent = 1;
00248                                    ++parentreq->lr_outrefcnt;
00249                             }
00250                             lc = ldap_new_connection( ld, srvlist, 0,
00251                                    1, bind, 1, m_res );
00252                      }
00253               }
00254        }
00255 
00256        /* async connect... */
00257        if ( lc != NULL && lc->lconn_status == LDAP_CONNST_CONNECTING ) {
00258               ber_socket_t  sd = AC_SOCKET_ERROR;
00259               struct timeval       tv = { 0 };
00260 
00261               ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sd );
00262 
00263               /* poll ... */
00264               switch ( ldap_int_poll( ld, sd, &tv ) ) {
00265               case 0:
00266                      /* go on! */
00267                      lc->lconn_status = LDAP_CONNST_CONNECTED;
00268                      break;
00269 
00270               case -2:
00271                      /* async only occurs if a network timeout is set */
00272 
00273                      /* honor network timeout */
00274                      LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
00275                      if ( time( NULL ) - lc->lconn_created <= ld->ld_options.ldo_tm_net.tv_sec )
00276                      {
00277                             /* caller will have to call again */
00278                             ld->ld_errno = LDAP_X_CONNECTING;
00279                      }
00280                      LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
00281                      /* fallthru */
00282 
00283               default:
00284                      /* error */
00285                      break;
00286               }
00287        }
00288 
00289        if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) {
00290               if ( ld->ld_errno == LDAP_SUCCESS ) {
00291                      ld->ld_errno = LDAP_SERVER_DOWN;
00292               }
00293 
00294               ber_free( ber, 1 );
00295               if ( incparent ) {
00296                      /* Forget about the bind */
00297                      --parentreq->lr_outrefcnt; 
00298               }
00299               LDAP_CONN_UNLOCK_IF(m_noconn);
00300               return( -1 );
00301        }
00302 
00303        use_connection( ld, lc );
00304 
00305 #ifdef LDAP_CONNECTIONLESS
00306        if ( LDAP_IS_UDP( ld )) {
00307               BerElement tmpber = *ber;
00308               ber_rewind( &tmpber );
00309               LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
00310               rc = ber_write( &tmpber, ld->ld_options.ldo_peer,
00311                      sizeof( struct sockaddr ), 0 );
00312               LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
00313               if ( rc == -1 ) {
00314                      ld->ld_errno = LDAP_ENCODING_ERROR;
00315                      LDAP_CONN_UNLOCK_IF(m_noconn);
00316                      return rc;
00317               }
00318        }
00319 #endif
00320 
00321        /* If we still have an incomplete write, try to finish it before
00322         * dealing with the new request. If we don't finish here, return
00323         * LDAP_BUSY and let the caller retry later. We only allow a single
00324         * request to be in WRITING state.
00325         */
00326        rc = 0;
00327        if ( ld->ld_requests &&
00328               ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
00329               ldap_int_flush_request( ld, ld->ld_requests ) < 0 )
00330        {
00331               rc = -1;
00332        }
00333        if ( rc ) {
00334               LDAP_CONN_UNLOCK_IF(m_noconn);
00335               return rc;
00336        }
00337 
00338        lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ) );
00339        if ( lr == NULL ) {
00340               ld->ld_errno = LDAP_NO_MEMORY;
00341               ldap_free_connection( ld, lc, 0, 0 );
00342               ber_free( ber, 1 );
00343               if ( incparent ) {
00344                      /* Forget about the bind */
00345                      --parentreq->lr_outrefcnt; 
00346               }
00347               LDAP_CONN_UNLOCK_IF(m_noconn);
00348               return( -1 );
00349        } 
00350        lr->lr_msgid = msgid;
00351        lr->lr_status = LDAP_REQST_INPROGRESS;
00352        lr->lr_res_errno = LDAP_SUCCESS;   /* optimistic */
00353        lr->lr_ber = ber;
00354        lr->lr_conn = lc;
00355        if ( parentreq != NULL ) {  /* sub-request */
00356               if ( !incparent ) { 
00357                      /* Increment if we didn't do it before the bind */
00358                      ++parentreq->lr_outrefcnt;
00359               }
00360               lr->lr_origid = parentreq->lr_origid;
00361               lr->lr_parentcnt = ++parentreq->lr_parentcnt;
00362               lr->lr_parent = parentreq;
00363               lr->lr_refnext = parentreq->lr_child;
00364               parentreq->lr_child = lr;
00365        } else {                    /* original request */
00366               lr->lr_origid = lr->lr_msgid;
00367        }
00368 
00369        /* Extract requestDN for future reference */
00370        {
00371               BerElement tmpber = *ber;
00372               ber_int_t     bint;
00373               ber_tag_t     tag, rtag;
00374 
00375               ber_reset( &tmpber, 1 );
00376               rtag = ber_scanf( &tmpber, "{it", /*}*/ &bint, &tag );
00377               switch ( tag ) {
00378               case LDAP_REQ_BIND:
00379                      rtag = ber_scanf( &tmpber, "{i" /*}*/, &bint );
00380                      break;
00381               case LDAP_REQ_DELETE:
00382                      break;
00383               default:
00384                      rtag = ber_scanf( &tmpber, "{" /*}*/ );
00385               case LDAP_REQ_ABANDON:
00386                      break;
00387               }
00388               if ( tag != LDAP_REQ_ABANDON ) {
00389                      ber_skip_tag( &tmpber, &lr->lr_dn.bv_len );
00390                      lr->lr_dn.bv_val = tmpber.ber_ptr;
00391               }
00392        }
00393 
00394        lr->lr_prev = NULL;
00395        lr->lr_next = ld->ld_requests;
00396        if ( lr->lr_next != NULL ) {
00397               lr->lr_next->lr_prev = lr;
00398        }
00399        ld->ld_requests = lr;
00400 
00401        ld->ld_errno = LDAP_SUCCESS;
00402        if ( ldap_int_flush_request( ld, lr ) == -1 ) {
00403               msgid = -1;
00404        }
00405 
00406        LDAP_CONN_UNLOCK_IF(m_noconn);
00407        return( msgid );
00408 }
00409 
00410 /* return 0 if no StartTLS ext, 1 if present, 2 if critical */
00411 static int
00412 find_tls_ext( LDAPURLDesc *srv )
00413 {
00414        int i, crit;
00415        char *ext;
00416 
00417        if ( !srv->lud_exts )
00418               return 0;
00419 
00420        for (i=0; srv->lud_exts[i]; i++) {
00421               crit = 0;
00422               ext = srv->lud_exts[i];
00423               if ( ext[0] == '!') {
00424                      ext++;
00425                      crit = 1;
00426               }
00427               if ( !strcasecmp( ext, "StartTLS" ) ||
00428                      !strcasecmp( ext, "X-StartTLS" ) ||
00429                      !strcmp( ext, LDAP_EXOP_START_TLS )) {
00430                      return crit + 1;
00431               }
00432        }
00433        return 0;
00434 }
00435 
00436 /*
00437  * always protected by conn_mutex
00438  * optionally protected by req_mutex and res_mutex
00439  */
00440 LDAPConn *
00441 ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
00442        int connect, LDAPreqinfo *bind, int m_req, int m_res )
00443 {
00444        LDAPConn      *lc;
00445        int           async = 0;
00446 
00447        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
00448        Debug( LDAP_DEBUG_TRACE, "ldap_new_connection %d %d %d\n",
00449               use_ldsb, connect, (bind != NULL) );
00450        /*
00451         * make a new LDAP server connection
00452         * XXX open connection synchronously for now
00453         */
00454        lc = (LDAPConn *)LDAP_CALLOC( 1, sizeof( LDAPConn ) );
00455        if ( lc == NULL ) {
00456               ld->ld_errno = LDAP_NO_MEMORY;
00457               return( NULL );
00458        }
00459        
00460        if ( use_ldsb ) {
00461               assert( ld->ld_sb != NULL );
00462               lc->lconn_sb = ld->ld_sb;
00463 
00464        } else {
00465               lc->lconn_sb = ber_sockbuf_alloc();
00466               if ( lc->lconn_sb == NULL ) {
00467                      LDAP_FREE( (char *)lc );
00468                      ld->ld_errno = LDAP_NO_MEMORY;
00469                      return( NULL );
00470               }
00471        }
00472 
00473        if ( connect ) {
00474               LDAPURLDesc   **srvp, *srv = NULL;
00475 
00476               async = LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_CONNECT_ASYNC );
00477 
00478               for ( srvp = srvlist; *srvp != NULL; srvp = &(*srvp)->lud_next ) {
00479                      int           rc;
00480 
00481                      rc = ldap_int_open_connection( ld, lc, *srvp, async );
00482                      if ( rc != -1 ) {
00483                             srv = *srvp;
00484 
00485                             if ( ld->ld_urllist_proc && ( !async || rc != -2 ) ) {
00486                                    ld->ld_urllist_proc( ld, srvlist, srvp, ld->ld_urllist_params );
00487                             }
00488 
00489                             break;
00490                      }
00491               }
00492 
00493               if ( srv == NULL ) {
00494                      if ( !use_ldsb ) {
00495                             ber_sockbuf_free( lc->lconn_sb );
00496                      }
00497                      LDAP_FREE( (char *)lc );
00498                      ld->ld_errno = LDAP_SERVER_DOWN;
00499                      return( NULL );
00500               }
00501 
00502               lc->lconn_server = ldap_url_dup( srv );
00503        }
00504 
00505        lc->lconn_status = async ? LDAP_CONNST_CONNECTING : LDAP_CONNST_CONNECTED;
00506        lc->lconn_next = ld->ld_conns;
00507        ld->ld_conns = lc;
00508 
00509        if ( connect ) {
00510 #ifdef HAVE_TLS
00511               if ( lc->lconn_server->lud_exts ) {
00512                      int rc, ext = find_tls_ext( lc->lconn_server );
00513                      if ( ext ) {
00514                             LDAPConn      *savedefconn;
00515 
00516                             savedefconn = ld->ld_defconn;
00517                             ++lc->lconn_refcnt;  /* avoid premature free */
00518                             ld->ld_defconn = lc;
00519 
00520                             LDAP_REQ_UNLOCK_IF(m_req);
00521                             LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
00522                             LDAP_RES_UNLOCK_IF(m_res);
00523                             rc = ldap_start_tls_s( ld, NULL, NULL );
00524                             LDAP_RES_LOCK_IF(m_res);
00525                             LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
00526                             LDAP_REQ_LOCK_IF(m_req);
00527                             ld->ld_defconn = savedefconn;
00528                             --lc->lconn_refcnt;
00529 
00530                             if ( rc != LDAP_SUCCESS && ext == 2 ) {
00531                                    ldap_free_connection( ld, lc, 1, 0 );
00532                                    return NULL;
00533                             }
00534                      }
00535               }
00536 #endif
00537        }
00538 
00539        if ( bind != NULL ) {
00540               int           err = 0;
00541               LDAPConn      *savedefconn;
00542 
00543               /* Set flag to prevent additional referrals
00544                * from being processed on this
00545                * connection until the bind has completed
00546                */
00547               lc->lconn_rebind_inprogress = 1;
00548               /* V3 rebind function */
00549               if ( ld->ld_rebind_proc != NULL) {
00550                      LDAPURLDesc   *srvfunc;
00551 
00552                      srvfunc = ldap_url_dup( *srvlist );
00553                      if ( srvfunc == NULL ) {
00554                             ld->ld_errno = LDAP_NO_MEMORY;
00555                             err = -1;
00556                      } else {
00557                             savedefconn = ld->ld_defconn;
00558                             ++lc->lconn_refcnt;  /* avoid premature free */
00559                             ld->ld_defconn = lc;
00560 
00561                             Debug( LDAP_DEBUG_TRACE, "Call application rebind_proc\n", 0, 0, 0);
00562                             LDAP_REQ_UNLOCK_IF(m_req);
00563                             LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
00564                             LDAP_RES_UNLOCK_IF(m_res);
00565                             err = (*ld->ld_rebind_proc)( ld,
00566                                    bind->ri_url, bind->ri_request, bind->ri_msgid,
00567                                    ld->ld_rebind_params );
00568                             LDAP_RES_LOCK_IF(m_res);
00569                             LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
00570                             LDAP_REQ_LOCK_IF(m_req);
00571 
00572                             ld->ld_defconn = savedefconn;
00573                             --lc->lconn_refcnt;
00574 
00575                             if ( err != 0 ) {
00576                                    err = -1;
00577                                    ldap_free_connection( ld, lc, 1, 0 );
00578                                    lc = NULL;
00579                             }
00580                             ldap_free_urldesc( srvfunc );
00581                      }
00582 
00583               } else {
00584                      int           msgid, rc;
00585                      struct berval passwd = BER_BVNULL;
00586 
00587                      savedefconn = ld->ld_defconn;
00588                      ++lc->lconn_refcnt;  /* avoid premature free */
00589                      ld->ld_defconn = lc;
00590 
00591                      Debug( LDAP_DEBUG_TRACE,
00592                             "anonymous rebind via ldap_sasl_bind(\"\")\n",
00593                             0, 0, 0);
00594 
00595                      LDAP_REQ_UNLOCK_IF(m_req);
00596                      LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
00597                      LDAP_RES_UNLOCK_IF(m_res);
00598                      rc = ldap_sasl_bind( ld, "", LDAP_SASL_SIMPLE, &passwd,
00599                             NULL, NULL, &msgid );
00600                      if ( rc != LDAP_SUCCESS ) {
00601                             err = -1;
00602 
00603                      } else {
00604                             for ( err = 1; err > 0; ) {
00605                                    struct timeval       tv = { 0, 100000 };
00606                                    LDAPMessage   *res = NULL;
00607 
00608                                    switch ( ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res ) ) {
00609                                    case -1:
00610                                           err = -1;
00611                                           break;
00612 
00613                                    case 0:
00614 #ifdef LDAP_R_COMPILE
00615                                           ldap_pvt_thread_yield();
00616 #endif
00617                                           break;
00618 
00619                                    case LDAP_RES_BIND:
00620                                           rc = ldap_parse_result( ld, res, &err, NULL, NULL, NULL, NULL, 1 );
00621                                           if ( rc != LDAP_SUCCESS ) {
00622                                                  err = -1;
00623 
00624                                           } else if ( err != LDAP_SUCCESS ) {
00625                                                  err = -1;
00626                                           }
00627                                           /* else err == LDAP_SUCCESS == 0 */
00628                                           break;
00629 
00630                                    default:
00631                                           Debug( LDAP_DEBUG_TRACE,
00632                                                  "ldap_new_connection %p: "
00633                                                  "unexpected response %d "
00634                                                  "from BIND request id=%d\n",
00635                                                  (void *) ld, ldap_msgtype( res ), msgid );
00636                                           err = -1;
00637                                           break;
00638                                    }
00639                             }
00640                      }
00641                      LDAP_RES_LOCK_IF(m_res);
00642                      LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
00643                      LDAP_REQ_LOCK_IF(m_req);
00644                      ld->ld_defconn = savedefconn;
00645                      --lc->lconn_refcnt;
00646 
00647                      if ( err != 0 ) {
00648                             ldap_free_connection( ld, lc, 1, 0 );
00649                             lc = NULL;
00650                      }
00651               }
00652               if ( lc != NULL )
00653                      lc->lconn_rebind_inprogress = 0;
00654        }
00655        return( lc );
00656 }
00657 
00658 
00659 /* protected by ld_conn_mutex */
00660 static LDAPConn *
00661 find_connection( LDAP *ld, LDAPURLDesc *srv, int any )
00662 /*
00663  * return an existing connection (if any) to the server srv
00664  * if "any" is non-zero, check for any server in the "srv" chain
00665  */
00666 {
00667        LDAPConn      *lc;
00668        LDAPURLDesc   *lcu, *lsu;
00669        int lcu_port, lsu_port;
00670        int found = 0;
00671 
00672        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
00673        for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
00674               lcu = lc->lconn_server;
00675               lcu_port = ldap_pvt_url_scheme_port( lcu->lud_scheme,
00676                      lcu->lud_port );
00677 
00678               for ( lsu = srv; lsu != NULL; lsu = lsu->lud_next ) {
00679                      lsu_port = ldap_pvt_url_scheme_port( lsu->lud_scheme,
00680                             lsu->lud_port );
00681 
00682                      if ( lsu_port == lcu_port
00683                             && strcmp( lcu->lud_scheme, lsu->lud_scheme ) == 0
00684                             && lcu->lud_host != NULL && lsu->lud_host != NULL
00685                             && strcasecmp( lsu->lud_host, lcu->lud_host ) == 0 )
00686                      {
00687                             found = 1;
00688                             break;
00689                      }
00690 
00691                      if ( !any ) break;
00692               }
00693               if ( found )
00694                      break;
00695        }
00696        return lc;
00697 }
00698 
00699 
00700 
00701 /* protected by ld_conn_mutex */
00702 static void
00703 use_connection( LDAP *ld, LDAPConn *lc )
00704 {
00705        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
00706        ++lc->lconn_refcnt;
00707        lc->lconn_lastused = time( NULL );
00708 }
00709 
00710 
00711 /* protected by ld_conn_mutex */
00712 void
00713 ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
00714 {
00715        LDAPConn      *tmplc, *prevlc;
00716 
00717        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
00718        Debug( LDAP_DEBUG_TRACE,
00719               "ldap_free_connection %d %d\n",
00720               force, unbind, 0 );
00721 
00722        if ( force || --lc->lconn_refcnt <= 0 ) {
00723               /* remove from connections list first */
00724 
00725               for ( prevlc = NULL, tmplc = ld->ld_conns;
00726                      tmplc != NULL;
00727                      tmplc = tmplc->lconn_next )
00728               {
00729                      if ( tmplc == lc ) {
00730                             if ( prevlc == NULL ) {
00731                                 ld->ld_conns = tmplc->lconn_next;
00732                             } else {
00733                                 prevlc->lconn_next = tmplc->lconn_next;
00734                             }
00735                             if ( ld->ld_defconn == lc ) {
00736                                    ld->ld_defconn = NULL;
00737                             }
00738                             break;
00739                      }
00740                      prevlc = tmplc;
00741               }
00742 
00743               /* process connection callbacks */
00744               {
00745                      struct ldapoptions *lo;
00746                      ldaplist *ll;
00747                      ldap_conncb *cb;
00748 
00749                      lo = &ld->ld_options;
00750                      LDAP_MUTEX_LOCK( &lo->ldo_mutex );
00751                      if ( lo->ldo_conn_cbs ) {
00752                             for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
00753                                    cb = ll->ll_data;
00754                                    cb->lc_del( ld, lc->lconn_sb, cb );
00755                             }
00756                      }
00757                      LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
00758                      lo = LDAP_INT_GLOBAL_OPT();
00759                      LDAP_MUTEX_LOCK( &lo->ldo_mutex );
00760                      if ( lo->ldo_conn_cbs ) {
00761                             for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
00762                                    cb = ll->ll_data;
00763                                    cb->lc_del( ld, lc->lconn_sb, cb );
00764                             }
00765                      }
00766                      LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
00767               }
00768 
00769               if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) {
00770                      ldap_mark_select_clear( ld, lc->lconn_sb );
00771                      if ( unbind ) {
00772                             ldap_send_unbind( ld, lc->lconn_sb,
00773                                           NULL, NULL );
00774                      }
00775               }
00776 
00777               if ( lc->lconn_ber != NULL ) {
00778                      ber_free( lc->lconn_ber, 1 );
00779               }
00780 
00781               ldap_int_sasl_close( ld, lc );
00782 #ifdef HAVE_GSSAPI
00783               ldap_int_gssapi_close( ld, lc );
00784 #endif
00785 
00786               ldap_free_urllist( lc->lconn_server );
00787 
00788               /* FIXME: is this at all possible?
00789                * ldap_ld_free() in unbind.c calls ldap_free_connection()
00790                * with force == 1 __after__ explicitly calling
00791                * ldap_free_request() on all requests */
00792               if ( force ) {
00793                      LDAPRequest   *lr;
00794 
00795                      for ( lr = ld->ld_requests; lr; ) {
00796                             LDAPRequest   *lr_next = lr->lr_next;
00797 
00798                             if ( lr->lr_conn == lc ) {
00799                                    ldap_free_request_int( ld, lr );
00800                             }
00801 
00802                             lr = lr_next;
00803                      }
00804               }
00805 
00806               if ( lc->lconn_sb != ld->ld_sb ) {
00807                      ber_sockbuf_free( lc->lconn_sb );
00808               } else {
00809                      ber_int_sb_close( lc->lconn_sb );
00810               }
00811 
00812               if ( lc->lconn_rebind_queue != NULL) {
00813                      int i;
00814                      for( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) {
00815                             LDAP_VFREE( lc->lconn_rebind_queue[i] );
00816                      }
00817                      LDAP_FREE( lc->lconn_rebind_queue );
00818               }
00819 
00820               LDAP_FREE( lc );
00821 
00822               Debug( LDAP_DEBUG_TRACE,
00823                      "ldap_free_connection: actually freed\n",
00824                      0, 0, 0 );
00825 
00826        } else {
00827               lc->lconn_lastused = time( NULL );
00828               Debug( LDAP_DEBUG_TRACE, "ldap_free_connection: refcnt %d\n",
00829                             lc->lconn_refcnt, 0, 0 );
00830        }
00831 }
00832 
00833 
00834 /* Protects self with ld_conn_mutex */
00835 #ifdef LDAP_DEBUG
00836 void
00837 ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all )
00838 {
00839        LDAPConn      *lc;
00840        char          timebuf[32];
00841 
00842        Debug( LDAP_DEBUG_TRACE, "** ld %p Connection%s:\n", (void *)ld, all ? "s" : "", 0 );
00843        LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
00844        for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) {
00845               if ( lc->lconn_server != NULL ) {
00846                      Debug( LDAP_DEBUG_TRACE, "* host: %s  port: %d%s\n",
00847                             ( lc->lconn_server->lud_host == NULL ) ? "(null)"
00848                             : lc->lconn_server->lud_host,
00849                             lc->lconn_server->lud_port, ( lc->lconn_sb ==
00850                             ld->ld_sb ) ? "  (default)" : "" );
00851               }
00852               Debug( LDAP_DEBUG_TRACE, "  refcnt: %d  status: %s\n", lc->lconn_refcnt,
00853                      ( lc->lconn_status == LDAP_CONNST_NEEDSOCKET )
00854                             ? "NeedSocket" :
00855                             ( lc->lconn_status == LDAP_CONNST_CONNECTING )
00856                                    ? "Connecting" : "Connected", 0 );
00857               Debug( LDAP_DEBUG_TRACE, "  last used: %s%s\n",
00858                      ldap_pvt_ctime( &lc->lconn_lastused, timebuf ),
00859                      lc->lconn_rebind_inprogress ? "  rebind in progress" : "", 0 );
00860               if ( lc->lconn_rebind_inprogress ) {
00861                      if ( lc->lconn_rebind_queue != NULL) {
00862                             int    i;
00863 
00864                             for ( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) {
00865                                    int    j;
00866                                    for( j = 0; lc->lconn_rebind_queue[i][j] != 0; j++ ) {
00867                                           Debug( LDAP_DEBUG_TRACE, "    queue %d entry %d - %s\n",
00868                                                  i, j, lc->lconn_rebind_queue[i][j] );
00869                                    }
00870                             }
00871                      } else {
00872                             Debug( LDAP_DEBUG_TRACE, "    queue is empty\n", 0, 0, 0 );
00873                      }
00874               }
00875               Debug( LDAP_DEBUG_TRACE, "\n", 0, 0, 0 );
00876               if ( !all ) {
00877                      break;
00878               }
00879        }
00880        LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
00881 }
00882 
00883 
00884 /* protected by req_mutex and res_mutex */
00885 void
00886 ldap_dump_requests_and_responses( LDAP *ld )
00887 {
00888        LDAPRequest   *lr;
00889        LDAPMessage   *lm, *l;
00890        int           i;
00891 
00892        Debug( LDAP_DEBUG_TRACE, "** ld %p Outstanding Requests:\n",
00893               (void *)ld, 0, 0 );
00894        lr = ld->ld_requests;
00895        if ( lr == NULL ) {
00896               Debug( LDAP_DEBUG_TRACE, "   Empty\n", 0, 0, 0 );
00897        }
00898        for ( i = 0; lr != NULL; lr = lr->lr_next, i++ ) {
00899               Debug( LDAP_DEBUG_TRACE, " * msgid %d,  origid %d, status %s\n",
00900                      lr->lr_msgid, lr->lr_origid,
00901                      ( lr->lr_status == LDAP_REQST_INPROGRESS ) ? "InProgress" :
00902                      ( lr->lr_status == LDAP_REQST_CHASINGREFS ) ? "ChasingRefs" :
00903                      ( lr->lr_status == LDAP_REQST_NOTCONNECTED ) ? "NotConnected" :
00904                      ( lr->lr_status == LDAP_REQST_WRITING ) ? "Writing" :
00905                      ( lr->lr_status == LDAP_REQST_COMPLETED ) ? "RequestCompleted"
00906                             : "InvalidStatus" );
00907               Debug( LDAP_DEBUG_TRACE, "   outstanding referrals %d, parent count %d\n",
00908                      lr->lr_outrefcnt, lr->lr_parentcnt, 0 );
00909        }
00910        Debug( LDAP_DEBUG_TRACE, "  ld %p request count %d (abandoned %lu)\n",
00911               (void *)ld, i, ld->ld_nabandoned );
00912        Debug( LDAP_DEBUG_TRACE, "** ld %p Response Queue:\n", (void *)ld, 0, 0 );
00913        if ( ( lm = ld->ld_responses ) == NULL ) {
00914               Debug( LDAP_DEBUG_TRACE, "   Empty\n", 0, 0, 0 );
00915        }
00916        for ( i = 0; lm != NULL; lm = lm->lm_next, i++ ) {
00917               Debug( LDAP_DEBUG_TRACE, " * msgid %d,  type %lu\n",
00918                   lm->lm_msgid, (unsigned long)lm->lm_msgtype, 0 );
00919               if ( lm->lm_chain != NULL ) {
00920                      Debug( LDAP_DEBUG_TRACE, "   chained responses:\n", 0, 0, 0 );
00921                      for ( l = lm->lm_chain; l != NULL; l = l->lm_chain ) {
00922                             Debug( LDAP_DEBUG_TRACE,
00923                                    "  * msgid %d,  type %lu\n",
00924                                    l->lm_msgid,
00925                                    (unsigned long)l->lm_msgtype, 0 );
00926                      }
00927               }
00928        }
00929        Debug( LDAP_DEBUG_TRACE, "  ld %p response count %d\n", (void *)ld, i, 0 );
00930 }
00931 #endif /* LDAP_DEBUG */
00932 
00933 /* protected by req_mutex */
00934 static void
00935 ldap_free_request_int( LDAP *ld, LDAPRequest *lr )
00936 {
00937        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
00938        /* if lr_refcnt > 0, the request has been looked up 
00939         * by ldap_find_request_by_msgid(); if in the meanwhile
00940         * the request is free()'d by someone else, just decrease
00941         * the reference count and extract it from the request
00942         * list; later on, it will be freed. */
00943        if ( lr->lr_prev == NULL ) {
00944               if ( lr->lr_refcnt == 0 ) {
00945                      /* free'ing the first request? */
00946                      assert( ld->ld_requests == lr );
00947               }
00948 
00949               if ( ld->ld_requests == lr ) {
00950                      ld->ld_requests = lr->lr_next;
00951               }
00952 
00953        } else {
00954               lr->lr_prev->lr_next = lr->lr_next;
00955        }
00956 
00957        if ( lr->lr_next != NULL ) {
00958               lr->lr_next->lr_prev = lr->lr_prev;
00959        }
00960 
00961        if ( lr->lr_refcnt > 0 ) {
00962               lr->lr_refcnt = -lr->lr_refcnt;
00963 
00964               lr->lr_prev = NULL;
00965               lr->lr_next = NULL;
00966 
00967               return;
00968        }
00969 
00970        if ( lr->lr_ber != NULL ) {
00971               ber_free( lr->lr_ber, 1 );
00972               lr->lr_ber = NULL;
00973        }
00974 
00975        if ( lr->lr_res_error != NULL ) {
00976               LDAP_FREE( lr->lr_res_error );
00977               lr->lr_res_error = NULL;
00978        }
00979 
00980        if ( lr->lr_res_matched != NULL ) {
00981               LDAP_FREE( lr->lr_res_matched );
00982               lr->lr_res_matched = NULL;
00983        }
00984 
00985        LDAP_FREE( lr );
00986 }
00987 
00988 /* protected by req_mutex */
00989 void
00990 ldap_free_request( LDAP *ld, LDAPRequest *lr )
00991 {
00992        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
00993        Debug( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n",
00994               lr->lr_origid, lr->lr_msgid, 0 );
00995 
00996        /* free all referrals (child requests) */
00997        while ( lr->lr_child ) {
00998               ldap_free_request( ld, lr->lr_child );
00999        }
01000 
01001        if ( lr->lr_parent != NULL ) {
01002               LDAPRequest     **lrp;
01003 
01004               --lr->lr_parent->lr_outrefcnt;
01005               for ( lrp = &lr->lr_parent->lr_child;
01006                      *lrp && *lrp != lr;
01007                      lrp = &(*lrp)->lr_refnext );
01008 
01009               if ( *lrp == lr ) {
01010                      *lrp = lr->lr_refnext;
01011               }
01012        }
01013        ldap_free_request_int( ld, lr );
01014 }
01015 
01016 /*
01017  * call first time with *cntp = -1
01018  * when returns *cntp == -1, no referrals are left
01019  *
01020  * NOTE: may replace *refsp, or shuffle the contents
01021  * of the original array.
01022  */
01023 static int ldap_int_nextref(
01024        LDAP                 *ld,
01025        char                 ***refsp,
01026        int                  *cntp,
01027        void                 *params )
01028 {
01029        assert( refsp != NULL );
01030        assert( *refsp != NULL );
01031        assert( cntp != NULL );
01032 
01033        if ( *cntp < -1 ) {
01034               *cntp = -1;
01035               return -1;
01036        }
01037 
01038        (*cntp)++;
01039 
01040        if ( (*refsp)[ *cntp ] == NULL ) {
01041               *cntp = -1;
01042        }
01043 
01044        return 0;
01045 }
01046 
01047 /*
01048  * Chase v3 referrals
01049  *
01050  * Parameters:
01051  *  (IN) ld = LDAP connection handle
01052  *  (IN) lr = LDAP Request structure
01053  *  (IN) refs = array of pointers to referral strings that we will chase
01054  *              The array will be free'd by this function when no longer needed
01055  *  (IN) sref != 0 if following search reference
01056  *  (OUT) errstrp = Place to return a string of referrals which could not be followed
01057  *  (OUT) hadrefp = 1 if sucessfully followed referral
01058  *
01059  * Return value - number of referrals followed
01060  *
01061  * Protected by res_mutex, conn_mutex and req_mutex     (try_read1msg)
01062  */
01063 int
01064 ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char **errstrp, int *hadrefp )
01065 {
01066        char          *unfollowed;
01067        int            unfollowedcnt = 0;
01068        LDAPRequest   *origreq;
01069        LDAPURLDesc   *srv = NULL;
01070        BerElement    *ber;
01071        char          **refarray = NULL;
01072        LDAPConn      *lc;
01073        int                   rc, count, i, j, id;
01074        LDAPreqinfo  rinfo;
01075        LDAP_NEXTREF_PROC    *nextref_proc = ld->ld_nextref_proc ? ld->ld_nextref_proc : ldap_int_nextref;
01076 
01077        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
01078        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
01079        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
01080        Debug( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n", 0, 0, 0 );
01081 
01082        ld->ld_errno = LDAP_SUCCESS;       /* optimistic */
01083        *hadrefp = 0;
01084 
01085        unfollowed = NULL;
01086        rc = count = 0;
01087 
01088        /* If no referrals in array, return */
01089        if ( (refs == NULL) || ( (refs)[0] == NULL) ) {
01090               rc = 0;
01091               goto done;
01092        }
01093 
01094        /* Check for hop limit exceeded */
01095        if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
01096               Debug( LDAP_DEBUG_ANY,
01097                   "more than %d referral hops (dropping)\n", ld->ld_refhoplimit, 0, 0 );
01098               ld->ld_errno = LDAP_REFERRAL_LIMIT_EXCEEDED;
01099               rc = -1;
01100               goto done;
01101        }
01102 
01103        /* find original request */
01104        for ( origreq = lr;
01105               origreq->lr_parent != NULL;
01106               origreq = origreq->lr_parent )
01107        {
01108               /* empty */ ;
01109        }
01110 
01111        refarray = refs;
01112        refs = NULL;
01113 
01114        /* parse out & follow referrals */
01115        /* NOTE: if nextref_proc == ldap_int_nextref, params is ignored */
01116        i = -1;
01117        for ( nextref_proc( ld, &refarray, &i, ld->ld_nextref_params );
01118                      i != -1;
01119                      nextref_proc( ld, &refarray, &i, ld->ld_nextref_params ) )
01120        {
01121 
01122               /* Parse the referral URL */
01123               rc = ldap_url_parse_ext( refarray[i], &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
01124               if ( rc != LDAP_URL_SUCCESS ) {
01125                      /* ldap_url_parse_ext() returns LDAP_URL_* errors
01126                       * which do not map on API errors */
01127                      ld->ld_errno = LDAP_PARAM_ERROR;
01128                      rc = -1;
01129                      goto done;
01130               }
01131 
01132               if( srv->lud_crit_exts ) {
01133                      int ok = 0;
01134 #ifdef HAVE_TLS
01135                      /* If StartTLS is the only critical ext, OK. */
01136                      if ( find_tls_ext( srv ) == 2 && srv->lud_crit_exts == 1 )
01137                             ok = 1;
01138 #endif
01139                      if ( !ok ) {
01140                             /* we do not support any other extensions */
01141                             ld->ld_errno = LDAP_NOT_SUPPORTED;
01142                             rc = -1;
01143                             goto done;
01144                      }
01145               }
01146 
01147               /* check connection for re-bind in progress */
01148               if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
01149                      /* See if we've already requested this DN with this conn */
01150                      LDAPRequest *lp;
01151                      int looped = 0;
01152                      ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
01153                      for ( lp = origreq; lp; ) {
01154                             if ( lp->lr_conn == lc
01155                                    && len == lp->lr_dn.bv_len
01156                                    && len
01157                                    && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) == 0 )
01158                             {
01159                                    looped = 1;
01160                                    break;
01161                             }
01162                             if ( lp == origreq ) {
01163                                    lp = lp->lr_child;
01164                             } else {
01165                                    lp = lp->lr_refnext;
01166                             }
01167                      }
01168                      if ( looped ) {
01169                             ldap_free_urllist( srv );
01170                             srv = NULL;
01171                             ld->ld_errno = LDAP_CLIENT_LOOP;
01172                             rc = -1;
01173                             continue;
01174                      }
01175 
01176                      if ( lc->lconn_rebind_inprogress ) {
01177                             /* We are already chasing a referral or search reference and a
01178                              * bind on that connection is in progress.  We must queue
01179                              * referrals on that connection, so we don't get a request
01180                              * going out before the bind operation completes. This happens
01181                              * if two search references come in one behind the other
01182                              * for the same server with different contexts.
01183                              */
01184                             Debug( LDAP_DEBUG_TRACE,
01185                                    "ldap_chase_v3referrals: queue referral \"%s\"\n",
01186                                    refarray[i], 0, 0);
01187                             if( lc->lconn_rebind_queue == NULL ) {
01188                                    /* Create a referral list */
01189                                    lc->lconn_rebind_queue =
01190                                           (char ***) LDAP_MALLOC( sizeof(void *) * 2);
01191 
01192                                    if( lc->lconn_rebind_queue == NULL) {
01193                                           ld->ld_errno = LDAP_NO_MEMORY;
01194                                           rc = -1;
01195                                           goto done;
01196                                    }
01197 
01198                                    lc->lconn_rebind_queue[0] = refarray;
01199                                    lc->lconn_rebind_queue[1] = NULL;
01200                                    refarray = NULL;
01201 
01202                             } else {
01203                                    /* Count how many referral arrays we already have */
01204                                    for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) {
01205                                           /* empty */;
01206                                    }
01207 
01208                                    /* Add the new referral to the list */
01209                                    lc->lconn_rebind_queue = (char ***) LDAP_REALLOC(
01210                                           lc->lconn_rebind_queue, sizeof(void *) * (j + 2));
01211 
01212                                    if( lc->lconn_rebind_queue == NULL ) {
01213                                           ld->ld_errno = LDAP_NO_MEMORY;
01214                                           rc = -1;
01215                                           goto done;
01216                                    }
01217                                    lc->lconn_rebind_queue[j] = refarray;
01218                                    lc->lconn_rebind_queue[j+1] = NULL;
01219                                    refarray = NULL;
01220                             }
01221 
01222                             /* We have queued the referral/reference, now just return */
01223                             rc = 0;
01224                             *hadrefp = 1;
01225                             count = 1; /* Pretend we already followed referral */
01226                             goto done;
01227                      }
01228               } 
01229               /* Re-encode the request with the new starting point of the search.
01230                * Note: In the future we also need to replace the filter if one
01231                * was provided with the search reference
01232                */
01233 
01234               /* For references we don't want old dn if new dn empty */
01235               if ( sref && srv->lud_dn == NULL ) {
01236                      srv->lud_dn = LDAP_STRDUP( "" );
01237               }
01238 
01239               LDAP_NEXT_MSGID( ld, id );
01240               ber = re_encode_request( ld, origreq->lr_ber, id,
01241                      sref, srv, &rinfo.ri_request );
01242 
01243               if( ber == NULL ) {
01244                      ld->ld_errno = LDAP_ENCODING_ERROR;
01245                      rc = -1;
01246                      goto done;
01247               }
01248 
01249               Debug( LDAP_DEBUG_TRACE,
01250                      "ldap_chase_v3referral: msgid %d, url \"%s\"\n",
01251                      lr->lr_msgid, refarray[i], 0);
01252 
01253               /* Send the new request to the server - may require a bind */
01254               rinfo.ri_msgid = origreq->lr_origid;
01255               rinfo.ri_url = refarray[i];
01256               rc = ldap_send_server_request( ld, ber, id,
01257                      origreq, &srv, NULL, &rinfo, 0, 1 );
01258               if ( rc < 0 ) {
01259                      /* Failure, try next referral in the list */
01260                      Debug( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%d: %s)\n", 
01261                             refarray[i], ld->ld_errno, ldap_err2string( ld->ld_errno ) );
01262                      unfollowedcnt += ldap_append_referral( ld, &unfollowed, refarray[i] );
01263                      ldap_free_urllist( srv );
01264                      srv = NULL;
01265                      ld->ld_errno = LDAP_REFERRAL;
01266               } else {
01267                      /* Success, no need to try this referral list further */
01268                      rc = 0;
01269                      ++count;
01270                      *hadrefp = 1;
01271 
01272                      /* check if there is a queue of referrals that came in during bind */
01273                      if ( lc == NULL) {
01274                             lc = find_connection( ld, srv, 1 );
01275                             if ( lc == NULL ) {
01276                                    ld->ld_errno = LDAP_OPERATIONS_ERROR;
01277                                    rc = -1;
01278                                    LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
01279                                    goto done;
01280                             }
01281                      }
01282 
01283                      if ( lc->lconn_rebind_queue != NULL ) {
01284                             /* Release resources of previous list */
01285                             LDAP_VFREE( refarray );
01286                             refarray = NULL;
01287                             ldap_free_urllist( srv );
01288                             srv = NULL;
01289 
01290                             /* Pull entries off end of queue so list always null terminated */
01291                             for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++ )
01292                                    ;
01293                             refarray = lc->lconn_rebind_queue[j - 1];
01294                             lc->lconn_rebind_queue[j-1] = NULL;
01295                             /* we pulled off last entry from queue, free queue */
01296                             if ( j == 1 ) {
01297                                    LDAP_FREE( lc->lconn_rebind_queue );
01298                                    lc->lconn_rebind_queue = NULL;
01299                             }
01300                             /* restart the loop the with new referral list */
01301                             i = -1;
01302                             continue;
01303                      }
01304                      break; /* referral followed, break out of for loop */
01305               }
01306        } /* end for loop */
01307 done:
01308        LDAP_VFREE( refarray );
01309        ldap_free_urllist( srv );
01310        LDAP_FREE( *errstrp );
01311        
01312        if( rc == 0 ) {
01313               *errstrp = NULL;
01314               LDAP_FREE( unfollowed );
01315               return count;
01316        } else {
01317               *errstrp = unfollowed;
01318               return rc;
01319        }
01320 }
01321 
01322 /*
01323  * XXX merging of errors in this routine needs to be improved
01324  * Protected by res_mutex, conn_mutex and req_mutex     (try_read1msg)
01325  */
01326 int
01327 ldap_chase_referrals( LDAP *ld,
01328        LDAPRequest *lr,
01329        char **errstrp,
01330        int sref,
01331        int *hadrefp )
01332 {
01333        int           rc, count, id;
01334        unsigned      len;
01335        char          *p, *ref, *unfollowed;
01336        LDAPRequest   *origreq;
01337        LDAPURLDesc   *srv;
01338        BerElement    *ber;
01339        LDAPreqinfo  rinfo;
01340        LDAPConn      *lc;
01341 
01342        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
01343        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
01344        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
01345        Debug( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n", 0, 0, 0 );
01346 
01347        ld->ld_errno = LDAP_SUCCESS;       /* optimistic */
01348        *hadrefp = 0;
01349 
01350        if ( *errstrp == NULL ) {
01351               return( 0 );
01352        }
01353 
01354        len = strlen( *errstrp );
01355        for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) {
01356               if ( strncasecmp( p, LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) {
01357                      *p = '\0';
01358                      p += LDAP_REF_STR_LEN;
01359                      break;
01360               }
01361        }
01362 
01363        if ( len < LDAP_REF_STR_LEN ) {
01364               return( 0 );
01365        }
01366 
01367        if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
01368               Debug( LDAP_DEBUG_ANY,
01369                   "more than %d referral hops (dropping)\n",
01370                   ld->ld_refhoplimit, 0, 0 );
01371                   /* XXX report as error in ld->ld_errno? */
01372                   return( 0 );
01373        }
01374 
01375        /* find original request */
01376        for ( origreq = lr; origreq->lr_parent != NULL;
01377             origreq = origreq->lr_parent ) {
01378               /* empty */;
01379        }
01380 
01381        unfollowed = NULL;
01382        rc = count = 0;
01383 
01384        /* parse out & follow referrals */
01385        for ( ref = p; rc == 0 && ref != NULL; ref = p ) {
01386               p = strchr( ref, '\n' );
01387               if ( p != NULL ) {
01388                      *p++ = '\0';
01389               }
01390 
01391               rc = ldap_url_parse_ext( ref, &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
01392               if ( rc != LDAP_URL_SUCCESS ) {
01393                      Debug( LDAP_DEBUG_TRACE,
01394                             "ignoring %s referral <%s>\n",
01395                             ref, rc == LDAP_URL_ERR_BADSCHEME ? "unknown" : "incorrect", 0 );
01396                      rc = ldap_append_referral( ld, &unfollowed, ref );
01397                      *hadrefp = 1;
01398                      continue;
01399               }
01400 
01401               Debug( LDAP_DEBUG_TRACE,
01402                   "chasing LDAP referral: <%s>\n", ref, 0, 0 );
01403 
01404               *hadrefp = 1;
01405 
01406               /* See if we've already been here */
01407               if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
01408                      LDAPRequest *lp;
01409                      int looped = 0;
01410                      ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
01411                      for ( lp = lr; lp; lp = lp->lr_parent ) {
01412                             if ( lp->lr_conn == lc
01413                                    && len == lp->lr_dn.bv_len )
01414                             {
01415                                    if ( len && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) )
01416                                                  continue;
01417                                    looped = 1;
01418                                    break;
01419                             }
01420                      }
01421                      if ( looped ) {
01422                             ldap_free_urllist( srv );
01423                             ld->ld_errno = LDAP_CLIENT_LOOP;
01424                             rc = -1;
01425                             continue;
01426                      }
01427               }
01428 
01429               LDAP_NEXT_MSGID( ld, id );
01430               ber = re_encode_request( ld, origreq->lr_ber,
01431                   id, sref, srv, &rinfo.ri_request );
01432 
01433               if ( ber == NULL ) {
01434                      return -1 ;
01435               }
01436 
01437               /* copy the complete referral for rebind process */
01438               rinfo.ri_url = LDAP_STRDUP( ref );
01439 
01440               rinfo.ri_msgid = origreq->lr_origid;
01441 
01442               rc = ldap_send_server_request( ld, ber, id,
01443                      lr, &srv, NULL, &rinfo, 0, 1 );
01444               LDAP_FREE( rinfo.ri_url );
01445 
01446               if( rc >= 0 ) {
01447                      ++count;
01448               } else {
01449                      Debug( LDAP_DEBUG_ANY,
01450                             "Unable to chase referral \"%s\" (%d: %s)\n", 
01451                             ref, ld->ld_errno, ldap_err2string( ld->ld_errno ) );
01452                      rc = ldap_append_referral( ld, &unfollowed, ref );
01453               }
01454 
01455               ldap_free_urllist(srv);
01456        }
01457 
01458        LDAP_FREE( *errstrp );
01459        *errstrp = unfollowed;
01460 
01461        return(( rc == 0 ) ? count : rc );
01462 }
01463 
01464 
01465 int
01466 ldap_append_referral( LDAP *ld, char **referralsp, char *s )
01467 {
01468        int    first;
01469 
01470        if ( *referralsp == NULL ) {
01471               first = 1;
01472               *referralsp = (char *)LDAP_MALLOC( strlen( s ) + LDAP_REF_STR_LEN
01473                   + 1 );
01474        } else {
01475               first = 0;
01476               *referralsp = (char *)LDAP_REALLOC( *referralsp,
01477                   strlen( *referralsp ) + strlen( s ) + 2 );
01478        }
01479 
01480        if ( *referralsp == NULL ) {
01481               ld->ld_errno = LDAP_NO_MEMORY;
01482               return( -1 );
01483        }
01484 
01485        if ( first ) {
01486               strcpy( *referralsp, LDAP_REF_STR );
01487        } else {
01488               strcat( *referralsp, "\n" );
01489        }
01490        strcat( *referralsp, s );
01491 
01492        return( 0 );
01493 }
01494 
01495 
01496 
01497 static BerElement *
01498 re_encode_request( LDAP *ld,
01499        BerElement *origber,
01500        ber_int_t msgid,
01501        int sref,
01502        LDAPURLDesc *srv,
01503        int *type )
01504 {
01505        /*
01506         * XXX this routine knows way too much about how the lber library works!
01507         */
01508        ber_int_t     along;
01509        ber_tag_t     tag;
01510        ber_tag_t     rtag;
01511        ber_int_t     ver;
01512        ber_int_t     scope;
01513        int           rc;
01514        BerElement    tmpber, *ber;
01515        struct berval        dn;
01516 
01517        Debug( LDAP_DEBUG_TRACE,
01518            "re_encode_request: new msgid %ld, new dn <%s>\n",
01519            (long) msgid,
01520               ( srv == NULL || srv->lud_dn == NULL) ? "NONE" : srv->lud_dn, 0 );
01521 
01522        tmpber = *origber;
01523 
01524        /*
01525         * all LDAP requests are sequences that start with a message id.
01526         * For all except delete, this is followed by a sequence that is
01527         * tagged with the operation code.  For delete, the provided DN
01528         * is not wrapped by a sequence.
01529         */
01530        rtag = ber_scanf( &tmpber, "{it", /*}*/ &along, &tag );
01531 
01532        if ( rtag == LBER_ERROR ) {
01533               ld->ld_errno = LDAP_DECODING_ERROR;
01534               return( NULL );
01535        }
01536 
01537        assert( tag != 0);
01538        if ( tag == LDAP_REQ_BIND ) {
01539               /* bind requests have a version number before the DN & other stuff */
01540               rtag = ber_scanf( &tmpber, "{im" /*}*/, &ver, &dn );
01541 
01542        } else if ( tag == LDAP_REQ_DELETE ) {
01543               /* delete requests don't have a DN wrapping sequence */
01544               rtag = ber_scanf( &tmpber, "m", &dn );
01545 
01546        } else if ( tag == LDAP_REQ_SEARCH ) {
01547               /* search requests need to be re-scope-ed */
01548               rtag = ber_scanf( &tmpber, "{me" /*"}"*/, &dn, &scope );
01549 
01550               if( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
01551                      /* use the scope provided in reference */
01552                      scope = srv->lud_scope;
01553 
01554               } else if ( sref ) {
01555                      /* use scope implied by previous operation
01556                       *   base -> base
01557                       *   one -> base
01558                       *   subtree -> subtree
01559                       *   subordinate -> subtree
01560                       */
01561                      switch( scope ) {
01562                      default:
01563                      case LDAP_SCOPE_BASE:
01564                      case LDAP_SCOPE_ONELEVEL:
01565                             scope = LDAP_SCOPE_BASE;
01566                             break;
01567                      case LDAP_SCOPE_SUBTREE:
01568                      case LDAP_SCOPE_SUBORDINATE:
01569                             scope = LDAP_SCOPE_SUBTREE;
01570                             break;
01571                      }
01572               }
01573 
01574        } else {
01575               rtag = ber_scanf( &tmpber, "{m" /*}*/, &dn );
01576        }
01577 
01578        if( rtag == LBER_ERROR ) {
01579               ld->ld_errno = LDAP_DECODING_ERROR;
01580               return NULL;
01581        }
01582 
01583        /* restore character zero'd out by ber_scanf*/
01584        dn.bv_val[dn.bv_len] = tmpber.ber_tag;
01585 
01586        if (( ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
01587               return NULL;
01588        }
01589 
01590        if ( srv->lud_dn ) {
01591               ber_str2bv( srv->lud_dn, 0, 0, &dn );
01592        }
01593 
01594        if ( tag == LDAP_REQ_BIND ) {
01595               rc = ber_printf( ber, "{it{iO" /*}}*/, msgid, tag, ver, &dn );
01596        } else if ( tag == LDAP_REQ_DELETE ) {
01597               rc = ber_printf( ber, "{itON}", msgid, tag, &dn );
01598        } else if ( tag == LDAP_REQ_SEARCH ) {
01599               rc = ber_printf( ber, "{it{Oe" /*}}*/, msgid, tag, &dn, scope );
01600        } else {
01601               rc = ber_printf( ber, "{it{O" /*}}*/, msgid, tag, &dn );
01602        }
01603 
01604        if ( rc == -1 ) {
01605               ld->ld_errno = LDAP_ENCODING_ERROR;
01606               ber_free( ber, 1 );
01607               return NULL;
01608        }
01609 
01610        if ( tag != LDAP_REQ_DELETE && (
01611               ber_write(ber, tmpber.ber_ptr, ( tmpber.ber_end - tmpber.ber_ptr ), 0)
01612               != ( tmpber.ber_end - tmpber.ber_ptr ) ||
01613            ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) )
01614        {
01615               ld->ld_errno = LDAP_ENCODING_ERROR;
01616               ber_free( ber, 1 );
01617               return NULL;
01618        }
01619 
01620 #ifdef LDAP_DEBUG
01621        if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
01622               Debug( LDAP_DEBUG_ANY, "re_encode_request new request is:\n",
01623                   0, 0, 0 );
01624               ber_log_dump( LDAP_DEBUG_BER, ldap_debug, ber, 0 );
01625        }
01626 #endif /* LDAP_DEBUG */
01627 
01628        *type = tag;  /* return request type */
01629        return ber;
01630 }
01631 
01632 
01633 /* protected by req_mutex */
01634 LDAPRequest *
01635 ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid )
01636 {
01637        LDAPRequest   *lr;
01638 
01639        for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
01640               if ( lr->lr_status == LDAP_REQST_COMPLETED ) {
01641                      continue;     /* Skip completed requests */
01642               }
01643               if ( msgid == lr->lr_msgid ) {
01644                      lr->lr_refcnt++;
01645                      break;
01646               }
01647        }
01648 
01649        return( lr );
01650 }
01651 
01652 /* protected by req_mutex */
01653 void
01654 ldap_return_request( LDAP *ld, LDAPRequest *lrx, int freeit )
01655 {
01656        LDAPRequest   *lr;
01657 
01658        for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
01659               if ( lr == lrx ) {
01660                      if ( lr->lr_refcnt > 0 ) {
01661                             lr->lr_refcnt--;
01662 
01663                      } else if ( lr->lr_refcnt < 0 ) {
01664                             lr->lr_refcnt++;
01665                             if ( lr->lr_refcnt == 0 ) {
01666                                    lr = NULL;
01667                             }
01668                      }
01669                      break;
01670               }
01671        }
01672        if ( lr == NULL ) {
01673               ldap_free_request_int( ld, lrx );
01674 
01675        } else if ( freeit ) {
01676               ldap_free_request( ld, lrx );
01677        }
01678 }