4 Copyright (C) Simo Sorce 2005-2006
6 ** NOTE! The following LGPL license applies to the ldb
7 ** library. This does NOT imply that all of Samba is released
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 3 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, see <http://www.gnu.org/licenses/>.
25 * Name: paged_searches
27 * Component: ldb paged searches module
29 * Description: this module detects if the remote ldap server supports
30 * paged results and use them to transparently access all objects
36 #include "ldb/include/includes.h"
38 #define PS_DEFAULT_PAGE_SIZE 500
39 /* 500 objects per query seem to be a decent compromise
40 * the default AD limit per request is 1000 entries */
48 struct ldb_module
*module
;
50 int (*up_callback
)(struct ldb_context
*, void *, struct ldb_reply
*);
52 struct ldb_request
*orig_req
;
54 struct ldb_request
*new_req
;
58 char **saved_referrals
;
62 static struct ldb_handle
*init_handle(void *mem_ctx
, struct ldb_module
*module
,
64 int (*callback
)(struct ldb_context
*, void *, struct ldb_reply
*))
66 struct ps_context
*ac
;
69 h
= talloc_zero(mem_ctx
, struct ldb_handle
);
71 ldb_set_errstring(module
->ldb
, "Out of Memory");
77 ac
= talloc_zero(h
, struct ps_context
);
79 ldb_set_errstring(module
->ldb
, "Out of Memory");
84 h
->private_data
= (void *)ac
;
86 h
->state
= LDB_ASYNC_INIT
;
87 h
->status
= LDB_SUCCESS
;
90 ac
->up_context
= context
;
91 ac
->up_callback
= callback
;
94 ac
->saved_referrals
= NULL
;
95 ac
->num_referrals
= 0;
100 static int check_ps_continuation(struct ldb_reply
*ares
, struct ps_context
*ac
)
102 struct ldb_paged_control
*rep_control
, *req_control
;
104 /* look up our paged control */
105 if (!ares
->controls
|| strcmp(LDB_CONTROL_PAGED_RESULTS_OID
, ares
->controls
[0]->oid
) != 0) {
106 /* something wrong here */
107 return LDB_ERR_OPERATIONS_ERROR
;
110 rep_control
= talloc_get_type(ares
->controls
[0]->data
, struct ldb_paged_control
);
111 if (rep_control
->cookie_len
== 0) {
117 /* more processing required */
118 /* let's fill in the request control with the new cookie */
119 /* if there's a reply control we must find a request
120 * control matching it */
122 if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID
, ac
->new_req
->controls
[0]->oid
) != 0) {
123 /* something wrong here */
124 return LDB_ERR_OPERATIONS_ERROR
;
127 req_control
= talloc_get_type(ac
->new_req
->controls
[0]->data
, struct ldb_paged_control
);
129 if (req_control
->cookie
) {
130 talloc_free(req_control
->cookie
);
133 req_control
->cookie
= talloc_memdup(req_control
,
135 rep_control
->cookie_len
);
136 req_control
->cookie_len
= rep_control
->cookie_len
;
142 static int store_referral(char *referral
, struct ps_context
*ac
)
144 ac
->saved_referrals
= talloc_realloc(ac
, ac
->saved_referrals
, char *, ac
->num_referrals
+ 2);
145 if (!ac
->saved_referrals
) {
146 return LDB_ERR_OPERATIONS_ERROR
;
149 ac
->saved_referrals
[ac
->num_referrals
] = talloc_strdup(ac
->saved_referrals
, referral
);
150 if (!ac
->saved_referrals
[ac
->num_referrals
]) {
151 return LDB_ERR_OPERATIONS_ERROR
;
155 ac
->saved_referrals
[ac
->num_referrals
] = NULL
;
160 static int send_referrals(struct ldb_context
*ldb
, struct ps_context
*ac
)
162 struct ldb_reply
*ares
;
165 for (i
= 0; i
< ac
->num_referrals
; i
++) {
166 ares
= talloc_zero(ac
, struct ldb_reply
);
168 return LDB_ERR_OPERATIONS_ERROR
;
171 ares
->type
= LDB_REPLY_REFERRAL
;
172 ares
->referral
= ac
->saved_referrals
[i
];
174 ac
->up_callback(ldb
, ac
->up_context
, ares
);
180 static int ps_callback(struct ldb_context
*ldb
, void *context
, struct ldb_reply
*ares
)
182 struct ps_context
*ac
= NULL
;
183 int ret
= LDB_ERR_OPERATIONS_ERROR
;
185 if (!context
|| !ares
) {
186 ldb_set_errstring(ldb
, "NULL Context or Result in callback");
190 ac
= talloc_get_type(context
, struct ps_context
);
192 switch (ares
->type
) {
193 case LDB_REPLY_ENTRY
:
194 ac
->up_callback(ldb
, ac
->up_context
, ares
);
197 case LDB_REPLY_REFERRAL
:
198 ret
= store_referral(ares
->referral
, ac
);
199 if (ret
!= LDB_SUCCESS
) {
205 ret
= check_ps_continuation(ares
, ac
);
206 if (ret
!= LDB_SUCCESS
) {
211 ret
= send_referrals(ldb
, ac
);
212 if (ret
!= LDB_SUCCESS
) {
216 /* send REPLY_DONE */
217 ac
->up_callback(ldb
, ac
->up_context
, ares
);
231 static int ps_search(struct ldb_module
*module
, struct ldb_request
*req
)
233 struct private_data
*private_data
;
234 struct ldb_paged_control
*control
;
235 struct ps_context
*ac
;
236 struct ldb_handle
*h
;
238 private_data
= talloc_get_type(module
->private_data
, struct private_data
);
240 /* check if paging is supported and if there is a any control */
241 if (!private_data
|| !private_data
->paged_supported
|| req
->controls
) {
242 /* do not touch this request paged controls not
243 * supported or explicit controls have been set or we
244 * are just not setup yet */
245 return ldb_next_request(module
, req
);
248 if (!req
->callback
|| !req
->context
) {
249 ldb_set_errstring(module
->ldb
,
250 "Async interface called with NULL callback function or NULL context");
251 return LDB_ERR_OPERATIONS_ERROR
;
254 h
= init_handle(req
, module
, req
->context
, req
->callback
);
256 return LDB_ERR_OPERATIONS_ERROR
;
258 ac
= talloc_get_type(h
->private_data
, struct ps_context
);
260 ac
->new_req
= talloc(ac
, struct ldb_request
);
261 if (!ac
->new_req
) return LDB_ERR_OPERATIONS_ERROR
;
263 ac
->new_req
->controls
= talloc_array(ac
->new_req
, struct ldb_control
*, 2);
264 if (!ac
->new_req
->controls
) return LDB_ERR_OPERATIONS_ERROR
;
266 ac
->new_req
->controls
[0] = talloc(ac
->new_req
->controls
, struct ldb_control
);
267 if (!ac
->new_req
->controls
[0]) return LDB_ERR_OPERATIONS_ERROR
;
269 control
= talloc(ac
->new_req
->controls
[0], struct ldb_paged_control
);
270 if (!control
) return LDB_ERR_OPERATIONS_ERROR
;
272 control
->size
= PS_DEFAULT_PAGE_SIZE
;
273 control
->cookie
= NULL
;
274 control
->cookie_len
= 0;
276 ac
->new_req
->controls
[0]->oid
= LDB_CONTROL_PAGED_RESULTS_OID
;
277 ac
->new_req
->controls
[0]->critical
= 1;
278 ac
->new_req
->controls
[0]->data
= control
;
280 ac
->new_req
->controls
[1] = NULL
;
282 ac
->new_req
->operation
= req
->operation
;
283 ac
->new_req
->op
.search
.base
= req
->op
.search
.base
;
284 ac
->new_req
->op
.search
.scope
= req
->op
.search
.scope
;
285 ac
->new_req
->op
.search
.tree
= req
->op
.search
.tree
;
286 ac
->new_req
->op
.search
.attrs
= req
->op
.search
.attrs
;
287 ac
->new_req
->context
= ac
;
288 ac
->new_req
->callback
= ps_callback
;
289 ldb_set_timeout_from_prev_req(module
->ldb
, req
, ac
->new_req
);
293 return ldb_next_request(module
, ac
->new_req
);
296 static int ps_continuation(struct ldb_handle
*handle
)
298 struct ps_context
*ac
;
300 if (!handle
|| !handle
->private_data
) {
301 return LDB_ERR_OPERATIONS_ERROR
;
304 ac
= talloc_get_type(handle
->private_data
, struct ps_context
);
306 /* reset the requests handle */
307 ac
->new_req
->handle
= NULL
;
309 return ldb_next_request(handle
->module
, ac
->new_req
);
312 static int ps_wait_none(struct ldb_handle
*handle
)
314 struct ps_context
*ac
;
317 if (!handle
|| !handle
->private_data
) {
318 return LDB_ERR_OPERATIONS_ERROR
;
321 if (handle
->state
== LDB_ASYNC_DONE
) {
322 return handle
->status
;
325 handle
->state
= LDB_ASYNC_PENDING
;
326 handle
->status
= LDB_SUCCESS
;
328 ac
= talloc_get_type(handle
->private_data
, struct ps_context
);
330 ret
= ldb_wait(ac
->new_req
->handle
, LDB_WAIT_NONE
);
332 if (ret
!= LDB_SUCCESS
) {
333 handle
->status
= ret
;
337 if (ac
->new_req
->handle
->status
!= LDB_SUCCESS
) {
338 handle
->status
= ac
->new_req
->handle
->status
;
342 if (ac
->new_req
->handle
->state
!= LDB_ASYNC_DONE
) {
346 /* see if we need to send another request for the next batch */
348 ret
= ps_continuation(handle
);
349 if (ret
!= LDB_SUCCESS
) {
350 handle
->status
= ret
;
354 /* continue the search with the next request */
361 handle
->state
= LDB_ASYNC_DONE
;
365 static int ps_wait_all(struct ldb_handle
*handle
)
369 while (handle
->state
!= LDB_ASYNC_DONE
) {
370 ret
= ps_wait_none(handle
);
371 if (ret
!= LDB_SUCCESS
) {
376 return handle
->status
;
379 static int ps_wait(struct ldb_handle
*handle
, enum ldb_wait_type type
)
381 if (type
== LDB_WAIT_ALL
) {
382 return ps_wait_all(handle
);
384 return ps_wait_none(handle
);
388 static int check_supported_paged(struct ldb_context
*ldb
, void *context
,
389 struct ldb_reply
*ares
)
391 struct private_data
*data
;
392 data
= talloc_get_type(context
,
393 struct private_data
);
394 if (ares
->type
== LDB_REPLY_ENTRY
) {
395 if (ldb_msg_check_string_attribute(ares
->message
,
397 LDB_CONTROL_PAGED_RESULTS_OID
)) {
398 data
->paged_supported
= True
;
405 static int ps_init(struct ldb_module
*module
)
407 static const char *attrs
[] = { "supportedControl", NULL
};
408 struct private_data
*data
;
410 struct ldb_request
*req
;
412 data
= talloc(module
, struct private_data
);
414 return LDB_ERR_OTHER
;
416 module
->private_data
= data
;
417 data
->paged_supported
= False
;
419 req
= talloc(module
, struct ldb_request
);
421 ldb_set_errstring(module
->ldb
, "Out of Memory");
422 return LDB_ERR_OPERATIONS_ERROR
;
425 req
->operation
= LDB_SEARCH
;
426 req
->op
.search
.base
= ldb_dn_new(req
);
427 req
->op
.search
.scope
= LDB_SCOPE_BASE
;
429 req
->op
.search
.tree
= ldb_parse_tree(req
, "objectClass=*");
430 if (req
->op
.search
.tree
== NULL
) {
431 ldb_set_errstring(module
->ldb
, "Unable to parse search expression");
433 return LDB_ERR_OPERATIONS_ERROR
;
436 req
->op
.search
.attrs
= attrs
;
437 req
->controls
= NULL
;
439 req
->callback
= check_supported_paged
;
440 ldb_set_timeout(module
->ldb
, req
, 0); /* use default timeout */
442 ret
= ldb_next_request(module
, req
);
444 if (ret
== LDB_SUCCESS
) {
445 ret
= ldb_wait(req
->handle
, LDB_WAIT_ALL
);
449 if (ret
!= LDB_SUCCESS
) {
453 return ldb_next_init(module
);
456 static const struct ldb_module_ops ps_ops
= {
457 .name
= "paged_searches",
460 .init_context
= ps_init
463 int ldb_paged_searches_init(void)
465 return ldb_register_module(&ps_ops
);