]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/conn.c
Fix cursor initialization, scope IDs
[openldap] / servers / slapd / back-meta / conn.c
1 /*
2  * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
3  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
4  *
5  * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
6  *
7  * This work has been developed to fulfill the requirements
8  * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
9  * to the OpenLDAP Foundation in the hope that it may be useful
10  * to the Open Source community, but WITHOUT ANY WARRANTY.
11  *
12  * Permission is granted to anyone to use this software for any purpose
13  * on any computer system, and to alter it and redistribute it, subject
14  * to the following restrictions:
15  *
16  * 1. The author and SysNet s.n.c. are not responsible for the consequences
17  *    of use of this software, no matter how awful, even if they arise from 
18  *    flaws in it.
19  *
20  * 2. The origin of this software must not be misrepresented, either by
21  *    explicit claim or by omission.  Since few users ever read sources,
22  *    credits should appear in the documentation.
23  *
24  * 3. Altered versions must be plainly marked as such, and must not be
25  *    misrepresented as being the original software.  Since few users
26  *    ever read sources, credits should appear in the documentation.
27  *    SysNet s.n.c. cannot be responsible for the consequences of the
28  *    alterations.
29  *
30  * 4. This notice may not be removed or altered.
31  *
32  *
33  * This software is based on the backend back-ldap, implemented
34  * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
35  * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
36  * contributors. The contribution of the original software to the present
37  * implementation is acknowledged in this copyright statement.
38  *
39  * A special acknowledgement goes to Howard for the overall architecture
40  * (and for borrowing large pieces of code), and to Mark, who implemented
41  * from scratch the attribute/objectclass mapping.
42  *
43  * The original copyright statement follows.
44  *
45  * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
46  *
47  * Permission is granted to anyone to use this software for any purpose
48  * on any computer system, and to alter it and redistribute it, subject
49  * to the following restrictions:
50  *
51  * 1. The author is not responsible for the consequences of use of this
52  *    software, no matter how awful, even if they arise from flaws in it.
53  *
54  * 2. The origin of this software must not be misrepresented, either by
55  *    explicit claim or by omission.  Since few users ever read sources,
56  *    credits should appear in the documentation.
57  *
58  * 3. Altered versions must be plainly marked as such, and must not be
59  *    misrepresented as being the original software.  Since few users
60  *    ever read sources, credits should appear in the
61  *    documentation.
62  *
63  * 4. This notice may not be removed or altered.
64  *
65  */
66
67 #include "portable.h"
68
69 #include <stdio.h>
70
71 #include <ac/socket.h>
72 #include <ac/string.h>
73
74
75 #define AVL_INTERNAL
76 #include "slap.h"
77 #include "../back-ldap/back-ldap.h"
78 #include "back-meta.h"
79
80 /*
81  * Set PRINT_CONNTREE larger than 0 to dump the connection tree (debug only)
82  */
83 #define PRINT_CONNTREE 0
84
85 /*
86  * meta_back_conn_cmp
87  *
88  * compares two struct metaconn based on the value of the conn pointer;
89  * used by avl stuff
90  */
91 int
92 meta_back_conn_cmp(
93         const void *c1,
94         const void *c2
95         )
96 {
97         struct metaconn *lc1 = ( struct metaconn * )c1;
98         struct metaconn *lc2 = ( struct metaconn * )c2;
99         
100         return ( ( lc1->conn < lc2->conn ) ? -1 :
101                         ( ( lc1->conn > lc2-> conn ) ? 1 : 0 ) );
102 }
103
104 /*
105  * meta_back_conn_dup
106  *
107  * returns -1 in case a duplicate struct metaconn has been inserted;
108  * used by avl stuff
109  */
110 int
111 meta_back_conn_dup(
112         void *c1,
113         void *c2
114         )
115 {
116         struct metaconn *lc1 = ( struct metaconn * )c1;
117         struct metaconn *lc2 = ( struct metaconn * )c2;
118
119         return( ( lc1->conn == lc2->conn ) ? -1 : 0 );
120 }
121
122 /*
123  * Debug stuff (got it from libavl)
124  */
125 #if PRINT_CONNTREE > 0
126 static void
127 ravl_print( Avlnode *root, int depth )
128 {
129         int     i;
130         
131         if ( root == 0 ) {
132                 return;
133         }
134         
135         ravl_print( root->avl_right, depth+1 );
136         
137         for ( i = 0; i < depth; i++ ) {
138                 printf( "    " );
139         }
140
141         printf( "c(%d) %d\n", ( ( struct metaconn * )root->avl_data )->conn->c_connid, root->avl_bf );
142         
143         ravl_print( root->avl_left, depth+1 );
144 }
145
146 static void
147 myprint( Avlnode *root )
148 {
149         printf( "********\n" );
150         
151         if ( root == 0 ) {
152                 printf( "\tNULL\n" );
153         } else {
154                 ravl_print( root, 0 );
155         }
156         
157         printf( "********\n" );
158 }
159 #endif /* PRINT_CONNTREE */
160 /*
161  * End of debug stuff
162  */
163
164 /*
165  * metaconn_alloc
166  * 
167  * Allocates a connection structure, making room for all the referenced targets
168  */
169 static struct metaconn *
170 metaconn_alloc( int ntargets )
171 {
172         struct metaconn *lc;
173
174         assert( ntargets > 0 );
175
176         lc = ch_calloc( sizeof( struct metaconn ), 1 );
177         if ( lc == NULL ) {
178                 return NULL;
179         }
180         
181         /*
182          * make it a null-terminated array ...
183          */
184         lc->conns = ch_calloc( sizeof( struct metasingleconn ), ntargets+1 );
185         if ( lc->conns == NULL ) {
186                 free( lc );
187                 return NULL;
188         }
189         lc->conns[ ntargets ].candidate = META_LAST_CONN;
190
191         lc->bound_target = META_BOUND_NONE;
192
193         return lc;
194 }
195
196 /*
197  * metaconn_free
198  *
199  * clears a metaconn
200  */
201 static void
202 metaconn_free(
203                 struct metaconn *lc
204 )
205 {
206         if ( !lc ) {
207                 return;
208         }
209         
210         if ( lc->conns ) {
211                 ch_free( lc->conns );
212         }
213
214         free( lc );
215 }
216
217 /*
218  * init_one_conn
219  * 
220  * Initializes one connection
221  */
222 static int
223 init_one_conn(
224                 Operation *op,
225                 SlapReply *rs,
226                 struct metatarget       *lt, 
227                 struct metasingleconn   *lsc
228                 )
229 {
230         int             err, vers;
231
232         /*
233          * Already init'ed
234          */
235         if ( lsc->ld != NULL ) {
236                 return LDAP_SUCCESS;
237         }
238        
239         /*
240          * Attempts to initialize the connection to the target ds
241          */
242         err = ldap_initialize( &lsc->ld, lt->uri );
243         if ( err != LDAP_SUCCESS ) {
244                 return ldap_back_map_result( err );
245         }
246
247         /*
248          * Set LDAP version. This will always succeed: If the client
249          * bound with a particular version, then so can we.
250          */
251         vers = op->o_conn->c_protocol;
252         ldap_set_option( lsc->ld, LDAP_OPT_PROTOCOL_VERSION, &vers );
253         /* FIXME: configurable? */
254         ldap_set_option(lsc->ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
255
256         /*
257          * Sets a cookie for the rewrite session
258          */
259         ( void )rewrite_session_init( lt->rwinfo, op->o_conn );
260
261         /*
262          * If the connection dn is not null, an attempt to rewrite it is made
263          */
264         if ( op->o_conn->c_dn.bv_len != 0 ) {
265                 
266                 /*
267                  * Rewrite the bind dn if needed
268                  */
269                 lsc->bound_dn.bv_val = NULL;
270                 switch ( rewrite_session( lt->rwinfo, "bindDn",
271                                         op->o_conn->c_dn.bv_val, op->o_conn, 
272                                         &lsc->bound_dn.bv_val ) ) {
273                 case REWRITE_REGEXEC_OK:
274                         if ( lsc->bound_dn.bv_val == NULL ) {
275                                 ber_dupbv( &lsc->bound_dn, &op->o_conn->c_dn );
276                         }
277 #ifdef NEW_LOGGING
278                         LDAP_LOG( BACK_META, DETAIL1,
279                                 "[rw] bindDn: \"%s\" -> \"%s\"\n",
280                                 op->o_conn->c_dn.bv_val,
281                                 lsc->bound_dn.bv_val, 0 );
282 #else /* !NEW_LOGGING */
283                         Debug( LDAP_DEBUG_ARGS,
284                                         "rw> bindDn: \"%s\" -> \"%s\"\n",
285                                         op->o_conn->c_dn.bv_val,
286                                         lsc->bound_dn.bv_val, 0 );
287 #endif /* !NEW_LOGGING */
288                         break;
289                         
290                 case REWRITE_REGEXEC_UNWILLING:
291                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
292                         rs->sr_text = "Operation not allowed";
293 #if 0
294                         send_ldap_result( conn, op,
295                                         LDAP_UNWILLING_TO_PERFORM,
296                                         NULL, "Operation not allowed",
297                                         NULL, NULL );
298 #endif
299                         return rs->sr_err;
300                         
301                 case REWRITE_REGEXEC_ERR:
302                         rs->sr_err = LDAP_OTHER;
303                         rs->sr_text = "Rewrite error";
304 #if 0
305                         send_ldap_result( conn, op,
306                                         LDAP_OTHER,
307                                         NULL, "Rewrite error",
308                                         NULL, NULL );
309 #endif
310                         return rs->sr_err;
311                 }
312
313                 assert( lsc->bound_dn.bv_val );
314
315         } else {
316                 ber_str2bv( "", 0, 1, &lsc->bound_dn );
317         }
318
319         lsc->bound = META_UNBOUND;
320
321         /*
322          * The candidate is activated
323          */
324         lsc->candidate = META_CANDIDATE;
325         return LDAP_SUCCESS;
326 }
327
328 /*
329  * meta_back_getconn
330  * 
331  * Prepares the connection structure
332  * 
333  * FIXME: This function needs to receive some info on the type of operation
334  * it is invoked by, so that only the correct pool of candidate targets
335  * is initialized in case no connection was available yet.
336  * 
337  * At present a flag that says whether the candidate target must be unique
338  * is passed; eventually an operation agent will be used.
339  */
340 struct metaconn *
341 meta_back_getconn(
342                 Operation       *op,
343                 SlapReply       *rs,
344                 int             op_type,
345                 struct berval   *ndn,
346                 int             *candidate )
347 {
348         struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
349         struct metaconn *lc, lc_curr;
350         int cached = -1, i = -1, err = LDAP_SUCCESS;
351         int new_conn = 0;
352
353         /* Searches for a metaconn in the avl tree */
354         lc_curr.conn = op->o_conn;
355         ldap_pvt_thread_mutex_lock( &li->conn_mutex );
356         lc = (struct metaconn *)avl_find( li->conntree, 
357                 (caddr_t)&lc_curr, meta_back_conn_cmp );
358         ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
359
360         /* Looks like we didn't get a bind. Open a new session... */
361         if ( !lc ) {
362                 lc = metaconn_alloc( li->ntargets );
363                 lc->conn = op->o_conn;
364                 new_conn = 1;
365         }
366
367         /*
368          * looks in cache, if any
369          */
370         if ( li->cache.ttl != META_DNCACHE_DISABLED ) {
371                 cached = i = meta_dncache_get_target( &li->cache, ndn );
372         }
373
374         if ( op_type == META_OP_REQUIRE_SINGLE ) {
375
376                 /*
377                  * tries to get a unique candidate
378                  * (takes care of default target 
379                  */
380                 if ( i < 0 ) {
381                         i = meta_back_select_unique_candidate( li, ndn );
382                 }
383
384                 /*
385                  * if any is found, inits the connection
386                  */
387                 if ( i < 0 ) {
388                         if ( new_conn ) {
389                                 metaconn_free( lc );
390                         }
391
392                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
393                         return NULL;
394                 }
395                                 
396 #ifdef NEW_LOGGING
397                 LDAP_LOG( BACK_META, INFO,
398                         "meta_back_getconn: got target %d for ndn=\"%s\" from cache\n", 
399                         i, ndn->bv_val, 0 );
400 #else /* !NEW_LOGGING */
401                 Debug( LDAP_DEBUG_CACHE,
402         "==>meta_back_getconn: got target %d for ndn=\"%s\" from cache\n%s",
403                                 i, ndn->bv_val, "" );
404 #endif /* !NEW_LOGGING */
405
406                 /*
407                  * Clear all other candidates
408                  */
409                 ( void )meta_clear_unused_candidates( li, lc, i, 0 );
410
411                 /*
412                  * The target is activated; if needed, it is
413                  * also init'd. In case of error, init_one_conn
414                  * sends the appropriate result.
415                  */
416                 err = init_one_conn( op, rs, li->targets[ i ],
417                                 &lc->conns[ i ] );
418                 if ( err != LDAP_SUCCESS ) {
419                 
420                         /*
421                          * FIXME: in case one target cannot
422                          * be init'd, should the other ones
423                          * be tried?
424                          */
425                         ( void )meta_clear_one_candidate( &lc->conns[ i ], 1 );
426                         if ( new_conn ) {
427                                 metaconn_free( lc );
428                         }
429                         return NULL;
430                 }
431
432                 if ( candidate ) {
433                         *candidate = i;
434                 }
435
436         /*
437          * require all connections ...
438          */
439         } else if (op_type == META_OP_REQUIRE_ALL) {
440                 for ( i = 0; i < li->ntargets; i++ ) {
441
442                         /*
443                          * The target is activated; if needed, it is
444                          * also init'd
445                          */
446                         int lerr = init_one_conn( op, rs, li->targets[ i ],
447                                         &lc->conns[ i ] );
448                         if ( lerr != LDAP_SUCCESS ) {
449                                 
450                                 /*
451                                  * FIXME: in case one target cannot
452                                  * be init'd, should the other ones
453                                  * be tried?
454                                  */
455                                 ( void )meta_clear_one_candidate( &lc->conns[ i ], 1 );
456                                 err = lerr;
457                                 continue;
458                         }
459                 }
460
461         /*
462          * if no unique candidate ...
463          */
464         } else {
465                 for ( i = 0; i < li->ntargets; i++ ) {
466                         if ( i == cached 
467                 || meta_back_is_candidate( &li->targets[ i ]->suffix, ndn ) ) {
468
469                                 /*
470                                  * The target is activated; if needed, it is
471                                  * also init'd
472                                  */
473                                 int lerr = init_one_conn( op, rs,
474                                                 li->targets[ i ],
475                                                 &lc->conns[ i ] );
476                                 if ( lerr != LDAP_SUCCESS ) {
477                                 
478                                         /*
479                                          * FIXME: in case one target cannot
480                                          * be init'd, should the other ones
481                                          * be tried?
482                                          */
483                                         ( void )meta_clear_one_candidate( &lc->conns[ i ], 1 );
484                                         err = lerr;
485                                         continue;
486                                 }
487                         }
488                 }
489         }
490
491         /* clear out init_one_conn non-fatal errors */
492         rs->sr_err = LDAP_SUCCESS;
493         rs->sr_text = NULL;
494
495         if ( new_conn ) {
496                 
497                 /*
498                  * Inserts the newly created metaconn in the avl tree
499                  */
500                 ldap_pvt_thread_mutex_lock( &li->conn_mutex );
501                 err = avl_insert( &li->conntree, ( caddr_t )lc,
502                                 meta_back_conn_cmp, meta_back_conn_dup );
503
504 #if PRINT_CONNTREE > 0
505                 myprint( li->conntree );
506 #endif /* PRINT_CONNTREE */
507                 
508                 ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
509
510 #ifdef NEW_LOGGING
511                 LDAP_LOG( BACK_META, INFO,
512                         "meta_back_getconn: conn %ld inserted\n", lc->conn->c_connid, 0, 0);
513 #else /* !NEW_LOGGING */
514                 Debug( LDAP_DEBUG_TRACE,
515                         "=>meta_back_getconn: conn %ld inserted\n%s%s",
516                         lc->conn->c_connid, "", "" );
517 #endif /* !NEW_LOGGING */
518                 
519                 /*
520                  * Err could be -1 in case a duplicate metaconn is inserted
521                  */
522                 if ( err != 0 ) {
523                         rs->sr_err = LDAP_OTHER;
524                         rs->sr_text = "Internal server error";
525                         metaconn_free( lc );
526                         return NULL;
527                 }
528         } else {
529 #ifdef NEW_LOGGING
530                 LDAP_LOG( BACK_META, INFO,
531                         "meta_back_getconn: conn %ld fetched\n", lc->conn->c_connid, 0, 0 );
532 #else /* !NEW_LOGGING */
533                 Debug( LDAP_DEBUG_TRACE,
534                         "=>meta_back_getconn: conn %ld fetched\n%s%s",
535                         lc->conn->c_connid, "", "" );
536 #endif /* !NEW_LOGGING */
537         }
538         
539         return lc;
540 }
541