Commit bb7e14d2 authored by Nadezhda Ivanova's avatar Nadezhda Ivanova Committed by Quanah Gibson-Mount
Browse files

ITS#8734 Fixes for many back-asyncmeta issues

Includes all the changes necessary to fix back-asyncmeta issues
discovered during on-site testing since the start of 2016.
These include:
Issues with stability - crashes and assetion failures
Incorrect behavior during unstable network conditions, such as inability to reset connections
or process responses, or "hanging" to wait for a response that would never be received.
Memory leaks and memory management fixes - major redesign of the way back-asyncmeta
works with memory contexts.
Rewrite was replaced with suffix-massage in configuration, and the network-timeout value was changed to milliseconds.
Incorrect behavior when SASL is used to bind to a target.
Many problems caused by race conditions
Fixes for compiler warnings, and tests.
Cleanup of unused code.
parent 06d289f9
......@@ -376,16 +376,18 @@ Sets the network timeout value after which
.BR poll (2)/ select (2)
following a
.BR connect (2)
returns in case of no activity.
The value is in seconds, and it can be specified as for
returns in case of no activity while sending an operation to the remote target.
The value is in milliseconds, and it can be specified as for
.BR idle\-timeout .
If set before any target specification, it affects all targets, unless
overridden by any per-target directive.
.TP
.B nretries {forever|never|<nretries>}
This directive defines how many times a bind should be retried
in case of temporary failure in contacting a target. If defined
This directive defines how many times forwarding an operation should be retried
in case of temporary failure in contacting a target. The number of retries
is per operation, so if a bind to the target is necessary first, the remaining
number is decremented. If defined
before any target specification, it applies to all targets (by default,
.BR 3
times);
......@@ -409,11 +411,13 @@ by a target. See
for details.
.TP
.B suffixmassage "<virtual naming context>" "<real naming context>"
All the directives starting with "rewrite" refer to the rewrite engine
that has been added to slapd. See
.B slapd\-meta(5)
for details.
.B suffixmassage "<local suffix>" "<remote suffix>"
.B slapd\-asyncmeta
does not support the rewrite engine used by
the LDAP and META backends.
.B suffixmassage
can be used to perform DN suffix rewriting, the same way as the obsoleted suffixmassage directive
previously used by the LDAP backend.
.TP
.B t\-f\-support {NO|yes|discover}
......
......@@ -19,12 +19,12 @@
## based on back-meta module for inclusion in OpenLDAP Software.
## This work was sponsored by Ericsson
SRCS = init.c config.c search.c message_queue.c bind.c unbind.c add.c compare.c \
delete.c modify.c modrdn.c suffixmassage.c map.c \
conn.c candidates.c dncache.c meta_result.c abandon.c
OBJS = init.lo config.lo search.lo message_queue.lo bind.lo unbind.lo add.lo compare.lo \
delete.lo modify.lo modrdn.lo suffixmassage.lo map.lo \
conn.lo candidates.lo dncache.lo meta_result.lo abandon.lo
SRCS = init.c config.c search.c message_queue.c bind.c add.c compare.c \
delete.c modify.c modrdn.c map.c \
conn.c candidates.c dncache.c meta_result.c
OBJS = init.lo config.lo search.lo message_queue.lo bind.lo add.lo compare.lo \
delete.lo modify.lo modrdn.lo map.lo \
conn.lo candidates.lo dncache.lo meta_result.lo
LDAP_INCDIR= ../../../include
LDAP_LIBDIR= ../../../libraries
......
/* abandon.c - abandon request handler for back-asyncmeta */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2016-2019 The OpenLDAP Foundation.
* Portions Copyright 2016 Symas Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in the file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/* ACKNOWLEDGEMENTS:
* This work was developed by Symas Corporation
* based on back-meta module for inclusion in OpenLDAP Software.
* This work was sponsored by Ericsson. */
#include "portable.h"
#include <stdio.h>
#include <ac/string.h>
#include <ac/socket.h>
#include "slap.h"
#include "../back-ldap/back-ldap.h"
#include "back-asyncmeta.h"
#include "ldap_rq.h"
/* function is unused */
int
asyncmeta_back_abandon( Operation *op, SlapReply *rs )
{
Operation *t_op;
/* Find the ops being abandoned */
ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
LDAP_STAILQ_FOREACH( t_op, &op->o_conn->c_ops, o_next ) {
if ( t_op->o_msgid == op->orn_msgid ) {
t_op->o_abandon = 1;
}
}
ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
return LDAP_SUCCESS;
}
......@@ -27,23 +27,33 @@
#include <ac/string.h>
#include <ac/socket.h>
#include "slap.h"
#include "../../../libraries/liblber/lber-int.h"
#include "../../../libraries/libldap/ldap-int.h"
#include "../back-ldap/back-ldap.h"
#include "back-asyncmeta.h"
#include "ldap_rq.h"
#include "../../../libraries/liblber/lber-int.h"
#include "../../../libraries/libldap/ldap-int.h"
void
asyncmeta_sender_error(Operation *op,
SlapReply *rs,
slap_callback *cb)
int
asyncmeta_error_cleanup(Operation *op,
SlapReply *rs,
bm_context_t *bc,
a_metaconn_t *mc,
int candidate)
{
if (cb != NULL) {
op->o_callback = cb;
ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
mc->mc_conns[candidate].msc_active--;
if (asyncmeta_bc_in_queue(mc,bc) == NULL || bc->bc_active > 1) {
bc->bc_active--;
ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
return LDAP_SUCCESS;
}
asyncmeta_drop_bc(mc, bc);
slap_sl_mem_setctx(op->o_threadctx, op->o_tmpmemctx);
ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
send_ldap_result(op, rs);
return LDAP_SUCCESS;
}
meta_search_candidate_t
......@@ -51,195 +61,174 @@ asyncmeta_back_add_start(Operation *op,
SlapReply *rs,
a_metaconn_t *mc,
bm_context_t *bc,
int candidate)
int candidate,
int do_lock)
{
int isupdate;
Attribute *a;
int i;
LDAPMod **attrs;
struct berval mapped;
a_dncookie dc;
a_metainfo_t *mi = mc->mc_info;
a_metatarget_t *mt = mi->mi_targets[ candidate ];
struct berval mdn;
struct berval mdn = {0, NULL};
meta_search_candidate_t retcode = META_SEARCH_CANDIDATE;
BerElement *ber = NULL;
a_metasingleconn_t *msc = &mc->mc_conns[ candidate ];
SlapReply *candidates = bc->candidates;
ber_int_t msgid;
LDAPControl **ctrls = NULL;
int rc, nretries = 1;
int rc;
dc.op = op;
dc.target = mt;
dc.conn = op->o_conn;
dc.rs = rs;
dc.ctx = "addDN";
mdn.bv_len = 0;
switch (asyncmeta_dn_massage( &dc, &bc->op->o_req_dn, &mdn ) )
{
case LDAP_SUCCESS:
break;
case LDAP_UNWILLING_TO_PERFORM:
rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
rs->sr_text = "Operation not allowed";
retcode = META_SEARCH_ERR;
goto doreturn;
default:
rs->sr_err = LDAP_NO_SUCH_OBJECT;
retcode = META_SEARCH_NOT_CANDIDATE;
goto doreturn;
}
dc.memctx = op->o_tmpmemctx;
dc.to_from = MASSAGE_REQ;
asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn );
/* Count number of attributes in entry ( +1 ) */
for ( i = 1, a = op->ora_e->e_attrs; a; i++, a = a->a_next );
/* Create array of LDAPMods for ldap_add() */
attrs = ch_malloc( sizeof( LDAPMod * )*i );
attrs = op->o_tmpalloc(sizeof( LDAPMod * )*i, op->o_tmpmemctx);
dc.ctx = "addAttrDN";
isupdate = be_shadow_update( op );
for ( i = 0, a = op->ora_e->e_attrs; a; a = a->a_next ) {
int j, is_oc = 0;
int j;
if ( !isupdate && !get_relax( op ) && a->a_desc->ad_type->sat_no_user_mod )
{
continue;
}
if ( a->a_desc == slap_schema.si_ad_objectClass
|| a->a_desc == slap_schema.si_ad_structuralObjectClass )
{
is_oc = 1;
mapped = a->a_desc->ad_cname;
} else {
asyncmeta_map( &mt->mt_rwmap.rwm_at,
&a->a_desc->ad_cname, &mapped, BACKLDAP_MAP );
if ( BER_BVISNULL( &mapped ) || BER_BVISEMPTY( &mapped ) ) {
continue;
}
}
attrs[ i ] = ch_malloc( sizeof( LDAPMod ) );
attrs[ i ] = op->o_tmpalloc( sizeof( LDAPMod ), op->o_tmpmemctx );
if ( attrs[ i ] == NULL ) {
continue;
}
attrs[ i ]->mod_op = LDAP_MOD_BVALUES;
attrs[ i ]->mod_type = mapped.bv_val;
if ( is_oc ) {
for ( j = 0; !BER_BVISNULL( &a->a_vals[ j ] ); j++ );
attrs[ i ]->mod_bvalues =
(struct berval **)ch_malloc( ( j + 1 ) *
sizeof( struct berval * ) );
for ( j = 0; !BER_BVISNULL( &a->a_vals[ j ] ); ) {
struct ldapmapping *mapping;
asyncmeta_mapping( &mt->mt_rwmap.rwm_oc,
&a->a_vals[ j ], &mapping, BACKLDAP_MAP );
if ( mapping == NULL ) {
if ( mt->mt_rwmap.rwm_oc.drop_missing ) {
continue;
}
attrs[ i ]->mod_bvalues[ j ] = &a->a_vals[ j ];
} else {
attrs[ i ]->mod_bvalues[ j ] = &mapping->dst;
}
j++;
}
attrs[ i ]->mod_bvalues[ j ] = NULL;
} else {
/*
* FIXME: dn-valued attrs should be rewritten
* to allow their use in ACLs at the back-ldap
* level.
*/
if ( a->a_desc->ad_type->sat_syntax ==
slap_schema.si_syn_distinguishedName )
{
(void)asyncmeta_dnattr_rewrite( &dc, a->a_vals );
if ( a->a_vals == NULL ) {
continue;
}
}
for ( j = 0; !BER_BVISNULL( &a->a_vals[ j ] ); j++ )
;
attrs[ i ]->mod_bvalues = ch_malloc( ( j + 1 ) * sizeof( struct berval * ) );
for ( j = 0; !BER_BVISNULL( &a->a_vals[ j ] ); j++ ) {
attrs[ i ]->mod_bvalues[ j ] = &a->a_vals[ j ];
}
attrs[ i ]->mod_bvalues[ j ] = NULL;
attrs[ i ]->mod_type = a->a_desc->ad_cname.bv_val;
j = a->a_numvals;
attrs[ i ]->mod_bvalues = op->o_tmpalloc( ( j + 1 ) * sizeof( struct berval * ), op->o_tmpmemctx );
for (j=0; j<a->a_numvals; j++) {
attrs[ i ]->mod_bvalues[ j ] = op->o_tmpalloc( sizeof( struct berval ), op->o_tmpmemctx );
if ( a->a_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName )
asyncmeta_dn_massage( &dc, &a->a_vals[ j ], attrs[ i ]->mod_bvalues[ j ] );
else
*attrs[ i ]->mod_bvalues[ j ] = a->a_vals[ j ];
}
attrs[ i ]->mod_bvalues[ j ] = NULL;
i++;
}
attrs[ i ] = NULL;
retry:;
asyncmeta_set_msc_time(msc);
ctrls = op->o_ctrls;
if ( asyncmeta_controls_add( op, rs, mc, candidate, &ctrls ) != LDAP_SUCCESS )
if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root, &ctrls ) != LDAP_SUCCESS )
{
candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
retcode = META_SEARCH_ERR;
goto done;
}
/* someone might have reset the connection */
if (!( LDAP_BACK_CONN_ISBOUND( msc )
|| LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
goto error_unavailable;
}
ber = ldap_build_add_req( msc->msc_ld, mdn.bv_val, attrs, ctrls, NULL, &msgid);
if (!ber) {
Debug( asyncmeta_debug, "%s asyncmeta_back_add_start: Operation encoding failed with errno %d\n",
op->o_log_prefix, msc->msc_ld->ld_errno );
rs->sr_err = LDAP_OPERATIONS_ERROR;
rs->sr_text = "Failed to encode proxied request";
retcode = META_SEARCH_ERR;
goto done;
}
if (ber) {
candidates[ candidate ].sr_msgid = msgid;
rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_ADD,
mdn.bv_val, ber, msgid );
if (rc == msgid)
rc = LDAP_SUCCESS;
else
rc = LDAP_SERVER_DOWN;
struct timeval tv = {0, mt->mt_network_timeout*1000};
ber_socket_t s;
if (!( LDAP_BACK_CONN_ISBOUND( msc )
|| LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
goto error_unavailable;
}
ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
if (s < 0) {
Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
goto error_unavailable;
}
rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
if (rc < 0) {
Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ );
if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) {
rc = LDAP_SERVER_DOWN;
} else {
goto error_unavailable;
}
} else {
candidates[ candidate ].sr_msgid = msgid;
rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_ADD,
mdn.bv_val, ber, msgid );
if (rc == msgid)
rc = LDAP_SUCCESS;
else
rc = LDAP_SERVER_DOWN;
ber = NULL;
}
switch ( rc ) {
case LDAP_SUCCESS:
retcode = META_SEARCH_CANDIDATE;
asyncmeta_set_msc_time(msc);
break;
goto done;
case LDAP_SERVER_DOWN:
ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
asyncmeta_clear_one_msc(NULL, mc, candidate);
ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
if ( nretries && asyncmeta_retry( op, rs, &mc, candidate, LDAP_BACK_DONTSEND ) ) {
nretries = 0;
/* if the identity changed, there might be need to re-authz */
(void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
goto retry;
/* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */
if (do_lock > 0) {
ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__);
ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
}
/* fall though*/
default:
candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
retcode = META_SEARCH_ERR;
Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ );
goto error_unavailable;
}
}
done:
error_unavailable:
if (ber)
ber_free(ber, 1);
switch (bc->nretries[candidate]) {
case -1: /* nretries = forever */
ldap_pvt_thread_yield();
retcode = META_SEARCH_NEED_BIND;
break;
case 0: /* no retries left */
candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
rs->sr_err = LDAP_UNAVAILABLE;
rs->sr_text = "Unable to send add request to target";
retcode = META_SEARCH_ERR;
break;
default: /* more retries left - try to rebind and go again */
retcode = META_SEARCH_NEED_BIND;
bc->nretries[candidate]--;
ldap_pvt_thread_yield();
break;
}
done:
(void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
for ( --i; i >= 0; --i ) {
free( attrs[ i ]->mod_bvalues );
free( attrs[ i ] );
}
free( attrs );
if ( mdn.bv_val != op->ora_e->e_dn ) {
free( mdn.bv_val );
BER_BVZERO( &mdn );
if ( mdn.bv_val != op->o_req_dn.bv_val ) {
op->o_tmpfree( mdn.bv_val, op->o_tmpmemctx );
}
doreturn:;
Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_add_start[%p]=%d\n", op->o_log_prefix, msc, candidates[candidate].sr_msgid );
return retcode;
}
......@@ -252,26 +241,31 @@ asyncmeta_back_add( Operation *op, SlapReply *rs )
a_metatarget_t *mt;
a_metaconn_t *mc;
int rc, candidate = -1;
OperationBuffer opbuf;
void *thrctx = op->o_threadctx;
bm_context_t *bc;
SlapReply *candidates;
slap_callback *cb = op->o_callback;
time_t current_time = slap_get_time();
int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops;
Debug(LDAP_DEBUG_ARGS, "==> asyncmeta_back_add: %s\n",
Debug(LDAP_DEBUG_TRACE, "==> asyncmeta_back_add: %s\n",
op->o_req_dn.bv_val );
asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets );
if (current_time > op->o_time) {
Debug(asyncmeta_debug, "==> asyncmeta_back_add[%s]: o_time:[%ld], current time: [%ld]\n",
op->o_log_prefix, op->o_time, current_time );
}
asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi );
if (bc == NULL) {
rs->sr_err = LDAP_OTHER;
asyncmeta_sender_error(op, rs, cb);
send_ldap_result(op, rs);
return rs->sr_err;
}
candidates = bc->candidates;
mc = asyncmeta_getconn( op, rs, candidates, &candidate, LDAP_BACK_DONTSEND, 0);
if ( !mc || rs->sr_err != LDAP_SUCCESS) {
asyncmeta_sender_error(op, rs, cb);
asyncmeta_clear_bm_context(bc);
send_ldap_result(op, rs);
return rs->sr_err;
}
......@@ -280,16 +274,39 @@ asyncmeta_back_add( Operation *op, SlapReply *rs )
bc->retrying = LDAP_BACK_RETRYING;
bc->sendok = ( LDAP_BACK_SENDRESULT | bc->retrying );
bc->stoptime = op->o_time + bc->timeout;
bc->bc_active = 1;
if (mc->pending_ops >= max_pending_ops) {
rs->sr_err = LDAP_BUSY;
rs->sr_text = "Maximum pending ops limit exceeded";
send_ldap_result(op, rs);
return rs->sr_err;
}
ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
rc = asyncmeta_add_message_queue(mc, bc);
mc->mc_conns[candidate].msc_active++;
ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
if (rc != LDAP_SUCCESS) {
rs->sr_err = LDAP_BUSY;
rs->sr_text = "Maximum pending ops limit exceeded";
asyncmeta_clear_bm_context(bc);
asyncmeta_sender_error(op, rs, cb);
send_ldap_result(op, rs);
ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
mc->mc_conns[candidate].msc_active--;
ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
goto finish;
}
retry:
current_time = slap_get_time();
if (bc->timeout && bc->stoptime < current_time) {
int timeout_err;
timeout_err = op->o_protocol >= LDAP_VERSION3 ?
LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
rs->sr_err = timeout_err;
rs->sr_text = "Operation timed out before it was sent to target";
asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
goto finish;
}
......@@ -298,72 +315,49 @@ asyncmeta_back_add( Operation *op, SlapReply *rs )
{
case META_SEARCH_CANDIDATE:
/* target is already bound, just send the request */
Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_add: "
"cnd=\"%ld\"\n", op->o_log_prefix, candidate );
Debug(LDAP_DEBUG_TRACE , "%s asyncmeta_back_add: "
"cnd=\"%d\"\n", op->o_log_prefix, candidate );
rc = asyncmeta_back_add_start( op, rs, mc, bc, candidate);
rc = asyncmeta_back_add_start( op, rs, mc, bc, candidate, 1);
if (rc == META_SEARCH_ERR) {
ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
asyncmeta_drop_bc(mc, bc);
ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
asyncmeta_sender_error(op, rs, cb);
asyncmeta_clear_bm_context(bc);
asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
goto finish;
} else if (rc == META_SEARCH_NEED_BIND) {
goto retry;
}
break;
break;
case META_SEARCH_NOT_CANDIDATE:
Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_add: NOT_CANDIDATE "
"cnd=\"%ld\"\n", op->o_log_prefix, candidate );
candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
asyncmeta_drop_bc(mc, bc);
ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
asyncmeta_sender_error(op, rs, cb);
asyncmeta_clear_bm_context(bc);
"cnd=\"%d\"\n", op->o_log_prefix, candidate );
asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
goto finish;
case META_SEARCH_NEED_BIND:
case META_SEARCH_CONNECTING:
Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_add: NEED_BIND "
"cnd=\"%ld\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
rc = asyncmeta_dobind_init(op, rs, bc, mc, candidate);
if (rc == META_SEARCH_ERR) {
ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
asyncmeta_drop_bc(mc, bc);
ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
asyncmeta_sender_error(op, rs, cb);
asyncmeta_clear_bm_context(bc);
goto finish;
}
break;
case META_SEARCH_BINDING:
Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_add: BINDING "
"cnd=\"%ld\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
/* Todo add the context to the message queue but do not send the request
the receiver must send this when we are done binding */
/* question - how would do receiver know to which targets??? */
break;
Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_add: BINDING "
"cnd=\"%d\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
/* add the context to the message queue but do not send the request