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 )
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 );
121 rc = wait4msg( ld, msgid, all, timeout, result );
123 #ifdef LDAP_R_COMPILE
124 ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
136 LDAPMessage *lm, **lastlm, *nextlm;
140 * Look through the list of responses we have received on
141 * this association and see if the response we're interested in
142 * is there. If it is, return it. If not, call wait4msg() to
143 * wait until it arrives or timeout occurs.
146 #ifdef LDAP_R_COMPILE
147 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
150 Debug( LDAP_DEBUG_TRACE,
151 "ldap_chkResponseList ld %p msgid %d all %d\n",
152 (void *)ld, msgid, all );
154 lastlm = &ld->ld_responses;
155 for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
158 nextlm = lm->lm_next;
161 if ( ldap_abandoned( ld, lm->lm_msgid, &idx ) ) {
162 Debug( LDAP_DEBUG_ANY,
163 "response list msg abandoned, "
164 "msgid %d message type %s\n",
165 lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ), 0 );
167 switch ( lm->lm_msgtype ) {
168 case LDAP_RES_SEARCH_ENTRY:
169 case LDAP_RES_SEARCH_REFERENCE:
170 case LDAP_RES_INTERMEDIATE:
174 /* there's no need to keep the id
175 * in the abandoned list any longer */
176 ldap_mark_abandoned( ld, lm->lm_msgid, idx );
180 /* Remove this entry from list */
188 if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
191 if ( all == LDAP_MSG_ONE ||
192 all == LDAP_MSG_RECEIVED ||
193 msgid == LDAP_RES_UNSOLICITED )
198 tmp = lm->lm_chain_tail;
199 if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY ||
200 tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ||
201 tmp->lm_msgtype == LDAP_RES_INTERMEDIATE )
212 lastlm = &lm->lm_next;
216 /* Found an entry, remove it from the list */
217 if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) {
218 *lastlm = lm->lm_chain;
219 lm->lm_chain->lm_next = lm->lm_next;
220 lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain;
222 lm->lm_chain_tail = NULL;
224 *lastlm = lm->lm_next;
231 Debug( LDAP_DEBUG_TRACE,
232 "ldap_chkResponseList returns ld %p NULL\n", (void *)ld, 0, 0);
234 Debug( LDAP_DEBUG_TRACE,
235 "ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n",
236 (void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
248 struct timeval *timeout,
249 LDAPMessage **result )
252 struct timeval tv = { 0 },
254 start_time_tv = { 0 },
258 assert( ld != NULL );
259 assert( result != NULL );
261 #ifdef LDAP_R_COMPILE
262 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
265 if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) {
266 tv = ld->ld_options.ldo_tm_api;
271 if ( timeout == NULL ) {
272 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
273 (void *)ld, msgid, 0 );
275 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n",
276 (void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec );
278 #endif /* LDAP_DEBUG */
280 if ( timeout != NULL ) {
284 #ifdef HAVE_GETTIMEOFDAY
285 gettimeofday( &start_time_tv, NULL );
286 #else /* ! HAVE_GETTIMEOFDAY */
287 time( &start_time_tv.tv_sec );
288 start_time_tv.tv_usec = 0;
289 #endif /* ! HAVE_GETTIMEOFDAY */
292 rc = LDAP_MSG_X_KEEP_LOOKING;
293 while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
295 if ( ldap_debug & LDAP_DEBUG_TRACE ) {
296 Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
297 (void *)ld, msgid, all );
298 #ifdef LDAP_R_COMPILE
299 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
301 ldap_dump_connection( ld, ld->ld_conns, 1 );
302 #ifdef LDAP_R_COMPILE
303 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
304 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
306 ldap_dump_requests_and_responses( ld );
307 #ifdef LDAP_R_COMPILE
308 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
311 #endif /* LDAP_DEBUG */
313 if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
314 rc = (*result)->lm_msgtype;
319 #ifdef LDAP_R_COMPILE
320 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
322 for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
323 if ( ber_sockbuf_ctrl( lc->lconn_sb,
324 LBER_SB_OPT_DATA_READY, NULL ) )
330 #ifdef LDAP_R_COMPILE
331 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
336 rc = ldap_int_select( ld, tvp );
340 Debug( LDAP_DEBUG_TRACE,
341 "ldap_int_select returned -1: errno %d\n",
346 if ( rc == 0 || ( rc == -1 && (
347 !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
348 || err != EINTR ) ) )
350 ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
356 rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */
364 rc = LDAP_MSG_X_KEEP_LOOKING;
365 #ifdef LDAP_R_COMPILE
366 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
368 if ( ld->ld_requests &&
369 ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
370 ldap_is_write_ready( ld,
371 ld->ld_requests->lr_conn->lconn_sb ) )
373 ldap_int_flush_request( ld, ld->ld_requests );
375 #ifdef LDAP_R_COMPILE
376 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
377 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
379 for ( lc = ld->ld_conns;
380 rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL;
383 if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
384 ldap_is_read_ready( ld, lc->lconn_sb ) )
386 /* Don't let it get freed out from under us */
388 #ifdef LDAP_R_COMPILE
389 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
391 rc = try_read1msg( ld, msgid, all, lc, result );
392 lnext = lc->lconn_next;
394 /* Only take locks if we're really freeing */
395 if ( lc->lconn_refcnt <= 1 ) {
396 #ifdef LDAP_R_COMPILE
397 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
399 ldap_free_connection( ld, lc, 0, 1 );
400 #ifdef LDAP_R_COMPILE
401 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
406 #ifdef LDAP_R_COMPILE
407 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
410 lnext = lc->lconn_next;
413 #ifdef LDAP_R_COMPILE
414 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
419 if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
420 struct timeval curr_time_tv = { 0 },
421 delta_time_tv = { 0 };
423 #ifdef HAVE_GETTIMEOFDAY
424 gettimeofday( &curr_time_tv, NULL );
425 #else /* ! HAVE_GETTIMEOFDAY */
426 time( &curr_time_tv.tv_sec );
427 curr_time_tv.tv_usec = 0;
428 #endif /* ! HAVE_GETTIMEOFDAY */
430 /* delta_time = tmp_time - start_time */
431 delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
432 delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
433 if ( delta_time_tv.tv_usec < 0 ) {
434 delta_time_tv.tv_sec--;
435 delta_time_tv.tv_usec += 1000000;
438 /* tv0 < delta_time ? */
439 if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
440 ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
442 rc = 0; /* timed out */
443 ld->ld_errno = LDAP_TIMEOUT;
447 /* tv0 -= delta_time */
448 tv0.tv_sec -= delta_time_tv.tv_sec;
449 tv0.tv_usec -= delta_time_tv.tv_usec;
450 if ( tv0.tv_usec < 0 ) {
452 tv0.tv_usec += 1000000;
455 tv.tv_sec = tv0.tv_sec;
456 tv.tv_usec = tv0.tv_usec;
458 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n",
459 (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
461 start_time_tv.tv_sec = curr_time_tv.tv_sec;
462 start_time_tv.tv_usec = curr_time_tv.tv_usec;
476 LDAPMessage **result )
479 LDAPMessage *newmsg, *l, *prev;
485 LDAPRequest *lr, *tmplr, dummy_lr = { 0 };
487 int rc, refer_cnt, hadref, simple_request, err;
490 #ifdef LDAP_CONNECTIONLESS
491 LDAPMessage *tmp = NULL, *chain_head = NULL;
492 int moremsgs = 0, isv2 = 0;
495 assert( ld != NULL );
496 assert( lc != NULL );
498 #ifdef LDAP_R_COMPILE
499 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
502 Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
503 (void *)ld, msgid, all );
506 if ( lc->lconn_ber == NULL ) {
507 lc->lconn_ber = ldap_alloc_ber_with_options( ld );
509 if ( lc->lconn_ber == NULL ) {
515 assert( LBER_VALID (ber) );
517 /* get the next message */
519 #ifdef LDAP_CONNECTIONLESS
520 if ( LDAP_IS_UDP(ld) ) {
521 struct sockaddr from;
522 ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr) );
523 if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
527 tag = ber_get_next( lc->lconn_sb, &len, ber );
529 case LDAP_TAG_MESSAGE:
531 * We read a complete message.
532 * The connection should no longer need this ber.
534 lc->lconn_ber = NULL;
540 Debug( LDAP_DEBUG_CONNS,
541 "ber_get_next failed.\n", 0, 0, 0 );
544 if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
547 if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
549 ld->ld_errno = LDAP_SERVER_DOWN;
551 lc->lconn_status = 0;
555 ld->ld_errno = LDAP_LOCAL_ERROR;
560 if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
562 ld->ld_errno = LDAP_DECODING_ERROR;
566 /* id == 0 iff unsolicited notification message (RFC 4511) */
568 /* id < 0 is invalid, just toss it. FIXME: should we disconnect? */
573 /* if it's been abandoned, toss it */
575 if ( ldap_abandoned( ld, id, &idx ) ) {
576 /* the message type */
577 tag = ber_peek_tag( ber, &len );
579 case LDAP_RES_SEARCH_ENTRY:
580 case LDAP_RES_SEARCH_REFERENCE:
581 case LDAP_RES_INTERMEDIATE:
586 /* there's no need to keep the id
587 * in the abandoned list any longer */
588 ldap_mark_abandoned( ld, id, idx );
592 Debug( LDAP_DEBUG_ANY,
593 "abandoned/discarded ld %p msgid %d message type %s\n",
594 (void *)ld, id, ldap_int_msgtype2str( tag ) );
598 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
601 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
604 lr = ldap_find_request_by_msgid( ld, id );
606 const char *msg = "unknown";
608 /* the message type */
609 tag = ber_peek_tag( ber, &len );
615 msg = ldap_int_msgtype2str( tag );
619 Debug( LDAP_DEBUG_ANY,
620 "no request for response on ld %p msgid %d message type %s (tossing)\n",
621 (void *)ld, id, msg );
626 #ifdef LDAP_CONNECTIONLESS
627 if ( LDAP_IS_UDP(ld) && isv2 ) {
628 ber_scanf(ber, "x{");
635 /* the message type */
636 tag = ber_peek_tag( ber, &len );
637 if ( tag == LBER_ERROR ) {
638 ld->ld_errno = LDAP_DECODING_ERROR;
643 Debug( LDAP_DEBUG_TRACE,
644 "read1msg: ld %p msgid %d message type %s\n",
645 (void *)ld, id, ldap_int_msgtype2str( tag ) );
648 /* unsolicited notification message (RFC 4511) */
649 if ( tag != LDAP_RES_EXTENDED ) {
653 /* strictly speaking, it's an error; from RFC 4511:
655 4.4. Unsolicited Notification
657 An unsolicited notification is an LDAPMessage sent from the server to
658 the client that is not in response to any LDAPMessage received by the
659 server. It is used to signal an extraordinary condition in the
660 server or in the LDAP session between the client and the server. The
661 notification is of an advisory nature, and the server will not expect
662 any response to be returned from the client.
664 The unsolicited notification is structured as an LDAPMessage in which
665 the messageID is zero and protocolOp is set to the extendedResp
666 choice using the ExtendedResponse type (See Section 4.12). The
667 responseName field of the ExtendedResponse always contains an LDAPOID
668 that is unique for this notification.
670 * however, since unsolicited responses
671 * are of advisory nature, better
676 ld->ld_errno = LDAP_DECODING_ERROR;
687 hadref = simple_request = 0;
688 rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */
689 lr->lr_res_msgtype = tag;
692 * Check for V3 search reference
694 if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
695 if ( ld->ld_version > LDAP_VERSION2 ) {
696 /* This is a V3 search reference */
697 if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
698 lr->lr_parent != NULL )
703 /* Get the referral list */
704 if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
705 rc = LDAP_DECODING_ERROR;
708 /* Note: refs array is freed by ldap_chase_v3referrals */
709 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
710 1, &lr->lr_res_error, &hadref );
711 if ( refer_cnt > 0 ) {
712 /* successfully chased reference */
713 /* If haven't got end search, set chasing referrals */
714 if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
715 lr->lr_status = LDAP_REQST_CHASINGREFS;
716 Debug( LDAP_DEBUG_TRACE,
717 "read1msg: search ref chased, "
718 "mark request chasing refs, "
720 lr->lr_msgid, 0, 0 );
727 } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
728 /* All results that just return a status, i.e. don't return data
729 * go through the following code. This code also chases V2 referrals
730 * and checks if all referrals have been chased.
732 char *lr_res_error = NULL;
734 tmpber = *ber; /* struct copy */
735 if ( ber_scanf( &tmpber, "{eAA", &lderr,
736 &lr->lr_res_matched, &lr_res_error )
739 if ( lr_res_error != NULL ) {
740 if ( lr->lr_res_error != NULL ) {
741 (void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
742 LDAP_FREE( (char *)lr_res_error );
745 lr->lr_res_error = lr_res_error;
750 /* Do we need to check for referrals? */
751 if ( 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 */
851 if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
852 /* request without any referrals */
853 simple_request = ( hadref ? 0 : 1 );
856 /* request with referrals or child request */
861 lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
862 Debug( LDAP_DEBUG_TRACE,
863 "read1msg: mark request completed, ld %p msgid %d\n",
864 (void *)ld, lr->lr_msgid, 0);
865 while ( lr->lr_parent != NULL ) {
866 merge_error_info( ld, lr->lr_parent, lr );
869 if ( --lr->lr_outrefcnt > 0 ) {
870 break; /* not completely done yet */
874 /* Check if all requests are finished, lr is now parent */
876 if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
877 for ( tmplr = lr->lr_child;
879 tmplr = tmplr->lr_refnext )
881 if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
885 /* This is the parent request if the request has referrals */
886 if ( lr->lr_outrefcnt <= 0 &&
887 lr->lr_parent == NULL &&
891 tag = lr->lr_res_msgtype;
892 Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
894 Debug( LDAP_DEBUG_TRACE,
895 "res_errno: %d, res_error: <%s>, "
896 "res_matched: <%s>\n",
898 lr->lr_res_error ? lr->lr_res_error : "",
899 lr->lr_res_matched ? lr->lr_res_matched : "" );
900 if ( !simple_request ) {
903 if ( build_result_ber( ld, &ber, lr )
906 rc = -1; /* fatal error */
910 if ( lr != &dummy_lr ) {
911 ldap_return_request( ld, lr, 1 );
917 * RF 4511 unsolicited (id == 0) responses
918 * shouldn't necessarily end the connection
920 if ( lc != NULL && id != 0 ) {
928 if ( lr != &dummy_lr ) {
929 ldap_return_request( ld, lr, 0 );
938 /* try to handle unsolicited responses as appropriate */
939 if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
942 tag = ber_peek_tag( &tmpber, &len );
944 /* we have a res oid */
945 if ( tag == LDAP_TAG_EXOP_RES_OID ) {
946 static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
947 struct berval resoid = BER_BVNULL;
949 if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
950 ld->ld_errno = LDAP_DECODING_ERROR;
955 assert( !BER_BVISEMPTY( &resoid ) );
957 is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
959 tag = ber_peek_tag( &tmpber, &len );
962 #if 0 /* don't need right now */
963 /* we have res data */
964 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
965 struct berval resdata;
967 if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
968 ld->ld_errno = LDAP_DECODING_ERROR;
977 /* handle RFC 4511 "Notice of Disconnection" locally */
980 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
981 ld->ld_errno = LDAP_DECODING_ERROR;
986 /* get rid of the connection... */
991 /* need to return -1, because otherwise
992 * a valid result is expected */
997 /* make a new ldap message */
998 newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
999 if ( newmsg == NULL ) {
1000 ld->ld_errno = LDAP_NO_MEMORY;
1003 newmsg->lm_msgid = (int)id;
1004 newmsg->lm_msgtype = tag;
1005 newmsg->lm_ber = ber;
1006 newmsg->lm_chain_tail = newmsg;
1008 #ifdef LDAP_CONNECTIONLESS
1009 /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
1010 * the responses are all a sequence wrapped in one message. In
1011 * LDAPv3 each response is in its own message. The datagram must
1012 * end with a SearchResult. We can't just parse each response in
1013 * separate calls to try_read1msg because the header info is only
1014 * present at the beginning of the datagram, not at the beginning
1015 * of each response. So parse all the responses at once and queue
1016 * them up, then pull off the first response to return to the
1017 * caller when all parsing is complete.
1019 if ( LDAP_IS_UDP(ld) ) {
1020 /* If not a result, look for more */
1021 if ( tag != LDAP_RES_SEARCH_RESULT ) {
1025 /* LDAPv2: dup the current ber, skip past the current
1026 * response, and see if there are any more after it.
1028 ber = ber_dup( ber );
1029 ber_scanf( ber, "x" );
1030 if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
1031 /* There's more - dup the ber buffer so they can all be
1032 * individually freed by ldap_msgfree.
1035 ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1036 bv.bv_val = LDAP_MALLOC( len );
1039 ber_read( ber, bv.bv_val, len );
1041 ber_init2( ber, &bv, ld->ld_lberoptions );
1045 /* LDAPv3: Just allocate a new ber. Since this is a buffered
1046 * datagram, if the sockbuf is readable we still have data
1049 ber = ldap_alloc_ber_with_options( ld );
1050 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1052 /* set up response chain */
1053 if ( tmp == NULL ) {
1054 newmsg->lm_next = ld->ld_responses;
1055 ld->ld_responses = newmsg;
1056 chain_head = newmsg;
1058 tmp->lm_chain = newmsg;
1060 chain_head->lm_chain_tail = newmsg;
1062 /* "ok" means there's more to parse */
1071 /* got to end of datagram without a SearchResult. Free
1072 * our dup'd ber, but leave any buffer alone. For v2 case,
1073 * the previous response is still using this buffer. For v3,
1074 * the new ber has no buffer to free yet.
1079 } else if ( moremsgs ) {
1080 /* got search result, and we had multiple responses in 1 datagram.
1081 * stick the result onto the end of the chain, and then pull the
1082 * first response off the head of the chain.
1084 tmp->lm_chain = newmsg;
1085 chain_head->lm_chain_tail = newmsg;
1086 *result = chkResponseList( ld, msgid, all );
1087 ld->ld_errno = LDAP_SUCCESS;
1088 return( (*result)->lm_msgtype );
1091 #endif /* LDAP_CONNECTIONLESS */
1093 /* is this the one we're looking for? */
1094 if ( msgid == LDAP_RES_ANY || id == msgid ) {
1095 if ( all == LDAP_MSG_ONE
1096 || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1097 && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1098 && newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE
1099 && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1102 ld->ld_errno = LDAP_SUCCESS;
1105 } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1106 foundit = 1; /* return the chain later */
1111 * if not, we must add it to the list of responses. if
1112 * the msgid is already there, it must be part of an existing
1117 for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1118 if ( l->lm_msgid == newmsg->lm_msgid ) {
1124 /* not part of an existing search response */
1131 newmsg->lm_next = ld->ld_responses;
1132 ld->ld_responses = newmsg;
1136 Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
1137 (void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1139 /* part of a search response - add to end of list of entries */
1140 l->lm_chain_tail->lm_chain = newmsg;
1141 l->lm_chain_tail = newmsg;
1143 /* return the whole chain if that's what we were looking for */
1145 if ( prev == NULL ) {
1146 ld->ld_responses = l->lm_next;
1148 prev->lm_next = l->lm_next;
1155 ld->ld_errno = LDAP_SUCCESS;
1158 if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1161 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
1166 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1174 ber = ldap_alloc_ber_with_options( ld );
1177 ld->ld_errno = LDAP_NO_MEMORY;
1181 if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1182 lr->lr_res_msgtype, lr->lr_res_errno,
1183 lr->lr_res_matched ? lr->lr_res_matched : "",
1184 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1186 ld->ld_errno = LDAP_ENCODING_ERROR;
1188 return( LBER_ERROR );
1191 ber_reset( ber, 1 );
1193 if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1194 ld->ld_errno = LDAP_DECODING_ERROR;
1196 return( LBER_ERROR );
1199 if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1200 ld->ld_errno = LDAP_DECODING_ERROR;
1202 return( LBER_ERROR );
1205 tag = ber_peek_tag( ber, &len );
1207 if ( tag == LBER_ERROR ) {
1208 ld->ld_errno = LDAP_DECODING_ERROR;
1210 return( LBER_ERROR );
1219 * Merge error information in "lr" with "parentr" error code and string.
1222 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1224 if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1225 parentr->lr_res_errno = lr->lr_res_errno;
1226 if ( lr->lr_res_error != NULL ) {
1227 (void)ldap_append_referral( ld, &parentr->lr_res_error,
1231 } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1232 parentr->lr_res_errno == LDAP_SUCCESS )
1234 parentr->lr_res_errno = lr->lr_res_errno;
1235 if ( parentr->lr_res_error != NULL ) {
1236 LDAP_FREE( parentr->lr_res_error );
1238 parentr->lr_res_error = lr->lr_res_error;
1239 lr->lr_res_error = NULL;
1240 if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1241 if ( parentr->lr_res_matched != NULL ) {
1242 LDAP_FREE( parentr->lr_res_matched );
1244 parentr->lr_res_matched = lr->lr_res_matched;
1245 lr->lr_res_matched = NULL;
1249 Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
1250 parentr->lr_msgid, 0, 0 );
1251 Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1252 parentr->lr_res_errno,
1253 parentr->lr_res_error ? parentr->lr_res_error : "",
1254 parentr->lr_res_matched ? parentr->lr_res_matched : "" );
1260 ldap_msgtype( LDAPMessage *lm )
1262 assert( lm != NULL );
1263 return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1268 ldap_msgid( LDAPMessage *lm )
1270 assert( lm != NULL );
1272 return ( lm != NULL ) ? lm->lm_msgid : -1;
1277 ldap_int_msgtype2str( ber_tag_t tag )
1280 case LDAP_RES_ADD: return "add";
1281 case LDAP_RES_BIND: return "bind";
1282 case LDAP_RES_COMPARE: return "compare";
1283 case LDAP_RES_DELETE: return "delete";
1284 case LDAP_RES_EXTENDED: return "extended-result";
1285 case LDAP_RES_INTERMEDIATE: return "intermediate";
1286 case LDAP_RES_MODIFY: return "modify";
1287 case LDAP_RES_RENAME: return "rename";
1288 case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1289 case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1290 case LDAP_RES_SEARCH_RESULT: return "search-result";
1296 ldap_msgfree( LDAPMessage *lm )
1301 Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1303 for ( ; lm != NULL; lm = next ) {
1304 next = lm->lm_chain;
1305 type = lm->lm_msgtype;
1306 ber_free( lm->lm_ber, 1 );
1307 LDAP_FREE( (char *) lm );
1314 * ldap_msgdelete - delete a message. It returns:
1315 * 0 if the entire message was deleted
1316 * -1 if the message was not found, or only part of it was found
1319 ldap_msgdelete( LDAP *ld, int msgid )
1321 LDAPMessage *lm, *prev;
1324 assert( ld != NULL );
1326 Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1327 (void *)ld, msgid, 0 );
1329 #ifdef LDAP_R_COMPILE
1330 ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
1333 for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1334 if ( lm->lm_msgid == msgid ) {
1344 if ( prev == NULL ) {
1345 ld->ld_responses = lm->lm_next;
1347 prev->lm_next = lm->lm_next;
1350 #ifdef LDAP_R_COMPILE
1351 ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
1354 switch ( ldap_msgfree( lm ) ) {
1355 case LDAP_RES_SEARCH_ENTRY:
1356 case LDAP_RES_SEARCH_REFERENCE:
1357 case LDAP_RES_INTERMEDIATE:
1373 * return the location of the message id in the array of abandoned
1374 * message ids, or -1
1376 * expects ld_res_mutex to be locked
1379 ldap_abandoned( LDAP *ld, ber_int_t msgid, int *idxp )
1381 #ifdef LDAP_R_COMPILE
1382 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1385 assert( idxp != NULL );
1386 assert( msgid >= 0 );
1387 assert( ld->ld_nabandoned >= 0 );
1389 return ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp );
1393 * ldap_mark_abandoned
1395 * expects ld_res_mutex to be locked
1398 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid, int idx )
1400 #ifdef LDAP_R_COMPILE
1401 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1404 /* NOTE: those assertions are repeated in ldap_int_bisect_delete() */
1406 assert( (unsigned) idx < ld->ld_nabandoned );
1407 assert( ld->ld_abandoned[ idx ] == msgid );
1409 return ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,