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 if (req_control
== NULL
|| req_control
->data
== NULL
) {
76 ldb_set_errstring(ldb
, "paged_searches: control is missing or malformed");
77 return LDB_ERR_OPERATIONS_ERROR
;
80 paged_req_control
= talloc_get_type(req_control
->data
, struct ldb_paged_control
);
82 if (!rep_control
|| !paged_rep_control
) {
83 if (paged_req_control
->cookie
) {
84 /* something wrong here - why give us a control back befre, but not one now? */
85 ldb_set_errstring(ldb
, "paged_searches: ERROR: We got back a control from a previous page, but this time no control was returned!");
86 return LDB_ERR_OPERATIONS_ERROR
;
88 /* No cookie received yet, valid to just return the full data set */
96 if (paged_rep_control
->cookie_len
== 0) {
102 /* more processing required */
103 /* let's fill in the request control with the new cookie */
104 /* if there's a reply control we must find a request
105 * control matching it */
107 if (paged_req_control
->cookie
) {
108 talloc_free(paged_req_control
->cookie
);
111 paged_req_control
->cookie
= talloc_memdup(req_control
,
112 paged_rep_control
->cookie
,
113 paged_rep_control
->cookie_len
);
114 paged_req_control
->cookie_len
= paged_rep_control
->cookie_len
;
120 static int store_referral(struct ps_context
*ac
, char *referral
)
122 ac
->saved_referrals
= talloc_realloc(ac
, ac
->saved_referrals
, char *, ac
->num_referrals
+ 2);
123 if (!ac
->saved_referrals
) {
124 return LDB_ERR_OPERATIONS_ERROR
;
127 ac
->saved_referrals
[ac
->num_referrals
] = talloc_strdup(ac
->saved_referrals
, referral
);
128 if (!ac
->saved_referrals
[ac
->num_referrals
]) {
129 return LDB_ERR_OPERATIONS_ERROR
;
133 ac
->saved_referrals
[ac
->num_referrals
] = NULL
;
138 static int send_referrals(struct ps_context
*ac
)
140 struct ldb_reply
*ares
;
144 for (i
= 0; i
< ac
->num_referrals
; i
++) {
145 ares
= talloc_zero(ac
->req
, struct ldb_reply
);
147 return LDB_ERR_OPERATIONS_ERROR
;
150 ares
->type
= LDB_REPLY_REFERRAL
;
151 ares
->referral
= ac
->saved_referrals
[i
];
153 ret
= ldb_module_send_referral(ac
->req
, ares
->referral
);
154 if (ret
!= LDB_SUCCESS
) {
162 static int ps_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
164 struct ps_context
*ac
;
167 ac
= talloc_get_type(req
->context
, struct ps_context
);
170 return ldb_module_done(ac
->req
, NULL
, NULL
,
171 LDB_ERR_OPERATIONS_ERROR
);
173 if (ares
->error
!= LDB_SUCCESS
) {
174 return ldb_module_done(ac
->req
, ares
->controls
,
175 ares
->response
, ares
->error
);
178 switch (ares
->type
) {
179 case LDB_REPLY_ENTRY
:
180 ret
= ldb_module_send_entry(ac
->req
, ares
->message
, ares
->controls
);
181 if (ret
!= LDB_SUCCESS
) {
182 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
186 case LDB_REPLY_REFERRAL
:
187 ret
= store_referral(ac
, ares
->referral
);
188 if (ret
!= LDB_SUCCESS
) {
189 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
195 ret
= check_ps_continuation(ac
, req
, ares
);
196 if (ret
!= LDB_SUCCESS
) {
197 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
202 ret
= ldb_next_request(ac
->module
, ac
->down_req
);
204 if (ret
!= LDB_SUCCESS
) {
205 return ldb_module_done(ac
->req
,
212 ret
= send_referrals(ac
);
213 if (ret
!= LDB_SUCCESS
) {
214 return ldb_module_done(ac
->req
,
218 /* send REPLY_DONE */
219 return ldb_module_done(ac
->req
, ares
->controls
,
220 ares
->response
, LDB_SUCCESS
);
229 static int ps_search(struct ldb_module
*module
, struct ldb_request
*req
)
231 struct ldb_context
*ldb
;
232 struct private_data
*private_data
;
233 struct ps_context
*ac
;
234 struct ldb_paged_control
*control
;
237 private_data
= talloc_get_type(ldb_module_get_private(module
), struct private_data
);
238 ldb
= ldb_module_get_ctx(module
);
240 /* check if paging is supported */
241 if (!private_data
|| !private_data
->paged_supported
) {
242 /* do not touch this request paged controls not
244 * are just not setup yet */
245 return ldb_next_request(module
, req
);
248 ac
= talloc_zero(req
, struct ps_context
);
251 return LDB_ERR_OPERATIONS_ERROR
;
257 ac
->saved_referrals
= NULL
;
258 ac
->num_referrals
= 0;
260 ldb
= ldb_module_get_ctx(ac
->module
);
262 control
= talloc(ac
, struct ldb_paged_control
);
264 return LDB_ERR_OPERATIONS_ERROR
;
267 control
->size
= PS_DEFAULT_PAGE_SIZE
;
268 control
->cookie
= NULL
;
269 control
->cookie_len
= 0;
271 ret
= ldb_build_search_req_ex(&ac
->down_req
, ldb
, ac
,
272 ac
->req
->op
.search
.base
,
273 ac
->req
->op
.search
.scope
,
274 ac
->req
->op
.search
.tree
,
275 ac
->req
->op
.search
.attrs
,
280 LDB_REQ_SET_LOCATION(ac
->down_req
);
281 if (ret
!= LDB_SUCCESS
) {
285 ret
= ldb_request_add_control(ac
->down_req
, LDB_CONTROL_PAGED_RESULTS_OID
,
287 if (ret
!= LDB_SUCCESS
) {
291 talloc_steal(ac
->down_req
, control
);
293 return ldb_next_request(ac
->module
, ac
->down_req
);
296 static int check_supported_paged(struct ldb_request
*req
,
297 struct ldb_reply
*ares
)
299 struct private_data
*data
;
301 data
= talloc_get_type(req
->context
, struct private_data
);
304 return ldb_request_done(req
, LDB_ERR_OPERATIONS_ERROR
);
306 if (ares
->error
!= LDB_SUCCESS
) {
307 return ldb_request_done(req
, LDB_ERR_OPERATIONS_ERROR
);
310 switch (ares
->type
) {
311 case LDB_REPLY_ENTRY
:
312 if (ldb_msg_check_string_attribute(ares
->message
,
314 LDB_CONTROL_PAGED_RESULTS_OID
)) {
315 data
->paged_supported
= true;
319 case LDB_REPLY_REFERRAL
:
324 return ldb_request_done(req
, LDB_SUCCESS
);
331 static int ps_init(struct ldb_module
*module
)
333 struct ldb_context
*ldb
;
334 static const char *attrs
[] = { "supportedControl", NULL
};
335 struct private_data
*data
;
338 struct ldb_request
*req
;
340 ldb
= ldb_module_get_ctx(module
);
342 data
= talloc(module
, struct private_data
);
345 return LDB_ERR_OPERATIONS_ERROR
;
347 data
->paged_supported
= false;
349 ldb_module_set_private(module
, data
);
351 base
= ldb_dn_new(module
, ldb
, "");
354 return LDB_ERR_OPERATIONS_ERROR
;
356 ret
= ldb_build_search_req(&req
, ldb
, module
,
357 base
, LDB_SCOPE_BASE
,
360 data
, check_supported_paged
,
362 LDB_REQ_SET_LOCATION(req
);
363 if (ret
!= LDB_SUCCESS
) {
367 ret
= ldb_next_request(module
, req
);
368 if (ret
== LDB_SUCCESS
) {
369 ret
= ldb_wait(req
->handle
, LDB_WAIT_ALL
);
371 if (ret
!= LDB_SUCCESS
) {
378 return ldb_next_init(module
);
381 static const struct ldb_module_ops ldb_paged_searches_module_ops
= {
382 .name
= "paged_searches",
384 .init_context
= ps_init
387 int ldb_paged_searches_init(const char *version
)
389 LDB_MODULE_CHECK_VERSION(version
);
390 return ldb_register_module(&ldb_paged_searches_module_ops
);