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