1 /* result.c - wait for an ldap result */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 1998-2010 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 )
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 LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
117 rc = wait4msg( ld, msgid, all, timeout, result );
118 LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
129 LDAPMessage *lm, **lastlm, *nextlm;
133 * Look through the list of responses we have received on
134 * this association and see if the response we're interested in
135 * is there. If it is, return it. If not, call wait4msg() to
136 * wait until it arrives or timeout occurs.
139 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
141 Debug( LDAP_DEBUG_TRACE,
142 "ldap_chkResponseList ld %p msgid %d all %d\n",
143 (void *)ld, msgid, all );
145 lastlm = &ld->ld_responses;
146 for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
149 nextlm = lm->lm_next;
152 if ( ldap_abandoned( ld, lm->lm_msgid, &idx ) ) {
153 Debug( LDAP_DEBUG_ANY,
154 "response list msg abandoned, "
155 "msgid %d message type %s\n",
156 lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ), 0 );
158 switch ( lm->lm_msgtype ) {
159 case LDAP_RES_SEARCH_ENTRY:
160 case LDAP_RES_SEARCH_REFERENCE:
161 case LDAP_RES_INTERMEDIATE:
165 /* there's no need to keep the id
166 * in the abandoned list any longer */
167 ldap_mark_abandoned( ld, lm->lm_msgid, idx );
171 /* Remove this entry from list */
179 if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
182 if ( all == LDAP_MSG_ONE ||
183 all == LDAP_MSG_RECEIVED ||
184 msgid == LDAP_RES_UNSOLICITED )
189 tmp = lm->lm_chain_tail;
190 if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY ||
191 tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ||
192 tmp->lm_msgtype == LDAP_RES_INTERMEDIATE )
203 lastlm = &lm->lm_next;
207 /* Found an entry, remove it from the list */
208 if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) {
209 *lastlm = lm->lm_chain;
210 lm->lm_chain->lm_next = lm->lm_next;
211 lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain;
213 lm->lm_chain_tail = NULL;
215 *lastlm = lm->lm_next;
222 Debug( LDAP_DEBUG_TRACE,
223 "ldap_chkResponseList returns ld %p NULL\n", (void *)ld, 0, 0);
225 Debug( LDAP_DEBUG_TRACE,
226 "ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n",
227 (void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
239 struct timeval *timeout,
240 LDAPMessage **result )
243 struct timeval tv = { 0 },
245 start_time_tv = { 0 },
249 assert( ld != NULL );
250 assert( result != NULL );
252 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
254 if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) {
255 tv = ld->ld_options.ldo_tm_api;
260 if ( timeout == NULL ) {
261 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
262 (void *)ld, msgid, 0 );
264 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n",
265 (void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec );
267 #endif /* LDAP_DEBUG */
269 if ( timeout != NULL && timeout->tv_sec != -1 ) {
273 #ifdef HAVE_GETTIMEOFDAY
274 gettimeofday( &start_time_tv, NULL );
275 #else /* ! HAVE_GETTIMEOFDAY */
276 time( &start_time_tv.tv_sec );
277 start_time_tv.tv_usec = 0;
278 #endif /* ! HAVE_GETTIMEOFDAY */
281 rc = LDAP_MSG_X_KEEP_LOOKING;
282 while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
284 if ( ldap_debug & LDAP_DEBUG_TRACE ) {
285 Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
286 (void *)ld, msgid, all );
287 LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
288 ldap_dump_connection( ld, ld->ld_conns, 1 );
289 LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
290 LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
291 ldap_dump_requests_and_responses( ld );
292 LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
294 #endif /* LDAP_DEBUG */
296 if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
297 rc = (*result)->lm_msgtype;
302 LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
303 for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
304 if ( ber_sockbuf_ctrl( lc->lconn_sb,
305 LBER_SB_OPT_DATA_READY, NULL ) )
311 LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
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 :
335 rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */
343 rc = LDAP_MSG_X_KEEP_LOOKING;
344 LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
345 if ( ld->ld_requests &&
346 ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
347 ldap_is_write_ready( ld,
348 ld->ld_requests->lr_conn->lconn_sb ) )
350 ldap_int_flush_request( ld, ld->ld_requests );
352 LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
353 LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
354 for ( lc = ld->ld_conns;
355 rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL;
358 if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
359 ldap_is_read_ready( ld, lc->lconn_sb ) )
361 /* Don't let it get freed out from under us */
363 LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
364 rc = try_read1msg( ld, msgid, all, lc, result );
365 lnext = lc->lconn_next;
367 /* Only take locks if we're really freeing */
368 if ( lc->lconn_refcnt <= 1 ) {
369 LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
370 ldap_free_connection( ld, lc, 0, 1 );
371 LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
375 LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
377 lnext = lc->lconn_next;
380 LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
384 if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
385 struct timeval curr_time_tv = { 0 },
386 delta_time_tv = { 0 };
388 #ifdef HAVE_GETTIMEOFDAY
389 gettimeofday( &curr_time_tv, NULL );
390 #else /* ! HAVE_GETTIMEOFDAY */
391 time( &curr_time_tv.tv_sec );
392 curr_time_tv.tv_usec = 0;
393 #endif /* ! HAVE_GETTIMEOFDAY */
395 /* delta_time = tmp_time - start_time */
396 delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
397 delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
398 if ( delta_time_tv.tv_usec < 0 ) {
399 delta_time_tv.tv_sec--;
400 delta_time_tv.tv_usec += 1000000;
403 /* tv0 < delta_time ? */
404 if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
405 ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
407 rc = 0; /* timed out */
408 ld->ld_errno = LDAP_TIMEOUT;
412 /* tv0 -= delta_time */
413 tv0.tv_sec -= delta_time_tv.tv_sec;
414 tv0.tv_usec -= delta_time_tv.tv_usec;
415 if ( tv0.tv_usec < 0 ) {
417 tv0.tv_usec += 1000000;
420 tv.tv_sec = tv0.tv_sec;
421 tv.tv_usec = tv0.tv_usec;
423 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n",
424 (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
426 start_time_tv.tv_sec = curr_time_tv.tv_sec;
427 start_time_tv.tv_usec = curr_time_tv.tv_usec;
441 LDAPMessage **result )
444 LDAPMessage *newmsg, *l, *prev;
450 LDAPRequest *lr, *tmplr, dummy_lr = { 0 };
452 int rc, refer_cnt, hadref, simple_request, err;
455 #ifdef LDAP_CONNECTIONLESS
456 LDAPMessage *tmp = NULL, *chain_head = NULL;
457 int moremsgs = 0, isv2 = 0;
460 assert( ld != NULL );
461 assert( lc != NULL );
463 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
465 Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
466 (void *)ld, msgid, all );
469 if ( lc->lconn_ber == NULL ) {
470 lc->lconn_ber = ldap_alloc_ber_with_options( ld );
472 if ( lc->lconn_ber == NULL ) {
478 assert( LBER_VALID (ber) );
480 /* get the next message */
482 #ifdef LDAP_CONNECTIONLESS
483 if ( LDAP_IS_UDP(ld) ) {
484 struct sockaddr from;
485 ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr) );
486 if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
490 tag = ber_get_next( lc->lconn_sb, &len, ber );
492 case LDAP_TAG_MESSAGE:
494 * We read a complete message.
495 * The connection should no longer need this ber.
497 lc->lconn_ber = NULL;
503 Debug( LDAP_DEBUG_CONNS,
504 "ber_get_next failed.\n", 0, 0, 0 );
507 if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
510 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, &idx ) ) {
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, idx );
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);
833 while ( lr->lr_parent != NULL ) {
834 merge_error_info( ld, lr->lr_parent, lr );
837 if ( --lr->lr_outrefcnt > 0 ) {
838 break; /* not completely done yet */
842 /* Check if all requests are finished, lr is now parent */
844 if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
845 for ( tmplr = lr->lr_child;
847 tmplr = tmplr->lr_refnext )
849 if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
853 /* This is the parent request if the request has referrals */
854 if ( lr->lr_outrefcnt <= 0 &&
855 lr->lr_parent == NULL &&
859 tag = lr->lr_res_msgtype;
860 Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
862 Debug( LDAP_DEBUG_TRACE,
863 "res_errno: %d, res_error: <%s>, "
864 "res_matched: <%s>\n",
866 lr->lr_res_error ? lr->lr_res_error : "",
867 lr->lr_res_matched ? lr->lr_res_matched : "" );
868 if ( !simple_request ) {
871 if ( build_result_ber( ld, &ber, lr )
874 rc = -1; /* fatal error */
878 if ( lr != &dummy_lr ) {
879 ldap_return_request( ld, lr, 1 );
885 * RF 4511 unsolicited (id == 0) responses
886 * shouldn't necessarily end the connection
888 if ( lc != NULL && id != 0 ) {
896 if ( lr != &dummy_lr ) {
897 ldap_return_request( ld, lr, 0 );
906 /* try to handle unsolicited responses as appropriate */
907 if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
910 tag = ber_peek_tag( &tmpber, &len );
912 /* we have a res oid */
913 if ( tag == LDAP_TAG_EXOP_RES_OID ) {
914 static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
915 struct berval resoid = BER_BVNULL;
917 if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
918 ld->ld_errno = LDAP_DECODING_ERROR;
923 assert( !BER_BVISEMPTY( &resoid ) );
925 is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
927 tag = ber_peek_tag( &tmpber, &len );
930 #if 0 /* don't need right now */
931 /* we have res data */
932 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
933 struct berval resdata;
935 if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
936 ld->ld_errno = LDAP_DECODING_ERROR;
945 /* handle RFC 4511 "Notice of Disconnection" locally */
948 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
949 ld->ld_errno = LDAP_DECODING_ERROR;
954 /* get rid of the connection... */
959 /* need to return -1, because otherwise
960 * a valid result is expected */
961 ld->ld_errno = lderr;
966 /* make a new ldap message */
967 newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
968 if ( newmsg == NULL ) {
969 ld->ld_errno = LDAP_NO_MEMORY;
972 newmsg->lm_msgid = (int)id;
973 newmsg->lm_msgtype = tag;
974 newmsg->lm_ber = ber;
975 newmsg->lm_chain_tail = newmsg;
977 #ifdef LDAP_CONNECTIONLESS
978 /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
979 * the responses are all a sequence wrapped in one message. In
980 * LDAPv3 each response is in its own message. The datagram must
981 * end with a SearchResult. We can't just parse each response in
982 * separate calls to try_read1msg because the header info is only
983 * present at the beginning of the datagram, not at the beginning
984 * of each response. So parse all the responses at once and queue
985 * them up, then pull off the first response to return to the
986 * caller when all parsing is complete.
988 if ( LDAP_IS_UDP(ld) ) {
989 /* If not a result, look for more */
990 if ( tag != LDAP_RES_SEARCH_RESULT ) {
994 /* LDAPv2: dup the current ber, skip past the current
995 * response, and see if there are any more after it.
997 ber = ber_dup( ber );
998 ber_scanf( ber, "x" );
999 if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
1000 /* There's more - dup the ber buffer so they can all be
1001 * individually freed by ldap_msgfree.
1004 ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1005 bv.bv_val = LDAP_MALLOC( len );
1008 ber_read( ber, bv.bv_val, len );
1010 ber_init2( ber, &bv, ld->ld_lberoptions );
1014 /* LDAPv3: Just allocate a new ber. Since this is a buffered
1015 * datagram, if the sockbuf is readable we still have data
1018 ber = ldap_alloc_ber_with_options( ld );
1019 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1021 /* set up response chain */
1022 if ( tmp == NULL ) {
1023 newmsg->lm_next = ld->ld_responses;
1024 ld->ld_responses = newmsg;
1025 chain_head = newmsg;
1027 tmp->lm_chain = newmsg;
1029 chain_head->lm_chain_tail = newmsg;
1031 /* "ok" means there's more to parse */
1040 /* got to end of datagram without a SearchResult. Free
1041 * our dup'd ber, but leave any buffer alone. For v2 case,
1042 * the previous response is still using this buffer. For v3,
1043 * the new ber has no buffer to free yet.
1048 } else if ( moremsgs ) {
1049 /* got search result, and we had multiple responses in 1 datagram.
1050 * stick the result onto the end of the chain, and then pull the
1051 * first response off the head of the chain.
1053 tmp->lm_chain = newmsg;
1054 chain_head->lm_chain_tail = newmsg;
1055 *result = chkResponseList( ld, msgid, all );
1056 ld->ld_errno = LDAP_SUCCESS;
1057 return( (*result)->lm_msgtype );
1060 #endif /* LDAP_CONNECTIONLESS */
1062 /* is this the one we're looking for? */
1063 if ( msgid == LDAP_RES_ANY || id == msgid ) {
1064 if ( all == LDAP_MSG_ONE
1065 || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1066 && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1067 && newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE
1068 && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1071 ld->ld_errno = LDAP_SUCCESS;
1074 } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1075 foundit = 1; /* return the chain later */
1080 * if not, we must add it to the list of responses. if
1081 * the msgid is already there, it must be part of an existing
1086 for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1087 if ( l->lm_msgid == newmsg->lm_msgid ) {
1093 /* not part of an existing search response */
1100 newmsg->lm_next = ld->ld_responses;
1101 ld->ld_responses = newmsg;
1105 Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
1106 (void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1108 /* part of a search response - add to end of list of entries */
1109 l->lm_chain_tail->lm_chain = newmsg;
1110 l->lm_chain_tail = newmsg;
1112 /* return the whole chain if that's what we were looking for */
1114 if ( prev == NULL ) {
1115 ld->ld_responses = l->lm_next;
1117 prev->lm_next = l->lm_next;
1124 ld->ld_errno = LDAP_SUCCESS;
1127 if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1130 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
1135 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1143 ber = ldap_alloc_ber_with_options( ld );
1146 ld->ld_errno = LDAP_NO_MEMORY;
1150 if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1151 lr->lr_res_msgtype, lr->lr_res_errno,
1152 lr->lr_res_matched ? lr->lr_res_matched : "",
1153 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1155 ld->ld_errno = LDAP_ENCODING_ERROR;
1157 return( LBER_ERROR );
1160 ber_reset( ber, 1 );
1162 if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1163 ld->ld_errno = LDAP_DECODING_ERROR;
1165 return( LBER_ERROR );
1168 if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1169 ld->ld_errno = LDAP_DECODING_ERROR;
1171 return( LBER_ERROR );
1174 tag = ber_peek_tag( ber, &len );
1176 if ( tag == LBER_ERROR ) {
1177 ld->ld_errno = LDAP_DECODING_ERROR;
1179 return( LBER_ERROR );
1188 * Merge error information in "lr" with "parentr" error code and string.
1191 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1193 if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1194 parentr->lr_res_errno = lr->lr_res_errno;
1195 if ( lr->lr_res_error != NULL ) {
1196 (void)ldap_append_referral( ld, &parentr->lr_res_error,
1200 } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1201 parentr->lr_res_errno == LDAP_SUCCESS )
1203 parentr->lr_res_errno = lr->lr_res_errno;
1204 if ( parentr->lr_res_error != NULL ) {
1205 LDAP_FREE( parentr->lr_res_error );
1207 parentr->lr_res_error = lr->lr_res_error;
1208 lr->lr_res_error = NULL;
1209 if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1210 if ( parentr->lr_res_matched != NULL ) {
1211 LDAP_FREE( parentr->lr_res_matched );
1213 parentr->lr_res_matched = lr->lr_res_matched;
1214 lr->lr_res_matched = NULL;
1218 Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
1219 parentr->lr_msgid, 0, 0 );
1220 Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1221 parentr->lr_res_errno,
1222 parentr->lr_res_error ? parentr->lr_res_error : "",
1223 parentr->lr_res_matched ? parentr->lr_res_matched : "" );
1229 ldap_msgtype( LDAPMessage *lm )
1231 assert( lm != NULL );
1232 return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1237 ldap_msgid( LDAPMessage *lm )
1239 assert( lm != NULL );
1241 return ( lm != NULL ) ? lm->lm_msgid : -1;
1246 ldap_int_msgtype2str( ber_tag_t tag )
1249 case LDAP_RES_ADD: return "add";
1250 case LDAP_RES_BIND: return "bind";
1251 case LDAP_RES_COMPARE: return "compare";
1252 case LDAP_RES_DELETE: return "delete";
1253 case LDAP_RES_EXTENDED: return "extended-result";
1254 case LDAP_RES_INTERMEDIATE: return "intermediate";
1255 case LDAP_RES_MODIFY: return "modify";
1256 case LDAP_RES_RENAME: return "rename";
1257 case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1258 case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1259 case LDAP_RES_SEARCH_RESULT: return "search-result";
1265 ldap_msgfree( LDAPMessage *lm )
1270 Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1272 for ( ; lm != NULL; lm = next ) {
1273 next = lm->lm_chain;
1274 type = lm->lm_msgtype;
1275 ber_free( lm->lm_ber, 1 );
1276 LDAP_FREE( (char *) lm );
1283 * ldap_msgdelete - delete a message. It returns:
1284 * 0 if the entire message was deleted
1285 * -1 if the message was not found, or only part of it was found
1288 ldap_msgdelete( LDAP *ld, int msgid )
1290 LDAPMessage *lm, *prev;
1293 assert( ld != NULL );
1295 Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1296 (void *)ld, msgid, 0 );
1298 LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
1300 for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1301 if ( lm->lm_msgid == msgid ) {
1311 if ( prev == NULL ) {
1312 ld->ld_responses = lm->lm_next;
1314 prev->lm_next = lm->lm_next;
1317 LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
1319 switch ( ldap_msgfree( lm ) ) {
1320 case LDAP_RES_SEARCH_ENTRY:
1321 case LDAP_RES_SEARCH_REFERENCE:
1322 case LDAP_RES_INTERMEDIATE:
1338 * return the location of the message id in the array of abandoned
1339 * message ids, or -1
1341 * expects ld_res_mutex to be locked
1344 ldap_abandoned( LDAP *ld, ber_int_t msgid, int *idxp )
1346 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1348 assert( idxp != NULL );
1349 assert( msgid >= 0 );
1351 return ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp );
1355 * ldap_mark_abandoned
1357 * expects ld_res_mutex to be locked
1360 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid, int idx )
1362 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1364 /* NOTE: those assertions are repeated in ldap_int_bisect_delete() */
1366 assert( (unsigned) idx < ld->ld_nabandoned );
1367 assert( ld->ld_abandoned[ idx ] == msgid );
1369 return ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,