Reputation: 63
I'm trying to do a paged search in an Active Directory using Novell.Directory.Ldap.NETStandard (https://github.com/dsbenghe/Novell.Directory.Ldap.NETStandard) and Simple Paged Results control (https://ldapwiki.com/wiki/Simple%20Paged%20Results%20Control).
First page works fine but the second one throws "Unavailable Critical Extension" on the searchResult.next() line. When looking in the event log for ActiveDirectory I found:
00000057: LdapErr: DSID-0C090809, comment: Error processing control, data 0, v23f0 0000208D: NameErr: DSID-03100213, problem 2001 (NO_OBJECT), data 0, best match of:
We have also tried the LdapVirtualListControl but run into a different problem, see How to do a paged search on an Ldap server with > 10000 entries using Novell.Directory.Ldap.NETStandard?
Here are a simplified code we use to reproduce:
// Connection
var ldapConn = new LdapConnection()
{
SecureSocketLayer = true,
};
ldapConn.UserDefinedServerCertValidationDelegate += (sender, certificate, chain, sslPolicyErrors) => true;
ldapConn.Connect(host, 636);
ldapConn.Bind(username, password);
// Constraints
LdapSearchConstraints searchConstraints = (LdapSearchConstraints)_conn.SearchConstraints.Clone();
int pageSize = 100, count = 0;
bool exit = false;
const string LDAP_SERVER_SIMPLE_PAGED_RESULT_OID = "1.2.840.113556.1.4.319";
LdapControl pageControl = null;
do
{
int inPageCount = 0;
// Add Simple Paged Result control
var request = new Asn1Sequence(2);
request.add(new Asn1Integer(pageSize));
request.add(pageControl == null ? new Asn1OctetString("") : new Asn1OctetString(pageControl.getValue()));
searchConstraints.setControls(
new LdapControl(LDAP_SERVER_SIMPLE_PAGED_RESULT_OID, true, request.getEncoding(new LBEREncoder()))
);
// Get search result
var searchResult = (LdapSearchResults)ldapConn.Search(container, LdapConnection.SCOPE_SUB, query, null, false, searchConstraints);
while (searchResult.hasMore())
{
// Detect simple paged result control
pageControl = searchResult.ResponseControls?.Where(rc => rc.ID == LDAP_SERVER_SIMPLE_PAGED_RESULT_OID).FirstOrDefault();
if (pageControl != null) break;
var nextEntry = searchResult.next();
inPageCount++;
}
count += inPageCount;
// Exit if no more pages
exit = pageControl == null;
} while (!exit);
Upvotes: 4
Views: 3412
Reputation: 328
According to RFC Simple Paged Results Control encoded as
realSearchControlValue ::= SEQUENCE {
size INTEGER (0..maxInt),
-- requested page size from client
-- result set size estimate from server
cookie OCTET STRING
}
which may be clear seen on the next screenshot (taken from Wireshark).
When the client adds control to the request, size is set to the desired number of elements in the page and cookie is the opaque structure from the previous server response (empty for the first request).
When you try to construct control in your request, you mistakenly add the whole control value instead of cookie (pageControl.getValue()):
var request = new Asn1Sequence(2);
request.add(new Asn1Integer(pageSize));
request.add(pageControl == null ? new Asn1OctetString("") : new Asn1OctetString(pageControl.getValue()));
It makes all requests after the first one incorrect.
Take a look at https://github.com/metacube/PagedResultsControl. I've created typed Simple Paged Results Control implementation which encapsulates decoding/ encoding logic. Works perfectly fine for me in the case of 100 000+ entries from Active Directory.
The test application shows basic usage.
Upvotes: 5