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