LCOV - code coverage report
Current view: top level - lib/ldb/modules - paged_searches.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 128 177 72.3 %
Date: 2024-04-21 15:09:00 Functions: 8 8 100.0 %

          Line data    Source code
       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 "replace.h"
      37             : #include "system/filesys.h"
      38             : #include "system/time.h"
      39             : #include "ldb_module.h"
      40             : 
      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 */
      44             : 
      45             : struct private_data {
      46             : 
      47             :         bool paged_supported;
      48             : };
      49             : 
      50             : struct ps_context {
      51             :         struct ldb_module *module;
      52             :         struct ldb_request *req;
      53             : 
      54             :         bool pending;
      55             : 
      56             :         char **saved_referrals;
      57             :         unsigned int num_referrals;
      58             : 
      59             :         struct ldb_request *down_req;
      60             : };
      61             : 
      62       71088 : static int check_ps_continuation(struct ps_context *ac, struct ldb_request *req, struct ldb_reply *ares)
      63             : {
      64           0 :         struct ldb_context *ldb;
      65           0 :         struct ldb_control *rep_control, *req_control;
      66       71088 :         struct ldb_paged_control *paged_rep_control = NULL, *paged_req_control = NULL;
      67       71088 :         ldb = ldb_module_get_ctx(ac->module);
      68             : 
      69       71088 :         rep_control = ldb_reply_get_control(ares, LDB_CONTROL_PAGED_RESULTS_OID);
      70       71088 :         if (rep_control) {
      71       67232 :                 paged_rep_control = talloc_get_type(rep_control->data, struct ldb_paged_control);
      72             :         }
      73             : 
      74       71088 :         req_control = ldb_request_get_control(req, LDB_CONTROL_PAGED_RESULTS_OID);
      75       71088 :         if (req_control == NULL || req_control->data == NULL) {
      76           0 :                 ldb_set_errstring(ldb, "paged_searches: control is missing or malformed");
      77           0 :                 return LDB_ERR_OPERATIONS_ERROR;
      78             :         }
      79             : 
      80       71088 :         paged_req_control = talloc_get_type(req_control->data, struct ldb_paged_control);
      81             : 
      82       71088 :         if (!rep_control || !paged_rep_control) {
      83        3856 :                 if (paged_req_control->cookie) {
      84             :                         /* something wrong here - why give us a control back befre, but not one now? */
      85           0 :                         ldb_set_errstring(ldb, "paged_searches:  ERROR: We got back a control from a previous page, but this time no control was returned!");
      86           0 :                         return LDB_ERR_OPERATIONS_ERROR;
      87             :                 } else {
      88             :                         /* No cookie received yet, valid to just return the full data set */
      89             : 
      90             :                         /* we are done */
      91        3856 :                         ac->pending = false;
      92        3856 :                         return LDB_SUCCESS;
      93             :                 }
      94             :         }
      95             : 
      96       67232 :         if (paged_rep_control->cookie_len == 0) {
      97             :                 /* we are done */
      98       67149 :                 ac->pending = false;
      99       67149 :                 return LDB_SUCCESS;
     100             :         }
     101             : 
     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 */
     106             : 
     107          83 :         if (paged_req_control->cookie) {
     108          49 :                 talloc_free(paged_req_control->cookie);
     109             :         }
     110             : 
     111          83 :         paged_req_control->cookie = talloc_memdup(req_control,
     112             :                                                   paged_rep_control->cookie,
     113             :                                                   paged_rep_control->cookie_len);
     114          83 :         paged_req_control->cookie_len = paged_rep_control->cookie_len;
     115             : 
     116          83 :         ac->pending = true;
     117          83 :         return LDB_SUCCESS;
     118             : }
     119             : 
     120       36762 : static int store_referral(struct ps_context *ac, char *referral)
     121             : {
     122       36762 :         ac->saved_referrals = talloc_realloc(ac, ac->saved_referrals, char *, ac->num_referrals + 2);
     123       36762 :         if (!ac->saved_referrals) {
     124           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     125             :         }
     126             : 
     127       36762 :         ac->saved_referrals[ac->num_referrals] = talloc_strdup(ac->saved_referrals, referral);
     128       36762 :         if (!ac->saved_referrals[ac->num_referrals]) {
     129           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     130             :         }
     131             : 
     132       36762 :         ac->num_referrals++;
     133       36762 :         ac->saved_referrals[ac->num_referrals] = NULL;
     134             : 
     135       36762 :         return LDB_SUCCESS;
     136             : }
     137             : 
     138       71005 : static int send_referrals(struct ps_context *ac)
     139             : {
     140           0 :         struct ldb_reply *ares;
     141           0 :         int ret;
     142           0 :         unsigned int i;
     143             : 
     144      107767 :         for (i = 0; i < ac->num_referrals; i++) {
     145       36762 :                 ares = talloc_zero(ac->req, struct ldb_reply);
     146       36762 :                 if (!ares) {
     147           0 :                         return LDB_ERR_OPERATIONS_ERROR;
     148             :                 }
     149             : 
     150       36762 :                 ares->type = LDB_REPLY_REFERRAL;
     151       36762 :                 ares->referral = ac->saved_referrals[i];
     152             : 
     153       36762 :                 ret = ldb_module_send_referral(ac->req, ares->referral);
     154       36762 :                 if (ret != LDB_SUCCESS) {
     155           0 :                         return ret;
     156             :                 }
     157             :         }
     158             : 
     159       71005 :         return LDB_SUCCESS;
     160             : }
     161             : 
     162      232846 : static int ps_callback(struct ldb_request *req, struct ldb_reply *ares)
     163             : {
     164           0 :         struct ps_context *ac;
     165           0 :         int ret;
     166             : 
     167      232846 :         ac = talloc_get_type(req->context, struct ps_context);
     168             : 
     169      232846 :         if (!ares) {
     170           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     171             :                                         LDB_ERR_OPERATIONS_ERROR);
     172             :         }
     173      232846 :         if (ares->error != LDB_SUCCESS) {
     174         156 :                 return ldb_module_done(ac->req, ares->controls,
     175             :                                         ares->response, ares->error);
     176             :         }
     177             : 
     178      232690 :         switch (ares->type) {
     179      124840 :         case LDB_REPLY_ENTRY:
     180      124840 :                 ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
     181      124840 :                 if (ret != LDB_SUCCESS) {
     182           0 :                         return ldb_module_done(ac->req, NULL, NULL, ret);
     183             :                 }
     184      124840 :                 break;
     185             : 
     186       36762 :         case LDB_REPLY_REFERRAL:
     187       36762 :                 ret = store_referral(ac, ares->referral);
     188       36762 :                 if (ret != LDB_SUCCESS) {
     189           0 :                         return ldb_module_done(ac->req, NULL, NULL, ret);
     190             :                 }
     191       36762 :                 break;
     192             : 
     193       71088 :         case LDB_REPLY_DONE:
     194             : 
     195       71088 :                 ret = check_ps_continuation(ac, req, ares);
     196       71088 :                 if (ret != LDB_SUCCESS) {
     197           0 :                         return ldb_module_done(ac->req, NULL, NULL, ret);
     198             :                 }
     199             : 
     200       71088 :                 if (ac->pending) {
     201             : 
     202          83 :                         ret = ldb_next_request(ac->module, ac->down_req);
     203             : 
     204          83 :                         if (ret != LDB_SUCCESS) {
     205           0 :                                 return ldb_module_done(ac->req,
     206             :                                                         NULL, NULL, ret);
     207             :                         }
     208             : 
     209             :                 } else {
     210             : 
     211             :                         /* send referrals */
     212       71005 :                         ret = send_referrals(ac);
     213       71005 :                         if (ret != LDB_SUCCESS) {
     214           0 :                                 return ldb_module_done(ac->req,
     215             :                                                         NULL, NULL, ret);
     216             :                         }
     217             : 
     218             :                         /* send REPLY_DONE */
     219       71005 :                         return ldb_module_done(ac->req, ares->controls,
     220             :                                                 ares->response, LDB_SUCCESS);
     221             :                 }
     222          83 :                 break;
     223             :         }
     224             : 
     225      161685 :         talloc_free(ares);
     226      161685 :         return LDB_SUCCESS;
     227             : }
     228             : 
     229       71161 : static int ps_search(struct ldb_module *module, struct ldb_request *req)
     230             : {
     231           0 :         struct ldb_context *ldb;
     232           0 :         struct private_data *private_data;
     233           0 :         struct ps_context *ac;
     234           0 :         struct ldb_paged_control *control;
     235           0 :         int ret;
     236             : 
     237       71161 :         private_data = talloc_get_type(ldb_module_get_private(module), struct private_data);
     238       71161 :         ldb = ldb_module_get_ctx(module);
     239             : 
     240             :         /* check if paging is supported */
     241       71161 :         if (!private_data || !private_data->paged_supported) {
     242             :                 /* do not touch this request paged controls not
     243             :                  * supported or we
     244             :                  * are just not setup yet */
     245           0 :                 return ldb_next_request(module, req);
     246             :         }
     247             : 
     248       71161 :         ac = talloc_zero(req, struct ps_context);
     249       71161 :         if (ac == NULL) {
     250           0 :                 ldb_oom(ldb);
     251           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     252             :         }
     253             : 
     254       71161 :         ac->module = module;
     255       71161 :         ac->req = req;
     256       71161 :         ac->pending = false;
     257       71161 :         ac->saved_referrals = NULL;
     258       71161 :         ac->num_referrals = 0;
     259             : 
     260       71161 :         ldb = ldb_module_get_ctx(ac->module);
     261             : 
     262       71161 :         control = talloc(ac, struct ldb_paged_control);
     263       71161 :         if (!control) {
     264           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     265             :         }
     266             : 
     267       71161 :         control->size = PS_DEFAULT_PAGE_SIZE;
     268       71161 :         control->cookie = NULL;
     269       71161 :         control->cookie_len = 0;
     270             : 
     271       71161 :         ret = ldb_build_search_req_ex(&ac->down_req, ldb, ac,
     272       71161 :                                         ac->req->op.search.base,
     273       71161 :                                         ac->req->op.search.scope,
     274       71161 :                                         ac->req->op.search.tree,
     275       71161 :                                         ac->req->op.search.attrs,
     276       71161 :                                         ac->req->controls,
     277             :                                         ac,
     278             :                                         ps_callback,
     279             :                                         ac->req);
     280       71161 :         LDB_REQ_SET_LOCATION(ac->down_req);
     281       71161 :         if (ret != LDB_SUCCESS) {
     282           0 :                 return ret;
     283             :         }
     284             : 
     285       71161 :         ret = ldb_request_add_control(ac->down_req, LDB_CONTROL_PAGED_RESULTS_OID,
     286             :                                       true, control);
     287       71161 :         if (ret != LDB_SUCCESS) {
     288           0 :                 return ret;
     289             :         }
     290             : 
     291       71161 :         talloc_steal(ac->down_req, control);
     292             : 
     293       71161 :         return ldb_next_request(ac->module, ac->down_req);
     294             : }
     295             : 
     296        3928 : static int check_supported_paged(struct ldb_request *req,
     297             :                                  struct ldb_reply *ares)
     298             : {
     299           0 :         struct private_data *data;
     300             : 
     301        3928 :         data = talloc_get_type(req->context, struct private_data);
     302             : 
     303        3928 :         if (!ares) {
     304           0 :                 return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
     305             :         }
     306        3928 :         if (ares->error != LDB_SUCCESS) {
     307           0 :                 return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
     308             :         }
     309             : 
     310        3928 :         switch (ares->type) {
     311        1964 :         case LDB_REPLY_ENTRY:
     312        1964 :                 if (ldb_msg_check_string_attribute(ares->message,
     313             :                                                    "supportedControl",
     314             :                                                    LDB_CONTROL_PAGED_RESULTS_OID)) {
     315        1964 :                         data->paged_supported = true;
     316             :                 }
     317        1964 :                 break;
     318             : 
     319           0 :         case LDB_REPLY_REFERRAL:
     320             :                 /* ignore */
     321           0 :                 break;
     322             : 
     323        1964 :         case LDB_REPLY_DONE:
     324        1964 :                 return ldb_request_done(req, LDB_SUCCESS);
     325             :         }
     326             : 
     327        1964 :         talloc_free(ares);
     328        1964 :         return LDB_SUCCESS;
     329             : }
     330             : 
     331        1964 : static int ps_init(struct ldb_module *module)
     332             : {
     333           0 :         struct ldb_context *ldb;
     334           0 :         static const char *attrs[] = { "supportedControl", NULL };
     335           0 :         struct private_data *data;
     336           0 :         struct ldb_dn *base;
     337           0 :         int ret;
     338           0 :         struct ldb_request *req;
     339             : 
     340        1964 :         ldb = ldb_module_get_ctx(module);
     341             : 
     342        1964 :         data = talloc(module, struct private_data);
     343        1964 :         if (data == NULL) {
     344           0 :                 ldb_oom(ldb);
     345           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     346             :         }
     347        1964 :         data->paged_supported = false;
     348             : 
     349        1964 :         ldb_module_set_private(module, data);
     350             : 
     351        1964 :         base = ldb_dn_new(module, ldb, "");
     352        1964 :         if (base == NULL) {
     353           0 :                 ldb_oom(ldb);
     354           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     355             :         }
     356        1964 :         ret = ldb_build_search_req(&req, ldb, module,
     357             :                                    base, LDB_SCOPE_BASE,
     358             :                                    "(objectClass=*)",
     359             :                                    attrs, NULL,
     360             :                                    data, check_supported_paged,
     361             :                                    NULL);
     362        1964 :         LDB_REQ_SET_LOCATION(req);
     363        1964 :         if (ret != LDB_SUCCESS) {
     364           0 :                 return ret;
     365             :         }
     366             : 
     367        1964 :         ret = ldb_next_request(module, req);
     368        1964 :         if (ret == LDB_SUCCESS) {
     369        1964 :                 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
     370             :         }
     371        1964 :         if (ret != LDB_SUCCESS) {
     372           0 :                 return ret;
     373             :         }
     374             : 
     375        1964 :         talloc_free(base);
     376        1964 :         talloc_free(req);
     377             : 
     378        1964 :         return ldb_next_init(module);
     379             : }
     380             : 
     381             : static const struct ldb_module_ops ldb_paged_searches_module_ops = {
     382             :         .name           = "paged_searches",
     383             :         .search         = ps_search,
     384             :         .init_context   = ps_init
     385             : };
     386             : 
     387        6091 : int ldb_paged_searches_init(const char *version)
     388             : {
     389        6091 :         LDB_MODULE_CHECK_VERSION(version);
     390        6091 :         return ldb_register_module(&ldb_paged_searches_module_ops);
     391             : }

Generated by: LCOV version 1.14