2006-01-06 07:01:23 +03:00
/*
ldb database library
2008-09-12 02:35:38 +04:00
Copyright ( C ) Simo Sorce 2005 - 2008
2006-01-06 07:01:23 +03:00
* * NOTE ! The following LGPL license applies to the ldb
* * library . This does NOT imply that all of Samba is released
* * under the LGPL
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation ; either
2007-07-10 06:46:15 +04:00
version 3 of the License , or ( at your option ) any later version .
2006-01-06 07:01:23 +03:00
This library is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public
2007-07-10 07:42:26 +04:00
License along with this library ; if not , see < http : //www.gnu.org/licenses/>.
2006-01-06 07:01:23 +03:00
*/
/*
2006-03-10 18:28:25 +03:00
* Name : paged_result
2006-01-06 07:01:23 +03:00
*
* Component : ldb paged results control module
*
* Description : this module caches a complete search and sends back
* results in chunks as asked by the client
*
* Author : Simo Sorce
*/
2010-11-01 15:36:42 +03:00
# include "replace.h"
# include "system/filesys.h"
# include "system/time.h"
2009-01-30 02:39:30 +03:00
# include "ldb_module.h"
2006-01-06 07:01:23 +03:00
2006-03-10 18:28:25 +03:00
struct message_store {
2006-07-22 20:56:33 +04:00
/* keep the whole ldb_reply as an optimization
2006-03-10 18:28:25 +03:00
* instead of freeing and talloc - ing the container
* on each result */
2006-07-22 20:56:33 +04:00
struct ldb_reply * r ;
2006-03-10 18:28:25 +03:00
struct message_store * next ;
} ;
2006-09-17 07:00:05 +04:00
struct private_data ;
2006-01-06 07:01:23 +03:00
struct results_store {
2006-09-17 07:00:05 +04:00
struct private_data * priv ;
2006-01-06 07:01:23 +03:00
char * cookie ;
time_t timestamp ;
2006-09-17 07:00:05 +04:00
2006-01-06 07:01:23 +03:00
struct results_store * next ;
2008-09-12 02:35:38 +04:00
2006-03-10 18:28:25 +03:00
struct message_store * first ;
struct message_store * last ;
int num_entries ;
struct message_store * first_ref ;
struct message_store * last_ref ;
struct ldb_control * * controls ;
2006-01-06 07:01:23 +03:00
} ;
struct private_data {
2016-01-26 03:34:58 +03:00
uint32_t next_free_id ;
2006-01-06 07:01:23 +03:00
struct results_store * store ;
} ;
2008-10-20 20:59:51 +04:00
static int store_destructor ( struct results_store * del )
2006-03-10 18:28:25 +03:00
{
2008-09-12 02:35:38 +04:00
struct private_data * priv = del - > priv ;
struct results_store * loop ;
if ( priv - > store = = del ) {
priv - > store = del - > next ;
return 0 ;
2006-03-10 18:28:25 +03:00
}
2006-09-17 07:00:05 +04:00
2008-09-12 02:35:38 +04:00
for ( loop = priv - > store ; loop ; loop = loop - > next ) {
if ( loop - > next = = del ) {
loop - > next = del - > next ;
return 0 ;
}
2006-09-17 07:00:05 +04:00
}
2008-09-12 02:35:38 +04:00
/* is not in list ? */
return - 1 ;
2006-03-10 18:28:25 +03:00
}
2006-01-06 07:01:23 +03:00
static struct results_store * new_store ( struct private_data * priv )
{
2006-08-17 05:52:24 +04:00
struct results_store * newr ;
2016-01-26 03:34:58 +03:00
uint32_t new_id = priv - > next_free_id + + ;
2006-01-06 07:01:23 +03:00
/* TODO: we should have a limit on the number of
* outstanding paged searches
*/
2006-08-17 05:52:24 +04:00
newr = talloc ( priv , struct results_store ) ;
if ( ! newr ) return NULL ;
2006-01-06 07:01:23 +03:00
2006-09-17 07:00:05 +04:00
newr - > priv = priv ;
2006-08-17 05:52:24 +04:00
newr - > cookie = talloc_asprintf ( newr , " %d " , new_id ) ;
if ( ! newr - > cookie ) {
talloc_free ( newr ) ;
2006-01-06 07:01:23 +03:00
return NULL ;
}
2006-08-17 05:52:24 +04:00
newr - > timestamp = time ( NULL ) ;
2006-01-06 07:01:23 +03:00
2006-08-17 05:52:24 +04:00
newr - > first = NULL ;
newr - > num_entries = 0 ;
newr - > first_ref = NULL ;
newr - > controls = NULL ;
2006-01-06 07:01:23 +03:00
2006-08-17 05:52:24 +04:00
newr - > next = priv - > store ;
priv - > store = newr ;
2006-01-06 07:01:23 +03:00
2006-08-17 05:52:24 +04:00
talloc_set_destructor ( newr , store_destructor ) ;
2006-01-06 07:01:23 +03:00
2006-08-17 05:52:24 +04:00
return newr ;
2006-01-06 07:01:23 +03:00
}
2006-07-22 20:56:33 +04:00
struct paged_context {
2006-03-14 00:05:55 +03:00
struct ldb_module * module ;
2008-09-12 02:35:38 +04:00
struct ldb_request * req ;
2006-03-14 00:05:55 +03:00
struct results_store * store ;
2008-09-12 02:35:38 +04:00
int size ;
struct ldb_control * * controls ;
2006-03-14 00:05:55 +03:00
} ;
2008-09-12 02:35:38 +04:00
static int paged_results ( struct paged_context * ac )
2006-03-14 00:05:55 +03:00
{
2008-09-12 02:35:38 +04:00
struct ldb_paged_control * paged ;
struct message_store * msg ;
2010-03-08 20:01:32 +03:00
unsigned int i , num_ctrls ;
int ret ;
2006-03-14 00:05:55 +03:00
2008-09-12 02:35:38 +04:00
if ( ac - > store = = NULL ) {
return LDB_ERR_OPERATIONS_ERROR ;
2006-03-14 00:05:55 +03:00
}
2008-09-12 02:35:38 +04:00
while ( ac - > store - > num_entries > 0 & & ac - > size > 0 ) {
msg = ac - > store - > first ;
2008-12-16 10:59:05 +03:00
ret = ldb_module_send_entry ( ac - > req , msg - > r - > message , msg - > r - > controls ) ;
2008-09-12 02:35:38 +04:00
if ( ret ! = LDB_SUCCESS ) {
return ret ;
}
2006-03-14 00:05:55 +03:00
2008-09-12 02:35:38 +04:00
ac - > store - > first = msg - > next ;
talloc_free ( msg ) ;
ac - > store - > num_entries - - ;
ac - > size - - ;
2006-03-14 00:05:55 +03:00
}
2008-09-12 02:35:38 +04:00
while ( ac - > store - > first_ref ! = NULL ) {
msg = ac - > store - > first_ref ;
ret = ldb_module_send_referral ( ac - > req , msg - > r - > referral ) ;
if ( ret ! = LDB_SUCCESS ) {
return ret ;
}
2006-03-14 00:05:55 +03:00
2008-09-12 02:35:38 +04:00
ac - > store - > first_ref = msg - > next ;
talloc_free ( msg ) ;
}
2006-03-14 00:05:55 +03:00
2008-09-12 02:35:38 +04:00
/* return result done */
num_ctrls = 1 ;
i = 0 ;
if ( ac - > store - > controls ! = NULL ) {
while ( ac - > store - > controls [ i ] ) i + + ; /* counting */
num_ctrls + = i ;
}
ac - > controls = talloc_array ( ac , struct ldb_control * , num_ctrls + 1 ) ;
if ( ac - > controls = = NULL ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
ac - > controls [ num_ctrls ] = NULL ;
for ( i = 0 ; i < ( num_ctrls - 1 ) ; i + + ) {
ac - > controls [ i ] = talloc_reference ( ac - > controls , ac - > store - > controls [ i ] ) ;
}
ac - > controls [ i ] = talloc ( ac - > controls , struct ldb_control ) ;
if ( ac - > controls [ i ] = = NULL ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
2006-03-14 00:05:55 +03:00
2008-09-12 02:35:38 +04:00
ac - > controls [ i ] - > oid = talloc_strdup ( ac - > controls [ i ] ,
LDB_CONTROL_PAGED_RESULTS_OID ) ;
if ( ac - > controls [ i ] - > oid = = NULL ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
ac - > controls [ i ] - > critical = 0 ;
paged = talloc ( ac - > controls [ i ] , struct ldb_paged_control ) ;
if ( paged = = NULL ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
ac - > controls [ i ] - > data = paged ;
if ( ac - > size > 0 ) {
paged - > size = 0 ;
paged - > cookie = NULL ;
paged - > cookie_len = 0 ;
} else {
paged - > size = ac - > store - > num_entries ;
paged - > cookie = talloc_strdup ( paged , ac - > store - > cookie ) ;
paged - > cookie_len = strlen ( paged - > cookie ) + 1 ;
}
return LDB_SUCCESS ;
2006-03-14 00:05:55 +03:00
}
2008-09-12 02:35:38 +04:00
static int paged_search_callback ( struct ldb_request * req , struct ldb_reply * ares )
2006-03-10 18:28:25 +03:00
{
2008-09-12 02:35:38 +04:00
struct paged_context * ac ;
struct message_store * msg_store ;
int ret ;
2006-03-10 18:28:25 +03:00
2008-09-12 02:35:38 +04:00
ac = talloc_get_type ( req - > context , struct paged_context ) ;
if ( ! ares ) {
return ldb_module_done ( ac - > req , NULL , NULL ,
LDB_ERR_OPERATIONS_ERROR ) ;
}
if ( ares - > error ! = LDB_SUCCESS ) {
return ldb_module_done ( ac - > req , ares - > controls ,
ares - > response , ares - > error ) ;
2006-03-10 18:28:25 +03:00
}
2008-09-12 02:35:38 +04:00
switch ( ares - > type ) {
case LDB_REPLY_ENTRY :
msg_store = talloc ( ac - > store , struct message_store ) ;
if ( msg_store = = NULL ) {
return ldb_module_done ( ac - > req , NULL , NULL ,
LDB_ERR_OPERATIONS_ERROR ) ;
}
msg_store - > next = NULL ;
msg_store - > r = talloc_steal ( msg_store , ares ) ;
2006-03-10 18:28:25 +03:00
if ( ac - > store - > first = = NULL ) {
2008-09-12 02:35:38 +04:00
ac - > store - > first = msg_store ;
2006-03-10 18:28:25 +03:00
} else {
2008-09-12 02:35:38 +04:00
ac - > store - > last - > next = msg_store ;
2006-03-10 18:28:25 +03:00
}
2008-09-12 02:35:38 +04:00
ac - > store - > last = msg_store ;
2006-03-10 18:28:25 +03:00
ac - > store - > num_entries + + ;
2008-09-12 02:35:38 +04:00
break ;
case LDB_REPLY_REFERRAL :
msg_store = talloc ( ac - > store , struct message_store ) ;
if ( msg_store = = NULL ) {
return ldb_module_done ( ac - > req , NULL , NULL ,
LDB_ERR_OPERATIONS_ERROR ) ;
}
msg_store - > next = NULL ;
msg_store - > r = talloc_steal ( msg_store , ares ) ;
2006-03-10 18:28:25 +03:00
if ( ac - > store - > first_ref = = NULL ) {
2008-09-12 02:35:38 +04:00
ac - > store - > first_ref = msg_store ;
2006-03-10 18:28:25 +03:00
} else {
2008-09-12 02:35:38 +04:00
ac - > store - > last_ref - > next = msg_store ;
2006-03-10 18:28:25 +03:00
}
2008-09-12 02:35:38 +04:00
ac - > store - > last_ref = msg_store ;
2006-03-10 18:28:25 +03:00
2008-09-12 02:35:38 +04:00
break ;
2006-03-10 18:28:25 +03:00
2008-09-12 02:35:38 +04:00
case LDB_REPLY_DONE :
2006-09-13 06:33:51 +04:00
ac - > store - > controls = talloc_move ( ac - > store , & ares - > controls ) ;
2008-09-12 02:35:38 +04:00
ret = paged_results ( ac ) ;
return ldb_module_done ( ac - > req , ac - > controls ,
ares - > response , ret ) ;
2006-03-10 18:28:25 +03:00
}
return LDB_SUCCESS ;
}
2006-05-29 05:30:02 +04:00
static int paged_search ( struct ldb_module * module , struct ldb_request * req )
2006-03-10 18:28:25 +03:00
{
2009-01-30 02:39:30 +03:00
struct ldb_context * ldb ;
2006-05-29 05:30:02 +04:00
struct ldb_control * control ;
struct private_data * private_data ;
2006-03-10 18:28:25 +03:00
struct ldb_paged_control * paged_ctrl ;
struct ldb_control * * saved_controls ;
2008-09-12 02:35:38 +04:00
struct ldb_request * search_req ;
2006-07-22 20:56:33 +04:00
struct paged_context * ac ;
2006-03-10 18:28:25 +03:00
int ret ;
2009-01-30 02:39:30 +03:00
ldb = ldb_module_get_ctx ( module ) ;
2006-05-29 05:30:02 +04:00
/* check if there's a paged request control */
2007-02-22 04:54:40 +03:00
control = ldb_request_get_control ( req , LDB_CONTROL_PAGED_RESULTS_OID ) ;
2006-05-29 05:30:02 +04:00
if ( control = = NULL ) {
/* not found go on */
return ldb_next_request ( module , req ) ;
}
2006-03-10 18:28:25 +03:00
paged_ctrl = talloc_get_type ( control - > data , struct ldb_paged_control ) ;
if ( ! paged_ctrl ) {
return LDB_ERR_PROTOCOL_ERROR ;
}
2009-01-30 02:39:30 +03:00
private_data = talloc_get_type ( ldb_module_get_private ( module ) ,
struct private_data ) ;
2008-09-12 02:35:38 +04:00
ac = talloc_zero ( req , struct paged_context ) ;
if ( ac = = NULL ) {
2009-01-30 02:39:30 +03:00
ldb_set_errstring ( ldb , " Out of Memory " ) ;
2006-03-10 18:28:25 +03:00
return LDB_ERR_OPERATIONS_ERROR ;
}
2008-09-12 02:35:38 +04:00
ac - > module = module ;
ac - > req = req ;
2006-03-10 18:28:25 +03:00
ac - > size = paged_ctrl - > size ;
2009-11-30 09:47:56 +03:00
if ( ac - > size < 0 ) {
/* apparently some clients send more than 2^31. This
violates the ldap standard , but we need to cope */
ac - > size = 0x7FFFFFFF ;
}
2006-03-10 18:28:25 +03:00
/* check if it is a continuation search the store */
if ( paged_ctrl - > cookie_len = = 0 ) {
2008-09-12 02:35:38 +04:00
if ( paged_ctrl - > size = = 0 ) {
return LDB_ERR_OPERATIONS_ERROR ;
2006-03-10 18:28:25 +03:00
}
2008-09-12 02:35:38 +04:00
ac - > store = new_store ( private_data ) ;
if ( ac - > store = = NULL ) {
2006-03-10 18:28:25 +03:00
return LDB_ERR_OPERATIONS_ERROR ;
2008-09-12 02:35:38 +04:00
}
2006-03-10 18:28:25 +03:00
2009-01-30 02:39:30 +03:00
ret = ldb_build_search_req_ex ( & search_req , ldb , ac ,
2008-09-12 02:35:38 +04:00
req - > op . search . base ,
req - > op . search . scope ,
req - > op . search . tree ,
req - > op . search . attrs ,
req - > controls ,
ac ,
paged_search_callback ,
req ) ;
2009-10-06 21:27:17 +04:00
if ( ret ! = LDB_SUCCESS ) {
return ret ;
}
2006-03-10 18:28:25 +03:00
/* save it locally and remove it from the list */
/* we do not need to replace them later as we
* are keeping the original req intact */
2010-12-18 01:00:46 +03:00
if ( ! ldb_save_controls ( control , search_req , & saved_controls ) ) {
2006-03-10 18:28:25 +03:00
return LDB_ERR_OPERATIONS_ERROR ;
}
2008-09-12 02:35:38 +04:00
return ldb_next_request ( module , search_req ) ;
2006-03-10 18:28:25 +03:00
} else {
struct results_store * current = NULL ;
2008-09-12 02:35:38 +04:00
/* TODO: age out old outstanding requests */
2006-03-10 18:28:25 +03:00
for ( current = private_data - > store ; current ; current = current - > next ) {
if ( strcmp ( current - > cookie , paged_ctrl - > cookie ) = = 0 ) {
current - > timestamp = time ( NULL ) ;
break ;
}
}
if ( current = = NULL ) {
return LDB_ERR_UNWILLING_TO_PERFORM ;
}
ac - > store = current ;
2008-09-12 02:35:38 +04:00
/* check if it is an abandon */
if ( ac - > size = = 0 ) {
return ldb_module_done ( req , NULL , NULL ,
LDB_SUCCESS ) ;
2006-03-10 18:28:25 +03:00
}
2008-09-12 02:35:38 +04:00
ret = paged_results ( ac ) ;
2006-03-10 18:28:25 +03:00
if ( ret ! = LDB_SUCCESS ) {
2008-09-12 02:35:38 +04:00
return ldb_module_done ( req , NULL , NULL , ret ) ;
2006-03-10 18:28:25 +03:00
}
2008-09-12 02:35:38 +04:00
return ldb_module_done ( req , ac - > controls , NULL ,
LDB_SUCCESS ) ;
2006-03-10 18:28:25 +03:00
}
2007-12-24 10:38:37 +03:00
}
2006-03-10 18:28:25 +03:00
2006-03-02 19:32:53 +03:00
static int paged_request_init ( struct ldb_module * module )
2006-01-06 19:12:45 +03:00
{
2009-01-30 02:39:30 +03:00
struct ldb_context * ldb ;
2006-03-02 19:32:53 +03:00
struct private_data * data ;
2006-03-08 04:01:14 +03:00
int ret ;
2006-03-02 19:32:53 +03:00
2009-01-30 02:39:30 +03:00
ldb = ldb_module_get_ctx ( module ) ;
2006-03-02 19:32:53 +03:00
data = talloc ( module , struct private_data ) ;
if ( data = = NULL ) {
return LDB_ERR_OTHER ;
}
2008-09-12 02:35:38 +04:00
2006-03-02 19:32:53 +03:00
data - > next_free_id = 1 ;
data - > store = NULL ;
2009-01-30 02:39:30 +03:00
ldb_module_set_private ( module , data ) ;
2006-01-06 19:12:45 +03:00
2008-09-12 02:35:38 +04:00
ret = ldb_mod_register_control ( module , LDB_CONTROL_PAGED_RESULTS_OID ) ;
2006-01-06 19:12:45 +03:00
if ( ret ! = LDB_SUCCESS ) {
2009-01-30 02:39:30 +03:00
ldb_debug ( ldb , LDB_DEBUG_WARNING ,
2009-05-27 00:22:13 +04:00
" paged_results: "
2009-07-11 00:44:27 +04:00
" Unable to register control with rootdse! " ) ;
2006-01-06 19:12:45 +03:00
}
2006-03-02 19:32:53 +03:00
return ldb_next_init ( module ) ;
2006-01-06 19:12:45 +03:00
}
2010-11-01 06:59:28 +03:00
static const struct ldb_module_ops ldb_paged_results_module_ops = {
2006-05-29 05:30:02 +04:00
. name = " paged_results " ,
. search = paged_search ,
2006-03-02 19:32:53 +03:00
. init_context = paged_request_init
2006-01-06 07:01:23 +03:00
} ;
2010-11-01 06:59:28 +03:00
int ldb_paged_results_init ( const char * version )
{
2010-11-01 14:30:23 +03:00
LDB_MODULE_CHECK_VERSION ( version ) ;
2010-11-01 06:59:28 +03:00
return ldb_register_module ( & ldb_paged_results_module_ops ) ;
}