result.c 35.4 KB
Newer Older
1
/* result.c - wait for an ldap result */
2
/* $OpenLDAP$ */
3
4
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
5
 * Copyright 1998-2021 The OpenLDAP Foundation.
6
7
8
9
10
11
12
13
14
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
15
 */
16
17
/* Portions Copyright (c) 1990 Regents of the University of Michigan.
 * All rights reserved.
18
 */
19
/* This notice applies to changes, created by or for Novell, Inc.,
20
21
22
23
24
25
26
27
28
29
30
31
 * to preexisting works for which notices appear elsewhere in this file.
 *
 * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
 *
 * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
 * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
 * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
 * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
 * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
 * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
 * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
 * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 
32
 *---
33
34
 * Modification to OpenLDAP source by Novell, Inc.
 * 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
38
39
 * can be found in the file "build/LICENSE-2.0.1" in this distribution
 * of OpenLDAP Software.
 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
40

41
/*
42
 * LDAPv3 (RFC 4511)
43
 *	LDAPResult ::= SEQUENCE {
44
45
 *		resultCode			ENUMERATED { ... },
 *		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
54
#include "portable.h"

Kurt Zeilenga's avatar
Kurt Zeilenga committed
55
#include <stdio.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
56
57

#include <ac/stdlib.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
58

59
60
61
62
63
#include <ac/errno.h>
#include <ac/socket.h>
#include <ac/string.h>
#include <ac/time.h>
#include <ac/unistd.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
64

Kurt Zeilenga's avatar
Kurt Zeilenga committed
65
#include "ldap-int.h"
Julius Enarusai's avatar
   
Julius Enarusai committed
66
#include "ldap_log.h"
67
#include "lutil.h"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
68

69
70
static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
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));
Kurt Zeilenga's avatar
Kurt Zeilenga committed
78

Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
79
80
#define LDAP_MSG_X_KEEP_LOOKING		(-2)

Kurt Zeilenga's avatar
Kurt Zeilenga committed
81
82
83

/*
 * ldap_result - wait for an ldap result response to a message from the
84
85
86
87
88
89
 * ldap server.  If msgid is LDAP_RES_ANY (-1), any message will be
 * accepted.  If msgid is LDAP_RES_UNSOLICITED (0), any unsolicited
 * message is accepted.  Otherwise ldap_result will wait for a response
 * with msgid.  If all is LDAP_MSG_ONE (0) the first message with id
 * msgid will be accepted, otherwise, ldap_result will wait for all
 * responses with id msgid and then return a pointer to the entire list
90
91
92
93
94
 * of messages.  In general, this is only useful for search responses,
 * which can be of three message types (zero or more entries, zero or
 * search references, followed by an ldap result).  An extension to
 * LDAPv3 allows partial extended responses to be returned in response
 * to any request.  The type of the first message received is returned.
95
96
 * When waiting, any messages that have been abandoned/discarded are 
 * discarded.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
97
98
99
100
101
 *
 * Example:
 *	ldap_result( s, msgid, all, timeout, result )
 */
int
102
103
104
105
106
ldap_result(
	LDAP *ld,
	int msgid,
	int all,
	struct timeval *timeout,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
107
108
	LDAPMessage **result )
{
109
	int		rc;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
110

Kurt Zeilenga's avatar
Kurt Zeilenga committed
111
112
113
	assert( ld != NULL );
	assert( result != NULL );

114
	Debug2( LDAP_DEBUG_TRACE, "ldap_result ld %p msgid %d\n", (void *)ld, msgid );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
115

116
117
118
	if (ld->ld_errno == LDAP_LOCAL_ERROR || ld->ld_errno == LDAP_SERVER_DOWN)
		return -1;

Howard Chu's avatar
Howard Chu committed
119
	LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
120
	rc = wait4msg( ld, msgid, all, timeout, result );
Howard Chu's avatar
Howard Chu committed
121
	LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
122
123

	return rc;
124
125
}

126
/* protected by res_mutex */
127
static LDAPMessage *
128
129
130
131
chkResponseList(
	LDAP *ld,
	int msgid,
	int all)
