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 );
278 if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) {
279 tv = ld->ld_options.ldo_tm_api;
284 if ( timeout == NULL ) {
285 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
286 (void *)ld, msgid, 0 );
288 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n",
289 (void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec );
291 #endif /* LDAP_DEBUG */
293 if ( timeout == NULL ) {
299 start_time = time( NULL );
302 rc = LDAP_MSG_X_KEEP_LOOKING;
303 while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
305 if ( ldap_debug & LDAP_DEBUG_TRACE ) {
306 Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
307 (void *)ld, msgid, all );
308 #ifdef LDAP_R_COMPILE
309 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
311 ldap_dump_connection( ld, ld->ld_conns, 1 );
312 #ifdef LDAP_R_COMPILE
313 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
314 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
316 ldap_dump_requests_and_responses( ld );
317 #ifdef LDAP_R_COMPILE
318 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
321 #endif /* LDAP_DEBUG */
323 if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
324 rc = (*result)->lm_msgtype;
329 #ifdef LDAP_R_COMPILE
330 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
332 for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
333 if ( ber_sockbuf_ctrl( lc->lconn_sb,
334 LBER_SB_OPT_DATA_READY, NULL ) )
336 #ifdef LDAP_R_COMPILE
337 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
339 rc = try_read1msg( ld, msgid, all, &lc, result );
340 #ifdef LDAP_R_COMPILE
341 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
347 #ifdef LDAP_R_COMPILE
348 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
352 rc = ldap_int_select( ld, tvp );
355 Debug( LDAP_DEBUG_TRACE,
356 "ldap_int_select returned -1: errno %d\n",
357 sock_errno(), 0, 0 );
361 if ( rc == 0 || ( rc == -1 && (
362 !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
363 || sock_errno() != EINTR ) ) )
365 ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
371 rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */
374 rc = LDAP_MSG_X_KEEP_LOOKING;
375 #ifdef LDAP_R_COMPILE
376 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
378 if ( ld->ld_requests &&
379 ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
380 ldap_is_write_ready( ld,
381 ld->ld_requests->lr_conn->lconn_sb ) )
383 ldap_int_flush_request( ld, ld->ld_requests );
385 #ifdef LDAP_R_COMPILE
386 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
387 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
389 for ( lc = ld->ld_conns;
390 rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL; )
392 if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
393 ldap_is_read_ready( ld, lc->lconn_sb ) )
395 #ifdef LDAP_R_COMPILE
396 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
398 rc = try_read1msg( ld, msgid, all, &lc, result );
399 #ifdef LDAP_R_COMPILE
400 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
403 /* if lc gets free()'d,
404 * there's no guarantee
405 * lc->lconn_next is still
406 * sane; better restart
410 /* don't get to next conn! */
418 #ifdef LDAP_R_COMPILE
419 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
425 if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
428 tmp_time = time( NULL );
429 delta_time = tmp_time - start_time;
431 /* do not assume time_t is signed */
432 if ( tv0.tv_sec <= delta_time ) {
433 rc = 0; /* timed out */
434 ld->ld_errno = LDAP_TIMEOUT;
437 tv0.tv_sec -= delta_time;
438 tv.tv_sec = tv0.tv_sec;
440 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld secs to go\n",
441 (void *)ld, (long) tv.tv_sec, 0 );
442 start_time = tmp_time;
456 LDAPMessage **result )
459 LDAPMessage *newmsg, *l, *prev;
465 LDAPRequest *lr, *tmplr, dummy_lr = { 0 };
468 int rc, refer_cnt, hadref, simple_request;
471 #ifdef LDAP_CONNECTIONLESS
472 LDAPMessage *tmp = NULL, *chain_head = NULL;
473 int moremsgs = 0, isv2 = 0;
476 assert( ld != NULL );
477 assert( lcp != NULL );
478 assert( *lcp != NULL );
480 #ifdef LDAP_R_COMPILE
481 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
484 Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
485 (void *)ld, msgid, all );
490 if ( lc->lconn_ber == NULL ) {
491 lc->lconn_ber = ldap_alloc_ber_with_options( ld );
493 if ( lc->lconn_ber == NULL ) {
499 assert( LBER_VALID (ber) );
501 /* get the next message */
503 #ifdef LDAP_CONNECTIONLESS
504 if ( LDAP_IS_UDP(ld) ) {
505 struct sockaddr from;
506 ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr) );
507 if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
511 tag = ber_get_next( lc->lconn_sb, &len, ber );
513 case LDAP_TAG_MESSAGE:
515 * We read a complete message.
516 * The connection should no longer need this ber.
518 lc->lconn_ber = NULL;
523 Debug( LDAP_DEBUG_CONNS,
524 "ber_get_next failed.\n", 0, 0, 0 );
527 if ( sock_errno() == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
530 if ( sock_errno() == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
532 ld->ld_errno = LDAP_SERVER_DOWN;
536 ld->ld_errno = LDAP_LOCAL_ERROR;
541 if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
543 ld->ld_errno = LDAP_DECODING_ERROR;
547 /* id == 0 iff unsolicited notification message (RFC 4511) */
549 /* if it's been abandoned, toss it */
551 if ( ldap_abandoned( ld, id, &idx ) ) {
552 /* the message type */
553 tag = ber_peek_tag( ber, &len );
555 case LDAP_RES_SEARCH_ENTRY:
556 case LDAP_RES_SEARCH_REFERENCE:
557 case LDAP_RES_INTERMEDIATE:
562 /* there's no need to keep the id
563 * in the abandoned list any longer */
564 ldap_mark_abandoned( ld, id, idx );
568 Debug( LDAP_DEBUG_ANY,
569 "abandoned/discarded ld %p msgid %ld message type %s\n",
570 (void *)ld, (long)id, ldap_int_msgtype2str( tag ) );
574 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
577 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
580 lr = ldap_find_request_by_msgid( ld, id );
582 const char *msg = "unknown";
584 /* the message type */
585 tag = ber_peek_tag( ber, &len );
591 msg = ldap_int_msgtype2str( tag );
595 Debug( LDAP_DEBUG_ANY,
596 "no request for response on ld %p msgid %ld message type %s (tossing)\n",
597 (void *)ld, (long)id, msg );
602 #ifdef LDAP_CONNECTIONLESS
603 if ( LDAP_IS_UDP(ld) && isv2 ) {
604 ber_scanf(ber, "x{");
610 /* the message type */
611 tag = ber_peek_tag( ber, &len );
612 if ( tag == LBER_ERROR ) {
613 ld->ld_errno = LDAP_DECODING_ERROR;
618 Debug( LDAP_DEBUG_TRACE,
619 "read1msg: ld %p msgid %ld message type %s\n",
620 (void *)ld, (long)lr->lr_msgid, ldap_int_msgtype2str( tag ) );
623 /* unsolicited notification message (RFC 4511) */
624 if ( tag != LDAP_RES_EXTENDED ) {
628 /* strictly speaking, it's an error; from RFC 4511:
630 4.4. Unsolicited Notification
632 An unsolicited notification is an LDAPMessage sent from the server to
633 the client that is not in response to any LDAPMessage received by the
634 server. It is used to signal an extraordinary condition in the
635 server or in the LDAP session between the client and the server. The
636 notification is of an advisory nature, and the server will not expect
637 any response to be returned from the client.
639 The unsolicited notification is structured as an LDAPMessage in which
640 the messageID is zero and protocolOp is set to the extendedResp
641 choice using the ExtendedResponse type (See Section 4.12). The
642 responseName field of the ExtendedResponse always contains an LDAPOID
643 that is unique for this notification.
645 * however, since unsolicited responses
646 * are of advisory nature, better
651 ld->ld_errno = LDAP_DECODING_ERROR;
662 hadref = simple_request = 0;
663 rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */
664 lr->lr_res_msgtype = tag;
667 * Check for V3 search reference
669 if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
670 if ( ld->ld_version > LDAP_VERSION2 ) {
671 /* This is a V3 search reference */
672 if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
673 lr->lr_parent != NULL )
678 /* Get the referral list */
679 if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
680 rc = LDAP_DECODING_ERROR;
683 /* Note: refs array is freed by ldap_chase_v3referrals */
684 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
685 1, &lr->lr_res_error, &hadref );
686 if ( refer_cnt > 0 ) {
687 /* successfully chased reference */
688 /* If haven't got end search, set chasing referrals */
689 if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
690 lr->lr_status = LDAP_REQST_CHASINGREFS;
691 Debug( LDAP_DEBUG_TRACE,
692 "read1msg: search ref chased, "
693 "mark request chasing refs, "
695 lr->lr_msgid, 0, 0 );
702 } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
703 /* All results that just return a status, i.e. don't return data
704 * go through the following code. This code also chases V2 referrals
705 * and checks if all referrals have been chased.
707 char *lr_res_error = NULL;
709 tmpber = *ber; /* struct copy */
710 if ( ber_scanf( &tmpber, "{eAA", &lderr,
711 &lr->lr_res_matched, &lr_res_error )
714 if ( lr_res_error != NULL ) {
715 if ( lr->lr_res_error != NULL ) {
716 (void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
717 LDAP_FREE( (char *)lr_res_error );
720 lr->lr_res_error = lr_res_error;
725 /* Do we need to check for referrals? */
726 if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
727 lr->lr_parent != NULL )
732 /* Check if V3 referral */
733 if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
734 if ( ld->ld_version > LDAP_VERSION2 ) {
735 /* Get the referral list */
736 if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
737 rc = LDAP_DECODING_ERROR;
738 lr->lr_status = LDAP_REQST_COMPLETED;
739 Debug( LDAP_DEBUG_TRACE,
740 "read1msg: referral decode error, "
741 "mark request completed, ld %p msgid %d\n",
742 (void *)ld, lr->lr_msgid, 0 );
745 /* Chase the referral
746 * refs array is freed by ldap_chase_v3referrals
748 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
749 0, &lr->lr_res_error, &hadref );
750 lr->lr_status = LDAP_REQST_COMPLETED;
751 Debug( LDAP_DEBUG_TRACE,
752 "read1msg: referral %s chased, "
753 "mark request completed, ld %p msgid %d\n",
754 refer_cnt > 0 ? "" : "not",
755 (void *)ld, lr->lr_msgid);
756 if ( refer_cnt < 0 ) {
764 case LDAP_COMPARE_TRUE:
765 case LDAP_COMPARE_FALSE:
769 if ( lr->lr_res_error == NULL ) {
773 /* pedantic, should never happen */
774 if ( lr->lr_res_error[ 0 ] == '\0' ) {
775 LDAP_FREE( lr->lr_res_error );
776 lr->lr_res_error = NULL;
780 /* V2 referrals are in error string */
781 refer_cnt = ldap_chase_referrals( ld, lr,
782 &lr->lr_res_error, -1, &hadref );
783 lr->lr_status = LDAP_REQST_COMPLETED;
784 Debug( LDAP_DEBUG_TRACE,
785 "read1msg: V2 referral chased, "
786 "mark request completed, id = %d\n",
787 lr->lr_msgid, 0, 0 );
793 /* save errno, message, and matched string */
794 if ( !hadref || lr->lr_res_error == NULL ) {
796 lderr == LDAP_PARTIAL_RESULTS
797 ? LDAP_SUCCESS : lderr;
799 } else if ( ld->ld_errno != LDAP_SUCCESS ) {
800 lr->lr_res_errno = ld->ld_errno;
803 lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
807 /* in any case, don't leave any lr_res_error 'round */
808 if ( lr_res_error ) {
809 LDAP_FREE( lr_res_error );
812 Debug( LDAP_DEBUG_TRACE,
813 "read1msg: ld %p %d new referrals\n",
814 (void *)ld, refer_cnt, 0 );
816 if ( refer_cnt != 0 ) { /* chasing referrals */
819 if ( refer_cnt < 0 ) {
820 ldap_return_request( ld, lr, 0 );
821 return( -1 ); /* fatal error */
823 lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
826 if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
827 /* request without any referrals */
828 simple_request = ( hadref ? 0 : 1 );
831 /* request with referrals or child request */
836 lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
837 Debug( LDAP_DEBUG_TRACE,
838 "read1msg: mark request completed, ld %p msgid %d\n",
839 (void *)ld, lr->lr_msgid, 0);
840 while ( lr->lr_parent != NULL ) {
841 merge_error_info( ld, lr->lr_parent, lr );
844 if ( --lr->lr_outrefcnt > 0 ) {
845 break; /* not completely done yet */
849 /* Check if all requests are finished, lr is now parent */
851 if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
852 for ( tmplr = lr->lr_child;
854 tmplr = tmplr->lr_refnext )
856 if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
860 /* This is the parent request if the request has referrals */
861 if ( lr->lr_outrefcnt <= 0 &&
862 lr->lr_parent == NULL &&
866 tag = lr->lr_res_msgtype;
867 Debug( LDAP_DEBUG_ANY, "request done: ld %p msgid %ld\n",
868 (void *)ld, (long) id, 0 );
869 Debug( LDAP_DEBUG_TRACE,
870 "res_errno: %d, res_error: <%s>, "
871 "res_matched: <%s>\n",
873 lr->lr_res_error ? lr->lr_res_error : "",
874 lr->lr_res_matched ? lr->lr_res_matched : "" );
875 if ( !simple_request ) {
878 if ( build_result_ber( ld, &ber, lr )
881 rc = -1; /* fatal error */
885 if ( lr != &dummy_lr ) {
886 ldap_return_request( ld, lr, 1 );
892 * RF 4511 unsolicited (id == 0) responses
893 * shouldn't necessarily end the connection
895 if ( lc != NULL && id != 0 ) {
896 #ifdef LDAP_R_COMPILE
897 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
899 ldap_free_connection( ld, lc, 0, 1 );
900 #ifdef LDAP_R_COMPILE
901 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
909 if ( lr != &dummy_lr ) {
910 ldap_return_request( ld, lr, 0 );
919 /* try to handle unsolicited responses as appropriate */
920 if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
923 tag = ber_peek_tag( &tmpber, &len );
925 /* we have a res oid */
926 if ( tag == LDAP_TAG_EXOP_RES_OID ) {
927 static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
928 struct berval resoid = BER_BVNULL;
930 if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
931 ld->ld_errno = LDAP_DECODING_ERROR;
936 assert( !BER_BVISEMPTY( &resoid ) );
938 is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
940 tag = ber_peek_tag( &tmpber, &len );
943 #if 0 /* don't need right now */
944 /* we have res data */
945 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
946 struct berval resdata;
948 if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
949 ld->ld_errno = LDAP_DECODING_ERROR;
958 /* handle RFC 4511 "Notice of Disconnection" locally */
961 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
962 ld->ld_errno = LDAP_DECODING_ERROR;
967 /* get rid of the connection... */
969 #ifdef LDAP_R_COMPILE
970 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
972 ldap_free_connection( ld, lc, 0, 1 );
973 #ifdef LDAP_R_COMPILE
974 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
979 /* need to return -1, because otherwise
980 * a valid result is expected */
985 /* make a new ldap message */
986 newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
987 if ( newmsg == NULL ) {
988 ld->ld_errno = LDAP_NO_MEMORY;
991 newmsg->lm_msgid = (int)id;
992 newmsg->lm_msgtype = tag;
993 newmsg->lm_ber = ber;
994 newmsg->lm_chain_tail = newmsg;
996 #ifdef LDAP_CONNECTIONLESS
997 /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
998 * the responses are all a sequence wrapped in one message. In
999 * LDAPv3 each response is in its own message. The datagram must
1000 * end with a SearchResult. We can't just parse each response in
1001 * separate calls to try_read1msg because the header info is only
1002 * present at the beginning of the datagram, not at the beginning
1003 * of each response. So parse all the responses at once and queue
1004 * them up, then pull off the first response to return to the
1005 * caller when all parsing is complete.
1007 if ( LDAP_IS_UDP(ld) ) {
1008 /* If not a result, look for more */
1009 if ( tag != LDAP_RES_SEARCH_RESULT ) {
1013 /* LDAPv2: dup the current ber, skip past the current
1014 * response, and see if there are any more after it.
1016 ber = ber_dup( ber );
1017 ber_scanf( ber, "x" );
1018 if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
1019 /* There's more - dup the ber buffer so they can all be
1020 * individually freed by ldap_msgfree.
1023 ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1024 bv.bv_val = LDAP_MALLOC( len );
1027 ber_read( ber, bv.bv_val, len );
1029 ber_init2( ber, &bv, ld->ld_lberoptions );
1033 /* LDAPv3: Just allocate a new ber. Since this is a buffered
1034 * datagram, if the sockbuf is readable we still have data
1037 ber = ldap_alloc_ber_with_options( ld );
1038 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1040 /* set up response chain */
1041 if ( tmp == NULL ) {
1042 newmsg->lm_next = ld->ld_responses;
1043 ld->ld_responses = newmsg;
1044 chain_head = newmsg;
1046 tmp->lm_chain = newmsg;
1048 chain_head->lm_chain_tail = newmsg;
1050 /* "ok" means there's more to parse */
1059 /* got to end of datagram without a SearchResult. Free
1060 * our dup'd ber, but leave any buffer alone. For v2 case,
1061 * the previous response is still using this buffer. For v3,
1062 * the new ber has no buffer to free yet.
1067 } else if ( moremsgs ) {
1068 /* got search result, and we had multiple responses in 1 datagram.
1069 * stick the result onto the end of the chain, and then pull the
1070 * first response off the head of the chain.
1072 tmp->lm_chain = newmsg;
1073 chain_head->lm_chain_tail = newmsg;
1074 *result = chkResponseList( ld, msgid, all );
1075 ld->ld_errno = LDAP_SUCCESS;
1076 return( (*result)->lm_msgtype );
1079 #endif /* LDAP_CONNECTIONLESS */
1081 /* is this the one we're looking for? */
1082 if ( msgid == LDAP_RES_ANY || id == msgid ) {
1083 if ( all == LDAP_MSG_ONE
1084 || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1085 && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1086 && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1089 ld->ld_errno = LDAP_SUCCESS;
1092 } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1093 foundit = 1; /* return the chain later */
1098 * if not, we must add it to the list of responses. if
1099 * the msgid is already there, it must be part of an existing
1104 for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1105 if ( l->lm_msgid == newmsg->lm_msgid ) {
1111 /* not part of an existing search response */
1118 newmsg->lm_next = ld->ld_responses;
1119 ld->ld_responses = newmsg;
1123 Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %ld type %ld:\n",
1124 (void *)ld, (long) newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1126 /* part of a search response - add to end of list of entries */
1127 l->lm_chain_tail->lm_chain = newmsg;
1128 l->lm_chain_tail = newmsg;
1130 /* return the whole chain if that's what we were looking for */
1132 if ( prev == NULL ) {
1133 ld->ld_responses = l->lm_next;
1135 prev->lm_next = l->lm_next;
1142 ld->ld_errno = LDAP_SUCCESS;
1145 if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1148 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
1153 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1161 ber = ldap_alloc_ber_with_options( ld );
1164 ld->ld_errno = LDAP_NO_MEMORY;
1168 if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1169 lr->lr_res_msgtype, lr->lr_res_errno,
1170 lr->lr_res_matched ? lr->lr_res_matched : "",
1171 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1173 ld->ld_errno = LDAP_ENCODING_ERROR;
1175 return( LBER_ERROR );
1178 ber_reset( ber, 1 );
1180 if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1181 ld->ld_errno = LDAP_DECODING_ERROR;
1183 return( LBER_ERROR );
1186 if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1187 ld->ld_errno = LDAP_DECODING_ERROR;
1189 return( LBER_ERROR );
1192 tag = ber_peek_tag( ber, &len );
1194 if ( tag == LBER_ERROR ) {
1195 ld->ld_errno = LDAP_DECODING_ERROR;
1197 return( LBER_ERROR );
1206 * Merge error information in "lr" with "parentr" error code and string.
1209 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1211 if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1212 parentr->lr_res_errno = lr->lr_res_errno;
1213 if ( lr->lr_res_error != NULL ) {
1214 (void)ldap_append_referral( ld, &parentr->lr_res_error,
1218 } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1219 parentr->lr_res_errno == LDAP_SUCCESS )
1221 parentr->lr_res_errno = lr->lr_res_errno;
1222 if ( parentr->lr_res_error != NULL ) {
1223 LDAP_FREE( parentr->lr_res_error );
1225 parentr->lr_res_error = lr->lr_res_error;
1226 lr->lr_res_error = NULL;
1227 if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1228 if ( parentr->lr_res_matched != NULL ) {
1229 LDAP_FREE( parentr->lr_res_matched );
1231 parentr->lr_res_matched = lr->lr_res_matched;
1232 lr->lr_res_matched = NULL;
1236 Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
1237 parentr->lr_msgid, 0, 0 );
1238 Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1239 parentr->lr_res_errno,
1240 parentr->lr_res_error ? parentr->lr_res_error : "",
1241 parentr->lr_res_matched ? parentr->lr_res_matched : "" );
1247 ldap_msgtype( LDAPMessage *lm )
1249 assert( lm != NULL );
1250 return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1255 ldap_msgid( LDAPMessage *lm )
1257 assert( lm != NULL );
1259 return ( lm != NULL ) ? lm->lm_msgid : -1;
1264 ldap_int_msgtype2str( ber_tag_t tag )
1267 case LDAP_RES_ADD: return "add";
1268 case LDAP_RES_BIND: return "bind";
1269 case LDAP_RES_COMPARE: return "compare";
1270 case LDAP_RES_DELETE: return "delete";
1271 case LDAP_RES_EXTENDED: return "extended-result";
1272 case LDAP_RES_INTERMEDIATE: return "intermediate";
1273 case LDAP_RES_MODIFY: return "modify";
1274 case LDAP_RES_RENAME: return "rename";
1275 case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1276 case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1277 case LDAP_RES_SEARCH_RESULT: return "search-result";
1283 ldap_msgfree( LDAPMessage *lm )
1288 Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1290 for ( ; lm != NULL; lm = next ) {
1291 next = lm->lm_chain;
1292 type = lm->lm_msgtype;
1293 ber_free( lm->lm_ber, 1 );
1294 LDAP_FREE( (char *) lm );
1301 * ldap_msgdelete - delete a message. It returns:
1302 * 0 if the entire message was deleted
1303 * -1 if the message was not found, or only part of it was found
1306 ldap_msgdelete( LDAP *ld, int msgid )
1308 LDAPMessage *lm, *prev;
1311 assert( ld != NULL );
1313 Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1314 (void *)ld, msgid, 0 );
1316 #ifdef LDAP_R_COMPILE
1317 ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
1320 for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1321 if ( lm->lm_msgid == msgid ) {
1331 if ( prev == NULL ) {
1332 ld->ld_responses = lm->lm_next;
1334 prev->lm_next = lm->lm_next;
1337 #ifdef LDAP_R_COMPILE
1338 ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
1341 switch ( ldap_msgfree( lm ) ) {
1342 case LDAP_RES_SEARCH_ENTRY:
1343 case LDAP_RES_SEARCH_REFERENCE:
1344 case LDAP_RES_INTERMEDIATE:
1360 * return the location of the message id in the array of abandoned
1361 * message ids, or -1
1363 * expects ld_res_mutex to be locked
1366 ldap_abandoned( LDAP *ld, ber_int_t msgid, int *idxp )
1368 #ifdef LDAP_R_COMPILE
1369 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1372 assert( idxp != NULL );
1373 assert( msgid >= 0 );
1374 assert( ld->ld_nabandoned >= 0 );
1376 return ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp );
1380 * ldap_mark_abandoned
1382 * expects ld_res_mutex to be locked
1385 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid, int idx )
1387 #ifdef LDAP_R_COMPILE
1388 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1391 /* NOTE: those assertions are repeated in ldap_int_bisect_delete() */
1393 assert( idx < ld->ld_nabandoned );
1394 assert( ld->ld_abandoned[ idx ] == msgid );
1396 return ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,