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