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