Home | History | Annotate | Line # | Download | only in back-asyncmeta
modrdn.c revision 1.3
      1  1.1  christos /*	$NetBSD: modrdn.c,v 1.3 2025/09/05 21:16:26 christos Exp $	*/
      2  1.1  christos 
      3  1.1  christos /* modrdn.c - modrdn request handler for back-syncmeta */
      4  1.1  christos /* $OpenLDAP$ */
      5  1.1  christos /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  1.1  christos  *
      7  1.3  christos  * Copyright 2016-2024 The OpenLDAP Foundation.
      8  1.1  christos  * Portions Copyright 2016 Symas Corporation.
      9  1.1  christos  * All rights reserved.
     10  1.1  christos  *
     11  1.1  christos  * Redistribution and use in source and binary forms, with or without
     12  1.1  christos  * modification, are permitted only as authorized by the OpenLDAP
     13  1.1  christos  * Public License.
     14  1.1  christos  *
     15  1.1  christos  * A copy of this license is available in the file LICENSE in the
     16  1.1  christos  * top-level directory of the distribution or, alternatively, at
     17  1.1  christos  * <http://www.OpenLDAP.org/license.html>.
     18  1.1  christos  */
     19  1.1  christos 
     20  1.1  christos /* ACKNOWLEDGEMENTS:
     21  1.1  christos  * This work was developed by Symas Corporation
     22  1.1  christos  * based on back-meta module for inclusion in OpenLDAP Software.
     23  1.1  christos  * This work was sponsored by Ericsson. */
     24  1.1  christos 
     25  1.1  christos #include <sys/cdefs.h>
     26  1.1  christos __RCSID("$NetBSD: modrdn.c,v 1.3 2025/09/05 21:16:26 christos Exp $");
     27  1.1  christos 
     28  1.1  christos #include "portable.h"
     29  1.1  christos 
     30  1.1  christos #include <stdio.h>
     31  1.1  christos 
     32  1.1  christos #include <ac/socket.h>
     33  1.1  christos #include <ac/string.h>
     34  1.1  christos #include "slap.h"
     35  1.1  christos #include "../../../libraries/liblber/lber-int.h"
     36  1.1  christos #include "../../../libraries/libldap/ldap-int.h"
     37  1.1  christos #include "../back-ldap/back-ldap.h"
     38  1.1  christos #include "back-asyncmeta.h"
     39  1.1  christos 
     40  1.1  christos meta_search_candidate_t
     41  1.1  christos asyncmeta_back_modrdn_start(Operation *op,
     42  1.1  christos 			    SlapReply *rs,
     43  1.1  christos 			    a_metaconn_t *mc,
     44  1.1  christos 			    bm_context_t *bc,
     45  1.1  christos 			    int candidate,
     46  1.1  christos 			    int do_lock)
     47  1.1  christos {
     48  1.1  christos 	a_dncookie	dc;
     49  1.1  christos 	a_metainfo_t	*mi = mc->mc_info;
     50  1.1  christos 	a_metatarget_t	*mt = mi->mi_targets[ candidate ];
     51  1.1  christos 	struct berval	mdn = BER_BVNULL,
     52  1.1  christos 		mnewSuperior = BER_BVNULL,
     53  1.1  christos 		newrdn = BER_BVNULL;
     54  1.1  christos 	int rc = 0;
     55  1.1  christos 	LDAPControl	**ctrls = NULL;
     56  1.1  christos 	meta_search_candidate_t retcode = META_SEARCH_CANDIDATE;
     57  1.1  christos 	BerElement *ber = NULL;
     58  1.1  christos 	a_metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
     59  1.1  christos 	SlapReply		*candidates = bc->candidates;
     60  1.1  christos 	ber_int_t	msgid;
     61  1.1  christos 
     62  1.1  christos 	dc.op = op;
     63  1.1  christos 	dc.target = mt;
     64  1.1  christos 	dc.memctx = op->o_tmpmemctx;
     65  1.1  christos 	dc.to_from = MASSAGE_REQ;
     66  1.1  christos 
     67  1.1  christos 	if ( op->orr_newSup ) {
     68  1.1  christos 
     69  1.1  christos 		/*
     70  1.1  christos 		 * NOTE: the newParent, if defined, must be on the
     71  1.1  christos 		 * same target as the entry to be renamed.  This check
     72  1.1  christos 		 * has been anticipated in meta_back_getconn()
     73  1.1  christos 		 */
     74  1.1  christos 		/*
     75  1.1  christos 		 * FIXME: one possibility is to delete the entry
     76  1.1  christos 		 * from one target and add it to the other;
     77  1.1  christos 		 * unfortunately we'd need write access to both,
     78  1.1  christos 		 * which is nearly impossible; for administration
     79  1.1  christos 		 * needs, the rootdn of the metadirectory could
     80  1.1  christos 		 * be mapped to an administrative account on each
     81  1.1  christos 		 * target (the binddn?); we'll see.
     82  1.1  christos 		 */
     83  1.1  christos 		/*
     84  1.1  christos 		 * NOTE: we need to port the identity assertion
     85  1.1  christos 		 * feature from back-ldap
     86  1.1  christos 		 */
     87  1.1  christos 
     88  1.1  christos 		/* needs LDAPv3 */
     89  1.1  christos 		switch ( mt->mt_version ) {
     90  1.1  christos 		case LDAP_VERSION3:
     91  1.1  christos 			break;
     92  1.1  christos 
     93  1.1  christos 		case 0:
     94  1.1  christos 			if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
     95  1.1  christos 				break;
     96  1.1  christos 			}
     97  1.1  christos 			/* fall thru */
     98  1.1  christos 
     99  1.1  christos 		default:
    100  1.1  christos 			/* op->o_protocol cannot be anything but LDAPv3,
    101  1.1  christos 			 * otherwise wouldn't be here */
    102  1.1  christos 			rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
    103  1.1  christos 			retcode = META_SEARCH_ERR;
    104  1.1  christos 			goto done;
    105  1.1  christos 		}
    106  1.1  christos 
    107  1.1  christos 		/*
    108  1.1  christos 		 * Rewrite the new superior, if defined and required
    109  1.1  christos 		 */
    110  1.1  christos 		asyncmeta_dn_massage( &dc, op->orr_newSup, &mnewSuperior );
    111  1.1  christos 	}
    112  1.1  christos 
    113  1.1  christos 	/*
    114  1.1  christos 	 * Rewrite the modrdn dn, if required
    115  1.1  christos 	 */
    116  1.1  christos 	asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn );
    117  1.1  christos 
    118  1.1  christos 	/* NOTE: we need to copy the newRDN in case it was formed
    119  1.1  christos 	 * from a DN by simply changing the length (ITS#5397) */
    120  1.1  christos 	newrdn = op->orr_newrdn;
    121  1.1  christos 	if ( newrdn.bv_val[ newrdn.bv_len ] != '\0' ) {
    122  1.1  christos 		ber_dupbv_x( &newrdn, &op->orr_newrdn, op->o_tmpmemctx );
    123  1.1  christos 	}
    124  1.1  christos 
    125  1.1  christos 	asyncmeta_set_msc_time(msc);
    126  1.1  christos 	ctrls = op->o_ctrls;
    127  1.1  christos 	if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root, &ctrls ) != LDAP_SUCCESS )
    128  1.1  christos 	{
    129  1.1  christos 		candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
    130  1.1  christos 		retcode = META_SEARCH_ERR;
    131  1.1  christos 		goto done;
    132  1.1  christos 	}
    133  1.1  christos 	/* someone might have reset the connection */
    134  1.1  christos 	if (!( LDAP_BACK_CONN_ISBOUND( msc )
    135  1.1  christos 	       || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
    136  1.1  christos 		Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
    137  1.1  christos 		goto error_unavailable;
    138  1.1  christos 	}
    139  1.1  christos 	ber = ldap_build_moddn_req( msc->msc_ld, mdn.bv_val, newrdn.bv_val,
    140  1.1  christos 			mnewSuperior.bv_val, op->orr_deleteoldrdn, ctrls, NULL, &msgid);
    141  1.1  christos 
    142  1.1  christos 	if (!ber) {
    143  1.1  christos 		Debug( asyncmeta_debug, "%s asyncmeta_back_modrdn_start: Operation encoding failed with errno %d\n",
    144  1.1  christos 		       op->o_log_prefix, msc->msc_ld->ld_errno );
    145  1.1  christos 		rs->sr_err = LDAP_OPERATIONS_ERROR;
    146  1.1  christos 		rs->sr_text = "Failed to encode proxied request";
    147  1.1  christos 		retcode = META_SEARCH_ERR;
    148  1.1  christos 		goto done;
    149  1.1  christos 	}
    150  1.1  christos 
    151  1.1  christos 	if (ber) {
    152  1.1  christos 		struct timeval tv = {0, mt->mt_network_timeout*1000};
    153  1.1  christos 		ber_socket_t s;
    154  1.1  christos 
    155  1.1  christos 		if (!( LDAP_BACK_CONN_ISBOUND( msc )
    156  1.1  christos 		       || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
    157  1.1  christos 			Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
    158  1.1  christos 			goto error_unavailable;
    159  1.1  christos 		}
    160  1.1  christos 
    161  1.1  christos 		ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
    162  1.1  christos 		if (s < 0) {
    163  1.1  christos 			Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
    164  1.1  christos 			goto error_unavailable;
    165  1.1  christos 		}
    166  1.1  christos 
    167  1.1  christos 		rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
    168  1.1  christos 		if (rc < 0) {
    169  1.1  christos 			Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ );
    170  1.1  christos 			if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) {
    171  1.1  christos 				rc = LDAP_SERVER_DOWN;
    172  1.1  christos 			} else {
    173  1.1  christos 				goto error_unavailable;
    174  1.1  christos 			}
    175  1.1  christos 		} else {
    176  1.1  christos 			candidates[ candidate ].sr_msgid = msgid;
    177  1.1  christos 			rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_MODRDN,
    178  1.1  christos 							mdn.bv_val, ber, msgid );
    179  1.1  christos 			if (rc == msgid)
    180  1.1  christos 				rc = LDAP_SUCCESS;
    181  1.1  christos 			else
    182  1.1  christos 				rc = LDAP_SERVER_DOWN;
    183  1.1  christos 			ber = NULL;
    184  1.1  christos 		}
    185  1.1  christos 
    186  1.1  christos 		switch ( rc ) {
    187  1.1  christos 		case LDAP_SUCCESS:
    188  1.1  christos 			retcode = META_SEARCH_CANDIDATE;
    189  1.1  christos 			asyncmeta_set_msc_time(msc);
    190  1.1  christos 			goto done;
    191  1.1  christos 
    192  1.1  christos 		case LDAP_SERVER_DOWN:
    193  1.1  christos 			/* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */
    194  1.1  christos 			if (do_lock > 0) {
    195  1.1  christos 				ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
    196  1.1  christos 				asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__ );
    197  1.1  christos 				ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
    198  1.1  christos 			}
    199  1.1  christos 			/* fall though*/
    200  1.1  christos 		default:
    201  1.1  christos 			Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ );
    202  1.1  christos 			goto error_unavailable;
    203  1.1  christos 		}
    204  1.1  christos 	}
    205  1.1  christos 
    206  1.1  christos error_unavailable:
    207  1.1  christos 	if (ber)
    208  1.1  christos 		ber_free(ber, 1);
    209  1.1  christos 	switch (bc->nretries[candidate]) {
    210  1.1  christos 	case -1: /* nretries = forever */
    211  1.1  christos 		retcode = META_SEARCH_NEED_BIND;
    212  1.1  christos 		ldap_pvt_thread_yield();
    213  1.1  christos 		break;
    214  1.1  christos 	case 0: /* no retries left */
    215  1.1  christos 		candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
    216  1.1  christos 		rs->sr_err = LDAP_UNAVAILABLE;
    217  1.1  christos 		rs->sr_text = "Unable to send modrdn request to target";
    218  1.1  christos 		retcode = META_SEARCH_ERR;
    219  1.1  christos 		break;
    220  1.1  christos 	default: /* more retries left - try to rebind and go again */
    221  1.1  christos 		retcode = META_SEARCH_NEED_BIND;
    222  1.1  christos 		bc->nretries[candidate]--;
    223  1.1  christos 		ldap_pvt_thread_yield();
    224  1.1  christos 		break;
    225  1.1  christos 	}
    226  1.1  christos done:
    227  1.1  christos 	(void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
    228  1.1  christos 
    229  1.1  christos 	if ( mdn.bv_val != op->o_req_dn.bv_val ) {
    230  1.1  christos 		op->o_tmpfree( mdn.bv_val, op->o_tmpmemctx );
    231  1.1  christos 	}
    232  1.1  christos 
    233  1.1  christos 	if ( !BER_BVISNULL( &mnewSuperior )
    234  1.1  christos 			&& mnewSuperior.bv_val != op->orr_newSup->bv_val )
    235  1.1  christos 	{
    236  1.1  christos 		op->o_tmpfree( mnewSuperior.bv_val, op->o_tmpmemctx );
    237  1.1  christos 	}
    238  1.1  christos 
    239  1.1  christos 	if ( newrdn.bv_val != op->orr_newrdn.bv_val ) {
    240  1.1  christos 		op->o_tmpfree( newrdn.bv_val, op->o_tmpmemctx );
    241  1.1  christos 	}
    242  1.1  christos 
    243  1.1  christos 	Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_modrdn_start[%p]=%d\n", op->o_log_prefix, msc, candidates[candidate].sr_msgid );
    244  1.1  christos 	return retcode;
    245  1.1  christos }
    246  1.1  christos 
    247  1.1  christos int
    248  1.1  christos asyncmeta_back_modrdn( Operation *op, SlapReply *rs )
    249  1.1  christos {
    250  1.1  christos 	a_metainfo_t	*mi = ( a_metainfo_t * )op->o_bd->be_private;
    251  1.1  christos 	a_metatarget_t	*mt;
    252  1.1  christos 	a_metaconn_t	*mc;
    253  1.1  christos 	int		rc, candidate = -1;
    254  1.1  christos 	void *thrctx = op->o_threadctx;
    255  1.1  christos 	bm_context_t *bc;
    256  1.1  christos 	SlapReply *candidates;
    257  1.1  christos 	time_t current_time = slap_get_time();
    258  1.1  christos 	int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops;
    259  1.1  christos 
    260  1.1  christos 	Debug(LDAP_DEBUG_ARGS, "==> asyncmeta_back_modrdn: %s\n",
    261  1.1  christos 	      op->o_req_dn.bv_val );
    262  1.1  christos 
    263  1.1  christos 	if (current_time > op->o_time) {
    264  1.1  christos 		Debug(asyncmeta_debug, "==> asyncmeta_back_modrdn[%s]: o_time:[%ld], current time: [%ld]\n",
    265  1.1  christos 		      op->o_log_prefix, op->o_time, current_time );
    266  1.1  christos 	}
    267  1.3  christos 
    268  1.3  christos 	if ( mi->mi_ntargets == 0 ) {
    269  1.3  christos 		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
    270  1.3  christos 		rs->sr_text = "No targets are configured for this database";
    271  1.3  christos 		send_ldap_result(op, rs);
    272  1.3  christos 		return rs->sr_err;
    273  1.3  christos 	}
    274  1.3  christos 
    275  1.1  christos 	asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi );
    276  1.1  christos 	if (bc == NULL) {
    277  1.1  christos 		rs->sr_err = LDAP_OTHER;
    278  1.1  christos 		send_ldap_result(op, rs);
    279  1.1  christos 		return rs->sr_err;
    280  1.1  christos 	}
    281  1.1  christos 
    282  1.1  christos 	candidates = bc->candidates;
    283  1.1  christos 	mc = asyncmeta_getconn( op, rs, candidates, &candidate, LDAP_BACK_DONTSEND, 0);
    284  1.1  christos 	if ( !mc || rs->sr_err != LDAP_SUCCESS) {
    285  1.1  christos 		send_ldap_result(op, rs);
    286  1.1  christos 		return rs->sr_err;
    287  1.1  christos 	}
    288  1.1  christos 
    289  1.1  christos 	mt = mi->mi_targets[ candidate ];
    290  1.1  christos 	bc->timeout = mt->mt_timeout[ SLAP_OP_MODRDN ];
    291  1.1  christos 	bc->retrying = LDAP_BACK_RETRYING;
    292  1.1  christos 	bc->sendok = ( LDAP_BACK_SENDRESULT | bc->retrying );
    293  1.1  christos 	bc->stoptime = op->o_time + bc->timeout;
    294  1.1  christos 	bc->bc_active = 1;
    295  1.1  christos 
    296  1.1  christos 	if (mc->pending_ops >= max_pending_ops) {
    297  1.1  christos 		rs->sr_err = LDAP_BUSY;
    298  1.1  christos 		rs->sr_text = "Maximum pending ops limit exceeded";
    299  1.1  christos 		send_ldap_result(op, rs);
    300  1.1  christos 		return rs->sr_err;
    301  1.1  christos 	}
    302  1.1  christos 
    303  1.1  christos 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
    304  1.1  christos 	rc = asyncmeta_add_message_queue(mc, bc);
    305  1.1  christos 	mc->mc_conns[candidate].msc_active++;
    306  1.1  christos 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
    307  1.1  christos 
    308  1.1  christos 	if (rc != LDAP_SUCCESS) {
    309  1.1  christos 		rs->sr_err = LDAP_BUSY;
    310  1.1  christos 		rs->sr_text = "Maximum pending ops limit exceeded";
    311  1.1  christos 		send_ldap_result(op, rs);
    312  1.1  christos 		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
    313  1.1  christos 		mc->mc_conns[candidate].msc_active--;
    314  1.1  christos 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
    315  1.1  christos 		goto finish;
    316  1.1  christos 	}
    317  1.1  christos 
    318  1.1  christos retry:
    319  1.1  christos 	if (bc->timeout && bc->stoptime < slap_get_time()) {
    320  1.1  christos 		int		timeout_err;
    321  1.1  christos 		timeout_err = op->o_protocol >= LDAP_VERSION3 ?
    322  1.1  christos 			LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
    323  1.1  christos 		rs->sr_err = timeout_err;
    324  1.1  christos 		rs->sr_text = "Operation timed out before it was sent to target";
    325  1.1  christos 		asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
    326  1.1  christos 		goto finish;
    327  1.1  christos 
    328  1.1  christos 	}
    329  1.1  christos 
    330  1.1  christos 	rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, candidate);
    331  1.1  christos 	switch (rc)
    332  1.1  christos 	{
    333  1.1  christos 	case META_SEARCH_CANDIDATE:
    334  1.1  christos 		/* target is already bound, just send the request */
    335  1.1  christos 		Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modrdn:  "
    336  1.1  christos 		       "cnd=\"%d\"\n", op->o_log_prefix, candidate );
    337  1.1  christos 
    338  1.1  christos 		rc = asyncmeta_back_modrdn_start( op, rs, mc, bc, candidate, 1);
    339  1.1  christos 		if (rc == META_SEARCH_ERR) {
    340  1.1  christos 			asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
    341  1.1  christos 			goto finish;
    342  1.1  christos 
    343  1.1  christos 		} else if (rc == META_SEARCH_NEED_BIND) {
    344  1.1  christos 			goto retry;
    345  1.1  christos 		}
    346  1.1  christos 		break;
    347  1.1  christos 	case META_SEARCH_NOT_CANDIDATE:
    348  1.1  christos 		Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modrdn: NOT_CANDIDATE "
    349  1.1  christos 		       "cnd=\"%d\"\n", op->o_log_prefix, candidate );
    350  1.1  christos 		asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
    351  1.1  christos 		goto finish;
    352  1.1  christos 
    353  1.1  christos 	case META_SEARCH_NEED_BIND:
    354  1.1  christos 	case META_SEARCH_BINDING:
    355  1.1  christos 			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modrdn: BINDING "
    356  1.1  christos 			       "cnd=\"%d\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
    357  1.1  christos 			/* Todo add the context to the message queue but do not send the request
    358  1.1  christos 			   the receiver must send this when we are done binding */
    359  1.1  christos 			/* question - how would do receiver know to which targets??? */
    360  1.1  christos 			break;
    361  1.1  christos 
    362  1.1  christos 	case META_SEARCH_ERR:
    363  1.1  christos 			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modrdn: ERR "
    364  1.1  christos 			       "cnd=\"%d\"\n", op->o_log_prefix, candidate );
    365  1.1  christos 			asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
    366  1.1  christos 			goto finish;
    367  1.1  christos 		default:
    368  1.1  christos 			assert( 0 );
    369  1.1  christos 			break;
    370  1.1  christos 		}
    371  1.1  christos 
    372  1.1  christos 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
    373  1.1  christos 	mc->mc_conns[candidate].msc_active--;
    374  1.1  christos 	asyncmeta_start_one_listener(mc, candidates, bc, candidate);
    375  1.1  christos 	bc->bc_active--;
    376  1.1  christos 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
    377  1.1  christos 	rs->sr_err = SLAPD_ASYNCOP;
    378  1.1  christos finish:
    379  1.1  christos 	return rs->sr_err;
    380  1.1  christos }
    381