+ /* find original request */
+ for ( origreq = lr;
+ origreq->lr_parent != NULL;
+ origreq = origreq->lr_parent )
+ {
+ /* empty */ ;
+ }
+
+ refarray = refs;
+ refs = NULL;
+ /* parse out & follow referrals */
+ for( i=0; refarray[i] != NULL; i++) {
+ /* Parse the referral URL */
+ if (( rc = ldap_url_parse_ext( refarray[i], &srv)) != LDAP_SUCCESS) {
+ ld->ld_errno = rc;
+ rc = -1;
+ goto done;
+ }
+
+ if( srv->lud_crit_exts ) {
+ /* we do not support any extensions */
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ rc = -1;
+ goto done;
+ }
+
+ /* treat ldap://hostpart and ldap://hostpart/ the same */
+ if ( srv->lud_dn && srv->lud_dn[0] == '\0' ) {
+ LDAP_FREE( srv->lud_dn );
+ srv->lud_dn = NULL;
+ }
+
+ /* check connection for re-bind in progress */
+ if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
+ if( lc->lconn_rebind_inprogress) {
+ /* We are already chasing a referral or search reference and a
+ * bind on that connection is in progress. We must queue
+ * referrals on that connection, so we don't get a request
+ * going out before the bind operation completes. This happens
+ * if two search references come in one behind the other
+ * for the same server with different contexts.
+ */
+#ifdef NEW_LOGGING
+ LDAP_LOG (( "request", LDAP_LEVEL_DETAIL1,
+ "ldap_chase_v3referrals: queue referral \"%s\"\n",
+ refarray[i] ));
+#else
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_chase_v3referrals: queue referral \"%s\"\n",
+ refarray[i], 0, 0);
+#endif
+ if( lc->lconn_rebind_queue == NULL ) {
+ /* Create a referral list */
+ lc->lconn_rebind_queue =
+ (char ***) LDAP_MALLOC( sizeof(void *) * 2);
+
+ if( lc->lconn_rebind_queue == NULL) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ rc = -1;
+ goto done;
+ }
+
+ lc->lconn_rebind_queue[0] = refarray;
+ lc->lconn_rebind_queue[1] = NULL;
+ refarray = NULL;
+
+ } else {
+ /* Count how many referral arrays we already have */
+ for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) {
+ /* empty */;
+ }
+
+ /* Add the new referral to the list */
+ lc->lconn_rebind_queue = (char ***) LDAP_REALLOC(
+ lc->lconn_rebind_queue, sizeof(void *) * (j + 2));
+
+ if( lc->lconn_rebind_queue == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ rc = -1;
+ goto done;
+ }
+ lc->lconn_rebind_queue[j] = refarray;
+ lc->lconn_rebind_queue[j+1] = NULL;
+ refarray = NULL;
+ }
+
+ /* We have queued the referral/reference, now just return */
+ rc = 0;
+ *hadrefp = 1;
+ count = 1; /* Pretend we already followed referral */
+ goto done;
+ }
+ }
+ /* Re-encode the request with the new starting point of the search.
+ * Note: In the future we also need to replace the filter if one
+ * was provided with the search reference
+ */
+
+ /* For references we don't want old dn if new dn empty */
+ if ( sref && srv->lud_dn == NULL ) {
+ srv->lud_dn = LDAP_STRDUP( "" );
+ }
+
+ ber = re_encode_request( ld, origreq->lr_ber, ++ld->ld_msgid,
+ sref, srv, &rinfo.ri_request );
+
+ if( ber == NULL ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ rc = -1;
+ goto done;
+ }
+
+#ifdef NEW_LOGGING
+ LDAP_LOG (( "request", LDAP_LEVEL_DETAIL1,
+ "ldap_chase_v3referrals: msgid %d, url \"%s\"\n",
+ lr->lr_msgid, refarray[i] ));
+#else
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_chase_v3referral: msgid %d, url \"%s\"\n",
+ lr->lr_msgid, refarray[i], 0);
+#endif
+
+ /* Send the new request to the server - may require a bind */
+ rinfo.ri_msgid = origreq->lr_origid;
+ rinfo.ri_url = refarray[i];
+ if ( (rc = ldap_send_server_request( ld, ber, ld->ld_msgid,
+ origreq, srv, NULL, &rinfo )) < 0 ) {
+ /* Failure, try next referral in the list */
+#ifdef NEW_LOGGING
+ LDAP_LOG (( "request", LDAP_LEVEL_ERR,
+ "ldap_chase_v3referrals: Unable to chase referral \"%s\" (%s)\n",
+ refarray[i], ldap_err2string( ld->ld_errno ) ));
+#else
+ Debug( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%s)\n",
+ refarray[i], ldap_err2string( ld->ld_errno ), 0);
+#endif
+ unfollowedcnt += ldap_append_referral( ld, &unfollowed, refarray[i]);
+ ldap_free_urllist(srv);
+ srv = NULL;
+ } else {
+ /* Success, no need to try this referral list further */
+ rc = 0;
+ ++count;
+ *hadrefp = 1;
+
+ /* check if there is a queue of referrals that came in during bind */
+ if( lc == NULL) {
+ if (( lc = find_connection( ld, srv, 1 )) == NULL ) {
+ ld->ld_errno = LDAP_OPERATIONS_ERROR;
+ rc = -1;
+ goto done;
+ }
+ }
+
+ if( lc->lconn_rebind_queue != NULL) {
+ /* Release resources of previous list */
+ LDAP_VFREE(refarray);
+ refarray = NULL;
+ ldap_free_urllist(srv);
+ srv = NULL;
+
+ /* Pull entries off end of queue so list always null terminated */
+ for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) {
+ ;
+ }
+ refarray = lc->lconn_rebind_queue[j-1];
+ lc->lconn_rebind_queue[j-1] = NULL;
+ /* we pulled off last entry from queue, free queue */
+ if ( j == 1 ) {
+ LDAP_FREE( lc->lconn_rebind_queue);
+ lc->lconn_rebind_queue = NULL;
+ }
+ /* restart the loop the with new referral list */
+ i = -1;
+ continue;
+ }
+ break; /* referral followed, break out of for loop */
+ }
+ } /* end for loop */
+done:
+ LDAP_VFREE(refarray);
+ ldap_free_urllist(srv);
+ LDAP_FREE( *errstrp );
+
+ if( rc == 0) {
+ *errstrp = NULL;
+ LDAP_FREE( unfollowed );
+ return count;
+ } else {
+ ld->ld_errno = LDAP_REFERRAL;
+ *errstrp = unfollowed;
+ return rc;
+ }
+}