3 * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
4 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
7 * Copyright (c) 1990 Regents of the University of Michigan.
10 * result.c - wait for an ldap result
15 * LDAPResult ::= SEQUENCE {
16 * resultCode ENUMERATED { ... },
18 * errorMessage LDAPString,
19 * referral Referral OPTIONAL
21 * Referral ::= SEQUENCE OF LDAPURL (one or more)
22 * LDAPURL ::= LDAPString (limited to URL chars)
29 #include <ac/stdlib.h>
32 #include <ac/socket.h>
33 #include <ac/string.h>
35 #include <ac/unistd.h>
40 static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
41 static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
42 static int wait4msg LDAP_P(( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout,
43 LDAPMessage **result ));
44 static ber_tag_t try_read1msg LDAP_P(( LDAP *ld, ber_int_t msgid,
45 int all, Sockbuf *sb, LDAPConn *lc, LDAPMessage **result ));
46 static ber_tag_t build_result_ber LDAP_P(( LDAP *ld, BerElement **bp, LDAPRequest *lr ));
47 static void merge_error_info LDAP_P(( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ));
51 * ldap_result - wait for an ldap result response to a message from the
52 * ldap server. If msgid is -1, any message will be accepted, otherwise
53 * ldap_result will wait for a response with msgid. If all is 0 the
54 * first message with id msgid will be accepted, otherwise, ldap_result
55 * will wait for all responses with id msgid and then return a pointer to
56 * the entire list of messages. This is only useful for search responses,
57 * which can be of two message types (zero or more entries, followed by an
58 * ldap result). The type of the first message received is returned.
59 * When waiting, any messages that have been abandoned are discarded.
62 * ldap_result( s, msgid, all, timeout, result )
69 struct timeval *timeout,
70 LDAPMessage **result )
72 LDAPMessage *lm, *lastlm, *nextlm;
75 assert( result != NULL );
77 Debug( LDAP_DEBUG_TRACE, "ldap_result\n", 0, 0, 0 );
83 if( result == NULL ) {
84 ld->ld_errno = LDAP_PARAM_ERROR;
89 * First, look through the list of responses we have received on
90 * this association and see if the response we're interested in
91 * is there. If it is, return it. If not, call wait4msg() to
92 * wait until it arrives or timeout occurs.
97 for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
100 if ( ldap_abandoned( ld, lm->lm_msgid ) ) {
101 ldap_mark_abandoned( ld, lm->lm_msgid );
103 if ( lastlm == NULL ) {
104 ld->ld_responses = lm->lm_next;
106 lastlm->lm_next = nextlm;
114 if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
118 || (lm->lm_msgtype != LDAP_RES_SEARCH_RESULT
119 && lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE /* LDAPv3 */
120 && lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY) )
123 for ( tmp = lm; tmp != NULL; tmp = tmp->lm_chain ) {
124 if ( tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT )
137 return( wait4msg( ld, msgid, all, timeout, result ) );
140 if ( lastlm == NULL ) {
141 ld->ld_responses = (all == 0 && lm->lm_chain != NULL
142 ? lm->lm_chain : lm->lm_next);
144 lastlm->lm_next = (all == 0 && lm->lm_chain != NULL
145 ? lm->lm_chain : lm->lm_next);
147 if ( all == 0 && lm->lm_chain != NULL )
149 lm->lm_chain->lm_next = lm->lm_next;
155 ld->ld_errno = LDAP_SUCCESS;
156 return( lm->lm_msgtype );
164 struct timeval *timeout,
165 LDAPMessage **result )
168 struct timeval tv, *tvp;
169 time_t start_time = 0;
171 LDAPConn *lc, *nextlc;
173 assert( ld != NULL );
174 assert( result != NULL );
177 if ( timeout == NULL ) {
178 Debug( LDAP_DEBUG_TRACE, "wait4msg (infinite timeout)\n",
181 Debug( LDAP_DEBUG_TRACE, "wait4msg (timeout %ld sec, %ld usec)\n",
182 (long) timeout->tv_sec, (long) timeout->tv_usec, 0 );
184 #endif /* LDAP_DEBUG */
186 if ( timeout == NULL ) {
191 start_time = time( NULL );
197 if ( ldap_debug & LDAP_DEBUG_TRACE ) {
198 ldap_dump_connection( ld, ld->ld_conns, 1 );
199 ldap_dump_requests_and_responses( ld );
201 #endif /* LDAP_DEBUG */
202 for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
203 if ( ber_pvt_sb_data_ready(lc->lconn_sb) ) {
204 rc = try_read1msg( ld, msgid, all, lc->lconn_sb,
211 rc = do_ldap_select( ld, tvp );
216 Debug( LDAP_DEBUG_TRACE,
217 "do_ldap_select returned -1: errno %d\n",
222 if ( rc == 0 || ( rc == -1 && (
223 !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
224 || errno != EINTR )))
226 ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
232 rc = -2; /* select interrupted: loop */
235 for ( lc = ld->ld_conns; rc == -2 && lc != NULL;
237 nextlc = lc->lconn_next;
238 if ( lc->lconn_status ==
239 LDAP_CONNST_CONNECTED &&
240 ldap_is_read_ready( ld,
242 rc = try_read1msg( ld, msgid, all,
243 lc->lconn_sb, lc, result );
249 if ( rc == -2 && tvp != NULL ) {
250 tmp_time = time( NULL );
251 if (( tv.tv_sec -= ( tmp_time - start_time )) <= 0 ) {
252 rc = 0; /* timed out */
253 ld->ld_errno = LDAP_TIMEOUT;
257 Debug( LDAP_DEBUG_TRACE, "wait4msg: %ld secs to go\n",
258 (long) tv.tv_sec, 0, 0 );
259 start_time = tmp_time;
274 LDAPMessage **result )
277 LDAPMessage *new, *l, *prev, *tmp;
284 int rc, refer_cnt, hadref, simple_request;
287 assert( ld != NULL );
288 assert( lc != NULL );
290 Debug( LDAP_DEBUG_TRACE, "read1msg\n", 0, 0, 0 );
292 if ( lc->lconn_ber == NULL ) {
293 lc->lconn_ber = ldap_alloc_ber_with_options(ld);
295 if( lc->lconn_ber == NULL ) {
301 assert( BER_VALID (ber) );
303 /* get the next message */
305 if ( (tag = ber_get_next( sb, &len, ber ))
306 != LDAP_TAG_MESSAGE ) {
307 if ( tag == LBER_DEFAULT) {
309 Debug( LDAP_DEBUG_CONNS,
310 "ber_get_next failed.\n", 0, 0, 0 );
313 if (errno==EWOULDBLOCK) return -2;
316 if (errno == EAGAIN) return -2;
318 ld->ld_errno = LDAP_SERVER_DOWN;
321 ld->ld_errno = LDAP_LOCAL_ERROR;
326 * We read a complete message.
327 * The connection should no longer need this ber.
329 lc->lconn_ber = NULL;
332 if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
334 ld->ld_errno = LDAP_DECODING_ERROR;
338 /* if it's been abandoned, toss it */
339 if ( ldap_abandoned( ld, id ) ) {
341 return( -2 ); /* continue looking */
344 if (( lr = ldap_find_request_by_msgid( ld, id )) == NULL ) {
345 Debug( LDAP_DEBUG_ANY,
346 "no request for response with msgid %ld (tossing)\n",
349 return( -2 ); /* continue looking */
352 /* the message type */
353 if ( (tag = ber_peek_tag( ber, &len )) == LBER_ERROR ) {
354 ld->ld_errno = LDAP_DECODING_ERROR;
359 Debug( LDAP_DEBUG_TRACE, "ldap_read: %s msgid %ld, original id %ld\n",
360 ( tag == LDAP_RES_SEARCH_ENTRY ) ? "entry" :
361 ( tag == LDAP_RES_SEARCH_REFERENCE ) ? "reference" : "result",
362 (long) id, (long) lr->lr_origid );
366 hadref = simple_request = 0;
367 rc = -2; /* default is to keep looking (no response found) */
368 lr->lr_res_msgtype = tag;
370 if ( tag != LDAP_RES_SEARCH_ENTRY ) {
371 if ( ld->ld_version >= LDAP_VERSION2 &&
372 ( lr->lr_parent != NULL ||
373 LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ) )
375 tmpber = *ber; /* struct copy */
376 if ( ber_scanf( &tmpber, "{iaa}", &lderr,
377 &lr->lr_res_matched, &lr->lr_res_error )
379 if ( lderr != LDAP_SUCCESS ) {
380 /* referrals are in error string */
381 refer_cnt = ldap_chase_referrals( ld, lr,
382 &lr->lr_res_error, &hadref );
385 /* save errno, message, and matched string */
386 if ( !hadref || lr->lr_res_error == NULL ) {
387 lr->lr_res_errno = ( lderr ==
388 LDAP_PARTIAL_RESULTS ) ? LDAP_SUCCESS
390 } else if ( ld->ld_errno != LDAP_SUCCESS ) {
391 lr->lr_res_errno = ld->ld_errno;
393 lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
395 Debug( LDAP_DEBUG_TRACE,
396 "new result: res_errno: %d, res_error: <%s>, res_matched: <%s>\n",
397 lr->lr_res_errno, lr->lr_res_error ? lr->lr_res_error : "",
398 lr->lr_res_matched ? lr->lr_res_matched : "" );
402 Debug( LDAP_DEBUG_TRACE,
403 "read1msg: %d new referrals\n", refer_cnt, 0, 0 );
405 if ( refer_cnt != 0 ) { /* chasing referrals */
408 if ( refer_cnt < 0 ) {
409 return( -1 ); /* fatal error */
411 lr->lr_status = LDAP_REQST_CHASINGREFS;
413 if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
414 /* request without any referrals */
415 simple_request = ( hadref ? 0 : 1 );
417 /* request with referrals or child request */
422 while ( lr->lr_parent != NULL ) {
423 merge_error_info( ld, lr->lr_parent, lr );
426 if ( --lr->lr_outrefcnt > 0 ) {
427 break; /* not completely done yet */
431 if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
433 tag = lr->lr_res_msgtype;
434 Debug( LDAP_DEBUG_ANY, "request %ld done\n",
436 Debug( LDAP_DEBUG_TRACE,
437 "res_errno: %d, res_error: <%s>, res_matched: <%s>\n",
438 lr->lr_res_errno, lr->lr_res_error ? lr->lr_res_error : "",
439 lr->lr_res_matched ? lr->lr_res_matched : "" );
440 if ( !simple_request ) {
443 if ( build_result_ber( ld, &ber, lr )
445 rc = -1; /* fatal error */
449 ldap_free_request( ld, lr );
453 ldap_free_connection( ld, lc, 0, 1 );
462 /* make a new ldap message */
463 if ( (new = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) ))
465 ld->ld_errno = LDAP_NO_MEMORY;
468 new->lm_msgid = (int)id;
469 new->lm_msgtype = tag;
473 if ( ld->ld_cache != NULL ) {
474 ldap_add_result_to_cache( ld, new );
476 #endif /* LDAP_NOCACHE */
478 /* is this the one we're looking for? */
479 if ( msgid == LDAP_RES_ANY || id == msgid ) {
481 || (new->lm_msgtype != LDAP_RES_SEARCH_RESULT
482 && new->lm_msgtype != LDAP_RES_SEARCH_ENTRY) ) {
484 ld->ld_errno = LDAP_SUCCESS;
486 } else if ( new->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
487 foundit = 1; /* return the chain later */
492 * if not, we must add it to the list of responses. if
493 * the msgid is already there, it must be part of an existing
498 for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
499 if ( l->lm_msgid == new->lm_msgid )
504 /* not part of an existing search response */
508 ld->ld_errno = LDAP_SUCCESS;
512 new->lm_next = ld->ld_responses;
513 ld->ld_responses = new;
514 return( -2 ); /* continue looking */
517 Debug( LDAP_DEBUG_TRACE, "adding response id %ld type %ld:\n",
518 (long) new->lm_msgid, (long) new->lm_msgtype, 0 );
520 /* part of a search response - add to end of list of entries */
521 for ( tmp = l; tmp->lm_chain != NULL &&
522 tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY;
523 tmp = tmp->lm_chain )
527 /* return the whole chain if that's what we were looking for */
530 ld->ld_responses = l->lm_next;
532 prev->lm_next = l->lm_next;
534 ld->ld_errno = LDAP_SUCCESS;
535 #ifdef LDAP_WORLD_P16
537 * XXX questionable fix; see text for [P16] on
538 * http://www.critical-angle.com/ldapworld/patch/
540 * inclusion of this patch causes searchs to hang on
543 return( l->lm_msgtype );
544 #else /* LDAP_WORLD_P16 */
546 #endif /* !LDAP_WORLD_P16 */
549 return( -2 ); /* continue looking */
554 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
562 ber = ldap_alloc_ber_with_options( ld );
565 ld->ld_errno = LDAP_NO_MEMORY;
569 if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
570 lr->lr_res_msgtype, lr->lr_res_errno,
571 lr->lr_res_matched ? lr->lr_res_matched : "",
572 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 ) {
574 ld->ld_errno = LDAP_ENCODING_ERROR;
576 return( LBER_ERROR );
581 if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
582 ld->ld_errno = LDAP_DECODING_ERROR;
584 return( LBER_ERROR );
587 if ( ber_get_int( ber, &along ) == LBER_ERROR ) {
588 ld->ld_errno = LDAP_DECODING_ERROR;
590 return( LBER_ERROR );
593 tag = ber_peek_tag( ber, &len );
595 if ( tag == LBER_ERROR ) {
596 ld->ld_errno = LDAP_DECODING_ERROR;
598 return( LBER_ERROR );
607 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
610 * Merge error information in "lr" with "parentr" error code and string.
612 if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
613 parentr->lr_res_errno = lr->lr_res_errno;
614 if ( lr->lr_res_error != NULL ) {
615 (void)ldap_append_referral( ld, &parentr->lr_res_error,
618 } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
619 parentr->lr_res_errno == LDAP_SUCCESS ) {
620 parentr->lr_res_errno = lr->lr_res_errno;
621 if ( parentr->lr_res_error != NULL ) {
622 LDAP_FREE( parentr->lr_res_error );
624 parentr->lr_res_error = lr->lr_res_error;
625 lr->lr_res_error = NULL;
626 if ( LDAP_NAME_ERROR( lr->lr_res_errno )) {
627 if ( parentr->lr_res_matched != NULL ) {
628 LDAP_FREE( parentr->lr_res_matched );
630 parentr->lr_res_matched = lr->lr_res_matched;
631 lr->lr_res_matched = NULL;
635 Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
636 parentr->lr_msgid, 0, 0 );
637 Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
638 parentr->lr_res_errno, parentr->lr_res_error ?
639 parentr->lr_res_error : "", parentr->lr_res_matched ?
640 parentr->lr_res_matched : "" );
646 ldap_msgtype( LDAPMessage *lm )
648 assert( lm != NULL );
649 return ( lm != NULL ) ? lm->lm_msgtype : -1;
654 ldap_msgid( LDAPMessage *lm )
656 assert( lm != NULL );
658 return ( lm != NULL ) ? lm->lm_msgid : -1;
663 ldap_msgfree( LDAPMessage *lm )
668 assert( lm != NULL );
670 Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
672 for ( ; lm != NULL; lm = next ) {
674 type = lm->lm_msgtype;
675 ber_free( lm->lm_ber, 1 );
676 LDAP_FREE( (char *) lm );
683 * ldap_msgdelete - delete a message. It returns:
684 * 0 if the entire message was deleted
685 * -1 if the message was not found, or only part of it was found
688 ldap_msgdelete( LDAP *ld, int msgid )
690 LDAPMessage *lm, *prev;
692 assert( ld != NULL );
694 Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete\n", 0, 0, 0 );
697 for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
698 if ( lm->lm_msgid == msgid )
707 ld->ld_responses = lm->lm_next;
709 prev->lm_next = lm->lm_next;
711 if ( ldap_msgfree( lm ) == LDAP_RES_SEARCH_ENTRY )
719 * return 1 if message msgid is waiting to be abandoned, 0 otherwise
722 ldap_abandoned( LDAP *ld, ber_int_t msgid )
726 if ( ld->ld_abandoned == NULL )
729 for ( i = 0; ld->ld_abandoned[i] != -1; i++ )
730 if ( ld->ld_abandoned[i] == msgid )
738 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid )
742 if ( ld->ld_abandoned == NULL )
745 for ( i = 0; ld->ld_abandoned[i] != -1; i++ )
746 if ( ld->ld_abandoned[i] == msgid )
749 if ( ld->ld_abandoned[i] == -1 )
752 for ( ; ld->ld_abandoned[i] != -1; i++ ) {
753 ld->ld_abandoned[i] = ld->ld_abandoned[i + 1];
760 #ifdef LDAP_CONNECTIONLESS
762 cldap_getmsg( LDAP *ld, struct timeval *timeout, BerElement *ber )
768 if ( ! ber_pvt_sb_data_ready(&ld->ld_sb) ) {
769 /* restored from ldap_select1() in result.c version 1.24 */
771 if ( ldap_int_tblsize == 0 )
774 FD_SET( ber_pvt_sb_get_desc(&ld->ld_sb), &readfds );
775 rc = select( ldap_int_tblsize, &readfds, 0, 0, timeout );
777 if ( rc == -1 || rc == 0 ) {
778 ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
784 /* get the next message */
785 if ( (tag = ber_get_next( &ld->ld_sb, &len, ber ))
786 != LDAP_TAG_MESSAGE ) {
787 ld->ld_errno = (tag == LBER_DEFAULT ? LDAP_SERVER_DOWN :
794 #endif /* LDAP_CONNECTIONLESS */