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