From: Howard Chu Date: Mon, 25 Feb 2019 11:53:55 +0000 (+0000) Subject: ITS#8983 Add draft Persistent Search X-Git-Tag: OPENLDAP_REL_ENG_2_5_0ALPHA~67^2~100 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=06d289f985f06c80825dfe35d2bed36e4eb82ba8;p=thirdparty%2Fopenldap.git ITS#8983 Add draft Persistent Search --- diff --git a/clients/tools/common.c b/clients/tools/common.c index 4c1c4d774e..672bd07bb0 100644 --- a/clients/tools/common.c +++ b/clients/tools/common.c @@ -139,6 +139,7 @@ typedef int (*print_ctrl_fn)( LDAP *ld, LDAPControl *ctrl ); static int print_preread( LDAP *ld, LDAPControl *ctrl ); static int print_postread( LDAP *ld, LDAPControl *ctrl ); static int print_paged_results( LDAP *ld, LDAPControl *ctrl ); +static int print_psearch( LDAP *ld, LDAPControl *ctrl ); #ifdef LDAP_CONTROL_AUTHZID_RESPONSE static int print_authzid( LDAP *ld, LDAPControl *ctrl ); #endif @@ -167,6 +168,7 @@ static struct tool_ctrls_t { { LDAP_CONTROL_PRE_READ, TOOL_ALL, print_preread }, { LDAP_CONTROL_POST_READ, TOOL_ALL, print_postread }, { LDAP_CONTROL_PAGEDRESULTS, TOOL_SEARCH, print_paged_results }, + { LDAP_CONTROL_PERSIST_ENTRY_CHANGE_NOTICE, TOOL_SEARCH, print_psearch }, #ifdef LDAP_CONTROL_AUTHZID_RESPONSE /* this is generally deprecated in favor of LDAP WhoAmI? operation, hence only supported as a VC inner control */ { LDAP_CONTROL_AUTHZID_RESPONSE, TOOL_VC, print_authzid }, @@ -2161,6 +2163,60 @@ print_paged_results( LDAP *ld, LDAPControl *ctrl ) return 0; } +static int +print_psearch( LDAP *ld, LDAPControl *ctrl ) +{ + int rc; + int chgtype; + int chgpres; + long chgnum; + struct berval prevdn; + + rc = ldap_parse_entrychange_control( ld, ctrl, &chgtype, &prevdn, + &chgpres, &chgnum ); + if ( rc == LDAP_SUCCESS ) { + char buf[ BUFSIZ ]; + char *ptr = buf; + int blen = sizeof(buf), len; + + switch( chgtype ) { + case LDAP_CONTROL_PERSIST_ENTRY_CHANGE_ADD: + len = snprintf( ptr, blen, "add" ); + ptr += len; + blen -= len; + break; + case LDAP_CONTROL_PERSIST_ENTRY_CHANGE_DELETE: + len = snprintf( ptr, blen, "delete" ); + ptr += len; + blen -= len; + break; + case LDAP_CONTROL_PERSIST_ENTRY_CHANGE_MODIFY: + len = snprintf( ptr, blen, "modify" ); + ptr += len; + blen -= len; + break; + case LDAP_CONTROL_PERSIST_ENTRY_CHANGE_RENAME: + len = snprintf( ptr, blen, "moddn" ); + ptr += len; + blen -= len; + if ( prevdn.bv_val != NULL ) { + len = snprintf( ptr, blen, " prevdn %s", prevdn.bv_val ); + ptr += len; + blen -= len; + } + break; + } + if ( chgpres ) { + len = snprintf( ptr, blen, " changeNumber %ld", chgnum) ; + ptr += len; + blen -= len; + } + + tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE, + ldif ? "persistentSearch: " : "persistentSearch", buf, len ); + } +} + static int print_sss( LDAP *ld, LDAPControl *ctrl ) { diff --git a/clients/tools/ldapsearch.c b/clients/tools/ldapsearch.c index 696837ff63..e0ee59f9b4 100644 --- a/clients/tools/ldapsearch.c +++ b/clients/tools/ldapsearch.c @@ -129,6 +129,7 @@ usage( void ) fprintf( stderr, _(" !dontUseCopy (Don't Use Copy)\n")); fprintf( stderr, _(" [!]mv= (RFC 3876 matched values filter)\n")); fprintf( stderr, _(" [!]pr=[/prompt|noprompt] (RFC 2696 paged results/prompt)\n")); + fprintf( stderr, _(" [!]ps=// (draft persisten search)\n")); fprintf( stderr, _(" [!]sss=[-][/[-]...]\n")); fprintf( stderr, _(" (RFC 2891 server side sorting)\n")); fprintf( stderr, _(" [!]subentries[=true|false] (RFC 3672 subentries)\n")); @@ -237,6 +238,9 @@ static int ldapsync = 0; static struct berval sync_cookie = { 0, NULL }; static int sync_slimit = -1; +static int psearch = 0; +static int ps_chgtypes, ps_chgsonly, ps_echg_ctrls; + /* cookie and morePagedResults moved to common.c */ static int pagedResults = 0; static int pagePrompt = 1; @@ -467,6 +471,28 @@ handle_private_option( int i ) pageSize = (ber_int_t) tmp; pagedResults = 1 + crit; + } else if ( strcasecmp( control, "ps" ) == 0 ) { + int num, tmp; + /* PersistentSearch control */ + if ( psearch != 0 ) { + fprintf( stderr, + _("PersistentSearch previously specified\n") ); + exit( EXIT_FAILURE ); + } + if( cvalue != NULL ) { + num = sscanf( cvalue, "%i/%d/%d", &ps_chgtypes, &ps_chgsonly, &ps_echg_ctrls ); + if ( num != 3 ) { + fprintf( stderr, + _("Invalid value for PersistentSearch, %s.\n"), + cvalue ); + exit( EXIT_FAILURE ); + } + } else { + fprintf(stderr, _("Invalid value for PersistentSearch.\n")); + exit( EXIT_FAILURE ); + } + psearch = 1 + crit; + #ifdef LDAP_CONTROL_DONTUSECOPY } else if ( strcasecmp( control, "dontUseCopy" ) == 0 ) { if( dontUseCopy ) { @@ -1097,6 +1123,7 @@ getNextPage: #endif || domainScope || pagedResults + || psearch || ldapsync || sss || subentries @@ -1233,6 +1260,22 @@ getNextPage: i++; } + if ( psearch ) { + if ( ctrl_add() ) { + tool_exit( ld, EXIT_FAILURE ); + } + + if ( ldap_create_persistentsearch_control_value( ld, + ps_chgtypes, ps_chgsonly, ps_echg_ctrls, &c[i].ldctl_value ) ) + { + tool_exit( ld, EXIT_FAILURE ); + } + + c[i].ldctl_oid = LDAP_CONTROL_PERSIST_REQUEST; + c[i].ldctl_iscritical = psearch > 1; + i++; + } + if ( sss ) { if ( ctrl_add() ) { tool_exit( ld, EXIT_FAILURE ); diff --git a/include/ldap.h b/include/ldap.h index 0359a757ff..9f3351025b 100644 --- a/include/ldap.h +++ b/include/ldap.h @@ -2679,6 +2679,37 @@ ldap_parse_deref_control LDAP_P(( LDAPControl **ctrls, LDAPDerefRes **drp )); +/* + * in psearch.c + */ + +LDAP_F( int ) +ldap_create_persistentsearch_control_value LDAP_P(( + LDAP *ld, + int changetypes, + int changesonly, + int return_echg_ctls, + struct berval *value )); + +LDAP_F( int ) +ldap_create_persistentsearch_control LDAP_P(( + LDAP *ld, + int changetypes, + int changesonly, + int return_echg_ctls, + int isCritical, + LDAPControl **ctrlp )); + +LDAP_F( int ) +ldap_parse_entrychange_control LDAP_P(( + LDAP *ld, + LDAPControl *ctrl, + int *chgtypep, + struct berval *prevdnp, + int *chgnumpresentp, + long *chgnump )); + + /* * high level LDIF to LDAP structure support */ diff --git a/libraries/libldap/Makefile.in b/libraries/libldap/Makefile.in index 9449f40c29..399e85381f 100644 --- a/libraries/libldap/Makefile.in +++ b/libraries/libldap/Makefile.in @@ -29,7 +29,7 @@ SRCS = bind.c open.c result.c error.c compare.c search.c \ tls2.c tls_o.c tls_g.c tls_m.c \ turn.c ppolicy.c dds.c txn.c ldap_sync.c stctrl.c \ assertion.c deref.c ldifutil.c ldif.c fetch.c lbase64.c \ - msctrl.c + msctrl.c psearchctrl.c OBJS = bind.lo open.lo result.lo error.lo compare.lo search.lo \ controls.lo messages.lo references.lo extended.lo cyrus.lo \ @@ -43,7 +43,7 @@ OBJS = bind.lo open.lo result.lo error.lo compare.lo search.lo \ tls2.lo tls_o.lo tls_g.lo tls_m.lo \ turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo stctrl.lo \ assertion.lo deref.lo ldifutil.lo ldif.lo fetch.lo lbase64.lo \ - msctrl.lo + msctrl.lo psearchctrl.lo LDAP_INCDIR= ../../include LDAP_LIBDIR= ../../libraries diff --git a/libraries/libldap/psearchctrl.c b/libraries/libldap/psearchctrl.c new file mode 100644 index 0000000000..6050477be2 --- /dev/null +++ b/libraries/libldap/psearchctrl.c @@ -0,0 +1,348 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * Copyright 1998-2019 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by Howard Chu for inclusion in + * OpenLDAP Software. + */ + +#include "portable.h" + +#include +#include +#include +#include + +#include "ldap-int.h" + +/* Based on draft-ietf-ldapext-c-api-psearch-00 */ + +/* --------------------------------------------------------------------------- + ldap_create_persistentsearch_control_value + + Create and encode the value of the server-side sort control. + + ld (IN) An LDAP session handle, as obtained from a call to + ldap_init(). + + changetypes (IN) A bit-sensitive field that indicates which kinds of + changes the client wants to be informed about. Its + value should be LDAP_CHANGETYPE_ANY, or any logical-OR + combination of LDAP_CHANGETYPE_ADD, + LDAP_CHANGETYPE_DELETE, LDAP_CHANGETYPE_MODIFY, and + LDAP_CHANGETYPE_MODDN. This field corresponds to the + changeType element of the BER-encoded PersistentSearch + control value itself. + + changesonly (IN) A Boolean field that indicates whether the client + wishes to only receive searchResultEntry messages for + entries that have been changed. If non-zero, only + entries that result from changes are returned; other- + wise, all of the static entries that match the search + criteria are returned before the server begins change + notification. This field corresponds to the changes- + Only element of the BER-encoded PersistentSearch con- + trol value itself. + + return_echg_ctls (IN) A Boolean field that indicates whether the server + should send back an Entry Change Notification control + with each searchResultEntry that is returned due to a + change to an entry. If non-zero, Entry Change + Notification controls are requested; if zero, they are + not. This field corresponds to the returnECs element + of the BER-encoded PersistentSearch control value + itself. + + value (OUT) Contains the control value; the bv_val member of the berval structure + SHOULD be freed by calling ldap_memfree() when done. + + ---------------------------------------------------------------------------*/ + +int +ldap_create_persistentsearch_control_value( + LDAP *ld, + int changetypes, + int changesonly, + int return_echg_ctls, + struct berval *value ) +{ + int i; + BerElement *ber = NULL; + ber_tag_t tag; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + if ( ld == NULL ) return LDAP_PARAM_ERROR; + if ( value == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return LDAP_PARAM_ERROR; + } + if (( changetypes & 0x0f ) != changetypes ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return LDAP_PARAM_ERROR; + } + + value->bv_val = NULL; + value->bv_len = 0; + ld->ld_errno = LDAP_SUCCESS; + + ber = ldap_alloc_ber_with_options( ld ); + if ( ber == NULL) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_printf( ber, "{ibb}", changetypes, changesonly, return_echg_ctls ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + + if ( ber_flatten2( ber, value, 1 ) == -1 ) { + ld->ld_errno = LDAP_NO_MEMORY; + } + + if ( 0 ) { +error_return:; + ld->ld_errno = LDAP_ENCODING_ERROR; + } + + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return ld->ld_errno; +} + + +/* --------------------------------------------------------------------------- + ldap_create_persistentsearch_control + + Create and encode the persistent search control. + + ld (IN) An LDAP session handle, as obtained from a call to + ldap_init(). + + changetypes (IN) A bit-sensitive field that indicates which kinds of + changes the client wants to be informed about. Its + value should be LDAP_CHANGETYPE_ANY, or any logical-OR + combination of LDAP_CHANGETYPE_ADD, + LDAP_CHANGETYPE_DELETE, LDAP_CHANGETYPE_MODIFY, and + LDAP_CHANGETYPE_MODDN. This field corresponds to the + changeType element of the BER-encoded PersistentSearch + control value itself. + + changesonly (IN) A Boolean field that indicates whether the client + wishes to only receive searchResultEntry messages for + entries that have been changed. If non-zero, only + entries that result from changes are returned; other- + wise, all of the static entries that match the search + criteria are returned before the server begins change + notification. This field corresponds to the changes- + Only element of the BER-encoded PersistentSearch con- + trol value itself. + + return_echg_ctls (IN) A Boolean field that indicates whether the server + should send back an Entry Change Notification control + with each searchResultEntry that is returned due to a + change to an entry. If non-zero, Entry Change + Notification controls are requested; if zero, they are + not. This field corresponds to the returnECs element + of the BER-encoded PersistentSearch control value + itself. + + isCritical (IN) 0 - Indicates the control is not critical to the operation. + non-zero - The control is critical to the operation. + + ctrlp (OUT) Returns a pointer to the LDAPControl created. This control + SHOULD be freed by calling ldap_control_free() when done. + + ---------------------------------------------------------------------------*/ + +int +ldap_create_persistentsearch_control( + LDAP *ld, + int changetypes, + int changesonly, + int return_echg_ctls, + int isCritical, + LDAPControl **ctrlp ) +{ + struct berval value; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + if ( ld == NULL ) { + return LDAP_PARAM_ERROR; + } + + if ( ctrlp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + ld->ld_errno = ldap_create_persistentsearch_control_value( ld, changetypes, changesonly, return_echg_ctls, &value ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = ldap_control_create( LDAP_CONTROL_PERSIST_REQUEST, + isCritical, &value, 0, ctrlp ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + LDAP_FREE( value.bv_val ); + } + } + + return ld->ld_errno; +} + + +/* --------------------------------------------------------------------------- + ldap_parse_entrychange_control + + Decode the entry change notification control return information. + + ld (IN) An LDAP session handle, as obtained from a call to + ldap_init(). + + ctrl (IN) The address of the LDAP Control Structure. + + chgtypep (OUT) This result parameter is filled in with one of the + following values to indicate the type of change that was + made that caused the entry to be returned: + LDAP_CONTROL_PERSIST_ENTRY_CHANGE_ADD (1), + LDAP_CONTROL_PERSIST_ENTRY_CHANGE_DELETE (2), + LDAP_CONTROL_PERSIST_ENTRY_CHANGE_MODIFY (4), or + LDAP_CONTROL_PERSIST_ENTRY_CHANGE_RENAME (8). + If this parameter is NULL, the change type information + is not returned. + + prevdnp (OUT) This result parameter points to the DN the + entry had before it was renamed and/or moved by a + modifyDN operation. It is set to NULL for other types + of changes. If this parameter is NULL, the previous DN + information is not returned. The returned value is a + pointer to the contents of the control; it is not a + copy of the data. + + chgnumpresentp (OUT) This result parameter is filled in with a non-zero + value if a change number was returned in the control + (the change number is optional and servers MAY choose + not to return it). If this parameter is NULL, no indication + of whether the change number was present is returned. + + chgnump (OUT) This result paramter is filled in with the change number + if one was returned in the control. If this parameter + is NULL, the change number is not returned. + + ---------------------------------------------------------------------------*/ + +int +ldap_parse_entrychange_control( + LDAP *ld, + LDAPControl *ctrl, + int *chgtypep, + struct berval *prevdnp, + int *chgnumpresentp, + long *chgnump ) +{ + BerElement *ber; + ber_tag_t tag, berTag; + ber_len_t berLen; + ber_int_t chgtype; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( ctrl != NULL ); + + if (ld == NULL) { + return LDAP_PARAM_ERROR; + } + + if (ctrl == NULL) { + ld->ld_errno = LDAP_PARAM_ERROR; + return(ld->ld_errno); + } + + if ( !ctrl->ldctl_value.bv_val ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + + /* Create a BerElement from the berval returned in the control. */ + ber = ber_init(&ctrl->ldctl_value); + + if (ber == NULL) { + ld->ld_errno = LDAP_NO_MEMORY; + return(ld->ld_errno); + } + + if ( prevdnp != NULL ) { + BER_BVZERO( prevdnp ); + } + if ( chgnumpresentp != NULL ) + *chgnumpresentp = 0; + if ( chgnump != NULL ) + *chgnump = 0; + + /* Extract the change type from the control. */ + tag = ber_scanf(ber, "{e" /*}*/, &chgtype); + + if( tag != LBER_ENUMERATED ) { + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + if ( chgtypep != NULL ) + *chgtypep = chgtype; + + tag = ber_peek_tag( ber, &berLen ); + if ( berLen ) { + if (tag == LBER_OCTETSTRING) { + if (prevdnp != NULL) { + tag = ber_get_stringbv( ber, prevdnp, 0 ); + } else { + struct berval bv; + tag = ber_skip_element( ber, &bv ); + } + if ( tag == LBER_ERROR ) { + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + tag = ber_peek_tag( ber, &berLen ); + } + + if ( chgnumpresentp != NULL || chgnump != NULL ) { + ber_int_t chgnum = 0; + int present = 0; + if (tag == LBER_INTEGER) { + present = 1; + tag = ber_get_int( ber, &chgnum ); + if ( tag == LBER_ERROR ) { + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + if ( chgnumpresentp != NULL ) + *chgnumpresentp = present; + if ( chgnump != NULL ) + *chgnump = chgnum; + } + } + } + + ber_free(ber,1); + + ld->ld_errno = LDAP_SUCCESS; + return(ld->ld_errno); +} diff --git a/libraries/libldap_r/Makefile.in b/libraries/libldap_r/Makefile.in index a3e0896293..527bc691d0 100644 --- a/libraries/libldap_r/Makefile.in +++ b/libraries/libldap_r/Makefile.in @@ -31,7 +31,7 @@ XXSRCS = apitest.c test.c \ tls2.c tls_o.c tls_g.c tls_m.c \ turn.c ppolicy.c dds.c txn.c ldap_sync.c stctrl.c \ assertion.c deref.c ldifutil.c ldif.c fetch.c lbase64.c \ - msctrl.c + msctrl.c psearchctrl.c SRCS = threads.c rdwr.c tpool.c rq.c \ thr_posix.c thr_thr.c thr_nt.c \ thr_pth.c thr_stub.c thr_debug.c @@ -50,7 +50,7 @@ OBJS = threads.lo rdwr.lo tpool.lo rq.lo \ tls2.lo tls_o.lo tls_g.lo tls_m.lo \ turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo stctrl.lo \ assertion.lo deref.lo ldifutil.lo ldif.lo fetch.lo lbase64.lo \ - msctrl.lo + msctrl.lo psearchctrl.lo LDAP_INCDIR= ../../include LDAP_LIBDIR= ../../libraries