xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/overlays/retcode.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
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@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