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);
829 while ( lr->lr_parent != NULL ) {
830 merge_error_info( ld, lr->lr_parent, lr );
833 if ( --lr->lr_outrefcnt > 0 ) {
834 break; /* not completely done yet */
838 /* Check if all requests are finished, lr is now parent */
840 if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
841 for ( tmplr = lr->lr_child;
843 tmplr = tmplr->lr_refnext )
845 if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
849 /* This is the parent request if the request has referrals */
850 if ( lr->lr_outrefcnt <= 0 &&
851 lr->lr_parent == NULL &&
855 tag = lr->lr_res_msgtype;
856 Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
858 Debug( LDAP_DEBUG_TRACE,
859 "res_errno: %d, res_error: <%s>, "
860 "res_matched: <%s>\n",
862 lr->lr_res_error ? lr->lr_res_error : "",
863 lr->lr_res_matched ? lr->lr_res_matched : "" );
864 if ( !simple_request ) {
867 if ( build_result_ber( ld, &ber, lr )
870 rc = -1; /* fatal error */
874 if ( lr != &dummy_lr ) {
875 ldap_return_request( ld, lr, 1 );
881 * RF 4511 unsolicited (id == 0) responses
882 * shouldn't necessarily end the connection
884 if ( lc != NULL && id != 0 ) {
892 if ( lr != &dummy_lr ) {
893 ldap_return_request( ld, lr, 0 );
902 /* try to handle unsolicited responses as appropriate */
903 if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
906 tag = ber_peek_tag( &tmpber, &len );
908 /* we have a res oid */
909 if ( tag == LDAP_TAG_EXOP_RES_OID ) {
910 static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
911 struct berval resoid = BER_BVNULL;
913 if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
914 ld->ld_errno = LDAP_DECODING_ERROR;
919 assert( !BER_BVISEMPTY( &resoid ) );
921 is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
923 tag = ber_peek_tag( &tmpber, &len );
926 #if 0 /* don't need right now */
927 /* we have res data */
928 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
929 struct berval resdata;
931 if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
932 ld->ld_errno = LDAP_DECODING_ERROR;
941 /* handle RFC 4511 "Notice of Disconnection" locally */
944 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
945 ld->ld_errno = LDAP_DECODING_ERROR;
950 /* get rid of the connection... */
955 /* need to return -1, because otherwise
956 * a valid result is expected */
957 ld->ld_errno = lderr;
962 /* make a new ldap message */
963 newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
964 if ( newmsg == NULL ) {
965 ld->ld_errno = LDAP_NO_MEMORY;
968 newmsg->lm_msgid = (int)id;
969 newmsg->lm_msgtype = tag;
970 newmsg->lm_ber = ber;
971 newmsg->lm_chain_tail = newmsg;
973 #ifdef LDAP_CONNECTIONLESS
974 /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
975 * the responses are all a sequence wrapped in one message. In
976 * LDAPv3 each response is in its own message. The datagram must
977 * end with a SearchResult. We can't just parse each response in
978 * separate calls to try_read1msg because the header info is only
979 * present at the beginning of the datagram, not at the beginning
980 * of each response. So parse all the responses at once and queue
981 * them up, then pull off the first response to return to the
982 * caller when all parsing is complete.
984 if ( LDAP_IS_UDP(ld) ) {
985 /* If not a result, look for more */
986 if ( tag != LDAP_RES_SEARCH_RESULT ) {
990 /* LDAPv2: dup the current ber, skip past the current
991 * response, and see if there are any more after it.
993 ber = ber_dup( ber );
994 ber_scanf( ber, "x" );
995 if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
996 /* There's more - dup the ber buffer so they can all be
997 * individually freed by ldap_msgfree.
1000 ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1001 bv.bv_val = LDAP_MALLOC( len );
1004 ber_read( ber, bv.bv_val, len );
1006 ber_init2( ber, &bv, ld->ld_lberoptions );
1010 /* LDAPv3: Just allocate a new ber. Since this is a buffered
1011 * datagram, if the sockbuf is readable we still have data
1014 ber = ldap_alloc_ber_with_options( ld );
1015 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1017 /* set up response chain */
1018 if ( tmp == NULL ) {
1019 newmsg->lm_next = ld->ld_responses;
1020 ld->ld_responses = newmsg;
1021 chain_head = newmsg;
1023 tmp->lm_chain = newmsg;
1025 chain_head->lm_chain_tail = newmsg;
1027 /* "ok" means there's more to parse */
1036 /* got to end of datagram without a SearchResult. Free
1037 * our dup'd ber, but leave any buffer alone. For v2 case,
1038 * the previous response is still using this buffer. For v3,
1039 * the new ber has no buffer to free yet.
1044 } else if ( moremsgs ) {
1045 /* got search result, and we had multiple responses in 1 datagram.
1046 * stick the result onto the end of the chain, and then pull the
1047 * first response off the head of the chain.
1049 tmp->lm_chain = newmsg;
1050 chain_head->lm_chain_tail = newmsg;
1051 *result = chkResponseList( ld, msgid, all );
1052 ld->ld_errno = LDAP_SUCCESS;
1053 return( (*result)->lm_msgtype );
1056 #endif /* LDAP_CONNECTIONLESS */
1058 /* is this the one we're looking for? */
1059 if ( msgid == LDAP_RES_ANY || id == msgid ) {
1060 if ( all == LDAP_MSG_ONE
1061 || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1062 && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1063 && newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE
1064 && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1067 ld->ld_errno = LDAP_SUCCESS;
1070 } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1071 foundit = 1; /* return the chain later */
1076 * if not, we must add it to the list of responses. if
1077 * the msgid is already there, it must be part of an existing
1082 for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1083 if ( l->lm_msgid == newmsg->lm_msgid ) {
1089 /* not part of an existing search response */
1096 newmsg->lm_next = ld->ld_responses;
1097 ld->ld_responses = newmsg;
1101 Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
1102 (void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1104 /* part of a search response - add to end of list of entries */
1105 l->lm_chain_tail->lm_chain = newmsg;
1106 l->lm_chain_tail = newmsg;
1108 /* return the whole chain if that's what we were looking for */
1110 if ( prev == NULL ) {
1111 ld->ld_responses = l->lm_next;
1113 prev->lm_next = l->lm_next;
1120 ld->ld_errno = LDAP_SUCCESS;
1123 if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1126 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
1131 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1139 ber = ldap_alloc_ber_with_options( ld );
1142 ld->ld_errno = LDAP_NO_MEMORY;
1146 if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1147 lr->lr_res_msgtype, lr->lr_res_errno,
1148 lr->lr_res_matched ? lr->lr_res_matched : "",
1149 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1151 ld->ld_errno = LDAP_ENCODING_ERROR;
1153 return( LBER_ERROR );
1156 ber_reset( ber, 1 );
1158 if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1159 ld->ld_errno = LDAP_DECODING_ERROR;
1161 return( LBER_ERROR );
1164 if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1165 ld->ld_errno = LDAP_DECODING_ERROR;
1167 return( LBER_ERROR );
1170 tag = ber_peek_tag( ber, &len );
1172 if ( tag == LBER_ERROR ) {
1173 ld->ld_errno = LDAP_DECODING_ERROR;
1175 return( LBER_ERROR );
1184 * Merge error information in "lr" with "parentr" error code and string.
1187 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1189 if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1190 parentr->lr_res_errno = lr->lr_res_errno;
1191 if ( lr->lr_res_error != NULL ) {
1192 (void)ldap_append_referral( ld, &parentr->lr_res_error,
1196 } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1197 parentr->lr_res_errno == LDAP_SUCCESS )
1199 parentr->lr_res_errno = lr->lr_res_errno;
1200 if ( parentr->lr_res_error != NULL ) {
1201 LDAP_FREE( parentr->lr_res_error );
1203 parentr->lr_res_error = lr->lr_res_error;
1204 lr->lr_res_error = NULL;
1205 if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1206 if ( parentr->lr_res_matched != NULL ) {
1207 LDAP_FREE( parentr->lr_res_matched );
1209 parentr->lr_res_matched = lr->lr_res_matched;
1210 lr->lr_res_matched = NULL;
1214 Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
1215 parentr->lr_msgid, 0, 0 );
1216 Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1217 parentr->lr_res_errno,
1218 parentr->lr_res_error ? parentr->lr_res_error : "",
1219 parentr->lr_res_matched ? parentr->lr_res_matched : "" );
1225 ldap_msgtype( LDAPMessage *lm )
1227 assert( lm != NULL );
1228 return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1233 ldap_msgid( LDAPMessage *lm )
1235 assert( lm != NULL );
1237 return ( lm != NULL ) ? lm->lm_msgid : -1;
1242 ldap_int_msgtype2str( ber_tag_t tag )
1245 case LDAP_RES_ADD: return "add";
1246 case LDAP_RES_BIND: return "bind";
1247 case LDAP_RES_COMPARE: return "compare";
1248 case LDAP_RES_DELETE: return "delete";
1249 case LDAP_RES_EXTENDED: return "extended-result";
1250 case LDAP_RES_INTERMEDIATE: return "intermediate";
1251 case LDAP_RES_MODIFY: return "modify";
1252 case LDAP_RES_RENAME: return "rename";
1253 case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1254 case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1255 case LDAP_RES_SEARCH_RESULT: return "search-result";
1261 ldap_msgfree( LDAPMessage *lm )
1266 Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1268 for ( ; lm != NULL; lm = next ) {
1269 next = lm->lm_chain;
1270 type = lm->lm_msgtype;
1271 ber_free( lm->lm_ber, 1 );
1272 LDAP_FREE( (char *) lm );
1279 * ldap_msgdelete - delete a message. It returns:
1280 * 0 if the entire message was deleted
1281 * -1 if the message was not found, or only part of it was found
1284 ldap_msgdelete( LDAP *ld, int msgid )
1286 LDAPMessage *lm, *prev;
1289 assert( ld != NULL );
1291 Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1292 (void *)ld, msgid, 0 );
1294 LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
1296 for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1297 if ( lm->lm_msgid == msgid ) {
1307 if ( prev == NULL ) {
1308 ld->ld_responses = lm->lm_next;
1310 prev->lm_next = lm->lm_next;
1313 LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
1315 switch ( ldap_msgfree( lm ) ) {
1316 case LDAP_RES_SEARCH_ENTRY:
1317 case LDAP_RES_SEARCH_REFERENCE:
1318 case LDAP_RES_INTERMEDIATE:
1334 * return the location of the message id in the array of abandoned
1335 * message ids, or -1
1337 * expects ld_res_mutex to be locked
1340 ldap_abandoned( LDAP *ld, ber_int_t msgid, int *idxp )
1342 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1344 assert( idxp != NULL );
1345 assert( msgid >= 0 );
1347 return ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp );
1351 * ldap_mark_abandoned
1353 * expects ld_res_mutex to be locked
1356 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid, int idx )
1358 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1360 /* NOTE: those assertions are repeated in ldap_int_bisect_delete() */
1362 assert( (unsigned) idx < ld->ld_nabandoned );
1363 assert( ld->ld_abandoned[ idx ] == msgid );
1365 return ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,