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
35 static char copyright
[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
42 /* high resolution timer usage */
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
);
59 static int cldap_select1( LDAP
*ld
, struct timeval
*timeout
);
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
);
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.
79 * ldap_result( s, msgid, all, timeout, result )
87 struct timeval
*timeout
,
93 LDAPDebug( LDAP_DEBUG_TRACE
, "ldap_result\n", 0, 0, 0 );
95 if ( !NSLDAPI_VALID_LDAP_POINTER( ld
)) {
96 return( -1 ); /* punt */
99 LDAP_MUTEX_LOCK( ld
, LDAP_RESULT_LOCK
);
101 rc
= nsldapi_result_nolock(ld
, msgid
, all
, 1, timeout
, result
);
103 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESULT_LOCK
);
110 nsldapi_result_nolock( LDAP
*ld
, int msgid
, int all
, int unlock_permitted
,
111 struct timeval
*timeout
, LDAPMessage
**result
)
115 LDAPDebug( LDAP_DEBUG_TRACE
,
116 "nsldapi_result_nolock (msgid=%d, all=%d)\n", msgid
, all
, 0 );
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.
125 if ( result
== NULL
) {
126 LDAP_SET_LDERRNO( ld
, LDAP_PARAM_ERROR
, NULL
, NULL
);
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
;
134 rc
= wait4msg( ld
, msgid
, all
, unlock_permitted
, timeout
,
139 * XXXmcs should use cache function pointers to hook in memcache
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
);
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
156 * If an appropriate message is found, a non-zero value is returned and the
157 * message is dequeued and assigned to *result.
159 * If not, *result is set to NULL and this function returns 0.
162 check_response_queue( LDAP
*ld
, int msgid
, int all
, int do_abandon_check
,
163 LDAPMessage
**result
)
165 LDAPMessage
*lm
, *lastlm
, *nextlm
;
168 LDAPDebug( LDAP_DEBUG_TRACE
,
169 "=> check_response_queue (msgid=%d, all=%d)\n", msgid
, all
, 0 );
173 LDAP_MUTEX_LOCK( ld
, LDAP_RESP_LOCK
);
174 for ( lm
= ld
->ld_responses
; lm
!= NULL
; lm
= nextlm
) {
175 nextlm
= lm
->lm_next
;
177 if ( do_abandon_check
&& ldap_abandoned( ld
, lm
->lm_msgid
) ) {
178 ldap_mark_abandoned( ld
, lm
->lm_msgid
);
180 if ( lastlm
== NULL
) {
181 ld
->ld_responses
= lm
->lm_next
;
183 lastlm
->lm_next
= nextlm
;
191 if ( msgid
== LDAP_RES_ANY
|| lm
->lm_msgid
== msgid
) {
195 || (lm
->lm_msgtype
!= LDAP_RES_SEARCH_RESULT
196 && lm
->lm_msgtype
!= LDAP_RES_SEARCH_REFERENCE
197 && lm
->lm_msgtype
!= LDAP_RES_SEARCH_ENTRY
) )
200 for ( tmp
= lm
; tmp
!= NULL
; tmp
= tmp
->lm_chain
) {
201 if ( tmp
->lm_msgtype
== LDAP_RES_SEARCH_RESULT
)
206 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESP_LOCK
);
207 LDAPDebug( LDAP_DEBUG_TRACE
,
208 "<= check_response_queue NOT FOUND\n",
210 return( 0 ); /* no message to return */
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.
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",
229 return( 0 ); /* no message to return */
233 if ( lm
->lm_chain
== NULL
) {
234 if ( lastlm
== NULL
) {
235 ld
->ld_responses
= lm
->lm_next
;
237 lastlm
->lm_next
= lm
->lm_next
;
240 if ( lastlm
== NULL
) {
241 ld
->ld_responses
= lm
->lm_chain
;
242 ld
->ld_responses
->lm_next
= lm
->lm_next
;
244 lastlm
->lm_next
= lm
->lm_chain
;
245 lastlm
->lm_next
->lm_next
= lm
->lm_next
;
249 if ( lastlm
== NULL
) {
250 ld
->ld_responses
= lm
->lm_next
;
252 lastlm
->lm_next
= lm
->lm_next
;
260 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESP_LOCK
);
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 */
271 wait4msg( LDAP
*ld
, int msgid
, int all
, int unlock_permitted
,
272 struct timeval
*timeout
, LDAPMessage
**result
)
275 struct timeval tv
, *tvp
;
277 hrtime_t start_time
= 0, tmp_time
, tv_time
;
279 long start_time
= 0, tmp_time
;
281 LDAPConn
*lc
, *nextlc
;
285 if ( timeout
== NULL
) {
286 LDAPDebug( LDAP_DEBUG_TRACE
, "wait4msg (infinite timeout)\n",
289 LDAPDebug( LDAP_DEBUG_TRACE
, "wait4msg (timeout %ld sec, %ld usec)\n",
290 timeout
->tv_sec
, timeout
->tv_usec
, 0 );
292 #endif /* LDAP_DEBUG */
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 ); */
304 if ( ld
->ld_cache_strategy
== LDAP_CACHE_LOCALDB
) {
305 LDAP_SET_LDERRNO( ld
, LDAP_TIMEOUT
, NULL
, NULL
);
306 return( 0 ); /* timeout */
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.
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
))
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 */
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 */
331 LDAP_MUTEX_UNLOCK( ld
, LDAP_REQ_LOCK
);
334 if ( timeout
== NULL
) {
340 start_time
= gethrtime();
341 tv_time
= ((hrtime_t
)tv
.tv_sec
* NANOSEC
+
342 (hrtime_t
)tv
.tv_usec
* (NANOSEC
/ MICROSEC
));
344 start_time
= (long)time( NULL
);
351 if ( ldap_debug
& LDAP_DEBUG_TRACE
) {
352 nsldapi_dump_connection( ld
, ld
->ld_conns
, 1 );
353 nsldapi_dump_requests_and_responses( ld
);
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
,
366 LDAP_MUTEX_UNLOCK( ld
, LDAP_REQ_LOCK
);
367 LDAP_MUTEX_UNLOCK( ld
, LDAP_CONN_LOCK
);
370 rc
= nsldapi_iostatus_poll( ld
, tvp
);
372 #if defined( LDAP_DEBUG ) && !defined( macintosh ) && !defined( DOS )
374 LDAPDebug( LDAP_DEBUG_TRACE
,
375 "nsldapi_iostatus_poll returned -1: errno %d\n",
376 LDAP_GET_ERRNO( ld
), 0, 0 );
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
))) {
385 if ( rc
== -1 || rc
== 0 ) {
387 LDAP_SET_LDERRNO( ld
, (rc
== -1 ?
388 LDAP_SERVER_DOWN
: LDAP_TIMEOUT
), NULL
,
391 LDAP_MUTEX_LOCK( ld
, LDAP_REQ_LOCK
);
392 nsldapi_connection_lost_nolock( ld
,
394 LDAP_MUTEX_UNLOCK( ld
, LDAP_REQ_LOCK
);
400 rc
= -2; /* select interrupted: loop */
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
;
407 nextlc
= lc
->lconn_next
;
408 if ( lc
->lconn_status
==
409 LDAP_CONNST_CONNECTED
&&
410 nsldapi_iostatus_is_read_ready( ld
,
412 rc
= read1msg( ld
, msgid
, all
,
413 lc
->lconn_sb
, lc
, result
);
415 else if (ld
->ld_options
& LDAP_BITOPT_ASYNC
) {
417 && lc
->lconn_status
== LDAP_CONNST_CONNECTING
418 && nsldapi_iostatus_is_write_ready( ld
,
420 rc
= nsldapi_ber_flush( ld
, lc
->lconn_sb
, lr
->lr_ber
, 0, 1 );
423 lc
->lconn_status
= LDAP_CONNST_CONNECTED
;
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
);
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
,
439 LDAP_MUTEX_UNLOCK( ld
, LDAP_REQ_LOCK
);
440 LDAP_MUTEX_UNLOCK( ld
, LDAP_CONN_LOCK
);
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
453 check_response_queue( ld
, msgid
, all
, 0, result
) != 0 ) {
454 LDAP_SET_LDERRNO( ld
, LDAP_SUCCESS
, NULL
, NULL
);
455 rc
= (*result
)->lm_msgtype
;
459 * honor the timeout if specified
461 if ( rc
== -2 && tvp
!= NULL
) {
463 tmp_time
= gethrtime();
464 if ((tv_time
-= (tmp_time
- start_time
)) <= 0) {
466 tmp_time
= (long)time( NULL
);
467 if (( tv
.tv_sec
-= ( tmp_time
- start_time
)) <= 0 ) {
469 rc
= 0; /* timed out */
470 LDAP_SET_LDERRNO( ld
, LDAP_TIMEOUT
, NULL
,
476 tv
.tv_sec
= tv_time
/ NANOSEC
;
477 tv
.tv_usec
= (tv_time
% NANOSEC
) / (NANOSEC
/ MICROSEC
);
479 LDAPDebug( LDAP_DEBUG_TRACE
, "wait4msg: %ld secs to go\n",
481 start_time
= tmp_time
;
490 * read1msg() should be called with LDAP_CONN_LOCK and LDAP_REQ_LOCK locked.
493 read1msg( LDAP
*ld
, int msgid
, int all
, Sockbuf
*sb
, LDAPConn
*lc
,
494 LDAPMessage
**result
)
497 LDAPMessage
*new, *l
, *prev
, *chainprev
, *tmp
;
501 int terrno
, lderr
, foundit
= 0;
503 int rc
, has_parent
, message_can_be_returned
;
504 int manufactured_result
= 0;
506 LDAPDebug( LDAP_DEBUG_TRACE
, "read1msg\n", 0, 0, 0 );
508 message_can_be_returned
= 1; /* the usual case... */
511 * if we are not already in the midst of reading a message, allocate
512 * a ber that is associated with this connection
514 if ( lc
->lconn_ber
== NULLBER
&& nsldapi_alloc_ber_with_options( ld
,
515 &lc
->lconn_ber
) != LDAP_SUCCESS
) {
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
523 LDAP_SET_ERRNO( ld
, 0 );
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 */
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
);
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.
545 lc
->lconn_ber
= NULLBER
;
548 if ( ber_get_int( ber
, &id
) == LBER_ERROR
) {
549 LDAP_SET_LDERRNO( ld
, LDAP_DECODING_ERROR
, NULL
, NULL
);
553 /* if it's been abandoned, toss it */
554 if ( ldap_abandoned( ld
, (int)id
) ) {
556 return( -2 ); /* continue looking */
559 if ( id
== LDAP_RES_UNSOLICITED
) {
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",
566 return( -2 ); /* continue looking */
569 /* the message type */
570 if ( (tag
= ber_peek_tag( ber
, &len
)) == LBER_ERROR
) {
571 LDAP_SET_LDERRNO( ld
, LDAP_DECODING_ERROR
, NULL
, NULL
);
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
);
581 lr
->lr_res_msgtype
= tag
;
583 rc
= -2; /* default is to keep looking (no response found) */
585 if ( id
!= LDAP_RES_UNSOLICITED
&& ( tag
== LDAP_RES_SEARCH_REFERENCE
||
586 tag
!= LDAP_RES_SEARCH_ENTRY
)) {
587 int refchasing
, reftotal
, simple_request
= 0;
589 check_for_refs( ld
, lr
, ber
, lc
->lconn_version
, &reftotal
,
592 if ( refchasing
> 0 || lr
->lr_outrefcnt
> 0 ) {
594 * we're chasing one or more new refs...
598 lr
->lr_status
= LDAP_REQST_CHASINGREFS
;
599 message_can_be_returned
= 0;
601 } else if ( tag
!= LDAP_RES_SEARCH_REFERENCE
) {
603 * this request is complete...
605 has_parent
= ( lr
->lr_parent
!= NULL
);
607 if ( lr
->lr_outrefcnt
<= 0 && !has_parent
) {
608 /* request without any refs */
609 simple_request
= ( reftotal
== 0 );
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.
618 LDAP_RES_BIND
== lr
->lr_res_msgtype
&&
619 lr
->lr_conn
!= NULL
) {
620 if ( lr
->lr_conn
->lconn_binddn
!= NULL
) {
622 lr
->lr_conn
->lconn_binddn
);
624 if ( LDAP_SUCCESS
== nsldapi_parse_result( ld
,
625 lr
->lr_res_msgtype
, ber
, &lderr
, NULL
,
627 && LDAP_SUCCESS
== lderr
) {
628 lr
->lr_conn
->lconn_bound
= 1;
629 lr
->lr_conn
->lconn_binddn
=
631 lr
->lr_binddn
= NULL
;
633 lr
->lr_conn
->lconn_bound
= 0;
634 lr
->lr_conn
->lconn_binddn
= NULL
;
639 * if this response is to a child request, we toss
640 * the message contents and just merge error info.
647 while ( lr
->lr_parent
!= NULL
) {
648 merge_error_info( ld
, lr
->lr_parent
, lr
);
651 if ( --lr
->lr_outrefcnt
> 0 ) {
652 break; /* not completely done yet */
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).
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
) {
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
) {
679 if ( build_result_ber( ld
, &ber
, lr
)
681 rc
= -1; /* fatal error */
683 manufactured_result
= 1;
687 nsldapi_free_request( ld
, lr
, 1 );
689 message_can_be_returned
= 0;
694 if ( ber
== NULLBER
) {
698 /* make a new ldap message */
699 if ( (new = (LDAPMessage
*)NSLDAPI_CALLOC( 1, sizeof(struct ldapmsg
) ))
701 LDAP_SET_LDERRNO( ld
, LDAP_NO_MEMORY
, NULL
, NULL
);
704 new->lm_msgid
= (int)id
;
705 new->lm_msgtype
= tag
;
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.
713 if ( message_can_be_returned
) {
714 if ( ld
->ld_cache_on
) {
715 nsldapi_add_result_to_cache( ld
, new );
718 if ( msgid
== LDAP_RES_ANY
|| id
== msgid
) {
719 if ( new->lm_msgtype
== LDAP_RES_SEARCH_RESULT
) {
721 * return the first response we have for this
722 * search request later (possibly an entire
723 * chain of messages).
727 || (new->lm_msgtype
!= LDAP_RES_SEARCH_REFERENCE
728 && new->lm_msgtype
!= LDAP_RES_SEARCH_ENTRY
) ) {
730 LDAP_SET_LDERRNO( ld
, LDAP_SUCCESS
, NULL
,
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
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
)
751 /* not part of an existing search response */
754 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESP_LOCK
);
756 LDAP_SET_LDERRNO( ld
, LDAP_SUCCESS
, NULL
, NULL
);
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 */
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
);
776 * part of a search response - add to end of list of entries
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.
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
) {
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.
797 if ( manufactured_result
&&
798 tmp
->lm_msgtype
== LDAP_RES_SEARCH_RESULT
) {
800 * the result is the only thing in the chain... replace it.
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;
811 chainprev
->lm_chain
= new;
818 } else if ( manufactured_result
&& tmp
->lm_chain
!= NULL
819 && tmp
->lm_chain
->lm_msgtype
== LDAP_RES_SEARCH_RESULT
) {
821 * entries or references are also present, so the result
822 * is the next entry after tmp. replace it.
824 new->lm_chain
= tmp
->lm_chain
->lm_chain
;
825 new->lm_next
= tmp
->lm_chain
->lm_next
;
826 ldap_msgfree( tmp
->lm_chain
);
829 } else if ( tmp
->lm_msgtype
== LDAP_RES_SEARCH_RESULT
) {
831 * the result is the only thing in the chain... add before it.
834 if ( chainprev
== NULL
) {
835 if ( prev
== NULL
) {
836 ld
->ld_responses
= new;
841 chainprev
->lm_chain
= new;
849 * entries and/or references are present... add to the end
850 * of the entry/reference part of the chain.
852 new->lm_chain
= tmp
->lm_chain
;
857 * return the first response or the whole chain if that's what
858 * we were looking for....
861 if ( all
== 0 && l
->lm_chain
!= NULL
) {
863 * only return the first response in the chain
865 if ( prev
== NULL
) {
866 ld
->ld_responses
= l
->lm_chain
;
868 prev
->lm_next
= l
->lm_chain
;
874 * return all of the responses (may be a chain)
876 if ( prev
== NULL
) {
877 ld
->ld_responses
= l
->lm_next
;
879 prev
->lm_next
= l
->lm_next
;
883 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESP_LOCK
);
884 LDAP_SET_LDERRNO( ld
, LDAP_SUCCESS
, NULL
, NULL
);
887 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESP_LOCK
);
888 return( -2 ); /* continue looking */
893 * check for LDAPv2+ (UMich extension) or LDAPv3 referrals or references
894 * errors are merged in "lr".
897 check_for_refs( LDAP
*ld
, LDAPRequest
*lr
, BerElement
*ber
,
898 int ldapversion
, int *totalcountp
, int *chasingcountp
)
901 char *errstr
, *matcheddn
, **v3refs
;
903 LDAPDebug( LDAP_DEBUG_TRACE
, "check_for_refs\n", 0, 0, 0 );
905 *chasingcountp
= *totalcountp
= 0;
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 */
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
;
918 err
= nsldapi_parse_result( ld
, lr
->lr_res_msgtype
, ber
,
919 &origerr
, &matcheddn
, &errstr
, &v3refs
, NULL
);
922 if ( err
!= LDAP_SUCCESS
) {
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
);
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
);
941 /* set LDAP errno, message, and matched string appropriately */
942 if ( lr
->lr_res_error
!= NULL
) {
943 NSLDAPI_FREE( lr
->lr_res_error
);
945 lr
->lr_res_error
= errstr
;
947 if ( lr
->lr_res_matched
!= NULL
) {
948 NSLDAPI_FREE( lr
->lr_res_matched
);
950 lr
->lr_res_matched
= matcheddn
;
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
;
958 /* preserve existing non-referral error code */
959 lr
->lr_res_errno
= origerr
;
961 } else if ( err
!= LDAP_SUCCESS
) {
962 /* error occurred while trying to chase referrals */
963 lr
->lr_res_errno
= err
;
965 /* some referrals were not recognized */
966 lr
->lr_res_errno
= ( ldapversion
== LDAP_VERSION2
)
967 ? LDAP_PARTIAL_RESULTS
: LDAP_REFERRAL
;
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 );
982 /* returns an LDAP error code and also sets it in LDAP * */
984 build_result_ber( LDAP
*ld
, BerElement
**berp
, LDAPRequest
*lr
)
991 if (( err
= nsldapi_alloc_ber_with_options( ld
, &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
);
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
);
1010 return( LDAP_SUCCESS
);
1015 merge_error_info( LDAP
*ld
, LDAPRequest
*parentr
, LDAPRequest
*lr
)
1018 * Merge error information in "lr" with "parentr" error code and string.
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
,
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
);
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
);
1038 parentr
->lr_res_matched
= lr
->lr_res_matched
;
1039 lr
->lr_res_matched
= NULL
;
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
: "" );
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! */
1055 cldap_select1( LDAP
*ld
, struct timeval
*timeout
)
1058 static int tblsize
= 0;
1059 NSLDAPIIOStatus
*iosp
= ld
->ld_iostatus
;
1061 if ( tblsize
== 0 ) {
1063 tblsize
= sysconf( _SC_OPEN_MAX
);
1064 #else /* USE_SYSCONF */
1065 tblsize
= getdtablesize();
1066 #endif /* USE_SYSCONF */
1069 if ( tblsize
>= FD_SETSIZE
) {
1071 * clamp value so we don't overrun the fd_set structure
1073 tblsize
= FD_SETSIZE
- 1;
1076 if ( NSLDAPI_IOSTATUS_TYPE_OSNATIVE
== iosp
->ios_type
) {
1079 FD_ZERO( &readfds
);
1080 FD_SET( ld
->ld_sbp
->sb_sd
, &readfds
);
1082 /* XXXmcs: UNIX platforms should use poll() */
1083 rc
= select( tblsize
, &readfds
, 0, 0, timeout
) );
1085 } else if ( NSLDAPI_IOSTATUS_TYPE_CALLBACK
== iosp
->ios_type
) {
1086 LDAP_X_PollFD pollfds
[ 1 ];
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
);
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?) */
1102 #endif /* !macintosh */
1107 cldap_select1( LDAP
*ld
, struct timeval
*timeout
)
1109 /* XXXmcs: needs to be revised to support I/O callbacks */
1110 return( tcpselect( ld
->ld_sbp
->sb_sd
, timeout
));
1112 #endif /* macintosh */
1115 #if (defined( DOS ) && defined( WINSOCK )) || defined( _WINDOWS ) || defined(XP_OS2)
1116 /* XXXmcs: needs to be revised to support extended I/O callbacks */
1118 cldap_select1( LDAP
*ld
, struct timeval
*timeout
)
1123 FD_ZERO( &readfds
);
1124 FD_SET( ld
->ld_sbp
->sb_sd
, &readfds
);
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,
1134 /* XXXmcs: UNIX platforms should use poll() */
1135 rc
= select( 1, &readfds
, 0, 0, timeout
) );
1138 return( rc
== SOCKET_ERROR
? -1 : rc
);
1140 #endif /* WINSOCK || _WINDOWS */
1145 ldap_msgfree( LDAPMessage
*lm
)
1150 LDAPDebug( LDAP_DEBUG_TRACE
, "ldap_msgfree\n", 0, 0, 0 );
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
);
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
1168 ldap_msgdelete( LDAP
*ld
, int msgid
)
1170 LDAPMessage
*lm
, *prev
;
1173 LDAPDebug( LDAP_DEBUG_TRACE
, "ldap_msgdelete\n", 0, 0, 0 );
1175 if ( !NSLDAPI_VALID_LDAP_POINTER( ld
)) {
1176 return( -1 ); /* punt */
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
)
1189 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESP_LOCK
);
1194 ld
->ld_responses
= lm
->lm_next
;
1196 prev
->lm_next
= lm
->lm_next
;
1197 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESP_LOCK
);
1199 msgtype
= ldap_msgfree( lm
);
1200 if ( msgtype
== LDAP_RES_SEARCH_ENTRY
1201 || msgtype
== LDAP_RES_SEARCH_REFERENCE
) {
1210 * return 1 if message msgid is waiting to be abandoned, 0 otherwise
1213 ldap_abandoned( LDAP
*ld
, int msgid
)
1217 LDAP_MUTEX_LOCK( ld
, LDAP_ABANDON_LOCK
);
1218 if ( ld
->ld_abandoned
== NULL
)
1220 LDAP_MUTEX_UNLOCK( ld
, LDAP_ABANDON_LOCK
);
1224 for ( i
= 0; ld
->ld_abandoned
[i
] != -1; i
++ )
1225 if ( ld
->ld_abandoned
[i
] == msgid
)
1227 LDAP_MUTEX_UNLOCK( ld
, LDAP_ABANDON_LOCK
);
1231 LDAP_MUTEX_UNLOCK( ld
, LDAP_ABANDON_LOCK
);
1237 ldap_mark_abandoned( LDAP
*ld
, int msgid
)
1241 LDAP_MUTEX_LOCK( ld
, LDAP_ABANDON_LOCK
);
1242 if ( ld
->ld_abandoned
== NULL
)
1244 LDAP_MUTEX_UNLOCK( ld
, LDAP_ABANDON_LOCK
);
1248 for ( i
= 0; ld
->ld_abandoned
[i
] != -1; i
++ )
1249 if ( ld
->ld_abandoned
[i
] == msgid
)
1252 if ( ld
->ld_abandoned
[i
] == -1 )
1254 LDAP_MUTEX_UNLOCK( ld
, LDAP_ABANDON_LOCK
);
1258 for ( ; ld
->ld_abandoned
[i
] != -1; i
++ ) {
1259 ld
->ld_abandoned
[i
] = ld
->ld_abandoned
[i
+ 1];
1262 LDAP_MUTEX_UNLOCK( ld
, LDAP_ABANDON_LOCK
);
1269 cldap_getmsg( LDAP
*ld
, struct timeval
*timeout
, BerElement
**ber
)
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
);
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
);
1297 nsldapi_post_result( LDAP
*ld
, int msgid
, LDAPMessage
*result
)
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
) {
1307 * Look for any pending request for which someone is waiting.
1309 for( lp
= ld
->ld_pend
; lp
!= NULL
; lp
= lp
->lp_next
)
1311 if ( lp
->lp_sema
!= NULL
) {
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.
1324 * Look for a pending request specific to this message id
1326 for( lp
= ld
->ld_pend
; lp
!= NULL
; lp
= lp
->lp_next
)
1328 if( lp
->lp_msgid
== msgid
)
1335 * No pending requests for this response... append to
1336 * our pending result list.
1339 newlp
= (LDAPPend
*)NSLDAPI_CALLOC( 1,
1340 sizeof( LDAPPend
));
1343 LDAP_MUTEX_UNLOCK( ld
, LDAP_PEND_LOCK
);
1344 LDAP_SET_LDERRNO( ld
, LDAP_NO_MEMORY
, NULL
,
1348 newlp
->lp_msgid
= msgid
;
1349 newlp
->lp_result
= result
;
1350 link_pend( ld
, newlp
);
1358 * Wake up a thread that is waiting for this result.
1360 lp
->lp_msgid
= msgid
;
1361 lp
->lp_result
= result
;
1362 LDAP_SEMA_POST( ld
, lp
);
1365 LDAP_MUTEX_UNLOCK( ld
, LDAP_PEND_LOCK
);
1370 link_pend( LDAP
*ld
, LDAPPend
*lp
)
1372 if (( lp
->lp_next
= ld
->ld_pend
) != NULL
)
1374 lp
->lp_next
->lp_prev
= lp
;
1380 #if 0 /* these functions are no longer used */
1382 unlink_pend( LDAP
*ld
, LDAPPend
*lp
)
1384 if ( lp
->lp_prev
== NULL
) {
1385 ld
->ld_pend
= lp
->lp_next
;
1387 lp
->lp_prev
->lp_next
= lp
->lp_next
;
1390 if ( lp
->lp_next
!= NULL
) {
1391 lp
->lp_next
->lp_prev
= lp
->lp_prev
;
1396 unlink_msg( LDAP
*ld
, int msgid
, int all
)
1399 LDAPMessage
*lm
, *lastlm
, *nextlm
;
1402 LDAP_MUTEX_LOCK( ld
, LDAP_RESP_LOCK
);
1403 for ( lm
= ld
->ld_responses
; lm
!= NULL
; lm
= nextlm
)
1405 nextlm
= lm
->lm_next
;
1407 if ( lm
->lm_msgid
== msgid
)
1412 || (lm
->lm_msgtype
!= LDAP_RES_SEARCH_RESULT
1413 && lm
->lm_msgtype
!= LDAP_RES_SEARCH_REFERENCE
1414 && lm
->lm_msgtype
!= LDAP_RES_SEARCH_ENTRY
) )
1417 for ( tmp
= lm
; tmp
!= NULL
; tmp
= tmp
->lm_chain
) {
1418 if ( tmp
->lm_msgtype
== LDAP_RES_SEARCH_RESULT
)
1432 if ( lm
->lm_chain
== NULL
)
1434 if ( lastlm
== NULL
)
1435 ld
->ld_responses
= lm
->lm_next
;
1437 lastlm
->lm_next
= lm
->lm_next
;
1441 if ( lastlm
== NULL
)
1443 ld
->ld_responses
= lm
->lm_chain
;
1444 ld
->ld_responses
->lm_next
= lm
->lm_next
;
1448 lastlm
->lm_next
= lm
->lm_chain
;
1449 lastlm
->lm_next
->lm_next
= lm
->lm_next
;
1455 if ( lastlm
== NULL
)
1456 ld
->ld_responses
= lm
->lm_next
;
1458 lastlm
->lm_next
= lm
->lm_next
;
1462 lm
->lm_chain
= NULL
;
1464 rc
= lm
->lm_msgtype
;
1470 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESP_LOCK
);