Commit 8ca6626f authored by Quanah Gibson-Mount's avatar Quanah Gibson-Mount
Browse files

ITS#5191

Fix paged results control handling
parent c371349a
OpenLDAP 2.4 Change Log
OpenLDAP 2.4.7 Engineering
Fixed slapd paged results control handling (ITS#5191)
Documentation
Fixed grammar errors (ITS#5223)
Refint overlay doc contribution (ITS#5217)
......
......@@ -1123,12 +1123,7 @@ static int search_candidates(
static int
parse_paged_cookie( Operation *op, SlapReply *rs )
{
LDAPControl **c;
int rc = LDAP_SUCCESS;
ber_tag_t tag;
ber_int_t size;
BerElement *ber;
struct berval cookie = BER_BVNULL;
PagedResultsState *ps = op->o_pagedresults_state;
/* this function must be invoked only if the pagedResults
......@@ -1136,53 +1131,17 @@ parse_paged_cookie( Operation *op, SlapReply *rs )
* by the frontend */
assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
/* look for the appropriate ctrl structure */
for ( c = op->o_ctrls; c[0] != NULL; c++ ) {
if ( strcmp( c[0]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS ) == 0 )
{
break;
}
}
if ( c[0] == NULL ) {
rs->sr_text = "missing pagedResults control";
return LDAP_PROTOCOL_ERROR;
}
/* Tested by frontend */
assert( c[0]->ldctl_value.bv_len > 0 );
/* Parse the control value
* realSearchControlValue ::= SEQUENCE {
* size INTEGER (0..maxInt),
* -- requested page size from client
* -- result set size estimate from server
* cookie OCTET STRING
* }
*/
ber = ber_init( &c[0]->ldctl_value );
if ( ber == NULL ) {
rs->sr_text = "internal error";
return LDAP_OTHER;
}
tag = ber_scanf( ber, "{im}", &size, &cookie );
/* Tested by frontend */
assert( tag != LBER_ERROR );
assert( size >= 0 );
/* cookie decoding/checks deferred to backend... */
if ( cookie.bv_len ) {
if ( ps->ps_cookieval.bv_len ) {
PagedResultsCookie reqcookie;
if( cookie.bv_len != sizeof( reqcookie ) ) {
if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
/* bad cookie */
rs->sr_text = "paged results cookie is invalid";
rc = LDAP_PROTOCOL_ERROR;
goto done;
}
AC_MEMCPY( &reqcookie, cookie.bv_val, sizeof( reqcookie ));
AC_MEMCPY( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));
if ( reqcookie > ps->ps_cookie ) {
/* bad cookie */
......@@ -1198,22 +1157,11 @@ parse_paged_cookie( Operation *op, SlapReply *rs )
} else {
/* Initial request. Initialize state. */
#if 0
if ( op->o_conn->c_pagedresults_state.ps_cookie != 0 ) {
/* There's another pagedResults control on the
* same connection; reject new pagedResults controls
* (allowed by RFC2696) */
rs->sr_text = "paged results cookie unavailable; try later";
rc = LDAP_UNWILLING_TO_PERFORM;
goto done;
}
#endif
ps->ps_cookie = 0;
ps->ps_count = 0;
}
done:;
(void)ber_free( ber, 1 );
return rc;
}
......
......@@ -2668,12 +2668,7 @@ backsql_entry_release(
static int
parse_paged_cookie( Operation *op, SlapReply *rs )
{
LDAPControl **c;
int rc = LDAP_SUCCESS;
ber_tag_t tag;
ber_int_t size;
BerElement *ber;
struct berval cookie = BER_BVNULL;
PagedResultsState *ps = op->o_pagedresults_state;
/* this function must be invoked only if the pagedResults
......@@ -2681,53 +2676,17 @@ parse_paged_cookie( Operation *op, SlapReply *rs )
* by the frontend */
assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
/* look for the appropriate ctrl structure */
for ( c = op->o_ctrls; c[0] != NULL; c++ ) {
if ( strcmp( c[0]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS ) == 0 )
{
break;
}
}
if ( c[0] == NULL ) {
rs->sr_text = "missing pagedResults control";
return LDAP_PROTOCOL_ERROR;
}
/* Tested by frontend */
assert( c[0]->ldctl_value.bv_len > 0 );
/* Parse the control value
* realSearchControlValue ::= SEQUENCE {
* size INTEGER (0..maxInt),
* -- requested page size from client
* -- result set size estimate from server
* cookie OCTET STRING
* }
*/
ber = ber_init( &c[0]->ldctl_value );
if ( ber == NULL ) {
rs->sr_text = "internal error";
return LDAP_OTHER;
}
tag = ber_scanf( ber, "{im}", &size, &cookie );
/* Tested by frontend */
assert( tag != LBER_ERROR );
assert( size >= 0 );
/* cookie decoding/checks deferred to backend... */
if ( cookie.bv_len ) {
if ( ps->ps_cookieval.bv_len ) {
PagedResultsCookie reqcookie;
if( cookie.bv_len != sizeof( reqcookie ) ) {
if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
/* bad cookie */
rs->sr_text = "paged results cookie is invalid";
rc = LDAP_PROTOCOL_ERROR;
goto done;
}
AC_MEMCPY( &reqcookie, cookie.bv_val, sizeof( reqcookie ));
AC_MEMCPY( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));
if ( reqcookie > ps->ps_cookie ) {
/* bad cookie */
......@@ -2743,22 +2702,11 @@ parse_paged_cookie( Operation *op, SlapReply *rs )
} else {
/* Initial request. Initialize state. */
#if 0
if ( op->o_conn->c_pagedresults_state.ps_cookie != 0 ) {
/* There's another pagedResults control on the
* same connection; reject new pagedResults controls
* (allowed by RFC2696) */
rs->sr_text = "paged results cookie unavailable; try later";
rc = LDAP_UNWILLING_TO_PERFORM;
goto done;
}
#endif
ps->ps_cookie = 0;
ps->ps_count = 0;
}
done:;
(void)ber_free( ber, 1 );
return rc;
}
......
......@@ -161,6 +161,31 @@ glue_op_response ( Operation *op, SlapReply *rs )
if (!j) {
newctrls = ch_malloc((i+1)*sizeof(LDAPControl *));
} else {
/* Forget old pagedResults response if we're sending
* a new one now
*/
if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
int newpage = 0;
for ( k=0; k<i; k++ ) {
if ( !strcmp(rs->sr_ctrls[k]->ldctl_oid,
LDAP_CONTROL_PAGEDRESULTS )) {
newpage = 1;
break;
}
}
if ( newpage ) {
for ( k=0; k<j; k++ ) {
if ( !strcmp(gs->ctrls[k]->ldctl_oid,
LDAP_CONTROL_PAGEDRESULTS )) {
gs->ctrls[k]->ldctl_oid = NULL;
ldap_control_free( gs->ctrls[k] );
gs->ctrls[k] = gs->ctrls[--j];
gs->ctrls[j] = NULL;
break;
}
}
}
}
newctrls = ch_realloc(gs->ctrls,
(j+i+1)*sizeof(LDAPControl *));
}
......@@ -321,7 +346,7 @@ glue_op_search ( Operation *op, SlapReply *rs )
slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
glueinfo *gi = (glueinfo *)on->on_bi.bi_private;
BackendDB *b0 = op->o_bd;
BackendDB *b1 = NULL, *btmp;
BackendDB *btmp;
BackendInfo *bi0 = op->o_bd->bd_info;
int i;
long stoptime = 0, starttime;
......@@ -360,7 +385,6 @@ glue_op_search ( Operation *op, SlapReply *rs )
tlimit0 = op->ors_tlimit;
dn = op->o_req_dn;
ndn = op->o_req_ndn;
b1 = op->o_bd;
/*
* Execute in reverse order, most specific first
......@@ -375,9 +399,16 @@ glue_op_search ( Operation *op, SlapReply *rs )
}
if (!btmp || !btmp->be_search)
continue;
if (!dnIsSuffix(&btmp->be_nsuffix[0], &b1->be_nsuffix[0]))
if (!dnIsSuffix(&btmp->be_nsuffix[0], &b0->be_nsuffix[0]))
continue;
if (get_no_subordinate_glue(op) && btmp != b0)
continue;
if (get_no_subordinate_glue(op) && btmp != b1)
/* If we remembered which backend we were on before,
* skip down to it now
*/
if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED &&
op->o_conn->c_pagedresults_state.ps_be &&
op->o_conn->c_pagedresults_state.ps_be != btmp )
continue;
if (tlimit0 != SLAP_NO_LIMIT) {
......@@ -449,7 +480,41 @@ glue_op_search ( Operation *op, SlapReply *rs )
case LDAP_X_CANNOT_CHAIN:
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
goto end_of_loop;
case LDAP_SUCCESS:
if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
PagedResultsState *ps = op->o_pagedresults_state;
/* Assume this backend can be forgotten now */
op->o_conn->c_pagedresults_state.ps_be = NULL;
/* If we have a full page, exit the loop. We may
* need to remember this backend so we can continue
* from here on a subsequent request.
*/
if ( rs->sr_nentries >= ps->ps_size ) {
/* Don't bother to remember the first backend.
* Only remember the last one if there's more state left.
*/
if ( op->o_bd != b0 &&
( op->o_conn->c_pagedresults_state.ps_cookie ||
op->o_bd != gi->gi_n[0].gn_be ))
op->o_conn->c_pagedresults_state.ps_be = op->o_bd;
goto end_of_loop;
}
/* This backend has run out of entries, but more responses
* can fit in the page. Fake a reset of the state so the
* next backend will start up properly. Only back-[bh]db
* and back-sql look at this state info.
*/
if ( ps->ps_cookieval.bv_len == sizeof( PagedResultsCookie )) {
ps->ps_cookie = 0;
memset( ps->ps_cookieval.bv_val, 0,
sizeof( PagedResultsCookie ));
}
}
default:
break;
}
......
......@@ -1050,12 +1050,13 @@ static int parsePagedResults (
SlapReply *rs,
LDAPControl *ctrl )
{
BerElementBuffer berbuf;
BerElement *ber = (BerElement *)&berbuf;
struct berval cookie;
PagedResultsState *ps;
int rc = LDAP_SUCCESS;
ber_tag_t tag;
ber_int_t size;
BerElement *ber;
struct berval cookie = BER_BVNULL;
PagedResultsState *ps;
if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
rs->sr_text = "paged results control specified multiple times";
......@@ -1080,11 +1081,7 @@ static int parsePagedResults (
* cookie OCTET STRING
* }
*/
ber = ber_init( &ctrl->ldctl_value );
if ( ber == NULL ) {
rs->sr_text = "internal error";
return LDAP_OTHER;
}
ber_init2( ber, &ctrl->ldctl_value, LBER_USE_DER );
tag = ber_scanf( ber, "{im}", &size, &cookie );
......@@ -1103,6 +1100,7 @@ static int parsePagedResults (
ps = op->o_tmpalloc( sizeof(PagedResultsState), op->o_tmpmemctx );
*ps = op->o_conn->c_pagedresults_state;
ps->ps_size = size;
ps->ps_cookieval = cookie;
op->o_pagedresults_state = ps;
/* NOTE: according to RFC 2696 3.:
......@@ -1126,7 +1124,6 @@ static int parsePagedResults (
}
done:;
(void)ber_free( ber, 1 );
return rc;
}
......
......@@ -2330,8 +2330,9 @@ typedef unsigned long PagedResultsCookie;
typedef struct PagedResultsState {
Backend *ps_be;
ber_int_t ps_size;
PagedResultsCookie ps_cookie;
int ps_count;
PagedResultsCookie ps_cookie;
struct berval ps_cookieval;
} PagedResultsState;
struct slap_csn_entry {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment