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 );
506 if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
507 if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
508 ld->ld_errno = LDAP_SERVER_DOWN;
510 lc->lconn_status = 0;
514 ld->ld_errno = LDAP_LOCAL_ERROR;
519 if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
521 ld->ld_errno = LDAP_DECODING_ERROR;
525 /* id == 0 iff unsolicited notification message (RFC 4511) */
527 /* id < 0 is invalid, just toss it. FIXME: should we disconnect? */
532 /* if it's been abandoned, toss it */
534 if ( ldap_abandoned( ld, id, &idx ) ) {
535 /* the message type */
536 tag = ber_peek_tag( ber, &len );
538 case LDAP_RES_SEARCH_ENTRY:
539 case LDAP_RES_SEARCH_REFERENCE:
540 case LDAP_RES_INTERMEDIATE:
545 /* there's no need to keep the id
546 * in the abandoned list any longer */
547 ldap_mark_abandoned( ld, id, idx );
551 Debug( LDAP_DEBUG_ANY,
552 "abandoned/discarded ld %p msgid %d message type %s\n",
553 (void *)ld, id, ldap_int_msgtype2str( tag ) );
557 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
560 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
563 lr = ldap_find_request_by_msgid( ld, id );
565 const char *msg = "unknown";
567 /* the message type */
568 tag = ber_peek_tag( ber, &len );
574 msg = ldap_int_msgtype2str( tag );
578 Debug( LDAP_DEBUG_ANY,
579 "no request for response on ld %p msgid %d message type %s (tossing)\n",
580 (void *)ld, id, msg );
585 #ifdef LDAP_CONNECTIONLESS
586 if ( LDAP_IS_UDP(ld) && isv2 ) {
587 ber_scanf(ber, "x{");
594 /* the message type */
595 tag = ber_peek_tag( ber, &len );
596 if ( tag == LBER_ERROR ) {
597 ld->ld_errno = LDAP_DECODING_ERROR;
602 Debug( LDAP_DEBUG_TRACE,
603 "read1msg: ld %p msgid %d message type %s\n",
604 (void *)ld, id, ldap_int_msgtype2str( tag ) );
607 /* unsolicited notification message (RFC 4511) */
608 if ( tag != LDAP_RES_EXTENDED ) {
612 /* strictly speaking, it's an error; from RFC 4511:
614 4.4. Unsolicited Notification
616 An unsolicited notification is an LDAPMessage sent from the server to
617 the client that is not in response to any LDAPMessage received by the
618 server. It is used to signal an extraordinary condition in the
619 server or in the LDAP session between the client and the server. The
620 notification is of an advisory nature, and the server will not expect
621 any response to be returned from the client.
623 The unsolicited notification is structured as an LDAPMessage in which
624 the messageID is zero and protocolOp is set to the extendedResp
625 choice using the ExtendedResponse type (See Section 4.12). The
626 responseName field of the ExtendedResponse always contains an LDAPOID
627 that is unique for this notification.
629 * however, since unsolicited responses
630 * are of advisory nature, better
635 ld->ld_errno = LDAP_DECODING_ERROR;
646 hadref = simple_request = 0;
647 rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */
648 lr->lr_res_msgtype = tag;
651 * Check for V3 search reference
653 if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
654 if ( ld->ld_version > LDAP_VERSION2 ) {
655 /* This is a V3 search reference */
656 if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
657 lr->lr_parent != NULL )
662 /* Get the referral list */
663 if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
664 rc = LDAP_DECODING_ERROR;
667 /* Note: refs array is freed by ldap_chase_v3referrals */
668 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
669 1, &lr->lr_res_error, &hadref );
670 if ( refer_cnt > 0 ) {
671 /* successfully chased reference */
672 /* If haven't got end search, set chasing referrals */
673 if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
674 lr->lr_status = LDAP_REQST_CHASINGREFS;
675 Debug( LDAP_DEBUG_TRACE,
676 "read1msg: search ref chased, "
677 "mark request chasing refs, "
679 lr->lr_msgid, 0, 0 );
686 } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
687 /* All results that just return a status, i.e. don't return data
688 * go through the following code. This code also chases V2 referrals
689 * and checks if all referrals have been chased.
691 char *lr_res_error = NULL;
693 tmpber = *ber; /* struct copy */
694 if ( ber_scanf( &tmpber, "{eAA", &lderr,
695 &lr->lr_res_matched, &lr_res_error )
698 if ( lr_res_error != NULL ) {
699 if ( lr->lr_res_error != NULL ) {
700 (void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
701 LDAP_FREE( (char *)lr_res_error );
704 lr->lr_res_error = lr_res_error;
709 /* Do we need to check for referrals? */
710 if ( tag != LDAP_RES_BIND &&
711 ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
712 lr->lr_parent != NULL ))
717 /* Check if V3 referral */
718 if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
719 if ( ld->ld_version > LDAP_VERSION2 ) {
720 /* Get the referral list */
721 if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
722 rc = LDAP_DECODING_ERROR;
723 lr->lr_status = LDAP_REQST_COMPLETED;
724 Debug( LDAP_DEBUG_TRACE,
725 "read1msg: referral decode error, "
726 "mark request completed, ld %p msgid %d\n",
727 (void *)ld, lr->lr_msgid, 0 );
730 /* Chase the referral
731 * refs array is freed by ldap_chase_v3referrals
733 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
734 0, &lr->lr_res_error, &hadref );
735 lr->lr_status = LDAP_REQST_COMPLETED;
736 Debug( LDAP_DEBUG_TRACE,
737 "read1msg: referral %s chased, "
738 "mark request completed, ld %p msgid %d\n",
739 refer_cnt > 0 ? "" : "not",
740 (void *)ld, lr->lr_msgid);
741 if ( refer_cnt < 0 ) {
749 case LDAP_COMPARE_TRUE:
750 case LDAP_COMPARE_FALSE:
754 if ( lr->lr_res_error == NULL ) {
758 /* pedantic, should never happen */
759 if ( lr->lr_res_error[ 0 ] == '\0' ) {
760 LDAP_FREE( lr->lr_res_error );
761 lr->lr_res_error = NULL;
765 /* V2 referrals are in error string */
766 refer_cnt = ldap_chase_referrals( ld, lr,
767 &lr->lr_res_error, -1, &hadref );
768 lr->lr_status = LDAP_REQST_COMPLETED;
769 Debug( LDAP_DEBUG_TRACE,
770 "read1msg: V2 referral chased, "
771 "mark request completed, id = %d\n",
772 lr->lr_msgid, 0, 0 );
778 /* save errno, message, and matched string */
779 if ( !hadref || lr->lr_res_error == NULL ) {
781 lderr == LDAP_PARTIAL_RESULTS
782 ? LDAP_SUCCESS : lderr;
784 } else if ( ld->ld_errno != LDAP_SUCCESS ) {
785 lr->lr_res_errno = ld->ld_errno;
788 lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
792 /* in any case, don't leave any lr_res_error 'round */
793 if ( lr_res_error ) {
794 LDAP_FREE( lr_res_error );
797 Debug( LDAP_DEBUG_TRACE,
798 "read1msg: ld %p %d new referrals\n",
799 (void *)ld, refer_cnt, 0 );
801 if ( refer_cnt != 0 ) { /* chasing referrals */
804 if ( refer_cnt < 0 ) {
805 ldap_return_request( ld, lr, 0 );
806 return( -1 ); /* fatal error */
808 lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
809 if ( lr->lr_res_matched ) {
810 LDAP_FREE( lr->lr_res_matched );
811 lr->lr_res_matched = NULL;
815 if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
816 /* request without any referrals */
817 simple_request = ( hadref ? 0 : 1 );
820 /* request with referrals or child request */
825 lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
826 Debug( LDAP_DEBUG_TRACE,
827 "read1msg: mark request completed, ld %p msgid %d\n",
828 (void *)ld, lr->lr_msgid, 0);
830 while ( lr->lr_parent != NULL ) {
831 merge_error_info( ld, lr->lr_parent, lr );
834 if ( --lr->lr_outrefcnt > 0 ) {
835 break; /* not completely done yet */
838 /* ITS#6744: Original lr was refcounted when we retrieved it,
839 * must release it now that we're working with the parent
841 if ( tmplr->lr_parent ) {
842 ldap_return_request( ld, tmplr, 0 );
845 /* Check if all requests are finished, lr is now parent */
847 if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
848 for ( tmplr = lr->lr_child;
850 tmplr = tmplr->lr_refnext )
852 if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
856 /* This is the parent request if the request has referrals */
857 if ( lr->lr_outrefcnt <= 0 &&
858 lr->lr_parent == NULL &&
862 tag = lr->lr_res_msgtype;
863 Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
865 Debug( LDAP_DEBUG_TRACE,
866 "res_errno: %d, res_error: <%s>, "
867 "res_matched: <%s>\n",
869 lr->lr_res_error ? lr->lr_res_error : "",
870 lr->lr_res_matched ? lr->lr_res_matched : "" );
871 if ( !simple_request ) {
874 if ( build_result_ber( ld, &ber, lr )
877 rc = -1; /* fatal error */
881 if ( lr != &dummy_lr ) {
882 ldap_return_request( ld, lr, 1 );
888 * RFC 4511 unsolicited (id == 0) responses
889 * shouldn't necessarily end the connection
891 if ( lc != NULL && id != 0 ) {
899 if ( lr != &dummy_lr ) {
900 ldap_return_request( ld, lr, 0 );
909 /* try to handle unsolicited responses as appropriate */
910 if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
913 tag = ber_peek_tag( &tmpber, &len );
915 /* we have a res oid */
916 if ( tag == LDAP_TAG_EXOP_RES_OID ) {
917 static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
918 struct berval resoid = BER_BVNULL;
920 if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
921 ld->ld_errno = LDAP_DECODING_ERROR;
926 assert( !BER_BVISEMPTY( &resoid ) );
928 is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
930 tag = ber_peek_tag( &tmpber, &len );
933 #if 0 /* don't need right now */
934 /* we have res data */
935 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
936 struct berval resdata;
938 if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
939 ld->ld_errno = LDAP_DECODING_ERROR;
948 /* handle RFC 4511 "Notice of Disconnection" locally */
951 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
952 ld->ld_errno = LDAP_DECODING_ERROR;
957 /* get rid of the connection... */
962 /* need to return -1, because otherwise
963 * a valid result is expected */
964 ld->ld_errno = lderr;
969 /* make a new ldap message */
970 newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
971 if ( newmsg == NULL ) {
972 ld->ld_errno = LDAP_NO_MEMORY;
975 newmsg->lm_msgid = (int)id;
976 newmsg->lm_msgtype = tag;
977 newmsg->lm_ber = ber;
978 newmsg->lm_chain_tail = newmsg;
980 #ifdef LDAP_CONNECTIONLESS
981 /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
982 * the responses are all a sequence wrapped in one message. In
983 * LDAPv3 each response is in its own message. The datagram must
984 * end with a SearchResult. We can't just parse each response in
985 * separate calls to try_read1msg because the header info is only
986 * present at the beginning of the datagram, not at the beginning
987 * of each response. So parse all the responses at once and queue
988 * them up, then pull off the first response to return to the
989 * caller when all parsing is complete.
991 if ( LDAP_IS_UDP(ld) ) {
992 /* If not a result, look for more */
993 if ( tag != LDAP_RES_SEARCH_RESULT ) {
997 /* LDAPv2: dup the current ber, skip past the current
998 * response, and see if there are any more after it.
1000 ber = ber_dup( ber );
1001 ber_scanf( ber, "x" );
1002 if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
1003 /* There's more - dup the ber buffer so they can all be
1004 * individually freed by ldap_msgfree.
1007 ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1008 bv.bv_val = LDAP_MALLOC( len );
1011 ber_read( ber, bv.bv_val, len );
1013 ber_init2( ber, &bv, ld->ld_lberoptions );
1017 /* LDAPv3: Just allocate a new ber. Since this is a buffered
1018 * datagram, if the sockbuf is readable we still have data
1021 ber = ldap_alloc_ber_with_options( ld );
1022 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1024 /* set up response chain */
1025 if ( tmp == NULL ) {
1026 newmsg->lm_next = ld->ld_responses;
1027 ld->ld_responses = newmsg;
1028 chain_head = newmsg;
1030 tmp->lm_chain = newmsg;
1032 chain_head->lm_chain_tail = newmsg;
1034 /* "ok" means there's more to parse */
1043 /* got to end of datagram without a SearchResult. Free
1044 * our dup'd ber, but leave any buffer alone. For v2 case,
1045 * the previous response is still using this buffer. For v3,
1046 * the new ber has no buffer to free yet.
1051 } else if ( moremsgs ) {
1052 /* got search result, and we had multiple responses in 1 datagram.
1053 * stick the result onto the end of the chain, and then pull the
1054 * first response off the head of the chain.
1056 tmp->lm_chain = newmsg;
1057 chain_head->lm_chain_tail = newmsg;
1058 *result = chkResponseList( ld, msgid, all );
1059 ld->ld_errno = LDAP_SUCCESS;
1060 return( (*result)->lm_msgtype );
1063 #endif /* LDAP_CONNECTIONLESS */
1065 /* is this the one we're looking for? */
1066 if ( msgid == LDAP_RES_ANY || id == msgid ) {
1067 if ( all == LDAP_MSG_ONE
1068 || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1069 && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1070 && newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE
1071 && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1074 ld->ld_errno = LDAP_SUCCESS;
1077 } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1078 foundit = 1; /* return the chain later */
1083 * if not, we must add it to the list of responses. if
1084 * the msgid is already there, it must be part of an existing
1089 for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1090 if ( l->lm_msgid == newmsg->lm_msgid ) {
1096 /* not part of an existing search response */
1103 newmsg->lm_next = ld->ld_responses;
1104 ld->ld_responses = newmsg;
1108 Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
1109 (void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1111 /* part of a search response - add to end of list of entries */
1112 l->lm_chain_tail->lm_chain = newmsg;
1113 l->lm_chain_tail = newmsg;
1115 /* return the whole chain if that's what we were looking for */
1117 if ( prev == NULL ) {
1118 ld->ld_responses = l->lm_next;
1120 prev->lm_next = l->lm_next;
1127 ld->ld_errno = LDAP_SUCCESS;
1130 if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1133 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
1138 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1146 ber = ldap_alloc_ber_with_options( ld );
1149 ld->ld_errno = LDAP_NO_MEMORY;
1153 if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1154 lr->lr_res_msgtype, lr->lr_res_errno,
1155 lr->lr_res_matched ? lr->lr_res_matched : "",
1156 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1158 ld->ld_errno = LDAP_ENCODING_ERROR;
1160 return( LBER_ERROR );
1163 ber_reset( ber, 1 );
1165 if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1166 ld->ld_errno = LDAP_DECODING_ERROR;
1168 return( LBER_ERROR );
1171 if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1172 ld->ld_errno = LDAP_DECODING_ERROR;
1174 return( LBER_ERROR );
1177 tag = ber_peek_tag( ber, &len );
1179 if ( tag == LBER_ERROR ) {
1180 ld->ld_errno = LDAP_DECODING_ERROR;
1182 return( LBER_ERROR );
1191 * Merge error information in "lr" with "parentr" error code and string.
1194 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1196 if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1197 parentr->lr_res_errno = lr->lr_res_errno;
1198 if ( lr->lr_res_error != NULL ) {
1199 (void)ldap_append_referral( ld, &parentr->lr_res_error,
1203 } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1204 parentr->lr_res_errno == LDAP_SUCCESS )
1206 parentr->lr_res_errno = lr->lr_res_errno;
1207 if ( parentr->lr_res_error != NULL ) {
1208 LDAP_FREE( parentr->lr_res_error );
1210 parentr->lr_res_error = lr->lr_res_error;
1211 lr->lr_res_error = NULL;
1212 if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1213 if ( parentr->lr_res_matched != NULL ) {
1214 LDAP_FREE( parentr->lr_res_matched );
1216 parentr->lr_res_matched = lr->lr_res_matched;
1217 lr->lr_res_matched = NULL;
1221 Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
1222 parentr->lr_msgid, 0, 0 );
1223 Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1224 parentr->lr_res_errno,
1225 parentr->lr_res_error ? parentr->lr_res_error : "",
1226 parentr->lr_res_matched ? parentr->lr_res_matched : "" );
1232 ldap_msgtype( LDAPMessage *lm )
1234 assert( lm != NULL );
1235 return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1240 ldap_msgid( LDAPMessage *lm )
1242 assert( lm != NULL );
1244 return ( lm != NULL ) ? lm->lm_msgid : -1;
1249 ldap_int_msgtype2str( ber_tag_t tag )
1252 case LDAP_RES_ADD: return "add";
1253 case LDAP_RES_BIND: return "bind";
1254 case LDAP_RES_COMPARE: return "compare";
1255 case LDAP_RES_DELETE: return "delete";
1256 case LDAP_RES_EXTENDED: return "extended-result";
1257 case LDAP_RES_INTERMEDIATE: return "intermediate";
1258 case LDAP_RES_MODIFY: return "modify";
1259 case LDAP_RES_RENAME: return "rename";
1260 case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1261 case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1262 case LDAP_RES_SEARCH_RESULT: return "search-result";
1268 ldap_msgfree( LDAPMessage *lm )
1273 Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1275 for ( ; lm != NULL; lm = next ) {
1276 next = lm->lm_chain;
1277 type = lm->lm_msgtype;
1278 ber_free( lm->lm_ber, 1 );
1279 LDAP_FREE( (char *) lm );
1286 * ldap_msgdelete - delete a message. It returns:
1287 * 0 if the entire message was deleted
1288 * -1 if the message was not found, or only part of it was found
1291 ldap_msgdelete( LDAP *ld, int msgid )
1293 LDAPMessage *lm, *prev;
1296 assert( ld != NULL );
1298 Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1299 (void *)ld, msgid, 0 );
1301 LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
1303 for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1304 if ( lm->lm_msgid == msgid ) {
1314 if ( prev == NULL ) {
1315 ld->ld_responses = lm->lm_next;
1317 prev->lm_next = lm->lm_next;
1320 LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
1322 switch ( ldap_msgfree( lm ) ) {
1323 case LDAP_RES_SEARCH_ENTRY:
1324 case LDAP_RES_SEARCH_REFERENCE:
1325 case LDAP_RES_INTERMEDIATE:
1341 * return the location of the message id in the array of abandoned
1342 * message ids, or -1
1344 * expects ld_res_mutex to be locked
1347 ldap_abandoned( LDAP *ld, ber_int_t msgid, int *idxp )
1349 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1351 assert( idxp != NULL );
1352 assert( msgid >= 0 );
1354 return ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp );
1358 * ldap_mark_abandoned
1360 * expects ld_res_mutex to be locked
1363 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid, int idx )
1365 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1367 /* NOTE: those assertions are repeated in ldap_int_bisect_delete() */
1369 assert( (unsigned) idx < ld->ld_nabandoned );
1370 assert( ld->ld_abandoned[ idx ] == msgid );
1372 return ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,