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