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