1 /* conn.c - handles connections to remote targets */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 *
5 * Copyright 2016-2021 The OpenLDAP Foundation.
6 * Portions Copyright 2016 Symas Corporation.
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
18 /* ACKNOWLEDGEMENTS:
19 + * This work was developed by Symas Corporation
20 + * based on back-meta module for inclusion in OpenLDAP Software.
21 + * This work was sponsored by Ericsson. */
22
23 #include "portable.h"
24
25 #include <stdio.h>
26
27 #include <ac/errno.h>
28 #include <ac/socket.h>
29 #include <ac/string.h>
30 #include "slap.h"
31 #include "../../../libraries/libldap/ldap-int.h"
32 #include "../back-ldap/back-ldap.h"
33 #include "back-asyncmeta.h"
34
35 /*
36 * asyncmeta_conn_alloc
37 *
38 * Allocates a connection structure, making room for all the referenced targets
39 */
40 static a_metaconn_t *
asyncmeta_conn_alloc(a_metainfo_t * mi)41 asyncmeta_conn_alloc(
42 a_metainfo_t *mi)
43 {
44 a_metaconn_t *mc;
45 int ntargets = mi->mi_ntargets;
46
47 assert( ntargets > 0 );
48
49 /* malloc all in one */
50 mc = ( a_metaconn_t * )ch_calloc( 1, sizeof( a_metaconn_t ) + ntargets * sizeof( a_metasingleconn_t ));
51 if ( mc == NULL ) {
52 return NULL;
53 }
54
55 mc->mc_info = mi;
56 ldap_pvt_thread_mutex_init( &mc->mc_om_mutex);
57 mc->mc_authz_target = META_BOUND_NONE;
58 mc->mc_conns = (a_metasingleconn_t *)(mc+1);
59 return mc;
60 }
61
62 /*
63 * asyncmeta_init_one_conn
64 *
65 * Initializes one connection
66 */
67 int
asyncmeta_init_one_conn(Operation * op,SlapReply * rs,a_metaconn_t * mc,int candidate,int ispriv,ldap_back_send_t sendok,int dolock)68 asyncmeta_init_one_conn(
69 Operation *op,
70 SlapReply *rs,
71 a_metaconn_t *mc,
72 int candidate,
73 int ispriv,
74 ldap_back_send_t sendok,
75 int dolock)
76 {
77 a_metainfo_t *mi = mc->mc_info;
78 a_metatarget_t *mt = mi->mi_targets[ candidate ];
79 a_metasingleconn_t *msc = NULL;
80 int version;
81 a_dncookie dc;
82 int isauthz = ( candidate == mc->mc_authz_target );
83 int do_return = 0;
84 #ifdef HAVE_TLS
85 int is_ldaps = 0;
86 int do_start_tls = 0;
87 #endif /* HAVE_TLS */
88
89 /* if the server is quarantined, and
90 * - the current interval did not expire yet, or
91 * - no more retries should occur,
92 * don't return the connection */
93 if ( mt->mt_isquarantined ) {
94 slap_retry_info_t *ri = &mt->mt_quarantine;
95 int dont_retry = 0;
96
97 if ( mt->mt_quarantine.ri_interval ) {
98 ldap_pvt_thread_mutex_lock( &mt->mt_quarantine_mutex );
99 dont_retry = ( mt->mt_isquarantined > LDAP_BACK_FQ_NO );
100 if ( dont_retry ) {
101 dont_retry = ( ri->ri_num[ ri->ri_idx ] == SLAP_RETRYNUM_TAIL
102 || slap_get_time() < ri->ri_last + ri->ri_interval[ ri->ri_idx ] );
103 if ( !dont_retry ) {
104 Debug(LDAP_DEBUG_ANY,
105 "%s asyncmeta_init_one_conn[%d]: quarantine " "retry block #%d try #%d.\n",
106 op->o_log_prefix,
107 candidate, ri->ri_idx,
108 ri->ri_count );
109
110 mt->mt_isquarantined = LDAP_BACK_FQ_RETRYING;
111 }
112
113 }
114 ldap_pvt_thread_mutex_unlock( &mt->mt_quarantine_mutex );
115 }
116
117 if ( dont_retry ) {
118 rs->sr_err = LDAP_UNAVAILABLE;
119 rs->sr_text = "Target is quarantined";
120 Debug( LDAP_DEBUG_ANY, "%s asyncmeta_init_one_conn: Target is quarantined\n",
121 op->o_log_prefix );
122 if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
123 send_ldap_result( op, rs );
124 }
125 return rs->sr_err;
126 }
127 }
128 msc = &mc->mc_conns[candidate];
129 /*
130 * Already init'ed
131 */
132 if ( LDAP_BACK_CONN_ISBOUND( msc )
133 || LDAP_BACK_CONN_ISANON( msc ) )
134 {
135 assert( msc->msc_ld != NULL );
136 rs->sr_err = LDAP_SUCCESS;
137 do_return = 1;
138
139 } else if ( META_BACK_CONN_CREATING( msc )
140 || LDAP_BACK_CONN_BINDING( msc ) )
141 {
142 rs->sr_err = LDAP_SUCCESS;
143 do_return = 1;
144
145 } else if ( META_BACK_CONN_INITED( msc ) ) {
146 assert( msc->msc_ld != NULL );
147 rs->sr_err = LDAP_SUCCESS;
148 do_return = 1;
149
150 } else {
151 /*
152 * creating...
153 */
154 META_BACK_CONN_CREATING_SET( msc );
155 }
156
157 if ( do_return ) {
158 if ( rs->sr_err != LDAP_SUCCESS
159 && op->o_conn
160 && ( sendok & LDAP_BACK_SENDERR ) )
161 {
162 send_ldap_result( op, rs );
163 }
164
165 return rs->sr_err;
166 }
167
168 assert( msc->msc_ld == NULL );
169
170 /*
171 * Attempts to initialize the connection to the target ds
172 */
173 ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
174
175 rs->sr_err = ldap_initialize( &msc->msc_ld, mt->mt_uri );
176 #ifdef HAVE_TLS
177 is_ldaps = ldap_is_ldaps_url( mt->mt_uri );
178 #endif /* HAVE_TLS */
179 ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
180 if ( rs->sr_err != LDAP_SUCCESS ) {
181 Debug( LDAP_DEBUG_ANY, "%s asyncmeta_init_one_conn: ldap_initialize failed err=%d\n",
182 op->o_log_prefix, rs->sr_err );
183 goto error_return;
184 }
185
186 ldap_set_option( msc->msc_ld, LDAP_OPT_KEEPCONN, LDAP_OPT_ON);
187
188 msc->msc_ldr = ldap_dup(msc->msc_ld);
189 if (!msc->msc_ldr) {
190 ldap_ld_free(msc->msc_ld, 0, NULL, NULL);
191 rs->sr_err = LDAP_NO_MEMORY;
192 goto error_return;
193 }
194
195 /*
196 * Set LDAP version. This will always succeed: If the client
197 * bound with a particular version, then so can we.
198 */
199 if ( mt->mt_version != 0 ) {
200 version = mt->mt_version;
201
202 } else if ( op->o_conn->c_protocol != 0 ) {
203 version = op->o_conn->c_protocol;
204
205 } else {
206 version = LDAP_VERSION3;
207 }
208 ldap_set_option( msc->msc_ld, LDAP_OPT_PROTOCOL_VERSION, &version );
209 ldap_set_urllist_proc( msc->msc_ld, mt->mt_urllist_f, mt->mt_urllist_p );
210
211 /* automatically chase referrals ("chase-referrals [{yes|no}]" statement) */
212 ldap_set_option( msc->msc_ld, LDAP_OPT_REFERRALS,
213 META_BACK_TGT_CHASE_REFERRALS( mt ) ? LDAP_OPT_ON : LDAP_OPT_OFF );
214
215 slap_client_keepalive(msc->msc_ld, &mt->mt_tls.sb_keepalive);
216
217 if ( mt->mt_tls.sb_tcp_user_timeout > 0 ) {
218 ldap_set_option( msc->msc_ld, LDAP_OPT_TCP_USER_TIMEOUT,
219 &mt->mt_tls.sb_tcp_user_timeout );
220 }
221
222 #ifdef HAVE_TLS
223 {
224 slap_bindconf *sb = NULL;
225
226 if ( ispriv ) {
227 sb = &mt->mt_idassert.si_bc;
228 } else {
229 sb = &mt->mt_tls;
230 }
231
232 bindconf_tls_set( sb, msc->msc_ld );
233
234 if ( !is_ldaps ) {
235 if ( META_BACK_TGT_USE_TLS( mt )
236 || ( op->o_conn->c_is_tls && META_BACK_TGT_PROPAGATE_TLS( mt ) ) )
237 {
238 do_start_tls = 1;
239 }
240 }
241 }
242
243 /* start TLS ("tls [try-]{start|propagate}" statement) */
244 if ( do_start_tls ) {
245 #ifdef SLAP_STARTTLS_ASYNCHRONOUS
246 /*
247 * use asynchronous StartTLS; in case, chase referral
248 * FIXME: OpenLDAP does not return referral on StartTLS yet
249 */
250 int msgid;
251
252 rs->sr_err = ldap_start_tls( msc->msc_ld, NULL, NULL, &msgid );
253 if ( rs->sr_err == LDAP_SUCCESS ) {
254 LDAPMessage *res = NULL;
255 int rc, nretries = mt->mt_nretries;
256 struct timeval tv;
257
258 LDAP_BACK_TV_SET( &tv );
259
260 retry:;
261 rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
262 switch ( rc ) {
263 case -1:
264 rs->sr_err = LDAP_OTHER;
265 break;
266
267 case 0:
268 if ( nretries != 0 ) {
269 if ( nretries > 0 ) {
270 nretries--;
271 }
272 LDAP_BACK_TV_SET( &tv );
273 goto retry;
274 }
275 rs->sr_err = LDAP_OTHER;
276 break;
277
278 default:
279 /* only touch when activity actually took place... */
280 if ( mi->mi_idle_timeout != 0 ) {
281 asyncmeta_set_msc_time(msc);
282 }
283 break;
284 }
285
286 if ( rc == LDAP_RES_EXTENDED ) {
287 struct berval *data = NULL;
288
289 /* NOTE: right now, data is unused, so don't get it */
290 rs->sr_err = ldap_parse_extended_result( msc->msc_ld,
291 res, NULL, NULL /* &data */ , 0 );
292 if ( rs->sr_err == LDAP_SUCCESS ) {
293 int err;
294
295 /* FIXME: matched? referrals? response controls? */
296 rs->sr_err = ldap_parse_result( msc->msc_ld,
297 res, &err, NULL, NULL, NULL, NULL, 1 );
298 res = NULL;
299
300 if ( rs->sr_err == LDAP_SUCCESS ) {
301
302 rs->sr_err = err;
303 }
304 rs->sr_err = slap_map_api2result( rs );
305
306 /* FIXME: in case a referral
307 * is returned, should we try
308 * using it instead of the
309 * configured URI? */
310 if ( rs->sr_err == LDAP_SUCCESS ) {
311 rs->sr_err = ldap_install_tls( msc->msc_ld );
312
313 } else if ( rs->sr_err == LDAP_REFERRAL ) {
314 /* FIXME: LDAP_OPERATIONS_ERROR? */
315 rs->sr_err = LDAP_OTHER;
316 rs->sr_text = "Unwilling to chase referral "
317 "returned by Start TLS exop";
318 }
319
320 if ( data ) {
321 ber_bvfree( data );
322 }
323 }
324
325 } else {
326 rs->sr_err = LDAP_OTHER;
327 }
328
329 if ( res != NULL ) {
330 ldap_msgfree( res );
331 }
332 }
333 #else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
334 /*
335 * use synchronous StartTLS
336 */
337 rs->sr_err = ldap_start_tls_s( msc->msc_ld, NULL, NULL );
338 #endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
339 if (rs->sr_err != LDAP_SUCCESS) {
340 Debug( LDAP_DEBUG_ANY, "%s asyncmeta_init_one_conn: ldap_start_tls_s failed err=%d\n",
341 op->o_log_prefix, rs->sr_err );
342 }
343 /* if StartTLS is requested, only attempt it if the URL
344 * is not "ldaps://"; this may occur not only in case
345 * of misconfiguration, but also when used in the chain
346 * overlay, where the "uri" can be parsed out of a referral */
347 if ( rs->sr_err == LDAP_SERVER_DOWN
348 || ( rs->sr_err != LDAP_SUCCESS
349 && META_BACK_TGT_TLS_CRITICAL( mt ) ) )
350 {
351
352 #ifdef DEBUG_205
353 Debug( LDAP_DEBUG_ANY,
354 "### %s asyncmeta_init_one_conn(TLS) "
355 "ldap_unbind_ext[%d] ld=%p\n",
356 op->o_log_prefix, candidate,
357 (void *)msc->msc_ld );
358 #endif /* DEBUG_205 */
359
360 /* need to trash a failed Start TLS */
361 asyncmeta_clear_one_msc( op, mc, candidate, 1, __FUNCTION__ );
362 goto error_return;
363 }
364 }
365 #endif /* HAVE_TLS */
366 /*
367 * Set the network timeout if set
368 */
369 if ( mt->mt_network_timeout != 0 ) {
370 struct timeval network_timeout;
371 network_timeout.tv_sec = 0;
372 network_timeout.tv_usec = mt->mt_network_timeout*1000;
373
374 ldap_set_option( msc->msc_ld, LDAP_OPT_NETWORK_TIMEOUT,
375 (void *)&network_timeout );
376 }
377
378 /*
379 * If the connection DN is not null, an attempt to rewrite it is made
380 */
381
382 if ( ispriv ) {
383 if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) ) {
384 ber_bvreplace( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN );
385 if ( !BER_BVISNULL( &mt->mt_idassert_passwd ) ) {
386 if ( !BER_BVISNULL( &msc->msc_cred ) ) {
387 memset( msc->msc_cred.bv_val, 0,
388 msc->msc_cred.bv_len );
389 }
390 ber_bvreplace( &msc->msc_cred, &mt->mt_idassert_passwd );
391 }
392 LDAP_BACK_CONN_ISIDASSERT_SET( msc );
393
394 } else {
395 ber_bvreplace( &msc->msc_bound_ndn, &slap_empty_bv );
396 }
397
398 } else {
399 if ( !BER_BVISNULL( &msc->msc_cred ) ) {
400 memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
401 ber_memfree_x( msc->msc_cred.bv_val, NULL );
402 BER_BVZERO( &msc->msc_cred );
403 }
404 if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
405 ber_memfree_x( msc->msc_bound_ndn.bv_val, NULL );
406 BER_BVZERO( &msc->msc_bound_ndn );
407 }
408 if ( !BER_BVISEMPTY( &op->o_ndn )
409 && isauthz )
410 {
411 dc.op = op;
412 dc.target = mt;
413 dc.memctx = NULL;
414 dc.to_from = MASSAGE_REQ;
415
416 /*
417 * Rewrite the bind dn if needed
418 */
419 asyncmeta_dn_massage( &dc, &op->o_conn->c_dn, &msc->msc_bound_ndn );
420
421 /* copy the DN if needed */
422 if ( msc->msc_bound_ndn.bv_val == op->o_conn->c_dn.bv_val ) {
423 ber_dupbv( &msc->msc_bound_ndn, &op->o_conn->c_dn );
424 }
425 } else {
426 ber_dupbv( &msc->msc_bound_ndn, (struct berval *)&slap_empty_bv );
427 }
428 }
429 assert( !BER_BVISNULL( &msc->msc_bound_ndn ) );
430
431 error_return:;
432
433 if (msc != NULL) {
434 META_BACK_CONN_CREATING_CLEAR( msc );
435 }
436 if ( rs->sr_err == LDAP_SUCCESS && msc != NULL) {
437 META_BACK_CONN_INITED_SET( msc );
438 }
439
440 if ( rs->sr_err != LDAP_SUCCESS ) {
441 rs->sr_err = slap_map_api2result( rs );
442 if ( sendok & LDAP_BACK_SENDERR ) {
443 send_ldap_result( op, rs );
444 }
445 }
446 return rs->sr_err;
447 }
448
449
450 static int
asyncmeta_get_candidate(Operation * op,SlapReply * rs,struct berval * ndn)451 asyncmeta_get_candidate(
452 Operation *op,
453 SlapReply *rs,
454 struct berval *ndn )
455 {
456 a_metainfo_t *mi = ( a_metainfo_t * )op->o_bd->be_private;
457 long candidate;
458
459 /*
460 * tries to get a unique candidate
461 * (takes care of default target)
462 */
463 candidate = asyncmeta_select_unique_candidate( mi, ndn );
464
465 /*
466 * if any is found, inits the connection
467 */
468 if ( candidate == META_TARGET_NONE ) {
469 rs->sr_err = LDAP_NO_SUCH_OBJECT;
470 rs->sr_text = "No suitable candidate target found";
471
472 } else {
473 rs->sr_err = LDAP_SUCCESS;
474 }
475
476 return candidate;
477 }
478
479
480 /*
481 * asyncmeta_getconn
482 *
483 * Prepares the connection structure
484 *
485 * RATIONALE:
486 *
487 * - determine what DN is being requested:
488 *
489 * op requires candidate checks
490 *
491 * add unique parent of o_req_ndn
492 * bind unique^*[/all] o_req_ndn [no check]
493 * compare unique^+ o_req_ndn
494 * delete unique o_req_ndn
495 * modify unique o_req_ndn
496 * search any o_req_ndn
497 * modrdn unique[, unique] o_req_ndn[, orr_nnewSup]
498 *
499 * - for ops that require the candidate to be unique, in case of multiple
500 * occurrences an internal search with sizeLimit=1 is performed
501 * if a unique candidate can actually be determined. If none is found,
502 * the operation aborts; if multiple are found, the default target
503 * is used if defined and candidate; otherwise the operation aborts.
504 *
505 * *^note: actually, the bind operation is handled much like a search;
506 * i.e. the bind is broadcast to all candidate targets.
507 *
508 * +^note: actually, the compare operation is handled much like a search;
509 * i.e. the compare is broadcast to all candidate targets, while checking
510 * that exactly none (noSuchObject) or one (TRUE/FALSE/UNDEFINED) is
511 * returned.
512 */
513 a_metaconn_t *
asyncmeta_getconn(Operation * op,SlapReply * rs,SlapReply * candidates,int * candidate,ldap_back_send_t sendok,int alloc_new)514 asyncmeta_getconn(
515 Operation *op,
516 SlapReply *rs,
517 SlapReply *candidates,
518 int *candidate,
519 ldap_back_send_t sendok,
520 int alloc_new)
521 {
522 a_metainfo_t *mi = ( a_metainfo_t * )op->o_bd->be_private;
523 a_metaconn_t *mc = NULL,
524 mc_curr = {{ 0 }};
525 int cached = META_TARGET_NONE,
526 i = META_TARGET_NONE,
527 err = LDAP_SUCCESS,
528 new_conn = 0,
529 ncandidates = 0;
530
531
532 meta_op_type op_type = META_OP_REQUIRE_SINGLE;
533 enum {
534 META_DNTYPE_ENTRY,
535 META_DNTYPE_PARENT,
536 META_DNTYPE_NEWPARENT
537 } dn_type = META_DNTYPE_ENTRY;
538 struct berval ndn = op->o_req_ndn,
539 pndn;
540
541 if (alloc_new > 0) {
542 mc = asyncmeta_conn_alloc(mi);
543 new_conn = 0;
544 } else {
545 mc = asyncmeta_get_next_mc(mi);
546 }
547
548 ldap_pvt_thread_mutex_lock(&mc->mc_om_mutex);
549 /* Internal searches are privileged and shared. So is root. */
550 if ( ( !BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_ALWAYS( mi ) )
551 || ( BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_ANON( mi ) )
552 || op->o_do_not_cache || be_isroot( op ) )
553 {
554 LDAP_BACK_CONN_ISPRIV_SET( &mc_curr );
555 LDAP_BACK_PCONN_ROOTDN_SET( &mc_curr, op );
556
557 } else if ( BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_NOANON( mi ) )
558 {
559 LDAP_BACK_CONN_ISANON_SET( &mc_curr );
560 LDAP_BACK_PCONN_ANON_SET( &mc_curr, op );
561
562 } else {
563 /* Explicit binds must not be shared */
564 if ( !BER_BVISEMPTY( &op->o_ndn )
565 || op->o_tag == LDAP_REQ_BIND
566 || SLAP_IS_AUTHZ_BACKEND( op ) )
567 {
568 //mc_curr.mc_conn = op->o_conn;
569
570 } else {
571 LDAP_BACK_CONN_ISANON_SET( &mc_curr );
572 LDAP_BACK_PCONN_ANON_SET( &mc_curr, op );
573 }
574 }
575
576 switch ( op->o_tag ) {
577 case LDAP_REQ_ADD:
578 /* if we go to selection, the entry must not exist,
579 * and we must be able to resolve the parent */
580 dn_type = META_DNTYPE_PARENT;
581 dnParent( &ndn, &pndn );
582 break;
583
584 case LDAP_REQ_MODRDN:
585 /* if nnewSuperior is not NULL, it must resolve
586 * to the same candidate as the req_ndn */
587 if ( op->orr_nnewSup ) {
588 dn_type = META_DNTYPE_NEWPARENT;
589 }
590 break;
591
592 case LDAP_REQ_BIND:
593 /* if bound as rootdn, the backend must bind to all targets
594 * with the administrative identity
595 * (unless pseoudoroot-bind-defer is TRUE) */
596 if ( op->orb_method == LDAP_AUTH_SIMPLE && be_isroot_pw( op ) ) {
597 op_type = META_OP_REQUIRE_ALL;
598 }
599 break;
600
601 case LDAP_REQ_COMPARE:
602 case LDAP_REQ_DELETE:
603 case LDAP_REQ_MODIFY:
604 /* just a unique candidate */
605 break;
606
607 case LDAP_REQ_SEARCH:
608 /* allow multiple candidates for the searchBase */
609 op_type = META_OP_ALLOW_MULTIPLE;
610 break;
611
612 default:
613 /* right now, just break (exop?) */
614 break;
615 }
616
617 /*
618 * require all connections ...
619 */
620 if ( op_type == META_OP_REQUIRE_ALL ) {
621 if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
622 LDAP_BACK_CONN_ISPRIV_SET( mc );
623
624 } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
625 LDAP_BACK_CONN_ISANON_SET( mc );
626 }
627
628 for ( i = 0; i < mi->mi_ntargets; i++ ) {
629 /*
630 * The target is activated; if needed, it is
631 * also init'd
632 */
633 candidates[ i ].sr_err = asyncmeta_init_one_conn( op,
634 rs, mc, i, LDAP_BACK_CONN_ISPRIV( &mc_curr ),
635 LDAP_BACK_DONTSEND, !new_conn );
636 if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
637 if ( new_conn && ( sendok & LDAP_BACK_BINDING ) ) {
638 LDAP_BACK_CONN_BINDING_SET( &mc->mc_conns[ i ] );
639 }
640 META_CANDIDATE_SET( &candidates[ i ] );
641 ncandidates++;
642
643 } else {
644
645 /*
646 * FIXME: in case one target cannot
647 * be init'd, should the other ones
648 * be tried?
649 */
650 META_CANDIDATE_RESET( &candidates[ i ] );
651 err = candidates[ i ].sr_err;
652 continue;
653 }
654 }
655
656 if ( ncandidates == 0 ) {
657 rs->sr_err = LDAP_NO_SUCH_OBJECT;
658 rs->sr_text = "Unable to select valid candidates";
659
660 if ( sendok & LDAP_BACK_SENDERR ) {
661 if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
662 rs->sr_matched = mi->mi_suffix.bv_val;
663 }
664 send_ldap_result( op, rs );
665 rs->sr_matched = NULL;
666 }
667 ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
668 if ( alloc_new > 0) {
669 asyncmeta_back_conn_free( mc );
670 }
671 return NULL;
672 }
673
674 goto done;
675 }
676
677 /*
678 * looks in cache, if any
679 */
680 if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
681 cached = i = asyncmeta_dncache_get_target( &mi->mi_cache, &op->o_req_ndn );
682 }
683
684 if ( op_type == META_OP_REQUIRE_SINGLE ) {
685 int j;
686
687 for ( j = 0; j < mi->mi_ntargets; j++ ) {
688 META_CANDIDATE_RESET( &candidates[ j ] );
689 }
690
691 /*
692 * tries to get a unique candidate
693 * (takes care of default target)
694 */
695 if ( i == META_TARGET_NONE ) {
696 i = asyncmeta_get_candidate( op, rs, &ndn );
697
698 if ( rs->sr_err == LDAP_NO_SUCH_OBJECT && dn_type == META_DNTYPE_PARENT ) {
699 i = asyncmeta_get_candidate( op, rs, &pndn );
700 }
701
702 if ( i < 0 || rs->sr_err != LDAP_SUCCESS ) {
703 if ( sendok & LDAP_BACK_SENDERR ) {
704 if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
705 rs->sr_matched = mi->mi_suffix.bv_val;
706 }
707 send_ldap_result( op, rs );
708 rs->sr_matched = NULL;
709 }
710 ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
711 if ( mc != NULL && alloc_new ) {
712 asyncmeta_back_conn_free( mc );
713 }
714 return NULL;
715 }
716 }
717
718 if ( dn_type == META_DNTYPE_NEWPARENT && asyncmeta_get_candidate( op, rs, op->orr_nnewSup ) != i )
719 {
720 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
721 rs->sr_text = "Cross-target rename not supported";
722 if ( sendok & LDAP_BACK_SENDERR ) {
723 send_ldap_result( op, rs );
724 }
725 ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
726 if ( mc != NULL && alloc_new > 0 ) {
727 asyncmeta_back_conn_free( mc );
728 }
729 return NULL;
730 }
731
732 Debug( LDAP_DEBUG_TRACE,
733 "==>asyncmeta__getconn: got target=%d for ndn=\"%s\" from cache\n",
734 i, op->o_req_ndn.bv_val );
735 if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
736 LDAP_BACK_CONN_ISPRIV_SET( mc );
737
738 } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
739 LDAP_BACK_CONN_ISANON_SET( mc );
740 }
741
742 /*
743 * Clear all other candidates
744 */
745 ( void )asyncmeta_clear_unused_candidates( op, i , mc, candidates);
746
747 /*
748 * The target is activated; if needed, it is
749 * also init'd. In case of error, asyncmeta_init_one_conn
750 * sends the appropriate result.
751 */
752 err = asyncmeta_init_one_conn( op, rs, mc, i,
753 LDAP_BACK_CONN_ISPRIV( &mc_curr ), sendok, !new_conn );
754 if ( err != LDAP_SUCCESS ) {
755 /*
756 * FIXME: in case one target cannot
757 * be init'd, should the other ones
758 * be tried?
759 */
760 META_CANDIDATE_RESET( &candidates[ i ] );
761 ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
762 if ( mc != NULL && alloc_new > 0 ) {
763 asyncmeta_back_conn_free( mc );
764 }
765 return NULL;
766 }
767
768 candidates[ i ].sr_err = LDAP_SUCCESS;
769 META_CANDIDATE_SET( &candidates[ i ] );
770 ncandidates++;
771
772 if ( candidate ) {
773 *candidate = i;
774 }
775
776 /*
777 * if no unique candidate ...
778 */
779 } else {
780 if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
781 LDAP_BACK_CONN_ISPRIV_SET( mc );
782
783 } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
784 LDAP_BACK_CONN_ISANON_SET( mc );
785 }
786
787 for ( i = 0; i < mi->mi_ntargets; i++ ) {
788 a_metatarget_t *mt = mi->mi_targets[ i ];
789
790 META_CANDIDATE_RESET( &candidates[ i ] );
791
792 if ( i == cached
793 || asyncmeta_is_candidate( mt, &op->o_req_ndn,
794 op->o_tag == LDAP_REQ_SEARCH ? op->ors_scope : LDAP_SCOPE_SUBTREE ) )
795 {
796
797 /*
798 * The target is activated; if needed, it is
799 * also init'd
800 */
801 int lerr = asyncmeta_init_one_conn( op, rs, mc, i,
802 LDAP_BACK_CONN_ISPRIV( &mc_curr ),
803 LDAP_BACK_DONTSEND, !new_conn );
804 candidates[ i ].sr_err = lerr;
805 if ( lerr == LDAP_SUCCESS ) {
806 META_CANDIDATE_SET( &candidates[ i ] );
807 ncandidates++;
808
809 Debug( LDAP_DEBUG_TRACE, "%s: asyncmeta_getconn[%d]\n",
810 op->o_log_prefix, i );
811
812 } else if ( lerr == LDAP_UNAVAILABLE && !META_BACK_ONERR_STOP( mi ) ) {
813 META_CANDIDATE_SET( &candidates[ i ] );
814
815 Debug( LDAP_DEBUG_TRACE, "%s: asyncmeta_getconn[%d] %s\n",
816 op->o_log_prefix, i,
817 mt->mt_isquarantined != LDAP_BACK_FQ_NO ? "quarantined" : "unavailable" );
818
819 } else {
820
821 /*
822 * FIXME: in case one target cannot
823 * be init'd, should the other ones
824 * be tried?
825 */
826 /* leave the target candidate, but record the error for later use */
827 err = lerr;
828
829 if ( lerr == LDAP_UNAVAILABLE && mt->mt_isquarantined != LDAP_BACK_FQ_NO ) {
830 Debug( LDAP_DEBUG_TRACE, "%s: asyncmeta_getconn[%d] quarantined err=%d\n",
831 op->o_log_prefix, i, lerr );
832
833 } else {
834 Debug( LDAP_DEBUG_ANY, "%s: asyncmeta_getconn[%d] failed err=%d\n",
835 op->o_log_prefix, i, lerr );
836 }
837
838 if ( META_BACK_ONERR_STOP( mi ) ) {
839 if ( sendok & LDAP_BACK_SENDERR ) {
840 send_ldap_result( op, rs );
841 }
842 ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
843 if ( alloc_new > 0 ) {
844 asyncmeta_back_conn_free( mc );
845
846 }
847 return NULL;
848 }
849
850 continue;
851 }
852
853 }
854 }
855
856 if ( ncandidates == 0 ) {
857 if ( rs->sr_err == LDAP_SUCCESS ) {
858 rs->sr_err = LDAP_NO_SUCH_OBJECT;
859 rs->sr_text = "Unable to select valid candidates";
860 }
861
862 if ( sendok & LDAP_BACK_SENDERR ) {
863 if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
864 rs->sr_matched = mi->mi_suffix.bv_val;
865 }
866 send_ldap_result( op, rs );
867 rs->sr_matched = NULL;
868 }
869 if ( alloc_new > 0 ) {
870 asyncmeta_back_conn_free( mc );
871
872 }
873 ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
874 return NULL;
875 }
876 }
877
878 done:;
879 rs->sr_err = LDAP_SUCCESS;
880 rs->sr_text = NULL;
881
882 if ( new_conn ) {
883 if ( !LDAP_BACK_PCONN_ISPRIV( mc ) ) {
884 /*
885 * Err could be -1 in case a duplicate metaconn is inserted
886 */
887 switch ( err ) {
888 case 0:
889 break;
890 default:
891 LDAP_BACK_CONN_CACHED_CLEAR( mc );
892 if ( LogTest( LDAP_DEBUG_ANY ) ) {
893 char buf[STRLENOF("4294967295U") + 1] = { 0 };
894 mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
895
896 Debug( LDAP_DEBUG_ANY,
897 "%s asyncmeta_getconn: candidates=%d conn=%s insert failed\n",
898 op->o_log_prefix, ncandidates, buf );
899 }
900
901 asyncmeta_back_conn_free( mc );
902
903 rs->sr_err = LDAP_OTHER;
904 rs->sr_text = "Proxy bind collision";
905 if ( sendok & LDAP_BACK_SENDERR ) {
906 send_ldap_result( op, rs );
907 }
908 return NULL;
909 }
910 }
911
912 if ( LogTest( LDAP_DEBUG_TRACE ) ) {
913 char buf[STRLENOF("4294967295U") + 1] = { 0 };
914 mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
915
916 Debug( LDAP_DEBUG_TRACE,
917 "%s asyncmeta_getconn: candidates=%d conn=%s inserted\n",
918 op->o_log_prefix, ncandidates, buf );
919 }
920
921 } else {
922 if ( LogTest( LDAP_DEBUG_TRACE ) ) {
923 char buf[STRLENOF("4294967295U") + 1] = { 0 };
924 mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
925
926 Debug( LDAP_DEBUG_TRACE,
927 "%s asyncmeta_getconn: candidates=%d conn=%s fetched\n",
928 op->o_log_prefix, ncandidates, buf );
929 }
930 }
931 ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
932 return mc;
933 }
934
935 void
asyncmeta_quarantine(Operation * op,a_metainfo_t * mi,SlapReply * rs,int candidate)936 asyncmeta_quarantine(
937 Operation *op,
938 a_metainfo_t *mi,
939 SlapReply *rs,
940 int candidate )
941 {
942 a_metatarget_t *mt = mi->mi_targets[ candidate ];
943
944 slap_retry_info_t *ri = &mt->mt_quarantine;
945
946 ldap_pvt_thread_mutex_lock( &mt->mt_quarantine_mutex );
947
948 if ( rs->sr_err == LDAP_UNAVAILABLE ) {
949 time_t new_last = slap_get_time();
950
951 switch ( mt->mt_isquarantined ) {
952 case LDAP_BACK_FQ_NO:
953 if ( ri->ri_last == new_last ) {
954 goto done;
955 }
956
957 Debug( LDAP_DEBUG_ANY,
958 "%s asyncmeta_quarantine[%d]: enter.\n",
959 op->o_log_prefix, candidate );
960
961 ri->ri_idx = 0;
962 ri->ri_count = 0;
963 break;
964
965 case LDAP_BACK_FQ_RETRYING:
966 Debug(LDAP_DEBUG_ANY,
967 "%s asyncmeta_quarantine[%d]: block #%d try #%d failed.\n",
968 op->o_log_prefix, candidate, ri->ri_idx,
969 ri->ri_count );
970
971 ++ri->ri_count;
972 if ( ri->ri_num[ ri->ri_idx ] != SLAP_RETRYNUM_FOREVER
973 && ri->ri_count == ri->ri_num[ ri->ri_idx ] )
974 {
975 ri->ri_count = 0;
976 ++ri->ri_idx;
977 }
978 break;
979
980 default:
981 goto done;
982 }
983
984 mt->mt_isquarantined = LDAP_BACK_FQ_YES;
985 ri->ri_last = new_last;
986
987 } else if ( mt->mt_isquarantined == LDAP_BACK_FQ_RETRYING ) {
988 Debug( LDAP_DEBUG_ANY,
989 "%s asyncmeta_quarantine[%d]: exit.\n",
990 op->o_log_prefix, candidate );
991
992 if ( mi->mi_quarantine_f ) {
993 (void)mi->mi_quarantine_f( mi, candidate,
994 mi->mi_quarantine_p );
995 }
996
997 ri->ri_count = 0;
998 ri->ri_idx = 0;
999 mt->mt_isquarantined = LDAP_BACK_FQ_NO;
1000 mt->mt_timeout_ops = 0;
1001 }
1002
1003 done:;
1004 ldap_pvt_thread_mutex_unlock( &mt->mt_quarantine_mutex );
1005 }
1006
1007 a_metaconn_t *
asyncmeta_get_next_mc(a_metainfo_t * mi)1008 asyncmeta_get_next_mc( a_metainfo_t *mi )
1009 {
1010 a_metaconn_t *mc = NULL;
1011
1012 ldap_pvt_thread_mutex_lock( &mi->mi_mc_mutex );
1013 if (mi->mi_next_conn >= mi->mi_num_conns-1) {
1014 mi->mi_next_conn = 0;
1015 } else {
1016 mi->mi_next_conn++;
1017 }
1018
1019 mc = &mi->mi_conns[mi->mi_next_conn];
1020 ldap_pvt_thread_mutex_unlock( &mi->mi_mc_mutex );
1021 return mc;
1022 }
1023
asyncmeta_start_listeners(a_metaconn_t * mc,SlapReply * candidates,bm_context_t * bc)1024 int asyncmeta_start_listeners(a_metaconn_t *mc, SlapReply *candidates, bm_context_t *bc)
1025 {
1026 int i;
1027 for (i = 0; i < mc->mc_info->mi_ntargets; i++) {
1028 asyncmeta_start_one_listener(mc, candidates, bc, i);
1029 }
1030 return LDAP_SUCCESS;
1031 }
1032
asyncmeta_start_one_listener(a_metaconn_t * mc,SlapReply * candidates,bm_context_t * bc,int candidate)1033 int asyncmeta_start_one_listener(a_metaconn_t *mc,
1034 SlapReply *candidates,
1035 bm_context_t *bc,
1036 int candidate)
1037 {
1038 a_metasingleconn_t *msc;
1039 ber_socket_t s;
1040
1041 msc = &mc->mc_conns[candidate];
1042 if ( slapd_shutdown || !META_BACK_CONN_INITED( msc ) || msc->msc_ld == NULL
1043 || META_BACK_CONN_INVALID(msc) || !META_IS_CANDIDATE( &candidates[ candidate ] )) {
1044 return LDAP_SUCCESS;
1045 }
1046 bc->msgids[candidate] = candidates[candidate].sr_msgid;
1047 if ( msc->conn == NULL) {
1048 ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
1049 if (s < 0) {
1050 /* Todo a meaningful log pls */
1051 return LDAP_OTHER;
1052 }
1053 msc->conn = connection_client_setup( s, asyncmeta_op_handle_result, mc );
1054 }
1055 connection_client_enable( msc->conn );
1056 return LDAP_SUCCESS;
1057 }
1058
1059 int
asyncmeta_clear_one_msc(Operation * op,a_metaconn_t * mc,int candidate,int unbind,const char * caller)1060 asyncmeta_clear_one_msc(
1061 Operation *op,
1062 a_metaconn_t *mc,
1063 int candidate,
1064 int unbind,
1065 const char *caller)
1066 {
1067 a_metasingleconn_t *msc;
1068 if (mc == NULL) {
1069 return 0;
1070 }
1071 msc = &mc->mc_conns[candidate];
1072 if ( LogTest( asyncmeta_debug ) ) {
1073 char time_buf[ SLAP_TEXT_BUFLEN ];
1074 asyncmeta_get_timestamp(time_buf);
1075 Debug( asyncmeta_debug, "[%s] Resetting msc: %p, msc_ld: %p, "
1076 "msc_bound_ndn: %s, msc->conn: %p, %s \n",
1077 time_buf, msc, msc->msc_ld, msc->msc_bound_ndn.bv_val,
1078 msc->conn, caller ? caller : "" );
1079 }
1080 msc->msc_mscflags = 0;
1081 if (msc->conn) {
1082 connection_client_stop( msc->conn );
1083 msc->conn = NULL;
1084 }
1085
1086 if ( msc->msc_ld != NULL ) {
1087
1088 #ifdef DEBUG_205
1089 Debug( LDAP_DEBUG_ANY, "### %s asyncmeta_clear_one_msc ldap_unbind_ext[%d] ld=%p\n",
1090 op ? op->o_log_prefix : "", candidate, (void *)msc->msc_ld );
1091 #endif /* DEBUG_205 */
1092
1093 ldap_unbind_ext( msc->msc_ld, NULL, NULL );
1094 msc->msc_ld = NULL;
1095 ldap_ld_free( msc->msc_ldr, 0, NULL, NULL );
1096 msc->msc_ldr = NULL;
1097 }
1098
1099 if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
1100 ber_memfree_x( msc->msc_bound_ndn.bv_val, NULL );
1101 BER_BVZERO( &msc->msc_bound_ndn );
1102 }
1103
1104 if ( !BER_BVISNULL( &msc->msc_cred ) ) {
1105 memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
1106 ber_memfree_x( msc->msc_cred.bv_val, NULL );
1107 BER_BVZERO( &msc->msc_cred );
1108 }
1109 msc->msc_time = 0;
1110 msc->msc_binding_time = 0;
1111 msc->msc_result_time = 0;
1112 return 0;
1113 }
1114
asyncmeta_get_timestamp(char * buf)1115 void asyncmeta_get_timestamp(char *buf)
1116 {
1117 struct timespec tp;
1118 struct tm *ttm;
1119 clock_gettime(CLOCK_REALTIME, &tp);
1120 ttm = gmtime(&tp.tv_sec);
1121 sprintf(buf, "%d:%d:%d.%ld", ttm->tm_hour, ttm->tm_min, ttm->tm_sec, tp.tv_nsec/1000);
1122 }
1123
1124 int
asyncmeta_reset_msc(Operation * op,a_metaconn_t * mc,int candidate,int unbind,const char * caller)1125 asyncmeta_reset_msc(
1126 Operation *op,
1127 a_metaconn_t *mc,
1128 int candidate,
1129 int unbind,
1130 const char *caller)
1131 {
1132 a_metasingleconn_t *msc = &mc->mc_conns[candidate];
1133 if ( LogTest( asyncmeta_debug ) ) {
1134 char time_buf[ SLAP_TEXT_BUFLEN ];
1135 asyncmeta_get_timestamp(time_buf);
1136 Debug(asyncmeta_debug, "[%x] Will attempt to reset [%s] msc: %p, "
1137 "msc->msc_binding_time: %x, msc->msc_flags:%x %s\n",
1138 (unsigned int)slap_get_time(), time_buf, msc,
1139 (unsigned int)msc->msc_binding_time, msc->msc_mscflags, caller );
1140 }
1141 if (msc->msc_active <= 1 && mc->mc_active < 1) {
1142 bm_context_t *om;
1143 asyncmeta_clear_one_msc(NULL, mc, candidate, 0, caller);
1144 /* set whatever's in the queue to invalid, so the timeout loop cleans it up,
1145 * but do not invalidate the current op*/
1146 LDAP_STAILQ_FOREACH( om, &mc->mc_om_list, bc_next ) {
1147 if (om->candidates[candidate].sr_msgid >= 0 && (om->op != op)) {
1148 om->bc_invalid = 1;
1149 }
1150 }
1151 return LDAP_SUCCESS;
1152 } else {
1153 META_BACK_CONN_INVALID_SET(msc);
1154 Debug( asyncmeta_debug, "[%x] Failed to reset msc %p, msc_active=%d, mc_active=%d, %s\n",
1155 (unsigned int)slap_get_time(), msc, msc->msc_active, mc->mc_active, caller );
1156 }
1157 return LDAP_OTHER;
1158 }
1159
1160
asyncmeta_log_msc(a_metasingleconn_t * msc)1161 void asyncmeta_log_msc(a_metasingleconn_t *msc)
1162 {
1163 ber_socket_t s = 0;
1164 if (msc->msc_ld) {
1165 ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
1166 }
1167 Debug( asyncmeta_debug, "msc: %p, msc_ld: %p, msc_ld socket: %d, "
1168 "msc_bound_ndn: %s, msc->conn: %p\n", msc, msc->msc_ld,
1169 (int)s, msc->msc_bound_ndn.bv_val, msc->conn );
1170 }
1171
asyncmeta_log_conns(a_metainfo_t * mi)1172 void asyncmeta_log_conns(a_metainfo_t *mi)
1173 {
1174 a_metaconn_t *mc;
1175 int i, j;
1176 for (i = 0; i < mi->mi_num_conns; i++) {
1177 mc = &mi->mi_conns[i];
1178 Debug(asyncmeta_debug, "mc: %p, mc->pending_ops: %d\n", mc, mc->pending_ops);
1179 for (j = 0; j < mi->mi_ntargets; j++ ) {
1180 asyncmeta_log_msc(&mc->mc_conns[j]);
1181 }
1182
1183 }
1184 }
1185
1186 int
asyncmeta_db_has_pending_ops(a_metainfo_t * mi)1187 asyncmeta_db_has_pending_ops(a_metainfo_t *mi)
1188 {
1189 int i;
1190 if (mi->mi_ntargets == 0) {
1191 return 0;
1192 }
1193
1194 for (i = 0; i < mi->mi_num_conns; i++) {
1195 if (mi->mi_conns[i].pending_ops > 0) {
1196 return mi->mi_conns[i].pending_ops;
1197 }
1198 }
1199
1200 return 0;
1201 }
1202
1203
1204 int
asyncmeta_db_has_mscs(a_metainfo_t * mi)1205 asyncmeta_db_has_mscs(a_metainfo_t *mi)
1206 {
1207 int i, j;
1208 if (mi->mi_ntargets == 0) {
1209 return 0;
1210 }
1211
1212 for (i = 0; i < mi->mi_num_conns; i++) {
1213 for (j = 0; j < mi->mi_ntargets; j++) {
1214 if (mi->mi_conns[i].mc_conns[j].msc_ld != NULL ||
1215 mi->mi_conns[i].mc_conns[j].msc_ldr != NULL ) {
1216 return 1;
1217 }
1218 }
1219 }
1220
1221 return 0;
1222 }
1223