1 /* result.c - wait for an ldap result */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 1998-2009 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 #ifdef LDAP_R_COMPILE
117 ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
120 rc = wait4msg( ld, msgid, all, timeout, result );
122 #ifdef LDAP_R_COMPILE
123 ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
135 LDAPMessage *lm, **lastlm, *nextlm;
139 * Look through the list of responses we have received on
140 * this association and see if the response we're interested in
141 * is there. If it is, return it. If not, call wait4msg() to
142 * wait until it arrives or timeout occurs.
145 #ifdef LDAP_R_COMPILE
146 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
149 Debug( LDAP_DEBUG_TRACE,
150 "ldap_chkResponseList ld %p msgid %d all %d\n",
151 (void *)ld, msgid, all );
153 lastlm = &ld->ld_responses;
154 for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
157 nextlm = lm->lm_next;
160 if ( ldap_abandoned( ld, lm->lm_msgid, &idx ) ) {
161 Debug( LDAP_DEBUG_ANY,
162 "response list msg abandoned, "
163 "msgid %d message type %s\n",
164 lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ), 0 );
166 switch ( lm->lm_msgtype ) {
167 case LDAP_RES_SEARCH_ENTRY:
168 case LDAP_RES_SEARCH_REFERENCE:
169 case LDAP_RES_INTERMEDIATE:
173 /* there's no need to keep the id
174 * in the abandoned list any longer */
175 ldap_mark_abandoned( ld, lm->lm_msgid, idx );
179 /* Remove this entry from list */
187 if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
190 if ( all == LDAP_MSG_ONE ||
191 all == LDAP_MSG_RECEIVED ||
192 msgid == LDAP_RES_UNSOLICITED )
197 tmp = lm->lm_chain_tail;
198 if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY ||
199 tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ||
200 tmp->lm_msgtype == LDAP_RES_INTERMEDIATE )
211 lastlm = &lm->lm_next;
215 /* Found an entry, remove it from the list */
216 if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) {
217 *lastlm = lm->lm_chain;
218 lm->lm_chain->lm_next = lm->lm_next;
219 lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain;
221 lm->lm_chain_tail = NULL;
223 *lastlm = lm->lm_next;
230 Debug( LDAP_DEBUG_TRACE,
231 "ldap_chkResponseList returns ld %p NULL\n", (void *)ld, 0, 0);
233 Debug( LDAP_DEBUG_TRACE,
234 "ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n",
235 (void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
247 struct timeval *timeout,
248 LDAPMessage **result )
251 struct timeval tv = { 0 },
253 start_time_tv = { 0 },
257 assert( ld != NULL );
258 assert( result != NULL );
260 #ifdef LDAP_R_COMPILE
261 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
264 if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) {
265 tv = ld->ld_options.ldo_tm_api;
270 if ( timeout == NULL ) {
271 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
272 (void *)ld, msgid, 0 );
274 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n",
275 (void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec );
277 #endif /* LDAP_DEBUG */
279 if ( timeout != NULL && timeout->tv_sec != -1 ) {
283 #ifdef HAVE_GETTIMEOFDAY
284 gettimeofday( &start_time_tv, NULL );
285 #else /* ! HAVE_GETTIMEOFDAY */
286 time( &start_time_tv.tv_sec );
287 start_time_tv.tv_usec = 0;
288 #endif /* ! HAVE_GETTIMEOFDAY */
291 rc = LDAP_MSG_X_KEEP_LOOKING;
292 while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
294 if ( ldap_debug & LDAP_DEBUG_TRACE ) {
295 Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
296 (void *)ld, msgid, all );
297 #ifdef LDAP_R_COMPILE
298 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
300 ldap_dump_connection( ld, ld->ld_conns, 1 );
301 #ifdef LDAP_R_COMPILE
302 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
303 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
305 ldap_dump_requests_and_responses( ld );
306 #ifdef LDAP_R_COMPILE
307 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
310 #endif /* LDAP_DEBUG */
312 if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
313 rc = (*result)->lm_msgtype;
318 #ifdef LDAP_R_COMPILE
319 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
321 for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
322 if ( ber_sockbuf_ctrl( lc->lconn_sb,
323 LBER_SB_OPT_DATA_READY, NULL ) )
329 #ifdef LDAP_R_COMPILE
330 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
335 rc = ldap_int_select( ld, tvp );
339 Debug( LDAP_DEBUG_TRACE,
340 "ldap_int_select returned -1: errno %d\n",
345 if ( rc == 0 || ( rc == -1 && (
346 !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
347 || err != EINTR ) ) )
349 ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
355 rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */
363 rc = LDAP_MSG_X_KEEP_LOOKING;
364 #ifdef LDAP_R_COMPILE
365 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
367 if ( ld->ld_requests &&
368 ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
369 ldap_is_write_ready( ld,
370 ld->ld_requests->lr_conn->lconn_sb ) )
372 ldap_int_flush_request( ld, ld->ld_requests );
374 #ifdef LDAP_R_COMPILE
375 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
376 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
378 for ( lc = ld->ld_conns;
379 rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL;
382 if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
383 ldap_is_read_ready( ld, lc->lconn_sb ) )
385 /* Don't let it get freed out from under us */
387 #ifdef LDAP_R_COMPILE
388 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
390 rc = try_read1msg( ld, msgid, all, lc, result );
391 lnext = lc->lconn_next;
393 /* Only take locks if we're really freeing */
394 if ( lc->lconn_refcnt <= 1 ) {
395 #ifdef LDAP_R_COMPILE
396 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
398 ldap_free_connection( ld, lc, 0, 1 );
399 #ifdef LDAP_R_COMPILE
400 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
405 #ifdef LDAP_R_COMPILE
406 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
409 lnext = lc->lconn_next;
412 #ifdef LDAP_R_COMPILE
413 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
418 if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
419 struct timeval curr_time_tv = { 0 },
420 delta_time_tv = { 0 };
422 #ifdef HAVE_GETTIMEOFDAY
423 gettimeofday( &curr_time_tv, NULL );
424 #else /* ! HAVE_GETTIMEOFDAY */
425 time( &curr_time_tv.tv_sec );
426 curr_time_tv.tv_usec = 0;
427 #endif /* ! HAVE_GETTIMEOFDAY */
429 /* delta_time = tmp_time - start_time */
430 delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
431 delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
432 if ( delta_time_tv.tv_usec < 0 ) {
433 delta_time_tv.tv_sec--;
434 delta_time_tv.tv_usec += 1000000;
437 /* tv0 < delta_time ? */
438 if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
439 ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
441 rc = 0; /* timed out */
442 ld->ld_errno = LDAP_TIMEOUT;
446 /* tv0 -= delta_time */
447 tv0.tv_sec -= delta_time_tv.tv_sec;
448 tv0.tv_usec -= delta_time_tv.tv_usec;
449 if ( tv0.tv_usec < 0 ) {
451 tv0.tv_usec += 1000000;
454 tv.tv_sec = tv0.tv_sec;
455 tv.tv_usec = tv0.tv_usec;
457 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n",
458 (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
460 start_time_tv.tv_sec = curr_time_tv.tv_sec;
461 start_time_tv.tv_usec = curr_time_tv.tv_usec;
475 LDAPMessage **result )
478 LDAPMessage *newmsg, *l, *prev;
484 LDAPRequest *lr, *tmplr, dummy_lr = { 0 };
486 int rc, refer_cnt, hadref, simple_request, err;
489 #ifdef LDAP_CONNECTIONLESS
490 LDAPMessage *tmp = NULL, *chain_head = NULL;
491 int moremsgs = 0, isv2 = 0;
494 assert( ld != NULL );
495 assert( lc != NULL );
497 #ifdef LDAP_R_COMPILE
498 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
501 Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
502 (void *)ld, msgid, all );
505 if ( lc->lconn_ber == NULL ) {
506 lc->lconn_ber = ldap_alloc_ber_with_options( ld );
508 if ( lc->lconn_ber == NULL ) {
514 assert( LBER_VALID (ber) );
516 /* get the next message */
518 #ifdef LDAP_CONNECTIONLESS
519 if ( LDAP_IS_UDP(ld) ) {
520 struct sockaddr from;
521 ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr) );
522 if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
526 tag = ber_get_next( lc->lconn_sb, &len, ber );
528 case LDAP_TAG_MESSAGE:
530 * We read a complete message.
531 * The connection should no longer need this ber.
533 lc->lconn_ber = NULL;
539 Debug( LDAP_DEBUG_CONNS,
540 "ber_get_next failed.\n", 0, 0, 0 );
543 if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
546 if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
548 ld->ld_errno = LDAP_SERVER_DOWN;
550 lc->lconn_status = 0;
554 ld->ld_errno = LDAP_LOCAL_ERROR;
559 if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
561 ld->ld_errno = LDAP_DECODING_ERROR;
565 /* id == 0 iff unsolicited notification message (RFC 4511) */
567 /* id < 0 is invalid, just toss it. FIXME: should we disconnect? */
572 /* if it's been abandoned, toss it */
574 if ( ldap_abandoned( ld, id, &idx ) ) {
575 /* the message type */
576 tag = ber_peek_tag( ber, &len );
578 case LDAP_RES_SEARCH_ENTRY:
579 case LDAP_RES_SEARCH_REFERENCE:
580 case LDAP_RES_INTERMEDIATE:
585 /* there's no need to keep the id
586 * in the abandoned list any longer */
587 ldap_mark_abandoned( ld, id, idx );
591 Debug( LDAP_DEBUG_ANY,
592 "abandoned/discarded ld %p msgid %d message type %s\n",
593 (void *)ld, id, ldap_int_msgtype2str( tag ) );
597 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
600 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
603 lr = ldap_find_request_by_msgid( ld, id );
605 const char *msg = "unknown";
607 /* the message type */
608 tag = ber_peek_tag( ber, &len );
614 msg = ldap_int_msgtype2str( tag );
618 Debug( LDAP_DEBUG_ANY,
619 "no request for response on ld %p msgid %d message type %s (tossing)\n",
620 (void *)ld, id, msg );
625 #ifdef LDAP_CONNECTIONLESS
626 if ( LDAP_IS_UDP(ld) && isv2 ) {
627 ber_scanf(ber, "x{");
634 /* the message type */
635 tag = ber_peek_tag( ber, &len );
636 if ( tag == LBER_ERROR ) {
637 ld->ld_errno = LDAP_DECODING_ERROR;
642 Debug( LDAP_DEBUG_TRACE,
643 "read1msg: ld %p msgid %d message type %s\n",
644 (void *)ld, id, ldap_int_msgtype2str( tag ) );
647 /* unsolicited notification message (RFC 4511) */
648 if ( tag != LDAP_RES_EXTENDED ) {
652 /* strictly speaking, it's an error; from RFC 4511:
654 4.4. Unsolicited Notification
656 An unsolicited notification is an LDAPMessage sent from the server to
657 the client that is not in response to any LDAPMessage received by the
658 server. It is used to signal an extraordinary condition in the
659 server or in the LDAP session between the client and the server. The
660 notification is of an advisory nature, and the server will not expect
661 any response to be returned from the client.
663 The unsolicited notification is structured as an LDAPMessage in which
664 the messageID is zero and protocolOp is set to the extendedResp
665 choice using the ExtendedResponse type (See Section 4.12). The
666 responseName field of the ExtendedResponse always contains an LDAPOID
667 that is unique for this notification.
669 * however, since unsolicited responses
670 * are of advisory nature, better
675 ld->ld_errno = LDAP_DECODING_ERROR;
686 hadref = simple_request = 0;
687 rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */
688 lr->lr_res_msgtype = tag;
691 * Check for V3 search reference
693 if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
694 if ( ld->ld_version > LDAP_VERSION2 ) {
695 /* This is a V3 search reference */
696 if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
697 lr->lr_parent != NULL )
702 /* Get the referral list */
703 if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
704 rc = LDAP_DECODING_ERROR;
707 /* Note: refs array is freed by ldap_chase_v3referrals */
708 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
709 1, &lr->lr_res_error, &hadref );
710 if ( refer_cnt > 0 ) {
711 /* successfully chased reference */
712 /* If haven't got end search, set chasing referrals */
713 if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
714 lr->lr_status = LDAP_REQST_CHASINGREFS;
715 Debug( LDAP_DEBUG_TRACE,
716 "read1msg: search ref chased, "
717 "mark request chasing refs, "
719 lr->lr_msgid, 0, 0 );
726 } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
727 /* All results that just return a status, i.e. don't return data
728 * go through the following code. This code also chases V2 referrals
729 * and checks if all referrals have been chased.
731 char *lr_res_error = NULL;
733 tmpber = *ber; /* struct copy */
734 if ( ber_scanf( &tmpber, "{eAA", &lderr,
735 &lr->lr_res_matched, &lr_res_error )
738 if ( lr_res_error != NULL ) {
739 if ( lr->lr_res_error != NULL ) {
740 (void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
741 LDAP_FREE( (char *)lr_res_error );
744 lr->lr_res_error = lr_res_error;
749 /* Do we need to check for referrals? */
750 if ( tag != LDAP_RES_BIND &&
751 ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
752 lr->lr_parent != NULL ))
757 /* Check if V3 referral */
758 if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
759 if ( ld->ld_version > LDAP_VERSION2 ) {
760 /* Get the referral list */
761 if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
762 rc = LDAP_DECODING_ERROR;
763 lr->lr_status = LDAP_REQST_COMPLETED;
764 Debug( LDAP_DEBUG_TRACE,
765 "read1msg: referral decode error, "
766 "mark request completed, ld %p msgid %d\n",
767 (void *)ld, lr->lr_msgid, 0 );
770 /* Chase the referral
771 * refs array is freed by ldap_chase_v3referrals
773 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
774 0, &lr->lr_res_error, &hadref );
775 lr->lr_status = LDAP_REQST_COMPLETED;
776 Debug( LDAP_DEBUG_TRACE,
777 "read1msg: referral %s chased, "
778 "mark request completed, ld %p msgid %d\n",
779 refer_cnt > 0 ? "" : "not",
780 (void *)ld, lr->lr_msgid);
781 if ( refer_cnt < 0 ) {
789 case LDAP_COMPARE_TRUE:
790 case LDAP_COMPARE_FALSE:
794 if ( lr->lr_res_error == NULL ) {
798 /* pedantic, should never happen */
799 if ( lr->lr_res_error[ 0 ] == '\0' ) {
800 LDAP_FREE( lr->lr_res_error );
801 lr->lr_res_error = NULL;
805 /* V2 referrals are in error string */
806 refer_cnt = ldap_chase_referrals( ld, lr,
807 &lr->lr_res_error, -1, &hadref );
808 lr->lr_status = LDAP_REQST_COMPLETED;
809 Debug( LDAP_DEBUG_TRACE,
810 "read1msg: V2 referral chased, "
811 "mark request completed, id = %d\n",
812 lr->lr_msgid, 0, 0 );
818 /* save errno, message, and matched string */
819 if ( !hadref || lr->lr_res_error == NULL ) {
821 lderr == LDAP_PARTIAL_RESULTS
822 ? LDAP_SUCCESS : lderr;
824 } else if ( ld->ld_errno != LDAP_SUCCESS ) {
825 lr->lr_res_errno = ld->ld_errno;
828 lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
832 /* in any case, don't leave any lr_res_error 'round */
833 if ( lr_res_error ) {
834 LDAP_FREE( lr_res_error );
837 Debug( LDAP_DEBUG_TRACE,
838 "read1msg: ld %p %d new referrals\n",
839 (void *)ld, refer_cnt, 0 );
841 if ( refer_cnt != 0 ) { /* chasing referrals */
844 if ( refer_cnt < 0 ) {
845 ldap_return_request( ld, lr, 0 );
846 return( -1 ); /* fatal error */
848 lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
849 if ( lr->lr_res_matched ) {
850 LDAP_FREE( lr->lr_res_matched );
851 lr->lr_res_matched = NULL;
855 if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
856 /* request without any referrals */
857 simple_request = ( hadref ? 0 : 1 );
860 /* request with referrals or child request */
865 lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
866 Debug( LDAP_DEBUG_TRACE,
867 "read1msg: mark request completed, ld %p msgid %d\n",
868 (void *)ld, lr->lr_msgid, 0);
869 while ( lr->lr_parent != NULL ) {
870 merge_error_info( ld, lr->lr_parent, lr );
873 if ( --lr->lr_outrefcnt > 0 ) {
874 break; /* not completely done yet */
878 /* Check if all requests are finished, lr is now parent */
880 if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
881 for ( tmplr = lr->lr_child;
883 tmplr = tmplr->lr_refnext )
885 if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
889 /* This is the parent request if the request has referrals */
890 if ( lr->lr_outrefcnt <= 0 &&
891 lr->lr_parent == NULL &&
895 tag = lr->lr_res_msgtype;
896 Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
898 Debug( LDAP_DEBUG_TRACE,
899 "res_errno: %d, res_error: <%s>, "
900 "res_matched: <%s>\n",
902 lr->lr_res_error ? lr->lr_res_error : "",
903 lr->lr_res_matched ? lr->lr_res_matched : "" );
904 if ( !simple_request ) {
907 if ( build_result_ber( ld, &ber, lr )
910 rc = -1; /* fatal error */
914 if ( lr != &dummy_lr ) {
915 ldap_return_request( ld, lr, 1 );
921 * RF 4511 unsolicited (id == 0) responses
922 * shouldn't necessarily end the connection
924 if ( lc != NULL && id != 0 ) {
932 if ( lr != &dummy_lr ) {
933 ldap_return_request( ld, lr, 0 );
942 /* try to handle unsolicited responses as appropriate */
943 if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
946 tag = ber_peek_tag( &tmpber, &len );
948 /* we have a res oid */
949 if ( tag == LDAP_TAG_EXOP_RES_OID ) {
950 static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
951 struct berval resoid = BER_BVNULL;
953 if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
954 ld->ld_errno = LDAP_DECODING_ERROR;
959 assert( !BER_BVISEMPTY( &resoid ) );
961 is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
963 tag = ber_peek_tag( &tmpber, &len );
966 #if 0 /* don't need right now */
967 /* we have res data */
968 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
969 struct berval resdata;
971 if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
972 ld->ld_errno = LDAP_DECODING_ERROR;
981 /* handle RFC 4511 "Notice of Disconnection" locally */
984 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
985 ld->ld_errno = LDAP_DECODING_ERROR;
990 /* get rid of the connection... */
995 /* need to return -1, because otherwise
996 * a valid result is expected */
1001 /* make a new ldap message */
1002 newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
1003 if ( newmsg == NULL ) {
1004 ld->ld_errno = LDAP_NO_MEMORY;
1007 newmsg->lm_msgid = (int)id;
1008 newmsg->lm_msgtype = tag;
1009 newmsg->lm_ber = ber;
1010 newmsg->lm_chain_tail = newmsg;
1012 #ifdef LDAP_CONNECTIONLESS
1013 /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
1014 * the responses are all a sequence wrapped in one message. In
1015 * LDAPv3 each response is in its own message. The datagram must
1016 * end with a SearchResult. We can't just parse each response in
1017 * separate calls to try_read1msg because the header info is only
1018 * present at the beginning of the datagram, not at the beginning
1019 * of each response. So parse all the responses at once and queue
1020 * them up, then pull off the first response to return to the
1021 * caller when all parsing is complete.
1023 if ( LDAP_IS_UDP(ld) ) {
1024 /* If not a result, look for more */
1025 if ( tag != LDAP_RES_SEARCH_RESULT ) {
1029 /* LDAPv2: dup the current ber, skip past the current
1030 * response, and see if there are any more after it.
1032 ber = ber_dup( ber );
1033 ber_scanf( ber, "x" );
1034 if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
1035 /* There's more - dup the ber buffer so they can all be
1036 * individually freed by ldap_msgfree.
1039 ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1040 bv.bv_val = LDAP_MALLOC( len );
1043 ber_read( ber, bv.bv_val, len );
1045 ber_init2( ber, &bv, ld->ld_lberoptions );
1049 /* LDAPv3: Just allocate a new ber. Since this is a buffered
1050 * datagram, if the sockbuf is readable we still have data
1053 ber = ldap_alloc_ber_with_options( ld );
1054 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1056 /* set up response chain */
1057 if ( tmp == NULL ) {
1058 newmsg->lm_next = ld->ld_responses;
1059 ld->ld_responses = newmsg;
1060 chain_head = newmsg;
1062 tmp->lm_chain = newmsg;
1064 chain_head->lm_chain_tail = newmsg;
1066 /* "ok" means there's more to parse */
1075 /* got to end of datagram without a SearchResult. Free
1076 * our dup'd ber, but leave any buffer alone. For v2 case,
1077 * the previous response is still using this buffer. For v3,
1078 * the new ber has no buffer to free yet.
1083 } else if ( moremsgs ) {
1084 /* got search result, and we had multiple responses in 1 datagram.
1085 * stick the result onto the end of the chain, and then pull the
1086 * first response off the head of the chain.
1088 tmp->lm_chain = newmsg;
1089 chain_head->lm_chain_tail = newmsg;
1090 *result = chkResponseList( ld, msgid, all );
1091 ld->ld_errno = LDAP_SUCCESS;
1092 return( (*result)->lm_msgtype );
1095 #endif /* LDAP_CONNECTIONLESS */
1097 /* is this the one we're looking for? */
1098 if ( msgid == LDAP_RES_ANY || id == msgid ) {
1099 if ( all == LDAP_MSG_ONE
1100 || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1101 && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1102 && newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE
1103 && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1106 ld->ld_errno = LDAP_SUCCESS;
1109 } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1110 foundit = 1; /* return the chain later */
1115 * if not, we must add it to the list of responses. if
1116 * the msgid is already there, it must be part of an existing
1121 for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1122 if ( l->lm_msgid == newmsg->lm_msgid ) {
1128 /* not part of an existing search response */
1135 newmsg->lm_next = ld->ld_responses;
1136 ld->ld_responses = newmsg;
1140 Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
1141 (void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1143 /* part of a search response - add to end of list of entries */
1144 l->lm_chain_tail->lm_chain = newmsg;
1145 l->lm_chain_tail = newmsg;
1147 /* return the whole chain if that's what we were looking for */
1149 if ( prev == NULL ) {
1150 ld->ld_responses = l->lm_next;
1152 prev->lm_next = l->lm_next;
1159 ld->ld_errno = LDAP_SUCCESS;
1162 if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1165 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
1170 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1178 ber = ldap_alloc_ber_with_options( ld );
1181 ld->ld_errno = LDAP_NO_MEMORY;
1185 if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1186 lr->lr_res_msgtype, lr->lr_res_errno,
1187 lr->lr_res_matched ? lr->lr_res_matched : "",
1188 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1190 ld->ld_errno = LDAP_ENCODING_ERROR;
1192 return( LBER_ERROR );
1195 ber_reset( ber, 1 );
1197 if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1198 ld->ld_errno = LDAP_DECODING_ERROR;
1200 return( LBER_ERROR );
1203 if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1204 ld->ld_errno = LDAP_DECODING_ERROR;
1206 return( LBER_ERROR );
1209 tag = ber_peek_tag( ber, &len );
1211 if ( tag == LBER_ERROR ) {
1212 ld->ld_errno = LDAP_DECODING_ERROR;
1214 return( LBER_ERROR );
1223 * Merge error information in "lr" with "parentr" error code and string.
1226 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1228 if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1229 parentr->lr_res_errno = lr->lr_res_errno;
1230 if ( lr->lr_res_error != NULL ) {
1231 (void)ldap_append_referral( ld, &parentr->lr_res_error,
1235 } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1236 parentr->lr_res_errno == LDAP_SUCCESS )
1238 parentr->lr_res_errno = lr->lr_res_errno;
1239 if ( parentr->lr_res_error != NULL ) {
1240 LDAP_FREE( parentr->lr_res_error );
1242 parentr->lr_res_error = lr->lr_res_error;
1243 lr->lr_res_error = NULL;
1244 if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1245 if ( parentr->lr_res_matched != NULL ) {
1246 LDAP_FREE( parentr->lr_res_matched );
1248 parentr->lr_res_matched = lr->lr_res_matched;
1249 lr->lr_res_matched = NULL;
1253 Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
1254 parentr->lr_msgid, 0, 0 );
1255 Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1256 parentr->lr_res_errno,
1257 parentr->lr_res_error ? parentr->lr_res_error : "",
1258 parentr->lr_res_matched ? parentr->lr_res_matched : "" );
1264 ldap_msgtype( LDAPMessage *lm )
1266 assert( lm != NULL );
1267 return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1272 ldap_msgid( LDAPMessage *lm )
1274 assert( lm != NULL );
1276 return ( lm != NULL ) ? lm->lm_msgid : -1;
1281 ldap_int_msgtype2str( ber_tag_t tag )
1284 case LDAP_RES_ADD: return "add";
1285 case LDAP_RES_BIND: return "bind";
1286 case LDAP_RES_COMPARE: return "compare";
1287 case LDAP_RES_DELETE: return "delete";
1288 case LDAP_RES_EXTENDED: return "extended-result";
1289 case LDAP_RES_INTERMEDIATE: return "intermediate";
1290 case LDAP_RES_MODIFY: return "modify";
1291 case LDAP_RES_RENAME: return "rename";
1292 case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1293 case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1294 case LDAP_RES_SEARCH_RESULT: return "search-result";
1300 ldap_msgfree( LDAPMessage *lm )
1305 Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1307 for ( ; lm != NULL; lm = next ) {
1308 next = lm->lm_chain;
1309 type = lm->lm_msgtype;
1310 ber_free( lm->lm_ber, 1 );
1311 LDAP_FREE( (char *) lm );
1318 * ldap_msgdelete - delete a message. It returns:
1319 * 0 if the entire message was deleted
1320 * -1 if the message was not found, or only part of it was found
1323 ldap_msgdelete( LDAP *ld, int msgid )
1325 LDAPMessage *lm, *prev;
1328 assert( ld != NULL );
1330 Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1331 (void *)ld, msgid, 0 );
1333 #ifdef LDAP_R_COMPILE
1334 ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
1337 for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1338 if ( lm->lm_msgid == msgid ) {
1348 if ( prev == NULL ) {
1349 ld->ld_responses = lm->lm_next;
1351 prev->lm_next = lm->lm_next;
1354 #ifdef LDAP_R_COMPILE
1355 ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
1358 switch ( ldap_msgfree( lm ) ) {
1359 case LDAP_RES_SEARCH_ENTRY:
1360 case LDAP_RES_SEARCH_REFERENCE:
1361 case LDAP_RES_INTERMEDIATE:
1377 * return the location of the message id in the array of abandoned
1378 * message ids, or -1
1380 * expects ld_res_mutex to be locked
1383 ldap_abandoned( LDAP *ld, ber_int_t msgid, int *idxp )
1385 #ifdef LDAP_R_COMPILE
1386 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1389 assert( idxp != NULL );
1390 assert( msgid >= 0 );
1392 return ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp );
1396 * ldap_mark_abandoned
1398 * expects ld_res_mutex to be locked
1401 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid, int idx )
1403 #ifdef LDAP_R_COMPILE
1404 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1407 /* NOTE: those assertions are repeated in ldap_int_bisect_delete() */
1409 assert( (unsigned) idx < ld->ld_nabandoned );
1410 assert( ld->ld_abandoned[ idx ] == msgid );
1412 return ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,