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