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