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 ));
70 static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
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 );
123 /* protected by res_mutex */
130 LDAPMessage *lm, **lastlm, *nextlm;
134 * Look through the list of responses we have received on
135 * this association and see if the response we're interested in
136 * is there. If it is, return it. If not, call wait4msg() to
137 * wait until it arrives or timeout occurs.
140 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
142 Debug( LDAP_DEBUG_TRACE,
143 "ldap_chkResponseList ld %p msgid %d all %d\n",
144 (void *)ld, msgid, all );
146 lastlm = &ld->ld_responses;
147 for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
148 nextlm = lm->lm_next;
151 if ( ldap_abandoned( ld, lm->lm_msgid ) ) {
152 Debug( LDAP_DEBUG_ANY,
153 "response list msg abandoned, "
154 "msgid %d message type %s\n",
155 lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ), 0 );
157 switch ( lm->lm_msgtype ) {
158 case LDAP_RES_SEARCH_ENTRY:
159 case LDAP_RES_SEARCH_REFERENCE:
160 case LDAP_RES_INTERMEDIATE:
164 /* there's no need to keep the id
165 * in the abandoned list any longer */
166 ldap_mark_abandoned( ld, lm->lm_msgid );
170 /* Remove this entry from list */
178 if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
181 if ( all == LDAP_MSG_ONE ||
182 all == LDAP_MSG_RECEIVED ||
183 msgid == LDAP_RES_UNSOLICITED )
188 tmp = lm->lm_chain_tail;
189 if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY ||
190 tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ||
191 tmp->lm_msgtype == LDAP_RES_INTERMEDIATE )
202 lastlm = &lm->lm_next;
206 /* Found an entry, remove it from the list */
207 if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) {
208 *lastlm = lm->lm_chain;
209 lm->lm_chain->lm_next = lm->lm_next;
210 lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain;
212 lm->lm_chain_tail = NULL;
214 *lastlm = lm->lm_next;
221 Debug( LDAP_DEBUG_TRACE,
222 "ldap_chkResponseList returns ld %p NULL\n", (void *)ld, 0, 0);
224 Debug( LDAP_DEBUG_TRACE,
225 "ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n",
226 (void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
233 /* protected by res_mutex */
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_dump_connection( ld, ld->ld_conns, 1 );
288 LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
289 ldap_dump_requests_and_responses( ld );
290 LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
292 #endif /* LDAP_DEBUG */
294 if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
295 rc = (*result)->lm_msgtype;
300 LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
301 for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
302 if ( ber_sockbuf_ctrl( lc->lconn_sb,
303 LBER_SB_OPT_DATA_READY, NULL ) )
312 rc = ldap_int_select( ld, tvp );
316 Debug( LDAP_DEBUG_TRACE,
317 "ldap_int_select returned -1: errno %d\n",
322 if ( rc == 0 || ( rc == -1 && (
323 !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
324 || err != EINTR ) ) )
326 ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
328 LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
333 rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */
341 rc = LDAP_MSG_X_KEEP_LOOKING;
342 LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
343 if ( ld->ld_requests &&
344 ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
345 ldap_is_write_ready( ld,
346 ld->ld_requests->lr_conn->lconn_sb ) )
348 ldap_int_flush_request( ld, ld->ld_requests );
350 for ( lc = ld->ld_conns;
351 rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL;
354 if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
355 ldap_is_read_ready( ld, lc->lconn_sb ) )
357 /* Don't let it get freed out from under us */
359 rc = try_read1msg( ld, msgid, all, lc, result );
360 lnext = lc->lconn_next;
362 /* Only take locks if we're really freeing */
363 if ( lc->lconn_refcnt <= 1 ) {
364 ldap_free_connection( ld, lc, 0, 1 );
369 lnext = lc->lconn_next;
372 LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
374 LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
377 if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
378 struct timeval curr_time_tv = { 0 },
379 delta_time_tv = { 0 };
381 #ifdef HAVE_GETTIMEOFDAY
382 gettimeofday( &curr_time_tv, NULL );
383 #else /* ! HAVE_GETTIMEOFDAY */
384 time( &curr_time_tv.tv_sec );
385 curr_time_tv.tv_usec = 0;
386 #endif /* ! HAVE_GETTIMEOFDAY */
388 /* delta_time = tmp_time - start_time */
389 delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
390 delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
391 if ( delta_time_tv.tv_usec < 0 ) {
392 delta_time_tv.tv_sec--;
393 delta_time_tv.tv_usec += 1000000;
396 /* tv0 < delta_time ? */
397 if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
398 ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
400 rc = 0; /* timed out */
401 ld->ld_errno = LDAP_TIMEOUT;
405 /* tv0 -= delta_time */
406 tv0.tv_sec -= delta_time_tv.tv_sec;
407 tv0.tv_usec -= delta_time_tv.tv_usec;
408 if ( tv0.tv_usec < 0 ) {
410 tv0.tv_usec += 1000000;
413 tv.tv_sec = tv0.tv_sec;
414 tv.tv_usec = tv0.tv_usec;
416 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n",
417 (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
419 start_time_tv.tv_sec = curr_time_tv.tv_sec;
420 start_time_tv.tv_usec = curr_time_tv.tv_usec;
428 /* protected by res_mutex, conn_mutex and req_mutex */
435 LDAPMessage **result )
438 LDAPMessage *newmsg, *l, *prev;
443 LDAPRequest *lr, *tmplr, dummy_lr = { 0 };
445 int rc, refer_cnt, hadref, simple_request, err;
448 #ifdef LDAP_CONNECTIONLESS
449 LDAPMessage *tmp = NULL, *chain_head = NULL;
450 int moremsgs = 0, isv2 = 0;
453 assert( ld != NULL );
454 assert( lc != NULL );
456 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
457 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
458 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
460 Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
461 (void *)ld, msgid, all );
464 if ( lc->lconn_ber == NULL ) {
465 lc->lconn_ber = ldap_alloc_ber_with_options( ld );
467 if ( lc->lconn_ber == NULL ) {
473 assert( LBER_VALID (ber) );
475 /* get the next message */
477 #ifdef LDAP_CONNECTIONLESS
478 if ( LDAP_IS_UDP(ld) ) {
479 struct sockaddr from;
480 ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr) );
481 if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
485 tag = ber_get_next( lc->lconn_sb, &len, ber );
487 case LDAP_TAG_MESSAGE:
489 * We read a complete message.
490 * The connection should no longer need this ber.
492 lc->lconn_ber = NULL;
498 Debug( LDAP_DEBUG_CONNS,
499 "ber_get_next failed.\n", 0, 0, 0 );
501 if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
502 if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
503 ld->ld_errno = LDAP_SERVER_DOWN;
505 lc->lconn_status = 0;
509 ld->ld_errno = LDAP_LOCAL_ERROR;
514 if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
516 ld->ld_errno = LDAP_DECODING_ERROR;
520 /* id == 0 iff unsolicited notification message (RFC 4511) */
522 /* id < 0 is invalid, just toss it. FIXME: should we disconnect? */
527 /* if it's been abandoned, toss it */
529 if ( ldap_abandoned( ld, id ) ) {
530 /* the message type */
531 tag = ber_peek_tag( ber, &len );
533 case LDAP_RES_SEARCH_ENTRY:
534 case LDAP_RES_SEARCH_REFERENCE:
535 case LDAP_RES_INTERMEDIATE:
540 /* there's no need to keep the id
541 * in the abandoned list any longer */
542 ldap_mark_abandoned( ld, id );
546 Debug( LDAP_DEBUG_ANY,
547 "abandoned/discarded ld %p msgid %d message type %s\n",
548 (void *)ld, id, ldap_int_msgtype2str( tag ) );
552 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
555 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
558 lr = ldap_find_request_by_msgid( ld, id );
560 const char *msg = "unknown";
562 /* the message type */
563 tag = ber_peek_tag( ber, &len );
569 msg = ldap_int_msgtype2str( tag );
573 Debug( LDAP_DEBUG_ANY,
574 "no request for response on ld %p msgid %d message type %s (tossing)\n",
575 (void *)ld, id, msg );
580 #ifdef LDAP_CONNECTIONLESS
581 if ( LDAP_IS_UDP(ld) && isv2 ) {
582 ber_scanf(ber, "x{");
589 /* the message type */
590 tag = ber_peek_tag( ber, &len );
591 if ( tag == LBER_ERROR ) {
592 ld->ld_errno = LDAP_DECODING_ERROR;
597 Debug( LDAP_DEBUG_TRACE,
598 "read1msg: ld %p msgid %d message type %s\n",
599 (void *)ld, id, ldap_int_msgtype2str( tag ) );
602 /* unsolicited notification message (RFC 4511) */
603 if ( tag != LDAP_RES_EXTENDED ) {
607 /* strictly speaking, it's an error; from RFC 4511:
609 4.4. Unsolicited Notification
611 An unsolicited notification is an LDAPMessage sent from the server to
612 the client that is not in response to any LDAPMessage received by the
613 server. It is used to signal an extraordinary condition in the
614 server or in the LDAP session between the client and the server. The
615 notification is of an advisory nature, and the server will not expect
616 any response to be returned from the client.
618 The unsolicited notification is structured as an LDAPMessage in which
619 the messageID is zero and protocolOp is set to the extendedResp
620 choice using the ExtendedResponse type (See Section 4.12). The
621 responseName field of the ExtendedResponse always contains an LDAPOID
622 that is unique for this notification.
624 * however, since unsolicited responses
625 * are of advisory nature, better
630 ld->ld_errno = LDAP_DECODING_ERROR;
641 hadref = simple_request = 0;
642 rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */
643 lr->lr_res_msgtype = tag;
646 * Check for V3 search reference
648 if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
649 if ( ld->ld_version > LDAP_VERSION2 ) {
650 /* This is a V3 search reference */
651 if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
652 lr->lr_parent != NULL )
657 /* Get the referral list */
658 if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
659 rc = LDAP_DECODING_ERROR;
662 /* Note: refs array is freed by ldap_chase_v3referrals */
663 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
664 1, &lr->lr_res_error, &hadref );
665 if ( refer_cnt > 0 ) {
666 /* successfully chased reference */
667 /* If haven't got end search, set chasing referrals */
668 if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
669 lr->lr_status = LDAP_REQST_CHASINGREFS;
670 Debug( LDAP_DEBUG_TRACE,
671 "read1msg: search ref chased, "
672 "mark request chasing refs, "
674 lr->lr_msgid, 0, 0 );
681 } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
682 /* All results that just return a status, i.e. don't return data
683 * go through the following code. This code also chases V2 referrals
684 * and checks if all referrals have been chased.
686 char *lr_res_error = NULL;
688 tmpber = *ber; /* struct copy */
689 if ( ber_scanf( &tmpber, "{eAA", &lderr,
690 &lr->lr_res_matched, &lr_res_error )
693 if ( lr_res_error != NULL ) {
694 if ( lr->lr_res_error != NULL ) {
695 (void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
696 LDAP_FREE( (char *)lr_res_error );
699 lr->lr_res_error = lr_res_error;
704 /* Do we need to check for referrals? */
705 if ( tag != LDAP_RES_BIND &&
706 ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
707 lr->lr_parent != NULL ))
712 /* Check if V3 referral */
713 if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
714 if ( ld->ld_version > LDAP_VERSION2 ) {
715 /* Get the referral list */
716 if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
717 rc = LDAP_DECODING_ERROR;
718 lr->lr_status = LDAP_REQST_COMPLETED;
719 Debug( LDAP_DEBUG_TRACE,
720 "read1msg: referral decode error, "
721 "mark request completed, ld %p msgid %d\n",
722 (void *)ld, lr->lr_msgid, 0 );
725 /* Chase the referral
726 * refs array is freed by ldap_chase_v3referrals
728 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
729 0, &lr->lr_res_error, &hadref );
730 lr->lr_status = LDAP_REQST_COMPLETED;
731 Debug( LDAP_DEBUG_TRACE,
732 "read1msg: referral %s chased, "
733 "mark request completed, ld %p msgid %d\n",
734 refer_cnt > 0 ? "" : "not",
735 (void *)ld, lr->lr_msgid);
736 if ( refer_cnt < 0 ) {
744 case LDAP_COMPARE_TRUE:
745 case LDAP_COMPARE_FALSE:
749 if ( lr->lr_res_error == NULL ) {
753 /* pedantic, should never happen */
754 if ( lr->lr_res_error[ 0 ] == '\0' ) {
755 LDAP_FREE( lr->lr_res_error );
756 lr->lr_res_error = NULL;
760 /* V2 referrals are in error string */
761 refer_cnt = ldap_chase_referrals( ld, lr,
762 &lr->lr_res_error, -1, &hadref );
763 lr->lr_status = LDAP_REQST_COMPLETED;
764 Debug( LDAP_DEBUG_TRACE,
765 "read1msg: V2 referral chased, "
766 "mark request completed, id = %d\n",
767 lr->lr_msgid, 0, 0 );
773 /* save errno, message, and matched string */
774 if ( !hadref || lr->lr_res_error == NULL ) {
776 lderr == LDAP_PARTIAL_RESULTS
777 ? LDAP_SUCCESS : lderr;
779 } else if ( ld->ld_errno != LDAP_SUCCESS ) {
780 lr->lr_res_errno = ld->ld_errno;
783 lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
787 /* in any case, don't leave any lr_res_error 'round */
788 if ( lr_res_error ) {
789 LDAP_FREE( lr_res_error );
792 Debug( LDAP_DEBUG_TRACE,
793 "read1msg: ld %p %d new referrals\n",
794 (void *)ld, refer_cnt, 0 );
796 if ( refer_cnt != 0 ) { /* chasing referrals */
799 if ( refer_cnt < 0 ) {
800 ldap_return_request( ld, lr, 0 );
801 return( -1 ); /* fatal error */
803 lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
804 if ( lr->lr_res_matched ) {
805 LDAP_FREE( lr->lr_res_matched );
806 lr->lr_res_matched = NULL;
810 LDAPRequest *lrf = lr;
811 if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
812 /* request without any referrals */
813 simple_request = ( hadref ? 0 : 1 );
816 /* request with referrals or child request */
821 lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
822 Debug( LDAP_DEBUG_TRACE,
823 "read1msg: mark request completed, ld %p msgid %d\n",
824 (void *)ld, lr->lr_msgid, 0);
825 while ( lr->lr_parent != NULL ) {
826 merge_error_info( ld, lr->lr_parent, lr );
829 if ( --lr->lr_outrefcnt > 0 ) {
830 break; /* not completely done yet */
833 /* ITS#6744: Original lr was refcounted when we retrieved it,
834 * must release it now that we're working with the parent
836 if ( lrf != &dummy_lr ) {
837 ldap_return_request( ld, lrf, 0 );
840 /* Check if all requests are finished, lr is now parent */
842 if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
843 for ( tmplr = lr->lr_child;
845 tmplr = tmplr->lr_refnext )
847 if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
851 /* This is the parent request if the request has referrals */
852 if ( lr->lr_outrefcnt <= 0 &&
853 lr->lr_parent == NULL &&
857 tag = lr->lr_res_msgtype;
858 Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
860 Debug( LDAP_DEBUG_TRACE,
861 "res_errno: %d, res_error: <%s>, "
862 "res_matched: <%s>\n",
864 lr->lr_res_error ? lr->lr_res_error : "",
865 lr->lr_res_matched ? lr->lr_res_matched : "" );
866 if ( !simple_request ) {
869 if ( build_result_ber( ld, &ber, lr )
872 rc = -1; /* fatal error */
876 if ( lr != &dummy_lr ) {
877 ldap_return_request( ld, lr, 1 );
883 * RFC 4511 unsolicited (id == 0) responses
884 * shouldn't necessarily end the connection
886 if ( lc != NULL && id != 0 ) {
894 if ( lr != &dummy_lr ) {
895 ldap_return_request( ld, lr, 0 );
904 /* try to handle unsolicited responses as appropriate */
905 if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
908 tag = ber_peek_tag( &tmpber, &len );
910 /* we have a res oid */
911 if ( tag == LDAP_TAG_EXOP_RES_OID ) {
912 static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
913 struct berval resoid = BER_BVNULL;
915 if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
916 ld->ld_errno = LDAP_DECODING_ERROR;
921 assert( !BER_BVISEMPTY( &resoid ) );
923 is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
925 tag = ber_peek_tag( &tmpber, &len );
928 #if 0 /* don't need right now */
929 /* we have res data */
930 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
931 struct berval resdata;
933 if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
934 ld->ld_errno = LDAP_DECODING_ERROR;
943 /* handle RFC 4511 "Notice of Disconnection" locally */
946 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
947 ld->ld_errno = LDAP_DECODING_ERROR;
952 /* get rid of the connection... */
957 /* need to return -1, because otherwise
958 * a valid result is expected */
959 ld->ld_errno = lderr;
964 /* make a new ldap message */
965 newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
966 if ( newmsg == NULL ) {
967 ld->ld_errno = LDAP_NO_MEMORY;
970 newmsg->lm_msgid = (int)id;
971 newmsg->lm_msgtype = tag;
972 newmsg->lm_ber = ber;
973 newmsg->lm_chain_tail = newmsg;
975 #ifdef LDAP_CONNECTIONLESS
976 /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
977 * the responses are all a sequence wrapped in one message. In
978 * LDAPv3 each response is in its own message. The datagram must
979 * end with a SearchResult. We can't just parse each response in
980 * separate calls to try_read1msg because the header info is only
981 * present at the beginning of the datagram, not at the beginning
982 * of each response. So parse all the responses at once and queue
983 * them up, then pull off the first response to return to the
984 * caller when all parsing is complete.
986 if ( LDAP_IS_UDP(ld) ) {
987 /* If not a result, look for more */
988 if ( tag != LDAP_RES_SEARCH_RESULT ) {
992 /* LDAPv2: dup the current ber, skip past the current
993 * response, and see if there are any more after it.
995 ber = ber_dup( ber );
996 ber_scanf( ber, "x" );
997 if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
998 /* There's more - dup the ber buffer so they can all be
999 * individually freed by ldap_msgfree.
1002 ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1003 bv.bv_val = LDAP_MALLOC( len );
1006 ber_read( ber, bv.bv_val, len );
1008 ber_init2( ber, &bv, ld->ld_lberoptions );
1012 /* LDAPv3: Just allocate a new ber. Since this is a buffered
1013 * datagram, if the sockbuf is readable we still have data
1016 ber = ldap_alloc_ber_with_options( ld );
1017 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1019 /* set up response chain */
1020 if ( tmp == NULL ) {
1021 newmsg->lm_next = ld->ld_responses;
1022 ld->ld_responses = newmsg;
1023 chain_head = newmsg;
1025 tmp->lm_chain = newmsg;
1027 chain_head->lm_chain_tail = newmsg;
1029 /* "ok" means there's more to parse */
1038 /* got to end of datagram without a SearchResult. Free
1039 * our dup'd ber, but leave any buffer alone. For v2 case,
1040 * the previous response is still using this buffer. For v3,
1041 * the new ber has no buffer to free yet.
1046 } else if ( moremsgs ) {
1047 /* got search result, and we had multiple responses in 1 datagram.
1048 * stick the result onto the end of the chain, and then pull the
1049 * first response off the head of the chain.
1051 tmp->lm_chain = newmsg;
1052 chain_head->lm_chain_tail = newmsg;
1053 *result = chkResponseList( ld, msgid, all );
1054 ld->ld_errno = LDAP_SUCCESS;
1055 return( (*result)->lm_msgtype );
1058 #endif /* LDAP_CONNECTIONLESS */
1060 /* is this the one we're looking for? */
1061 if ( msgid == LDAP_RES_ANY || id == msgid ) {
1062 if ( all == LDAP_MSG_ONE
1063 || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1064 && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1065 && newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE
1066 && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1069 ld->ld_errno = LDAP_SUCCESS;
1072 } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1073 foundit = 1; /* return the chain later */
1078 * if not, we must add it to the list of responses. if
1079 * the msgid is already there, it must be part of an existing
1084 for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1085 if ( l->lm_msgid == newmsg->lm_msgid ) {
1091 /* not part of an existing search response */
1098 newmsg->lm_next = ld->ld_responses;
1099 ld->ld_responses = newmsg;
1103 Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
1104 (void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1106 /* part of a search response - add to end of list of entries */
1107 l->lm_chain_tail->lm_chain = newmsg;
1108 l->lm_chain_tail = newmsg;
1110 /* return the whole chain if that's what we were looking for */
1112 if ( prev == NULL ) {
1113 ld->ld_responses = l->lm_next;
1115 prev->lm_next = l->lm_next;
1122 ld->ld_errno = LDAP_SUCCESS;
1125 if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1128 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
1133 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1141 ber = ldap_alloc_ber_with_options( ld );
1144 ld->ld_errno = LDAP_NO_MEMORY;
1148 if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1149 lr->lr_res_msgtype, lr->lr_res_errno,
1150 lr->lr_res_matched ? lr->lr_res_matched : "",
1151 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1153 ld->ld_errno = LDAP_ENCODING_ERROR;
1155 return( LBER_ERROR );
1158 ber_reset( ber, 1 );
1160 if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1161 ld->ld_errno = LDAP_DECODING_ERROR;
1163 return( LBER_ERROR );
1166 if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1167 ld->ld_errno = LDAP_DECODING_ERROR;
1169 return( LBER_ERROR );
1172 tag = ber_peek_tag( ber, &len );
1174 if ( tag == LBER_ERROR ) {
1175 ld->ld_errno = LDAP_DECODING_ERROR;
1177 return( LBER_ERROR );
1186 * Merge error information in "lr" with "parentr" error code and string.
1189 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1191 if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1192 parentr->lr_res_errno = lr->lr_res_errno;
1193 if ( lr->lr_res_error != NULL ) {
1194 (void)ldap_append_referral( ld, &parentr->lr_res_error,
1198 } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1199 parentr->lr_res_errno == LDAP_SUCCESS )
1201 parentr->lr_res_errno = lr->lr_res_errno;
1202 if ( parentr->lr_res_error != NULL ) {
1203 LDAP_FREE( parentr->lr_res_error );
1205 parentr->lr_res_error = lr->lr_res_error;
1206 lr->lr_res_error = NULL;
1207 if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1208 if ( parentr->lr_res_matched != NULL ) {
1209 LDAP_FREE( parentr->lr_res_matched );
1211 parentr->lr_res_matched = lr->lr_res_matched;
1212 lr->lr_res_matched = NULL;
1216 Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
1217 parentr->lr_msgid, 0, 0 );
1218 Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1219 parentr->lr_res_errno,
1220 parentr->lr_res_error ? parentr->lr_res_error : "",
1221 parentr->lr_res_matched ? parentr->lr_res_matched : "" );
1227 ldap_msgtype( LDAPMessage *lm )
1229 assert( lm != NULL );
1230 return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1235 ldap_msgid( LDAPMessage *lm )
1237 assert( lm != NULL );
1239 return ( lm != NULL ) ? lm->lm_msgid : -1;
1244 ldap_int_msgtype2str( ber_tag_t tag )
1247 case LDAP_RES_ADD: return "add";
1248 case LDAP_RES_BIND: return "bind";
1249 case LDAP_RES_COMPARE: return "compare";
1250 case LDAP_RES_DELETE: return "delete";
1251 case LDAP_RES_EXTENDED: return "extended-result";
1252 case LDAP_RES_INTERMEDIATE: return "intermediate";
1253 case LDAP_RES_MODIFY: return "modify";
1254 case LDAP_RES_RENAME: return "rename";
1255 case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1256 case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1257 case LDAP_RES_SEARCH_RESULT: return "search-result";
1263 ldap_msgfree( LDAPMessage *lm )
1268 Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1270 for ( ; lm != NULL; lm = next ) {
1271 next = lm->lm_chain;
1272 type = lm->lm_msgtype;
1273 ber_free( lm->lm_ber, 1 );
1274 LDAP_FREE( (char *) lm );
1281 * ldap_msgdelete - delete a message. It returns:
1282 * 0 if the entire message was deleted
1283 * -1 if the message was not found, or only part of it was found
1286 ldap_msgdelete( LDAP *ld, int msgid )
1288 LDAPMessage *lm, *prev;
1291 assert( ld != NULL );
1293 Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1294 (void *)ld, msgid, 0 );
1296 LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
1298 for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1299 if ( lm->lm_msgid == msgid ) {
1309 if ( prev == NULL ) {
1310 ld->ld_responses = lm->lm_next;
1312 prev->lm_next = lm->lm_next;
1315 LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
1317 switch ( ldap_msgfree( lm ) ) {
1318 case LDAP_RES_SEARCH_ENTRY:
1319 case LDAP_RES_SEARCH_REFERENCE:
1320 case LDAP_RES_INTERMEDIATE:
1336 * return the location of the message id in the array of abandoned
1337 * message ids, or -1
1340 ldap_abandoned( LDAP *ld, ber_int_t msgid )
1343 assert( msgid >= 0 );
1345 LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
1346 ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx );
1347 LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
1352 * ldap_mark_abandoned
1355 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid )
1359 assert( msgid >= 0 );
1360 LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
1361 ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx );
1362 if (ret <= 0) { /* error or already deleted by another thread */
1363 LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
1366 /* still in abandoned array, so delete */
1367 ret = ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,
1369 LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );