7

Because of the fact that we are required to connect to an LDAP server using LDAPS we must use LdapConnection instead of DirectoryEntry.

Here is the source code:

        SearchResponse response;
        using (LdapConnection con = new LdapConnection(new LdapDirectoryIdentifier(Host, Port)))
        {
            if (IsSSL)
            {
                con.SessionOptions.SecureSocketLayer = true;
                con.SessionOptions.VerifyServerCertificate =
                    (connection, certificate)
                    => true;
            }
            con.Credential = new NetworkCredential(_username, _password);
            con.AuthType = AuthType.Basic;
            con.Bind();

            if (logMessage != null)
                logMessage("Connected to LDAP");

            string sFilter = String.Format(
                "(&(objectcategory=person)(objectclass=user){0}(!(userAccountControl:1.2.840.113556.1.4.803:=2)))",
                filter
                );

            SearchRequest request = new SearchRequest("OU=Corp,DC=mydc,DC=com", sFilter, SearchScope.Subtree);
            request.Attributes.Add(Resources.objectguid);
            request.Attributes.Add(Resources.givenname);
            request.Attributes.Add(Resources.sn);
            request.Attributes.Add(Resources.initials);
            request.Attributes.Add(Resources.samaccountname);
            request.Attributes.Add(Resources.userprincipalname);
            request.Attributes.Add(Resources.mail);
            request.Attributes.Add(Resources.objectsid);
            request.Attributes.Add(Resources.department);
            request.Attributes.Add(Resources.company);
            request.SizeLimit = 10;

            response = (SearchResponse) con.SendRequest(request);
        }

Upon execution of the source code (we have verified credentials, host, port, etc - using an external 3rd party software) we get the following exception:

The size limit was exceeded

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.DirectoryServices.Protocols.DirectoryOperationException: The size limit was exceeded

Source Error:

response = (SearchResponse) con.SendRequest(request);
[DirectoryOperationException: The size limit was exceeded]
   System.DirectoryServices.Protocols.LdapConnection.ConstructResponse(Int32

messageId, LdapOperation operation, ResultAll resultType, TimeSpan requestTimeOut, Boolean exceptionOnTimeOut) +2385 System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request, TimeSpan requestTimeout) +499 System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request) +50 UserSearchProvider.ADUserSearchProvider.QueryStore(UserSearchCriteriaCollection criterias, Action1 logMessage) in c:\Users\stemarie\Documents\Visual Studio 2012\Projects\Idealink.Modules\UserSearchProvider\UserSearchProvider\ADUserSearchProvider.cs:298 UserSearchProvider.UserSearchProvider.QueryAndSort(UserSearchCriteriaCollection criterias, Action1 logMessage) in c:\Users\stemarie\Documents\Visual Studio 2012\Projects\Idealink.Modules\UserSearchProvider\UserSearchProvider\UserSearchProvider.cs:77 UserSearchProvider.UserSearchProvider.Search(UserSearchCriteriaCollection criterias, Action1 logMessage) in c:\Users\stemarie\Documents\Visual Studio 2012\Projects\Idealink.Modules\UserSearchProvider\UserSearchProvider\UserSearchProvider.cs:33 UserSearchProvider.UserSearchService.Search(UserSearchCriteriaCollection criterias, Action1 logMessage) in c:\Users\stemarie\Documents\Visual Studio 2012\Projects\Idealink.Modules\UserSearchProvider\UserSearchProvider\UserSearchService.cs:44 UserSearchProviderTest._Default.Page_Load(Object sender, EventArgs e) in c:\Users\stemarie\Documents\Visual Studio 2012\Projects\Idealink.Modules\UserSearchProvider\UserSearchProviderTest\Default.aspx.cs:28

The part that confuses me is that we did specify the maximum size limit, we don't want more than 100 entries - we want to limit it. But yet the library consistently throws the error even if we specify a SizeLimit of 1.

Does anyone have any insights/suggestions regarding this issue? We are very close to getting this working and just need to resolve this last problem.