132
{
133
	LDAPMessage	*lm, **lastlm, *nextlm;
134
	int		cnt = 0;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
135
136

	/*
137
	 * Look through the list of responses we have received on
Kurt Zeilenga's avatar
Kurt Zeilenga committed
138
139
140
141
142
	 * this association and see if the response we're interested in
	 * is there.  If it is, return it.  If not, call wait4msg() to
	 * wait until it arrives or timeout occurs.
	 */

Howard Chu's avatar
Howard Chu committed
143
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
144

145
	Debug3( LDAP_DEBUG_TRACE,
146
		"ldap_chkResponseList ld %p msgid %d all %d\n",
Pierangelo Masarati's avatar
Pierangelo Masarati committed
147
		(void *)ld, msgid, all );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
148

149
	lastlm = &ld->ld_responses;
150
	for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
151
		nextlm = lm->lm_next;
152
		++cnt;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
153

154
		if ( ldap_abandoned( ld, lm->lm_msgid ) ) {
155
			Debug2( LDAP_DEBUG_ANY,
156
157
				"response list msg abandoned, "
				"msgid %d message type %s\n",
158
				lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ) );
159
160
161
162
163
164
165
166
167
168

			switch ( lm->lm_msgtype ) {
			case LDAP_RES_SEARCH_ENTRY:
			case LDAP_RES_SEARCH_REFERENCE:
			case LDAP_RES_INTERMEDIATE:
				break;

			default:
				/* there's no need to keep the id
				 * in the abandoned list any longer */
169
				ldap_mark_abandoned( ld, lm->lm_msgid );
170
171
				break;
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
172

173
174
			/* Remove this entry from list */
			*lastlm = nextlm;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
175
176
177
178
179
180
181
182
183

			ldap_msgfree( lm );

			continue;
		}

		if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
			LDAPMessage	*tmp;

184
185
			if ( all == LDAP_MSG_ONE ||
				all == LDAP_MSG_RECEIVED ||
186
187
				msgid == LDAP_RES_UNSOLICITED )
			{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
188
				break;
189
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
190

191
			tmp = lm->lm_chain_tail;
192
193
194
			if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY ||
				tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ||
				tmp->lm_msgtype == LDAP_RES_INTERMEDIATE )
195
			{
196
				tmp = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
197
198
			}

199
			if ( tmp == NULL ) {
200
				lm = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
201
202
203
204
			}

			break;
		}
205
		lastlm = &lm->lm_next;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
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;
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
212
213
214
215
			lm->lm_chain->lm_next = lm->lm_next;
			lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain;
			lm->lm_chain = NULL;
			lm->lm_chain_tail = NULL;
216
		} else {
217
218
			*lastlm = lm->lm_next;
		}
219
220
		lm->lm_next = NULL;
	}
221
222

