From cb20d9eea5c48d40e8696731a2b05ddf508b1cbd Mon Sep 17 00:00:00 2001 From: Oleg Date: Wed, 5 Aug 2026 11:32:21 +0000 Subject: [PATCH] Drop RFC 2696 paging now that the response is attribute-limited Paging was introduced to work around what looked like a response-size problem, but the actual cause (previous commit) was @ldapjs/asn1's BER decoder choking on a specific oversized attribute value, not overall response size. With attributes now limited to mail/cn/displayName, the whole response is tiny, and paging's extra sequential round-trips on the same connection were themselves causing an ECONNRESET partway through. An unpaged single request+response now matches what a plain ldapsearch against this same AD does successfully. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_012o9j9RezxbZVKQMrB7oRLY --- src/lib/ldap/client.ts | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/lib/ldap/client.ts b/src/lib/ldap/client.ts index bf08041..a6d34e9 100644 --- a/src/lib/ldap/client.ts +++ b/src/lib/ldap/client.ts @@ -61,25 +61,22 @@ function search( ): Promise<{ dn: string; attributes: Record }[]> { return new Promise((resolve, reject) => { const results: { dn: string; attributes: Record }[] = []; - // Paged (RFC 2696) rather than one unbounded response: a directory-wide - // browse on a real AD domain can return ~1000 entries, and ldapjs's - // single-response handling for a batch that size was observed to drop - // the connection (" closed") even though a plain `ldapsearch` with - // the identical filter completes cleanly — so the fix is on our client's - // response handling, not the query itself. ldapjs runs the multi-page - // RFC 2696 round-trips internally; callers still just see one continuous - // stream of `searchEntry` events followed by a single `end`. - // - // `attributes` is also restricted to just what callers actually read + // `attributes` is restricted to just what callers actually read // (mail/cn/displayName) instead of requesting every attribute AD has on - // an object. The real failure turned out to be @ldapjs/asn1's BER - // decoder throwing "Encoding too long" — a parser desync, most likely - // triggered while decoding some oversized/exotic AD attribute we never - // use anyway (e.g. nTSecurityDescriptor, msDS-ReplAttributeMetaData). - // Not requesting them sidesteps the bug entirely. + // an object. The original failure was @ldapjs/asn1's BER decoder + // throwing "Encoding too long" — a parser desync, most likely triggered + // while decoding some oversized/exotic AD attribute we never use anyway + // (e.g. nTSecurityDescriptor, msDS-ReplAttributeMetaData). Not requesting + // them sidesteps that bug entirely. + // + // Deliberately NOT using RFC 2696 paging here: with the response now + // this much smaller, an unpaged single request+response is simpler and + // matches what a plain `ldapsearch` against this same AD does — paging + // instead caused ldapjs's multiple sequential requests on one connection + // to get an ECONNRESET partway through. client.search( baseDn, - { filter, scope: "sub", paged: { pageSize: 200 }, attributes: ["mail", "cn", "displayName"] }, + { filter, scope: "sub", attributes: ["mail", "cn", "displayName"] }, (err, res) => { if (err) { reject(describeError(client, err));