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