2006-08-20 00:53:43 +04:00
/*
ldb database library
2008-09-12 02:35:38 +04:00
Copyright ( C ) Simo Sorce 2005 - 2008
2009-03-20 08:26:42 +03:00
Copyright ( C ) Andrew Bartlett < abartlet @ samba . org > 2009
2006-08-20 00:53:43 +04: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-08-20 00:53:43 +04: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-08-20 00:53:43 +04:00
*/
/*
* Name : paged_searches
*
* Component : ldb paged searches module
*
* Description : this module detects if the remote ldap server supports
* paged results and use them to transparently access all objects
*
* 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-08-20 00:53:43 +04:00
# define PS_DEFAULT_PAGE_SIZE 500
/* 500 objects per query seem to be a decent compromise
* the default AD limit per request is 1000 entries */
struct private_data {
bool paged_supported ;
} ;
struct ps_context {
struct ldb_module * module ;
2008-09-12 02:35:38 +04:00
struct ldb_request * req ;
2006-08-20 00:53:43 +04:00
bool pending ;
char * * saved_referrals ;
2010-03-08 20:01:32 +03:00
unsigned int num_referrals ;
2009-03-20 08:26:42 +03:00
struct ldb_request * down_req ;
2006-08-20 00:53:43 +04:00
} ;
2009-03-20 08:26:42 +03:00
static int check_ps_continuation ( struct ps_context * ac , struct ldb_request * req , struct ldb_reply * ares )
2006-08-20 00:53:43 +04:00
{
2009-03-20 08:26:42 +03:00
struct ldb_context * ldb ;
struct ldb_control * rep_control , * req_control ;
struct ldb_paged_control * paged_rep_control = NULL , * paged_req_control = NULL ;
ldb = ldb_module_get_ctx ( ac - > module ) ;
2006-08-20 00:53:43 +04:00
2009-03-20 08:26:42 +03:00
rep_control = ldb_reply_get_control ( ares , LDB_CONTROL_PAGED_RESULTS_OID ) ;
if ( rep_control ) {
paged_rep_control = talloc_get_type ( rep_control - > data , struct ldb_paged_control ) ;
}
2008-09-12 02:35:38 +04:00
2009-03-20 08:26:42 +03:00
req_control = ldb_request_get_control ( req , LDB_CONTROL_PAGED_RESULTS_OID ) ;
paged_req_control = talloc_get_type ( req_control - > data , struct ldb_paged_control ) ;
if ( ! rep_control | | ! paged_rep_control ) {
if ( paged_req_control - > cookie ) {
/* something wrong here - why give us a control back befre, but not one now? */
ldb_set_errstring ( ldb , " paged_searches: ERROR: We got back a control from a previous page, but this time no control was returned! " ) ;
return LDB_ERR_OPERATIONS_ERROR ;
} else {
2010-02-21 09:22:45 +03:00
/* No cookie received yet, valid to just return the full data set */
2009-03-20 08:26:42 +03:00
/* we are done */
ac - > pending = false ;
return LDB_SUCCESS ;
}
2006-08-20 00:53:43 +04:00
}
2009-03-20 08:26:42 +03:00
if ( paged_rep_control - > cookie_len = = 0 ) {
2006-08-20 00:53:43 +04:00
/* we are done */
2007-10-05 22:03:01 +04:00
ac - > pending = false ;
2006-09-21 08:52:49 +04:00
return LDB_SUCCESS ;
2006-08-20 00:53:43 +04:00
}
/* more processing required */
/* let's fill in the request control with the new cookie */
/* if there's a reply control we must find a request
* control matching it */
2009-03-20 08:26:42 +03:00
if ( paged_req_control - > cookie ) {
talloc_free ( paged_req_control - > cookie ) ;
2006-08-20 00:53:43 +04:00
}
2009-03-20 08:26:42 +03:00
paged_req_control - > cookie = talloc_memdup ( req_control ,
paged_rep_control - > cookie ,
paged_rep_control - > cookie_len ) ;
paged_req_control - > cookie_len = paged_rep_control - > cookie_len ;
2006-08-20 00:53:43 +04:00
2007-10-05 22:03:01 +04:00
ac - > pending = true ;
2006-09-21 08:52:49 +04:00
return LDB_SUCCESS ;
2006-08-20 00:53:43 +04:00
}
2008-09-12 02:35:38 +04:00
static int store_referral ( struct ps_context * ac , char * referral )
2006-08-20 00:53:43 +04:00
{
ac - > saved_referrals = talloc_realloc ( ac , ac - > saved_referrals , char * , ac - > num_referrals + 2 ) ;
if ( ! ac - > saved_referrals ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
ac - > saved_referrals [ ac - > num_referrals ] = talloc_strdup ( ac - > saved_referrals , referral ) ;
if ( ! ac - > saved_referrals [ ac - > num_referrals ] ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
ac - > num_referrals + + ;
ac - > saved_referrals [ ac - > num_referrals ] = NULL ;
return LDB_SUCCESS ;
}
2008-09-12 02:35:38 +04:00
static int send_referrals ( struct ps_context * ac )
2006-08-20 00:53:43 +04:00
{
struct ldb_reply * ares ;
2008-09-12 02:35:38 +04:00
int ret ;
2010-03-08 20:01:32 +03:00
unsigned int i ;
2006-08-20 00:53:43 +04:00
for ( i = 0 ; i < ac - > num_referrals ; i + + ) {
2008-09-12 02:35:38 +04:00
ares = talloc_zero ( ac - > req , struct ldb_reply ) ;
2006-08-20 00:53:43 +04:00
if ( ! ares ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
ares - > type = LDB_REPLY_REFERRAL ;
ares - > referral = ac - > saved_referrals [ i ] ;
2008-09-12 02:35:38 +04:00
ret = ldb_module_send_referral ( ac - > req , ares - > referral ) ;
if ( ret ! = LDB_SUCCESS ) {
return ret ;
}
2006-08-20 00:53:43 +04:00
}
return LDB_SUCCESS ;
}
2008-09-12 02:35:38 +04:00
static int ps_callback ( struct ldb_request * req , struct ldb_reply * ares )
2006-08-20 00:53:43 +04:00
{
2008-09-12 02:35:38 +04:00
struct ps_context * ac ;
int ret ;
2006-08-20 00:53:43 +04:00
2008-09-12 02:35:38 +04:00
ac = talloc_get_type ( req - > context , struct ps_context ) ;
2006-08-20 00:53:43 +04:00
2008-09-12 02:35:38 +04:00
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-08-20 00:53:43 +04:00
switch ( ares - > type ) {
case LDB_REPLY_ENTRY :
2008-12-16 10:59:05 +03:00
ret = ldb_module_send_entry ( ac - > req , ares - > message , ares - > controls ) ;
2008-09-12 02:35:38 +04:00
if ( ret ! = LDB_SUCCESS ) {
return ldb_module_done ( ac - > req , NULL , NULL , ret ) ;
}
2006-08-20 00:53:43 +04:00
break ;
case LDB_REPLY_REFERRAL :
2008-09-12 02:35:38 +04:00
ret = store_referral ( ac , ares - > referral ) ;
2006-08-20 00:53:43 +04:00
if ( ret ! = LDB_SUCCESS ) {
2008-09-12 02:35:38 +04:00
return ldb_module_done ( ac - > req , NULL , NULL , ret ) ;
2006-08-20 00:53:43 +04:00
}
break ;
case LDB_REPLY_DONE :
2008-09-12 02:35:38 +04:00
2009-03-20 08:26:42 +03:00
ret = check_ps_continuation ( ac , req , ares ) ;
2006-09-21 08:52:49 +04:00
if ( ret ! = LDB_SUCCESS ) {
2008-09-12 02:35:38 +04:00
return ldb_module_done ( ac - > req , NULL , NULL , ret ) ;
2006-08-20 00:53:43 +04:00
}
2008-09-12 02:35:38 +04:00
if ( ac - > pending ) {
2009-03-20 08:26:42 +03:00
ret = ldb_next_request ( ac - > module , ac - > down_req ) ;
2008-09-12 02:35:38 +04:00
if ( ret ! = LDB_SUCCESS ) {
return ldb_module_done ( ac - > req ,
NULL , NULL , ret ) ;
}
} else {
2006-08-20 00:53:43 +04:00
/* send referrals */
2008-09-12 02:35:38 +04:00
ret = send_referrals ( ac ) ;
2006-08-20 00:53:43 +04:00
if ( ret ! = LDB_SUCCESS ) {
2008-09-12 02:35:38 +04:00
return ldb_module_done ( ac - > req ,
NULL , NULL , ret ) ;
2006-08-20 00:53:43 +04:00
}
/* send REPLY_DONE */
2008-09-12 02:35:38 +04:00
return ldb_module_done ( ac - > req , ares - > controls ,
ares - > response , LDB_SUCCESS ) ;
2006-08-20 00:53:43 +04:00
}
break ;
}
talloc_free ( ares ) ;
2008-09-12 02:35:38 +04:00
return LDB_SUCCESS ;
2006-08-20 00:53:43 +04:00
}
static int ps_search ( struct ldb_module * module , struct ldb_request * req )
{
2009-01-30 02:39:30 +03:00
struct ldb_context * ldb ;
2006-08-20 00:53:43 +04:00
struct private_data * private_data ;
struct ps_context * ac ;
2009-03-20 08:26:42 +03:00
struct ldb_paged_control * control ;
int ret ;
2006-08-20 00:53:43 +04:00
2009-01-30 02:39:30 +03:00
private_data = talloc_get_type ( ldb_module_get_private ( module ) , struct private_data ) ;
ldb = ldb_module_get_ctx ( module ) ;
2006-08-20 00:53:43 +04:00
2009-03-20 08:45:52 +03:00
/* check if paging is supported */
if ( ! private_data | | ! private_data - > paged_supported ) {
2006-08-22 10:18:07 +04:00
/* do not touch this request paged controls not
2009-03-20 08:45:52 +03:00
* supported or we
2006-08-22 10:18:07 +04:00
* are just not setup yet */
2006-08-20 00:53:43 +04:00
return ldb_next_request ( module , req ) ;
}
2008-09-12 02:35:38 +04:00
ac = talloc_zero ( req , struct ps_context ) ;
if ( ac = = NULL ) {
2009-01-30 02:39:30 +03:00
ldb_oom ( ldb ) ;
2006-08-20 00:53:43 +04:00
return LDB_ERR_OPERATIONS_ERROR ;
}
2008-09-12 02:35:38 +04:00
ac - > module = module ;
ac - > req = req ;
ac - > pending = false ;
ac - > saved_referrals = NULL ;
ac - > num_referrals = 0 ;
2006-08-20 00:53:43 +04:00
2009-01-30 02:39:30 +03:00
ldb = ldb_module_get_ctx ( ac - > module ) ;
2009-03-20 08:26:42 +03:00
control = talloc ( ac , struct ldb_paged_control ) ;
2008-09-12 02:35:38 +04:00
if ( ! control ) {
return LDB_ERR_OPERATIONS_ERROR ;
2006-08-20 00:53:43 +04:00
}
2008-09-12 02:35:38 +04:00
control - > size = PS_DEFAULT_PAGE_SIZE ;
control - > cookie = NULL ;
control - > cookie_len = 0 ;
2006-08-20 00:53:43 +04:00
2009-03-20 08:26:42 +03:00
ret = ldb_build_search_req_ex ( & ac - > down_req , ldb , ac ,
2008-09-12 02:35:38 +04:00
ac - > req - > op . search . base ,
ac - > req - > op . search . scope ,
ac - > req - > op . search . tree ,
ac - > req - > op . search . attrs ,
2009-03-20 08:26:42 +03:00
ac - > req - > controls ,
2008-09-12 02:35:38 +04:00
ac ,
ps_callback ,
ac - > req ) ;
2010-10-18 15:13:20 +04:00
LDB_REQ_SET_LOCATION ( ac - > down_req ) ;
2006-08-20 00:53:43 +04:00
if ( ret ! = LDB_SUCCESS ) {
2008-09-12 02:35:38 +04:00
return ret ;
2006-08-20 00:53:43 +04:00
}
2009-03-20 08:26:42 +03:00
ret = ldb_request_add_control ( ac - > down_req , LDB_CONTROL_PAGED_RESULTS_OID ,
true , control ) ;
if ( ret ! = LDB_SUCCESS ) {
return ret ;
}
talloc_steal ( ac - > down_req , control ) ;
return ldb_next_request ( ac - > module , ac - > down_req ) ;
2006-08-20 00:53:43 +04:00
}
2008-09-12 02:35:38 +04:00
static int check_supported_paged ( struct ldb_request * req ,
struct ldb_reply * ares )
2006-08-20 00:53:43 +04:00
{
2008-09-12 02:35:38 +04:00
struct private_data * data ;
2006-08-20 00:53:43 +04:00
2008-09-12 02:35:38 +04:00
data = talloc_get_type ( req - > context , struct private_data ) ;
2007-12-24 10:38:37 +03:00
2008-09-12 02:35:38 +04:00
if ( ! ares ) {
return ldb_request_done ( req , LDB_ERR_OPERATIONS_ERROR ) ;
}
if ( ares - > error ! = LDB_SUCCESS ) {
return ldb_request_done ( req , LDB_ERR_OPERATIONS_ERROR ) ;
2006-08-20 00:53:43 +04:00
}
2008-09-12 02:35:38 +04:00
switch ( ares - > type ) {
case LDB_REPLY_ENTRY :
2006-09-21 08:52:49 +04:00
if ( ldb_msg_check_string_attribute ( ares - > message ,
" supportedControl " ,
LDB_CONTROL_PAGED_RESULTS_OID ) ) {
2007-10-05 22:03:01 +04:00
data - > paged_supported = true ;
2006-09-21 08:52:49 +04:00
}
2008-09-12 02:35:38 +04:00
break ;
case LDB_REPLY_REFERRAL :
/* ignore */
break ;
case LDB_REPLY_DONE :
return ldb_request_done ( req , LDB_SUCCESS ) ;
2006-09-21 08:52:49 +04:00
}
2008-09-12 02:35:38 +04:00
talloc_free ( ares ) ;
2006-09-21 08:52:49 +04:00
return LDB_SUCCESS ;
}
2006-08-20 00:53:43 +04:00
static int ps_init ( struct ldb_module * module )
{
2009-01-30 02:39:30 +03:00
struct ldb_context * ldb ;
2007-12-20 02:02:15 +03:00
static const char * attrs [ ] = { " supportedControl " , NULL } ;
2006-08-20 00:53:43 +04:00
struct private_data * data ;
2008-09-12 02:35:38 +04:00
struct ldb_dn * base ;
2006-08-20 00:53:43 +04:00
int ret ;
2006-09-21 08:52:49 +04:00
struct ldb_request * req ;
2006-08-20 00:53:43 +04:00
2009-01-30 02:39:30 +03:00
ldb = ldb_module_get_ctx ( module ) ;
2006-08-20 00:53:43 +04:00
data = talloc ( module , struct private_data ) ;
if ( data = = NULL ) {
2009-01-30 02:39:30 +03:00
ldb_oom ( ldb ) ;
2008-09-12 02:35:38 +04:00
return LDB_ERR_OPERATIONS_ERROR ;
2006-08-20 00:53:43 +04:00
}
2007-10-05 22:03:01 +04:00
data - > paged_supported = false ;
2006-09-21 08:52:49 +04:00
2009-01-30 02:39:30 +03:00
ldb_module_set_private ( module , data ) ;
base = ldb_dn_new ( module , ldb , " " ) ;
2008-09-12 02:35:38 +04:00
if ( base = = NULL ) {
2009-01-30 02:39:30 +03:00
ldb_oom ( ldb ) ;
2006-09-21 08:52:49 +04:00
return LDB_ERR_OPERATIONS_ERROR ;
2006-08-20 00:53:43 +04:00
}
2009-01-30 02:39:30 +03:00
ret = ldb_build_search_req ( & req , ldb , module ,
2008-09-12 02:35:38 +04:00
base , LDB_SCOPE_BASE ,
" (objectClass=*) " ,
attrs , NULL ,
data , check_supported_paged ,
NULL ) ;
2010-10-18 15:13:20 +04:00
LDB_REQ_SET_LOCATION ( req ) ;
2008-09-12 02:35:38 +04:00
if ( ret ! = LDB_SUCCESS ) {
return ret ;
2006-08-20 00:53:43 +04:00
}
2006-09-21 08:52:49 +04:00
ret = ldb_next_request ( module , req ) ;
if ( ret = = LDB_SUCCESS ) {
ret = ldb_wait ( req - > handle , LDB_WAIT_ALL ) ;
}
if ( ret ! = LDB_SUCCESS ) {
return ret ;
}
2006-08-20 00:53:43 +04:00
2008-09-12 02:35:38 +04:00
talloc_free ( base ) ;
talloc_free ( req ) ;
2006-08-20 00:53:43 +04:00
return ldb_next_init ( module ) ;
}
2010-11-01 06:59:28 +03:00
static const struct ldb_module_ops ldb_paged_searches_module_ops = {
2006-08-20 00:53:43 +04:00
. name = " paged_searches " ,
. search = ps_search ,
. init_context = ps_init
} ;
2010-11-01 06:59:28 +03:00
int ldb_paged_searches_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_searches_module_ops ) ;
}