X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=servers%2Fslapd%2Foverlays%2Fretcode.c;h=4c8af8898695bcc2e7c3cb3509c6e1f9ccb2da3f;hb=fb2e448e877a8a387084dde517362c9f7bf1b6b7;hp=5788ccf7b8c7ac247354d8241013ac3ab22eb384;hpb=ec4d6d0d69d75b4b4f2fd4b314711b2730349f79;p=openldap diff --git a/servers/slapd/overlays/retcode.c b/servers/slapd/overlays/retcode.c index 5788ccf7b8..4c8af88986 100644 --- a/servers/slapd/overlays/retcode.c +++ b/servers/slapd/overlays/retcode.c @@ -2,7 +2,7 @@ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * - * Copyright 2005 The OpenLDAP Foundation. + * Copyright 2005-2007 The OpenLDAP Foundation. * Portions Copyright 2005 Pierangelo Masarati * All rights reserved. * @@ -32,6 +32,7 @@ #include "slap.h" #include "lutil.h" +#include "ldif.h" static slap_overinst retcode; @@ -39,7 +40,14 @@ static AttributeDescription *ad_errCode; static AttributeDescription *ad_errText; static AttributeDescription *ad_errOp; static AttributeDescription *ad_errSleepTime; +static AttributeDescription *ad_errMatchedDN; +static AttributeDescription *ad_errUnsolicitedOID; +static AttributeDescription *ad_errUnsolicitedData; +static AttributeDescription *ad_errDisconnect; + +static ObjectClass *oc_errAbsObject; static ObjectClass *oc_errObject; +static ObjectClass *oc_errAuxObject; typedef enum retcode_op_e { SN_DG_OP_NONE = 0x0000, @@ -61,11 +69,19 @@ typedef struct retcode_item_t { struct berval rdi_dn; struct berval rdi_ndn; struct berval rdi_text; + struct berval rdi_matched; int rdi_err; BerVarray rdi_ref; int rdi_sleeptime; Entry rdi_e; slap_mask_t rdi_mask; + struct berval rdi_unsolicited_oid; + struct berval rdi_unsolicited_data; + + unsigned rdi_flags; +#define RDI_PRE_DISCONNECT (0x1U) +#define RDI_POST_DISCONNECT (0x2U) + struct retcode_item_t *rdi_next; } retcode_item_t; @@ -73,6 +89,8 @@ typedef struct retcode_t { struct berval rd_pdn; struct berval rd_npdn; + int rd_sleep; + retcode_item_t *rd_item; unsigned rd_flags; @@ -82,7 +100,28 @@ typedef struct retcode_t { } retcode_t; static int -retcode_entry_response( Operation *op, SlapReply *rs, Entry *e ); +retcode_entry_response( Operation *op, SlapReply *rs, BackendInfo *bi, Entry *e ); + +static unsigned int +retcode_sleep( int s ) +{ + unsigned int r = 0; + + /* sleep as required */ + if ( s < 0 ) { +#if 0 /* use high-order bits for better randomness (Numerical Recipes in "C") */ + r = rand() % (-s); +#endif + r = ((double)(-s))*rand()/(RAND_MAX + 1.0); + } else if ( s > 0 ) { + r = (unsigned int)s; + } + if ( r ) { + sleep( r ); + } + + return r; +} static int retcode_cleanup_cb( Operation *op, SlapReply *rs ) @@ -110,19 +149,12 @@ retcode_send_onelevel( Operation *op, SlapReply *rs ) retcode_item_t *rdi; for ( rdi = rd->rd_item; rdi != NULL; rdi = rdi->rdi_next ) { - int rc; - if ( op->o_abandon ) { return rs->sr_err = SLAPD_ABANDON; } rs->sr_err = test_filter( op, &rdi->rdi_e, op->ors_filter ); if ( rs->sr_err == LDAP_COMPARE_TRUE ) { - if ( op->ors_slimit == rs->sr_nentries ) { - rs->sr_err = LDAP_SIZELIMIT_EXCEEDED; - goto done; - } - /* safe default */ rs->sr_attrs = op->ors_attrs; rs->sr_operational_attrs = NULL; @@ -131,16 +163,14 @@ retcode_send_onelevel( Operation *op, SlapReply *rs ) rs->sr_err = LDAP_SUCCESS; rs->sr_entry = &rdi->rdi_e; - rc = send_search_entry( op, rs ); + rs->sr_err = send_search_entry( op, rs ); + rs->sr_entry = NULL; - switch ( rc ) { - case 0: /* entry sent ok */ - break; - case 1: /* entry not sent */ - break; - case -1: /* connection closed */ - rs->sr_entry = NULL; + switch ( rs->sr_err ) { + case LDAP_UNAVAILABLE: /* connection closed */ rs->sr_err = LDAP_OTHER; + /* fallthru */ + case LDAP_SIZELIMIT_EXCEEDED: goto done; } } @@ -157,12 +187,14 @@ done:; static int retcode_op_add( Operation *op, SlapReply *rs ) { - return retcode_entry_response( op, rs, op->ora_e ); + return retcode_entry_response( op, rs, NULL, op->ora_e ); } typedef struct retcode_cb_t { + BackendInfo *rdc_info; unsigned rdc_flags; ber_tag_t rdc_tag; + AttributeName *rdc_attrs; } retcode_cb_t; static int @@ -175,14 +207,19 @@ retcode_cb_response( Operation *op, SlapReply *rs ) int rc; op->o_tag = rdc->rdc_tag; - rc = retcode_entry_response( op, rs, rs->sr_entry ); + if ( op->o_tag == LDAP_REQ_SEARCH ) { + rs->sr_attrs = rdc->rdc_attrs; + } + rc = retcode_entry_response( op, rs, rdc->rdc_info, rs->sr_entry ); op->o_tag = o_tag; return rc; } if ( rs->sr_err == LDAP_SUCCESS ) { - rdc->rdc_flags = SLAP_CB_CONTINUE; + if ( !op->o_abandon ) { + rdc->rdc_flags = SLAP_CB_CONTINUE; + } return 0; } @@ -195,7 +232,6 @@ retcode_op_internal( Operation *op, SlapReply *rs ) slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; Operation op2 = *op; - SlapReply rs2 = { 0 }; BackendDB db = *op->o_bd; slap_callback sc = { 0 }; retcode_cb_t rdc; @@ -211,20 +247,26 @@ retcode_op_internal( Operation *op, SlapReply *rs ) op2.ors_attrsonly = 0; op2.ors_attrs = slap_anlist_all_attributes; - ber_str2bv_x( "(objectClass=errObject)", STRLENOF( "(objectClass=errObject)" ), + ber_str2bv_x( "(objectClass=errAbsObject)", + STRLENOF( "(objectClass=errAbsObject)" ), 1, &op2.ors_filterstr, op2.o_tmpmemctx ); op2.ors_filter = str2filter_x( &op2, op2.ors_filterstr.bv_val ); db.bd_info = on->on_info->oi_orig; op2.o_bd = &db; + rdc.rdc_info = on->on_info->oi_orig; rdc.rdc_flags = RETCODE_FINDIR; + if ( op->o_tag == LDAP_REQ_SEARCH ) { + rdc.rdc_attrs = op->ors_attrs; + } rdc.rdc_tag = op->o_tag; sc.sc_response = retcode_cb_response; sc.sc_private = &rdc; op2.o_callback = ≻ - rc = op2.o_bd->be_search( &op2, &rs2 ); + rc = op2.o_bd->be_search( &op2, rs ); + op->o_abandon = op2.o_abandon; filter_free_x( &op2, op2.ors_filter ); ber_memfree_x( op2.ors_filterstr.bv_val, op2.o_tmpmemctx ); @@ -247,6 +289,9 @@ retcode_op_func( Operation *op, SlapReply *rs ) slap_callback *cb = NULL; + /* sleep as required */ + retcode_sleep( rd->rd_sleep ); + if ( !dnIsSuffix( &op->o_req_ndn, &rd->rd_npdn ) ) { if ( RETCODE_INDIR( rd ) ) { switch ( op->o_tag ) { @@ -254,16 +299,35 @@ retcode_op_func( Operation *op, SlapReply *rs ) return retcode_op_add( op, rs ); case LDAP_REQ_BIND: + /* skip if rootdn */ if ( be_isroot_pw( op ) ) { return SLAP_CB_CONTINUE; } - /* fallthru */ + return retcode_op_internal( op, rs ); + + case LDAP_REQ_SEARCH: + if ( op->ors_scope == LDAP_SCOPE_BASE ) { + rs->sr_err = retcode_op_internal( op, rs ); + switch ( rs->sr_err ) { + case SLAP_CB_CONTINUE: + if ( rs->sr_nentries == 0 ) { + break; + } + rs->sr_err = LDAP_SUCCESS; + /* fallthru */ + + default: + send_ldap_result( op, rs ); + break; + } + return rs->sr_err; + } + break; case LDAP_REQ_MODIFY: case LDAP_REQ_DELETE: case LDAP_REQ_MODRDN: case LDAP_REQ_COMPARE: - case LDAP_REQ_SEARCH: return retcode_op_internal( op, rs ); } } @@ -350,8 +414,13 @@ retcode_op_func( Operation *op, SlapReply *rs ) rs->sr_text = "retcode not found"; } else { + if ( rdi->rdi_flags & RDI_PRE_DISCONNECT ) { + return rs->sr_err = SLAPD_DISCONNECT; + } + rs->sr_err = rdi->rdi_err; rs->sr_text = rdi->rdi_text.bv_val; + rs->sr_matched = rdi->rdi_matched.bv_val; /* FIXME: we only honor the rdi_ref field in case rdi_err * is LDAP_REFERRAL otherwise send_ldap_result() bails out */ @@ -374,9 +443,7 @@ retcode_op_func( Operation *op, SlapReply *rs ) } } - if ( rdi->rdi_sleeptime > 0 ) { - sleep( rdi->rdi_sleeptime ); - } + retcode_sleep( rdi->rdi_sleeptime ); } switch ( op->o_tag ) { @@ -391,12 +458,45 @@ retcode_op_func( Operation *op, SlapReply *rs ) break; default: - send_ldap_result( op, rs ); + if ( rdi && !BER_BVISNULL( &rdi->rdi_unsolicited_oid ) ) { + ber_int_t msgid = op->o_msgid; + + /* RFC 4511 unsolicited response */ + + op->o_msgid = 0; + if ( strcmp( rdi->rdi_unsolicited_oid.bv_val, "0" ) == 0 ) { + send_ldap_result( op, rs ); + + } else { + ber_tag_t tag = op->o_tag; + + op->o_tag = LDAP_REQ_EXTENDED; + rs->sr_rspoid = rdi->rdi_unsolicited_oid.bv_val; + if ( !BER_BVISNULL( &rdi->rdi_unsolicited_data ) ) { + rs->sr_rspdata = &rdi->rdi_unsolicited_data; + } + send_ldap_extended( op, rs ); + rs->sr_rspoid = NULL; + rs->sr_rspdata = NULL; + op->o_tag = tag; + + } + op->o_msgid = msgid; + + } else { + send_ldap_result( op, rs ); + } + if ( rs->sr_ref != NULL ) { ber_bvarray_free( rs->sr_ref ); + rs->sr_ref = NULL; } rs->sr_matched = NULL; rs->sr_text = NULL; + + if ( rdi && rdi->rdi_flags & RDI_POST_DISCONNECT ) { + return rs->sr_err = SLAPD_DISCONNECT; + } break; } @@ -436,19 +536,18 @@ retcode_op2str( ber_tag_t op, struct berval *bv ) } static int -retcode_entry_response( Operation *op, SlapReply *rs, Entry *e ) +retcode_entry_response( Operation *op, SlapReply *rs, BackendInfo *bi, Entry *e ) { - slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; - Attribute *a; int err; char *next; + int disconnect = 0; if ( get_manageDSAit( op ) ) { return SLAP_CB_CONTINUE; } - if ( !is_entry_objectclass( e, oc_errObject, 0 ) ) { + if ( !is_entry_objectclass_or_sub( e, oc_errAbsObject ) ) { return SLAP_CB_CONTINUE; } @@ -477,6 +576,15 @@ retcode_entry_response( Operation *op, SlapReply *rs, Entry *e ) } } + /* disconnect */ + a = attr_find( e->e_attrs, ad_errDisconnect ); + if ( a != NULL ) { + if ( bvmatch( &a->a_nvals[ 0 ], &slap_true_bv ) ) { + return rs->sr_err = SLAPD_DISCONNECT; + } + disconnect = 1; + } + /* error code */ a = attr_find( e->e_attrs, ad_errCode ); if ( a == NULL ) { @@ -490,17 +598,17 @@ retcode_entry_response( Operation *op, SlapReply *rs, Entry *e ) /* sleep time */ a = attr_find( e->e_attrs, ad_errSleepTime ); - if ( a != NULL ) { + if ( a != NULL && a->a_nvals[ 0 ].bv_val[ 0 ] != '-' ) { int sleepTime; - sleepTime = strtoul( a->a_nvals[ 0 ].bv_val, &next, 0 ); - if ( next != a->a_nvals[ 0 ].bv_val && next[ 0 ] == '\0' ) { - sleep( sleepTime ); + if ( lutil_atoi( &sleepTime, a->a_nvals[ 0 ].bv_val ) == 0 ) { + retcode_sleep( sleepTime ); } } - if ( rs->sr_err != LDAP_SUCCESS ) { - BackendDB db = *op->o_bd; + if ( rs->sr_err != LDAP_SUCCESS && !LDAP_API_ERROR( rs->sr_err )) { + BackendDB db = *op->o_bd, + *o_bd = op->o_bd; void *o_callback = op->o_callback; /* message text */ @@ -509,7 +617,19 @@ retcode_entry_response( Operation *op, SlapReply *rs, Entry *e ) rs->sr_text = a->a_vals[ 0 ].bv_val; } - db.bd_info = on->on_info->oi_orig; + /* matched DN */ + a = attr_find( e->e_attrs, ad_errMatchedDN ); + if ( a != NULL ) { + rs->sr_matched = a->a_vals[ 0 ].bv_val; + } + + if ( bi == NULL ) { + slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; + + bi = on->on_info->oi_orig; + } + + db.bd_info = bi; op->o_bd = &db; op->o_callback = NULL; @@ -529,14 +649,57 @@ retcode_entry_response( Operation *op, SlapReply *rs, Entry *e ) rs->sr_ref = NULL; } else { - send_ldap_result( op, rs ); + a = attr_find( e->e_attrs, ad_errUnsolicitedOID ); + if ( a != NULL ) { + struct berval oid = BER_BVNULL, + data = BER_BVNULL; + ber_int_t msgid = op->o_msgid; + + /* RFC 4511 unsolicited response */ + + op->o_msgid = 0; + + oid = a->a_nvals[ 0 ]; + + a = attr_find( e->e_attrs, ad_errUnsolicitedData ); + if ( a != NULL ) { + data = a->a_nvals[ 0 ]; + } + + if ( strcmp( oid.bv_val, "0" ) == 0 ) { + send_ldap_result( op, rs ); + + } else { + ber_tag_t tag = op->o_tag; + + op->o_tag = LDAP_REQ_EXTENDED; + rs->sr_rspoid = oid.bv_val; + if ( !BER_BVISNULL( &data ) ) { + rs->sr_rspdata = &data; + } + send_ldap_extended( op, rs ); + rs->sr_rspoid = NULL; + rs->sr_rspdata = NULL; + op->o_tag = tag; + } + op->o_msgid = msgid; + + } else { + send_ldap_result( op, rs ); + } } rs->sr_text = NULL; + rs->sr_matched = NULL; + op->o_bd = o_bd; op->o_callback = o_callback; } - + if ( rs->sr_err != LDAP_SUCCESS ) { + if ( disconnect ) { + return rs->sr_err = SLAPD_DISCONNECT; + } + op->o_abandon = 1; return rs->sr_err; } @@ -554,7 +717,7 @@ retcode_response( Operation *op, SlapReply *rs ) return SLAP_CB_CONTINUE; } - return retcode_entry_response( op, rs, rs->sr_entry ); + return retcode_entry_response( op, rs, NULL, rs->sr_entry ); } static int @@ -563,6 +726,8 @@ retcode_db_init( BackendDB *be ) slap_overinst *on = (slap_overinst *)be->bd_info; retcode_t *rd; + srand( getpid() ); + rd = (retcode_t *)ch_malloc( sizeof( retcode_t ) ); memset( rd, 0, sizeof( retcode_t ) ); @@ -700,13 +865,15 @@ retcode_db_config( } else if ( strcasecmp( ops[ j ], "compare" ) == 0 ) { rdi.rdi_mask |= SN_DG_OP_COMPARE; - } else if ( strcasecmp( ops[ j ], "add" ) == 0 ) { + } else if ( strcasecmp( ops[ j ], "delete" ) == 0 ) { rdi.rdi_mask |= SN_DG_OP_DELETE; } else if ( strcasecmp( ops[ j ], "modify" ) == 0 ) { rdi.rdi_mask |= SN_DG_OP_MODIFY; - } else if ( strcasecmp( ops[ j ], "rename" ) == 0 ) { + } else if ( strcasecmp( ops[ j ], "rename" ) == 0 + || strcasecmp( ops[ j ], "modrdn" ) == 0 ) + { rdi.rdi_mask |= SN_DG_OP_RENAME; } else if ( strcasecmp( ops[ j ], "search" ) == 0 ) { @@ -730,6 +897,7 @@ retcode_db_config( } else { fprintf( stderr, "retcode: unknown op \"%s\"\n", ops[ j ] ); + ldap_charray_free( ops ); return 1; } } @@ -746,6 +914,24 @@ retcode_db_config( } ber_str2bv( &argv[ i ][ STRLENOF( "text=" ) ], 0, 1, &rdi.rdi_text ); + } else if ( strncasecmp( argv[ i ], "matched=", STRLENOF( "matched=" ) ) == 0 ) + { + struct berval dn; + + if ( !BER_BVISNULL( &rdi.rdi_matched ) ) { + fprintf( stderr, "%s: line %d: retcode: " + "\"matched\" already provided.\n", + fname, lineno ); + return 1; + } + ber_str2bv( &argv[ i ][ STRLENOF( "matched=" ) ], 0, 0, &dn ); + if ( dnPretty( NULL, &dn, &rdi.rdi_matched, NULL ) != LDAP_SUCCESS ) { + fprintf( stderr, "%s: line %d: retcode: " + "unable to prettify matched DN \"%s\".\n", + fname, lineno, &argv[ i ][ STRLENOF( "matched=" ) ] ); + return 1; + } + } else if ( strncasecmp( argv[ i ], "ref=", STRLENOF( "ref=" ) ) == 0 ) { char **refs; @@ -781,7 +967,6 @@ retcode_db_config( } else if ( strncasecmp( argv[ i ], "sleeptime=", STRLENOF( "sleeptime=" ) ) == 0 ) { - char *next; if ( rdi.rdi_sleeptime != 0 ) { fprintf( stderr, "%s: line %d: retcode: " "\"sleeptime\" already provided.\n", @@ -789,18 +974,67 @@ retcode_db_config( return 1; } - rdi.rdi_sleeptime = strtol( &argv[ i ][ STRLENOF( "sleeptime=" ) ], &next, 10 ); - if ( next == argv[ i ] || next[ 0 ] != '\0' ) { + if ( lutil_atoi( &rdi.rdi_sleeptime, &argv[ i ][ STRLENOF( "sleeptime=" ) ] ) ) { fprintf( stderr, "%s: line %d: retcode: " "unable to parse \"sleeptime=%s\".\n", fname, lineno, &argv[ i ][ STRLENOF( "sleeptime=" ) ] ); return 1; } + } else if ( strncasecmp( argv[ i ], "unsolicited=", STRLENOF( "unsolicited=" ) ) == 0 ) + { + char *data; + + if ( !BER_BVISNULL( &rdi.rdi_unsolicited_oid ) ) { + fprintf( stderr, "%s: line %d: retcode: " + "\"unsolicited\" already provided.\n", + fname, lineno ); + return 1; + } + + data = strchr( &argv[ i ][ STRLENOF( "unsolicited=" ) ], ':' ); + if ( data != NULL ) { + struct berval oid; + + if ( ldif_parse_line2( &argv[ i ][ STRLENOF( "unsolicited=" ) ], + &oid, &rdi.rdi_unsolicited_data, NULL ) ) + { + fprintf( stderr, "%s: line %d: retcode: " + "unable to parse \"unsolicited\".\n", + fname, lineno ); + return 1; + } + + ber_dupbv( &rdi.rdi_unsolicited_oid, &oid ); + + } else { + ber_str2bv( &argv[ i ][ STRLENOF( "unsolicited=" ) ], 0, 1, + &rdi.rdi_unsolicited_oid ); + } + + } else if ( strncasecmp( argv[ i ], "flags=", STRLENOF( "flags=" ) ) == 0 ) + { + char *arg = &argv[ i ][ STRLENOF( "flags=" ) ]; + if ( strcasecmp( arg, "disconnect" ) == 0 ) { + rdi.rdi_flags |= RDI_PRE_DISCONNECT; + + } else if ( strcasecmp( arg, "pre-disconnect" ) == 0 ) { + rdi.rdi_flags |= RDI_PRE_DISCONNECT; + + } else if ( strcasecmp( arg, "post-disconnect" ) == 0 ) { + rdi.rdi_flags |= RDI_POST_DISCONNECT; + + } else { + fprintf( stderr, "%s: line %d: retcode: " + "unknown flag \"%s\".\n", + fname, lineno, arg ); + return 1; + } + } else { fprintf( stderr, "%s: line %d: retcode: " "unknown option \"%s\".\n", - fname, lineno, argv[ i ] ); + fname, lineno, argv[ i ] ); return 1; } } @@ -816,6 +1050,31 @@ retcode_db_config( } else if ( strcasecmp( argv0, "indir" ) == 0 ) { rd->rd_flags |= RETCODE_FINDIR; + } else if ( strcasecmp( argv0, "sleep" ) == 0 ) { + switch ( argc ) { + case 1: + fprintf( stderr, "%s: line %d: retcode: " + "\"retcode-sleep