2 * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
6 #pragma ident "%Z%%M% %I% %E% SMI"
8 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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/
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
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
26 * Copyright (c) 1990 Regents of the University of Michigan.
27 * All rights reserved.
30 * result.c - wait for an ldap result
34 static char copyright
[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
40 /* high resolution timer usage */
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
);
57 static int cldap_select1( LDAP
*ld
, struct timeval
*timeout
);
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
);
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.
77 * ldap_result( s, msgid, all, timeout, result )
85 struct timeval
*timeout
,
91 LDAPDebug( LDAP_DEBUG_TRACE
, "ldap_result\n", 0, 0, 0 );
93 if ( !NSLDAPI_VALID_LDAP_POINTER( ld
)) {
94 return( -1 ); /* punt */
97 LDAP_MUTEX_LOCK( ld
, LDAP_RESULT_LOCK
);
99 rc
= nsldapi_result_nolock(ld
, msgid
, all
, 1, timeout
, result
);
101 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESULT_LOCK
);
108 nsldapi_result_nolock( LDAP
*ld
, int msgid
, int all
, int unlock_permitted
,
109 struct timeval
*timeout
, LDAPMessage
**result
)
113 LDAPDebug( LDAP_DEBUG_TRACE
,
114 "nsldapi_result_nolock (msgid=%d, all=%d)\n", msgid
, all
, 0 );
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.
123 if ( result
== NULL
) {
124 LDAP_SET_LDERRNO( ld
, LDAP_PARAM_ERROR
, NULL
, NULL
);
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
;
132 rc
= wait4msg( ld
, msgid
, all
, unlock_permitted
, timeout
,
137 * XXXmcs should use cache function pointers to hook in memcache
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
);
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
154 * If an appropriate message is found, a non-zero value is returned and the
155 * message is dequeued and assigned to *result.
157 * If not, *result is set to NULL and this function returns 0.
160 check_response_queue( LDAP
*ld
, int msgid
, int all
, int do_abandon_check
,
161 LDAPMessage
**result
)
163 LDAPMessage
*lm
, *lastlm
, *nextlm
;
166 LDAPDebug( LDAP_DEBUG_TRACE
,
167 "=> check_response_queue (msgid=%d, all=%d)\n", msgid
, all
, 0 );
171 LDAP_MUTEX_LOCK( ld
, LDAP_RESP_LOCK
);
172 for ( lm
= ld
->ld_responses
; lm
!= NULL
; lm
= nextlm
) {
173 nextlm
= lm
->lm_next
;
175 if ( do_abandon_check
&& ldap_abandoned( ld
, lm
->lm_msgid
) ) {
176 ldap_mark_abandoned( ld
, lm
->lm_msgid
);
178 if ( lastlm
== NULL
) {
179 ld
->ld_responses
= lm
->lm_next
;
181 lastlm
->lm_next
= nextlm
;
189 if ( msgid
== LDAP_RES_ANY
|| lm
->lm_msgid
== msgid
) {
193 || (lm
->lm_msgtype
!= LDAP_RES_SEARCH_RESULT
194 && lm
->lm_msgtype
!= LDAP_RES_SEARCH_REFERENCE
195 && lm
->lm_msgtype
!= LDAP_RES_SEARCH_ENTRY
) )
198 for ( tmp
= lm
; tmp
!= NULL
; tmp
= tmp
->lm_chain
) {
199 if ( tmp
->lm_msgtype
== LDAP_RES_SEARCH_RESULT
)
204 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESP_LOCK
);
205 LDAPDebug( LDAP_DEBUG_TRACE
,
206 "<= check_response_queue NOT FOUND\n",
208 return( 0 ); /* no message to return */
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.
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",
227 return( 0 ); /* no message to return */
231 if ( lm
->lm_chain
== NULL
) {
232 if ( lastlm
== NULL
) {
233 ld
->ld_responses
= lm
->lm_next
;
235 lastlm
->lm_next
= lm
->lm_next
;
238 if ( lastlm
== NULL
) {
239 ld
->ld_responses
= lm
->lm_chain
;
240 ld
->ld_responses
->lm_next
= lm
->lm_next
;
242 lastlm
->lm_next
= lm
->lm_chain
;
243 lastlm
->lm_next
->lm_next
= lm
->lm_next
;
247 if ( lastlm
== NULL
) {
248 ld
->ld_responses
= lm
->lm_next
;
250 lastlm
->lm_next
= lm
->lm_next
;
258 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESP_LOCK
);
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 */
269 wait4msg( LDAP
*ld
, int msgid
, int all
, int unlock_permitted
,
270 struct timeval
*timeout
, LDAPMessage
**result
)
273 struct timeval tv
, *tvp
;
275 hrtime_t start_time
= 0, tmp_time
, tv_time
;
277 long start_time
= 0, tmp_time
;
279 LDAPConn
*lc
, *nextlc
;
283 if ( timeout
== NULL
) {
284 LDAPDebug( LDAP_DEBUG_TRACE
, "wait4msg (infinite timeout)\n",
287 LDAPDebug( LDAP_DEBUG_TRACE
, "wait4msg (timeout %ld sec, %ld usec)\n",
288 timeout
->tv_sec
, timeout
->tv_usec
, 0 );
290 #endif /* LDAP_DEBUG */
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 ); */
302 if ( ld
->ld_cache_strategy
== LDAP_CACHE_LOCALDB
) {
303 LDAP_SET_LDERRNO( ld
, LDAP_TIMEOUT
, NULL
, NULL
);
304 return( 0 ); /* timeout */
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.
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
))
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 */
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 */
329 LDAP_MUTEX_UNLOCK( ld
, LDAP_REQ_LOCK
);
332 if ( timeout
== NULL
) {
338 start_time
= gethrtime();
339 tv_time
= ((hrtime_t
)tv
.tv_sec
* NANOSEC
+
340 (hrtime_t
)tv
.tv_usec
* (NANOSEC
/ MICROSEC
));
342 start_time
= (long)time( NULL
);
349 if ( ldap_debug
& LDAP_DEBUG_TRACE
) {
350 nsldapi_dump_connection( ld
, ld
->ld_conns
, 1 );
351 nsldapi_dump_requests_and_responses( ld
);
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
,
364 LDAP_MUTEX_UNLOCK( ld
, LDAP_REQ_LOCK
);
365 LDAP_MUTEX_UNLOCK( ld
, LDAP_CONN_LOCK
);
368 rc
= nsldapi_iostatus_poll( ld
, tvp
);
370 #if defined( LDAP_DEBUG ) && !defined( macintosh ) && !defined( DOS )
372 LDAPDebug( LDAP_DEBUG_TRACE
,
373 "nsldapi_iostatus_poll returned -1: errno %d\n",
374 LDAP_GET_ERRNO( ld
), 0, 0 );
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
))) {
383 if ( rc
== -1 || rc
== 0 ) {
385 LDAP_SET_LDERRNO( ld
, (rc
== -1 ?
386 LDAP_SERVER_DOWN
: LDAP_TIMEOUT
), NULL
,
389 LDAP_MUTEX_LOCK( ld
, LDAP_REQ_LOCK
);
390 nsldapi_connection_lost_nolock( ld
,
392 LDAP_MUTEX_UNLOCK( ld
, LDAP_REQ_LOCK
);
398 rc
= -2; /* select interrupted: loop */
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
;
405 nextlc
= lc
->lconn_next
;
406 if ( lc
->lconn_status
==
407 LDAP_CONNST_CONNECTED
&&
408 nsldapi_iostatus_is_read_ready( ld
,
410 rc
= read1msg( ld
, msgid
, all
,
411 lc
->lconn_sb
, lc
, result
);
413 else if (ld
->ld_options
& LDAP_BITOPT_ASYNC
) {
415 && lc
->lconn_status
== LDAP_CONNST_CONNECTING
416 && nsldapi_iostatus_is_write_ready( ld
,
418 rc
= nsldapi_ber_flush( ld
, lc
->lconn_sb
, lr
->lr_ber
, 0, 1 );
421 lc
->lconn_status
= LDAP_CONNST_CONNECTED
;
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
);
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
,
437 LDAP_MUTEX_UNLOCK( ld
, LDAP_REQ_LOCK
);
438 LDAP_MUTEX_UNLOCK( ld
, LDAP_CONN_LOCK
);
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
451 check_response_queue( ld
, msgid
, all
, 0, result
) != 0 ) {
452 LDAP_SET_LDERRNO( ld
, LDAP_SUCCESS
, NULL
, NULL
);
453 rc
= (*result
)->lm_msgtype
;
457 * honor the timeout if specified
459 if ( rc
== -2 && tvp
!= NULL
) {
461 tmp_time
= gethrtime();
462 if ((tv_time
-= (tmp_time
- start_time
)) <= 0) {
464 tmp_time
= (long)time( NULL
);
465 if (( tv
.tv_sec
-= ( tmp_time
- start_time
)) <= 0 ) {
467 rc
= 0; /* timed out */
468 LDAP_SET_LDERRNO( ld
, LDAP_TIMEOUT
, NULL
,
474 tv
.tv_sec
= tv_time
/ NANOSEC
;
475 tv
.tv_usec
= (tv_time
% NANOSEC
) / (NANOSEC
/ MICROSEC
);
477 LDAPDebug( LDAP_DEBUG_TRACE
, "wait4msg: %ld secs to go\n",
479 start_time
= tmp_time
;
488 * read1msg() should be called with LDAP_CONN_LOCK and LDAP_REQ_LOCK locked.
491 read1msg( LDAP
*ld
, int msgid
, int all
, Sockbuf
*sb
, LDAPConn
*lc
,
492 LDAPMessage
**result
)
495 LDAPMessage
*new, *l
, *prev
, *chainprev
, *tmp
;
499 int terrno
, lderr
, foundit
= 0;
501 int rc
, has_parent
, message_can_be_returned
;
502 int manufactured_result
= 0;
504 LDAPDebug( LDAP_DEBUG_TRACE
, "read1msg\n", 0, 0, 0 );
506 message_can_be_returned
= 1; /* the usual case... */
509 * if we are not already in the midst of reading a message, allocate
510 * a ber that is associated with this connection
512 if ( lc
->lconn_ber
== NULLBER
&& nsldapi_alloc_ber_with_options( ld
,
513 &lc
->lconn_ber
) != LDAP_SUCCESS
) {
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
521 LDAP_SET_ERRNO( ld
, 0 );
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 */
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
);
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.
543 lc
->lconn_ber
= NULLBER
;
546 if ( ber_get_int( ber
, &id
) == LBER_ERROR
) {
547 LDAP_SET_LDERRNO( ld
, LDAP_DECODING_ERROR
, NULL
, NULL
);
551 /* if it's been abandoned, toss it */
552 if ( ldap_abandoned( ld
, (int)id
) ) {
554 return( -2 ); /* continue looking */
557 if ( id
== LDAP_RES_UNSOLICITED
) {
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",
564 return( -2 ); /* continue looking */
567 /* the message type */
568 if ( (tag
= ber_peek_tag( ber
, &len
)) == LBER_ERROR
) {
569 LDAP_SET_LDERRNO( ld
, LDAP_DECODING_ERROR
, NULL
, NULL
);
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
);
579 lr
->lr_res_msgtype
= tag
;
581 rc
= -2; /* default is to keep looking (no response found) */
583 if ( id
!= LDAP_RES_UNSOLICITED
&& ( tag
== LDAP_RES_SEARCH_REFERENCE
||
584 tag
!= LDAP_RES_SEARCH_ENTRY
)) {
585 int refchasing
, reftotal
, simple_request
= 0;
587 check_for_refs( ld
, lr
, ber
, lc
->lconn_version
, &reftotal
,
590 if ( refchasing
> 0 || lr
->lr_outrefcnt
> 0 ) {
592 * we're chasing one or more new refs...
596 lr
->lr_status
= LDAP_REQST_CHASINGREFS
;
597 message_can_be_returned
= 0;
599 } else if ( tag
!= LDAP_RES_SEARCH_REFERENCE
) {
601 * this request is complete...
603 has_parent
= ( lr
->lr_parent
!= NULL
);
605 if ( lr
->lr_outrefcnt
<= 0 && !has_parent
) {
606 /* request without any refs */
607 simple_request
= ( reftotal
== 0 );
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.
616 LDAP_RES_BIND
== lr
->lr_res_msgtype
&&
617 lr
->lr_conn
!= NULL
) {
618 if ( lr
->lr_conn
->lconn_binddn
!= NULL
) {
620 lr
->lr_conn
->lconn_binddn
);
622 if ( LDAP_SUCCESS
== nsldapi_parse_result( ld
,
623 lr
->lr_res_msgtype
, ber
, &lderr
, NULL
,
625 && LDAP_SUCCESS
== lderr
) {
626 lr
->lr_conn
->lconn_bound
= 1;
627 lr
->lr_conn
->lconn_binddn
=
629 lr
->lr_binddn
= NULL
;
631 lr
->lr_conn
->lconn_bound
= 0;
632 lr
->lr_conn
->lconn_binddn
= NULL
;
637 * if this response is to a child request, we toss
638 * the message contents and just merge error info.
645 while ( lr
->lr_parent
!= NULL
) {
646 merge_error_info( ld
, lr
->lr_parent
, lr
);
649 if ( --lr
->lr_outrefcnt
> 0 ) {
650 break; /* not completely done yet */
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).
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
) {
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
) {
677 if ( build_result_ber( ld
, &ber
, lr
)
679 rc
= -1; /* fatal error */
681 manufactured_result
= 1;
685 nsldapi_free_request( ld
, lr
, 1 );
687 message_can_be_returned
= 0;
692 if ( ber
== NULLBER
) {
696 /* make a new ldap message */
697 if ( (new = (LDAPMessage
*)NSLDAPI_CALLOC( 1, sizeof(struct ldapmsg
) ))
699 LDAP_SET_LDERRNO( ld
, LDAP_NO_MEMORY
, NULL
, NULL
);
702 new->lm_msgid
= (int)id
;
703 new->lm_msgtype
= tag
;
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.
711 if ( message_can_be_returned
) {
712 if ( ld
->ld_cache_on
) {
713 nsldapi_add_result_to_cache( ld
, new );
716 if ( msgid
== LDAP_RES_ANY
|| id
== msgid
) {
717 if ( new->lm_msgtype
== LDAP_RES_SEARCH_RESULT
) {
719 * return the first response we have for this
720 * search request later (possibly an entire
721 * chain of messages).
725 || (new->lm_msgtype
!= LDAP_RES_SEARCH_REFERENCE
726 && new->lm_msgtype
!= LDAP_RES_SEARCH_ENTRY
) ) {
728 LDAP_SET_LDERRNO( ld
, LDAP_SUCCESS
, NULL
,
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
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
)
749 /* not part of an existing search response */
752 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESP_LOCK
);
754 LDAP_SET_LDERRNO( ld
, LDAP_SUCCESS
, NULL
, NULL
);
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 */
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
);
774 * part of a search response - add to end of list of entries
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.
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
) {
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.
795 if ( manufactured_result
&&
796 tmp
->lm_msgtype
== LDAP_RES_SEARCH_RESULT
) {
798 * the result is the only thing in the chain... replace it.
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;
809 chainprev
->lm_chain
= new;
816 } else if ( manufactured_result
&& tmp
->lm_chain
!= NULL
817 && tmp
->lm_chain
->lm_msgtype
== LDAP_RES_SEARCH_RESULT
) {
819 * entries or references are also present, so the result
820 * is the next entry after tmp. replace it.
822 new->lm_chain
= tmp
->lm_chain
->lm_chain
;
823 new->lm_next
= tmp
->lm_chain
->lm_next
;
824 ldap_msgfree( tmp
->lm_chain
);
827 } else if ( tmp
->lm_msgtype
== LDAP_RES_SEARCH_RESULT
) {
829 * the result is the only thing in the chain... add before it.
832 if ( chainprev
== NULL
) {
833 if ( prev
== NULL
) {
834 ld
->ld_responses
= new;
839 chainprev
->lm_chain
= new;
847 * entries and/or references are present... add to the end
848 * of the entry/reference part of the chain.
850 new->lm_chain
= tmp
->lm_chain
;
855 * return the first response or the whole chain if that's what
856 * we were looking for....
859 if ( all
== 0 && l
->lm_chain
!= NULL
) {
861 * only return the first response in the chain
863 if ( prev
== NULL
) {
864 ld
->ld_responses
= l
->lm_chain
;
866 prev
->lm_next
= l
->lm_chain
;
872 * return all of the responses (may be a chain)
874 if ( prev
== NULL
) {
875 ld
->ld_responses
= l
->lm_next
;
877 prev
->lm_next
= l
->lm_next
;
881 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESP_LOCK
);
882 LDAP_SET_LDERRNO( ld
, LDAP_SUCCESS
, NULL
, NULL
);
885 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESP_LOCK
);
886 return( -2 ); /* continue looking */
891 * check for LDAPv2+ (UMich extension) or LDAPv3 referrals or references
892 * errors are merged in "lr".
895 check_for_refs( LDAP
*ld
, LDAPRequest
*lr
, BerElement
*ber
,
896 int ldapversion
, int *totalcountp
, int *chasingcountp
)
899 char *errstr
, *matcheddn
, **v3refs
;
901 LDAPDebug( LDAP_DEBUG_TRACE
, "check_for_refs\n", 0, 0, 0 );
903 *chasingcountp
= *totalcountp
= 0;
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 */
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
;
916 err
= nsldapi_parse_result( ld
, lr
->lr_res_msgtype
, ber
,
917 &origerr
, &matcheddn
, &errstr
, &v3refs
, NULL
);
920 if ( err
!= LDAP_SUCCESS
) {
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
);
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
);
939 /* set LDAP errno, message, and matched string appropriately */
940 if ( lr
->lr_res_error
!= NULL
) {
941 NSLDAPI_FREE( lr
->lr_res_error
);
943 lr
->lr_res_error
= errstr
;
945 if ( lr
->lr_res_matched
!= NULL
) {
946 NSLDAPI_FREE( lr
->lr_res_matched
);
948 lr
->lr_res_matched
= matcheddn
;
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
;
956 /* preserve existing non-referral error code */
957 lr
->lr_res_errno
= origerr
;
959 } else if ( err
!= LDAP_SUCCESS
) {
960 /* error occurred while trying to chase referrals */
961 lr
->lr_res_errno
= err
;
963 /* some referrals were not recognized */
964 lr
->lr_res_errno
= ( ldapversion
== LDAP_VERSION2
)
965 ? LDAP_PARTIAL_RESULTS
: LDAP_REFERRAL
;
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 );
980 /* returns an LDAP error code and also sets it in LDAP * */
982 build_result_ber( LDAP
*ld
, BerElement
**berp
, LDAPRequest
*lr
)
989 if (( err
= nsldapi_alloc_ber_with_options( ld
, &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
);
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
);
1008 return( LDAP_SUCCESS
);
1013 merge_error_info( LDAP
*ld
, LDAPRequest
*parentr
, LDAPRequest
*lr
)
1016 * Merge error information in "lr" with "parentr" error code and string.
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
,
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
);
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
);
1036 parentr
->lr_res_matched
= lr
->lr_res_matched
;
1037 lr
->lr_res_matched
= NULL
;
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
: "" );
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! */
1053 cldap_select1( LDAP
*ld
, struct timeval
*timeout
)
1056 static int tblsize
= 0;
1057 NSLDAPIIOStatus
*iosp
= ld
->ld_iostatus
;
1059 if ( tblsize
== 0 ) {
1061 tblsize
= sysconf( _SC_OPEN_MAX
);
1062 #else /* USE_SYSCONF */
1063 tblsize
= getdtablesize();
1064 #endif /* USE_SYSCONF */
1067 if ( tblsize
>= FD_SETSIZE
) {
1069 * clamp value so we don't overrun the fd_set structure
1071 tblsize
= FD_SETSIZE
- 1;
1074 if ( NSLDAPI_IOSTATUS_TYPE_OSNATIVE
== iosp
->ios_type
) {
1077 FD_ZERO( &readfds
);
1078 FD_SET( ld
->ld_sbp
->sb_sd
, &readfds
);
1080 /* XXXmcs: UNIX platforms should use poll() */
1081 rc
= select( tblsize
, &readfds
, 0, 0, timeout
) );
1083 } else if ( NSLDAPI_IOSTATUS_TYPE_CALLBACK
== iosp
->ios_type
) {
1084 LDAP_X_PollFD pollfds
[ 1 ];
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
);
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?) */
1100 #endif /* !macintosh */
1105 cldap_select1( LDAP
*ld
, struct timeval
*timeout
)
1107 /* XXXmcs: needs to be revised to support I/O callbacks */
1108 return( tcpselect( ld
->ld_sbp
->sb_sd
, timeout
));
1110 #endif /* macintosh */
1113 #if (defined( DOS ) && defined( WINSOCK )) || defined( _WINDOWS ) || defined(XP_OS2)
1114 /* XXXmcs: needs to be revised to support extended I/O callbacks */
1116 cldap_select1( LDAP
*ld
, struct timeval
*timeout
)
1121 FD_ZERO( &readfds
);
1122 FD_SET( ld
->ld_sbp
->sb_sd
, &readfds
);
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,
1132 /* XXXmcs: UNIX platforms should use poll() */
1133 rc
= select( 1, &readfds
, 0, 0, timeout
) );
1136 return( rc
== SOCKET_ERROR
? -1 : rc
);
1138 #endif /* WINSOCK || _WINDOWS */
1143 ldap_msgfree( LDAPMessage
*lm
)
1148 LDAPDebug( LDAP_DEBUG_TRACE
, "ldap_msgfree\n", 0, 0, 0 );
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
);
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
1166 ldap_msgdelete( LDAP
*ld
, int msgid
)
1168 LDAPMessage
*lm
, *prev
;
1171 LDAPDebug( LDAP_DEBUG_TRACE
, "ldap_msgdelete\n", 0, 0, 0 );
1173 if ( !NSLDAPI_VALID_LDAP_POINTER( ld
)) {
1174 return( -1 ); /* punt */
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
)
1187 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESP_LOCK
);
1192 ld
->ld_responses
= lm
->lm_next
;
1194 prev
->lm_next
= lm
->lm_next
;
1195 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESP_LOCK
);
1197 msgtype
= ldap_msgfree( lm
);
1198 if ( msgtype
== LDAP_RES_SEARCH_ENTRY
1199 || msgtype
== LDAP_RES_SEARCH_REFERENCE
) {
1208 * return 1 if message msgid is waiting to be abandoned, 0 otherwise
1211 ldap_abandoned( LDAP
*ld
, int msgid
)
1215 LDAP_MUTEX_LOCK( ld
, LDAP_ABANDON_LOCK
);
1216 if ( ld
->ld_abandoned
== NULL
)
1218 LDAP_MUTEX_UNLOCK( ld
, LDAP_ABANDON_LOCK
);
1222 for ( i
= 0; ld
->ld_abandoned
[i
] != -1; i
++ )
1223 if ( ld
->ld_abandoned
[i
] == msgid
)
1225 LDAP_MUTEX_UNLOCK( ld
, LDAP_ABANDON_LOCK
);
1229 LDAP_MUTEX_UNLOCK( ld
, LDAP_ABANDON_LOCK
);
1235 ldap_mark_abandoned( LDAP
*ld
, int msgid
)
1239 LDAP_MUTEX_LOCK( ld
, LDAP_ABANDON_LOCK
);
1240 if ( ld
->ld_abandoned
== NULL
)
1242 LDAP_MUTEX_UNLOCK( ld
, LDAP_ABANDON_LOCK
);
1246 for ( i
= 0; ld
->ld_abandoned
[i
] != -1; i
++ )
1247 if ( ld
->ld_abandoned
[i
] == msgid
)
1250 if ( ld
->ld_abandoned
[i
] == -1 )
1252 LDAP_MUTEX_UNLOCK( ld
, LDAP_ABANDON_LOCK
);
1256 for ( ; ld
->ld_abandoned
[i
] != -1; i
++ ) {
1257 ld
->ld_abandoned
[i
] = ld
->ld_abandoned
[i
+ 1];
1260 LDAP_MUTEX_UNLOCK( ld
, LDAP_ABANDON_LOCK
);
1267 cldap_getmsg( LDAP
*ld
, struct timeval
*timeout
, BerElement
**ber
)
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
);
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
);
1295 nsldapi_post_result( LDAP
*ld
, int msgid
, LDAPMessage
*result
)
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
) {
1305 * Look for any pending request for which someone is waiting.
1307 for( lp
= ld
->ld_pend
; lp
!= NULL
; lp
= lp
->lp_next
)
1309 if ( lp
->lp_sema
!= NULL
) {
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.
1322 * Look for a pending request specific to this message id
1324 for( lp
= ld
->ld_pend
; lp
!= NULL
; lp
= lp
->lp_next
)
1326 if( lp
->lp_msgid
== msgid
)
1333 * No pending requests for this response... append to
1334 * our pending result list.
1337 newlp
= (LDAPPend
*)NSLDAPI_CALLOC( 1,
1338 sizeof( LDAPPend
));
1341 LDAP_MUTEX_UNLOCK( ld
, LDAP_PEND_LOCK
);
1342 LDAP_SET_LDERRNO( ld
, LDAP_NO_MEMORY
, NULL
,
1346 newlp
->lp_msgid
= msgid
;
1347 newlp
->lp_result
= result
;
1348 link_pend( ld
, newlp
);
1356 * Wake up a thread that is waiting for this result.
1358 lp
->lp_msgid
= msgid
;
1359 lp
->lp_result
= result
;
1360 LDAP_SEMA_POST( ld
, lp
);
1363 LDAP_MUTEX_UNLOCK( ld
, LDAP_PEND_LOCK
);
1368 link_pend( LDAP
*ld
, LDAPPend
*lp
)
1370 if (( lp
->lp_next
= ld
->ld_pend
) != NULL
)
1372 lp
->lp_next
->lp_prev
= lp
;
1378 #if 0 /* these functions are no longer used */
1380 unlink_pend( LDAP
*ld
, LDAPPend
*lp
)
1382 if ( lp
->lp_prev
== NULL
) {
1383 ld
->ld_pend
= lp
->lp_next
;
1385 lp
->lp_prev
->lp_next
= lp
->lp_next
;
1388 if ( lp
->lp_next
!= NULL
) {
1389 lp
->lp_next
->lp_prev
= lp
->lp_prev
;
1394 unlink_msg( LDAP
*ld
, int msgid
, int all
)
1397 LDAPMessage
*lm
, *lastlm
, *nextlm
;
1400 LDAP_MUTEX_LOCK( ld
, LDAP_RESP_LOCK
);
1401 for ( lm
= ld
->ld_responses
; lm
!= NULL
; lm
= nextlm
)
1403 nextlm
= lm
->lm_next
;
1405 if ( lm
->lm_msgid
== msgid
)
1410 || (lm
->lm_msgtype
!= LDAP_RES_SEARCH_RESULT
1411 && lm
->lm_msgtype
!= LDAP_RES_SEARCH_REFERENCE
1412 && lm
->lm_msgtype
!= LDAP_RES_SEARCH_ENTRY
) )
1415 for ( tmp
= lm
; tmp
!= NULL
; tmp
= tmp
->lm_chain
) {
1416 if ( tmp
->lm_msgtype
== LDAP_RES_SEARCH_RESULT
)
1430 if ( lm
->lm_chain
== NULL
)
1432 if ( lastlm
== NULL
)
1433 ld
->ld_responses
= lm
->lm_next
;
1435 lastlm
->lm_next
= lm
->lm_next
;
1439 if ( lastlm
== NULL
)
1441 ld
->ld_responses
= lm
->lm_chain
;
1442 ld
->ld_responses
->lm_next
= lm
->lm_next
;
1446 lastlm
->lm_next
= lm
->lm_chain
;
1447 lastlm
->lm_next
->lm_next
= lm
->lm_next
;
1453 if ( lastlm
== NULL
)
1454 ld
->ld_responses
= lm
->lm_next
;
1456 lastlm
->lm_next
= lm
->lm_next
;
1460 lm
->lm_chain
= NULL
;
1462 rc
= lm
->lm_msgtype
;
1468 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESP_LOCK
);