Home | History | Annotate | Line # | Download | only in overlays
retcode.c revision 1.1
      1 /* retcode.c - customizable response for client testing purposes */
      2 /* $OpenLDAP: pkg/ldap/servers/slapd/overlays/retcode.c,v 1.18.2.7 2008/02/11 23:26:48 kurt Exp $ */
      3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      4  *
      5  * Copyright 2005-2008 The OpenLDAP Foundation.
      6  * Portions Copyright 2005 Pierangelo Masarati <ando (at) sys-net.it>
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted only as authorized by the OpenLDAP
     11  * Public License.
     12  *
     13  * A copy of this license is available in the file LICENSE in the
     14  * top-level directory of the distribution or, alternatively, at
     15  * <http://www.OpenLDAP.org/license.html>.
     16  */
     17 /* ACKNOWLEDGEMENTS:
     18  * This work was initially developed by Pierangelo Masarati for inclusion
     19  * in OpenLDAP Software.
     20  */
     21 
     22 #include "portable.h"
     23 
     24 #ifdef SLAPD_OVER_RETCODE
     25 
     26 #include <stdio.h>
     27 
     28 #include <ac/unistd.h>
     29 #include <ac/string.h>
     30 #include <ac/ctype.h>
     31 #include <ac/socket.h>
     32 
     33 #include "slap.h"
     34 #include "config.h"
     35 #include "lutil.h"
     36 #include "ldif.h"
     37 
     38 static slap_overinst		retcode;
     39 
     40 static AttributeDescription	*ad_errCode;
     41 static AttributeDescription	*ad_errText;
     42 static AttributeDescription	*ad_errOp;
     43 static AttributeDescription	*ad_errSleepTime;
     44 static AttributeDescription	*ad_errMatchedDN;
     45 static AttributeDescription	*ad_errUnsolicitedOID;
     46 static AttributeDescription	*ad_errUnsolicitedData;
     47 static AttributeDescription	*ad_errDisconnect;
     48 
     49 static ObjectClass		*oc_errAbsObject;
     50 static ObjectClass		*oc_errObject;
     51 static ObjectClass		*oc_errAuxObject;
     52 
     53 typedef enum retcode_op_e {
     54 	SN_DG_OP_NONE		= 0x0000,
     55 	SN_DG_OP_ADD		= 0x0001,
     56 	SN_DG_OP_BIND		= 0x0002,
     57 	SN_DG_OP_COMPARE	= 0x0004,
     58 	SN_DG_OP_DELETE		= 0x0008,
     59 	SN_DG_OP_MODIFY		= 0x0010,
     60 	SN_DG_OP_RENAME		= 0x0020,
     61 	SN_DG_OP_SEARCH		= 0x0040,
     62 	SN_DG_EXTENDED		= 0x0080,
     63 	SN_DG_OP_AUTH		= SN_DG_OP_BIND,
     64 	SN_DG_OP_READ		= (SN_DG_OP_COMPARE|SN_DG_OP_SEARCH),
     65 	SN_DG_OP_WRITE		= (SN_DG_OP_ADD|SN_DG_OP_DELETE|SN_DG_OP_MODIFY|SN_DG_OP_RENAME),
     66 	SN_DG_OP_ALL		= (SN_DG_OP_AUTH|SN_DG_OP_READ|SN_DG_OP_WRITE|SN_DG_EXTENDED)
     67 } retcode_op_e;
     68 
     69 typedef struct retcode_item_t {
     70 	struct berval		rdi_dn;
     71 	struct berval		rdi_ndn;
     72 	struct berval		rdi_text;
     73 	struct berval		rdi_matched;
     74 	int			rdi_err;
     75 	BerVarray		rdi_ref;
     76 	int			rdi_sleeptime;
     77 	Entry			rdi_e;
     78 	slap_mask_t		rdi_mask;
     79 	struct berval		rdi_unsolicited_oid;
     80 	struct berval		rdi_unsolicited_data;
     81 
     82 	unsigned		rdi_flags;
     83 #define	RDI_PRE_DISCONNECT	(0x1U)
     84 #define	RDI_POST_DISCONNECT	(0x2U)
     85 
     86 	struct retcode_item_t	*rdi_next;
     87 } retcode_item_t;
     88 
     89 typedef struct retcode_t {
     90 	struct berval		rd_pdn;
     91 	struct berval		rd_npdn;
     92 
     93 	int			rd_sleep;
     94 
     95 	retcode_item_t		*rd_item;
     96 
     97 	unsigned		rd_flags;
     98 #define	RETCODE_FNONE		0x00
     99 #define	RETCODE_FINDIR		0x01
    100 #define	RETCODE_INDIR( rd )	( (rd)->rd_flags & RETCODE_FINDIR )
    101 } retcode_t;
    102 
    103 static int
    104 retcode_entry_response( Operation *op, SlapReply *rs, BackendInfo *bi, Entry *e );
    105 
    106 static unsigned int
    107 retcode_sleep( int s )
    108 {
    109 	unsigned int r = 0;
    110 
    111 	/* sleep as required */
    112 	if ( s < 0 ) {
    113 #if 0	/* use high-order bits for better randomness (Numerical Recipes in "C") */
    114 		r = rand() % (-s);
    115 #endif
    116 		r = ((double)(-s))*rand()/(RAND_MAX + 1.0);
    117 	} else if ( s > 0 ) {
    118 		r = (unsigned int)s;
    119 	}
    120 	if ( r ) {
    121 		sleep( r );
    122 	}
    123 
    124 	return r;
    125 }
    126 
    127 static int
    128 retcode_cleanup_cb( Operation *op, SlapReply *rs )
    129 {
    130 	rs->sr_matched = NULL;
    131 	rs->sr_text = NULL;
    132 
    133 	if ( rs->sr_ref != NULL ) {
    134 		ber_bvarray_free( rs->sr_ref );
    135 		rs->sr_ref = NULL;
    136 	}
    137 
    138 	ch_free( op->o_callback );
    139 	op->o_callback = NULL;
    140 
    141 	return SLAP_CB_CONTINUE;
    142 }
    143 
    144 static int
    145 retcode_send_onelevel( Operation *op, SlapReply *rs )
    146 {
    147 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
    148 	retcode_t	*rd = (retcode_t *)on->on_bi.bi_private;
    149 
    150 	retcode_item_t	*rdi;
    151 
    152 	for ( rdi = rd->rd_item; rdi != NULL; rdi = rdi->rdi_next ) {
    153 		if ( op->o_abandon ) {
    154 			return rs->sr_err = SLAPD_ABANDON;
    155 		}
    156 
    157 		rs->sr_err = test_filter( op, &rdi->rdi_e, op->ors_filter );
    158 		if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
    159 			/* safe default */
    160 			rs->sr_attrs = op->ors_attrs;
    161 			rs->sr_operational_attrs = NULL;
    162 			rs->sr_ctrls = NULL;
    163 			rs->sr_flags = 0;
    164 			rs->sr_err = LDAP_SUCCESS;
    165 			rs->sr_entry = &rdi->rdi_e;
    166 
    167 			rs->sr_err = send_search_entry( op, rs );
    168 			rs->sr_entry = NULL;
    169 
    170 			switch ( rs->sr_err ) {
    171 			case LDAP_UNAVAILABLE:	/* connection closed */
    172 				rs->sr_err = LDAP_OTHER;
    173 				/* fallthru */
    174 			case LDAP_SIZELIMIT_EXCEEDED:
    175 				goto done;
    176 			}
    177 		}
    178 		rs->sr_err = LDAP_SUCCESS;
    179 	}
    180 
    181 done:;
    182 
    183 	send_ldap_result( op, rs );
    184 
    185 	return rs->sr_err;
    186 }
    187 
    188 static int
    189 retcode_op_add( Operation *op, SlapReply *rs )
    190 {
    191 	return retcode_entry_response( op, rs, NULL, op->ora_e );
    192 }
    193 
    194 typedef struct retcode_cb_t {
    195 	BackendInfo	*rdc_info;
    196 	unsigned	rdc_flags;
    197 	ber_tag_t	rdc_tag;
    198 	AttributeName	*rdc_attrs;
    199 } retcode_cb_t;
    200 
    201 static int
    202 retcode_cb_response( Operation *op, SlapReply *rs )
    203 {
    204 	retcode_cb_t	*rdc = (retcode_cb_t *)op->o_callback->sc_private;
    205 
    206 	op->o_tag = rdc->rdc_tag;
    207 	if ( rs->sr_type == REP_SEARCH ) {
    208 		ber_tag_t	o_tag = op->o_tag;
    209 		int		rc;
    210 
    211 		if ( op->o_tag == LDAP_REQ_SEARCH ) {
    212 			rs->sr_attrs = rdc->rdc_attrs;
    213 		}
    214 		rc = retcode_entry_response( op, rs, rdc->rdc_info, rs->sr_entry );
    215 		op->o_tag = o_tag;
    216 
    217 		return rc;
    218 	}
    219 
    220 	switch ( rs->sr_err ) {
    221 	case LDAP_SUCCESS:
    222 	case LDAP_NO_SUCH_OBJECT:
    223 		/* in case of noSuchObject, stop the internal search
    224 		 * for in-directory error stuff */
    225 		if ( !op->o_abandon ) {
    226 			rdc->rdc_flags = SLAP_CB_CONTINUE;
    227 		}
    228 		return 0;
    229 	}
    230 
    231 	return SLAP_CB_CONTINUE;
    232 }
    233 
    234 static int
    235 retcode_op_internal( Operation *op, SlapReply *rs )
    236 {
    237 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
    238 
    239 	Operation	op2 = *op;
    240 	BackendDB	db = *op->o_bd;
    241 	slap_callback	sc = { 0 };
    242 	retcode_cb_t	rdc;
    243 
    244 	int		rc;
    245 
    246 	op2.o_tag = LDAP_REQ_SEARCH;
    247 	op2.ors_scope = LDAP_SCOPE_BASE;
    248 	op2.ors_deref = LDAP_DEREF_NEVER;
    249 	op2.ors_tlimit = SLAP_NO_LIMIT;
    250 	op2.ors_slimit = SLAP_NO_LIMIT;
    251 	op2.ors_limit = NULL;
    252 	op2.ors_attrsonly = 0;
    253 	op2.ors_attrs = slap_anlist_all_attributes;
    254 
    255 	ber_str2bv_x( "(objectClass=errAbsObject)",
    256 		STRLENOF( "(objectClass=errAbsObject)" ),
    257 		1, &op2.ors_filterstr, op2.o_tmpmemctx );
    258 	op2.ors_filter = str2filter_x( &op2, op2.ors_filterstr.bv_val );
    259 
    260 	db.bd_info = on->on_info->oi_orig;
    261 	op2.o_bd = &db;
    262 
    263 	rdc.rdc_info = on->on_info->oi_orig;
    264 	rdc.rdc_flags = RETCODE_FINDIR;
    265 	if ( op->o_tag == LDAP_REQ_SEARCH ) {
    266 		rdc.rdc_attrs = op->ors_attrs;
    267 	}
    268 	rdc.rdc_tag = op->o_tag;
    269 	sc.sc_response = retcode_cb_response;
    270 	sc.sc_private = &rdc;
    271 	op2.o_callback = &sc;
    272 
    273 	rc = op2.o_bd->be_search( &op2, rs );
    274 	op->o_abandon = op2.o_abandon;
    275 
    276 	filter_free_x( &op2, op2.ors_filter );
    277 	ber_memfree_x( op2.ors_filterstr.bv_val, op2.o_tmpmemctx );
    278 
    279 	if ( rdc.rdc_flags == SLAP_CB_CONTINUE ) {
    280 		return SLAP_CB_CONTINUE;
    281 	}
    282 
    283 	return rc;
    284 }
    285 
    286 static int
    287 retcode_op_func( Operation *op, SlapReply *rs )
    288 {
    289 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
    290 	retcode_t	*rd = (retcode_t *)on->on_bi.bi_private;
    291 
    292 	retcode_item_t	*rdi;
    293 	struct berval		nrdn, npdn;
    294 
    295 	slap_callback		*cb = NULL;
    296 
    297 	/* sleep as required */
    298 	retcode_sleep( rd->rd_sleep );
    299 
    300 	if ( !dnIsSuffix( &op->o_req_ndn, &rd->rd_npdn ) ) {
    301 		if ( RETCODE_INDIR( rd ) ) {
    302 			switch ( op->o_tag ) {
    303 			case LDAP_REQ_ADD:
    304 				return retcode_op_add( op, rs );
    305 
    306 			case LDAP_REQ_BIND:
    307 				/* skip if rootdn */
    308 				/* FIXME: better give the db a chance? */
    309 				if ( be_isroot_pw( op ) ) {
    310 					return LDAP_SUCCESS;
    311 				}
    312 				return retcode_op_internal( op, rs );
    313 
    314 			case LDAP_REQ_SEARCH:
    315 				if ( op->ors_scope == LDAP_SCOPE_BASE ) {
    316 					rs->sr_err = retcode_op_internal( op, rs );
    317 					switch ( rs->sr_err ) {
    318 					case SLAP_CB_CONTINUE:
    319 						if ( rs->sr_nentries == 0 ) {
    320 							break;
    321 						}
    322 						rs->sr_err = LDAP_SUCCESS;
    323 						/* fallthru */
    324 
    325 					default:
    326 						send_ldap_result( op, rs );
    327 						break;
    328 					}
    329 					return rs->sr_err;
    330 				}
    331 				break;
    332 
    333 			case LDAP_REQ_MODIFY:
    334 			case LDAP_REQ_DELETE:
    335 			case LDAP_REQ_MODRDN:
    336 			case LDAP_REQ_COMPARE:
    337 				return retcode_op_internal( op, rs );
    338 			}
    339 		}
    340 
    341 		return SLAP_CB_CONTINUE;
    342 	}
    343 
    344 	if ( op->o_tag == LDAP_REQ_SEARCH
    345 			&& op->ors_scope != LDAP_SCOPE_BASE
    346 			&& op->o_req_ndn.bv_len == rd->rd_npdn.bv_len )
    347 	{
    348 		return retcode_send_onelevel( op, rs );
    349 	}
    350 
    351 	dnParent( &op->o_req_ndn, &npdn );
    352 	if ( npdn.bv_len != rd->rd_npdn.bv_len ) {
    353 		rs->sr_err = LDAP_NO_SUCH_OBJECT;
    354 		rs->sr_matched = rd->rd_pdn.bv_val;
    355 		send_ldap_result( op, rs );
    356 		rs->sr_matched = NULL;
    357 		return rs->sr_err;
    358 	}
    359 
    360 	dnRdn( &op->o_req_ndn, &nrdn );
    361 
    362 	for ( rdi = rd->rd_item; rdi != NULL; rdi = rdi->rdi_next ) {
    363 		struct berval	rdi_nrdn;
    364 
    365 		dnRdn( &rdi->rdi_ndn, &rdi_nrdn );
    366 		if ( dn_match( &nrdn, &rdi_nrdn ) ) {
    367 			break;
    368 		}
    369 	}
    370 
    371 	if ( rdi != NULL && rdi->rdi_mask != SN_DG_OP_ALL ) {
    372 		retcode_op_e	o_tag = SN_DG_OP_NONE;
    373 
    374 		switch ( op->o_tag ) {
    375 		case LDAP_REQ_ADD:
    376 			o_tag = SN_DG_OP_ADD;
    377 			break;
    378 
    379 		case LDAP_REQ_BIND:
    380 			o_tag = SN_DG_OP_BIND;
    381 			break;
    382 
    383 		case LDAP_REQ_COMPARE:
    384 			o_tag = SN_DG_OP_COMPARE;
    385 			break;
    386 
    387 		case LDAP_REQ_DELETE:
    388 			o_tag = SN_DG_OP_DELETE;
    389 			break;
    390 
    391 		case LDAP_REQ_MODIFY:
    392 			o_tag = SN_DG_OP_MODIFY;
    393 			break;
    394 
    395 		case LDAP_REQ_MODRDN:
    396 			o_tag = SN_DG_OP_RENAME;
    397 			break;
    398 
    399 		case LDAP_REQ_SEARCH:
    400 			o_tag = SN_DG_OP_SEARCH;
    401 			break;
    402 
    403 		case LDAP_REQ_EXTENDED:
    404 			o_tag = SN_DG_EXTENDED;
    405 			break;
    406 
    407 		default:
    408 			/* Should not happen */
    409 			break;
    410 		}
    411 
    412 		if ( !( o_tag & rdi->rdi_mask ) ) {
    413 			return SLAP_CB_CONTINUE;
    414 		}
    415 	}
    416 
    417 	if ( rdi == NULL ) {
    418 		rs->sr_matched = rd->rd_pdn.bv_val;
    419 		rs->sr_err = LDAP_NO_SUCH_OBJECT;
    420 		rs->sr_text = "retcode not found";
    421 
    422 	} else {
    423 		if ( rdi->rdi_flags & RDI_PRE_DISCONNECT ) {
    424 			return rs->sr_err = SLAPD_DISCONNECT;
    425 		}
    426 
    427 		rs->sr_err = rdi->rdi_err;
    428 		rs->sr_text = rdi->rdi_text.bv_val;
    429 		rs->sr_matched = rdi->rdi_matched.bv_val;
    430 
    431 		/* FIXME: we only honor the rdi_ref field in case rdi_err
    432 		 * is LDAP_REFERRAL otherwise send_ldap_result() bails out */
    433 		if ( rs->sr_err == LDAP_REFERRAL ) {
    434 			BerVarray	ref;
    435 
    436 			if ( rdi->rdi_ref != NULL ) {
    437 				ref = rdi->rdi_ref;
    438 			} else {
    439 				ref = default_referral;
    440 			}
    441 
    442 			if ( ref != NULL ) {
    443 				rs->sr_ref = referral_rewrite( ref,
    444 					NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
    445 
    446 			} else {
    447 				rs->sr_err = LDAP_OTHER;
    448 				rs->sr_text = "bad referral object";
    449 			}
    450 		}
    451 
    452 		retcode_sleep( rdi->rdi_sleeptime );
    453 	}
    454 
    455 	switch ( op->o_tag ) {
    456 	case LDAP_REQ_EXTENDED:
    457 		if ( rdi == NULL ) {
    458 			break;
    459 		}
    460 		cb = ( slap_callback * )ch_malloc( sizeof( slap_callback ) );
    461 		memset( cb, 0, sizeof( slap_callback ) );
    462 		cb->sc_cleanup = retcode_cleanup_cb;
    463 		op->o_callback = cb;
    464 		break;
    465 
    466 	default:
    467 		if ( rdi && !BER_BVISNULL( &rdi->rdi_unsolicited_oid ) ) {
    468 			ber_int_t	msgid = op->o_msgid;
    469 
    470 			/* RFC 4511 unsolicited response */
    471 
    472 			op->o_msgid = 0;
    473 			if ( strcmp( rdi->rdi_unsolicited_oid.bv_val, "0" ) == 0 ) {
    474 				send_ldap_result( op, rs );
    475 
    476 			} else {
    477 				ber_tag_t	tag = op->o_tag;
    478 
    479 				op->o_tag = LDAP_REQ_EXTENDED;
    480 				rs->sr_rspoid = rdi->rdi_unsolicited_oid.bv_val;
    481 				if ( !BER_BVISNULL( &rdi->rdi_unsolicited_data ) ) {
    482 					rs->sr_rspdata = &rdi->rdi_unsolicited_data;
    483 				}
    484 				send_ldap_extended( op, rs );
    485 				rs->sr_rspoid = NULL;
    486 				rs->sr_rspdata = NULL;
    487 				op->o_tag = tag;
    488 
    489 			}
    490 			op->o_msgid = msgid;
    491 
    492 		} else {
    493 			send_ldap_result( op, rs );
    494 		}
    495 
    496 		if ( rs->sr_ref != NULL ) {
    497 			ber_bvarray_free( rs->sr_ref );
    498 			rs->sr_ref = NULL;
    499 		}
    500 		rs->sr_matched = NULL;
    501 		rs->sr_text = NULL;
    502 
    503 		if ( rdi && rdi->rdi_flags & RDI_POST_DISCONNECT ) {
    504 			return rs->sr_err = SLAPD_DISCONNECT;
    505 		}
    506 		break;
    507 	}
    508 
    509 	return rs->sr_err;
    510 }
    511 
    512 static int
    513 retcode_op2str( ber_tag_t op, struct berval *bv )
    514 {
    515 	switch ( op ) {
    516 	case LDAP_REQ_BIND:
    517 		BER_BVSTR( bv, "bind" );
    518 		return 0;
    519 	case LDAP_REQ_ADD:
    520 		BER_BVSTR( bv, "add" );
    521 		return 0;
    522 	case LDAP_REQ_DELETE:
    523 		BER_BVSTR( bv, "delete" );
    524 		return 0;
    525 	case LDAP_REQ_MODRDN:
    526 		BER_BVSTR( bv, "modrdn" );
    527 		return 0;
    528 	case LDAP_REQ_MODIFY:
    529 		BER_BVSTR( bv, "modify" );
    530 		return 0;
    531 	case LDAP_REQ_COMPARE:
    532 		BER_BVSTR( bv, "compare" );
    533 		return 0;
    534 	case LDAP_REQ_SEARCH:
    535 		BER_BVSTR( bv, "search" );
    536 		return 0;
    537 	case LDAP_REQ_EXTENDED:
    538 		BER_BVSTR( bv, "extended" );
    539 		return 0;
    540 	}
    541 	return -1;
    542 }
    543 
    544 static int
    545 retcode_entry_response( Operation *op, SlapReply *rs, BackendInfo *bi, Entry *e )
    546 {
    547 	Attribute	*a;
    548 	int		err;
    549 	char		*next;
    550 	int		disconnect = 0;
    551 
    552 	if ( get_manageDSAit( op ) ) {
    553 		return SLAP_CB_CONTINUE;
    554 	}
    555 
    556 	if ( !is_entry_objectclass_or_sub( e, oc_errAbsObject ) ) {
    557 		return SLAP_CB_CONTINUE;
    558 	}
    559 
    560 	/* operation */
    561 	a = attr_find( e->e_attrs, ad_errOp );
    562 	if ( a != NULL ) {
    563 		int		i,
    564 				gotit = 0;
    565 		struct berval	bv = BER_BVNULL;
    566 
    567 		(void)retcode_op2str( op->o_tag, &bv );
    568 
    569 		if ( BER_BVISNULL( &bv ) ) {
    570 			return SLAP_CB_CONTINUE;
    571 		}
    572 
    573 		for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
    574 			if ( bvmatch( &a->a_nvals[ i ], &bv ) ) {
    575 				gotit = 1;
    576 				break;
    577 			}
    578 		}
    579 
    580 		if ( !gotit ) {
    581 			return SLAP_CB_CONTINUE;
    582 		}
    583 	}
    584 
    585 	/* disconnect */
    586 	a = attr_find( e->e_attrs, ad_errDisconnect );
    587 	if ( a != NULL ) {
    588 		if ( bvmatch( &a->a_nvals[ 0 ], &slap_true_bv ) ) {
    589 			return rs->sr_err = SLAPD_DISCONNECT;
    590 		}
    591 		disconnect = 1;
    592 	}
    593 
    594 	/* error code */
    595 	a = attr_find( e->e_attrs, ad_errCode );
    596 	if ( a == NULL ) {
    597 		return SLAP_CB_CONTINUE;
    598 	}
    599 	err = strtol( a->a_nvals[ 0 ].bv_val, &next, 0 );
    600 	if ( next == a->a_nvals[ 0 ].bv_val || next[ 0 ] != '\0' ) {
    601 		return SLAP_CB_CONTINUE;
    602 	}
    603 	rs->sr_err = err;
    604 
    605 	/* sleep time */
    606 	a = attr_find( e->e_attrs, ad_errSleepTime );
    607 	if ( a != NULL && a->a_nvals[ 0 ].bv_val[ 0 ] != '-' ) {
    608 		int	sleepTime;
    609 
    610 		if ( lutil_atoi( &sleepTime, a->a_nvals[ 0 ].bv_val ) == 0 ) {
    611 			retcode_sleep( sleepTime );
    612 		}
    613 	}
    614 
    615 	if ( rs->sr_err != LDAP_SUCCESS && !LDAP_API_ERROR( rs->sr_err )) {
    616 		BackendDB	db = *op->o_bd,
    617 				*o_bd = op->o_bd;
    618 		void		*o_callback = op->o_callback;
    619 
    620 		/* message text */
    621 		a = attr_find( e->e_attrs, ad_errText );
    622 		if ( a != NULL ) {
    623 			rs->sr_text = a->a_vals[ 0 ].bv_val;
    624 		}
    625 
    626 		/* matched DN */
    627 		a = attr_find( e->e_attrs, ad_errMatchedDN );
    628 		if ( a != NULL ) {
    629 			rs->sr_matched = a->a_vals[ 0 ].bv_val;
    630 		}
    631 
    632 		if ( bi == NULL ) {
    633 			slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
    634 
    635 			bi = on->on_info->oi_orig;
    636 		}
    637 
    638 		db.bd_info = bi;
    639 		op->o_bd = &db;
    640 		op->o_callback = NULL;
    641 
    642 		/* referral */
    643 		if ( rs->sr_err == LDAP_REFERRAL ) {
    644 			BerVarray	refs = default_referral;
    645 
    646 			a = attr_find( e->e_attrs, slap_schema.si_ad_ref );
    647 			if ( a != NULL ) {
    648 				refs = a->a_vals;
    649 			}
    650 			rs->sr_ref = referral_rewrite( refs,
    651 				NULL, &op->o_req_dn, op->oq_search.rs_scope );
    652 
    653 			send_search_reference( op, rs );
    654 			ber_bvarray_free( rs->sr_ref );
    655 			rs->sr_ref = NULL;
    656 
    657 		} else {
    658 			a = attr_find( e->e_attrs, ad_errUnsolicitedOID );
    659 			if ( a != NULL ) {
    660 				struct berval	oid = BER_BVNULL,
    661 						data = BER_BVNULL;
    662 				ber_int_t	msgid = op->o_msgid;
    663 
    664 				/* RFC 4511 unsolicited response */
    665 
    666 				op->o_msgid = 0;
    667 
    668 				oid = a->a_nvals[ 0 ];
    669 
    670 				a = attr_find( e->e_attrs, ad_errUnsolicitedData );
    671 				if ( a != NULL ) {
    672 					data = a->a_nvals[ 0 ];
    673 				}
    674 
    675 				if ( strcmp( oid.bv_val, "0" ) == 0 ) {
    676 					send_ldap_result( op, rs );
    677 
    678 				} else {
    679 					ber_tag_t	tag = op->o_tag;
    680 
    681 					op->o_tag = LDAP_REQ_EXTENDED;
    682 					rs->sr_rspoid = oid.bv_val;
    683 					if ( !BER_BVISNULL( &data ) ) {
    684 						rs->sr_rspdata = &data;
    685 					}
    686 					send_ldap_extended( op, rs );
    687 					rs->sr_rspoid = NULL;
    688 					rs->sr_rspdata = NULL;
    689 					op->o_tag = tag;
    690 				}
    691 				op->o_msgid = msgid;
    692 
    693 			} else {
    694 				send_ldap_result( op, rs );
    695 			}
    696 		}
    697 
    698 		rs->sr_text = NULL;
    699 		rs->sr_matched = NULL;
    700 		op->o_bd = o_bd;
    701 		op->o_callback = o_callback;
    702 	}
    703 
    704 	if ( rs->sr_err != LDAP_SUCCESS ) {
    705 		if ( disconnect ) {
    706 			return rs->sr_err = SLAPD_DISCONNECT;
    707 		}
    708 
    709 		op->o_abandon = 1;
    710 		return rs->sr_err;
    711 	}
    712 
    713 	return SLAP_CB_CONTINUE;
    714 }
    715 
    716 static int
    717 retcode_response( Operation *op, SlapReply *rs )
    718 {
    719 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
    720 	retcode_t	*rd = (retcode_t *)on->on_bi.bi_private;
    721 
    722 	if ( rs->sr_type != REP_SEARCH || !RETCODE_INDIR( rd ) ) {
    723 		return SLAP_CB_CONTINUE;
    724 	}
    725 
    726 	return retcode_entry_response( op, rs, NULL, rs->sr_entry );
    727 }
    728 
    729 static int
    730 retcode_db_init( BackendDB *be, ConfigReply *cr )
    731 {
    732 	slap_overinst	*on = (slap_overinst *)be->bd_info;
    733 	retcode_t	*rd;
    734 
    735 	srand( getpid() );
    736 
    737 	rd = (retcode_t *)ch_malloc( sizeof( retcode_t ) );
    738 	memset( rd, 0, sizeof( retcode_t ) );
    739 
    740 	on->on_bi.bi_private = (void *)rd;
    741 
    742 	return 0;
    743 }
    744 
    745 static int
    746 retcode_db_config(
    747 	BackendDB	*be,
    748 	const char	*fname,
    749 	int		lineno,
    750 	int		argc,
    751 	char		**argv )
    752 {
    753 	slap_overinst	*on = (slap_overinst *)be->bd_info;
    754 	retcode_t	*rd = (retcode_t *)on->on_bi.bi_private;
    755 
    756 	char			*argv0 = argv[ 0 ] + STRLENOF( "retcode-" );
    757 
    758 	if ( strncasecmp( argv[ 0 ], "retcode-", STRLENOF( "retcode-" ) ) != 0 ) {
    759 		return SLAP_CONF_UNKNOWN;
    760 	}
    761 
    762 	if ( strcasecmp( argv0, "parent" ) == 0 ) {
    763 		struct berval	dn;
    764 		int		rc;
    765 
    766 		if ( argc != 2 ) {
    767 			fprintf( stderr, "%s: line %d: retcode: "
    768 				"\"retcode-parent <DN>\": missing <DN>\n",
    769 				fname, lineno );
    770 			return 1;
    771 		}
    772 
    773 		if ( !BER_BVISNULL( &rd->rd_pdn ) ) {
    774 			fprintf( stderr, "%s: line %d: retcode: "
    775 				"parent already defined.\n", fname, lineno );
    776 			return 1;
    777 		}
    778 
    779 		ber_str2bv( argv[ 1 ], 0, 0, &dn );
    780 
    781 		rc = dnPrettyNormal( NULL, &dn, &rd->rd_pdn, &rd->rd_npdn, NULL );
    782 		if ( rc != LDAP_SUCCESS ) {
    783 			fprintf( stderr, "%s: line %d: retcode: "
    784 				"unable to normalize parent DN \"%s\": %d\n",
    785 				fname, lineno, argv[ 1 ], rc );
    786 			return 1;
    787 		}
    788 
    789 	} else if ( strcasecmp( argv0, "item" ) == 0 ) {
    790 		retcode_item_t	rdi = { BER_BVNULL }, **rdip;
    791 		struct berval		bv, rdn, nrdn;
    792 		int			rc;
    793 		char			*next = NULL;
    794 
    795 		if ( argc < 3 ) {
    796 			fprintf( stderr, "%s: line %d: retcode: "
    797 				"\"retcode-item <RDN> <retcode> [<text>]\": "
    798 				"missing args\n",
    799 				fname, lineno );
    800 			return 1;
    801 		}
    802 
    803 		ber_str2bv( argv[ 1 ], 0, 0, &bv );
    804 
    805 		rc = dnPrettyNormal( NULL, &bv, &rdn, &nrdn, NULL );
    806 		if ( rc != LDAP_SUCCESS ) {
    807 			fprintf( stderr, "%s: line %d: retcode: "
    808 				"unable to normalize RDN \"%s\": %d\n",
    809 				fname, lineno, argv[ 1 ], rc );
    810 			return 1;
    811 		}
    812 
    813 		if ( !dnIsOneLevelRDN( &nrdn ) ) {
    814 			fprintf( stderr, "%s: line %d: retcode: "
    815 				"value \"%s\" is not a RDN\n",
    816 				fname, lineno, argv[ 1 ] );
    817 			return 1;
    818 		}
    819 
    820 		if ( BER_BVISNULL( &rd->rd_npdn ) ) {
    821 			/* FIXME: we use the database suffix */
    822 			if ( be->be_nsuffix == NULL ) {
    823 				fprintf( stderr, "%s: line %d: retcode: "
    824 					"either \"retcode-parent\" "
    825 					"or \"suffix\" must be defined.\n",
    826 					fname, lineno );
    827 				return 1;
    828 			}
    829 
    830 			ber_dupbv( &rd->rd_pdn, &be->be_suffix[ 0 ] );
    831 			ber_dupbv( &rd->rd_npdn, &be->be_nsuffix[ 0 ] );
    832 		}
    833 
    834 		build_new_dn( &rdi.rdi_dn, &rd->rd_pdn, &rdn, NULL );
    835 		build_new_dn( &rdi.rdi_ndn, &rd->rd_npdn, &nrdn, NULL );
    836 
    837 		ch_free( rdn.bv_val );
    838 		ch_free( nrdn.bv_val );
    839 
    840 		rdi.rdi_err = strtol( argv[ 2 ], &next, 0 );
    841 		if ( next == argv[ 2 ] || next[ 0 ] != '\0' ) {
    842 			fprintf( stderr, "%s: line %d: retcode: "
    843 				"unable to parse return code \"%s\"\n",
    844 				fname, lineno, argv[ 2 ] );
    845 			return 1;
    846 		}
    847 
    848 		rdi.rdi_mask = SN_DG_OP_ALL;
    849 
    850 		if ( argc > 3 ) {
    851 			int	i;
    852 
    853 			for ( i = 3; i < argc; i++ ) {
    854 				if ( strncasecmp( argv[ i ], "op=", STRLENOF( "op=" ) ) == 0 )
    855 				{
    856 					char		**ops;
    857 					int		j;
    858 
    859 					ops = ldap_str2charray( &argv[ i ][ STRLENOF( "op=" ) ], "," );
    860 					assert( ops != NULL );
    861 
    862 					rdi.rdi_mask = SN_DG_OP_NONE;
    863 
    864 					for ( j = 0; ops[ j ] != NULL; j++ ) {
    865 						if ( strcasecmp( ops[ j ], "add" ) == 0 ) {
    866 							rdi.rdi_mask |= SN_DG_OP_ADD;
    867 
    868 						} else if ( strcasecmp( ops[ j ], "bind" ) == 0 ) {
    869 							rdi.rdi_mask |= SN_DG_OP_BIND;
    870 
    871 						} else if ( strcasecmp( ops[ j ], "compare" ) == 0 ) {
    872 							rdi.rdi_mask |= SN_DG_OP_COMPARE;
    873 
    874 						} else if ( strcasecmp( ops[ j ], "delete" ) == 0 ) {
    875 							rdi.rdi_mask |= SN_DG_OP_DELETE;
    876 
    877 						} else if ( strcasecmp( ops[ j ], "modify" ) == 0 ) {
    878 							rdi.rdi_mask |= SN_DG_OP_MODIFY;
    879 
    880 						} else if ( strcasecmp( ops[ j ], "rename" ) == 0
    881 							|| strcasecmp( ops[ j ], "modrdn" ) == 0 )
    882 						{
    883 							rdi.rdi_mask |= SN_DG_OP_RENAME;
    884 
    885 						} else if ( strcasecmp( ops[ j ], "search" ) == 0 ) {
    886 							rdi.rdi_mask |= SN_DG_OP_SEARCH;
    887 
    888 						} else if ( strcasecmp( ops[ j ], "extended" ) == 0 ) {
    889 							rdi.rdi_mask |= SN_DG_EXTENDED;
    890 
    891 						} else if ( strcasecmp( ops[ j ], "auth" ) == 0 ) {
    892 							rdi.rdi_mask |= SN_DG_OP_AUTH;
    893 
    894 						} else if ( strcasecmp( ops[ j ], "read" ) == 0 ) {
    895 							rdi.rdi_mask |= SN_DG_OP_READ;
    896 
    897 						} else if ( strcasecmp( ops[ j ], "write" ) == 0 ) {
    898 							rdi.rdi_mask |= SN_DG_OP_WRITE;
    899 
    900 						} else if ( strcasecmp( ops[ j ], "all" ) == 0 ) {
    901 							rdi.rdi_mask |= SN_DG_OP_ALL;
    902 
    903 						} else {
    904 							fprintf( stderr, "retcode: unknown op \"%s\"\n",
    905 								ops[ j ] );
    906 							ldap_charray_free( ops );
    907 							return 1;
    908 						}
    909 					}
    910 
    911 					ldap_charray_free( ops );
    912 
    913 				} else if ( strncasecmp( argv[ i ], "text=", STRLENOF( "text=" ) ) == 0 )
    914 				{
    915 					if ( !BER_BVISNULL( &rdi.rdi_text ) ) {
    916 						fprintf( stderr, "%s: line %d: retcode: "
    917 							"\"text\" already provided.\n",
    918 							fname, lineno );
    919 						return 1;
    920 					}
    921 					ber_str2bv( &argv[ i ][ STRLENOF( "text=" ) ], 0, 1, &rdi.rdi_text );
    922 
    923 				} else if ( strncasecmp( argv[ i ], "matched=", STRLENOF( "matched=" ) ) == 0 )
    924 				{
    925 					struct berval	dn;
    926 
    927 					if ( !BER_BVISNULL( &rdi.rdi_matched ) ) {
    928 						fprintf( stderr, "%s: line %d: retcode: "
    929 							"\"matched\" already provided.\n",
    930 							fname, lineno );
    931 						return 1;
    932 					}
    933 					ber_str2bv( &argv[ i ][ STRLENOF( "matched=" ) ], 0, 0, &dn );
    934 					if ( dnPretty( NULL, &dn, &rdi.rdi_matched, NULL ) != LDAP_SUCCESS ) {
    935 						fprintf( stderr, "%s: line %d: retcode: "
    936 							"unable to prettify matched DN \"%s\".\n",
    937 							fname, lineno, &argv[ i ][ STRLENOF( "matched=" ) ] );
    938 						return 1;
    939 					}
    940 
    941 				} else if ( strncasecmp( argv[ i ], "ref=", STRLENOF( "ref=" ) ) == 0 )
    942 				{
    943 					char		**refs;
    944 					int		j;
    945 
    946 					if ( rdi.rdi_ref != NULL ) {
    947 						fprintf( stderr, "%s: line %d: retcode: "
    948 							"\"ref\" already provided.\n",
    949 							fname, lineno );
    950 						return 1;
    951 					}
    952 
    953 					if ( rdi.rdi_err != LDAP_REFERRAL ) {
    954 						fprintf( stderr, "%s: line %d: retcode: "
    955 							"providing \"ref\"\n"
    956 							"\talong with a non-referral "
    957 							"resultCode may cause slapd failures\n"
    958 							"\trelated to internal checks.\n",
    959 							fname, lineno );
    960 					}
    961 
    962 					refs = ldap_str2charray( &argv[ i ][ STRLENOF( "ref=" ) ], " " );
    963 					assert( refs != NULL );
    964 
    965 					for ( j = 0; refs[ j ] != NULL; j++ ) {
    966 						struct berval	bv;
    967 
    968 						ber_str2bv( refs[ j ], 0, 1, &bv );
    969 						ber_bvarray_add( &rdi.rdi_ref, &bv );
    970 					}
    971 
    972 					ldap_charray_free( refs );
    973 
    974 				} else if ( strncasecmp( argv[ i ], "sleeptime=", STRLENOF( "sleeptime=" ) ) == 0 )
    975 				{
    976 					if ( rdi.rdi_sleeptime != 0 ) {
    977 						fprintf( stderr, "%s: line %d: retcode: "
    978 							"\"sleeptime\" already provided.\n",
    979 							fname, lineno );
    980 						return 1;
    981 					}
    982 
    983 					if ( lutil_atoi( &rdi.rdi_sleeptime, &argv[ i ][ STRLENOF( "sleeptime=" ) ] ) ) {
    984 						fprintf( stderr, "%s: line %d: retcode: "
    985 							"unable to parse \"sleeptime=%s\".\n",
    986 							fname, lineno, &argv[ i ][ STRLENOF( "sleeptime=" ) ] );
    987 						return 1;
    988 					}
    989 
    990 				} else if ( strncasecmp( argv[ i ], "unsolicited=", STRLENOF( "unsolicited=" ) ) == 0 )
    991 				{
    992 					char		*data;
    993 
    994 					if ( !BER_BVISNULL( &rdi.rdi_unsolicited_oid ) ) {
    995 						fprintf( stderr, "%s: line %d: retcode: "
    996 							"\"unsolicited\" already provided.\n",
    997 							fname, lineno );
    998 						return 1;
    999 					}
   1000 
   1001 					data = strchr( &argv[ i ][ STRLENOF( "unsolicited=" ) ], ':' );
   1002 					if ( data != NULL ) {
   1003 						struct berval	oid;
   1004 
   1005 						if ( ldif_parse_line2( &argv[ i ][ STRLENOF( "unsolicited=" ) ],
   1006 							&oid, &rdi.rdi_unsolicited_data, NULL ) )
   1007 						{
   1008 							fprintf( stderr, "%s: line %d: retcode: "
   1009 								"unable to parse \"unsolicited\".\n",
   1010 								fname, lineno );
   1011 							return 1;
   1012 						}
   1013 
   1014 						ber_dupbv( &rdi.rdi_unsolicited_oid, &oid );
   1015 
   1016 					} else {
   1017 						ber_str2bv( &argv[ i ][ STRLENOF( "unsolicited=" ) ], 0, 1,
   1018 							&rdi.rdi_unsolicited_oid );
   1019 					}
   1020 
   1021 				} else if ( strncasecmp( argv[ i ], "flags=", STRLENOF( "flags=" ) ) == 0 )
   1022 				{
   1023 					char *arg = &argv[ i ][ STRLENOF( "flags=" ) ];
   1024 					if ( strcasecmp( arg, "disconnect" ) == 0 ) {
   1025 						rdi.rdi_flags |= RDI_PRE_DISCONNECT;
   1026 
   1027 					} else if ( strcasecmp( arg, "pre-disconnect" ) == 0 ) {
   1028 						rdi.rdi_flags |= RDI_PRE_DISCONNECT;
   1029 
   1030 					} else if ( strcasecmp( arg, "post-disconnect" ) == 0 ) {
   1031 						rdi.rdi_flags |= RDI_POST_DISCONNECT;
   1032 
   1033 					} else {
   1034 						fprintf( stderr, "%s: line %d: retcode: "
   1035 							"unknown flag \"%s\".\n",
   1036 							fname, lineno, arg );
   1037 						return 1;
   1038 					}
   1039 
   1040 				} else {
   1041 					fprintf( stderr, "%s: line %d: retcode: "
   1042 						"unknown option \"%s\".\n",
   1043 						fname, lineno, argv[ i ] );
   1044 					return 1;
   1045 				}
   1046 			}
   1047 		}
   1048 
   1049 		for ( rdip = &rd->rd_item; *rdip; rdip = &(*rdip)->rdi_next )
   1050 			/* go to last */ ;
   1051 
   1052 
   1053 		*rdip = ( retcode_item_t * )ch_malloc( sizeof( retcode_item_t ) );
   1054 		*(*rdip) = rdi;
   1055 
   1056 	} else if ( strcasecmp( argv0, "indir" ) == 0 ) {
   1057 		rd->rd_flags |= RETCODE_FINDIR;
   1058 
   1059 	} else if ( strcasecmp( argv0, "sleep" ) == 0 ) {
   1060 		switch ( argc ) {
   1061 		case 1:
   1062 			fprintf( stderr, "%s: line %d: retcode: "
   1063 				"\"retcode-sleep <time>\": missing <time>\n",
   1064 				fname, lineno );
   1065 			return 1;
   1066 
   1067 		case 2:
   1068 			break;
   1069 
   1070 		default:
   1071 			fprintf( stderr, "%s: line %d: retcode: "
   1072 				"\"retcode-sleep <time>\": extra cruft after <time>\n",
   1073 				fname, lineno );
   1074 			return 1;
   1075 		}
   1076 
   1077 		if ( lutil_atoi( &rd->rd_sleep, argv[ 1 ] ) != 0 ) {
   1078 			fprintf( stderr, "%s: line %d: retcode: "
   1079 				"\"retcode-sleep <time>\": unable to parse <time>\n",
   1080 				fname, lineno );
   1081 			return 1;
   1082 		}
   1083 
   1084 	} else {
   1085 		return SLAP_CONF_UNKNOWN;
   1086 	}
   1087 
   1088 	return 0;
   1089 }
   1090 
   1091 static int
   1092 retcode_db_open( BackendDB *be, ConfigReply *cr)
   1093 {
   1094 	slap_overinst	*on = (slap_overinst *)be->bd_info;
   1095 	retcode_t	*rd = (retcode_t *)on->on_bi.bi_private;
   1096 
   1097 	retcode_item_t	*rdi;
   1098 
   1099 	for ( rdi = rd->rd_item; rdi; rdi = rdi->rdi_next ) {
   1100 		LDAPRDN			rdn = NULL;
   1101 		int			rc, j;
   1102 		char*			p;
   1103 		struct berval		val[ 3 ];
   1104 		char			buf[ SLAP_TEXT_BUFLEN ];
   1105 
   1106 		/* DN */
   1107 		rdi->rdi_e.e_name = rdi->rdi_dn;
   1108 		rdi->rdi_e.e_nname = rdi->rdi_ndn;
   1109 
   1110 		/* objectClass */
   1111 		val[ 0 ] = oc_errObject->soc_cname;
   1112 		val[ 1 ] = slap_schema.si_oc_extensibleObject->soc_cname;
   1113 		BER_BVZERO( &val[ 2 ] );
   1114 
   1115 		attr_merge( &rdi->rdi_e, slap_schema.si_ad_objectClass, val, NULL );
   1116 
   1117 		/* RDN avas */
   1118 		rc = ldap_bv2rdn( &rdi->rdi_dn, &rdn, (char **) &p,
   1119 				LDAP_DN_FORMAT_LDAP );
   1120 
   1121 		assert( rc == LDAP_SUCCESS );
   1122 
   1123 		for ( j = 0; rdn[ j ]; j++ ) {
   1124 			LDAPAVA			*ava = rdn[ j ];
   1125 			AttributeDescription	*ad = NULL;
   1126 			const char		*text;
   1127 
   1128 			rc = slap_bv2ad( &ava->la_attr, &ad, &text );
   1129 			assert( rc == LDAP_SUCCESS );
   1130 
   1131 			attr_merge_normalize_one( &rdi->rdi_e, ad,
   1132 					&ava->la_value, NULL );
   1133 		}
   1134 
   1135 		ldap_rdnfree( rdn );
   1136 
   1137 		/* error code */
   1138 		snprintf( buf, sizeof( buf ), "%d", rdi->rdi_err );
   1139 		ber_str2bv( buf, 0, 0, &val[ 0 ] );
   1140 
   1141 		attr_merge_one( &rdi->rdi_e, ad_errCode, &val[ 0 ], NULL );
   1142 
   1143 		if ( rdi->rdi_ref != NULL ) {
   1144 			attr_merge_normalize( &rdi->rdi_e, slap_schema.si_ad_ref,
   1145 				rdi->rdi_ref, NULL );
   1146 		}
   1147 
   1148 		/* text */
   1149 		if ( !BER_BVISNULL( &rdi->rdi_text ) ) {
   1150 			val[ 0 ] = rdi->rdi_text;
   1151 
   1152 			attr_merge_normalize_one( &rdi->rdi_e, ad_errText, &val[ 0 ], NULL );
   1153 		}
   1154 
   1155 		/* matched */
   1156 		if ( !BER_BVISNULL( &rdi->rdi_matched ) ) {
   1157 			val[ 0 ] = rdi->rdi_matched;
   1158 
   1159 			attr_merge_normalize_one( &rdi->rdi_e, ad_errMatchedDN, &val[ 0 ], NULL );
   1160 		}
   1161 
   1162 		/* sleep time */
   1163 		if ( rdi->rdi_sleeptime ) {
   1164 			snprintf( buf, sizeof( buf ), "%d", rdi->rdi_sleeptime );
   1165 			ber_str2bv( buf, 0, 0, &val[ 0 ] );
   1166 
   1167 			attr_merge_one( &rdi->rdi_e, ad_errSleepTime, &val[ 0 ], NULL );
   1168 		}
   1169 
   1170 		/* operations */
   1171 		if ( rdi->rdi_mask & SN_DG_OP_ADD ) {
   1172 			BER_BVSTR( &val[ 0 ], "add" );
   1173 			attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
   1174 		}
   1175 
   1176 		if ( rdi->rdi_mask & SN_DG_OP_BIND ) {
   1177 			BER_BVSTR( &val[ 0 ], "bind" );
   1178 			attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
   1179 		}
   1180 
   1181 		if ( rdi->rdi_mask & SN_DG_OP_COMPARE ) {
   1182 			BER_BVSTR( &val[ 0 ], "compare" );
   1183 			attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
   1184 		}
   1185 
   1186 		if ( rdi->rdi_mask & SN_DG_OP_DELETE ) {
   1187 			BER_BVSTR( &val[ 0 ], "delete" );
   1188 			attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
   1189 		}
   1190 
   1191 		if ( rdi->rdi_mask & SN_DG_EXTENDED ) {
   1192 			BER_BVSTR( &val[ 0 ], "extended" );
   1193 			attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
   1194 		}
   1195 
   1196 		if ( rdi->rdi_mask & SN_DG_OP_MODIFY ) {
   1197 			BER_BVSTR( &val[ 0 ], "modify" );
   1198 			attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
   1199 		}
   1200 
   1201 		if ( rdi->rdi_mask & SN_DG_OP_RENAME ) {
   1202 			BER_BVSTR( &val[ 0 ], "rename" );
   1203 			attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
   1204 		}
   1205 
   1206 		if ( rdi->rdi_mask & SN_DG_OP_SEARCH ) {
   1207 			BER_BVSTR( &val[ 0 ], "search" );
   1208 			attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
   1209 		}
   1210 	}
   1211 
   1212 	return 0;
   1213 }
   1214 
   1215 static int
   1216 retcode_db_destroy( BackendDB *be, ConfigReply *cr )
   1217 {
   1218 	slap_overinst	*on = (slap_overinst *)be->bd_info;
   1219 	retcode_t	*rd = (retcode_t *)on->on_bi.bi_private;
   1220 
   1221 	if ( rd ) {
   1222 		retcode_item_t	*rdi, *next;
   1223 
   1224 		for ( rdi = rd->rd_item; rdi != NULL; rdi = next ) {
   1225 			ber_memfree( rdi->rdi_dn.bv_val );
   1226 			ber_memfree( rdi->rdi_ndn.bv_val );
   1227 
   1228 			if ( !BER_BVISNULL( &rdi->rdi_text ) ) {
   1229 				ber_memfree( rdi->rdi_text.bv_val );
   1230 			}
   1231 
   1232 			if ( !BER_BVISNULL( &rdi->rdi_matched ) ) {
   1233 				ber_memfree( rdi->rdi_matched.bv_val );
   1234 			}
   1235 
   1236 			if ( rdi->rdi_ref ) {
   1237 				ber_bvarray_free( rdi->rdi_ref );
   1238 			}
   1239 
   1240 			BER_BVZERO( &rdi->rdi_e.e_name );
   1241 			BER_BVZERO( &rdi->rdi_e.e_nname );
   1242 
   1243 			entry_clean( &rdi->rdi_e );
   1244 
   1245 			next = rdi->rdi_next;
   1246 
   1247 			ch_free( rdi );
   1248 		}
   1249 
   1250 		if ( !BER_BVISNULL( &rd->rd_pdn ) ) {
   1251 			ber_memfree( rd->rd_pdn.bv_val );
   1252 		}
   1253 
   1254 		if ( !BER_BVISNULL( &rd->rd_npdn ) ) {
   1255 			ber_memfree( rd->rd_npdn.bv_val );
   1256 		}
   1257 
   1258 		ber_memfree( rd );
   1259 	}
   1260 
   1261 	return 0;
   1262 }
   1263 
   1264 #if SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC
   1265 static
   1266 #endif /* SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC */
   1267 int
   1268 retcode_initialize( void )
   1269 {
   1270 	int		i, code;
   1271 
   1272 	static struct {
   1273 		char			*desc;
   1274 		AttributeDescription	**ad;
   1275 	} retcode_at[] = {
   1276 	        { "( 1.3.6.1.4.1.4203.666.11.4.1.1 "
   1277 		        "NAME ( 'errCode' ) "
   1278 		        "DESC 'LDAP error code' "
   1279 		        "EQUALITY integerMatch "
   1280 		        "ORDERING integerOrderingMatch "
   1281 		        "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
   1282 			"SINGLE-VALUE )",
   1283 			&ad_errCode },
   1284 		{ "( 1.3.6.1.4.1.4203.666.11.4.1.2 "
   1285 			"NAME ( 'errOp' ) "
   1286 			"DESC 'Operations the errObject applies to' "
   1287 			"EQUALITY caseIgnoreMatch "
   1288 			"SUBSTR caseIgnoreSubstringsMatch "
   1289 			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
   1290 			&ad_errOp},
   1291 		{ "( 1.3.6.1.4.1.4203.666.11.4.1.3 "
   1292 			"NAME ( 'errText' ) "
   1293 			"DESC 'LDAP error textual description' "
   1294 			"EQUALITY caseIgnoreMatch "
   1295 			"SUBSTR caseIgnoreSubstringsMatch "
   1296 			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
   1297 			"SINGLE-VALUE )",
   1298 			&ad_errText },
   1299 		{ "( 1.3.6.1.4.1.4203.666.11.4.1.4 "
   1300 			"NAME ( 'errSleepTime' ) "
   1301 			"DESC 'Time to wait before returning the error' "
   1302 			"EQUALITY integerMatch "
   1303 			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
   1304 			"SINGLE-VALUE )",
   1305 			&ad_errSleepTime },
   1306 		{ "( 1.3.6.1.4.1.4203.666.11.4.1.5 "
   1307 			"NAME ( 'errMatchedDN' ) "
   1308 			"DESC 'Value to be returned as matched DN' "
   1309 			"EQUALITY distinguishedNameMatch "
   1310 			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
   1311 			"SINGLE-VALUE )",
   1312 			&ad_errMatchedDN },
   1313 		{ "( 1.3.6.1.4.1.4203.666.11.4.1.6 "
   1314 			"NAME ( 'errUnsolicitedOID' ) "
   1315 			"DESC 'OID to be returned within unsolicited response' "
   1316 			"EQUALITY objectIdentifierMatch "
   1317 			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
   1318 			"SINGLE-VALUE )",
   1319 			&ad_errUnsolicitedOID },
   1320 		{ "( 1.3.6.1.4.1.4203.666.11.4.1.7 "
   1321 			"NAME ( 'errUnsolicitedData' ) "
   1322 			"DESC 'Data to be returned within unsolicited response' "
   1323 			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 "
   1324 			"SINGLE-VALUE )",
   1325 			&ad_errUnsolicitedData },
   1326 		{ "( 1.3.6.1.4.1.4203.666.11.4.1.8 "
   1327 			"NAME ( 'errDisconnect' ) "
   1328 			"DESC 'Disconnect without notice' "
   1329 			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
   1330 			"SINGLE-VALUE )",
   1331 			&ad_errDisconnect },
   1332 		{ NULL }
   1333 	};
   1334 
   1335 	static struct {
   1336 		char		*desc;
   1337 		ObjectClass	**oc;
   1338 	} retcode_oc[] = {
   1339 		{ "( 1.3.6.1.4.1.4203.666.11.4.3.0 "
   1340 			"NAME ( 'errAbsObject' ) "
   1341 			"SUP top ABSTRACT "
   1342 			"MUST ( errCode ) "
   1343 			"MAY ( "
   1344 				"cn "
   1345 				"$ description "
   1346 				"$ errOp "
   1347 				"$ errText "
   1348 				"$ errSleepTime "
   1349 				"$ errMatchedDN "
   1350 				"$ errUnsolicitedOID "
   1351 				"$ errUnsolicitedData "
   1352 				"$ errDisconnect "
   1353 			") )",
   1354 			&oc_errAbsObject },
   1355 		{ "( 1.3.6.1.4.1.4203.666.11.4.3.1 "
   1356 			"NAME ( 'errObject' ) "
   1357 			"SUP errAbsObject STRUCTURAL "
   1358 			")",
   1359 			&oc_errObject },
   1360 		{ "( 1.3.6.1.4.1.4203.666.11.4.3.2 "
   1361 			"NAME ( 'errAuxObject' ) "
   1362 			"SUP errAbsObject AUXILIARY "
   1363 			")",
   1364 			&oc_errAuxObject },
   1365 		{ NULL }
   1366 	};
   1367 
   1368 
   1369 	for ( i = 0; retcode_at[ i ].desc != NULL; i++ ) {
   1370 		code = register_at( retcode_at[ i ].desc, retcode_at[ i ].ad, 0 );
   1371 		if ( code ) {
   1372 			Debug( LDAP_DEBUG_ANY,
   1373 				"retcode: register_at failed\n", 0, 0, 0 );
   1374 			return code;
   1375 		}
   1376 
   1377 		(*retcode_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
   1378 	}
   1379 
   1380 	for ( i = 0; retcode_oc[ i ].desc != NULL; i++ ) {
   1381 		code = register_oc( retcode_oc[ i ].desc, retcode_oc[ i ].oc, 0 );
   1382 		if ( code ) {
   1383 			Debug( LDAP_DEBUG_ANY,
   1384 				"retcode: register_oc failed\n", 0, 0, 0 );
   1385 			return code;
   1386 		}
   1387 
   1388 		(*retcode_oc[ i ].oc)->soc_flags |= SLAP_OC_HIDE;
   1389 	}
   1390 
   1391 	retcode.on_bi.bi_type = "retcode";
   1392 
   1393 	retcode.on_bi.bi_db_init = retcode_db_init;
   1394 	retcode.on_bi.bi_db_config = retcode_db_config;
   1395 	retcode.on_bi.bi_db_open = retcode_db_open;
   1396 	retcode.on_bi.bi_db_destroy = retcode_db_destroy;
   1397 
   1398 	retcode.on_bi.bi_op_add = retcode_op_func;
   1399 	retcode.on_bi.bi_op_bind = retcode_op_func;
   1400 	retcode.on_bi.bi_op_compare = retcode_op_func;
   1401 	retcode.on_bi.bi_op_delete = retcode_op_func;
   1402 	retcode.on_bi.bi_op_modify = retcode_op_func;
   1403 	retcode.on_bi.bi_op_modrdn = retcode_op_func;
   1404 	retcode.on_bi.bi_op_search = retcode_op_func;
   1405 
   1406 	retcode.on_bi.bi_extended = retcode_op_func;
   1407 
   1408 	retcode.on_response = retcode_response;
   1409 
   1410 	return overlay_register( &retcode );
   1411 }
   1412 
   1413 #if SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC
   1414 int
   1415 init_module( int argc, char *argv[] )
   1416 {
   1417 	return retcode_initialize();
   1418 }
   1419 #endif /* SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC */
   1420 
   1421 #endif /* SLAPD_OVER_RETCODE */
   1422