1 /* result.c - wait for an ldap result */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 1998-2017 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 ));
70 static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
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 )
111 assert( ld != NULL );
112 assert( result != NULL );
114 Debug( LDAP_DEBUG_TRACE, "ldap_result ld %p msgid %d\n", (void *)ld, msgid, 0 );
116 if (ld->ld_errno == LDAP_LOCAL_ERROR || ld->ld_errno == LDAP_SERVER_DOWN)
119 LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
120 rc = wait4msg( ld, msgid, all, timeout, result );
121 LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
126 /* protected by res_mutex */
133 LDAPMessage *lm, **lastlm, *nextlm;
137 * Look through the list of responses we have received on
138 * this association and see if the response we're interested in
139 * is there. If it is, return it. If not, call wait4msg() to
140 * wait until it arrives or timeout occurs.
143 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
145 Debug( LDAP_DEBUG_TRACE,
146 "ldap_chkResponseList ld %p msgid %d all %d\n",
147 (void *)ld, msgid, all );
149 lastlm = &ld->ld_responses;
150 for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
151 nextlm = lm->lm_next;
154 if ( ldap_abandoned( ld, lm->lm_msgid ) ) {
155 Debug( LDAP_DEBUG_ANY,
156 "response list msg abandoned, "
157 "msgid %d message type %s\n",
158 lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ), 0 );
160 switch ( lm->lm_msgtype ) {
161 case LDAP_RES_SEARCH_ENTRY:
162 case LDAP_RES_SEARCH_REFERENCE:
163 case LDAP_RES_INTERMEDIATE:
167 /* there's no need to keep the id
168 * in the abandoned list any longer */
169 ldap_mark_abandoned( ld, lm->lm_msgid );
173 /* Remove this entry from list */
181 if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
184 if ( all == LDAP_MSG_ONE ||
185 all == LDAP_MSG_RECEIVED ||
186 msgid == LDAP_RES_UNSOLICITED )
191 tmp = lm->lm_chain_tail;
192 if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY ||
193 tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ||
194 tmp->lm_msgtype == LDAP_RES_INTERMEDIATE )
205 lastlm = &lm->lm_next;
209 /* Found an entry, remove it from the list */
210 if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) {
211 *lastlm = lm->lm_chain;
212 lm->lm_chain->lm_next = lm->lm_next;
213 lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain;
215 lm->lm_chain_tail = NULL;
217 *lastlm = lm->lm_next;
224 Debug( LDAP_DEBUG_TRACE,
225 "ldap_chkResponseList returns ld %p NULL\n", (void *)ld, 0, 0);
227 Debug( LDAP_DEBUG_TRACE,
228 "ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n",
229 (void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
236 /* protected by res_mutex */
242 struct timeval *timeout,
243 LDAPMessage **result )
246 struct timeval tv = { 0 },
248 start_time_tv = { 0 },
252 assert( ld != NULL );
253 assert( result != NULL );
255 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
257 if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) {
258 tv = ld->ld_options.ldo_tm_api;
263 if ( timeout == NULL ) {
264 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
265 (void *)ld, msgid, 0 );
267 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n",
268 (void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec );
270 #endif /* LDAP_DEBUG */
272 if ( timeout != NULL && timeout->tv_sec != -1 ) {
276 #ifdef HAVE_GETTIMEOFDAY
277 gettimeofday( &start_time_tv, NULL );
278 #else /* ! HAVE_GETTIMEOFDAY */
279 start_time_tv.tv_sec = time( NULL );
280 start_time_tv.tv_usec = 0;
281 #endif /* ! HAVE_GETTIMEOFDAY */
284 rc = LDAP_MSG_X_KEEP_LOOKING;
285 while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
287 if ( ldap_debug & LDAP_DEBUG_TRACE ) {
288 Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
289 (void *)ld, msgid, all );
290 ldap_dump_connection( ld, ld->ld_conns, 1 );
291 LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
292 ldap_dump_requests_and_responses( ld );
293 LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
295 #endif /* LDAP_DEBUG */
297 if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
298 rc = (*result)->lm_msgtype;
303 LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
304 for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
305 if ( ber_sockbuf_ctrl( lc->lconn_sb,
306 LBER_SB_OPT_DATA_READY, NULL ) )
308 lc_ready = 2; /* ready at ber level, not socket level */
315 rc = ldap_int_select( ld, tvp );
319 Debug( LDAP_DEBUG_TRACE,
320 "ldap_int_select returned -1: errno %d\n",
325 if ( rc == 0 || ( rc == -1 && (
326 !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
327 || err != EINTR ) ) )
329 ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
331 LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
336 rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */
345 rc = LDAP_MSG_X_KEEP_LOOKING;
346 LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
347 if ( ld->ld_requests &&
348 ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
349 ldap_is_write_ready( ld,
350 ld->ld_requests->lr_conn->lconn_sb ) )
353 ldap_int_flush_request( ld, ld->ld_requests );
355 for ( lc = ld->ld_conns;
356 rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL;
359 if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
360 ldap_is_read_ready( ld, lc->lconn_sb ) )
363 /* Don't let it get freed out from under us */
365 rc = try_read1msg( ld, msgid, all, lc, result );
366 lnext = lc->lconn_next;
368 /* Only take locks if we're really freeing */
369 if ( lc->lconn_refcnt <= 1 ) {
370 ldap_free_connection( ld, lc, 0, 1 );
375 lnext = lc->lconn_next;
378 LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
379 /* Quit looping if no one handled any socket events */
380 if (!serviced && lc_ready == 1)
383 LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
386 if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
387 struct timeval curr_time_tv = { 0 },
388 delta_time_tv = { 0 };
390 #ifdef HAVE_GETTIMEOFDAY
391 gettimeofday( &curr_time_tv, NULL );
392 #else /* ! HAVE_GETTIMEOFDAY */
393 curr_time_tv.tv_sec = time( NULL );
394 curr_time_tv.tv_usec = 0;
395 #endif /* ! HAVE_GETTIMEOFDAY */
397 /* delta_time = tmp_time - start_time */
398 delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
399 delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
400 if ( delta_time_tv.tv_usec < 0 ) {
401 delta_time_tv.tv_sec--;
402 delta_time_tv.tv_usec += 1000000;
405 /* tv0 < delta_time ? */
406 if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
407 ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
409 rc = 0; /* timed out */
410 ld->ld_errno = LDAP_TIMEOUT;
414 /* tv0 -= delta_time */
415 tv0.tv_sec -= delta_time_tv.tv_sec;
416 tv0.tv_usec -= delta_time_tv.tv_usec;
417 if ( tv0.tv_usec < 0 ) {
419 tv0.tv_usec += 1000000;
422 tv.tv_sec = tv0.tv_sec;
423 tv.tv_usec = tv0.tv_usec;
425 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n",
426 (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
428 start_time_tv.tv_sec = curr_time_tv.tv_sec;
429 start_time_tv.tv_usec = curr_time_tv.tv_usec;
437 /* protected by res_mutex, conn_mutex and req_mutex */
444 LDAPMessage **result )
447 LDAPMessage *newmsg, *l, *prev;
452 LDAPRequest *lr, *tmplr, dummy_lr = { 0 };
454 int rc, refer_cnt, hadref, simple_request, err;
457 #ifdef LDAP_CONNECTIONLESS
458 LDAPMessage *tmp = NULL, *chain_head = NULL;
459 int moremsgs = 0, isv2 = 0;
462 assert( ld != NULL );
463 assert( lc != NULL );
465 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
466 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
467 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
469 Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
470 (void *)ld, msgid, all );
473 if ( lc->lconn_ber == NULL ) {
474 lc->lconn_ber = ldap_alloc_ber_with_options( ld );
476 if ( lc->lconn_ber == NULL ) {
482 assert( LBER_VALID (ber) );
484 /* get the next message */
486 #ifdef LDAP_CONNECTIONLESS
487 if ( LDAP_IS_UDP(ld) ) {
488 struct sockaddr_storage from;
489 ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr_storage) );
490 if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
494 tag = ber_get_next( lc->lconn_sb, &len, ber );
496 case LDAP_TAG_MESSAGE:
498 * We read a complete message.
499 * The connection should no longer need this ber.
501 lc->lconn_ber = NULL;
507 Debug( LDAP_DEBUG_CONNS,
508 "ber_get_next failed.\n", 0, 0, 0 );
510 if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
511 if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
512 ld->ld_errno = LDAP_SERVER_DOWN;
514 lc->lconn_status = 0;
518 ld->ld_errno = LDAP_LOCAL_ERROR;
523 if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
525 ld->ld_errno = LDAP_DECODING_ERROR;
529 /* id == 0 iff unsolicited notification message (RFC 4511) */
531 /* id < 0 is invalid, just toss it. FIXME: should we disconnect? */
536 /* if it's been abandoned, toss it */
538 if ( ldap_abandoned( ld, id ) ) {
539 /* the message type */
540 tag = ber_peek_tag( ber, &len );
542 case LDAP_RES_SEARCH_ENTRY:
543 case LDAP_RES_SEARCH_REFERENCE:
544 case LDAP_RES_INTERMEDIATE:
549 /* there's no need to keep the id
550 * in the abandoned list any longer */
551 ldap_mark_abandoned( ld, id );
555 Debug( LDAP_DEBUG_ANY,
556 "abandoned/discarded ld %p msgid %d message type %s\n",
557 (void *)ld, id, ldap_int_msgtype2str( tag ) );
561 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
564 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
567 lr = ldap_find_request_by_msgid( ld, id );
569 const char *msg = "unknown";
571 /* the message type */
572 tag = ber_peek_tag( ber, &len );
578 msg = ldap_int_msgtype2str( tag );
582 Debug( LDAP_DEBUG_ANY,
583 "no request for response on ld %p msgid %d message type %s (tossing)\n",
584 (void *)ld, id, msg );
589 #ifdef LDAP_CONNECTIONLESS
590 if ( LDAP_IS_UDP(ld) && isv2 ) {
591 ber_scanf(ber, "x{");
598 /* the message type */
599 tag = ber_peek_tag( ber, &len );
600 if ( tag == LBER_ERROR ) {
601 ld->ld_errno = LDAP_DECODING_ERROR;
606 Debug( LDAP_DEBUG_TRACE,
607 "read1msg: ld %p msgid %d message type %s\n",
608 (void *)ld, id, ldap_int_msgtype2str( tag ) );
611 /* unsolicited notification message (RFC 4511) */
612 if ( tag != LDAP_RES_EXTENDED ) {
616 /* strictly speaking, it's an error; from RFC 4511:
618 4.4. Unsolicited Notification
620 An unsolicited notification is an LDAPMessage sent from the server to
621 the client that is not in response to any LDAPMessage received by the
622 server. It is used to signal an extraordinary condition in the
623 server or in the LDAP session between the client and the server. The
624 notification is of an advisory nature, and the server will not expect
625 any response to be returned from the client.
627 The unsolicited notification is structured as an LDAPMessage in which
628 the messageID is zero and protocolOp is set to the extendedResp
629 choice using the ExtendedResponse type (See Section 4.12). The
630 responseName field of the ExtendedResponse always contains an LDAPOID
631 that is unique for this notification.
633 * however, since unsolicited responses
634 * are of advisory nature, better
639 ld->ld_errno = LDAP_DECODING_ERROR;
650 hadref = simple_request = 0;
651 rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */
652 lr->lr_res_msgtype = tag;
655 * Check for V3 search reference
657 if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
658 if ( ld->ld_version > LDAP_VERSION2 ) {
659 /* This is a V3 search reference */
660 if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
661 lr->lr_parent != NULL )
666 /* Get the referral list */
667 if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
668 rc = LDAP_DECODING_ERROR;
671 /* Note: refs array is freed by ldap_chase_v3referrals */
672 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
673 1, &lr->lr_res_error, &hadref );
674 if ( refer_cnt > 0 ) {
675 /* successfully chased reference */
676 /* If haven't got end search, set chasing referrals */
677 if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
678 lr->lr_status = LDAP_REQST_CHASINGREFS;
679 Debug( LDAP_DEBUG_TRACE,
680 "read1msg: search ref chased, "
681 "mark request chasing refs, "
683 lr->lr_msgid, 0, 0 );
690 } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
691 /* All results that just return a status, i.e. don't return data
692 * go through the following code. This code also chases V2 referrals
693 * and checks if all referrals have been chased.
695 char *lr_res_error = NULL;
697 tmpber = *ber; /* struct copy */
698 if ( ber_scanf( &tmpber, "{eAA", &lderr,
699 &lr->lr_res_matched, &lr_res_error )
702 if ( lr_res_error != NULL ) {
703 if ( lr->lr_res_error != NULL ) {
704 (void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
705 LDAP_FREE( (char *)lr_res_error );
708 lr->lr_res_error = lr_res_error;
713 /* Do we need to check for referrals? */
714 if ( tag != LDAP_RES_BIND &&
715 ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
716 lr->lr_parent != NULL ))
721 /* Check if V3 referral */
722 if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
723 if ( ld->ld_version > LDAP_VERSION2 ) {
724 /* Get the referral list */
725 if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
726 rc = LDAP_DECODING_ERROR;
727 lr->lr_status = LDAP_REQST_COMPLETED;
728 Debug( LDAP_DEBUG_TRACE,
729 "read1msg: referral decode error, "
730 "mark request completed, ld %p msgid %d\n",
731 (void *)ld, lr->lr_msgid, 0 );
734 /* Chase the referral
735 * refs array is freed by ldap_chase_v3referrals
737 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
738 0, &lr->lr_res_error, &hadref );
739 lr->lr_status = LDAP_REQST_COMPLETED;
740 Debug( LDAP_DEBUG_TRACE,
741 "read1msg: referral %s chased, "
742 "mark request completed, ld %p msgid %d\n",
743 refer_cnt > 0 ? "" : "not",
744 (void *)ld, lr->lr_msgid);
745 if ( refer_cnt < 0 ) {
753 case LDAP_COMPARE_TRUE:
754 case LDAP_COMPARE_FALSE:
758 if ( lr->lr_res_error == NULL ) {
762 /* pedantic, should never happen */
763 if ( lr->lr_res_error[ 0 ] == '\0' ) {
764 LDAP_FREE( lr->lr_res_error );
765 lr->lr_res_error = NULL;
769 /* V2 referrals are in error string */
770 refer_cnt = ldap_chase_referrals( ld, lr,
771 &lr->lr_res_error, -1, &hadref );
772 lr->lr_status = LDAP_REQST_COMPLETED;
773 Debug( LDAP_DEBUG_TRACE,
774 "read1msg: V2 referral chased, "
775 "mark request completed, id = %d\n",
776 lr->lr_msgid, 0, 0 );
782 /* save errno, message, and matched string */
783 if ( !hadref || lr->lr_res_error == NULL ) {
785 lderr == LDAP_PARTIAL_RESULTS
786 ? LDAP_SUCCESS : lderr;
788 } else if ( ld->ld_errno != LDAP_SUCCESS ) {
789 lr->lr_res_errno = ld->ld_errno;
792 lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
796 /* in any case, don't leave any lr_res_error 'round */
797 if ( lr_res_error ) {
798 LDAP_FREE( lr_res_error );
801 Debug( LDAP_DEBUG_TRACE,
802 "read1msg: ld %p %d new referrals\n",
803 (void *)ld, refer_cnt, 0 );
805 if ( refer_cnt != 0 ) { /* chasing referrals */
808 if ( refer_cnt < 0 ) {
809 ldap_return_request( ld, lr, 0 );
810 return( -1 ); /* fatal error */
812 lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
813 if ( lr->lr_res_matched ) {
814 LDAP_FREE( lr->lr_res_matched );
815 lr->lr_res_matched = NULL;
819 if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
820 /* request without any referrals */
821 simple_request = ( hadref ? 0 : 1 );
824 /* request with referrals or child request */
829 lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
830 Debug( LDAP_DEBUG_TRACE,
831 "read1msg: mark request completed, ld %p msgid %d\n",
832 (void *)ld, lr->lr_msgid, 0);
834 while ( lr->lr_parent != NULL ) {
835 merge_error_info( ld, lr->lr_parent, lr );
838 if ( --lr->lr_outrefcnt > 0 ) {
839 break; /* not completely done yet */
842 /* ITS#6744: Original lr was refcounted when we retrieved it,
843 * must release it now that we're working with the parent
845 if ( tmplr->lr_parent ) {
846 ldap_return_request( ld, tmplr, 0 );
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_TRACE, "request done: ld %p msgid %d\n",
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 * RFC 4511 unsolicited (id == 0) responses
893 * shouldn't necessarily end the connection
895 if ( lc != NULL && id != 0 ) {
903 if ( lr != &dummy_lr ) {
904 ldap_return_request( ld, lr, 0 );
913 /* try to handle unsolicited responses as appropriate */
914 if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
917 tag = ber_peek_tag( &tmpber, &len );
919 /* we have a res oid */
920 if ( tag == LDAP_TAG_EXOP_RES_OID ) {
921 static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
922 struct berval resoid = BER_BVNULL;
924 if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
925 ld->ld_errno = LDAP_DECODING_ERROR;
930 assert( !BER_BVISEMPTY( &resoid ) );
932 is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
934 tag = ber_peek_tag( &tmpber, &len );
937 #if 0 /* don't need right now */
938 /* we have res data */
939 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
940 struct berval resdata;
942 if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
943 ld->ld_errno = LDAP_DECODING_ERROR;
952 /* handle RFC 4511 "Notice of Disconnection" locally */
955 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
956 ld->ld_errno = LDAP_DECODING_ERROR;
961 /* get rid of the connection... */
966 /* need to return -1, because otherwise
967 * a valid result is expected */
968 ld->ld_errno = lderr;
973 /* make a new ldap message */
974 newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
975 if ( newmsg == NULL ) {
976 ld->ld_errno = LDAP_NO_MEMORY;
979 newmsg->lm_msgid = (int)id;
980 newmsg->lm_msgtype = tag;
981 newmsg->lm_ber = ber;
982 newmsg->lm_chain_tail = newmsg;
984 #ifdef LDAP_CONNECTIONLESS
985 /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
986 * the responses are all a sequence wrapped in one message. In
987 * LDAPv3 each response is in its own message. The datagram must
988 * end with a SearchResult. We can't just parse each response in
989 * separate calls to try_read1msg because the header info is only
990 * present at the beginning of the datagram, not at the beginning
991 * of each response. So parse all the responses at once and queue
992 * them up, then pull off the first response to return to the
993 * caller when all parsing is complete.
995 if ( LDAP_IS_UDP(ld) ) {
996 /* If not a result, look for more */
997 if ( tag != LDAP_RES_SEARCH_RESULT ) {
1001 /* LDAPv2: dup the current ber, skip past the current
1002 * response, and see if there are any more after it.
1004 ber = ber_dup( ber );
1005 ber_scanf( ber, "x" );
1006 if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
1007 /* There's more - dup the ber buffer so they can all be
1008 * individually freed by ldap_msgfree.
1011 ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1012 bv.bv_val = LDAP_MALLOC( len );
1015 ber_read( ber, bv.bv_val, len );
1017 ber_init2( ber, &bv, ld->ld_lberoptions );
1021 /* LDAPv3: Just allocate a new ber. Since this is a buffered
1022 * datagram, if the sockbuf is readable we still have data
1025 ber = ldap_alloc_ber_with_options( ld );
1026 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1028 /* set up response chain */
1029 if ( tmp == NULL ) {
1030 newmsg->lm_next = ld->ld_responses;
1031 ld->ld_responses = newmsg;
1032 chain_head = newmsg;
1034 tmp->lm_chain = newmsg;
1036 chain_head->lm_chain_tail = newmsg;
1038 /* "ok" means there's more to parse */
1047 /* got to end of datagram without a SearchResult. Free
1048 * our dup'd ber, but leave any buffer alone. For v2 case,
1049 * the previous response is still using this buffer. For v3,
1050 * the new ber has no buffer to free yet.
1055 } else if ( moremsgs ) {
1056 /* got search result, and we had multiple responses in 1 datagram.
1057 * stick the result onto the end of the chain, and then pull the
1058 * first response off the head of the chain.
1060 tmp->lm_chain = newmsg;
1061 chain_head->lm_chain_tail = newmsg;
1062 *result = chkResponseList( ld, msgid, all );
1063 ld->ld_errno = LDAP_SUCCESS;
1064 return( (*result)->lm_msgtype );
1067 #endif /* LDAP_CONNECTIONLESS */
1069 /* is this the one we're looking for? */
1070 if ( msgid == LDAP_RES_ANY || id == msgid ) {
1071 if ( all == LDAP_MSG_ONE
1072 || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1073 && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1074 && newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE
1075 && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1078 ld->ld_errno = LDAP_SUCCESS;
1081 } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1082 foundit = 1; /* return the chain later */
1087 * if not, we must add it to the list of responses. if
1088 * the msgid is already there, it must be part of an existing
1093 for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1094 if ( l->lm_msgid == newmsg->lm_msgid ) {
1100 /* not part of an existing search response */
1107 newmsg->lm_next = ld->ld_responses;
1108 ld->ld_responses = newmsg;
1112 Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
1113 (void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1115 /* part of a search response - add to end of list of entries */
1116 l->lm_chain_tail->lm_chain = newmsg;
1117 l->lm_chain_tail = newmsg;
1119 /* return the whole chain if that's what we were looking for */
1121 if ( prev == NULL ) {
1122 ld->ld_responses = l->lm_next;
1124 prev->lm_next = l->lm_next;
1131 ld->ld_errno = LDAP_SUCCESS;
1134 if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1137 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
1142 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1150 ber = ldap_alloc_ber_with_options( ld );
1153 ld->ld_errno = LDAP_NO_MEMORY;
1157 if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1158 lr->lr_res_msgtype, lr->lr_res_errno,
1159 lr->lr_res_matched ? lr->lr_res_matched : "",
1160 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1162 ld->ld_errno = LDAP_ENCODING_ERROR;
1164 return( LBER_ERROR );
1167 ber_reset( ber, 1 );
1169 if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1170 ld->ld_errno = LDAP_DECODING_ERROR;
1172 return( LBER_ERROR );
1175 if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1176 ld->ld_errno = LDAP_DECODING_ERROR;
1178 return( LBER_ERROR );
1181 tag = ber_peek_tag( ber, &len );
1183 if ( tag == LBER_ERROR ) {
1184 ld->ld_errno = LDAP_DECODING_ERROR;
1186 return( LBER_ERROR );
1195 * Merge error information in "lr" with "parentr" error code and string.
1198 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1200 if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1201 parentr->lr_res_errno = lr->lr_res_errno;
1202 if ( lr->lr_res_error != NULL ) {
1203 (void)ldap_append_referral( ld, &parentr->lr_res_error,
1207 } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1208 parentr->lr_res_errno == LDAP_SUCCESS )
1210 parentr->lr_res_errno = lr->lr_res_errno;
1211 if ( parentr->lr_res_error != NULL ) {
1212 LDAP_FREE( parentr->lr_res_error );
1214 parentr->lr_res_error = lr->lr_res_error;
1215 lr->lr_res_error = NULL;
1216 if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1217 if ( parentr->lr_res_matched != NULL ) {
1218 LDAP_FREE( parentr->lr_res_matched );
1220 parentr->lr_res_matched = lr->lr_res_matched;
1221 lr->lr_res_matched = NULL;
1225 Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
1226 parentr->lr_msgid, 0, 0 );
1227 Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1228 parentr->lr_res_errno,
1229 parentr->lr_res_error ? parentr->lr_res_error : "",
1230 parentr->lr_res_matched ? parentr->lr_res_matched : "" );
1236 ldap_msgtype( LDAPMessage *lm )
1238 assert( lm != NULL );
1239 return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1244 ldap_msgid( LDAPMessage *lm )
1246 assert( lm != NULL );
1248 return ( lm != NULL ) ? lm->lm_msgid : -1;
1253 ldap_int_msgtype2str( ber_tag_t tag )
1256 case LDAP_RES_ADD: return "add";
1257 case LDAP_RES_BIND: return "bind";
1258 case LDAP_RES_COMPARE: return "compare";
1259 case LDAP_RES_DELETE: return "delete";
1260 case LDAP_RES_EXTENDED: return "extended-result";
1261 case LDAP_RES_INTERMEDIATE: return "intermediate";
1262 case LDAP_RES_MODIFY: return "modify";
1263 case LDAP_RES_RENAME: return "rename";
1264 case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1265 case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1266 case LDAP_RES_SEARCH_RESULT: return "search-result";
1272 ldap_msgfree( LDAPMessage *lm )
1277 Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1279 for ( ; lm != NULL; lm = next ) {
1280 next = lm->lm_chain;
1281 type = lm->lm_msgtype;
1282 ber_free( lm->lm_ber, 1 );
1283 LDAP_FREE( (char *) lm );
1290 * ldap_msgdelete - delete a message. It returns:
1291 * 0 if the entire message was deleted
1292 * -1 if the message was not found, or only part of it was found
1295 ldap_msgdelete( LDAP *ld, int msgid )
1297 LDAPMessage *lm, *prev;
1300 assert( ld != NULL );
1302 Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1303 (void *)ld, msgid, 0 );
1305 LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
1307 for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1308 if ( lm->lm_msgid == msgid ) {
1318 if ( prev == NULL ) {
1319 ld->ld_responses = lm->lm_next;
1321 prev->lm_next = lm->lm_next;
1324 LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
1326 switch ( ldap_msgfree( lm ) ) {
1327 case LDAP_RES_SEARCH_ENTRY:
1328 case LDAP_RES_SEARCH_REFERENCE:
1329 case LDAP_RES_INTERMEDIATE:
1345 * return the location of the message id in the array of abandoned
1346 * message ids, or -1
1349 ldap_abandoned( LDAP *ld, ber_int_t msgid )
1352 assert( msgid >= 0 );
1354 LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
1355 ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx );
1356 LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
1361 * ldap_mark_abandoned
1364 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid )
1368 assert( msgid >= 0 );
1369 LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
1370 ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx );
1371 if (ret <= 0) { /* error or already deleted by another thread */
1372 LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
1375 /* still in abandoned array, so delete */
1376 ret = ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,
1378 LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );