Home | History | Annotate | Line # | Download | only in libldap
      1 /*	$NetBSD: result.c,v 1.4 2025/09/05 21:16:21 christos Exp $	*/
      2 
      3 /* result.c - wait for an ldap result */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 1998-2024 The OpenLDAP Foundation.
      8  * All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted only as authorized by the OpenLDAP
     12  * Public License.
     13  *
     14  * A copy of this license is available in the file LICENSE in the
     15  * top-level directory of the distribution or, alternatively, at
     16  * <http://www.OpenLDAP.org/license.html>.
     17  */
     18 /* Portions Copyright (c) 1990 Regents of the University of Michigan.
     19  * All rights reserved.
     20  */
     21 /* This notice applies to changes, created by or for Novell, Inc.,
     22  * to preexisting works for which notices appear elsewhere in this file.
     23  *
     24  * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
     25  *
     26  * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
     27  * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
     28  * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
     29  * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
     30  * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
     31  * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
     32  * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
     33  * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
     34  *---
     35  * Modification to OpenLDAP source by Novell, Inc.
     36  * April 2000 sfs Add code to process V3 referrals and search results
     37  *---
     38  * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
     39  * can be found in the file "build/LICENSE-2.0.1" in this distribution
     40  * of OpenLDAP Software.
     41  */
     42 
     43 /*
     44  * LDAPv3 (RFC 4511)
     45  *	LDAPResult ::= SEQUENCE {
     46  *		resultCode			ENUMERATED { ... },
     47  *		matchedDN			LDAPDN,
     48  *		diagnosticMessage		LDAPString,
     49  *		referral			[3] Referral OPTIONAL
     50  *	}
     51  *	Referral ::= SEQUENCE OF LDAPURL	(one or more)
     52  *	LDAPURL ::= LDAPString			(limited to URL chars)
     53  */
     54 
     55 #include <sys/cdefs.h>
     56 __RCSID("$NetBSD: result.c,v 1.4 2025/09/05 21:16:21 christos Exp $");
     57 
     58 #include "portable.h"
     59 
     60 #include <stdio.h>
     61 
     62 #include <ac/stdlib.h>
     63 
     64 #include <ac/errno.h>
     65 #include <ac/socket.h>
     66 #include <ac/string.h>
     67 #include <ac/time.h>
     68 #include <ac/unistd.h>
     69 
     70 #include "ldap-int.h"
     71 #include "ldap_log.h"
     72 #include "lutil.h"
     73 
     74 static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
     75 static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
     76 static int wait4msg LDAP_P(( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout,
     77 	LDAPMessage **result ));
     78 static ber_tag_t try_read1msg LDAP_P(( LDAP *ld, ber_int_t msgid,
     79 	int all, LDAPConn *lc, LDAPMessage **result ));
     80 static ber_tag_t build_result_ber LDAP_P(( LDAP *ld, BerElement **bp, LDAPRequest *lr ));
     81 static void merge_error_info LDAP_P(( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ));
     82 static LDAPMessage * chkResponseList LDAP_P(( LDAP *ld, int msgid, int all));
     83 
     84 #define LDAP_MSG_X_KEEP_LOOKING		(-2)
     85 
     86 
     87 /*
     88  * ldap_result - wait for an ldap result response to a message from the
     89  * ldap server.  If msgid is LDAP_RES_ANY (-1), any message will be
     90  * accepted.  If msgid is LDAP_RES_UNSOLICITED (0), any unsolicited
     91  * message is accepted.  Otherwise ldap_result will wait for a response
     92  * with msgid.  If all is LDAP_MSG_ONE (0) the first message with id
     93  * msgid will be accepted, otherwise, ldap_result will wait for all
     94  * responses with id msgid and then return a pointer to the entire list
     95  * of messages.  In general, this is only useful for search responses,
     96  * which can be of three message types (zero or more entries, zero or
     97  * search references, followed by an ldap result).  An extension to
     98  * LDAPv3 allows partial extended responses to be returned in response
     99  * to any request.  The type of the first message received is returned.
    100  * When waiting, any messages that have been abandoned/discarded are
    101  * discarded.
    102  *
    103  * Example:
    104  *	ldap_result( s, msgid, all, timeout, result )
    105  */
    106 int
    107 ldap_result(
    108 	LDAP *ld,
    109 	int msgid,
    110 	int all,
    111 	struct timeval *timeout,
    112 	LDAPMessage **result )
    113 {
    114 	int		rc;
    115 
    116 	assert( ld != NULL );
    117 	assert( result != NULL );
    118 
    119 	Debug2( LDAP_DEBUG_TRACE, "ldap_result ld %p msgid %d\n", (void *)ld, msgid );
    120 
    121 	if (ld->ld_errno == LDAP_LOCAL_ERROR || ld->ld_errno == LDAP_SERVER_DOWN)
    122 		return -1;
    123 
    124 	LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
    125 	rc = wait4msg( ld, msgid, all, timeout, result );
    126 	LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
    127 
    128 	return rc;
    129 }
    130 
    131 /* protected by res_mutex */
    132 static LDAPMessage *
    133 chkResponseList(
    134 	LDAP *ld,
    135 	int msgid,
    136 	int all)
    137 {
    138 	LDAPMessage	*lm, **lastlm, *nextlm;
    139 	int		cnt = 0;
    140 
    141 	/*
    142 	 * Look through the list of responses we have received on
    143 	 * this association and see if the response we're interested in
    144 	 * is there.  If it is, return it.  If not, call wait4msg() to
    145 	 * wait until it arrives or timeout occurs.
    146 	 */
    147 
    148 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
    149 
    150 	Debug3( LDAP_DEBUG_TRACE,
    151 		"ldap_chkResponseList ld %p msgid %d all %d\n",
    152 		(void *)ld, msgid, all );
    153 
    154 	lm = ld->ld_responses;
    155 	if ( lm && msgid == LDAP_RES_ANY && all == LDAP_MSG_RECEIVED ) {
    156 		/*
    157 		 * ITS#10229: asked to return all messages received so far,
    158 		 * draft-ietf-ldapext-ldap-c-api which defines LDAP_MSG_RECEIVED lets
    159 		 * us mix different msgids in what we return
    160 		 *
    161 		 * We have two choices in *how* we return the messages:
    162 		 * - we link all chains together
    163 		 * - we keep the chains intact and use lm_next
    164 		 *
    165 		 * The former will make life harder for ldap_parse_result finding a
    166 		 * result message, the latter affects routines that iterate over
    167 		 * messages. This take does the former.
    168 		 */
    169 		ld->ld_responses = NULL;
    170 		while ( lm->lm_next ) {
    171 			lm->lm_chain_tail->lm_chain = lm->lm_next;
    172 			lm->lm_chain_tail = lm->lm_next->lm_chain_tail;
    173 			lm->lm_next = lm->lm_next->lm_next;
    174 		}
    175 		return lm;
    176 	}
    177 
    178 	lastlm = &ld->ld_responses;
    179 	for ( ; lm != NULL; lm = nextlm ) {
    180 		nextlm = lm->lm_next;
    181 		++cnt;
    182 
    183 		if ( ldap_abandoned( ld, lm->lm_msgid ) ) {
    184 			Debug2( LDAP_DEBUG_ANY,
    185 				"response list msg abandoned, "
    186 				"msgid %d message type %s\n",
    187 				lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ) );
    188 
    189 			switch ( lm->lm_msgtype ) {
    190 			case LDAP_RES_SEARCH_ENTRY:
    191 			case LDAP_RES_SEARCH_REFERENCE:
    192 			case LDAP_RES_INTERMEDIATE:
    193 				break;
    194 
    195 			default:
    196 				/* there's no need to keep the id
    197 				 * in the abandoned list any longer */
    198 				ldap_mark_abandoned( ld, lm->lm_msgid );
    199 				break;
    200 			}
    201 
    202 			/* Remove this entry from list */
    203 			*lastlm = nextlm;
    204 
    205 			ldap_msgfree( lm );
    206 
    207 			continue;
    208 		}
    209 
    210 		if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
    211 			LDAPMessage	*tmp;
    212 
    213 			if ( all == LDAP_MSG_ONE ||
    214 				all == LDAP_MSG_RECEIVED ||
    215 				msgid == LDAP_RES_UNSOLICITED )
    216 			{
    217 				break;
    218 			}
    219 
    220 			tmp = lm->lm_chain_tail;
    221 			if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY ||
    222 				tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ||
    223 				tmp->lm_msgtype == LDAP_RES_INTERMEDIATE )
    224 			{
    225 				tmp = NULL;
    226 			}
    227 
    228 			if ( tmp == NULL ) {
    229 				lm = NULL;
    230 			}
    231 
    232 			break;
    233 		}
    234 		lastlm = &lm->lm_next;
    235 	}
    236 
    237 	if ( lm != NULL ) {
    238 		/* Found an entry, remove it from the list */
    239 		if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) {
    240 			*lastlm = lm->lm_chain;
    241 			lm->lm_chain->lm_next = lm->lm_next;
    242 			lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain;
    243 			lm->lm_chain = NULL;
    244 			lm->lm_chain_tail = NULL;
    245 		} else {
    246 			*lastlm = lm->lm_next;
    247 		}
    248 		lm->lm_next = NULL;
    249 	}
    250 
    251 #ifdef LDAP_DEBUG
    252 	if ( lm == NULL) {
    253 		Debug1( LDAP_DEBUG_TRACE,
    254 			"ldap_chkResponseList returns ld %p NULL\n", (void *)ld );
    255 	} else {
    256 		Debug3( LDAP_DEBUG_TRACE,
    257 			"ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n",
    258 			(void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
    259 	}
    260 #endif
    261 
    262 	return lm;
    263 }
    264 
    265 /* protected by res_mutex */
    266 static int
    267 wait4msg(
    268 	LDAP *ld,
    269 	ber_int_t msgid,
    270 	int all,
    271 	struct timeval *timeout,
    272 	LDAPMessage **result )
    273 {
    274 	int		rc;
    275 	struct timeval	tv = { 0 },
    276 			tv0 = { 0 },
    277 			start_time_tv = { 0 },
    278 			*tvp = NULL;
    279 	LDAPConn	*lc;
    280 
    281 	assert( ld != NULL );
    282 	assert( result != NULL );
    283 
    284 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
    285 
    286 	if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) {
    287 		tv = ld->ld_options.ldo_tm_api;
    288 		timeout = &tv;
    289 	}
    290 
    291 #ifdef LDAP_DEBUG
    292 	if ( timeout == NULL ) {
    293 		Debug2( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
    294 			(void *)ld, msgid );
    295 	} else {
    296 		Debug3( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n",
    297 			(void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec );
    298 	}
    299 #endif /* LDAP_DEBUG */
    300 
    301 	if ( timeout != NULL && timeout->tv_sec != -1 ) {
    302 		tv0 = *timeout;
    303 		tv = *timeout;
    304 		tvp = &tv;
    305 #ifdef HAVE_GETTIMEOFDAY
    306 		gettimeofday( &start_time_tv, NULL );
    307 #else /* ! HAVE_GETTIMEOFDAY */
    308 		start_time_tv.tv_sec = time( NULL );
    309 		start_time_tv.tv_usec = 0;
    310 #endif /* ! HAVE_GETTIMEOFDAY */
    311 	}
    312 
    313 	rc = LDAP_MSG_X_KEEP_LOOKING;
    314 	while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
    315 #ifdef LDAP_DEBUG
    316 		if ( ldap_debug & LDAP_DEBUG_TRACE ) {
    317 			Debug3( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
    318 				(void *)ld, msgid, all );
    319 			ldap_dump_connection( ld, ld->ld_conns, 1 );
    320 			LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
    321 			ldap_dump_requests_and_responses( ld );
    322 			LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
    323 		}
    324 #endif /* LDAP_DEBUG */
    325 
    326 		if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
    327 			rc = (*result)->lm_msgtype;
    328 
    329 		} else {
    330 			int lc_ready = 0;
    331 
    332 			LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
    333 			for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
    334 				if ( ber_sockbuf_ctrl( lc->lconn_sb,
    335 					LBER_SB_OPT_DATA_READY, NULL ) )
    336 				{
    337 					lc_ready = 2;	/* ready at ber level, not socket level */
    338 					break;
    339 				}
    340 			}
    341 
    342 			if ( !lc_ready ) {
    343 				int err;
    344 				rc = ldap_int_select( ld, tvp );
    345 				if ( rc == -1 ) {
    346 					err = sock_errno();
    347 #ifdef LDAP_DEBUG
    348 					Debug1( LDAP_DEBUG_TRACE,
    349 						"ldap_int_select returned -1: errno %d\n",
    350 						err );
    351 #endif
    352 				}
    353 
    354 				if ( rc == 0 || ( rc == -1 && (
    355 					!LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
    356 						|| err != EINTR ) ) )
    357 				{
    358 					ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
    359 						LDAP_TIMEOUT);
    360 					LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
    361 					return( rc );
    362 				}
    363 
    364 				if ( rc == -1 ) {
    365 					rc = LDAP_MSG_X_KEEP_LOOKING;	/* select interrupted: loop */
    366 
    367 				} else {
    368 					lc_ready = 1;
    369 				}
    370 			}
    371 			if ( lc_ready ) {
    372 				LDAPConn *lnext;
    373 				int serviced = 0;
    374 				rc = LDAP_MSG_X_KEEP_LOOKING;
    375 				LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
    376 				if ( ld->ld_requests != NULL ) {
    377 					TAvlnode *node = ldap_tavl_end( ld->ld_requests, TAVL_DIR_RIGHT );
    378 					LDAPRequest *lr;
    379 
    380 					assert( node != NULL );
    381 					lr = node->avl_data;
    382 					if ( lr->lr_status == LDAP_REQST_WRITING &&
    383 							ldap_is_write_ready( ld, lr->lr_conn->lconn_sb ) ) {
    384 						serviced = 1;
    385 						ldap_int_flush_request( ld, lr );
    386 					}
    387 				}
    388 				for ( lc = ld->ld_conns;
    389 					rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL;
    390 					lc = lnext )
    391 				{
    392 					if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
    393 						ldap_is_read_ready( ld, lc->lconn_sb ) )
    394 					{
    395 						serviced = 1;
    396 						/* Don't let it get freed out from under us */
    397 						++lc->lconn_refcnt;
    398 						rc = try_read1msg( ld, msgid, all, lc, result );
    399 						lnext = lc->lconn_next;
    400 
    401 						/* Only take locks if we're really freeing */
    402 						if ( lc->lconn_refcnt <= 1 ) {
    403 							ldap_free_connection( ld, lc, 0, 1 );
    404 						} else {
    405 							--lc->lconn_refcnt;
    406 						}
    407 					} else {
    408 						lnext = lc->lconn_next;
    409 					}
    410 				}
    411 				LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
    412 				/* Quit looping if no one handled any socket events */
    413 				if (!serviced && lc_ready == 1)
    414 					rc = -1;
    415 			}
    416 			LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
    417 		}
    418 
    419 		if ( all == LDAP_MSG_RECEIVED ) {
    420 			/*
    421 			 * ITS#10229: we looped over all ready connections accumulating
    422 			 * messages in ld_responses, check if we have something to return
    423 			 * right now.
    424 			 */
    425 			LDAPMessage **lp, *lm = ld->ld_responses;
    426 
    427 			if ( lm && msgid == LDAP_RES_ANY ) {
    428 				*result = lm;
    429 
    430 				ld->ld_responses = NULL;
    431 				while ( lm->lm_next ) {
    432 					lm->lm_chain_tail->lm_chain = lm->lm_next;
    433 					lm->lm_chain_tail = lm->lm_next->lm_chain_tail;
    434 					lm->lm_next = lm->lm_next->lm_next;
    435 				}
    436 				rc = lm->lm_msgtype;
    437 				break;
    438 			}
    439 
    440 			for ( lp = &ld->ld_responses; lm; lp = &lm->lm_next, lm = *lp ) {
    441 				if ( msgid == lm->lm_msgid ) break;
    442 			}
    443 			if ( lm ) {
    444 				*lp = lm->lm_next;
    445 				*result = lm;
    446 				rc = lm->lm_msgtype;
    447 			}
    448 		}
    449 
    450 		if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
    451 			struct timeval	curr_time_tv = { 0 },
    452 					delta_time_tv = { 0 };
    453 
    454 #ifdef HAVE_GETTIMEOFDAY
    455 			gettimeofday( &curr_time_tv, NULL );
    456 #else /* ! HAVE_GETTIMEOFDAY */
    457 			curr_time_tv.tv_sec = time( NULL );
    458 			curr_time_tv.tv_usec = 0;
    459 #endif /* ! HAVE_GETTIMEOFDAY */
    460 
    461 			/* delta_time = tmp_time - start_time */
    462 			delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
    463 			delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
    464 			if ( delta_time_tv.tv_usec < 0 ) {
    465 				delta_time_tv.tv_sec--;
    466 				delta_time_tv.tv_usec += 1000000;
    467 			}
    468 
    469 			/* tv0 < delta_time ? */
    470 			if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
    471 			     ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
    472 			{
    473 				rc = 0; /* timed out */
    474 				ld->ld_errno = LDAP_TIMEOUT;
    475 				break;
    476 			}
    477 
    478 			/* tv0 -= delta_time */
    479 			tv0.tv_sec -= delta_time_tv.tv_sec;
    480 			tv0.tv_usec -= delta_time_tv.tv_usec;
    481 			if ( tv0.tv_usec < 0 ) {
    482 				tv0.tv_sec--;
    483 				tv0.tv_usec += 1000000;
    484 			}
    485 
    486 			tv.tv_sec = tv0.tv_sec;
    487 			tv.tv_usec = tv0.tv_usec;
    488 
    489 			Debug3( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n",
    490 				(void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
    491 
    492 			start_time_tv.tv_sec = curr_time_tv.tv_sec;
    493 			start_time_tv.tv_usec = curr_time_tv.tv_usec;
    494 		}
    495 	}
    496 
    497 	return( rc );
    498 }
    499 
    500 
    501 /* protected by res_mutex, conn_mutex and req_mutex */
    502 static ber_tag_t
    503 try_read1msg(
    504 	LDAP *ld,
    505 	ber_int_t msgid,
    506 	int all,
    507 	LDAPConn *lc,
    508 	LDAPMessage **result )
    509 {
    510 	BerElement	*ber;
    511 	LDAPMessage	*newmsg, *l, *prev;
    512 	ber_int_t	id;
    513 	ber_tag_t	tag;
    514 	ber_len_t	len;
    515 	int		foundit = 0;
    516 	LDAPRequest	*lr, *tmplr, dummy_lr = { 0 };
    517 	BerElement	tmpber;
    518 	int		rc, refer_cnt, hadref, simple_request, err;
    519 	ber_int_t	lderr = -1;
    520 
    521 #ifdef LDAP_CONNECTIONLESS
    522 	LDAPMessage	*tmp = NULL, *chain_head = NULL;
    523 	int		moremsgs = 0, isv2 = 0;
    524 #endif
    525 
    526 	assert( ld != NULL );
    527 	assert( lc != NULL );
    528 
    529 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
    530 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
    531 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
    532 
    533 	Debug3( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
    534 		(void *)ld, msgid, all );
    535 
    536 retry:
    537 	if ( lc->lconn_ber == NULL ) {
    538 		lc->lconn_ber = ldap_alloc_ber_with_options( ld );
    539 
    540 		if ( lc->lconn_ber == NULL ) {
    541 			return -1;
    542 		}
    543 	}
    544 
    545 	ber = lc->lconn_ber;
    546 	assert( LBER_VALID (ber) );
    547 
    548 	/* get the next message */
    549 	sock_errset(0);
    550 #ifdef LDAP_CONNECTIONLESS
    551 	if ( LDAP_IS_UDP(ld) ) {
    552 		struct sockaddr_storage from;
    553 		if ( ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr_storage) ) < 0 )
    554 			goto fail;
    555 		if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
    556 	}
    557 nextresp3:
    558 #endif
    559 	tag = ber_get_next( lc->lconn_sb, &len, ber );
    560 	switch ( tag ) {
    561 	case LDAP_TAG_MESSAGE:
    562 		/*
    563 	 	 * We read a complete message.
    564 	 	 * The connection should no longer need this ber.
    565 	 	 */
    566 		lc->lconn_ber = NULL;
    567 		break;
    568 
    569 	default:
    570 		/*
    571 		 * We read a BerElement that isn't LDAP or the stream has desync'd.
    572 		 * In either case, anything we read from now on is probably garbage,
    573 		 * just drop the connection.
    574 		 */
    575 		ber_free( ber, 1 );
    576 		lc->lconn_ber = NULL;
    577 		/* FALLTHRU */
    578 
    579 	case LBER_DEFAULT:
    580 fail:
    581 		err = sock_errno();
    582 #ifdef LDAP_DEBUG
    583 		Debug1( LDAP_DEBUG_CONNS,
    584 			"ber_get_next failed, errno=%d.\n", err );
    585 #endif
    586 		if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
    587 		if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
    588 		ld->ld_errno = LDAP_SERVER_DOWN;
    589 		if ( !LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_KEEPCONN )) {
    590 			--lc->lconn_refcnt;
    591 		}
    592 		lc->lconn_status = 0;
    593 		return -1;
    594 	}
    595 
    596 	/* message id */
    597 	if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
    598 		ber_free( ber, 1 );
    599 		ld->ld_errno = LDAP_DECODING_ERROR;
    600 		return( -1 );
    601 	}
    602 
    603 	/* id == 0 iff unsolicited notification message (RFC 4511) */
    604 
    605 	/* id < 0 is invalid, just toss it. FIXME: should we disconnect? */
    606 	if ( id < 0 ) {
    607 		goto retry_ber;
    608 	}
    609 
    610 	/* if it's been abandoned, toss it */
    611 	if ( id > 0 ) {
    612 		if ( ldap_abandoned( ld, id ) ) {
    613 			/* the message type */
    614 			tag = ber_peek_tag( ber, &len );
    615 			switch ( tag ) {
    616 			case LDAP_RES_SEARCH_ENTRY:
    617 			case LDAP_RES_SEARCH_REFERENCE:
    618 			case LDAP_RES_INTERMEDIATE:
    619 			case LBER_ERROR:
    620 				break;
    621 
    622 			default:
    623 				/* there's no need to keep the id
    624 				 * in the abandoned list any longer */
    625 				ldap_mark_abandoned( ld, id );
    626 				break;
    627 			}
    628 
    629 			Debug3( LDAP_DEBUG_ANY,
    630 				"abandoned/discarded ld %p msgid %d message type %s\n",
    631 				(void *)ld, id, ldap_int_msgtype2str( tag ) );
    632 
    633 retry_ber:
    634 			ber_free( ber, 1 );
    635 			if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
    636 				goto retry;
    637 			}
    638 			return( LDAP_MSG_X_KEEP_LOOKING );	/* continue looking */
    639 		}
    640 
    641 		lr = ldap_find_request_by_msgid( ld, id );
    642 		if ( lr == NULL ) {
    643 			const char	*msg = "unknown";
    644 
    645 			/* the message type */
    646 			tag = ber_peek_tag( ber, &len );
    647 			switch ( tag ) {
    648 			case LBER_ERROR:
    649 				break;
    650 
    651 			default:
    652 				msg = ldap_int_msgtype2str( tag );
    653 				break;
    654 			}
    655 
    656 			Debug3( LDAP_DEBUG_ANY,
    657 				"no request for response on ld %p msgid %d message type %s (tossing)\n",
    658 				(void *)ld, id, msg );
    659 
    660 			goto retry_ber;
    661 		}
    662 
    663 #ifdef LDAP_CONNECTIONLESS
    664 		if ( LDAP_IS_UDP(ld) && isv2 ) {
    665 			ber_scanf(ber, "x{");
    666 		}
    667 nextresp2:
    668 		;
    669 #endif
    670 	}
    671 
    672 	/* the message type */
    673 	tag = ber_peek_tag( ber, &len );
    674 	if ( tag == LBER_ERROR ) {
    675 		ld->ld_errno = LDAP_DECODING_ERROR;
    676 		ber_free( ber, 1 );
    677 		return( -1 );
    678 	}
    679 
    680 	Debug3( LDAP_DEBUG_TRACE,
    681 		"read1msg: ld %p msgid %d message type %s\n",
    682 		(void *)ld, id, ldap_int_msgtype2str( tag ) );
    683 
    684 	if ( id == 0 ) {
    685 		/* unsolicited notification message (RFC 4511) */
    686 		if ( tag != LDAP_RES_EXTENDED ) {
    687 			/* toss it */
    688 			goto retry_ber;
    689 
    690 			/* strictly speaking, it's an error; from RFC 4511:
    691 
    692 4.4.  Unsolicited Notification
    693 
    694    An unsolicited notification is an LDAPMessage sent from the server to
    695    the client that is not in response to any LDAPMessage received by the
    696    server.  It is used to signal an extraordinary condition in the
    697    server or in the LDAP session between the client and the server.  The
    698    notification is of an advisory nature, and the server will not expect
    699    any response to be returned from the client.
    700 
    701    The unsolicited notification is structured as an LDAPMessage in which
    702    the messageID is zero and protocolOp is set to the extendedResp
    703    choice using the ExtendedResponse type (See Section 4.12).  The
    704    responseName field of the ExtendedResponse always contains an LDAPOID
    705    that is unique for this notification.
    706 
    707 			 * however, since unsolicited responses
    708 			 * are of advisory nature, better
    709 			 * toss it, right now
    710 			 */
    711 
    712 #if 0
    713 			ld->ld_errno = LDAP_DECODING_ERROR;
    714 			ber_free( ber, 1 );
    715 			return( -1 );
    716 #endif
    717 		}
    718 
    719 		lr = &dummy_lr;
    720 	}
    721 
    722 	id = lr->lr_origid;
    723 	refer_cnt = 0;
    724 	hadref = simple_request = 0;
    725 	rc = LDAP_MSG_X_KEEP_LOOKING;	/* default is to keep looking (no response found) */
    726 	lr->lr_res_msgtype = tag;
    727 
    728 	/*
    729 	 * Check for V3 search reference
    730 	 */
    731 	if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
    732 		if ( ld->ld_version > LDAP_VERSION2 ) {
    733 			/* This is a V3 search reference */
    734 			if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
    735 					lr->lr_parent != NULL )
    736 			{
    737 				char **refs = NULL;
    738 				tmpber = *ber;
    739 
    740 				/* Get the referral list */
    741 				if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
    742 					rc = LDAP_DECODING_ERROR;
    743 
    744 				} else {
    745 					/* Note: refs array is freed by ldap_chase_v3referrals */
    746 					refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
    747 						1, &lr->lr_res_error, &hadref );
    748 					if ( refer_cnt > 0 ) {
    749 						/* successfully chased reference */
    750 						/* If haven't got end search, set chasing referrals */
    751 						if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
    752 							lr->lr_status = LDAP_REQST_CHASINGREFS;
    753 							Debug1( LDAP_DEBUG_TRACE,
    754 								"read1msg:  search ref chased, "
    755 								"mark request chasing refs, "
    756 								"id = %d\n",
    757 								lr->lr_msgid );
    758 						}
    759 					}
    760 				}
    761 			}
    762 		}
    763 
    764 	} else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
    765 		/* All results that just return a status, i.e. don't return data
    766 		 * go through the following code.  This code also chases V2 referrals
    767 		 * and checks if all referrals have been chased.
    768 		 */
    769 		char		*lr_res_error = NULL;
    770 
    771 		tmpber = *ber; 	/* struct copy */
    772 		if ( ber_scanf( &tmpber, "{eAA", &lderr,
    773 				&lr->lr_res_matched, &lr_res_error )
    774 				!= LBER_ERROR )
    775 		{
    776 			if ( lr_res_error != NULL ) {
    777 				if ( lr->lr_res_error != NULL ) {
    778 					(void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
    779 					LDAP_FREE( (char *)lr_res_error );
    780 
    781 				} else {
    782 					lr->lr_res_error = lr_res_error;
    783 				}
    784 				lr_res_error = NULL;
    785 			}
    786 
    787 			/* Do we need to check for referrals? */
    788 			if ( tag != LDAP_RES_BIND &&
    789 				( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
    790 					lr->lr_parent != NULL ))
    791 			{
    792 				char		**refs = NULL;
    793 				ber_len_t	len;
    794 
    795 				/* Check if V3 referral */
    796 				if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
    797 					if ( ld->ld_version > LDAP_VERSION2 ) {
    798 						/* Get the referral list */
    799 						if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
    800 							rc = LDAP_DECODING_ERROR;
    801 							lr->lr_status = LDAP_REQST_COMPLETED;
    802 							Debug2( LDAP_DEBUG_TRACE,
    803 								"read1msg: referral decode error, "
    804 								"mark request completed, ld %p msgid %d\n",
    805 								(void *)ld, lr->lr_msgid );
    806 
    807 						} else {
    808 							/* Chase the referral
    809 							 * refs array is freed by ldap_chase_v3referrals
    810 							 */
    811 							refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
    812 								0, &lr->lr_res_error, &hadref );
    813 							lr->lr_status = LDAP_REQST_COMPLETED;
    814 							Debug3( LDAP_DEBUG_TRACE,
    815 								"read1msg: referral %s chased, "
    816 								"mark request completed, ld %p msgid %d\n",
    817 								refer_cnt > 0 ? "" : "not",
    818 								(void *)ld, lr->lr_msgid);
    819 							if ( refer_cnt < 0 ) {
    820 								refer_cnt = 0;
    821 							}
    822 						}
    823 					}
    824 				} else {
    825 					switch ( lderr ) {
    826 					case LDAP_SUCCESS:
    827 					case LDAP_COMPARE_TRUE:
    828 					case LDAP_COMPARE_FALSE:
    829 						break;
    830 
    831 					default:
    832 						if ( lr->lr_res_error == NULL ) {
    833 							break;
    834 						}
    835 
    836 						/* pedantic, should never happen */
    837 						if ( lr->lr_res_error[ 0 ] == '\0' ) {
    838 							LDAP_FREE( lr->lr_res_error );
    839 							lr->lr_res_error = NULL;
    840 							break;
    841 						}
    842 
    843 						/* V2 referrals are in error string */
    844 						refer_cnt = ldap_chase_referrals( ld, lr,
    845 							&lr->lr_res_error, -1, &hadref );
    846 						lr->lr_status = LDAP_REQST_COMPLETED;
    847 						Debug1( LDAP_DEBUG_TRACE,
    848 							"read1msg:  V2 referral chased, "
    849 							"mark request completed, id = %d\n",
    850 							lr->lr_msgid );
    851 						break;
    852 					}
    853 				}
    854 			}
    855 
    856 			/* save errno, message, and matched string */
    857 			if ( !hadref || lr->lr_res_error == NULL ) {
    858 				lr->lr_res_errno =
    859 					lderr == LDAP_PARTIAL_RESULTS
    860 					? LDAP_SUCCESS : lderr;
    861 
    862 			} else if ( ld->ld_errno != LDAP_SUCCESS ) {
    863 				lr->lr_res_errno = ld->ld_errno;
    864 
    865 			} else {
    866 				lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
    867 			}
    868 		}
    869 
    870 		/* in any case, don't leave any lr_res_error 'round */
    871 		if ( lr_res_error ) {
    872 			LDAP_FREE( lr_res_error );
    873 		}
    874 
    875 		Debug2( LDAP_DEBUG_TRACE,
    876 			"read1msg: ld %p %d new referrals\n",
    877 			(void *)ld, refer_cnt );
    878 
    879 		if ( refer_cnt != 0 ) {	/* chasing referrals */
    880 			ber_free( ber, 1 );
    881 			ber = NULL;
    882 			if ( refer_cnt < 0 ) {
    883 				ldap_return_request( ld, lr, 0 );
    884 				return( -1 );	/* fatal error */
    885 			}
    886 			lr->lr_res_errno = LDAP_SUCCESS; /* successfully chased referral */
    887 			if ( lr->lr_res_matched ) {
    888 				LDAP_FREE( lr->lr_res_matched );
    889 				lr->lr_res_matched = NULL;
    890 			}
    891 
    892 		} else {
    893 			if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
    894 				/* request without any referrals */
    895 				simple_request = ( hadref ? 0 : 1 );
    896 
    897 			} else {
    898 				/* request with referrals or child request */
    899 				ber_free( ber, 1 );
    900 				ber = NULL;
    901 			}
    902 
    903 			lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
    904 			Debug2( LDAP_DEBUG_TRACE,
    905 				"read1msg:  mark request completed, ld %p msgid %d\n",
    906 				(void *)ld, lr->lr_msgid );
    907 			tmplr = lr;
    908 			while ( lr->lr_parent != NULL ) {
    909 				merge_error_info( ld, lr->lr_parent, lr );
    910 
    911 				lr = lr->lr_parent;
    912 				if ( --lr->lr_outrefcnt > 0 ) {
    913 					break;	/* not completely done yet */
    914 				}
    915 			}
    916 			/* ITS#6744: Original lr was refcounted when we retrieved it,
    917 			 * must release it now that we're working with the parent
    918 			 */
    919 			if ( tmplr->lr_parent ) {
    920 				ldap_return_request( ld, tmplr, 0 );
    921 			}
    922 
    923 			/* Check if all requests are finished, lr is now parent */
    924 			tmplr = lr;
    925 			if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
    926 				for ( tmplr = lr->lr_child;
    927 					tmplr != NULL;
    928 					tmplr = tmplr->lr_refnext )
    929 				{
    930 					if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
    931 				}
    932 			}
    933 
    934 			/* This is the parent request if the request has referrals */
    935 			if ( lr->lr_outrefcnt <= 0 &&
    936 				lr->lr_parent == NULL &&
    937 				tmplr == NULL )
    938 			{
    939 				id = lr->lr_msgid;
    940 				tag = lr->lr_res_msgtype;
    941 				Debug2( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
    942 					(void *)ld, id );
    943 				Debug3( LDAP_DEBUG_TRACE,
    944 					"res_errno: %d, res_error: <%s>, "
    945 					"res_matched: <%s>\n",
    946 					lr->lr_res_errno,
    947 					lr->lr_res_error ? lr->lr_res_error : "",
    948 					lr->lr_res_matched ? lr->lr_res_matched : "" );
    949 				if ( !simple_request ) {
    950 					ber_free( ber, 1 );
    951 					ber = NULL;
    952 					if ( build_result_ber( ld, &ber, lr )
    953 					    == LBER_ERROR )
    954 					{
    955 						rc = -1; /* fatal error */
    956 					}
    957 				}
    958 
    959 				if ( lr != &dummy_lr ) {
    960 					ldap_return_request( ld, lr, 1 );
    961 				} else {
    962 					if ( lr->lr_res_matched ) {
    963 						LDAP_FREE( lr->lr_res_matched );
    964 					}
    965 					if ( lr->lr_res_error ) {
    966 						LDAP_FREE( lr->lr_res_error );
    967 					}
    968 				}
    969 				lr = NULL;
    970 			}
    971 
    972 			/*
    973 			 * RFC 4511 unsolicited (id == 0) responses
    974 			 * shouldn't necessarily end the connection
    975 			 */
    976 			if ( lc != NULL && id != 0 &&
    977 			     !LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_KEEPCONN )) {
    978 				--lc->lconn_refcnt;
    979 				lc = NULL;
    980 			}
    981 		}
    982 	}
    983 
    984 	if ( lr != NULL ) {
    985 		if ( lr != &dummy_lr ) {
    986 			ldap_return_request( ld, lr, 0 );
    987 		}
    988 		lr = NULL;
    989 	}
    990 
    991 	if ( ber == NULL ) {
    992 		return( rc );
    993 	}
    994 
    995 	/* try to handle unsolicited responses as appropriate */
    996 	if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
    997 		int	is_nod = 0;
    998 
    999 		tag = ber_peek_tag( &tmpber, &len );
   1000 
   1001 		/* we have a res oid */
   1002 		if ( tag == LDAP_TAG_EXOP_RES_OID ) {
   1003 			static struct berval	bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
   1004 			struct berval		resoid = BER_BVNULL;
   1005 
   1006 			if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
   1007 				ld->ld_errno = LDAP_DECODING_ERROR;
   1008 				ber_free( ber, 1 );
   1009 				return -1;
   1010 			}
   1011 
   1012 			assert( !BER_BVISEMPTY( &resoid ) );
   1013 
   1014 			is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
   1015 
   1016 			tag = ber_peek_tag( &tmpber, &len );
   1017 		}
   1018 
   1019 #if 0 /* don't need right now */
   1020 		/* we have res data */
   1021 		if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
   1022 			struct berval resdata;
   1023 
   1024 			if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
   1025 				ld->ld_errno = LDAP_DECODING_ERROR;
   1026 				ber_free( ber, 0 );
   1027 				return ld->ld_errno;
   1028 			}
   1029 
   1030 			/* use it... */
   1031 		}
   1032 #endif
   1033 
   1034 		/* handle RFC 4511 "Notice of Disconnection" locally */
   1035 
   1036 		if ( is_nod ) {
   1037 			if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
   1038 				ld->ld_errno = LDAP_DECODING_ERROR;
   1039 				ber_free( ber, 1 );
   1040 				return -1;
   1041 			}
   1042 
   1043 			/* get rid of the connection... */
   1044 			if ( lc != NULL &&
   1045 			     !LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_KEEPCONN )) {
   1046 				--lc->lconn_refcnt;
   1047 			}
   1048 
   1049 			/* need to return -1, because otherwise
   1050 			 * a valid result is expected */
   1051 			ld->ld_errno = lderr;
   1052 			return -1;
   1053 		}
   1054 	}
   1055 
   1056 	/* make a new ldap message */
   1057 	newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
   1058 	if ( newmsg == NULL ) {
   1059 		ld->ld_errno = LDAP_NO_MEMORY;
   1060 		return( -1 );
   1061 	}
   1062 	newmsg->lm_msgid = (int)id;
   1063 	newmsg->lm_msgtype = tag;
   1064 	newmsg->lm_ber = ber;
   1065 	newmsg->lm_chain_tail = newmsg;
   1066 
   1067 #ifdef LDAP_CONNECTIONLESS
   1068 	/* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
   1069 	 * the responses are all a sequence wrapped in one message. In
   1070 	 * LDAPv3 each response is in its own message. The datagram must
   1071 	 * end with a SearchResult. We can't just parse each response in
   1072 	 * separate calls to try_read1msg because the header info is only
   1073 	 * present at the beginning of the datagram, not at the beginning
   1074 	 * of each response. So parse all the responses at once and queue
   1075 	 * them up, then pull off the first response to return to the
   1076 	 * caller when all parsing is complete.
   1077 	 */
   1078 	if ( LDAP_IS_UDP(ld) ) {
   1079 		/* If not a result, look for more */
   1080 		if ( tag != LDAP_RES_SEARCH_RESULT ) {
   1081 			int ok = 0;
   1082 			moremsgs = 1;
   1083 			if (isv2) {
   1084 				/* LDAPv2: dup the current ber, skip past the current
   1085 				 * response, and see if there are any more after it.
   1086 				 */
   1087 				ber = ber_dup( ber );
   1088 				ber_scanf( ber, "x" );
   1089 				if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
   1090 					/* There's more - dup the ber buffer so they can all be
   1091 					 * individually freed by ldap_msgfree.
   1092 					 */
   1093 					struct berval bv;
   1094 					ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
   1095 					bv.bv_val = LDAP_MALLOC( len );
   1096 					if ( bv.bv_val ) {
   1097 						ok = 1;
   1098 						ber_read( ber, bv.bv_val, len );
   1099 						bv.bv_len = len;
   1100 						ber_init2( ber, &bv, ld->ld_lberoptions );
   1101 					}
   1102 				}
   1103 			} else {
   1104 				/* LDAPv3: Just allocate a new ber. Since this is a buffered
   1105 				 * datagram, if the sockbuf is readable we still have data
   1106 				 * to parse.
   1107 				 */
   1108 				ber = ldap_alloc_ber_with_options( ld );
   1109 				if ( ber == NULL ) {
   1110 					ld->ld_errno = LDAP_NO_MEMORY;
   1111 					return -1;
   1112 				}
   1113 
   1114 				if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
   1115 			}
   1116 			/* set up response chain */
   1117 			if ( tmp == NULL ) {
   1118 				newmsg->lm_next = ld->ld_responses;
   1119 				ld->ld_responses = newmsg;
   1120 				chain_head = newmsg;
   1121 			} else {
   1122 				tmp->lm_chain = newmsg;
   1123 			}
   1124 			chain_head->lm_chain_tail = newmsg;
   1125 			tmp = newmsg;
   1126 			/* "ok" means there's more to parse */
   1127 			if ( ok ) {
   1128 				if ( isv2 ) {
   1129 					goto nextresp2;
   1130 
   1131 				} else {
   1132 					goto nextresp3;
   1133 				}
   1134 			} else {
   1135 				/* got to end of datagram without a SearchResult. Free
   1136 				 * our dup'd ber, but leave any buffer alone. For v2 case,
   1137 				 * the previous response is still using this buffer. For v3,
   1138 				 * the new ber has no buffer to free yet.
   1139 				 */
   1140 				ber_free( ber, 0 );
   1141 				return -1;
   1142 			}
   1143 		} else if ( moremsgs ) {
   1144 		/* got search result, and we had multiple responses in 1 datagram.
   1145 		 * stick the result onto the end of the chain, and then pull the
   1146 		 * first response off the head of the chain.
   1147 		 */
   1148 			tmp->lm_chain = newmsg;
   1149 			chain_head->lm_chain_tail = newmsg;
   1150 			*result = chkResponseList( ld, msgid, all );
   1151 			ld->ld_errno = LDAP_SUCCESS;
   1152 			return( (*result)->lm_msgtype );
   1153 		}
   1154 	}
   1155 #endif /* LDAP_CONNECTIONLESS */
   1156 
   1157 	/* is this the one we're looking for? */
   1158 	if ( msgid == LDAP_RES_ANY || id == msgid ) {
   1159 		if ( msgid == LDAP_RES_ANY && all == LDAP_MSG_RECEIVED ) {
   1160 			/* ITS#10229: We want to keep going so long as there's anything to
   1161 			 * read. */
   1162 		} else if ( all == LDAP_MSG_ONE
   1163 			|| ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
   1164 				&& newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
   1165 				&& newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE
   1166 			  	&& newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
   1167 		{
   1168 			*result = newmsg;
   1169 			ld->ld_errno = LDAP_SUCCESS;
   1170 			return( tag );
   1171 
   1172 		} else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
   1173 			foundit = 1;	/* return the chain later */
   1174 		}
   1175 	}
   1176 
   1177 	/*
   1178 	 * if not, we must add it to the list of responses.  if
   1179 	 * the msgid is already there, it must be part of an existing
   1180 	 * search response.
   1181 	 */
   1182 
   1183 	prev = NULL;
   1184 	for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
   1185 		if ( l->lm_msgid == newmsg->lm_msgid ) {
   1186 			break;
   1187 		}
   1188 		prev = l;
   1189 	}
   1190 
   1191 	/* not part of an existing search response */
   1192 	if ( l == NULL ) {
   1193 		if ( foundit ) {
   1194 			*result = newmsg;
   1195 			goto exit;
   1196 		}
   1197 
   1198 		newmsg->lm_next = ld->ld_responses;
   1199 		ld->ld_responses = newmsg;
   1200 		goto exit;
   1201 	}
   1202 
   1203 	Debug3( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
   1204 		(void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );
   1205 
   1206 	/* part of a search response - add to end of list of entries */
   1207 	l->lm_chain_tail->lm_chain = newmsg;
   1208 	l->lm_chain_tail = newmsg;
   1209 
   1210 	/* return the whole chain if that's what we were looking for */
   1211 	if ( foundit ) {
   1212 		if ( prev == NULL ) {
   1213 			ld->ld_responses = l->lm_next;
   1214 		} else {
   1215 			prev->lm_next = l->lm_next;
   1216 		}
   1217 		*result = l;
   1218 	}
   1219 
   1220 exit:
   1221 	if ( foundit ) {
   1222 		ld->ld_errno = LDAP_SUCCESS;
   1223 		return( tag );
   1224 	}
   1225 	if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
   1226 		goto retry;
   1227 	}
   1228 	return( LDAP_MSG_X_KEEP_LOOKING );	/* continue looking */
   1229 }
   1230 
   1231 
   1232 static ber_tag_t
   1233 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
   1234 {
   1235 	ber_len_t	len;
   1236 	ber_tag_t	tag;
   1237 	ber_int_t	along;
   1238 	BerElement *ber;
   1239 
   1240 	*bp = NULL;
   1241 	ber = ldap_alloc_ber_with_options( ld );
   1242 
   1243 	if( ber == NULL ) {
   1244 		ld->ld_errno = LDAP_NO_MEMORY;
   1245 		return LBER_ERROR;
   1246 	}
   1247 
   1248 	if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
   1249 		lr->lr_res_msgtype, lr->lr_res_errno,
   1250 		lr->lr_res_matched ? lr->lr_res_matched : "",
   1251 		lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
   1252 	{
   1253 		ld->ld_errno = LDAP_ENCODING_ERROR;
   1254 		ber_free( ber, 1 );
   1255 		return( LBER_ERROR );
   1256 	}
   1257 
   1258 	ber_reset( ber, 1 );
   1259 
   1260 	if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
   1261 		ld->ld_errno = LDAP_DECODING_ERROR;
   1262 		ber_free( ber, 1 );
   1263 		return( LBER_ERROR );
   1264 	}
   1265 
   1266 	if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
   1267 		ld->ld_errno = LDAP_DECODING_ERROR;
   1268 		ber_free( ber, 1 );
   1269 		return( LBER_ERROR );
   1270 	}
   1271 
   1272 	tag = ber_peek_tag( ber, &len );
   1273 
   1274 	if ( tag == LBER_ERROR ) {
   1275 		ld->ld_errno = LDAP_DECODING_ERROR;
   1276 		ber_free( ber, 1 );
   1277 		return( LBER_ERROR );
   1278 	}
   1279 
   1280 	*bp = ber;
   1281 	return tag;
   1282 }
   1283 
   1284 
   1285 /*
   1286  * Merge error information in "lr" with "parentr" error code and string.
   1287  */
   1288 static void
   1289 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
   1290 {
   1291 	if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
   1292 		parentr->lr_res_errno = lr->lr_res_errno;
   1293 		if ( lr->lr_res_error != NULL ) {
   1294 			(void)ldap_append_referral( ld, &parentr->lr_res_error,
   1295 				lr->lr_res_error );
   1296 		}
   1297 
   1298 	} else if ( lr->lr_res_errno != LDAP_SUCCESS &&
   1299 		parentr->lr_res_errno == LDAP_SUCCESS )
   1300 	{
   1301 		parentr->lr_res_errno = lr->lr_res_errno;
   1302 		if ( parentr->lr_res_error != NULL ) {
   1303 			LDAP_FREE( parentr->lr_res_error );
   1304 		}
   1305 		parentr->lr_res_error = lr->lr_res_error;
   1306 		lr->lr_res_error = NULL;
   1307 		if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
   1308 			if ( parentr->lr_res_matched != NULL ) {
   1309 				LDAP_FREE( parentr->lr_res_matched );
   1310 			}
   1311 			parentr->lr_res_matched = lr->lr_res_matched;
   1312 			lr->lr_res_matched = NULL;
   1313 		}
   1314 	}
   1315 
   1316 	Debug1( LDAP_DEBUG_TRACE, "merged parent (id %d) error info:  ",
   1317 		parentr->lr_msgid );
   1318 	Debug3( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
   1319 		parentr->lr_res_errno,
   1320 		parentr->lr_res_error ?  parentr->lr_res_error : "",
   1321 		parentr->lr_res_matched ?  parentr->lr_res_matched : "" );
   1322 }
   1323 
   1324 
   1325 
   1326 int
   1327 ldap_msgtype( LDAPMessage *lm )
   1328 {
   1329 	assert( lm != NULL );
   1330 	return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
   1331 }
   1332 
   1333 
   1334 int
   1335 ldap_msgid( LDAPMessage *lm )
   1336 {
   1337 	assert( lm != NULL );
   1338 
   1339 	return ( lm != NULL ) ? lm->lm_msgid : -1;
   1340 }
   1341 
   1342 
   1343 const char *
   1344 ldap_int_msgtype2str( ber_tag_t tag )
   1345 {
   1346 	switch( tag ) {
   1347 	case LDAP_RES_ADD: return "add";
   1348 	case LDAP_RES_BIND: return "bind";
   1349 	case LDAP_RES_COMPARE: return "compare";
   1350 	case LDAP_RES_DELETE: return "delete";
   1351 	case LDAP_RES_EXTENDED: return "extended-result";
   1352 	case LDAP_RES_INTERMEDIATE: return "intermediate";
   1353 	case LDAP_RES_MODIFY: return "modify";
   1354 	case LDAP_RES_RENAME: return "rename";
   1355 	case LDAP_RES_SEARCH_ENTRY: return "search-entry";
   1356 	case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
   1357 	case LDAP_RES_SEARCH_RESULT: return "search-result";
   1358 	}
   1359 	return "unknown";
   1360 }
   1361 
   1362 int
   1363 ldap_msgfree( LDAPMessage *lm )
   1364 {
   1365 	LDAPMessage	*next;
   1366 	int		type = 0;
   1367 
   1368 	Debug0( LDAP_DEBUG_TRACE, "ldap_msgfree\n" );
   1369 
   1370 	for ( ; lm != NULL; lm = next ) {
   1371 		next = lm->lm_chain;
   1372 		type = lm->lm_msgtype;
   1373 		ber_free( lm->lm_ber, 1 );
   1374 		LDAP_FREE( (char *) lm );
   1375 	}
   1376 
   1377 	return type;
   1378 }
   1379 
   1380 /*
   1381  * ldap_msgdelete - delete a message.  It returns:
   1382  *	0	if the entire message was deleted
   1383  *	-1	if the message was not found, or only part of it was found
   1384  */
   1385 int
   1386 ldap_msgdelete( LDAP *ld, int msgid )
   1387 {
   1388 	LDAPMessage	*lm, *prev;
   1389 	int		rc = 0;
   1390 
   1391 	assert( ld != NULL );
   1392 
   1393 	Debug2( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
   1394 		(void *)ld, msgid );
   1395 
   1396 	LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
   1397 	prev = NULL;
   1398 	for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
   1399 		if ( lm->lm_msgid == msgid ) {
   1400 			break;
   1401 		}
   1402 		prev = lm;
   1403 	}
   1404 
   1405 	if ( lm == NULL ) {
   1406 		rc = -1;
   1407 
   1408 	} else {
   1409 		if ( prev == NULL ) {
   1410 			ld->ld_responses = lm->lm_next;
   1411 		} else {
   1412 			prev->lm_next = lm->lm_next;
   1413 		}
   1414 	}
   1415 	LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
   1416 	if ( lm ) {
   1417 		switch ( ldap_msgfree( lm ) ) {
   1418 		case LDAP_RES_SEARCH_ENTRY:
   1419 		case LDAP_RES_SEARCH_REFERENCE:
   1420 		case LDAP_RES_INTERMEDIATE:
   1421 			rc = -1;
   1422 			break;
   1423 
   1424 		default:
   1425 			break;
   1426 		}
   1427 	}
   1428 
   1429 	return rc;
   1430 }
   1431 
   1432 
   1433 /*
   1434  * ldap_abandoned
   1435  *
   1436  * return the location of the message id in the array of abandoned
   1437  * message ids, or -1
   1438  */
   1439 static int
   1440 ldap_abandoned( LDAP *ld, ber_int_t msgid )
   1441 {
   1442 	int	ret, idx;
   1443 	assert( msgid >= 0 );
   1444 
   1445 	LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
   1446 	ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx );
   1447 	LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
   1448 	return ret;
   1449 }
   1450 
   1451 /*
   1452  * ldap_mark_abandoned
   1453  */
   1454 static int
   1455 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid )
   1456 {
   1457 	int	ret, idx;
   1458 
   1459 	assert( msgid >= 0 );
   1460 	LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
   1461 	ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx );
   1462 	if (ret <= 0) {		/* error or already deleted by another thread */
   1463 		LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
   1464 		return ret;
   1465 	}
   1466 	/* still in abandoned array, so delete */
   1467 	ret = ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,
   1468 		msgid, idx );
   1469 	LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
   1470 	return ret;
   1471 }
   1472