#ifdef LDAP_DEBUG
223
	if ( lm == NULL) {
224
225
		Debug1( LDAP_DEBUG_TRACE,
			"ldap_chkResponseList returns ld %p NULL\n", (void *)ld );
226
	} else {
227
		Debug3( LDAP_DEBUG_TRACE,
Pierangelo Masarati's avatar
Pierangelo Masarati committed
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;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
234
}
235

236
/* protected by res_mutex */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
237
static int
238
239
240
241
242
wait4msg(
	LDAP *ld,
	ber_int_t msgid,
	int all,
	struct timeval *timeout,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
243
244
245
	LDAPMessage **result )
{
	int		rc;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
246
247
	struct timeval	tv = { 0 },
			tv0 = { 0 },
248
249
			start_time_tv = { 0 },
			*tvp = NULL;
250
	LDAPConn	*lc;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
251

Kurt Zeilenga's avatar
Kurt Zeilenga committed
252
253
254
	assert( ld != NULL );
	assert( result != NULL );

Howard Chu's avatar
Howard Chu committed
255
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
256

Howard Chu's avatar
Howard Chu committed
257
258
259
	if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) {
		tv = ld->ld_options.ldo_tm_api;
		timeout = &tv;
260
261
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
262
263
#ifdef LDAP_DEBUG
	if ( timeout == NULL ) {
264
265
		Debug2( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
			(void *)ld, msgid );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
266
	} else {
267
		Debug3( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n",
Pierangelo Masarati's avatar
Pierangelo Masarati committed
268
			(void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
269
270
271
	}
#endif /* LDAP_DEBUG */

272
	if ( timeout != NULL && timeout->tv_sec != -1 ) {
273
		tv0 = *timeout;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
274
275
		tv = *timeout;
		tvp = &tv;
276
277
278
#ifdef HAVE_GETTIMEOFDAY
		gettimeofday( &start_time_tv, NULL );
#else /* ! HAVE_GETTIMEOFDAY */
279
		start_time_tv.tv_sec = time( NULL );
280
281
		start_time_tv.tv_usec = 0;
#endif /* ! HAVE_GETTIMEOFDAY */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
282
283
	}
		    
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
284
285
	rc = LDAP_MSG_X_KEEP_LOOKING;
	while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
286
287
#ifdef LDAP_DEBUG
		if ( ldap_debug & LDAP_DEBUG_TRACE ) {
288
			Debug3( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
Pierangelo Masarati's avatar
Pierangelo Masarati committed
289
				(void *)ld, msgid, all );
290
			ldap_dump_connection( ld, ld->ld_conns, 1 );
Howard Chu's avatar
Howard Chu committed
291
			LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
292
			ldap_dump_requests_and_responses( ld );
Howard Chu's avatar
Howard Chu committed
293
			LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
294
295
296
		}
#endif /* LDAP_DEBUG */

Howard Chu's avatar
Howard Chu committed
297
		if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
298
299
300
			rc = (*result)->lm_msgtype;

		} else {
301
			int lc_ready = 0;
302

Howard Chu's avatar
Howard Chu committed
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 ) )
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
307
				{
308
					lc_ready = 2;	/* ready at ber level, not socket level */
309
					break;
310
				}
311
			}
312

Howard Chu's avatar
Howard Chu committed
313
			if ( !lc_ready ) {
Howard Chu's avatar
Howard Chu committed
314
				int err;
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
315
316
				rc = ldap_int_select( ld, tvp );
				if ( rc == -1 ) {
Howard Chu's avatar
Howard Chu committed
317
318
					err = sock_errno();
#ifdef LDAP_DEBUG
319
					Debug1( LDAP_DEBUG_TRACE,
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
320
						"ldap_int_select returned -1: errno %d\n",
321
						err );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
322
#endif
Howard Chu's avatar
Howard Chu committed
323
				}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
324

Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
325
326
				if ( rc == 0 || ( rc == -1 && (
					!LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
Howard Chu's avatar
Howard Chu committed
327
						|| err != EINTR ) ) )
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
328
329
330
				{
					ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
						LDAP_TIMEOUT);
331
					LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
332
333
334
335
					return( rc );
				}

				if ( rc == -1 ) {
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
336
					rc = LDAP_MSG_X_KEEP_LOOKING;	/* select interrupted: loop */
337

Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
338
				} else {
339
340
341
342
343
					lc_ready = 1;
				}
			}
			if ( lc_ready ) {
				LDAPConn *lnext;
344
				int serviced = 0;
345
				rc = LDAP_MSG_X_KEEP_LOOKING;
Howard Chu's avatar
Howard Chu committed
346
				LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
347
348
349
350
351
352
353
354
355
356
357
				if ( ld->ld_requests != NULL ) {
					TAvlnode *node = ldap_tavl_end( ld->ld_requests, TAVL_DIR_RIGHT );
					LDAPRequest *lr;

					assert( node != NULL );
					lr = node->avl_data;
					if ( lr->lr_status == LDAP_REQST_WRITING &&
							ldap_is_write_ready( ld, lr->lr_conn->lconn_sb ) ) {
						serviced = 1;
						ldap_int_flush_request( ld, lr );
					}
358
359
360
361
362
363
364
				}
				for ( lc = ld->ld_conns;
					rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL;
					lc = lnext )
				{
					if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
						ldap_is_read_ready( ld, lc->lconn_sb ) )
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
365
					{
366
						serviced = 1;
367
368
369
370
371
372
373
374
375
376
						/* Don't let it get freed out from under us */
						++lc->lconn_refcnt;
						rc = try_read1msg( ld, msgid, all, lc, result );
						lnext = lc->lconn_next;

						/* Only take locks if we're really freeing */
						if ( lc->lconn_refcnt <= 1 ) {
							ldap_free_connection( ld, lc, 0, 1 );
						} else {
							--lc->lconn_refcnt;
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
377
						}
378
379
380
					} else {
						lnext = lc->lconn_next;
					}
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
381
				}
382
				LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
383
384
				/* Quit looping if no one handled any socket events */
				if (!serviced && lc_ready == 1)
385
					rc = -1;
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
386
			}
