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