• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/samba-3.5.8/source4/lib/ldb/modules/
1/*
2   ldb database library
3
4   Copyright (C) Simo Sorce  2005-2008
5   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
6
7     ** NOTE! The following LGPL license applies to the ldb
8     ** library. This does NOT imply that all of Samba is released
9     ** under the LGPL
10
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.
15
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.
20
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/>.
23*/
24
25/*
26 *  Name: paged_searches
27 *
28 *  Component: ldb paged searches module
29 *
30 *  Description: this module detects if the remote ldap server supports
31 *               paged results and use them to transparently access all objects
32 *
33 *  Author: Simo Sorce
34 */
35
36#include "includes.h"
37#include "ldb_module.h"
38
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 */
42
43struct private_data {
44
45	bool paged_supported;
46};
47
48struct ps_context {
49	struct ldb_module *module;
50	struct ldb_request *req;
51
52	bool pending;
53
54	char **saved_referrals;
55	int num_referrals;
56
57	struct ldb_request *down_req;
58};
59
60static int check_ps_continuation(struct ps_context *ac, struct ldb_request *req, struct ldb_reply *ares)
61{
62	struct ldb_context *ldb;
63	struct ldb_control *rep_control, *req_control;
64	struct ldb_paged_control *paged_rep_control = NULL, *paged_req_control = NULL;
65	ldb = ldb_module_get_ctx(ac->module);
66
67	rep_control = ldb_reply_get_control(ares, LDB_CONTROL_PAGED_RESULTS_OID);
68	if (rep_control) {
69		paged_rep_control = talloc_get_type(rep_control->data, struct ldb_paged_control);
70	}
71
72	req_control = ldb_request_get_control(req, LDB_CONTROL_PAGED_RESULTS_OID);
73	paged_req_control = talloc_get_type(req_control->data, struct ldb_paged_control);
74
75	if (!rep_control || !paged_rep_control) {
76		if (paged_req_control->cookie) {
77			/* something wrong here - why give us a control back befre, but not one now? */
78			ldb_set_errstring(ldb, "paged_searches:  ERROR: We got back a control from a previous page, but this time no control was returned!");
79			return LDB_ERR_OPERATIONS_ERROR;
80		} else {
81			/* No cookie recived yet, valid to just return the full data set */
82
83			/* we are done */
84			ac->pending = false;
85			return LDB_SUCCESS;
86		}
87	}
88
89	if (paged_rep_control->cookie_len == 0) {
90		/* we are done */
91		ac->pending = false;
92		return LDB_SUCCESS;
93	}
94
95	/* more processing required */
96	/* let's fill in the request control with the new cookie */
97	/* if there's a reply control we must find a request
98	 * control matching it */
99
100	if (paged_req_control->cookie) {
101		talloc_free(paged_req_control->cookie);
102	}
103
104	paged_req_control->cookie = talloc_memdup(req_control,
105						  paged_rep_control->cookie,
106						  paged_rep_control->cookie_len);
107	paged_req_control->cookie_len = paged_rep_control->cookie_len;
108
109	ac->pending = true;
110	return LDB_SUCCESS;
111}
112
113static int store_referral(struct ps_context *ac, char *referral)
114{
115	ac->saved_referrals = talloc_realloc(ac, ac->saved_referrals, char *, ac->num_referrals + 2);
116	if (!ac->saved_referrals) {
117		return LDB_ERR_OPERATIONS_ERROR;
118	}
119
120	ac->saved_referrals[ac->num_referrals] = talloc_strdup(ac->saved_referrals, referral);
121	if (!ac->saved_referrals[ac->num_referrals]) {
122		return LDB_ERR_OPERATIONS_ERROR;
123	}
124
125	ac->num_referrals++;
126	ac->saved_referrals[ac->num_referrals] = NULL;
127
128	return LDB_SUCCESS;
129}
130
131static int send_referrals(struct ps_context *ac)
132{
133	struct ldb_reply *ares;
134	int ret;
135	int i;
136
137	for (i = 0; i < ac->num_referrals; i++) {
138		ares = talloc_zero(ac->req, struct ldb_reply);
139		if (!ares) {
140			return LDB_ERR_OPERATIONS_ERROR;
141		}
142
143		ares->type = LDB_REPLY_REFERRAL;
144		ares->referral = ac->saved_referrals[i];
145
146		ret = ldb_module_send_referral(ac->req, ares->referral);
147		if (ret != LDB_SUCCESS) {
148			return ret;
149		}
150	}
151
152	return LDB_SUCCESS;
153}
154
155static int ps_callback(struct ldb_request *req, struct ldb_reply *ares)
156{
157	struct ps_context *ac;
158	int ret;
159
160	ac = talloc_get_type(req->context, struct ps_context);
161
162	if (!ares) {
163		return ldb_module_done(ac->req, NULL, NULL,
164					LDB_ERR_OPERATIONS_ERROR);
165	}
166	if (ares->error != LDB_SUCCESS) {
167		return ldb_module_done(ac->req, ares->controls,
168					ares->response, ares->error);
169	}
170
171	switch (ares->type) {
172	case LDB_REPLY_ENTRY:
173		ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
174		if (ret != LDB_SUCCESS) {
175			return ldb_module_done(ac->req, NULL, NULL, ret);
176		}
177		break;
178
179	case LDB_REPLY_REFERRAL:
180		ret = store_referral(ac, ares->referral);
181		if (ret != LDB_SUCCESS) {
182			return ldb_module_done(ac->req, NULL, NULL, ret);
183		}
184		break;
185
186	case LDB_REPLY_DONE:
187
188		ret = check_ps_continuation(ac, req, ares);
189		if (ret != LDB_SUCCESS) {
190			return ldb_module_done(ac->req, NULL, NULL, ret);
191		}
192
193		if (ac->pending) {
194
195			ret = ldb_next_request(ac->module, ac->down_req);
196
197			if (ret != LDB_SUCCESS) {
198				return ldb_module_done(ac->req,
199							NULL, NULL, ret);
200			}
201
202		} else {
203
204			/* send referrals */
205			ret = send_referrals(ac);
206			if (ret != LDB_SUCCESS) {
207				return ldb_module_done(ac->req,
208							NULL, NULL, ret);
209			}
210
211			/* send REPLY_DONE */
212			return ldb_module_done(ac->req, ares->controls,
213						ares->response, LDB_SUCCESS);
214		}
215		break;
216	}
217
218	talloc_free(ares);
219	return LDB_SUCCESS;
220}
221
222static int ps_search(struct ldb_module *module, struct ldb_request *req)
223{
224	struct ldb_context *ldb;
225	struct private_data *private_data;
226	struct ps_context *ac;
227	struct ldb_paged_control *control;
228	int ret;
229
230	private_data = talloc_get_type(ldb_module_get_private(module), struct private_data);
231	ldb = ldb_module_get_ctx(module);
232
233	/* check if paging is supported */
234	if (!private_data || !private_data->paged_supported) {
235		/* do not touch this request paged controls not
236		 * supported or we
237		 * are just not setup yet */
238		return ldb_next_request(module, req);
239	}
240
241	ac = talloc_zero(req, struct ps_context);
242	if (ac == NULL) {
243		ldb_oom(ldb);
244		return LDB_ERR_OPERATIONS_ERROR;
245	}
246
247	ac->module = module;
248	ac->req = req;
249	ac->pending = false;
250	ac->saved_referrals = NULL;
251	ac->num_referrals = 0;
252
253	ldb = ldb_module_get_ctx(ac->module);
254
255	control = talloc(ac, struct ldb_paged_control);
256	if (!control) {
257		return LDB_ERR_OPERATIONS_ERROR;
258	}
259
260	control->size = PS_DEFAULT_PAGE_SIZE;
261	control->cookie = NULL;
262	control->cookie_len = 0;
263
264	ret = ldb_build_search_req_ex(&ac->down_req, ldb, ac,
265					ac->req->op.search.base,
266					ac->req->op.search.scope,
267					ac->req->op.search.tree,
268					ac->req->op.search.attrs,
269					ac->req->controls,
270					ac,
271					ps_callback,
272					ac->req);
273	if (ret != LDB_SUCCESS) {
274		return ret;
275	}
276
277	ret = ldb_request_add_control(ac->down_req, LDB_CONTROL_PAGED_RESULTS_OID,
278				      true, control);
279	if (ret != LDB_SUCCESS) {
280		return ret;
281	}
282
283	talloc_steal(ac->down_req, control);
284
285	return ldb_next_request(ac->module, ac->down_req);
286}
287
288static int check_supported_paged(struct ldb_request *req,
289				 struct ldb_reply *ares)
290{
291	struct private_data *data;
292
293	data = talloc_get_type(req->context, struct private_data);
294
295	if (!ares) {
296		return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
297	}
298	if (ares->error != LDB_SUCCESS) {
299		return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
300	}
301
302	switch (ares->type) {
303	case LDB_REPLY_ENTRY:
304		if (ldb_msg_check_string_attribute(ares->message,
305						   "supportedControl",
306						   LDB_CONTROL_PAGED_RESULTS_OID)) {
307			data->paged_supported = true;
308		}
309		break;
310
311	case LDB_REPLY_REFERRAL:
312		/* ignore */
313		break;
314
315	case LDB_REPLY_DONE:
316		return ldb_request_done(req, LDB_SUCCESS);
317	}
318
319	talloc_free(ares);
320	return LDB_SUCCESS;
321}
322
323static int ps_init(struct ldb_module *module)
324{
325	struct ldb_context *ldb;
326	static const char *attrs[] = { "supportedControl", NULL };
327	struct private_data *data;
328	struct ldb_dn *base;
329	int ret;
330	struct ldb_request *req;
331
332	ldb = ldb_module_get_ctx(module);
333
334	data = talloc(module, struct private_data);
335	if (data == NULL) {
336		ldb_oom(ldb);
337		return LDB_ERR_OPERATIONS_ERROR;
338	}
339	data->paged_supported = false;
340
341	ldb_module_set_private(module, data);
342
343	base = ldb_dn_new(module, ldb, "");
344	if (base == NULL) {
345		ldb_oom(ldb);
346		return LDB_ERR_OPERATIONS_ERROR;
347	}
348	ret = ldb_build_search_req(&req, ldb, module,
349				   base, LDB_SCOPE_BASE,
350				   "(objectClass=*)",
351				   attrs, NULL,
352				   data, check_supported_paged,
353				   NULL);
354	if (ret != LDB_SUCCESS) {
355		return ret;
356	}
357
358	ret = ldb_next_request(module, req);
359	if (ret == LDB_SUCCESS) {
360		ret = ldb_wait(req->handle, LDB_WAIT_ALL);
361	}
362	if (ret != LDB_SUCCESS) {
363		return ret;
364	}
365
366	talloc_free(base);
367	talloc_free(req);
368
369	return ldb_next_init(module);
370}
371
372_PUBLIC_ const struct ldb_module_ops ldb_paged_searches_module_ops = {
373	.name           = "paged_searches",
374	.search         = ps_search,
375	.init_context 	= ps_init
376};
377