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 ) {
426 tmp_time = time( NULL );
427 tv0.tv_sec -= ( tmp_time - start_time );
428 if ( tv0.tv_sec <= 0 ) {
429 rc = 0; /* timed out */
430 ld->ld_errno = LDAP_TIMEOUT;
433 tv.tv_sec = tv0.tv_sec;
435 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld secs to go\n",
436 (void *)ld, (long) tv.tv_sec, 0 );
437 start_time = tmp_time;
451 LDAPMessage **result )
454 LDAPMessage *newmsg, *l, *prev;
460 LDAPRequest *lr, *tmplr, dummy_lr = { 0 };
463 int rc, refer_cnt, hadref, simple_request;
466 #ifdef LDAP_CONNECTIONLESS
467 LDAPMessage *tmp = NULL, *chain_head = NULL;
468 int moremsgs = 0, isv2 = 0;
471 assert( ld != NULL );
472 assert( lcp != NULL );
473 assert( *lcp != NULL );
475 #ifdef LDAP_R_COMPILE
476 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
479 Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
480 (void *)ld, msgid, all );
485 if ( lc->lconn_ber == NULL ) {
486 lc->lconn_ber = ldap_alloc_ber_with_options( ld );
488 if ( lc->lconn_ber == NULL ) {
494 assert( LBER_VALID (ber) );
496 /* get the next message */
498 #ifdef LDAP_CONNECTIONLESS
499 if ( LDAP_IS_UDP(ld) ) {
500 struct sockaddr from;
501 ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr) );
502 if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
506 tag = ber_get_next( lc->lconn_sb, &len, ber );
508 case LDAP_TAG_MESSAGE:
510 * We read a complete message.
511 * The connection should no longer need this ber.
513 lc->lconn_ber = NULL;
518 Debug( LDAP_DEBUG_CONNS,
519 "ber_get_next failed.\n", 0, 0, 0 );
522 if ( sock_errno() == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
525 if ( sock_errno() == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
527 ld->ld_errno = LDAP_SERVER_DOWN;
531 ld->ld_errno = LDAP_LOCAL_ERROR;
536 if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
538 ld->ld_errno = LDAP_DECODING_ERROR;
542 /* id == 0 iff unsolicited notification message (RFC 4511) */
544 /* if it's been abandoned, toss it */
546 if ( ldap_abandoned( ld, id, &idx ) ) {
547 /* the message type */
548 tag = ber_peek_tag( ber, &len );
550 case LDAP_RES_SEARCH_ENTRY:
551 case LDAP_RES_SEARCH_REFERENCE:
552 case LDAP_RES_INTERMEDIATE:
557 /* there's no need to keep the id
558 * in the abandoned list any longer */
559 ldap_mark_abandoned( ld, id, idx );
563 Debug( LDAP_DEBUG_ANY,
564 "abandoned/discarded ld %p msgid %ld message type %s\n",
565 (void *)ld, (long)id, ldap_int_msgtype2str( tag ) );
569 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
572 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
575 lr = ldap_find_request_by_msgid( ld, id );
577 const char *msg = "unknown";
579 /* the message type */
580 tag = ber_peek_tag( ber, &len );
586 msg = ldap_int_msgtype2str( tag );
590 Debug( LDAP_DEBUG_ANY,
591 "no request for response on ld %p msgid %ld message type %s (tossing)\n",
592 (void *)ld, (long)id, msg );
597 #ifdef LDAP_CONNECTIONLESS
598 if ( LDAP_IS_UDP(ld) && isv2 ) {
599 ber_scanf(ber, "x{");
605 /* the message type */
606 tag = ber_peek_tag( ber, &len );
607 if ( tag == LBER_ERROR ) {
608 ld->ld_errno = LDAP_DECODING_ERROR;
613 Debug( LDAP_DEBUG_TRACE,
614 "read1msg: ld %p msgid %ld message type %s\n",
615 (void *)ld, (long)lr->lr_msgid, ldap_int_msgtype2str( tag ) );
618 /* unsolicited notification message (RFC 4511) */
619 if ( tag != LDAP_RES_EXTENDED ) {
623 /* strictly speaking, it's an error; from RFC 4511:
625 4.4. Unsolicited Notification
627 An unsolicited notification is an LDAPMessage sent from the server to
628 the client that is not in response to any LDAPMessage received by the
629 server. It is used to signal an extraordinary condition in the
630 server or in the LDAP session between the client and the server. The
631 notification is of an advisory nature, and the server will not expect
632 any response to be returned from the client.
634 The unsolicited notification is structured as an LDAPMessage in which
635 the messageID is zero and protocolOp is set to the extendedResp
636 choice using the ExtendedResponse type (See Section 4.12). The
637 responseName field of the ExtendedResponse always contains an LDAPOID
638 that is unique for this notification.
640 * however, since unsolicited responses
641 * are of advisory nature, better
646 ld->ld_errno = LDAP_DECODING_ERROR;
657 hadref = simple_request = 0;
658 rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */
659 lr->lr_res_msgtype = tag;
662 * Check for V3 search reference
664 if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
665 if ( ld->ld_version > LDAP_VERSION2 ) {
666 /* This is a V3 search reference */
667 if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
668 lr->lr_parent != NULL )
673 /* Get the referral list */
674 if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
675 rc = LDAP_DECODING_ERROR;
678 /* Note: refs array is freed by ldap_chase_v3referrals */
679 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
680 1, &lr->lr_res_error, &hadref );
681 if ( refer_cnt > 0 ) {
682 /* successfully chased reference */
683 /* If haven't got end search, set chasing referrals */
684 if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
685 lr->lr_status = LDAP_REQST_CHASINGREFS;
686 Debug( LDAP_DEBUG_TRACE,
687 "read1msg: search ref chased, "
688 "mark request chasing refs, "
690 lr->lr_msgid, 0, 0 );
697 } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
698 /* All results that just return a status, i.e. don't return data
699 * go through the following code. This code also chases V2 referrals
700 * and checks if all referrals have been chased.
702 char *lr_res_error = NULL;
704 tmpber = *ber; /* struct copy */
705 if ( ber_scanf( &tmpber, "{eAA", &lderr,
706 &lr->lr_res_matched, &lr_res_error )
709 if ( lr_res_error != NULL ) {
710 if ( lr->lr_res_error != NULL ) {
711 (void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
712 LDAP_FREE( (char *)lr_res_error );
715 lr->lr_res_error = lr_res_error;
720 /* Do we need to check for referrals? */
721 if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
722 lr->lr_parent != NULL )
727 /* Check if V3 referral */
728 if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
729 if ( ld->ld_version > LDAP_VERSION2 ) {
730 /* Get the referral list */
731 if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
732 rc = LDAP_DECODING_ERROR;
733 lr->lr_status = LDAP_REQST_COMPLETED;
734 Debug( LDAP_DEBUG_TRACE,
735 "read1msg: referral decode error, "
736 "mark request completed, ld %p msgid %d\n",
737 (void *)ld, lr->lr_msgid, 0 );
740 /* Chase the referral
741 * refs array is freed by ldap_chase_v3referrals
743 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
744 0, &lr->lr_res_error, &hadref );
745 lr->lr_status = LDAP_REQST_COMPLETED;
746 Debug( LDAP_DEBUG_TRACE,
747 "read1msg: referral %s chased, "
748 "mark request completed, ld %p msgid %d\n",
749 refer_cnt > 0 ? "" : "not",
750 (void *)ld, lr->lr_msgid);
751 if ( refer_cnt < 0 ) {
759 case LDAP_COMPARE_TRUE:
760 case LDAP_COMPARE_FALSE:
764 if ( lr->lr_res_error == NULL ) {
768 /* pedantic, should never happen */
769 if ( lr->lr_res_error[ 0 ] == '\0' ) {
770 LDAP_FREE( lr->lr_res_error );
771 lr->lr_res_error = NULL;
775 /* V2 referrals are in error string */
776 refer_cnt = ldap_chase_referrals( ld, lr,
777 &lr->lr_res_error, -1, &hadref );
778 lr->lr_status = LDAP_REQST_COMPLETED;
779 Debug( LDAP_DEBUG_TRACE,
780 "read1msg: V2 referral chased, "
781 "mark request completed, id = %d\n",
782 lr->lr_msgid, 0, 0 );
788 /* save errno, message, and matched string */
789 if ( !hadref || lr->lr_res_error == NULL ) {
791 lderr == LDAP_PARTIAL_RESULTS
792 ? LDAP_SUCCESS : lderr;
794 } else if ( ld->ld_errno != LDAP_SUCCESS ) {
795 lr->lr_res_errno = ld->ld_errno;
798 lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
802 /* in any case, don't leave any lr_res_error 'round */
803 if ( lr_res_error ) {
804 LDAP_FREE( lr_res_error );
807 Debug( LDAP_DEBUG_TRACE,
808 "read1msg: ld %p %d new referrals\n",
809 (void *)ld, refer_cnt, 0 );
811 if ( refer_cnt != 0 ) { /* chasing referrals */
814 if ( refer_cnt < 0 ) {
815 ldap_return_request( ld, lr, 0 );
816 return( -1 ); /* fatal error */
818 lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
821 if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
822 /* request without any referrals */
823 simple_request = ( hadref ? 0 : 1 );
826 /* request with referrals or child request */
831 lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
832 Debug( LDAP_DEBUG_TRACE,
833 "read1msg: mark request completed, ld %p msgid %d\n",
834 (void *)ld, lr->lr_msgid, 0);
835 while ( lr->lr_parent != NULL ) {
836 merge_error_info( ld, lr->lr_parent, lr );
839 if ( --lr->lr_outrefcnt > 0 ) {
840 break; /* not completely done yet */
844 /* Check if all requests are finished, lr is now parent */
846 if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
847 for ( tmplr = lr->lr_child;
849 tmplr = tmplr->lr_refnext )
851 if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
855 /* This is the parent request if the request has referrals */
856 if ( lr->lr_outrefcnt <= 0 &&
857 lr->lr_parent == NULL &&
861 tag = lr->lr_res_msgtype;
862 Debug( LDAP_DEBUG_ANY, "request done: ld %p msgid %ld\n",
863 (void *)ld, (long) id, 0 );
864 Debug( LDAP_DEBUG_TRACE,
865 "res_errno: %d, res_error: <%s>, "
866 "res_matched: <%s>\n",
868 lr->lr_res_error ? lr->lr_res_error : "",
869 lr->lr_res_matched ? lr->lr_res_matched : "" );
870 if ( !simple_request ) {
873 if ( build_result_ber( ld, &ber, lr )
876 rc = -1; /* fatal error */
880 if ( lr != &dummy_lr ) {
881 ldap_return_request( ld, lr, 1 );
887 * RF 4511 unsolicited (id == 0) responses
888 * shouldn't necessarily end the connection
890 if ( lc != NULL && id != 0 ) {
891 #ifdef LDAP_R_COMPILE
892 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
894 ldap_free_connection( ld, lc, 0, 1 );
895 #ifdef LDAP_R_COMPILE
896 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
904 if ( lr != &dummy_lr ) {
905 ldap_return_request( ld, lr, 0 );
914 /* try to handle unsolicited responses as appropriate */
915 if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
918 tag = ber_peek_tag( &tmpber, &len );
920 /* we have a res oid */
921 if ( tag == LDAP_TAG_EXOP_RES_OID ) {
922 static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
923 struct berval resoid = BER_BVNULL;
925 if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
926 ld->ld_errno = LDAP_DECODING_ERROR;
931 assert( !BER_BVISEMPTY( &resoid ) );
933 is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
935 tag = ber_peek_tag( &tmpber, &len );
938 #if 0 /* don't need right now */
939 /* we have res data */
940 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
941 struct berval resdata;
943 if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
944 ld->ld_errno = LDAP_DECODING_ERROR;
953 /* handle RFC 4511 "Notice of Disconnection" locally */
956 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
957 ld->ld_errno = LDAP_DECODING_ERROR;
962 /* get rid of the connection... */
964 #ifdef LDAP_R_COMPILE
965 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
967 ldap_free_connection( ld, lc, 0, 1 );
968 #ifdef LDAP_R_COMPILE
969 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
974 /* need to return -1, because otherwise
975 * a valid result is expected */
980 /* make a new ldap message */
981 newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
982 if ( newmsg == NULL ) {
983 ld->ld_errno = LDAP_NO_MEMORY;
986 newmsg->lm_msgid = (int)id;
987 newmsg->lm_msgtype = tag;
988 newmsg->lm_ber = ber;
989 newmsg->lm_chain_tail = newmsg;
991 #ifdef LDAP_CONNECTIONLESS
992 /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
993 * the responses are all a sequence wrapped in one message. In
994 * LDAPv3 each response is in its own message. The datagram must
995 * end with a SearchResult. We can't just parse each response in
996 * separate calls to try_read1msg because the header info is only
997 * present at the beginning of the datagram, not at the beginning
998 * of each response. So parse all the responses at once and queue
999 * them up, then pull off the first response to return to the
1000 * caller when all parsing is complete.
1002 if ( LDAP_IS_UDP(ld) ) {
1003 /* If not a result, look for more */
1004 if ( tag != LDAP_RES_SEARCH_RESULT ) {
1008 /* LDAPv2: dup the current ber, skip past the current
1009 * response, and see if there are any more after it.
1011 ber = ber_dup( ber );
1012 ber_scanf( ber, "x" );
1013 if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
1014 /* There's more - dup the ber buffer so they can all be
1015 * individually freed by ldap_msgfree.
1018 ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1019 bv.bv_val = LDAP_MALLOC( len );
1022 ber_read( ber, bv.bv_val, len );
1024 ber_init2( ber, &bv, ld->ld_lberoptions );
1028 /* LDAPv3: Just allocate a new ber. Since this is a buffered
1029 * datagram, if the sockbuf is readable we still have data
1032 ber = ldap_alloc_ber_with_options( ld );
1033 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1035 /* set up response chain */
1036 if ( tmp == NULL ) {
1037 newmsg->lm_next = ld->ld_responses;
1038 ld->ld_responses = newmsg;
1039 chain_head = newmsg;
1041 tmp->lm_chain = newmsg;
1043 chain_head->lm_chain_tail = newmsg;
1045 /* "ok" means there's more to parse */
1054 /* got to end of datagram without a SearchResult. Free
1055 * our dup'd ber, but leave any buffer alone. For v2 case,
1056 * the previous response is still using this buffer. For v3,
1057 * the new ber has no buffer to free yet.
1062 } else if ( moremsgs ) {
1063 /* got search result, and we had multiple responses in 1 datagram.
1064 * stick the result onto the end of the chain, and then pull the
1065 * first response off the head of the chain.
1067 tmp->lm_chain = newmsg;
1068 chain_head->lm_chain_tail = newmsg;
1069 *result = chkResponseList( ld, msgid, all );
1070 ld->ld_errno = LDAP_SUCCESS;
1071 return( (*result)->lm_msgtype );
1074 #endif /* LDAP_CONNECTIONLESS */
1076 /* is this the one we're looking for? */
1077 if ( msgid == LDAP_RES_ANY || id == msgid ) {
1078 if ( all == LDAP_MSG_ONE
1079 || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1080 && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1081 && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1084 ld->ld_errno = LDAP_SUCCESS;
1087 } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1088 foundit = 1; /* return the chain later */
1093 * if not, we must add it to the list of responses. if
1094 * the msgid is already there, it must be part of an existing
1099 for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1100 if ( l->lm_msgid == newmsg->lm_msgid ) {
1106 /* not part of an existing search response */
1113 newmsg->lm_next = ld->ld_responses;
1114 ld->ld_responses = newmsg;
1118 Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %ld type %ld:\n",
1119 (void *)ld, (long) newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1121 /* part of a search response - add to end of list of entries */
1122 l->lm_chain_tail->lm_chain = newmsg;
1123 l->lm_chain_tail = newmsg;
1125 /* return the whole chain if that's what we were looking for */
1127 if ( prev == NULL ) {
1128 ld->ld_responses = l->lm_next;
1130 prev->lm_next = l->lm_next;
1137 ld->ld_errno = LDAP_SUCCESS;
1140 if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1143 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
1148 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1156 ber = ldap_alloc_ber_with_options( ld );
1159 ld->ld_errno = LDAP_NO_MEMORY;
1163 if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1164 lr->lr_res_msgtype, lr->lr_res_errno,
1165 lr->lr_res_matched ? lr->lr_res_matched : "",
1166 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1168 ld->ld_errno = LDAP_ENCODING_ERROR;
1170 return( LBER_ERROR );
1173 ber_reset( ber, 1 );
1175 if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1176 ld->ld_errno = LDAP_DECODING_ERROR;
1178 return( LBER_ERROR );
1181 if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1182 ld->ld_errno = LDAP_DECODING_ERROR;
1184 return( LBER_ERROR );
1187 tag = ber_peek_tag( ber, &len );
1189 if ( tag == LBER_ERROR ) {
1190 ld->ld_errno = LDAP_DECODING_ERROR;
1192 return( LBER_ERROR );
1201 * Merge error information in "lr" with "parentr" error code and string.
1204 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1206 if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1207 parentr->lr_res_errno = lr->lr_res_errno;
1208 if ( lr->lr_res_error != NULL ) {
1209 (void)ldap_append_referral( ld, &parentr->lr_res_error,
1213 } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1214 parentr->lr_res_errno == LDAP_SUCCESS )
1216 parentr->lr_res_errno = lr->lr_res_errno;
1217 if ( parentr->lr_res_error != NULL ) {
1218 LDAP_FREE( parentr->lr_res_error );
1220 parentr->lr_res_error = lr->lr_res_error;
1221 lr->lr_res_error = NULL;
1222 if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1223 if ( parentr->lr_res_matched != NULL ) {
1224 LDAP_FREE( parentr->lr_res_matched );
1226 parentr->lr_res_matched = lr->lr_res_matched;
1227 lr->lr_res_matched = NULL;
1231 Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
1232 parentr->lr_msgid, 0, 0 );
1233 Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1234 parentr->lr_res_errno,
1235 parentr->lr_res_error ? parentr->lr_res_error : "",
1236 parentr->lr_res_matched ? parentr->lr_res_matched : "" );
1242 ldap_msgtype( LDAPMessage *lm )
1244 assert( lm != NULL );
1245 return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1250 ldap_msgid( LDAPMessage *lm )
1252 assert( lm != NULL );
1254 return ( lm != NULL ) ? lm->lm_msgid : -1;
1259 ldap_int_msgtype2str( ber_tag_t tag )
1262 case LDAP_RES_ADD: return "add";
1263 case LDAP_RES_BIND: return "bind";
1264 case LDAP_RES_COMPARE: return "compare";
1265 case LDAP_RES_DELETE: return "delete";
1266 case LDAP_RES_EXTENDED: return "extended-result";
1267 case LDAP_RES_INTERMEDIATE: return "intermediate";
1268 case LDAP_RES_MODIFY: return "modify";
1269 case LDAP_RES_RENAME: return "rename";
1270 case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1271 case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1272 case LDAP_RES_SEARCH_RESULT: return "search-result";
1278 ldap_msgfree( LDAPMessage *lm )
1283 Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1285 for ( ; lm != NULL; lm = next ) {
1286 next = lm->lm_chain;
1287 type = lm->lm_msgtype;
1288 ber_free( lm->lm_ber, 1 );
1289 LDAP_FREE( (char *) lm );
1296 * ldap_msgdelete - delete a message. It returns:
1297 * 0 if the entire message was deleted
1298 * -1 if the message was not found, or only part of it was found
1301 ldap_msgdelete( LDAP *ld, int msgid )
1303 LDAPMessage *lm, *prev;
1306 assert( ld != NULL );
1308 Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1309 (void *)ld, msgid, 0 );
1311 #ifdef LDAP_R_COMPILE
1312 ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
1315 for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1316 if ( lm->lm_msgid == msgid ) {
1326 if ( prev == NULL ) {
1327 ld->ld_responses = lm->lm_next;
1329 prev->lm_next = lm->lm_next;
1332 #ifdef LDAP_R_COMPILE
1333 ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
1336 switch ( ldap_msgfree( lm ) ) {
1337 case LDAP_RES_SEARCH_ENTRY:
1338 case LDAP_RES_SEARCH_REFERENCE:
1339 case LDAP_RES_INTERMEDIATE:
1355 * return the location of the message id in the array of abandoned
1356 * message ids, or -1
1358 * expects ld_res_mutex to be locked
1361 ldap_abandoned( LDAP *ld, ber_int_t msgid, int *idxp )
1363 #ifdef LDAP_R_COMPILE
1364 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1367 assert( idxp != NULL );
1368 assert( msgid >= 0 );
1369 assert( ld->ld_nabandoned >= 0 );
1371 return ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp );
1375 * ldap_mark_abandoned
1377 * expects ld_res_mutex to be locked
1380 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid, int idx )
1382 #ifdef LDAP_R_COMPILE
1383 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1386 /* NOTE: those assertions are repeated in ldap_int_bisect_delete() */
1388 assert( idx < ld->ld_nabandoned );
1389 assert( ld->ld_abandoned[ idx ] == msgid );
1391 return ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,