]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
ITS#10229 Adjust ldap_result behaviour with LDAP_MSG_RECEIVED
authorOndřej Kuzník <ondra@mistotebe.net>
Thu, 26 Sep 2024 11:27:05 +0000 (12:27 +0100)
committerQuanah Gibson-Mount <quanah@openldap.org>
Mon, 16 Dec 2024 16:18:53 +0000 (16:18 +0000)
libraries/libldap/error.c
libraries/libldap/result.c

index 297553d1cc52db01032dfc4e0d88b7f5a46cff9b..bb3868872109022ccafa5d8de8f0069af4246471 100644 (file)
@@ -261,6 +261,25 @@ ldap_parse_result(
        LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
        /* Find the result, last msg in chain... */
        lm = r->lm_chain_tail;
+       if ( r->lm_msgid != lm->lm_msgid ) {
+               /*
+                * ITS#10229: Returned with LDAP_MSG_ALL+LDAP_MSG_RECEIVED. People who
+                * do that aren't expected to call ldap_parse_result not least because
+                * they have no idea what the msgid of the result would be. Just do our
+                * best.
+                *
+                * We could also return LDAP_NO_RESULTS_RETURNED if there isn't a
+                * result for r's operation.
+                */
+               lm = r;
+               for ( lm = r; lm; lm = lm->lm_chain ) {
+                       if ( lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY &&
+                                       lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE &&
+                                       lm->lm_msgtype != LDAP_RES_INTERMEDIATE )
+                               break;
+               }
+       }
+
        /* FIXME: either this is not possible (assert?)
         * or it should be handled */
        if ( lm != NULL ) {
index e9ac9f32b36d249b181368f2ab82c39464b32860..148f81cd8c5660150c8cf0daf5f5b4298a52e2f3 100644 (file)
@@ -146,8 +146,32 @@ chkResponseList(
                "ldap_chkResponseList ld %p msgid %d all %d\n",
                (void *)ld, msgid, all );
 
+       lm = ld->ld_responses;
+       if ( lm && msgid == LDAP_RES_ANY && all == LDAP_MSG_RECEIVED ) {
+               /*
+                * ITS#10229: asked to return all messages received so far,
+                * draft-ietf-ldapext-ldap-c-api which defines LDAP_MSG_RECEIVED lets
+                * us mix different msgids in what we return
+                *
+                * We have two choices in *how* we return the messages:
+                * - we link all chains together
+                * - we keep the chains intact and use lm_next
+                *
+                * The former will make life harder for ldap_parse_result finding a
+                * result message, the latter affects routines that iterate over
+                * messages. This take does the former.
+                */
+               ld->ld_responses = NULL;
+               while ( lm->lm_next ) {
+                       lm->lm_chain_tail->lm_chain = lm->lm_next;
+                       lm->lm_chain_tail = lm->lm_next->lm_chain_tail;
+                       lm->lm_next = lm->lm_next->lm_next;
+               }
+               return lm;
+       }
+
        lastlm = &ld->ld_responses;
-       for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
+       for ( ; lm != NULL; lm = nextlm ) {
                nextlm = lm->lm_next;
                ++cnt;
 
@@ -393,6 +417,37 @@ wait4msg(
                        LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
                }
 
+               if ( all == LDAP_MSG_RECEIVED ) {
+                       /*
+                        * ITS#10229: we looped over all ready connections accumulating
+                        * messages in ld_responses, check if we have something to return
+                        * right now.
+                        */
+                       LDAPMessage **lp, *lm = ld->ld_responses;
+
+                       if ( lm && msgid == LDAP_RES_ANY ) {
+                               *result = lm;
+
+                               ld->ld_responses = NULL;
+                               while ( lm->lm_next ) {
+                                       lm->lm_chain_tail->lm_chain = lm->lm_next;
+                                       lm->lm_chain_tail = lm->lm_next->lm_chain_tail;
+                                       lm->lm_next = lm->lm_next->lm_next;
+                               }
+                               rc = lm->lm_msgtype;
+                               break;
+                       }
+
+                       for ( lp = &ld->ld_responses; lm; lp = &lm->lm_next, lm = *lp ) {
+                               if ( msgid == lm->lm_msgid ) break;
+                       }
+                       if ( lm ) {
+                               *lp = lm->lm_next;
+                               *result = lm;
+                               rc = lm->lm_msgtype;
+                       }
+               }
+
                if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
                        struct timeval  curr_time_tv = { 0 },
                                        delta_time_tv = { 0 };
@@ -1102,7 +1157,10 @@ nextresp2:
 
        /* is this the one we're looking for? */
        if ( msgid == LDAP_RES_ANY || id == msgid ) {
-               if ( all == LDAP_MSG_ONE
+               if ( msgid == LDAP_RES_ANY && all == LDAP_MSG_RECEIVED ) {
+                       /* ITS#10229: We want to keep going so long as there's anything to
+                        * read. */
+               } else if ( all == LDAP_MSG_ONE
                        || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
                                && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
                                && newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE