Back to index

openldap  2.4.31
result.c
Go to the documentation of this file.
00001 /* result.c - wait for an ldap result */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 1998-2012 The OpenLDAP Foundation.
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted only as authorized by the OpenLDAP
00010  * Public License.
00011  *
00012  * A copy of this license is available in the file LICENSE in the
00013  * top-level directory of the distribution or, alternatively, at
00014  * <http://www.OpenLDAP.org/license.html>.
00015  */
00016 /* Portions Copyright (c) 1990 Regents of the University of Michigan.
00017  * All rights reserved.
00018  */
00019 /* This notice applies to changes, created by or for Novell, Inc.,
00020  * to preexisting works for which notices appear elsewhere in this file.
00021  *
00022  * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
00023  *
00024  * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
00025  * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
00026  * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
00027  * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
00028  * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
00029  * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
00030  * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
00031  * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 
00032  *---
00033  * Modification to OpenLDAP source by Novell, Inc.
00034  * April 2000 sfs Add code to process V3 referrals and search results
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 /*
00042  * LDAPv3 (RFC 4511)
00043  *     LDAPResult ::= SEQUENCE {
00044  *            resultCode                  ENUMERATED { ... },
00045  *            matchedDN                   LDAPDN,
00046  *            diagnosticMessage           LDAPString,
00047  *            referral                    [3] Referral OPTIONAL
00048  *     }
00049  *     Referral ::= SEQUENCE OF LDAPURL   (one or more)
00050  *     LDAPURL ::= LDAPString                    (limited to URL chars)
00051  */
00052 
00053 #include "portable.h"
00054 
00055 #include <stdio.h>
00056 
00057 #include <ac/stdlib.h>
00058 
00059 #include <ac/errno.h>
00060 #include <ac/socket.h>
00061 #include <ac/string.h>
00062 #include <ac/time.h>
00063 #include <ac/unistd.h>
00064 
00065 #include "ldap-int.h"
00066 #include "ldap_log.h"
00067 #include "lutil.h"
00068 
00069 static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
00070 static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
00071 static int wait4msg LDAP_P(( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout,
00072        LDAPMessage **result ));
00073 static ber_tag_t try_read1msg LDAP_P(( LDAP *ld, ber_int_t msgid,
00074        int all, LDAPConn *lc, LDAPMessage **result ));
00075 static ber_tag_t build_result_ber LDAP_P(( LDAP *ld, BerElement **bp, LDAPRequest *lr ));
00076 static void merge_error_info LDAP_P(( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ));
00077 static LDAPMessage * chkResponseList LDAP_P(( LDAP *ld, int msgid, int all));
00078 
00079 #define LDAP_MSG_X_KEEP_LOOKING           (-2)
00080 
00081 
00082 /*
00083  * ldap_result - wait for an ldap result response to a message from the
00084  * ldap server.  If msgid is LDAP_RES_ANY (-1), any message will be
00085  * accepted.  If msgid is LDAP_RES_UNSOLICITED (0), any unsolicited
00086  * message is accepted.  Otherwise ldap_result will wait for a response
00087  * with msgid.  If all is LDAP_MSG_ONE (0) the first message with id
00088  * msgid will be accepted, otherwise, ldap_result will wait for all
00089  * responses with id msgid and then return a pointer to the entire list
00090  * of messages.  In general, this is only useful for search responses,
00091  * which can be of three message types (zero or more entries, zero or
00092  * search references, followed by an ldap result).  An extension to
00093  * LDAPv3 allows partial extended responses to be returned in response
00094  * to any request.  The type of the first message received is returned.
00095  * When waiting, any messages that have been abandoned/discarded are 
00096  * discarded.
00097  *
00098  * Example:
00099  *     ldap_result( s, msgid, all, timeout, result )
00100  */
00101 int
00102 ldap_result(
00103        LDAP *ld,
00104        int msgid,
00105        int all,
00106        struct timeval *timeout,
00107        LDAPMessage **result )
00108 {
00109        int           rc;
00110 
00111        assert( ld != NULL );
00112        assert( result != NULL );
00113 
00114        Debug( LDAP_DEBUG_TRACE, "ldap_result ld %p msgid %d\n", (void *)ld, msgid, 0 );
00115 
00116        LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
00117        rc = wait4msg( ld, msgid, all, timeout, result );
00118        LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
00119 
00120        return rc;
00121 }
00122 
00123 /* protected by res_mutex */
00124 static LDAPMessage *
00125 chkResponseList(
00126        LDAP *ld,
00127        int msgid,
00128        int all)
00129 {
00130        LDAPMessage   *lm, **lastlm, *nextlm;
00131        int           cnt = 0;
00132 
00133        /*
00134         * Look through the list of responses we have received on
00135         * this association and see if the response we're interested in
00136         * is there.  If it is, return it.  If not, call wait4msg() to
00137         * wait until it arrives or timeout occurs.
00138         */
00139 
00140        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
00141 
00142        Debug( LDAP_DEBUG_TRACE,
00143               "ldap_chkResponseList ld %p msgid %d all %d\n",
00144               (void *)ld, msgid, all );
00145 
00146        lastlm = &ld->ld_responses;
00147        for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
00148               nextlm = lm->lm_next;
00149               ++cnt;
00150 
00151               if ( ldap_abandoned( ld, lm->lm_msgid ) ) {
00152                      Debug( LDAP_DEBUG_ANY,
00153                             "response list msg abandoned, "
00154                             "msgid %d message type %s\n",
00155                             lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ), 0 );
00156 
00157                      switch ( lm->lm_msgtype ) {
00158                      case LDAP_RES_SEARCH_ENTRY:
00159                      case LDAP_RES_SEARCH_REFERENCE:
00160                      case LDAP_RES_INTERMEDIATE:
00161                             break;
00162 
00163                      default:
00164                             /* there's no need to keep the id
00165                              * in the abandoned list any longer */
00166                             ldap_mark_abandoned( ld, lm->lm_msgid );
00167                             break;
00168                      }
00169 
00170                      /* Remove this entry from list */
00171                      *lastlm = nextlm;
00172 
00173                      ldap_msgfree( lm );
00174 
00175                      continue;
00176               }
00177 
00178               if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
00179                      LDAPMessage   *tmp;
00180 
00181                      if ( all == LDAP_MSG_ONE ||
00182                             all == LDAP_MSG_RECEIVED ||
00183                             msgid == LDAP_RES_UNSOLICITED )
00184                      {
00185                             break;
00186                      }
00187 
00188                      tmp = lm->lm_chain_tail;
00189                      if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY ||
00190                             tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ||
00191                             tmp->lm_msgtype == LDAP_RES_INTERMEDIATE )
00192                      {
00193                             tmp = NULL;
00194                      }
00195 
00196                      if ( tmp == NULL ) {
00197                             lm = NULL;
00198                      }
00199 
00200                      break;
00201               }
00202               lastlm = &lm->lm_next;
00203        }
00204 
00205        if ( lm != NULL ) {
00206               /* Found an entry, remove it from the list */
00207               if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) {
00208                      *lastlm = lm->lm_chain;
00209                      lm->lm_chain->lm_next = lm->lm_next;
00210                      lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain;
00211                      lm->lm_chain = NULL;
00212                      lm->lm_chain_tail = NULL;
00213               } else {
00214                      *lastlm = lm->lm_next;
00215               }
00216               lm->lm_next = NULL;
00217        }
00218 
00219 #ifdef LDAP_DEBUG
00220        if ( lm == NULL) {
00221               Debug( LDAP_DEBUG_TRACE,
00222                      "ldap_chkResponseList returns ld %p NULL\n", (void *)ld, 0, 0);
00223        } else {
00224               Debug( LDAP_DEBUG_TRACE,
00225                      "ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n",
00226                      (void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
00227        }
00228 #endif
00229 
00230        return lm;
00231 }
00232 
00233 /* protected by res_mutex */
00234 static int
00235 wait4msg(
00236        LDAP *ld,
00237        ber_int_t msgid,
00238        int all,
00239        struct timeval *timeout,
00240        LDAPMessage **result )
00241 {
00242        int           rc;
00243        struct timeval       tv = { 0 },
00244                      tv0 = { 0 },
00245                      start_time_tv = { 0 },
00246                      *tvp = NULL;
00247        LDAPConn      *lc;
00248 
00249        assert( ld != NULL );
00250        assert( result != NULL );
00251 
00252        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
00253 
00254        if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) {
00255               tv = ld->ld_options.ldo_tm_api;
00256               timeout = &tv;
00257        }
00258 
00259 #ifdef LDAP_DEBUG
00260        if ( timeout == NULL ) {
00261               Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
00262                      (void *)ld, msgid, 0 );
00263        } else {
00264               Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n",
00265                      (void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec );
00266        }
00267 #endif /* LDAP_DEBUG */
00268 
00269        if ( timeout != NULL && timeout->tv_sec != -1 ) {
00270               tv0 = *timeout;
00271               tv = *timeout;
00272               tvp = &tv;
00273 #ifdef HAVE_GETTIMEOFDAY
00274               gettimeofday( &start_time_tv, NULL );
00275 #else /* ! HAVE_GETTIMEOFDAY */
00276               time( &start_time_tv.tv_sec );
00277               start_time_tv.tv_usec = 0;
00278 #endif /* ! HAVE_GETTIMEOFDAY */
00279        }
00280                   
00281        rc = LDAP_MSG_X_KEEP_LOOKING;
00282        while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
00283 #ifdef LDAP_DEBUG
00284               if ( ldap_debug & LDAP_DEBUG_TRACE ) {
00285                      Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
00286                             (void *)ld, msgid, all );
00287                      ldap_dump_connection( ld, ld->ld_conns, 1 );
00288                      LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
00289                      ldap_dump_requests_and_responses( ld );
00290                      LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
00291               }
00292 #endif /* LDAP_DEBUG */
00293 
00294               if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
00295                      rc = (*result)->lm_msgtype;
00296 
00297               } else {
00298                      int lc_ready = 0;
00299 
00300                      LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
00301                      for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
00302                             if ( ber_sockbuf_ctrl( lc->lconn_sb,
00303                                    LBER_SB_OPT_DATA_READY, NULL ) )
00304                             {
00305                                    lc_ready = 2; /* ready at ber level, not socket level */
00306                                    break;
00307                             }
00308                      }
00309 
00310                      if ( !lc_ready ) {
00311                             int err;
00312                             rc = ldap_int_select( ld, tvp );
00313                             if ( rc == -1 ) {
00314                                    err = sock_errno();
00315 #ifdef LDAP_DEBUG
00316                                    Debug( LDAP_DEBUG_TRACE,
00317                                           "ldap_int_select returned -1: errno %d\n",
00318                                           err, 0, 0 );
00319 #endif
00320                             }
00321 
00322                             if ( rc == 0 || ( rc == -1 && (
00323                                    !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
00324                                           || err != EINTR ) ) )
00325                             {
00326                                    ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
00327                                           LDAP_TIMEOUT);
00328                                    LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
00329                                    return( rc );
00330                             }
00331 
00332                             if ( rc == -1 ) {
00333                                    rc = LDAP_MSG_X_KEEP_LOOKING;      /* select interrupted: loop */
00334 
00335                             } else {
00336                                    lc_ready = 1;
00337                             }
00338                      }
00339                      if ( lc_ready ) {
00340                             LDAPConn *lnext;
00341                             int serviced = 0;
00342                             rc = LDAP_MSG_X_KEEP_LOOKING;
00343                             LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
00344                             if ( ld->ld_requests &&
00345                                    ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
00346                                    ldap_is_write_ready( ld,
00347                                           ld->ld_requests->lr_conn->lconn_sb ) )
00348                             {
00349                                    serviced = 1;
00350                                    ldap_int_flush_request( ld, ld->ld_requests );
00351                             }
00352                             for ( lc = ld->ld_conns;
00353                                    rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL;
00354                                    lc = lnext )
00355                             {
00356                                    if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
00357                                           ldap_is_read_ready( ld, lc->lconn_sb ) )
00358                                    {
00359                                           serviced = 1;
00360                                           /* Don't let it get freed out from under us */
00361                                           ++lc->lconn_refcnt;
00362                                           rc = try_read1msg( ld, msgid, all, lc, result );
00363                                           lnext = lc->lconn_next;
00364 
00365                                           /* Only take locks if we're really freeing */
00366                                           if ( lc->lconn_refcnt <= 1 ) {
00367                                                  ldap_free_connection( ld, lc, 0, 1 );
00368                                           } else {
00369                                                  --lc->lconn_refcnt;
00370                                           }
00371                                    } else {
00372                                           lnext = lc->lconn_next;
00373                                    }
00374                             }
00375                             LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
00376                             /* Quit looping if no one handled any socket events */
00377                             if (!serviced && lc_ready == 1)
00378                                    rc = -1;
00379                      }
00380                      LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
00381               }
00382 
00383               if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
00384                      struct timeval       curr_time_tv = { 0 },
00385                                    delta_time_tv = { 0 };
00386 
00387 #ifdef HAVE_GETTIMEOFDAY
00388                      gettimeofday( &curr_time_tv, NULL );
00389 #else /* ! HAVE_GETTIMEOFDAY */
00390                      time( &curr_time_tv.tv_sec );
00391                      curr_time_tv.tv_usec = 0;
00392 #endif /* ! HAVE_GETTIMEOFDAY */
00393 
00394                      /* delta_time = tmp_time - start_time */
00395                      delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
00396                      delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
00397                      if ( delta_time_tv.tv_usec < 0 ) {
00398                             delta_time_tv.tv_sec--;
00399                             delta_time_tv.tv_usec += 1000000;
00400                      }
00401 
00402                      /* tv0 < delta_time ? */
00403                      if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
00404                           ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
00405                      {
00406                             rc = 0; /* timed out */
00407                             ld->ld_errno = LDAP_TIMEOUT;
00408                             break;
00409                      }
00410 
00411                      /* tv0 -= delta_time */
00412                      tv0.tv_sec -= delta_time_tv.tv_sec;
00413                      tv0.tv_usec -= delta_time_tv.tv_usec;
00414                      if ( tv0.tv_usec < 0 ) {
00415                             tv0.tv_sec--;
00416                             tv0.tv_usec += 1000000;
00417                      }
00418 
00419                      tv.tv_sec = tv0.tv_sec;
00420                      tv.tv_usec = tv0.tv_usec;
00421 
00422                      Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n",
00423                             (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
00424 
00425                      start_time_tv.tv_sec = curr_time_tv.tv_sec;
00426                      start_time_tv.tv_usec = curr_time_tv.tv_usec;
00427               }
00428        }
00429 
00430        return( rc );
00431 }
00432 
00433 
00434 /* protected by res_mutex, conn_mutex and req_mutex */
00435 static ber_tag_t
00436 try_read1msg(
00437        LDAP *ld,
00438        ber_int_t msgid,
00439        int all,
00440        LDAPConn *lc,
00441        LDAPMessage **result )
00442 {
00443        BerElement    *ber;
00444        LDAPMessage   *newmsg, *l, *prev;
00445        ber_int_t     id;
00446        ber_tag_t     tag;
00447        ber_len_t     len;
00448        int           foundit = 0;
00449        LDAPRequest   *lr, *tmplr, dummy_lr = { 0 };
00450        BerElement    tmpber;
00451        int           rc, refer_cnt, hadref, simple_request, err;
00452        ber_int_t     lderr;
00453 
00454 #ifdef LDAP_CONNECTIONLESS
00455        LDAPMessage   *tmp = NULL, *chain_head = NULL;
00456        int           moremsgs = 0, isv2 = 0;
00457 #endif
00458 
00459        assert( ld != NULL );
00460        assert( lc != NULL );
00461        
00462        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
00463        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
00464        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
00465 
00466        Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
00467               (void *)ld, msgid, all );
00468 
00469 retry:
00470        if ( lc->lconn_ber == NULL ) {
00471               lc->lconn_ber = ldap_alloc_ber_with_options( ld );
00472 
00473               if ( lc->lconn_ber == NULL ) {
00474                      return -1;
00475               }
00476        }
00477 
00478        ber = lc->lconn_ber;
00479        assert( LBER_VALID (ber) );
00480 
00481        /* get the next message */
00482        sock_errset(0);
00483 #ifdef LDAP_CONNECTIONLESS
00484        if ( LDAP_IS_UDP(ld) ) {
00485               struct sockaddr from;
00486               ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr) );
00487               if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
00488        }
00489 nextresp3:
00490 #endif
00491        tag = ber_get_next( lc->lconn_sb, &len, ber );
00492        switch ( tag ) {
00493        case LDAP_TAG_MESSAGE:
00494               /*
00495                * We read a complete message.
00496                * The connection should no longer need this ber.
00497                */
00498               lc->lconn_ber = NULL;
00499               break;
00500 
00501        case LBER_DEFAULT:
00502               err = sock_errno();
00503 #ifdef LDAP_DEBUG              
00504               Debug( LDAP_DEBUG_CONNS,
00505                      "ber_get_next failed.\n", 0, 0, 0 );
00506 #endif           
00507               if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
00508               if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
00509               ld->ld_errno = LDAP_SERVER_DOWN;
00510               --lc->lconn_refcnt;
00511               lc->lconn_status = 0;
00512               return -1;
00513 
00514        default:
00515               ld->ld_errno = LDAP_LOCAL_ERROR;
00516               return -1;
00517        }
00518 
00519        /* message id */
00520        if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
00521               ber_free( ber, 1 );
00522               ld->ld_errno = LDAP_DECODING_ERROR;
00523               return( -1 );
00524        }
00525 
00526        /* id == 0 iff unsolicited notification message (RFC 4511) */
00527 
00528        /* id < 0 is invalid, just toss it. FIXME: should we disconnect? */
00529        if ( id < 0 ) {
00530               goto retry_ber;
00531        }
00532        
00533        /* if it's been abandoned, toss it */
00534        if ( id > 0 ) {
00535               if ( ldap_abandoned( ld, id ) ) {
00536                      /* the message type */
00537                      tag = ber_peek_tag( ber, &len );
00538                      switch ( tag ) {
00539                      case LDAP_RES_SEARCH_ENTRY:
00540                      case LDAP_RES_SEARCH_REFERENCE:
00541                      case LDAP_RES_INTERMEDIATE:
00542                      case LBER_ERROR:
00543                             break;
00544 
00545                      default:
00546                             /* there's no need to keep the id
00547                              * in the abandoned list any longer */
00548                             ldap_mark_abandoned( ld, id );
00549                             break;
00550                      }
00551 
00552                      Debug( LDAP_DEBUG_ANY,
00553                             "abandoned/discarded ld %p msgid %d message type %s\n",
00554                             (void *)ld, id, ldap_int_msgtype2str( tag ) );
00555 
00556 retry_ber:
00557                      ber_free( ber, 1 );
00558                      if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
00559                             goto retry;
00560                      }
00561                      return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
00562               }
00563 
00564               lr = ldap_find_request_by_msgid( ld, id );
00565               if ( lr == NULL ) {
00566                      const char    *msg = "unknown";
00567 
00568                      /* the message type */
00569                      tag = ber_peek_tag( ber, &len );
00570                      switch ( tag ) {
00571                      case LBER_ERROR:
00572                             break;
00573 
00574                      default:
00575                             msg = ldap_int_msgtype2str( tag );
00576                             break;
00577                      }
00578 
00579                      Debug( LDAP_DEBUG_ANY,
00580                             "no request for response on ld %p msgid %d message type %s (tossing)\n",
00581                             (void *)ld, id, msg );
00582 
00583                      goto retry_ber;
00584               }
00585 
00586 #ifdef LDAP_CONNECTIONLESS
00587               if ( LDAP_IS_UDP(ld) && isv2 ) {
00588                      ber_scanf(ber, "x{");
00589               }
00590 nextresp2:
00591               ;
00592 #endif
00593        }
00594 
00595        /* the message type */
00596        tag = ber_peek_tag( ber, &len );
00597        if ( tag == LBER_ERROR ) {
00598               ld->ld_errno = LDAP_DECODING_ERROR;
00599               ber_free( ber, 1 );
00600               return( -1 );
00601        }
00602 
00603        Debug( LDAP_DEBUG_TRACE,
00604               "read1msg: ld %p msgid %d message type %s\n",
00605               (void *)ld, id, ldap_int_msgtype2str( tag ) );
00606 
00607        if ( id == 0 ) {
00608               /* unsolicited notification message (RFC 4511) */
00609               if ( tag != LDAP_RES_EXTENDED ) {
00610                      /* toss it */
00611                      goto retry_ber;
00612 
00613                      /* strictly speaking, it's an error; from RFC 4511:
00614 
00615 4.4.  Unsolicited Notification
00616 
00617    An unsolicited notification is an LDAPMessage sent from the server to
00618    the client that is not in response to any LDAPMessage received by the
00619    server.  It is used to signal an extraordinary condition in the
00620    server or in the LDAP session between the client and the server.  The
00621    notification is of an advisory nature, and the server will not expect
00622    any response to be returned from the client.
00623 
00624    The unsolicited notification is structured as an LDAPMessage in which
00625    the messageID is zero and protocolOp is set to the extendedResp
00626    choice using the ExtendedResponse type (See Section 4.12).  The
00627    responseName field of the ExtendedResponse always contains an LDAPOID
00628    that is unique for this notification.
00629 
00630                       * however, since unsolicited responses
00631                       * are of advisory nature, better
00632                       * toss it, right now
00633                       */
00634 
00635 #if 0
00636                      ld->ld_errno = LDAP_DECODING_ERROR;
00637                      ber_free( ber, 1 );
00638                      return( -1 );
00639 #endif
00640               }
00641 
00642               lr = &dummy_lr;
00643        }
00644 
00645        id = lr->lr_origid;
00646        refer_cnt = 0;
00647        hadref = simple_request = 0;
00648        rc = LDAP_MSG_X_KEEP_LOOKING;      /* default is to keep looking (no response found) */
00649        lr->lr_res_msgtype = tag;
00650 
00651        /*
00652         * Check for V3 search reference
00653         */
00654        if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
00655               if ( ld->ld_version > LDAP_VERSION2 ) {
00656                      /* This is a V3 search reference */
00657                      if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
00658                                    lr->lr_parent != NULL )
00659                      {
00660                             char **refs = NULL;
00661                             tmpber = *ber;
00662 
00663                             /* Get the referral list */
00664                             if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
00665                                    rc = LDAP_DECODING_ERROR;
00666 
00667                             } else {
00668                                    /* Note: refs array is freed by ldap_chase_v3referrals */
00669                                    refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
00670                                           1, &lr->lr_res_error, &hadref );
00671                                    if ( refer_cnt > 0 ) {
00672                                           /* successfully chased reference */
00673                                           /* If haven't got end search, set chasing referrals */
00674                                           if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
00675                                                  lr->lr_status = LDAP_REQST_CHASINGREFS;
00676                                                  Debug( LDAP_DEBUG_TRACE,
00677                                                         "read1msg:  search ref chased, "
00678                                                         "mark request chasing refs, "
00679                                                         "id = %d\n",
00680                                                         lr->lr_msgid, 0, 0 );
00681                                           }
00682                                    }
00683                             }
00684                      }
00685               }
00686 
00687        } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
00688               /* All results that just return a status, i.e. don't return data
00689                * go through the following code.  This code also chases V2 referrals
00690                * and checks if all referrals have been chased.
00691                */
00692               char          *lr_res_error = NULL;
00693 
00694               tmpber = *ber;       /* struct copy */
00695               if ( ber_scanf( &tmpber, "{eAA", &lderr,
00696                             &lr->lr_res_matched, &lr_res_error )
00697                             != LBER_ERROR )
00698               {
00699                      if ( lr_res_error != NULL ) {
00700                             if ( lr->lr_res_error != NULL ) {
00701                                    (void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
00702                                    LDAP_FREE( (char *)lr_res_error );
00703 
00704                             } else {
00705                                    lr->lr_res_error = lr_res_error;
00706                             }
00707                             lr_res_error = NULL;
00708                      }
00709 
00710                      /* Do we need to check for referrals? */
00711                      if ( tag != LDAP_RES_BIND &&
00712                             ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
00713                                    lr->lr_parent != NULL ))
00714                      {
00715                             char          **refs = NULL;
00716                             ber_len_t     len;
00717 
00718                             /* Check if V3 referral */
00719                             if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
00720                                    if ( ld->ld_version > LDAP_VERSION2 ) {
00721                                           /* Get the referral list */
00722                                           if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
00723                                                  rc = LDAP_DECODING_ERROR;
00724                                                  lr->lr_status = LDAP_REQST_COMPLETED;
00725                                                  Debug( LDAP_DEBUG_TRACE,
00726                                                         "read1msg: referral decode error, "
00727                                                         "mark request completed, ld %p msgid %d\n",
00728                                                         (void *)ld, lr->lr_msgid, 0 );
00729 
00730                                           } else {
00731                                                  /* Chase the referral 
00732                                                   * refs array is freed by ldap_chase_v3referrals
00733                                                   */
00734                                                  refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
00735                                                         0, &lr->lr_res_error, &hadref );
00736                                                  lr->lr_status = LDAP_REQST_COMPLETED;
00737                                                  Debug( LDAP_DEBUG_TRACE,
00738                                                         "read1msg: referral %s chased, "
00739                                                         "mark request completed, ld %p msgid %d\n",
00740                                                         refer_cnt > 0 ? "" : "not",
00741                                                         (void *)ld, lr->lr_msgid);
00742                                                  if ( refer_cnt < 0 ) {
00743                                                         refer_cnt = 0;
00744                                                  }
00745                                           }
00746                                    }
00747                             } else {
00748                                    switch ( lderr ) {
00749                                    case LDAP_SUCCESS:
00750                                    case LDAP_COMPARE_TRUE:
00751                                    case LDAP_COMPARE_FALSE:
00752                                           break;
00753 
00754                                    default:
00755                                           if ( lr->lr_res_error == NULL ) {
00756                                                  break;
00757                                           }
00758 
00759                                           /* pedantic, should never happen */
00760                                           if ( lr->lr_res_error[ 0 ] == '\0' ) {
00761                                                  LDAP_FREE( lr->lr_res_error );
00762                                                  lr->lr_res_error = NULL;
00763                                                  break; 
00764                                           }
00765 
00766                                           /* V2 referrals are in error string */
00767                                           refer_cnt = ldap_chase_referrals( ld, lr,
00768                                                  &lr->lr_res_error, -1, &hadref );
00769                                           lr->lr_status = LDAP_REQST_COMPLETED;
00770                                           Debug( LDAP_DEBUG_TRACE,
00771                                                  "read1msg:  V2 referral chased, "
00772                                                  "mark request completed, id = %d\n",
00773                                                  lr->lr_msgid, 0, 0 );
00774                                           break;
00775                                    }
00776                             }
00777                      }
00778 
00779                      /* save errno, message, and matched string */
00780                      if ( !hadref || lr->lr_res_error == NULL ) {
00781                             lr->lr_res_errno =
00782                                    lderr == LDAP_PARTIAL_RESULTS
00783                                    ? LDAP_SUCCESS : lderr;
00784 
00785                      } else if ( ld->ld_errno != LDAP_SUCCESS ) {
00786                             lr->lr_res_errno = ld->ld_errno;
00787 
00788                      } else {
00789                             lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
00790                      }
00791               }
00792 
00793               /* in any case, don't leave any lr_res_error 'round */
00794               if ( lr_res_error ) {
00795                      LDAP_FREE( lr_res_error );
00796               }
00797 
00798               Debug( LDAP_DEBUG_TRACE,
00799                      "read1msg: ld %p %d new referrals\n",
00800                      (void *)ld, refer_cnt, 0 );
00801 
00802               if ( refer_cnt != 0 ) {     /* chasing referrals */
00803                      ber_free( ber, 1 );
00804                      ber = NULL;
00805                      if ( refer_cnt < 0 ) {
00806                             ldap_return_request( ld, lr, 0 );
00807                             return( -1 ); /* fatal error */
00808                      }
00809                      lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
00810                      if ( lr->lr_res_matched ) {
00811                             LDAP_FREE( lr->lr_res_matched );
00812                             lr->lr_res_matched = NULL;
00813                      }
00814 
00815               } else {
00816                      if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
00817                             /* request without any referrals */
00818                             simple_request = ( hadref ? 0 : 1 );
00819 
00820                      } else {
00821                             /* request with referrals or child request */
00822                             ber_free( ber, 1 );
00823                             ber = NULL;
00824                      }
00825 
00826                      lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
00827                      Debug( LDAP_DEBUG_TRACE,
00828                             "read1msg:  mark request completed, ld %p msgid %d\n",
00829                             (void *)ld, lr->lr_msgid, 0);
00830                      tmplr = lr;
00831                      while ( lr->lr_parent != NULL ) {
00832                             merge_error_info( ld, lr->lr_parent, lr );
00833 
00834                             lr = lr->lr_parent;
00835                             if ( --lr->lr_outrefcnt > 0 ) {
00836                                    break; /* not completely done yet */
00837                             }
00838                      }
00839                      /* ITS#6744: Original lr was refcounted when we retrieved it,
00840                       * must release it now that we're working with the parent
00841                       */
00842                      if ( tmplr->lr_parent ) {
00843                             ldap_return_request( ld, tmplr, 0 );
00844                      }
00845 
00846                      /* Check if all requests are finished, lr is now parent */
00847                      tmplr = lr;
00848                      if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
00849                             for ( tmplr = lr->lr_child;
00850                                    tmplr != NULL;
00851                                    tmplr = tmplr->lr_refnext )
00852                             {
00853                                    if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
00854                             }
00855                      }
00856 
00857                      /* This is the parent request if the request has referrals */
00858                      if ( lr->lr_outrefcnt <= 0 &&
00859                             lr->lr_parent == NULL &&
00860                             tmplr == NULL )
00861                      {
00862                             id = lr->lr_msgid;
00863                             tag = lr->lr_res_msgtype;
00864                             Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
00865                                    (void *)ld, id, 0 );
00866                             Debug( LDAP_DEBUG_TRACE,
00867                                    "res_errno: %d, res_error: <%s>, "
00868                                    "res_matched: <%s>\n",
00869                                    lr->lr_res_errno,
00870                                    lr->lr_res_error ? lr->lr_res_error : "",
00871                                    lr->lr_res_matched ? lr->lr_res_matched : "" );
00872                             if ( !simple_request ) {
00873                                    ber_free( ber, 1 );
00874                                    ber = NULL;
00875                                    if ( build_result_ber( ld, &ber, lr )
00876                                        == LBER_ERROR )
00877                                    {
00878                                           rc = -1; /* fatal error */
00879                                    }
00880                             }
00881 
00882                             if ( lr != &dummy_lr ) {
00883                                    ldap_return_request( ld, lr, 1 );
00884                             }
00885                             lr = NULL;
00886                      }
00887 
00888                      /*
00889                       * RFC 4511 unsolicited (id == 0) responses
00890                       * shouldn't necessarily end the connection
00891                       */
00892                      if ( lc != NULL && id != 0 ) {
00893                             --lc->lconn_refcnt;
00894                             lc = NULL;
00895                      }
00896               }
00897        }
00898 
00899        if ( lr != NULL ) {
00900               if ( lr != &dummy_lr ) {
00901                      ldap_return_request( ld, lr, 0 );
00902               }
00903               lr = NULL;
00904        }
00905 
00906        if ( ber == NULL ) {
00907               return( rc );
00908        }
00909 
00910        /* try to handle unsolicited responses as appropriate */
00911        if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
00912               int    is_nod = 0;
00913 
00914               tag = ber_peek_tag( &tmpber, &len );
00915 
00916               /* we have a res oid */
00917               if ( tag == LDAP_TAG_EXOP_RES_OID ) {
00918                      static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
00919                      struct berval        resoid = BER_BVNULL;
00920 
00921                      if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
00922                             ld->ld_errno = LDAP_DECODING_ERROR;
00923                             ber_free( ber, 1 );
00924                             return -1;
00925                      }
00926 
00927                      assert( !BER_BVISEMPTY( &resoid ) );
00928 
00929                      is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
00930 
00931                      tag = ber_peek_tag( &tmpber, &len );
00932               }
00933 
00934 #if 0 /* don't need right now */
00935               /* we have res data */
00936               if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
00937                      struct berval resdata;
00938 
00939                      if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
00940                             ld->ld_errno = LDAP_DECODING_ERROR;
00941                             ber_free( ber, 0 );
00942                             return ld->ld_errno;
00943                      }
00944 
00945                      /* use it... */
00946               }
00947 #endif
00948 
00949               /* handle RFC 4511 "Notice of Disconnection" locally */
00950 
00951               if ( is_nod ) {
00952                      if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
00953                             ld->ld_errno = LDAP_DECODING_ERROR;
00954                             ber_free( ber, 1 );
00955                             return -1;
00956                      }
00957 
00958                      /* get rid of the connection... */
00959                      if ( lc != NULL ) {
00960                             --lc->lconn_refcnt;
00961                      }
00962 
00963                      /* need to return -1, because otherwise
00964                       * a valid result is expected */
00965                      ld->ld_errno = lderr;
00966                      return -1;
00967               }
00968        }
00969 
00970        /* make a new ldap message */
00971        newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
00972        if ( newmsg == NULL ) {
00973               ld->ld_errno = LDAP_NO_MEMORY;
00974               return( -1 );
00975        }
00976        newmsg->lm_msgid = (int)id;
00977        newmsg->lm_msgtype = tag;
00978        newmsg->lm_ber = ber;
00979        newmsg->lm_chain_tail = newmsg;
00980 
00981 #ifdef LDAP_CONNECTIONLESS
00982        /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
00983         * the responses are all a sequence wrapped in one message. In
00984         * LDAPv3 each response is in its own message. The datagram must
00985         * end with a SearchResult. We can't just parse each response in
00986         * separate calls to try_read1msg because the header info is only
00987         * present at the beginning of the datagram, not at the beginning
00988         * of each response. So parse all the responses at once and queue
00989         * them up, then pull off the first response to return to the
00990         * caller when all parsing is complete.
00991         */
00992        if ( LDAP_IS_UDP(ld) ) {
00993               /* If not a result, look for more */
00994               if ( tag != LDAP_RES_SEARCH_RESULT ) {
00995                      int ok = 0;
00996                      moremsgs = 1;
00997                      if (isv2) {
00998                             /* LDAPv2: dup the current ber, skip past the current
00999                              * response, and see if there are any more after it.
01000                              */
01001                             ber = ber_dup( ber );
01002                             ber_scanf( ber, "x" );
01003                             if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
01004                                    /* There's more - dup the ber buffer so they can all be
01005                                     * individually freed by ldap_msgfree.
01006                                     */
01007                                    struct berval bv;
01008                                    ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
01009                                    bv.bv_val = LDAP_MALLOC( len );
01010                                    if ( bv.bv_val ) {
01011                                           ok = 1;
01012                                           ber_read( ber, bv.bv_val, len );
01013                                           bv.bv_len = len;
01014                                           ber_init2( ber, &bv, ld->ld_lberoptions );
01015                                    }
01016                             }
01017                      } else {
01018                             /* LDAPv3: Just allocate a new ber. Since this is a buffered
01019                              * datagram, if the sockbuf is readable we still have data
01020                              * to parse.
01021                              */
01022                             ber = ldap_alloc_ber_with_options( ld );
01023                             if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
01024                      }
01025                      /* set up response chain */
01026                      if ( tmp == NULL ) {
01027                             newmsg->lm_next = ld->ld_responses;
01028                             ld->ld_responses = newmsg;
01029                             chain_head = newmsg;
01030                      } else {
01031                             tmp->lm_chain = newmsg;
01032                      }
01033                      chain_head->lm_chain_tail = newmsg;
01034                      tmp = newmsg;
01035                      /* "ok" means there's more to parse */
01036                      if ( ok ) {
01037                             if ( isv2 ) {
01038                                    goto nextresp2;
01039 
01040                             } else {
01041                                    goto nextresp3;
01042                             }
01043                      } else {
01044                             /* got to end of datagram without a SearchResult. Free
01045                              * our dup'd ber, but leave any buffer alone. For v2 case,
01046                              * the previous response is still using this buffer. For v3,
01047                              * the new ber has no buffer to free yet.
01048                              */
01049                             ber_free( ber, 0 );
01050                             return -1;
01051                      }
01052               } else if ( moremsgs ) {
01053               /* got search result, and we had multiple responses in 1 datagram.
01054                * stick the result onto the end of the chain, and then pull the
01055                * first response off the head of the chain.
01056                */
01057                      tmp->lm_chain = newmsg;
01058                      chain_head->lm_chain_tail = newmsg;
01059                      *result = chkResponseList( ld, msgid, all );
01060                      ld->ld_errno = LDAP_SUCCESS;
01061                      return( (*result)->lm_msgtype );
01062               }
01063        }
01064 #endif /* LDAP_CONNECTIONLESS */
01065 
01066        /* is this the one we're looking for? */
01067        if ( msgid == LDAP_RES_ANY || id == msgid ) {
01068               if ( all == LDAP_MSG_ONE
01069                      || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
01070                             && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
01071                             && newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE
01072                             && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
01073               {
01074                      *result = newmsg;
01075                      ld->ld_errno = LDAP_SUCCESS;
01076                      return( tag );
01077 
01078               } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
01079                      foundit = 1;  /* return the chain later */
01080               }
01081        }
01082 
01083        /* 
01084         * if not, we must add it to the list of responses.  if
01085         * the msgid is already there, it must be part of an existing
01086         * search response.
01087         */
01088 
01089        prev = NULL;
01090        for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
01091               if ( l->lm_msgid == newmsg->lm_msgid ) {
01092                      break;
01093               }
01094               prev = l;
01095        }
01096 
01097        /* not part of an existing search response */
01098        if ( l == NULL ) {
01099               if ( foundit ) {
01100                      *result = newmsg;
01101                      goto exit;
01102               }
01103 
01104               newmsg->lm_next = ld->ld_responses;
01105               ld->ld_responses = newmsg;
01106               goto exit;
01107        }
01108 
01109        Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
01110               (void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );
01111 
01112        /* part of a search response - add to end of list of entries */
01113        l->lm_chain_tail->lm_chain = newmsg;
01114        l->lm_chain_tail = newmsg;
01115 
01116        /* return the whole chain if that's what we were looking for */
01117        if ( foundit ) {
01118               if ( prev == NULL ) {
01119                      ld->ld_responses = l->lm_next;
01120               } else {
01121                      prev->lm_next = l->lm_next;
01122               }
01123               *result = l;
01124        }
01125 
01126 exit:
01127        if ( foundit ) {
01128               ld->ld_errno = LDAP_SUCCESS;
01129               return( tag );
01130        }
01131        if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
01132               goto retry;
01133        }
01134        return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
01135 }
01136 
01137 
01138 static ber_tag_t
01139 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
01140 {
01141        ber_len_t     len;
01142        ber_tag_t     tag;
01143        ber_int_t     along;
01144        BerElement *ber;
01145 
01146        *bp = NULL;
01147        ber = ldap_alloc_ber_with_options( ld );
01148 
01149        if( ber == NULL ) {
01150               ld->ld_errno = LDAP_NO_MEMORY;
01151               return LBER_ERROR;
01152        }
01153 
01154        if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
01155               lr->lr_res_msgtype, lr->lr_res_errno,
01156               lr->lr_res_matched ? lr->lr_res_matched : "",
01157               lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
01158        {
01159               ld->ld_errno = LDAP_ENCODING_ERROR;
01160               ber_free( ber, 1 );
01161               return( LBER_ERROR );
01162        }
01163 
01164        ber_reset( ber, 1 );
01165 
01166        if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
01167               ld->ld_errno = LDAP_DECODING_ERROR;
01168               ber_free( ber, 1 );
01169               return( LBER_ERROR );
01170        }
01171 
01172        if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
01173               ld->ld_errno = LDAP_DECODING_ERROR;
01174               ber_free( ber, 1 );
01175               return( LBER_ERROR );
01176        }
01177 
01178        tag = ber_peek_tag( ber, &len );
01179 
01180        if ( tag == LBER_ERROR ) {
01181               ld->ld_errno = LDAP_DECODING_ERROR;
01182               ber_free( ber, 1 );
01183               return( LBER_ERROR );
01184        }
01185 
01186        *bp = ber;
01187        return tag;
01188 }
01189 
01190 
01191 /*
01192  * Merge error information in "lr" with "parentr" error code and string.
01193  */
01194 static void
01195 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
01196 {
01197        if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
01198               parentr->lr_res_errno = lr->lr_res_errno;
01199               if ( lr->lr_res_error != NULL ) {
01200                      (void)ldap_append_referral( ld, &parentr->lr_res_error,
01201                             lr->lr_res_error );
01202               }
01203 
01204        } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
01205               parentr->lr_res_errno == LDAP_SUCCESS )
01206        {
01207               parentr->lr_res_errno = lr->lr_res_errno;
01208               if ( parentr->lr_res_error != NULL ) {
01209                      LDAP_FREE( parentr->lr_res_error );
01210               }
01211               parentr->lr_res_error = lr->lr_res_error;
01212               lr->lr_res_error = NULL;
01213               if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
01214                      if ( parentr->lr_res_matched != NULL ) {
01215                             LDAP_FREE( parentr->lr_res_matched );
01216                      }
01217                      parentr->lr_res_matched = lr->lr_res_matched;
01218                      lr->lr_res_matched = NULL;
01219               }
01220        }
01221 
01222        Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info:  ",
01223               parentr->lr_msgid, 0, 0 );
01224        Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
01225               parentr->lr_res_errno,
01226               parentr->lr_res_error ?  parentr->lr_res_error : "",
01227               parentr->lr_res_matched ?  parentr->lr_res_matched : "" );
01228 }
01229 
01230 
01231 
01232 int
01233 ldap_msgtype( LDAPMessage *lm )
01234 {
01235        assert( lm != NULL );
01236        return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
01237 }
01238 
01239 
01240 int
01241 ldap_msgid( LDAPMessage *lm )
01242 {
01243        assert( lm != NULL );
01244 
01245        return ( lm != NULL ) ? lm->lm_msgid : -1;
01246 }
01247 
01248 
01249 const char *
01250 ldap_int_msgtype2str( ber_tag_t tag )
01251 {
01252        switch( tag ) {
01253        case LDAP_RES_ADD: return "add";
01254        case LDAP_RES_BIND: return "bind";
01255        case LDAP_RES_COMPARE: return "compare";
01256        case LDAP_RES_DELETE: return "delete";
01257        case LDAP_RES_EXTENDED: return "extended-result";
01258        case LDAP_RES_INTERMEDIATE: return "intermediate";
01259        case LDAP_RES_MODIFY: return "modify";
01260        case LDAP_RES_RENAME: return "rename";
01261        case LDAP_RES_SEARCH_ENTRY: return "search-entry";
01262        case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
01263        case LDAP_RES_SEARCH_RESULT: return "search-result";
01264        }
01265        return "unknown";
01266 }
01267 
01268 int
01269 ldap_msgfree( LDAPMessage *lm )
01270 {
01271        LDAPMessage   *next;
01272        int           type = 0;
01273 
01274        Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
01275 
01276        for ( ; lm != NULL; lm = next ) {
01277               next = lm->lm_chain;
01278               type = lm->lm_msgtype;
01279               ber_free( lm->lm_ber, 1 );
01280               LDAP_FREE( (char *) lm );
01281        }
01282 
01283        return type;
01284 }
01285 
01286 /*
01287  * ldap_msgdelete - delete a message.  It returns:
01288  *     0      if the entire message was deleted
01289  *     -1     if the message was not found, or only part of it was found
01290  */
01291 int
01292 ldap_msgdelete( LDAP *ld, int msgid )
01293 {
01294        LDAPMessage   *lm, *prev;
01295        int           rc = 0;
01296 
01297        assert( ld != NULL );
01298 
01299        Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
01300               (void *)ld, msgid, 0 );
01301 
01302        LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
01303        prev = NULL;
01304        for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
01305               if ( lm->lm_msgid == msgid ) {
01306                      break;
01307               }
01308               prev = lm;
01309        }
01310 
01311        if ( lm == NULL ) {
01312               rc = -1;
01313 
01314        } else {
01315               if ( prev == NULL ) {
01316                      ld->ld_responses = lm->lm_next;
01317               } else {
01318                      prev->lm_next = lm->lm_next;
01319               }
01320        }
01321        LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
01322        if ( lm ) {
01323               switch ( ldap_msgfree( lm ) ) {
01324               case LDAP_RES_SEARCH_ENTRY:
01325               case LDAP_RES_SEARCH_REFERENCE:
01326               case LDAP_RES_INTERMEDIATE:
01327                      rc = -1;
01328                      break;
01329 
01330               default:
01331                      break;
01332               }
01333        }
01334 
01335        return rc;
01336 }
01337 
01338 
01339 /*
01340  * ldap_abandoned
01341  *
01342  * return the location of the message id in the array of abandoned
01343  * message ids, or -1
01344  */
01345 static int
01346 ldap_abandoned( LDAP *ld, ber_int_t msgid )
01347 {
01348        int    ret, idx;
01349        assert( msgid >= 0 );
01350 
01351        LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
01352        ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx );
01353        LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
01354        return ret;
01355 }
01356 
01357 /*
01358  * ldap_mark_abandoned
01359  */
01360 static int
01361 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid )
01362 {
01363        int    ret, idx;
01364 
01365        assert( msgid >= 0 );
01366        LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
01367        ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx );
01368        if (ret <= 0) {             /* error or already deleted by another thread */
01369               LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
01370               return ret;
01371        }
01372        /* still in abandoned array, so delete */
01373        ret = ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,
01374               msgid, idx );
01375        LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
01376        return ret;
01377 }