From c354bb23bf78f414e1ffdfbb0173a042c112ec3e Mon Sep 17 00:00:00 2001 From: Pierangelo Masarati Date: Thu, 21 Nov 2002 02:16:53 +0000 Subject: [PATCH] Support for RFC 2696: LDAP Control Extension for Simple Paged Results Manipulation contributed by Lynn Moss (ITS#2189) applied with changes. --- clients/tools/ldapsearch.c | 223 +++++++++++++++++++++++++++++++- include/ldap.h | 2 +- servers/slapd/back-bdb/init.c | 3 + servers/slapd/back-bdb/search.c | 125 ++++++++++++++++++ servers/slapd/controls.c | 20 ++- servers/slapd/slap.h | 4 + 6 files changed, 368 insertions(+), 9 deletions(-) diff --git a/clients/tools/ldapsearch.c b/clients/tools/ldapsearch.c index 28e5325b11..241e9c8071 100644 --- a/clients/tools/ldapsearch.c +++ b/clients/tools/ldapsearch.c @@ -56,6 +56,9 @@ usage( const char *s ) " -b basedn base dn for search\n" " -E [!][=] search controls (! indicates criticality)\n" " [!]mv= (matched values filter)\n" +#ifdef LDAP_CONTROL_PAGEDRESULTS +" [!]pr= (paged results)\n" +#endif /* LDAP_CONTROL_PAGEDRESULTS */ #ifdef LDAP_CONTROL_SUBENTRIES " [!]subentries[=true|false] (subentries)\n" #endif @@ -150,6 +153,13 @@ static int dosearch LDAP_P(( struct timeval *timeout, int sizelimit )); +#ifdef LDAP_CONTROL_PAGEDRESULTS +static int parse_page_control( + LDAP *ld, + LDAPMessage *result, + struct berval *cookie ); +#endif /* LDAP_CONTROL_PAGEDRESULTS */ + static char *tmpdir = NULL; static char *urlpre = NULL; static char *prog = NULL; @@ -170,6 +180,17 @@ static char *sasl_secprops = NULL; static int use_tls = 0; static char *sortattr = NULL; static int verbose, not, includeufn, vals2tmp, ldif; +#ifdef LDAP_CONTROL_PAGEDRESULTS +static int pageSize; +static ber_int_t searchControlSize = 0; +static ber_int_t morePagedResults = 1; +static struct berval cookie = { 0, NULL }; +static int npagedresponses; +static int npagedentries; +static int npagedreferences; +static int npagedextended; +static int npagedpartial; +#endif /* LDAP_CONTROL_PAGEDRESULTS */ static void urlize(char *url) @@ -198,6 +219,11 @@ main( int argc, char **argv ) struct berval *bvalp = NULL; char *vrFilter = NULL, *control = NULL, *cvalue; char *pw_file = NULL; +#ifdef LDAP_CONTROL_PAGEDRESULTS + BerElement *pageber = NULL; + struct berval *bvalptr = NULL; + int num = 0, moreEntries, searchControlCrit = 0; +#endif /* LDAP_CONTROL_PAGEDRESULTS */ infile = NULL; @@ -205,6 +231,11 @@ main( int argc, char **argv ) subentries = valuesReturnFilter = attrsonly = manageDSAit = noop = ldif = want_bindpw = 0; +#ifdef LDAP_CONTROL_PAGEDRESULTS + npagedresponses = npagedentries = npagedreferences = + npagedextended = npagedpartial = 0; +#endif /* LDAP_CONTROL_PAGEDRESULTS */ + prog = lutil_progname( "ldapsearch", argc, argv ); lutil_log_initialize(argc, argv); @@ -302,6 +333,25 @@ main( int argc, char **argv ) version = LDAP_VERSION3; break; +#ifdef LDAP_CONTROL_PAGEDRESULTS + } else if ( strcasecmp( control, "pr" ) == 0 ) { + /* PagedResults control */ + if ( searchControlSize !=0 ) { + fprintf( stderr, "PagedResultsControl previously specified" ); + return EXIT_FAILURE; + } + + num = sscanf( cvalue, "%d", &pageSize ); + if ( num != 1 ) { + fprintf( stderr, "Invalid value for PagedResultsControl, %s.\n", cvalue); + return EXIT_FAILURE; + + } + searchControlSize = (ber_int_t)pageSize; + searchControlCrit = crit; + break; +#endif /* LDAP_CONTROL_PAGEDRESULTS */ + #ifdef LDAP_CONTROL_SUBENTRIES } else if ( strcasecmp( control, "subentries" ) == 0 ) { if( subentries ) { @@ -973,11 +1023,17 @@ main( int argc, char **argv ) } } +#ifdef LDAP_CONTROL_PAGEDRESULTS +getNextPage: + if ( manageDSAit || noop || valuesReturnFilter || searchControlSize ) { + int critical = 0; +#else /* !LDAP_CONTROL_PAGEDRESULTS */ if ( manageDSAit || noop || valuesReturnFilter ) { +#endif /* !LDAP_CONTROL_PAGEDRESULTS */ int err; int i=0; - LDAPControl c1,c2,c3,c4; - LDAPControl *ctrls[5]; + LDAPControl c1,c2,c3,c4,c5; + LDAPControl *ctrls[6]; if ( manageDSAit ) { ctrls[i++]=&c1; @@ -987,6 +1043,9 @@ main( int argc, char **argv ) c1.ldctl_value.bv_val = NULL; c1.ldctl_value.bv_len = 0; c1.ldctl_iscritical = manageDSAit > 1; +#ifdef LDAP_CONTROL_PAGEDRESULTS + if ( c1.ldctl_iscritical ) critical = 1; +#endif /* LDAP_CONTROL_PAGEDRESULTS */ } if ( noop ) { @@ -997,6 +1056,9 @@ main( int argc, char **argv ) c2.ldctl_value.bv_val = NULL; c2.ldctl_value.bv_len = 0; c2.ldctl_iscritical = noop > 1; +#ifdef LDAP_CONTROL_PAGEDRESULTS + if ( c2.ldctl_iscritical ) critical = 1; +#endif /* LDAP_CONTROL_PAGEDRESULTS */ } #ifdef LDAP_CONTROL_SUBENTRIES @@ -1006,6 +1068,9 @@ main( int argc, char **argv ) c3.ldctl_oid = LDAP_CONTROL_SUBENTRIES; c3.ldctl_iscritical = subentries < 1; +#ifdef LDAP_CONTROL_PAGEDRESULTS + if ( c3.ldctl_iscritical ) critical = 1; +#endif /* LDAP_CONTROL_PAGEDRESULTS */ if (( ber = ber_alloc_t(LBER_USE_DER)) == NULL ) { return EXIT_FAILURE; @@ -1032,6 +1097,9 @@ main( int argc, char **argv ) c4.ldctl_oid = LDAP_CONTROL_VALUESRETURNFILTER; c4.ldctl_iscritical = valuesReturnFilter > 1; +#ifdef LDAP_CONTROL_PAGEDRESULTS + if ( c4.ldctl_iscritical ) critical = 1; +#endif /* LDAP_CONTROL_PAGEDRESULTS */ if (( ber = ber_alloc_t(LBER_USE_DER)) == NULL ) { return EXIT_FAILURE; @@ -1050,11 +1118,46 @@ main( int argc, char **argv ) c4.ldctl_value=(*bvalp); } +#ifdef LDAP_CONTROL_PAGEDRESULTS + if ( searchControlSize ) { + if (( pageber = ber_alloc_t(LBER_USE_DER)) == NULL ) { + return EXIT_FAILURE; + } + + ber_printf( pageber, "{iO}", searchControlSize, &cookie ); + if ( ber_flatten( pageber, &bvalptr ) == LBER_ERROR) { + return EXIT_FAILURE; + } + + ctrls[i++]=&c5; + ctrls[i] = NULL; + + c5.ldctl_oid = LDAP_CONTROL_PAGEDRESULTS; + c5.ldctl_value = ( *bvalptr ); + c5.ldctl_iscritical = searchControlCrit; + if ( c5.ldctl_iscritical ) critical = 1; + } +#endif /* LDAP_CONTROL_PAGEDRESULTS */ + err = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, ctrls ); ber_bvfree(bvalp); ber_free( ber, 1 ); +#ifdef LDAP_CONTROL_PAGEDRESULTS + ber_free( pageber, 1 ); + ber_bvfree( bvalptr ); +#endif /* LDAP_CONTROL_PAGEDRESULTS */ +#ifdef LDAP_CONTROL_PAGEDRESULTS + if( err != LDAP_OPT_SUCCESS ) { + if ( critical ) { + fprintf( stderr, "Could not set controls\n"); + return EXIT_FAILURE; + } else { + fprintf( stderr, "Could not set critical controls\n" ); + } + } +#else /* !LDAP_CONTROL_PAGEDRESULTS */ if( err != LDAP_OPT_SUCCESS ) { fprintf( stderr, "Could not set %scontrols\n", (c1.ldctl_iscritical || c2.ldctl_iscritical) @@ -1063,6 +1166,7 @@ main( int argc, char **argv ) return EXIT_FAILURE; } } +#endif /* !LDAP_CONTROL_PAGEDRESULTS */ } if ( verbose ) { @@ -1149,6 +1253,22 @@ main( int argc, char **argv ) } } +#ifdef LDAP_CONTROL_PAGEDRESULTS + if ( ( searchControlSize != 0 ) && ( morePagedResults != 0 ) ) { + /* Loop to get the next pages when + * enter is pressed on the terminal. + */ + printf( "Press Enter for the next %d entries.\n", + (int)searchControlSize ); + moreEntries = getchar(); + while ( moreEntries != EOF && moreEntries != '\n' ) { + moreEntries = getchar(); + } + + goto getNextPage; + } +#endif /* LDAP_CONTROL_PAGEDRESULTS */ + ldap_unbind( ld ); return( rc ); } @@ -1262,6 +1382,11 @@ static int dosearch( case LDAP_RES_SEARCH_RESULT: rc = print_result( ld, msg, 1 ); +#ifdef LDAP_CONTROL_PAGEDRESULTS + if ( searchControlSize != 0 ) { + rc = parse_page_control( ld, msg, &cookie ); + } +#endif /* LDAP_CONTROL_PAGEDRESULTS */ goto done; } } @@ -1275,6 +1400,30 @@ static int dosearch( } done: +#ifdef LDAP_CONTROL_PAGEDRESULTS + if ( searchControlSize == 0 ) { + if ( ldif < 2 ) { + printf( "\n# numResponses: %d\n", nresponses ); + if( nentries ) printf( "# numEntries: %d\n", nentries ); + if( nextended ) printf( "# numExtended: %d\n", nextended ); + if( npartial ) printf( "# numPartial: %d\n", npartial ); + if( nreferences ) printf( "# numReferences: %d\n", nreferences ); + } + } else { + npagedresponses = npagedresponses + nresponses; + npagedentries = npagedentries + nentries; + npagedreferences = npagedreferences + nreferences; + npagedextended = npagedextended + nextended; + npagedpartial = npagedpartial + npartial; + if ( ( morePagedResults == 0 ) && ( ldif < 2 ) ) { + printf( "\n# numResponses: %d\n", npagedresponses ); + if( nentries ) printf( "# numEntries: %d\n", npagedentries ); + if( nextended ) printf( "# numExtended: %d\n", npagedextended ); + if( npartial ) printf( "# numPartial: %d\n", npagedpartial ); + if( nreferences ) printf( "# numReferences: %d\n", npagedreferences ); + } + } +#else /* !LDAP_CONTROL_PAGEDRESULTS */ if ( ldif < 2 ) { printf( "\n# numResponses: %d\n", nresponses ); if( nentries ) printf( "# numEntries: %d\n", nentries ); @@ -1282,6 +1431,7 @@ done: if( npartial ) printf( "# numPartial: %d\n", npartial ); if( nreferences ) printf( "# numReferences: %d\n", nreferences ); } +#endif /* LDAP_CONTROL_PAGEDRESULTS */ return( rc ); } @@ -1759,3 +1909,72 @@ write_ldif( int type, char *name, char *value, ber_len_t vallen ) return( 0 ); } + + +#ifdef LDAP_CONTROL_PAGEDRESULTS +static int +parse_page_control( + LDAP *ld, + LDAPMessage *result, + struct berval *cookie ) +{ + int rc; + int err; + LDAPControl **ctrl = NULL; + LDAPControl *ctrlp = NULL; + BerElement *ber; + ber_tag_t tag; + ber_int_t entriesLeft; + struct berval servercookie = { 0, NULL }; + + + rc = ldap_parse_result( ld, result, + &err, NULL, NULL, NULL, &ctrl, 0 ); + + if( rc != LDAP_SUCCESS ) { + ldap_perror(ld, "ldap_parse_result"); + exit( EXIT_FAILURE ); + } + + if ( err != LDAP_SUCCESS ) { + fprintf( stderr, "%s (%d)\n", ldap_err2string(err), err ); + } + + if( ctrl ) { + /* Parse the control value + * searchResult ::= SEQUENCE { + * size INTEGER (0..maxInt), + * -- result set size estimate from server - unused + * cookie OCTET STRING + */ + ctrlp = *ctrl; + ber = ber_init( &ctrlp->ldctl_value ); + if ( ber == NULL ) { + fprintf( stderr, "Internal error.\n" ); + return EXIT_FAILURE; + } + + tag = ber_scanf( ber, "{im}", &entriesLeft, &servercookie ); + ber_dupbv( cookie, &servercookie ); + (void) ber_free( ber, 1 ); + + if( tag == LBER_ERROR ) { + fprintf( stderr, "Paged results response control could not be decoded.\n" ); + return EXIT_FAILURE; + } + + if( entriesLeft < 0 ) { + fprintf( stderr, "Invalid entries estimate in paged results response.\n" ); + return EXIT_FAILURE; + } + + + ldap_controls_free( ctrl ); + } else { + morePagedResults = 0; + } + + return err; +} +#endif /* LDAP_CONTROL_PAGEDRESULTS */ + diff --git a/include/ldap.h b/include/ldap.h index e34b9c4568..d2dd256d2c 100644 --- a/include/ldap.h +++ b/include/ldap.h @@ -198,9 +198,9 @@ typedef struct ldapcontrol { #define LDAP_CONTROL_DUPENT_RESPONSE "2.16.840.1.113719.1.27.101.2" #define LDAP_CONTROL_DUPENT_ENTRY "2.16.840.1.113719.1.27.101.3" #define LDAP_CONTROL_DUPENT LDAP_CONTROL_DUPENT_REQUEST +#endif #define LDAP_CONTROL_PAGEDRESULTS "1.2.840.113556.1.4.319" -#endif /* #define LDAP_CLIENT_UPDATE 1 diff --git a/servers/slapd/back-bdb/init.c b/servers/slapd/back-bdb/init.c index ccfc59edb9..d4a183302c 100644 --- a/servers/slapd/back-bdb/init.c +++ b/servers/slapd/back-bdb/init.c @@ -507,6 +507,9 @@ bdb_initialize( #ifdef LDAP_CLIENT_UPDATE LDAP_CONTROL_CLIENT_UPDATE, #endif +#ifdef LDAP_CONTROL_PAGEDRESULTS + LDAP_CONTROL_PAGEDRESULTS, +#endif /* LDAP_CONTROL_PAGEDRESULTS */ NULL }; diff --git a/servers/slapd/back-bdb/search.c b/servers/slapd/back-bdb/search.c index 3ecc66bb92..3f87c6ec7b 100644 --- a/servers/slapd/back-bdb/search.c +++ b/servers/slapd/back-bdb/search.c @@ -26,6 +26,13 @@ static int search_candidates( int scope, int deref, ID *ids ); +#ifdef LDAP_CONTROL_PAGEDRESULTS +static void send_pagerequest_response( + Connection *conn, + Operation *op, + ID lastid, + int nentries ); +#endif /* LDAP_CONTROL_PAGEDRESULTS */ int bdb_search( @@ -55,6 +62,10 @@ bdb_search( struct berval realbase = { 0, NULL }; int nentries = 0; int manageDSAit; +#ifdef LDAP_CONTROL_PAGEDRESULTS + int pagedresults; + ID lastid = NOID; +#endif /* LDAP_CONTROL_PAGEDRESULTS */ #ifdef LDAP_CLIENT_UPDATE Filter lcupf, csnfnot, csnfeq, csnfand, csnfge; @@ -88,6 +99,10 @@ bdb_search( manageDSAit = get_manageDSAit( op ); +#ifdef LDAP_CONTROL_PAGEDRESULTS + pagedresults = get_pagedresults( op ); +#endif /* LDAP_CONTROL_PAGEDRESULTS */ + rc = LOCK_ID (bdb->bi_dbenv, &locker ); switch(rc) { case 0: @@ -338,6 +353,34 @@ dn2entry_retry: } } +#ifdef LDAP_CONTROL_PAGEDRESULTS + if ( pagedresults ) { + if ( op->o_pagedresults_state.ps_cookie == 0 ) { + id = 0; + } else { + for ( id = bdb_idl_first( candidates, &cursor ); + id != NOID && id <= (ID)( op->o_pagedresults_state.ps_cookie ); + id = bdb_idl_next( candidates, &cursor ) ); + } + if ( cursor == NOID ) { +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, RESULTS, + "bdb_search: no paged results candidates\n", + 0, 0, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, + "bdb_search: no paged results candidates\n", + 0, 0, 0 ); +#endif + send_pagerequest_response( conn, op, lastid, 0 ); + + rc = 1; + goto done; + } + goto loop_begin; + } +#endif /* LDAP_CONTROL_PAGEDRESULTS */ + #ifdef LDAP_CLIENT_UPDATE if ( op->o_clientupdate_type & SLAP_LCUP_SYNC ) { lcupf.f_choice = LDAP_FILTER_AND; @@ -369,8 +412,13 @@ dn2entry_retry: id != NOID; id = bdb_idl_next( candidates, &cursor ) ) { + int scopeok = 0; +#ifdef LDAP_CONTROL_PAGEDRESULTS +loop_begin: +#endif /* LDAP_CONTROL_PAGEDRESULTS */ + /* check for abandon */ if ( op->o_abandon ) { rc = 0; @@ -573,6 +621,15 @@ id2entry_retry: v2refs, NULL, nentries ); goto done; } +#ifdef LDAP_CONTROL_PAGEDRESULTS + if ( pagedresults ) { + if ( nentries >= op->o_pagedresults_size ) { + send_pagerequest_response( conn, op, lastid, nentries ); + goto done; + } + lastid = id; + } +#endif /* LDAP_CONTROL_PAGEDRESULTS */ if (e) { int result; @@ -1023,3 +1080,71 @@ static int search_candidates( return rc; } +#ifdef LDAP_CONTROL_PAGEDRESULTS +static void +send_pagerequest_response( + Connection *conn, + Operation *op, + ID lastid, + int nentries ) +{ + LDAPControl ctrl, *ctrls[2]; + BerElement *ber; + struct berval *bvalp, cookie = { 0, NULL }; + PagedResultsCookie respcookie; + +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ENTRY, + "send_pagerequest_response: lastid: (0x%08lx) " + "nentries: (0x%081x)\n", + lastid, nentries ); +#else + Debug(LDAP_DEBUG_ARGS, "send_pagerequest_response: lastid: (0x%08lx) " + "nentries: (0x%081x)\n", lastid, nentries, NULL ); +#endif + + ctrl.ldctl_value.bv_val = NULL; + ctrls[0] = &ctrl; + ctrls[1] = NULL; + + if (( ber = ber_alloc_t(LBER_USE_DER)) == NULL ) { + goto done; + } + + respcookie = ( PagedResultsCookie )lastid; + conn->c_pagedresults_state.ps_cookie = respcookie; + cookie.bv_len = sizeof( respcookie ); +#if 0 + cookie.bv_val = ber_memalloc( sizeof( respcookie ) ); + AC_MEMCPY( cookie.bv_val, &respcookie, sizeof( respcookie ) ); +#else + cookie.bv_val = (char *)&respcookie; +#endif +/* + conn->c_pagedresults_state.ps_cookie = cookie.bv_val; +*/ + + ber_printf( ber, "{iO}", 0, &cookie ); +#if 0 + ber_memfree( cookie.bv_val ); +#endif + + if ( ber_flatten( ber, &bvalp ) == LBER_ERROR ) { + goto done; + } + + ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS; + ctrls[0]->ldctl_value = ( *bvalp ); + ctrls[0]->ldctl_iscritical = 0; + + send_search_result( conn, op, + LDAP_SUCCESS, + NULL, NULL, NULL, ctrls, nentries ); + +done: + if ( ctrls[0]->ldctl_value.bv_val ) { + ch_free( ctrls[0]->ldctl_value.bv_val ); + } +} +#endif /* LDAP_CONTROL_PAGEDRESULTS */ + diff --git a/servers/slapd/controls.c b/servers/slapd/controls.c index acd9f6949f..b8a2011112 100644 --- a/servers/slapd/controls.c +++ b/servers/slapd/controls.c @@ -76,11 +76,11 @@ static struct slap_control { SLAP_CTRL_ACCESS, NULL, parseNoOp }, #endif -#ifdef LDAP_CONTROL_PAGEDRESULTS_REQUEST - { LDAP_CONTROL_PAGEDRESULTS_REQUEST, +#ifdef LDAP_CONTROL_PAGEDRESULTS + { LDAP_CONTROL_PAGEDRESULTS, SLAP_CTRL_SEARCH, NULL, parsePagedResults }, -#endif +#endif /* LDAP_CONTROL_PAGEDRESULTS */ #ifdef LDAP_CONTROL_VALUESRETURNFILTER { LDAP_CONTROL_VALUESRETURNFILTER, SLAP_CTRL_SEARCH, NULL, @@ -155,7 +155,12 @@ int get_ctrls( } /* one for first control, one for termination */ +#ifndef LDAP_CONTROL_PAGEDRESULTS op->o_ctrls = ch_malloc( 2 * sizeof(LDAPControl *) ); +#else /* LDAP_CONTROL_PAGEDRESULTS */ + /* FIXME: are we sure we need this? */ + op->o_ctrls = ch_malloc( 3 * sizeof(LDAPControl *) ); +#endif /* LDAP_CONTROL_PAGEDRESULTS */ #if 0 if( op->ctrls == NULL ) { @@ -470,7 +475,7 @@ static int parseNoOp ( } #endif -#ifdef LDAP_CONTROL_PAGEDRESULTS_REQUEST +#ifdef LDAP_CONTROL_PAGEDRESULTS static int parsePagedResults ( Connection *conn, Operation *op, @@ -537,9 +542,12 @@ static int parsePagedResults ( *text = "paged results cookie is invalid or old"; return LDAP_UNWILLING_TO_PERFORM; } + } else { + /* Initial request. Initialize state. */ + op->o_pagedresults_state.ps_cookie = 0; + op->o_pagedresults_state.ps_id = NOID; } - op->o_pagedresults_state.ps_cookie = op->o_opid; op->o_pagedresults_size = size; op->o_pagedresults = ctrl->ldctl_iscritical @@ -548,7 +556,7 @@ static int parsePagedResults ( return LDAP_SUCCESS; } -#endif +#endif /* LDAP_CONTROL_PAGEDRESULTS */ #ifdef LDAP_CONTROL_VALUESRETURNFILTER int parseValuesReturnFilter ( diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h index 71213fbe8f..4510edfa3b 100644 --- a/servers/slapd/slap.h +++ b/servers/slapd/slap.h @@ -1681,6 +1681,10 @@ typedef struct slap_op { #define get_manageDSAit(op) ((int)(op)->o_managedsait) #define get_subentries(op) ((int)(op)->o_subentries) #define get_subentries_visibility(op) ((int)(op)->o_subentries_visibility) +#ifdef LDAP_CONTROL_PAGEDRESULTS +#define get_pagedresults(op) ((int)(op)->o_pagedresults) +#endif /* LDAP_CONTROL_PAGEDRESULTS */ + /* * Caches the result of a backend_group check for ACL evaluation