387
			LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
388
389
		}

Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
390
		if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
391
392
393
394
395
396
			struct timeval	curr_time_tv = { 0 },
					delta_time_tv = { 0 };

#ifdef HAVE_GETTIMEOFDAY
			gettimeofday( &curr_time_tv, NULL );
#else /* ! HAVE_GETTIMEOFDAY */
397
			curr_time_tv.tv_sec = time( NULL );
398
399
400
401
402
403
404
405
406
407
			curr_time_tv.tv_usec = 0;
#endif /* ! HAVE_GETTIMEOFDAY */

			/* delta_time = tmp_time - start_time */
			delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
			delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
			if ( delta_time_tv.tv_usec < 0 ) {
				delta_time_tv.tv_sec--;
				delta_time_tv.tv_usec += 1000000;
			}
408

409
410
411
412
413
			/* tv0 < delta_time ? */
			if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
			     ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
			{
				rc = 0; /* timed out */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
414
415
416
				ld->ld_errno = LDAP_TIMEOUT;
				break;
			}
417
418
419
420
421
422
423
424
425

			/* tv0 -= delta_time */
			tv0.tv_sec -= delta_time_tv.tv_sec;
			tv0.tv_usec -= delta_time_tv.tv_usec;
			if ( tv0.tv_usec < 0 ) {
				tv0.tv_sec--;
				tv0.tv_usec += 1000000;
			}

426
			tv.tv_sec = tv0.tv_sec;
427
428
			tv.tv_usec = tv0.tv_usec;

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 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
431

432
433
			start_time_tv.tv_sec = curr_time_tv.tv_sec;
			start_time_tv.tv_usec = curr_time_tv.tv_usec;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
434
435
436
		}
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
437
	return( rc );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
438
439
440
}


441
/* protected by res_mutex, conn_mutex and req_mutex */
442
443
444
445
446
static ber_tag_t
try_read1msg(
	LDAP *ld,
	ber_int_t msgid,
	int all,
447
	LDAPConn *lc,
448
	LDAPMessage **result )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
449
{
450
	BerElement	*ber;
451
	LDAPMessage	*newmsg, *l, *prev;
452
453
454
	ber_int_t	id;
	ber_tag_t	tag;
	ber_len_t	len;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
455
	int		foundit = 0;
456
	LDAPRequest	*lr, *tmplr, dummy_lr = { 0 };
Kurt Zeilenga's avatar
Kurt Zeilenga committed
457
	BerElement	tmpber;
Howard Chu's avatar
Howard Chu committed
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

Kurt Zeilenga's avatar
Kurt Zeilenga committed
466
	assert( ld != NULL );
467
	assert( lc != NULL );
468
	
Howard Chu's avatar
Howard Chu committed
469
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
470
471
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
472

473
	Debug3( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
Pierangelo Masarati's avatar
Pierangelo Masarati committed
474
		(void *)ld, msgid, all );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
475

476
retry:
477
	if ( lc->lconn_ber == NULL ) {
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
478
		lc->lconn_ber = ldap_alloc_ber_with_options( ld );
479

480
		if ( lc->lconn_ber == NULL ) {
481
482
			return -1;
		}
483
	}
484
485

	ber = lc->lconn_ber;
486
	assert( LBER_VALID (ber) );
Kurt Zeilenga's avatar
ldap.h:    
Kurt Zeilenga committed
487

Kurt Zeilenga's avatar
Kurt Zeilenga committed
488
	/* get the next message */
489
	sock_errset(0);
490
491
#ifdef LDAP_CONNECTIONLESS
	if ( LDAP_IS_UDP(ld) ) {
492
		struct sockaddr_storage from;
493
494
		if ( ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr_storage) ) < 0 )
			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 );
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
500
501
	switch ( tag ) {
	case LDAP_TAG_MESSAGE:
502
503
504
505
506
		/*
	 	 * We read a complete message.
	 	 * The connection should no longer need this ber.
	 	 */
		lc->lconn_ber = NULL;
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
507
508
509
		break;

	case LBER_DEFAULT:
510
fail:
Howard Chu's avatar
Howard Chu committed
511
		err = sock_errno();
512
#ifdef LDAP_DEBUG		   
513
514
		Debug1( LDAP_DEBUG_CONNS,
			"ber_get_next failed, errno=%d.\n", err );
515
#endif		   
Howard Chu's avatar
Howard Chu committed
516
517
		if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
		if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
518
		ld->ld_errno = LDAP_SERVER_DOWN;
519
520
521
		if ( !LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_KEEPCONN )) {
			--lc->lconn_refcnt;
		}
