1 /* result.c - wait for an ldap result */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 1998-2013 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 ) )
305 lc_ready = 2; /* ready at ber level, not socket level */
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 */
342 rc = LDAP_MSG_X_KEEP_LOOKING;
343 LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
344 if ( ld->ld_requests &&
345 ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
346 ldap_is_write_ready( ld,
347 ld->ld_requests->lr_conn->lconn_sb ) )
350 ldap_int_flush_request( ld, ld->ld_requests );
352 for ( lc = ld->ld_conns;
353 rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL;
356 if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
357 ldap_is_read_ready( ld, lc->lconn_sb ) )
360 /* Don't let it get freed out from under us */
362 rc = try_read1msg( ld, msgid, all, lc, result );
363 lnext = lc->lconn_next;
365 /* Only take locks if we're really freeing */
366 if ( lc->lconn_refcnt <= 1 ) {
367 ldap_free_connection( ld, lc, 0, 1 );
372 lnext = lc->lconn_next;
375 LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
376 /* Quit looping if no one handled any socket events */
377 if (!serviced && lc_ready == 1)
380 LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
383 if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
384 struct timeval curr_time_tv = { 0 },
385 delta_time_tv = { 0 };
387 #ifdef HAVE_GETTIMEOFDAY
388 gettimeofday( &curr_time_tv, NULL );
389 #else /* ! HAVE_GETTIMEOFDAY */
390 time( &curr_time_tv.tv_sec );
391 curr_time_tv.tv_usec = 0;
392 #endif /* ! HAVE_GETTIMEOFDAY */
394 /* delta_time = tmp_time - start_time */
395 delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
396 delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
397 if ( delta_time_tv.tv_usec < 0 ) {
398 delta_time_tv.tv_sec--;
399 delta_time_tv.tv_usec += 1000000;
402 /* tv0 < delta_time ? */
403 if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
404 ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
406 rc = 0; /* timed out */
407 ld->ld_errno = LDAP_TIMEOUT;
411 /* tv0 -= delta_time */
412 tv0.tv_sec -= delta_time_tv.tv_sec;
413 tv0.tv_usec -= delta_time_tv.tv_usec;
414 if ( tv0.tv_usec < 0 ) {
416 tv0.tv_usec += 1000000;
419 tv.tv_sec = tv0.tv_sec;
420 tv.tv_usec = tv0.tv_usec;
422 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n",
423 (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
425 start_time_tv.tv_sec = curr_time_tv.tv_sec;
426 start_time_tv.tv_usec = curr_time_tv.tv_usec;
434 /* protected by res_mutex, conn_mutex and req_mutex */
441 LDAPMessage **result )
444 LDAPMessage *newmsg, *l, *prev;
449 LDAPRequest *lr, *tmplr, dummy_lr = { 0 };
451 int rc, refer_cnt, hadref, simple_request, err;
454 #ifdef LDAP_CONNECTIONLESS
455 LDAPMessage *tmp = NULL, *chain_head = NULL;
456 int moremsgs = 0, isv2 = 0;
459 assert( ld != NULL );
460 assert( lc != NULL );
462 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
463 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
464 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
466 Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
467 (void *)ld, msgid, all );
470 if ( lc->lconn_ber == NULL ) {
471 lc->lconn_ber = ldap_alloc_ber_with_options( ld );
473 if ( lc->lconn_ber == NULL ) {
479 assert( LBER_VALID (ber) );
481 /* get the next message */
483 #ifdef LDAP_CONNECTIONLESS
484 if ( LDAP_IS_UDP(ld) ) {
485 struct sockaddr_storage from;
486 ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr_storage) );
487 if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
491 tag = ber_get_next( lc->lconn_sb, &len, ber );
493 case LDAP_TAG_MESSAGE:
495 * We read a complete message.
496 * The connection should no longer need this ber.
498 lc->lconn_ber = NULL;
504 Debug( LDAP_DEBUG_CONNS,
505 "ber_get_next failed.\n", 0, 0, 0 );
507 if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
508 if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
509 ld->ld_errno = LDAP_SERVER_DOWN;
511 lc->lconn_status = 0;
515 ld->ld_errno = LDAP_LOCAL_ERROR;
520 if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
522 ld->ld_errno = LDAP_DECODING_ERROR;
526 /* id == 0 iff unsolicited notification message (RFC 4511) */
528 /* id < 0 is invalid, just toss it. FIXME: should we disconnect? */
533 /* if it's been abandoned, toss it */
535 if ( ldap_abandoned( ld, id ) ) {
536 /* the message type */
537 tag = ber_peek_tag( ber, &len );
539 case LDAP_RES_SEARCH_ENTRY:
540 case LDAP_RES_SEARCH_REFERENCE:
541 case LDAP_RES_INTERMEDIATE:
546 /* there's no need to keep the id
547 * in the abandoned list any longer */
548 ldap_mark_abandoned( ld, id );
552 Debug( LDAP_DEBUG_ANY,
553 "abandoned/discarded ld %p msgid %d message type %s\n",
554 (void *)ld, id, ldap_int_msgtype2str( tag ) );
558 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
561 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
564 lr = ldap_find_request_by_msgid( ld, id );
566 const char *msg = "unknown";
568 /* the message type */
569 tag = ber_peek_tag( ber, &len );
575 msg = ldap_int_msgtype2str( tag );
579 Debug( LDAP_DEBUG_ANY,
580 "no request for response on ld %p msgid %d message type %s (tossing)\n",
581 (void *)ld, id, msg );
586 #ifdef LDAP_CONNECTIONLESS
587 if ( LDAP_IS_UDP(ld) && isv2 ) {
588 ber_scanf(ber, "x{");
595 /* the message type */
596 tag = ber_peek_tag( ber, &len );
597 if ( tag == LBER_ERROR ) {
598 ld->ld_errno = LDAP_DECODING_ERROR;
603 Debug( LDAP_DEBUG_TRACE,
604 "read1msg: ld %p msgid %d message type %s\n",
605 (void *)ld, id, ldap_int_msgtype2str( tag ) );
608 /* unsolicited notification message (RFC 4511) */
609 if ( tag != LDAP_RES_EXTENDED ) {
613 /* strictly speaking, it's an error; from RFC 4511:
615 4.4. Unsolicited Notification
617 An unsolicited notification is an LDAPMessage sent from the server to
618 the client that is not in response to any LDAPMessage received by the
619 server. It is used to signal an extraordinary condition in the
620 server or in the LDAP session between the client and the server. The
621 notification is of an advisory nature, and the server will not expect
622 any response to be returned from the client.
624 The unsolicited notification is structured as an LDAPMessage in which
625 the messageID is zero and protocolOp is set to the extendedResp
626 choice using the ExtendedResponse type (See Section 4.12). The
627 responseName field of the ExtendedResponse always contains an LDAPOID
628 that is unique for this notification.
630 * however, since unsolicited responses
631 * are of advisory nature, better
636 ld->ld_errno = LDAP_DECODING_ERROR;
647 hadref = simple_request = 0;
648 rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */
649 lr->lr_res_msgtype = tag;
652 * Check for V3 search reference
654 if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
655 if ( ld->ld_version > LDAP_VERSION2 ) {
656 /* This is a V3 search reference */
657 if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
658 lr->lr_parent != NULL )
663 /* Get the referral list */
664 if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
665 rc = LDAP_DECODING_ERROR;
668 /* Note: refs array is freed by ldap_chase_v3referrals */
669 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
670 1, &lr->lr_res_error, &hadref );
671 if ( refer_cnt > 0 ) {
672 /* successfully chased reference */
673 /* If haven't got end search, set chasing referrals */
674 if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
675 lr->lr_status = LDAP_REQST_CHASINGREFS;
676 Debug( LDAP_DEBUG_TRACE,
677 "read1msg: search ref chased, "
678 "mark request chasing refs, "
680 lr->lr_msgid, 0, 0 );
687 } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
688 /* All results that just return a status, i.e. don't return data
689 * go through the following code. This code also chases V2 referrals
690 * and checks if all referrals have been chased.
692 char *lr_res_error = NULL;
694 tmpber = *ber; /* struct copy */
695 if ( ber_scanf( &tmpber, "{eAA", &lderr,
696 &lr->lr_res_matched, &lr_res_error )
699 if ( lr_res_error != NULL ) {
700 if ( lr->lr_res_error != NULL ) {
701 (void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
702 LDAP_FREE( (char *)lr_res_error );
705 lr->lr_res_error = lr_res_error;
710 /* Do we need to check for referrals? */
711 if ( tag != LDAP_RES_BIND &&
712 ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
713 lr->lr_parent != NULL ))
718 /* Check if V3 referral */
719 if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
720 if ( ld->ld_version > LDAP_VERSION2 ) {
721 /* Get the referral list */
722 if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
723 rc = LDAP_DECODING_ERROR;
724 lr->lr_status = LDAP_REQST_COMPLETED;
725 Debug( LDAP_DEBUG_TRACE,
726 "read1msg: referral decode error, "
727 "mark request completed, ld %p msgid %d\n",
728 (void *)ld, lr->lr_msgid, 0 );
731 /* Chase the referral
732 * refs array is freed by ldap_chase_v3referrals
734 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
735 0, &lr->lr_res_error, &hadref );
736 lr->lr_status = LDAP_REQST_COMPLETED;
737 Debug( LDAP_DEBUG_TRACE,
738 "read1msg: referral %s chased, "
739 "mark request completed, ld %p msgid %d\n",
740 refer_cnt > 0 ? "" : "not",
741 (void *)ld, lr->lr_msgid);
742 if ( refer_cnt < 0 ) {
750 case LDAP_COMPARE_TRUE:
751 case LDAP_COMPARE_FALSE:
755 if ( lr->lr_res_error == NULL ) {
759 /* pedantic, should never happen */
760 if ( lr->lr_res_error[ 0 ] == '\0' ) {
761 LDAP_FREE( lr->lr_res_error );
762 lr->lr_res_error = NULL;
766 /* V2 referrals are in error string */
767 refer_cnt = ldap_chase_referrals( ld, lr,
768 &lr->lr_res_error, -1, &hadref );
769 lr->lr_status = LDAP_REQST_COMPLETED;
770 Debug( LDAP_DEBUG_TRACE,
771 "read1msg: V2 referral chased, "
772 "mark request completed, id = %d\n",
773 lr->lr_msgid, 0, 0 );
779 /* save errno, message, and matched string */
780 if ( !hadref || lr->lr_res_error == NULL ) {
782 lderr == LDAP_PARTIAL_RESULTS
783 ? LDAP_SUCCESS : lderr;
785 } else if ( ld->ld_errno != LDAP_SUCCESS ) {
786 lr->lr_res_errno = ld->ld_errno;
789 lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
793 /* in any case, don't leave any lr_res_error 'round */
794 if ( lr_res_error ) {
795 LDAP_FREE( lr_res_error );
798 Debug( LDAP_DEBUG_TRACE,
799 "read1msg: ld %p %d new referrals\n",
800 (void *)ld, refer_cnt, 0 );
802 if ( refer_cnt != 0 ) { /* chasing referrals */
805 if ( refer_cnt < 0 ) {
806 ldap_return_request( ld, lr, 0 );
807 return( -1 ); /* fatal error */
809 lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
810 if ( lr->lr_res_matched ) {
811 LDAP_FREE( lr->lr_res_matched );
812 lr->lr_res_matched = NULL;
816 if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
817 /* request without any referrals */
818 simple_request = ( hadref ? 0 : 1 );
821 /* request with referrals or child request */
826 lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
827 Debug( LDAP_DEBUG_TRACE,
828 "read1msg: mark request completed, ld %p msgid %d\n",
829 (void *)ld, lr->lr_msgid, 0);
831 while ( lr->lr_parent != NULL ) {
832 merge_error_info( ld, lr->lr_parent, lr );
835 if ( --lr->lr_outrefcnt > 0 ) {
836 break; /* not completely done yet */
839 /* ITS#6744: Original lr was refcounted when we retrieved it,
840 * must release it now that we're working with the parent
842 if ( tmplr->lr_parent ) {
843 ldap_return_request( ld, tmplr, 0 );
846 /* Check if all requests are finished, lr is now parent */
848 if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
849 for ( tmplr = lr->lr_child;
851 tmplr = tmplr->lr_refnext )
853 if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
857 /* This is the parent request if the request has referrals */
858 if ( lr->lr_outrefcnt <= 0 &&
859 lr->lr_parent == NULL &&
863 tag = lr->lr_res_msgtype;
864 Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
866 Debug( LDAP_DEBUG_TRACE,
867 "res_errno: %d, res_error: <%s>, "
868 "res_matched: <%s>\n",
870 lr->lr_res_error ? lr->lr_res_error : "",
871 lr->lr_res_matched ? lr->lr_res_matched : "" );
872 if ( !simple_request ) {
875 if ( build_result_ber( ld, &ber, lr )
878 rc = -1; /* fatal error */
882 if ( lr != &dummy_lr ) {
883 ldap_return_request( ld, lr, 1 );
889 * RFC 4511 unsolicited (id == 0) responses
890 * shouldn't necessarily end the connection
892 if ( lc != NULL && id != 0 ) {
900 if ( lr != &dummy_lr ) {
901 ldap_return_request( ld, lr, 0 );
910 /* try to handle unsolicited responses as appropriate */
911 if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
914 tag = ber_peek_tag( &tmpber, &len );
916 /* we have a res oid */
917 if ( tag == LDAP_TAG_EXOP_RES_OID ) {
918 static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
919 struct berval resoid = BER_BVNULL;
921 if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
922 ld->ld_errno = LDAP_DECODING_ERROR;
927 assert( !BER_BVISEMPTY( &resoid ) );
929 is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
931 tag = ber_peek_tag( &tmpber, &len );
934 #if 0 /* don't need right now */
935 /* we have res data */
936 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
937 struct berval resdata;
939 if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
940 ld->ld_errno = LDAP_DECODING_ERROR;
949 /* handle RFC 4511 "Notice of Disconnection" locally */
952 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
953 ld->ld_errno = LDAP_DECODING_ERROR;
958 /* get rid of the connection... */
963 /* need to return -1, because otherwise
964 * a valid result is expected */
965 ld->ld_errno = lderr;
970 /* make a new ldap message */
971 newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
972 if ( newmsg == NULL ) {
973 ld->ld_errno = LDAP_NO_MEMORY;
976 newmsg->lm_msgid = (int)id;
977 newmsg->lm_msgtype = tag;
978 newmsg->lm_ber = ber;
979 newmsg->lm_chain_tail = newmsg;
981 #ifdef LDAP_CONNECTIONLESS
982 /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
983 * the responses are all a sequence wrapped in one message. In
984 * LDAPv3 each response is in its own message. The datagram must
985 * end with a SearchResult. We can't just parse each response in
986 * separate calls to try_read1msg because the header info is only
987 * present at the beginning of the datagram, not at the beginning
988 * of each response. So parse all the responses at once and queue
989 * them up, then pull off the first response to return to the
990 * caller when all parsing is complete.
992 if ( LDAP_IS_UDP(ld) ) {
993 /* If not a result, look for more */
994 if ( tag != LDAP_RES_SEARCH_RESULT ) {
998 /* LDAPv2: dup the current ber, skip past the current
999 * response, and see if there are any more after it.
1001 ber = ber_dup( ber );
1002 ber_scanf( ber, "x" );
1003 if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
1004 /* There's more - dup the ber buffer so they can all be
1005 * individually freed by ldap_msgfree.
1008 ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1009 bv.bv_val = LDAP_MALLOC( len );
1012 ber_read( ber, bv.bv_val, len );
1014 ber_init2( ber, &bv, ld->ld_lberoptions );
1018 /* LDAPv3: Just allocate a new ber. Since this is a buffered
1019 * datagram, if the sockbuf is readable we still have data
1022 ber = ldap_alloc_ber_with_options( ld );
1023 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1025 /* set up response chain */
1026 if ( tmp == NULL ) {
1027 newmsg->lm_next = ld->ld_responses;
1028 ld->ld_responses = newmsg;
1029 chain_head = newmsg;
1031 tmp->lm_chain = newmsg;
1033 chain_head->lm_chain_tail = newmsg;
1035 /* "ok" means there's more to parse */
1044 /* got to end of datagram without a SearchResult. Free
1045 * our dup'd ber, but leave any buffer alone. For v2 case,
1046 * the previous response is still using this buffer. For v3,
1047 * the new ber has no buffer to free yet.
1052 } else if ( moremsgs ) {
1053 /* got search result, and we had multiple responses in 1 datagram.
1054 * stick the result onto the end of the chain, and then pull the
1055 * first response off the head of the chain.
1057 tmp->lm_chain = newmsg;
1058 chain_head->lm_chain_tail = newmsg;
1059 *result = chkResponseList( ld, msgid, all );
1060 ld->ld_errno = LDAP_SUCCESS;
1061 return( (*result)->lm_msgtype );
1064 #endif /* LDAP_CONNECTIONLESS */
1066 /* is this the one we're looking for? */
1067 if ( msgid == LDAP_RES_ANY || id == msgid ) {
1068 if ( all == LDAP_MSG_ONE
1069 || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1070 && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1071 && newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE
1072 && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1075 ld->ld_errno = LDAP_SUCCESS;
1078 } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1079 foundit = 1; /* return the chain later */
1084 * if not, we must add it to the list of responses. if
1085 * the msgid is already there, it must be part of an existing
1090 for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1091 if ( l->lm_msgid == newmsg->lm_msgid ) {
1097 /* not part of an existing search response */
1104 newmsg->lm_next = ld->ld_responses;
1105 ld->ld_responses = newmsg;
1109 Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
1110 (void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1112 /* part of a search response - add to end of list of entries */
1113 l->lm_chain_tail->lm_chain = newmsg;
1114 l->lm_chain_tail = newmsg;
1116 /* return the whole chain if that's what we were looking for */
1118 if ( prev == NULL ) {
1119 ld->ld_responses = l->lm_next;
1121 prev->lm_next = l->lm_next;
1128 ld->ld_errno = LDAP_SUCCESS;
1131 if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1134 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
1139 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1147 ber = ldap_alloc_ber_with_options( ld );
1150 ld->ld_errno = LDAP_NO_MEMORY;
1154 if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1155 lr->lr_res_msgtype, lr->lr_res_errno,
1156 lr->lr_res_matched ? lr->lr_res_matched : "",
1157 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1159 ld->ld_errno = LDAP_ENCODING_ERROR;
1161 return( LBER_ERROR );
1164 ber_reset( ber, 1 );
1166 if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1167 ld->ld_errno = LDAP_DECODING_ERROR;
1169 return( LBER_ERROR );
1172 if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1173 ld->ld_errno = LDAP_DECODING_ERROR;
1175 return( LBER_ERROR );
1178 tag = ber_peek_tag( ber, &len );
1180 if ( tag == LBER_ERROR ) {
1181 ld->ld_errno = LDAP_DECODING_ERROR;
1183 return( LBER_ERROR );
1192 * Merge error information in "lr" with "parentr" error code and string.
1195 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1197 if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1198 parentr->lr_res_errno = lr->lr_res_errno;
1199 if ( lr->lr_res_error != NULL ) {
1200 (void)ldap_append_referral( ld, &parentr->lr_res_error,
1204 } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1205 parentr->lr_res_errno == LDAP_SUCCESS )
1207 parentr->lr_res_errno = lr->lr_res_errno;
1208 if ( parentr->lr_res_error != NULL ) {
1209 LDAP_FREE( parentr->lr_res_error );
1211 parentr->lr_res_error = lr->lr_res_error;
1212 lr->lr_res_error = NULL;
1213 if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1214 if ( parentr->lr_res_matched != NULL ) {
1215 LDAP_FREE( parentr->lr_res_matched );
1217 parentr->lr_res_matched = lr->lr_res_matched;
1218 lr->lr_res_matched = NULL;
1222 Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
1223 parentr->lr_msgid, 0, 0 );
1224 Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1225 parentr->lr_res_errno,
1226 parentr->lr_res_error ? parentr->lr_res_error : "",
1227 parentr->lr_res_matched ? parentr->lr_res_matched : "" );
1233 ldap_msgtype( LDAPMessage *lm )
1235 assert( lm != NULL );
1236 return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1241 ldap_msgid( LDAPMessage *lm )
1243 assert( lm != NULL );
1245 return ( lm != NULL ) ? lm->lm_msgid : -1;
1250 ldap_int_msgtype2str( ber_tag_t tag )
1253 case LDAP_RES_ADD: return "add";
1254 case LDAP_RES_BIND: return "bind";
1255 case LDAP_RES_COMPARE: return "compare";
1256 case LDAP_RES_DELETE: return "delete";
1257 case LDAP_RES_EXTENDED: return "extended-result";
1258 case LDAP_RES_INTERMEDIATE: return "intermediate";
1259 case LDAP_RES_MODIFY: return "modify";
1260 case LDAP_RES_RENAME: return "rename";
1261 case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1262 case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1263 case LDAP_RES_SEARCH_RESULT: return "search-result";
1269 ldap_msgfree( LDAPMessage *lm )
1274 Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1276 for ( ; lm != NULL; lm = next ) {
1277 next = lm->lm_chain;
1278 type = lm->lm_msgtype;
1279 ber_free( lm->lm_ber, 1 );
1280 LDAP_FREE( (char *) lm );
1287 * ldap_msgdelete - delete a message. It returns:
1288 * 0 if the entire message was deleted
1289 * -1 if the message was not found, or only part of it was found
1292 ldap_msgdelete( LDAP *ld, int msgid )
1294 LDAPMessage *lm, *prev;
1297 assert( ld != NULL );
1299 Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1300 (void *)ld, msgid, 0 );
1302 LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
1304 for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1305 if ( lm->lm_msgid == msgid ) {
1315 if ( prev == NULL ) {
1316 ld->ld_responses = lm->lm_next;
1318 prev->lm_next = lm->lm_next;
1321 LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
1323 switch ( ldap_msgfree( lm ) ) {
1324 case LDAP_RES_SEARCH_ENTRY:
1325 case LDAP_RES_SEARCH_REFERENCE:
1326 case LDAP_RES_INTERMEDIATE:
1342 * return the location of the message id in the array of abandoned
1343 * message ids, or -1
1346 ldap_abandoned( LDAP *ld, ber_int_t msgid )
1349 assert( msgid >= 0 );
1351 LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
1352 ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx );
1353 LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
1358 * ldap_mark_abandoned
1361 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid )
1365 assert( msgid >= 0 );
1366 LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
1367 ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx );
1368 if (ret <= 0) { /* error or already deleted by another thread */
1369 LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
1372 /* still in abandoned array, so delete */
1373 ret = ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,
1375 LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );