[ 
https://issues.apache.org/jira/browse/DIRAPI-434?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Emmanuel Lécharny updated DIRAPI-434:
-------------------------------------
    Description: 
I've been re-running a simple paging search against apacheds and occasionally 
fails, indicating that the cookie was bad. I ran directory server version 
2.0.0.AM27 with directory api version 2.1.8. While the error is raised by 
directory server, I think the problem is in the api library.

Here's the error from directory server

{code}
2026-08-06T18:55:05,375 DEBUG handlers.LdapResponseHandler 
(SearchResultDoneHandler.java:handle(41)) - Message Sent : MessageType : 
SEARCH_RESULT_DONE
Message ID : 65419
    Search Result Done
        Ldap Result
            Result code : (UNWILLING_TO_PERFORM) unwillingToPerform
            Matched Dn : 'null'
            Diagnostic message : 'Invalid cookie for this PagedSearch request.'
{code}

I looked into the problem a bit and it seems that the cookie encoding does not 
match the cookie decoding for some cases. From what I can tell, the page cookie 
is BER-encoded shifted message id.
See 
[PagedSearchContext|https://github.com/apache/directory-server/blob/master/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/controls/PagedSearchContext.java#L75-L80].

{code}
    /**
     * Creates a new instance of this class, storing the SearchRequest into it.
     * 
     * @param searchRequest The SearchRequest
     */
    public PagedSearchContext( SearchRequest searchRequest )
    {
        previousSearchRequest = searchRequest;
        currentPosition = 0;

        // We compute a key for this cookie. It combines the search request
        // and some time seed, in order to avoid possible collisions, as
        // a user may send more than one PagedSearch on the same session.
        cookieValue = new AtomicInteger( searchRequest.getMessageId() << 16 );

        cookie = BerValue.getBytes( cookieValue.get() );
    }
{code}

BER encoding is performed by the 
[BerValue|https://github.com/apache/directory-ldap-api/blob/master/asn1/ber/src/main/java/org/apache/directory/api/asn1/ber/tlv/BerValue.java#L315]
 class.

The bytes are decoded back into an integer in 
[SearchRequestHandler.doPagedSearch|https://github.com/apache/directory-server/blob/master/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/request/SearchRequestHandler.java#L691-L692].

{code}
            int cookieValue = pagedSearchControl.getCookieValue();
            pagedContext = session.getPagedSearchContext( cookieValue );
{code}

This is implemented by the 
[PagedResultsImpl|https://github.com/apache/directory-ldap-api/blob/61bb5105971cda38ac56d20313554b680436a249/ldap/model/src/main/java/org/apache/directory/api/ldap/model/message/controls/PagedResultsImpl.java#L121]
 class.

However, PagedResultsImpl.getCookieValue does not match BerValue.getBytes for 
some values. Here's a small unit test the illustrates the affected range that I 
ran into. I have not tested all ranges.

{code}
package test;

import org.apache.directory.api.asn1.ber.tlv.BerValue;
import org.apache.directory.api.ldap.model.message.SearchRequest;
import org.apache.directory.api.ldap.model.message.SearchRequestImpl;
import org.apache.directory.api.ldap.model.message.controls.PagedResultsImpl;
import org.apache.directory.server.ldap.handlers.controls.PagedSearchContext;
import org.junit.Test;

import static org.junit.Assert.assertEquals;

/**
 * Tests illustrating that PagedResultsImpl cookie decoding does not match 
PagedSearchContext cookie encoding for
 * at least one range of message ids.
 */
public class CookieCalcTest {

    @Test
    public void testCookieCalculationGoodUpper() {
        SearchRequest searchRequest = new SearchRequestImpl();
        searchRequest.setMessageId(65536);
        PagedSearchContext pagedSearchContext = new 
PagedSearchContext(searchRequest);
        int cookieValue = pagedSearchContext.getCookieValue();

        byte[] cookie = BerValue.getBytes(cookieValue);

        PagedResultsImpl pagedResults = new PagedResultsImpl();
        pagedResults.setCookie(cookie);

        int uncookieValue = pagedResults.getCookieValue();
        assertEquals(cookieValue, uncookieValue); // succeeds
    }

    @Test
    public void testCookieCalculationBadUpperLimit() {
        SearchRequest searchRequest = new SearchRequestImpl();
        searchRequest.setMessageId(65535);
        PagedSearchContext pagedSearchContext = new 
PagedSearchContext(searchRequest);
        int cookieValue = pagedSearchContext.getCookieValue();

        byte[] cookie = BerValue.getBytes(cookieValue);

        PagedResultsImpl pagedResults = new PagedResultsImpl();
        pagedResults.setCookie(cookie);

        int uncookieValue = pagedResults.getCookieValue();
        assertEquals(cookieValue, uncookieValue); // fails
    }

    @Test
    public void testCookieCalculationBadLowerLimit() {
        SearchRequest searchRequest = new SearchRequestImpl();
        searchRequest.setMessageId(65408);
        PagedSearchContext pagedSearchContext = new 
PagedSearchContext(searchRequest);
        int cookieValue = pagedSearchContext.getCookieValue();

        byte[] cookie = BerValue.getBytes(cookieValue);

        PagedResultsImpl pagedResults = new PagedResultsImpl();
        pagedResults.setCookie(cookie);

        int uncookieValue = pagedResults.getCookieValue();
        assertEquals(cookieValue, uncookieValue); // fails
    }

    @Test
    public void testCookieCalculationGoodLower() {
        SearchRequest searchRequest = new SearchRequestImpl();
        searchRequest.setMessageId(65407);
        PagedSearchContext pagedSearchContext = new 
PagedSearchContext(searchRequest);
        int cookieValue = pagedSearchContext.getCookieValue();

        byte[] cookie = BerValue.getBytes(cookieValue);

        PagedResultsImpl pagedResults = new PagedResultsImpl();
        pagedResults.setCookie(cookie);

        int uncookieValue = pagedResults.getCookieValue();
        assertEquals(cookieValue, uncookieValue); // succeeds
    }
}
{code}





  was:
I've been re-running a simple paging search against apacheds and occasionally 
fails, indicating that the cookie was bad. I ran directory server version 
2.0.0.AM27 with directory api version 2.1.8. While the error is raised by 
directory server, I think the problem is in the api library.

Here's the error from directory server
```
2026-08-06T18:55:05,375 DEBUG handlers.LdapResponseHandler 
(SearchResultDoneHandler.java:handle(41)) - Message Sent : MessageType : 
SEARCH_RESULT_DONE
Message ID : 65419
    Search Result Done
        Ldap Result
            Result code : (UNWILLING_TO_PERFORM) unwillingToPerform
            Matched Dn : 'null'
            Diagnostic message : 'Invalid cookie for this PagedSearch request.'
```

I looked into the problem a bit and it seems that the cookie encoding does not 
match the cookie decoding for some cases. From what I can tell, the page cookie 
is BER-encoded shifted message id.
See 
[PagedSearchContext|https://github.com/apache/directory-server/blob/master/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/controls/PagedSearchContext.java#L75-L80].
```
    /**
     * Creates a new instance of this class, storing the SearchRequest into it.
     * 
     * @param searchRequest The SearchRequest
     */
    public PagedSearchContext( SearchRequest searchRequest )
    {
        previousSearchRequest = searchRequest;
        currentPosition = 0;

        // We compute a key for this cookie. It combines the search request
        // and some time seed, in order to avoid possible collisions, as
        // a user may send more than one PagedSearch on the same session.
        cookieValue = new AtomicInteger( searchRequest.getMessageId() << 16 );

        cookie = BerValue.getBytes( cookieValue.get() );
    }
```

BER encoding is performed by the 
[BerValue|https://github.com/apache/directory-ldap-api/blob/master/asn1/ber/src/main/java/org/apache/directory/api/asn1/ber/tlv/BerValue.java#L315]
 class.

The bytes are decoded back into an integer in 
[SearchRequestHandler.doPagedSearch|https://github.com/apache/directory-server/blob/master/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/request/SearchRequestHandler.java#L691-L692].
```
            int cookieValue = pagedSearchControl.getCookieValue();
            pagedContext = session.getPagedSearchContext( cookieValue );
```
This is implemented by the 
[PagedResultsImpl|https://github.com/apache/directory-ldap-api/blob/61bb5105971cda38ac56d20313554b680436a249/ldap/model/src/main/java/org/apache/directory/api/ldap/model/message/controls/PagedResultsImpl.java#L121]
 class.

However, PagedResultsImpl.getCookieValue does not match BerValue.getBytes for 
some values. Here's a small unit test the illustrates the affected range that I 
ran into. I have not tested all ranges.

```
package test;

import org.apache.directory.api.asn1.ber.tlv.BerValue;
import org.apache.directory.api.ldap.model.message.SearchRequest;
import org.apache.directory.api.ldap.model.message.SearchRequestImpl;
import org.apache.directory.api.ldap.model.message.controls.PagedResultsImpl;
import org.apache.directory.server.ldap.handlers.controls.PagedSearchContext;
import org.junit.Test;

import static org.junit.Assert.assertEquals;

/**
 * Tests illustrating that PagedResultsImpl cookie decoding does not match 
PagedSearchContext cookie encoding for
 * at least one range of message ids.
 */
public class CookieCalcTest {

    @Test
    public void testCookieCalculationGoodUpper() {
        SearchRequest searchRequest = new SearchRequestImpl();
        searchRequest.setMessageId(65536);
        PagedSearchContext pagedSearchContext = new 
PagedSearchContext(searchRequest);
        int cookieValue = pagedSearchContext.getCookieValue();

        byte[] cookie = BerValue.getBytes(cookieValue);

        PagedResultsImpl pagedResults = new PagedResultsImpl();
        pagedResults.setCookie(cookie);

        int uncookieValue = pagedResults.getCookieValue();
        assertEquals(cookieValue, uncookieValue); // succeeds
    }

    @Test
    public void testCookieCalculationBadUpperLimit() {
        SearchRequest searchRequest = new SearchRequestImpl();
        searchRequest.setMessageId(65535);
        PagedSearchContext pagedSearchContext = new 
PagedSearchContext(searchRequest);
        int cookieValue = pagedSearchContext.getCookieValue();

        byte[] cookie = BerValue.getBytes(cookieValue);

        PagedResultsImpl pagedResults = new PagedResultsImpl();
        pagedResults.setCookie(cookie);

        int uncookieValue = pagedResults.getCookieValue();
        assertEquals(cookieValue, uncookieValue); // fails
    }

    @Test
    public void testCookieCalculationBadLowerLimit() {
        SearchRequest searchRequest = new SearchRequestImpl();
        searchRequest.setMessageId(65408);
        PagedSearchContext pagedSearchContext = new 
PagedSearchContext(searchRequest);
        int cookieValue = pagedSearchContext.getCookieValue();

        byte[] cookie = BerValue.getBytes(cookieValue);

        PagedResultsImpl pagedResults = new PagedResultsImpl();
        pagedResults.setCookie(cookie);

        int uncookieValue = pagedResults.getCookieValue();
        assertEquals(cookieValue, uncookieValue); // fails
    }

    @Test
    public void testCookieCalculationGoodLower() {
        SearchRequest searchRequest = new SearchRequestImpl();
        searchRequest.setMessageId(65407);
        PagedSearchContext pagedSearchContext = new 
PagedSearchContext(searchRequest);
        int cookieValue = pagedSearchContext.getCookieValue();

        byte[] cookie = BerValue.getBytes(cookieValue);

        PagedResultsImpl pagedResults = new PagedResultsImpl();
        pagedResults.setCookie(cookie);

        int uncookieValue = pagedResults.getCookieValue();
        assertEquals(cookieValue, uncookieValue); // succeeds
    }
}
```






> Directory server paging fails with UNWILLING_TO_PERFORM for some message ids
> ----------------------------------------------------------------------------
>
>                 Key: DIRAPI-434
>                 URL: https://issues.apache.org/jira/browse/DIRAPI-434
>             Project: Directory Client API
>          Issue Type: Bug
>    Affects Versions: 2.1.8
>            Reporter: David Han
>            Priority: Major
>
> I've been re-running a simple paging search against apacheds and occasionally 
> fails, indicating that the cookie was bad. I ran directory server version 
> 2.0.0.AM27 with directory api version 2.1.8. While the error is raised by 
> directory server, I think the problem is in the api library.
> Here's the error from directory server
> {code}
> 2026-08-06T18:55:05,375 DEBUG handlers.LdapResponseHandler 
> (SearchResultDoneHandler.java:handle(41)) - Message Sent : MessageType : 
> SEARCH_RESULT_DONE
> Message ID : 65419
>     Search Result Done
>         Ldap Result
>             Result code : (UNWILLING_TO_PERFORM) unwillingToPerform
>             Matched Dn : 'null'
>             Diagnostic message : 'Invalid cookie for this PagedSearch 
> request.'
> {code}
> I looked into the problem a bit and it seems that the cookie encoding does 
> not match the cookie decoding for some cases. From what I can tell, the page 
> cookie is BER-encoded shifted message id.
> See 
> [PagedSearchContext|https://github.com/apache/directory-server/blob/master/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/controls/PagedSearchContext.java#L75-L80].
> {code}
>     /**
>      * Creates a new instance of this class, storing the SearchRequest into 
> it.
>      * 
>      * @param searchRequest The SearchRequest
>      */
>     public PagedSearchContext( SearchRequest searchRequest )
>     {
>         previousSearchRequest = searchRequest;
>         currentPosition = 0;
>         // We compute a key for this cookie. It combines the search request
>         // and some time seed, in order to avoid possible collisions, as
>         // a user may send more than one PagedSearch on the same session.
>         cookieValue = new AtomicInteger( searchRequest.getMessageId() << 16 );
>         cookie = BerValue.getBytes( cookieValue.get() );
>     }
> {code}
> BER encoding is performed by the 
> [BerValue|https://github.com/apache/directory-ldap-api/blob/master/asn1/ber/src/main/java/org/apache/directory/api/asn1/ber/tlv/BerValue.java#L315]
>  class.
> The bytes are decoded back into an integer in 
> [SearchRequestHandler.doPagedSearch|https://github.com/apache/directory-server/blob/master/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/request/SearchRequestHandler.java#L691-L692].
> {code}
>             int cookieValue = pagedSearchControl.getCookieValue();
>             pagedContext = session.getPagedSearchContext( cookieValue );
> {code}
> This is implemented by the 
> [PagedResultsImpl|https://github.com/apache/directory-ldap-api/blob/61bb5105971cda38ac56d20313554b680436a249/ldap/model/src/main/java/org/apache/directory/api/ldap/model/message/controls/PagedResultsImpl.java#L121]
>  class.
> However, PagedResultsImpl.getCookieValue does not match BerValue.getBytes for 
> some values. Here's a small unit test the illustrates the affected range that 
> I ran into. I have not tested all ranges.
> {code}
> package test;
> import org.apache.directory.api.asn1.ber.tlv.BerValue;
> import org.apache.directory.api.ldap.model.message.SearchRequest;
> import org.apache.directory.api.ldap.model.message.SearchRequestImpl;
> import org.apache.directory.api.ldap.model.message.controls.PagedResultsImpl;
> import org.apache.directory.server.ldap.handlers.controls.PagedSearchContext;
> import org.junit.Test;
> import static org.junit.Assert.assertEquals;
> /**
>  * Tests illustrating that PagedResultsImpl cookie decoding does not match 
> PagedSearchContext cookie encoding for
>  * at least one range of message ids.
>  */
> public class CookieCalcTest {
>     @Test
>     public void testCookieCalculationGoodUpper() {
>         SearchRequest searchRequest = new SearchRequestImpl();
>         searchRequest.setMessageId(65536);
>         PagedSearchContext pagedSearchContext = new 
> PagedSearchContext(searchRequest);
>         int cookieValue = pagedSearchContext.getCookieValue();
>         byte[] cookie = BerValue.getBytes(cookieValue);
>         PagedResultsImpl pagedResults = new PagedResultsImpl();
>         pagedResults.setCookie(cookie);
>         int uncookieValue = pagedResults.getCookieValue();
>         assertEquals(cookieValue, uncookieValue); // succeeds
>     }
>     @Test
>     public void testCookieCalculationBadUpperLimit() {
>         SearchRequest searchRequest = new SearchRequestImpl();
>         searchRequest.setMessageId(65535);
>         PagedSearchContext pagedSearchContext = new 
> PagedSearchContext(searchRequest);
>         int cookieValue = pagedSearchContext.getCookieValue();
>         byte[] cookie = BerValue.getBytes(cookieValue);
>         PagedResultsImpl pagedResults = new PagedResultsImpl();
>         pagedResults.setCookie(cookie);
>         int uncookieValue = pagedResults.getCookieValue();
>         assertEquals(cookieValue, uncookieValue); // fails
>     }
>     @Test
>     public void testCookieCalculationBadLowerLimit() {
>         SearchRequest searchRequest = new SearchRequestImpl();
>         searchRequest.setMessageId(65408);
>         PagedSearchContext pagedSearchContext = new 
> PagedSearchContext(searchRequest);
>         int cookieValue = pagedSearchContext.getCookieValue();
>         byte[] cookie = BerValue.getBytes(cookieValue);
>         PagedResultsImpl pagedResults = new PagedResultsImpl();
>         pagedResults.setCookie(cookie);
>         int uncookieValue = pagedResults.getCookieValue();
>         assertEquals(cookieValue, uncookieValue); // fails
>     }
>     @Test
>     public void testCookieCalculationGoodLower() {
>         SearchRequest searchRequest = new SearchRequestImpl();
>         searchRequest.setMessageId(65407);
>         PagedSearchContext pagedSearchContext = new 
> PagedSearchContext(searchRequest);
>         int cookieValue = pagedSearchContext.getCookieValue();
>         byte[] cookie = BerValue.getBytes(cookieValue);
>         PagedResultsImpl pagedResults = new PagedResultsImpl();
>         pagedResults.setCookie(cookie);
>         int uncookieValue = pagedResults.getCookieValue();
>         assertEquals(cookieValue, uncookieValue); // succeeds
>     }
> }
> {code}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to