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 35struct rr_context { 36 struct ldb_module *module; 37 struct ldb_request *req; 38}; 39 40static struct rr_context *rr_init_context(struct ldb_module *module, 41 struct ldb_request *req) 42{ 43 struct rr_context *ac; 44 45 ac = talloc_zero(req, struct rr_context); 46 if (ac == NULL) { 47 ldb_set_errstring(ldb_module_get_ctx(module), "Out of Memory"); 48 return NULL; 49 } 50 51 ac->module = module; 52 ac->req = req; 53 54 return ac; 55} 56 57static int rr_search_callback(struct ldb_request *req, struct ldb_reply *ares) 58{ 59 struct ldb_context *ldb; 60 struct rr_context *ac; 61 int i, j; 62 63 ac = talloc_get_type(req->context, struct rr_context); 64 ldb = ldb_module_get_ctx(ac->module); 65 66 if (!ares) { 67 return ldb_module_done(ac->req, NULL, NULL, 68 LDB_ERR_OPERATIONS_ERROR); 69 } 70 if (ares->error != LDB_SUCCESS) { 71 return ldb_module_done(ac->req, ares->controls, 72 ares->response, ares->error); 73 } 74 75 if (ares->type == LDB_REPLY_REFERRAL) { 76 return ldb_module_send_referral(ac->req, ares->referral); 77 } 78 79 if (ares->type == LDB_REPLY_DONE) { 80 return ldb_module_done(ac->req, ares->controls, 81 ares->response, ares->error); 82 } 83 84 /* LDB_REPLY_ENTRY */ 85 86 /* Find those that are range requests from the attribute list */ 87 for (i = 0; ac->req->op.search.attrs[i]; i++) { 88 char *p, *new_attr; 89 const char *end_str; 90 unsigned int start, end, orig_num_values; 91 struct ldb_message_element *el; 92 struct ldb_val *orig_values; 93 p = strchr(ac->req->op.search.attrs[i], ';'); 94 if (!p) { 95 continue; 96 } 97 if (strncasecmp(p, ";range=", strlen(";range=")) != 0) { 98 continue; 99 } 100 if (sscanf(p, ";range=%u-%u", &start, &end) != 2) { 101 if (sscanf(p, ";range=%u-*", &start) == 1) { 102 end = (unsigned int)-1; 103 } else { 104 continue; 105 } 106 } 107 new_attr = talloc_strndup(ac->req, 108 ac->req->op.search.attrs[i], 109 (size_t)(p - ac->req->op.search.attrs[i])); 110 111 if (!new_attr) { 112 ldb_oom(ldb); 113 return ldb_module_done(ac->req, NULL, NULL, 114 LDB_ERR_OPERATIONS_ERROR); 115 } 116 el = ldb_msg_find_element(ares->message, new_attr); 117 talloc_free(new_attr); 118 if (!el) { 119 continue; 120 } 121 if (end >= (el->num_values - 1)) { 122 /* Need to leave the requested attribute in 123 * there (so add an empty one to match) */ 124 end_str = "*"; 125 end = el->num_values - 1; 126 } else { 127 end_str = talloc_asprintf(el, "%u", end); 128 if (!end_str) { 129 ldb_oom(ldb); 130 return ldb_module_done(ac->req, NULL, NULL, 131 LDB_ERR_OPERATIONS_ERROR); 132 } 133 } 134 /* If start is greater then where we are find the end to be */ 135 if (start > end) { 136 el->num_values = 0; 137 el->values = NULL; 138 } else { 139 orig_values = el->values; 140 orig_num_values = el->num_values; 141 142 if ((start + end < start) || (start + end < end)) { 143 ldb_asprintf_errstring(ldb, 144 "range request error: start or end would overflow!"); 145 return ldb_module_done(ac->req, NULL, NULL, 146 LDB_ERR_UNWILLING_TO_PERFORM); 147 } 148 149 el->num_values = 0; 150 151 el->values = talloc_array(el, struct ldb_val, (end - start) + 1); 152 if (!el->values) { 153 ldb_oom(ldb); 154 return ldb_module_done(ac->req, NULL, NULL, 155 LDB_ERR_OPERATIONS_ERROR); 156 } 157 for (j=start; j <= end; j++) { 158 el->values[el->num_values] = orig_values[j]; 159 el->num_values++; 160 } 161 } 162 el->name = talloc_asprintf(el, "%s;range=%u-%s", el->name, start, end_str); 163 if (!el->name) { 164 ldb_oom(ldb); 165 return ldb_module_done(ac->req, NULL, NULL, 166 LDB_ERR_OPERATIONS_ERROR); 167 } 168 } 169 170 return ldb_module_send_entry(ac->req, ares->message, ares->controls); 171} 172 173/* search */ 174static int rr_search(struct ldb_module *module, struct ldb_request *req) 175{ 176 struct ldb_context *ldb; 177 int i; 178 unsigned int start, end; 179 const char **new_attrs = NULL; 180 bool found_rr = false; 181 struct ldb_request *down_req; 182 struct rr_context *ac; 183 int ret; 184 185 ldb = ldb_module_get_ctx(module); 186 187 /* Strip the range request from the attribute */ 188 for (i = 0; req->op.search.attrs && req->op.search.attrs[i]; i++) { 189 char *p; 190 new_attrs = talloc_realloc(req, new_attrs, const char *, i+2); 191 new_attrs[i] = req->op.search.attrs[i]; 192 new_attrs[i+1] = NULL; 193 p = strchr(new_attrs[i], ';'); 194 if (!p) { 195 continue; 196 } 197 if (strncasecmp(p, ";range=", strlen(";range=")) != 0) { 198 continue; 199 } 200 end = (unsigned int)-1; 201 if (sscanf(p, ";range=%u-*", &start) != 1) { 202 if (sscanf(p, ";range=%u-%u", &start, &end) != 2) { 203 ldb_asprintf_errstring(ldb, 204 "range request error: " 205 "range request malformed"); 206 return LDB_ERR_UNWILLING_TO_PERFORM; 207 } 208 } 209 if (start > end) { 210 ldb_asprintf_errstring(ldb, "range request error: start must not be greater than end"); 211 return LDB_ERR_UNWILLING_TO_PERFORM; 212 } 213 214 found_rr = true; 215 new_attrs[i] = talloc_strndup(new_attrs, new_attrs[i], 216 (size_t)(p - new_attrs[i])); 217 218 if (!new_attrs[i]) { 219 ldb_oom(ldb); 220 return LDB_ERR_OPERATIONS_ERROR; 221 } 222 } 223 224 if (found_rr) { 225 ac = rr_init_context(module, req); 226 if (!ac) { 227 return LDB_ERR_OPERATIONS_ERROR; 228 } 229 230 ret = ldb_build_search_req_ex(&down_req, ldb, ac, 231 req->op.search.base, 232 req->op.search.scope, 233 req->op.search.tree, 234 new_attrs, 235 req->controls, 236 ac, rr_search_callback, 237 req); 238 if (ret != LDB_SUCCESS) { 239 return ret; 240 } 241 return ldb_next_request(module, down_req); 242 } 243 244 /* No change, just run the original request as if we were never here */ 245 talloc_free(new_attrs); 246 return ldb_next_request(module, req); 247} 248 249const struct ldb_module_ops ldb_ranged_results_module_ops = { 250 .name = "ranged_results", 251 .search = rr_search, 252}; 253