1/* $NetBSD: add.c,v 1.2 2021/08/14 16:14:59 christos Exp $ */ 2 3/* add.c - add request handler for back-asyncmeta */ 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 26#include <sys/cdefs.h> 27__RCSID("$NetBSD: add.c,v 1.2 2021/08/14 16:14:59 christos Exp $"); 28 29#include "portable.h" 30 31#include <stdio.h> 32 33#include <ac/string.h> 34#include <ac/socket.h> 35#include "slap.h" 36#include "../../../libraries/liblber/lber-int.h" 37#include "../../../libraries/libldap/ldap-int.h" 38#include "../back-ldap/back-ldap.h" 39#include "back-asyncmeta.h" 40#include "ldap_rq.h" 41 42 43int 44asyncmeta_error_cleanup(Operation *op, 45 SlapReply *rs, 46 bm_context_t *bc, 47 a_metaconn_t *mc, 48 int candidate) 49{ 50 ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex); 51 mc->mc_conns[candidate].msc_active--; 52 if (asyncmeta_bc_in_queue(mc,bc) == NULL || bc->bc_active > 1) { 53 bc->bc_active--; 54 ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex); 55 return LDAP_SUCCESS; 56 } 57 asyncmeta_drop_bc(mc, bc); 58 slap_sl_mem_setctx(op->o_threadctx, op->o_tmpmemctx); 59 ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex); 60 send_ldap_result(op, rs); 61 return LDAP_SUCCESS; 62} 63 64meta_search_candidate_t 65asyncmeta_back_add_start(Operation *op, 66 SlapReply *rs, 67 a_metaconn_t *mc, 68 bm_context_t *bc, 69 int candidate, 70 int do_lock) 71{ 72 int isupdate; 73 Attribute *a; 74 int i; 75 LDAPMod **attrs; 76 a_dncookie dc; 77 a_metainfo_t *mi = mc->mc_info; 78 a_metatarget_t *mt = mi->mi_targets[ candidate ]; 79 struct berval mdn = {0, NULL}; 80 meta_search_candidate_t retcode = META_SEARCH_CANDIDATE; 81 BerElement *ber = NULL; 82 a_metasingleconn_t *msc = &mc->mc_conns[ candidate ]; 83 SlapReply *candidates = bc->candidates; 84 ber_int_t msgid; 85 LDAPControl **ctrls = NULL; 86 int rc; 87 88 dc.op = op; 89 dc.target = mt; 90 dc.memctx = op->o_tmpmemctx; 91 dc.to_from = MASSAGE_REQ; 92 asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn ); 93 94 /* Count number of attributes in entry ( +1 ) */ 95 for ( i = 1, a = op->ora_e->e_attrs; a; i++, a = a->a_next ); 96 97 /* Create array of LDAPMods for ldap_add() */ 98 attrs = op->o_tmpalloc(sizeof( LDAPMod * )*i, op->o_tmpmemctx); 99 100 isupdate = be_shadow_update( op ); 101 for ( i = 0, a = op->ora_e->e_attrs; a; a = a->a_next ) { 102 int j; 103 104 if ( !isupdate && !get_relax( op ) && a->a_desc->ad_type->sat_no_user_mod ) 105 { 106 continue; 107 } 108 109 attrs[ i ] = op->o_tmpalloc( sizeof( LDAPMod ), op->o_tmpmemctx ); 110 if ( attrs[ i ] == NULL ) { 111 continue; 112 } 113 attrs[ i ]->mod_op = LDAP_MOD_BVALUES; 114 attrs[ i ]->mod_type = a->a_desc->ad_cname.bv_val; 115 j = a->a_numvals; 116 attrs[ i ]->mod_bvalues = op->o_tmpalloc( ( j + 1 ) * sizeof( struct berval * ), op->o_tmpmemctx ); 117 for (j=0; j<a->a_numvals; j++) { 118 attrs[ i ]->mod_bvalues[ j ] = op->o_tmpalloc( sizeof( struct berval ), op->o_tmpmemctx ); 119 if ( a->a_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) 120 asyncmeta_dn_massage( &dc, &a->a_vals[ j ], attrs[ i ]->mod_bvalues[ j ] ); 121 else 122 *attrs[ i ]->mod_bvalues[ j ] = a->a_vals[ j ]; 123 } 124 125 attrs[ i ]->mod_bvalues[ j ] = NULL; 126 i++; 127 } 128 attrs[ i ] = NULL; 129 130 asyncmeta_set_msc_time(msc); 131 132 ctrls = op->o_ctrls; 133 if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root, &ctrls ) != LDAP_SUCCESS ) 134 { 135 candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; 136 retcode = META_SEARCH_ERR; 137 goto done; 138 } 139 /* someone might have reset the connection */ 140 if (!( LDAP_BACK_CONN_ISBOUND( msc ) 141 || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) { 142 Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ ); 143 goto error_unavailable; 144 } 145 146 ber = ldap_build_add_req( msc->msc_ld, mdn.bv_val, attrs, ctrls, NULL, &msgid); 147 if (!ber) { 148 Debug( asyncmeta_debug, "%s asyncmeta_back_add_start: Operation encoding failed with errno %d\n", 149 op->o_log_prefix, msc->msc_ld->ld_errno ); 150 rs->sr_err = LDAP_OPERATIONS_ERROR; 151 rs->sr_text = "Failed to encode proxied request"; 152 retcode = META_SEARCH_ERR; 153 goto done; 154 } 155 156 if (ber) { 157 struct timeval tv = {0, mt->mt_network_timeout*1000}; 158 ber_socket_t s; 159 if (!( LDAP_BACK_CONN_ISBOUND( msc ) 160 || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) { 161 Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ ); 162 goto error_unavailable; 163 } 164 ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s ); 165 if (s < 0) { 166 Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ ); 167 goto error_unavailable; 168 } 169 170 rc = ldap_int_poll( msc->msc_ld, s, &tv, 1); 171 if (rc < 0) { 172 Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ ); 173 if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) { 174 rc = LDAP_SERVER_DOWN; 175 } else { 176 goto error_unavailable; 177 } 178 } else { 179 candidates[ candidate ].sr_msgid = msgid; 180 rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_ADD, 181 mdn.bv_val, ber, msgid ); 182 if (rc == msgid) 183 rc = LDAP_SUCCESS; 184 else 185 rc = LDAP_SERVER_DOWN; 186 ber = NULL; 187 } 188 189 switch ( rc ) { 190 case LDAP_SUCCESS: 191 retcode = META_SEARCH_CANDIDATE; 192 asyncmeta_set_msc_time(msc); 193 goto done; 194 195 case LDAP_SERVER_DOWN: 196 /* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */ 197 if (do_lock > 0) { 198 ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex); 199 asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__); 200 ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex); 201 } 202 /* fall though*/ 203 default: 204 Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ ); 205 goto error_unavailable; 206 } 207 } 208 209error_unavailable: 210 if (ber) 211 ber_free(ber, 1); 212 switch (bc->nretries[candidate]) { 213 case -1: /* nretries = forever */ 214 ldap_pvt_thread_yield(); 215 retcode = META_SEARCH_NEED_BIND; 216 break; 217 case 0: /* no retries left */ 218 candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; 219 rs->sr_err = LDAP_UNAVAILABLE; 220 rs->sr_text = "Unable to send add request to target"; 221 retcode = META_SEARCH_ERR; 222 break; 223 default: /* more retries left - try to rebind and go again */ 224 retcode = META_SEARCH_NEED_BIND; 225 bc->nretries[candidate]--; 226 ldap_pvt_thread_yield(); 227 break; 228 } 229 230done: 231 (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls ); 232 233 if ( mdn.bv_val != op->o_req_dn.bv_val ) { 234 op->o_tmpfree( mdn.bv_val, op->o_tmpmemctx ); 235 } 236 237 Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_add_start[%p]=%d\n", op->o_log_prefix, msc, candidates[candidate].sr_msgid ); 238 return retcode; 239} 240 241 242int 243asyncmeta_back_add( Operation *op, SlapReply *rs ) 244{ 245 a_metainfo_t *mi = ( a_metainfo_t * )op->o_bd->be_private; 246 a_metatarget_t *mt; 247 a_metaconn_t *mc; 248 int rc, candidate = -1; 249 void *thrctx = op->o_threadctx; 250 bm_context_t *bc; 251 SlapReply *candidates; 252 time_t current_time = slap_get_time(); 253 int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops; 254 255 Debug(LDAP_DEBUG_TRACE, "==> asyncmeta_back_add: %s\n", 256 op->o_req_dn.bv_val ); 257 258 if (current_time > op->o_time) { 259 Debug(asyncmeta_debug, "==> asyncmeta_back_add[%s]: o_time:[%ld], current time: [%ld]\n", 260 op->o_log_prefix, op->o_time, current_time ); 261 } 262 263 asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi ); 264 if (bc == NULL) { 265 rs->sr_err = LDAP_OTHER; 266 send_ldap_result(op, rs); 267 return rs->sr_err; 268 } 269 270 candidates = bc->candidates; 271 mc = asyncmeta_getconn( op, rs, candidates, &candidate, LDAP_BACK_DONTSEND, 0); 272 if ( !mc || rs->sr_err != LDAP_SUCCESS) { 273 send_ldap_result(op, rs); 274 return rs->sr_err; 275 } 276 277 mt = mi->mi_targets[ candidate ]; 278 bc->timeout = mt->mt_timeout[ SLAP_OP_ADD ]; 279 bc->retrying = LDAP_BACK_RETRYING; 280 bc->sendok = ( LDAP_BACK_SENDRESULT | bc->retrying ); 281 bc->stoptime = op->o_time + bc->timeout; 282 bc->bc_active = 1; 283 284 if (mc->pending_ops >= max_pending_ops) { 285 rs->sr_err = LDAP_BUSY; 286 rs->sr_text = "Maximum pending ops limit exceeded"; 287 send_ldap_result(op, rs); 288 return rs->sr_err; 289 } 290 291 ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex); 292 rc = asyncmeta_add_message_queue(mc, bc); 293 mc->mc_conns[candidate].msc_active++; 294 ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex); 295 296 if (rc != LDAP_SUCCESS) { 297 rs->sr_err = LDAP_BUSY; 298 rs->sr_text = "Maximum pending ops limit exceeded"; 299 send_ldap_result(op, rs); 300 ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex); 301 mc->mc_conns[candidate].msc_active--; 302 ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex); 303 goto finish; 304 } 305 306retry: 307 current_time = slap_get_time(); 308 if (bc->timeout && bc->stoptime < current_time) { 309 int timeout_err; 310 timeout_err = op->o_protocol >= LDAP_VERSION3 ? 311 LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER; 312 rs->sr_err = timeout_err; 313 rs->sr_text = "Operation timed out before it was sent to target"; 314 asyncmeta_error_cleanup(op, rs, bc, mc, candidate); 315 goto finish; 316 } 317 318 rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, candidate); 319 switch (rc) 320 { 321 case META_SEARCH_CANDIDATE: 322 /* target is already bound, just send the request */ 323 Debug(LDAP_DEBUG_TRACE , "%s asyncmeta_back_add: " 324 "cnd=\"%d\"\n", op->o_log_prefix, candidate ); 325 326 rc = asyncmeta_back_add_start( op, rs, mc, bc, candidate, 1); 327 if (rc == META_SEARCH_ERR) { 328 asyncmeta_error_cleanup(op, rs, bc, mc, candidate); 329 goto finish; 330 331 } else if (rc == META_SEARCH_NEED_BIND) { 332 goto retry; 333 } 334 break; 335 case META_SEARCH_NOT_CANDIDATE: 336 Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_add: NOT_CANDIDATE " 337 "cnd=\"%d\"\n", op->o_log_prefix, candidate ); 338 asyncmeta_error_cleanup(op, rs, bc, mc, candidate); 339 goto finish; 340 341 case META_SEARCH_NEED_BIND: 342 case META_SEARCH_BINDING: 343 Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_add: BINDING " 344 "cnd=\"%d\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]); 345 /* add the context to the message queue but do not send the request 346 the receiver must send this when we are done binding */ 347 break; 348 349 case META_SEARCH_ERR: 350 Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_add: ERR " 351 "cnd=\"%d\"\n", op->o_log_prefix, candidate ); 352 asyncmeta_error_cleanup(op, rs, bc, mc, candidate); 353 goto finish; 354 default: 355 assert( 0 ); 356 break; 357 } 358 359 ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex); 360 mc->mc_conns[candidate].msc_active--; 361 asyncmeta_start_one_listener(mc, candidates, bc, candidate); 362 bc->bc_active--; 363 ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex); 364 rs->sr_err = SLAPD_ASYNCOP; 365finish: 366 return rs->sr_err; 367} 368