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