522
		lc->lconn_status = 0;
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
523
524
525
		return -1;

	default:
526
527
		ld->ld_errno = LDAP_LOCAL_ERROR;
		return -1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
528
529
530
	}

	/* message id */
531
	if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
532
		ber_free( ber, 1 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
533
534
535
536
		ld->ld_errno = LDAP_DECODING_ERROR;
		return( -1 );
	}

537
538
	/* id == 0 iff unsolicited notification message (RFC 4511) */

539
540
541
542
543
	/* id < 0 is invalid, just toss it. FIXME: should we disconnect? */
	if ( id < 0 ) {
		goto retry_ber;
	}
	
Kurt Zeilenga's avatar
Kurt Zeilenga committed
544
	/* if it's been abandoned, toss it */
545
	if ( id > 0 ) {
546
		if ( ldap_abandoned( ld, id ) ) {
547
548
549
550
551
552
553
554
			/* the message type */
			tag = ber_peek_tag( ber, &len );
			switch ( tag ) {
			case LDAP_RES_SEARCH_ENTRY:
			case LDAP_RES_SEARCH_REFERENCE:
			case LDAP_RES_INTERMEDIATE:
			case LBER_ERROR:
				break;
555

556
557
558
			default:
				/* there's no need to keep the id
				 * in the abandoned list any longer */
559
				ldap_mark_abandoned( ld, id );
560
561
				break;
			}
562

563
			Debug3( LDAP_DEBUG_ANY,
564
565
				"abandoned/discarded ld %p msgid %d message type %s\n",
				(void *)ld, id, ldap_int_msgtype2str( tag ) );
566

567
retry_ber:
568
569
570
571
572
			ber_free( ber, 1 );
			if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
				goto retry;
			}
			return( LDAP_MSG_X_KEEP_LOOKING );	/* continue looking */
573
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
574

575
576
577
		lr = ldap_find_request_by_msgid( ld, id );
		if ( lr == NULL ) {
			const char	*msg = "unknown";
578

579
580
581
582
583
			/* the message type */
			tag = ber_peek_tag( ber, &len );
			switch ( tag ) {
			case LBER_ERROR:
				break;
584

585
586
587
588
			default:
				msg = ldap_int_msgtype2str( tag );
				break;
			}
589

590
			Debug3( LDAP_DEBUG_ANY,
591
592
				"no request for response on ld %p msgid %d message type %s (tossing)\n",
				(void *)ld, id, msg );
593
594
595

			goto retry_ber;
		}
596

597
#ifdef LDAP_CONNECTIONLESS
598
599
600
		if ( LDAP_IS_UDP(ld) && isv2 ) {
			ber_scanf(ber, "x{");
		}
Howard Chu's avatar
Howard Chu committed
601
nextresp2:
602
		;
603
#endif
604
605
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
606
	/* the message type */
607
608
	tag = ber_peek_tag( ber, &len );
	if ( tag == LBER_ERROR ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
609
		ld->ld_errno = LDAP_DECODING_ERROR;
610
		ber_free( ber, 1 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
611
612
613
		return( -1 );
	}

614
	Debug3( LDAP_DEBUG_TRACE,
615
616
		"read1msg: ld %p msgid %d message type %s\n",
		(void *)ld, id, ldap_int_msgtype2str( tag ) );
617

618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
	if ( id == 0 ) {
		/* unsolicited notification message (RFC 4511) */
		if ( tag != LDAP_RES_EXTENDED ) {
			/* toss it */
			goto retry_ber;

			/* strictly speaking, it's an error; from RFC 4511:

4.4.  Unsolicited Notification

   An unsolicited notification is an LDAPMessage sent from the server to
   the client that is not in response to any LDAPMessage received by the
   server.  It is used to signal an extraordinary condition in the
   server or in the LDAP session between the client and the server.  The
   notification is of an advisory nature, and the server will not expect
   any response to be returned from the client.

   The unsolicited notification is structured as an LDAPMessage in which
   the messageID is zero and protocolOp is set to the extendedResp
   choice using the ExtendedResponse type (See Section 4.12).  The
   responseName field of the ExtendedResponse always contains an LDAPOID
   that is unique for this notification.

			 * however, since unsolicited responses
			 * are of advisory nature, better
			 * toss it, right now
			 */

#if 0
			ld->ld_errno = LDAP_DECODING_ERROR;
			ber_free( ber, 1 );
			return( -1 );
#endif
		}

		lr = &dummy_lr;
	}

656
	id = lr->lr_origid;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
657
658
	refer_cnt = 0;
	hadref = simple_request = 0;
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
659
	rc = LDAP_MSG_X_KEEP_LOOKING;	/* default is to keep looking (no response found) */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
660
661
	lr->lr_res_msgtype = tag;

662
	/*
663
	 * Check for V3 search reference
664
	 */
665
666
	if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
		if ( ld->ld_version > LDAP_VERSION2 ) {
667
			/* This is a V3 search reference */
668
669
			if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
					lr->lr_parent != NULL )
670
			{
671
672
673
				char **refs = NULL;
				tmpber = *ber;

674
675
676
				/* Get the referral list */
				if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
					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 */
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
685
						if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
686
							lr->lr_status = LDAP_REQST_CHASINGREFS;
687
							Debug1( LDAP_DEBUG_TRACE,
688
689
690
								"read1msg:  search ref chased, "
								"mark request chasing refs, "
								"id = %d\n",
691
								lr->lr_msgid );
692
693
694
695
						}
					}
				}
			}
