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 2 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, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 * Name: paged_searches
28 * Component: ldb paged searches module
30 * Description: this module detects if the remote ldap server supports
31 * paged results and use them to transparently access all objects
37 #include "ldb/include/includes.h"
39 #define PS_DEFAULT_PAGE_SIZE 500
40 /* 500 objects per query seem to be a decent compromise
41 * the default AD limit per request is 1000 entries */
49 struct ldb_module
*module
;
51 int (*up_callback
)(struct ldb_context
*, void *, struct ldb_reply
*);
53 struct ldb_request
*orig_req
;
55 struct ldb_request
*new_req
;
59 char **saved_referrals
;
63 static struct ldb_handle
*init_handle(void *mem_ctx
, struct ldb_module
*module
,
65 int (*callback
)(struct ldb_context
*, void *, struct ldb_reply
*))
67 struct ps_context
*ac
;
70 h
= talloc_zero(mem_ctx
, struct ldb_handle
);
72 ldb_set_errstring(module
->ldb
, "Out of Memory");
78 ac
= talloc_zero(h
, struct ps_context
);
80 ldb_set_errstring(module
->ldb
, "Out of Memory");
85 h
->private_data
= (void *)ac
;
87 h
->state
= LDB_ASYNC_INIT
;
88 h
->status
= LDB_SUCCESS
;
91 ac
->up_context
= context
;
92 ac
->up_callback
= callback
;
95 ac
->saved_referrals
= NULL
;
96 ac
->num_referrals
= 0;
101 static int check_ps_continuation(struct ldb_reply
*ares
, struct ps_context
*ac
)
103 struct ldb_paged_control
*rep_control
, *req_control
;
105 /* look up our paged control */
106 if (!ares
->controls
|| strcmp(LDB_CONTROL_PAGED_RESULTS_OID
, ares
->controls
[0]->oid
) != 0) {
107 /* something wrong here */
108 return LDB_ERR_OPERATIONS_ERROR
;
111 rep_control
= talloc_get_type(ares
->controls
[0]->data
, struct ldb_paged_control
);
112 if (rep_control
->cookie_len
== 0) {
118 /* more processing required */
119 /* let's fill in the request control with the new cookie */
120 /* if there's a reply control we must find a request
121 * control matching it */
123 if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID
, ac
->new_req
->controls
[0]->oid
) != 0) {
124 /* something wrong here */
125 return LDB_ERR_OPERATIONS_ERROR
;
128 req_control
= talloc_get_type(ac
->new_req
->controls
[0]->data
, struct ldb_paged_control
);
130 if (req_control
->cookie
) {
131 talloc_free(req_control
->cookie
);
134 req_control
->cookie
= talloc_memdup(req_control
,
136 rep_control
->cookie_len
);
137 req_control
->cookie_len
= rep_control
->cookie_len
;
143 static int store_referral(char *referral
, struct ps_context
*ac
)
145 ac
->saved_referrals
= talloc_realloc(ac
, ac
->saved_referrals
, char *, ac
->num_referrals
+ 2);
146 if (!ac
->saved_referrals
) {
147 return LDB_ERR_OPERATIONS_ERROR
;
150 ac
->saved_referrals
[ac
->num_referrals
] = talloc_strdup(ac
->saved_referrals
, referral
);
151 if (!ac
->saved_referrals
[ac
->num_referrals
]) {
152 return LDB_ERR_OPERATIONS_ERROR
;
156 ac
->saved_referrals
[ac
->num_referrals
] = NULL
;
161 static int send_referrals(struct ldb_context
*ldb
, struct ps_context
*ac
)
163 struct ldb_reply
*ares
;
166 for (i
= 0; i
< ac
->num_referrals
; i
++) {
167 ares
= talloc_zero(ac
, struct ldb_reply
);
169 return LDB_ERR_OPERATIONS_ERROR
;
172 ares
->type
= LDB_REPLY_REFERRAL
;
173 ares
->referral
= ac
->saved_referrals
[i
];
175 ac
->up_callback(ldb
, ac
->up_context
, ares
);
181 static int ps_callback(struct ldb_context
*ldb
, void *context
, struct ldb_reply
*ares
)
183 struct ps_context
*ac
= NULL
;
184 int ret
= LDB_ERR_OPERATIONS_ERROR
;
186 if (!context
|| !ares
) {
187 ldb_set_errstring(ldb
, "NULL Context or Result in callback");
191 ac
= talloc_get_type(context
, struct ps_context
);
193 switch (ares
->type
) {
194 case LDB_REPLY_ENTRY
:
195 ac
->up_callback(ldb
, ac
->up_context
, ares
);
198 case LDB_REPLY_REFERRAL
:
199 ret
= store_referral(ares
->referral
, ac
);
200 if (ret
!= LDB_SUCCESS
) {
206 ret
= check_ps_continuation(ares
, ac
);
207 if (ret
!= LDB_SUCCESS
) {
212 ret
= send_referrals(ldb
, ac
);
213 if (ret
!= LDB_SUCCESS
) {
217 /* send REPLY_DONE */
218 ac
->up_callback(ldb
, ac
->up_context
, ares
);
232 static int ps_search(struct ldb_module
*module
, struct ldb_request
*req
)
234 struct private_data
*private_data
;
235 struct ldb_paged_control
*control
;
236 struct ps_context
*ac
;
237 struct ldb_handle
*h
;
239 private_data
= talloc_get_type(module
->private_data
, struct private_data
);
241 /* check if paging is supported and if there is a any control */
242 if (!private_data
|| !private_data
->paged_supported
|| req
->controls
) {
243 /* do not touch this request paged controls not
244 * supported or explicit controls have been set or we
245 * are just not setup yet */
246 return ldb_next_request(module
, req
);
249 if (!req
->callback
|| !req
->context
) {
250 ldb_set_errstring(module
->ldb
,
251 "Async interface called with NULL callback function or NULL context");
252 return LDB_ERR_OPERATIONS_ERROR
;
255 h
= init_handle(req
, module
, req
->context
, req
->callback
);
257 return LDB_ERR_OPERATIONS_ERROR
;
259 ac
= talloc_get_type(h
->private_data
, struct ps_context
);
261 ac
->new_req
= talloc(ac
, struct ldb_request
);
262 if (!ac
->new_req
) return LDB_ERR_OPERATIONS_ERROR
;
264 ac
->new_req
->controls
= talloc_array(ac
->new_req
, struct ldb_control
*, 2);
265 if (!ac
->new_req
->controls
) return LDB_ERR_OPERATIONS_ERROR
;
267 ac
->new_req
->controls
[0] = talloc(ac
->new_req
->controls
, struct ldb_control
);
268 if (!ac
->new_req
->controls
[0]) return LDB_ERR_OPERATIONS_ERROR
;
270 control
= talloc(ac
->new_req
->controls
[0], struct ldb_paged_control
);
271 if (!control
) return LDB_ERR_OPERATIONS_ERROR
;
273 control
->size
= PS_DEFAULT_PAGE_SIZE
;
274 control
->cookie
= NULL
;
275 control
->cookie_len
= 0;
277 ac
->new_req
->controls
[0]->oid
= LDB_CONTROL_PAGED_RESULTS_OID
;
278 ac
->new_req
->controls
[0]->critical
= 1;
279 ac
->new_req
->controls
[0]->data
= control
;
281 ac
->new_req
->controls
[1] = NULL
;
283 ac
->new_req
->operation
= req
->operation
;
284 ac
->new_req
->op
.search
.base
= req
->op
.search
.base
;
285 ac
->new_req
->op
.search
.scope
= req
->op
.search
.scope
;
286 ac
->new_req
->op
.search
.tree
= req
->op
.search
.tree
;
287 ac
->new_req
->op
.search
.attrs
= req
->op
.search
.attrs
;
288 ac
->new_req
->context
= ac
;
289 ac
->new_req
->callback
= ps_callback
;
290 ldb_set_timeout_from_prev_req(module
->ldb
, req
, ac
->new_req
);
294 return ldb_next_request(module
, ac
->new_req
);
297 static int ps_continuation(struct ldb_handle
*handle
)
299 struct ps_context
*ac
;
301 if (!handle
|| !handle
->private_data
) {
302 return LDB_ERR_OPERATIONS_ERROR
;
305 ac
= talloc_get_type(handle
->private_data
, struct ps_context
);
307 /* reset the requests handle */
308 ac
->new_req
->handle
= NULL
;
310 return ldb_next_request(handle
->module
, ac
->new_req
);
313 static int ps_wait_none(struct ldb_handle
*handle
)
315 struct ps_context
*ac
;
318 if (!handle
|| !handle
->private_data
) {
319 return LDB_ERR_OPERATIONS_ERROR
;
322 if (handle
->state
== LDB_ASYNC_DONE
) {
323 return handle
->status
;
326 handle
->state
= LDB_ASYNC_PENDING
;
327 handle
->status
= LDB_SUCCESS
;
329 ac
= talloc_get_type(handle
->private_data
, struct ps_context
);
331 ret
= ldb_wait(ac
->new_req
->handle
, LDB_WAIT_NONE
);
333 if (ret
!= LDB_SUCCESS
) {
334 handle
->status
= ret
;
338 if (ac
->new_req
->handle
->status
!= LDB_SUCCESS
) {
339 handle
->status
= ac
->new_req
->handle
->status
;
343 if (ac
->new_req
->handle
->state
!= LDB_ASYNC_DONE
) {
347 /* see if we need to send another request for the next batch */
349 ret
= ps_continuation(handle
);
350 if (ret
!= LDB_SUCCESS
) {
351 handle
->status
= ret
;
355 /* continue the search with the next request */
362 handle
->state
= LDB_ASYNC_DONE
;
366 static int ps_wait_all(struct ldb_handle
*handle
)
370 while (handle
->state
!= LDB_ASYNC_DONE
) {
371 ret
= ps_wait_none(handle
);
372 if (ret
!= LDB_SUCCESS
) {
377 return handle
->status
;
380 static int ps_wait(struct ldb_handle
*handle
, enum ldb_wait_type type
)
382 if (type
== LDB_WAIT_ALL
) {
383 return ps_wait_all(handle
);
385 return ps_wait_none(handle
);
389 static int check_supported_paged(struct ldb_context
*ldb
, void *context
,
390 struct ldb_reply
*ares
)
392 struct private_data
*data
;
393 data
= talloc_get_type(context
,
394 struct private_data
);
395 if (ares
->type
== LDB_REPLY_ENTRY
) {
396 if (ldb_msg_check_string_attribute(ares
->message
,
398 LDB_CONTROL_PAGED_RESULTS_OID
)) {
399 data
->paged_supported
= True
;
406 static int ps_init(struct ldb_module
*module
)
408 static const char *attrs
[] = { "supportedControl", NULL
};
409 struct private_data
*data
;
411 struct ldb_request
*req
;
413 data
= talloc(module
, struct private_data
);
415 return LDB_ERR_OTHER
;
417 module
->private_data
= data
;
418 data
->paged_supported
= False
;
420 req
= talloc(module
, struct ldb_request
);
422 ldb_set_errstring(module
->ldb
, "Out of Memory");
423 return LDB_ERR_OPERATIONS_ERROR
;
426 req
->operation
= LDB_SEARCH
;
427 req
->op
.search
.base
= ldb_dn_new(req
);
428 req
->op
.search
.scope
= LDB_SCOPE_BASE
;
430 req
->op
.search
.tree
= ldb_parse_tree(req
, "objectClass=*");
431 if (req
->op
.search
.tree
== NULL
) {
432 ldb_set_errstring(module
->ldb
, "Unable to parse search expression");
434 return LDB_ERR_OPERATIONS_ERROR
;
437 req
->op
.search
.attrs
= attrs
;
438 req
->controls
= NULL
;
440 req
->callback
= check_supported_paged
;
441 ldb_set_timeout(module
->ldb
, req
, 0); /* use default timeout */
443 ret
= ldb_next_request(module
, req
);
445 if (ret
== LDB_SUCCESS
) {
446 ret
= ldb_wait(req
->handle
, LDB_WAIT_ALL
);
450 if (ret
!= LDB_SUCCESS
) {
454 return ldb_next_init(module
);
457 static const struct ldb_module_ops ps_ops
= {
458 .name
= "paged_searches",
461 .init_context
= ps_init
464 int ldb_paged_searches_init(void)
466 return ldb_register_module(&ps_ops
);