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