Line data Source code
1 : /*
2 : ldb database library
3 :
4 : Copyright (C) Andrew Bartlett 2007
5 :
6 : This program is free software; you can redistribute it and/or modify
7 : it under the terms of the GNU General Public License as published by
8 : the Free Software Foundation; either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : /*
21 : * Name: ldb
22 : *
23 : * Component: ldb ranged results module
24 : *
25 : * Description: munge AD-style 'ranged results' requests into
26 : * requests for all values in an attribute, then return the range to
27 : * the client.
28 : *
29 : * Author: Andrew Bartlett
30 : */
31 :
32 : #include "includes.h"
33 : #include "ldb_module.h"
34 :
35 : #undef strncasecmp
36 :
37 : struct rr_context {
38 : struct ldb_module *module;
39 : struct ldb_request *req;
40 : bool dirsync_in_use;
41 : };
42 :
43 12 : static struct rr_context *rr_init_context(struct ldb_module *module,
44 : struct ldb_request *req)
45 : {
46 12 : struct ldb_control *dirsync_control = NULL;
47 12 : struct rr_context *ac = talloc_zero(req, struct rr_context);
48 12 : if (ac == NULL) {
49 0 : ldb_set_errstring(ldb_module_get_ctx(module), "Out of Memory");
50 0 : return NULL;
51 : }
52 :
53 12 : ac->module = module;
54 12 : ac->req = req;
55 :
56 : /*
57 : * check if there's a dirsync control (as there is an
58 : * interaction between these modules)
59 : */
60 12 : dirsync_control = ldb_request_get_control(req,
61 : LDB_CONTROL_DIRSYNC_OID);
62 12 : if (dirsync_control != NULL) {
63 1 : ac->dirsync_in_use = true;
64 : }
65 :
66 12 : return ac;
67 : }
68 :
69 48 : static int rr_search_callback(struct ldb_request *req, struct ldb_reply *ares)
70 : {
71 0 : struct ldb_context *ldb;
72 0 : struct rr_context *ac;
73 0 : unsigned int i, j;
74 0 : TALLOC_CTX *temp_ctx;
75 :
76 48 : ac = talloc_get_type(req->context, struct rr_context);
77 48 : ldb = ldb_module_get_ctx(ac->module);
78 :
79 48 : if (!ares) {
80 0 : return ldb_module_done(ac->req, NULL, NULL,
81 : LDB_ERR_OPERATIONS_ERROR);
82 : }
83 48 : if (ares->error != LDB_SUCCESS) {
84 0 : return ldb_module_done(ac->req, ares->controls,
85 : ares->response, ares->error);
86 : }
87 :
88 48 : if (ares->type == LDB_REPLY_REFERRAL) {
89 27 : return ldb_module_send_referral(ac->req, ares->referral);
90 : }
91 :
92 21 : if (ares->type == LDB_REPLY_DONE) {
93 12 : return ldb_module_done(ac->req, ares->controls,
94 : ares->response, ares->error);
95 : }
96 :
97 9 : if (ac->dirsync_in_use) {
98 : /*
99 : * We return full attribute values when mixed with
100 : * dirsync
101 : */
102 1 : return ldb_module_send_entry(ac->req,
103 : ares->message,
104 : ares->controls);
105 : }
106 : /* LDB_REPLY_ENTRY */
107 :
108 8 : temp_ctx = talloc_new(ac->req);
109 8 : if (!temp_ctx) {
110 0 : ldb_module_oom(ac->module);
111 0 : return ldb_module_done(ac->req, NULL, NULL,
112 : LDB_ERR_OPERATIONS_ERROR);
113 : }
114 :
115 : /* Find those that are range requests from the attribute list */
116 16 : for (i = 0; ac->req->op.search.attrs[i]; i++) {
117 0 : char *p, *new_attr;
118 0 : const char *end_str;
119 0 : unsigned int start, end;
120 0 : struct ldb_message_element *el;
121 0 : struct ldb_val *orig_values;
122 :
123 8 : p = strchr(ac->req->op.search.attrs[i], ';');
124 8 : if (!p) {
125 0 : continue;
126 : }
127 8 : if (strncasecmp(p, ";range=", strlen(";range=")) != 0) {
128 0 : continue;
129 : }
130 8 : if (sscanf(p, ";range=%u-%u", &start, &end) != 2) {
131 1 : if (sscanf(p, ";range=%u-*", &start) == 1) {
132 1 : end = (unsigned int)-1;
133 : } else {
134 0 : continue;
135 : }
136 : }
137 8 : new_attr = talloc_strndup(temp_ctx,
138 8 : ac->req->op.search.attrs[i],
139 8 : (size_t)(p - ac->req->op.search.attrs[i]));
140 :
141 8 : if (!new_attr) {
142 0 : ldb_oom(ldb);
143 0 : return ldb_module_done(ac->req, NULL, NULL,
144 : LDB_ERR_OPERATIONS_ERROR);
145 : }
146 8 : el = ldb_msg_find_element(ares->message, new_attr);
147 8 : talloc_free(new_attr);
148 8 : if (!el) {
149 0 : continue;
150 : }
151 8 : if (end >= (el->num_values - 1)) {
152 : /* Need to leave the requested attribute in
153 : * there (so add an empty one to match) */
154 6 : end_str = "*";
155 6 : end = el->num_values - 1;
156 : } else {
157 2 : end_str = talloc_asprintf(temp_ctx, "%u", end);
158 2 : if (!end_str) {
159 0 : ldb_oom(ldb);
160 0 : return ldb_module_done(ac->req, NULL, NULL,
161 : LDB_ERR_OPERATIONS_ERROR);
162 : }
163 : }
164 : /* If start is greater then where we are find the end to be */
165 8 : if (start > end) {
166 1 : el->num_values = 0;
167 1 : el->values = NULL;
168 : } else {
169 7 : orig_values = el->values;
170 :
171 7 : if ((start + end < start) || (start + end < end)) {
172 0 : ldb_asprintf_errstring(ldb,
173 : "range request error: start or end would overflow!");
174 0 : return ldb_module_done(ac->req, NULL, NULL,
175 : LDB_ERR_UNWILLING_TO_PERFORM);
176 : }
177 :
178 7 : el->num_values = 0;
179 :
180 7 : el->values = talloc_array(ares->message->elements,
181 : struct ldb_val,
182 : (end - start) + 1);
183 7 : if (!el->values) {
184 0 : ldb_oom(ldb);
185 0 : return ldb_module_done(ac->req, NULL, NULL,
186 : LDB_ERR_OPERATIONS_ERROR);
187 : }
188 161 : for (j=start; j <= end; j++) {
189 154 : el->values[el->num_values] = orig_values[j];
190 154 : el->num_values++;
191 : }
192 : }
193 8 : el->name = talloc_asprintf(ares->message->elements,
194 : "%s;range=%u-%s", el->name, start,
195 : end_str);
196 8 : if (!el->name) {
197 0 : ldb_oom(ldb);
198 0 : return ldb_module_done(ac->req, NULL, NULL,
199 : LDB_ERR_OPERATIONS_ERROR);
200 : }
201 : }
202 :
203 8 : talloc_free(temp_ctx);
204 :
205 8 : return ldb_module_send_entry(ac->req, ares->message, ares->controls);
206 : }
207 :
208 : /* search */
209 19969960 : static int rr_search(struct ldb_module *module, struct ldb_request *req)
210 : {
211 1144549 : struct ldb_context *ldb;
212 1144549 : unsigned int i;
213 1144549 : unsigned int start, end;
214 19969960 : const char **new_attrs = NULL;
215 19969960 : bool found_rr = false;
216 1144549 : struct ldb_request *down_req;
217 1144549 : struct rr_context *ac;
218 1144549 : int ret;
219 :
220 19969960 : ldb = ldb_module_get_ctx(module);
221 :
222 : /* Strip the range request from the attribute */
223 66554508 : for (i = 0; req->op.search.attrs && req->op.search.attrs[i]; i++) {
224 1386388 : char *p;
225 45439999 : size_t range_len = strlen(";range=");
226 :
227 45439999 : new_attrs = talloc_realloc(req, new_attrs, const char *, i+2);
228 45439999 : new_attrs[i] = req->op.search.attrs[i];
229 45439999 : new_attrs[i+1] = NULL;
230 45439999 : p = strchr(new_attrs[i], ';');
231 45439999 : if (!p) {
232 45439987 : continue;
233 : }
234 12 : if (strncasecmp(p, ";range=", range_len) != 0) {
235 0 : continue;
236 : }
237 12 : end = (unsigned int)-1;
238 12 : if (sscanf(p + range_len, "%u-*", &start) != 1) {
239 0 : if (sscanf(p + range_len, "%u-%u", &start, &end) != 2) {
240 0 : ldb_asprintf_errstring(ldb,
241 : "range request error: "
242 : "range request malformed");
243 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
244 : }
245 : }
246 12 : if (start > end) {
247 0 : ldb_asprintf_errstring(ldb, "range request error: start must not be greater than end");
248 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
249 : }
250 :
251 12 : found_rr = true;
252 24 : new_attrs[i] = talloc_strndup(new_attrs, new_attrs[i],
253 12 : (size_t)(p - new_attrs[i]));
254 :
255 12 : if (!new_attrs[i]) {
256 0 : return ldb_oom(ldb);
257 : }
258 : }
259 :
260 19969960 : if (found_rr) {
261 12 : ac = rr_init_context(module, req);
262 12 : if (!ac) {
263 0 : return ldb_operr(ldb);
264 : }
265 :
266 12 : ret = ldb_build_search_req_ex(&down_req, ldb, ac,
267 : req->op.search.base,
268 : req->op.search.scope,
269 : req->op.search.tree,
270 : new_attrs,
271 : req->controls,
272 : ac, rr_search_callback,
273 : req);
274 12 : LDB_REQ_SET_LOCATION(down_req);
275 12 : if (ret != LDB_SUCCESS) {
276 0 : return ret;
277 : }
278 12 : return ldb_next_request(module, down_req);
279 : }
280 :
281 : /* No change, just run the original request as if we were never here */
282 19969948 : talloc_free(new_attrs);
283 19969948 : return ldb_next_request(module, req);
284 : }
285 :
286 : static const struct ldb_module_ops ldb_ranged_results_module_ops = {
287 : .name = "ranged_results",
288 : .search = rr_search,
289 : };
290 :
291 6040 : int ldb_ranged_results_module_init(const char *version)
292 : {
293 6040 : LDB_MODULE_CHECK_VERSION(version);
294 6040 : return ldb_register_module(&ldb_ranged_results_module_ops);
295 : }
|