1 /* result.c - wait for an ldap result */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 1998-2007 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
16 /* Portions Copyright (c) 1990 Regents of the University of Michigan.
17 * All rights reserved.
19 /* This notice applies to changes, created by or for Novell, Inc.,
20 * to preexisting works for which notices appear elsewhere in this file.
22 * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
24 * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
25 * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
26 * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
27 * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
28 * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
29 * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
30 * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
31 * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
33 * Modification to OpenLDAP source by Novell, Inc.
34 * April 2000 sfs Add code to process V3 referrals and search results
36 * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
37 * can be found in the file "build/LICENSE-2.0.1" in this distribution
38 * of OpenLDAP Software.
43 * LDAPResult ::= SEQUENCE {
44 * resultCode ENUMERATED { ... },
46 * diagnosticMessage LDAPString,
47 * referral [3] Referral OPTIONAL
49 * Referral ::= SEQUENCE OF LDAPURL (one or more)
50 * LDAPURL ::= LDAPString (limited to URL chars)
57 #include <ac/stdlib.h>
60 #include <ac/socket.h>
61 #include <ac/string.h>
63 #include <ac/unistd.h>
69 static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid, int *idx ));
70 static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid, int idx ));
71 static int wait4msg LDAP_P(( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout,
72 LDAPMessage **result ));
73 static ber_tag_t try_read1msg LDAP_P(( LDAP *ld, ber_int_t msgid,
74 int all, LDAPConn **lc, LDAPMessage **result ));
75 static ber_tag_t build_result_ber LDAP_P(( LDAP *ld, BerElement **bp, LDAPRequest *lr ));
76 static void merge_error_info LDAP_P(( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ));
77 static LDAPMessage * chkResponseList LDAP_P(( LDAP *ld, int msgid, int all));
79 #define LDAP_MSG_X_KEEP_LOOKING (-2)
83 * ldap_result - wait for an ldap result response to a message from the
84 * ldap server. If msgid is LDAP_RES_ANY (-1), any message will be
85 * accepted. If msgid is LDAP_RES_UNSOLICITED (0), any unsolicited
86 * message is accepted. Otherwise ldap_result will wait for a response
87 * with msgid. If all is LDAP_MSG_ONE (0) the first message with id
88 * msgid will be accepted, otherwise, ldap_result will wait for all
89 * responses with id msgid and then return a pointer to the entire list
90 * of messages. In general, this is only useful for search responses,
91 * which can be of three message types (zero or more entries, zero or
92 * search references, followed by an ldap result). An extension to
93 * LDAPv3 allows partial extended responses to be returned in response
94 * to any request. The type of the first message received is returned.
95 * When waiting, any messages that have been abandoned/discarded are
99 * ldap_result( s, msgid, all, timeout, result )
106 struct timeval *timeout,
107 LDAPMessage **result )
109 LDAPMessage *lm = NULL;
112 assert( ld != NULL );
113 assert( result != NULL );
115 Debug( LDAP_DEBUG_TRACE, "ldap_result ld %p msgid %d\n", (void *)ld, msgid, 0 );
117 #ifdef LDAP_R_COMPILE
118 ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
122 /* this is already done inside wait4msg(), right?... */
123 lm = chkResponseList( ld, msgid, all );
127 rc = wait4msg( ld, msgid, all, timeout, result );
131 ld->ld_errno = LDAP_SUCCESS;
135 #ifdef LDAP_R_COMPILE
136 ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
148 LDAPMessage *lm, **lastlm, *nextlm;
152 * Look through the list of responses we have received on
153 * this association and see if the response we're interested in
154 * is there. If it is, return it. If not, call wait4msg() to
155 * wait until it arrives or timeout occurs.
158 #ifdef LDAP_R_COMPILE
159 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
162 Debug( LDAP_DEBUG_TRACE,
163 "ldap_chkResponseList ld %p msgid %d all %d\n",
164 (void *)ld, msgid, all );
166 lastlm = &ld->ld_responses;
167 for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
170 nextlm = lm->lm_next;
173 if ( ldap_abandoned( ld, lm->lm_msgid, &idx ) ) {
174 Debug( LDAP_DEBUG_ANY,
175 "response list msg abandoned, "
176 "msgid %d message type %s\n",
177 lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ), 0 );
179 switch ( lm->lm_msgtype ) {
180 case LDAP_RES_SEARCH_ENTRY:
181 case LDAP_RES_SEARCH_REFERENCE:
182 case LDAP_RES_INTERMEDIATE:
186 /* there's no need to keep the id
187 * in the abandoned list any longer */
188 ldap_mark_abandoned( ld, lm->lm_msgid, idx );
192 /* Remove this entry from list */
200 if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
203 if ( all == LDAP_MSG_ONE ||
204 all == LDAP_MSG_RECEIVED ||
205 msgid == LDAP_RES_UNSOLICITED )
210 tmp = lm->lm_chain_tail;
211 if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY ||
212 tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ||
213 tmp->lm_msgtype == LDAP_RES_INTERMEDIATE )
224 lastlm = &lm->lm_next;
228 /* Found an entry, remove it from the list */
229 if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) {
230 *lastlm = lm->lm_chain;
231 lm->lm_chain->lm_next = lm->lm_next;
232 lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain;
234 lm->lm_chain_tail = NULL;
236 *lastlm = lm->lm_next;
243 Debug( LDAP_DEBUG_TRACE,
244 "ldap_chkResponseList returns ld %p NULL\n", (void *)ld, 0, 0);
246 Debug( LDAP_DEBUG_TRACE,
247 "ldap_chkResponseList returns ld %p msgid %d, type 0x%02lu\n",
248 (void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
260 struct timeval *timeout,
261 LDAPMessage **result )
264 struct timeval tv = { 0 },
267 time_t start_time = 0;
271 assert( ld != NULL );
272 assert( result != NULL );
274 #ifdef LDAP_R_COMPILE
275 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
279 if ( timeout == NULL ) {
280 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
281 (void *)ld, msgid, 0 );
283 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n",
284 (void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec );
286 #endif /* LDAP_DEBUG */
288 if ( timeout == NULL ) {
294 start_time = time( NULL );
297 rc = LDAP_MSG_X_KEEP_LOOKING;
298 while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
300 if ( ldap_debug & LDAP_DEBUG_TRACE ) {
301 Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
302 (void *)ld, msgid, all );
303 #ifdef LDAP_R_COMPILE
304 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
306 ldap_dump_connection( ld, ld->ld_conns, 1 );
307 #ifdef LDAP_R_COMPILE
308 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
309 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
311 ldap_dump_requests_and_responses( ld );
312 #ifdef LDAP_R_COMPILE
313 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
316 #endif /* LDAP_DEBUG */
318 if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
319 rc = (*result)->lm_msgtype;
324 #ifdef LDAP_R_COMPILE
325 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
327 for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
328 if ( ber_sockbuf_ctrl( lc->lconn_sb,
329 LBER_SB_OPT_DATA_READY, NULL ) )
331 #ifdef LDAP_R_COMPILE
332 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
334 rc = try_read1msg( ld, msgid, all, &lc, result );
335 #ifdef LDAP_R_COMPILE
336 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
342 #ifdef LDAP_R_COMPILE
343 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
347 rc = ldap_int_select( ld, tvp );
350 Debug( LDAP_DEBUG_TRACE,
351 "ldap_int_select returned -1: errno %d\n",
352 sock_errno(), 0, 0 );
356 if ( rc == 0 || ( rc == -1 && (
357 !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
358 || sock_errno() != EINTR ) ) )
360 ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
366 rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */
369 rc = LDAP_MSG_X_KEEP_LOOKING;
370 #ifdef LDAP_R_COMPILE
371 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
373 if ( ld->ld_requests &&
374 ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
375 ldap_is_write_ready( ld,
376 ld->ld_requests->lr_conn->lconn_sb ) )
378 ldap_int_flush_request( ld, ld->ld_requests );
380 #ifdef LDAP_R_COMPILE
381 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
382 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
384 for ( lc = ld->ld_conns;
385 rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL; )
387 if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
388 ldap_is_read_ready( ld, lc->lconn_sb ) )
390 #ifdef LDAP_R_COMPILE
391 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
393 rc = try_read1msg( ld, msgid, all, &lc, result );
394 #ifdef LDAP_R_COMPILE
395 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
398 /* if lc gets free()'d,
399 * there's no guarantee
400 * lc->lconn_next is still
401 * sane; better restart
405 /* don't get to next conn! */
413 #ifdef LDAP_R_COMPILE
414 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
420 if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
421 tmp_time = time( NULL );
422 tv0.tv_sec -= ( tmp_time - start_time );
423 if ( tv0.tv_sec <= 0 ) {
424 rc = 0; /* timed out */
425 ld->ld_errno = LDAP_TIMEOUT;
428 tv.tv_sec = tv0.tv_sec;
430 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld secs to go\n",
431 (void *)ld, (long) tv.tv_sec, 0 );
432 start_time = tmp_time;
446 LDAPMessage **result )
449 LDAPMessage *newmsg, *l, *prev;
455 LDAPRequest *lr, *tmplr, dummy_lr = { 0 };
458 int rc, refer_cnt, hadref, simple_request;
461 #ifdef LDAP_CONNECTIONLESS
462 LDAPMessage *tmp = NULL, *chain_head = NULL;
463 int moremsgs = 0, isv2 = 0;
466 assert( ld != NULL );
467 assert( lcp != NULL );
468 assert( *lcp != NULL );
470 #ifdef LDAP_R_COMPILE
471 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
474 Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
475 (void *)ld, msgid, all );
480 if ( lc->lconn_ber == NULL ) {
481 lc->lconn_ber = ldap_alloc_ber_with_options( ld );
483 if ( lc->lconn_ber == NULL ) {
489 assert( LBER_VALID (ber) );
491 /* get the next message */
493 #ifdef LDAP_CONNECTIONLESS
494 if ( LDAP_IS_UDP(ld) ) {
495 struct sockaddr from;
496 ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr) );
497 if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
501 tag = ber_get_next( lc->lconn_sb, &len, ber );
503 case LDAP_TAG_MESSAGE:
505 * We read a complete message.
506 * The connection should no longer need this ber.
508 lc->lconn_ber = NULL;
513 Debug( LDAP_DEBUG_CONNS,
514 "ber_get_next failed.\n", 0, 0, 0 );
517 if ( sock_errno() == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
520 if ( sock_errno() == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
522 ld->ld_errno = LDAP_SERVER_DOWN;
526 ld->ld_errno = LDAP_LOCAL_ERROR;
531 if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
533 ld->ld_errno = LDAP_DECODING_ERROR;
537 /* id == 0 iff unsolicited notification message (RFC 4511) */
539 /* if it's been abandoned, toss it */
541 if ( ldap_abandoned( ld, id, &idx ) ) {
542 /* the message type */
543 tag = ber_peek_tag( ber, &len );
545 case LDAP_RES_SEARCH_ENTRY:
546 case LDAP_RES_SEARCH_REFERENCE:
547 case LDAP_RES_INTERMEDIATE:
552 /* there's no need to keep the id
553 * in the abandoned list any longer */
554 ldap_mark_abandoned( ld, id, idx );
558 Debug( LDAP_DEBUG_ANY,
559 "abandoned/discarded ld %p msgid %ld message type %s\n",
560 (void *)ld, (long)id, ldap_int_msgtype2str( tag ) );
564 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
567 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
570 lr = ldap_find_request_by_msgid( ld, id );
572 const char *msg = "unknown";
574 /* the message type */
575 tag = ber_peek_tag( ber, &len );
581 msg = ldap_int_msgtype2str( tag );
585 Debug( LDAP_DEBUG_ANY,
586 "no request for response on ld %p msgid %ld message type %s (tossing)\n",
587 (void *)ld, (long)id, msg );
592 #ifdef LDAP_CONNECTIONLESS
593 if ( LDAP_IS_UDP(ld) && isv2 ) {
594 ber_scanf(ber, "x{");
600 /* the message type */
601 tag = ber_peek_tag( ber, &len );
602 if ( tag == LBER_ERROR ) {
603 ld->ld_errno = LDAP_DECODING_ERROR;
608 Debug( LDAP_DEBUG_TRACE,
609 "read1msg: ld %p msgid %ld message type %s\n",
610 (void *)ld, (long)lr->lr_msgid, ldap_int_msgtype2str( tag ) );
613 /* unsolicited notification message (RFC 4511) */
614 if ( tag != LDAP_RES_EXTENDED ) {
618 /* strictly speaking, it's an error; from RFC 4511:
620 4.4. Unsolicited Notification
622 An unsolicited notification is an LDAPMessage sent from the server to
623 the client that is not in response to any LDAPMessage received by the
624 server. It is used to signal an extraordinary condition in the
625 server or in the LDAP session between the client and the server. The
626 notification is of an advisory nature, and the server will not expect
627 any response to be returned from the client.
629 The unsolicited notification is structured as an LDAPMessage in which
630 the messageID is zero and protocolOp is set to the extendedResp
631 choice using the ExtendedResponse type (See Section 4.12). The
632 responseName field of the ExtendedResponse always contains an LDAPOID
633 that is unique for this notification.
635 * however, since unsolicited responses
636 * are of advisory nature, better
641 ld->ld_errno = LDAP_DECODING_ERROR;
652 hadref = simple_request = 0;
653 rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */
654 lr->lr_res_msgtype = tag;
657 * Check for V3 search reference
659 if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
660 if ( ld->ld_version > LDAP_VERSION2 ) {
661 /* This is a V3 search reference */
662 if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
663 lr->lr_parent != NULL )
668 /* Get the referral list */
669 if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
670 rc = LDAP_DECODING_ERROR;
673 /* Note: refs array is freed by ldap_chase_v3referrals */
674 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
675 1, &lr->lr_res_error, &hadref );
676 if ( refer_cnt > 0 ) {
677 /* successfully chased reference */
678 /* If haven't got end search, set chasing referrals */
679 if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
680 lr->lr_status = LDAP_REQST_CHASINGREFS;
681 Debug( LDAP_DEBUG_TRACE,
682 "read1msg: search ref chased, "
683 "mark request chasing refs, "
685 lr->lr_msgid, 0, 0 );
692 } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
693 /* All results that just return a status, i.e. don't return data
694 * go through the following code. This code also chases V2 referrals
695 * and checks if all referrals have been chased.
697 char *lr_res_error = NULL;
699 tmpber = *ber; /* struct copy */
700 if ( ber_scanf( &tmpber, "{eAA", &lderr,
701 &lr->lr_res_matched, &lr_res_error )
704 if ( lr_res_error != NULL ) {
705 if ( lr->lr_res_error != NULL ) {
706 (void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
707 LDAP_FREE( (char *)lr_res_error );
710 lr->lr_res_error = lr_res_error;
715 /* Do we need to check for referrals? */
716 if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
717 lr->lr_parent != NULL )
722 /* Check if V3 referral */
723 if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
724 if ( ld->ld_version > LDAP_VERSION2 ) {
725 /* Get the referral list */
726 if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
727 rc = LDAP_DECODING_ERROR;
728 lr->lr_status = LDAP_REQST_COMPLETED;
729 Debug( LDAP_DEBUG_TRACE,
730 "read1msg: referral decode error, "
731 "mark request completed, ld %p msgid %d\n",
732 (void *)ld, lr->lr_msgid, 0 );
735 /* Chase the referral
736 * refs array is freed by ldap_chase_v3referrals
738 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
739 0, &lr->lr_res_error, &hadref );
740 lr->lr_status = LDAP_REQST_COMPLETED;
741 Debug( LDAP_DEBUG_TRACE,
742 "read1msg: referral %s chased, "
743 "mark request completed, ld %p msgid %d\n",
744 refer_cnt > 0 ? "" : "not",
745 (void *)ld, lr->lr_msgid);
746 if ( refer_cnt < 0 ) {
754 case LDAP_COMPARE_TRUE:
755 case LDAP_COMPARE_FALSE:
759 if ( lr->lr_res_error == NULL ) {
763 /* pedantic, should never happen */
764 if ( lr->lr_res_error[ 0 ] == '\0' ) {
765 LDAP_FREE( lr->lr_res_error );
766 lr->lr_res_error = NULL;
770 /* V2 referrals are in error string */
771 refer_cnt = ldap_chase_referrals( ld, lr,
772 &lr->lr_res_error, -1, &hadref );
773 lr->lr_status = LDAP_REQST_COMPLETED;
774 Debug( LDAP_DEBUG_TRACE,
775 "read1msg: V2 referral chased, "
776 "mark request completed, id = %d\n",
777 lr->lr_msgid, 0, 0 );
783 /* save errno, message, and matched string */
784 if ( !hadref || lr->lr_res_error == NULL ) {
786 lderr == LDAP_PARTIAL_RESULTS
787 ? LDAP_SUCCESS : lderr;
789 } else if ( ld->ld_errno != LDAP_SUCCESS ) {
790 lr->lr_res_errno = ld->ld_errno;
793 lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
797 /* in any case, don't leave any lr_res_error 'round */
798 if ( lr_res_error ) {
799 LDAP_FREE( lr_res_error );
802 Debug( LDAP_DEBUG_TRACE,
803 "read1msg: ld %p %d new referrals\n",
804 (void *)ld, refer_cnt, 0 );
806 if ( refer_cnt != 0 ) { /* chasing referrals */
809 if ( refer_cnt < 0 ) {
810 ldap_return_request( ld, lr, 0 );
811 return( -1 ); /* fatal error */
813 lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
816 if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
817 /* request without any referrals */
818 simple_request = ( hadref ? 0 : 1 );
821 /* request with referrals or child request */
826 lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
827 Debug( LDAP_DEBUG_TRACE,
828 "read1msg: mark request completed, ld %p msgid %d\n",
829 (void *)ld, lr->lr_msgid, 0);
830 while ( lr->lr_parent != NULL ) {
831 merge_error_info( ld, lr->lr_parent, lr );
834 if ( --lr->lr_outrefcnt > 0 ) {
835 break; /* not completely done yet */
839 /* Check if all requests are finished, lr is now parent */
841 if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
842 for ( tmplr = lr->lr_child;
844 tmplr = tmplr->lr_refnext )
846 if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
850 /* This is the parent request if the request has referrals */
851 if ( lr->lr_outrefcnt <= 0 &&
852 lr->lr_parent == NULL &&
856 tag = lr->lr_res_msgtype;
857 Debug( LDAP_DEBUG_ANY, "request done: ld %p msgid %ld\n",
858 (void *)ld, (long) id, 0 );
859 Debug( LDAP_DEBUG_TRACE,
860 "res_errno: %d, res_error: <%s>, "
861 "res_matched: <%s>\n",
863 lr->lr_res_error ? lr->lr_res_error : "",
864 lr->lr_res_matched ? lr->lr_res_matched : "" );
865 if ( !simple_request ) {
868 if ( build_result_ber( ld, &ber, lr )
871 rc = -1; /* fatal error */
875 if ( lr != &dummy_lr ) {
876 ldap_return_request( ld, lr, 1 );
882 * RF 4511 unsolicited (id == 0) responses
883 * shouldn't necessarily end the connection
885 if ( lc != NULL && id != 0 ) {
886 #ifdef LDAP_R_COMPILE
887 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
889 ldap_free_connection( ld, lc, 0, 1 );
890 #ifdef LDAP_R_COMPILE
891 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
899 if ( lr != &dummy_lr ) {
900 ldap_return_request( ld, lr, 0 );
909 /* try to handle unsolicited responses as appropriate */
910 if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
913 tag = ber_peek_tag( &tmpber, &len );
915 /* we have a res oid */
916 if ( tag == LDAP_TAG_EXOP_RES_OID ) {
917 static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
918 struct berval resoid = BER_BVNULL;
920 if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
921 ld->ld_errno = LDAP_DECODING_ERROR;
926 assert( !BER_BVISEMPTY( &resoid ) );
928 is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
930 tag = ber_peek_tag( &tmpber, &len );
933 #if 0 /* don't need right now */
934 /* we have res data */
935 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
936 struct berval resdata;
938 if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
939 ld->ld_errno = LDAP_DECODING_ERROR;
948 /* handle RFC 4511 "Notice of Disconnection" locally */
951 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
952 ld->ld_errno = LDAP_DECODING_ERROR;
957 /* get rid of the connection... */
959 #ifdef LDAP_R_COMPILE
960 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
962 ldap_free_connection( ld, lc, 0, 1 );
963 #ifdef LDAP_R_COMPILE
964 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
969 /* need to return -1, because otherwise
970 * a valid result is expected */
975 /* make a new ldap message */
976 newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
977 if ( newmsg == NULL ) {
978 ld->ld_errno = LDAP_NO_MEMORY;
981 newmsg->lm_msgid = (int)id;
982 newmsg->lm_msgtype = tag;
983 newmsg->lm_ber = ber;
984 newmsg->lm_chain_tail = newmsg;
986 #ifdef LDAP_CONNECTIONLESS
987 /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
988 * the responses are all a sequence wrapped in one message. In
989 * LDAPv3 each response is in its own message. The datagram must
990 * end with a SearchResult. We can't just parse each response in
991 * separate calls to try_read1msg because the header info is only
992 * present at the beginning of the datagram, not at the beginning
993 * of each response. So parse all the responses at once and queue
994 * them up, then pull off the first response to return to the
995 * caller when all parsing is complete.
997 if ( LDAP_IS_UDP(ld) ) {
998 /* If not a result, look for more */
999 if ( tag != LDAP_RES_SEARCH_RESULT ) {
1003 /* LDAPv2: dup the current ber, skip past the current
1004 * response, and see if there are any more after it.
1006 ber = ber_dup( ber );
1007 ber_scanf( ber, "x" );
1008 if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
1009 /* There's more - dup the ber buffer so they can all be
1010 * individually freed by ldap_msgfree.
1013 ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1014 bv.bv_val = LDAP_MALLOC( len );
1017 ber_read( ber, bv.bv_val, len );
1019 ber_init2( ber, &bv, ld->ld_lberoptions );
1023 /* LDAPv3: Just allocate a new ber. Since this is a buffered
1024 * datagram, if the sockbuf is readable we still have data
1027 ber = ldap_alloc_ber_with_options( ld );
1028 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1030 /* set up response chain */
1031 if ( tmp == NULL ) {
1032 newmsg->lm_next = ld->ld_responses;
1033 ld->ld_responses = newmsg;
1034 chain_head = newmsg;
1036 tmp->lm_chain = newmsg;
1038 chain_head->lm_chain_tail = newmsg;
1040 /* "ok" means there's more to parse */
1049 /* got to end of datagram without a SearchResult. Free
1050 * our dup'd ber, but leave any buffer alone. For v2 case,
1051 * the previous response is still using this buffer. For v3,
1052 * the new ber has no buffer to free yet.
1057 } else if ( moremsgs ) {
1058 /* got search result, and we had multiple responses in 1 datagram.
1059 * stick the result onto the end of the chain, and then pull the
1060 * first response off the head of the chain.
1062 tmp->lm_chain = newmsg;
1063 chain_head->lm_chain_tail = newmsg;
1064 *result = chkResponseList( ld, msgid, all );
1065 ld->ld_errno = LDAP_SUCCESS;
1066 return( (*result)->lm_msgtype );
1069 #endif /* LDAP_CONNECTIONLESS */
1071 /* is this the one we're looking for? */
1072 if ( msgid == LDAP_RES_ANY || id == msgid ) {
1073 if ( all == LDAP_MSG_ONE
1074 || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1075 && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1076 && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1079 ld->ld_errno = LDAP_SUCCESS;
1082 } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1083 foundit = 1; /* return the chain later */
1088 * if not, we must add it to the list of responses. if
1089 * the msgid is already there, it must be part of an existing
1094 for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1095 if ( l->lm_msgid == newmsg->lm_msgid ) {
1101 /* not part of an existing search response */
1108 newmsg->lm_next = ld->ld_responses;
1109 ld->ld_responses = newmsg;
1113 Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %ld type %ld:\n",
1114 (void *)ld, (long) newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1116 /* part of a search response - add to end of list of entries */
1117 l->lm_chain_tail->lm_chain = newmsg;
1118 l->lm_chain_tail = newmsg;
1120 /* return the whole chain if that's what we were looking for */
1122 if ( prev == NULL ) {
1123 ld->ld_responses = l->lm_next;
1125 prev->lm_next = l->lm_next;
1132 ld->ld_errno = LDAP_SUCCESS;
1135 if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1138 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
1143 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1151 ber = ldap_alloc_ber_with_options( ld );
1154 ld->ld_errno = LDAP_NO_MEMORY;
1158 if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1159 lr->lr_res_msgtype, lr->lr_res_errno,
1160 lr->lr_res_matched ? lr->lr_res_matched : "",
1161 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1163 ld->ld_errno = LDAP_ENCODING_ERROR;
1165 return( LBER_ERROR );
1168 ber_reset( ber, 1 );
1170 if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1171 ld->ld_errno = LDAP_DECODING_ERROR;
1173 return( LBER_ERROR );
1176 if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1177 ld->ld_errno = LDAP_DECODING_ERROR;
1179 return( LBER_ERROR );
1182 tag = ber_peek_tag( ber, &len );
1184 if ( tag == LBER_ERROR ) {
1185 ld->ld_errno = LDAP_DECODING_ERROR;
1187 return( LBER_ERROR );
1196 * Merge error information in "lr" with "parentr" error code and string.
1199 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1201 if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1202 parentr->lr_res_errno = lr->lr_res_errno;
1203 if ( lr->lr_res_error != NULL ) {
1204 (void)ldap_append_referral( ld, &parentr->lr_res_error,
1208 } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1209 parentr->lr_res_errno == LDAP_SUCCESS )
1211 parentr->lr_res_errno = lr->lr_res_errno;
1212 if ( parentr->lr_res_error != NULL ) {
1213 LDAP_FREE( parentr->lr_res_error );
1215 parentr->lr_res_error = lr->lr_res_error;
1216 lr->lr_res_error = NULL;
1217 if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1218 if ( parentr->lr_res_matched != NULL ) {
1219 LDAP_FREE( parentr->lr_res_matched );
1221 parentr->lr_res_matched = lr->lr_res_matched;
1222 lr->lr_res_matched = NULL;
1226 Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
1227 parentr->lr_msgid, 0, 0 );
1228 Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1229 parentr->lr_res_errno,
1230 parentr->lr_res_error ? parentr->lr_res_error : "",
1231 parentr->lr_res_matched ? parentr->lr_res_matched : "" );
1237 ldap_msgtype( LDAPMessage *lm )
1239 assert( lm != NULL );
1240 return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1245 ldap_msgid( LDAPMessage *lm )
1247 assert( lm != NULL );
1249 return ( lm != NULL ) ? lm->lm_msgid : -1;
1254 ldap_int_msgtype2str( ber_tag_t tag )
1257 case LDAP_RES_ADD: return "add";
1258 case LDAP_RES_BIND: return "bind";
1259 case LDAP_RES_COMPARE: return "compare";
1260 case LDAP_RES_DELETE: return "delete";
1261 case LDAP_RES_EXTENDED: return "extended-result";
1262 case LDAP_RES_INTERMEDIATE: return "intermediate";
1263 case LDAP_RES_MODIFY: return "modify";
1264 case LDAP_RES_RENAME: return "rename";
1265 case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1266 case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1267 case LDAP_RES_SEARCH_RESULT: return "search-result";
1273 ldap_msgfree( LDAPMessage *lm )
1278 Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1280 for ( ; lm != NULL; lm = next ) {
1281 next = lm->lm_chain;
1282 type = lm->lm_msgtype;
1283 ber_free( lm->lm_ber, 1 );
1284 LDAP_FREE( (char *) lm );
1291 * ldap_msgdelete - delete a message. It returns:
1292 * 0 if the entire message was deleted
1293 * -1 if the message was not found, or only part of it was found
1296 ldap_msgdelete( LDAP *ld, int msgid )
1298 LDAPMessage *lm, *prev;
1301 assert( ld != NULL );
1303 Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1304 (void *)ld, msgid, 0 );
1306 #ifdef LDAP_R_COMPILE
1307 ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
1310 for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1311 if ( lm->lm_msgid == msgid ) {
1321 if ( prev == NULL ) {
1322 ld->ld_responses = lm->lm_next;
1324 prev->lm_next = lm->lm_next;
1327 #ifdef LDAP_R_COMPILE
1328 ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
1331 switch ( ldap_msgfree( lm ) ) {
1332 case LDAP_RES_SEARCH_ENTRY:
1333 case LDAP_RES_SEARCH_REFERENCE:
1334 case LDAP_RES_INTERMEDIATE:
1350 * return the location of the message id in the array of abandoned
1351 * message ids, or -1
1353 * expects ld_res_mutex to be locked
1356 ldap_abandoned( LDAP *ld, ber_int_t msgid, int *idxp )
1358 #ifdef LDAP_R_COMPILE
1359 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1362 assert( idxp != NULL );
1363 assert( msgid >= 0 );
1364 assert( ld->ld_nabandoned >= 0 );
1366 return ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp );
1370 * ldap_mark_abandoned
1372 * expects ld_res_mutex to be locked
1375 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid, int idx )
1377 #ifdef LDAP_R_COMPILE
1378 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1381 /* NOTE: those assertions are repeated in ldap_int_bisect_delete() */
1383 assert( idx < ld->ld_nabandoned );
1384 assert( ld->ld_abandoned[ idx ] == msgid );
1386 return ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,