696
		}
697
698
699
700
701
702

	} else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
		/* All results that just return a status, i.e. don't return data
		 * go through the following code.  This code also chases V2 referrals
		 * and checks if all referrals have been chased.
		 */
703
		char		*lr_res_error = NULL;
704

705
706
707
708
709
710
711
712
713
		tmpber = *ber; 	/* struct copy */
		if ( ber_scanf( &tmpber, "{eAA", &lderr,
				&lr->lr_res_matched, &lr_res_error )
				!= LBER_ERROR )
		{
			if ( lr_res_error != NULL ) {
				if ( lr->lr_res_error != NULL ) {
					(void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
					LDAP_FREE( (char *)lr_res_error );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
714

715
716
				} else {
					lr->lr_res_error = lr_res_error;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
717
				}
718
719
720
721
				lr_res_error = NULL;
			}

			/* Do we need to check for referrals? */
722
723
724
			if ( tag != LDAP_RES_BIND &&
				( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
					lr->lr_parent != NULL ))
725
726
727
			{
				char		**refs = NULL;
				ber_len_t	len;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
728

729
				/* Check if V3 referral */
Pierangelo Masarati's avatar
Pierangelo Masarati committed
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
735
							rc = LDAP_DECODING_ERROR;
							lr->lr_status = LDAP_REQST_COMPLETED;
736
							Debug2( LDAP_DEBUG_TRACE,
737
738
								"read1msg: referral decode error, "
								"mark request completed, ld %p msgid %d\n",
739
								(void *)ld, lr->lr_msgid );
740

741
742
						} else {
							/* Chase the referral 
743
							 * refs array is freed by ldap_chase_v3referrals
744
745
							 */
							refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
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
759
760
761
762
763
				} else {
					switch ( lderr ) {
					case LDAP_SUCCESS:
					case LDAP_COMPARE_TRUE:
					case LDAP_COMPARE_FALSE:
						break;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
764

765
					default:
766
						if ( lr->lr_res_error == NULL ) {
767
768
							break;
						}
769

770
771
772
773
774
775
776
						/* pedantic, should never happen */
						if ( lr->lr_res_error[ 0 ] == '\0' ) {
							LDAP_FREE( lr->lr_res_error );
							lr->lr_res_error = NULL;
							break;	
						}

777
778
779
780
						/* V2 referrals are in error string */
						refer_cnt = ldap_chase_referrals( ld, lr,
							&lr->lr_res_error, -1, &hadref );
						lr->lr_status = LDAP_REQST_COMPLETED;
781
						Debug1( LDAP_DEBUG_TRACE,
782
783
							"read1msg:  V2 referral chased, "
							"mark request completed, id = %d\n",
784
							lr->lr_msgid );
785
786
						break;
					}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
787
				}
788
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
789

790
791
792
793
794
			/* save errno, message, and matched string */
			if ( !hadref || lr->lr_res_error == NULL ) {
				lr->lr_res_errno =
					lderr == LDAP_PARTIAL_RESULTS
					? LDAP_SUCCESS : lderr;
795

796
797
			} else if ( ld->ld_errno != LDAP_SUCCESS ) {
				lr->lr_res_errno = ld->ld_errno;
798

799
800
			} else {
				lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
801
			}
802
		}
803

804
805
806
		/* in any case, don't leave any lr_res_error 'round */
		if ( lr_res_error ) {
			LDAP_FREE( lr_res_error );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
807
808
		}

809
		Debug2( LDAP_DEBUG_TRACE,
Pierangelo Masarati's avatar
Pierangelo Masarati committed
810
			"read1msg: ld %p %d new referrals\n",
811
			(void *)ld, refer_cnt );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
812
813

		if ( refer_cnt != 0 ) {	/* chasing referrals */
814
815
			ber_free( ber, 1 );
			ber = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
816
			if ( refer_cnt < 0 ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
817
				ldap_return_request( ld, lr, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
818
819
				return( -1 );	/* fatal error */
			}
Josh Soref's avatar
Josh Soref committed
820
			lr->lr_res_errno = LDAP_SUCCESS; /* successfully chased referral */
821
822
823
824
			if ( lr->lr_res_matched ) {
				LDAP_FREE( lr->lr_res_matched );
				lr->lr_res_matched = NULL;
			}
825

Kurt Zeilenga's avatar
Kurt Zeilenga committed
826
827
828
829
		} else {
			if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
				/* request without any referrals */
				simple_request = ( hadref ? 0 : 1 );
830

Kurt Zeilenga's avatar
Kurt Zeilenga committed
831
832
			} else {
				/* request with referrals or child request */
833
834
				ber_free( ber, 1 );
				ber = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
835
836
			}

837
			lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
838
			Debug2( LDAP_DEBUG_TRACE,
Pierangelo Masarati's avatar
Pierangelo Masarati committed
839
				"read1msg:  mark request completed, ld %p msgid %d\n",
840
				(void *)ld, lr->lr_msgid );
Howard Chu's avatar
Howard Chu committed
841
			tmplr = lr;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
842
843
844
845
846
847
848
849
			while ( lr->lr_parent != NULL ) {
				merge_error_info( ld, lr->lr_parent, lr );

				lr = lr->lr_parent;
				if ( --lr->lr_outrefcnt > 0 ) {
					break;	/* not completely done yet */
				}
			}
Howard Chu's avatar
Howard Chu committed
850
851
852
			/* ITS#6744: Original lr was refcounted when we retrieved it,
			 * must release it now that we're working with the parent
			 */
Howard Chu's avatar
Howard Chu committed
853
854
			if ( tmplr->lr_parent ) {
				ldap_return_request( ld, tmplr, 0 );
Howard Chu's avatar
Howard Chu committed
855
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
856

857
			/* Check if all requests are finished, lr is now parent */
858
			tmplr = lr;
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
859
			if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
860
				for ( tmplr = lr->lr_child;
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
861
					tmplr != NULL;
862
					tmplr = tmplr->lr_refnext )
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
863
				{
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
864
					if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
865
866
867
868
				}
			}

			/* This is the parent request if the request has referrals */
869
870
			if ( lr->lr_outrefcnt <= 0 &&
				lr->lr_parent == NULL &&
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
871
872
				tmplr == NULL )
			{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
873
874
				id = lr->lr_msgid;
				tag = lr->lr_res_msgtype;
875
876
877
				Debug2( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
					(void *)ld, id );
				Debug3( LDAP_DEBUG_TRACE,
878
879
880
881
882
					"res_errno: %d, res_error: <%s>, "
					"res_matched: <%s>\n",
					lr->lr_res_errno,
					lr->lr_res_error ? lr->lr_res_error : "",
					lr->lr_res_matched ? lr->lr_res_matched : "" );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
883
				if ( !simple_request ) {
884
885
					ber_free( ber, 1 );
					ber = NULL;
886
					if ( build_result_ber( ld, &ber, lr )
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
887
888
					    == LBER_ERROR )
					{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
889
890
891
892
						rc = -1; /* fatal error */
					}
				}

893
894
895
				if ( lr != &dummy_lr ) {
					ldap_return_request( ld, lr, 1 );
				}
896
				lr = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
897
898
			}

Pierangelo Masarati's avatar