4 Copyright (C) Simo Sorce 2005-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
7 ** NOTE! The following LGPL license applies to the ldb
8 ** library. This does NOT imply that all of Samba is released
11 This library is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Lesser General Public
13 License as published by the Free Software Foundation; either
14 version 3 of the License, or (at your option) any later version.
16 This library is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Lesser General Public License for more details.
21 You should have received a copy of the GNU Lesser General Public
22 License along with this library; if not, see <http://www.gnu.org/licenses/>.
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 "system/filesys.h"
38 #include "system/time.h"
39 #include "ldb_module.h"
41 #define PS_DEFAULT_PAGE_SIZE 500
42 /* 500 objects per query seem to be a decent compromise
43 * the default AD limit per request is 1000 entries */
51 struct ldb_module
*module
;
52 struct ldb_request
*req
;
56 char **saved_referrals
;
57 unsigned int num_referrals
;
59 struct ldb_request
*down_req
;
62 static int check_ps_continuation(struct ps_context
*ac
, struct ldb_request
*req
, struct ldb_reply
*ares
)
64 struct ldb_context
*ldb
;
65 struct ldb_control
*rep_control
, *req_control
;
66 struct ldb_paged_control
*paged_rep_control
= NULL
, *paged_req_control
= NULL
;
67 ldb
= ldb_module_get_ctx(ac
->module
);
69 rep_control
= ldb_reply_get_control(ares
, LDB_CONTROL_PAGED_RESULTS_OID
);
71 paged_rep_control
= talloc_get_type(rep_control
->data
, struct ldb_paged_control
);
74 req_control
= ldb_request_get_control(req
, LDB_CONTROL_PAGED_RESULTS_OID
);
75 paged_req_control
= talloc_get_type(req_control
->data
, struct ldb_paged_control
);
77 if (!rep_control
|| !paged_rep_control
) {
78 if (paged_req_control
->cookie
) {
79 /* something wrong here - why give us a control back befre, but not one now? */
80 ldb_set_errstring(ldb
, "paged_searches: ERROR: We got back a control from a previous page, but this time no control was returned!");
81 return LDB_ERR_OPERATIONS_ERROR
;
83 /* No cookie received yet, valid to just return the full data set */
91 if (paged_rep_control
->cookie_len
== 0) {
97 /* more processing required */
98 /* let's fill in the request control with the new cookie */
99 /* if there's a reply control we must find a request
100 * control matching it */
102 if (paged_req_control
->cookie
) {
103 talloc_free(paged_req_control
->cookie
);
106 paged_req_control
->cookie
= talloc_memdup(req_control
,
107 paged_rep_control
->cookie
,
108 paged_rep_control
->cookie_len
);
109 paged_req_control
->cookie_len
= paged_rep_control
->cookie_len
;
115 static int store_referral(struct ps_context
*ac
, char *referral
)
117 ac
->saved_referrals
= talloc_realloc(ac
, ac
->saved_referrals
, char *, ac
->num_referrals
+ 2);
118 if (!ac
->saved_referrals
) {
119 return LDB_ERR_OPERATIONS_ERROR
;
122 ac
->saved_referrals
[ac
->num_referrals
] = talloc_strdup(ac
->saved_referrals
, referral
);
123 if (!ac
->saved_referrals
[ac
->num_referrals
]) {
124 return LDB_ERR_OPERATIONS_ERROR
;
128 ac
->saved_referrals
[ac
->num_referrals
] = NULL
;
133 static int send_referrals(struct ps_context
*ac
)
135 struct ldb_reply
*ares
;
139 for (i
= 0; i
< ac
->num_referrals
; i
++) {
140 ares
= talloc_zero(ac
->req
, struct ldb_reply
);
142 return LDB_ERR_OPERATIONS_ERROR
;
145 ares
->type
= LDB_REPLY_REFERRAL
;
146 ares
->referral
= ac
->saved_referrals
[i
];
148 ret
= ldb_module_send_referral(ac
->req
, ares
->referral
);
149 if (ret
!= LDB_SUCCESS
) {
157 static int ps_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
159 struct ps_context
*ac
;
162 ac
= talloc_get_type(req
->context
, struct ps_context
);
165 return ldb_module_done(ac
->req
, NULL
, NULL
,
166 LDB_ERR_OPERATIONS_ERROR
);
168 if (ares
->error
!= LDB_SUCCESS
) {
169 return ldb_module_done(ac
->req
, ares
->controls
,
170 ares
->response
, ares
->error
);
173 switch (ares
->type
) {
174 case LDB_REPLY_ENTRY
:
175 ret
= ldb_module_send_entry(ac
->req
, ares
->message
, ares
->controls
);
176 if (ret
!= LDB_SUCCESS
) {
177 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
181 case LDB_REPLY_REFERRAL
:
182 ret
= store_referral(ac
, ares
->referral
);
183 if (ret
!= LDB_SUCCESS
) {
184 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
190 ret
= check_ps_continuation(ac
, req
, ares
);
191 if (ret
!= LDB_SUCCESS
) {
192 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
197 ret
= ldb_next_request(ac
->module
, ac
->down_req
);
199 if (ret
!= LDB_SUCCESS
) {
200 return ldb_module_done(ac
->req
,
207 ret
= send_referrals(ac
);
208 if (ret
!= LDB_SUCCESS
) {
209 return ldb_module_done(ac
->req
,
213 /* send REPLY_DONE */
214 return ldb_module_done(ac
->req
, ares
->controls
,
215 ares
->response
, LDB_SUCCESS
);
224 static int ps_search(struct ldb_module
*module
, struct ldb_request
*req
)
226 struct ldb_context
*ldb
;
227 struct private_data
*private_data
;
228 struct ps_context
*ac
;
229 struct ldb_paged_control
*control
;
232 private_data
= talloc_get_type(ldb_module_get_private(module
), struct private_data
);
233 ldb
= ldb_module_get_ctx(module
);
235 /* check if paging is supported */
236 if (!private_data
|| !private_data
->paged_supported
) {
237 /* do not touch this request paged controls not
239 * are just not setup yet */
240 return ldb_next_request(module
, req
);
243 ac
= talloc_zero(req
, struct ps_context
);
246 return LDB_ERR_OPERATIONS_ERROR
;
252 ac
->saved_referrals
= NULL
;
253 ac
->num_referrals
= 0;
255 ldb
= ldb_module_get_ctx(ac
->module
);
257 control
= talloc(ac
, struct ldb_paged_control
);
259 return LDB_ERR_OPERATIONS_ERROR
;
262 control
->size
= PS_DEFAULT_PAGE_SIZE
;
263 control
->cookie
= NULL
;
264 control
->cookie_len
= 0;
266 ret
= ldb_build_search_req_ex(&ac
->down_req
, ldb
, ac
,
267 ac
->req
->op
.search
.base
,
268 ac
->req
->op
.search
.scope
,
269 ac
->req
->op
.search
.tree
,
270 ac
->req
->op
.search
.attrs
,
275 LDB_REQ_SET_LOCATION(ac
->down_req
);
276 if (ret
!= LDB_SUCCESS
) {
280 ret
= ldb_request_add_control(ac
->down_req
, LDB_CONTROL_PAGED_RESULTS_OID
,
282 if (ret
!= LDB_SUCCESS
) {
286 talloc_steal(ac
->down_req
, control
);
288 return ldb_next_request(ac
->module
, ac
->down_req
);
291 static int check_supported_paged(struct ldb_request
*req
,
292 struct ldb_reply
*ares
)
294 struct private_data
*data
;
296 data
= talloc_get_type(req
->context
, struct private_data
);
299 return ldb_request_done(req
, LDB_ERR_OPERATIONS_ERROR
);
301 if (ares
->error
!= LDB_SUCCESS
) {
302 return ldb_request_done(req
, LDB_ERR_OPERATIONS_ERROR
);
305 switch (ares
->type
) {
306 case LDB_REPLY_ENTRY
:
307 if (ldb_msg_check_string_attribute(ares
->message
,
309 LDB_CONTROL_PAGED_RESULTS_OID
)) {
310 data
->paged_supported
= true;
314 case LDB_REPLY_REFERRAL
:
319 return ldb_request_done(req
, LDB_SUCCESS
);
326 static int ps_init(struct ldb_module
*module
)
328 struct ldb_context
*ldb
;
329 static const char *attrs
[] = { "supportedControl", NULL
};
330 struct private_data
*data
;
333 struct ldb_request
*req
;
335 ldb
= ldb_module_get_ctx(module
);
337 data
= talloc(module
, struct private_data
);
340 return LDB_ERR_OPERATIONS_ERROR
;
342 data
->paged_supported
= false;
344 ldb_module_set_private(module
, data
);
346 base
= ldb_dn_new(module
, ldb
, "");
349 return LDB_ERR_OPERATIONS_ERROR
;
351 ret
= ldb_build_search_req(&req
, ldb
, module
,
352 base
, LDB_SCOPE_BASE
,
355 data
, check_supported_paged
,
357 LDB_REQ_SET_LOCATION(req
);
358 if (ret
!= LDB_SUCCESS
) {
362 ret
= ldb_next_request(module
, req
);
363 if (ret
== LDB_SUCCESS
) {
364 ret
= ldb_wait(req
->handle
, LDB_WAIT_ALL
);
366 if (ret
!= LDB_SUCCESS
) {
373 return ldb_next_init(module
);
376 static const struct ldb_module_ops ldb_paged_searches_module_ops
= {
377 .name
= "paged_searches",
379 .init_context
= ps_init
382 int ldb_paged_searches_init(const char *version
)
384 LDB_MODULE_CHECK_VERSION(version
);
385 return ldb_register_module(&ldb_paged_searches_module_ops
);