Karell Ste-Marie
  • 1,022
  • 1
  • 10
  • 22
  • How many directory entries match your search ? What happens if you remove the SizeLimit line ? Seems like the sizelimit will fail if its value is lower than the server page size. I guess you should try issuing a paged search see http://stackoverflow.com/q/1646518/1236044 – jbl Nov 26 '12 at 15:55
  • I did issues a paged search control in the request but got a response that the server doesn't support paged search. – Karell Ste-Marie Nov 26 '12 at 16:00
  • I don't know how many entries could match my search, but I only want 100 of them. The purpose of the code is to create a component that performs "user searches" in LDAPS for our product - This library is not a frankenlibrary - we want it to be a very sturdy LDAPS user search (adapter pattern) library so that we can offer this to our clients. So I have no way to predict user input and how many results this could mean for the library. – Karell Ste-Marie Nov 26 '12 at 16:02
  • If I remove the SizeLimit line, I get the exact same error – Karell Ste-Marie Nov 26 '12 at 16:13
  • 1
    On this page http://go4answers.webhost4life.com/Example/get-partial-results-encountering-s-size-218994.aspx it is said : "Yes, you can catch the exception and see its Response.Entries property!" I think it's worth giving a try ;-) – jbl Nov 26 '12 at 16:54
  • Regretfully, it seems that this is not accurate - the documentation for the DirectoryOperationException on MSDN @ http://msdn.microsoft.com/en-us/library/ms141745.aspx does mention the Response property as a DirectoryResponse class which does not have an Entries property documented nor in Intellisense. Thanks for the solid attempt but looks like it's not the right thing to try in this situation. But I will mention that I Googled it prior to posted it here and found the same suggestion everywhere - it just doesn't seems to apply to a SearchRequest but may apply to another type of Request. – Karell Ste-Marie Nov 26 '12 at 18:35
  • 1
    Found it - I need to cast the Exception.Response to a SearchResponse - then I get all of my results! – Karell Ste-Marie Nov 26 '12 at 18:43

2 Answers2

10

You should use cookies in a function similar to this. The function returns a collection of SearchResponse objects, which the caller should loop though.

private List<SearchResponse> SearchDirectory(string distinguishedName, string searchFilter, System.DirectoryServices.Protocols.SearchScope searchScope, params string[] attributeList)
{
    List<SearchResponse> result = new List<SearchResponse>();
    SearchResponse response = null;
    int maxResultsToRequest = 100;
    try
    {
        PageResultRequestControl pageRequestControl = new PageResultRequestControl(maxResultsToRequest);

        // used to retrieve the cookie to send for the subsequent request
        PageResultResponseControl pageResponseControl;
        SearchRequest searchRequest = new SearchRequest(distinguishedName, searchFilter, searchScope, attributeList);
        searchRequest.Controls.Add(pageRequestControl);

        while (true)
        {
            response = (SearchResponse)connection.SendRequest(searchRequest);
            result.Add(response);
            pageResponseControl = (PageResultResponseControl)response.Controls[0];
            if (pageResponseControl.Cookie.Length == 0)
                break;
            pageRequestControl.Cookie = pageResponseControl.Cookie;
        }
    }
    catch (Exception e)
    {
        // do something with the error

    }
    return result;
}
John
  • 1,058
  • 1
  • 11
  • 21
  • Run into this error today... "paged" requests worked for me. – musium Feb 19 '21 at 18:51
  • This is the best way to solve to this particular issue. I prefer a do-while loop in this situation. Where you remove the "if (pageResponseControl.Cookie.Length == 0)" and following break statement. And make the while condition pageRequestControl.Cookie.Length > 0. – Zach W Mar 22 '22 at 22:48
  • thanks, this finally fixed my issue! Very weird logic solving this with "cookies" though... – rotsch Feb 28 '23 at 14:38
0

As it turns out, this works:

            try
            {
                response = (SearchResponse)con.SendRequest(request);

                return response.Entries.Cast<SearchResultEntry>()
                    .Select(entry => entry.Attributes)
                    .Select(x => GetADUserSearchItemFromADProperties(x, logMessage))
                    .Where(user => user.HasName)
                    .Cast<IUserSearchItem>();
            }
            catch (DirectoryOperationException ex)
            {
                response = (SearchResponse) ex.Response;
                return response.Entries.Cast<SearchResultEntry>()
                    .Select(entry => entry.Attributes)
                    .Select(x => GetADUserSearchItemFromADProperties(x, logMessage))
                    .Where(user => user.HasName)
                    .Cast<IUserSearchItem>();
            }

The MSDN documentation states that you get a DirectoryResponse class as the DirectoryOperationException.Response property. You can however cast this property to a SearchResponse type and then use the SearchResponse.Entries property to get the entries that you have received prior to hitting the specified SizeLimit.

I have tried this and I get the expected results, I'm just a bit upset that I have to work with an exception in order to perform the operation.

Karell Ste-Marie
  • 1,022
  • 1
  • 10
  • 22
  • 2
    One problem with this solution, I think, is that you don't get *all* the search responses, just the responses up to SizeLimit. A more correct solution would be to extract the responses you received from Ex.Response, then re-run the SendRequest but somehow limiting the scope to what you haven't retrieved yet... – Irinotecan Jul 09 '13 at 18:34
  • I know this is an old post, but it should not be marked as the accepted answer, even if the other one has more votes, it is still misleading to other viewers. So I've downvoted this to try and make it more obvious. – Kevin Jun 29 '22 at 04:53