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