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%02lx\n",
248 (void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
260 struct timeval *timeout,
261 LDAPMessage **result )
264 struct timeval tv = { 0 },
266 start_time_tv = { 0 },
270 assert( ld != NULL );
271 assert( result != NULL );
273 #ifdef LDAP_R_COMPILE
274 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
277 if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) {
278 tv = ld->ld_options.ldo_tm_api;
283 if ( timeout == NULL ) {
284 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
285 (void *)ld, msgid, 0 );
287 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n",
288 (void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec );
290 #endif /* LDAP_DEBUG */
292 if ( timeout != NULL ) {
296 #ifdef HAVE_GETTIMEOFDAY
297 gettimeofday( &start_time_tv, NULL );
298 #else /* ! HAVE_GETTIMEOFDAY */
299 time( &start_time_tv.tv_sec );
300 start_time_tv.tv_usec = 0;
301 #endif /* ! HAVE_GETTIMEOFDAY */
304 rc = LDAP_MSG_X_KEEP_LOOKING;
305 while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
307 if ( ldap_debug & LDAP_DEBUG_TRACE ) {
308 Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
309 (void *)ld, msgid, all );
310 #ifdef LDAP_R_COMPILE
311 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
313 ldap_dump_connection( ld, ld->ld_conns, 1 );
314 #ifdef LDAP_R_COMPILE
315 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
316 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
318 ldap_dump_requests_and_responses( ld );
319 #ifdef LDAP_R_COMPILE
320 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
323 #endif /* LDAP_DEBUG */
325 if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
326 rc = (*result)->lm_msgtype;
331 #ifdef LDAP_R_COMPILE
332 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
334 for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
335 if ( ber_sockbuf_ctrl( lc->lconn_sb,
336 LBER_SB_OPT_DATA_READY, NULL ) )
338 #ifdef LDAP_R_COMPILE
339 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
341 rc = try_read1msg( ld, msgid, all, &lc, result );
342 #ifdef LDAP_R_COMPILE
343 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
349 #ifdef LDAP_R_COMPILE
350 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
354 rc = ldap_int_select( ld, tvp );
357 Debug( LDAP_DEBUG_TRACE,
358 "ldap_int_select returned -1: errno %d\n",
359 sock_errno(), 0, 0 );
363 if ( rc == 0 || ( rc == -1 && (
364 !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
365 || sock_errno() != EINTR ) ) )
367 ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
373 rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */
376 rc = LDAP_MSG_X_KEEP_LOOKING;
377 #ifdef LDAP_R_COMPILE
378 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
380 if ( ld->ld_requests &&
381 ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
382 ldap_is_write_ready( ld,
383 ld->ld_requests->lr_conn->lconn_sb ) )
385 ldap_int_flush_request( ld, ld->ld_requests );
387 #ifdef LDAP_R_COMPILE
388 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
389 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
391 for ( lc = ld->ld_conns;
392 rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL; )
394 if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
395 ldap_is_read_ready( ld, lc->lconn_sb ) )
397 #ifdef LDAP_R_COMPILE
398 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
400 rc = try_read1msg( ld, msgid, all, &lc, result );
401 #ifdef LDAP_R_COMPILE
402 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
405 /* if lc gets free()'d,
406 * there's no guarantee
407 * lc->lconn_next is still
408 * sane; better restart
412 /* don't get to next conn! */
420 #ifdef LDAP_R_COMPILE
421 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
427 if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
428 struct timeval curr_time_tv = { 0 },
429 delta_time_tv = { 0 };
431 #ifdef HAVE_GETTIMEOFDAY
432 gettimeofday( &curr_time_tv, NULL );
433 #else /* ! HAVE_GETTIMEOFDAY */
434 time( &curr_time_tv.tv_sec );
435 curr_time_tv.tv_usec = 0;
436 #endif /* ! HAVE_GETTIMEOFDAY */
438 /* delta_time = tmp_time - start_time */
439 delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
440 delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
441 if ( delta_time_tv.tv_usec < 0 ) {
442 delta_time_tv.tv_sec--;
443 delta_time_tv.tv_usec += 1000000;
446 /* tv0 < delta_time ? */
447 if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
448 ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
450 rc = 0; /* timed out */
451 ld->ld_errno = LDAP_TIMEOUT;
455 /* tv0 -= delta_time */
456 tv0.tv_sec -= delta_time_tv.tv_sec;
457 tv0.tv_usec -= delta_time_tv.tv_usec;
458 if ( tv0.tv_usec < 0 ) {
460 tv0.tv_usec += 1000000;
463 tv.tv_sec = tv0.tv_sec;
464 tv.tv_usec = tv0.tv_usec;
466 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n",
467 (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
469 start_time_tv.tv_sec = curr_time_tv.tv_sec;
470 start_time_tv.tv_usec = curr_time_tv.tv_usec;
484 LDAPMessage **result )
487 LDAPMessage *newmsg, *l, *prev;
493 LDAPRequest *lr, *tmplr, dummy_lr = { 0 };
496 int rc, refer_cnt, hadref, simple_request;
499 #ifdef LDAP_CONNECTIONLESS
500 LDAPMessage *tmp = NULL, *chain_head = NULL;
501 int moremsgs = 0, isv2 = 0;
504 assert( ld != NULL );
505 assert( lcp != NULL );
506 assert( *lcp != NULL );
508 #ifdef LDAP_R_COMPILE
509 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
512 Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
513 (void *)ld, msgid, all );
518 if ( lc->lconn_ber == NULL ) {
519 lc->lconn_ber = ldap_alloc_ber_with_options( ld );
521 if ( lc->lconn_ber == NULL ) {
527 assert( LBER_VALID (ber) );
529 /* get the next message */
531 #ifdef LDAP_CONNECTIONLESS
532 if ( LDAP_IS_UDP(ld) ) {
533 struct sockaddr from;
534 ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr) );
535 if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
539 tag = ber_get_next( lc->lconn_sb, &len, ber );
541 case LDAP_TAG_MESSAGE:
543 * We read a complete message.
544 * The connection should no longer need this ber.
546 lc->lconn_ber = NULL;
551 Debug( LDAP_DEBUG_CONNS,
552 "ber_get_next failed.\n", 0, 0, 0 );
555 if ( sock_errno() == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
558 if ( sock_errno() == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
560 ld->ld_errno = LDAP_SERVER_DOWN;
561 #ifdef LDAP_R_COMPILE
562 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
564 ldap_free_connection( ld, lc, 1, 0 );
565 #ifdef LDAP_R_COMPILE
566 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
572 ld->ld_errno = LDAP_LOCAL_ERROR;
577 if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
579 ld->ld_errno = LDAP_DECODING_ERROR;
583 /* id == 0 iff unsolicited notification message (RFC 4511) */
585 /* if it's been abandoned, toss it */
587 if ( ldap_abandoned( ld, id, &idx ) ) {
588 /* the message type */
589 tag = ber_peek_tag( ber, &len );
591 case LDAP_RES_SEARCH_ENTRY:
592 case LDAP_RES_SEARCH_REFERENCE:
593 case LDAP_RES_INTERMEDIATE:
598 /* there's no need to keep the id
599 * in the abandoned list any longer */
600 ldap_mark_abandoned( ld, id, idx );
604 Debug( LDAP_DEBUG_ANY,
605 "abandoned/discarded ld %p msgid %ld message type %s\n",
606 (void *)ld, (long)id, ldap_int_msgtype2str( tag ) );
610 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
613 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
616 lr = ldap_find_request_by_msgid( ld, id );
618 const char *msg = "unknown";
620 /* the message type */
621 tag = ber_peek_tag( ber, &len );
627 msg = ldap_int_msgtype2str( tag );
631 Debug( LDAP_DEBUG_ANY,
632 "no request for response on ld %p msgid %ld message type %s (tossing)\n",
633 (void *)ld, (long)id, msg );
638 #ifdef LDAP_CONNECTIONLESS
639 if ( LDAP_IS_UDP(ld) && isv2 ) {
640 ber_scanf(ber, "x{");
646 /* the message type */
647 tag = ber_peek_tag( ber, &len );
648 if ( tag == LBER_ERROR ) {
649 ld->ld_errno = LDAP_DECODING_ERROR;
654 Debug( LDAP_DEBUG_TRACE,
655 "read1msg: ld %p msgid %ld message type %s\n",
656 (void *)ld, (long)lr->lr_msgid, ldap_int_msgtype2str( tag ) );
659 /* unsolicited notification message (RFC 4511) */
660 if ( tag != LDAP_RES_EXTENDED ) {
664 /* strictly speaking, it's an error; from RFC 4511:
666 4.4. Unsolicited Notification
668 An unsolicited notification is an LDAPMessage sent from the server to
669 the client that is not in response to any LDAPMessage received by the
670 server. It is used to signal an extraordinary condition in the
671 server or in the LDAP session between the client and the server. The
672 notification is of an advisory nature, and the server will not expect
673 any response to be returned from the client.
675 The unsolicited notification is structured as an LDAPMessage in which
676 the messageID is zero and protocolOp is set to the extendedResp
677 choice using the ExtendedResponse type (See Section 4.12). The
678 responseName field of the ExtendedResponse always contains an LDAPOID
679 that is unique for this notification.
681 * however, since unsolicited responses
682 * are of advisory nature, better
687 ld->ld_errno = LDAP_DECODING_ERROR;
698 hadref = simple_request = 0;
699 rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */
700 lr->lr_res_msgtype = tag;
703 * Check for V3 search reference
705 if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
706 if ( ld->ld_version > LDAP_VERSION2 ) {
707 /* This is a V3 search reference */
708 if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
709 lr->lr_parent != NULL )
714 /* Get the referral list */
715 if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
716 rc = LDAP_DECODING_ERROR;
719 /* Note: refs array is freed by ldap_chase_v3referrals */
720 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
721 1, &lr->lr_res_error, &hadref );
722 if ( refer_cnt > 0 ) {
723 /* successfully chased reference */
724 /* If haven't got end search, set chasing referrals */
725 if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
726 lr->lr_status = LDAP_REQST_CHASINGREFS;
727 Debug( LDAP_DEBUG_TRACE,
728 "read1msg: search ref chased, "
729 "mark request chasing refs, "
731 lr->lr_msgid, 0, 0 );
738 } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
739 /* All results that just return a status, i.e. don't return data
740 * go through the following code. This code also chases V2 referrals
741 * and checks if all referrals have been chased.
743 char *lr_res_error = NULL;
745 tmpber = *ber; /* struct copy */
746 if ( ber_scanf( &tmpber, "{eAA", &lderr,
747 &lr->lr_res_matched, &lr_res_error )
750 if ( lr_res_error != NULL ) {
751 if ( lr->lr_res_error != NULL ) {
752 (void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
753 LDAP_FREE( (char *)lr_res_error );
756 lr->lr_res_error = lr_res_error;
761 /* Do we need to check for referrals? */
762 if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
763 lr->lr_parent != NULL )
768 /* Check if V3 referral */
769 if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
770 if ( ld->ld_version > LDAP_VERSION2 ) {
771 /* Get the referral list */
772 if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
773 rc = LDAP_DECODING_ERROR;
774 lr->lr_status = LDAP_REQST_COMPLETED;
775 Debug( LDAP_DEBUG_TRACE,
776 "read1msg: referral decode error, "
777 "mark request completed, ld %p msgid %d\n",
778 (void *)ld, lr->lr_msgid, 0 );
781 /* Chase the referral
782 * refs array is freed by ldap_chase_v3referrals
784 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
785 0, &lr->lr_res_error, &hadref );
786 lr->lr_status = LDAP_REQST_COMPLETED;
787 Debug( LDAP_DEBUG_TRACE,
788 "read1msg: referral %s chased, "
789 "mark request completed, ld %p msgid %d\n",
790 refer_cnt > 0 ? "" : "not",
791 (void *)ld, lr->lr_msgid);
792 if ( refer_cnt < 0 ) {
800 case LDAP_COMPARE_TRUE:
801 case LDAP_COMPARE_FALSE:
805 if ( lr->lr_res_error == NULL ) {
809 /* pedantic, should never happen */
810 if ( lr->lr_res_error[ 0 ] == '\0' ) {
811 LDAP_FREE( lr->lr_res_error );
812 lr->lr_res_error = NULL;
816 /* V2 referrals are in error string */
817 refer_cnt = ldap_chase_referrals( ld, lr,
818 &lr->lr_res_error, -1, &hadref );
819 lr->lr_status = LDAP_REQST_COMPLETED;
820 Debug( LDAP_DEBUG_TRACE,
821 "read1msg: V2 referral chased, "
822 "mark request completed, id = %d\n",
823 lr->lr_msgid, 0, 0 );
829 /* save errno, message, and matched string */
830 if ( !hadref || lr->lr_res_error == NULL ) {
832 lderr == LDAP_PARTIAL_RESULTS
833 ? LDAP_SUCCESS : lderr;
835 } else if ( ld->ld_errno != LDAP_SUCCESS ) {
836 lr->lr_res_errno = ld->ld_errno;
839 lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
843 /* in any case, don't leave any lr_res_error 'round */
844 if ( lr_res_error ) {
845 LDAP_FREE( lr_res_error );
848 Debug( LDAP_DEBUG_TRACE,
849 "read1msg: ld %p %d new referrals\n",
850 (void *)ld, refer_cnt, 0 );
852 if ( refer_cnt != 0 ) { /* chasing referrals */
855 if ( refer_cnt < 0 ) {
856 ldap_return_request( ld, lr, 0 );
857 return( -1 ); /* fatal error */
859 lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
862 if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
863 /* request without any referrals */
864 simple_request = ( hadref ? 0 : 1 );
867 /* request with referrals or child request */
872 lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
873 Debug( LDAP_DEBUG_TRACE,
874 "read1msg: mark request completed, ld %p msgid %d\n",
875 (void *)ld, lr->lr_msgid, 0);
876 while ( lr->lr_parent != NULL ) {
877 merge_error_info( ld, lr->lr_parent, lr );
880 if ( --lr->lr_outrefcnt > 0 ) {
881 break; /* not completely done yet */
885 /* Check if all requests are finished, lr is now parent */
887 if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
888 for ( tmplr = lr->lr_child;
890 tmplr = tmplr->lr_refnext )
892 if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
896 /* This is the parent request if the request has referrals */
897 if ( lr->lr_outrefcnt <= 0 &&
898 lr->lr_parent == NULL &&
902 tag = lr->lr_res_msgtype;
903 Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %ld\n",
904 (void *)ld, (long) id, 0 );
905 Debug( LDAP_DEBUG_TRACE,
906 "res_errno: %d, res_error: <%s>, "
907 "res_matched: <%s>\n",
909 lr->lr_res_error ? lr->lr_res_error : "",
910 lr->lr_res_matched ? lr->lr_res_matched : "" );
911 if ( !simple_request ) {
914 if ( build_result_ber( ld, &ber, lr )
917 rc = -1; /* fatal error */
921 if ( lr != &dummy_lr ) {
922 ldap_return_request( ld, lr, 1 );
928 * RF 4511 unsolicited (id == 0) responses
929 * shouldn't necessarily end the connection
931 if ( lc != NULL && id != 0 ) {
932 #ifdef LDAP_R_COMPILE
933 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
935 ldap_free_connection( ld, lc, 0, 1 );
936 #ifdef LDAP_R_COMPILE
937 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
945 if ( lr != &dummy_lr ) {
946 ldap_return_request( ld, lr, 0 );
955 /* try to handle unsolicited responses as appropriate */
956 if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
959 tag = ber_peek_tag( &tmpber, &len );
961 /* we have a res oid */
962 if ( tag == LDAP_TAG_EXOP_RES_OID ) {
963 static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
964 struct berval resoid = BER_BVNULL;
966 if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
967 ld->ld_errno = LDAP_DECODING_ERROR;
972 assert( !BER_BVISEMPTY( &resoid ) );
974 is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
976 tag = ber_peek_tag( &tmpber, &len );
979 #if 0 /* don't need right now */
980 /* we have res data */
981 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
982 struct berval resdata;
984 if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
985 ld->ld_errno = LDAP_DECODING_ERROR;
994 /* handle RFC 4511 "Notice of Disconnection" locally */
997 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
998 ld->ld_errno = LDAP_DECODING_ERROR;
1003 /* get rid of the connection... */
1005 #ifdef LDAP_R_COMPILE
1006 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
1008 ldap_free_connection( ld, lc, 0, 1 );
1009 #ifdef LDAP_R_COMPILE
1010 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
1015 /* need to return -1, because otherwise
1016 * a valid result is expected */
1021 /* make a new ldap message */
1022 newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
1023 if ( newmsg == NULL ) {
1024 ld->ld_errno = LDAP_NO_MEMORY;
1027 newmsg->lm_msgid = (int)id;
1028 newmsg->lm_msgtype = tag;
1029 newmsg->lm_ber = ber;
1030 newmsg->lm_chain_tail = newmsg;
1032 #ifdef LDAP_CONNECTIONLESS
1033 /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
1034 * the responses are all a sequence wrapped in one message. In
1035 * LDAPv3 each response is in its own message. The datagram must
1036 * end with a SearchResult. We can't just parse each response in
1037 * separate calls to try_read1msg because the header info is only
1038 * present at the beginning of the datagram, not at the beginning
1039 * of each response. So parse all the responses at once and queue
1040 * them up, then pull off the first response to return to the
1041 * caller when all parsing is complete.
1043 if ( LDAP_IS_UDP(ld) ) {
1044 /* If not a result, look for more */
1045 if ( tag != LDAP_RES_SEARCH_RESULT ) {
1049 /* LDAPv2: dup the current ber, skip past the current
1050 * response, and see if there are any more after it.
1052 ber = ber_dup( ber );
1053 ber_scanf( ber, "x" );
1054 if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
1055 /* There's more - dup the ber buffer so they can all be
1056 * individually freed by ldap_msgfree.
1059 ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1060 bv.bv_val = LDAP_MALLOC( len );
1063 ber_read( ber, bv.bv_val, len );
1065 ber_init2( ber, &bv, ld->ld_lberoptions );
1069 /* LDAPv3: Just allocate a new ber. Since this is a buffered
1070 * datagram, if the sockbuf is readable we still have data
1073 ber = ldap_alloc_ber_with_options( ld );
1074 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1076 /* set up response chain */
1077 if ( tmp == NULL ) {
1078 newmsg->lm_next = ld->ld_responses;
1079 ld->ld_responses = newmsg;
1080 chain_head = newmsg;
1082 tmp->lm_chain = newmsg;
1084 chain_head->lm_chain_tail = newmsg;
1086 /* "ok" means there's more to parse */
1095 /* got to end of datagram without a SearchResult. Free
1096 * our dup'd ber, but leave any buffer alone. For v2 case,
1097 * the previous response is still using this buffer. For v3,
1098 * the new ber has no buffer to free yet.
1103 } else if ( moremsgs ) {
1104 /* got search result, and we had multiple responses in 1 datagram.
1105 * stick the result onto the end of the chain, and then pull the
1106 * first response off the head of the chain.
1108 tmp->lm_chain = newmsg;
1109 chain_head->lm_chain_tail = newmsg;
1110 *result = chkResponseList( ld, msgid, all );
1111 ld->ld_errno = LDAP_SUCCESS;
1112 return( (*result)->lm_msgtype );
1115 #endif /* LDAP_CONNECTIONLESS */
1117 /* is this the one we're looking for? */
1118 if ( msgid == LDAP_RES_ANY || id == msgid ) {
1119 if ( all == LDAP_MSG_ONE
1120 || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1121 && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1122 && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1125 ld->ld_errno = LDAP_SUCCESS;
1128 } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1129 foundit = 1; /* return the chain later */
1134 * if not, we must add it to the list of responses. if
1135 * the msgid is already there, it must be part of an existing
1140 for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1141 if ( l->lm_msgid == newmsg->lm_msgid ) {
1147 /* not part of an existing search response */
1154 newmsg->lm_next = ld->ld_responses;
1155 ld->ld_responses = newmsg;
1159 Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %ld type %ld:\n",
1160 (void *)ld, (long) newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1162 /* part of a search response - add to end of list of entries */
1163 l->lm_chain_tail->lm_chain = newmsg;
1164 l->lm_chain_tail = newmsg;
1166 /* return the whole chain if that's what we were looking for */
1168 if ( prev == NULL ) {
1169 ld->ld_responses = l->lm_next;
1171 prev->lm_next = l->lm_next;
1178 ld->ld_errno = LDAP_SUCCESS;
1181 if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1184 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
1189 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1197 ber = ldap_alloc_ber_with_options( ld );
1200 ld->ld_errno = LDAP_NO_MEMORY;
1204 if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1205 lr->lr_res_msgtype, lr->lr_res_errno,
1206 lr->lr_res_matched ? lr->lr_res_matched : "",
1207 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1209 ld->ld_errno = LDAP_ENCODING_ERROR;
1211 return( LBER_ERROR );
1214 ber_reset( ber, 1 );
1216 if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1217 ld->ld_errno = LDAP_DECODING_ERROR;
1219 return( LBER_ERROR );
1222 if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1223 ld->ld_errno = LDAP_DECODING_ERROR;
1225 return( LBER_ERROR );
1228 tag = ber_peek_tag( ber, &len );
1230 if ( tag == LBER_ERROR ) {
1231 ld->ld_errno = LDAP_DECODING_ERROR;
1233 return( LBER_ERROR );
1242 * Merge error information in "lr" with "parentr" error code and string.
1245 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1247 if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1248 parentr->lr_res_errno = lr->lr_res_errno;
1249 if ( lr->lr_res_error != NULL ) {
1250 (void)ldap_append_referral( ld, &parentr->lr_res_error,
1254 } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1255 parentr->lr_res_errno == LDAP_SUCCESS )
1257 parentr->lr_res_errno = lr->lr_res_errno;
1258 if ( parentr->lr_res_error != NULL ) {
1259 LDAP_FREE( parentr->lr_res_error );
1261 parentr->lr_res_error = lr->lr_res_error;
1262 lr->lr_res_error = NULL;
1263 if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1264 if ( parentr->lr_res_matched != NULL ) {
1265 LDAP_FREE( parentr->lr_res_matched );
1267 parentr->lr_res_matched = lr->lr_res_matched;
1268 lr->lr_res_matched = NULL;
1272 Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
1273 parentr->lr_msgid, 0, 0 );
1274 Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1275 parentr->lr_res_errno,
1276 parentr->lr_res_error ? parentr->lr_res_error : "",
1277 parentr->lr_res_matched ? parentr->lr_res_matched : "" );
1283 ldap_msgtype( LDAPMessage *lm )
1285 assert( lm != NULL );
1286 return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1291 ldap_msgid( LDAPMessage *lm )
1293 assert( lm != NULL );
1295 return ( lm != NULL ) ? lm->lm_msgid : -1;
1300 ldap_int_msgtype2str( ber_tag_t tag )
1303 case LDAP_RES_ADD: return "add";
1304 case LDAP_RES_BIND: return "bind";
1305 case LDAP_RES_COMPARE: return "compare";
1306 case LDAP_RES_DELETE: return "delete";
1307 case LDAP_RES_EXTENDED: return "extended-result";
1308 case LDAP_RES_INTERMEDIATE: return "intermediate";
1309 case LDAP_RES_MODIFY: return "modify";
1310 case LDAP_RES_RENAME: return "rename";
1311 case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1312 case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1313 case LDAP_RES_SEARCH_RESULT: return "search-result";
1319 ldap_msgfree( LDAPMessage *lm )
1324 Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1326 for ( ; lm != NULL; lm = next ) {
1327 next = lm->lm_chain;
1328 type = lm->lm_msgtype;
1329 ber_free( lm->lm_ber, 1 );
1330 LDAP_FREE( (char *) lm );
1337 * ldap_msgdelete - delete a message. It returns:
1338 * 0 if the entire message was deleted
1339 * -1 if the message was not found, or only part of it was found
1342 ldap_msgdelete( LDAP *ld, int msgid )
1344 LDAPMessage *lm, *prev;
1347 assert( ld != NULL );
1349 Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1350 (void *)ld, msgid, 0 );
1352 #ifdef LDAP_R_COMPILE
1353 ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
1356 for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1357 if ( lm->lm_msgid == msgid ) {
1367 if ( prev == NULL ) {
1368 ld->ld_responses = lm->lm_next;
1370 prev->lm_next = lm->lm_next;
1373 #ifdef LDAP_R_COMPILE
1374 ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
1377 switch ( ldap_msgfree( lm ) ) {
1378 case LDAP_RES_SEARCH_ENTRY:
1379 case LDAP_RES_SEARCH_REFERENCE:
1380 case LDAP_RES_INTERMEDIATE:
1396 * return the location of the message id in the array of abandoned
1397 * message ids, or -1
1399 * expects ld_res_mutex to be locked
1402 ldap_abandoned( LDAP *ld, ber_int_t msgid, int *idxp )
1404 #ifdef LDAP_R_COMPILE
1405 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1408 assert( idxp != NULL );
1409 assert( msgid >= 0 );
1410 assert( ld->ld_nabandoned >= 0 );
1412 return ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp );
1416 * ldap_mark_abandoned
1418 * expects ld_res_mutex to be locked
1421 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid, int idx )
1423 #ifdef LDAP_R_COMPILE
1424 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1427 /* NOTE: those assertions are repeated in ldap_int_bisect_delete() */
1429 assert( idx < ld->ld_nabandoned );
1430 assert( ld->ld_abandoned[ idx ] == msgid );
1432 return ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,