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