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