1 /*
2  * Copyright 2001-2003 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
9  *
10  * The contents of this file are subject to the Netscape Public License
11  * Version 1.0 (the "NPL"); you may not use this file except in
12  * compliance with the NPL.  You may obtain a copy of the NPL at
13  * http://www.mozilla.org/NPL/
14  *
15  * Software distributed under the NPL is distributed on an "AS IS" basis,
16  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
17  * for the specific language governing rights and limitations under the
18  * NPL.
19  *
20  * The Initial Developer of this code under the NPL is Netscape
21  * Communications Corporation.  Portions created by Netscape are
22  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
23  * Reserved.
24  */
25 /*
26  *  Copyright (c) 1990 Regents of the University of Michigan.
27  *  All rights reserved.
28  */
29 /*
30  *  result.c - wait for an ldap result
31  */
32 
33 #if 0
34 #ifndef lint
35 static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
36 #endif
37 #endif
38 
39 #include "ldap-int.h"
40 
41 #ifdef _SOLARIS_SDK
42 /* high resolution timer usage */
43 #include <sys/time.h>
44 #endif
45 
46 static int check_response_queue( LDAP *ld, int msgid, int all,
47 	int do_abandon_check, LDAPMessage **result );
48 static int ldap_abandoned( LDAP *ld, int msgid );
49 static int ldap_mark_abandoned( LDAP *ld, int msgid );
50 static int wait4msg( LDAP *ld, int msgid, int all, int unlock_permitted,
51 	struct timeval *timeout, LDAPMessage **result );
52 static int read1msg( LDAP *ld, int msgid, int all, Sockbuf *sb, LDAPConn *lc,
53 	LDAPMessage **result );
54 static void check_for_refs( LDAP *ld, LDAPRequest *lr, BerElement *ber,
55 	int ldapversion, int *totalcountp, int *chasingcountp );
56 static int build_result_ber( LDAP *ld, BerElement **berp, LDAPRequest *lr );
57 static void merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr );
58 #if defined( CLDAP )
59 static int cldap_select1( LDAP *ld, struct timeval *timeout );
60 #endif
61 static void link_pend( LDAP *ld, LDAPPend *lp );
62 #if 0 /* these functions are no longer used */
63 static void unlink_pend( LDAP *ld, LDAPPend *lp );
64 static int unlink_msg( LDAP *ld, int msgid, int all );
65 #endif /* 0 */
66 
67 /*
68  * ldap_result - wait for an ldap result response to a message from the
69  * ldap server.  If msgid is -1, any message will be accepted, otherwise
70  * ldap_result will wait for a response with msgid.  If all is 0 the
71  * first message with id msgid will be accepted, otherwise, ldap_result
72  * will wait for all responses with id msgid and then return a pointer to
73  * the entire list of messages.  This is only useful for search responses,
74  * which can be of two message types (zero or more entries, followed by an
75  * ldap result).  The type of the first message received is returned.
76  * When waiting, any messages that have been abandoned are discarded.
77  *
78  * Example:
79  *	ldap_result( s, msgid, all, timeout, result )
80  */
81 int
82 LDAP_CALL
ldap_result(LDAP * ld,int msgid,int all,struct timeval * timeout,LDAPMessage ** result)83 ldap_result(
84     LDAP 		*ld,
85     int 		msgid,
86     int 		all,
87     struct timeval	*timeout,
88     LDAPMessage		**result
89 )
90 {
91 	int		rc;
92 
93 	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_result\n", 0, 0, 0 );
94 
95 	if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
96 		return( -1 );	/* punt */
97 	}
98 
99 	LDAP_MUTEX_LOCK( ld, LDAP_RESULT_LOCK );
100 
101 	rc = nsldapi_result_nolock(ld, msgid, all, 1, timeout, result);
102 
103 	LDAP_MUTEX_UNLOCK( ld, LDAP_RESULT_LOCK );
104 
105 	return( rc );
106 }
107 
108 
109 int
nsldapi_result_nolock(LDAP * ld,int msgid,int all,int unlock_permitted,struct timeval * timeout,LDAPMessage ** result)110 nsldapi_result_nolock( LDAP *ld, int msgid, int all, int unlock_permitted,
111     struct timeval *timeout, LDAPMessage **result )
112 {
113 	int		rc;
114 
115 	LDAPDebug( LDAP_DEBUG_TRACE,
116 		"nsldapi_result_nolock (msgid=%d, all=%d)\n", msgid, all, 0 );
117 
118 	/*
119 	 * First, look through the list of responses we have received on
120 	 * this association and see if the response we're interested in
121 	 * is there.  If it is, return it.  If not, call wait4msg() to
122 	 * wait until it arrives or timeout occurs.
123 	 */
124 
125 	if ( result == NULL ) {
126 		LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
127 		return( -1 );
128 	}
129 
130 	if ( check_response_queue( ld, msgid, all, 1, result ) != 0 ) {
131 		LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL );
132 		rc = (*result)->lm_msgtype;
133 	} else {
134 		rc = wait4msg( ld, msgid, all, unlock_permitted, timeout,
135 		    result );
136 	}
137 
138 	/*
139 	 * XXXmcs should use cache function pointers to hook in memcache
140 	 */
141 	if ( ld->ld_memcache != NULL && NSLDAPI_SEARCH_RELATED_RESULT( rc ) &&
142 	     !((*result)->lm_fromcache )) {
143 		ldap_memcache_append( ld, (*result)->lm_msgid,
144 		    (all || NSLDAPI_IS_SEARCH_RESULT( rc )), *result );
145 	}
146 
147 	return( rc );
148 }
149 
150 
151 /*
152  * Look through the list of queued responses for a message that matches the
153  * criteria in the msgid and all parameters.  msgid == LDAP_RES_ANY matches
154  * all ids.
155  *
156  * If an appropriate message is found, a non-zero value is returned and the
157  * message is dequeued and assigned to *result.
158  *
159  * If not, *result is set to NULL and this function returns 0.
160  */
161 static int
check_response_queue(LDAP * ld,int msgid,int all,int do_abandon_check,LDAPMessage ** result)162 check_response_queue( LDAP *ld, int msgid, int all, int do_abandon_check,
163     LDAPMessage **result )
164 {
165 	LDAPMessage	*lm, *lastlm, *nextlm;
166 	LDAPRequest	*lr;
167 
168 	LDAPDebug( LDAP_DEBUG_TRACE,
169 	    "=> check_response_queue (msgid=%d, all=%d)\n", msgid, all, 0 );
170 
171 	*result = NULL;
172 	lastlm = NULL;
173 	LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK );
174 	for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
175 		nextlm = lm->lm_next;
176 
177 		if ( do_abandon_check && ldap_abandoned( ld, lm->lm_msgid ) ) {
178 			ldap_mark_abandoned( ld, lm->lm_msgid );
179 
180 			if ( lastlm == NULL ) {
181 				ld->ld_responses = lm->lm_next;
182 			} else {
183 				lastlm->lm_next = nextlm;
184 			}
185 
186 			ldap_msgfree( lm );
187 
188 			continue;
189 		}
190 
191 		if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
192 			LDAPMessage	*tmp;
193 
194 			if ( all == 0
195 			    || (lm->lm_msgtype != LDAP_RES_SEARCH_RESULT
196 			    && lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE
197 			    && lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY) )
198 				break;
199 
200 			for ( tmp = lm; tmp != NULL; tmp = tmp->lm_chain ) {
201 				if ( tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT )
202 					break;
203 			}
204 
205 			if ( tmp == NULL ) {
206 				LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
207 				LDAPDebug( LDAP_DEBUG_TRACE,
208 				    "<= check_response_queue NOT FOUND\n",
209 				    0, 0, 0 );
210 				return( 0 );	/* no message to return */
211 			}
212 
213 			break;
214 		}
215 		lastlm = lm;
216 	}
217 
218 	/*
219 	 * if we did not find a message OR if the one we found is a result for
220 	 * a request that is still pending, return failure.
221 	 */
222 	if ( lm == NULL
223              || (( lr = nsldapi_find_request_by_msgid( ld, lm->lm_msgid ))
224 		   != NULL && lr->lr_outrefcnt > 0 )) {
225 		LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
226 		LDAPDebug( LDAP_DEBUG_TRACE,
227 		    "<= check_response_queue NOT FOUND\n",
228 		    0, 0, 0 );
229 		return( 0 );	/* no message to return */
230 	}
231 
232 	if ( all == 0 ) {
233 		if ( lm->lm_chain == NULL ) {
234 			if ( lastlm == NULL ) {
235 				ld->ld_responses = lm->lm_next;
236 			} else {
237 				lastlm->lm_next = lm->lm_next;
238 			}
239 		} else {
240 			if ( lastlm == NULL ) {
241 				ld->ld_responses = lm->lm_chain;
242 				ld->ld_responses->lm_next = lm->lm_next;
243 			} else {
244 				lastlm->lm_next = lm->lm_chain;
245 				lastlm->lm_next->lm_next = lm->lm_next;
246 			}
247 		}
248 	} else {
249 		if ( lastlm == NULL ) {
250 			ld->ld_responses = lm->lm_next;
251 		} else {
252 			lastlm->lm_next = lm->lm_next;
253 		}
254 	}
255 
256 	if ( all == 0 ) {
257 		lm->lm_chain = NULL;
258 	}
259 	lm->lm_next = NULL;
260 	LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
261 
262 	*result = lm;
263 	LDAPDebug( LDAP_DEBUG_TRACE,
264 	    "<= check_response_queue returning msgid %d type %d\n",
265 	    lm->lm_msgid, lm->lm_msgtype, 0 );
266 	return( 1 );	/* a message was found and returned in *result */
267 }
268 
269 
270 static int
wait4msg(LDAP * ld,int msgid,int all,int unlock_permitted,struct timeval * timeout,LDAPMessage ** result)271 wait4msg( LDAP *ld, int msgid, int all, int unlock_permitted,
272 	struct timeval *timeout, LDAPMessage **result )
273 {
274 	int		rc;
275 	struct timeval	tv, *tvp;
276 #ifdef _SOLARIS_SDK
277 	hrtime_t	start_time = 0, tmp_time, tv_time;
278 #else
279 	long		start_time = 0, tmp_time;
280 #endif
281 	LDAPConn	*lc, *nextlc;
282 	LDAPRequest	*lr;
283 
284 #ifdef LDAP_DEBUG
285 	if ( timeout == NULL ) {
286 		LDAPDebug( LDAP_DEBUG_TRACE, "wait4msg (infinite timeout)\n",
287 		    0, 0, 0 );
288 	} else {
289 		LDAPDebug( LDAP_DEBUG_TRACE, "wait4msg (timeout %ld sec, %ld usec)\n",
290 		    timeout->tv_sec, timeout->tv_usec, 0 );
291 	}
292 #endif /* LDAP_DEBUG */
293 
294 	/* check the cache */
295 	if ( ld->ld_cache_on && ld->ld_cache_result != NULL ) {
296 		/* if ( unlock_permitted ) LDAP_MUTEX_UNLOCK( ld ); */
297 		LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK );
298 		rc = (ld->ld_cache_result)( ld, msgid, all, timeout, result );
299 		LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK );
300 		/* if ( unlock_permitted ) LDAP_MUTEX_LOCK( ld ); */
301 		if ( rc != 0 ) {
302 			return( rc );
303 		}
304 		if ( ld->ld_cache_strategy == LDAP_CACHE_LOCALDB ) {
305 			LDAP_SET_LDERRNO( ld, LDAP_TIMEOUT, NULL, NULL );
306 			return( 0 );	/* timeout */
307 		}
308 	}
309 
310 	/*
311 	 * if we are looking for a specific msgid, check to see if it is
312 	 * associated with a dead connection and return an error if so.
313 	 */
314 	if ( msgid != LDAP_RES_ANY && msgid != LDAP_RES_UNSOLICITED ) {
315 		LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK );
316 		if (( lr = nsldapi_find_request_by_msgid( ld, msgid ))
317 		    == NULL ) {
318 			LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
319 			LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL,
320 				nsldapi_strdup( dgettext(TEXT_DOMAIN,
321 					"unknown message id") ));
322 			return( -1 );	/* could not find request for msgid */
323 		}
324 		if ( lr->lr_conn != NULL &&
325 		    lr->lr_conn->lconn_status == LDAP_CONNST_DEAD ) {
326 			nsldapi_free_request( ld, lr, 1 );
327 			LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
328 			LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL );
329 			return( -1 );	/* connection dead */
330 		}
331 		LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
332 	}
333 
334 	if ( timeout == NULL ) {
335 		tvp = NULL;
336 	} else {
337 		tv = *timeout;
338 		tvp = &tv;
339 #ifdef _SOLARIS_SDK
340 		start_time = gethrtime();
341 		tv_time = ((hrtime_t)tv.tv_sec * NANOSEC +
342 			(hrtime_t)tv.tv_usec * (NANOSEC / MICROSEC));
343 #else
344 		start_time = (long)time( NULL );
345 #endif
346 	}
347 
348 	rc = -2;
349 	while ( rc == -2 ) {
350 #ifdef LDAP_DEBUG
351 		if ( ldap_debug & LDAP_DEBUG_TRACE ) {
352 			nsldapi_dump_connection( ld, ld->ld_conns, 1 );
353 			nsldapi_dump_requests_and_responses( ld );
354 		}
355 #endif /* LDAP_DEBUG */
356 		LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK );
357 		LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK );
358 		for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
359 			if ( lc->lconn_sb->sb_ber.ber_ptr <
360 			    lc->lconn_sb->sb_ber.ber_end ) {
361 				rc = read1msg( ld, msgid, all, lc->lconn_sb,
362 				    lc, result );
363 				break;
364 			}
365 		}
366 		LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
367 		LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK );
368 
369 		if ( lc == NULL ) {
370 			rc = nsldapi_iostatus_poll( ld, tvp );
371 
372 #if defined( LDAP_DEBUG ) && !defined( macintosh ) && !defined( DOS )
373 			if ( rc == -1 ) {
374 			    LDAPDebug( LDAP_DEBUG_TRACE,
375 				    "nsldapi_iostatus_poll returned -1: errno %d\n",
376 				    LDAP_GET_ERRNO( ld ), 0, 0 );
377 			}
378 #endif
379 
380 #if !defined( macintosh ) && !defined( DOS )
381 			if ( rc == 0 || ( rc == -1 && (( ld->ld_options &
382 			    LDAP_BITOPT_RESTART ) == 0 ||
383 			    LDAP_GET_ERRNO( ld ) != EINTR ))) {
384 #else
385 			if ( rc == -1 || rc == 0 ) {
386 #endif
387 				LDAP_SET_LDERRNO( ld, (rc == -1 ?
388 				    LDAP_SERVER_DOWN : LDAP_TIMEOUT), NULL,
389 				    NULL );
390 				if ( rc == -1 ) {
391 					LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK );
392 					nsldapi_connection_lost_nolock( ld,
393 						NULL );
394 					LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
395 				}
396 				return( rc );
397 			}
398 
399 			if ( rc == -1 ) {
400 				rc = -2;	/* select interrupted: loop */
401 			} else {
402 				rc = -2;
403 				LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK );
404 				LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK );
405 				for ( lc = ld->ld_conns; rc == -2 && lc != NULL;
406 				    lc = nextlc ) {
407 					nextlc = lc->lconn_next;
408 					if ( lc->lconn_status ==
409 					    LDAP_CONNST_CONNECTED &&
410 					    nsldapi_iostatus_is_read_ready( ld,
411 					    lc->lconn_sb )) {
412 						rc = read1msg( ld, msgid, all,
413 						    lc->lconn_sb, lc, result );
414 					}
415 					else if (ld->ld_options & LDAP_BITOPT_ASYNC) {
416                         if ( lr
417                               && lc->lconn_status == LDAP_CONNST_CONNECTING
418                               && nsldapi_iostatus_is_write_ready( ld,
419 			      lc->lconn_sb ) ) {
420                             rc = nsldapi_ber_flush( ld, lc->lconn_sb, lr->lr_ber, 0, 1 );
421                             if ( rc == 0 ) {
422                                 rc = LDAP_RES_BIND;
423                                 lc->lconn_status = LDAP_CONNST_CONNECTED;
424 
425                                 lr->lr_ber->ber_end = lr->lr_ber->ber_ptr;
426                                 lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf;
427                                 nsldapi_iostatus_interest_read( ld, lc->lconn_sb );
428                             }
429                             else if ( rc == -1 ) {
430                                 LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL );
431                                 nsldapi_free_request( ld, lr, 0 );
432                                 nsldapi_free_connection( ld, lc, NULL, NULL,
433 				    0, 0 );
434                             }
435                         }
436 
437 					}
438 				}
439 				LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
440 				LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK );
441 			}
442 		}
443 
444 		/*
445 		 * It is possible that recursion occurred while chasing
446 		 * referrals and as a result the message we are looking
447 		 * for may have been placed on the response queue.  Look
448 		 * for it there before continuing so we don't end up
449 		 * waiting on the network for a message that we already
450 		 * received!
451 		 */
452 		if ( rc == -2 &&
453 		    check_response_queue( ld, msgid, all, 0, result ) != 0 ) {
454 			LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL );
455 			rc = (*result)->lm_msgtype;
456 		}
457 
458 		/*
459 		 * honor the timeout if specified
460 		 */
461 		if ( rc == -2 && tvp != NULL ) {
462 #ifdef _SOLARIS_SDK
463 			tmp_time = gethrtime();
464 			if ((tv_time -=  (tmp_time - start_time)) <= 0) {
465 #else
466 			tmp_time = (long)time( NULL );
467 			if (( tv.tv_sec -=  ( tmp_time - start_time )) <= 0 ) {
468 #endif
469 				rc = 0;	/* timed out */
470 				LDAP_SET_LDERRNO( ld, LDAP_TIMEOUT, NULL,
471 				    NULL );
472 				break;
473 			}
474 
475 #ifdef _SOLARIS_SDK
476 			tv.tv_sec = tv_time / NANOSEC;
477 			tv.tv_usec = (tv_time % NANOSEC) / (NANOSEC / MICROSEC);
478 #endif
479 			LDAPDebug( LDAP_DEBUG_TRACE, "wait4msg:  %ld secs to go\n",
480 				tv.tv_sec, 0, 0 );
481 			start_time = tmp_time;
482 		}
483 	}
484 
485 	return( rc );
486 }
487 
488 
489 /*
490  * read1msg() should be called with LDAP_CONN_LOCK and LDAP_REQ_LOCK locked.
491  */
492 static int
493 read1msg( LDAP *ld, int msgid, int all, Sockbuf *sb, LDAPConn *lc,
494     LDAPMessage **result )
495 {
496 	BerElement	*ber;
497 	LDAPMessage	*new, *l, *prev, *chainprev, *tmp;
498 	ber_int_t	id;
499 	ber_tag_t	tag;
500 	ber_len_t	len;
501 	int		terrno, lderr, foundit = 0;
502 	LDAPRequest	*lr;
503 	int		rc, has_parent, message_can_be_returned;
504 	int		manufactured_result = 0;
505 
506 	LDAPDebug( LDAP_DEBUG_TRACE, "read1msg\n", 0, 0, 0 );
507 
508 	message_can_be_returned = 1;	/* the usual case... */
509 
510 	/*
511 	 * if we are not already in the midst of reading a message, allocate
512 	 * a ber that is associated with this connection
513 	 */
514 	if ( lc->lconn_ber == NULLBER && nsldapi_alloc_ber_with_options( ld,
515 	    &lc->lconn_ber ) != LDAP_SUCCESS ) {
516 		return( -1 );
517 	}
518 
519 	/*
520 	 * ber_get_next() doesn't set errno on EOF, so we pre-set it to
521 	 * zero to avoid getting tricked by leftover "EAGAIN" errors
522 	 */
523 	LDAP_SET_ERRNO( ld, 0 );
524 
525 	/* get the next message */
526 	if ( (tag = ber_get_next( sb, &len, lc->lconn_ber ))
527 	    != LDAP_TAG_MESSAGE ) {
528 		terrno = LDAP_GET_ERRNO( ld );
529 		if ( terrno == EWOULDBLOCK || terrno == EAGAIN ) {
530 		    return( -2 );	/* try again */
531 		}
532 		LDAP_SET_LDERRNO( ld, (tag == LBER_DEFAULT ? LDAP_SERVER_DOWN :
533                     LDAP_LOCAL_ERROR), NULL, NULL );
534 		if ( tag == LBER_DEFAULT ) {
535 			nsldapi_connection_lost_nolock( ld, sb );
536 		}
537 		return( -1 );
538 	}
539 
540 	/*
541 	 * Since we have received a complete message now, we pull this ber
542 	 * out of the connection structure and never read into it again.
543 	 */
544 	ber = lc->lconn_ber;
545 	lc->lconn_ber = NULLBER;
546 
547 	/* message id */
548 	if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
549 		LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL );
550 		return( -1 );
551 	}
552 
553 	/* if it's been abandoned, toss it */
554 	if ( ldap_abandoned( ld, (int)id ) ) {
555 		ber_free( ber, 1 );
556 		return( -2 );	/* continue looking */
557 	}
558 
559 	if ( id == LDAP_RES_UNSOLICITED ) {
560 		lr = NULL;
561 	} else if (( lr = nsldapi_find_request_by_msgid( ld, id )) == NULL ) {
562 		LDAPDebug( LDAP_DEBUG_ANY,
563 		    "no request for response with msgid %ld (tossing)\n",
564 		    id, 0, 0 );
565 		ber_free( ber, 1 );
566 		return( -2 );	/* continue looking */
567 	}
568 
569 	/* the message type */
570 	if ( (tag = ber_peek_tag( ber, &len )) == LBER_ERROR ) {
571 		LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL );
572 		return( -1 );
573 	}
574 	LDAPDebug( LDAP_DEBUG_TRACE, "got %s msgid %ld, original id %d\n",
575 	    ( tag == LDAP_RES_SEARCH_ENTRY ) ? "ENTRY" :
576 	    ( tag == LDAP_RES_SEARCH_REFERENCE ) ? "REFERENCE" : "RESULT", id,
577 	    ( lr == NULL ) ? id : lr->lr_origid );
578 
579 	if ( lr != NULL ) {
580 		id = lr->lr_origid;
581 		lr->lr_res_msgtype = tag;
582 	}
583 	rc = -2;	/* default is to keep looking (no response found) */
584 
585 	if ( id != LDAP_RES_UNSOLICITED && ( tag == LDAP_RES_SEARCH_REFERENCE ||
586 	    tag != LDAP_RES_SEARCH_ENTRY )) {
587 		int		refchasing, reftotal, simple_request = 0;
588 
589 		check_for_refs( ld, lr, ber, lc->lconn_version, &reftotal,
590 		    &refchasing );
591 
592 		if ( refchasing > 0 || lr->lr_outrefcnt > 0 ) {
593 			/*
594 			 * we're chasing one or more new refs...
595 			 */
596 			ber_free( ber, 1 );
597 			ber = NULLBER;
598 			lr->lr_status = LDAP_REQST_CHASINGREFS;
599 			message_can_be_returned = 0;
600 
601 		} else if ( tag != LDAP_RES_SEARCH_REFERENCE ) {
602 			/*
603 			 * this request is complete...
604 			 */
605 			has_parent = ( lr->lr_parent != NULL );
606 
607 			if ( lr->lr_outrefcnt <= 0 && !has_parent ) {
608 				/* request without any refs */
609 				simple_request = ( reftotal == 0 );
610 			}
611 
612 			/*
613 			 * If this is not a child request and it is a bind
614 			 * request, reset the connection's bind DN and
615 			 * status based on the result of the operation.
616 			 */
617 			if ( !has_parent &&
618 			    LDAP_RES_BIND == lr->lr_res_msgtype &&
619 			    lr->lr_conn != NULL ) {
620 				if ( lr->lr_conn->lconn_binddn != NULL ) {
621 					NSLDAPI_FREE(
622 					    lr->lr_conn->lconn_binddn );
623 				}
624 				if ( LDAP_SUCCESS == nsldapi_parse_result( ld,
625 				    lr->lr_res_msgtype, ber, &lderr, NULL,
626 				    NULL, NULL, NULL )
627 				    && LDAP_SUCCESS == lderr ) {
628 					lr->lr_conn->lconn_bound = 1;
629 					lr->lr_conn->lconn_binddn =
630 					    lr->lr_binddn;
631 					lr->lr_binddn = NULL;
632 				} else {
633 					lr->lr_conn->lconn_bound = 0;
634 					lr->lr_conn->lconn_binddn = NULL;
635 				}
636 			}
637 
638 			/*
639 			 * if this response is to a child request, we toss
640 			 * the message contents and just merge error info.
641 			 * into the parent.
642 			 */
643 			if ( has_parent ) {
644 				ber_free( ber, 1 );
645 				ber = NULLBER;
646 			}
647 			while ( lr->lr_parent != NULL ) {
648 				merge_error_info( ld, lr->lr_parent, lr );
649 
650 				lr = lr->lr_parent;
651 				if ( --lr->lr_outrefcnt > 0 ) {
652 					break;	/* not completely done yet */
653 				}
654 			}
655 
656 			/*
657 			 * we recognize a request as complete when:
658 			 *  1) it has no outstanding referrals
659 			 *  2) it is not a child request
660 			 *  3) we have received a result for the request (i.e.,
661 			 *     something other than an entry or a reference).
662 			 */
663 			if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL &&
664 			    lr->lr_res_msgtype != LDAP_RES_SEARCH_ENTRY &&
665 			    lr->lr_res_msgtype != LDAP_RES_SEARCH_REFERENCE ) {
666 				id = lr->lr_msgid;
667 				tag = lr->lr_res_msgtype;
668 				LDAPDebug( LDAP_DEBUG_TRACE,
669 				    "request %ld done\n", id, 0, 0 );
670 LDAPDebug( LDAP_DEBUG_TRACE,
671 "res_errno: %d, res_error: <%s>, res_matched: <%s>\n",
672 lr->lr_res_errno, lr->lr_res_error ? lr->lr_res_error : "",
673 lr->lr_res_matched ? lr->lr_res_matched : "" );
674 				if ( !simple_request ) {
675 					if ( ber != NULLBER ) {
676 						ber_free( ber, 1 );
677 						ber = NULLBER;
678 					}
679 					if ( build_result_ber( ld, &ber, lr )
680 					    != LDAP_SUCCESS ) {
681 						rc = -1; /* fatal error */
682 					} else {
683 						manufactured_result = 1;
684 					}
685 				}
686 
687 				nsldapi_free_request( ld, lr, 1 );
688 			} else {
689 				message_can_be_returned = 0;
690 			}
691 		}
692 	}
693 
694 	if ( ber == NULLBER ) {
695 		return( rc );
696 	}
697 
698 	/* make a new ldap message */
699 	if ( (new = (LDAPMessage*)NSLDAPI_CALLOC( 1, sizeof(struct ldapmsg) ))
700 	    == NULL ) {
701 		LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
702 		return( -1 );
703 	}
704 	new->lm_msgid = (int)id;
705 	new->lm_msgtype = tag;
706 	new->lm_ber = ber;
707 
708 	/*
709 	 * if this is a search entry or if this request is complete (i.e.,
710 	 * there are no outstanding referrals) then add to cache and check
711 	 * to see if we should return this to the caller right away or not.
712 	 */
713 	if ( message_can_be_returned ) {
714 		if ( ld->ld_cache_on ) {
715 			nsldapi_add_result_to_cache( ld, new );
716 		}
717 
718 		if ( msgid == LDAP_RES_ANY || id == msgid ) {
719 			if ( new->lm_msgtype == LDAP_RES_SEARCH_RESULT ) {
720 				/*
721 				 * return the first response we have for this
722 				 * search request later (possibly an entire
723 				 * chain of messages).
724 				 */
725 				foundit = 1;
726 			} else if ( all == 0
727 			    || (new->lm_msgtype != LDAP_RES_SEARCH_REFERENCE
728 			    && new->lm_msgtype != LDAP_RES_SEARCH_ENTRY) ) {
729 				*result = new;
730 				LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL,
731 				    NULL );
732 				return( tag );
733 			}
734 		}
735 	}
736 
737 	/*
738 	 * if not, we must add it to the list of responses.  if
739 	 * the msgid is already there, it must be part of an existing
740 	 * search response.
741 	 */
742 
743 	prev = NULL;
744 	LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK );
745 	for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
746 		if ( l->lm_msgid == new->lm_msgid )
747 			break;
748 		prev = l;
749 	}
750 
751 	/* not part of an existing search response */
752 	if ( l == NULL ) {
753 		if ( foundit ) {
754 			LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
755 			*result = new;
756 			LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL );
757 			return( tag );
758 		}
759 
760 		new->lm_next = ld->ld_responses;
761 		ld->ld_responses = new;
762 		LDAPDebug( LDAP_DEBUG_TRACE,
763 		    "adding new response id %d type %d (looking for id %d)\n",
764 		    new->lm_msgid, new->lm_msgtype, msgid );
765 		LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
766 		if( message_can_be_returned )
767 			POST( ld, new->lm_msgid, new );
768 		return( -2 );	/* continue looking */
769 	}
770 
771 	LDAPDebug( LDAP_DEBUG_TRACE,
772 	    "adding response id %d type %d (looking for id %d)\n",
773 	    new->lm_msgid, new->lm_msgtype, msgid );
774 
775 	/*
776 	 * part of a search response - add to end of list of entries
777 	 *
778 	 * the first step is to find the end of the list of entries and
779 	 * references.  after the following loop is executed, tmp points to
780 	 * the last entry or reference in the chain.  If there are none,
781 	 * tmp points to the search result.
782 	 */
783 	chainprev = NULL;
784 	for ( tmp = l; tmp->lm_chain != NULL &&
785 	    ( tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY
786 	    || tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_REFERENCE );
787 	    tmp = tmp->lm_chain ) {
788 		chainprev = tmp;
789 	}
790 
791 	/*
792 	 * If this is a manufactured result message and a result is already
793 	 * queued we throw away the one that is queued and replace it with
794 	 * our new result.  This is necessary so we don't end up returning
795 	 * more than one result.
796 	 */
797 	if ( manufactured_result &&
798 	    tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT ) {
799 		/*
800 		 * the result is the only thing in the chain... replace it.
801 		 */
802 		new->lm_chain = tmp->lm_chain;
803 		new->lm_next = tmp->lm_next;
804 		if ( chainprev == NULL ) {
805 			if ( prev == NULL ) {
806 				ld->ld_responses = new;
807 			} else {
808 				prev->lm_next = new;
809 			}
810 		} else {
811 		    chainprev->lm_chain = new;
812 		}
813 		if ( l == tmp ) {
814 			l = new;
815 		}
816 		ldap_msgfree( tmp );
817 
818 	} else if ( manufactured_result && tmp->lm_chain != NULL
819 	    && tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_RESULT ) {
820 		/*
821 		 * entries or references are also present, so the result
822 		 * is the next entry after tmp.  replace it.
823 		 */
824 		new->lm_chain = tmp->lm_chain->lm_chain;
825 		new->lm_next = tmp->lm_chain->lm_next;
826 		ldap_msgfree( tmp->lm_chain );
827 		tmp->lm_chain = new;
828 
829 	} else if ( tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT ) {
830 		/*
831 		 * the result is the only thing in the chain... add before it.
832 		 */
833 		new->lm_chain = tmp;
834 		if ( chainprev == NULL ) {
835 			if ( prev == NULL ) {
836 				ld->ld_responses = new;
837 			} else {
838 				prev->lm_next = new;
839 			}
840 		} else {
841 		    chainprev->lm_chain = new;
842 		}
843 		if ( l == tmp ) {
844 			l = new;
845 		}
846 
847 	} else {
848 		/*
849 		 * entries and/or references are present... add to the end
850 		 * of the entry/reference part of the chain.
851 		 */
852 		new->lm_chain = tmp->lm_chain;
853 		tmp->lm_chain = new;
854 	}
855 
856 	/*
857 	 * return the first response or the whole chain if that's what
858 	 * we were looking for....
859 	 */
860 	if ( foundit ) {
861 		if ( all == 0 && l->lm_chain != NULL ) {
862 			/*
863 			 * only return the first response in the chain
864 			 */
865 			if ( prev == NULL ) {
866 				ld->ld_responses = l->lm_chain;
867 			} else {
868 				prev->lm_next = l->lm_chain;
869 			}
870 			l->lm_chain = NULL;
871 			tag = l->lm_msgtype;
872 		} else {
873 			/*
874 			 * return all of the responses (may be a chain)
875 			 */
876 			if ( prev == NULL ) {
877 				ld->ld_responses = l->lm_next;
878 			} else {
879 				prev->lm_next = l->lm_next;
880 			}
881 		}
882 		*result = l;
883 		LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
884 		LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL );
885 		return( tag );
886 	}
887 	LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
888 	return( -2 );	/* continue looking */
889 }
890 
891 
892 /*
893  * check for LDAPv2+ (UMich extension) or LDAPv3 referrals or references
894  * errors are merged in "lr".
895  */
896 static void
897 check_for_refs( LDAP *ld, LDAPRequest *lr, BerElement *ber,
898     int ldapversion, int *totalcountp, int *chasingcountp )
899 {
900 	int		err, origerr;
901 	char		*errstr, *matcheddn, **v3refs;
902 
903 	LDAPDebug( LDAP_DEBUG_TRACE, "check_for_refs\n", 0, 0, 0 );
904 
905 	*chasingcountp = *totalcountp = 0;
906 
907 	if ( ldapversion < LDAP_VERSION2 || ( lr->lr_parent == NULL
908 	    && ( ld->ld_options & LDAP_BITOPT_REFERRALS ) == 0 )) {
909 		/* referrals are not supported or are disabled */
910 		return;
911 	}
912 
913 	if ( lr->lr_res_msgtype == LDAP_RES_SEARCH_REFERENCE ) {
914 		err = nsldapi_parse_reference( ld, ber, &v3refs, NULL );
915 		origerr = LDAP_REFERRAL;	/* a small lie... */
916 		matcheddn = errstr = NULL;
917 	} else {
918 		err = nsldapi_parse_result( ld, lr->lr_res_msgtype, ber,
919 		    &origerr, &matcheddn, &errstr, &v3refs, NULL );
920 	}
921 
922 	if ( err != LDAP_SUCCESS ) {
923 		/* parse failed */
924 		return;
925 	}
926 
927 	if ( origerr == LDAP_REFERRAL ) {	/* ldapv3 */
928 		if ( v3refs != NULL ) {
929 			err = nsldapi_chase_v3_refs( ld, lr, v3refs,
930 			    ( lr->lr_res_msgtype == LDAP_RES_SEARCH_REFERENCE ),
931 			    totalcountp, chasingcountp );
932 			ldap_value_free( v3refs );
933 		}
934 	} else if ( ldapversion == LDAP_VERSION2
935 	    && origerr != LDAP_SUCCESS ) {
936 		/* referrals may be present in the error string */
937 		err = nsldapi_chase_v2_referrals( ld, lr, &errstr,
938 		    totalcountp, chasingcountp );
939 	}
940 
941 	/* set LDAP errno, message, and matched string appropriately */
942 	if ( lr->lr_res_error != NULL ) {
943 		NSLDAPI_FREE( lr->lr_res_error );
944 	}
945 	lr->lr_res_error = errstr;
946 
947 	if ( lr->lr_res_matched != NULL ) {
948 		NSLDAPI_FREE( lr->lr_res_matched );
949 	}
950 	lr->lr_res_matched = matcheddn;
951 
952 	if ( err == LDAP_SUCCESS && ( *chasingcountp == *totalcountp )) {
953 		if ( *totalcountp > 0 && ( origerr == LDAP_PARTIAL_RESULTS
954 		    || origerr == LDAP_REFERRAL )) {
955 			/* substitute success for referral error codes */
956 			lr->lr_res_errno = LDAP_SUCCESS;
957 		} else {
958 			/* preserve existing non-referral error code */
959 			lr->lr_res_errno = origerr;
960 		}
961 	} else if ( err != LDAP_SUCCESS ) {
962 		/* error occurred while trying to chase referrals */
963 		lr->lr_res_errno = err;
964 	} else {
965 		/* some referrals were not recognized */
966 		lr->lr_res_errno = ( ldapversion == LDAP_VERSION2 )
967 		    ? LDAP_PARTIAL_RESULTS : LDAP_REFERRAL;
968 	}
969 
970 	LDAPDebug( LDAP_DEBUG_TRACE,
971 	    "check_for_refs: new result: msgid %d, res_errno %d, ",
972 	    lr->lr_msgid, lr->lr_res_errno, 0 );
973 	LDAPDebug( LDAP_DEBUG_TRACE, " res_error <%s>, res_matched <%s>\n",
974 	    lr->lr_res_error ? lr->lr_res_error : "",
975 	    lr->lr_res_matched ? lr->lr_res_matched : "", 0 );
976 	LDAPDebug( LDAP_DEBUG_TRACE,
977 	    "check_for_refs: %d new refs(s); chasing %d of them\n",
978 	    *totalcountp, *chasingcountp, 0 );
979 }
980 
981 
982 /* returns an LDAP error code and also sets it in LDAP * */
983 static int
984 build_result_ber( LDAP *ld, BerElement **berp, LDAPRequest *lr )
985 {
986 	ber_len_t	len;
987 	ber_int_t	along;
988 	BerElement	*ber;
989 	int		err;
990 
991 	if (( err = nsldapi_alloc_ber_with_options( ld, &ber ))
992 	    != LDAP_SUCCESS ) {
993 		return( err );
994 	}
995 	*berp = ber;
996 	if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
997 	    (long)lr->lr_res_msgtype, lr->lr_res_errno,
998 	    lr->lr_res_matched ? lr->lr_res_matched : "",
999 	    lr->lr_res_error ? lr->lr_res_error : "" ) == -1 ) {
1000 		return( LDAP_ENCODING_ERROR );
1001 	}
1002 
1003 	ber_reset( ber, 1 );
1004 	if ( ber_skip_tag( ber, &len ) == LBER_ERROR ||
1005 	    ber_get_int( ber, &along ) == LBER_ERROR ||
1006 	    ber_peek_tag( ber, &len ) == LBER_ERROR ) {
1007 		return( LDAP_DECODING_ERROR );
1008 	}
1009 
1010 	return( LDAP_SUCCESS );
1011 }
1012 
1013 
1014 static void
1015 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1016 {
1017 /*
1018  * Merge error information in "lr" with "parentr" error code and string.
1019  */
1020 	if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1021 		parentr->lr_res_errno = lr->lr_res_errno;
1022 		if ( lr->lr_res_error != NULL ) {
1023 			(void)nsldapi_append_referral( ld, &parentr->lr_res_error,
1024 			    lr->lr_res_error );
1025 		}
1026 	} else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1027 	    parentr->lr_res_errno == LDAP_SUCCESS ) {
1028 		parentr->lr_res_errno = lr->lr_res_errno;
1029 		if ( parentr->lr_res_error != NULL ) {
1030 			NSLDAPI_FREE( parentr->lr_res_error );
1031 		}
1032 		parentr->lr_res_error = lr->lr_res_error;
1033 		lr->lr_res_error = NULL;
1034 		if ( NAME_ERROR( lr->lr_res_errno )) {
1035 			if ( parentr->lr_res_matched != NULL ) {
1036 				NSLDAPI_FREE( parentr->lr_res_matched );
1037 			}
1038 			parentr->lr_res_matched = lr->lr_res_matched;
1039 			lr->lr_res_matched = NULL;
1040 		}
1041 	}
1042 
1043 	LDAPDebug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info:  ",
1044 	    parentr->lr_msgid, 0, 0 );
1045 	LDAPDebug( LDAP_DEBUG_TRACE, "result lderrno %d, error <%s>, matched <%s>\n",
1046 	    parentr->lr_res_errno, parentr->lr_res_error ?
1047 	    parentr->lr_res_error : "", parentr->lr_res_matched ?
1048 	    parentr->lr_res_matched : "" );
1049 }
1050 
1051 #if defined( CLDAP )
1052 #if !defined( macintosh ) && !defined( DOS ) && !defined( _WINDOWS ) && !defined(XP_OS2)
1053 /* XXXmcs: was revised to support extended I/O callbacks but never compiled! */
1054 static int
1055 cldap_select1( LDAP *ld, struct timeval *timeout )
1056 {
1057 	int		rc;
1058 	static int	tblsize = 0;
1059 	NSLDAPIIOStatus	*iosp = ld->ld_iostatus;
1060 
1061 	if ( tblsize == 0 ) {
1062 #ifdef USE_SYSCONF
1063 		tblsize = sysconf( _SC_OPEN_MAX );
1064 #else /* USE_SYSCONF */
1065 		tblsize = getdtablesize();
1066 #endif /* USE_SYSCONF */
1067 	}
1068 
1069 	if ( tblsize >= FD_SETSIZE ) {
1070 		/*
1071 		 * clamp value so we don't overrun the fd_set structure
1072 		 */
1073 		tblsize = FD_SETSIZE - 1;
1074 	}
1075 
1076 	if ( NSLDAPI_IOSTATUS_TYPE_OSNATIVE == iosp->ios_type ) {
1077 		fd_set		readfds;
1078 
1079 		FD_ZERO( &readfds );
1080 		FD_SET( ld->ld_sbp->sb_sd, &readfds );
1081 
1082 		/* XXXmcs: UNIX platforms should use poll() */
1083 		rc = select( tblsize, &readfds, 0, 0, timeout ) );
1084 
1085 	} else if ( NSLDAPI_IOSTATUS_TYPE_CALLBACK == iosp->ios_type ) {
1086 		LDAP_X_PollFD	pollfds[ 1 ];
1087 
1088 		pollfds[0].lpoll_fd = ld->ld_sbp->sb_sd;
1089 		pollfds[0].lpoll_arg = ld->ld_sbp->sb_arg;
1090 		pollfds[0].lpoll_events = LDAP_X_POLLIN;
1091 		pollfds[0].lpoll_revents = 0;
1092 		rc = ld->ld_extpoll_fn( pollfds, 1, nsldapi_tv2ms( timeout ),
1093 		    ld->ld_ext_session_arg );
1094 	} else {
1095 		LDAPDebug( LDAP_DEBUG_ANY,
1096 		    "nsldapi_iostatus_poll: unknown I/O type %d\n",
1097 		rc = 0; /* simulate a timeout (what else to do?) */
1098 	}
1099 
1100 	return( rc );
1101 }
1102 #endif /* !macintosh */
1103 
1104 
1105 #ifdef macintosh
1106 static int
1107 cldap_select1( LDAP *ld, struct timeval *timeout )
1108 {
1109 	/* XXXmcs: needs to be revised to support I/O callbacks */
1110 	return( tcpselect( ld->ld_sbp->sb_sd, timeout ));
1111 }
1112 #endif /* macintosh */
1113 
1114 
1115 #if (defined( DOS ) && defined( WINSOCK )) || defined( _WINDOWS ) || defined(XP_OS2)
1116 /* XXXmcs: needs to be revised to support extended I/O callbacks */
1117 static int
1118 cldap_select1( LDAP *ld, struct timeval *timeout )
1119 {
1120     fd_set          readfds;
1121     int             rc;
1122 
1123     FD_ZERO( &readfds );
1124     FD_SET( ld->ld_sbp->sb_sd, &readfds );
1125 
1126     if ( NSLDAPI_IO_TYPE_STANDARD == ld->ldiou_type &&
1127 	NULL != ld->ld_select_fn ) {
1128 	    rc = ld->ld_select_fn( 1, &readfds, 0, 0, timeout );
1129     } else if ( NSLDAPI_IO_TYPE_EXTENDED == ld->ldiou_type &&
1130 	NULL != ld->ld_extselect_fn ) {
1131 	    rc = ld->ld_extselect_fn( ld->ld_ext_session_arg, 1, &readfds, 0,
1132 		0, timeout ) );
1133     } else {
1134 	    /* XXXmcs: UNIX platforms should use poll() */
1135 	    rc = select( 1, &readfds, 0, 0, timeout ) );
1136     }
1137 
1138     return( rc == SOCKET_ERROR ? -1 : rc );
1139 }
1140 #endif /* WINSOCK || _WINDOWS */
1141 #endif /* CLDAP */
1142 
1143 int
1144 LDAP_CALL
1145 ldap_msgfree( LDAPMessage *lm )
1146 {
1147 	LDAPMessage	*next;
1148 	int		type = 0;
1149 
1150 	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1151 
1152 	for ( ; lm != NULL; lm = next ) {
1153 		next = lm->lm_chain;
1154 		type = lm->lm_msgtype;
1155 		ber_free( lm->lm_ber, 1 );
1156 		NSLDAPI_FREE( (char *) lm );
1157 	}
1158 
1159 	return( type );
1160 }
1161 
1162 /*
1163  * ldap_msgdelete - delete a message.  It returns:
1164  *	0	if the entire message was deleted
1165  *	-1	if the message was not found, or only part of it was found
1166  */
1167 int
1168 ldap_msgdelete( LDAP *ld, int msgid )
1169 {
1170 	LDAPMessage	*lm, *prev;
1171 	int		msgtype;
1172 
1173 	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_msgdelete\n", 0, 0, 0 );
1174 
1175 	if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
1176 		return( -1 );	/* punt */
1177 	}
1178 
1179 	prev = NULL;
1180         LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK );
1181 	for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1182 		if ( lm->lm_msgid == msgid )
1183 			break;
1184 		prev = lm;
1185 	}
1186 
1187 	if ( lm == NULL )
1188 	{
1189         	LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
1190 		return( -1 );
1191 	}
1192 
1193 	if ( prev == NULL )
1194 		ld->ld_responses = lm->lm_next;
1195 	else
1196 		prev->lm_next = lm->lm_next;
1197         LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
1198 
1199 	msgtype = ldap_msgfree( lm );
1200 	if ( msgtype == LDAP_RES_SEARCH_ENTRY
1201 	    || msgtype == LDAP_RES_SEARCH_REFERENCE ) {
1202 		return( -1 );
1203 	}
1204 
1205 	return( 0 );
1206 }
1207 
1208 
1209 /*
1210  * return 1 if message msgid is waiting to be abandoned, 0 otherwise
1211  */
1212 static int
1213 ldap_abandoned( LDAP *ld, int msgid )
1214 {
1215 	int	i;
1216 
1217 	LDAP_MUTEX_LOCK( ld, LDAP_ABANDON_LOCK );
1218 	if ( ld->ld_abandoned == NULL )
1219 	{
1220 		LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
1221 		return( 0 );
1222 	}
1223 
1224 	for ( i = 0; ld->ld_abandoned[i] != -1; i++ )
1225 		if ( ld->ld_abandoned[i] == msgid )
1226 		{
1227 			LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
1228 			return( 1 );
1229 		}
1230 
1231 	LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
1232 	return( 0 );
1233 }
1234 
1235 
1236 static int
1237 ldap_mark_abandoned( LDAP *ld, int msgid )
1238 {
1239 	int	i;
1240 
1241 	LDAP_MUTEX_LOCK( ld, LDAP_ABANDON_LOCK );
1242 	if ( ld->ld_abandoned == NULL )
1243 	{
1244 		LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
1245 		return( -1 );
1246 	}
1247 
1248 	for ( i = 0; ld->ld_abandoned[i] != -1; i++ )
1249 		if ( ld->ld_abandoned[i] == msgid )
1250 			break;
1251 
1252 	if ( ld->ld_abandoned[i] == -1 )
1253 	{
1254 		LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
1255 		return( -1 );
1256 	}
1257 
1258 	for ( ; ld->ld_abandoned[i] != -1; i++ ) {
1259 		ld->ld_abandoned[i] = ld->ld_abandoned[i + 1];
1260 	}
1261 
1262 	LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
1263 	return( 0 );
1264 }
1265 
1266 
1267 #ifdef CLDAP
1268 int
1269 cldap_getmsg( LDAP *ld, struct timeval *timeout, BerElement **ber )
1270 {
1271 	int		rc;
1272 	ber_tag_t	tag;
1273 	ber_len_t	len;
1274 
1275 	if ( ld->ld_sbp->sb_ber.ber_ptr >= ld->ld_sbp->sb_ber.ber_end ) {
1276 		rc = cldap_select1( ld, timeout );
1277 		if ( rc == -1 || rc == 0 ) {
1278 			LDAP_SET_LDERRNO( ld, (rc == -1 ? LDAP_SERVER_DOWN :
1279 			    LDAP_TIMEOUT), NULL, NULL );
1280 			return( rc );
1281 		}
1282 	}
1283 
1284 	/* get the next message */
1285 	if ( (tag = ber_get_next( ld->ld_sbp, &len, ber ))
1286 	    != LDAP_TAG_MESSAGE ) {
1287 		LDAP_SET_LDERRNO( ld, (tag == LBER_DEFAULT ? LDAP_SERVER_DOWN :
1288 		    LDAP_LOCAL_ERROR), NULL, NULL );
1289 		return( -1 );
1290 	}
1291 
1292 	return( tag );
1293 }
1294 #endif /* CLDAP */
1295 
1296 int
1297 nsldapi_post_result( LDAP *ld, int msgid, LDAPMessage *result )
1298 {
1299 	LDAPPend	*lp;
1300 
1301 	LDAPDebug( LDAP_DEBUG_TRACE,
1302 	    "nsldapi_post_result(ld=0x%x, msgid=%d, result=0x%x)\n",
1303 	    ld, msgid, result );
1304 	LDAP_MUTEX_LOCK( ld, LDAP_PEND_LOCK );
1305 	if( msgid == LDAP_RES_ANY ) {
1306 		/*
1307 		 * Look for any pending request for which someone is waiting.
1308 		 */
1309 		for( lp = ld->ld_pend; lp != NULL; lp = lp->lp_next )
1310 		{
1311 			if ( lp->lp_sema != NULL ) {
1312 				break;
1313 			}
1314 		}
1315 		/*
1316 		 * If we did't find a pending request, lp is NULL at this
1317 		 * point, and we will leave this function without doing
1318 		 * anything more -- which is exactly what we want to do.
1319 		 */
1320 	}
1321 	else
1322 	{
1323 		/*
1324 		 * Look for a pending request specific to this message id
1325 		 */
1326 		for( lp = ld->ld_pend; lp != NULL; lp = lp->lp_next )
1327 		{
1328 			if( lp->lp_msgid == msgid )
1329 				break;
1330 		}
1331 
1332 		if( lp == NULL )
1333 		{
1334 			/*
1335 			 * No pending requests for this response... append to
1336 			 * our pending result list.
1337 			 */
1338 			LDAPPend	*newlp;
1339 			newlp = (LDAPPend *)NSLDAPI_CALLOC( 1,
1340 			    sizeof( LDAPPend ));
1341 			if( newlp == NULL )
1342 			{
1343 				LDAP_MUTEX_UNLOCK( ld, LDAP_PEND_LOCK );
1344 				LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL,
1345 				    NULL );
1346 				return (-1);
1347 			}
1348 			newlp->lp_msgid = msgid;
1349 			newlp->lp_result = result;
1350 			link_pend( ld, newlp );
1351 		}
1352 	}
1353 
1354 
1355 	if( lp != NULL )
1356 	{
1357 		/*
1358 		 * Wake up a thread that is waiting for this result.
1359 		 */
1360 		lp->lp_msgid = msgid;
1361 		lp->lp_result = result;
1362 		LDAP_SEMA_POST( ld, lp );
1363 	}
1364 
1365 	LDAP_MUTEX_UNLOCK( ld, LDAP_PEND_LOCK );
1366 	return (0);
1367 }
1368 
1369 static void
1370 link_pend( LDAP *ld, LDAPPend *lp )
1371 {
1372 	if (( lp->lp_next = ld->ld_pend ) != NULL )
1373 	{
1374 		lp->lp_next->lp_prev = lp;
1375 	}
1376 	ld->ld_pend = lp;
1377 	lp->lp_prev = NULL;
1378 }
1379 
1380 #if 0 /* these functions are no longer used */
1381 static void
1382 unlink_pend( LDAP *ld, LDAPPend *lp )
1383 {
1384         if ( lp->lp_prev == NULL ) {
1385                 ld->ld_pend = lp->lp_next;
1386         } else {
1387                 lp->lp_prev->lp_next = lp->lp_next;
1388         }
1389 
1390         if ( lp->lp_next != NULL ) {
1391                 lp->lp_next->lp_prev = lp->lp_prev;
1392         }
1393 }
1394 
1395 static int
1396 unlink_msg( LDAP *ld, int msgid, int all )
1397 {
1398 	int rc;
1399 	LDAPMessage	*lm, *lastlm, *nextlm;
1400 
1401 	lastlm = NULL;
1402 	LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK );
1403 	for ( lm = ld->ld_responses; lm != NULL; lm = nextlm )
1404 	{
1405 		nextlm = lm->lm_next;
1406 
1407 		if ( lm->lm_msgid == msgid )
1408 		{
1409 			LDAPMessage	*tmp;
1410 
1411 			if ( all == 0
1412 			    || (lm->lm_msgtype != LDAP_RES_SEARCH_RESULT
1413 			    && lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE
1414 			    && lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY) )
1415 				break;
1416 
1417 			for ( tmp = lm; tmp != NULL; tmp = tmp->lm_chain ) {
1418 				if ( tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT )
1419 					break;
1420 			}
1421 			if( tmp != NULL )
1422 				break;
1423 		}
1424 		lastlm = lm;
1425 	}
1426 
1427 	if( lm != NULL )
1428 	{
1429 
1430 		if ( all == 0 )
1431 		{
1432 			if ( lm->lm_chain == NULL )
1433 			{
1434 				if ( lastlm == NULL )
1435 					ld->ld_responses = lm->lm_next;
1436 				else
1437 					lastlm->lm_next = lm->lm_next;
1438 			}
1439 			else
1440 			{
1441 				if ( lastlm == NULL )
1442 				{
1443 					ld->ld_responses = lm->lm_chain;
1444 					ld->ld_responses->lm_next = lm->lm_next;
1445 				}
1446 				else
1447 				{
1448 					lastlm->lm_next = lm->lm_chain;
1449 					lastlm->lm_next->lm_next = lm->lm_next;
1450 				}
1451 			}
1452 		}
1453 		else
1454 		{
1455 			if ( lastlm == NULL )
1456 				ld->ld_responses = lm->lm_next;
1457 			else
1458 				lastlm->lm_next = lm->lm_next;
1459 		}
1460 
1461 		if ( all == 0 )
1462 			lm->lm_chain = NULL;
1463 		lm->lm_next = NULL;
1464 		rc = lm->lm_msgtype;
1465 	}
1466 	else
1467 	{
1468 		rc = -2;
1469 	}
1470 	LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
1471 	return ( rc );
1472 }
1473 #endif /* 0 */
1474